New mission: Scoop
parent
62b8384558
commit
427b1c4a79
|
@ -0,0 +1,198 @@
|
|||
{
|
||||
"ACCEPTED_ARMS_DEALER": {
|
||||
"description": "",
|
||||
"message": "Great! Thank you."
|
||||
},
|
||||
"ACCEPTED_ILLEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "Thanks. You won't regret it!"
|
||||
},
|
||||
"ACCEPTED_LEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "Thank you. You know a good deal when you see one!"
|
||||
},
|
||||
"ACCEPTED_RESCUE": {
|
||||
"description": "",
|
||||
"message": "Thank you very much. The crew will appreciate that."
|
||||
},
|
||||
"ADTEXT_ARMS_DEALER": {
|
||||
"description": "",
|
||||
"message": "PICK UP: Prompt recovery of drifting container required."
|
||||
},
|
||||
"ADTEXT_ILLEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "EASY MONEY: Valuable information for sale."
|
||||
},
|
||||
"ADTEXT_LEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "INFORMATION FOR SALE: Location of a debris field with valuable cargo."
|
||||
},
|
||||
"ADTEXT_RESCUE": {
|
||||
"description": "",
|
||||
"message": "HELP NEEDED: Urgent escape capsule rescue operation."
|
||||
},
|
||||
"CLIENT": {
|
||||
"description": "",
|
||||
"message": "Client:"
|
||||
},
|
||||
"DANGER": {
|
||||
"description": "The risk level",
|
||||
"message": "Danger:"
|
||||
},
|
||||
"DEADLINE": {
|
||||
"description": "Must be delivered by",
|
||||
"message": "Deadline:"
|
||||
},
|
||||
"DENY_1": {
|
||||
"description": "",
|
||||
"message": "Excuse me, sir? I don't think your current qualification is sufficient."
|
||||
},
|
||||
"DENY_2": {
|
||||
"description": "",
|
||||
"message": "Sorry, I think this task is beyond your ability as a pilot."
|
||||
},
|
||||
"DETONATORS": {
|
||||
"description": "",
|
||||
"message": "Detonators"
|
||||
},
|
||||
"DOCKING_INSTRUCTION": {
|
||||
"description": "",
|
||||
"message": "Nice to meet you. Please approach to within 100m to complete the cargo transfer!"
|
||||
},
|
||||
"FAILURE_MSG_ARMS_DEALER": {
|
||||
"description": "",
|
||||
"message": "You fool. The client's not gonna like this!"
|
||||
},
|
||||
"FAILURE_MSG_RESCUE": {
|
||||
"description": "",
|
||||
"message": "Your unreliability has caused great harm. Get out of my sight!"
|
||||
},
|
||||
"HOW_MUCH_TIME": {
|
||||
"description": "",
|
||||
"message": "How much time do I have?"
|
||||
},
|
||||
"HOW_MUCH_TIME_ARMS_DEALER": {
|
||||
"description": "",
|
||||
"message": "My business partner is in orbit around {star} to avoid detection. It would be great if you could reach him before {date}."
|
||||
},
|
||||
"HOW_MUCH_TIME_ILLEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "Don't waste time. Only the early bird catches the worm!"
|
||||
},
|
||||
"HOW_MUCH_TIME_LEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "I don't know. But if I were you, I would be quick!"
|
||||
},
|
||||
"HOW_MUCH_TIME_RESCUE": {
|
||||
"description": "",
|
||||
"message": "It would be good if you could reach the location before {date}."
|
||||
},
|
||||
"INTROTEXT_ARMS_DEALER": {
|
||||
"description": "",
|
||||
"message": "Hello Commander. My name is {client}. It would be great if you could help me out. An unreliable freighter captain dropped my cargo near {planet} and has disappeared since. Your task would be to pick up one container and deliver it to ship {shipid} in orbit around {star}. I would pay you {cash}."
|
||||
},
|
||||
"INTROTEXT_ILLEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "Hey buddy, I know about a debris field with illegal goods. The police caught a smuggler close to {planet}. They haven't had time to clean up the area yet. If you are quick, you can make easy money! I can give you the coordinates. It only costs you {cash}. Don't let the chance slip by!"
|
||||
},
|
||||
"INTROTEXT_LEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "Hello my friend. I'm {client}. I heard about a cargo hauler that got into trouble near {planet} because of bad ship maintenance. Amazing who gets to fly a spaceship these days, isn't it? Anyway, they had to dump a lot of the cargo. If you're quick, you can grab the cargo very easily! This info is as good as new! I'll sell you the coordinates for lousy {cash}. What do you say?"
|
||||
},
|
||||
"INTROTEXT_RESCUE": {
|
||||
"description": "",
|
||||
"message": "Hi, my name is {client}. The crew of a freighter had to abandon their ship after an incident. The ship owner is willing to pay {cash} for a discreet rescue mission. The accident site is near {planet}. Please pick up the crew and bring them to a station!"
|
||||
},
|
||||
"NUCLEAR_MISSILE": {
|
||||
"description": "A cargo item",
|
||||
"message": "Nuclear missile"
|
||||
},
|
||||
"OK_AGREED": {
|
||||
"description": "",
|
||||
"message": "OK, agreed."
|
||||
},
|
||||
"REPEAT_THE_REQUEST": {
|
||||
"description": "",
|
||||
"message": "Could you please repeat the request?"
|
||||
},
|
||||
"RESCUE_CAPSULE": {
|
||||
"description": "",
|
||||
"message": "Rescue capsule"
|
||||
},
|
||||
"ROCKET_LAUNCHERS": {
|
||||
"description": "",
|
||||
"message": "Rocket launchers"
|
||||
},
|
||||
"SCOOP": {
|
||||
"description": "Name of mission type",
|
||||
"message": "Scoop"
|
||||
},
|
||||
"SET_AS_TARGET": {
|
||||
"description": "",
|
||||
"message": "Set as navigation target"
|
||||
},
|
||||
"SHIP": {
|
||||
"description": "",
|
||||
"message": "Ship:"
|
||||
},
|
||||
"SHIP_DESTROYED": {
|
||||
"description": "",
|
||||
"message": "Oh no. {shipid} has been destroyed. Deliver the cargo to {station} now!"
|
||||
},
|
||||
"SPACEPORT": {
|
||||
"description": "",
|
||||
"message": "Spaceport:"
|
||||
},
|
||||
"SPOILED_FOOD": {
|
||||
"description": "cargo item",
|
||||
"message": "Spoiled food"
|
||||
},
|
||||
"SUCCESS_MSG_ARMS_DEALER": {
|
||||
"description": "",
|
||||
"message": "Hey Ace, nice flying! Money has been transferred."
|
||||
},
|
||||
"SUCCESS_MSG_RESCUE": {
|
||||
"description": "",
|
||||
"message": "Thank you very much! The crew would also like to express their thanks."
|
||||
},
|
||||
"TOXIC_WASTE": {
|
||||
"description": "cargo item",
|
||||
"message": "Toxic waste"
|
||||
},
|
||||
"UNKNOWN": {
|
||||
"description": "",
|
||||
"message": "Unknown"
|
||||
},
|
||||
"WARNING": {
|
||||
"description": "",
|
||||
"message": "Hey! What are you trying to do? Our contract is at risk! Please pick that up again!"
|
||||
},
|
||||
"WHY_NOT_YOURSELF": {
|
||||
"description": "",
|
||||
"message": "Why don't you do it yourself?"
|
||||
},
|
||||
"WHY_NOT_YOURSELF_ARMS_DEALER": {
|
||||
"description": "",
|
||||
"message": "The local authorities are watching me! I have to keep a low profile for a few weeks."
|
||||
},
|
||||
"WHY_NOT_YOURSELF_ILLEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "I'm just an office clerk. I don't own a ship."
|
||||
},
|
||||
"WHY_NOT_YOURSELF_LEGAL_GOODS": {
|
||||
"description": "",
|
||||
"message": "I don't have a ship."
|
||||
},
|
||||
"WHY_NOT_YOURSELF_RESCUE": {
|
||||
"description": "",
|
||||
"message": "I have no ship available at this time."
|
||||
},
|
||||
"YOU_DO_NOT_HAVE_A_SCOOP": {
|
||||
"description": "",
|
||||
"message": "Sorry, you do not have a cargo scoop."
|
||||
},
|
||||
"YOU_DO_NOT_HAVE_ENOUGH_MONEY": {
|
||||
"description": "",
|
||||
"message": "You don't have enough money."
|
||||
}
|
||||
}
|
|
@ -0,0 +1,857 @@
|
|||
-- Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
|
||||
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
|
||||
|
||||
local Game = require 'Game'
|
||||
local Lang = require 'Lang'
|
||||
local Ship = require 'Ship'
|
||||
local Comms = require 'Comms'
|
||||
local Event = require 'Event'
|
||||
local Space = require 'Space'
|
||||
local Timer = require 'Timer'
|
||||
local Engine = require 'Engine'
|
||||
local Format = require 'Format'
|
||||
local Mission = require 'Mission'
|
||||
local ShipDef = require 'ShipDef'
|
||||
local Character = require 'Character'
|
||||
local Equipment = require 'Equipment'
|
||||
local Serializer = require 'Serializer'
|
||||
|
||||
local utils = require 'utils'
|
||||
|
||||
local l = Lang.GetResource("module-scoop")
|
||||
local lc = Lang.GetResource("ui-core")
|
||||
|
||||
local AU = 149597870700.0
|
||||
local LEGAL = 1
|
||||
local ILLEGAL = 2
|
||||
|
||||
local mission_reputation = 1
|
||||
local mission_time = 14*24*60*60
|
||||
local max_dist = 20 * AU
|
||||
|
||||
local ads = {}
|
||||
local missions = {}
|
||||
|
||||
local rescue_capsule = Equipment.EquipType.New({
|
||||
name = "rescue_capsule",
|
||||
l10n_key = "RESCUE_CAPSULE",
|
||||
l10n_resource = "module-scoop",
|
||||
slots = "cargo",
|
||||
price = 500,
|
||||
icon_name = "Default",
|
||||
model_name = "escape_pod",
|
||||
capabilities = { mass = 1, crew = 1 },
|
||||
purchasable = false
|
||||
})
|
||||
|
||||
local rocket_launchers = Equipment.EquipType.New({
|
||||
name = "rocket_launchers",
|
||||
l10n_key = "ROCKET_LAUNCHERS",
|
||||
l10n_resource = "module-scoop",
|
||||
slots = "cargo",
|
||||
price = 500,
|
||||
icon_name = "Default",
|
||||
capabilities = { mass = 1 },
|
||||
purchasable = false
|
||||
})
|
||||
|
||||
local detonators = Equipment.EquipType.New({
|
||||
name = "detonators",
|
||||
l10n_key = "DETONATORS",
|
||||
l10n_resource = "module-scoop",
|
||||
slots = "cargo",
|
||||
price = 250,
|
||||
icon_name = "Default",
|
||||
capabilities = { mass = 1 },
|
||||
purchasable = false
|
||||
})
|
||||
|
||||
local nuclear_missile = Equipment.EquipType.New({
|
||||
name = "nuclear_missile",
|
||||
l10n_key = "NUCLEAR_MISSILE",
|
||||
l10n_resource = "module-scoop",
|
||||
slots = "cargo",
|
||||
price = 1250,
|
||||
icon_name = "Default",
|
||||
model_name = "missile",
|
||||
capabilities = { mass = 1 },
|
||||
purchasable = false
|
||||
})
|
||||
|
||||
-- Useless waste that the player has to sort out
|
||||
local toxic_waste = Equipment.EquipType.New({
|
||||
name = "toxic_waste",
|
||||
l10n_key = "TOXIC_WASTE",
|
||||
l10n_resource = "module-scoop",
|
||||
slots = "cargo",
|
||||
price = -50,
|
||||
icon_name = "Default",
|
||||
capabilities = { mass = 1 },
|
||||
purchasable = false
|
||||
})
|
||||
|
||||
local spoiled_food = Equipment.EquipType.New({
|
||||
name = "spoiled_food",
|
||||
l10n_key = "SPOILED_FOOD",
|
||||
l10n_resource = "module-scoop",
|
||||
slots = "cargo",
|
||||
price = -10,
|
||||
icon_name = "Default",
|
||||
capabilities = { mass = 1 },
|
||||
purchasable = false
|
||||
})
|
||||
|
||||
local unknown = Equipment.EquipType.New({
|
||||
name = "unknown",
|
||||
l10n_key = "UNKNOWN",
|
||||
l10n_resource = "module-scoop",
|
||||
slots = "cargo",
|
||||
price = -5,
|
||||
icon_name = "Default",
|
||||
capabilities = { mass = 1 },
|
||||
purchasable = false
|
||||
})
|
||||
|
||||
local rescue_capsules = {
|
||||
rescue_capsule
|
||||
}
|
||||
|
||||
local weapons = {
|
||||
rocket_launchers,
|
||||
detonators,
|
||||
nuclear_missile
|
||||
}
|
||||
|
||||
local waste = {
|
||||
toxic_waste,
|
||||
spoiled_food,
|
||||
unknown,
|
||||
Equipment.cargo.radioactives,
|
||||
Equipment.cargo.rubbish
|
||||
}
|
||||
|
||||
local flavours = {
|
||||
{
|
||||
id = "LEGAL_GOODS",
|
||||
cargo_type = nil,
|
||||
reward = -500,
|
||||
amount = 20,
|
||||
},
|
||||
{
|
||||
id = "ILLEGAL_GOODS",
|
||||
cargo_type = nil,
|
||||
reward = -1000,
|
||||
amount = 10,
|
||||
},
|
||||
{
|
||||
id = "RESCUE",
|
||||
cargo_type = rescue_capsules,
|
||||
reward = 750,
|
||||
amount = 4,
|
||||
return_to_station = true,
|
||||
},
|
||||
{
|
||||
id = "ARMS_DEALER",
|
||||
cargo_type = weapons,
|
||||
reward = 1000,
|
||||
amount = 1,
|
||||
deliver_to_ship = true,
|
||||
},
|
||||
}
|
||||
|
||||
-- Sort goods, legal and illegal
|
||||
local sortGoods = function (goods)
|
||||
local legal_goods = {}
|
||||
local illegal_goods = {}
|
||||
local system = Game.system
|
||||
|
||||
for _, e in pairs(goods) do
|
||||
if e.purchasable and system:IsCommodityLegal(e.name) then
|
||||
table.insert(legal_goods, e)
|
||||
else
|
||||
table.insert(illegal_goods, e)
|
||||
end
|
||||
end
|
||||
|
||||
return legal_goods, illegal_goods
|
||||
end
|
||||
|
||||
-- Returns the number of flavours of the given string (assuming first flavour has suffix '_1').
|
||||
local getNumberOfFlavours = function (str)
|
||||
local num = 1
|
||||
|
||||
while l:get(str .. "_" .. num) do
|
||||
num = num + 1
|
||||
end
|
||||
|
||||
return num - 1
|
||||
end
|
||||
|
||||
-- Create a debris field in a random distance to a system body
|
||||
local spawnDebris = function (debris, amount, sbody, min, max, lifetime)
|
||||
local list = {}
|
||||
local cargo
|
||||
local body = Space.GetBody(sbody:GetSystemBody().index)
|
||||
|
||||
for i = 1, Engine.rand:Integer(math.ceil(amount / 4), amount) do
|
||||
cargo = debris[Engine.rand:Integer(1, #debris)]
|
||||
body = Space.SpawnCargoNear(cargo, body, min, max, lifetime)
|
||||
if i > 1 then body:SetVelocity(list[1].body:GetVelocity()) end
|
||||
table.insert(list, { cargo = cargo, body = body })
|
||||
min = 10
|
||||
max = 1000
|
||||
end
|
||||
|
||||
-- add some useless waste
|
||||
for i = 1, Engine.rand:Integer(1, 9) do
|
||||
cargo = waste[Engine.rand:Integer(1, #waste)]
|
||||
body = Space.SpawnCargoNear(cargo, body, min, max, lifetime)
|
||||
body:SetVelocity(list[1].body:GetVelocity())
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
-- Create a couple of police ships
|
||||
local spawnPolice = function (station)
|
||||
local ship
|
||||
local police = {}
|
||||
local shipdef = ShipDef[Game.system.faction.policeShip]
|
||||
|
||||
for i = 1, 2 do
|
||||
ship = Space.SpawnShipDocked(shipdef.id, station)
|
||||
ship:SetLabel(lc.POLICE)
|
||||
ship:AddEquip(Equipment.laser.pulsecannon_1mw)
|
||||
table.insert(police, ship)
|
||||
if station.type == "STARPORT_SURFACE" then
|
||||
ship:AIEnterLowOrbit(Space.GetBody(station:GetSystemBody().parent.index))
|
||||
end
|
||||
end
|
||||
|
||||
Timer:CallAt(Game.time + 5, function ()
|
||||
for _, s in pairs(police) do
|
||||
s:AIKill(Game.player)
|
||||
end
|
||||
end)
|
||||
|
||||
return police
|
||||
end
|
||||
|
||||
-- Returns a random system close to the players location
|
||||
local nearbySystem = function ()
|
||||
local dist = 5
|
||||
local systems = {}
|
||||
|
||||
while #systems < 1 do
|
||||
systems = Game.system:GetNearbySystems(dist)
|
||||
dist = dist + 5
|
||||
end
|
||||
|
||||
return systems[Engine.rand:Integer(1, #systems)].path
|
||||
end
|
||||
|
||||
-- Create a ship in orbit
|
||||
local spawnClientShip = function (star, ship_label)
|
||||
local shipdefs = utils.build_array(utils.filter(
|
||||
function (k, def)
|
||||
return def.tag == "SHIP" and def.hyperdriveClass > 0 and def.equipSlotCapacity["scoop"] > 0
|
||||
end,
|
||||
pairs(ShipDef)))
|
||||
local shipdef = shipdefs[Engine.rand:Integer(1, #shipdefs)]
|
||||
|
||||
local radius = star:GetSystemBody().radius
|
||||
local min, max
|
||||
if star:GetSystemBody().type == "WHITE_DWARF" then
|
||||
min = radius * 30
|
||||
max = radius * 40
|
||||
else
|
||||
min = radius * 3.5
|
||||
max = radius * 4.5
|
||||
end
|
||||
|
||||
local ship = Space.SpawnShipOrbit(shipdef.id, Space.GetBody(star:GetSystemBody().index), min, max)
|
||||
|
||||
ship:SetLabel(ship_label)
|
||||
ship:AddEquip(Equipment.hyperspace["hyperdrive_" .. shipdef.hyperdriveClass])
|
||||
ship:AddEquip(Equipment.laser.pulsecannon_2mw)
|
||||
ship:AddEquip(Equipment.misc.shield_generator)
|
||||
|
||||
return ship
|
||||
end
|
||||
|
||||
local removeMission = function (mission, ref)
|
||||
local oldReputation = Character.persistent.player.reputation
|
||||
local sender = mission.client_ship and mission.ship_label or mission.client.name
|
||||
|
||||
if mission.status == "COMPLETED" then
|
||||
Character.persistent.player.reputation = oldReputation + mission_reputation
|
||||
Game.player:AddMoney(mission.reward)
|
||||
Comms.ImportantMessage(l["SUCCESS_MSG_" .. mission.id], sender)
|
||||
elseif mission.status == "FAILED" then
|
||||
Character.persistent.player.reputation = oldReputation - mission_reputation
|
||||
Comms.ImportantMessage(l["FAILURE_MSG_" .. mission.id], sender)
|
||||
end
|
||||
Event.Queue("onReputationChanged", oldReputation, Character.persistent.player.killcount,
|
||||
Character.persistent.player.reputation, Character.persistent.player.killcount)
|
||||
|
||||
if ref == nil then
|
||||
for r, m in pairs(missions) do
|
||||
if m == mission then ref = r break end
|
||||
end
|
||||
end
|
||||
mission:Remove()
|
||||
missions[ref] = nil
|
||||
end
|
||||
|
||||
-- Cargo transfer to a ship
|
||||
local transferCargo = function (mission, ref)
|
||||
Timer:CallEvery(9, function ()
|
||||
if not mission.client_ship then return true end
|
||||
|
||||
if not mission.docking_in_progress and Game.player:DistanceTo(mission.client_ship) <= 5000 then
|
||||
mission.docking_in_progress = true
|
||||
Comms.ImportantMessage(l.DOCKING_INSTRUCTION, mission.ship_label)
|
||||
end
|
||||
|
||||
if Game.player:DistanceTo(mission.client_ship) <= 100 then
|
||||
|
||||
-- unload mission cargo
|
||||
for i, e in pairs(mission.debris) do
|
||||
if e.body == nil then
|
||||
if Game.player:RemoveEquip(e.cargo, 1, "cargo") == 1 then
|
||||
mission.client_ship:AddEquip(e.cargo, 1, "cargo")
|
||||
mission.debris[i] = nil
|
||||
mission.amount = mission.amount - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if mission.amount == 0 then
|
||||
mission.status = "COMPLETED"
|
||||
elseif mission.destination == nil then
|
||||
mission.status = "FAILED"
|
||||
end
|
||||
end
|
||||
|
||||
if mission.status == "COMPLETED" or mission.status == "FAILED" then
|
||||
local ship = mission.client_ship
|
||||
mission.client_ship = nil
|
||||
removeMission(mission, ref)
|
||||
ship:HyperjumpTo(nearbySystem())
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local isQualifiedFor = function(reputation, ad)
|
||||
return reputation > (ad.reward/100) or false
|
||||
end
|
||||
|
||||
local onDelete = function (ref)
|
||||
ads[ref] = nil
|
||||
end
|
||||
|
||||
local isEnabled = function (ref)
|
||||
return ads[ref] ~= nil and isQualifiedFor(Character.persistent.player.reputation, ads[ref])
|
||||
end
|
||||
|
||||
local onChat = function (form, ref, option)
|
||||
local ad = ads[ref]
|
||||
local player = Game.player
|
||||
local debris, ship, radius, mindist, maxdist
|
||||
|
||||
form:Clear()
|
||||
|
||||
if option == -1 then
|
||||
form:Close()
|
||||
return
|
||||
end
|
||||
|
||||
local qualified = isQualifiedFor(Character.persistent.player.reputation, ad)
|
||||
|
||||
form:SetFace(ad.client)
|
||||
|
||||
if not qualified then
|
||||
form:SetMessage(l["DENY_" .. Engine.rand:Integer(1, getNumberOfFlavours("DENY"))])
|
||||
return
|
||||
end
|
||||
|
||||
form:AddNavButton(ad.planet)
|
||||
|
||||
if option == 0 then
|
||||
local introtext = string.interp(ad.introtext, {
|
||||
client = ad.client.name,
|
||||
shipid = ad.ship_label,
|
||||
star = ad.star:GetSystemBody().name,
|
||||
planet = ad.planet:GetSystemBody().name,
|
||||
cash = Format.Money(math.abs(ad.reward), false),
|
||||
})
|
||||
form:SetMessage(introtext)
|
||||
|
||||
elseif option == 1 then
|
||||
form:SetMessage(l["WHY_NOT_YOURSELF_" .. ad.id])
|
||||
|
||||
elseif option == 2 then
|
||||
form:SetMessage(string.interp(l["HOW_MUCH_TIME_" .. ad.id], { star = ad.star:GetSystemBody().name, date = Format.Date(ad.due) }))
|
||||
|
||||
elseif option == 3 then
|
||||
if ad.reward > 0 and player:CountEquip(Equipment.misc.cargo_scoop) == 0 and player:CountEquip(Equipment.misc.multi_scoop) == 0 then
|
||||
form:SetMessage(l.YOU_DO_NOT_HAVE_A_SCOOP)
|
||||
form:RemoveNavButton()
|
||||
return
|
||||
end
|
||||
|
||||
if ad.reward < 0 and player:GetMoney() < math.abs(ad.reward) then
|
||||
form:SetMessage(l.YOU_DO_NOT_HAVE_ENOUGH_MONEY)
|
||||
form:RemoveNavButton()
|
||||
return
|
||||
end
|
||||
|
||||
form:RemoveAdvertOnClose()
|
||||
ads[ref] = nil
|
||||
|
||||
radius = ad.planet:GetSystemBody().radius
|
||||
if ad.planet:GetSystemBody().superType == "ROCKY_PLANET" then
|
||||
mindist = radius * 2.5
|
||||
maxdist = radius * 3.5
|
||||
else
|
||||
mindist = radius * 25
|
||||
maxdist = radius * 35
|
||||
end
|
||||
debris = spawnDebris(ad.debris_type, ad.amount, ad.planet, mindist, maxdist, ad.due - Game.time)
|
||||
|
||||
if ad.reward < 0 then player:AddMoney(ad.reward) end
|
||||
if ad.deliver_to_ship then
|
||||
ship = spawnClientShip(ad.star, ad.ship_label)
|
||||
end
|
||||
|
||||
local mission = {
|
||||
type = "Scoop",
|
||||
location = ad.location,
|
||||
introtext = ad.introtext,
|
||||
client = ad.client,
|
||||
station = ad.station.path,
|
||||
star = ad.star,
|
||||
planet = ad.planet,
|
||||
id = ad.id,
|
||||
debris = debris,
|
||||
amount = #debris,
|
||||
reward = ad.reward,
|
||||
due = ad.due,
|
||||
return_to_station = ad.return_to_station,
|
||||
deliver_to_ship = ad.deliver_to_ship,
|
||||
client_ship = ship,
|
||||
ship_label = ad.ship_label,
|
||||
destination = debris[1].body
|
||||
}
|
||||
|
||||
table.insert(missions, Mission.New(mission))
|
||||
form:SetMessage(l["ACCEPTED_" .. ad.id])
|
||||
form:RemoveNavButton()
|
||||
form:AddNavButton(debris[1].body)
|
||||
return
|
||||
end
|
||||
|
||||
form:AddOption(l.WHY_NOT_YOURSELF, 1)
|
||||
form:AddOption(l.HOW_MUCH_TIME, 2)
|
||||
form:AddOption(l.REPEAT_THE_REQUEST, 0)
|
||||
form:AddOption(l.OK_AGREED, 3)
|
||||
end
|
||||
|
||||
local getPlanets = function (system)
|
||||
local planets = {}
|
||||
|
||||
for _, p in ipairs(system:GetBodyPaths()) do
|
||||
if p:GetSystemBody().superType == "ROCKY_PLANET" or p:GetSystemBody().superType == "GAS_GIANT" then
|
||||
table.insert(planets, p)
|
||||
end
|
||||
end
|
||||
return planets
|
||||
end
|
||||
|
||||
local planets = nil
|
||||
|
||||
local makeAdvert = function (station)
|
||||
if planets == nil then planets = getPlanets(Game.system) end
|
||||
if #planets == 0 then return end
|
||||
|
||||
if flavours[LEGAL].cargo_type == nil then
|
||||
flavours[LEGAL].cargo_type, flavours[ILLEGAL].cargo_type = sortGoods(Equipment.cargo)
|
||||
end
|
||||
|
||||
local stars = Game.system:GetStars()
|
||||
local star = stars[Engine.rand:Integer(1, #stars)]
|
||||
local planet = planets[Engine.rand:Integer(1, #planets)]
|
||||
local dist = station:DistanceTo(Space.GetBody(planet:GetSystemBody().index))
|
||||
local flavour = flavours[Engine.rand:Integer(1, #flavours)]
|
||||
local due = Game.time + mission_time * (1 + dist / max_dist) * Engine.rand:Number(0.8, 1.2)
|
||||
local reward
|
||||
|
||||
if flavour.reward < 0 then
|
||||
reward = flavour.reward * (1.15 - dist / max_dist) * Engine.rand:Number(0.9, 1.1)
|
||||
else
|
||||
reward = flavour.reward * (1 + dist / max_dist) * Engine.rand:Number(0.75, 1.25)
|
||||
end
|
||||
reward = utils.round(reward, 50)
|
||||
|
||||
if #flavour.cargo_type > 0 and dist < max_dist and station:DistanceTo(Space.GetBody(star.index)) < max_dist then
|
||||
local ad = {
|
||||
station = station,
|
||||
location = planet,
|
||||
introtext = l["INTROTEXT_" .. flavour.id],
|
||||
client = Character.New(),
|
||||
star = star.path,
|
||||
planet = planet,
|
||||
id = flavour.id,
|
||||
debris_type = flavour.cargo_type,
|
||||
reward = math.ceil(reward),
|
||||
amount = flavour.amount,
|
||||
due = due,
|
||||
return_to_station = flavour.return_to_station,
|
||||
deliver_to_ship = flavour.deliver_to_ship,
|
||||
ship_label = flavour.deliver_to_ship and Ship.MakeRandomLabel() or nil
|
||||
}
|
||||
|
||||
ad.desc = string.interp(l["ADTEXT_" .. flavour.id], { cash = Format.Money(ad.reward, false) })
|
||||
|
||||
local ref = station:AddAdvert({
|
||||
description = ad.desc,
|
||||
icon = flavour.id == "RESCUE" and "searchrescue" or "haul",
|
||||
onChat = onChat,
|
||||
onDelete = onDelete,
|
||||
isEnabled = isEnabled
|
||||
})
|
||||
ads[ref] = ad
|
||||
end
|
||||
end
|
||||
|
||||
local onCreateBB = function (station)
|
||||
local num = Engine.rand:Integer(0, math.ceil(Game.system.population * Game.system.lawlessness))
|
||||
for i = 1, num do
|
||||
makeAdvert(station)
|
||||
end
|
||||
end
|
||||
|
||||
local onUpdateBB = function (station)
|
||||
for ref, ad in pairs(ads) do
|
||||
if ad.due < Game.time + 5*24*60*60 then -- five day timeout
|
||||
ad.station:RemoveAdvert(ref)
|
||||
end
|
||||
end
|
||||
if Engine.rand:Integer(4*24*60*60) < 60*60 then -- roughly once every four days
|
||||
makeAdvert(station)
|
||||
end
|
||||
end
|
||||
|
||||
local onShipEquipmentChanged = function (ship, equipment)
|
||||
if not ship:IsPlayer() or equipment == nil or equipment:GetDefaultSlot() ~= "cargo" then return end
|
||||
|
||||
for ref, mission in pairs(missions) do
|
||||
if not mission.police and not Game.system:IsCommodityLegal(equipment.name) and not ship:IsDocked() and mission.location:IsSameSystem(Game.system.path) then
|
||||
if (1 - Game.system.lawlessness) > Engine.rand:Number(4) then
|
||||
local station = ship:FindNearestTo("SPACESTATION")
|
||||
if station then mission.police = spawnPolice(station) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- The attacker could be a ship or the planet
|
||||
-- If scooped or destroyed by self-destruction, attacker is nil
|
||||
local onCargoDestroyed = function (body, attacker)
|
||||
for ref, mission in pairs(missions) do
|
||||
for i, e in pairs(mission.debris) do
|
||||
if body == e.body then
|
||||
e.body = nil
|
||||
if body == mission.destination then
|
||||
-- remove NavButton
|
||||
mission.destination = nil
|
||||
end
|
||||
if attacker and (mission.return_to_station or mission.deliver_to_ship) then
|
||||
mission.status = "FAILED"
|
||||
end
|
||||
if mission.destination == nil then
|
||||
for i, e in pairs(mission.debris) do
|
||||
if e.body ~= nil then
|
||||
-- set next target
|
||||
mission.destination = e.body
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onJettison = function (ship, cargo)
|
||||
if not ship:IsPlayer() then return end
|
||||
|
||||
for ref, mission in pairs(missions) do
|
||||
if mission.reward > 0 and not mission.warning then
|
||||
for i, e in pairs(mission.debris) do
|
||||
if cargo == e.cargo then
|
||||
mission.warning = true
|
||||
Comms.ImportantMessage(l.WARNING, mission.client.name)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onShipHit = function (ship, attacker)
|
||||
if ship:IsPlayer() then return end
|
||||
if attacker == nil or not attacker:isa('Ship') then return end
|
||||
|
||||
for ref, mission in pairs(missions) do
|
||||
if mission.police then
|
||||
for _, s in pairs(mission.police) do
|
||||
if s == ship then
|
||||
ship:AIKill(attacker)
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif mission.client_ship == ship then
|
||||
ship:AIKill(attacker)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onShipDestroyed = function (ship, attacker)
|
||||
if ship:IsPlayer() then return end
|
||||
|
||||
for ref, mission in pairs(missions) do
|
||||
if mission.police then
|
||||
for i, s in pairs(mission.police) do
|
||||
if s == ship then
|
||||
table.remove(mission.police, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif mission.client_ship == ship then
|
||||
mission.client_ship = nil
|
||||
local msg = string.interp(l.SHIP_DESTROYED, {
|
||||
shipid = mission.ship_label,
|
||||
station = mission.station:GetSystemBody().name
|
||||
})
|
||||
Comms.ImportantMessage(msg, mission.client.name)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onShipDocked = function (player, station)
|
||||
if not player:IsPlayer() then return end
|
||||
|
||||
for ref, mission in pairs(missions) do
|
||||
if mission.police then
|
||||
for _, s in pairs(mission.police) do
|
||||
if station.type == "STARPORT_SURFACE" then
|
||||
s:AIEnterLowOrbit(Space.GetBody(station:GetSystemBody().parent.index))
|
||||
else
|
||||
s:AIFlyTo(station)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if mission.return_to_station or mission.deliver_to_ship and not mission.client_ship and mission.station == station.path then
|
||||
|
||||
-- unload mission cargo
|
||||
for i, e in pairs(mission.debris) do
|
||||
if e.body == nil then
|
||||
if player:RemoveEquip(e.cargo, 1, "cargo") == 1 then
|
||||
mission.debris[i] = nil
|
||||
mission.amount = mission.amount - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if mission.amount == 0 then
|
||||
mission.status = "COMPLETED"
|
||||
elseif mission.destination == nil then
|
||||
mission.status = "FAILED"
|
||||
end
|
||||
|
||||
if mission.status == "COMPLETED" or mission.status == "FAILED" then
|
||||
removeMission(mission, ref)
|
||||
end
|
||||
|
||||
-- remove stale missions, if any
|
||||
-- all cargo related to flavour 1 and 2 scooped or destroyed
|
||||
elseif mission.reward < 0 and mission.destination == nil then
|
||||
mission:Remove()
|
||||
missions[ref] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onShipUndocked = function (player, station)
|
||||
if not player:IsPlayer() then return end
|
||||
|
||||
for ref, mission in pairs(missions) do
|
||||
if mission.police then
|
||||
for _, s in pairs(mission.police) do
|
||||
s:AIKill(player)
|
||||
end
|
||||
end
|
||||
|
||||
if mission.deliver_to_ship and not mission.in_progess then
|
||||
mission.in_progress = true
|
||||
transferCargo(mission, ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local getPopulatedPlanets = function (system)
|
||||
local planets = {}
|
||||
|
||||
for _, p in ipairs(system:GetBodyPaths()) do
|
||||
if p:GetSystemBody().population > 0 then
|
||||
table.insert(planets, p)
|
||||
end
|
||||
end
|
||||
return planets
|
||||
end
|
||||
|
||||
local onEnterSystem = function (ship)
|
||||
if not ship:IsPlayer() or Game.system.population == 0 then return end
|
||||
|
||||
local planets = getPopulatedPlanets(Game.system)
|
||||
local num = Engine.rand:Integer(0, math.ceil(Game.system.population * Game.system.lawlessness))
|
||||
|
||||
flavours[LEGAL].cargo_type, flavours[ILLEGAL].cargo_type = sortGoods(Equipment.cargo)
|
||||
|
||||
-- spawn random cargo (legal or illegal goods)
|
||||
for i = 1, num do
|
||||
local planet = planets[Engine.rand:Integer(1, #planets)]
|
||||
local radius = planet:GetSystemBody().radius
|
||||
local flavour = flavours[Engine.rand:Integer(LEGAL, ILLEGAL)]
|
||||
local debris = flavour.cargo_type
|
||||
if #debris > 0 then
|
||||
spawnDebris(debris, flavour.amount, planet, radius * 1.2, radius * 3.5, mission_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onLeaveSystem = function (ship)
|
||||
if ship:IsPlayer() then
|
||||
for ref, mission in pairs(missions) do
|
||||
mission.destination = nil
|
||||
mission.police = nil
|
||||
if mission.client_ship then
|
||||
mission.client_ship = nil
|
||||
mission.status = "FAILED"
|
||||
end
|
||||
for i, e in pairs(mission.debris) do
|
||||
e.body = nil
|
||||
end
|
||||
end
|
||||
planets = nil
|
||||
flavours[LEGAL].cargo_type = nil
|
||||
flavours[ILLEGAL].cargo_type = nil
|
||||
end
|
||||
end
|
||||
|
||||
local onReputationChanged = function (oldRep, oldKills, newRep, newKills)
|
||||
for ref, ad in pairs(ads) do
|
||||
local oldQualified = isQualifiedFor(oldRep, ad)
|
||||
if isQualifiedFor(newRep, ad) ~= oldQualified then
|
||||
Event.Queue("onAdvertChanged", ad.station, ref);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local buildMissionDescription = function(mission)
|
||||
local ui = require 'pigui'
|
||||
local desc = {}
|
||||
|
||||
desc.description = mission.introtext:interp({
|
||||
client = mission.client.name,
|
||||
shipid = mission.ship_label,
|
||||
star = mission.star:GetSystemBody().name,
|
||||
planet = mission.planet:GetSystemBody().name,
|
||||
cash = Format.Money(math.abs(mission.reward), false)
|
||||
})
|
||||
|
||||
desc.details = {
|
||||
{ l.CLIENT, mission.client.name },
|
||||
{ l.SPACEPORT, mission.station:GetSystemBody().name },
|
||||
mission.client_ship and { l.SHIP, mission.client_ship.label } or false,
|
||||
false,
|
||||
{ l.DEADLINE, ui.Format.Date(mission.due) }
|
||||
}
|
||||
|
||||
desc.client = mission.client
|
||||
desc.location = mission.destination or nil
|
||||
if mission.deliver_to_ship then
|
||||
desc.returnLocation = mission.client_ship or mission.station
|
||||
end
|
||||
|
||||
return desc
|
||||
end
|
||||
|
||||
local loaded_data
|
||||
|
||||
local onGameStart = function ()
|
||||
ads = {}
|
||||
missions = {}
|
||||
|
||||
if loaded_data and loaded_data.ads then
|
||||
|
||||
for k, ad in pairs(loaded_data.ads) do
|
||||
local ref = ad.station:AddAdvert({
|
||||
description = ad.desc,
|
||||
icon = ad.id == "RESCUE" and "searchrescue" or "haul",
|
||||
onChat = onChat,
|
||||
onDelete = onDelete,
|
||||
isEnabled = isEnabled
|
||||
})
|
||||
ads[ref] = ad
|
||||
end
|
||||
|
||||
missions = loaded_data.missions
|
||||
|
||||
loaded_data = nil
|
||||
|
||||
for ref, mission in pairs(missions) do
|
||||
if mission.deliver_to_ship then
|
||||
mission.in_progress = true
|
||||
transferCargo(mission, ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local onGameEnd = function ()
|
||||
planets = nil
|
||||
flavours[LEGAL].cargo_type = nil
|
||||
flavours[ILLEGAL].cargo_type = nil
|
||||
end
|
||||
|
||||
local serialize = function ()
|
||||
return { ads = ads, missions = missions }
|
||||
end
|
||||
|
||||
local unserialize = function (data)
|
||||
loaded_data = data
|
||||
end
|
||||
|
||||
Event.Register("onCreateBB", onCreateBB)
|
||||
Event.Register("onUpdateBB", onUpdateBB)
|
||||
Event.Register("onShipEquipmentChanged", onShipEquipmentChanged)
|
||||
Event.Register("onShipDocked", onShipDocked)
|
||||
Event.Register("onShipUndocked", onShipUndocked)
|
||||
Event.Register("onShipHit", onShipHit)
|
||||
Event.Register("onShipDestroyed", onShipDestroyed)
|
||||
Event.Register("onJettison", onJettison)
|
||||
Event.Register("onCargoDestroyed", onCargoDestroyed)
|
||||
Event.Register("onEnterSystem", onEnterSystem)
|
||||
Event.Register("onLeaveSystem", onLeaveSystem)
|
||||
Event.Register("onGameStart", onGameStart)
|
||||
Event.Register("onGameEnd", onGameEnd)
|
||||
Event.Register("onReputationChanged", onReputationChanged)
|
||||
|
||||
Mission.RegisterType("Scoop", l.SCOOP, buildMissionDescription)
|
||||
|
||||
Serializer:Register("Scoop", serialize, unserialize)
|
|
@ -102,7 +102,7 @@ void CargoBody::TimeStepUpdate(const float timeStep)
|
|||
if (m_hasSelfdestruct) {
|
||||
m_selfdestructTimer -= timeStep;
|
||||
if (m_selfdestructTimer <= 0) {
|
||||
LuaEvent::Queue("onCargoDestroyed", this, NULL);
|
||||
LuaEvent::Queue("onCargoDestroyed", this);
|
||||
Pi::game->GetSpace()->KillBody(this);
|
||||
SfxManager::Add(this, TYPE_EXPLOSION);
|
||||
}
|
||||
|
@ -114,10 +114,10 @@ bool CargoBody::OnDamage(Body *attacker, float kgDamage, const CollisionContact
|
|||
{
|
||||
m_hitpoints -= kgDamage * 0.001f;
|
||||
if (m_hitpoints < 0) {
|
||||
if (attacker && attacker->IsType(Object::BODY))
|
||||
if (attacker && attacker->IsType(ObjectType::BODY))
|
||||
LuaEvent::Queue("onCargoDestroyed", this, dynamic_cast<Body *>(attacker));
|
||||
else
|
||||
LuaEvent::Queue("onCargoDestroyed", this, NULL);
|
||||
LuaEvent::Queue("onCargoDestroyed", this);
|
||||
Pi::game->GetSpace()->KillBody(this);
|
||||
SfxManager::Add(this, TYPE_EXPLOSION);
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ bool CargoBody::OnCollision(Body *b, Uint32 flags, double relVel)
|
|||
int cargoscoop_cap = 0;
|
||||
static_cast<Ship *>(b)->Properties().Get("cargo_scoop_cap", cargoscoop_cap);
|
||||
if (cargoscoop_cap > 0) {
|
||||
LuaEvent::Queue("onCargoDestroyed", this, NULL);
|
||||
LuaEvent::Queue("onCargoDestroyed", this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class CargoBody : public DynamicBody {
|
|||
public:
|
||||
OBJDEF(CargoBody, DynamicBody, CARGOBODY);
|
||||
CargoBody() = delete;
|
||||
CargoBody(const LuaRef &cargo, float selfdestructTimer = 86400.0f); // default to 24 h lifetime
|
||||
CargoBody(const LuaRef &cargo, float selfdestructTimer = 86400.0f); // default to 24 h lifetime
|
||||
CargoBody(const char *modelName, const LuaRef &cargo, float selfdestructTimer = 86400.0f); // default to 24 h lifetime
|
||||
CargoBody(const Json &jsonObj, Space *space);
|
||||
LuaRef GetCargoType() const { return m_cargo; }
|
||||
|
|
|
@ -728,6 +728,20 @@ static bool _body_from_json(const Json &obj)
|
|||
return push_body_to_lua(body);
|
||||
}
|
||||
|
||||
static int l_body_get_velocity(lua_State *l)
|
||||
{
|
||||
Body *b = LuaObject<Body>::CheckFromLua(1);
|
||||
LuaPush<vector3d>(l, b->GetVelocity());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_body_set_velocity(lua_State *l)
|
||||
{
|
||||
Body *b = LuaObject<Body>::CheckFromLua(1);
|
||||
b->SetVelocity(LuaPull<vector3d>(l, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
const char *LuaObject<Body>::s_type = "Body";
|
||||
|
||||
|
@ -758,6 +772,8 @@ void LuaObject<Body>::RegisterClass()
|
|||
{ "IsGroundStation", l_body_is_ground_station },
|
||||
{ "IsCargoContainer", l_body_is_cargo_container },
|
||||
{ "GetSystemBody", l_body_get_system_body },
|
||||
{ "GetVelocity", l_body_get_velocity },
|
||||
{ "SetVelocity", l_body_set_velocity },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
|
|
@ -1891,7 +1891,7 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
|
|||
for (Body *body : Pi::game->GetSpace()->GetBodies()) {
|
||||
if (body == Pi::game->GetPlayer()) continue;
|
||||
if (body->GetType() == ObjectType::PROJECTILE) continue;
|
||||
if (body->GetType() == ObjectType::SHIP &&
|
||||
if ((body->GetType() == ObjectType::SHIP || body->GetType() == ObjectType::CARGOBODY) &&
|
||||
body->GetPositionRelTo(Pi::player).Length() > ship_max_distance) continue;
|
||||
const PiGui::TScreenSpace res = lua_world_space_to_screen_space(body); // defined in LuaPiGui.cpp
|
||||
if (!res._onScreen) continue;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
|
||||
|
||||
#include "LuaSpace.h"
|
||||
#include "CargoBody.h"
|
||||
#include "Frame.h"
|
||||
#include "Game.h"
|
||||
#include "HyperspaceCloud.h"
|
||||
|
@ -16,7 +17,6 @@
|
|||
#include "Ship.h"
|
||||
#include "Space.h"
|
||||
#include "SpaceStation.h"
|
||||
#include "CargoBody.h"
|
||||
|
||||
/*
|
||||
* Interface: Space
|
||||
|
@ -361,7 +361,7 @@ static int l_space_spawn_ship_parked(lua_State *l)
|
|||
Ship *ship = new Ship(type);
|
||||
assert(ship);
|
||||
|
||||
const double parkDist = station->GetStationType()->ParkingDistance() - ship->GetPhysRadius(); // park inside parking radius
|
||||
const double parkDist = station->GetStationType()->ParkingDistance() - ship->GetPhysRadius(); // park inside parking radius
|
||||
const double parkOffset = (0.5 * station->GetStationType()->ParkingGapSize()) + ship->GetPhysRadius(); // but outside the docking gap
|
||||
|
||||
double xpos = (slot == 0 || slot == 3) ? -parkOffset : parkOffset;
|
||||
|
@ -551,6 +551,27 @@ static int l_space_spawn_ship_landed_near(lua_State *l)
|
|||
return 1;
|
||||
}
|
||||
|
||||
// sb - central systembody, pos - absolute coordinates of given object
|
||||
static vector3d _orbital_velocity_random_direction(const SystemBody *sb, const vector3d &pos)
|
||||
{
|
||||
// If we got a zero mass of central body - there is no orbit
|
||||
if (sb->GetMass() < 0.01)
|
||||
return vector3d(0.0);
|
||||
// calculating basis from radius - vector
|
||||
vector3d k = pos.Normalized();
|
||||
vector3d i;
|
||||
if (std::fabs(k.z) > 0.999999) // very vertical = z
|
||||
i = vector3d(1.0, 0.0, 0.0); // second ort = x
|
||||
else
|
||||
i = k.Cross(vector3d(0.0, 0.0, 1.0)).Normalized();
|
||||
vector3d j = k.Cross(i);
|
||||
// generating random 2d direction and putting it into basis
|
||||
vector3d randomOrthoDirection = MathUtil::RandomPointOnCircle(1.0) * matrix3x3d::FromVectors(i, j, k).Transpose();
|
||||
// calculate the value of the orbital velocity
|
||||
double orbitalVelocity = sqrt(G * sb->GetMass() / pos.Length());
|
||||
return randomOrthoDirection * orbitalVelocity;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: SpawnCargoNear
|
||||
*
|
||||
|
@ -585,8 +606,8 @@ static int l_space_spawn_cargo_near(lua_State *l)
|
|||
|
||||
LUA_DEBUG_START(l);
|
||||
|
||||
CargoBody * c_body;
|
||||
const char * model;
|
||||
CargoBody *c_body;
|
||||
const char *model;
|
||||
|
||||
lua_getfield(l, 1, "model_name");
|
||||
if (lua_isstring(l, -1))
|
||||
|
@ -594,21 +615,29 @@ static int l_space_spawn_cargo_near(lua_State *l)
|
|||
else
|
||||
model = "cargo";
|
||||
|
||||
if (lua_gettop(l) >= 5){
|
||||
if (lua_gettop(l) >= 5) {
|
||||
float lifetime = lua_tonumber(l, 5);
|
||||
c_body = new CargoBody(model, LuaRef(l, 1), lifetime);
|
||||
} else {
|
||||
c_body = new CargoBody(model, LuaRef(l, 1));
|
||||
}
|
||||
Body * nearbody = LuaObject<Body>::CheckFromLua(2);
|
||||
Body *nearbody = LuaObject<Body>::CheckFromLua(2);
|
||||
float min_dist = luaL_checknumber(l, 3);
|
||||
float max_dist = luaL_checknumber(l, 4);
|
||||
if (min_dist > max_dist)
|
||||
luaL_error(l, "min_dist must not be larger than max_dist");
|
||||
|
||||
c_body->SetFrame(nearbody->GetFrame());
|
||||
c_body->SetPosition((MathUtil::RandomPointOnSphere(min_dist, max_dist)) + nearbody->GetPosition());
|
||||
c_body->SetVelocity(vector3d(0,0,0));
|
||||
FrameId frameId = nearbody->GetFrame();
|
||||
Frame *frame = Frame::GetFrame(frameId);
|
||||
// if the frame is rotating, use non-rotating parent
|
||||
if (frame->IsRotFrame()) {
|
||||
assert(frame->GetParent());
|
||||
frame = Frame::GetFrame(frame->GetParent());
|
||||
frameId = frame->GetId();
|
||||
}
|
||||
c_body->SetFrame(frameId);
|
||||
c_body->SetPosition(MathUtil::RandomPointOnSphere(min_dist, max_dist) + nearbody->GetPosition());
|
||||
c_body->SetVelocity(_orbital_velocity_random_direction(frame->GetSystemBody(), c_body->GetPosition()));
|
||||
Pi::game->GetSpace()->AddBody(c_body);
|
||||
|
||||
LuaObject<Body>::PushToLua(c_body);
|
||||
|
@ -618,6 +647,72 @@ static int l_space_spawn_cargo_near(lua_State *l)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: SpawnShipOrbit
|
||||
*
|
||||
* Create a ship and place it in orbit near the given <Body>.
|
||||
*
|
||||
* > ship = Space.SpawnShip(type, body, min, max)
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* type - the name of the ship
|
||||
*
|
||||
* body - the <Body> near which the ship should be spawned
|
||||
*
|
||||
* min - minimum distance from the body to place the ship, in m
|
||||
*
|
||||
* max - maximum distance to place the ship
|
||||
*
|
||||
*
|
||||
* Return:
|
||||
*
|
||||
* ship - a <Ship> object for the new ship
|
||||
*
|
||||
* Status:
|
||||
*
|
||||
* experimental
|
||||
*/
|
||||
static int l_space_spawn_ship_orbit(lua_State *l)
|
||||
{
|
||||
if (!Pi::game)
|
||||
luaL_error(l, "Game is not started");
|
||||
|
||||
LUA_DEBUG_START(l);
|
||||
|
||||
const char *type = luaL_checkstring(l, 1);
|
||||
if (!ShipType::Get(type))
|
||||
luaL_error(l, "Unknown ship type '%s'", type);
|
||||
|
||||
Body *nearbody = LuaObject<Body>::CheckFromLua(2);
|
||||
float min_dist = luaL_checknumber(l, 3);
|
||||
float max_dist = luaL_checknumber(l, 4);
|
||||
if (min_dist > max_dist)
|
||||
luaL_error(l, "min_dist must not be larger than max_dist");
|
||||
|
||||
Ship *ship = new Ship(type);
|
||||
assert(ship);
|
||||
|
||||
FrameId frameId = nearbody->GetFrame();
|
||||
Frame *frame = Frame::GetFrame(frameId);
|
||||
// if the frame is rotating, use non-rotating parent
|
||||
if (frame->IsRotFrame()) {
|
||||
assert(frame->GetParent());
|
||||
frame = Frame::GetFrame(frame->GetParent());
|
||||
frameId = frame->GetId();
|
||||
}
|
||||
ship->SetFrame(frameId);
|
||||
ship->SetPosition(MathUtil::RandomPointOnSphere(min_dist, max_dist) + nearbody->GetPosition());
|
||||
ship->SetVelocity(_orbital_velocity_random_direction(frame->GetSystemBody(), ship->GetPosition()));
|
||||
Pi::game->GetSpace()->AddBody(ship);
|
||||
|
||||
LuaObject<Ship>::PushToLua(ship);
|
||||
|
||||
LUA_DEBUG_END(l, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: GetBody
|
||||
*
|
||||
|
@ -789,11 +884,11 @@ void LuaSpace::Register()
|
|||
{ "SpawnShipLanded", l_space_spawn_ship_landed },
|
||||
{ "SpawnShipLandedNear", l_space_spawn_ship_landed_near },
|
||||
{ "SpawnCargoNear", l_space_spawn_cargo_near },
|
||||
{ "SpawnShipOrbit", l_space_spawn_ship_orbit },
|
||||
|
||||
{ "GetBody", l_space_get_body },
|
||||
{ "GetBodies", l_space_get_bodies },
|
||||
|
||||
|
||||
{ "DbgDumpFrames", l_space_dump_frames },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue