velocity problems?

master
John Jordan 2012-11-19 05:25:00 +00:00
parent a6c5e4470f
commit 3b9b41fe06
13 changed files with 322 additions and 1356 deletions

View File

@ -588,6 +588,7 @@ define_model('mushroom_station_2', {
materials = {'body', 'text', 'markings', 'lift_floor', 'tower_base', 'inside'},
tags = {'surface_station'},
num_docking_ports = 2,
ship_launch_stage = 2,
-- 1 - permission granted
-- 2 - position docked ship
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 2, 4, 4 },
@ -694,6 +695,7 @@ define_model('mushroom_station_4', {
materials = {'body', 'text', 'markings', 'lift_floor', 'tower_base', 'inside'},
tags = {'surface_station'},
num_docking_ports = 4,
ship_launch_stage = 2,
-- 1 - permission granted
-- 2 - position docked ship
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 2, 4, 4 },
@ -810,6 +812,7 @@ define_model('big_crappy_spacestation', {
angular_velocity = 0.1,
lod_pixels = {0},
num_docking_ports = 4,
ship_launch_stage = 3,
-- for stations where each docking port shares the
-- same front door, set dock_one_at_a_time_please = true,
dock_one_at_a_time_please = true,
@ -920,6 +923,7 @@ define_model('nice_spacestation', {
angular_velocity = 0.15,
lod_pixels = { 50, 0 },
num_docking_ports = 1,
ship_launch_stage = 8, -- lower than animation stage count
-- docking:
-- 1 - permission granted. open door1
-- 2 - center ship, close door1
@ -1096,6 +1100,7 @@ define_model('hoop_spacestation', {
angular_velocity = 0.08,
lod_pixels = { 50, 0 },
num_docking_ports = 1,
ship_launch_stage = 8, -- lower than animation stage count
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0 },
undock_anim_stage_duration = { 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 20.0 },
ship_dock_anim = function(port, stage, t, from, ship_aabb)
@ -1248,6 +1253,7 @@ define_model('basic_groundstation', {
bounding_radius=200.0,
materials = {'body', 'text', 'tower_base'},
num_docking_ports = 2,
ship_launch_stage = 0,
-- 1 - permission granted
-- 2 - position docked ship
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 2 },

View File

@ -742,6 +742,7 @@ define_model('ground_station_1', {
materials = {'text', 'pad', 'body', 'lens', 'screen', 'lit_lamp'},
tags = {'surface_station'},
num_docking_ports = 1,
ship_launch_stage = 0,
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 4.0},
undock_anim_stage_duration = { 0 },
ship_dock_anim = function(port, stage, t, from, ship_aabb)
@ -777,6 +778,7 @@ define_model('ground_station_2', {
materials = {'text', 'pad', 'body', 'lens', 'screen', 'lit_lamp'},
tags = {'surface_station'},
num_docking_ports = 2,
ship_launch_stage = 0,
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 4.0},
undock_anim_stage_duration = { 0 },
ship_dock_anim = function(port, stage, t, from, ship_aabb)
@ -814,6 +816,7 @@ define_model('ground_station_3', {
materials = {'text', 'pad', 'body', 'lens', 'screen', 'lit_lamp'},
tags = {'surface_station'},
num_docking_ports = 3,
ship_launch_stage = 0,
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 4.0},
undock_anim_stage_duration = { 0 },
ship_dock_anim = function(port, stage, t, from, ship_aabb)
@ -852,6 +855,7 @@ define_model('ground_station_4', {
materials = {'text', 'pad', 'body', 'lens', 'screen', 'lit_lamp'},
tags = {'surface_station'},
num_docking_ports = 4,
ship_launch_stage = 0,
dock_anim_stage_duration = { DOCKING_TIMEOUT_SECONDS, 4.0},
undock_anim_stage_duration = { 0 },
ship_dock_anim = function(port, stage, t, from, ship_aabb)

View File

@ -1,73 +0,0 @@
-- Copyright © 2008-2012 Pioneer Developers. See AUTHORS.txt for details
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
local loaded
local spawnShips = function ()
local population = Game.system.population
if population == 0 then
return
end
local stations = Space.GetBodies(function (body) return body:isa("SpaceStation") end)
if #stations == 0 then
return
end
local shiptypes = ShipType.GetShipTypes('STATIC_SHIP')
if #shiptypes == 0 then return end
--[[
assuming these are huge supply ships and not your run-of-the-mill
traders and not warships or whatever, we'll do it like this:
- first one is free
- one ship per billion up to 4 billion
- one ship per 5 billion after that
]]
local num_bulk_ships = 1
while population > 1 do
if num_bulk_ships < 4 then
population = population-1
num_bulk_ships = num_bulk_ships+1
elseif population > 5 then
population = population-5
num_bulk_ships = num_bulk_ships+1
else
break
end
end
for i=1, num_bulk_ships do
local station = stations[Engine.rand:Integer(1,#stations)]
Space.SpawnShipParked(shiptypes[Engine.rand:Integer(1,#shiptypes)], station)
end
end
local onEnterSystem = function (player)
if not player:IsPlayer() then return end
spawnShips()
end
local onGameStart = function ()
if loaded == nil then
spawnShips()
end
loaded = nil
end
local serialize = function ()
return true
end
local unserialize = function (data)
loaded = true
end
Event.Register("onEnterSystem", onEnterSystem)
Event.Register("onGameStart", onGameStart)
Serializer:Register("BulkShips", serialize, unserialize)

View File

@ -1,43 +0,0 @@
-- Copyright © 2008-2012 Pioneer Developers. See AUTHORS.txt for details
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
local onEnterSystem = function (player)
if not player:IsPlayer() then return end
local shiptypes = ShipType.GetShipTypes('SHIP', function (t)
local mass = t.hullMass
return mass >= 50 and mass <= 150
end)
if #shiptypes == 0 then return end
local lawlessness = Game.system.lawlessness
-- XXX number should be some combination of population, lawlessness,
-- proximity to shipping lanes, etc
local max_pirates = 6
while max_pirates > 0 and Engine.rand:Number(1) < lawlessness do
max_pirates = max_pirates-1
local shipid = shiptypes[Engine.rand:Integer(1,#shiptypes)]
local shiptype = ShipType.GetShipType(shipid)
local default_drive = shiptype.defaultHyperdrive
-- select a laser. this is naive - it simply chooses at random from
-- the set of lasers that will fit, but never more than one above the
-- player's current weapon.
-- XXX this should use external factors (eg lawlessness) and not be
-- dependent on the player in any way
local max_laser_size = shiptype.capacity - EquipType.GetEquipType(default_drive).mass
local lasers = EquipType.GetEquipTypes('LASER', function (e,et)
return et.mass <= max_laser_size and string.sub(e,0,11) == 'PULSECANNON'
end)
local laser = lasers[Engine.rand:Integer(1,#lasers)]
local ship = Space.SpawnShip(shipid, 8, 12)
ship:AddEquip(default_drive)
ship:AddEquip(laser)
ship:AIKill(Game.player)
end
end
Event.Register("onEnterSystem", onEnterSystem)

View File

@ -1,885 +0,0 @@
-- Copyright © 2008-2012 Pioneer Developers. See AUTHORS.txt for details
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
--[[
trade_ships
interval - is minimum amount of time between hyperspace arrivals,
stored here as it needs to be saved; number as seconds, updated by
spawnInitialShips
ship - object returned from Space:SpawnShip*
ship_name - of this ship type; string
ATMOSHIELD - flag indicating whether the ship has at atmospheric shield: boolean
starport - at which this ship intends to dock; SpaceStation object
dest_time - arrival time from hyperspace; number as Game.time
dest_path - for hyperspace; SystemPath object, may have body index
from_path - for hyperspace; SystemPath object
delay - indicates waiting for a Timer to give next action; number
as Game.time
status - of this ship; string, one of:
hyperspace - yet to arrive or has departed
inbound - in system and given AIDockWith order
docked - currently docked or un/docking
outbound - heading away from starport before hyperspacing
fleeing - has been attacked and is trying to get away
(currently still just following whatever AI order it had)
cowering - docked after having been attacked, waiting for
attacker to go away
orbit - was unable to dock, heading to or waiting in orbit
cargo - table of cargo types and amounts currently carried;
key: Constants.EquipType string, value: number
attacker - what this was last attacked by; Body object
chance - used to determine what action to take when attacked; number
last_flee - when last action was taken, number as Game.time
no_jump - whether this has tried to hyperspace away so it only
tries once; bool
system_updated - indicates whether the following tables have been updated
for the current system; bool, see onEnterSystem, onLeaveSystem, and
onGameStart
from_paths - paths of systems around the current system, used to get a
from_system for ships spawned in hyperspace; indexed array of
SystemPath objects, updated by spawnInitialShips
starports - in the current system; indexed array of SpaceStation objects,
updated by spawnInitialShips
imports, exports - in the current system, indexed array of
Constants.EquipType strings, updated by spawnInitialShips
--]]
local trade_ships, system_updated, from_paths, starports, imports, exports
local addFuel = function (ship)
local drive = ship:GetEquip('ENGINE', 1)
-- a drive must be installed
if drive == 'NONE' then
print(trade_ships[ship]['ship_name']..' has no drive!')
return nil
end
-- the last character of the fitted drive is the class
-- the fuel needed for max range is the square of the drive class
local count = tonumber(string.sub(drive, -1)) ^ 2
-- account for fuel it already has
count = count - ship:GetEquipCount('CARGO', 'HYDROGEN')
local added = ship:AddEquip('HYDROGEN', count)
return added
end
local addShipEquip = function (ship)
local trader = trade_ships[ship]
local ship_type = ShipType.GetShipType(trader.ship_name)
-- add standard equipment
ship:AddEquip(ship_type.defaultHyperdrive)
if ship:GetEquipSlotCapacity('ATMOSHIELD') > 0 then
ship:AddEquip('ATMOSPHERIC_SHIELDING')
trader.ATMOSHIELD = true -- flag this to save function calls later
else
-- This ship cannot safely land on a planet with an atmosphere.
trader.ATMOSHIELD = false
end
ship:AddEquip('SCANNER')
ship:AddEquip('AUTOPILOT')
ship:AddEquip('CARGO_LIFE_SUPPORT')
local stats = ship:GetStats()
-- add defensive equipment based on lawlessness, luck and size
local lawlessness = Game.system.lawlessness
local size_factor = stats.freeCapacity ^ 2 / 2000000
if Engine.rand:Number(1) - 0.1 < lawlessness then
local num = math.floor(math.sqrt(stats.freeCapacity / 50)) -
ship:GetEquipCount('SHIELD', 'SHIELD_GENERATOR')
if num > 0 then ship:AddEquip('SHIELD_GENERATOR', num) end
if ship_type:GetEquipSlotCapacity('ENERGYBOOSTER') > 0 and
Engine.rand:Number(1) + 0.5 - size_factor < lawlessness then
ship:AddEquip('SHIELD_ENERGY_BOOSTER')
end
end
-- we can't use these yet
if ship_type:GetEquipSlotCapacity('ECM') > 0 then
if Engine.rand:Number(1) + 0.2 < lawlessness then
ship:AddEquip('ECM_ADVANCED')
elseif Engine.rand:Number(1) < lawlessness then
ship:AddEquip('ECM_BASIC')
end
end
-- this should be rare
if ship_type:GetEquipSlotCapacity('HULLAUTOREPAIR') > 0 and
Engine.rand:Number(1) + 0.75 - size_factor < lawlessness then
ship:AddEquip('HULL_AUTOREPAIR')
end
end
local addShipCargo = function (ship, direction)
local prices = Game.system:GetCommodityBasePriceAlterations()
local total = 0
local empty_space = ship:GetStats().freeCapacity
local size_factor = empty_space / 20
local cargo = {}
if direction == 'import' and #imports == 1 then
total = ship:AddEquip(imports[1], empty_space)
cargo[imports[1]] = total
elseif direction == 'export' and #exports == 1 then
total = ship:AddEquip(exports[1], empty_space)
cargo[exports[1]] = total
elseif (direction == 'import' and #imports > 1) or
(direction == 'export' and #exports > 1) then
while total < empty_space do
local cargo_type
-- get random for direction
if direction == 'import' then
cargo_type = imports[Engine.rand:Integer(1, #imports)]
else
cargo_type = exports[Engine.rand:Integer(1, #exports)]
end
-- amount based on price and size of ship
local num = math.abs(prices[cargo_type]) * size_factor
num = Engine.rand:Integer(num, num * 2)
local added = ship:AddEquip(cargo_type, num)
if cargo[cargo_type] == nil then
cargo[cargo_type] = added
else
cargo[cargo_type] = cargo[cargo_type] + added
end
total = total + added
end
end
-- if the table for direction was empty then cargo is empty and total is 0
trade_ships[ship]['cargo'] = cargo
return total
end
local doUndock
doUndock = function (ship)
-- the player may have left the system or the ship may have already undocked
if ship:exists() and ship:GetDockedWith() then
local trader = trade_ships[ship]
if not ship:Undock() then
-- unable to undock, try again in ten minutes
trader['delay'] = Game.time + 600
Timer:CallAt(trader.delay, function () doUndock(ship) end)
else
trader['delay'] = nil
end
end
end
local doOrbit = function (ship)
local trader = trade_ships[ship]
local sbody = trader.starport.path:GetSystemBody()
local body = Space.GetBody(sbody.parent.index)
ship:AIEnterLowOrbit(body)
trader['status'] = 'orbit'
print(ship.label..' ordering orbit of '..body.label)
end
local getNearestStarport = function (ship, current)
if #starports == 0 then return nil end
if #starports == 1 then return starports[1] end
local trader = trade_ships[ship]
-- Find the nearest starport that we can land at (other than current)
local starport, distance
for i = 1, #starports do
local next_starport = starports[i]
if next_starport ~= current then
local next_distance = ship:DistanceTo(next_starport)
local next_canland = (trader.ATMOSHIELD or
(next_starport.type == 'STARPORT_ORBITAL') or
(not next_starport.path:GetSystemBody().parent.hasAtmosphere))
if next_canland and ((starport == nil) or (next_distance < distance)) then
starport, distance = next_starport, next_distance
end
end
end
return starport or current
end
local getSystem = function (ship)
local stats = ship:GetStats()
local systems_in_range = Game.system:GetNearbySystems(stats.hyperspaceRange)
if #systems_in_range == 0 then return nil end
if #systems_in_range == 1 then
return systems_in_range[1].path
end
local target_system = nil
local best_prices = 0
-- find best system for cargo
for _, next_system in ipairs(systems_in_range) do
if #next_system:GetStationPaths() > 0 then
local prices = next_system:GetCommodityBasePriceAlterations()
local next_prices = 0
for cargo, count in pairs(trade_ships[ship]['cargo']) do
next_prices = next_prices + (prices[cargo] * count)
end
if next_prices > best_prices then
target_system, best_prices = next_system, next_prices
end
end
end
if target_system == nil then
-- pick a random system as fallback
target_system = systems_in_range[Engine.rand:Integer(1, #systems_in_range)]
-- get closer systems
local systems_half_range = Game.system:GetNearbySystems(stats.hyperspaceRange / 2)
if #systems_half_range > 1 then
target_system = systems_half_range[Engine.rand:Integer(1, #systems_half_range)]
end
end
-- pick a random starport, if there are any, so the game can simulate
-- travel to it if player arrives after (see Space::DoHyperspaceTo)
local target_starport_paths = target_system:GetStationPaths()
if #target_starport_paths > 0 then
return target_starport_paths[Engine.rand:Integer(1, #target_starport_paths)]
end
return target_system.path
end
local jumpToSystem = function (ship, target_path)
if target_path == nil then return nil end
local status, fuel, duration = ship:HyperspaceTo(target_path)
if status ~= 'OK' then
print(trade_ships[ship]['ship_name']..' jump status is not OK')
return status
end
-- update table for ship
trade_ships[ship]['status'] = 'hyperspace'
trade_ships[ship]['starport'] = nil
trade_ships[ship]['dest_time'] = Game.time + duration
trade_ships[ship]['dest_path'] = target_path
trade_ships[ship]['from_path'] = Game.system.path
return status
end
local getSystemAndJump = function (ship)
return jumpToSystem(ship, getSystem(ship))
end
local filterAcceptableShips = function (ship_type)
-- only accept ships with enough capacity that are capable of landing in atmospheres
return (ship_type.hullMass >= 100) and (ship_type:GetEquipSlotCapacity('ATMOSHIELD') > 0)
end
local spawnInitialShips = function (game_start)
-- check if the current system can be traded in
starports = Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
if #starports == 0 then return nil end
local population = Game.system.population
if population == 0 then return nil end
local ship_names = ShipType.GetShipTypes('SHIP', filterAcceptableShips)
if #ship_names == 0 then return nil end
-- get a measure of the market size and build lists of imports and exports
local prices = Game.system:GetCommodityBasePriceAlterations()
local import_score, export_score = 0, 0
imports, exports = {}, {}
for k,v in pairs(prices) do
if k ~= 'RUBBISH' and k ~= 'RADIOACTIVES' and Game.system:IsCommodityLegal(k) then
-- values from SystemInfoView::UpdateEconomyTab
if v > 10 then
import_score = import_score + 2
elseif v > 2 then
import_score = import_score + 1
table.insert(imports, k)
elseif v < -10 then
export_score = export_score + 2
elseif v < -2 then
export_score = export_score + 1
table.insert(exports, k)
end
end
end
-- if there is no market then there is no trade
if #imports == 0 or #exports == 0 then return nil end
-- determine how many trade ships to spawn
local lawlessness = Game.system.lawlessness
-- start with three ships per two billion population
local num_trade_ships = population * 1.5
-- add the average of import_score and export_score
num_trade_ships = num_trade_ships + (import_score + export_score) / 2
-- reduce based on lawlessness
num_trade_ships = num_trade_ships * (1 - lawlessness)
-- vary by up to twice as many with a bell curve probability
num_trade_ships = num_trade_ships * (Engine.rand:Number(0.25, 1) + Engine.rand:Number(0.25, 1))
-- compute distance and interval between ships
-- the base number of AU between ships spawned in space
local range = (9 / (num_trade_ships * 0.75))
if game_start then
range = range * 1.5
end
-- the base number of seconds between ships spawned in hyperspace
trade_ships['interval'] = (864000 / (num_trade_ships / 4))
-- get nearby system paths for hyperspace spawns to come from
local from_systems, dist = {}, 10
while #from_systems < 10 do
dist = dist + 5
from_systems = Game.system:GetNearbySystems(dist)
end
from_paths = {}
for _, system in ipairs(from_systems) do
table.insert(from_paths, system.path)
end
-- spawn the initial trade ships
for i = 0, num_trade_ships do
-- get the name of a ship, for example 'imperial_courier'
local ship_name = ship_names[Engine.rand:Integer(1, #ship_names)]
local ship = nil
if game_start and i < num_trade_ships / 4 then
-- spawn the first quarter in port if at game start
local starport = starports[Engine.rand:Integer(1, #starports)]
ship = Space.SpawnShipDocked(ship_name, starport)
if ship ~= nil then
trade_ships[ship] = {
status = 'docked',
starport = starport,
ship_name = ship_name,
}
addShipEquip(ship)
else
-- the starport must have been full
ship = Space.SpawnShipNear(ship_name, starport, 10000000, 149598000) -- 10mkm - 1AU
trade_ships[ship] = {
status = 'inbound',
starport = starport,
ship_name = ship_name,
}
addShipEquip(ship)
end
elseif i < num_trade_ships * 0.75 then
-- spawn the first three quarters in space, or middle half if game start
local min_dist = range * i + 1
if game_start then
min_dist = min_dist - (range * (num_trade_ships / 4))
end
ship = Space.SpawnShip(ship_name, min_dist, min_dist + range)
trade_ships[ship] = {
status = 'inbound',
ship_name = ship_name,
}
-- Add ship equipment right now, because...
addShipEquip(ship)
-- ...this next call needs to see if there's an atmospheric shield.
trade_ships[ship].starport = getNearestStarport(ship)
else
-- spawn the last quarter in hyperspace
local min_time = trade_ships.interval * (i - num_trade_ships * 0.75)
local max_time = min_time + trade_ships.interval
local dest_time = Game.time + Engine.rand:Integer(min_time, max_time)
local from = from_paths[Engine.rand:Integer(1, #from_paths)]
ship = Space.SpawnShip(ship_name, 9, 11, {from, dest_time})
trade_ships[ship] = {
status = 'hyperspace',
dest_time = dest_time,
dest_path = Game.system.path,
from_path = from,
ship_name = ship_name,
}
addShipEquip(ship)
end
local trader = trade_ships[ship]
-- add cargo
local fuel_added = addFuel(ship)
if trader.status == 'docked' then
local delay = fuel_added + addShipCargo(ship, 'export')
-- have ship wait 30-45 seconds per unit of cargo
trader['delay'] = Game.time + (delay * Engine.rand:Number(30, 45))
Timer:CallAt(trader.delay, function () doUndock(ship) end)
else
addShipCargo(ship, 'import')
-- remove fuel used to get here
if fuel_added and fuel_added > 0 then
ship:RemoveEquip('HYDROGEN', Engine.rand:Integer(1, fuel_added)) end
if trader.status == 'inbound' then ship:AIDockWith(trader.starport) end
end
end
return num_trade_ships
end
local spawnReplacement = function ()
-- spawn new ship in hyperspace
if #starports > 0 and Game.system.population > 0 and #imports > 0 and #exports > 0 then
local ship_names = ShipType.GetShipTypes('SHIP', filterAcceptableShips)
local ship_name = ship_names[Engine.rand:Integer(1, #ship_names)]
local dest_time = Game.time + Engine.rand:Number(trade_ships.interval, trade_ships.interval * 2)
local from = from_paths[Engine.rand:Integer(1, #from_paths)]
local ship = Space.SpawnShip(ship_name, 9, 11, {from, dest_time})
trade_ships[ship] = {
status = 'hyperspace',
dest_time = dest_time,
dest_path = Game.system.path,
from_path = from,
ship_name = ship_name,
}
addShipEquip(ship)
local fuel_added = addFuel(ship)
addShipCargo(ship, 'import')
if fuel_added and fuel_added > 0 then
ship:RemoveEquip('HYDROGEN', Engine.rand:Integer(1, fuel_added))
end
end
end
local updateTradeShipsTable = function ()
local total, removed = 0, 0
for ship, trader in pairs(trade_ships) do
total = total + 1
if trader.status == 'hyperspace' then
-- remove ships not coming here
if not trader.dest_path:IsSameSystem(Game.system.path) then
trade_ships[ship] = nil
removed = removed + 1
end
else
-- remove ships that are not in hyperspace
trade_ships[ship] = nil
removed = removed + 1
end
end
print('updateTSTable:total:'..total..',removed:'..removed)
end
local cleanTradeShipsTable = function ()
local total, hyperspace, removed = 0, 0, 0
for ship, trader in pairs(trade_ships) do
if ship ~= 'interval' then
total = total + 1
if trader.status == 'hyperspace' then
hyperspace = hyperspace + 1
-- remove well past due ships as the player can not catch them
if trader.dest_time + 86400 < Game.time then
trade_ships[ship] = nil
removed = removed + 1
end
end
end
end
print('cleanTSTable:total:'..total..',active:'..total - hyperspace..',removed:'..removed)
end
local onEnterSystem = function (ship)
-- if the player is following a ship through hyperspace that ship may enter first
-- so update the system when the first ship enters (see Space::DoHyperspaceTo)
if not system_updated then
updateTradeShipsTable()
spawnInitialShips(false)
system_updated = true
end
if trade_ships[ship] ~= nil then
local trader = trade_ships[ship]
print(ship.label..' '..trader.ship_name..' entered '..Game.system.name..' from '..trader.from_path:GetStarSystem().name)
if #starports == 0 then
-- this only happens if player has followed ship to empty system
getSystemAndJump(ship)
-- if we couldn't reach any systems wait for player to attack
else
local starport = getNearestStarport(ship)
ship:AIDockWith(starport)
trade_ships[ship]['starport'] = starport
trade_ships[ship]['status'] = 'inbound'
end
end
end
Event.Register("onEnterSystem", onEnterSystem)
local onLeaveSystem = function (ship)
if ship:IsPlayer() then
-- the next onEnterSystem will be in a new system
system_updated = false
trade_ships['interval'] = nil
local total, removed = 0, 0
for t_ship, trader in pairs(trade_ships) do
total = total + 1
if trader.status == 'hyperspace' then
if trader.dest_path:IsSameSystem(Game.system.path) then
-- remove ships that are in hyperspace to here
trade_ships[t_ship] = nil
removed = removed + 1
end
else
-- remove all ships that are not in hyperspace
trade_ships[t_ship] = nil
removed = removed + 1
end
end
print('onLeaveSystem:total:'..total..',removed:'..removed)
elseif trade_ships[ship] ~= nil then
local system = trade_ships[ship]['dest_path']:GetStarSystem()
print(ship.label..' left '..Game.system.name..' for '..system.name)
cleanTradeShipsTable()
spawnReplacement()
end
end
Event.Register("onLeaveSystem", onLeaveSystem)
local onFrameChanged = function (ship)
if not ship:isa("Ship") or trade_ships[ship] == nil then return end
local trader = trade_ships[ship]
if trader.status == 'outbound' then
-- the cloud inherits the ship velocity and vector
ship:CancelAI()
if getSystemAndJump(ship) ~= 'OK' then
ship:AIDockWith(trader.starport)
trader['status'] = 'inbound'
end
end
end
Event.Register("onFrameChanged", onFrameChanged)
local onShipDocked = function (ship, starport)
if trade_ships[ship] == nil then return end
local trader = trade_ships[ship]
print(ship.label..' docked with '..starport.label..' ship:'..trader.ship_name)
if trader.status == 'fleeing' then
trader['status'] = 'cowering'
else
trader['status'] = 'docked'
end
if trader.chance then
trader['chance'] = trader.chance / 2
trader['last_flee'], trader['no_jump'] = nil, nil
end
-- 'sell' trade cargo
local delay = 0
for cargo, _ in pairs(trader.cargo) do
delay = delay + ship:RemoveEquip(cargo, 1000000)
end
local damage = ShipType.GetShipType(trader.ship_name).hullMass -
ship:GetStats().hullMassLeft
if damage > 0 then
ship:SetHullPercent()
addShipEquip(ship)
damage = damage * 4
end
addFuel(ship)
delay = delay + addShipCargo(ship, 'export')
if damage > delay then delay = damage end
-- delay undocking by 30-45 seconds for every unit of cargo transfered
-- or 2-3 minutes for every unit of hull repaired
if delay > 0 then
trader['delay'] = Game.time + (delay * Engine.rand:Number(30, 45))
else
trader['delay'] = Game.time + Engine.rand:Number(600, 3600)
end
if trader.status == 'docked' then
Timer:CallAt(trader.delay, function () doUndock(ship) end)
end
end
Event.Register("onShipDocked", onShipDocked)
local onShipUndocked = function (ship, starport)
if trade_ships[ship] == nil then return end
-- fly to the limit of the starport frame
ship:AIFlyTo(starport)
trade_ships[ship]['status'] = 'outbound'
end
Event.Register("onShipUndocked", onShipUndocked)
local onAICompleted = function (ship, ai_error)
if trade_ships[ship] == nil then return end
local trader = trade_ships[ship]
if ai_error ~= 'NONE' then
print(ship.label..' AICompleted: Error: '..ai_error..' Status: '..trader.status) end
if trader.status == 'outbound' then
if getSystemAndJump(ship) ~= 'OK' then
ship:AIDockWith(trader.starport)
trader['status'] = 'inbound'
end
elseif trader.status == 'orbit' then
if ai_error == 'NONE' then
trader['delay'] = Game.time + 21600 -- 6 hours
Timer:CallAt(trader.delay, function ()
if ship:exists() and ship.flightState ~= 'HYPERSPACE' then
trader['starport'] = getNearestStarport(ship, trader.starport)
ship:AIDockWith(trader.starport)
trader['status'] = 'inbound'
trader['delay'] = nil
end
end)
end
-- XXX if ORBIT_IMPOSSIBLE asteroid? get parent of parent and attempt orbit?
elseif trader.status == 'inbound' then
if ai_error == 'REFUSED_PERM' then doOrbit(ship) end
end
end
Event.Register("onAICompleted", onAICompleted)
local onShipLanded = function (ship, body)
if trade_ships[ship] == nil then return end
print(ship.label..' Landed: '..trade_ships[ship].starport.label)
doOrbit(ship)
end
Event.Register("onShipLanded", onShipLanded)
local onShipAlertChanged = function (ship, alert)
if trade_ships[ship] == nil then return end
if alert == 'SHIP_FIRING' then
print(ship.label..' alert changed to '..alert) end
local trader = trade_ships[ship]
if trader.attacker == nil then return end
if alert == 'NONE' or not trader.attacker:exists() or
(alert == 'SHIP_NEARBY' and ship:DistanceTo(trader.attacker) > 100) then
trader['attacker'] = nil
if trader.status == 'fleeing' then
-- had not reached starport yet
trader['status'] = 'inbound'
elseif trader.status == 'cowering' then
-- already reached starport and docked
trader['status'] = 'docked'
if trader.delay > Game.time then
--[[ not ready to undock, so schedule it
there is a slight chance that the status was changed while
onShipDocked was in progress so fire a bit later ]]
Timer:CallAt(trader.delay + 120, function () doUndock(ship) end)
else
-- ready to undock
doUndock(ship)
end
end
end
end
Event.Register("onShipAlertChanged", onShipAlertChanged)
local onShipHit = function (ship, attacker)
-- XXX this whole thing might be better if based on amount of damage sustained
if trade_ships[ship] == nil then return end
local trader = trade_ships[ship]
trader['chance'] = trader.chance or 0
trader['chance'] = trader.chance + 0.1
-- don't spam actions
if trader.last_flee and Game.time - trader.last_flee < Engine.rand:Integer(5, 7) then return end
-- if outbound jump now
if trader.status == 'outbound' then
if getSystemAndJump(ship) == 'OK' then
return
end
end
trader['status'] = 'fleeing'
trader['attacker'] = attacker
-- update last_flee
trader['last_flee'] = Game.time
-- if distance to starport is far attempt to hyperspace
if trader.no_jump ~= true then
if #starports == 0 then
trader['no_jump'] = true -- it already tried in onEnterSystem
elseif Engine.rand:Number(1) < trader.chance then
local distance = ship:DistanceTo(trader.starport)
if distance > 149598000 * (2 - trader.chance) then -- 149,598,000km = 1AU
if getSystemAndJump(ship) then
return
else
trader['no_jump'] = true
trader['chance'] = trader.chance + 0.3
end
end
end
end
-- maybe jettison a bit of cargo
if Engine.rand:Number(1) < trader.chance then
local cargo_type = nil
local max_cap = ship:GetStats().maxCapacity
for k, v in pairs(trader.cargo) do
if v > 1 and Engine.rand:Number(1) < v / max_cap then
cargo_type = k
break
end
end
if cargo_type and ship:Jettison(cargo_type) then
trader.cargo[cargo_type] = trader.cargo[cargo_type] - 1
Comms.ImportantMessage(attacker.label..', take this and leave us be, you filthy pirate!', ship.label)
trader['chance'] = trader.chance - 0.1
end
end
end
Event.Register("onShipHit", onShipHit)
local onShipCollided = function (ship, other)
if trade_ships[ship] == nil then return end
if other:isa('CargoBody') then return end
if other:isa('Ship') and other:IsPlayer() then
onShipHit(ship, other)
return
end
-- try to get away from body, onAICompleted will take over if we succeed
ship:AIFlyTo(other)
end
Event.Register("onShipCollided", onShipCollided)
local onShipDestroyed = function (ship, attacker)
if trade_ships[ship] ~= nil then
local trader = trade_ships[ship]
print(ship.label..' destroyed by '..attacker.label..', status:'..trader.status..' ship:'..trader.ship_name..', starport:'..trader.starport.label)
trade_ships[ship] = nil
if not attacker:isa("Ship") then
spawnReplacement()
end
-- XXX consider spawning some CargoBodies if killed by a ship
else
for t_ship, trader in pairs(trade_ships) do
if t_ship ~= 'interval' and trader.attacker and trader.attacker == ship then
trader['attacker'] = nil
if trader.status == 'fleeing' then
-- had not reached starport yet
trader['status'] = 'inbound'
elseif trader.status == 'cowering' then
-- already reached starport and docked
trader['status'] = 'docked'
if trader.delay > Game.time then
--[[ not ready to undock, so schedule it
there is a slight chance that the status was changed while
onShipDocked was in progress so fire a bit later ]]
Timer:CallAt(trader.delay + 120, function () doUndock(t_ship) end)
else
-- ready to undock
doUndock(t_ship)
end
end
return
end
end
end
end
Event.Register("onShipDestroyed", onShipDestroyed)
local onGameStart = function ()
-- create tables for data on the current system
from_paths, starports, imports, exports = {}, {}, {}, {}
system_updated = true
if trade_ships == nil then
-- create table to hold ships, keyed by ship object
trade_ships = {}
spawnInitialShips(true)
else
-- trade_ships was loaded by unserialize
-- rebuild starports, imports and exports tables
starports = Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
if #starports == 0 then
-- there are no starports so don't bother looking for goods
return
else
local prices = Game.system:GetCommodityBasePriceAlterations()
for k,v in pairs(prices) do
if k ~= 'RUBBISH' and k ~= 'RADIOACTIVES' and Game.system:IsCommodityLegal(k) then
if v > 2 then
table.insert(imports, k)
elseif v < -2 then
table.insert(exports, k)
end
end
end
end
-- rebuild nearby system paths for hyperspace spawns to come from
local from_systems, dist = {}, 10
while #from_systems < 10 do
dist = dist + 5
from_systems = Game.system:GetNearbySystems(dist)
end
from_paths = {}
for _, system in ipairs(from_systems) do
table.insert(from_paths, system.path)
end
-- check if any trade ships were waiting on a timer
for ship, trader in pairs(trade_ships) do
if ship ~= 'interval' and trader.delay and trader.delay > Game.time then
if trader.status == 'docked' then
Timer:CallAt(trader.delay, function () doUndock(ship) end)
elseif trader.status == 'orbit' then
Timer:CallAt(trader.delay, function ()
if ship:exists() and ship.flightState ~= 'HYPERSPACE' then
trader['starport'] = getNearestStarport(ship)
ship:AIDockWith(trader.starport)
trader['status'] = 'inbound'
trader['delay'] = nil
end
end)
end
end
end
end
end
Event.Register("onGameStart", onGameStart)
local onGameEnd = function ()
-- drop the references for our data so Lua can free them
-- and so we can start fresh if the player starts another game
trade_ships, system_updated, from_paths, starports, imports, exports = nil, nil, nil, nil, nil, nil
end
Event.Register("onGameEnd", onGameEnd)
local serialize = function ()
-- all we need to save is trade_ships, the rest can be rebuilt on load
return trade_ships
end
local unserialize = function (data)
trade_ships = data
end
Serializer:Register("TradeShips", serialize, unserialize)

View File

@ -158,9 +158,10 @@ vector3d Body::GetVelocityRelTo(const Frame *relTo) const
return forient * vel + m_frame->GetVelocityRelTo(relTo);
}
// umm, backwards: gets velocity in this's frame
vector3d Body::GetVelocityRelTo(const Body *relTo) const
{
return GetVelocityRelTo(relTo->m_frame) - relTo->GetVelocity();
return GetVelocityRelTo(m_frame) - relTo->GetVelocityRelTo(m_frame);
}
void Body::OrientOnSurface(double radius, double latitude, double longitude)

View File

@ -116,7 +116,7 @@ void DynamicBody::CalcExternalForce()
// atmospheric drag
m_atmosForce = vector3d(0.0);
if (GetFrame()->IsRotFrame() && body->IsType(Object::PLANET))
/* if (GetFrame()->IsRotFrame() && body->IsType(Object::PLANET))
{
Planet *planet = static_cast<Planet*>(body);
double dist = GetPosition().Length();
@ -145,6 +145,7 @@ void DynamicBody::CalcExternalForce()
m_externalForce -= m_mass * angRot.Cross(angRot.Cross(GetPosition())); // centrifugal
m_externalForce -= 2 * m_mass * angRot.Cross(GetVelocity()); // coriolis
}
*/
}
void DynamicBody::TimeStepUpdate(const float timeStep)

View File

@ -128,6 +128,7 @@ double ModelBody::GetBoundingRadius() const
void ModelBody::SetFrame(Frame *f)
{
if (f == GetFrame()) return;
if (GetFrame()) {
if (m_isStatic) GetFrame()->RemoveStaticGeom(m_geom);
else GetFrame()->RemoveGeom(m_geom);

View File

@ -636,10 +636,18 @@ bool Ship::FireMissile(int idx, Ship *target)
void Ship::SetFlightState(Ship::FlightState newState)
{
if (m_flightState == newState) return;
m_flightState = newState;
if (IsHyperspaceActive() && (newState != FLYING))
ResetHyperspaceCountdown();
if (newState == FLYING) {
m_testLanded = false;
if (m_flightState == DOCKING || m_flightState == DOCKED) onUndock.emit();
m_dockedWith = 0;
// lock thrusters for two seconds to push us out of station
m_launchLockTimeout = 2.0;
}
m_flightState = newState;
switch (m_flightState)
{
case FLYING: SetMoving(true); SetColliding(true); SetStatic(false); break;
@ -654,19 +662,12 @@ void Ship::Blastoff()
{
if (m_flightState != LANDED) return;
ClearThrusterState();
SetFlightState(FLYING);
m_testLanded = false;
m_dockedWith = 0;
m_launchLockTimeout = 2.0; // two second of applying thrusters
vector3d up = GetPosition().Normalized();
assert(GetFrame()->GetBody()->IsType(Object::PLANET));
const double planetRadius = 2.0 + static_cast<Planet*>(GetFrame()->GetBody())->GetTerrainHeight(up);
SetVelocity(vector3d(0, 0, 0));
SetAngVelocity(vector3d(0, 0, 0));
SetForce(vector3d(0, 0, 0));
SetTorque(vector3d(0, 0, 0));
SetFlightState(FLYING);
SetPosition(up*planetRadius - GetAabb().min.y*up);
SetThrusterState(1, 1.0); // thrust upwards
@ -696,8 +697,6 @@ void Ship::TestLanded()
SetVelocity(vector3d(0, 0, 0));
SetAngVelocity(vector3d(0, 0, 0));
SetForce(vector3d(0, 0, 0));
SetTorque(vector3d(0, 0, 0));
ClearThrusterState();
SetFlightState(LANDED);
Sound::BodyMakeNoise(this, "Rough_Landing", 1.0f);
@ -709,17 +708,8 @@ void Ship::TestLanded()
void Ship::TimeStepUpdate(const float timeStep)
{
// XXX why not just fucking do this rather than track down the
// reason that ship geoms are being collision tested during launch
if (m_flightState == FLYING) { SetColliding(true); }
else { SetColliding(false); }
if (IsDead()) SetColliding(false);
// can't orient ships in SetDockedWith() because it gets
// called from collision handler, and collision system gets a bit
// weirded out if bodies are moved in the middle of collision detection
if (m_dockedWith) m_dockedWith->OrientDockedShip(this, m_dockedWithPort);
// If docked, station is responsible for updating position/orient of ship
// but we call this crap anyway and hope it doesn't do anything bad
vector3d maxThrust = GetMaxThrust(m_thrusters);
AddRelForce(vector3d(maxThrust.x*m_thrusters.x, maxThrust.y*m_thrusters.y,
@ -1093,16 +1083,8 @@ const ShipType &Ship::GetShipType() const
bool Ship::Undock()
{
if (m_dockedWith && m_dockedWith->LaunchShip(this, m_dockedWithPort)) {
m_testLanded = false;
onUndock.emit();
m_dockedWith = 0;
// lock thrusters for two seconds to push us out of station
m_launchLockTimeout = 2.0;
return true;
} else {
return false;
}
if (m_dockedWith && m_dockedWith->LaunchShip(this, m_dockedWithPort)) return true;
else return false;
}
void Ship::SetDockedWith(SpaceStation *s, int port)
@ -1110,11 +1092,9 @@ void Ship::SetDockedWith(SpaceStation *s, int port)
if (s) {
m_dockedWith = s;
m_dockedWithPort = port;
m_wheelTransition = 0;
m_wheelState = 1.0f;
SetFlightState(DOCKED);
SetVelocity(vector3d(0,0,0));
SetAngVelocity(vector3d(0,0,0));
ClearThrusterState();
// hand position/state responsibility over to station
m_dockedWith->SetDocked(this, port);
onDock.emit();
} else {

View File

@ -530,8 +530,8 @@ static vector3d GetPosInFrame(Frame *frame, Frame *target, const vector3d &offse
static vector3d GetVelInFrame(Frame *frame, Frame *target, const vector3d &offset)
{
if (target != frame) return vector3d(0.0);
vector3d vel = -target->GetStasisVelocity(offset);
vector3d vel = vector3d(0.0);
if (target != frame) vel = -target->GetStasisVelocity(offset);
return target->GetOrientRelTo(frame) * vel + target->GetVelocityRelTo(frame);
}
@ -820,7 +820,7 @@ bool AICmdDock::TimeStepUpdate()
m_dockupdir = dockpos.yaxis.Normalized(); // don't trust these enough
if (type->dockMethod == SpaceStationType::ORBITAL) m_dockupdir = -m_dockupdir;
else if (m_state == 4) m_dockpos -= m_dockupdir * (m_ship->GetAabb().min.y + 1.0);
m_dockpos = m_target->GetOrient() * m_dockpos + m_target->GetPosition();
if (m_state != 2) m_dockpos = m_target->GetOrient() * m_dockpos + m_target->GetPosition();
m_state++;
// should have m_dockpos in target frame, dirs relative to target orient
}

View File

@ -107,14 +107,13 @@ bool SpaceStationType::GetShipApproachWaypoints(int port, int stage, positionOri
return gotOrient;
}
/* when ship is on rails it returns true and fills outPosOrient.
* when ship has been released (or docked) it returns false.
* Note station animations may continue for any number of stages after
* ship has been released and is under player control again */
// returns true and fills outPosOrient if model has suitable data available
// if stage is at one end, fills out last position in animation
bool SpaceStationType::GetDockAnimPositionOrient(int port, int stage, double t, const vector3d &from, positionOrient_t &outPosOrient, const Ship *ship) const
{
if ((stage < 0) && ((-stage) > numUndockStages)) return false;
if ((stage > 0) && (stage > numDockingStages)) return false;
if (stage < -shipLaunchStage) { stage = -shipLaunchStage; t = 1.0; }
if (stage > numDockingStages || !stage) { stage = numDockingStages; t = 1.0; }
// note case for stageless launch (shipLaunchStage==0)
lua_State *L = LmrGetLuaState();
@ -189,6 +188,7 @@ void SpaceStation::Init()
t.model = LmrLookupModelByName(t.modelName);
t.dockMethod = SpaceStationType::DOCKMETHOD(is_orbital);
t.numDockingPorts = (*i)->GetIntAttribute("num_docking_ports");
t.shipLaunchStage = (*i)->GetIntAttribute("ship_launch_stage");
t.dockOneAtATimePlease = (*i)->GetBoolAttribute("dock_one_at_a_time_please");
t.ReadStageDurations();
//printf("one at a time? %s\n", t.dockOneAtATimePlease ? "yes" : "no");
@ -240,6 +240,8 @@ void SpaceStation::Save(Serializer::Writer &wr, Space *space)
wr.Float(float(m_openAnimState[i]));
wr.Float(float(m_dockAnimState[i]));
}
wr.Bool(m_dockingLock);
wr.Bool(m_bbCreated);
wr.Double(m_lastUpdatedShipyard);
wr.Int32(space->GetIndexForSystemBody(m_sbody));
@ -275,6 +277,8 @@ void SpaceStation::Load(Serializer::Reader &rd, Space *space)
m_openAnimState[i] = rd.Float();
m_dockAnimState[i] = rd.Float();
}
m_dockingLock = rd.Bool();
m_bbCreated = rd.Bool();
m_lastUpdatedShipyard = rd.Double();
m_sbody = space->GetSystemBodyByIndex(rd.Int32());
@ -309,6 +313,7 @@ SpaceStation::SpaceStation(const SystemBody *sbody): ModelBody()
m_openAnimState[i] = 0;
m_dockAnimState[i] = 0;
}
m_dockingLock = false;
SetMoney(1000000000);
InitStation();
@ -375,195 +380,21 @@ void SpaceStation::UpdateShipyard()
onShipsForSaleChanged.emit();
}
void SpaceStation::DoDockingAnimation(const double timeStep)
void SpaceStation::NotifyRemoved(const Body* const removedBody)
{
vector3d p1, p2, zaxis;
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
shipDocking_t &dt = m_shipDocking[i];
if (!dt.ship) continue;
if (!dt.stage) continue;
// docked stage is m_type->numDockingPorts + 1
if (dt.stage > m_type->numDockingStages) continue;
double stageDuration = (dt.stage > 0 ?
m_type->dockAnimStageDuration[dt.stage-1] :
m_type->undockAnimStageDuration[abs(dt.stage)-1]);
dt.stagePos += timeStep / stageDuration;
if (dt.stage == 1) {
// SPECIAL stage! Docking granted but waiting for ship
// to dock
m_openAnimState[i] += 0.3*timeStep;
m_dockAnimState[i] -= 0.3*timeStep;
if (dt.stagePos >= 1.0) {
if (dt.ship == static_cast<Ship*>(Pi::player)) Pi::onDockingClearanceExpired.emit(this);
dt.ship = 0;
dt.stage = 0;
}
continue;
}
if (dt.stagePos > 1.0) {
dt.stagePos = 0;
if (dt.stage >= 0) dt.stage++;
else dt.stage--;
dt.fromPos = (dt.ship->GetPosition() - GetPosition()) * GetOrient(); // station space
dt.fromRot = Quaterniond::FromMatrix3x3(dt.ship->GetOrient());
}
SpaceStationType::positionOrient_t shipOrient;
bool onRails = m_type->GetDockAnimPositionOrient(i, dt.stage, dt.stagePos, dt.fromPos, shipOrient, dt.ship);
if (onRails) {
dt.ship->SetPosition(GetPosition() + GetOrient()*shipOrient.pos);
matrix3x3d wantRot = GetOrient() * matrix3x3d::BuildFromVectors(shipOrient.xaxis, shipOrient.yaxis);
// use quaternion spherical linear interpolation to do
// rotation smoothly
Quaterniond wantQuat = Quaterniond::FromMatrix3x3(wantRot);
Quaterniond q = Quaterniond::Nlerp(dt.fromRot, wantQuat, dt.stagePos);
wantRot = q.ToMatrix3x3<double>();
// wantRot.Renormalize();
dt.ship->SetOrient(wantRot);
} else {
if (dt.stage >= 0) {
// set docked
dt.ship->SetDockedWith(this, i);
LuaEvent::Queue("onShipDocked", dt.ship, this);
} else {
if (dt.ship->GetFlightState() != Ship::FLYING) {
// launch ship
dt.ship->SetFlightState(Ship::FLYING);
dt.ship->SetAngVelocity(GetAngVelocity());
dt.ship->SetForce(vector3d(0,0,0));
dt.ship->SetTorque(vector3d(0,0,0));
if (m_type->dockMethod == SpaceStationType::SURFACE) {
dt.ship->SetThrusterState(1, 1.0); // up
} else {
// dt.ship->SetVelocity(GetFrame()->GetStasisVelocity(dt.ship->GetPosition()));
dt.ship->SetThrusterState(2, -1.0); // forward
}
LuaEvent::Queue("onShipUndocked", dt.ship, this);
}
}
}
if ((dt.stage < 0) && ((-dt.stage) > m_type->numUndockStages)) {
dt.stage = 0;
dt.ship = 0;
if (m_shipDocking[i].ship == removedBody) {
m_shipDocking[i].ship = 0;
}
}
}
int SpaceStation::GetMyDockingPort(const Ship *s) const
{
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
m_openAnimState[i] = Clamp(m_openAnimState[i], 0.0, 1.0);
m_dockAnimState[i] = Clamp(m_dockAnimState[i], 0.0, 1.0);
}
}
void SpaceStation::DoLawAndOrder()
{
Sint64 fine, crimeBitset;
Polit::GetCrime(&crimeBitset, &fine);
if (Pi::player->GetFlightState() != Ship::DOCKED
&& m_numPoliceDocked
&& (fine > 1000)
&& (GetPositionRelTo(Pi::player).Length() < 100000.0)) {
int port = GetFreeDockingPort();
if (port != -1) {
m_numPoliceDocked--;
// Make police ship intent on killing the player
Ship *ship = new Ship(ShipType::LADYBIRD);
ship->AIKill(Pi::player);
ship->SetFrame(GetFrame());
ship->SetDockedWith(this, port);
Pi::game->GetSpace()->AddBody(ship);
{ // blue and white thang
ShipFlavour f;
f.id = ShipType::LADYBIRD;
f.regid = Lang::POLICE_SHIP_REGISTRATION;
f.price = ship->GetFlavour()->price;
LmrMaterial m;
m.diffuse[0] = 0.0f; m.diffuse[1] = 0.0f; m.diffuse[2] = 1.0f; m.diffuse[3] = 1.0f;
m.specular[0] = 0.0f; m.specular[1] = 0.0f; m.specular[2] = 1.0f; m.specular[3] = 1.0f;
m.emissive[0] = 0.0f; m.emissive[1] = 0.0f; m.emissive[2] = 0.0f; m.emissive[3] = 0.0f;
m.shininess = 50.0f;
f.primaryColor = m;
m.shininess = 0.0f;
m.diffuse[0] = 1.0f; m.diffuse[1] = 1.0f; m.diffuse[2] = 1.0f; m.diffuse[3] = 1.0f;
f.secondaryColor = m;
ship->ResetFlavour(&f);
}
ship->m_equipment.Set(Equip::SLOT_LASER, 0, Equip::PULSECANNON_DUAL_1MW);
ship->m_equipment.Add(Equip::SHIELD_GENERATOR);
ship->m_equipment.Add(Equip::LASER_COOLING_BOOSTER);
ship->m_equipment.Add(Equip::ATMOSPHERIC_SHIELDING);
ship->UpdateStats();
}
}
}
void SpaceStation::StaticUpdate(const float timeStep)
{
bool update = false;
// if there's no BB and there are ships here, make one
if (!m_bbCreated && GetFreeDockingPort() != 0) {
CreateBB();
update = true;
}
// if there is and it hasn't had an update for a while, update it
else if (Pi::game->GetTime() > m_lastUpdatedShipyard) {
LuaEvent::Queue("onUpdateBB", this);
update = true;
}
if (update) {
UpdateShipyard();
// update again in an hour or two
m_lastUpdatedShipyard = Pi::game->GetTime() + 3600.0 + 3600.0*Pi::rng.Double();
}
DoLawAndOrder();
}
void SpaceStation::TimeStepUpdate(const float timeStep)
{
// rotate the thing
double len = m_type->angVel;
if (len > 1e-16) {
vector3d axis = vector3d(0,0,1); // check
matrix3x3d r = matrix3x3d::BuildRotate(len * timeStep, axis);
SetOrient(r * GetOrient());
}
DoDockingAnimation(timeStep); // TODO: call from ship instead?
}
bool SpaceStation::IsGroundStation() const
{
return (m_type->dockMethod == SpaceStationType::SURFACE);
}
/* XXX THIS and PositionDockedShip do almost the same thing */
void SpaceStation::OrientDockedShip(Ship *ship, int port) const
{
SpaceStationType::positionOrient_t dport;
if (!m_type->GetDockAnimPositionOrient(port, m_type->numDockingStages, 1.0f, vector3d(0.0), dport, ship)) {
Error("Space station model %s does not specify valid ship_dock_anim positions", m_type->modelName);
}
const int dockMethod = m_type->dockMethod;
if (dockMethod == SpaceStationType::SURFACE) {
matrix3x3d rot = GetOrient() * matrix3x3d::BuildFromVectors(dport.xaxis, dport.yaxis);
vector3d pos = GetPosition() + GetOrient()*dport.pos;
// position with wheels perfectly on ground :D
// Aabb aabb;
// ship->GetAabb(aabb);
// pos += stationRot*vector3d(0,-aabb.min.y,0);
// should already be done by GetDockAnimPositionOrient
ship->SetPosition(pos);
ship->SetOrient(rot);
if (s == m_shipDocking[i].ship) return i;
}
return -1;
}
int SpaceStation::GetFreeDockingPort() const
@ -578,51 +409,30 @@ int SpaceStation::GetFreeDockingPort() const
void SpaceStation::SetDocked(Ship *ship, int port)
{
PositionDockedShip(ship, port);
m_shipDocking[port].ship = ship;
m_shipDocking[port].stage = m_type->numDockingStages+1;
}
void SpaceStation::PositionDockedShip(Ship *ship, int port)
{
SpaceStationType::positionOrient_t dport;
PiVerify(m_type->GetDockAnimPositionOrient(port, m_type->numDockingStages, 1.0f, vector3d(0.0), dport, ship));
const int dockMethod = m_type->dockMethod;
if (dockMethod == SpaceStationType::ORBITAL) {
ship->SetFrame(GetFrame()); // check this, performance?
ship->SetPosition(GetPosition() + GetOrient()*dport.pos);
// duplicated from DoDockingAnimation()
vector3d zaxis = dport.xaxis.Cross(dport.yaxis);
ship->SetOrient(GetOrient() * matrix3x3d::BuildFromVectors(dport.xaxis, dport.yaxis));
} else {
// no frame setting?
ship->SetPosition(GetPosition() + GetOrient()*(dport.pos + dport.yaxis));
// position slightly (1m) off landing surface
ship->SetOrient(GetOrient() * matrix3x3d::BuildFromVectors(dport.xaxis, dport.yaxis));
}
// have to do this crap again in case it was called directly (Ship::SetDockWith())
ship->SetFlightState(Ship::DOCKED);
ship->SetVelocity(vector3d(0.0));
ship->SetAngVelocity(vector3d(0.0));
ship->ClearThrusterState();
}
bool SpaceStation::LaunchShip(Ship *ship, int port)
{
/* XXX bad to keep duplicating this */
if (m_type->dockOneAtATimePlease) {
for (int i=0; i<m_type->numDockingPorts; i++) {
if (m_shipDocking[i].ship && m_shipDocking[i].stage &&
(m_shipDocking[i].stage != m_type->numDockingStages+1)) {
return false;
}
}
}
shipDocking_t &sd = m_shipDocking[port];
if (sd.stage < 0) return true; // already launching
if (m_dockingLock) return false; // another ship docking
if (m_type->dockOneAtATimePlease) m_dockingLock = true;
sd.ship = ship;
sd.stage = -1;
sd.stagePos = 0;
sd.fromPos = (ship->GetPosition() - GetPosition()) * GetOrient();
sd.fromRot = Quaterniond::FromMatrix3x3(ship->GetOrient());
sd.fromPos = (ship->GetPosition() - GetPosition()) * GetOrient(); // station space
sd.fromRot = Quaterniond::FromMatrix3x3(GetOrient().Transpose() * ship->GetOrient());
ship->SetFlightState(Ship::DOCKING);
PositionDockedShip(ship, port);
return true;
}
@ -649,6 +459,204 @@ bool SpaceStation::GetDockingClearance(Ship *s, std::string &outMsg)
return false;
}
bool SpaceStation::OnCollision(Object *b, Uint32 flags, double relVel)
{
if ((flags & 0x10) && (b->IsType(Object::SHIP))) {
Ship *s = static_cast<Ship*>(b);
int port = -1;
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
if (m_shipDocking[i].ship == s) { port = i; break; }
}
if (port == -1) return false; // no permission
if (!m_type->dockOneAtATimePlease) {
if (port != (flags&0xf)) return false; // wrong port
}
if (m_shipDocking[port].stage != 1) return false; // already docking?
SpaceStationType::positionOrient_t dport;
// why stage 2? Because stage 1 is permission to dock
// granted, stage 2 is start of docking animation.
PiVerify(m_type->GetDockAnimPositionOrient(port, 2, 0.0, vector3d(0.0), dport, s));
// must be oriented sensibly and have wheels down
if (IsGroundStation()) {
vector3d dockingNormal = GetOrient()*dport.yaxis;
const double dot = s->GetOrient().VectorY().Dot(dockingNormal);
if ((dot < 0.99) || (s->GetWheelState() < 1.0)) return false; // <0.99 harsh?
if (s->GetVelocity().Length() > MAX_LANDING_SPEED) return false;
}
// if there is more docking port anim to do, don't set docked yet
if (m_type->numDockingStages >= 2) {
shipDocking_t &sd = m_shipDocking[port];
sd.ship = s;
sd.stage = 2;
sd.stagePos = 0;
sd.fromPos = (s->GetPosition() - GetPosition()) * GetOrient(); // station space
sd.fromRot = Quaterniond::FromMatrix3x3(GetOrient().Transpose() * s->GetOrient());
if (m_type->dockOneAtATimePlease) m_dockingLock = true;
s->SetFlightState(Ship::DOCKING);
s->SetVelocity(vector3d(0.0));
s->SetAngVelocity(vector3d(0.0));
s->ClearThrusterState();
} else {
s->SetDockedWith(this, port); // bounces back to SS::SetDocked()
LuaEvent::Queue("onShipDocked", s, this);
}
return false;
} else {
return true;
}
}
void SpaceStation::DockingUpdate(const double timeStep)
{
vector3d p1, p2, zaxis;
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
shipDocking_t &dt = m_shipDocking[i];
if (!dt.ship) continue;
// docked stage is m_type->numDockingPorts + 1 => ship docked
if (dt.stage > m_type->numDockingStages) continue;
double stageDuration = (dt.stage > 0 ?
m_type->dockAnimStageDuration[dt.stage-1] :
m_type->undockAnimStageDuration[abs(dt.stage)-1]);
dt.stagePos += timeStep / stageDuration;
if (dt.stage == 1) {
// SPECIAL stage! Docking granted but waiting for ship to dock
m_openAnimState[i] += 0.3*timeStep;
m_dockAnimState[i] -= 0.3*timeStep;
if (dt.stagePos >= 1.0) {
if (dt.ship == static_cast<Ship*>(Pi::player)) Pi::onDockingClearanceExpired.emit(this);
dt.ship = 0;
dt.stage = 0;
}
continue;
}
if (dt.stagePos > 1.0) {
// transition between docking stages
dt.stagePos = 0;
if (dt.stage >= 0) dt.stage++;
else dt.stage--;
dt.fromPos = (dt.ship->GetPosition() - GetPosition()) * GetOrient(); // station space
dt.fromRot = Quaterniond::FromMatrix3x3(GetOrient().Transpose() * dt.ship->GetOrient());
}
if (dt.stage < -m_type->shipLaunchStage) {
// launch ship
dt.ship->SetFlightState(Ship::FLYING);
dt.ship->SetAngVelocity(GetFrame()->GetAngVelocity());
if (m_type->dockMethod == SpaceStationType::SURFACE) {
dt.ship->SetThrusterState(1, 1.0); // up
} else {
dt.ship->SetThrusterState(2, -1.0); // forward
}
LuaEvent::Queue("onShipUndocked", dt.ship, this);
}
if (dt.stage < -m_type->numUndockStages) {
// undock animation finished, clear port
dt.stage = 0;
dt.ship = 0;
if (m_type->dockOneAtATimePlease) m_dockingLock = false;
}
else if (dt.stage > m_type->numDockingStages) {
// set docked
dt.ship->SetDockedWith(this, i);
LuaEvent::Queue("onShipDocked", dt.ship, this);
if (m_type->dockOneAtATimePlease) m_dockingLock = false;
}
}
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
m_openAnimState[i] = Clamp(m_openAnimState[i], 0.0, 1.0);
m_dockAnimState[i] = Clamp(m_dockAnimState[i], 0.0, 1.0);
}
}
void SpaceStation::PositionDockedShip(Ship *ship, int port) const
{
const shipDocking_t &dt = m_shipDocking[port];
SpaceStationType::positionOrient_t dport;
PiVerify(m_type->GetDockAnimPositionOrient(port, dt.stage, dt.stagePos, dt.fromPos, dport, ship));
// Still in docking animation process?
if (dt.stage <= m_type->numDockingStages) {
ship->SetPosition(GetPosition() + GetOrient()*dport.pos);
matrix3x3d wantRot = matrix3x3d::BuildFromVectors(dport.xaxis, dport.yaxis);
// use quaternion spherical linear interpolation to do
// rotation smoothly
Quaterniond wantQuat = Quaterniond::FromMatrix3x3(wantRot);
Quaterniond q = Quaterniond::Nlerp(dt.fromRot, wantQuat, dt.stagePos);
wantRot = q.ToMatrix3x3<double>();
dt.ship->SetOrient(GetOrient() * wantRot);
return;
}
if (m_type->dockMethod == SpaceStationType::ORBITAL) {
ship->SetPosition(GetPosition() + GetOrient()*dport.pos);
vector3d zaxis = dport.xaxis.Cross(dport.yaxis);
ship->SetOrient(GetOrient() * matrix3x3d::BuildFromVectors(dport.xaxis, dport.yaxis));
} else {
ship->SetPosition(GetPosition() + GetOrient()*dport.pos); // + dport.yaxis));
ship->SetOrient(GetOrient() * matrix3x3d::BuildFromVectors(dport.xaxis, dport.yaxis));
}
}
void SpaceStation::StaticUpdate(const float timeStep)
{
bool update = false;
// if there's no BB and there are ships here, make one
if (!m_bbCreated && GetFreeDockingPort() != 0) {
CreateBB();
update = true;
}
// if there is and it hasn't had an update for a while, update it
else if (Pi::game->GetTime() > m_lastUpdatedShipyard) {
LuaEvent::Queue("onUpdateBB", this);
update = true;
}
if (update) {
UpdateShipyard();
// update again in an hour or two
m_lastUpdatedShipyard = Pi::game->GetTime() + 3600.0 + 3600.0*Pi::rng.Double();
}
DoLawAndOrder();
DockingUpdate(timeStep);
}
void SpaceStation::TimeStepUpdate(const float timeStep)
{
// rotate the thing
double len = m_type->angVel;
if (len > 1e-16) {
vector3d axis = vector3d(0,0,1); // check
matrix3x3d r = matrix3x3d::BuildRotate(len * timeStep, axis);
SetOrient(r * GetOrient());
}
// reposition the ships that are docked or docking here
for (int i=0; i<m_type->numDockingPorts; i++) {
const shipDocking_t &dt = m_shipDocking[i];
if (!dt.ship || dt.stage == 1) continue;
if (dt.ship->GetFlightState() == Ship::FLYING) continue;
PositionDockedShip(dt.ship, i);
}
}
bool SpaceStation::IsGroundStation() const
{
return (m_type->dockMethod == SpaceStationType::SURFACE);
}
/* MarketAgent shite */
void SpaceStation::Bought(Equip::Type t) {
m_equipmentStock[int(t)]++;
@ -675,81 +683,6 @@ Sint64 SpaceStation::GetPrice(Equip::Type t) const {
return (mul * Sint64(Equip::types[t].basePrice)) / 100;
}
bool SpaceStation::OnCollision(Object *b, Uint32 flags, double relVel)
{
if ((flags & 0x10) && (b->IsType(Object::SHIP))) {
Ship *s = static_cast<Ship*>(b);
bool canDock = true;
int port = -1;
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
if (m_shipDocking[i].ship == s) { port = i; break; }
}
if (m_type->dockOneAtATimePlease) {
for (int i=0; i<m_type->numDockingPorts; i++) {
if (m_shipDocking[i].ship && m_shipDocking[i].stage != 1 &&
(m_shipDocking[i].stage != m_type->numDockingStages+1)) {
canDock = false;
break;
}
}
} else {
// for non-dockOneAtATimePlease, the ship is expected
// to hit the right docking trigger surface for that port
if (m_shipDocking[flags&0xf].ship != s) canDock = false;
}
if (port == -1) canDock = false;
// hitting docking area of a station
if (canDock) {
SpaceStationType::positionOrient_t dport;
// why stage 2? Because stage 1 is permission to dock
// granted, stage 2 is start of docking animation.
PiVerify(m_type->GetDockAnimPositionOrient(port, 2, 0.0f, vector3d(0.0), dport, s));
double speed = s->GetVelocity().Length();
// must be oriented sensibly and have wheels down
if (IsGroundStation()) {
vector3d dockingNormal = GetOrient()*dport.yaxis;
const double dot = s->GetOrient().VectorY().Dot(dockingNormal);
if ((dot < 0.99) || (s->GetWheelState() < 1.0)) return false; // <0.99 harsh?
}
if ((speed < MAX_LANDING_SPEED) &&
(!s->GetDockedWith()) &&
(m_shipDocking[port].stage == 1)) {
// if there is more docking port anim to do,
// don't set docked yet
if (m_type->numDockingStages >= 2) {
shipDocking_t &sd = m_shipDocking[port];
sd.ship = s;
sd.stage = 2;
sd.stagePos = 0;
sd.fromPos = (s->GetPosition() - GetPosition()) * GetOrient();
sd.fromRot = Quaterniond::FromMatrix3x3(s->GetOrient());
s->ClearThrusterState();
s->SetFlightState(Ship::DOCKING);
} else {
s->SetDockedWith(this, port);
LuaEvent::Queue("onShipDocked", s, this);
}
}
}
return false;
} else {
return true;
}
}
void SpaceStation::NotifyRemoved(const Body* const removedBody)
{
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
if (m_shipDocking[i].ship == removedBody) {
m_shipDocking[i].ship = 0;
}
}
}
// Calculates the ambiently and directly lit portions of the lighting model taking into account the atmosphere and sun positions at a given location
// 1. Calculates the amount of direct illumination available taking into account
@ -1061,3 +994,45 @@ vector3d SpaceStation::GetTargetIndicatorPosition(const Frame *relTo) const
}
return GetInterpPositionRelTo(relTo);
}
void SpaceStation::DoLawAndOrder()
{
Sint64 fine, crimeBitset;
Polit::GetCrime(&crimeBitset, &fine);
if (Pi::player->GetFlightState() != Ship::DOCKED
&& m_numPoliceDocked
&& (fine > 1000)
&& (GetPositionRelTo(Pi::player).Length() < 100000.0)) {
int port = GetFreeDockingPort();
if (port != -1) {
m_numPoliceDocked--;
// Make police ship intent on killing the player
Ship *ship = new Ship(ShipType::LADYBIRD);
ship->AIKill(Pi::player);
ship->SetFrame(GetFrame());
ship->SetDockedWith(this, port);
Pi::game->GetSpace()->AddBody(ship);
{ // blue and white thang
ShipFlavour f;
f.id = ShipType::LADYBIRD;
f.regid = Lang::POLICE_SHIP_REGISTRATION;
f.price = ship->GetFlavour()->price;
LmrMaterial m;
m.diffuse[0] = 0.0f; m.diffuse[1] = 0.0f; m.diffuse[2] = 1.0f; m.diffuse[3] = 1.0f;
m.specular[0] = 0.0f; m.specular[1] = 0.0f; m.specular[2] = 1.0f; m.specular[3] = 1.0f;
m.emissive[0] = 0.0f; m.emissive[1] = 0.0f; m.emissive[2] = 0.0f; m.emissive[3] = 0.0f;
m.shininess = 50.0f;
f.primaryColor = m;
m.shininess = 0.0f;
m.diffuse[0] = 1.0f; m.diffuse[1] = 1.0f; m.diffuse[2] = 1.0f; m.diffuse[3] = 1.0f;
f.secondaryColor = m;
ship->ResetFlavour(&f);
}
ship->m_equipment.Set(Equip::SLOT_LASER, 0, Equip::PULSECANNON_DUAL_1MW);
ship->m_equipment.Add(Equip::SHIELD_GENERATOR);
ship->m_equipment.Add(Equip::LASER_COOLING_BOOSTER);
ship->m_equipment.Add(Equip::ATMOSPHERIC_SHIELDING);
ship->UpdateStats();
}
}
}

View File

@ -31,6 +31,7 @@ struct SpaceStationType {
int numDockingPorts;
int numDockingStages;
int numUndockStages;
int shipLaunchStage;
double *dockAnimStageDuration;
double *undockAnimStageDuration;
bool dockOneAtATimePlease;
@ -90,15 +91,9 @@ public:
virtual double GetBoundingRadius() const;
virtual bool OnCollision(Object *b, Uint32 flags, double relVel);
virtual void Render(Graphics::Renderer *r, const Camera *camera, const vector3d &viewCoords, const matrix4x4d &viewTransform);
/** You should call Ship::Undock() rather than this.
* Returns true on success, false if permission denied */
bool LaunchShip(Ship *ship, int port);
void OrientDockedShip(Ship *ship, int port) const;
bool GetDockingClearance(Ship *s, std::string &outMsg);
virtual void StaticUpdate(const float timeStep);
virtual void TimeStepUpdate(const float timeStep);
bool IsGroundStation() const;
float GetDesiredAngVel() const;
void AddEquipmentStock(Equip::Type t, int num) { m_equipmentStock[t] += num; }
/* MarketAgent stuff */
int GetStock(Equip::Type t) const { return m_equipmentStock[t]; }
@ -111,16 +106,20 @@ public:
const std::vector<ShipFlavour> &GetShipsOnSale() const { return m_shipsOnSale; }
virtual void PostLoadFixup(Space *space);
virtual void NotifyRemoved(const Body* const removedBody);
// should call Ship::Undock and Ship::SetDockedWith instead
// Returns true on success, false if permission denied
bool LaunchShip(Ship *ship, int port);
void SetDocked(Ship *ship, int port);
bool GetDockingClearance(Ship *s, std::string &outMsg);
int GetDockingPortCount() const { return m_type->numDockingPorts; }
int GetFreeDockingPort() const; // returns -1 if none free
int GetMyDockingPort(const Ship *s) const {
for (int i=0; i<MAX_DOCKING_PORTS; i++) {
if (s == m_shipDocking[i].ship) return i;
}
return -1;
}
void SetDocked(Ship *ship, int port);
int GetMyDockingPort(const Ship *s) const;
const SpaceStationType *GetSpaceStationType() const { return m_type; }
bool IsGroundStation() const;
sigc::signal<void> onShipsForSaleChanged;
sigc::signal<void, BBAdvert&> onBulletinBoardAdvertDeleted;
sigc::signal<void> onBulletinBoardChanged;
@ -129,7 +128,6 @@ public:
bool AllocateStaticSlot(int& slot);
void CreateBB();
int AddBBAdvert(std::string description, AdvertFormBuilder builder);
const BBAdvert *GetBBAdvert(int ref);
bool RemoveBBAdvert(int ref);
@ -145,7 +143,8 @@ protected:
void Bought(Equip::Type t);
void Sold(Equip::Type t);
private:
void DoDockingAnimation(const double timeStep);
void DockingUpdate(const double timeStep);
void PositionDockedShip(Ship *ship, int port) const;
void DoLawAndOrder();
void CalcLighting(Planet *planet, double &ambient, double &intensity, const std::vector<Camera::LightSource> &lightSources);
@ -164,12 +163,12 @@ private:
double stagePos; // 0 -> 1.0
};
shipDocking_t m_shipDocking[MAX_DOCKING_PORTS];
bool m_dockingLock;
double m_openAnimState[MAX_DOCKING_PORTS];
double m_dockAnimState[MAX_DOCKING_PORTS];
void InitStation();
void PositionDockedShip(Ship *ship, int port);
void UpdateShipyard();
const SpaceStationType *m_type;
const SystemBody *m_sbody;

View File

@ -48,7 +48,7 @@
SmallerTypeCheck="true"
RuntimeLibrary="3"
FloatingPointModel="2"
UsePrecompiledHeader="0"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="pch.h"
ProgramDataBaseFileName="$(IntDir)\$(ProjectName)D.pdb"
WarningLevel="3"