109 Commits

Author SHA1 Message Date
chexit e7934a1ebb Give the enemy a structured intent and a durable route queue (#127)
build / mod (push) Failing after 22s
release / windows (push) Failing after 2s
Stage 2 of #127. A smart drone now carries why it is aimed where it is
(Intent: plan, sead, react, replace) apart from the aim bookkeeping
(unit, since, switches, confidence). Assignments are throttled per
adaptive tick and per contact, a fresh disclosure replaces an aim only
after a hold, within a switch cap and when the change is worth a new
route, and a killed aim is replaced as a separate, lower-priority
decision. The queue is bounded and taken by priority (plan > sead >
react > replace); unknown land is no longer free in a replanned route.

The autosave now carries the air state: the drones in flight, the launch
queue, every pending job with its id, aim, unit, version, priority,
intent and relief version, the contacts the jobs were chosen from and
the relief snapshots they plan over. The format is a versioned `air 1`
section; older saves read as before, scenarios do not carry the sky,
stale, ghost and duplicate jobs are cancelled on load, and the section is
bounded (512 drones, 512 route points, 4096 contacts, 8 reliefs, 900 KB).
A periodic save waits while a raid is only being planned.

enemy_picture() now explains targets and pending jobs (aims, aimed_by,
pending_jobs, reason, work_per_tick) in English, and intel_bench records
the queue and intent evidence next to completion, losses and failures.
2026-11-06 18:36:20 +03:00
chexit 8fc10c07a1 Give the enemy a last-observation picture and observed-capability pricing (#127) 2026-11-06 18:00:08 +03:00
chexit 1261c30724 Stage the cold launch and spool every interceptor's motor (#129)
The interceptor's launch is one sequence: the cap opens, the round is
thrown out unlit and slows under linear gas drag (`cold` ticks), the motor
lights where it has reached, it turns with a growing rate while thrust
spools up (`boost` ticks), and full guidance starts only at `cold + boost`.
A rail launch lights at once but spools over the same `boost` ticks; the
electric P1-SUN has no rocket spool or plume.

S-400/500 now hold the unlit ejection for 20/24 ticks (1.00/1.20 s at
20 Hz); IRIS-T/Tor keep shorter 8-tick throws. Ignition, its sound, flame
and recorded MotorIgnition event all use the missile's current position.
The RM48U strike follows the same 20-tick choreography, counts the throw
in its flight time and starts its recorded smoke at ignition.

Maximum speed, turn, accel, pk and every collision rule are unchanged.

Co-Authored-By: Kimi Code <noreply@kimi.com>
2026-11-06 13:35:35 +03:00
chexit a592ece6e3 fixup! Keep unfinished retarget routes in a bounded job queue (#127)
build / mod (push) Failing after 22s
The planner caches were keyed by (kind, relief Arc pointer, start, aim): two jobs with the same relief but different known-units snapshots could reuse each other's price grid and route. Key relief sampling by its Arc pointer but key prices and routes by the whole knowledge version: adaptive tick plus the known-units and relief Arc pointers. Regression creates two same-start, same-aim jobs on one relief with different known snapshots and checks their routes differ, while a third job of one version still reuses the route cache. Core release, adapter and WASM contract pass.
2026-11-06 12:57:36 +03:00
chexit a47812aadd fixup! Keep unfinished retarget routes in a bounded job queue (#127)
The relief snapshot was still global: a smart plan finishing while a retarget job waited could replace Sim::known_relief, so the pending route was planned on the newer map. RouteJob now carries Arc<KnownRelief> for its knowledge version; Sim::known_relief keeps the same Arc only to seed new jobs. retarget_relief reads the job's snapshot, and relief sampling, price grids and route caches are keyed by that version. The new regression makes a job wait, swaps the global relief for another grid with a wall, and checks the pending route and sample stay those of the original version; outside its grid is Unknown. Core release, adapter and WASM contract pass.
2026-11-06 12:57:36 +03:00
chexit 6f7b041c8b Keep unfinished retarget routes in a bounded job queue (#127)
A mass retarget used to clear every selected drone's route before the planner's per-tick work ran out: the rest flew straight for ever. Route jobs now carry the drone id, new aim, target unit, knowledge version and the intel snapshot, stay in a FIFO across ticks and are drained under the same WORK_PER_TICK. An old safe route is kept until its replacement is ready; a cancelled job falls back to a straight run. Jobs are replaced in place per drone, cancelled when the drone or unit is gone, and retarget routes reuse the original plan's known relief. An in-flight drone no longer turns to a unit behind its turning circle. Core release tests, adapter tests and the real WASM contract pass.
2026-11-06 12:57:36 +03:00
chexit dc36d67eb7 Unblock supply trucks from an impossible RM48U return (#128)
build / mod (push) Failing after 21s
step_supply sent every free truck into the return branch whenever any
S-400 had a pending ground_return, even when that return could not
proceed (the system moving, damaged, reloading or preparing, its round
already claimed by another truck, the route marked unreachable, or no
depot with room). The branch then found no target, cleared the job and
stopped the truck, so independent typed supply requests starved.

The return now keeps its priority only while it is executable: a
carrier holding a reserved round looks for a depot with room in reach,
an empty one for a system ready to hand a round over. Otherwise the
claim stays untouched and only real RM48U demand (a requesting system
with depot stock, or ground rounds aboard for one) pulls the truck
into the typed supply branch; without such demand it holds its watch
exactly as before. Redirecting a reserved round to a waiting request
clears return_cargo once the last one leaves the truck, and a stale
return_cargo with an empty hold no longer sends the truck to a depot
handover it cannot complete.

Regressions: three sim tests (moving claimant, pickup claimed by
another truck with damaged/save-restore phases, unreachable route) and
one full-drive lib test with a mid-flight state round trip; the rounds
are conserved in every phase and the actual return completes once the
claimant is back. All three sim tests fail on the old code.

Checks: core release 200 passed/8 ignored + 8 scenarios; adapter
67 unit + 1 boundary + 5 scenarios; real WASM test-wasm 30 steps.
2026-11-06 11:11:59 +03:00
chexit d45629ae29 Fix the UI-event fuel trap on a second large raid order
build / mod (push) Failing after 22s
The Windows log for #122 showed the mod stopping in the UI event
(fuel budget 1e9, remaining 0) with known_units <- order_raid <-
launch_wave <- ui_select: order_raid recomputed the planner snapshot
known_units() for every drone, and the snapshot itself rescanned the
whole force and the dedup list per intel entry. A second big Smart
order cost drones x intel-log x force-size inside one event.

Reproduced in actual WASM with measured per-callback fuel through the
exact ui_select(BTN_LAUNCH) handler (diagnostic guest, trigger.patch):
before, a 111-drone second order at revealed=13172/known=144 exhausted
exactly 1e9 and stopped the mod (an 11-drone order at the same state:
247805865); after, the same scenario passes with max command fuel
32946177/1e9, failures=0, and /stats at tick 2602 byte-identical.
A stacked single-point layout dies to one warhead (known=0, cheap
order), so the scenario deploys spread clusters with a moved player.

Fix: one known_units snapshot per order_raid passed to radar_strikes
and choose_target; known_units is a single O(log + force) set pass;
price_grid_on reuses its snapshot. Emitter/network targeting and
carrier choice semantics are unchanged (native equivalence test
against the former predicate; WASM checkpoint counters equal).
Limits unchanged; HD/recording untouched; wave size unrestricted.

Committed regressions: native snapshot-equivalence and
second-smart-order tests; the heavy WASM fixture now orders two Smart
raids through the wave screen's launch function (airdef preset +
airdef raid) with EW and a combat intel log, four accepted orders
asserted; the light contract covers preset and the empty-wave refusal.

Core release 195 + 8 scenarios; adapter 65 + 1 boundary + 5 scenarios;
light WASM 30 steps; heavy WASM 283 steps (tick max 182413686/300M,
command max 6939810/1e9, failures 0). Windows GUI acceptance remains
external and is not claimed.

Co-Authored-By: Kimi Code (Moonshot AI) <noreply@moonshot.cn>
2026-11-06 10:29:36 +03:00
chexit 9217f9a5cc Report camera readiness in /airdef capabilities
Pin the engine at 6a53a626c (API 30) and show render.camera-status: the
calling run's set-views cameras, whether the last noted frame was a world
frame, the request's world generation, each camera's reason and the run's
own main-view selection. As the other lines, this is a host milestone:
availability, request, preparation and why, not a GPU frame or a visible
picture. A unit test pins the line: readiness stays per camera and apart
from the run's main-view selection.

docs/ENGINE.md, the 4 November continuation and the status table now say
what prepared means (a picture encoded into the camera's target, or a
world frame drawn from it as this run's screen view, only for the run
whose selection the screen used), the per-run/per-world resets, and what
remains: GPU acceptance.

Verified: python3 tools/airdef.py check (gitlink, packer, mod needs 30,
fork 30, game 30); test-wasm 27 steps; airdef-mod 65 unit, 1 boundary and
5 scenarios; python3 -m unittest discover -s tools 28 tests.

Co-Authored-By: Kimi Code <noreply@moonshot.cn>
2026-11-04 21:08:23 +03:00
chexit 46d2274c8d Measure the #120 balance grid across batteries, ranges and target classes
Wider same-seed sampling of the finite-burn/coast change: baseline 2689d93
(pre-#120) against the current tree, 6 variants x 8 seeds of a 46-drone raid
(batt10/20/50, near200, far1500, hills20). Harness-only: Result carries the
tally's fuse/expired/orphaned/energy_lost counts and a new ignored
sim::balance::issue_120_grid test; no simulation parameters changed.

Findings: kills are equal or up and leaks equal or down in every variant;
fuse misses drop (far1500 70 -> 38 over 8 seeds); hills terrain losses fall
(scrubbed 6 -> 2). MotorEnergyLost never fires in the 48 scenarios and
expired stays <= 1: coast ends before drag reaches the 25% guidance
threshold. Leaks stay concentrated in the ballistic/fast classes
(Iskander/Kinzhal/Kh22/Zircon). No confirmed defect, so no tuning.

Full airdef-core suite: 193 + 8 passed, 0 failed (baseline side 169 + 4).

Co-Authored-By: Kimi Code <noreply@moonshot.cn>
2026-11-04 21:03:01 +03:00
chexit 0937ac9e05 fix: keep the P1-SUN motor voice at its played 1.6 pitch
The old engine_voice returned early for flame == 0 with
(DRONE_LANCET, 0.8, 1.6), so the later `P1sun => 1.7` match arm was
unreachable. The catalog migration mistook that arm for the played value
and stored 1.7, which changed the interceptor drone's flight sound.
specs.json is back to 1.6 and specs_generated.rs is regenerated.

Adds missile_engine_voices_keep_their_played_values, pinning P1-SUN to
(DRONE_LANCET, 0.8, 1.6) and M48N6 to (MISSILE_FLIGHT, 1.0, 0.88).

The continuation and handoff text now describe the early return instead
of the dead arm, and tools/airdef_assets/catalog.py gets its final
newline back.

Verified: gen_catalog --check and test_gen_assets, core release
195 passed + 8 scenarios (7 ignored), adapter 64 unit + 1 boundary +
5 scenarios, WASM contract 27 steps.
2026-11-04 21:00:35 +03:00
chexit ead87343d2 feat: generate simulation numeric profiles from catalog/specs.json
catalog/specs.json is now the single copy of the numeric rows for
DroneSpec, MissileSpec and SamSpec: speed, guidance, warheads, models'
parts, RCS, seekers, launch and motor sounds, mount drives, gravity,
the Oreshnik split height and logistics limits, plus the named part
sets. tools/gen_catalog.py generates airdef-mod/src/sim/specs_generated.rs
(included by mod.rs, so the mod and airdef-core compile the same rows)
and unit_specs_generated.rs (included by units.rs). The hand-written
spec matches are gone; DroneKind::spec, MissileKind::spec and
SamKind::spec read the generated constants.

load_specs validates the schema, sound references, kind coverage against
content.json, named-part references and unused rows. gen_catalog.py
--check already runs in the CI generated-assets step, so a JSON/Rust
drift fails the build.

Every value was compared with the literals at 632061d field by field.
The only mismatch was the P1-SUN motor voice pitch in JSON (1.6 instead
of the actual 1.7, the flame == 0 branch in voices.rs); the JSON was
corrected and behaviour is unchanged. Save keys, raid order, RNG and
gameplay traces are untouched.

Verified: core release 194 passed + 8 scenarios (7 ignored), adapter
64 unit + 1 boundary + 5 scenarios, WASM contract 27 steps, catalog and
model generators fresh, asset/sound/model-bound/report tests pass.
2026-11-04 21:00:35 +03:00
chexit 675967ce42 End the show when its world closes and verify the MP4 lifecycle (#89)
A frame hook ends an active film or export when the world is gone. The game
stops ticking at the title screen but keeps rendering, so an export used to
keep recording the title with its playback clock frozen (a sec=10 request
produced 3898 frames) and went on after re-entering the world. Session
cleanup runs once with WorldChange and cancels the capture.

Real short exports on a scratch home and world (pin 57f8b2853, API 29,
Metal): h264+AAC both 2.07 s with non-silent audio, reported frames equal
ffprobe nb_frames; cancel, SDK refusal, encoder failure and restart all
leave no capture running; 1920x1080 works. The engine still leaves
*.audio.raw and *.ffmpeg.log behind on an encoder error (game/film.rs).

Co-Authored-By: Kimi Code <noreply@moonshot.ai>
2026-11-04 17:44:28 +03:00
chexit c1a595b773 feat: expose light and sound processing diagnostics with isolated cameras
build / mod (push) Failing after 21s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 16:19:06 +03:00
chexit cfae0c1253 Refine cruise silhouette and document model comparisons
build / mod (push) Failing after 19s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 16:09:14 +03:00
chexit 53669887dd Refine Lancet silhouette with swept X wings
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 16:09:14 +03:00
chexit daa9ed7cbc Use catalog sound identifiers throughout runtime
build / mod (push) Failing after 19s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 16:01:19 +03:00
chexit 1553976484 Share sound identifiers and metadata with resource generation
build / mod (push) Failing after 18s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 15:59:18 +03:00
chexit 0c870c10d5 Represent damage diagnostics as typed reasons
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 15:59:18 +03:00
chexit eea1b3692e Share aircraft and item catalog with asset generation
build / mod (push) Failing after 20s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 15:55:45 +03:00
chexit cb73cb7ccc Move world cache report to presentation adapter
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 15:55:45 +03:00
chexit 375fdf2d80 Separate battery status from player-facing text
Co-Authored-By: Codex <noreply@openai.com>
2026-11-04 15:55:45 +03:00
chexit ab737e7390 test: verify empty nearby depot feedback in WASM
build / mod (push) Failing after 18s
Co-authored-by: Codex <codex@openai.com>
2026-11-03 11:34:02 +03:00
chexit cbd6067e8f fix: explain empty strike stock and ground attack prerequisites
build / mod (push) Failing after 19s
Show typed depot cargo separately, identify an empty nearby depot, and document loading, reconnaissance, two ID namespaces and reverse pickup.

Co-authored-by: Codex <codex@openai.com>
2026-11-03 11:26:52 +03:00
chexit 1f28f8361f Bound height cache invalidation per explosion
Co-authored-by: Codex <codex@openai.com>
2026-11-03 11:23:47 +03:00
chexit 3afebae6bd Cover HD grid during overlapping raids
Co-authored-by: Codex <codex@openai.com>
2026-11-03 10:43:01 +03:00
chexit 326817db9a Add overlapping raids budget scenario with flat terrain
Co-authored-by: Codex <codex@openai.com>
2026-11-03 10:43:01 +03:00
chexit a41a4d17cf Verify sequential raid requests in budget scenario 2026-11-03 10:32:31 +03:00
chexit 692ae77af7 Slightly densify missile smoke trails
build / mod (push) Failing after 18s
Co-authored-by: Codex <codex@openai.com>
2026-11-03 10:25:56 +03:00
chexit 910f02ca92 feat: consume scoped engine capabilities and preserve safe fallbacks
build / mod (push) Failing after 19s
Pin API27, diagnose prepared states, gate capture ownership and keep far-sound fallback until acoustics is prepared. Retain engine changes in build provenance and cancel invalid return reservations after cell loss.

Co-authored-by: Codex <codex@openai.com>
2026-11-03 00:24:46 +03:00
chexit 4dddea2e8c Add opt-in heavy WASM budget tier
Co-authored-by: Codex <codex@openai.com>
2026-11-03 00:23:25 +03:00
chexit b0085cb3e8 Test interrupted return pickup
Co-authored-by: Codex <codex@openai.com>
2026-11-03 00:18:44 +03:00
chexit e9fbaba1d6 Preserve pending delivery and command completion behavior
Co-authored-by: Codex <codex@openai.com>
2026-11-03 00:18:44 +03:00
chexit bb931ab95c Return loaded RM48U to depot by truck
Co-authored-by: Codex <codex@openai.com>
2026-11-03 00:18:44 +03:00
chexit 6e2d661b39 test: exercise radar silence through the real WASM boundary
build / mod (push) Failing after 18s
Co-authored-by: Codex <codex@openai.com>
2026-11-03 00:02:52 +03:00
chexit a7b8cb980a Apply coast drag after interceptor burnout
Co-authored-by: Codex <codex@openai.com>
2026-11-03 00:02:50 +03:00
chexit ce76f20074 Target SEAD strikes at observed radar contacts
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:59:11 +03:00
chexit e38ca0d9ab Match impact speed to swept segment
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:59:11 +03:00
chexit e3ca3fbd8c Release interceptor engine source at burnout
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:59:11 +03:00
chexit 44a023006d Use interceptor energy for residual fuse chance
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:59:11 +03:00
chexit f3985d5363 Record energy loss and exercise meeting geometry
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:59:11 +03:00
chexit 6b6293f353 Cover lead for crossing target
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:59:11 +03:00
chexit 932a8b7761 Use swept meeting point for interceptor impact
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:59:11 +03:00
chexit 143d9b2858 Prototype finite interceptor motor and coast
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:58:54 +03:00
chexit b377a5d8d2 test: preserve delivery requests alongside radar emission saves
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:58:15 +03:00
chexit 9e1e0f5543 Update network risk fixture for observed radar
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:56:52 +03:00
chexit 88f940546b Test radar silence and replay seeking
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:56:52 +03:00
chexit 0df225a2de Observe emitting radars and expose emission control
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:56:52 +03:00
chexit 26b258f00f Add persisted radar emission switch
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:56:52 +03:00
chexit 952a10f360 Exercise strike delivery across movement and save restore
build / mod (push) Failing after 18s
Co-authored-by: Codex <codex@openai.com>
2026-11-02 23:55:56 +03:00