#include "shim/hooks/movement_inputs.h" #include #include #include #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 ShipRanges(const FleetStepSnapshot& s) { std::vector 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(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(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(r.shipRanges.size()); ++i) { r.shipRanges[static_cast(i)] = sots::sim::ConsumeShipRange(r.shipRanges[static_cast(i)], m.moved, s.ships[i].exempt != 0); } r.fraction = sots::sim::PassFraction(s.waypointType, r.distance, step); return r; } std::vector GateTraffic(const std::vector& fleets, int playerCount) { std::vector 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 PassSchedule(const std::vector& fleets) { std::vector 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 p, d; for (int i = 0; i < 3; ++i) p.push_back(tv::f32(static_cast(f.pos[i]))); for (int i = 0; i < 3; ++i) d.push_back(tv::f32(static_cast(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 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& totals) { std::vector items; for (int v : totals) items.push_back(tv::i32(v)); return tv::list(std::move(items)); } } // namespace shim::hooks