Skip to content

Commit

Permalink
Add function PutShipOnTheRoute
Browse files Browse the repository at this point in the history
Function description:

The ship rearranged from its current position to a given body in space, for a
given part of the path, as if it were flying in a straight line, consuming
fuel and changing speed. The ship's current speed is ignored and is
considered to be equal to target's.
This function changes the coordinates, the mass of fuel in the tank, and the
speed of the given ship.

This function will be used so that when the game starts, or the player
jump into the system, the NPC ships looked as if they had been moving on
their routes for a long time.
  • Loading branch information
Gliese852 committed Dec 13, 2020
1 parent 7873b82 commit 6bab35d
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 12 deletions.
3 changes: 2 additions & 1 deletion data/modules/TradeShips.lua
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ local spawnInitialShips = function (game_start)
min_dist = min_dist - (range * (num_trade_ships / 4))
end

ship = Space.SpawnShip(ship_name, min_dist, min_dist + range)
ship = Space.SpawnShip(ship_name, 9, 11, {from_paths[Engine.rand:Integer(1, #from_paths)], 0.0})
ship:SetLabel(Ship.MakeRandomLabel())
trade_ships[ship] = { status = 'inbound', ship_name = ship_name }
-- Add ship equipment right now, because...
Expand Down Expand Up @@ -495,6 +495,7 @@ local spawnInitialShips = function (game_start)
ship:RemoveEquip(e.cargo.hydrogen, Engine.rand:Integer(1, fuel_added))
end
if trader.status == 'inbound' then
Space.PutShipOnTheRoute(ship, trader.starport, Engine.rand:Number(0.0, 0.99999))-- don't generate at the destination
ship:AIDockWith(trader.starport)
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/ShipAICmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ static double MaxFeatureRad(Body *body)
return body->GetPhysRadius();
}

static double MaxEffectRad(Body *body, Propulsion *prop)
double MaxEffectRad(Body *body, Propulsion *prop)
{
if (!body) return 0.0;
if (!body->IsType(ObjectType::TERRAINBODY)) {
Expand Down Expand Up @@ -720,7 +720,7 @@ static vector3d GenerateTangent(DynamicBody *dBody, FrameId targframeId, const v
//2 - unsafe escape from effect radius
//3 - unsafe entry to effect radius
//4 - probable path intercept
static int CheckCollision(DynamicBody *dBody, const vector3d &pathdir, double pathdist, const vector3d &tpos, double endvel, double r)
int CheckCollision(DynamicBody *dBody, const vector3d &pathdir, double pathdist, const vector3d &tpos, double endvel, double r)
{
if (!dBody->Have(DynamicBody::PROPULSION)) return 0;
Propulsion *prop = dBody->GetPropulsion();
Expand Down
145 changes: 136 additions & 9 deletions src/lua/LuaSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Ship.h"
#include "Space.h"
#include "SpaceStation.h"
#include "ship/PrecalcPath.h"

/*
* Interface: Space
Expand Down Expand Up @@ -52,6 +53,7 @@ static void _unpack_hyperspace_args(lua_State *l, int index, SystemPath *&path,
static Body *_maybe_wrap_ship_with_cloud(Ship *ship, SystemPath *path, double due)
{
if (!path) return ship;
if (due <= 0) return ship;

HyperspaceCloud *cloud = new HyperspaceCloud(ship, due, true);
ship->SetHyperspaceDest(path);
Expand Down Expand Up @@ -91,11 +93,11 @@ static Body *_maybe_wrap_ship_with_cloud(Ship *ship, SystemPath *path, double du
* Examples:
*
* > -- spawn a ship 5-6AU from the system centre
* > local ship = Ship.Spawn("eagle_lrf", 5, 6)
* > local ship = Space.SpawnShip("eagle_lrf", 5, 6)
*
* > -- spawn a ship in the ~11AU hyperspace area and make it appear that it
* > -- came from Sol and will arrive in ten minutes
* > local ship = Ship.Spawn(
* > local ship = Space.SpawnShip(
* > "flowerfairy", 9, 11,
* > { SystemPath:New(0,0,0), Game.time + 600 }
* > )
Expand Down Expand Up @@ -149,12 +151,138 @@ static int l_space_spawn_ship(lua_State *l)
return 1;
}

// functions from ShipAiCmd.cpp
extern int CheckCollision(DynamicBody *dBody, const vector3d &pathdir, double pathdist, const vector3d &tpos, double endvel, double r);
extern double MaxEffectRad(Body *body, Propulsion *prop);

// for debug purposes
static SystemBody *find_child_by_name(SystemBody *sb, const std::string &name)
{
if (sb->GetName() == name) return sb;
if (sb->HasChildren())
for (auto b : sb->GetChildren()) {
SystemBody *fb = find_child_by_name(b, name);
if (fb) return fb;
}
return nullptr;
}
/*
* Function: PutShipOnTheRoute
*
* The ship rearranged from its current position to a given body in space, for a
* given part of the path, as if it were flying in a straight line, consuming
* fuel and changing speed. the ship's current speed is ignored and is
* considered to be equal to target's.
* This function changes the coordinates, the mass of fuel in the tank, and the
* speed of the given ship.
*
* > Space.PutShipOnTheRoute(ship, targetbody, t_ratio)
*
* Parameters:
*
* ship - a <Ship> object to be moved
*
* targetbody - the <Body> - route goal
*
* t_ratio - route completion rate, by time: 0.0 (begin) ... 1.0 (end)
*
* Return:
*
* nothing
*
* Examples:
*
* > -- move a ship to the middle of the route (by time)
* > Space.PutShipOnTheRoute(ship, some_staport, 0.5)
*
* Availability:
*
* 2020
*
* Status:
*
* experimental
*/

static int l_space_put_ship_on_the_route(lua_State *l)
{
LUA_DEBUG_START(l);
Ship *ship = LuaObject<Ship>::CheckFromLua(1);
const Body *targetbody = LuaObject<Body>::CheckFromLua(2);
const double t_ratio = LuaPull<double>(l, 3);
const ShipType *st = ship->GetShipType();
const shipstats_t ss = ship->GetStats();

const vector3d route = targetbody->GetPositionRelTo(ship->GetFrame()) - ship->GetPosition();
PrecalcPath pp(
route.Length(), // distance
0.0, // velocity at start
st->effectiveExhaustVelocity,
st->linThrust[THRUSTER_FORWARD],
st->linAccelerationCap[THRUSTER_FORWARD],
1000 * (ss.static_mass + ss.fuel_tank_mass_left), // 100% mass of the ship
1000 * ss.fuel_tank_mass_left * 0.8, // multipied to 0.8 have fuel reserve
0.9); // braking margin
// determine the place of the ship on the route
pp.setTRatio(t_ratio);
ship->SetPosition(ship->GetPosition() + route.Normalized() * pp.getDist());
ship->SetVelocity(route.Normalized() * pp.getVel() + targetbody->GetVelocityRelTo(ship->GetFrame()));
ship->SetFuel((0.001 * pp.getMass() - ss.static_mass) / st->fuelTankMass);

ship->UpdateFrame();
const Frame *shipFrame = Frame::GetFrame(ship->GetFrame());
const Frame *targFrame = Frame::GetFrame(targetbody->GetFrame());

// check for collision at spawn position
const vector3d shippos = ship->GetPosition();
const vector3d targpos = targetbody->GetPositionRelTo(ship->GetFrame());
const vector3d relpos = targpos - shippos;
const vector3d reldir = relpos.NormalizedSafe();
const double targdist = relpos.Length();
const Body *body = shipFrame->GetBody();
const double erad = MaxEffectRad(body, ship->GetPropulsion());
const int coll = CheckCollision(ship, reldir, targdist, targpos, 0, erad);

if (coll) {
// need to correct positon, to avoid collision
if (Frame::GetFrame(shipFrame->GetNonRotFrame()) != Frame::GetFrame(targFrame->GetNonRotFrame()) || targpos.Length() > erad) {
// the ship is not in the target's frame or target is above the effective radius of obstructor - rotate the ship's position
// around the target position, so that the obstructor's "effective radius" does not cross the path
// direction obstructor -> target
const vector3d z = targpos.Normalized();
// the axis around which the position of the ship will rotate
const vector3d y = z.Cross(shippos).Normalized();
// just the third axis of this basis
const vector3d x = y.Cross(z);
// this is the basis in which the position of the ship will rotate
const matrix3x3d corrCS = matrix3x3d::FromVectors(x, y, z);
const double len = targpos.Length();
// two possible positions of the ship, when flying around the obstructor to the right or left
// rotate (in the given basis) the direction from the target to the obstructor, so that it passes tangentially to the obstructor
const vector3d safe1 = corrCS.Transpose() * (matrix3x3d::RotateY(+asin(erad / len)) * corrCS * -targpos).Normalized() * targdist;
const vector3d safe2 = corrCS.Transpose() * (matrix3x3d::RotateY(-asin(erad / len)) * corrCS * -targpos).Normalized() * targdist;
// choose the one that is closer to the current position oh the ship
if ((safe1 + relpos).Length() < (safe2 + relpos).Length())
ship->SetPosition(safe1 + targpos);
else
ship->SetPosition(safe2 + targpos);
} else {
// we are in target's frame, and target below the effective radius of planet. Position the ship direct above the target
ship->SetPosition(targpos + targpos.Normalized() * targdist);
}
// update velocity direction
ship->SetVelocity((targpos - ship->GetPosition()).Normalized() * pp.getVel() + targetbody->GetVelocityRelTo(ship->GetFrame()));
}
LUA_DEBUG_END(l, 1);
return 0;
}

/*
* Function: SpawnShipNear
*
* Create a ship and place it in space near the given <Body>.
*
* > ship = Space.SpawnShip(type, body, min, max, hyperspace)
* > ship = Space.SpawnShipNear(type, body, min, max, hyperspace)
*
* Parameters:
*
Expand Down Expand Up @@ -183,10 +311,10 @@ static int l_space_spawn_ship(lua_State *l)
* Examples:
*
* > -- spawn a ship 10km from the player
* > local ship = Ship.SpawnNear("viper_police_craft", Game.player, 10, 10)
* > local ship = Space.SpawnShipNear("viper_police_craft", Game.player, 10, 10)
*
* > -- spawn a ship 10km from the player with the players velocity
* > local ship = Ship.SpawnNear("viper_police_craft", Game.player, 10, 10, nil, Game.player:velocity)
* > local ship = Space.SpawnShipNear("viper_police_craft", Game.player, 10, 10, nil, Game.player:velocity)
*
* Availability:
*
Expand Down Expand Up @@ -239,7 +367,6 @@ static int l_space_spawn_ship_near(lua_State *l)
}

thing->SetFrame(newframe->GetId());
;
thing->SetPosition(newPosition);
thing->SetVelocity(newVelocity);
Pi::game->GetSpace()->AddBody(thing);
Expand Down Expand Up @@ -360,7 +487,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;
Expand Down Expand Up @@ -476,7 +603,7 @@ static int l_space_spawn_ship_landed(lua_State *l)
* Example:
*
* > -- spawn a ship 10km from the player
* > local ship = Ship.SpawnShipLandedNear("viper_police", Game.player, 10, 10)
* > local ship = Space.SpawnShipLandedNear("viper_police", Game.player, 10, 10)
*
* Availability:
*
Expand Down Expand Up @@ -720,11 +847,11 @@ void LuaSpace::Register()
{ "SpawnShipParked", l_space_spawn_ship_parked },
{ "SpawnShipLanded", l_space_spawn_ship_landed },
{ "SpawnShipLandedNear", l_space_spawn_ship_landed_near },
{ "PutShipOnTheRoute", l_space_put_ship_on_the_route },

{ "GetBody", l_space_get_body },
{ "GetBodies", l_space_get_bodies },


{ "DbgDumpFrames", l_space_dump_frames },
{ 0, 0 }
};
Expand Down

0 comments on commit 6bab35d

Please sign in to comment.