velocity problems?
parent
a6c5e4470f
commit
3b9b41fe06
|
@ -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 },
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
52
src/Ship.cpp
52
src/Ship.cpp
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
SmallerTypeCheck="true"
|
||||
RuntimeLibrary="3"
|
||||
FloatingPointModel="2"
|
||||
UsePrecompiledHeader="0"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderThrough="pch.h"
|
||||
ProgramDataBaseFileName="$(IntDir)\$(ProjectName)D.pdb"
|
||||
WarningLevel="3"
|
||||
|
|
Loading…
Reference in New Issue