152 lines
5.4 KiB
C++
152 lines
5.4 KiB
C++
#include "shim/hooks/movement_inputs.h"
|
|
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <utility>
|
|
|
|
#include "game/sim/numeric.h"
|
|
|
|
namespace shim::hooks {
|
|
|
|
using sots::sim::JumpResult;
|
|
using sots::sim::MoveStepResult;
|
|
using sots::sim::Vec3;
|
|
using trace::Tv;
|
|
namespace tv = trace::tv;
|
|
|
|
namespace {
|
|
Vec3 vec_of(const double v[3]) { return Vec3{v[0], v[1], v[2]}; }
|
|
void store(double out[3], const Vec3& v) {
|
|
out[0] = v.x;
|
|
out[1] = v.y;
|
|
out[2] = v.z;
|
|
}
|
|
} // namespace
|
|
|
|
std::vector<double> ShipRanges(const FleetStepSnapshot& s) {
|
|
std::vector<double> r;
|
|
const int n = s.shipCount < kMaxTracedShips ? s.shipCount : kMaxTracedShips;
|
|
for (int i = 0; i < n; ++i) r.push_back(s.ships[i].range);
|
|
return r;
|
|
}
|
|
|
|
FleetStepResult StepFleet(const FleetStepSnapshot& s, double step, sots::sim::IRandom& rng) {
|
|
FleetStepResult r;
|
|
const Vec3 pos = vec_of(s.pos);
|
|
const Vec3 dest = vec_of(s.dest);
|
|
store(r.pos, pos);
|
|
r.shipRanges = ShipRanges(s);
|
|
if (s.heldFlag != 0 || s.waypointType < 0) return r; // held, or no waypoints: no move
|
|
|
|
switch (s.waypointType) {
|
|
case static_cast<int>(sots::sim::WaypointKind::ProbabilisticJump): {
|
|
const JumpResult j = sots::sim::RollProbabilisticJump(s.castEfficiency,
|
|
s.castThreshold, rng);
|
|
r.rngDraws = j.draws;
|
|
r.fraction = 1.0; // a type-5 waypoint never recurses
|
|
if (j.arrived) {
|
|
store(r.pos, dest);
|
|
r.arrived = true;
|
|
} else {
|
|
// The miss scatters the fleet around the destination in a random direction.
|
|
// The direction is a second draw whose mapping this milestone does not model,
|
|
// so the position is left where it was and the scatter distance is reported.
|
|
r.moved = j.scatter;
|
|
}
|
|
return r; // no fuel is charged and no clamp applies
|
|
}
|
|
case static_cast<int>(sots::sim::WaypointKind::GateTeleport):
|
|
store(r.pos, dest);
|
|
r.arrived = true;
|
|
r.fraction = 1.0;
|
|
return r; // no fuel, no clamp
|
|
default:
|
|
break;
|
|
}
|
|
|
|
r.distance = sots::sim::Distance(pos, dest);
|
|
const double minRange = sots::sim::FleetMinShipRange(r.shipRanges, 0.0);
|
|
const MoveStepResult m = sots::sim::ResolveMoveStep(step, minRange, r.distance);
|
|
r.moved = m.moved;
|
|
r.range = m.range;
|
|
r.arrived = m.arrived;
|
|
r.stranded = m.stranded;
|
|
store(r.pos, m.arrived ? dest : sots::sim::AdvanceAlongDirection(pos, dest, m.moved));
|
|
|
|
for (int i = 0; i < static_cast<int>(r.shipRanges.size()); ++i) {
|
|
r.shipRanges[static_cast<std::size_t>(i)] =
|
|
sots::sim::ConsumeShipRange(r.shipRanges[static_cast<std::size_t>(i)], m.moved,
|
|
s.ships[i].exempt != 0);
|
|
}
|
|
r.fraction = sots::sim::PassFraction(s.waypointType, r.distance, step);
|
|
return r;
|
|
}
|
|
|
|
std::vector<int> GateTraffic(const std::vector<FleetSummary>& fleets, int playerCount) {
|
|
std::vector<sots::sim::GateTrafficEntry> e;
|
|
e.reserve(fleets.size());
|
|
for (const FleetSummary& f : fleets) {
|
|
sots::sim::GateTrafficEntry g;
|
|
g.ownerIndex = f.ownerIndex;
|
|
g.waypointType = f.waypointType;
|
|
g.traffic = f.gateTraffic;
|
|
e.push_back(g);
|
|
}
|
|
return sots::sim::GateTrafficTotals(e, playerCount);
|
|
}
|
|
|
|
std::vector<sots::sim::MovementPass> PassSchedule(const std::vector<FleetSummary>& fleets) {
|
|
std::vector<sots::sim::FleetMovementEntry> e;
|
|
e.reserve(fleets.size());
|
|
for (const FleetSummary& f : fleets) {
|
|
sots::sim::FleetMovementEntry x;
|
|
x.fleetId = f.fleetId;
|
|
x.targetFleetId = f.targetFleetId;
|
|
x.relation = f.relation;
|
|
e.push_back(x);
|
|
}
|
|
return sots::sim::PlanFleetMovement(e);
|
|
}
|
|
|
|
Tv DescribeFleetStep(const void* data, std::size_t size, unsigned) {
|
|
Tv s = tv::struct_();
|
|
if (size < sizeof(FleetStepSnapshot)) return s;
|
|
FleetStepSnapshot f;
|
|
std::memcpy(&f, data, sizeof f);
|
|
s.add("fleet", tv::i32(f.fleetId));
|
|
s.add("owner", tv::i32(f.ownerIndex));
|
|
s.add("wpt_type", tv::i32(f.waypointType));
|
|
s.add("wpt_count", tv::i32(f.waypointCount));
|
|
s.add("dest_kind", tv::i32(f.destKind));
|
|
s.add("dt", tv::f32(f.dt));
|
|
s.add("speed", tv::f32(f.speed));
|
|
std::vector<Tv> p, d;
|
|
for (int i = 0; i < 3; ++i) p.push_back(tv::f32(static_cast<float>(f.pos[i])));
|
|
for (int i = 0; i < 3; ++i) d.push_back(tv::f32(static_cast<float>(f.dest[i])));
|
|
s.add("pos", tv::list(std::move(p)));
|
|
s.add("dest", tv::list(std::move(d)));
|
|
s.add("cst_e", tv::f32(f.castEfficiency));
|
|
s.add("cst_t", tv::f32(f.castThreshold));
|
|
s.add("ships", tv::i32(f.shipCount));
|
|
s.add("held", tv::i32(f.heldFlag));
|
|
s.add("rng_left_in", tv::i32(f.rngLeftIn));
|
|
s.add("fpu_cw", tv::u32(f.fpuControlWord));
|
|
const int n = f.shipCount < kMaxTracedShips ? f.shipCount : kMaxTracedShips;
|
|
std::vector<Tv> ranges;
|
|
for (int i = 0; i < n; ++i) {
|
|
Tv one = tv::struct_();
|
|
one.add("range", tv::f32(f.ships[i].range));
|
|
one.add("exempt", tv::boolean(f.ships[i].exempt != 0));
|
|
ranges.push_back(std::move(one));
|
|
}
|
|
s.add("ship_range", tv::list(std::move(ranges)));
|
|
return s;
|
|
}
|
|
|
|
Tv DescribeGateTraffic(const std::vector<int>& totals) {
|
|
std::vector<Tv> items;
|
|
for (int v : totals) items.push_back(tv::i32(v));
|
|
return tv::list(std::move(items));
|
|
}
|
|
|
|
} // namespace shim::hooks
|