Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/rules/ini_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ impl IniSection {
/// `FMUL double ptr [0x007E1710]` at `0x0047464C`, where that address holds
/// `0x4070000000000000` = `256.0`, then `Math__ftol @ 0x007C5F00`, whose
/// control word `0x0E7F` selects chop. `f64 as i32` truncates toward zero
/// like it does, and NOT `util::sim_to_i32`, which floors toward −∞ (DRIFT
/// on negatives, ledger #18).
/// like it does. Fixed-point `to_num::<i32>()` instead floors toward −∞;
/// it is not interchangeable with this reader's chop conversion.
///
/// Two residuals at the extremes, both out of reach of retail data:
///
Expand Down
335 changes: 191 additions & 144 deletions src/sim/aircraft/drop_payload.rs

Large diffs are not rendered by default.

38 changes: 17 additions & 21 deletions src/util/fixed_math.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! Deterministic fixed-point math for the simulation layer.
//!
//! All sim-critical arithmetic uses `SimFixed` (`I16F16`) instead of `f32`.
//! This guarantees identical results across CPU architectures, compilers,
//! and optimization levels — required for lockstep multiplayer and replays.
//! `SimFixed` (`I16F16`) is the default for fractional simulation quantities.
//! Integers and wider intermediates serve other ranges; selected mechanisms
//! preserve native rounding through the shared numeric owners. Determinism also
//! requires explicit conversion/overflow semantics, RNG and update ordering.
//!
//! ## Type
//! - `SimFixed` = `FixedI32<U16>` — 16 integer bits, 16 fractional bits
Expand All @@ -18,8 +19,8 @@ use fixed::types::I16F16;
/// Backed by `FixedI32<U16>` (aliased as `I16F16` in the `fixed` crate).
///
/// Range: −32768.0 to +32767.99998. Precision: ~0.000015 (1/65536).
/// Sufficient for cell coordinates (0–511), speeds (0–60), altitudes (0–1200),
/// damage multipliers (0.0–2.0), and timer durations (0–30s).
/// Choose units and bound intermediate values for each mechanism. Absolute
/// world leptons on large maps do not fit; Location stores cell plus sub-cell.
pub type SimFixed = I16F16;

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -68,12 +69,6 @@ pub const SIM_TICK_HZ: u32 = 45;
// Conversion helpers
// ---------------------------------------------------------------------------

/// Truncate `SimFixed` to `i32` (rounds toward zero).
#[inline]
pub fn sim_to_i32(val: SimFixed) -> i32 {
val.to_num::<i32>()
}

/// Convert `SimFixed` to `f32` for render-layer output.
/// Only use at the sim→render boundary, never within sim logic.
#[inline]
Expand Down Expand Up @@ -103,9 +98,10 @@ pub fn sim_from_f64(val: f64) -> SimFixed {
/// Euclidean distance for SimFixed values: `sqrt(dx² + dy²)`.
///
/// Widens to `I48F16` (i64-backed) for the intermediate `dx*dx + dy*dy` to
/// avoid I16F16 overflow when dx or dy exceed ~181. The result (a distance)
/// always fits back in SimFixed — e.g., max subcell delta 256 gives
/// `sqrt(256² + 256²) ≈ 362`, well within I16F16 range.
/// avoid I16F16 overflow when dx or dy exceed ~181. The caller must still
/// bound the final distance to SimFixed's range: widening the intermediate
/// does not widen the result. A subcell delta of 256 on each axis gives
/// `sqrt(256² + 256²) ≈ 362`, within that range.
pub fn fixed_distance(dx: SimFixed, dy: SimFixed) -> SimFixed {
use fixed::types::I48F16;
let dx_w = I48F16::from(dx);
Expand All @@ -127,14 +123,14 @@ pub fn fixed_distance(dx: SimFixed, dy: SimFixed) -> SimFixed {
SimFixed::from_bits(guess.to_bits() as i32)
}

/// Integer-based Euclidean distance: `sqrt(dx*dx + dy*dy)` returned as SimFixed.
///
/// Uses `i32` arithmetic for squaring to avoid I16F16 overflow on large maps.
/// SimFixed (I16F16) max integer is 32,767, so `dx * dx` overflows when `dx > 181`.
/// By computing in i32 (max ~2 billion), this handles maps up to ~46,000 cells.
/// Legacy whole-cell distance estimate used by Rocket movement.
///
/// The *result* (the distance itself, not the squared distance) always fits in
/// SimFixed — e.g., a 500x500 diagonal is ~707 cells, well within 32,767.
/// The caller must bound the squared sum to i32 and the result to SimFixed.
/// The fixed-count integer Newton iteration can end on either member of a
/// two-cycle (for example, sqrt(8) returns 3); this is not a floor-square-root
/// contract or native Rocket parity. Fractional distance is not retained.
/// Rocket's movement mechanism needs native validation before changing this
/// estimate, because its result controls flight progress and phase timing.
pub fn int_distance_to_sim(dx: i32, dy: i32) -> SimFixed {
let sum: i32 = dx * dx + dy * dy;
if sum <= 0 {
Expand Down
3 changes: 2 additions & 1 deletion src/util/native_trig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ fn advance_integer_coordinate(origin: i32, speed: i32, coefficient: u32) -> i32

/// Walk75C067..75C0CB and Fly4CDA84..4CDAE5 use this same full-direction math.
/// The caller chooses the direction (Walk's request, Fly's Primary.Current,
/// Hover's steering facing at 0x00514803..0x00514860).
/// Hover's steering facing at 0x00514803..0x00514860, or DropPayload's
/// wrapped +/-3FFF heading at 0x00415CC7/0x00415CEA).
/// Inputs/outputs are whole world leptons, with Z
/// and subsequent placement owned by the caller. Table products remain exact
/// at the simulation's bounded integer movement speeds; final truncation is
Expand Down
1 change: 1 addition & 0 deletions tools/native_oracle.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ These generators now default to **read-only checks**, also spelled `--check`:
| `tools.spatial_oracle.techno_rearm` | 267 original GetROF numeric/RNG cases with explicit virtual query substitutions; original RTTI leaves separately establish Unit1, Aircraft2, Building6, Infantry15 | Evidence for rearm migration; Unit delay and Building one-frame shortcut identities corrected; no complete production rearm parity claim |
| `tools.spatial_oracle.building_fire_turn` | 140 original voxel-building facing retry decisions using raw Type ROT; stops before Snap/GetFireError | `combat/combat_turret_facing_tests.rs` drives the production fire receiver against every decision |
| `tools.spatial_oracle.walk_direction_table` | All 65,536 heading words: original sine/cosine indexes and table bits, plus the complete retail table bytes | `src/util/native_trig.rs::tests::every_walk_heading_uses_the_original_trig_entries`; full-heading paid Walk displacement |
| `tools.spatial_oracle.paradrop_coordinates` | 393,216 original DropPayload coordinate prefixes: all 16-bit headings, both sides and three world origins; original full-word turn, trig and final-coordinate truncation | `sim/aircraft/drop_payload.rs` compares every native digest plus landing-cell/admission regressions. [Boundary and limits](spatial_oracle/paradrop_coordinates.md); excludes native downstream placement and complete mission parity |
| `tools.spatial_oracle.crate_speed_effect` | 23 original speed-crate recipient loops followed by live Foot speed queries; class/owner/factor gates, native 3-D distance and announcement flag | Native evidence for the open pickup/Foot speed dependency; no Rust production parity claim |
| `tools.spatial_oracle.flight_level` | 30 original type FlightLevel reads and effective-height queries, including the exact -1 fallback | `rules/object_type.rs`; Fly construction, attack recovery and paradrop carrier initialization use the resolved type value |
| `tools.spatial_oracle.fly_height` | 144 bounded original vertical steps with real GetHeight/SetHeight, Aircraft interface and type getter | `movement/fly_height.rs` compares all vectors; `world/fly_height_tests.rs` exercises healthy production height updates. Excludes complete Process admission, crash, descent drift and phase/Display transactions |
Expand Down
Loading
Loading