OpenZT2 Functional Specification
OpenZT2 is a clean-room, native 64-bit reconstruction of Zoo Tycoon 2 built with Bevy, a data-oriented Rust game engine. Its completion target is a faithful, runnable game with the architecture and performance expected from a new implementation. A readable recovered C++ implementation serves as a behavioral oracle: together with shipped assets, live observation, and binary investigation, it establishes what the original game did. That evidence does not prescribe Rust types, ownership, update loops, or runtime structure.
The finished game must support the complete source-driven shell, map and game mode selection, terrain and camera semantics, construction, living animals, guests, staff, facilities, economy, persistence, progression, scenarios, and expansion systems. It also adds controller support, removes the original 32-bit mod ceiling, and supplies redo alongside undo. A package is complete only when its behavior is integrated into the playable game. Green status is reserved for packages that meet their functional, allocation, mapping, and GPU acceptance contracts in the assembled application.
Native content boundary
Original Z2F game archives, Extensible Markup Language (XML) files, media, downloadable content (DLC), and mods are inputs to a separate preflight application. Preflight resolves enabled-pack precedence, inheritance, aliases, dependencies, original formats, derived data, and target-machine choices before launching the game. It writes one resolved binary profile in the exact layouts consumed by the engine: flat processor tables, graphics-upload-ready payloads, bounded streaming blocks, stable identifiers, and direct lookup indexes. Later-pack override semantics therefore leave no work for gameplay.
Native records use versioned rkyv archives and small fixed representations
such as AssetId, TableRange, StringRange, and AssetRange. The game maps
the resolved profile once, validates each typed family once, and retains the
mapping through ordinary Bevy asset handles. Archived records are read in
place. Meshes, textures, shaders, materials, audio, animation, UI documents,
world definitions, terrain, navigation, and scenarios arrive in the form their
CPU systems or GPU upload paths require. Runtime source parsing, decompression
into an intermediate graph, mod merging, asset-instance hardcoding, and a
second decoded asset cache are outside the engine boundary.
PreloadMode::Off leaves paging demand-driven. Core faults the bootstrap,
menu, zoo, and resident groups within measured free-memory headroom.
AllEnabled extends that policy to every enabled on-demand and streaming group.
It never reaches an unselected pack because preflight has already produced the
single final profile.
Bevy ownership model
Bevy's entity-component-system (ECS) world owns the live game. Each animal, guest, member of staff, facility, habitat, job, objective, vehicle, photo, effect, and other world object is an entity with small components for its current facts. Resources are limited to genuinely global policy, clocks, immutable indexes, reusable scratch, and bounded coordination. Systems query the narrow component sets required for one job and communicate through typed messages only when an operation is genuinely asynchronous.
There is one ECS world and one lifecycle relation for loaded-zoo entities. There are no compatibility hosts, domain managers, shadow runtimes, generic property bags, copied session worlds, string-command dispatchers, or Rust objects that preserve the C++ assembly shape. Package boundaries allocate parallel authorship. Integration may merge, move, inline, rename, or delete package code until the result reads like one coherent Bevy game written by one team.
Simulation uses a fixed authoritative zoo clock and deterministic domain-local random streams. Saved ages, deadlines, durations, cooldowns, calendars, and random-number-generator state use integer ticks or explicit fixed-point units compiled by preflight. Floating-point time remains appropriate for input and presentation that is deliberately independent of simulation time.
Performance contract
Performance is a product requirement. Mapped asset lookup, archived traversal, and cloning a Bevy handle perform zero heap allocations and copy no underlying asset data. Settled gameplay systems target zero allocations. Collections, route buffers, spatial indexes, render extraction, dependency-owned particle capacity, decoder surfaces, and GPU staging reserve bounded capacity before the settled measurement window. Game-content filesystem reads stop after the mapping is installed; save/profile I/O remains an explicit separate boundary.
The regression suite measures three independent categories:
- Rust heap allocations with exact counting tests and Tracy memory events.
- Mapped-page residency and page faults through Linux process counters.
- GPU texture, buffer, staging, and render work through explicit accounting and Tracy/wgpu spans.
A package cannot become green because it compiles in isolation. Green requires its observable behavior, persistence, cleanup, native-data traversal, steady allocation limit, mapped-page behavior, and GPU-resource accounting to pass in the integrated game. When all package cards are green, the expected result is a runnable complete game whose ordinary scene transitions and native save/load paths avoid repeated conversion, copying, and data reprocessing.
Evidence and implementation method
Behavior is recovered just in time from the shipped data, readable C++ oracle,
analysis records, and live original. Commit d5657e5a may supply behavioral
leads and previously discovered source vocabulary. Its architecture was deleted
by 19dd222f and must not return. Unknown source semantics block the relevant
preflight schema or gameplay acceptance until the evidence identifies a typed
owner and representation.
Implementation proceeds through the package contracts below. Asset packages own one consumer-shaped native family, its preflight producer, its mapped Bevy asset, and focused contract tests. Gameplay packages own cohesive components, resources, messages, and systems under one Bevy plugin directory. Root integration owns manifests, module declarations, plugin groups, schedules, shared vocabulary, dependency changes, and destructive cleanup across package boundaries.
Library ownership
OpenZT2 buys mature commodity infrastructure and writes recovered zoo-game
behavior. docs/work-packages/LIBRARY-DECISIONS.md is normative for
every package and assigns physics, particles, codecs, file dialogs, mesh/shader
processing, and Bevy-native rendering/UI/input/audio facilities to their
off-the-shelf owners. Package workers implement only native conversion,
integration, policy, and original-game semantics around those owners. They must
not build a parallel solver, renderer, mixer, codec, layout engine, particle
engine, optimizer, device backend, or copied library world.
Library adoption does not relax the mapping or allocation contract. The game
still loads only the resolved native profile; one-time body/GPU/decoder setup is
bounded and measured; capacity-stable operation remains subject to Tracy and
hard allocation regressions. A failing mandatory dependency is configured,
patched, or narrowly forked before bespoke replacement is considered. The
audited pre-library design is preserved at commit 68a4c481 for that explicit
fallback only.
Package status
The published grid is generated from the 56 normative package briefs in
docs/work-packages/briefs/. A brief may declare one of these states
with a Status: <state> line after its title. Absence means brief.
brief: contract complete and ready to implement.active: implementation is in progress.implemented: code is integrated but its complete acceptance contract has not passed.verified: functional, persistence, allocation, mapping, GPU, and integrated gameplay acceptance has passed. This is the only green state.blocked: a named evidence or integration dependency prevents progress.
The package briefs are the detailed normative sections of this specification. They retain exact Rust contracts, ECS queries, binary layouts, evidence gates, behavior, and acceptance criteria so each package can be implemented in parallel without inventing an architectural boundary.
Evidence coverage gate
Package-boundary exhaustiveness is checked by
tools/audit-work-package-coverage.py. Its generated
docs/work-packages/EVIDENCE-CROSSWALK.tsv assigns every current
binary function identity, recovered type/vtable identity, C++ oracle file, and
official core/DLC archive entry to one implementation package or an explicit
external, preflight-only, Bevy-replaced, non-shipping, or binary-data
disposition. EVIDENCE-COVERAGE.md summarizes the result.
The audit must be regenerated whenever accepted evidence or package boundaries change, and publication rejects a stale or incomplete crosswalk. Passing means there is no known unowned domain. It is not a claim that unresolved formulas, edge cases, presentation details, or behaviors absent from current evidence have already been recovered; each owning brief retains those acceptance gates.
Detailed package contracts
A01 — Launcher
1. Identity and outcome
The separate openzt2-launcher executable lets a player choose a Zoo
Tycoon 2 installation, scan its archives, select any discovered core/DLC/mod
layers, convert the selected set into one resolved native profile, remove an
installed profile, choose Off, Core, or All enabled fast preload, and
launch OpenZT2. Core/DLC/mod presentation and progress are ordinary launcher
UI; no part enters the game process. Integration wave 6; it consumes the pack
assembler and the statically linked A02–A19 source frontends.
2. Exclusive ownership
The launcher package owns launcher/src/ in full. Everything else is read-only. Cargo members,
dependencies, icons, converter module wiring and game binary wiring are
root handoffs. In particular, do not edit converter/src/pack.rs or any family.
rfd 0.17 is mandatory for the native installation-directory picker and owns
all platform dialog integration. The worker must not implement a filesystem
browser, portal/GTK/Win32/Cocoa backend, or retain dialog state after selection.
Root adds the minimal platform features.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
| PreflightAppPlugin | plugin | unit struct implementing Plugin | Registers only launcher state/UI/jobs. |
| PreflightPhase | state enum | ChooseLocation, ChoosingLocation, Scanning, Verifying, Ready, Converting, Launching, Failed | One launcher phase; work disables conflicting browse/remove/play actions. |
| PackRole | enum | Core, Dlc, Mod | Classification established by scan evidence, never filename colour. |
| PreloadMode | shared native enum | openzt2_native::contract::PreloadMode::{Off, Core, AllEnabled} | Sole launcher→engine page-fault policy; AllEnabled never includes unselected packs. |
| ArchiveKey | value | pub struct ArchiveKey(pub AssetId) | Stable ID of normalized source-relative archive path. |
| SourceArchive | record | { key:ArchiveKey, source_path:PathBuf, display_name:String, role:PackRole, original_bytes:u64, content_hash:[u8;32], verification:ArchiveVerification, required_for_launch:bool } | Scan result; checksumming is an explicit verification phase and only the three evidenced core archives set required_for_launch. |
| InstalledProfile | record | { id:AssetId, path:PathBuf, source_fingerprint:[u8;32], optimized_bytes:u64, target:TargetProfile } | id is the verified native pack/profile identity, never a slot integer. |
| ConvertedArchive | record | { key:ArchiveKey, source_hash:[u8;32], cache_path:PathBuf, output_hash:[u8;32], optimized_bytes:u64, target:TargetProfile, converter_schema:u32 } | One verified per-source conversion cache; never loaded by engine directly. |
| ConversionCacheManifest | record | { version:u32, converted:BTreeMap<ArchiveKey,ConvertedArchive> } | Atomically persisted launcher cache index; a row is complete only after cache header/hash/source/schema/target validation. |
| ArchiveRow | component | { key: ArchiveKey } | UI row identity; visual state is derived. |
| ArchiveSelection | resource | { selected: BTreeSet<ArchiveKey> } | One selection drives conversion, deletion and launch inclusion; default empty. |
| DiscoveredArchives | resource | { rows:Vec<SourceArchive>, cache:ConversionCacheManifest, installed:Option<InstalledProfile> } | Bounded scan plus per-row conversion truth and final resolved output. |
| PreflightSettings | resource | { source_root:Option<PathBuf>, output_profile:PathBuf, preload:PreloadMode } | Persisted launcher preferences, not live game state. |
| ConversionProgress | resource | { completed_bytes: u64, total_bytes: u64, current: Option<ArchiveKey>, failure: Option<String> } | Progress is byte based and monotonically increases for one job. |
| scan_installation | function | fn scan_installation(root: &Path, target: TargetProfile) -> io::Result<Vec<SourceArchive>> | Pure scan; deterministic normalized ordering. |
| convert_selection | function | fn convert_selection(source:&[SourceArchive], selected:&BTreeSet<ArchiveKey>, target:TargetProfile, cache_root:&Path, progress:impl FnMut(ArchiveKey,u64,u64))->io::Result<Vec<ConvertedArchive>> | Converts only missing selected rows to atomic cache entries; caller persists manifest then assembles profile. |
| remove_converted_archive | function | fn remove_converted_archive(key:ArchiveKey, cache:&mut ConversionCacheManifest, resolved:Option<&InstalledProfile>)->io::Result<()> | Deletes that row cache, atomically persists manifest and invalidates/deletes resolved output. Core is not special. |
| assemble_resolved_profile | function | fn assemble_resolved_profile(source:&[SourceArchive], selected:&BTreeSet<ArchiveKey>, cache:&ConversionCacheManifest, destination:&Path)->io::Result<InstalledProfile> | Flattens selected valid conversions in resolved precedence order into sole engine profile. |
| launch_game | function | fn launch_game(executable:&Path, profile:&InstalledProfile, preload:PreloadMode)->io::Result<Child> | Passes exact --profile <absolute path> --profile-id <32 lowercase hex> --target-profile <u32> --preload <off|core|all-enabled> arguments; starts only with selected core. |
4. Data layout or ECS ownership
Launcher entities are presentation only. DiscoveredArchives is the bounded
scan catalogue; ArchiveSelection is the single selection truth; one task
entity owns any active conversion. No source archive bytes or converted asset
graphs are retained after conversion. UI systems derive row colour, enabled
buttons, original / optimized sizes and per-row progress bars. Convert is
enabled only when the unified selection contains at least one missing/stale row
and no job is active; a completed valid row cannot be reconverted. Remove is
enabled only when the selection contains converted rows
and disabled while converting. Play is disabled until all three core rows are
selected and every selected row has a valid conversion represented by the
current resolved profile. Once true, Play is green regardless of unselected
optional rows. When anything is unselected, the informational text is exactly
Unselected packs will not be loaded. Only the three core packs are required to launch.; it does not turn a valid core selection into an error.
View contents opens a modal archive viewer over an immutable snapshot of the
same unified selection. It reads each chosen row from its exact source archive,
uses the selected set only to resolve referenced dependencies, and cannot
change selection or operate during scan, verification, conversion, or launch.
The viewer shares the tooling-only source-format crate with conversion; it does
not install an archive as a Bevy asset source or enter the game dependency graph.
5. Preflight producer contract
Accept installation roots chosen by a native directory picker. Scan Z2F/source
archives read-only, recognize core identities by evidenced canonical paths and
hashes, classify DLC/mods, and compute hashes lazily during explicit Scan—not
app startup. Later selected layers override earlier ones in preflight; only the
winning consumer assets enter assemble_pack. Conversion is atomic, cancelable
between files, deterministic, and rejects missing core inputs, corrupt archives,
unsupported producer output, source/output hash mismatch, target/schema
mismatch, duplicate paths, cache-manifest inconsistency and destination
verification failure. A completed valid row cannot be reconverted until removed.
assemble_resolved_profile alone applies selected layer precedence; engine sees
only that final output.
6. Behavioral evidence and disposition
Start at ignored vendor/original-software/ archive names/manifests, old
reconstruction/game/src/bin/launcher.rs and converter sources in commit
d5657e5a, and current converter/src/pack.rs. Evidence questions: exact three
core archive identities; official expansion versus downloaded-content
classification; original launcher naming only where player familiarity matters.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Nothing is selected on first scan. Core rows are deselectable and deletable but
launch requires all three selected, converted, and present in the verified
resolved profile. A valid checksum/profile is shown complete
across restart; changed source/schema/target or invalid cache becomes stale.
Each row shows original_bytes / optimized_bytes; no single estimate is reused.
Selecting a converted row permits deletion, never reconversion. Removing it
also invalidates the resolved profile. Collapsible, scrollable
Core/DLC/Mods sections expose all rows. Closing/canceling never leaves a profile
that passes validation. The deliberate OpenZT2 option Fast preload is passed
as PreloadMode; this application does not fault pages itself.
8. Performance contract
No automatic full scan or conversion on open. Directory enumeration is O(n), hashing and conversion O(total selected bytes), bounded to one conversion job. UI frames do no filesystem access and allocate nothing after row hydration unless scan progress changes. Stream source files with reusable buffers; never hold all source archives in RAM. Report converter heap peak and bytes/sec; game heap/mapping/GPU counters are out of process.
9. Acceptance criteria
All source under the owned subtree is complete formatted Rust. The demonstrated flow is: pick location → scan → select three core plus one DLC → convert with bars → play → quit → restart → scan and observe four individually valid converted rows and the same valid resolved profile. Button states and warnings obey the rules above. Non-goals: original UI styling and any runtime source parsing. No stubs, hidden auto-conversion or fake completion.
10. Required handoff
Report files/API, UI/file-dialog dependencies, static source-frontend wiring and root workspace/bin registration, game executable/environment wiring, remaining archive identity evidence, and conversion throughput/peak heap. Root should erase any temporary compatibility adapter. Archive inspection is launcher functionality and must remain source-tooling-only rather than becoming a game runtime loader.
A02 — Compiled UI Document
1. Identity and outcome
This package produces the one native representation of mod-authored UI: flat layout/style/ binding/command tables that G02 projects directly into Bevy UI. XML templates, skins and command names are completely compiled before launch. Integration wave 1; depends on A05 textures, A09 localization and A12 system-font policy.
2. Exclusive ownership
This package owns native/src/families/ui_document.rs,
converter/src/families/ui_document.rs, game/src/assets/ui_document.rs, and
preflight/tests/ui_document_contract.rs. All other paths are read-only; module,
loader and dependency registration are root handoffs.
Bevy 0.19 UI is the mandatory runtime target and owns layout, clipping,
scrolling, picking, focus and widget interaction. A02 compiles only the flat
data needed to spawn ordinary Bevy Node, image, text and interaction
components. It must not define a parallel layout tree, widget engine, focus
engine, retained XML document, or generic UI runtime.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
UI_ARCHIVE_VERSION |
constant | u32 = 1 |
Bumped on incompatible layout/command changes. |
UiArchive |
archived root | { version:u32, target:TargetProfile, id:AssetId, role:UiDocumentRole, logical_size:[f32;2], strings:Vec<u8>, nodes:Vec<UiNodeRecord>, children:Vec<u32>, styles:Vec<UiStyleRecord>, bindings:Vec<UiBindingRecord>, commands:Vec<UiCommandRecord>, dependencies:Vec<AssetId> } |
Tables immutable; nodes preorder/root index 0; ID is role-derived for semantic screens. |
UiDocumentRole |
enum | Splash, MainMenu, ProfileSelect, Globe, MapSelect, Loading, Options, SavedGames, InGameHud, Modal, EntityInfo, PurchaseCatalogue, Zoopedia, Scenario, PhotoAlbum, ShowEditor; pub const fn native_asset_path(self)->&'static str |
Fixed engine discovery vocabulary and semantic native paths; complete corpus is evidence-gated before acceptance and unknown roles reject. |
UiNodeFlags |
bitflags u16 |
VISIBLE, ENABLED, CLIP_CHILDREN, MODAL, FOCUSABLE, POINTER_BLOCKING |
Exact live projection defaults. |
UiNodeRecord |
record | { id:AssetId, kind:UiNodeKind, parent:u32, children:TableRange, rect:[f32;4], anchors:[f32;4], style:u32, bindings:TableRange, commands:TableRange, flags:UiNodeFlags, z:i16, tab_order:i16 } |
TableRange indexes typed tables; u32::MAX parent means root. |
UiNodeKind |
enum | Root, Container, Image, Text, Button, Toggle, Slider, Scroll, List, Edit |
Exhaustive fixed projector vocabulary; any other source widget blocks conversion until this versioned enum is evidence-expanded. |
UiStyleFlags |
bitflags u16 |
NINE_SLICE, PRESERVE_ASPECT, WRAP_TEXT, ELLIPSIS, PRESSED_OFFSET |
Exact presentation behavior. |
UiStyleRecord |
record | { background:AssetId, font:AssetId, text_key:AssetId, color:[f32;4], font_px:f32, padding:[f32;4], border:[f32;4], flags:UiStyleFlags } |
Missing references are AssetId::default(). |
UiBindingRecord |
record | { binding:UiBinding } |
Property and value type are one closed enum; no source key/property dispatcher. |
UiBinding |
enum | Visible(BoolBindingSource), Enabled(BoolBindingSource), Text(TextBindingSource), Image(ImageBindingSource), Value(ScalarBindingSource), Minimum(i64), Maximum(i64), Selected(BoolBindingSource) |
G02 matches exhaustively and writes only the encoded Bevy property type. |
BoolBindingSource |
enum | Constant(bool), HasSelectedEntity, SelectedEntitySellable, ProfilePresent, AnimalDiseased, AnimalUnderTreatment, AnimalTranquilized, AnimalEscaped, AnimalRampaging, AnimalDead, StaffAssigned, StaffWorking, FacilityOpen, HabitatBreached, IsTank, ShowScheduled, ShowRunning, TransportCircuitClosed, TransportWaiting, TransportRiding, ScenarioRunning, ScenarioObjectiveSatisfied{objective:AssetId}, DefinitionUnlocked{definition:AssetId}, ResearchActive, AwardEarned{award:AssetId}, CatalogueRowVisible{row:u16}, CatalogueEntryUnlocked{row:u16}, CatalogueEntryAffordable{row:u16} |
Every variant names one precise changed live fact. |
TextBindingSource |
enum | Localized{key:AssetId,format:AssetId}, SelectedEntityName, SelectedEntityDefinitionName, AnimalWelfareBand, AnimalHealthStatus, AnimalDiseaseName, AnimalTreatmentName, GuestPhase, StaffRoleName, StaffAssignmentName, StaffCurrentJobName, FacilityServiceName, HabitatPrimaryBiomeName, HabitatBiomeName{row:u16}, ShowStateName, ShowCurrentTrickName, ShowSlotTrickName{slot:u16}, TransportCircuitName, TransportStationName, TransportVehicleName, TransportTripState, ZooCash{format:AssetId}, ZooFame{format:AssetId}, ZooRating{format:AssetId}, ZooGuestCount{format:AssetId}, ProfileName, ScenarioObjectiveText{objective:AssetId}, ScenarioObjectiveStatus{objective:AssetId}, ScenarioObjectiveProgress{objective:AssetId,format:AssetId}, ScenarioTimeRemaining{format:AssetId}, ResearchName, ResearchProgress{format:AssetId}, LatestAwardName, CatalogueEntryName{row:u16}, CatalogueEntryPrice{row:u16,format:AssetId}, ZoopediaTitle, ZoopediaBody |
AssetId fields identify localization/format/objective data only; row/slot fields index bounded preallocated UI rows. G21/domain presenters supply the named fact; no selected-field lookup bag. |
ImageBindingSource |
enum | Constant{texture:AssetId}, SelectedEntityIcon, ProfilePortrait, ScenarioObjectiveIcon{objective:AssetId}, CatalogueEntryIcon{row:u16}, ZoopediaImage |
Resolves through A05's ordinary Bevy image bridge. |
ScalarBindingSource |
enum | Constant(i64), AnimalHungerQ16Permille, AnimalThirstQ16Permille, AnimalRestQ16Permille, AnimalPrivacyQ16Permille, AnimalSocialQ16Permille, AnimalExerciseQ16Permille, AnimalStimulationQ16Permille, AnimalEnvironmentQ16Permille, AnimalKeeperCareQ16Permille, AnimalHealthNeedQ16Permille, AnimalWelfarePermille, AnimalVitalityPermille, GuestHungerPermille, GuestThirstPermille, GuestEnergyPermille, GuestRestroomPermille, GuestComfortPermille, GuestSatisfactionPermille, GuestEducationPermille, GuestVisitTicks, StaffWageCentsPerDay, StaffJobUrgency, StaffJobRemainingTicks, FacilityPriceCents, FacilityCapacityUsed, FacilityCapacityTotal, FacilityConditionPermille, FacilityWasteUnits, FacilityWasteCapacity, FacilityInventoryCount, FacilityInventoryCapacity, LitterAmountUnits, HabitatWasteAmountUnits, ZooCleanlinessPermille, HabitatAreaMilliSquareMetres, HabitatLandMilliSquareMetres, HabitatWaterMilliSquareMetres, HabitatBiomeAreaMilliSquareMetres{row:u16}, TankWaterQualityPermille, TankUsedLitres, TankRequiredLitres, ShowScheduleCount, ShowScheduleCapacity, ShowRemainingTicks, ShowSlotDurationTicks{slot:u16}, TransportStationQueued, TransportStationOccupied, TransportStationCapacity, TransportVehicleOccupied, TransportVehicleSeats, TransportTripScoreMilli, ZooCashCents, ZooAdmissionCents, ZooFameHalfStars, ZooRatingPermille, ZooGuestCount, ScenarioObjectiveCurrent{objective:AssetId}, ScenarioObjectiveTarget{objective:AssetId}, ScenarioTimeRemainingTicks, ResearchElapsedTicks, ResearchRequiredTicks, AwardEarnedTick{award:AssetId}, CatalogueEntryPriceCents{row:u16}, CataloguePage |
Exact integer unit is named by variant; row/slot fields index bounded reusable views. G21 performs the one explicit live-fact quantization where ECS storage is float, then G02 writes the value without reflection or unit inference. |
UiCommandRecord |
record | { trigger:UiTrigger, command:UiCommand } |
Commands are validated preflight; payload belongs to its exact enum variant. |
UiTrigger |
enum | Press, Change, Submit, Cancel, Focus, Blur |
Fixed event vocabulary. |
UiCommand |
enum | StartFreeform, OpenChallenge, OpenCampaign, OpenOptions, OpenDownloads, OpenSavedGames, Quit, SetVisible{node:AssetId,visible:bool}, SetValue{node:AssetId,value:i32}, Focus{node:AssetId}, Navigate{role:UiDocumentRole}, SelectEntity, SetTool{tool:AssetId}, Buy{definition:AssetId}, SellSelected, Adopt{species:AssetId}, Save, Load |
Native fixed vocabulary; every AssetId is a data target. G02 translates shell variants to Bevy GameAction; unsupported semantics reject conversion. |
MappedUiArchive |
owning view | tuple wrapper over MappedAsset; new(MappedAsset)->io::Result<Self>, archived(&self)->&ArchivedUiArchive |
Validates once and keeps mapping alive. |
access_ui / access_ui_validated |
functions | fn access_ui(&[u8])->io::Result<&ArchivedUiArchive>; unsafe fn access_ui_validated(&[u8])->&ArchivedUiArchive |
Validate version, target and every range/reference. |
AuthoredUiDocument |
producer input | { virtual_path:String, xml:Vec<u8> } |
Preflight-only source bytes. |
compile_ui_document |
function | fn compile_ui_document(input:&AuthoredUiDocument, target:TargetProfile)->io::Result<Vec<u8>> |
Pure deterministic producer. |
UiDocumentAsset |
Bevy asset | { archive:MappedUiArchive } deriving Asset, TypePath, Clone |
Never materializes node copies. |
UiDocumentLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; implements AssetLoader<Asset=UiDocumentAsset> for ozui |
Uses LoadContext::path() to acquire mapped range, not reader copying. |
UiDocumentAssetPlugin |
plugin | unit struct | Registers asset and loader only. |
4. Data layout or ECS ownership
Little-endian rkyv root at alignment required by rkyv; frozen TableRange
indexes typed records while AssetRange is reserved for payload bytes. Strings are UTF-8 in one blob and
addressed by StringRange in producer-private extensions; all tables use u32
indexes and stay below u32::MAX. IDs and dependency tables are sorted.
Rendering traverses archived nodes directly; textures/fonts are Bevy handles by
AssetId. Only texture/font payloads upload to GPU.
5. Preflight producer contract
Accept UI XML/BFXML documents, referenced templates, replacements/skins,
localization keys and image/font paths from selected resolved layers. Normalize
virtual paths to lowercase /-separated UTF-8 before AssetId::from_key.
Resolve includes, template expansion, skin replacements and later-layer
overrides; flatten hierarchy and intern strings; validate unique IDs, acyclic
templates, finite geometry, valid ranges, known tags/bindings/commands and
dependency closure. Semantic documents use AssetId::from_key("ui/role/<role>")
so G01/G02 discover handles without paths; later layers replace a role before
output. Reject duplicate winners or a missing required splash/main/profile/
globe/map/loading/HUD role, and unknown behavior-bearing commands with source
path/node. Deterministic order is document preorder then stable source order.
The worker must inventory the full shipped binding corpus before freezing code:
every animal, guest, staff, facility, habitat/tank, show, transport, scenario
and progression binding must map to one named variant above (or a reviewed
addition with an exact unit). Unknown fields fail conversion; there is no
selected-field ID, generic property key or fallback reflection path.
6. Behavioral evidence and disposition
Search shipped ui/**/*.xml, *.layout, skin/replacement documents and command
attributes; cpp-reconstruction UI/BFXML types via RTTI TSV; the specification;
and commit d5657e5a domain/.../ui_contract.rs,
presentation/ui/{commands,layout,projection,widgets}.rs. Live-check focus,
modal and coordinate scaling. Questions: complete document-role and tag/widget set, command
argument semantics, anchor/aspect behavior, animation representation and list
templates. Unknown commands must remain named evidence failures.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Templates and overrides yield one final tree. Stable node IDs survive mod
replacement; duplicate IDs in a resolved document fail. Layout preserves
authored pixel coordinates against logical_size, anchors and z/tab order.
Bindings exhaustively match typed live facts; commands emit fixed Bevy actions/requests. No
runtime XPath, XML DOM, string dispatch or asset-instance hardcoding. Mod UI may
use only the frozen node/binding/command vocabulary.
8. Performance contract
Load validates O(nodes+edges+records) once; lookup/traversal/handle clone are zero-allocation. Projection may allocate entities only on document activation; steady binding uses changed facts and indexed node entities. No per-frame strings/hash growth. Contract test counts zero allocations for archived preorder and command iteration; page-fault and mapped-byte tests distinguish mapping; texture/font GPU bytes belong to their families.
9. Acceptance criteria
The four owned files export every symbol above, convert a nested fixture with template/skin/binding/command references across every G21 presenter family, reject malformed cycles/ranges and unknown bindings/commands, and prove archived traversal returns source order without copying. Acceptance includes a documented full-corpus binding inventory with no unclassified source field. No G02 projection, XML dependency in native/Bevy, stubs or generic property bags.
10. Required handoff
Report XML/parser dependencies, module/asset/loader/plugin registration, the exact G02 projector/command mapping, references to A05/A09/A12, unresolved UI vocabulary, mapped allocation checks and any temporary field that integration should eliminate.
A03 — Species Catalogue
1. Identity and outcome
This package produces the complete immutable species, sex/age variant, compatibility and authored-biological catalogue consumed by animal gameplay without source XML or per-animal copies. It replaces the bootstrap species schema. Integration wave 3; depends on A09 localization and A14 world definitions.
2. Exclusive ownership
This package owns native/src/families/species.rs, converter/src/families/species.rs,
game/src/assets/species.rs, preflight/tests/species_contract.rs. Existing
native/src/species.rs and converter/src/species.rs are read-only migration
inputs; root moves/deletes them and registers modules/loaders.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
SPECIES_ARCHIVE_VERSION |
constant | u32 = 1 |
Native layout version. |
SpeciesArchive |
root | { version:u32, target:TargetProfile, strings:Vec<u8>, species:Vec<SpeciesRecord>, variants:Vec<SpeciesVariantRecord>, needs:Vec<SpeciesNeedRecord>, preferences:Vec<NeedPreferenceRecord>, compatibilities:Vec<SpeciesCompatibilityRecord> } |
species sorted by id; ranges index typed tables. |
SpeciesRecord |
record | { id:AssetId, name_key:AssetId, taxonomy_key:AssetId, world_definition:AssetId, variants:TableRange, needs:TableRange, adult_mass_kg:[f32;2], stage_start_ticks:[u64;4], lifespan_ticks:[u64;2], gestation_ticks:u64, litter:[u16;2], move_speed_mps:f32, swim_speed_mps:f32, appetite_per_tick_q16:i32, waste_definition:AssetId, waste_interval_ticks:u32, waste_units:u16, flags:SpeciesFlags } |
G32 ticks; stages monotonic/inside lifespan; appetite signed 16.16 permille/tick. Waste fields are all zero/absent together or an evidenced positive G36 production rule. |
SpeciesFlags |
bit flags | u32: MALE, FEMALE, SWIMS, FLIES, PREDATOR, PREY, SOCIAL, SOLITARY, ADOPTABLE, EXTINCT |
Data facts, not live state. |
SpeciesVariantFlags |
bitflags u8 |
DEFAULT, RARE, ALBINO, STERILE |
Exact variant facts; evidence expands enum before acceptance if corpus adds one. |
SpeciesVariantRecord |
record | { id:AssetId, species:AssetId, sex:Sex, life_stage:LifeStage, model:AssetId, material_variant:AssetId, scale:[f32;2], weight_kg:[f32;2], probability:u16, flags:SpeciesVariantFlags } |
Probability is 0..=65535 fixed weight. |
Sex |
enum | Female, Male, Any |
Any only on authored compatibility/variant fallback. |
LifeStage |
enum | Juvenile, Young, Adult, Elder |
Ordered lifecycle vocabulary. |
NeedKind |
enum | Hunger, Thirst, Rest, Privacy, Social, Exercise, Stimulation, Environment, KeeperCare, Health |
Evidence-derived animal vocabulary. Scan the complete selected corpus; an additional behavior-bearing need is a schema gate, never Custom or a string. |
SpeciesNeedRecord |
record | { kind:NeedKind, initial_permille:u16, decay_per_tick_q16:i32, happiness_weight:i16, good_threshold:u16, critical_threshold:u16, preferences:TableRange } |
Values/thresholds 0..=1000; decay signed 16.16 permille/tick. |
NeedPreferenceRecord |
record | { kind:NeedKind, target:AssetId, weight:i16, minimum_quality:u16 } |
Target is an A14 definition/affordance owner; weight -1000..=1000. |
SpeciesCompatibilityRecord |
record | { species:AssetId, other:AssetId, relation:CompatibilityKind, score:i16 } |
Sorted (species,other,relation); score -1000..=1000. |
CompatibilityKind |
enum | Habitat, Social, Predator, Prey, Breeding |
Static authored relation. |
MappedSpeciesArchive / access_species / access_species_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedSpeciesArchive; find(&self,AssetId)->Option<&ArchivedSpeciesRecord>; fn access_species(&[u8])->io::Result<&ArchivedSpeciesArchive>; unsafe fn access_species_validated(&[u8])->&ArchivedSpeciesArchive |
Validate once; mapping owner keeps references alive. |
SpeciesSource |
producer input | { virtual_path:String, bytes:Vec<u8> } |
Resolved XML/data source. |
compile_species |
function | fn compile_species(inputs:&[SpeciesSource], timing:&ArchivedSimulationTimingDefinition, target:TargetProfile)->io::Result<Vec<u8>> |
Pure deterministic catalogue producer; all authored time/rates become G32 ticks/per-tick Q16 here. |
SpeciesCatalogAsset |
Bevy asset | { archive:MappedSpeciesArchive } deriving Asset, TypePath, Clone |
Mapped owner only. |
SpeciesAssetLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=SpeciesCatalogAsset> for ozspecies using LoadContext::path() + get_owned |
No decoded graph or reader copy. |
SpeciesAssetPlugin |
plugin | unit struct | Registers asset/loader only. |
4. Data layout or ECS ownership
rkyv, little-endian target, 16-byte stable IDs and frozen TableRange typed-table ranges. Archived
vectors are traversed directly. Repeated display/source strings are interned;
game-facing text uses A09 key IDs. Model/material IDs are dependencies, never
embedded handles. Per-animal age/sex/needs remain ECS components in G11/G12.
G11 uses this exact Sex/LifeStage; G12 uses this exact NeedKind and need
ranges rather than a duplicate enum or per-animal map.
5. Preflight producer contract
Accept resolved animal entity XML, variant/model bindings, BFAI state vars/need
adjusts/preferences, taxonomy, biome/compatibility and adoption metadata from
selected archives. Normalize canonical
entity keys before AssetId::from_key. Apply source inheritance/templates and
pack precedence before records. Convert authored days/rates against A14
SimulationTimingDefinition into integer ticks and signed Q16 per-tick rates,
resolve all
references, deduplicate inherited lists and sort. Reject missing IDs, duplicate
variants, nonmonotonic stage/lifespan ticks, impossible ranges, NaN/Inf, unknown sex/life stage/need and dangling
dependencies with source diagnostics. Any need outside the evidenced fixed
vocabulary blocks conversion and is reported for contract revision.
6. Behavioral evidence and disposition
Search shipped entities/units/animals, lang, biome and taxonomy XML; oracle
animal/animal-manager/entity-type classes and RTTI; analysis domain rows; commit
d5657e5a simulation/animal.rs, startup/animals.rs, catalog sources and old
species code. Questions: exact game-day age thresholds, variant probability,
sex availability, breeding compatibility, exact complete need vocabulary,
tick conversion, decay/update residual rules and thresholds, appetite/speed unit conversions and which
fields are inherited. Resolve before encoding; do not preserve old bags.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Every selectable species has localization, a world definition and at least one valid adult variant. Variant choice filters by sex/stage then uses deterministic weighted selection supplied by gameplay. Integer tick ranges drive lifecycle but do not mutate. Each fixed tick, G12 adds the signed Q16 need rate to its per-need signed Q16 residual, applies the arithmetic whole permille toward zero, retains the fractional remainder and clamps 0..=1000; no runtime day conversion or drift-prone float accumulation occurs. Missing optional model fallbacks are explicit zero IDs; required ones fail. Mod-defined species work through identical records. Authentic values must be recovered for units and lifecycle fields before completion.
8. Performance contract
Validation O(s+v+c), find O(log s), variant/compatibility traversal O(range),
all zero-allocation. No runtime strings or per-entity catalogue clones. Test
allocator counts find and full traversal; mapping counters cover archive pages;
no GPU ownership beyond referenced A04/A07 assets.
9. Acceptance criteria
Four owned files, full API above, fixtures covering two species/sex/stages/ needs/preferences/compatibility and rejection tests for corrupt ranges/NaN/ dangling IDs/unknown needs. Archived lookup returns mapped records without allocation. Non-goals: animal live state, behavior scoring and adoption UI. No bootstrap-only five-field schema remains in this package.
10. Required handoff
Report migration of old species modules, parser dependencies, root registrations, A04/A07/A09/A14 dependency IDs, G11/G12 field consumers, exact unresolved data semantics and allocator/mapping validation.
A04 — GPU-ready model
1. Identity and outcome
Preflight converts original NIF/BFB meshes into target-specific, meshopt- optimized vertex/index/meshlet payloads. Bevy/wgpu upload, extract, batch, cull, skin and draw them through the normal renderer. The game never reconstructs a scene graph or runs mesh optimization.
2. Exclusive ownership and mandatory dependencies
The package worker owns native/src/families/model.rs, converter/src/families/model.rs,
game/src/assets/model.rs, and preflight/tests/model_contract.rs only. Root
adds meshopt = 0.6 to preflight with only required features.
Meshopt exclusively owns vertex remapping, cache/fetch/overdraw optimization, quantization, LOD simplification and meshlet construction. Bevy/wgpu exclusively own render assets, GPU buffers, extraction, culling, batching and draw commands. No OpenZT2 mesh optimizer, renderer, scene graph, render world, or generic model runtime is permitted.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
MODEL_ARCHIVE_VERSION |
constant | u32 = 1 |
Native layout version. |
ModelArchive |
root | { version:u32, target:TargetProfile, id:AssetId, bounds:ModelBounds, meshes:Vec<MeshRecord>, lods:Vec<LodRecord>, submeshes:Vec<SubmeshRecord>, skeleton:AssetId, dependencies:Vec<AssetId>, payload:Vec<u8> } |
Metadata sorted; payload ranges aligned for direct upload. |
MeshRecord |
record | { vertex_layout:VertexLayout, vertex_count:u32, index_count:u32, vertices:AssetRange, indices:AssetRange, submeshes:TableRange, lods:TableRange, meshlets:AssetRange } |
Final target layout and index width. |
VertexLayout |
enum | fixed supported packed static/skinned layouts | Closed pipeline-key vocabulary. |
SubmeshRecord |
record | { first_index:u32, index_count:u32, material:AssetId, bounds:ModelBounds } |
Contiguous validated draw range. |
LodRecord |
record | { first_index:u32, index_count:u32, screen_error:f32 } |
Deterministic ordered LOD threshold. |
MappedModelArchive |
owning view | new(MappedAsset)->io::Result<Self> plus zero-copy record/payload access |
Validated once, mapping-owned. |
compile_model |
producer | fn compile_model(input:&ModelSource,target:TargetProfile)->io::Result<Vec<u8>> |
Pure source conversion using meshopt. |
ModelAsset |
Bevy asset | { archive:MappedModelArchive } |
CPU mapping owner. |
ModelAssetPlugin |
plugin | unit struct | Registers loader and normal Bevy render-asset preparation only. |
The Bevy preparation path reads mapped ranges and uploads directly to wgpu buffers. Private prepared-render values may retain GPU handles/accounting, not copied CPU vertices or an OpenZT2 renderer abstraction.
4. Data layout and ownership
All records are rkyv-compatible fixed data. Vertex/index/meshlet blocks are target-profile aligned, little-endian and directly uploadable. CPU collision, navigation and placement shapes belong to A15–A17 rather than being derived from render meshes in the game.
5. Preflight producer contract
Resolve NIF/BFB hierarchy, geometry, materials, skeletons, transforms and LODs. Bake transforms where semantics permit; normalize coordinates/units; triangulate and weld; invoke meshopt for remap, cache/fetch/overdraw optimization, quantization, optional simplification and meshlets; then emit deterministic payloads. Reject unsupported visible topology, invalid weights/ranges, missing materials/skeletons, nonfinite values, or target limit overflow.
6. Behavioral evidence and disposition
Inspect shipped models, Gamebryo/BF geometry classes, material links, earlier converter evidence and live original for coordinate conventions, LOD thresholds, billboards, alpha ordering, skinning and exceptional geometry.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Models render with correct transforms, bounds, submesh materials, LODs and skinning references. Mod meshes follow the same conversion contract. Unknown visible source semantics block conversion rather than producing placeholder geometry.
8. Performance contract
Archive lookup/traversal allocates zero. GPU preparation performs one direct upload per merged range where practical, reuses staging capacity and retains no second CPU geometry graph. Draws use Bevy batching/culling and stable pipeline keys. Track mapped pages, upload bytes/calls, retained CPU heap, GPU vertex/index bytes, draw counts and LOD behavior.
9. Acceptance criteria
Four files/API; static/skinned/multi-material/LOD fixtures, corrupt range/layout rejection, deterministic meshopt output, zero-copy CPU traversal and exact GPU accounting. Source review must find no custom optimization algorithm, renderer, scene graph, or CPU geometry cache.
10. Required handoff
Report meshopt version/options, Bevy render-asset integration, target layouts, A06/A07/A15 consumers, root registrations, unresolved model evidence and mapping/upload/GPU/render measurements.
A05 — GPU-Native Texture
1. Identity and outcome
This package produces the only runtime texture representation: target-profile GPU-compressed mip/layer payloads with exact dimensions, colour semantics and upload ranges. No DDS/TGA/BMP/JPEG decode occurs in the game. Integration wave 1; only the shared native contract is prerequisite.
2. Exclusive ownership
This package owns native/src/families/texture.rs, converter/src/families/texture.rs,
game/src/assets/texture.rs, preflight/tests/texture_contract.rs. Renderer,
GPU ledger and module/plugin registration are root-owned/read-only.
Existing image, ddsfile, and image_dds dependencies exclusively
own source image decoding/transcoding in preflight; Bevy/wgpu own runtime images,
GPU textures and upload. This package must not write an image codec, runtime
source decoder, texture renderer, or second CPU pixel cache.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
TEXTURE_ARCHIVE_VERSION |
constant | u32 = 1 |
Layout version. |
TextureArchive |
root | { version:u32, target:TargetProfile, id:AssetId, dimension:TextureDimension, width:u32, height:u32, depth_or_layers:u32, mip_count:u16, format:TextureFormat, color_space:ColorSpace, sampler:SamplerRecord, cursor_hotspot:Option<[u16;2]>, subresources:Vec<TextureSubresource>, payload:Vec<u8> } |
Payload is final GPU copy source; hotspot is present only for converted CUR. |
TextureDimension |
enum | D1, D2, D2Array, Cube, D3 |
Must match target GPU capabilities. |
TextureFormat |
enum | R8, Rg8, Rgba8, Bgra8, Bc1, Bc3, Bc5, Bc7 |
Exact native format; profile may restrict variants. |
ColorSpace |
enum | Linear, Srgb |
Explicit, never filename guessed at runtime. |
SamplerRecord |
record | { address:[AddressMode;3], mag:FilterMode, min:FilterMode, mip:FilterMode, anisotropy:u8 } |
Anisotropy 1..=16. |
AddressMode |
enum | Clamp, Repeat, Mirror |
Maps directly to wgpu. |
FilterMode |
enum | Nearest, Linear |
Maps directly to wgpu. |
TextureSubresource |
record | { mip:u16, layer:u16, range:AssetRange, bytes_per_row:u32, rows_per_image:u32 } |
Range is payload bytes; sorted layer then mip; block alignment valid. |
MappedTextureArchive / access_texture / access_texture_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedTextureArchive; fn access_texture(&[u8])->io::Result<&ArchivedTextureArchive>; unsafe fn access_texture_validated(&[u8])->&ArchivedTextureArchive |
Validates dimensions/ranges once and owns mapping. |
TextureSource |
producer input | { virtual_path:Cow<str>, bytes:Cow<[u8]>, usage:TextureUsage } |
Borrowed unless legacy normalization is required. |
TextureUsage |
enum | Color, Normal, Mask, Height, Ui, Lookup |
Preflight source/transcode meaning; distinct from A07 shader TextureSemantic. |
compile_texture |
producer | fn compile_texture(input:&TextureSource, target:TargetProfile)->io::Result<Vec<u8>> |
Deterministic decode/mipmap/transcode. |
TextureAsset |
Bevy asset | { archive:MappedTextureArchive } deriving Asset, TypePath, Clone; bevy_image(&self)->Handle<Image> |
Sole mapped CPU payload owner; the image handle is a stable UUID view of the same native asset ID. |
TextureGpuAsset |
render asset | custom RenderAsset<SourceAsset=TextureAsset> |
Borrows archived &[u8] and prepares the sole GpuImage directly; never constructs Image.data or another pixel buffer. |
TextureAssetLoader |
loader | AssetLoader<Asset=TextureAsset> for oztexture using path + get_owned |
Validates the mapped native archive and performs no pixel conversion or copy. |
TextureAssetPlugin |
plugin | unit struct | Registers asset/loader plus complete render extraction, preparation and eviction accounting. |
4. Data layout or ECS ownership
Metadata is rkyv; payload is contiguous, tightly packed block-compressed data in
the same archive. Ranges are bytes relative to payload. Little-endian metadata;
subresources have no storage padding. Mapping is kept until asset eviction. A
narrow GPU-image registration bridge lets ordinary Bevy materials and
ImageNode resolve the prepared texture by the native asset UUID without a CPU
Image or custom renderer. Upload creates exactly one GPU texture and view set. Preparation updates
GpuMemoryLedger; no external renderer wrapper or parallel texture cache exists.
5. Preflight producer contract
Accept only DDS/TGA/BMP/JPEG and loose-installation CUR, the formats evidenced by shipped archives or the original C++ loaders. PNG, KTX2, and unevidenced BFT texture inputs are not part of the source contract. Normalize virtual paths before ID. Resolve replacements and later pack layers before decode. Compatible DDS blocks and complete authored mips are copied unchanged. Decode, orient, select colour space, generate missing/invalid mips, renormalize normal maps, transcode only target-incompatible formats and compute tight row layout. CUR is decoded offline to the same D2 GPU texture while preserving its hotspot as native metadata. Reject corrupt dimensions, unsupported semantics, excessive target limits, incomplete compressed blocks and ambiguous colour space. Diagnostics name source and failing mip. Output ordering is layer then mip.
6. Behavioral evidence and disposition
Search shipped texture extensions and material slots; old commit
source_format/model.rs, presentation/renderer.rs, UI visuals and terrain;
oracle NiSourceTexture/BFRTexture classes and Gamebryo calls. Questions: vertical
orientation, alpha modes, normal channel convention, UI filtering, terrain tile
samplers and source-format coverage. Validate with UI, animal and terrain
screenshots rather than guessed defaults.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Pixel orientation, alpha, colour and sampling match original presentation. Missing optional textures resolve to an explicit preflight-generated fallback asset; missing required textures fail conversion. Runtime streams declared mip subresources without decoding. Mod textures are preflight-converted identically.
8. Performance contract
Metadata traversal/Handle<Image> construction is zero allocation and never
clones pixel data. Render preparation passes the mapped payload directly to
Bevy/wgpu as &[u8]; no payload-sized Rust heap allocation is permitted.
O(payload bytes) GPU transfer, O(subresources) validation, O(1)
subresource access. Record mapped/resident bytes separately from exact GPU
texture bytes and staging. No per-frame texture preparation after residency.
Repeated render extraction of unchanged handles is allocation-free.
9. Acceptance criteria
Four files and API above; fixtures cover sRGB colour, linear normal, preserved
DDS blocks, mip/layer ranges and non-tight payload rejection. Tests prove
expected dimensions/formats and zero-allocation hydration view. A Bevy fixture obtains
TextureAsset::bevy_image(), sets it directly on ordinary ImageNode, observes
one GPU preparation, and proves no CPU pixel copy. Non-goals: material selection, renderer batching and
source decode in engine. A render fixture must verify exact create/upload/remove
ledger deltas and no repeated preparation.
10. Required handoff
Report image/transcoder dependencies and profile feature choices, asset/render registration, A07/A12/A16 consumers, unresolved orientation/semantic evidence, and heap/mapping/GPU measurements.
A06 — Skeletal Animation
1. Identity and outcome
This package produces compact skeleton-indexed clips, markers and graph transition tables that can be sampled directly from mapped memory. Source BF/KF/BFM parsing and name resolution end in preflight. Integration wave 3; depends on A04 model.
2. Exclusive ownership
This package owns native/src/families/animation.rs, converter/src/families/animation.rs,
game/src/assets/animation.rs, preflight/tests/animation_contract.rs; all
other files and schedule/render integration are read-only/root handoffs.
Bevy owns transforms, skeleton/render integration and GPU skinning. A06 owns the mapped source-authentic track sampler, selection/transition policy and presentation markers only. It must not create another scene graph, skeleton world, transform hierarchy, render extraction path, or generic animation host.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ANIMATION_ARCHIVE_VERSION |
constant | u32 = 1 |
Layout version. |
AnimationArchive |
root | { version:u32, target:TargetProfile, id:AssetId, skeleton:AssetId, strings:Vec<u8>, clips:Vec<ClipRecord>, tracks:Vec<TrackRecord>, keys:Vec<TransformKey>, presentation_markers:Vec<PresentationMarkerRecord>, graph_nodes:Vec<GraphNode>, graph_edges:Vec<GraphEdge> } |
All times sorted; ranges index tables. Presentation markers never enter gameplay dispatch. |
ClipFlags |
bitflags u8 |
LOOP, ROOT_MOTION, ADDITIVE |
Exact clip behavior. |
ClipRecord |
record | { id:AssetId, name:StringRange, duration_ms:u32, tracks:TableRange, markers:TableRange, flags:ClipFlags } |
Duration > 0. |
TrackRecord |
record | { joint:u16, channel:TrackChannel, interpolation:Interpolation, keys:TableRange } |
One sorted track per joint/channel. |
TrackChannel |
enum | Translation, Rotation, Scale |
Runtime canonical TRS only. |
Interpolation |
enum | Step, Linear, Spherical |
Spherical valid only for rotation. |
TransformKey |
POD record | { time_ms:u32, value:[f32;4] } |
Translation xyz, quaternion xyzw, scale xyz; finite and normalized rotations. |
PresentationMarkerRecord |
private record | { time_ms:u32, label:StringRange } |
Diagnostic/presentation metadata within clip duration; never interpreted as a gameplay command. |
GraphNode |
record | { id:AssetId, clips:TableRange, selection:ClipSelection } |
Clips reference clip indexes. |
ClipSelection |
enum | First, RandomWeighted, Sequential |
Fixed authored selection vocabulary. |
GraphEdgeFlags |
bitflags u8 |
INTERRUPTIBLE, RETURN, IMMEDIATE |
Exact transition behavior. |
GraphEdge |
record | { from:u32, to:u32, transition_clip:u32, blend_ms:u16, flags:GraphEdgeFlags } |
Node/clip indexes validated. |
MappedAnimationArchive / access_animation / access_animation_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedAnimationArchive; clip(&self,AssetId)->Option<&ArchivedClipRecord>; fn access_animation(&[u8])->io::Result<&ArchivedAnimationArchive>; unsafe fn access_animation_validated(&[u8])->&ArchivedAnimationArchive |
Binary lookup, validate once, own mapping. |
AnimationSource |
input | { virtual_path:String, clip_bytes:Vec<Vec<u8>>, graph_bytes:Option<Vec<u8>> } |
BF clips/BFM graph. |
compile_animation |
producer | fn compile_animation(input:&AnimationSource, skeleton:&ArchivedModelArchive, target:TargetProfile)->io::Result<Vec<u8>> |
Pure resolved compilation. |
AnimationAsset |
Bevy asset | { archive:MappedAnimationArchive } |
Mapped view. |
Animator |
component | { animation:Handle<AnimationAsset>, clip:AssetId, elapsed_ms:u32, speed_permille:i16, state:AnimationState, looped:bool, clock:AnimationClock } |
Live playback fact on one animated entity. |
AnimationState |
enum | Playing, Paused, Finished |
Playback state only. |
AnimationClock |
enum | Simulation, RealTime |
World animation follows G32 fixed simulation ticks; shell/UI animation follows real time. |
PoseRange |
value | { start:u32, len:u16 } |
Contiguous joint-matrix span in the package pose buffer. |
SkinnedPose |
component | { range:PoseRange } |
Small allocation-free reference; range changes only with skeleton. |
AnimationPoseBuffer |
resource | { joints:Vec<[[f32;4];4]>, free:Vec<PoseRange> } |
Focused contiguous pool, reserved during spawn/load and reused; no per-entity heap allocation. |
advance_simulation_animators |
system | simulation-clock Query<&mut Animator> in FixedUpdate/FixedGameSet::Act using one G32 fixed tick |
Integer elapsed update, paused with zoo simulation. |
advance_realtime_animators |
system | Res<Time<Real>>, Query<&mut Animator> filtered to real-time clock in Update/GameSet::Presentation |
Shell/UI elapsed update independent of zoo pause. |
sample_animation_poses |
system | fn(Res<Assets<AnimationAsset>>, ResMut<AnimationPoseBuffer>, Query<(&Animator,&SkinnedPose), Changed<Animator>>) in Update/GameSet::Presentation after advancement |
Writes assigned span in place. |
advance_presentation_markers |
private system | animation assets plus changed animators in Update/GameSet::Presentation |
Advances animation-local presentation metadata only. It exposes no gameplay message or generic event dispatcher. |
AnimationAssetLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=AnimationAsset> for ozanim via path + get_owned |
No keyframe/reader copies. |
AnimationAssetPlugin |
plugin | unit struct | Registers asset/loader, playback components and narrow systems. |
4. Data layout or ECS ownership
rkyv tables with frozen TableRange spans; integer milliseconds prevent float search drift.
Joint indexes are A04 skeleton order. Sampling binary-searches mapped keys and
writes the SkinnedPose range in AnimationPoseBuffer; the asset owns no live playback state. GPU skin
matrices are transient A04 renderer data, not archived duplicates. Each system
performs one operation; there is no animation manager loop.
5. Preflight producer contract
Accept BF binary clips, NetImmerse KF controller sequences, BFM XML graph documents, and TXTKEYS presentation markers after layer resolution. Prefer the winning same-stem BF clip because the shipped BFM graphs reference BF; when no BF winner exists, compile the KF sequence as the clip. KF target names bind to the final model skeleton, NiKeyframeController/NiKeyframeData become canonical translation/rotation/scale tracks, and NiTextKeyExtraData becomes presentation markers. Preflight flattens quadratic and tension/bias/continuity curves into the native linear/spherical sampling contract. KFM, KFINFO, and BIP remain authoring/controller evidence with explicit validated disposition. None of these source formats or their block graphs reach the runtime. Normalize IDs, map source node names to model joints, convert Euler tracks to canonical quaternions, quantize time to milliseconds only within evidenced tolerance, compile nonsemantic presentation markers/transitions and close clip dependencies. Any source marker with gameplay semantics must be recovered from the complete corpus and promoted to the precise owning gameplay action/message before acceptance; it cannot remain an ID or string event. Reject unknown value-bearing tracks, missing joints/clips, nonmonotonic time, invalid quaternions, graph indexes/cycles where forbidden and source duration mismatch. Deterministic clip ID then track/joint/channel ordering.
6. Behavioral evidence and disposition
Search shipped .bf/.bfm, model skeleton names; commit d5657e5a
source_format/animation.rs, animation startup/AI code; oracle animation graph,
sequence and controller types. Questions: time unit/quantization, Euler order,
loop/end semantics, weighted clip selection, blend durations, whether each
marker is presentation metadata or an exact domain action, and graph transition
fallback. Live-check representative locomotion/idle clips.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Sampling at 0/end, looping, interpolation and presentation-marker crossing are deterministic.
Graphs select only compiled clips; Animator owns current clip/time. Root motion
is an explicit evidenced flag/track, never inferred silently. Mod clips/graphs
must use the same fixed channels and selection vocabulary.
8. Performance contract
Clip lookup O(log clips), per-track sample O(log keys), zero allocations into a pre-sized pose slice. Presentation-marker traversal is linear only over the crossed range. Allocator tests cover lookup/advance/sample/presentation-marker scan; mapped pages and animation GPU matrix capacity measured separately. No per-frame strings or graph cloning.
9. Acceptance criteria
Four files; convert fixture with translation/quaternion/scale, presentation markers and an edge; validate sampled values and failure cases. A Bevy fixture advances, samples and advances presentation metadata with zero allocations after pose warm-up. Model render skinning is A04; behavior selection is a gameplay concern.
10. Required handoff
Report model archive consumption, parser dependencies, root/schedule/message registrations, graph evidence and allocation/mapping/GPU-pose validation.
A07 — Material
1. Identity and outcome
This package produces compact immutable material render state with shader variant, typed parameters and texture bindings. Material selection requires no string lookup or Gamebryo-style object graph at runtime. Integration wave 1; depends on A05 and A08.
2. Exclusive ownership
This package owns native/src/families/material.rs, converter/src/families/material.rs,
game/src/assets/material.rs, preflight/tests/material_contract.rs only.
Bevy material/render-asset APIs and wgpu exclusively own bind groups, render state, pipelines and drawing. A07 supplies compact parameters, references and typed specialization keys; it must not create a material renderer, pipeline registry, graphics abstraction, or copied texture/material graph.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
MATERIAL_ARCHIVE_VERSION |
constant | u32 = 1 |
Layout version. |
MaterialArchive |
root | { version:u32, target:TargetProfile, id:AssetId, shader:AssetId, variant:u32, render:RenderState, parameters:Vec<MaterialParameter>, textures:Vec<TextureBinding>, dependencies:Vec<AssetId> } |
Parameters/bindings sorted by semantic. |
MaterialFlags |
bitflags u16 |
UNLIT, RECEIVE_FOG, VERTEX_COLOR, DEPTH_BIAS, TWO_SIDED_LIGHTING |
Exact compiled render features. |
RenderState |
record | { blend:BlendMode, cull:CullMode, depth_write:bool, depth_test:CompareMode, alpha_cutoff:f32, queue:i16, flags:MaterialFlags } |
Direct pipeline-selection facts. |
BlendMode |
enum | Opaque, Masked, Alpha, Additive, Multiply |
Fixed variants. |
CullMode |
enum | None, Front, Back |
Direct wgpu mapping. |
CompareMode |
enum | Always, Less, LessEqual, Equal, GreaterEqual, Greater |
Direct wgpu mapping. |
MaterialParameter |
record | { semantic:ParameterSemantic, value:[f32;4] } |
Finite, one row per semantic. |
ParameterSemantic |
enum | BaseColor, Emissive, Roughness, Metallic, Alpha, UvTransform0, UvTransform1 |
Exhaustive fixed shader ABI; unknown source semantics reject conversion. |
TextureBindingFlags |
bitflags u8 |
OPTIONAL, SRGB_OVERRIDE, CLAMP_OVERRIDE |
Exact evidenced binding policy; no opaque source bits. |
TextureBinding |
record | { semantic:TextureSemantic, texture:AssetId, sampler_override:u16, uv_set:u8, flags:TextureBindingFlags } |
Texture ID nonzero unless explicitly optional. |
TextureSemantic |
enum | BaseColor, Normal, Mask, Emissive, Detail, Environment |
Exhaustive stable bind vocabulary. |
MappedMaterialArchive / access_material / access_material_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedMaterialArchive; fn access_material(&[u8])->io::Result<&ArchivedMaterialArchive>; unsafe fn access_material_validated(&[u8])->&ArchivedMaterialArchive |
Validate once, own mapping. |
MaterialSource |
input | { virtual_path:String, bytes:Vec<u8> } |
Material XML/NIF properties. |
compile_material |
producer | fn compile_material(input:&MaterialSource, resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> |
Pure compilation. |
MaterialAsset |
Bevy asset | { archive:MappedMaterialArchive } |
Mapped view. |
ModelMaterial |
component | pub struct ModelMaterial(pub Handle<MaterialAsset>) |
Optional explicit material handle attached directly by G04. |
MaterialAssetLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=MaterialAsset> for ozmaterial via path + get_owned |
No reader copy/runtime assembly. |
MaterialAssetPlugin |
plugin | unit struct | Registers asset/loader and complete material extraction, preparation and eviction accounting. |
4. Data layout or ECS ownership
One rkyv root per material; fixed enums/numeric rows map to a cached Bevy/wgpu
pipeline and bind group. No maps, strings or boxed values. Texture/shader IDs
are pack dependencies and become handles during render preparation; GPU uniform
and bind-group ownership is implemented here in Bevy's render world. A04
extracts ModelMaterial directly; no renderer service facade exists.
5. Preflight producer contract
Accept NIF material/texturing/alpha properties, BFB material blocks and material XML after override resolution. Canonical path ID; merge inherited/default properties, map texture slots and shader variant, fold UV transforms, choose blend/depth/cull and resolve dependencies. Reject contradictory states, non-finite values, missing required bindings, unsupported value-bearing shader properties and target layout mismatch. Sort by semantic for deterministic bytes.
6. Behavioral evidence and disposition
Search model material properties and texture slots; commit d5657e5a
source_format/model.rs and presentation/renderer.rs BFR material records;
oracle NiMaterialProperty/NiTexturingProperty/BFRMaterial classes. Questions:
alpha test thresholds, blend functions, selector/variant rules, UV animation,
two-sided flags, environment/detail slots and queue ordering. Verify terrain,
glass/water, foliage and animal materials live.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Opaque/masked/transparent presentation, colour/emissive values, UV selection, texture dependencies and render ordering match evidence. Animated material parameters are immutable curves only if evidence requires them; live time stays outside asset. Unknown properties that change pixels fail rather than disappear.
8. Performance contract
Validation and preparation O(parameters+bindings), lookup O(log n) or bounded linear tiny table, zero allocation during steady draw extraction. Pipelines and bind groups cache by immutable archive key; no per-frame material clones or uniform Vec growth. Count mapped bytes, uniform/bind GPU bytes and cache misses. Unchanged extraction and prepared lookup allocate zero after cache warm-up.
9. Acceptance criteria
Four files/API; opaque, masked and alpha fixtures with texture/shader closure; reject duplicate semantics and invalid target layouts. Archived binding traversal and repeated prepared lookup are allocation-free. A render fixture verifies exact create/remove ledger deltas. Shader/texture production remain A08/A05; A04 owns draw batching.
10. Required handoff
Report A05/A08 IDs, parser deps, root registrations, renderer pipeline/cache requirements, unresolved original render-state evidence and memory counters.
A08 — Shader modules and variants
1. Identity and outcome
Preflight translates and validates the original FX effects and their shipped H includes, plus material-selected fixed-function variants, variant with Naga, selects a target-profile module representation, and emits exact bind/pipeline metadata. Bevy/wgpu create and cache runtime shader modules and pipelines. OpenZT2 contains no shader compiler or graphics API abstraction.
2. Exclusive ownership and mandatory dependencies
The package worker owns native/src/families/shader.rs, converter/src/families/shader.rs,
game/src/assets/shader.rs, and preflight/tests/shader_contract.rs only. Root
exposes to preflight the exact Naga version selected by Bevy 0.19/wgpu; a second
incompatible Naga version is forbidden.
Naga exclusively owns parsing, IR, translation and shader validation. Bevy/wgpu own shader modules, bind-group layouts, pipeline layouts, specialization/cache, driver compilation and render-world integration. This package must not write a shader parser/compiler, SPIR-V/WGSL translator, pipeline cache, renderer backend, or string-based runtime variant system.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
SHADER_ARCHIVE_VERSION |
constant | u32 = 1 |
Native layout version. |
ShaderArchive |
root | { version:u32, target:TargetProfile, id:AssetId, stages:Vec<ShaderStageRecord>, bindings:Vec<BindingRecord>, variants:Vec<ShaderVariant>, payload:Vec<u8> } |
Prevalidated modules and exact metadata. |
ShaderStageRecord |
record | { stage:ShaderStage, entry:StringRange, module:AssetRange, representation:ShaderRepresentation } |
One target-compatible module range. |
ShaderRepresentation |
enum | target-profile accepted Wgsl or SpirV representation |
Selected only by preflight/backend contract. |
BindingRecord |
record | { group:u8, binding:u8, visibility:u8, kind:BindingKind, count:u16, min_size:u32 } |
Matches Naga validation output and A07 layout. |
ShaderVariant |
record | { key:u64, stages:TableRange, bindings:TableRange, vertex_layout:u16, render_state:u32 } |
Sorted unique complete pipeline key. |
MappedShaderArchive |
owning view | new(MappedAsset)->io::Result<Self> and archived/module accessors |
Validated once; mapping-owned. |
compile_shader |
producer | fn compile_shader(input:&ShaderSource,target:TargetProfile)->io::Result<Vec<u8>> |
Pure Naga-backed translation/validation and native writing. |
ShaderAsset |
Bevy asset | { archive:MappedShaderArchive } |
Mapped modules/metadata. |
ShaderAssetPlugin |
plugin | unit struct | Registers loader and ordinary Bevy shader/pipeline preparation only. |
4. Data layout and ownership
Modules are stored in the exact representation accepted for the selected
backend profile, aligned and referenced by AssetRange. Binding/variant tables
are flat and directly traversed. Prepared runtime state is Bevy/wgpu state;
OpenZT2 retains no parallel module or pipeline registry.
5. Preflight producer contract
Resolve original FX/H effects and material techniques. Convert supported semantics to Naga input, parse, validate capabilities/uniformity, translate to the target representation, reflect exact bindings, enumerate only reachable variants and sort deterministically. Reject unknown required shader semantics, invalid bindings, unsupported capabilities, missing entry points, variant explosion, or target/backend mismatch. Runtime source translation is forbidden; unavoidable driver module/pipeline creation is prewarmed during loading.
6. Behavioral evidence and disposition
Inspect shipped .fx, materials, shader references, Gamebryo renderer evidence,
the C++ oracle and live original for techniques, blend/depth/cull policy,
skinning, terrain, water, fog, shadows and expansion effects.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Every material resolves a validated compatible variant. Pipeline keys are typed and deterministic. Missing or unsupported visible semantics fail preflight; there is no runtime text substitution, fallback uber-shader, or guessed render state.
8. Performance contract
Mapped module/variant lookup allocates zero. Loading creates each required Bevy/ wgpu module and pipeline once, prewarms scene/menu pipelines, and never compiles or specializes per frame. Track mapping, module/pipeline creation count/time, pipeline-cache misses, bind-group churn and shader/pipeline GPU/driver bytes where available.
9. Acceptance criteria
Four files/API; Naga translation/validation fixtures, binding/variant rejection, deterministic output, zero-allocation lookup and Bevy pipeline preparation for representative model/terrain/water/UI effects. Source review must find no custom shader compiler, runtime source converter, or duplicate pipeline registry.
10. Required handoff
Report exact Bevy/wgpu/Naga version alignment and features, target module representations, A07 bindings/keys, root registrations/prewarm order, unsupported original semantics and mapping/pipeline measurements.
A09 — Localization
1. Identity and outcome
This package produces collision-safe, allocation-free locale lookup tables with UTF-8 text, formatting metadata and pre-resolved fallback chains. Runtime never parses lang XML, merges locales or builds a hash map. Integration wave 1; shared native contract only.
2. Exclusive ownership
This package owns native/src/families/localization.rs,
converter/src/families/localization.rs, game/src/assets/localization.rs, and
preflight/tests/localization_contract.rs only.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
LOCALIZATION_ARCHIVE_VERSION |
constant | u32 = 1 |
Layout version. |
LocalizationArchive |
root | { version:u32, target:TargetProfile, locale:LocaleCode, fallbacks:Vec<AssetId>, strings:Vec<u8>, entries:Vec<LocaleEntry>, tokens:Vec<FormatToken> } |
Entries sorted by full 128-bit key ID. |
LocaleCode |
value | { language:[u8;2], region:[u8;2] } |
Lowercase ISO language, uppercase region or zero region. |
LocaleEntry |
record | { key:AssetId, key_text:StringRange, value:StringRange, tokens:TableRange } |
Key text retained to prove collision identity; unique full canonical key. |
FormatToken |
enum | Literal(StringRange), Argument{index:u8,kind:ArgumentKind}, Plural{index:u8,one:StringRange,other:StringRange} |
Fixed safe formatter, no runtime expression language. |
ArgumentKind |
enum | Text, Integer, Decimal, Currency, Percent |
Locale formatting behavior fixed. |
MappedLocalizationArchive / access_localization / access_localization_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedLocalizationArchive; get(&self,AssetId)->Option<&str>; tokens(&self,AssetId)->Option<&[ArchivedFormatToken]>; fn access_localization(&[u8])->io::Result<&ArchivedLocalizationArchive>; unsafe fn access_localization_validated(&[u8])->&ArchivedLocalizationArchive |
Validate once, own mapping, borrowed UTF-8. |
LocalizationSource |
input | { virtual_path:String, locale:LocaleCode, bytes:Vec<u8> } |
Lang XML/data. |
compile_localization |
producer | fn compile_localization(inputs:&[LocalizationSource], fallback_order:&[LocaleCode], target:TargetProfile)->io::Result<Vec<u8>> |
Pure resolved locale producer. |
LocalizationAsset |
Bevy asset | { archive:MappedLocalizationArchive } |
Borrowing mapped strings. |
LocalizationAssetLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=LocalizationAsset> for ozloc via path + get_owned |
No reader copy/String reconstruction. |
LocalizationAssetPlugin |
plugin | unit struct | Registers asset/loader only. |
4. Data layout or ECS ownership
UTF-8 key/value/literal text is interned once in strings; StringRange is
byte-based and validated on UTF-8 boundaries. AssetId::from_key hashes the
trimmed canonical localization key, while stored key_text detects accidental
collision. Fallbacks are resolved by preflight into the emitted selected-locale
table; fallbacks records provenance only and runtime never searches files.
5. Preflight producer contract
Accept selected language XML/string tables and DLC/mod overrides. Normalize keys with evidenced case/namespace rules, apply later-layer overrides per key, resolve requested locale → parent/region → base fallback, parse safe format placeholders, validate UTF-8 and intern deterministic key order. Reject duplicate keys within one precedence layer, hash collisions with differing key text, malformed formats, missing required fallback and invalid locale codes.
6. Behavioral evidence and disposition
Search shipped lang/locale XML and UI text keys; oracle locale/string manager
classes and functions; commit d5657e5a locale sources, UI text and formatter
paths. Questions: key case sensitivity, locale fallback order, formatting
syntax, plural/currency rules, escaped markup and whether region codes exist.
Compare live representative UI, finance and scenario strings.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Lookup returns borrowed selected-locale text; absent optional key yields None
for UI to display a diagnostic, never silently key-as-value in release. Fixed
format tokens support original authored placeholders without arbitrary scripts.
Mods override keys before output. User locale change selects another completed
native locale asset/profile, not source remerge.
8. Performance contract
Compile O(total text log entries); validation O(entries+bytes); lookup O(log n)
and zero allocation. Formatting writes into caller-provided reusable fmt::Write
or byte buffer and must allocate zero after capacity. Track mapped locale pages;
no GPU bytes. Regression tests cover lookup and formatted traversal counters.
9. Acceptance criteria
Four files/API; fixture covers override/fallback, UTF-8, arguments and collision rejection. Borrowed lookup and warmed formatting allocate zero. Non-goals: UI text components, locale picker and font shaping.
10. Required handoff
Report XML/parser/locale-format dependencies, root registrations, A02/A03/A14/ A19 consumers, exact unresolved key/format semantics and allocator/mapping validation.
A10 — Compiled Behavior Program
1. Identity and outcome
This package compiles data-authored animal, guest and staff behavior into fixed typed command, scoring and constant tables. Gameplay systems interpret narrow instructions against ECS components; this asset is not a shadow world or a general Lua VM. Integration wave 3; depends on A14 definitions.
2. Exclusive ownership
This package owns native/src/families/behavior_program.rs,
converter/src/families/behavior_program.rs,
game/src/assets/behavior_program.rs, and
preflight/tests/behavior_program_contract.rs only.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
BEHAVIOR_ARCHIVE_VERSION |
constant | u32 = 1 |
Opcode/layout version. |
BehaviorArchive |
root | { version:u32, target:TargetProfile, id:AssetId, sets:Vec<BehaviorSet>, set_programs:Vec<AssetId>, programs:Vec<BehaviorProgram>, considerations:Vec<Consideration>, actions:Vec<BehaviorAction>, constants:Vec<BehaviorConstant>, gesture_templates:Vec<GestureTemplate>, gesture_points:Vec<GestureTemplatePoint>, puzzles:Vec<PuzzleDefinition>, puzzle_steps:Vec<PuzzleStep>, dependencies:Vec<AssetId> } |
Sets/programs/templates/puzzles sorted by ID; all record spans use TableRange. |
BehaviorSet |
record | { id:AssetId, programs:TableRange, think_interval_ticks:u32, idle_program:AssetId } |
programs indexes root set_programs; IDs resolve to BehaviorProgram; nonzero cadence; idle belongs to the range. G13's BehaviorHandle::set indexes this table directly. |
BehaviorProgramFlags |
bitflags u8 |
INTERRUPTIBLE, REPEAT, REQUIRES_TARGET, EXCLUSIVE |
Exact task policy. |
BehaviorProgram |
record | { id:AssetId, considerations:TableRange, actions:TableRange, cooldown_ticks:u32, flags:BehaviorProgramFlags } |
G32 ticks; ranges bounded and nonempty action range. |
Consideration |
record | { input:BehaviorInput, curve:ResponseCurve, weight:i16 } |
Weight fixed-point -1000..=1000; all input parameters live in the typed BehaviorInput variant. |
BehaviorInput |
enum | Need(crate::families::species::NeedKind), DistanceTo(TargetKind), HasTarget(TargetKind), HabitatScore, Health, Age, TimeOfDay, Random(BehaviorRandomStream) |
Uses A03's exact fixed need vocabulary; no duplicate/string key or behavior-domain AssetId. |
BehaviorRandomStream |
enum | TaskScore, TargetChoice, ClipChoice |
Fixed G32-derived RNG domains; adding a domain requires corpus evidence and an enum revision, never an arbitrary ID. |
TargetKind |
enum | Food, Water, Shelter, Enrichment, Mate, Keeper, Guest, Facility, Habitat |
Query category, not entity bag key. |
ResponseCurve |
record | { kind:CurveKind, points:[[i16;2];4], count:u8 } |
Fixed-point x/y; 1..=4 points sorted x. |
CurveKind |
enum | Linear, Inverse, Step, Piecewise |
Deterministic scoring. |
BehaviorAction |
enum | ChooseTarget{kind:TargetKind,radius_cm:u32}, MoveToTarget, Consume{need:crate::families::species::NeedKind}, PlayAnimation{clip:AssetId}, Wait{duration_ticks:u32}, ClearTarget, SetCooldown{duration_ticks:u32} |
All simulation duration payloads are G32 ticks; gameplay package owns execution systems. There is no generic event action. |
BehaviorConstant |
record | { key:AssetId, value:BehaviorValue } |
Sorted by key. |
BehaviorValue |
enum | Bool(bool), Integer(i32), Scalar(i32), Asset(AssetId) |
Scalar is signed 16.16 fixed point. |
GestureTemplateKind |
enum | Stroke, Circle, Line, ZigZag, TapSequence, Drag |
Fixed authored recognition vocabulary; distinct from G30 live context. |
GestureTemplate |
record | { id:AssetId, kind:GestureTemplateKind, points:TableRange, tolerance_permille:u16, minimum_length_px:u16, maximum_duration_ms:u32, command:PuzzleCommand } |
Points index gesture_points; tolerance 0..=1000. |
GestureTemplatePoint |
POD record | { x_snorm:i16, y_snorm:i16, time_unorm:u16 } |
Normalized authored template point. |
PuzzleDefinition |
record | { id:AssetId, steps:TableRange, completion:PuzzleCommand, failure:PuzzleCommand, time_limit_ticks:u32 } |
Steps index puzzle_steps. |
PuzzleStep |
record | { id:AssetId, gesture:AssetId, target:AssetId, required:u16, command:PuzzleCommand } |
Typed authored step, no string dispatch. |
PuzzleCommand |
enum | None, Advance, Reset, Complete, Fail, SetMode(crate::families::world_definitions::ModeKind), PlaceFossil{slot:AssetId,piece:AssetId}, StartClone{species:AssetId}, TrainTrick{animal:AssetId,trick:AssetId} |
Fixed G30/G31 effects. AssetId fields identify data targets only; unknown semantic commands block conversion. |
MappedBehaviorArchive / access_behavior / access_behavior_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedBehaviorArchive; set(&self,AssetId)->Option<&ArchivedBehaviorSet>; set_programs(&self,&ArchivedBehaviorSet)->&[ArchivedAssetId]; program(&self,AssetId)->Option<&ArchivedBehaviorProgram>; fn access_behavior(&[u8])->io::Result<&ArchivedBehaviorArchive>; unsafe fn access_behavior_validated(&[u8])->&ArchivedBehaviorArchive |
Binary lookup, validate once, own mapping. |
BehaviorSource |
input | { virtual_path:String, bytes:Vec<u8>, language:BehaviorSourceLanguage } |
Resolved XML/task/Lua source. |
BehaviorSourceLanguage |
enum | XmlTask, XmlBehavior, LuaSubset, GestureXml, PuzzleXml |
Preflight-only. |
compile_behavior_programs |
producer | fn compile_behavior_programs(inputs:&[BehaviorSource], resolve:impl Fn(&str)->Option<AssetId>, timing:&ArchivedSimulationTimingDefinition, target:TargetProfile)->io::Result<Vec<u8>> |
Pure closed-world compilation; converts authored simulation durations/cadence to G32 ticks. |
BehaviorProgramAsset |
Bevy asset | { archive:MappedBehaviorArchive } |
Mapped program. |
BehaviorProgramLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=BehaviorProgramAsset> for ozbehavior via path + get_owned |
No reader copy/Lua parser. |
BehaviorProgramAssetPlugin |
plugin | unit struct | Registers only asset/loader. |
4. Data layout or ECS ownership
All variable collections, including behavior-set program IDs, gesture templates and puzzle steps, are
typed flat tables and frozen TableRanges. Fixed
point is used for deterministic score/constant values; simulation durations are
integer G32 ticks (gesture input windows alone remain real milliseconds) and
distances are centimetres. Programs contain no entity state,
closures, strings, bytecode stack or mutable instruction pointer. G13/G20
systems own current task/target/cooldown as small components.
5. Preflight producer contract
Accept resolved AI XML/task graphs, gesture/puzzle documents and the evidenced
supported subset of Lua behavior scripts. Normalize program/asset keys, expand inheritance/macros,
constant-fold expressions, compile recognized queries/actions, resolve asset
references and reject any command with effects outside the fixed vocabulary.
Resolve source behavior-set membership and idle fallback, then convert authored
wait/cooldown/think durations with A14 timing before serialization; runtime does
no milliseconds/day conversion.
Before implementation, exhaustively inventory every behavior and puzzle command
in the shipped/mod corpus and oracle. Each behavior-bearing command must become
an explicitly named enum variant with only its genuine typed payload; an
AssetId event name, string opcode, generic dispatcher or catch-all variant is
not an admissible representation.
Later packs replace definitions before compilation. Reject recursion, unbounded
loops, dynamic global access, arbitrary I/O, unknown fields, missing references,
invalid curves, malformed gesture geometry, puzzle cycles and action sequences
with no terminal/yield. Stable program/template/puzzle ID then authored order.
6. Behavioral evidence and disposition
Search shipped AI/task XML and Lua, command strings and entity definitions;
oracle BFAI task/evaluator/controller and script callback functions; analysis
function domains; commit d5657e5a simulation/behavior.rs, simulation/ai.rs,
infrastructure/lua*, gesture/mode dispatch and scripting startup. Questions: complete command/query
vocabulary, score formula/tie breaks, task cooldown/interrupt semantics, random
distribution, supported Lua expressions, exact gesture normalization/matching,
puzzle reset/completion semantics and emitted events. Add opcodes only
after evidence and version bump review.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Given identical component observations and RNG input, scoring and chosen action
are deterministic. Unknown source behavior fails conversion rather than
becoming no-op success. Mod authors may extend programs using the fixed
vocabulary, not native code. Action execution is divided among precise gameplay
systems; this package supplies immutable instructions only. G30 traverses
compiled templates/steps and translates PuzzleCommand to natural domain
messages; it does not parse source text or operate a generic puzzle VM.
8. Performance contract
Program lookup O(log n), score O(considerations), action traversal O(actions), all zero allocation. Constants use sorted lookup, not HashMap. Gameplay may evaluate candidates in bounded spatial sets and fixed timestep; no full-world dispatcher. Allocator tests cover lookup/score traversal; mapped pages tracked; no GPU resources.
9. Acceptance criteria
Four files/API; fixture compiles needs/distance behavior plus gesture template and multi-step puzzle, rejects unsupported Lua/I/O/loop/gesture/puzzle command and bad ranges, and proves mapped iteration zero-allocation. Non-goals: ECS target queries, locomotion and animation state.
10. Required handoff
Report parser/compiler dependencies, module/loader registration, A03
NeedKind use and TargetKind promotion request, G13/G20 executor and G30/G31
gesture/puzzle mapping, unresolved
opcode evidence and allocation/mapping checks.
A11 — Unified audio
1. Identity and outcome
Original music, ambience, speech, UI sounds, and positional effects become one native audio family consumed through Bevy's entity-based audio API. Bevy owns the device, sinks, playback and spatialization; OpenZT2 supplies mapped resident or streamed payloads plus recovered channel/priority policy. Integration wave 1.
2. Exclusive ownership and Bevy dependency
The package worker owns native/src/families/audio.rs, converter/src/families/audio.rs,
game/src/assets/audio.rs, and preflight/tests/audio_contract.rs only. Root
enables Bevy 0.19 bevy_audio and the selected native codec feature.
Bevy Audio exclusively owns device output, voice playback, sinks, decoding
integration, looping, volume/speed, and spatial emitters/listeners. The package
must not implement mix_audio_voices, an audio device backend, a software mixer,
a parallel channel world, or copied playback registry. Kira is not added while
Bevy Audio meets the recovered behavior; a missing measured behavior requires a
root decision before changing backend.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
AUDIO_ARCHIVE_VERSION |
constant | u32 = 1 |
Native layout version. |
AudioArchive |
root | { version:u32, target:TargetProfile, id:AssetId, format:NativeAudioFormat, channels:u8, sample_rate:u32, frames:u64, loop_range:Option<[u64;2]>, seek:Vec<AudioSeekBlock>, payload:Vec<u8> } |
Payload is final resident PCM or one selected Bevy/rodio-supported stream; no source WAV graph. |
NativeAudioFormat |
enum | PcmS16, StreamedMp3, StreamedVorbis |
Closed target-profile vocabulary. Authored MP3 is retained when it already satisfies the runtime stream contract. |
AudioSeekBlock |
record | { first_frame:u64, frame_count:u32, bytes:AssetRange } |
Sorted, nonoverlapping, bounded stream blocks. |
AudioUsage |
enum | Ui, Effect, Voice, Ambient, Music |
Recovered policy class, not a mixer channel object. |
MappedAudioArchive |
owning view | new(MappedAsset)->io::Result<Self>; archived, payload, and binary block_for_frame accessors |
Validated once and mapping-owned. |
compile_audio |
producer | fn compile_audio(input:&AudioSource,target:TargetProfile)->io::Result<Vec<u8>> |
Pure deterministic decode/normalize/transcode. |
AudioAsset |
Bevy asset | { archive:MappedAudioArchive } |
Keeps mapped bytes alive. |
NativeAudioSource |
Bevy decodable source | owning cursor over Arc<MappedPack> plus exact payload range |
Adapts mapped native bytes to Bevy Audio without copying the whole clip. |
AudioCue |
component | { clip:Handle<AudioAsset>, usage:AudioUsage, volume:Volume, looped:bool, spatial:bool, priority:u8 } |
Domain request on an entity; converted into Bevy AudioPlayer and PlaybackSettings. |
AudioAssetPlugin |
plugin | unit struct | Registers asset/source adapter and focused cue lifecycle systems. |
Live playback uses Bevy's AudioPlayer, PlaybackSettings, AudioSink,
SpatialAudioSink, and SpatialListener directly. OpenZT2 does not wrap or
mirror their state. A small global policy resource may hold recovered per-usage
volume/voice limits; it does not own voices.
4. Data layout and ECS ownership
Metadata and seek blocks are traversed from the mapping. Resident PCM and stream blocks remain mapped until Bevy's decoder/output path consumes them. Each sound is an ordinary playback entity spatially parented or transformed like any other Bevy entity. Completion uses component removal/despawn policy, not a central audio manager.
5. Preflight producer contract
Accept only WAV and MP3, the audio formats present in the official corpus and original loader evidence. Validate MP3 timing/headers but preserve its authored compressed bytes unchanged as the native streamed representation. Decode WAV; select resident PCM for small latency-sensitive clips and streamed Vorbis for large music/ambience. Normalize channel layout, loop points and usage. Emit deterministic ranges and reject corrupt media, invalid loops, unsupported required content, clipping beyond recovered policy, or target codec mismatch. OGG/WMA and other unevidenced source routes are absent.
6. Behavioral evidence and disposition
Inspect shipped audio, sound/music definitions, oracle audio calls and live mixing behavior. Recover usage classification, attenuation, concurrency, priority/stealing, randomness, delays, looping, fades, ducking, pause behavior, listener choice and music transitions. Bevy supplies mechanics, not these rules.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Cues create/update ordinary Bevy playback entities. Positional effects follow their world transforms; UI/music are nonspatial. Usage volumes, loop points, priority, limits, pause and transitions follow recovered policy. Missing optional clips fail quietly with a typed diagnostic; required music/voice conversion fails preflight.
8. Performance contract
Mapped lookup/block selection/handle cloning allocate zero. Playback start and decoder creation are explicit bounded exceptional operations; settled playback must not allocate per frame after decoder/output buffers stabilize. Stream only bounded upcoming blocks. Track Rust heap, mapped faults, active Bevy sinks, decoder buffers, underruns, voices and start latency. There is no OpenZT2 mix buffer or full decoded-cache copy.
9. Acceptance criteria
Four files/API; resident/streamed conversion, corrupt range/loop rejection, zero-allocation mapped lookup, and Bevy fixtures for UI, positional, loop, pause, priority, fade/transition and cleanup. Source review must find no custom mixer, device backend, voice registry, or second playback state.
10. Required handoff
Report exact Bevy audio/codec features, mapped Decodable lifetime strategy,
usage policy, A13 synchronization surface, root registrations, recovered audio
rules, and allocation/fault/underrun measurements.
A12 — System Font Integration
Outcome and evidence boundary
The official archives and loose installation contain no TTF, OTF, FNT, bitmap
font, or other authored font payload. The recovered executable creates fonts
through the operating-system font API (D3DXCreateFontA). A12 therefore does
not define a source frontend, native rkyv font family, pack entry, mapped asset,
or custom glyph cache. Adding any of those would duplicate bytes that do not
exist in the original content and would invent a mod contract without evidence.
OpenZT2 maps the recovered UI font aliases, sizes, weights, and fallback policy
onto Bevy's ordinary text components and platform/system font integration.
Bevy owns shaping, glyph caching, atlas allocation, layout, and rendering. G02
projects the compiled A02 text/style facts directly into Text, TextFont, and
TextColor; it does not attach an OpenZT2 font wrapper component.
Runtime and performance contract
Font selection happens once when a native UI document is projected. Unchanged text must not be reshaped or reallocated by OpenZT2 per frame. Font-face heap, glyph-cache heap, and atlas GPU memory are backend-owned measurements, not mapped-pack residency. There is no source-font parsing or filesystem lookup in the game content path.
Mod boundary
The current content contract supports the fixed font vocabulary evidenced by the original game. A future native editor may deliberately introduce authored font assets, but that is a new OpenZT2 feature and must receive its own measured contract; preflight must not silently accept unevidenced original formats.
Acceptance criteria
- Source inventory continues to prove no official font files.
- No
FontArchive,MappedFontArchive,FontAsset, orozfontloader exists. - Representative shell, HUD, and localized text use ordinary Bevy text nodes.
- Missing system aliases follow one explicit fallback chain and produce a diagnostic rather than an injected archive font.
A13 — Video stream
1. Identity and outcome
Preflight converts original movies into one seekable AV1 elementary stream with
an OpenZT2 timestamp/keyframe index and an A11 audio reference. In the game,
dav1d decodes into a bounded reusable picture pool and Bevy/wgpu presents those
frames. OpenZT2 does not contain a video codec or generic media framework.
2. Exclusive ownership and mandatory dependencies
The package worker owns native/src/families/video.rs, converter/src/families/video.rs,
game/src/assets/video.rs, and preflight/tests/video_contract.rs only. Root
adds dav1d = 0.11 and its pinned native packaging, plus the approved
FFmpeg/libav or rav1e preflight conversion dependency.
The preflight media dependency owns original container demux, source codec
decode, scaling/colorspace conversion and AV1 encode. Dav1d owns runtime AV1
entropy/picture decode. Bevy/wgpu own textures and GPU copies. The worker must
not write a codec, runtime container parser, general media graph, or per-frame
texture allocator. A mandatory dependency failure is configured or narrowly
forked before the bespoke baseline at 68a4c481 is considered.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
VIDEO_ARCHIVE_VERSION |
constant | u32 = 1 |
Layout version. |
VideoArchive |
root | { version:u32, target:TargetProfile, id:AssetId, codec:VideoCodec, width:u32, height:u32, time_base:[u32;2], frame_count:u64, duration_ticks:u64, color:VideoColor, audio:AssetId, seek:Vec<VideoSeekBlock>, payload:Vec<u8> } |
Fixed dimensions and AV1 payload; no source container. |
VideoCodec |
enum | Av1 |
One runtime decoder contract. |
VideoSeekBlock |
record | { first_frame:u64, frame_count:u32, first_tick:u64, duration_ticks:u64, keyframe:bool, bytes:AssetRange } |
Sorted, bounded, packet-aligned range. |
MappedVideoArchive |
owning view | new(MappedAsset)->io::Result<Self> plus archived/block accessors |
Validated once, mapping-owned. |
compile_video |
producer | fn compile_video(input:&VideoSource,target:TargetProfile)->io::Result<Vec<u8>> |
Deterministic invocation of the approved media stack and native index writer. |
VideoAsset |
Bevy asset | { archive:MappedVideoArchive } |
Mapped packets. |
VideoPlayer |
component | { video:Handle<VideoAsset>, elapsed_ticks:u64, state:VideoPlaybackState, looped:bool, audio_entity:Option<Entity> } |
One playback entity. |
VideoPlaybackState |
enum | Playing, Paused, Finished |
Closed state. |
VideoDecodePool |
private bounded resource | fixed dav1d decoder slots, custom picture allocator surfaces, staging and GPU texture handles | Capacity established before playback; never authoritative media state. |
VideoAssetPlugin |
plugin | unit struct | Registers asset, decoder pool and focused start/decode/upload/sync/cleanup systems. |
4. Data layout and ECS ownership
Seek metadata and AV1 packets remain mapped. Dav1d receives slices from the
mapped block through an input owner that retains Arc<MappedPack>. Its custom
picture allocator recycles aligned frame storage. GPU textures/staging buffers
are created per pool slot and reused. VideoPlayer and A11's playback entity
are the only live state; there is no media session graph.
5. Preflight producer contract
Accept only Bink (.bik), the movie format established by the executable's
Bink imports and loader calls. Use the approved media dependency to demux/decode
Bink, normalize dimensions, frame rate, pixel/color metadata and orientation,
split audio into A11, encode AV1 with deterministic target settings, and emit
packet/keyframe/timestamp blocks. Reject unsupported required source media,
invalid timing, excessive dimensions, missing keyframes, A/V duration mismatch,
or decoder-profile incompatibility. Never invoke media conversion in the game.
6. Behavioral evidence and disposition
Inspect Bink references, oracle Bink playback calls and live original for aspect/stretch, skippability, looping, end transitions, audio routing, pause/focus behavior and required seek accuracy.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Players start only after asset/decoder readiness, follow authored timestamps, stay synchronized to A11, skip/loop/end exactly once, and release slots on despawn or world transition. Back-pressure drops or delays presentation by an explicit policy; it never grows the pool.
8. Performance contract
Mapped seek/block lookup allocates zero. Decoder initialization and pool setup are bounded exceptional work. After warm-up, decoding reuses dav1d picture allocations and GPU staging/textures with zero recurring Rust allocation caused by OpenZT2. Prefetch only bounded upcoming blocks. Track decoder heap, mapped faults, queued packets/pictures, upload bytes, GPU texture bytes, decode/upload latency and dropped frames independently.
9. Acceptance criteria
Four files/API; deterministic conversion fixture, corrupt index/timing/profile rejection, seek lookup, decoder-pool reuse, A/V synchronization, skip/loop/end, and GPU accounting. Source review must find no codec algorithm, runtime source container parser, unbounded frame queue, or per-frame texture creation.
10. Required handoff
Report pinned dav1d/native packaging, preflight media dependency/version, picture allocator and mapped-input lifetime, Bevy texture format/upload path, A11 synchronization, root registration and allocation/fault/GPU/frame results.
A14 — World Definitions
1. Identity and outcome
This package produces the immutable typed catalogues used by every world/gameplay package: placeables, facilities, staff/jobs, guests, topology, biomes/tools, health, information, progression, expansion systems, modes/camera, simulation timing and environment. It is deliberately a set of narrow tables, not a generic entity definition graph. Integration wave 1; depends on A09 and stable IDs from the presentation families.
2. Exclusive ownership
This package owns native/src/families/world_definitions.rs,
converter/src/families/world_definitions.rs,
game/src/assets/world_definitions.rs, and
preflight/tests/world_definitions_contract.rs only. Everything else is
read-only. Root owns module/asset/plugin registration and any shared-type moves.
3. Frozen public contract
Root and common construction catalogues
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
WORLD_DEFINITIONS_VERSION |
constant | u32 = 1 |
Bumped for incompatible table/vocabulary change. |
WorldDefinitionsArchive |
root | fields are the exact table list under §4 | One immutable resolved catalogue; definition tables sorted by ID. |
ObjectDefinition |
record | { id:AssetId, kind:ObjectKind, name_key:AssetId, description_key:AssetId, prefab:AssetId, icon:AssetId, placeable:u32, price_cents:i64, upkeep_cents_per_day:i32, tags:ObjectTags, affordances:AffordanceFlags } |
placeable == u32::MAX when not placeable. |
ObjectKind |
enum | Scenery, Shelter, Food, Water, Enrichment, DonationBox, Bin, Bench, Facility, Fence, Gate, Path, Staff, Guest, Tank, ShowStage, Station, Vehicle, Laboratory |
Exact static classification. |
ObjectTags |
bitflags u64 |
ANIMAL_USABLE, GUEST_USABLE, STAFF_ONLY, INDOOR, OUTDOOR, WATER_PLACEABLE, WALL_PLACEABLE, PATH_REQUIRED, DONATION_ACCEPTOR, VIEWABLE, DELETABLE, SAVE_RELEVANT |
No opaque tag bits. Unknown semantic source tag rejects conversion. |
AffordanceFlags |
bitflags u64 |
EAT, DRINK, REST, PLAY, SHELTER, VIEW, BUY, DONATE, LEARN, CLEAN, REPAIR, TREAT, TRAIN, BOARD, DISEMBARK, OPERATE |
Fixed interaction capabilities. |
PlaceableDefinition |
record | { id:AssetId, footprint:TableRange, pivot_cm:[i16;2], rotation_increment_degrees:u16, constraints:PlacementConstraints, max_slope_permille:u16, min_clearance_cm:u16, price_cents:i64, unlock:AssetId, entrances:TableRange } |
Generates ArchivedPlaceableDefinition consumed directly by G10. |
FootprintCellFlags |
bitflags u16 |
OCCUPIED, WALKABLE, ENTRANCE, WATER, FOUNDATION_REQUIRED |
Exact construction/topology meaning; no opaque source bits. |
FootprintCell |
record | { offset:[i16;2], flags:FootprintCellFlags } |
Canonical row-major local cell. |
EntranceDefinition |
record | { position_cm:[i16;3], forward_snorm:[i16;3], purpose:EntrancePurpose } |
Local docking point. |
EntrancePurpose |
enum | Guest, Staff, Service, Vehicle, Animal |
Fixed docking class. |
PlacementConstraints |
bitflags u32 |
REQUIRE_PATH, REQUIRE_HABITAT, REQUIRE_WATER, REQUIRE_LAND, REQUIRE_WALL, REQUIRE_FLAT, ALLOW_OVERLAP_SCENERY, ALLOW_ELEVATED |
Ordered evaluation policy remains G10. |
FacilityDefinition |
record | { id:AssetId, object:AssetId, service:ServiceKind, capacity:u16, service_ticks:u32, price_cents:i32, staffing:StaffKind, inventory_capacity:u16, inventory_units_per_service:u16, inventory_restock_per_zoo_day:u16 } |
Static service facts; all inventory fields are zero for non-stocked services. |
ServiceKind |
enum | Food, Drink, Toilet, Gift, Education, Adoption, Transport, Maintenance, Show, Laboratory |
Fixed transaction vocabulary. |
MaintenanceDefinition |
record | { id:AssetId, object:AssetId, initial_condition_permille:u16, deterioration_per_zoo_day_permille:u16, repair_below_permille:u16, waste_capacity_units:u16, empty_at_units:u16, litter_definition:Option<AssetId>, litter_local_offset_cm:[i16;3], service_effects:TableRange } |
G36 facts only for evidenced maintainable/dirty objects; no synthesized defaults. |
ServiceMaintenanceEffect |
record | { service:AssetId, condition_loss_permille:u16, contained_waste_units:u16, loose_litter_units:u16 } |
Owner-contiguous range on MaintenanceDefinition; service ID is immutable G18 service data identity. |
CleanlinessPolicy |
record | { condition_weight:u16, waste_weight:u16, litter_weight:u16, litter_reference_units:u32 } |
Exactly one resolved G36/G23 integer aggregation policy; total weight nonzero. |
Staff and guests
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
StaffKind |
enum | None, Keeper, Maintenance, Educator, Veterinarian, Entertainer |
Static role. |
StaffJobKind |
enum | Feed, RefillWater, CleanHabitat, EmptyBin, SweepLitter, Repair, Treat, Educate, Entertain, Tranquilize, Capture, MaintainTank, OperateShow |
Exact job vocabulary; unknown job blocks conversion. |
StaffJobFlags |
bitflags u32 |
FEED, REFILL_WATER, CLEAN_HABITAT, EMPTY_BIN, SWEEP_LITTER, REPAIR, TREAT, EDUCATE, ENTERTAIN, TRANQUILIZE, CAPTURE, MAINTAIN_TANK, OPERATE_SHOW |
Capability mask with no opaque bits. |
StaffDefinition |
record | { id:AssetId, object:AssetId, role:StaffKind, wage_cents_per_day:i32, move_speed_mps:f32, permitted_jobs:StaffJobFlags, job_overrides:TableRange } |
Overrides index asset_links containing StaffJobDefinition IDs; finite speed. |
StaffJobDefinition |
record | { id:AssetId, kind:StaffJobKind, duration_ticks:u32, interaction_radius_cm:u16, capability:StaffJobFlags, effect:StaffJobEffect, priority:i16 } |
Immutable job duration/effect/capability. |
StaffJobEffect |
enum | Fill{amount:u16}, Clean{amount:u16}, Repair{amount:u16}, Treat{treatment:AssetId}, Educate{amount:u16}, Tranquilize{definition:AssetId}, Capture, Operate |
Fixed mutation request vocabulary. |
GuestNeedKind |
enum | Hunger, Thirst, Energy, Restroom, Comfort |
Exact G17 live-need vocabulary. Additional source need is an evidence/schema gate. |
GuestDefinition |
record | { id:AssetId, object:AssetId, starting_cash_cents:[i32;2], radius_cm:u16, move_speed_mps:f32, acceleration_mps2:f32, patience_ticks:u32, needs:TableRange, destination_weights:TableRange, reactions:TableRange, memory:GuestMemoryPolicy } |
Complete guest spawn/archetype defaults; navigation scalars are finite positive source values. |
GuestArrivalDefinition |
record | { id:AssetId, rules:TableRange, archetypes:TableRange } |
One preflight-resolved arrival policy selected by A19 MapRecord; rules and weighted archetypes are owner-contiguous. |
GuestArrivalRule |
record | { minimum_fame_half_stars:u8, maximum_admission_cents:i64, interval_ticks:u32, group_size:[u16;2], population_cap:u32 } |
Sorted most-specific-first; interval is nonzero G32 ticks and group bounds are ordered/nonzero. |
GuestArchetypeWeight |
record | { guest:AssetId, weight:u32 } |
guest resolves one GuestDefinition; each policy has positive total weight. |
GuestNeedDefinition |
record | { kind:GuestNeedKind, initial_permille:u16, decay_per_tick_q16:i32, reconsider_threshold:u16 } |
0..=1000 values; signed 16.16 permille/G32 tick with residual-preserving application. |
GuestVisitPurpose |
enum | View, Food, Drink, Restroom, Rest, Education, Shop, Exit, Show, Tour |
G17 must use/translate this exact native vocabulary without duplicate string keys. |
GuestDestinationWeight |
record | { purpose:GuestVisitPurpose, weight:i16, need:GuestNeedKind, minimum_need:u16, radius_cm:u32 } |
Deterministic selection inputs. |
GuestMemoryKind |
enum | AnimalView, Facility, Education, Crowding, Litter, Scenery, Danger, Show, Tour |
Exact memory/reaction category. |
GuestReactionDefinition |
record | { kind:GuestMemoryKind, satisfaction_delta:i16, education_delta:i16, retention_ticks:u64 } |
Deltas are signed permille. |
GUEST_MEMORY_CAPACITY |
constant | usize; implementation defines a numeric literal recovered before schema implementation begins |
Evidence gate: worker first recovers it from complete shipped definitions/oracle; delivered Rust must contain the justified literal, never a placeholder or guess. |
GuestMemoryPolicy |
record | { capacity:u16, retention_ticks:u64, replacement:MemoryReplacement } |
capacity <= GUEST_MEMORY_CAPACITY; preflight rejects excess. |
MemoryReplacement |
enum | Oldest, LowestAbsoluteValue |
Fixed inline-ring replacement behavior recovered from evidence. |
Topology, terrain and health
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
TraversalMask |
bitflags u16 |
GUEST, STAFF, ANIMAL, VEHICLE, AIR, WATER |
Exact topology traversal classes. |
FenceDefinition |
record | { id:AssetId, object:AssetId, height_cm:u16, strength:u16, blocks:TraversalMask, gate:AssetId, post_prefab:AssetId } |
Static topology facts. |
PathDefinition |
record | { id:AssetId, object:AssetId, width_cm:u16, capacity:u16, speed_permille:u16, elevated:bool, support_prefab:AssetId } |
Path facts. |
BiomeDefinition |
record | { id:AssetId, name_key:AssetId, terrain_material:AssetId, cliff_material:AssetId, water_material:AssetId, foliage:TableRange, temperature_c:[i16;2], humidity_permille:[u16;2] } |
Foliage indexes asset_links. |
BrushDefinition |
record | { id:AssetId, radius_cm:[u16;2], strength_permille:u16, falloff:BrushFalloff, operation:BrushOperation } |
Editing policy. |
BrushFalloff |
enum | Constant, Linear, Smooth |
Fixed shape. |
BrushOperation |
enum | Raise, Lower, Flatten, Smooth, PaintBiome, AddWater, RemoveWater |
Fixed tool vocabulary. |
DiseaseDefinition |
record | { id:AssetId, eligible_species:TableRange, check_interval_ticks:u32, chance_per_check:u32, severity_per_tick_q16:i32, vitality_per_tick_q16:i32, symptoms:TableRange, treatments:TableRange, fatal_threshold:u16, hint_thresholds:[u16;3] } |
Probabilities use 0..=u32::MAX; rates are signed 16.16 permille/G32 tick; ranges index asset_links. |
TreatmentDefinition |
record | { id:AssetId, disease:AssetId, required_staff:StaffKind, duration_ticks:u32, severity_delta:i16, vitality_delta:i16, research:AssetId } |
Typed cure facts. |
TranquilizerEligibility |
bitflags u8 |
ESCAPED, RAMPAGING, CONTAINED |
Exact target-state eligibility. |
TranquilizerDefinition |
record | { id:AssetId, duration_ticks:u32, range_cm:u32, eligible_states:TranquilizerEligibility, recovery_ticks:u32 } |
Fixed tranquilizer rule. |
RampageRule |
record | { id:AssetId, species:AssetId, welfare_below:u16, disease_above:u16, probability:u32, minimum_ticks:u32, behavior:AssetId } |
Changed-fact rule; no runtime guessed chance. |
Information and progression
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
CatalogueFilterFlags |
bitflags u16 |
PURCHASABLE, BUILDABLE, ADOPTABLE, HIREABLE, EXPANSION, MODDED, HIDDEN_UNTIL_UNLOCKED |
Exact native filter facts; current unlocked state is live G23 data. |
CatalogueEntry |
record | { id:AssetId, definition:AssetId, category:CatalogueCategory, order:u16, filters:CatalogueFilterFlags, name_key:AssetId, icon:AssetId } |
Stable sorted buy/information index. |
CatalogueCategory |
enum | Animals, Biomes, Scenery, Facilities, Fences, Paths, Staff, Tanks, Shows, Transport |
Complete fixed UI catalogue grouping; G21 consumes this owner. |
ZoopediaEntry |
record | { id:AssetId, subject:AssetId, title_key:AssetId, body_key:AssetId, image:AssetId, related:TableRange, order:u16 } |
Related indexes asset_links; no runtime source markup parse. |
ResearchDefinition |
record | { id:AssetId, name_key:AssetId, cost_cents:i64, duration_ticks:u64, prerequisites:TableRange, unlocks:TableRange } |
G32 ticks; ranges index asset_links; DAG. |
UnlockDefinition |
record | { id:AssetId, target:AssetId, requirement:UnlockRequirement } |
Static progression rule. |
UnlockRequirement |
enum | Always, Fame(u16), Research(AssetId), Scenario(AssetId), Award(AssetId) |
Fixed rule. |
RatingDefinition |
record | { id:AssetId, inputs:TableRange, minimum:u16, maximum:u16, smoothing_ticks:u32 } |
Inputs index rating_inputs. |
RatingInput |
record | { kind:RatingInputKind, weight:i16, minimum:i32, maximum:i32 } |
Fixed-point normalized contribution. |
RatingInputKind |
enum | AnimalWelfare, GuestSatisfaction, Education, Variety, Scenery, Finance, Cleanliness |
Fixed rating inputs. |
FameThreshold |
record | { level:u16, minimum_rating:u16, minimum_guests:u32, unlocks:TableRange } |
Ordered ascending; links index asset_links. |
AwardDefinition |
record | { id:AssetId, name_key:AssetId, description_key:AssetId, conditions:TableRange, reward_cents:i64, unlocks:TableRange } |
Conditions index award_conditions. |
AwardCondition |
record | { kind:RatingInputKind, comparison:Comparison, value:i32, duration_ticks:u64 } |
Typed threshold in G32 time. |
Comparison |
enum | AtLeast, AtMost, Equal |
Shared native comparison. |
Expansion definitions
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
TankDefinition |
record | { id:AssetId, wall:AssetId, floor_material:AssetId, water_material:AssetId, min_depth_cm:u16, max_depth_cm:u16, capacity_litres_per_cell:u32, filtration_per_day:u32 } |
Static tank construction/care. |
AquaticRequirement |
record | { species:AssetId, min_depth_cm:u16, salinity_permille:[u16;2], temperature_c:[i16;2], water_quality_min:u16, land_fraction_permille:[u16;2] } |
One per aquatic species. |
ShowStageDefinition |
record | { id:AssetId, object:AssetId, performer_slots:u16, audience_capacity:u16, schedule_slots:u16, supported_tricks:TableRange, admission_cents:i32 } |
Trick links index asset_links. |
TrickDefinition |
record | { id:AssetId, species:TableRange, animation:AssetId, training_ticks:u32, difficulty:u16, welfare_cost:u16, entertainment:u16 } |
Species links index asset_links. |
ShowRuleDefinition |
record | { id:AssetId, minimum_tricks:u16, duration_ticks:[u32;2], cooldown_ticks:u32, payout_per_guest_cents:i32 } |
Fixed show execution facts. |
StationDefinition |
record | { id:AssetId, object:AssetId, track:AssetId, dock_offset_cm:[i16;3], capacity:u16, dwell_ticks:u32 } |
Transport docking. |
TrackDefinition |
record | { id:AssetId, object:AssetId, vehicle:AssetId, width_cm:u16, min_radius_cm:u16, max_grade_permille:u16, speed_cm_per_tick:u16 } |
Track construction/kinematics. |
VehicleDefinition |
record | { id:AssetId, object:AssetId, seats:u16, speed_cm_per_tick:u16, boarding_ticks:u32, running_cost_cents_per_day:i32 } |
Vehicle facts. |
TourViewDefinition |
record | { id:AssetId, subject:AssetId, radius_cm:u32, base_score:i16, dwell_ticks:u32, occlusion_required:bool } |
Authored tour scoring. |
FossilSetDefinition |
record | { id:AssetId, species:AssetId, pieces:TableRange, completion_unlock:AssetId } |
Pieces index asset_links. |
FossilPieceDefinition |
record | { id:AssetId, set:AssetId, model:AssetId, slot:AssetId, discovery_weight:u16 } |
Weighted discovery. |
FossilSlotDefinition |
record | { id:AssetId, transform:[[f32;4];4], tolerance_cm:u16, tolerance_degrees:u16 } |
Assembly target. |
CloneLabDefinition |
record | { id:AssetId, object:AssetId, process_ticks:u32, cost_cents:i64, success_probability:u32, required_research:AssetId, spawn_offset_cm:[i16;3] } |
Cloning contract; successful animal placement is relative to the lab and validated inside its G09 habitat. |
Modes, time and environment
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ModeKind |
enum | GuestView, FirstPerson, Training, FossilSearch, FossilAssembly, Cloning, Photo, ShowEdit |
Exact G31 mode list. |
ModeActionFlags |
bitflags u32 |
MOVE_FORWARD, MOVE_BACK, STRAFE_LEFT, STRAFE_RIGHT, LOOK, PRIMARY, SECONDARY, CONFIRM, CANCEL, ZOOM, ROTATE |
Native authored mode actions translated by G31/G03, not Bevy-linked. |
ModeToolFlags |
bitflags u8 |
PAUSE_SIMULATION, HIDE_HUD, LOCK_SUBJECT, RETURN_CAMERA_ON_EXIT |
Exact mode lifecycle policy; every source flag is corpus-evidenced. |
ModeToolDefinition |
record | { id:AssetId, mode:ModeKind, prefab:AssetId, cursor:AssetId, camera:AssetId, allowed_actions:ModeActionFlags, flags:ModeToolFlags } |
Fixed native action eligibility/lifecycle. |
CameraTuningDefinition |
record | { id:AssetId, projection:CameraProjectionKind, fov_y_radians:f32, near_m:f32, far_m:f32, offset_m:[f32;3], pitch_radians:[f32;2], yaw_radians:[f32;2], zoom_m:[f32;2], collision_radius_cm:u16 } |
Finite evidenced camera profile. |
CameraProjectionKind |
enum | Perspective, Orthographic |
G06/G31 use exact identity. |
SimulationTimingDefinition |
record | { fixed_hz:u16, ticks_per_day:u32, speed_multipliers:TableRange, calendar:CalendarPolicy } |
Exactly one root value; all numbers evidence-gated. |
SpeedMultiplier |
record | { numerator:u16, denominator:u16 } |
Ordered speed tier; positive reduced rational. |
CalendarPolicy |
record | { epoch_year:u16, epoch_month:u8, epoch_day:u8, month_lengths:[u8;12], leap:LeapPolicy } |
Valid calendar used by G32. |
LeapPolicy |
enum | None, Gregorian |
No guessed policy. |
EnvironmentDefinition |
record | { id:AssetId, light:TableRange, fog:TableRange, sky:TableRange, weather:TableRange, transitions:TableRange, ambient:TableRange, initial_weather:AssetId, wind_mps:[f32;2] } |
Light/fog/sky/transitions index typed owner-grouped tables; weather/ambient index asset_links so ID tables remain searchable. |
LightKeyframe |
record | { environment:AssetId, day_fraction:u16, direction_snorm:[i16;3], color_unorm:[u16;3], illuminance_lux:f32 } |
Sorted fraction 0..=65535. |
FogKeyframe |
record | { environment:AssetId, day_fraction:u16, color_unorm:[u16;3], start_cm:u32, end_cm:u32 } |
Sorted, end >= start. |
SkyKeyframe |
record | { environment:AssetId, day_fraction:u16, texture:AssetId, tint_unorm:[u16;4], rotation_snorm:i16 } |
Sorted, resolved texture. |
WeatherDefinition |
record | { id:AssetId, environment:AssetId, duration_ticks:[u32;2], wind_mps:[f32;2], light_multiplier:u16, fog_multiplier:u16, audio:AssetId, effect:AssetId, welfare_delta:i16 } |
Duration/ranges valid; gameplay delta only if evidenced. |
WeatherTransitionRule |
record | { environment:AssetId, from:AssetId, to:AssetId, probability:u32, transition_ticks:u32, day_fraction:[u16;2] } |
Probability is 0..=u32::MAX. |
AmbientClass |
enum | Air, Ground, Water |
Exact G33 class. |
AmbientSpawnDefinition |
record | { id:AssetId, environment:AssetId, prefabs:TableRange, biomes:TableRange, class:AmbientClass, day_fraction:[u16;2], population:[u16;2], spawn_interval_ticks:[u32;2], radius_cm:[u32;2], lifetime_ticks:[u32;2], probability:u32, audio:AssetId, effect:AssetId } |
Prefab/biome links index asset_links; all bounds ordered. |
Mapped/producer/Bevy surface
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
MappedWorldDefinitions / access_world_definitions / access_world_definitions_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedWorldDefinitionsArchive; one typed find_<table>(&self,AssetId)->Option<&Archived<Record>> per ID table including find_maintenance_by_object; timing()->&ArchivedSimulationTimingDefinition; cleanliness_policy()->&ArchivedCleanlinessPolicy; fn access_world_definitions(&[u8])->io::Result<&ArchivedWorldDefinitionsArchive>; unsafe fn access_world_definitions_validated(&[u8])->&ArchivedWorldDefinitionsArchive |
Validate once, own mapping. |
DefinitionSource |
input | { virtual_path:String, bytes:Vec<u8>, family:DefinitionFamily } |
Resolved preflight source. |
DefinitionFamily |
enum | Object, Placeable, Facility, Maintenance, Staff, StaffJob, Guest, Fence, Path, Biome, Brush, Disease, Treatment, Tranquilizer, Rampage, Catalogue, Zoopedia, Research, Unlock, Rating, Fame, Award, Tank, AquaticRequirement, ShowStage, Trick, ShowRule, Station, Track, Vehicle, TourView, FossilSet, FossilPiece, FossilSlot, CloneLab, ModeTool, Camera, SimulationTiming, Environment, Light, Fog, Sky, Weather, WeatherTransition, AmbientSpawn |
Preflight routing only; not archived runtime polymorphism. |
compile_world_definitions |
producer | fn compile_world_definitions(inputs:&[DefinitionSource], resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> |
Pure deterministic producer. |
WorldDefinitionsAsset |
Bevy asset | { archive:MappedWorldDefinitions } deriving Asset, TypePath, Clone |
Sole mapped catalogue owner. |
WorldDefinitionsLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=WorldDefinitionsAsset> for ozdefs via path + get_owned |
No reader copy/source objects. |
WorldDefinitionsAssetPlugin |
plugin | unit struct | Registers asset/loader only. |
4. Data layout or ECS ownership
WorldDefinitionsArchive has this exact root shape and field order:
pub struct WorldDefinitionsArchive {
version: u32,
target: TargetProfile,
objects: Vec<ObjectDefinition>,
placeables: Vec<PlaceableDefinition>,
footprint_cells: Vec<FootprintCell>,
entrances: Vec<EntranceDefinition>,
facilities: Vec<FacilityDefinition>,
maintenance_definitions: Vec<MaintenanceDefinition>,
service_maintenance_effects: Vec<ServiceMaintenanceEffect>,
cleanliness_policy: CleanlinessPolicy,
staff: Vec<StaffDefinition>,
staff_jobs: Vec<StaffJobDefinition>,
guests: Vec<GuestDefinition>,
guest_arrivals: Vec<GuestArrivalDefinition>,
guest_arrival_rules: Vec<GuestArrivalRule>,
guest_archetype_weights: Vec<GuestArchetypeWeight>,
guest_needs: Vec<GuestNeedDefinition>,
guest_destination_weights: Vec<GuestDestinationWeight>,
guest_reactions: Vec<GuestReactionDefinition>,
fences: Vec<FenceDefinition>,
paths: Vec<PathDefinition>,
biomes: Vec<BiomeDefinition>,
brushes: Vec<BrushDefinition>,
diseases: Vec<DiseaseDefinition>,
treatments: Vec<TreatmentDefinition>,
tranquilizers: Vec<TranquilizerDefinition>,
rampage_rules: Vec<RampageRule>,
catalogue: Vec<CatalogueEntry>,
zoopedia: Vec<ZoopediaEntry>,
research: Vec<ResearchDefinition>,
unlocks: Vec<UnlockDefinition>,
rating_definitions: Vec<RatingDefinition>,
rating_inputs: Vec<RatingInput>,
fame_thresholds: Vec<FameThreshold>,
awards: Vec<AwardDefinition>,
award_conditions: Vec<AwardCondition>,
tanks: Vec<TankDefinition>,
aquatic_requirements: Vec<AquaticRequirement>,
show_stages: Vec<ShowStageDefinition>,
tricks: Vec<TrickDefinition>,
show_rules: Vec<ShowRuleDefinition>,
stations: Vec<StationDefinition>,
tracks: Vec<TrackDefinition>,
vehicles: Vec<VehicleDefinition>,
tour_views: Vec<TourViewDefinition>,
fossil_sets: Vec<FossilSetDefinition>,
fossil_pieces: Vec<FossilPieceDefinition>,
fossil_slots: Vec<FossilSlotDefinition>,
clone_labs: Vec<CloneLabDefinition>,
mode_tools: Vec<ModeToolDefinition>,
cameras: Vec<CameraTuningDefinition>,
simulation_timing: SimulationTimingDefinition,
speed_multipliers: Vec<SpeedMultiplier>,
light_keyframes: Vec<LightKeyframe>,
fog_keyframes: Vec<FogKeyframe>,
sky_keyframes: Vec<SkyKeyframe>,
weather: Vec<WeatherDefinition>,
weather_transitions: Vec<WeatherTransitionRule>,
ambient_spawns: Vec<AmbientSpawnDefinition>,
environments: Vec<EnvironmentDefinition>,
asset_links: Vec<AssetId>,
}
Every TableRange addresses its declared typed table or asset_links; byte
AssetRange is unused. Tables are rkyv, immutable, little-endian target data.
ID lookup tables are sorted by ID; child tables addressed only by TableRange
are grouped by owner and then authored order. catalogue is instead the stable
category/order definition index required by G21/G23.
Live cash, jobs, memories, diseases, progress, modes, weather and clocks remain
small ECS facts in natural gameplay packages. Static definitions are never
copied into components/resources.
5. Preflight producer contract
Accept the resolved entity/unit/object/fence/path/biome/AI/disease/research/
expansion/mode/camera/time/environment XML and data families. Normalize canonical
paths and aliases before AssetId; flatten inheritance/templates and selected
pack precedence; map every behavior-bearing property into the exact typed table;
convert authored simulation durations to G32 ticks and continuous guest/disease
rates to signed Q16 per tick before writing records; close dependencies and sort
deterministically. Integer *_cents_per_day fields are deliberate calendar
charges applied exactly once on G32 ZooDayAdvanced, not continuous rates and
require no runtime division. filtration_per_day is likewise an exact integer
daily tank-care capacity consumed on that event, not a continuous float rate.
Preflight derives the stable catalogue/Zoopedia index, guest inline-memory bound,
fixed timing/calendar, environment curves and ambient eligibility. Reject unknown
semantic properties/enums, opaque flag bits, missing required prefab/name/assets,
duplicate IDs, cycles, nonfinite or unordered ranges, table overflow and any
guest definition exceeding GUEST_MEMORY_CAPACITY. Guest-arrival conversion
also rejects empty policies, zero or overflowing weight totals, invalid guest
IDs, overlapping rules without recovered precedence, zero intervals, unordered
group bounds, absence of a terminal catch-all rule, and a policy not referenced
by any selected A19 map. The producer
worker must resolve the memory-capacity literal before implementing/serializing the schema; it may
not deliver a placeholder. Diagnostics include family,
source path and property. Conversion never emits a generic fallback record.
Maintenance conversion additionally rejects permille overflow, zero capacity
with nonzero waste rules, empty_at_units > waste_capacity_units, duplicate
service IDs within one owner, loose litter without a resolved litter definition,
zero total cleanliness weight, and a zero litter reference with nonzero litter
weight. Its exact daily cadence/effects/thresholds are evidence gates from
shipped definitions/oracle, never defaulted by the producer.
6. Behavioral evidence and disposition
Search all selected shipped entities, units, objects, AI/task, disease,
biome, research, expansion, camera/mode, environment/light/weather/ambient and
catalog/Zoopedia sources. Search oracle typed-object/entity/type registry,
placement/facility/staff/guest/disease/progression/tank/show/transport/fossil/
camera/time/environment classes and analysis TSV domains. Mine commit d5657e5a
only for already-found source keys/formulas in corresponding domain modules.
Live observation settles presentation and timing where source/oracle do not.
Evidence gates include the complete definition and flag vocabularies, units,
guest need/reaction/memory retention and the numeric GUEST_MEMORY_CAPACITY,
staff job effects/durations, disease/rampage formulas, catalogue ordering,
rating/fame/award formulas, expansion rules, camera values, fixed frequency,
ticks/day, speed tiers/calendar, light/fog/sky interpolation, weather transitions
and ambient bounds. Workers must recover these; familiar defaults are forbidden.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Every player-selectable/static gameplay concept resolves to exactly one narrow
record and required dependencies. Source inheritance and overrides are absent at
runtime. G10 directly receives ArchivedPlaceableDefinition; G16/G17/G20/G21/
G23/G25–G27/G29/G31–G33 use the named mapped records above. Guest memories use
inline fixed-capacity G17 storage and definition capacity/retention. G32 is the
only live clock; environments and expansion state are ordinary entities. Unknown
mod semantics fail conversion until assigned a typed owner and schema revision.
8. Performance contract
Validation is O(total records+links); each typed ID lookup O(log n); range traversal O(len), all zero allocation. Stable catalog indexes avoid runtime sorting/hashing. No per-frame catalogue clone/string/property lookup. Consumers use changed filters, fixed schedules and bounded spatial indexes. Regression tests count zero allocations for every typed lookup and representative range walk, track mapped pages/faults, and attribute referenced GPU bytes to A04–A08.
9. Acceptance criteria
The four files export every symbol and exact root field above. Fixtures cover at least one valid record from every table, inheritance/override flattening, cross-family dependencies, timing/environment links and guest memory bounds. Tests reject cycles, dangling links, unknown semantic flags/need/job/mode, nonfinite/bad ranges and excess guest memory; mapped typed access is allocation-free. No live gameplay, source registry, generic value bag, stubs or unresolved silent defaults are delivered.
10. Required handoff
Report files/API, parser dependencies, module/asset/loader/plugin registration, the recovered numeric memory/timing/calendar constants, exact downstream symbol alignment, evidence still unresolved and allocator/mapping checks. Root may move genuinely shared enums into the frozen native contract and must remove any temporary family router after integration; no engine adapter is retained.
A15 — Scene Prefab
1. Identity and outcome
This package produces flat, directly iterable entity/component spawn records for scenery, facilities, animals, staff, guests and map dressing. Source scene graphs are flattened; G04 creates ordinary ECS entities without a prefab VM or reflected property bag. Integration wave 1; depends on A04, A07 and A14.
2. Exclusive ownership
This package owns native/src/families/scene_prefab.rs,
converter/src/families/scene_prefab.rs, game/src/assets/scene_prefab.rs, and
preflight/tests/scene_prefab_contract.rs only.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
SCENE_PREFAB_VERSION |
constant | u32 = 1 |
Layout version. |
ScenePrefabArchive |
root | { version:u32, target:TargetProfile, id:AssetId, strings:Vec<u8>, entities:Vec<PrefabEntity>, children:Vec<u32>, renderables:Vec<PrefabRenderable>, material_overrides:Vec<MaterialOverride>, definitions:Vec<PrefabDefinition>, colliders:Vec<PrefabCollider>, attachments:Vec<PrefabAttachment>, dependencies:Vec<AssetId> } |
Entities parent-before-child; typed tables sorted by entity. |
PrefabEntityFlags |
bitflags u8 |
VISIBLE, ACTIVE, SAVE_RELEVANT, STATIC |
Exact spawn defaults. |
PrefabEntity |
record | { stable_id:AssetId, parent:u32, transform:PrefabTransform, children:TableRange, flags:PrefabEntityFlags } |
Parent u32::MAX is root; stable ID unique in prefab. |
PrefabTransform |
POD | { translation_m:[f32;3], rotation_xyzw:[f32;4], scale:[f32;3] } |
Finite; normalized quaternion; scale nonzero. |
VisibilityFlags |
bitflags u8 |
VISIBLE, CAST_SHADOW, RECEIVE_SHADOW, REFLECTION_VISIBLE |
Exact authored render participation. |
PrefabRenderable |
record | { entity:u32, model:AssetId, material_overrides:TableRange, visibility:VisibilityFlags } |
Overrides index root material_overrides:Vec<MaterialOverride> added to archive. |
MaterialOverride |
record | { slot:u16, material:AssetId } |
Slots unique per renderable. |
PrefabDefinition |
record | { entity:u32, definition:AssetId, role:PrefabRole } |
Links A14 typed definition; at most one per entity. |
PrefabRole |
enum | Object, Facility, Staff, Guest, Animal, FencePart, PathPart, Decoration |
Spawn classification only. |
CollisionLayerFlags |
bitflags u16 |
WORLD, ANIMAL, GUEST, STAFF, PROJECTILE, WATER, PLACEMENT |
Exact collision membership/filter vocabulary. |
PrefabCollider |
record | { entity:u32, source:ColliderSource, layer:CollisionLayerFlags, mask:CollisionLayerFlags } |
Static collider description. |
ColliderSource |
enum | Model{model:AssetId,index:u16}, Box{half_extent_m:[f32;3]}, Capsule{radius_m:f32,half_height_m:f32} |
No runtime source parsing. |
PrefabAttachment |
record | { entity:u32, target_name:StringRange, target_entity:u32 } |
Resolved target entity; name retained for diagnostics/animation attachment. |
MappedScenePrefab / access_scene_prefab / access_scene_prefab_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedScenePrefabArchive; entity_components(u32) returns borrowed typed ranges; fn access_scene_prefab(&[u8])->io::Result<&ArchivedScenePrefabArchive>; unsafe fn access_scene_prefab_validated(&[u8])->&ArchivedScenePrefabArchive |
Validate once, own mapping. |
SceneSource |
input | { virtual_path:String, bytes:Vec<u8>, format:SceneSourceFormat } |
Resolved NIF/BFB/XML scene/prefab. |
SceneSourceFormat |
enum | Nif, Bfb, EntityXml, MapObjects |
Preflight only. |
compile_scene_prefab |
producer | fn compile_scene_prefab(input:&SceneSource, resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> |
Pure flattening producer. |
ScenePrefabAsset |
Bevy asset | { archive:MappedScenePrefab } |
Mapped spawn rows. |
ScenePrefabLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=ScenePrefabAsset> for ozscene via path + get_owned |
No reader copy/object graph. |
ScenePrefabAssetPlugin |
plugin | unit struct | Registers asset/loader only. |
4. Data layout or ECS ownership
All relation spans use frozen
TableRange; strings use StringRange; byte AssetRange is not needed. Tables
are read directly from rkyv. A prefab is immutable construction data. Spawned
entities receive only natural G04/Gxx components and retain asset handles/IDs;
there is no persistent PrefabEntityBag or scene-tree runtime.
5. Preflight producer contract
Accept resolved scene/entity/map-object documents and model hierarchies. Normalize prefab and stable child keys. Resolve templates/inheritance, compose transforms, flatten parent hierarchy, produce stable child IDs, map recognized source properties to typed A14 definition links, resolve model/material/collision and attachment dependencies, and discard editor-only nodes only when proven nonsemantic. Reject cycles, duplicate stable IDs, nonfinite transforms, unresolved attachment/required assets, invalid material slots and unknown behavior-bearing components. Deterministic preorder and per-entity table order.
6. Behavioral evidence and disposition
Search shipped object/entity XML, NIF/BFB scene nodes and map object lists;
oracle entity/type/component/world placement classes; analysis RTTI/function
domains; commit d5657e5a model parser, world/templates/startup and placement
sources. Questions: coordinate transforms, node visibility flags, attachment
semantics, component inheritance, collision source, editor-only nodes and stable
identity across save/load. Compare representative facility/animal/scenery spawns.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
One archive traversal gives G04 all entities and exact typed component rows. Parent/child and attachments cleanly resolve; deleting a spawned root lets ECS despawn its hierarchy. Source inheritance is absent at runtime. Unsupported semantic component data fails conversion and is assigned to a natural package before extending the schema. Mods compile to identical rows.
8. Performance contract
Validation/spawn traversal O(entities+components), zero allocation in archived iteration; entity creation necessarily allocates ECS storage only during spawn. Repeated steady frames do no prefab work. Spawn batches reserve entity/component capacity from table lengths. Track mapped pages, spawn heap/entity counts and A04/A07 GPU resources separately.
9. Acceptance criteria
Four files/API; fixture with hierarchy, renderable override, definition, collider/attachment; reject cycle/dangling/invalid transform. Archived traversal allocates zero and exposes exact counts for reservation. Non-goals: ECS spawn systems, terrain and live component state.
10. Required handoff
Report parser dependencies, root registrations, A04/A07/A14 links, exact G04 spawn component mapping, unknown source components/evidence and mapping/spawn/GPU validation. Root should inline any package-only spawn adapter.
A16 — Streamable Terrain
1. Identity and outcome
This package produces streamable terrain chunks with quantized heights, biome blends, water, collision samples and GPU-upload-ready mesh/texture payloads. G05 edits live terrain from this immutable base without reparsing maps or rebuilding the entire world. Integration wave 1; depends on A05, A07 and A14.
2. Exclusive ownership
This package owns native/src/families/terrain.rs, converter/src/families/terrain.rs,
game/src/assets/terrain.rs, preflight/tests/terrain_contract.rs only.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
TERRAIN_ARCHIVE_VERSION |
constant | u32 = 1 |
Layout version. |
TerrainArchive |
root | { version:u32, target:TargetProfile, id:AssetId, cell_size_cm:u16, chunk_cells:u16, grid_chunks:[u32;2], origin_cm:[i32;2], height_scale_mm:u32, height_bias_mm:i32, chunks:Vec<TerrainChunkRecord>, biome_layers:Vec<BiomeLayer>, water:Vec<WaterRegion>, water_chunks:Vec<u32>, payload:Vec<u8>, dependencies:Vec<AssetId> } |
Chunks row-major and complete. |
TerrainChunkFlags |
bitflags u8 |
HAS_WATER, HAS_CLIFF, EDITABLE, STREAMING_BORDER |
Exact chunk facts. |
TerrainChunkRecord |
record | { coordinate:[u32;2], bounds:TerrainBounds, heights:AssetRange, blend:AssetRange, vertices:AssetRange, indices:AssetRange, collision:AssetRange, biome_layers:TableRange, flags:TerrainChunkFlags } |
Archived definition; distinct from G05 ECS TerrainChunk. |
TerrainBounds |
record | { min_m:[f32;3], max_m:[f32;3] } |
Finite world bounds. |
BiomeLayer |
record | { chunk:u32, biome:AssetId, channel:u8, weight_scale:u8, reserved:u16 } |
Max four active channels per blend texel unless target profile states eight. |
WaterFlags |
bitflags u8 |
SWIMMABLE, TANK, OCEAN, REFLECTIVE |
Exact water behavior. |
WaterRegion |
record | { id:AssetId, chunks:TableRange, surface_height_mm:i32, material:AssetId, flags:WaterFlags } |
chunks indexes root water_chunks. |
HeightSample |
value contract | little-endian u16; metres = (sample*height_scale_mm+height_bias_mm)/1000 |
Direct payload cast after alignment validation. |
TerrainVertex |
POD contract | { position:[f32;3], normal_packed:u32, uv:[f32;2], blend_uv:[f32;2] } |
Exact GPU vertex payload. |
MappedTerrainArchive / access_terrain / access_terrain_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedTerrainArchive; chunk(&self,[u32;2])->Option<&ArchivedTerrainChunkRecord>; payload slice accessors; fn access_terrain(&[u8])->io::Result<&ArchivedTerrainArchive>; unsafe fn access_terrain_validated(&[u8])->&ArchivedTerrainArchive |
O(1), validate once, own mapping. |
TerrainSource |
input | { virtual_path:String, bytes:Vec<u8>, format:TerrainSourceFormat } |
Original map/terrain data. |
TerrainSourceFormat |
enum | OriginalMap, HeightField, EditorTerrain |
Preflight only. |
compile_terrain |
producer | fn compile_terrain(input:&TerrainSource, resolve:impl Fn(&str)->Option<AssetId>, target:TargetProfile)->io::Result<Vec<u8>> |
Pure chunk/derived-data producer. |
TerrainAsset |
Bevy asset | { archive:MappedTerrainArchive } |
Mapping owner. |
TerrainAssetLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=TerrainAsset> for ozterrain via path + get_owned |
No reader copy/decode/retiling. |
TerrainAssetPlugin |
plugin | unit struct | Registers the mapped asset and loader only; G05 owns terrain presentation, preparation, eviction and GPU accounting. |
4. Data layout or ECS ownership
rkyv metadata is followed by aligned payload ranges: u16 height grids,
R8/RGBA8 biome blend blocks, TerrainVertex, u32 indices and compact collision
samples. Byte ranges use AssetRange; record links use TableRange. Chunks map
directly. G05 prepares these exact ranges into its persistent TerrainGpuAsset
and owns dirty-span extraction and partial GPU writes. Live edits never mutate
or copy the archive into a second base terrain graph.
5. Preflight producer contract
Accept original terrain/map records, biome paint, water and referenced material/ texture definitions after override resolution. Normalize ID, recover dimensions, orientation and units, chunk with overlap required for normals, quantize heights within target error, bake mesh normals/indices/blends/collision and water chunk links, resolve biomes/materials. Reject unknown record layout, truncated grids, NaN, invalid dimensions, quantization beyond tolerance, >profile biome channels, dangling dependencies and nonmanifold derived mesh. Deterministic row-major chunks and layer ordering.
6. Behavioral evidence and disposition
Search shipped map/terrain files and biome XML/textures; oracle BFTerrain*,
terrain data/mesh/composite/water/brush classes; byte-range map and function
domains; commit d5657e5a simulation/terrain.rs, placement and save terrain.
Questions: grid record layout, cell/height scale, axis orientation, texture tile
scale (avoid tiny repetition), normal/water rules, cliff blending, chunk seams,
collision precision and brush effects. Validate representative maps live.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Base surface geometry, texture scale/blends, water height and collision match the original. Adjacent chunks are seam-free. Runtime may stream/fault chunks and G05 may apply transactional edits, but archive bytes stay immutable. Missing biome assets fail. Mods/maps compile through the same record contract. Terrain editing authentic constants are recovered in G05/A14, not guessed here.
8. Performance contract
Chunk lookup O(1), payload slicing zero allocation; validation O(chunks+payload ranges). G05 consumes mapped ranges through persistent resources and reusable staging with no recurring allocation after capacity. Residency is bounded by visible/preload policy. This package tracks mapped/resident pages and faults; G05 tracks edit heap, exact terrain GPU bytes and chunk upload time.
9. Acceptance criteria
Four files/API; multi-chunk sloped/biome/water fixture, seam and height decode checks, corrupt range/alignment/dependency rejection, zero-allocation lookup/ traversal. Non-goals: live brush commands/camera/culling systems.
10. Required handoff
Report format/parser dependencies, TerrainVertex placement/root registration,
A05/A07/A14 IDs, G05/G17 residency consumers, unresolved grid/scale evidence and
mapping/edit/GPU measurements.
A17 — Navigation
1. Identity and outcome
This package produces precomputed navigation tiles, compact graphs, portals and spatial lookup ranges for walkers/vehicles. Runtime pathfinding traverses mapped records and bounded scratch; it never rebuilds a source nav world. Integration wave 1; depends on A15 and A16.
2. Exclusive ownership
This package owns native/src/families/navigation.rs,
converter/src/families/navigation.rs, game/src/assets/navigation.rs, and
preflight/tests/navigation_contract.rs only.
This package is deliberately bespoke. bevy_landmass is rejected because its
owned validated navmesh/Archipelago model would duplicate the final mapped A17
graph and synchronize agent state with G14. oxidized_navigation is rejected in
the game because runtime tile generation is preflight work. A17 emits the final
borrowed graph representation; it must not retain a library nav world. A small
algorithm crate is acceptable only after root proves it operates directly on
borrowed mapped slices without retained graph state.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
NAVIGATION_ARCHIVE_VERSION |
constant | u32 = 1 |
Layout version. |
NavigationArchive |
root | { version:u32, target:TargetProfile, id:AssetId, origin_cm:[i32;2], tile_size_cm:u32, grid:[u32;2], tiles:Vec<NavTile>, nodes:Vec<NavNode>, edges:Vec<NavEdge>, portals:Vec<NavPortal>, cells:Vec<NavCell>, cell_nodes:Vec<u32> } |
Tiles/cells row-major; nodes stable index. |
NavTile |
record | { coordinate:[u32;2], nodes:TableRange, edges:TableRange, portals:TableRange, bounds_cm:[i32;4] } |
Ranges valid and tile-local rows contiguous. |
NavNode |
record | { position_cm:[i32;3], edges:TableRange, clearance_cm:u16, flags:NavFlags } |
Edge range references outgoing edges. |
NavEdgeFlags |
bitflags u8 |
ONE_WAY, DISABLED_BY_DEFAULT, PREFERRED |
Exact edge policy. |
NavEdge |
record | { to:u32, cost_mm:u32, width_cm:u16, traversal:TraversalKind, flags:NavEdgeFlags } |
Destination valid; positive cost. |
TraversalKind |
enum | Ground, Path, Gate, Ramp, Stair, Swim, Vehicle |
Fixed routing capability. |
NavFlags |
bit flags | u16: GUEST, STAFF, ANIMAL, VEHICLE, ONE_WAY, DISABLED_BY_DEFAULT |
Static eligibility. |
NavPortalFlags |
bitflags u8 |
ONE_WAY, GATE_CONTROLLED, ELEVATED |
Exact portal policy. |
NavPortal |
record | { from_tile:u32, to_tile:u32, from_node:u32, to_node:u32, width_cm:u16, flags:NavPortalFlags } |
Symmetry/one-way validated. |
NavCell |
record | { nodes:TableRange } |
Range indexes cell_nodes; fixed spatial bucket. |
MappedNavigationArchive / access_navigation / access_navigation_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedNavigationArchive; tile(&self,[u32;2])->Option<&ArchivedNavTile>; nearest_candidates(&self,[i32;3])->&[u32]; fn access_navigation(&[u8])->io::Result<&ArchivedNavigationArchive>; unsafe fn access_navigation_validated(&[u8])->&ArchivedNavigationArchive |
O(1) range access, validate once, own mapping. |
NavigationSource |
input | { virtual_path:String, terrain:AssetId, scenes:Vec<AssetId>, authored:Option<Vec<u8>> } |
Resolved geometry/authored nav data. |
compile_navigation |
producer | fn compile_navigation(input:&NavigationSource, terrain:&ArchivedTerrainArchive, prefabs:impl Fn(AssetId)->io::Result<MappedScenePrefab>, target:TargetProfile)->io::Result<Vec<u8>> |
Pure derived graph producer. |
NavigationAsset |
Bevy asset | { archive:MappedNavigationArchive } |
Mapped graph. |
NavigationAssetLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=NavigationAsset> for oznav via path + get_owned |
No reader copy/graph reconstruction. |
NavigationAssetPlugin |
plugin | unit struct | Registers asset/loader only. |
4. Data layout or ECS ownership
rkyv fixed records and frozen TableRange adjacency/spatial spans. Centimetre/mm
integer coordinates/costs make graph traversal deterministic. Runtime dynamic
closures/reservations are sparse ECS/topology overlays keyed by node/edge, not a
copy of the graph. A* scratch is caller-owned reusable arrays/heap capacity.
5. Preflight producer contract
Consume A16 terrain collision/water plus A15 static obstacles, gates/paths and any evidenced authored nav records. Normalize ID, rasterize/walk-test target agent profiles, build nodes/edges/portals/clearance/spatial cells, remove unreachable duplicates and deterministically order by tile/position. Reject missing source assets, invalid geometry, asymmetric accidental links, zero cost, out-of-range indexes and target graphs exceeding u32. Pack precedence is already resolved for inputs.
6. Behavioral evidence and disposition
Search shipped path/fence/gate/terrain definitions and map nav records; oracle
navigation/pathfinding/influence/path classes and function domains; commit
d5657e5a AI navigation manager contracts, pathing, path/fence/transport sources.
Questions: grid/node scale, path preference/cost formula, animal versus guest
clearance, diagonal/corner rules, gates/portals, swimming, elevated paths and
vehicle networks. Live-check route shape/stuck behavior.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Eligible agents find routes consistent with terrain/path/gate restrictions; unreachable requests fail explicitly. Dynamic topology toggles overlay static flags and invalidates only affected routes. No full graph clone per agent. Mods alter inputs before nav build. Authentic cost/clearance semantics must be recovered before path behavior is called done.
8. Performance contract
Tile/cell lookup O(1); adjacency O(out-degree); A* O((V+E)log V) over bounded candidate region using preallocated scratch and zero steady allocations. Changed topology invalidation is event-driven. Count mapped pages, scratch heap capacity and path query time/expanded nodes; no GPU ownership.
9. Acceptance criteria
Four files/API; two-tile fixture with portal, blocked edge and traversal profiles; reject corrupt references/costs; prove lookup/route after warm scratch allocates zero. Non-goals: live steering/reservations and topology editing systems.
10. Required handoff
Report geometry/build dependencies, A15/A16 inputs, root registration, G08/G14 overlay/path API needs, unresolved routing evidence and mapping/scratch/timing validation.
A18 — GPU-ready effects
1. Identity and outcome
Preflight converts original particle/effect controllers into compact,
bounded, Hanabi-facing effect records. In the game, bevy_hanabi performs
particle storage, GPU simulation, compute dispatch, extraction, and rendering;
OpenZT2 entities only select an authored effect and carry zoo-clock/lifecycle
policy. Integration wave 1; depends on A04–A08.
2. Exclusive ownership and mandatory dependency
The package worker owns native/src/families/effect.rs, converter/src/families/effect.rs,
game/src/assets/effect.rs, and preflight/tests/effect_contract.rs only. Root
adds bevy_hanabi = 0.19 with minimal 3D features and registers its plugin.
Hanabi exclusively owns particle buffers, spawning execution, per-particle
simulation, GPU compute pipelines, batching, culling, and particle rendering.
This package must not implement CPU particle arrays, a GPU particle pool,
emitter simulation kernels, particle extraction, or a second effect renderer.
Configuration or a narrow Hanabi fork precedes any bespoke fallback; the deleted
custom design remains only at commit 68a4c481.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
EFFECT_ARCHIVE_VERSION |
constant | u32 = 1 |
Native layout version. |
EffectArchive |
root | { version:u32, target:TargetProfile, id:AssetId, emitters:Vec<HanabiEmitterRecord>, modifiers:Vec<HanabiModifierRecord>, curves:Vec<EffectCurvePoint>, dependencies:Vec<AssetId> } |
Deterministic flat configuration; no live particles. |
HanabiEmitterRecord |
record | { id:AssetId, capacity:u32, spawn:SpawnPolicy, simulation:EffectClock, shape:EmitterShape, modifiers:TableRange, material:AssetId, mesh:AssetId } |
Every value is convertible without source interpretation to one Hanabi effect configuration. |
SpawnPolicy |
enum | Rate{per_second:f32}, Burst{count:u32}, Once |
Closed preflight-validated spawning vocabulary. |
EffectClock |
enum | Presentation, ZooSimulation |
Selects real-time or G32-driven spawning/lifetime semantics. |
EmitterShape |
enum | Point, Sphere{radius:f32}, Box{half_extents:[f32;3]}, Circle{radius:f32} |
Supported Hanabi-facing source shapes. |
HanabiModifierRecord |
enum | InitialPosition, InitialVelocity, Acceleration, Drag, ColorOverLifetime, SizeOverLifetime, KillSphere, OrientToVelocity with typed payloads |
Fixed recovered subset; unknown visible source modules fail preflight. |
MappedEffectArchive |
owning view | new(MappedAsset)->io::Result<Self> and archived(&self)->&ArchivedEffectArchive |
Validated once; keeps mapping alive. |
compile_effect |
producer | fn compile_effect(input:&EffectSource,target:TargetProfile)->io::Result<Vec<u8>> |
Pure source-to-native conversion. |
EffectAsset |
Bevy asset | { archive:MappedEffectArchive } |
Mapped OpenZT2 configuration. |
EffectInstance |
component | { effect:Handle<EffectAsset>, emitter:AssetId, clock:EffectClock } |
One live authored effect on an ordinary entity. |
EffectAssetPlugin |
plugin | unit struct | Registers mapped asset loading and one-time Hanabi preparation/integration only. |
The Bevy bridge creates/caches one Hanabi EffectAsset per native emitter during
asset preparation. This one-time small configuration construction is analogous
to GPU resource preparation and is not a second content graph. Live instances
use Hanabi's normal ParticleEffect/spawner components directly rather than an
OpenZT2 wrapper hierarchy.
4. Data layout and ECS ownership
Archived tables remain mapped. Texture, mesh, material, curve, and modifier references use stable IDs/ranges. Hanabi owns every live particle and GPU byte. OpenZT2 owns only mapped authoring facts, the entity lifecycle relation, clock selection, and conversion of gameplay effect requests into ordinary Hanabi components.
5. Preflight producer contract
Resolve NIF particle controllers, BFB/effect XML, materials, meshes, textures, curves, and overrides. Convert supported source operations into the closed Hanabi-facing record vocabulary, normalize units/coordinates, derive safe capacities, and sort deterministically. Reject recursive triggers, missing dependencies, invalid curves/capacities, unsupported visible modules, or a target profile without required compute/render support. The game never parses a Hanabi text/serde format.
6. Behavioral evidence and disposition
Search shipped effects and particle controllers, Gamebryo particle identities, effect/material documents, the C++ oracle and live original. Recover spawn timing, lifetime, shapes, forces, collision/kill behavior, curves, clock source, overflow policy, blending, alignment, and attachment semantics.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Effects start, stop, loop, attach, detach, and clean up according to authored facts. Zoo-clock effects pause and change speed with G32; presentation effects remain real-time. Capacity overflow follows an explicit recovered policy. Missing dependencies or unsupported modules fail preflight rather than silently changing the visible effect.
8. Performance contract
Mapped lookup and archive traversal allocate zero. Hanabi configuration creation occurs once during preparation; settled instances allocate no Rust heap after capacity stabilizes. Particle simulation/rendering stays GPU-side and is batched/culling-aware. Track native mapping, Hanabi asset/config heap, particle GPU buffers, dispatch duration, instance counts and overflow. A settled effects scene must pass the frame allocation ceiling without an OpenZT2 particle pool.
9. Acceptance criteria
Four files/API, archive corruption tests, source conversion fixtures, and live fixtures for rate/burst, curves, attachment, both clocks, pause/speed, cleanup, capacity and GPU accounting. Source inspection must show no custom particle simulation, compute shader, pool, extraction pipeline, or renderer.
10. Required handoff
Report minimal Hanabi features/version, root plugin order, native-record to Hanabi preparation mapping, A04–A08 dependencies, unsupported source modules, clock integration, and allocator/GPU/dispatch measurements.
A19 — Worlds, Scenarios and Imported Starts
1. Identity and outcome
This package produces immutable map catalogues, starting-zoo snapshots, campaign/challenge metadata and fixed scenario objective/action programs. Original map/save import exists only here in preflight; G01/G04/G24 consume native records immediately. Integration wave 4; depends on A14–A17.
2. Exclusive ownership
This package owns native/src/families/world_scenario.rs,
converter/src/families/world_scenario.rs,
game/src/assets/world_scenario.rs, and
preflight/tests/world_scenario_contract.rs only.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
WORLD_SCENARIO_VERSION |
constant | u32 = 1 |
Layout/objective vocabulary version. |
WorldScenarioArchive |
root | { version:u32, target:TargetProfile, strings:Vec<u8>, maps:Vec<MapRecord>, starts:Vec<StartingZooRecord>, spawn_entities:Vec<SpawnEntityRecord>, spawn_values:Vec<SpawnValue>, campaigns:Vec<CampaignRecord>, scenario_links:Vec<AssetId>, scenarios:Vec<ScenarioRecord>, challenge_policies:Vec<ChallengeOfferPolicy>, objectives:Vec<ObjectiveRecord>, scenario_rules:Vec<ScenarioRule>, rule_links:Vec<u32>, actions:Vec<ScenarioActionRecord>, photo_scoring:Vec<PhotoScoringDefinition>, photo_challenges:Vec<PhotoChallengeRule>, photo_subject_links:Vec<AssetId>, dependencies:Vec<AssetId> } |
ID tables sorted; program ranges bounded; action table index is the stable completion correlation. |
MapRecord |
record | { id:AssetId, name_key:AssetId, description_key:AssetId, thumbnail:AssetId, globe_marker:[i16;2], environment:AssetId, camera:AssetId, guest_arrivals:AssetId, terrain:AssetId, navigation:AssetId, scene:AssetId, start:AssetId, modes:GameModeFlags } |
environment, camera, and guest_arrivals name exactly one A14 definition each; complete closure. |
GameModeFlags |
bit flags | u16: FREEFORM, CHALLENGE, CAMPAIGN, TUTORIAL |
Shell availability. |
StartingZooRecord |
record | { id:AssetId, profile:AssetId, map:AssetId, cash_cents:i64, admission_cents:i64, fame_half_stars:u8, start_tick:u64, absolute_day:u32, calendar:[u16;3], imported_seed:Option<u64>, entities:TableRange, values:TableRange } |
profile is player profile identity (zero only for an unbound template); calendar is [year,month,day]. |
SpawnFlags |
bitflags u8 |
ACTIVE, VISIBLE, SAVE_RELEVANT, PLAYER_OWNED |
Exact imported/start state. |
SpawnEntityRecord |
record | { persistent_id:u64, prefab:AssetId, definition:AssetId, transform:PrefabTransform, parent:u32, flags:SpawnFlags } |
Immutable initial spawn; nonzero world-local G04 ID is unique, and parent is an earlier record index or u32::MAX. Preflight deterministically assigns IDs from canonical source order after override resolution. |
SpawnValue |
enum | Name{entity:u32,text:StringRange}, Condition{entity:u32,permille:u16}, CashRegister{entity:u32,cents:i64}, Research{item:AssetId,progress:u16} |
entity is an index into this start's SpawnEntityRecord range; content definitions remain AssetId. Root also has strings:Vec<u8>. |
CampaignRecord |
record | { id:AssetId, name_key:AssetId, description_key:AssetId, icon:AssetId, scenarios:TableRange, order:u16 } |
Range indexes scenario_links. |
ScenarioFlags |
bitflags u8 |
TUTORIAL, CHALLENGE, REPEATABLE, ALLOW_PAUSE, HIDDEN_UNTIL_UNLOCKED |
Exact scenario policy. |
ScenarioRecord |
record | { id:AssetId, map:AssetId, start:AssetId, name_key:AssetId, description_key:AssetId, objectives:TableRange, start_actions:TableRange, success_actions:TableRange, failure_actions:TableRange, time_limit_ticks:u64, flags:ScenarioFlags } |
Time is G32 ticks; ranges index typed tables. |
ChallengeOfferPolicy |
record | { id:AssetId, scenario:AssetId, eligible_maps:TableRange, prerequisites:TableRange, minimum_fame_half_stars:u8, first_offer_ticks:u64, repeat_interval_ticks:u64, response_ticks:u64, weight:u32 } |
Ranges index scenario_links; all durations/weight are positive. Preflight resolves original challenge generation/eligibility into this fixed policy rather than runtime scripts. |
ObjectiveRecord |
record | { id:AssetId, text_key:AssetId, rule_index:u32, prerequisite_objectives:TableRange, success_actions:TableRange, failure_actions:TableRange, hidden:bool } |
Rule indexes scenario_rules; prerequisite IDs use scenario_links. |
ScenarioRuleKind |
enum | All, Any, Not, CashCents, FameHalfStars, ZooRatingPermille, SpeciesCount, AnimalCount, GuestCount, ObjectCount, ResearchComplete, ElapsedTicks, SpeciesWelfarePermille, AnimalBirthCount, AnimalAdoptionCount, AnimalReleaseCount, SuccessfulShowCount, PhotoScoreMilli, PhotoSubjectCount, PhotoChallengeComplete |
Exact stable G24 evaluator vocabulary; event-count variants consume their natural typed lifecycle/result messages, never a generic authored-event counter. Full corpus must compile to these variants or block schema acceptance. |
ScenarioRule |
record | { id:AssetId, kind:ScenarioRuleKind, subject:AssetId, comparison:crate::families::world_definitions::Comparison, target:i64, children:TableRange } |
Children index rule_links; units are cents, half-stars, 0..=1000 permille, counts, ticks or score thousandths by kind. |
ScenarioAction |
enum | GrantCashCents(i64), Unlock(AssetId), ShowMessage(AssetId), SetAdmissionCents(i64), Spawn{prefab:AssetId,definition:AssetId}, SetWeather(AssetId), Complete, Fail |
Fixed requests executed once by natural owners. AssetId identifies data targets only; there is no generic event action. |
ScenarioActionCompletion |
enum | Immediate, EconomyCommit, ProgressionCommit, SpawnCommit, WeatherCommit, PresentationCommit |
Immediate: SetAdmissionCents/Complete/Fail; EconomyCommit: GrantCashCents; ProgressionCommit: Unlock; SpawnCommit: Spawn; WeatherCommit: SetWeather; PresentationCommit: ShowMessage. Preflight rejects every other pair. |
ScenarioActionRecord |
record | { action:ScenarioAction, completion:ScenarioActionCompletion } |
Its root table index is immutable for the loaded scenario and identifies one apply attempt/result. |
ScenarioActionKey |
native value | #[repr(C)] { scenario:AssetId, action_index:u32 } |
G24 and every natural-owner request/completion carry this exact key; no success may satisfy another action. |
PHOTO_SUBJECT_CAPACITY |
constant | usize literal recovered from complete shipped/oracle evidence before schema implementation begins |
The delivered Rust constant must be a numeric literal justified in handoff; a placeholder or guess is not accepted. |
PhotoScoringDefinition |
record | { id:AssetId, subject:AssetId, max_subjects:u16, minimum_screen_permille:u16, base_score_milli:i32, center_weight:i16, size_weight:i16, facing_weight:i16, behavior_weight:i16, occlusion_required:bool } |
max_subjects <= PHOTO_SUBJECT_CAPACITY; score integer thousandths. |
PhotoChallengeRule |
record | { id:AssetId, scoring:AssetId, required_subjects:TableRange, minimum_score_milli:i32 } |
Subject links index photo_subject_links; G24 evaluates completion through the existing typed photo score/subject-count rules. |
MappedWorldScenarioArchive / access_world_scenario / access_world_scenario_validated |
owning view/functions | tuple wrapper; new(MappedAsset)->io::Result<Self>; archived(&self)->&ArchivedWorldScenarioArchive; typed find_map/find_start/find_scenario(AssetId)->Option<&Archived...>; fn access_world_scenario(&[u8])->io::Result<&ArchivedWorldScenarioArchive>; unsafe fn access_world_scenario_validated(&[u8])->&ArchivedWorldScenarioArchive |
Binary lookup, validate once, own mapping. |
WorldScenarioSource |
input | { virtual_path:String, bytes:Vec<u8>, format:WorldScenarioFormat } |
Map/scenario/campaign/original save source. |
WorldScenarioFormat |
enum | Map, ScenarioXml, CampaignXml, OriginalSave |
Preflight only. |
compile_world_scenarios |
producer | fn compile_world_scenarios(inputs:&[WorldScenarioSource], resolve:impl Fn(&str)->Option<AssetId>, timing:&ArchivedSimulationTimingDefinition, target:TargetProfile)->io::Result<Vec<u8>> |
Pure import/compilation; authored simulation durations become G32 ticks preflight. |
WorldScenarioAsset |
Bevy asset | { archive:MappedWorldScenarioArchive } |
Mapped catalogue. |
WorldScenarioLoader |
loader | { profile:Arc<MappedPack> }; new(Arc<MappedPack>)->Self; AssetLoader<Asset=WorldScenarioAsset> for ozworlds via path + get_owned |
No reader copy/original parser. |
WorldScenarioAssetPlugin |
plugin | unit struct | Registers asset/loader only. |
4. Data layout or ECS ownership
The exact root declaration is the WorldScenarioArchive shape above. All
variable record spans use frozen TableRange; text uses StringRange. Maps reference A14–A17
handles. Objective records are immutable typed programs; live counters, rule
state and current scenario belong to G24 components/resources, not this asset.
Every action range indexes ScenarioActionRecord. G24 queues an action by
ScenarioActionKey, sends that same key to the natural owner selected by
ScenarioActionCompletion, and marks the index applied only after the matching
success acknowledgement (Immediate completes synchronously). Rejection never
counts as application. ScenarioResult is emitted only after every required
terminal action index reaches its correlated terminal state.
5. Preflight producer contract
Accept resolved map/starting-zoo/scenario/campaign XML and original save/map
formats. Normalize canonical IDs, apply source precedence, parse original binary
only here, resolve referenced definitions/prefabs/terrain/nav/localization,
compile scenario commands/tests into ScenarioRuleKind/typed actions, compile
photo rules, and convert mutable initial values plus time/calendar/seed into
closed spawn rows and sort deterministically. Reject unsupported original
state/commands, zero/duplicate assigned persistent IDs, non-earlier parent
indexes, spawn-value indexes outside their owning start range, dangling assets,
invalid transforms,
rule/objective cycles/ranges, invalid timing/calendar, challenge policies with
unknown scenarios/maps/prerequisites, zero timing/weight or impossible
eligibility, photo capacity overflow,
impossible counters and map/start mismatches. Diagnostics
name source rule/command. Later-layer semantics are fully resolved before output.
Assign the canonical completion kind to every action, reject invalid
action/completion pairs and preserve table indexes across deterministic output.
Before implementation, inventory every scenario rule/action in the full corpus
and oracle. Any additional semantic operation becomes an explicitly named enum
variant with only its genuine typed payload. Event IDs, string opcodes,
catch-alls and a generic runtime dispatcher are forbidden.
6. Behavioral evidence and disposition
Search shipped maps, freeform/challenge/campaign/scenario XML and original saves;
oracle BF/ZT scenario managers/rules/actions/map entries and save/map readers;
analysis function domains/byte ranges; commit d5657e5a simulation/scenario.rs,
interaction/shell_content.rs, persistence/save.rs, scenario startup. Questions:
complete command/test vocabulary/units, globe coordinate projection, campaign
ordering, starting money/time/calendar/seed, rule visibility/transitions,
photo scoring/capacity, challenge selection/delay,
save record units/relationships and map availability. Live-check shell and
representative objective progression.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
G01 can enumerate maps/campaigns/markers with no file access and select an exact
map/start/scenario. G04 spawns stable parent-before-child initial entities. G24
matches ScenarioRuleKind exhaustively, converts G18 cents and G23 half-stars/
normalized ratings to frozen integer units, and issues actions exactly once.
Unknown original rule/action blocks conversion. Imported original saves preserve
every supported player-visible fact and explicitly diagnose unsupported facts;
native ongoing saves are G22. Mods extend only the fixed vocabulary.
8. Performance contract
Catalogue lookup O(log n), starting spawn/objective/rule/challenge/photo traversal O(range), all zero-allocation. Scenario evaluation is event/changed-fact driven in G24, not a full archive scan per frame. Spawn reserves ECS capacity. Track mapped pages, spawn heap/entity counts and referenced terrain/model GPU bytes separately; allocator tests cover map/scenario lookup and traversal.
9. Acceptance criteria
Four files/API; fixtures cover freeform map/start, ordered campaign, objective
typed rule tree/actions, weighted challenge-offer policy, photo scoring/challenge and an original-import subset;
each action fixture carries its canonical completion kind and a simulated
out-of-order acknowledgement proves only the matching ScenarioActionKey
completes. Reject unknown command,
dangling parent/dependency and bad ranges. Mapped lookup/traversal zero allocation.
Non-goals: live shell UI, ECS spawning, native save writer and objective systems.
10. Required handoff
Report original parser dependencies, module/loader registration, A14–A17/A09 links, G01/G04/G22/G24 API mapping, unsupported save/rule evidence and allocation/ mapping/spawn/GPU validation. Root must remove any import adapter from engine dependency closure.
G01 — Source-driven game shell
1. Identity and outcome
The player reaches the authentic splash and main menu, chooses a profile and
freeform, challenge, or campaign play, sees the source-defined globe and world
markers, selects a map, and enters GamePhase::Loading. The shell owns only
this application flow; UI projection belongs to G02 and world creation to G04.
Integration wave 2. Prerequisites: A02, A09, A19, G02, G03 and G22's focused
native-profile index/operation contract. G34 owns display/graphics settings;
A11 owns audio settings and G03 owns input bindings.
2. Exclusive ownership
The worker owns game/src/plugins/shell/ and no other path. Crate declarations,
plugin registration, state initialization and ordering are root handoffs.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ShellPlugin |
Plugin |
pub struct ShellPlugin; |
Registers shell messages and systems; owns no state outside ECS. |
PlayMode |
enum | Freeform \| Challenge \| Campaign |
Selected product mode, not a source token. |
ShellScreen |
component enum | Splash \| MainMenu \| ProfileSelect \| Options \| Globe { mode: PlayMode } \| MapSelect { mode: PlayMode } |
Exactly one shell root bears the current screen. |
ProfileChoice |
component | pub struct ProfileChoice(pub AssetId); |
Stable native profile identity; never a positional slot. |
WorldChoice |
component | pub struct WorldChoice { pub scenario: AssetId, pub mode: PlayMode } |
Attached to a selectable marker/card; ID resolves in A19. |
ShellSelection |
resource | pub struct ShellSelection { pub profile: Option<AssetId>, pub mode: Option<PlayMode>, pub scenario: Option<AssetId> } |
Sole bounded cross-screen selection. |
ShellCommand |
message enum | ShowMainMenu \| ShowProfiles \| ShowOptions \| CreateProfile(String) \| ChooseProfile(AssetId) \| DeleteProfile(AssetId) \| ChooseMode(PlayMode) \| ChooseWorld(AssetId) \| StartSelected \| Back \| Quit |
Fixed semantic commands; the bounded player-entered profile name is the only owned string. |
SplashFinished |
message | pub struct SplashFinished; |
Source-defined splash sequence finished or authentic skip succeeded. |
Root contract requests: A02 exposes UiDocumentRole semantic discovery,
A19 must expose WorldScenarioAsset and allocation-free AssetId lookup, G02
must expose ShowUiRole/UiCommandActivated, G22 owns ProfileIndex and every
profile request/result named below, and G04 owns
BeginWorldLoad/WorldLoadFinished/WorldLoadFailed. G01 consumes or writes
those exact G04 messages and must not redeclare them. G34 applies display and
graphics settings; G01 owns only navigation into/out of the options document.
4. ECS ownership
ShellSelection is genuinely global bounded navigation state. ShellScreen,
ProfileChoice, and WorldChoice are facts on projected UI entities.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
enter_boot_splash |
OnEnter(GamePhase::Boot) |
Commands, MessageWriter<ShowUiRole> |
one splash screen and UiDocumentRole::Splash request |
mapped bootstrap assets ready |
finish_boot_splash |
Update/GameSet::Intent |
MessageReader<SplashFinished>, Query<&ShellScreen> |
NextState<GamePhase::MainMenu> |
current screen is splash |
enter_shell_phase |
OnEnter(GamePhase::MainMenu) |
Commands, MessageWriter<ShowUiRole> |
one main-menu screen and UiDocumentRole::MainMenu |
after splash |
translate_shell_ui_commands |
Update/GameSet::Intent |
MessageReader<UiCommandActivated>, Query<Option<&WorldChoice>>, Query<Option<&ProfileChoice>>, MessageWriter<ShellCommand> |
typed shell commands | maps only A02 shell commands/arguments; after G02 |
route_shell_commands |
Update/GameSet::Intent |
MessageReader<ShellCommand>, ResMut<ShellSelection>, Query<&WorldChoice> |
selection, NextState<GamePhase>, BeginWorldLoad, AppExit |
in menu phases; ordered after G03/G02 input |
request_profile_operations |
Update/GameSet::Intent |
MessageReader<ShellCommand>, MessageWriter<LoadProfileIndex>, MessageWriter<CreateProfile>, MessageWriter<SelectProfile>, MessageWriter<DeleteProfile> |
exact G22 requests | profile commands only; storage mutation remains G22 |
show_profile_choices |
Update/GameSet::Ui |
Commands, MessageReader<ProfileIndexReady>, Res<ProfileIndex>, MessageWriter<ShowUiRole> |
UiDocumentRole::ProfileSelect plus ProfileChoice entities |
sorted G22 records only |
finish_profile_operations |
Update/GameSet::Intent |
MessageReader<ProfileCreated>, MessageReader<ProfileSelected>, MessageReader<ProfileDeleted>, MessageReader<ProfileOperationFailed>, ResMut<ShellSelection> |
selected ID and source-defined success/error UI requests | only after durable G22 result; never optimistic |
show_options_screen |
Update/GameSet::Intent |
Commands, MessageReader<ShellCommand>, Query<Entity,With<ShellScreen>>, MessageWriter<ShowUiRole> |
despawns the current shell root, creates ShellScreen::Options and requests UiDocumentRole::Options for it |
ShowOptions only; settings commands remain with G34/A11/G03 |
show_globe_choices |
OnEnter(GamePhase::MapSelection) |
Commands, Res<ShellSelection>, Res<Assets<WorldScenarioAsset>>, MessageWriter<ShowUiRole> |
globe/map role and source-defined marker entities | mode must be selected |
finish_world_load |
Update/GameSet::Intent |
MessageReader<WorldLoadFinished>, Res<ShellSelection> |
NextState<GamePhase::InGame> |
only matching pending scenario |
clear_main_menu_entities |
OnExit(GamePhase::MainMenu) |
Commands, Query<Entity,With<ShellScreen>> |
matching screen teardown | before next phase |
clear_map_selection_entities |
OnExit(GamePhase::MapSelection) |
Commands, Query<Entity,With<ShellScreen>> |
matching screen teardown | before loading |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Start with shipped ui/ shell XML/images, globe/map/campaign/challenge records,
localization keys and profiles; search the oracle and analysis TSVs for
ZTMainMenu, ZTWorldSelect, campaign, challenge, freeform, globe and
profile functions. Inspect commit d5657e5a only in app/launch.rs,
features/core/app_flow.rs, interaction/menu.rs, ui/native.rs and world
selection code. Live observation settles splash duration/skipping, back-stack,
marker layout, disabled choices and transition timing.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence questions: exact startup screen order; profile-slot limits; whether each product mode shares the globe; world-marker selection/default/focus rules; and original loading failure presentation. Resolve from shipped documents and live behavior, not guessed timeouts or hardcoded marker positions.
7. Required behavior
Every visible item/marker comes from A02/A19/G22 data. StartSelected is ignored
unless profile, mode and scenario are all present and compatible. Back returns
one authentic level without retaining hidden screen trees. A matching load
completion enters the game; stale completions cannot. Missing/corrupt native
assets produce an explicit disabled/error UI state, never fake success.
Controller navigation is through G03/G02. The options screen presents G34/A11/
G03 settings and returns through the authentic shell back flow; G01 never owns
their live values or application. OpenZT2 adds no new shell rule here.
8. Performance contract
No filesystem access, source parsing, string construction, asset-payload clone,
or per-frame scan of scenarios. Screen entry may allocate Bevy UI entities;
settled shell frames and command routing allocate zero. Marker creation is
O(visible worlds) once; command routing is O(messages). Allocation tests
cover idle shell and a command without transition; mapped-page counters cover
A02/A19 faults, and shell adds no GPU bytes beyond referenced UI assets.
9. Acceptance criteria
Deliver ordinary formatted Rust under the owned directory exporting every item
above. A deterministic system test must prove invalid start is rejected and a
valid selection emits one BeginWorldLoad. No placeholder screens, guessed
coordinates, todo!, silent stubs, or source-format dependency. Bulk handoff:
integrate with A02/A11/A19/G02/G03/G04/G34 and verify the complete navigable shell.
10. Required handoff
Report files/API; dependencies/features; root module/plugin/message/state/order registrations; consumed A02/A19/G02/G03 and requested G04 symbols; unresolved evidence; G34/A11/G03 options seams; seams to erase; and expected allocation, mapping, GPU and navigation validation.
G02 — Native game UI
1. Identity and outcome
Compiled A02 UI documents become ordinary Bevy UI entities with authored layout, style, focus, bindings and fixed commands. Modded documents work because preflight compiles the same fixed vocabulary; XML never enters the game. Integration wave 2. Prerequisites: A02, A05, A09 and A12.
2. Exclusive ownership
The worker owns game/src/plugins/ui/ only. Root owns module/plugin,
asset-loader, reflection and schedule registration.
Bevy 0.19 UI is the exclusive layout/widget/picking/focus implementation. Use its ordinary nodes, flex/grid engine, clipping, scrolling, interactions and automatic directional navigation directly. G02 owns document projection and Zoo Tycoon bindings/commands only; no custom layout pass, widget hierarchy, focus graph, UI scene, or egui/third-party UI world is permitted.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
GameUiPlugin |
Plugin |
pub struct GameUiPlugin; |
Registers projection, interaction, focus and teardown. |
UiDocumentRoot |
component | pub struct UiDocumentRoot { pub document: Handle<UiDocumentAsset> } |
One projected tree owns one A02 handle. |
UiNodeId |
component | pub struct UiNodeId { pub index: u32, pub id: AssetId } |
Exact A02 record index and stable authored ID. |
UiDocumentOwner |
component | pub struct UiDocumentOwner(pub Entity); |
On every projected node; points to its UiDocumentRoot without walking parents. |
UiCommandRange |
component | pub struct UiCommandRange(pub TableRange); |
Complete validated archived command range; multiple triggers, no record copies. |
UiVisibleBinding |
component | pub struct UiVisibleBinding(pub BoolBindingSource); |
Exact A02 UiBinding::Visible source only. |
UiEnabledBinding |
component | pub struct UiEnabledBinding(pub BoolBindingSource); |
Exact A02 UiBinding::Enabled source only. |
UiTextBinding |
component | pub struct UiTextBinding(pub TextBindingSource); |
Exact A02 text source only. |
UiImageBinding |
component | pub struct UiImageBinding(pub ImageBindingSource); |
Exact A02 image source only. |
UiValueBinding |
component | pub struct UiValueBinding(pub ScalarBindingSource); |
Exact A02 scalar-value source only. |
UiValue |
component | pub struct UiValue(pub i64); |
Current slider/progress integer in the exact unit encoded by its source variant. |
UiMinimum / UiMaximum |
components | pub struct UiMinimum(pub i64); pub struct UiMaximum(pub i64); |
Authored integer slider bounds; no generic numeric property. |
UiSelectedBinding |
component | pub struct UiSelectedBinding(pub BoolBindingSource); |
Exact A02 selected-state source only. |
UiSelected |
component | pub struct UiSelected(pub bool); |
Current toggle/list selection fact. |
UiFocusScope |
component | pub struct UiFocusScope { pub focused: Option<Entity>, pub default: Option<Entity> } |
Per-screen focus, never a global mirrored UI tree. |
UiFocusable |
component | pub struct UiFocusable { pub order: u32, pub enabled: bool } |
Authored deterministic focus order within nearest scope. |
UiCommandActivated |
message | pub struct UiCommandActivated { pub command: UiCommand, pub source: ActionSource, pub node: Entity } |
Copies the exact typed enum payload from the mapped record and emits once on a confirmed enabled target. |
ShowUiDocument |
message | pub struct ShowUiDocument { pub document: Handle<UiDocumentAsset>, pub owner: Entity } |
Requests one projection parented to an existing owner. |
ShowUiRole |
message | pub struct ShowUiRole { pub role: UiDocumentRole, pub owner: Entity } |
Resolves A02's closed semantic role to its fixed native virtual path; no source path, winning asset instance or registry is retained in the engine. |
HideUiDocument |
message | pub struct HideUiDocument { pub owner: Entity } |
Despawns only roots projected for that owner. |
SetUiNodeEnabled |
message | pub struct SetUiNodeEnabled { pub root: Entity, pub node: u32, pub enabled: bool } |
Event-driven enable state change; node index is validated. |
ShowNotification |
message | pub struct ShowNotification { pub operation: Entity, pub message: AssetId } |
G24 requests presentation of one localized notification key. |
NotificationPresented |
message | pub struct NotificationPresented { pub operation: Entity } |
Emitted after the notification UI entity is projected successfully. |
NotificationRejected |
message | pub struct NotificationRejected { pub operation: Entity, pub reason: NotificationFailure } |
Typed no-fake-success result. |
NotificationFailure |
enum | MissingLocalization(AssetId) \| MissingNotificationRole \| ProjectionFailed |
Exact presentation failures; no string reason. |
PendingNotification |
component | pub struct PendingNotification { pub operation: Entity, pub message: AssetId } |
Temporary owner fact removed after projection acknowledgement/rejection. |
UiDocumentProjectionFailed |
message | pub struct UiDocumentProjectionFailed { pub owner: Entity, pub reason: UiProjectionFailure } |
Exact role-load/project failure correlated to the requesting owner. |
UiProjectionFailure |
enum | MissingRoleAsset \| AssetLoadFailed \| InvalidDocument |
Closed projection failures used by notifications and shell error presentation. |
A02 freezes UiDocumentAsset, UiCommand, UiBinding and its typed source
enums, archived node/style
records, zero-copy indexed accessors, and
UiDocumentRole::native_asset_path(self) -> &'static str. Preflight writes the
single precedence-resolved document for every supported role at that semantic
native virtual path. Do not invent local command strings, JSON-like values,
widget traits, a DOM, a binding property bag, or a role-to-handle store.
Projection exhaustively converts each UiBinding variant into the corresponding
typed component above once. A05's stable native-ID Handle<Image> is the sole
bridge to Bevy ImageNode, while a retained Handle<TextureAsset> owns its mapped
source through preparation; A12 resolves the recovered system-font policy into ordinary Bevy
TextFont beside Text.
4. ECS ownership
All live UI is Bevy Node, Text, ImageNode, interaction and small marker
components. Only per-screen focus is retained.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
project_ui_documents |
Update/GameSet::Ui |
Commands, MessageReader<ShowUiDocument>, Res<Assets<UiDocumentAsset>>, Res<AssetServer>, Res<ContentMap> |
Bevy UI tree, exhaustive typed binding components, ImageNode using A05's native-ID image handle plus its mapped texture source handle, and ordinary TextFont using A12 system-font policy |
only loaded validated assets; before interaction; no custom image/font renderer |
resolve_ui_roles |
Update/GameSet::Ui |
MessageReader<ShowUiRole>, Res<AssetServer>, MessageWriter<ShowUiDocument>, MessageWriter<UiDocumentProjectionFailed> |
asset_server.load(role.native_asset_path()) and ShowUiDocument, or correlated failure |
before projection; role mapping returns a static native path and allocates no constructed path |
apply_ui_enable_changes |
Update/GameSet::Ui |
MessageReader<SetUiNodeEnabled>, Query<(&UiNodeId,&mut UiFocusable,&mut Interaction)> |
enabled/interaction state | before focus and activation |
pointer_activation |
Update/GameSet::Ui |
Res<Assets<UiDocumentAsset>>, Query<(Entity,&Interaction,&UiCommandRange,&UiFocusable,&UiDocumentOwner),Changed<Interaction>>, Query<&UiDocumentRoot>, MessageWriter<UiCommandActivated> |
one message per matching archived Press record |
enabled pressed nodes |
navigate_focus |
Update/GameSet::Ui |
MessageReader<ActionRequest>, Query<(Entity,&UiFocusable,&InheritedVisibility)>, Query<&mut UiFocusScope> |
scope focus | navigation actions; after G03 |
activate_focus |
Update/GameSet::Ui |
MessageReader<ActionRequest>, Res<Assets<UiDocumentAsset>>, Query<(&UiFocusScope,)>, Query<(Entity,&UiCommandRange,&UiFocusable,&UiDocumentOwner)>, Query<&UiDocumentRoot> |
matching archived command activations | Confirm only |
present_focus |
Update/GameSet::Presentation |
Query<(&UiFocusScope,), Changed<UiFocusScope>>, Query<&mut BackgroundColor,With<UiFocusable>> |
focus visual state | after navigation |
hide_ui_documents |
Update/GameSet::Ui |
Commands, MessageReader<HideUiDocument>, Query<(Entity,&ChildOf),With<UiDocumentRoot>> |
recursive despawn | before new projection for owner |
request_notifications |
Update/GameSet::Ui |
Commands, MessageReader<ShowNotification>, Res<Assets<LocalizationAsset>>, MessageWriter<ShowUiRole>, MessageWriter<NotificationRejected> |
one PendingNotification owner plus modal role request, or rejection |
validates localization/role before projection; no optimistic success |
acknowledge_notifications |
Update/GameSet::Ui |
Commands, Query<(&ChildOf,&UiDocumentRoot),Added<UiDocumentRoot>>, Query<&PendingNotification>, MessageWriter<NotificationPresented> |
exact success and removes pending marker | only after the requested document root actually exists under its owner |
reject_failed_notifications |
Update/GameSet::Ui |
Commands, MessageReader<UiDocumentProjectionFailed>, Query<&PendingNotification>, MessageWriter<NotificationRejected> |
mapped typed failure and removes pending owner | matching owner only; load/projection failure can never emit NotificationPresented |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect shipped UI XML/styles/hotkeys/localization and images; oracle/TSVs for
BFUI, UIManager, layout, widget, message and focus classes; and commit
d5657e5a presentation/ui/{native,layout,tree,widgets,interactions}.rs only for
discovered vocabulary. Live observation settles scaling, layering, clipping,
hover/pressed/disabled visuals, focus wrapping and modal input capture.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence questions: coordinate origin/anchor rules, logical-resolution scaling, z/layer ordering, nine-slice behavior, modal focus escape and command repeat. Resolve them in A02 records or observation; never approximate layouts in code.
7. Required behavior
Projection preserves authored parent order, anchors, clipping, visibility,
enabled state, style and localized text/image/font references. Unknown validated
commands are rejected by preflight, not ignored here. Bindings are precise
property sink components, not a generic range, property/source-key dispatcher
or copied UI state table. G02 does not read economy, selection, profile,
progression, scenario or other gameplay state. G21 owns selected-entity,
information and catalogue presenters; G01/G22/G23/G24 and later natural domain
owners query their own narrow facts plus the exact G02 sink component and write
ordinary Bevy Visibility, Text, ImageNode, UiValue or UiSelected.
Hidden/despawned roots cannot retain focus or activate commands. Pointer,
keyboard and controller produce identical typed activation facts.
8. Performance contract
Projection may allocate exactly once per created tree; idle UI, focus movement,
activation and binding presentation allocate zero. Archived traversal is direct;
handles clone without payload allocation. No per-frame tree rebuild, text key
formatting, hash growth or asset lookup by constructed path. Role resolution is
startup/screen-transition work and loads a fixed &'static str. Work is
O(changed nodes + input messages). Counters cover idle UI and focus/confirm;
mapped faults remain attributed to A02/A05; backend font/glyph memory is measured under A12.
9. Acceptance criteria
Deliver all symbols above. Pure/system tests prove deterministic projection order, disabled targets never activate, controller and pointer emit the same command, and teardown removes the entire owned tree. No DOM, command router, runtime XML, placeholder widget or generated source. Root performs visual comparison and bulk compile.
10. Required handoff
Report files/API, dependency and registration requests, A02/A05/A09/A12 symbols, command consumers needing root wiring, evidence gaps, integration seams, and allocation/mapping/GPU/visual validation.
G03 — Device-independent input
1. Identity and outcome
Keyboard, mouse and any connected controller drive the same fixed game actions, including controller UI focus, camera controls, undo and redo, with rebinding. The package translates devices only; it does not route domain commands. Integration wave 2. Prerequisite: frozen Bevy contracts.
2. Exclusive ownership
The worker owns game/src/plugins/input/ only. Root owns plugin/schedule and
reflection registration and persistence integration.
Bevy Input and bevy_gilrs exclusively own keyboard, mouse, touch, gamepad
discovery and raw device state. G03 only maps those facts to GameAction,
rebinding and active-device policy. It must not add an input backend, poll an OS
API, or mirror device state through Leafwing or another input framework.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
GameInputPlugin |
Plugin |
pub struct GameInputPlugin; |
Produces shared ActionRequest and control state. |
InputChord |
enum | Key(KeyCode) \| Mouse(MouseButton) \| Gamepad(GamepadButton) |
One digital binding; serializable by G22 without strings. |
ActionBinding |
value | pub struct ActionBinding { pub action: GameAction, pub primary: InputChord, pub alternate: Option<InputChord> } |
One action has deterministic primary/optional alternate. |
InputBindings |
resource | pub struct InputBindings { pub entries: Box<[ActionBinding]> } |
Global user configuration, unique chords per context; replaced only on rebind/load. |
ControlAxes |
resource | pub struct ControlAxes { pub pan: Vec2, pub look: Vec2, pub zoom: f32 } |
Current normalized active-device axes; each axis in [-1,1]. |
PointerInput |
resource | pub struct PointerInput { pub screen: Vec2, pub pressed: bool } |
Latest raw primary pointer facts in physical window pixels. G06 exclusively derives the world ray. |
ActiveInput |
resource | pub struct ActiveInput { pub source: ActionSource, pub changed_at: Duration } |
Last device producing non-dead-zone input; controller entity must still exist. |
RebindAction |
message | pub struct RebindAction { pub action: GameAction, pub slot: u8, pub chord: InputChord } |
Slot 0/1 replaces primary/alternate after collision validation. |
BindingRejected |
message enum | InvalidSlot \| Duplicate { action: GameAction } |
Explicit rebinding failure, never silent. |
ActionRequest, ActionSource, GameAction are the sole shared semantic
actions. Domain-specific input remains owned by its feature, not added here.
4. ECS ownership
Resources are genuinely global device/configuration facts and fixed-size during settled play. No entity input state is mirrored.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
read_keyboard_mouse |
PreUpdate/GameSet::Input |
Res<ButtonInput<KeyCode>>, Res<ButtonInput<MouseButton>>, Res<InputBindings>, ResMut<ControlAxes>, ResMut<PointerInput>, ResMut<ActiveInput>, MessageWriter<ActionRequest> |
axes/pointer/source and edge-triggered actions | every frame, before intent/UI |
read_gamepads |
PreUpdate/GameSet::Input |
Query<(Entity,&Gamepad)>, Res<InputBindings>, ResMut<ControlAxes>, ResMut<ActiveInput>, MessageWriter<ActionRequest> |
axes/source/actions | connected pads; dead-zone before normalization |
apply_rebindings |
Update/GameSet::Intent |
MessageReader<RebindAction>, ResMut<InputBindings>, MessageWriter<BindingRejected> |
binding entry only | outside active chord iteration |
clear_disconnected_source |
PreUpdate/GameSet::Input |
ResMut<ActiveInput>, Query<(),With<Gamepad>> |
fallback to keyboard/mouse | active controller absent |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect shipped hotkey/input XML and original options, oracle/TSVs for input,
hotkey and camera-command readers, and commit d5657e5a
interaction/controller.rs, ui/hotkeys.rs, input_actions.rs solely for
known bindings/tokens. Observe original press/repeat/modifier and mouse-camera
semantics. Controller support is the deliberate OpenZT2 extension.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence questions: original default keys, action repeat, wheel sign/scale, mouse buttons and mode-dependent capture. Controller dead zones, glyph choice and default layout are new policy and must be documented, deterministic and data-free, not guessed as original behavior.
7. Required behavior
Digital actions emit once on press unless the original binding explicitly
repeats. Opposing axes cancel; analog magnitude survives normalization outside
dead zone. Last meaningful device wins without flicker from noise. Rebinding
rejects invalid slot and collisions atomically. Disconnect clears axes and
falls back safely. Undo and Redo are first-class. No gameplay mutation or
UI traversal occurs here.
8. Performance contract
All steady input systems allocate zero and perform O(bindings + connected controllers) bounded work. No action-name strings, maps grown per frame or
message buffering outside Bevy. The binding box changes only on load/rebind.
Allocator regressions cover idle, keyboard edge, analog frame and controller
disconnect. No mapped pages or GPU resources are owned.
9. Acceptance criteria
Deliver all symbols above and tests for edge triggering, opposing axes, dead-zone stability, duplicate rejection, disconnect and equivalent keyboard/ controller actions. No command dispatcher, hidden context world, placeholder bindings or polling allocations. Root bulk-validates live devices and G02/G06/ G07 integration.
10. Required handoff
Report API/files, root registrations, any Bevy feature needs, persistence fields, original binding evidence and controller policy, integration seams and exact allocation/device validation.
G04 — World instantiation
1. Identity and outcome
A selected native scenario/map is instantiated directly as Bevy entities with typed components and asset handles, then loading completes. This owns the one-time immutable-record-to-ECS boundary, not a retained prefab world. Integration wave 2. Prerequisites: A14, A15 and A19.
2. Exclusive ownership
The package worker owns game/src/plugins/world_spawn/ only. Root owns declarations, registrations,
schedule wiring and cross-plugin type registration.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
WorldSpawnPlugin |
Plugin |
pub struct WorldSpawnPlugin; |
Registers request, instantiation and cleanup. |
WorldRoot |
component | pub struct WorldRoot { pub scenario: AssetId } |
Root of exactly one loaded zoo. |
WorldMember |
component | pub struct WorldMember { pub root: Entity } |
Lifecycle relation on every in-zoo durable or transient entity, independent of transform parenting. |
DefinitionId |
component | pub struct DefinitionId(pub AssetId); |
Immutable native catalogue definition used to create an entity. |
PersistentId |
component | pub struct PersistentId(pub u64); |
Nonzero ID unique within a world and stable across G22 save/load. |
PersistentIdAllocator |
focused resource | pub struct PersistentIdAllocator { pub root: Entity, next: u64 } with pub fn allocate(&mut self, root: Entity) -> Result<PersistentId,PersistentIdError> and pub fn reserve_imported(&mut self, root: Entity, id: PersistentId) -> Result<(),PersistentIdError> |
Sole world-local ID authority. next starts at 1; fresh allocation returns next then checked-increments, and validated imported IDs advance it to max(next,id.0+1). No heap/set lookup or ID reuse. |
PersistentIdError |
enum | WrongWorld \| Zero \| Exhausted |
Fixed allocation/reservation failure; IDs never fall back to Bevy entity bits or random values. |
AssignPersistentId |
message | pub struct AssignPersistentId { pub entity: Entity, pub root: Entity } |
Requests one newly allocated ID for an already spawned saveable entity before it becomes observable to persistence/gameplay. |
PersistentIdAssigned |
message | pub struct PersistentIdAssigned { pub entity: Entity, pub id: PersistentId } |
Emitted after the component is inserted exactly once. |
PersistentIdAssignmentFailed |
message | pub struct PersistentIdAssignmentFailed { pub entity: Entity, pub reason: PersistentIdError } |
Explicit failure; caller must roll back the incomplete entity. |
SpawnRecordId |
component | pub struct SpawnRecordId(pub u32); |
A15/A19 record index, for diagnostics only; never live mutable state. |
WorldBounds |
component | pub struct WorldBounds { pub min: Vec2, pub max: Vec2 } |
World XZ metres, finite and ordered. |
ZooEntrance |
component | pub struct ZooEntrance { pub arrival: Vec3, pub inside: Vec3, pub exit: Vec3 } |
Authored finite arrival, inside-routing and exit points; never guessed from world position. |
SpawnScenarioObject |
message | pub struct SpawnScenarioObject { pub operation: Entity, pub root: Entity, pub prefab: AssetId, pub definition: AssetId } |
G24 request for one native prefab/definition instance in an existing world. |
ScenarioObjectSpawned |
message | pub struct ScenarioObjectSpawned { pub operation: Entity, pub entity: Entity } |
Exact completion after full typed bundle, WorldMember and allocator-issued PersistentId exist. |
ScenarioObjectSpawnRejected |
message | pub struct ScenarioObjectSpawnRejected { pub operation: Entity, pub reason: ScenarioObjectSpawnFailure } |
Typed failure with no partial entity. |
ScenarioObjectSpawnFailure |
enum | WrongWorld \| MissingPrefab(AssetId) \| MissingDefinition(AssetId) \| InvalidPrefab \| PersistentId(PersistentIdError) |
Complete engine-side G24 spawn rejection vocabulary. |
BeginWorldLoad |
message | pub struct BeginWorldLoad { pub scenario: AssetId, pub mode: PlayMode, pub profile: AssetId } |
G04 owns this request; mode is G01 vocabulary and profile is stable G22 identity. |
WorldLoadFinished |
message | pub struct WorldLoadFinished { pub scenario: AssetId, pub root: Entity } |
Emitted after all records and required handles are instantiated. |
WorldLoadFailed |
message | pub struct WorldLoadFailed { pub scenario: AssetId, pub reason: WorldLoadFailure } |
Explicit failure without runtime path strings. |
WorldLoadFailure |
enum | MissingScenario \| InvalidRecord(u32) \| MissingDependency(AssetId) \| DuplicatePersistentId(u64) |
Exhaustive engine-side failures after archive validation. |
UnloadWorld |
message | pub struct UnloadWorld(pub Entity); |
Despawns every matching WorldMember then the root. |
G04 consumes G01's already frozen PlayMode; the load request/result messages
above remain G04-owned and must not be redeclared by G01. A15/A19 expose
ScenePrefabAsset, WorldScenarioAsset, fixed spawn-kind records and
allocation-free ID lookup.
4. ECS ownership
Components are durable identity/bounds facts. PersistentIdAllocator is the
one focused coordination resource for the active world and contains only its
root and monotonic cursor. A bounded PendingWorldSpawn
resource may privately retain one scenario handle and next record index during
loading; it must be removed at completion and is not a world representation.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
begin_world_spawn |
Update/GameSet::Intent |
Commands, MessageReader<BeginWorldLoad>, Res<Assets<WorldScenarioAsset>> |
root, PersistentIdAllocator { next: 1 } and private pending cursor or failure |
GamePhase::Loading, no existing root |
reserve_world_record_ids |
Update/GameSet::Intent |
Res<Assets<WorldScenarioAsset>>, ResMut<PersistentIdAllocator>, Option<Res<PendingWorldSpawn>> |
after A19 validation, visits every nonzero matching-root imported ID and positions next above the largest without spawning |
once before the first record; A19 has already rejected zero/duplicate IDs, and arithmetic exhaustion fails the whole load |
spawn_world_records |
Update/GameSet::Intent |
Commands, Res<Assets<WorldScenarioAsset>>, Res<Assets<ScenePrefabAsset>>, Res<PersistentIdAllocator>, Option<ResMut<PendingWorldSpawn>> |
typed ECS entities/handles, their already-reserved PersistentIds and cursor |
bounded batch after complete reservation |
assign_requested_persistent_ids |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<AssignPersistentId>, ResMut<PersistentIdAllocator>, Query<(),(With<WorldMember>,Without<PersistentId>)>, MessageWriter<PersistentIdAssigned>, MessageWriter<PersistentIdAssignmentFailed> |
one new nonzero ID component or typed failure | live-created saveable entities only; before any domain completion acknowledgement |
spawn_scenario_objects |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<SpawnScenarioObject>, Res<Assets<ScenePrefabAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<(),With<WorldRoot>>, MessageWriter<ScenarioObjectSpawned>, MessageWriter<ScenarioObjectSpawnRejected> |
one natural prefab bundle with DefinitionId, WorldMember and fresh PersistentId, or no entity |
validates root/assets first; allocates synchronously immediately before spawn; acknowledges only after complete bundle insertion |
finish_world_spawn |
Update/GameSet::Intent |
Commands, Option<Res<PendingWorldSpawn>>, Query<Entity,With<WorldRoot>>, MessageWriter<WorldLoadFinished> |
completion and removes cursor | all required records emitted |
unload_world |
Update/GameSet::Intent |
Commands, MessageReader<UnloadWorld>, Query<(Entity,&WorldMember)>, Query<(),With<WorldRoot>> |
deterministically despawns matching members then root | after persistence capture; hierarchy only for natural transforms |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Start with shipped map/scenario/entity instance data and save maps; oracle/TSVs
for world/entity factory, placement, load and ID functions; commit d5657e5a
startup/world.rs, simulation/world.rs, presentation/world.rs only for
record coverage and discovered transforms. Observe map orientation, entrance,
parenting, initial visibility and mode-specific starting conditions.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: original coordinate/rotation convention, stable-ID derivation, instance parent/attachment order, absent optional dependency handling and exact mode overrides. Resolve in A15/A19/preflight; do not keep generic component bags.
7. Required behavior
One request creates one world or one failure. Record order and IDs are
deterministic. A19 (or G22 for a save) validates imported IDs for matching root,
nonzero value and duplicates before allocator mutation; reserve_imported only
advances the high-water mark. Every fixed spawn-kind becomes its natural component bundle and
typed handle; unknown kinds were rejected preflight. Parent/attachment links
resolve without linear name lookup. Completion waits for required native asset
availability, not GPU residency. Every later package spawn attaches
WorldMember; every live-created entity that G22 can save obtains its ID only
through PersistentIdAllocator (direct allocate while exclusively borrowed or
AssignPersistentId) before its owning domain acknowledges creation. IDs are
never reused after despawn during that world lifetime. Unload removes all
matching members regardless of transform hierarchy plus pending/allocator
state. Save IDs never depend on Bevy Entity bits.
8. Performance contract
Loading may allocate ECS storage and GPU handles but never reconstructs an
intermediate graph. Record traversal is direct, O(records + dependencies),
with bounded per-frame batching and pre-sized temporary lookup. Fresh ID
allocation and validated import reservation are O(1) and zero-allocation.
Settled frames
have zero G04 work/allocation. Measure total load allocations, mapped faults and
time; GPU bytes remain owned by asset families. A post-load allocation test
must show zero.
9. Acceptance criteria
Deliver exports above and tests for deterministic allocation, imported-ID
high-water advancement, upstream duplicate validation, exhaustion rejection, typed spawn
coverage and complete unload. No second ID allocator, domain-local counters,
generic authored entity, property
map, source parser, retained spawn mirror or fake completion. Root validates a
real map from G01 through InGame in bulk.
10. Required handoff
Report files/API, registrations, A14/A15/A19 and G01 types, every spawn-kind and G22/live-creation owner needing allocator integration, unresolved records, temporary cursor seam, and load allocation/page/GPU/gameplay validation.
G05 — Editable terrain and water
1. Identity and outcome
Native terrain chunks render with authentic scale and blending, answer height/
surface queries, support terrain/biome/water brushes, update collision and GPU
data, and emit reversible changes. Integration wave 2. Prerequisites: A16, G04,
G07's frozen atomic edit protocol and G18's Money.
2. Exclusive ownership
The package worker owns game/src/plugins/terrain/ only. Root owns module/plugin/render feature and
schedule registration. Do not edit A16 or G07.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
TerrainPlugin |
Plugin |
pub struct TerrainPlugin; |
Registers chunk lifecycle, edits, collision and presentation. |
TerrainChunk |
component | pub struct TerrainChunk { pub coord: IVec2, pub asset: Handle<TerrainAsset>, pub record: u32, pub origin: Vec2, pub spacing_m: f32, pub side: u16 } |
Static chunk mapping; record indexes the validated A16 chunk table and side/spacing match A16 validation. |
TerrainRenderChunk |
component | pub struct TerrainRenderChunk; |
Small render marker on a terrain chunk entity; render extraction reads the A16 handle and record from TerrainChunk, never a duplicated render-world terrain graph. |
EditedTerrainSamples |
component | pub struct EditedTerrainSamples { pub heights_cm: Box<[i16]>, pub surfaces: Box<[u32]>, pub water_cm: Box<[i16]> } |
Dense per-edited-chunk copy-on-first-write overlay. Unedited chunks never copy A16 arrays. |
TerrainDirty |
component | pub struct TerrainDirty { pub min: UVec2, pub max: UVec2, pub flags: TerrainDirtyFlags, pub revision: u64 } |
Inclusive coalesced changed sample rectangle and monotonic nonzero edit revision. Render/collision consumers remember the last applied revision, so an unchanged retained record is a no-op. |
TerrainCollisionRevision |
component | pub struct TerrainCollisionRevision(pub u64); |
Last terrain edit revision applied to this chunk's collider. |
TerrainDirtyFlags |
bitflags | HEIGHT = 1, SURFACE = 2, WATER = 4, COLLISION = 8 |
Only declared bits valid. |
TerrainIndex |
resource | pub struct TerrainIndex { pub chunks: HashMap<IVec2,Entity>, pub chunk_span_m: f32 } |
Sole bounded coordinate-to-chunk accelerator; capacity changes only at load/unload. |
TerrainBrushKind |
enum | Raise \| Lower \| Smooth \| Flatten { height_cm: i16 } \| Paint { biome: AssetId } \| AddWater \| RemoveWater |
Fixed native tool vocabulary; parameters have explicit units. |
TerrainBrushPreview |
component | pub struct TerrainBrushPreview { pub center: Vec2, pub radius_m: f32, pub strength_per_s: f32, pub seconds: f32, pub kind: TerrainBrushKind } |
Frozen finite brush input on the G07 preview; preparation reads it without world mutation. |
TerrainStroke |
message | pub struct TerrainStroke { pub preview: Entity, pub center: Vec2, pub radius_m: f32, pub strength_per_s: f32, pub seconds: f32, pub kind: TerrainBrushKind } |
Updates the one terrain preview during drag; never carries a transaction or mutates terrain. |
TerrainChanged |
message | pub struct TerrainChanged { pub transaction: Entity, pub chunk: Entity, pub min: UVec2, pub max: UVec2 } |
Emitted once per affected chunk after mutation. |
TerrainEdit |
component | pub struct TerrainEdit { pub deltas: Box<[TerrainEditDelta]> } |
One G05 operation component on the G07 transaction, covering every affected chunk in stable chunk-ID order. |
TerrainEditDelta |
value | pub struct TerrainEditDelta { pub chunk: PersistentId, pub min: UVec2, pub max: UVec2, pub before: Box<[TerrainSample]>, pub after: Box<[TerrainSample]> } |
Inclusive row-major rectangle; array length is (max.x-min.x+1)*(max.y-min.y+1). |
TerrainSample |
POD value | pub struct TerrainSample { pub height_cm: i16, pub surface: u32, pub water_cm: i16 } |
Exact reversible live sample. |
TerrainEditPrepared |
message | pub struct TerrainEditPrepared { pub transaction: Entity, pub cost: Money } |
Emitted after TerrainEdit is attached, with checked total cost and no world mutation. |
TerrainEditPreparationRejected |
message | pub struct TerrainEditPreparationRejected { pub transaction: Entity, pub reason: PlacementFailure } |
Typed pre-debit rejection. |
CommitTerrainEdit |
message | pub struct CommitTerrainEdit { pub transaction: Entity, pub application: EditApplication } |
Exact G07-authorized application; initial/redo exists only after successful G18 debit. |
TerrainEditAcknowledged |
message | pub struct TerrainEditAcknowledged { pub transaction: Entity, pub application: EditApplication, pub accepted: bool } |
Exactly one result after all deltas apply atomically or none do. |
sample_terrain |
function | pub fn sample_terrain(chunk: &TerrainChunk, archive: &MappedTerrainArchive, archived: &ArchivedTerrainChunkRecord, edited: Option<&EditedTerrainSamples>, world_xz: Vec2) -> Option<TerrainPoint> |
Reads overlay when present, otherwise MappedTerrainArchive::heights/blend payload directly; allocation-free. |
terrain_chunk_at |
function | pub fn terrain_chunk_at(index: &TerrainIndex, world_xz: Vec2) -> Option<Entity> |
Allocation-free O(1) chunk entity lookup using canonical floor division. |
TerrainPoint |
value | pub struct TerrainPoint { pub height_m: f32, pub water_height_m: Option<f32>, pub surface: u32, pub normal: Vec3 } |
World metres, normalized finite normal. |
TerrainGpuAsset |
prepared render asset | pub struct TerrainGpuAsset { pub heights: Buffer, pub blends: Buffer, pub water: Buffer, pub index: Buffer, pub vertex_bytes: u64, pub blend_bytes: u64, pub water_bytes: u64 } |
Persistent buffers prepared once from A16's GPU-ready payload through Bevy's RenderAsset path; never reconstructed as a CPU Mesh. |
TerrainGpuUpdate |
render value | pub struct TerrainGpuUpdate { pub entity: Entity, pub asset: bevy::asset::AssetId<TerrainAsset>, pub record: u32, pub min: UVec2, pub max: UVec2, pub flags: TerrainDirtyFlags, pub revision: u64, pub byte_start: u32, pub byte_len: u32 } |
One extracted dirty rectangle referencing bytes in reusable staging. |
ExtractedTerrainUpdates |
render resource | pub struct ExtractedTerrainUpdates { pub updates: Vec<TerrainGpuUpdate>, pub bytes: Vec<u8>, pub uploaded_revisions: EntityHashMap<u64> } |
Render-world-only bounded queue and staging storage; capacities grow during load/edit warm-up, then remain stable. |
A16 freezes TerrainAsset archived grids/GPU payload and biome layer IDs. G07
owns PrepareConstruction, EditApplication, payment/rollback coordination and
history; G18's Money is the only cost type.
4. ECS ownership
Only a chunk changed by gameplay owns editable samples. Unedited sampling,
collision and initial GPU preparation read A16 mapped arrays/payload directly
through the handle. Each chunk also has the unit TerrainRenderChunk marker.
TerrainDirty is a monotonic change fact: collision and render consumers compare
its revision with their last-applied revision, so retaining it cannot cause idle
work or repeated upload.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
index_terrain_chunks |
Update/GameSet::Intent |
Commands, Res<Assets<TerrainAsset>>, ResMut<TerrainIndex>, Query<(Entity,&TerrainChunk),Added<TerrainChunk>> |
index and TerrainRenderChunk |
loading only; validates A16 record; no sample copy |
update_terrain_preview |
Update/GameSet::Intent |
Commands, MessageReader<TerrainStroke>, Query<&mut TerrainBrushPreview,With<ConstructionPreview>> |
latest preview brush only | matching active preview; no terrain write |
prepare_terrain_edits |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<PrepareConstruction>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>,&PersistentId)>, Query<&TerrainBrushPreview>, MessageWriter<TerrainEditPrepared>, MessageWriter<TerrainEditPreparationRejected> |
exact sorted before/after deltas and checked cost on transaction | preparation phase only; reads mapped/base or existing overlay; never inserts overlay/dirty state |
apply_terrain_edits |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<CommitTerrainEdit>, Query<&TerrainEdit>, Query<(&TerrainChunk,Option<&mut EditedTerrainSamples>,Option<&mut TerrainDirty>)>, Res<Assets<TerrainAsset>>, MessageWriter<TerrainChanged>, MessageWriter<TerrainEditAcknowledged> |
copy-on-first-write overlay, selected before/after rectangles and dirty revision | exact G07 application; validates every referenced chunk before changing any, then applies all or rejects all |
update_terrain_collision |
FixedUpdate/FixedGameSet::Cleanup |
Query<(&TerrainChunk,&EditedTerrainSamples,&TerrainDirty,&mut TerrainCollisionRevision,&mut Collider)> |
changed collider region and applied revision | edited revision only; initial collider uses A16 |
extract_terrain_updates |
render ExtractSchedule |
Extract<Query<(Entity,&TerrainChunk,&EditedTerrainSamples,&TerrainDirty),With<TerrainRenderChunk>>>, ResMut<ExtractedTerrainUpdates> |
coalesced rectangle bytes and metadata in reused render staging | only revisions newer than uploaded_revisions; no full chunk copy |
write_terrain_updates |
render Render/RenderSet::PrepareResources |
Res<RenderQueue>, Res<RenderAssets<TerrainGpuAsset>>, ResMut<ExtractedTerrainUpdates> |
RenderQueue::write_buffer into exact existing height/blend/water ranges and uploaded revision |
after extraction; no buffer recreation or full upload |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect shipped terrain/map/biome/brush data; oracle/TSVs for terrain manager,
height interpolation, cursor, biome paint, water and undo; commit d5657e5a
simulation/terrain.rs and terrain source records only for equations/tokens.
Observe tile scale, brush falloff, smoothing, water plane, texture scale/blend,
clamps, cost and collision timing.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: exact grid dimensions/axis orientation; centimetre quantization; height clamps; each brush kernel; biome channel normalization; water depth and terrain transaction cost. Recover before implementing those branches.
7. Required behavior
Sampling is continuous across chunk seams. Brush influence uses recovered
kernel and clamps; preparation records exact before/after values once without
touching live terrain. Only CommitTerrainEdit after the G18 debit may
materialize an overlay and apply after; undo/failure rollback applies before
and redo applies after. Undo/redo restores identical samples. Dirty bounds coalesce, update collision before
agents navigate, and upload only affected ranges. Surface texture world scale
comes from A16/material data, never guessed tiling. Corrupt dimensions fail at
asset validation, not with partial terrain.
8. Performance contract
Settled systems allocate/do work only on changed chunks. Sampling is O(1) and
zero-allocation; a stroke is O(samples in radius). An overlay allocates once
on first write to that chunk; unedited maps remain zero-copy. Deltas allocate
only during per-player-edit preparation and are discarded without world
mutation if preparation/debit fails. A16 payload preparation creates persistent GPU
buffers once. Dirty extraction copies only the changed rectangle into reused
staging and RenderQueue::write_buffer updates only corresponding ranges; it
never rebuilds a Mesh, vertex buffer, or full chunk. Tests count idle frame,
sample and unchanged-revision upload allocations; smaps/page faults cover A16;
the GPU ledger covers exact persistent buffers, textures and update bytes.
9. Acceptance criteria
Deliver all exports, mapped-versus-overlay sampling/seam/kernel/round-trip tests,
prepare-without-mutation, debit-gate and atomic multi-chunk acknowledgement,
proof unedited load/sampling allocates no sample arrays, and a no-dirty no-op
test. A changed 8x8 rectangle must produce only the corresponding height/blend/
water queue writes, while a repeated revision produces none. No source parsing,
whole-map rebuild/reupload per stroke, CPU Mesh mutation, per-frame GPU asset
creation, renderer wrapper, guess constants or eager second terrain graph. Root
bulk-validates original map appearance, editing, collision and history.
10. Required handoff
Report API/files, Bevy/render/physics dependencies, A16/G04/G07 symbols, registrations/order, evidence gaps, copy-on-first-write rationale, and allocator/ mapping/GPU/visual validation.
G06 — Authentic game cameras
1. Identity and outcome
The in-game camera pans, edge-scrolls, rotates, zooms, fits terrain, respects map bounds and performs source-authored transitions with original overhead semantics. It also exposes clean mode transitions for later immersive modes. Integration wave 2. Prerequisites: G03 and G05.
2. Exclusive ownership
The package worker owns game/src/plugins/camera/ only. Root owns registration/schedule/reflection.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ZooCameraPlugin |
Plugin |
pub struct ZooCameraPlugin; |
Registers camera lifecycle, input, fit and transition systems. |
ZooCamera |
component | pub struct ZooCamera; |
Marks the single active Bevy Camera3d for a zoo. |
CameraMode |
component enum | Overhead \| Follow(Entity) \| FirstPerson(Entity) \| Rail(Entity) \| Free |
Current presentation mode; referenced entity must exist. |
OverheadRig |
component | pub struct OverheadRig { pub focus: Vec2, pub yaw: f32, pub pitch: f32, pub distance: f32, pub pan_velocity: Vec2, pub turn_velocity: f32, pub zoom_velocity: f32 } |
World XZ/metres and radians; finite values clamped by tuning. |
CameraBounds |
component | pub struct CameraBounds { pub min: Vec2, pub max: Vec2, pub ground_buffer_m: f32 } |
Derived from G04 bounds/source tuning. |
CameraTuning |
component | pub struct CameraTuning { pub distance: RangeInclusive<f32>, pub pitch: RangeInclusive<f32>, pub pan_speed_mps: f32, pub turn_speed_rps: f32, pub zoom_speed_mps: f32, pub start_accel: f32, pub stop_accel: f32, pub edge_scroll: bool } |
Native recovered values; never fallback guesses. |
CameraTransition |
component | pub struct CameraTransition { pub from: Transform, pub to: Transform, pub elapsed: f32, pub duration: f32, pub easing: CameraEasing } |
Positive duration, removed at completion. |
CameraReturnState |
component | pub struct CameraReturnState { pub mode: CameraMode, pub transform: Transform, pub overhead: OverheadRig } |
Temporary snapshot on the actual camera, captured once before entering an immersive mode and removed after exact restoration; G31 must not duplicate it. |
CameraEasing |
enum | Linear \| SmoothStep |
Only modes evidenced in native camera records. |
SetCameraMode |
message | pub struct SetCameraMode { pub mode: CameraMode, pub transition_seconds: Option<f32> } |
Requests validated mode/target transition. |
RestoreCameraMode |
message | pub struct RestoreCameraMode { pub transition_seconds: Option<f32> } |
Returns to the stored G06 state; rejected if none exists. |
FocusCamera |
message | pub struct FocusCamera { pub world: Vec3, pub transition_seconds: Option<f32> } |
Changes overhead focus, preserving bounded tuning. |
CameraMoved |
message | pub struct CameraMoved { pub eye: Vec3, pub focus: Vec3 } |
Emitted only when pose materially changes. |
WorldPointerRay |
resource | pub struct WorldPointerRay(pub Option<Ray3d>); |
G06-owned projection of G03 raw PointerInput through the authoritative camera; absent outside viewport/no camera. |
Camera tuning comes directly from A14 CameraTuningDefinition selected by the
A19 map/scenario record; an absent or invalid reference is a load failure, never
a runtime default.
4. ECS ownership
Rig/tuning/bounds/transition are facts on the actual Bevy camera entity; there is no camera resource shadowing its transform.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
spawn_zoo_camera |
OnEnter(GamePhase::InGame) |
Commands, Query<(Entity,&WorldBounds),With<WorldRoot>>, Res<Assets<WorldScenarioAsset>> |
one camera entity/rig/tuning/bounds carrying root WorldMember |
no existing ZooCamera |
apply_camera_actions |
Update/GameSet::Intent |
MessageReader<ActionRequest>, Res<ControlAxes>, Query<(&mut OverheadRig,&CameraMode),With<ZooCamera>> |
desired velocities | after G03; overhead only |
apply_edge_scroll |
Update/GameSet::Intent |
Query<&Window>, Res<PointerInput>, Query<(&mut OverheadRig,&CameraTuning,&CameraMode)> |
pan velocity | enabled, pointer inside window |
advance_overhead_camera |
Update/GameSet::Presentation |
Res<Time<Real>>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(&mut Transform,&mut OverheadRig,&CameraTuning,&CameraBounds),With<ZooCamera>> |
rig and actual transform | unscaled real presentation delta; indexed Query::get, then mapped-or-overlay sample_terrain |
project_pointer_ray |
PostUpdate/GameSet::Presentation |
Res<PointerInput>, Query<(&Camera,&GlobalTransform),With<ZooCamera>>, ResMut<WorldPointerRay> |
current world ray | after camera transform propagation; G03 remains sole raw-pointer writer |
begin_camera_transition |
Update/GameSet::Intent |
Commands, MessageReader<SetCameraMode>, MessageReader<FocusCamera>, Query<(Entity,&Transform,&mut CameraMode),With<ZooCamera>> |
mode/transition | valid target only |
capture_camera_return_state |
Update/GameSet::Intent |
Commands, MessageReader<SetCameraMode>, Query<(Entity,&CameraMode,&Transform,&OverheadRig,Option<&CameraReturnState>),With<ZooCamera>> |
inserts one return state | transition from ordinary overhead to immersive mode only; before mode mutation |
restore_camera_return_state |
Update/GameSet::Intent |
Commands, MessageReader<RestoreCameraMode>, Query<(Entity,&mut CameraMode,&mut OverheadRig,&CameraReturnState),With<ZooCamera>> |
restored mode/rig and transition target; removes state after restoration | valid stored state only |
advance_camera_transition |
Update/GameSet::Presentation |
Commands, Res<Time<Real>>, Query<(Entity,&mut Transform,&mut CameraTransition),With<ZooCamera>> |
transform/removes transition | transition present; unscaled real delta; suppress ordinary advance |
report_camera_motion |
PostUpdate/GameSet::Diagnostics |
Query<(&Transform,&OverheadRig), (With<ZooCamera>,Changed<Transform>)>, MessageWriter<CameraMoved> |
message | changed pose only |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect camera XML/hotkeys/options and map records; oracle/TSVs for
ZTCameraCommandReader, overhead/UDV/ground-fit/bounded-object components;
commit d5657e5a domain/interaction/camera.rs and
features/presentation/camera.rs for evidence only. Live comparison is required
for pan axes, acceleration, zoom, pitch, fit, edge thresholds and transitions.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: units/signs and zoom-dependent speed curve; wheel accumulation; camera anchor clamp vs frustum; soft-fit equations; initial yaw/focus; easing; sky/frustum switches. Resolve exact values from native assets/oracle.
7. Required behavior
Inputs accelerate/decelerate per tuning, remain frame-rate independent, clamp
zoom/pitch/anchor and terrain-fit without penetrating water/ground. Rotation is
around focus. Transition interpolation uses shortest angular path and reaches
the exact endpoint. Invalid/deleted target returns safely to overhead. The
component Transform is the authoritative pose used by Bevy rendering and ray
casting. Camera motion and transitions always use Time<Real> and therefore
continue consistently while simulation speed or pause changes G32 time. No
guessed 24/70-style limits.
8. Performance contract
All steady camera systems allocate zero and are O(1) plus at most the terrain
chunk spatial lookup, which must not scan all chunks. Only changed poses emit.
No strings, path lookup or transform clone collections. Allocation tests cover
idle, pan/rotate/zoom and transition frames. Camera owns no mapped or GPU data.
9. Acceptance criteria
Deliver all exports with pure tests for clamp, acceleration, shortest-angle easing, exact transition completion and immersive capture/restore. No camera god resource, fixed guessed semantics, per-frame terrain scan or renderer wrapper. Root validates against live original at several map edges/zoom tiers and controller input.
10. Required handoff
Report API/files, source tuning contract, G03/G04/G05 and later G31 symbols, root registrations/order, evidence gaps, temporary mode seams and allocation/ camera-comparison validation.
G07 — Construction interaction and history
1. Identity and outcome
The player selects a build/delete tool, sees a validated preview, commits or cancels it, and can undo and redo every accepted world edit. G07 owns the generic edit lifecycle, atomic coordination and history; each domain owns its typed transaction component and application system. Integration wave 2 foundation. Prerequisites: A14, A15, G03, G04 and G18. G05/G08/G10 build on this protocol; they are not prerequisites of G07.
2. Exclusive ownership
The package worker owns game/src/plugins/construction/ only. Root owns registrations, schedules and
cross-domain transaction ordering.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ConstructionPlugin |
Plugin |
pub struct ConstructionPlugin; |
Registers tool, preview, commit, delete and history systems. |
ConstructionTool |
resource enum | Inspect \| Place(AssetId) \| Fence(AssetId) \| Path(AssetId) \| Terrain(TerrainBrushKind) \| Delete |
Sole active player tool; referenced definition must be native. |
ConstructionCursor |
component | pub struct ConstructionCursor { pub world: Vec3, pub normal: Vec3 } |
Actual ray hit; finite world metres. |
ConstructionPreview |
component | pub struct ConstructionPreview { pub definition: AssetId, pub transform: Transform, pub validity: PlacementValidity } |
At most one active preview entity. |
PlacementValidity |
enum | Pending \| Valid { cost: Money } \| Invalid(PlacementFailure) |
Uses the exact fixed-point currency value owned by G18; no raw integer convention or display strings. |
PlacementFailure |
enum | Unaffordable \| Locked \| Occupied \| OutsideMap \| TooSteep \| NoHeadroom \| InvalidHabitat \| InvalidTopology \| AuthoredRule(AssetId) |
Fixed UI-localizable reason vocabulary; the ID is localization/data identity only and is never dispatched as behavior. |
SelectConstructionTool |
message | pub struct SelectConstructionTool(pub ConstructionTool); |
Replaces tool and cancels incompatible preview. |
CommitConstruction |
message | pub struct CommitConstruction { pub preview: Entity } |
Confirm request for current valid preview only. |
CancelConstruction |
message | pub struct CancelConstruction; |
Cancels preview/tool drag without a transaction. |
DeleteEntity |
message | pub struct DeleteEntity(pub Entity); |
Requests validated deletion; dependent cleanup remains domain-owned. |
EditTransaction |
component | pub struct EditTransaction { pub sequence: u64, pub state: TransactionState, pub cost: Money } |
Durable history entity bearing one or more G05/G08/G10 typed operation components; cost is the exact checked total committed through G18. |
TransactionState |
enum | Applied \| Undone |
Matches live world exactly. |
EditApplication |
enum | InitialCommit \| Undo \| Redo \| FailureRollback |
Exact application direction sent to domains; FailureRollback reverses only an acknowledged partial application and never enters history. |
DomainAckStatus |
enum | NotExpected \| Pending \| Applied \| Reverted \| Rejected |
Fixed state for one explicit domain lane. |
EditCommitPhase |
enum | Preparing \| AwaitingEconomy \| Applying \| RollingBack \| AwaitingCompensation \| Complete \| Failed |
Transaction coordinator phase; world mutation is legal only in Applying/RollingBack. |
EditCommitProgress |
component | pub struct EditCommitProgress { pub phase: EditCommitPhase, pub application: EditApplication, pub cost: Money, pub terrain: DomainAckStatus, pub topology: DomainAckStatus, pub placement: DomainAckStatus } |
Focused fixed-size coordination on a pending transaction. Three named fields are the complete domain set; counts are derived from them, never stored in a generic domain map. |
EditPayment |
component | pub struct EditPayment { pub transaction: Entity, pub purpose: EditPaymentPurpose } |
Lives on the separate G18 operation entity so forward/reverse transfer results correlate without reusing the history entity as two economy operations. |
EditPaymentPurpose |
enum | InitialTransfer \| RedoTransfer \| FailureCompensation \| UndoCompensation |
Exact economy leg. Positive cost debits zoo, negative cost credits zoo, and the reverse compensation occurs only after inverse/rollback acknowledgements. |
EditHistory |
resource | pub struct EditHistory { pub entries: Vec<Entity>, pub cursor: usize, pub limit: usize, pub next_sequence: u64 } |
Bounded chronological transaction entities; redo tail is despawned on new commit. |
PrepareConstruction |
message | pub struct PrepareConstruction { pub transaction: Entity, pub preview: Entity } |
Fan-out preparation trigger. G05/G08/G10 respond only when their exact preview marker is present and never mutate world state. |
PrepareDeletion |
message | pub struct PrepareDeletion { pub transaction: Entity, pub target: Entity } |
Fan-out pre-removal trigger; every natural owner may attach one typed inverse before any despawn. |
EditApplicationAuthorized |
message | pub struct EditApplicationAuthorized { pub transaction: Entity, pub application: EditApplication } |
Sole forward/inverse dispatch gate: initial/redo only after exact G18 transfer, undo after history validation. |
RecordTransaction |
message | pub struct RecordTransaction(pub Entity); |
Emitted only by G07 after every expected domain acknowledgement succeeds; domains never emit it. |
ApplyTransaction |
message | pub struct ApplyTransaction { pub transaction: Entity, pub direction: EditDirection } |
Domains apply their typed operation on next Act pass. |
EditDirection |
enum | Undo \| Redo |
Exact requested direction. |
TransactionApplied |
message | pub struct TransactionApplied { pub transaction: Entity, pub direction: EditDirection, pub accepted: bool } |
G07 aggregate result after all exact domain acknowledgements and required economy leg; history moves only on accepted result. |
ConstructionCommitFailed |
message | pub struct ConstructionCommitFailed { pub transaction: Entity, pub reason: ConstructionCommitFailure } |
Typed terminal failure after preparation rejection, economy rejection or completed rollback/compensation. |
ConstructionCommitFailure |
enum | NoDomain \| Preparation(PlacementFailure) \| Economy(TransactionRejection) \| DomainRejected \| Compensation(TransactionRejection) |
Exact player/error surface; no partial mutation is reported as success. |
G10 owns placement-rule evaluation; G05 owns TerrainEdit,
TerrainEditPrepared/TerrainEditPreparationRejected, CommitTerrainEdit and
TerrainEditAcknowledged; G08 owns the corresponding Topology* types; G10
owns the corresponding Placement* types. Every acknowledgement has exact
shape { transaction:Entity, application:EditApplication, accepted:bool }.
G18 owns Money, TransactionRequest, TransactionCompleted,
TransactionRejected and cash mutation. This is the complete atomic policy;
there is no unresolved root decision, Box<dyn Command>, closure or blob.
4. ECS ownership
Only current tool and bounded entity-ID history are resources. Preview and transaction facts are entities/components, enabling typed domain systems.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
route_construction_actions |
Update/GameSet::Intent |
MessageReader<ActionRequest>, ResMut<ConstructionTool>, Query<Entity,With<ConstructionPreview>>, MessageWriter<CommitConstruction>, MessageWriter<CancelConstruction> |
typed requests/tool | after G03 |
update_construction_cursor |
Update/GameSet::Intent |
Res<WorldPointerRay>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<&mut ConstructionCursor> |
indexed mapped-or-overlay ray hit | active tool; consumes G06 ray, never mutates G03 input |
begin_construction_preparation |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<CommitConstruction>, Query<(&ConstructionPreview,&WorldMember)>, ResMut<EditHistory>, MessageWriter<PrepareConstruction> |
one transaction/progress carrying the preview's WorldMember plus preparation fan-out; no debit/world/history mutation |
valid preview with no pending operation |
begin_deletion_preparation |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<DeleteEntity>, ResMut<EditHistory>, Query<&WorldMember>, MessageWriter<PrepareDeletion> |
one transaction/progress carrying the target's WorldMember plus exact pre-removal fan-out |
target belongs to active world; no despawn |
collect_domain_preparations |
FixedUpdate/FixedGameSet::Act |
MessageReader<TerrainEditPrepared>, MessageReader<TerrainEditPreparationRejected>, MessageReader<TopologyEditPrepared>, MessageReader<TopologyEditPreparationRejected>, MessageReader<PlacementEditPrepared>, MessageReader<PlacementEditPreparationRejected>, Query<(&mut EditCommitProgress,&mut EditTransaction)> |
checked total cost in progress/history record and named expected-domain statuses | after G05/G08/G10 preparation and apply_deferred; rejection/no-domain fails before debit |
request_construction_economy |
FixedUpdate/FixedGameSet::Economy |
Commands, Query<(Entity,&WorldMember,&mut EditCommitProgress),Changed<EditCommitProgress>>, MessageWriter<TransactionRequest> |
separate payment operation carrying the transaction's WorldMember plus EditPayment::InitialTransfer; positive cost requests zoo debit, negative cost requests zoo credit for its checked absolute value |
complete successful preparation; zero cost authorizes directly; before G18 apply_transactions |
complete_construction_economy |
FixedUpdate/FixedGameSet::Economy |
Commands, MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<&EditPayment>, Query<&mut EditCommitProgress>, MessageWriter<EditApplicationAuthorized>, MessageWriter<ConstructionCommitFailed> |
exact initial/redo authorization or terminal no-mutation failure | exact transfer operation/purpose only; after G18 apply_transactions |
begin_history_application |
FixedUpdate/FixedGameSet::Economy |
Commands, MessageReader<ApplyTransaction>, Query<(&EditTransaction,&WorldMember,&mut EditCommitProgress)>, MessageWriter<TransactionRequest>, MessageWriter<EditApplicationAuthorized> |
undo authorization immediately after validation; redo creates a world-scoped EditPayment::RedoTransfer and waits |
no existing application; redo never reaches domains before G18 completion |
dispatch_domain_application |
FixedUpdate/FixedGameSet::Act |
MessageReader<EditApplicationAuthorized>, Query<(Entity,Option<&TerrainEdit>,Option<&TopologyEdit>,Option<&PlacementEdit>,&mut EditCommitProgress)>, MessageWriter<CommitTerrainEdit>, MessageWriter<CommitTopologyEdit>, MessageWriter<CommitPlacementEdit> |
one exact message for each present typed component and pending named status | application must equal progress; initial/redo already paid, undo validated |
collect_domain_acknowledgements |
FixedUpdate/FixedGameSet::Cleanup |
MessageReader<TerrainEditAcknowledged>, MessageReader<TopologyEditAcknowledged>, MessageReader<PlacementEditAcknowledged>, Query<&mut EditCommitProgress>, MessageWriter<RecordTransaction>, MessageWriter<TransactionApplied> |
named statuses; initial records, redo completes, undo advances to AwaitingCompensation only after all expected lanes acknowledge |
duplicate/stale/wrong-application acknowledgements ignored; first rejection begins rollback; undo cannot move history before compensation |
dispatch_failure_rollback |
FixedUpdate/FixedGameSet::Act |
Query<(Entity,Option<&TerrainEdit>,Option<&TopologyEdit>,Option<&PlacementEdit>,&mut EditCommitProgress)>, MessageWriter<CommitTerrainEdit>, MessageWriter<CommitTopologyEdit>, MessageWriter<CommitPlacementEdit> |
FailureRollback only to lanes currently Applied |
after a domain rejection; never asks an unapplied lane to undo |
finish_rollback_and_compensation |
FixedUpdate/FixedGameSet::Economy |
Commands, Query<(Entity,&mut EditCommitProgress,&EditTransaction)>, MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<&EditPayment>, MessageWriter<TransactionRequest>, MessageWriter<TransactionApplied>, MessageWriter<ConstructionCommitFailed> |
exact reverse G18 transfer after successful undo or failure rollback; undo emits accepted aggregate only after compensation, rollback emits terminal failure | accounts/direction exactly reverse the corresponding completed transfer |
cancel_preview |
Update/GameSet::Ui |
Commands, MessageReader<CancelConstruction>, Query<Entity,With<ConstructionPreview>> |
despawn preview | any phase/tool exit; after intent routing |
record_transactions |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<RecordTransaction>, ResMut<EditHistory>, Query<&EditTransaction> |
bounded history/redo disposal | acknowledged applied transaction |
request_undo_redo |
Update/GameSet::Intent |
MessageReader<ActionRequest>, Res<EditHistory>, MessageWriter<ApplyTransaction> |
one direction request | no transaction pending |
advance_history |
FixedUpdate/FixedGameSet::Cleanup |
MessageReader<TransactionApplied>, ResMut<EditHistory>, Query<&mut EditTransaction> |
cursor/state | accepted aggregate result only; redo transfer precedes domain application and undo compensation follows successful inverse application |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect build-mode/UI data, placement cursors, failure localization and entity
rules; oracle/TSVs for placement, delete and undo functions; commit d5657e5a
domain/simulation/placement.rs, features/simulation/{placement,delete,undo}.rs
only for discovered commands/rules. Observe drag, single-placement, right-click,
refund, selection, confirmation and undo granularity.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: which actions are transactional together; history bound; redo-tail behavior; refund ratios; preview LOD/colors; continuous placement; deletability and dependent cleanup. Recover, do not infer generic editor behavior.
7. Required behavior
Only valid current previews commit. Preparation freezes typed before/after records and checked signed net cost but cannot mutate the world. G18 transfer completes before any initial/redo forward application. The three named domain acknowledgement lanes are counted from fixed fields; history records only after all expected lanes accept. Any rejection reverses only lanes that acknowledged application, waits for their rollback acknowledgements, then reverses the completed transfer through G18. Undo applies exact inverses before its compensation; redo completes its transfer before exact forwards. Failure during history application restores the pre-request world/economy state and leaves cursor/state unchanged. A new commit after undo deletes the redo tail. History stores transaction entities, never snapshots of the world. Deletion uses domain cleanup and records sufficient data for exact restoration.
8. Performance contract
Idle construction/history systems allocate zero. Cursor update is O(1) plus
indexed terrain hit. History allocation occurs only on edit and retains a
configured bound; dropping redo/old entries recursively frees their components.
No per-frame world scan, payload cloning or generic serialization. Counters
cover idle/cursor/undo request; transaction heap is measured per edit. No mapped
or GPU bytes beyond preview handles.
9. Acceptance criteria
Deliver exports and tests for invalid/no-domain preparation, economy-before-world ordering, exact cursor progression, multi-domain acknowledgement counting, partial rollback/compensation, undo/redo, stale/duplicate acknowledgement rejection, redo-tail disposal and history bound. No generic command trait, opaque operation enum collecting every domain, world snapshot or fake success. Root bulk-tests terrain/topology/object/economy edits.
10. Required handoff
Report API/files, registrations/order, G03/G04/G05/G08/G10/G18 symbols, exact three-lane ordering, evidence gaps, seams and allocation/history/gameplay checks.
G08 — Fences, paths and portals
1. Identity and outcome
Players construct authentic fences, gates, portals, ground/elevated paths and supports as connected ECS topology used directly by habitats and navigation. Integration wave 2. Prerequisites: A14, A15, A17, G04, G07 and G18.
2. Exclusive ownership
The package worker owns game/src/plugins/topology/ only. Root owns declarations, registration,
schedules and operation integration.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
TopologyPlugin |
Plugin |
pub struct TopologyPlugin; |
Registers typed placement, connectivity and history application. |
TopologyNode |
component | pub struct TopologyNode { pub cell: IVec3 } |
Grid coordinate; Z component is authored elevation level. |
FenceEdge |
component | pub struct FenceEdge { pub definition: AssetId, pub a: Entity, pub b: Entity } |
Connects distinct topology-node entities; one undirected edge per pair/level. |
PathTile |
component | pub struct PathTile { pub definition: AssetId, pub cell: IVec3 } |
One path tile per cell/level. |
Gate |
component | pub struct Gate { pub open: bool, pub locked: bool } |
Traversable fence opening; state is live/save-relevant. |
Portal |
component | pub struct Portal { pub destination: Entity, pub bidirectional: bool } |
Valid destination topology entity. |
PathSupport |
component | pub struct PathSupport { pub path: Entity, pub ground_height_m: f32 } |
Derived support for an elevated path tile. |
TopologyIndex |
resource | pub struct TopologyIndex { pub nodes: HashMap<IVec3,Entity>, pub paths: HashMap<IVec3,Entity>, pub edges: HashMap<EdgeKey,Entity> } |
Sole bounded spatial lookup; capacity changes only on edits/load. |
EdgeKey |
value | pub struct EdgeKey { pub lo: IVec3, pub hi: IVec3 } |
Canonically sorted endpoints. |
FencePreview |
component | pub struct FencePreview { pub definition: AssetId, pub from: IVec3, pub to: IVec3 } |
Exact straight/rubber-band candidate on a G07 preview; no live topology. |
PathPreview |
component | pub struct PathPreview { pub definition: AssetId, pub from: IVec3, pub to: IVec3 } |
Exact ground/elevated candidate on a G07 preview. |
PlaceFence |
message | pub struct PlaceFence { pub preview: Entity, pub definition: AssetId, pub from: IVec3, pub to: IVec3 } |
Updates/creates FencePreview; never mutates topology or cash. |
PlacePath |
message | pub struct PlacePath { pub preview: Entity, pub definition: AssetId, pub from: IVec3, pub to: IVec3 } |
Updates/creates PathPreview; never mutates topology or cash. |
SetGateState |
message | pub struct SetGateState { pub gate: Entity, pub open: bool } |
State request; lock/behavior policy validates separately. |
TopologyChanged |
message | pub struct TopologyChanged { pub transaction: Option<Entity>, pub bounds: IRect } |
Affected XZ grid region for G09/G14 incremental rebuild. |
TopologyEdit |
component | pub struct TopologyEdit { pub created: Box<[TopologySnapshot]>, pub removed: Box<[TopologySnapshot]> } |
Attached to G07 transaction; snapshots use stable IDs so repeated undo/redo never retain stale Bevy entities. |
TopologySnapshot |
value | Fixed enum Node { id: PersistentId, cell: IVec3 } \| Fence { id, definition, a: PersistentId, b: PersistentId, gate: Option<Gate> } \| Path { id, definition, cell } \| Portal { id, destination, bidirectional } |
Typed restoration data; no generic bag. |
TopologyEditPrepared |
message | pub struct TopologyEditPrepared { pub transaction: Entity, pub cost: Money } |
TopologyEdit is complete and immutable; no live topology/cash changed. |
TopologyEditPreparationRejected |
message | pub struct TopologyEditPreparationRejected { pub transaction: Entity, pub reason: PlacementFailure } |
Typed pre-debit rejection. |
CommitTopologyEdit |
message | pub struct CommitTopologyEdit { pub transaction: Entity, pub application: EditApplication } |
Exact authorized forward/inverse; initial/redo is sent only after G18 debit completes. |
TopologyEditAcknowledged |
message | pub struct TopologyEditAcknowledged { pub transaction: Entity, pub application: EditApplication, pub accepted: bool } |
One all-or-none G08 result. |
Definition grid scale, fence/path placement constraints and navigation-link
records come from A14/A17. Every HashMap above is exactly
bevy::platform::collections::HashMap. G07 owns the payment gate and
coordination. Therefore every
priced G08 edit reaches G18 before CommitTopologyEdit; G08 never debits cash
directly and never mutates topology from PlaceFence/PlacePath.
4. ECS ownership
Topology facts live on actual nodes/edges/tiles. The resource is only a spatial index, rebuilt from components on load and never authoritative over them.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
index_loaded_topology |
OnEnter(GamePhase::InGame) |
ResMut<TopologyIndex>, Query<(Entity,&TopologyNode)>, Query<(Entity,&PathTile)>, Query<(Entity,&FenceEdge)> |
pre-sized index | once after G04 |
update_fence_previews |
Update/GameSet::Intent |
Commands, MessageReader<PlaceFence>, Query<&mut FencePreview,With<ConstructionPreview>> |
preview facts only | active fence tool |
update_path_previews |
Update/GameSet::Intent |
Commands, MessageReader<PlacePath>, Query<&mut PathPreview,With<ConstructionPreview>> |
preview facts only | active path tool |
prepare_topology_construction |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<PrepareConstruction>, Res<TopologyIndex>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(Option<&FencePreview>,Option<&PathPreview>)>, MessageWriter<TopologyEditPrepared>, MessageWriter<TopologyEditPreparationRejected> |
immutable snapshots with allocator-issued IDs, checked cost and one prepared result | matching topology preview; validates every segment/headroom/overlap, reserves IDs, but changes no entity/index/cash |
prepare_topology_deletion |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<PrepareDeletion>, Res<Assets<WorldDefinitionsAsset>>, Query<(Option<&TopologyNode>,Option<&FenceEdge>,Option<&PathTile>,Option<&Gate>,&PersistentId)>, Query<&PersistentId>, MessageWriter<TopologyEditPrepared>, MessageWriter<TopologyEditPreparationRejected> |
exact removed snapshots/refund cost on transaction | target has G08-owned topology; reads all dependent portals/supports before any despawn |
apply_topology_edits |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<CommitTopologyEdit>, Query<&TopologyEdit>, Query<Entity,With<WorldRoot>>, ResMut<TopologyIndex>, MessageWriter<TopologyChanged>, MessageWriter<TopologyEditAcknowledged> |
exact forward/inverse entities with WorldMember/reserved PersistentIds and index |
validates full applicability first, then all-or-none; initial/redo only after G07 reports G18 debit completion |
set_gate_state |
FixedUpdate/FixedGameSet::Act |
MessageReader<SetGateState>, Query<&mut Gate> |
gate state, change message | valid unlocked gate |
remove_orphan_supports |
FixedUpdate/FixedGameSet::Cleanup |
Commands, Query<(Entity,&PathSupport)>, Query<(),With<PathTile>> |
orphan despawn | changed topology only |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect fence/path/gate/elevated-path definitions and placement UI; oracle/TSVs
for ZTFence, ZTPath, portals, rubber band and traversal; commit d5657e5a
domain/runtime fence/path files only for evidence. Observe grid size, 45-degree
rotation, intersections, cost, auto-gates/supports, slopes and deletion.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: exact world-to-grid formula (old evidence includes source tile quantization and a 0.785398185 rotation step); rubber-band line rule; overlaps; gate orientation; path curb variants; elevated levels, slopes/headroom/supports.
7. Required behavior
Canonical indexing rejects duplicate nodes/edges/tiles. Placement expands by authentic rules and preparation validates every segment without mutation. G07 completes one checked G18 debit before G08 applies the immutable snapshots; acknowledgement occurs only after the whole index/entity change succeeds. Gates alter traversal without destroying the fence. Elevated paths retain levels and supports. Undo/redo restores IDs/connectivity. Deleting topology cleans portals/supports and triggers containment/navigation.
8. Performance contract
Steady systems allocate/do no work without changes. Lookup is expected O(1);
placement is O(new segments), affected rebuilds are regional. Index reserves
on load/edit outside settled frames. No all-world connectivity rebuild per frame
or path-vector cloning. Count idle/gate allocations, index heap, A14/A17 faults
and mesh/support GPU bytes.
9. Acceptance criteria
Deliver exports and tests for canonical edges, duplicate rejection, recovered line expansion, preparation-without-mutation, debit-before-apply, atomic acknowledgement, gate traversal, elevated support cleanup and undo/redo identity. No topology manager world, string token state or speculative geometry. Root bulk-validates construction, habitats and navigation.
10. Required handoff
Report API/files, map/definition dependencies, root registrations/order, G04/G07/G09/G14/G18 contracts, evidence gaps, index seam and allocator/mapping/GPU/ topology validation.
G09 — Habitat containment and membership
1. Identity and outcome
Closed fence topology forms habitats; animals/objects acquire correct habitat membership, breaches update containment, and suitability systems receive exact area, biome and traversability facts. Integration wave 2. Prerequisites: A16, G05 and G08.
2. Exclusive ownership
The package worker owns game/src/plugins/habitat/ only. Root owns module/plugin/message ordering.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
HabitatPlugin |
Plugin |
pub struct HabitatPlugin; |
Registers regional rebuild, membership and containment. |
Habitat |
component | pub struct Habitat; |
Marks a habitat entity. |
HabitatRegion |
component | pub struct HabitatRegion { pub cells: Box<[IVec2]>, pub area_m2: f32, pub boundary_edges: Box<[Entity]> } |
Deterministically sorted enclosed cells and fence edges; rebuilt only on topology change. |
HabitatSummary |
component | pub struct HabitatSummary { pub land_m2: f32, pub water_m2: f32, pub biome_area_m2: Box<[(AssetId,f32)]>, pub breached: bool } |
Derived exact regional facts; biome pairs sorted by ID. |
HabitatMember |
component | pub struct HabitatMember(pub Entity); |
Entity's current habitat; absent means none. |
HabitatLocatable |
component | pub struct HabitatLocatable; |
Explicit marker for entities whose habitat membership follows world position; prevents querying every transformed entity. |
Containment |
component | pub struct Containment { pub habitat: Entity, pub contained: bool } |
Animal containment relative to membership/boundary. |
HabitatIndex |
resource | pub struct HabitatIndex { pub cells: bevy::platform::collections::HashMap<IVec2,Entity> } |
Sole bounded cell-to-habitat index; component state remains authoritative. |
HabitatChanged |
message | pub struct HabitatChanged { pub habitat: Entity, pub topology_bounds: IRect } |
Region membership/summary changed. |
ContainmentChanged |
message | pub struct ContainmentChanged { pub entity: Entity, pub habitat: Option<Entity>, pub contained: bool } |
Emitted on material membership/containment transition. |
RebuildHabitats |
message | pub struct RebuildHabitats(pub IRect); |
Regional request derived from G08 TopologyChanged. |
locate_habitat |
function | pub fn locate_habitat(index: &HabitatIndex, world_xz: Vec2, cell_size_m: f32) -> Option<Entity> |
Allocation-free canonical floor conversion and lookup. |
Cell conversion uses the active A16 TerrainArchive.cell_size_cm; preflight
validates that G08 topology cells use that same grid. Do not add a second
grid-scale resource or generic membership trait.
4. ECS ownership
Regions/summaries are live components on habitat entities; index is derived bounded acceleration data. Objects and animals carry one direct relationship.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
request_habitat_rebuilds |
FixedUpdate/FixedGameSet::Cleanup |
MessageReader<TopologyChanged>, MessageWriter<RebuildHabitats> |
regional requests | after G08 edits |
rebuild_habitat_regions |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<RebuildHabitats>, ResMut<HabitatIndex>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(Entity,&FenceEdge,Option<&Gate>)>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<Entity,With<WorldRoot>> |
habitats with WorldMember, regions, summaries, index and HabitatChanged |
exactly one world root; affected region; indexed mapped-or-overlay sampling |
update_habitat_membership |
FixedUpdate/FixedGameSet::Cleanup |
Commands, Res<HabitatIndex>, Query<(Entity,&GlobalTransform,Option<&HabitatMember>),(With<HabitatLocatable>,Or<(Changed<GlobalTransform>,Without<HabitatMember>)>)>, MessageWriter<ContainmentChanged> |
direct relationship | only explicitly eligible moving entities |
update_containment_on_change |
FixedUpdate/FixedGameSet::Cleanup |
Query<(Entity,&HabitatMember,&mut Containment)>, MessageReader<HabitatChanged>, MessageWriter<ContainmentChanged> |
containment | changed habitat only |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect habitat/fence/biome/species data; oracle/TSVs for habitat manager,
containment, traversable area and suitability; commit d5657e5a habitat regions
and animal assessment solely for evidence. Observe enclosure recognition, gates,
map boundary, multi-region fences, escape and habitat UI area calculation.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: flood-fill domain and diagonal rules; map-edge containment; gates/ portals; fence traversability by species; habitat identity persistence through edits; water/biome area integration; what objects count as members.
7. Required behavior
Closed valid boundaries produce deterministic regions; open boundaries mark breach/no enclosure according to recovered rules. Regional edits preserve unaffected habitat entities/IDs where possible. Moved/spawned entities update membership once; deleted habitats remove relationships. Summaries match terrain cells and expose facts, not a guessed suitability score. No rectangular shortcut except where evidence defines one.
8. Performance contract
No per-frame global flood fill or entity scan. Rebuild is O(affected cells + edges) on topology events; membership is changed-transform O(1) lookup.
Region arrays allocate only on rebuild and replace old storage. Idle/movement
allocation tests are zero; track index/region heap and terrain mapped pages; no
GPU ownership.
9. Acceptance criteria
Deliver exports and tests for enclosure/breach, deterministic regions, regional preservation, membership transitions and area summaries using recovered grid rules. No habitat world mirror, bounding-box guess or per-frame rebuild. Root validates with fence edits, animals and suitability.
10. Required handoff
Report API/files, registrations/filter wiring, G05/G08/G11/G12 symbols, evidence gaps, index seam and allocator/region/gameplay validation.
G10 — Scenery and facility placement
1. Identity and outcome
Scenery and facilities use authored footprints, orientation, entrances, terrain/topology/habitat constraints, locks and costs to produce an authentic preview and atomic placed entity. Integration wave 2. Prerequisites: A14, A15, G04, G05, G07–G09 and G18.
2. Exclusive ownership
The package worker owns game/src/plugins/placement/ only. Root owns registrations/order and G18/G23
integration.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
PlacementPlugin |
Plugin |
pub struct PlacementPlugin; |
Registers preview construction, rule evaluation, commit and history. |
Placeable |
component | pub struct Placeable { pub definition: AssetId } |
Immutable A14 object/placeable definition identity. |
FootprintOccupancy |
component | pub struct FootprintOccupancy { pub origin: IVec2, pub quarter_turns: u8 } |
Fixed live placement fact; occupied cells are iterated directly from A14's mapped footprint range and transformed without a per-entity heap copy. |
AuthoredEntrance |
component | pub struct AuthoredEntrance { pub local_position: Vec3, pub local_forward: Vec3 } |
Definition-provided service/path docking point. |
PlacementIndex |
resource | pub struct PlacementIndex { pub occupied: HashMap<IVec2,Entity> } |
Sole bounded footprint acceleration index. |
EvaluatePlacement |
message | pub struct EvaluatePlacement { pub preview: Entity, pub definition: AssetId, pub transform: Transform } |
Latest preview fact; repeated requests replace validity. |
PlaceObject |
message | pub struct PlaceObject { pub preview: Entity } |
Player/domain request routed back through G07 CommitConstruction; never mutates or debits directly. |
RemovePlacedObject |
message | pub struct RemovePlacedObject { pub entity: Entity } |
Player/domain deletion request routed through G07 DeleteEntity; G10 reads footprint before any removal. |
ObjectPlaced |
message | pub struct ObjectPlaced { pub transaction: Entity, pub entity: Entity, pub definition: AssetId } |
Emitted after entity, occupancy and economy effects succeed. |
PlacementMutation |
enum | Create \| Remove |
Meaning of the initial commit; undo applies the opposite and redo repeats it. |
PlacementEdit |
component | pub struct PlacementEdit { pub entity: PersistentId, pub definition: AssetId, pub transform: Transform, pub cells: Box<[IVec2]>, pub mutation: PlacementMutation, pub applied_entity: Option<Entity> } |
Exact reversible object placement/removal on G07 transaction. |
PlacementEditPrepared |
message | pub struct PlacementEditPrepared { pub transaction: Entity, pub cost: Money } |
Immutable edit and checked cost/refund are attached without world/index/cash mutation. |
PlacementEditPreparationRejected |
message | pub struct PlacementEditPreparationRejected { pub transaction: Entity, pub reason: PlacementFailure } |
Typed pre-debit rejection. |
CommitPlacementEdit |
message | pub struct CommitPlacementEdit { pub transaction: Entity, pub application: EditApplication } |
Exact G07-authorized application; forward create/redo follows completed G18 debit. |
PlacementEditAcknowledged |
message | pub struct PlacementEditAcknowledged { pub transaction: Entity, pub application: EditApplication, pub accepted: bool } |
One all-or-none placement-domain result. |
validate_placement |
function | pub fn validate_placement(definition: &ArchivedPlaceableDefinition, transform: &Transform, terrain: impl Fn(Vec2)->Option<TerrainPoint>, occupied: impl Fn(IVec2)->bool, habitat: Option<Entity>, unlocked: bool, affordable: bool) -> PlacementValidity |
Pure, allocation-free rule evaluation in authoritative order. |
A14 must freeze ArchivedPlaceableDefinition including footprint cells, pivot,
rotation increments, constraints, cost, unlock and entrance. G18/G23 provide
affordable/unlocked facts without G10 owning money/progression copies.
4. ECS ownership
Placed entities carry their own definition, transform, footprint and entrance. The resource accelerates occupancy only and is reconstructed from components.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
create_object_preview |
Update/GameSet::Intent |
Commands, Res<ConstructionTool>, Query<&ConstructionCursor>, Res<Assets<WorldDefinitionsAsset>>, Query<Entity,With<ConstructionPreview>>, Query<Entity,With<WorldRoot>> |
one preview entity/handle with WorldMember |
active Place tool; exactly one world root |
evaluate_object_preview |
Update/GameSet::Intent |
MessageReader<EvaluatePlacement>, Res<PlacementIndex>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<&HabitatMember>, Query<&mut ConstructionPreview> |
validity only | changed input; indexed mapped-or-overlay sampling |
route_object_commit_requests |
Update/GameSet::Intent |
MessageReader<PlaceObject>, MessageWriter<CommitConstruction> |
exact G07 request | matching active object preview only |
route_object_removal_requests |
Update/GameSet::Intent |
MessageReader<RemovePlacedObject>, MessageWriter<DeleteEntity> |
exact G07 deletion request | never despawns directly |
prepare_object_placement |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<PrepareConstruction>, Res<ConstructionTool>, Query<&ConstructionPreview>, Res<Assets<WorldDefinitionsAsset>>, Res<TerrainIndex>, Res<PlacementIndex>, ResMut<PersistentIdAllocator>, MessageWriter<PlacementEditPrepared>, MessageWriter<PlacementEditPreparationRejected> |
immutable create edit with allocator-issued ID, exact cells and checked cost | ConstructionTool::Place; revalidates preview; no entity/index/cash mutation |
prepare_object_removal |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<PrepareDeletion>, Res<Assets<WorldDefinitionsAsset>>, Query<(&PersistentId,&Placeable,&Transform,&FootprintOccupancy)>, MessageWriter<PlacementEditPrepared>, MessageWriter<PlacementEditPreparationRejected> |
immutable remove edit and evidenced refund | reads mapped footprint/dependencies before despawn; no mutation |
apply_placement_edits |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<CommitPlacementEdit>, Query<&mut PlacementEdit>, Query<Entity,With<WorldRoot>>, ResMut<PlacementIndex>, Res<Assets<ScenePrefabAsset>>, MessageWriter<ObjectPlaced>, MessageWriter<PlacementEditAcknowledged> |
exact create/remove or inverse, WorldMember, reserved PersistentId and occupancy |
validates every cell/entity first, then applies all or rejects all; initial/redo only after G07's G18 payment gate |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect entity/footprint/placement/UI/localization records; oracle/TSVs for
place object, footprint, collision, affordability and research predicates;
commit d5657e5a placement/world/facility files for evidence only. Observe
preview LOD/colors, grid snap, free rotation, entrances, slopes, tank/habitat
rules, repeated placement, cost/refund and errors.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Known original failure tokens include unaffordable, unresearched, overlap, too-steep, no-headroom and out-of-traversable-area. Questions: predicate order, cell rounding, rotation step, footprint pivot, terrain tolerance, object-object exceptions and special placement types. Recover before coding each rule.
7. Required behavior
Preview uses the same transform/footprint/rules as commit. First failing rule
produces the authentic typed/localizable reason. Preparation freezes the
complete edit and cost/refund without mutation. G07 completes the required G18
economy leg before CommitPlacementEdit; commit is atomic and creates
natural typed prefab components/handles, not a generic entity bag. Occupancy is
updated with world mutation. Undo/redo restores exact persistent identity and
cells. Removal releases cells and dependent domains clean relationships.
8. Performance contract
Idle systems allocate zero. Preview validation is O(footprint cells) with
O(1) indexed checks and changed-input gating. Preview handles are reused;
placement arrays allocate only at commit. No whole-world collision scan, path
construction or strings. Count idle/unchanged validation; track placement index
heap, A14/A15 faults and referenced model/material GPU bytes.
9. Acceptance criteria
Deliver exports and tests for transform/footprint parity, predicate precedence, prepare-without-mutation, payment-before-apply, exact acknowledgement, atomic failure, occupancy, rotation and undo/redo. No guessed fallback rules, generic prefab runtime or duplicate economy/progression state. Root bulk-tests representative scenery/facilities against original behavior.
10. Required handoff
Report API/files, A14/A15/G05/G07–G09/G18/G23 contracts, registrations/order, evidence gaps, atomicity seams and allocator/mapping/GPU/placement validation.
G11 — Animal lifecycle and family
1. Identity and outcome
Animals are adopted from native species/variant data, age through authentic stages, mate, become pregnant, give birth with correct inheritance/family links, and can be released. Integration wave 3. Prerequisites: A03, A14, G04 and G09.
2. Exclusive ownership
The package worker owns game/src/plugins/animal_lifecycle/ only. Root owns declarations,
registrations, schedules and later economy/health/persistence wiring.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
AnimalLifecyclePlugin |
Plugin |
pub struct AnimalLifecyclePlugin; |
Registers adoption, ageing, reproduction, birth, release and cleanup. |
Animal |
component | pub struct Animal; |
Marker for a live animal entity. |
SpeciesHandle |
component | pub struct SpeciesHandle { pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId } |
Sole owning A03 catalogue handle plus stable record ID; lookup stays mapped and allocation-free. |
AnimalVariant |
component | pub struct AnimalVariant(pub AssetId); |
Valid A03 variant ID for the species. |
AnimalSex |
component | pub struct AnimalSex(pub Sex); |
Thin live component around A03's exact Sex; live animals may only use Female or Male, never authored fallback Any. |
AnimalLifeStage |
component | pub struct AnimalLifeStage(pub LifeStage); |
Wraps A03's exact Juvenile \| Young \| Adult \| Elder vocabulary. |
Age |
component | pub struct Age { pub born_tick: u64 } |
Exact G32 tick of birth/import-derived birth; current age is saturating tick subtraction. |
Pregnancy |
component | pub struct Pregnancy { pub father: Entity, pub due_tick: u64 } |
Female-only absolute G32 due tick; no float accumulation. |
BirthDue |
component | pub struct BirthDue; |
One-shot marker preventing duplicate due requests before delivery removes pregnancy. |
ReproductionRng |
component | pub struct ReproductionRng(pub DeterministicRng); |
Domain-local G32 stream derived from ZooSeed plus PersistentId; sole source for reproduction rolls. |
Parents |
component | pub struct Parents { pub mother: Option<Entity>, pub father: Option<Entity> } |
Direct lineage links; missing/deleted ancestors become None. |
AdoptAnimal |
message | pub struct AdoptAnimal { pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId, pub variant: Option<AssetId>, pub sex: Option<Sex>, pub habitat: Entity, pub transform: Transform } |
Uses A03 IDs/vocabulary directly; Sex::Any is rejected for an explicit live choice. |
PendingAdoption |
component | pub struct PendingAdoption { pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId, pub variant: AssetId, pub sex: Sex, pub habitat: Entity, pub transform: Transform, pub cost: Money } |
Immutable resolved adoption on its operation entity; no animal exists yet. |
AnimalAdopted |
message | pub struct AnimalAdopted { pub operation: Entity, pub animal: Entity, pub cost: Money } |
Emitted after matching economy completion and spawn. |
SpawnAnimalRequest |
message | pub struct SpawnAnimalRequest { pub operation: Entity, pub catalog: Handle<SpeciesCatalogAsset>, pub species: AssetId, pub variant: Option<AssetId>, pub sex: Option<Sex>, pub habitat: Entity, pub transform: Transform, pub parents: Parents } |
Natural-owner creation boundary used after adoption payment and by G29 cloning; operation is caller-owned correlation, while G11 resolves omitted variant/sex with its deterministic domain rules. |
AnimalSpawned |
message | pub struct AnimalSpawned { pub operation: Entity, pub animal: Entity } |
Emitted once only after the complete ordinary animal bundle, WorldMember, and fresh PersistentId exist. |
AnimalSpawnRejected |
message | pub struct AnimalSpawnRejected { pub operation: Entity, pub reason: AnimalSpawnFailure } |
Typed failure with no partial animal. |
AnimalSpawnFailure |
enum | MissingSpecies, InvalidVariant, InvalidSex, InvalidHabitat, MarinePlacement(MarinePlacementFailure), PersistentId(PersistentIdError) |
Closed engine-side creation rejection vocabulary. |
MateAnimals |
message | pub struct MateAnimals { pub female: Entity, pub male: Entity } |
G13 behavior outcome; conception eligibility is revalidated here. |
AnimalBorn |
message | pub struct AnimalBorn { pub child: Entity, pub mother: Entity, pub father: Entity } |
Emitted after child is fully spawned/linked. |
ReleaseAnimal |
message | pub struct ReleaseAnimal(pub Entity); |
Requests authentic eligibility/economy/progression handling. |
AnimalReleased |
message | pub struct AnimalReleased { pub animal: PersistentId, pub species: AssetId } |
Stable completion fact after cleanup. |
A03 owns Sex, LifeStage, SpeciesCatalogAsset, variant probabilities,
stage_start_ticks, breeding compatibility, gestation_ticks and litter ranges.
Exact adoption inventory/cost requires an A14/A03 evidence-backed extension;
G11 must not invent it. G18 owns Money; G09 membership is reused.
4. ECS ownership
Each component is one animal fact. There is no animal collection resource;
lineage is queried from Parents, not mirrored child vectors.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
request_adoptions |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<AdoptAnimal>, Res<ZooSeed>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<&WorldMember,With<Habitat>>, Query<(&Tank,&TankGeometry,&TankCapacity)>, MessageWriter<MarinePlacementRejected>, MessageWriter<TransactionRequest>, MessageWriter<SpawnAnimalRequest> |
creates one operation carrying the habitat's WorldMember plus resolved PendingAdoption; positive cost emits kind=Purchase, operation=entity, zero cost immediately requests the same natural spawn path |
validates ordinary habitat facts and, for marine species, G25 tank/depth/space/water before payment; no G25 interception |
authorize_paid_adoptions |
FixedUpdate/FixedGameSet::Act |
MessageReader<TransactionCompleted>, Query<&PendingAdoption>, MessageWriter<SpawnAnimalRequest> |
Requests one correlated natural spawn while retaining pending adoption | Matching operation/purchase only. |
spawn_requested_animals |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<SpawnAnimalRequest>, Res<ZooSeed>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<&WorldMember,With<Habitat>>, Query<(&Tank,&TankGeometry,&TankCapacity)>, MessageWriter<AnimalSpawned>, MessageWriter<AnimalSpawnRejected> |
Validates first, then creates exactly one ordinary animal bundle with allocator-issued ID, habitat root, G09 facts and RNG, or no entity | Single authoritative live-animal creation boundary; no payment/adoption semantics. |
complete_adoptions |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<AnimalSpawned>, Query<(Entity,&PendingAdoption)>, MessageWriter<AnimalAdopted> |
Removes matching operation/pending and acknowledges adoption | spawned.operation exact match only. |
reject_adoptions |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<TransactionRejected>, Query<&PendingAdoption> |
removes operation/pending; no animal/world mutation | matching operation only; typed failure to UI |
reject_adoption_spawns |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<AnimalSpawnRejected>, Query<(Entity,&PendingAdoption)>, MessageWriter<TransactionRequest> |
Removes pending operation and issues an exact refund only when payment had completed | Exact operation match; no double refund and no animal. |
advance_animal_age |
FixedUpdate/FixedGameSet::Clock |
Res<ZooClock>, Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&Age,&mut AnimalLifeStage),With<Animal>> |
compares tick-born_tick to A03 stage starts |
after G32 clock advance |
begin_pregnancies |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<MateAnimals>, Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&AnimalSex,&AnimalLifeStage,Option<&Pregnancy>),With<Animal>> |
female pregnancy | compatible eligible pair only |
mark_due_births |
FixedUpdate/FixedGameSet::Clock |
Commands, Res<ZooClock>, Query<(Entity,&Pregnancy),Without<BirthDue>> |
inserts one due marker | clock.tick >= due_tick |
deliver_litters |
FixedUpdate/FixedGameSet::Act |
Commands, Res<ZooClock>, ResMut<PersistentIdAllocator>, Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&AnimalVariant,&Pregnancy,&mut ReproductionRng,&Transform,&HabitatMember,&WorldMember),With<BirthDue>>, MessageWriter<AnimalBorn> |
allocator-issued PersistentId child bundles with mother's world root and born_tick=clock.tick; removes pregnancy/due only after successful complete spawn |
A03 rules using domain RNG only; no domain-local ID counter |
release_animals |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<ReleaseAnimal>, Query<(&PersistentId,&SpeciesHandle,&AnimalLifeStage,Option<&Pregnancy>),With<Animal>>, MessageWriter<AnimalReleased> |
cleanup/despawn/completion | recovered eligibility only |
clear_deleted_parents |
FixedUpdate/FixedGameSet::Cleanup |
RemovedComponents<Animal>, Query<&mut Parents> |
only matching parent fields | removal events only |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect species entities, young/adult variants, adoption and AI reproduction
data; oracle/TSVs for animal, adoption, lifecycle, family, pregnancy and birth;
commit d5657e5a domain/simulation/animal.rs and animal lifecycle systems only
for evidence. Observe adoption inventory, sex/variant choice, ageing morph,
mating gates, gestation, litter placement, naming, release and family UI.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
G32/A03 own tick conversion. Questions: stage thresholds; conception probability/ cooldown; relatedness; litter distribution; variant inheritance; parent links beyond parents; adoption stock refresh; release restrictions/rewards. All are asset/oracle questions, not constants to invent.
7. Required behavior
Adoption validates availability, habitat and funds atomically. Age crosses each stage once and swaps only variant/presentation handles required by A03. Mating cannot self-pair, violate sex/stage/species/family/cooldown rules or overwrite a pregnancy. Birth uses stored seed, creates all children atomically where required, keeps stable parent links and emits one event each. Release cleans habitat, behavior, navigation and selection relationships through owner events.
8. Performance contract
Clock systems are linear in live/pregnant animals, fixed-step and allocation- free. Spawn/birth/release may allocate ECS entities only on transition. No per-frame species cloning, name strings, family vectors or adoption catalogue copy. Counters cover settled ageing and non-due pregnancy; A03 mapped faults and animal model GPU bytes are separately tracked.
9. Acceptance criteria
Deliver exports and deterministic tests for stage crossing, invalid pairing, gestation/litter/inheritance, parent cleanup and release eligibility using recovered fixtures. No adoption manager, animal bag, guessed rates, placeholder birth or duplicated catalogue. Root bulk-tests economy, UI, save and behavior.
10. Required handoff
Report API/files, registrations/order, A03/A14/G04/G09/G13/G18/G22/G23/G32 symbols, evidence gaps, lineage seams and allocation/mapping/GPU/lifecycle validation.
G12 — Animal needs, welfare and suitability
1. Identity and outcome
Each animal has small explicit need components that decay/recover by species rules; habitat suitability and overall welfare are derived by narrow systems and drive authentic statuses and behavior. Integration wave 3. Prerequisites: G09 and G11 plus A03 species data.
2. Exclusive ownership
The package worker owns game/src/plugins/animal_welfare/ only. Root owns registration, scheduling
and information/health integration.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
AnimalWelfarePlugin |
Plugin |
pub struct AnimalWelfarePlugin; |
Registers explicit need and derived-fact systems. |
Hunger |
component | pub struct Hunger(pub i32); |
A03 satisfaction in signed Q16 permille, clamped to 0..=(1000 << 16); preserves fractional per-tick decay. |
Thirst |
component | pub struct Thirst(pub i32); |
Thirst satisfaction in the same Q16 permille representation. |
RestNeed |
component | pub struct RestNeed(pub i32); |
Exact A03 NeedKind::Rest Q16 live value. |
PrivacyNeed |
component | pub struct PrivacyNeed(pub i32); |
Exact A03 privacy Q16 live value. |
SocialNeed |
component | pub struct SocialNeed(pub i32); |
Exact A03 social Q16 live value. |
ExerciseNeed |
component | pub struct ExerciseNeed(pub i32); |
Exact A03 exercise Q16 live value. |
StimulationNeed |
component | pub struct StimulationNeed(pub i32); |
Exact A03 stimulation Q16 live value. |
EnvironmentNeed |
component | pub struct EnvironmentNeed(pub i32); |
Exact A03 environment Q16 live value. |
KeeperCareNeed |
component | pub struct KeeperCareNeed(pub i32); |
Exact A03 keeper-care Q16 live value. |
HealthNeed |
component | pub struct HealthNeed(pub i32); |
A03 welfare-health Q16 value; distinct from G16 disease/physical health state. |
HabitatSuitability |
component | pub struct HabitatSuitability { pub space: u16, pub biome: u16, pub foliage: u16, pub shelter: u16, pub privacy: u16, pub overall: u16 } |
Each derived permille 0..=1000. |
AnimalWelfare |
component | pub struct AnimalWelfare(pub u16); |
Recovered aggregate permille; not a need table. |
WelfareBand |
component enum | Critical \| Poor \| Fair \| Good \| Excellent |
Thresholds come from native definitions/localization semantics. |
AdjustHunger |
message | pub struct AdjustHunger { pub animal: Entity, pub delta_q16: i32 } |
Focused Q16 permille adjustment. |
AdjustThirst |
message | pub struct AdjustThirst { pub animal: Entity, pub delta_q16: i32 } |
Focused thirst adjustment. |
AdjustRest |
message | pub struct AdjustRest { pub animal: Entity, pub delta_q16: i32 } |
Focused rest adjustment. |
AdjustPrivacy |
message | pub struct AdjustPrivacy { pub animal: Entity, pub delta_q16: i32 } |
Focused privacy adjustment. |
AdjustSocial |
message | pub struct AdjustSocial { pub animal: Entity, pub delta_q16: i32 } |
Focused social adjustment. |
AdjustExercise |
message | pub struct AdjustExercise { pub animal: Entity, pub delta_q16: i32 } |
Focused exercise adjustment. |
AdjustStimulation |
message | pub struct AdjustStimulation { pub animal: Entity, pub delta_q16: i32 } |
Focused stimulation adjustment. |
AdjustEnvironment |
message | pub struct AdjustEnvironment { pub animal: Entity, pub delta_q16: i32 } |
Focused environment adjustment. |
AdjustKeeperCare |
message | pub struct AdjustKeeperCare { pub animal: Entity, pub delta_q16: i32 } |
Focused keeper-care adjustment. |
AdjustHealthNeed |
message | pub struct AdjustHealthNeed { pub animal: Entity, pub delta_q16: i32 } |
Focused welfare-health adjustment. |
NeedChanged |
message | pub struct NeedChanged { pub animal: Entity, pub need: NeedKind, pub value: u16 } |
Uses A03's exact vocabulary/value units; threshold crossings only. |
WelfareChanged |
message | pub struct WelfareChanged { pub animal: Entity, pub old: WelfareBand, pub new: WelfareBand } |
Material band transition. |
A03 owns NeedKind, SpeciesNeedRecord and exact signed Q16 permille/tick units; A10
references that same type. G09 supplies habitat facts.
4. ECS ownership
Every need is independently queryable. Suitability/welfare are derived components retained only because multiple systems/UI consume them.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_animal_needs |
FixedUpdate/FixedGameSet::Cleanup |
Commands, Res<Assets<SpeciesCatalogAsset>>, Query<(Entity,&SpeciesHandle),(Added<Animal>,Without<AnimalWelfare>)> |
ten independent Q16 need components plus initial suitability, welfare and band | Fresh G11 animals only; G22-restored animals already carry AnimalWelfare and are untouched. |
decay_hunger |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut Hunger),With<Animal>> |
adds A03 decay_per_tick_q16 with checked saturating clamp |
once per running G32 simulation tick, after advance_zoo_clock |
decay_thirst |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut Thirst),With<Animal>> |
thirst Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_rest |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut RestNeed),With<Animal>> |
rest Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_privacy |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut PrivacyNeed),With<Animal>> |
privacy Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_social |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut SocialNeed),With<Animal>> |
social Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_exercise |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut ExerciseNeed),With<Animal>> |
exercise Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_stimulation |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut StimulationNeed),With<Animal>> |
stimulation Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_environment |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut EnvironmentNeed),With<Animal>> |
environment Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_keeper_care |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut KeeperCareNeed),With<Animal>> |
keeper-care Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
decay_health_need |
FixedUpdate/FixedGameSet::Clock |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&mut HealthNeed),With<Animal>> |
welfare-health Q16 decay | once per running G32 simulation tick, after advance_zoo_clock |
adjust_hunger |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustHunger>, Query<&mut Hunger,With<Animal>> |
hunger only | matching target |
adjust_thirst |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustThirst>, Query<&mut Thirst,With<Animal>> |
thirst only | matching target |
adjust_rest |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustRest>, Query<&mut RestNeed,With<Animal>> |
rest only | matching target |
adjust_privacy |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustPrivacy>, Query<&mut PrivacyNeed,With<Animal>> |
privacy only | matching target |
adjust_social |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustSocial>, Query<&mut SocialNeed,With<Animal>> |
social only | matching target |
adjust_exercise |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustExercise>, Query<&mut ExerciseNeed,With<Animal>> |
exercise only | matching target |
adjust_stimulation |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustStimulation>, Query<&mut StimulationNeed,With<Animal>> |
stimulation only | matching target |
adjust_environment |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustEnvironment>, Query<&mut EnvironmentNeed,With<Animal>> |
environment only | matching target |
adjust_keeper_care |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustKeeperCare>, Query<&mut KeeperCareNeed,With<Animal>> |
keeper care only | matching target |
adjust_health_need |
FixedUpdate/FixedGameSet::Act |
MessageReader<AdjustHealthNeed>, Query<&mut HealthNeed,With<Animal>> |
health need only | matching target |
assess_habitat_suitability |
FixedUpdate/FixedGameSet::Think |
Res<Assets<SpeciesCatalogAsset>>, MessageReader<HabitatChanged>, Query<(&SpeciesHandle,&HabitatMember,&mut HabitatSuitability),With<Animal>>, Query<&HabitatSummary> |
affected members' suitability | habitat/member change only |
derive_animal_welfare |
FixedUpdate/FixedGameSet::Think |
Res<Assets<SpeciesCatalogAsset>>, Query<(&SpeciesHandle,&Hunger,&Thirst,&RestNeed,&PrivacyNeed,&SocialNeed,&ExerciseNeed,&StimulationNeed,&EnvironmentNeed,&KeeperCareNeed,&HealthNeed,&HabitatSuitability,&mut AnimalWelfare,&mut WelfareBand),With<Animal>> |
aggregate/band/messages | changed facts only; this one formula genuinely consumes every A03 need |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect species need definitions, needAdjusts, biome/habitat preferences and
status localization; oracle/TSVs for animal needs/happy factors/habitat score;
commit d5657e5a animal need and habitat assessment code only as evidence. Live
observation settles displayed scale, arrows, thresholds and update cadence.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: whether source values are satisfaction or deficit; exact time scale, decay and clamping; happiness aggregation; space/biome/foliage/shelter/privacy formulas; species/group effects and critical statuses. Recover each formula.
7. Required behavior
New animals initialize from A03, not universal 100s. A03
decay_per_tick_q16 is applied once per running G32 simulation tick directly to
the live Q16 component, with integer saturating clamp and no private time
conversion, float residual or lost fractional decay. Each need changes only by
its own rate/effects and clamps exactly. Suitability reacts to membership/
habitat changes and uses authored species preferences and measured G09 facts.
Welfare aggregation and bands match evidence. Threshold messages are edge-
triggered. Disease/health belongs to G16; food transactions to G15.
8. Performance contract
All steady systems allocate zero. Decay/derivation are O(relevant animals) on
fixed ticks with change/event filters; suitability is O(animals in affected habitats) and must not scan every habitat for each animal. No need maps,
strings, per-frame asset clones or vectors. Count representative settled need
and welfare passes; A03 pages separate; no GPU ownership.
9. Acceptance criteria
Deliver all exports and tests using recovered fixtures for decay, clamp,
adjustment, suitability, aggregate and single threshold emission. No generic
Needs component, guessed percentages, animal update dispatcher or no-op score.
Root validates status UI, feeding/behavior and habitat edits.
10. Required handoff
Report API/files, exact A03 fields/formulas, registrations/order, G09/G11/G13/ G15/G16/G21/G32 consumers, evidence gaps and allocation/mapping/gameplay validation.
G13 — Data-authored animal task selection
1. Identity and outcome
Compiled A10 rules score animal tasks through focused ECS systems, select one deterministically, and expose a changed typed task for the domain that executes it. This is not a behavior VM or animal update dispatcher. Integration wave 3. Prerequisites: A03, A10, G09, G11, G12, G14, G15 and G32.
2. Exclusive ownership
The package worker owns game/src/plugins/animal_behavior/ only. Root owns registrations, ordering
and command-executor integration.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
AnimalBehaviorPlugin |
Plugin |
pub struct AnimalBehaviorPlugin; |
Registers hydration, focused scorers, selection and completion. |
BehaviorHandle |
component | pub struct BehaviorHandle { pub asset: Handle<BehaviorProgramAsset>, pub set: AssetId } |
Sole mapped A10 archive/set reference. |
TaskCandidate |
value | #[repr(C)] pub struct TaskCandidate { pub program: AssetId, pub first_action: u32, pub score: i32, pub target: Option<Entity> } |
Inline copyable fixed-point candidate; no heap payload. |
HungerCandidate |
component | pub struct HungerCandidate(pub Option<TaskCandidate>); |
Written only by hunger scorer. |
ThirstCandidate |
component | pub struct ThirstCandidate(pub Option<TaskCandidate>); |
Written only by thirst scorer. |
RestCandidate |
component | pub struct RestCandidate(pub Option<TaskCandidate>); |
Written only by rest scorer. |
WelfareCandidate |
component | pub struct WelfareCandidate(pub Option<TaskCandidate>); |
Written only by welfare scorer. |
MatingCandidate |
component | pub struct MatingCandidate(pub Option<TaskCandidate>); |
Written only by the reproduction-eligibility scorer using G11 lifecycle facts. |
ActiveTask |
component | pub struct ActiveTask { pub program: AssetId, pub action: u32, pub target: Option<Entity>, pub phase: TaskPhase } |
Program/action indexes mapped A10 records. |
TaskPhase |
enum | Starting \| Running |
Executors change Starting to their own focused execution component then report completion. |
BehaviorRng |
component | pub struct BehaviorRng(pub DeterministicRng); |
Domain-local G32 stream derived from zoo seed and persistent animal ID. |
BehaviorThinkSchedule |
component | pub struct BehaviorThinkSchedule { pub next_tick: u64 } |
Per-agent integer cadence derived from A10 BehaviorSet::think_interval_ticks; no render-frame thinking. |
TaskCompleted |
message | pub struct TaskCompleted { pub animal: Entity, pub program: AssetId, pub outcome: TaskOutcome } |
Completion for the current program only. |
TaskOutcome |
enum | Succeeded \| Failed \| Interrupted |
Fixed lifecycle result; scoring/cooldown effects come from A10. |
CancelTask |
message | pub struct CancelTask { pub animal: Entity, pub reason: TaskCancelReason } |
Typed exceptional cancellation. |
TaskCancelReason |
enum | TargetGone \| Unreachable \| NeedSatisfied \| HabitatChanged \| LifecycleChanged |
No strings or opaque codes. |
A10 owns BehaviorProgramAsset, exact BehaviorSet, BehaviorAction,
BehaviorInput and A03 NeedKind references. BehaviorHandle::set resolves by
MappedBehaviorArchive::set, and scorers traverse only
MappedBehaviorArchive::set_programs plus direct program lookup. No
per-entity program list or unresolved root extension remains. Executors inspect
only the current mapped action on Changed<ActiveTask> and attach focused components.
4. ECS ownership
An animal carries an archive/set reference, inline disjoint candidate components, active task and RNG. Candidate writers touch different component types and can run in parallel. No manager/vector/blackboard exists.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
hydrate_behavior_agents |
FixedUpdate/FixedGameSet::Cleanup |
Commands, Res<ZooSeed>, Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&PersistentId,&BehaviorHandle),Without<BehaviorRng>> |
inline candidates, RNG and BehaviorThinkSchedule |
once per agent after exact A10 set validation |
score_hunger_tasks |
FixedUpdate/FixedGameSet::Think |
Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&Hunger,&mut HungerCandidate),With<Animal>> |
hunger candidate from exact mapped set programs | clock.tick >= next_tick |
score_thirst_tasks |
FixedUpdate/FixedGameSet::Think |
Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&Thirst,&mut ThirstCandidate),With<Animal>> |
thirst candidate from exact mapped set programs | due only; parallel with hunger |
score_rest_tasks |
FixedUpdate/FixedGameSet::Think |
Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&RestNeed,&mut RestCandidate),With<Animal>> |
rest candidate from exact mapped set programs | due only; parallel with hunger and thirst |
score_welfare_tasks |
FixedUpdate/FixedGameSet::Think |
Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(&BehaviorHandle,&BehaviorThinkSchedule,&AnimalWelfare,&HabitatSuitability,&mut WelfareCandidate),With<Animal>> |
welfare candidate from exact mapped set programs | due only; parallel with the other scorers |
score_mating_tasks |
FixedUpdate/FixedGameSet::Think |
Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Res<SpatialGrid>, Query<(Entity,&BehaviorHandle,&BehaviorThinkSchedule,&SpeciesHandle,&AnimalSex,&AnimalLifeStage,Option<&Pregnancy>,&HabitatMember,&GlobalTransform,&mut MatingCandidate),With<Animal>> |
mating candidate with an exact eligible nearby mate target | Due only; uses A03/A10 eligibility and bounded G14 neighbours, never all-pairs. |
select_animal_tasks |
FixedUpdate/FixedGameSet::Think |
Commands, Res<ZooClock>, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&BehaviorHandle,&HungerCandidate,&ThirstCandidate,&RestCandidate,&WelfareCandidate,&MatingCandidate,Option<&FoodCandidate>,Option<&DrinkCandidate>,&mut BehaviorRng,&mut BehaviorThinkSchedule,Option<&ActiveTask>),With<Animal>> |
active task and next_tick = clock.tick + set.think_interval_ticks |
due after all scorers; stable reduction across the complete frozen candidate tuple; A10 idle program if none wins |
finish_animal_tasks |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<TaskCompleted>, Query<&ActiveTask,With<Animal>> |
removes matching active task/applies A10 cooldown state | exact task match only |
cancel_invalid_tasks |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<CancelTask>, Query<&ActiveTask,With<Animal>> |
removes matching task | owner-requested typed reason |
The selector tuple above is complete for the recovered animal task families. Any newly evidenced family requires a reviewed schema/brief revision before implementation; runtime registration or a shared mutable score bag is forbidden. Pure generic score helpers may be zero-cost monomorphs.
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect AI task/behavior-set/entity files; oracle/TSVs for AI thinker,
evaluators, selectors, qualifiers, behavior sets and completion; commit
d5657e5a domain AI and simulation/behavior.rs only to mine fixed command and
formula evidence. Observe task cadence, priorities, interruption, target choice,
cooldowns, weighted ties and idle behavior.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: exact score composition/clamps; selector order; deterministic/random ties; think cadence; current-task inertia; interrupt and failure cooldowns; each fixed command's owner. Resolve/transcode into command-specific A10 tables.
7. Required behavior
All authored supported tasks participate. Disabled qualifiers cannot win;
scores and targets correspond to the same task slot. Selection is deterministic
under the G32 seed except evidenced random choices. An active task persists
until authentic interrupt/completion; stale completions do nothing. Domain
packages attach focused execution components on Changed<ActiveTask> and never
call a G13 dispatcher. Unsupported source commands fail preflight.
8. Performance contract
Hydration inserts inline candidates and RNG state with no candidate heap. Think passes allocate zero and are
O(agents * relevant rule slots) at G32 cadence, not necessarily every render
frame. No maps, strings, task trees, boxes or candidate vectors are built at runtime;
A10 traversal is mapped. Count each scorer/selection and settled non-think
frame; mapped pages and no GPU bytes are tracked separately.
9. Acceptance criteria
Deliver exports and tests for inline candidate reuse, focused scorer isolation,
deterministic tie, interrupt policy, stale completion and zero allocation after
hydration. No VM, opcode loop, blackboard bag, UpdateAnimalsRuntime, giant
query or placeholder success. Root integrates at least feed, drink, rest,
locomotion and mating executors in bulk.
10. Required handoff
Report API/files, exact A10 tables/commands, registrations/order, G11/G12/G14– G16/G32 consumers, evidence gaps, scorer seams and allocation/mapping/behavior validation.
G14 — Navigation, steering and docking
1. Identity and outcome
Animals, guests, staff and vehicles request destinations, plan through native and live topology, steer without collisions, dock at precise affordances, and emit arrival/failure facts. Integration wave 3. Prerequisites: A17, G04, G08, G09 and G32 fixed scheduling.
2. Exclusive ownership
The package worker owns game/src/plugins/locomotion/ only. Root owns registrations, schedule and
navigation-asset integration.
This package directly traverses A17's mapped graph with bounded reusable A*,
funnel/route, spatial-grid and steering scratch. bevy_landmass is rejected
because its Archipelago/Island/Agent state would duplicate the native graph and
live ECS movement facts; oxidized_navigation is forbidden at runtime. Do not
hide either library behind an adapter or reproduce a navigation manager.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
LocomotionPlugin |
Plugin |
pub struct LocomotionPlugin; |
Registers spatial indexing, route planning, steering, motion and docking. |
NavAgent |
component | pub struct NavAgent { pub radius_m: f32, pub max_speed_mps: f32, pub acceleration_mps2: f32, pub capabilities: NavFlags } |
Finite positive parameters; uses A17's exact eligibility flags. |
Velocity |
component | pub struct Velocity(pub Vec3); |
World metres/second; Y follows mode-specific terrain/water rules. |
Destination |
component | pub struct Destination { pub world: Vec3, pub arrival_radius_m: f32 } |
Current requested point; changed value triggers planning. |
Route |
component | pub struct Route { pub points: Vec<Vec3>, pub cursor: usize } |
Reusable pre-reserved path; never reallocated below validated route capacity. |
Steering |
component | pub struct Steering { pub desired_velocity: Vec3 } |
Derived current-step fact. |
SpatialCell |
component | pub struct SpatialCell(pub u32); |
Current G14 grid bucket index. |
SpatialGrid |
resource | pub struct SpatialGrid { pub origin: Vec2, pub cell_size_m: f32, pub width: u32, pub height: u32, pub cell_offsets: Box<[u32]>, pub write_cursors: Box<[u32]>, pub entities: Vec<Entity> } |
Canonical CSR: offsets length is cells+1, range is offsets[i]..offsets[i+1]; cursors are reusable rebuild scratch and flat storage is pre-reserved. |
Docking |
component | pub struct Docking { pub target: Entity, pub point: Vec3, pub forward: Vec3, pub radius_m: f32 } |
Precise target affordance; removed on dock/failure. |
NavigateTo |
message | pub struct NavigateTo { pub entity: Entity, pub destination: Vec3, pub arrival_radius_m: f32 } |
Typed destination request. |
DockAt |
message | pub struct DockAt { pub entity: Entity, pub target: Entity, pub local_point: Vec3, pub local_forward: Vec3, pub radius_m: f32 } |
Target-relative docking request. |
Arrived |
message | pub struct Arrived { pub entity: Entity, pub target: Option<Entity> } |
Emitted once when arrival/docking completes. |
NavigationFailed |
message | pub struct NavigationFailed { pub entity: Entity, pub reason: NavigationFailure } |
Explicit failure. |
NavigationFailure |
enum | OutsideWorld \| NoStartNode \| NoRoute \| TargetGone \| ContainmentBlocked |
Fixed evidence-backed reasons. |
A17 owns NavigationAsset, NavFlags, TraversalKind, direct graph/tile access and path
capacity bounds. G08 supplies live links/gate state; G09 containment. Route
scratch is per agent, never one locked global manager.
4. ECS ownership
Motion facts live on each actual entity. SpatialGrid is a bounded derived
accelerator reconstructed from transforms; route vectors retain capacity.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_spatial_grid |
OnEnter(GamePhase::InGame) |
Commands, Query<&WorldBounds>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&GlobalTransform,&NavAgent)> |
CSR arrays, flat capacity and SpatialCell |
once after G04; capacity from native world policy |
initialize_nav_agents |
FixedUpdate/FixedGameSet::Think |
Commands, Res<Assets<NavigationAsset>>, Query<(Entity,&NavAgent),(Added<NavAgent>,Without<Route>)> |
Inserts zero Velocity/Steering, pre-reserved Route, and initial SpatialCell |
Fresh live agents only; G22-restored agents already carry Route and are untouched. |
accept_navigation_requests |
FixedUpdate/FixedGameSet::Think |
Commands, MessageReader<NavigateTo>, Query<(),With<NavAgent>> |
destination/route dirty | valid finite target |
accept_docking_requests |
FixedUpdate/FixedGameSet::Think |
Commands, MessageReader<DockAt>, Query<(),With<NavAgent>>, Query<&GlobalTransform> |
docking plus destination | valid target |
plan_changed_routes |
FixedUpdate/FixedGameSet::Navigate |
Res<Assets<NavigationAsset>>, Res<TopologyIndex>, Query<(&NavAgent,&GlobalTransform,&Destination,&mut Route),Changed<Destination>>, MessageWriter<NavigationFailed> |
existing route buffer/cursor | changed requests only; containment/gates considered |
compute_local_steering |
FixedUpdate/FixedGameSet::Navigate |
Res<SpatialGrid>, Query<(Entity,&NavAgent,&GlobalTransform,&Route,&mut Steering)> |
desired velocity | active routes; bounded neighboring CSR ranges |
integrate_motion |
FixedUpdate/FixedGameSet::Act |
Res<Time<Fixed>>, Res<TerrainIndex>, Res<Assets<TerrainAsset>>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(Entity,&NavAgent,&mut Transform,&mut Velocity,&Steering,&mut Route)> |
transform/velocity/cursor | fixed step; indexed terrain only |
rebuild_spatial_grid |
FixedUpdate/FixedGameSet::Cleanup |
ResMut<SpatialGrid>, Query<(Entity,&GlobalTransform,&mut SpatialCell),With<NavAgent>> |
counts into offsets, prefix sum, copies offsets to cursors, counting-sorts flat entities | after motion; allocation-free |
complete_docking |
FixedUpdate/FixedGameSet::Cleanup |
Commands, Query<(Entity,&GlobalTransform,&Docking)>, Query<&GlobalTransform>, MessageWriter<Arrived>, MessageWriter<NavigationFailed> |
snaps/faces/removes movement facts | within recovered dock tolerance |
Time<Fixed> supplies only physical seconds for integration; G32 controls
whether fixed simulation runs and its deterministic ordering.
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect navigation/path/fence/entity locomotion data; oracle/TSVs for locomotion,
steering, path route, docking and traversability; commit d5657e5a pathing,
domain AI locomotion and behavior docking code only for evidence. Observe gait
speeds, turning, obstacle separation, path preference, gates, habitat escape,
swimming/flying and docking.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: native nav cell/graph convention; route cost/heuristic/tie order; traversal classes; replanning; steering weights; max neighbors; slope/water; dock positions/tolerances and animation handoff. Recover from A17/oracle.
7. Required behavior
Requests mutate only the target agent. Routes honor closed gates, topology, containment and traversal class, with deterministic ties. Movement accelerates, steers and terrain-fits at fixed physical time; arrival emits once. Deleted targets fail and release reservations. Dynamic topology invalidates only affected routes. No straight-line fallback may cross invalid space.
8. Performance contract
Steady motion/steering allocates zero. Route planning occurs only on changed/ invalidated destinations and writes reusable capacity; complexity is bounded by A17 affected graph. Local steering visits fixed neighboring CSR ranges, not all agents. Flat capacity is established from native policy during loading; overflow is explicit outside settled measurement and recurring growth fails regression. Track route/grid heap, A17/terrain faults and no G14 GPU bytes.
9. Acceptance criteria
Deliver exports and tests for deterministic routing, gate/containment rejection, changed-only planning, bounded neighbor visits, exact arrival/docking and zero- allocation steady movement. No nav manager, per-agent world scan, direct-line fake route or runtime navmesh generation. Root validates animals/staff/guests.
10. Required handoff
Report API/files, A17/G04/G05/G08/G09/G32 contracts, registrations/order, physics feature needs, evidence gaps, buffer/index seams and allocator/mapping/ navigation validation.
G15 — Food, drink and refill transactions
1. Identity and outcome
Hungry/thirsty animals select compatible authored food or water, reserve and navigate to its container, consume exact portions, satisfy the appropriate need, and create keeper refill work when required. Integration wave 3. Prerequisites: A03, A14 and G12–G14.
2. Exclusive ownership
The package worker owns game/src/plugins/feeding/ only. Root owns declarations, schedules and
staff/economy integration.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
FeedingPlugin |
Plugin |
pub struct FeedingPlugin; |
Registers focused scoring, task materialization, consumption and refill. |
FoodContainer |
component | pub struct FoodContainer { pub food: AssetId, pub amount_milli: u32, pub capacity_milli: u32 } |
Amount/capacity in definition-specific milli-portions; amount never exceeds capacity. |
DrinkContainer |
component | pub struct DrinkContainer { pub drink: AssetId, pub amount_milli: u32, pub capacity_milli: u32 } |
Amount/capacity in definition-specific milli-portions; amount never exceeds capacity. |
FoodCandidate |
component | pub struct FoodCandidate(pub Option<TaskCandidate>); |
Inline disjoint G15 candidate read by G13 selection; no shared score bag. |
DrinkCandidate |
component | pub struct DrinkCandidate(pub Option<TaskCandidate>); |
Inline drink candidate. |
FeedingTask |
component | pub struct FeedingTask { pub source: Entity, pub program: AssetId } |
Focused execution for mapped A10 food target/move/consume actions. |
DrinkingTask |
component | pub struct DrinkingTask { pub source: Entity, pub program: AssetId } |
Focused drink execution. |
ConsumptionReservation |
component | pub struct ConsumptionReservation { pub consumer: Entity } |
On food/drink source while exclusively reserved where definition requires it. |
RefillPending |
component | pub struct RefillPending; |
Prevents duplicate keeper requests. |
RefillRequested |
message | pub struct RefillRequested { pub container: Entity, pub kind: RefillKind } |
Edge-triggered empty/threshold work fact. |
RefillKind |
enum | Food(AssetId) \| Drink(AssetId) |
Fixed typed refill content. |
RefillContainer |
message | pub struct RefillContainer { pub staff: Entity, pub container: Entity, pub amount_milli: u32 } |
G20 completion request; compatibility/capacity revalidated. |
ContainerRefilled |
message | pub struct ContainerRefilled { pub staff: Entity, pub container: Entity, pub accepted_milli: u32 } |
Exact accepted quantity. |
EndFeedingTask |
message | pub struct EndFeedingTask { pub consumer: Entity, pub outcome: TaskOutcome } |
Explicit pre-removal cleanup request; lifecycle/behavior owners must send before despawn/task removal. |
is_food_compatible |
function | pub fn is_food_compatible(species: &ArchivedSpeciesRecord, object: &ArchivedObjectDefinition) -> bool |
Checks A03 hunger preferences plus A14 ObjectKind::Food/EAT. |
is_drink_compatible |
function | pub fn is_drink_compatible(species: &ArchivedSpeciesRecord, object: &ArchivedObjectDefinition) -> bool |
Checks thirst preferences plus A14 water/DRINK. |
A03 owns diet/feeding vocabulary and need kinds; A14 owns food/drink/container
object definitions/affordances; exact portion, capacity and refill fields are an
A14 root contract extension required before implementation. A10 owns the action
sequence ChooseTarget, MoveToTarget, Consume { need: NeedKind }.
4. ECS ownership
Containers own only live amount/reservation/refill status. Animal execution uses short-lived focused components, not an internal feeding state machine.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
score_food_tasks |
FixedUpdate/FixedGameSet::Think |
Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Res<SpatialGrid>, Query<(&SpeciesHandle,&BehaviorHandle,&Hunger,&HabitatMember,&mut FoodCandidate),With<Animal>>, Query<(Entity,&FoodContainer,&GlobalTransform,Option<&ConsumptionReservation>)> |
food candidate only | bounded CSR neighbors; parallel with other scorers |
score_drink_tasks |
FixedUpdate/FixedGameSet::Think |
Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Res<SpatialGrid>, Query<(&SpeciesHandle,&BehaviorHandle,&Thirst,&HabitatMember,&mut DrinkCandidate),With<Animal>>, Query<(Entity,&DrinkContainer,&GlobalTransform,Option<&ConsumptionReservation>)> |
drink candidate only | bounded CSR neighbors; parallel |
begin_feeding_tasks |
FixedUpdate/FixedGameSet::Act |
Commands, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&ActiveTask),Changed<ActiveTask>>, Query<&mut FoodContainer> |
task/reservation/NavigateTo |
mapped current action targets food |
begin_drinking_tasks |
FixedUpdate/FixedGameSet::Act |
Commands, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&ActiveTask),Changed<ActiveTask>>, Query<&mut DrinkContainer> |
task/reservation/navigation | mapped current action targets water |
consume_food |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<Arrived>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Query<(&SpeciesHandle,&mut Hunger,&FeedingTask),With<Animal>>, Query<(&mut FoodContainer,Option<&ConsumptionReservation>)>, MessageWriter<TaskCompleted>, MessageWriter<RefillRequested> |
exact portion/need/task/refill | mapped Consume { need: NeedKind::Hunger } at matching source |
consume_drink |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<Arrived>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<BehaviorProgramAsset>>, Query<(&SpeciesHandle,&mut Thirst,&DrinkingTask),With<Animal>>, Query<(&mut DrinkContainer,Option<&ConsumptionReservation>)>, MessageWriter<TaskCompleted>, MessageWriter<RefillRequested> |
drink/need/task/refill | mapped thirst consume at matching source |
refill_food_containers |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<RefillContainer>, Query<(&mut FoodContainer,Option<&RefillPending>)>, MessageWriter<ContainerRefilled> |
amount/pending | matching compatible food request |
refill_drink_containers |
FixedUpdate/FixedGameSet::Act |
Commands, MessageReader<RefillContainer>, Query<(&mut DrinkContainer,Option<&RefillPending>)>, MessageWriter<ContainerRefilled> |
amount/pending | matching compatible drink request |
end_feeding_tasks |
FixedUpdate/FixedGameSet::Cleanup |
Commands, MessageReader<EndFeedingTask>, Query<(Option<&FeedingTask>,Option<&DrinkingTask>)>, Query<&ConsumptionReservation> |
removes exact source reservation and task component, emits TaskCompleted |
before G13 completion and before lifecycle despawn; never scans sources |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
6. Behavioral evidence and disposition
Inspect animal diet flags, food/water/container entities, need adjusts and
keeper task records; oracle/TSVs for food/feeding/consume/refill/keeper requests;
commit d5657e5a animal care, AI task and staff feeding code only for evidence.
Observe compatibility, portions, duration, depletion, multi-user access,
spoilage/dirtiness, refill thresholds and keeper interaction.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: diet token normalization resolved by A03; need-adjust formula; portion units; exclusive/shared capacity; reservation timeout; source search radius/preference; ground food; water bodies; container dirtiness/spoilage; refill amount/cost. Recover rather than assume a universal feeder.
7. Required behavior
Only compatible nonempty reachable sources score. Reservation and navigation target the same entity. Arrival revalidates task/source/reservation; consumption atomically decrements exact amount and adjusts only hunger or thirst. Empty or threshold crossing emits one refill request. Refill clamps capacity and clears pending only on accepted work. Failure/death/task switch releases reservation. Keeper execution belongs to G20; health contamination effects to G16.
8. Performance contract
Steady execution allocates zero. Scoring uses G14 CSR ranges and bounded
nearby candidates, never scans every container per animal. Compatibility is
O(log n) or better direct archived lookup with no strings. Containers/tasks do
not allocate. Count idle/scoring/consume/refill passes; A03/A14 faults separate;
no package-owned GPU bytes.
9. Acceptance criteria
Deliver exports and recovered-fixture tests for compatibility, candidate selection, atomic portion, correct single-need adjustment, duplicate refill suppression, capacity clamp and reservation cleanup. No feeder manager, generic care transaction, all-needs query, guessed food values or fake navigation. Root bulk-tests animals and keeper refills.
10. Required handoff
Report API/files, A03/A10/A14/G12–G14/G20/G32 contracts, registrations/order, evidence gaps, explicit cleanup ordering and allocation/mapping/gameplay validation.
G16 — Animal Health
1. Identity and outcome
This package delivers disease, injury treatment, death, escape, rampage, tranquilization, recapture, and recovery as ordinary facts on animal entities. Players can identify and treat sick animals, respond to escaped or dangerous animals, and observe authentic terminal and recovery outcomes. These behaviors share one natural responsibility because each changes whether an animal is healthy, controllable, alive, and available to normal behavior.
Integration wave: 3. Logical prerequisites: A10 behavior programs, A14 world definitions, G11 animal lifecycle, G12 animal welfare, G13 animal behavior, and G14 locomotion. Prerequisites are read-only and must not be copied or wrapped.
2. Exclusive ownership
The worker exclusively owns reconstruction/game/src/plugins/animal_health/
and may create mod.rs plus private child modules there. Every other path is
read-only. Root owns crate/module declarations, plugin registration, schedule
configuration, dependencies, reflection registration, and alignment of names
requested from earlier packages. Report collisions; do not broaden ownership.
3. Frozen public contract
All scalar health ratios use finite f32 in inclusive 0.0..=1.0; durable
durations are integer G32 simulation ticks. An AssetId names an immutable
native definition.
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
AnimalHealthPlugin |
Plugin |
unit struct; fn build(&self, app: &mut App) |
Registers only this package's types, messages, and systems. |
Vitality |
Component |
pub struct Vitality(pub f32) |
Current physical health; finite and clamped. Death occurs only at zero after authentic rule evaluation. |
Disease |
Component |
pub struct Disease { pub definition: AssetId, pub elapsed_ticks: u64, pub severity_permille: u16, pub hint_level: u8 } |
Severity uses A14's exact 0..=1000 fixed unit; at most one active disease unless evidence establishes stacking. |
DiseaseRng |
Component |
pub struct DiseaseRng(pub DeterministicRng) |
G32 domain-local stream derived from zoo seed and animal PersistentId; sole disease/exposure roll source and persisted by G22. |
Treatment |
Component |
pub struct Treatment { pub definition: AssetId, pub provider: Entity, pub remaining_ticks: u32 } |
Temporary treatment actually being performed; provider must be live staff. |
Tranquilized |
Component |
pub struct Tranquilized { pub remaining_ticks: u32 } |
Suppresses locomotion/behavior while positive. |
Escaped |
Component |
pub struct Escaped { pub since_tick: u64 } |
Animal is outside valid containment; removed immediately after valid recapture. |
Rampaging |
Component |
pub struct Rampaging { pub elapsed_ticks: u64 } |
Dangerous behavior state selected by authored rules, not a random default. |
Dead |
Component |
pub struct Dead { pub cause: DeathCause, pub since_tick: u64 } |
Terminal marker; ordinary animal think/act systems must exclude it. |
DeathCause |
enum | Disease(AssetId), Injury, Age, Welfare |
Only a cause proven by native rules may be generated; A19 rejects an imported cause outside the frozen vocabulary. |
TreatmentRequest |
Message |
pub struct TreatmentRequest { pub animal: Entity, pub treatment: AssetId, pub provider: Entity } |
Real asynchronous request from player/staff interaction. |
TranquilizeRequest |
Message |
pub struct TranquilizeRequest { pub animal: Entity, pub source: Entity } |
Requests an evidence-authorized tranquilizer action. |
AnimalDied |
Message |
pub struct AnimalDied { pub animal: Entity, pub cause: DeathCause } |
Emitted exactly once when Dead is inserted. |
AnimalEscaped |
Message |
pub struct AnimalEscaped { pub animal: Entity } |
Emitted exactly once per transition into Escaped. |
AnimalRecovered |
Message |
pub struct AnimalRecovered { pub animal: Entity, pub disease: Option<AssetId> } |
Emitted after disease/treatment state is actually cleared. |
Root contract requests: align direct names for G11 Animal, G12 welfare facts,
G13 authored behavior selection, G14 navigation/motion facts, G09 containment,
G20 staff role/availability, and the A10/A14 mapped definition handles. Do not
substitute local traits or mirror components when their final names differ.
4. ECS ownership
Each component is one live per-animal fact. There is no disease manager, health registry, animal snapshot, or package world.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
detect_containment_breaches |
FixedUpdate / FixedGameSet::Cleanup |
Res<ZooClock>; animal Query<(Entity,&HabitatMember,&Containment,Option<&Escaped>), (With<Animal>,Without<Dead>)>; MessageWriter<AnimalEscaped>; Commands |
Inserts Escaped only on contained: true -> false; removal remains recapture-owned |
After G09 has updated exact membership/containment facts; never re-derives boundaries. |
evaluate_disease_exposure |
FixedUpdate / FixedGameSet::Think |
MessageReader<ZooDayAdvanced>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&SpeciesHandle,&HabitatMember,&mut DiseaseRng),(With<Animal>,Without<Disease>,Without<Dead>)>, Query<(&WorldEnvironment,&Weather)>, Commands |
Inserts eligible Disease |
Exact A14 interval/event only. |
advance_disease |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut Disease,&mut DiseaseRng,&mut Vitality,Option<&Treatment>),(With<Animal>,Without<Dead>)>, Commands |
Disease ticks/severity/vitality and terminal components | Every simulation tick. |
begin_treatment |
FixedUpdate / FixedGameSet::Act |
MessageReader<TreatmentRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(Option<&Disease>,Option<&Treatment>),(With<Animal>,Without<Dead>)>, Query<(),(With<Staff>,With<AvailableForWork>)>, Commands |
Inserts valid Treatment |
Before treatment advance. |
advance_treatment |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut Treatment,&mut Vitality,Option<&mut Disease>),(With<Animal>,Without<Dead>)>, Query<(&Transform,Option<&AvailableForWork>),With<Staff>>, Commands, MessageWriter<AnimalRecovered> |
Treatment/disease/vitality components | After docking. |
apply_tranquilizer_requests |
FixedUpdate / FixedGameSet::Act |
MessageReader<TranquilizeRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(Option<&Escaped>,Option<&Rampaging>,Option<&Tranquilized>),(With<Animal>,Without<Dead>)>, Commands |
Inserts/refreshes Tranquilized |
Before countdown. |
advance_tranquilization |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>; Query<(Entity, &mut Tranquilized), (With<Animal>, Without<Dead>)>; Commands |
Tick countdown/removal | In game. G13/G14 exclude Tranquilized. |
evaluate_rampage |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&AnimalWelfare,Option<&Disease>,Option<&Rampaging>),(With<Animal>,Or<(Changed<AnimalWelfare>,Changed<Disease>)>)>, Commands |
Inserts/removes Rampaging |
Changed animals only. |
finalize_deaths |
FixedUpdate / FixedGameSet::Cleanup |
Query<(Entity, &Vitality, Option<&Disease>), (With<Animal>, Without<Dead>)>; Commands; MessageWriter<AnimalDied> |
Inserts Dead and emits once |
After all vitality writers. Does not despawn presentation immediately. |
recapture_animals |
FixedUpdate / FixedGameSet::Cleanup |
Query<(Entity,&Containment,&Docking),(With<Animal>,With<Escaped>,With<Tranquilized>)>, Commands, MessageWriter<AnimalRecovered> |
Removes Escaped, Rampaging, Tranquilized |
Contained and docked after G09/G14. |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch. This package must not add filesystem discovery, Z2F, XML, original codecs, source precedence, or conversion dependencies.
6. Behavioral evidence and disposition
Start with shipped disease/task/entity definitions under ignored vendor/
archives; search cpp-reconstruction/ and
docs/analysis/type-coverage-reconciliation.tsv for ZTDiseaseMgr,
ZTDisease, ZTCureDiseaseMode, ZTBehCureDisease, tranquilizer, rampage,
escape, death, and recapture tokens; consult zt-byte-range-map.tsv; observe
the original for warning timing and presentation. Mine only behavior/evidence
from commit d5657e5a, especially domain/src/simulation/disease.rs and
game/src/features/simulation/disease.rs.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence questions to settle before completion: disease eligibility and fame gates; check intervals/chance formula; symptom effects; cure research/hints and rewards; exact mortality rules; escape detection grace; rampage triggers; tranquilizer duration, miss/failure rules, and recapture sequence. No guessed defaults are permitted: absent values remain rejected or inactive according to the native definition contract.
7. Required behavior
- Disease assignment obeys compiled qualifiers, target types, force-only flags, fame gates, and deterministic simulation RNG. It cannot infect dead animals.
- Treatment validates disease/cure compatibility, provider role, reachability, timing, and authored reward before mutation.
- Escape and recapture preserve the animal entity and relationships. Death severs reservations/tasks and produces one terminal fact for economy, scenario, progression, information, and persistence consumers.
- Tranquilized animals cannot navigate, attack, eat, breed, perform, or board transport until recovered. Rampage and escape are distinct facts.
- Save-relevant components are all public and reflectable/serializable as root integration requires. Entity-reference repair is G22's responsibility.
- Mod extension occurs only through fixed disease/behavior records compiled by A10/A14; no runtime script or string command dispatch is added.
8. Performance contract
Steady systems allocate zero heap memory. Disease/containment work is changed-
or timer-driven; proximity uses G09/G14 bounded spatial queries rather than
animal-by-world scans. Definition lookup is O(1) by validated AssetId index;
each active case is O(1) per due tick. No strings, growing history, cloned asset
payloads, or per-frame vectors are allowed. Allocation tests cover each system
after warm-up; mapped traversal/handle clone remain zero-allocation; mapped
pages and definition page faults are attributed separately. This package owns
no GPU resources.
9. Acceptance criteria
Deliver formatted ordinary Rust under the owned directory exporting every
symbol above. Focused tests must establish transition uniqueness, compatible
treatment, timer clamping, terminal exclusion, and relationship cleanup using
data-derived definitions. No todo!, stub, guessed constant, fake success,
include_str!, source concatenation, manager, or shadow world. Observable done:
a legitimately eligible animal can become sick, show authentic symptoms, be
treated/recover, or die; an escaped/rampaging animal can be tranquilized and
recaptured. Rendering, UI layout, staff scheduling, and definition conversion
are non-goals.
10. Required handoff
Report files/API, Cargo or feature needs, root module/plugin/message/schedule/ reflection registrations, consumed cross-package symbols, evidence resolved or still open, integration seams to erase, and expected allocator/page-fault and gameplay checks. Root performs bulk compilation and integration.
G17 — Guests
1. Identity and outcome
This package makes each visitor a normal ECS entity with arrival/departure, personal needs, satisfaction, viewing, bounded memories, education, and reactions. Guests walk through the zoo, choose authored destinations, respond to what they experience, and leave with an observable evaluation. These facts are one cohesive lifecycle, not a guest simulation resource.
Integration wave: 3. Logical prerequisites: A14 world definitions, G04 world spawn, G14 locomotion, and G36 sanitation facts. G18–G21 consume its facts.
2. Exclusive ownership
The worker exclusively owns reconstruction/game/src/plugins/guests/. Every
other path is read-only. Root owns declarations, registration, schedules,
dependencies, reflection, and cross-package renames. No collision broadens
ownership.
3. Frozen public contract
Needs, satisfaction, education, reactions, memory values, and departure facts
use exact integer permille (u16 absolute, i16 signed delta). Durable simulation time uses G32 ticks. A14 owns the one recovered
GUEST_MEMORY_CAPACITY literal and rejects a definition exceeding it; this
package imports that constant directly rather than redeclaring it.
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
GuestsPlugin |
Plugin |
unit struct | Registers this guest lifecycle only. |
Guest |
Component |
marker | Identifies visitor entities; never stored in a guest collection resource. |
GuestArchetype |
Component |
pub struct GuestArchetype(pub AssetId) |
ID of one immutable A14 GuestDefinition; distinct name prevents static/live ownership ambiguity. |
GuestRng |
Component |
pub struct GuestRng(pub DeterministicRng) |
G32 domain-local destination/reaction stream derived from zoo seed and guest PersistentId; persisted by G22. |
GuestPhase |
Component + enum |
Arriving, Visiting, Leaving |
Exactly one lifecycle phase. |
GuestHunger |
Component |
pub struct GuestHunger { pub value:u16, pub residual_q16:u16 } |
A14 satiation permille, 1000 full; residual preserves exact per-tick Q16 decay. |
GuestThirst |
Component |
pub struct GuestThirst { pub value:u16, pub residual_q16:u16 } |
A14 hydration permille, 1000 full; exact Q16 decay. |
GuestEnergy |
Component |
pub struct GuestEnergy { pub value:u16, pub residual_q16:u16 } |
A14 rest permille, 1000 rested; exact Q16 decay. |
GuestRestroom |
Component |
pub struct GuestRestroom { pub value:u16, pub residual_q16:u16 } |
A14 relief permille, 1000 no current need; exact Q16 decay. |
GuestComfort |
Component |
pub struct GuestComfort { pub value:u16, pub residual_q16:u16 } |
Environmental comfort permille with exact Q16 adjustments. |
GuestSatisfaction |
Component |
pub struct GuestSatisfaction(pub u16) |
Current aggregate satisfaction permille; changed only by recovered reactions. |
GuestEducation |
Component |
pub struct GuestEducation(pub u16) |
Cumulative bounded education permille. |
VisitTime |
Component |
pub struct VisitTime { pub elapsed_ticks: u64 } |
Time since entry, excluding paused simulation. |
Viewing |
Component |
pub struct Viewing { pub subject: Entity, pub elapsed_ticks: u64 } |
Current valid view target. |
GuestDestination |
Component |
pub struct GuestDestination { pub entity: Entity, pub purpose: GuestVisitPurpose } |
Chosen destination using A14's exact enum; G14 owns route/path state. |
GuestMemoryEntry |
struct | pub struct GuestMemoryEntry { pub subject: Entity, pub kind: GuestMemoryKind, pub value_permille: i16, pub age_ticks: u64 } |
Copyable bounded fixed-unit fact using A14's exact enum; entity validity checked before use. |
GuestMemories |
Component |
pub struct GuestMemories { pub entries: ArrayVec<GuestMemoryEntry, GUEST_MEMORY_CAPACITY>, pub active_capacity: u16, pub next: u16 } |
Inline storage; len <= active_capacity <= GUEST_MEMORY_CAPACITY; active capacity comes from A14 definition and zero is valid. No per-guest heap owner. |
GuestArrivalState |
Resource |
pub struct GuestArrivalState { pub policy: AssetId, pub next_tick: u64, pub rng: DeterministicRng } |
One active world's deterministic visitor-arrival cursor; policy comes from the selected A19 map and state persists in G22. |
GuestReaction |
Message |
pub struct GuestReaction { pub guest: Entity, pub subject: Entity, pub kind: GuestMemoryKind, pub satisfaction_delta_permille: i16, pub education_delta_permille: i16 } |
Exact signed A14 fixed-unit outcome; checked/clamped on application. |
GuestArrived |
Message |
pub struct GuestArrived { pub guest: Entity } |
Emitted once after entry is completed. |
GuestDeparted |
Message |
pub struct GuestDeparted { pub guest: Entity, pub satisfaction_permille: u16, pub education_permille: u16 } |
Emitted once immediately before despawn with exact live fixed values. |
GuestArrivalFailed |
Message |
pub struct GuestArrivalFailed { pub reason: GuestArrivalFailure } |
Explicit policy/entrance/definition/ID failure; never silently substitutes a guest. |
GuestArrivalFailure |
enum | MissingPolicy, MissingEntrance, InvalidDefinition(AssetId), PersistentId(PersistentIdError) |
Closed runtime failure vocabulary after archive validation. |
Root contract requests: align G04 entrance/spawn-table facts, G14 navigation
target/reached facts, A14 definition handle, G09/G08 traversability, and G18
Wallet. Do not create a local navigation API, facility facade, or economy
copy.
4. ECS ownership
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_guest_arrivals |
Update / GameSet::Intent |
MessageReader<WorldLoadFinished>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<ZooSeed>, Option<Res<GuestArrivalState>>, Commands, MessageWriter<GuestArrivalFailed> |
Inserts one state resolved through scenario→map→A14 policy | New native world only; G22 restores saved state instead. |
spawn_arriving_guests |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Res<Fame>, Res<AdmissionPrice>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<GuestArrivalState>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(&PersistentId,&WorldMember,&ZooEntrance)>, Query<(),With<Guest>>, Commands, MessageWriter<NavigateTo>, MessageWriter<GuestArrivalFailed> |
At the due tick, allocator-issued PersistentId, WorldMember, Guest, GuestArchetype, Wallet, Transform, and G14 NavAgent for an authored group; emits NavigateTo to the entrance's inside point and advances policy RNG/cursor |
Chooses the lowest-PersistentId entrance and the first matching compiled rule; respects population cap and current admission/fame. |
initialize_spawned_guests |
FixedUpdate / FixedGameSet::Think |
Res<ZooSeed>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&PersistentId,&GuestArchetype),(Added<Guest>,Without<GuestRng>)>, Commands |
Inserts phase/needs/satisfaction/education/time/memory/RNG | Fresh G17 guests only; G22-restored guests already carry GuestRng and are untouched. |
decay_guest_needs |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldDefinitionsAsset>>, Query<(&GuestArchetype,&mut GuestHunger,&mut GuestThirst,&mut GuestEnergy,&mut GuestRestroom,&mut GuestComfort),With<Guest>> |
Five need values and their residuals | Exactly once per G32 simulation tick using A14 signed Q16 rates; no float/day batching. |
choose_guest_destination |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldDefinitionsAsset>>, Res<SpatialGrid>, Query<(Entity,&GuestArchetype,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,&GuestSatisfaction,&mut GuestRng),(With<Guest>,Without<GuestDestination>)>, Query<(Entity,&Transform,&Inspectable)>, Commands |
Inserts GuestDestination |
Due reconsideration after needs. |
begin_viewing |
FixedUpdate / FixedGameSet::Act |
Query<(Entity,&GuestDestination,&GlobalTransform),(With<Guest>,Without<Viewing>)>, Query<(&GlobalTransform,&Visibility)>, MessageReader<Arrived>, Commands |
Inserts Viewing |
Matching reached View destination. |
advance_viewing |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut Viewing,&mut GuestSatisfaction,&mut GuestEducation,&mut GuestMemories),With<Guest>>, Query<(&SpeciesHandle,&AnimalWelfare)>, MessageWriter<GuestReaction> |
Viewing ticks and reaction messages | Current viewers only. |
react_to_visible_litter |
FixedUpdate / FixedGameSet::Think |
Res<SpatialGrid>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&GlobalTransform),Or<(Added<Litter>,Added<HabitatWaste>)>>, Query<(&GlobalTransform,&mut GuestRng),With<Guest>>, MessageWriter<GuestReaction> |
Exact A14 GuestMemoryKind::Litter reactions for bounded nearby eligible guests |
Newly visible waste only; distance/probability/delta come from native definitions, never all-guests × all-waste. |
apply_guest_reactions |
FixedUpdate / FixedGameSet::Act |
MessageReader<GuestReaction>, Query<(&mut GuestSatisfaction,&mut GuestEducation,&mut GuestMemories),With<Guest>> |
Fixed guest facts/memory slot | After producers. |
advance_guest_lifecycle |
FixedUpdate / FixedGameSet::Cleanup |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, MessageReader<Arrived>, Query<(Entity,&mut GuestPhase,&mut VisitTime,&GuestSatisfaction,&GuestEducation,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,Option<&GuestDestination>),With<Guest>>, Commands, MessageWriter<GuestArrived>, MessageWriter<GuestDeparted> |
Phase/time/despawn and lifecycle messages | After economy. |
age_guest_memories |
FixedUpdate / FixedGameSet::Cleanup |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&mut GuestMemories,With<Guest>> |
Ages/evicts fixed entries in ticks | Only when ZooClock.tick reaches the next exact A14 interval boundary. |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch. No original source parsing or filesystem discovery belongs here.
6. Behavioral evidence and disposition
Search shipped guest AI/task/need/entity definitions under ignored vendor/;
search analysis TSVs and oracle names for ZTGuest, guest needs, viewing,
education, thoughts/emoticons, group arrival, and exit behavior. Observe live
original destination priorities, viewing arcs, crowd reactions, and departure.
Mine commit d5657e5a only for behavioral leads in
domain/src/simulation/guest.rs, features/simulation/guests.rs, AI, ambient,
influence, tours, and facilities.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Questions: numeric GUEST_MEMORY_CAPACITY from maximum valid shipped/mod
contract and original storage evidence; authentic need scales/rates; group membership effects; destination
scoring; line abandonment; animal visibility and viewing duration; memory
retention; education sources; happiness aggregation; admission refusal and
departure conditions. Recover from definitions/oracle/live behavior; do not
reuse old aggregate guest snapshots or guessed constants.
7. Required behavior
- The selected map's compiled arrival rules are the only source of new visitor
groups. Spawned guests receive only authored archetype values, carry the
entrance's
WorldMember, start at its authored arrival point, and route to its authored inside point. Arrival/departure messages are unique. - Destination choice responds to needs and authored affordances, respects reachability/capacity, and never reserves multiple destinations.
- Viewing requires line of sight/range and a live subject; invalid targets clear immediately. Reactions alter only the specified guest and use data-defined deltas.
- Entity despawn clears destination, reservation, viewing, commerce, show, tour, and UI relationships through their natural owners before departure.
- Components are save facts. Mods extend fixed native guest/affordance records, never runtime scripts or source documents.
- Controller/UI presentation and facility/economy execution are non-goals.
8. Performance contract
All steady systems allocate zero. Due arrival may allocate only the new ECS entities/components after reserving group capacity; rule and weighted-archetype selection traverse mapped ranges without allocation. Guest memory is inline fixed-capacity storage with no per-guest heap allocation; A14 validates its active bound. Needs are O(guests due); destination search is O(candidates in bounded spatial cells), not O(guests × world); viewing is O(active viewers). Memory is fixed capacity. No per-frame strings, vectors, asset clones, or hash growth. Changed filters/timers suppress unnecessary work. Allocator regressions cover a settled guest tick and view/reaction path; mapped definition lookup and handle clones remain allocation-free; this package owns no GPU data.
9. Acceptance criteria
Deliver the owned Rust subtree and all exports above. Focused tests cover needs clamping, one destination/reservation, view invalidation, evidence-bounded memory eviction, reaction application, and unique departure. No stubs, guessed behavior, record bags, manager resources, or runtime parsing. Observable done: guests arrive, navigate, satisfy needs, view/learn/react, and depart authentically while other guests remain independently parallelizable.
10. Required handoff
Report files/API, dependencies, root registrations/wiring, consumed symbols, evidence answers/open questions, temporary seams, and allocator/page/gameplay validation. Root performs bulk checks.
G18 — Economy
1. Identity and outcome
This package delivers authoritative zoo cash, admission, prices, commerce, facility capacity, and completed service transactions. Money moves once through typed transactions when a service actually completes; menus and facilities read the same live facts. This is one natural global accounting responsibility, not a port of the original economy component graph.
Integration wave: 3. Prerequisites: A14 definitions, G10 placement/facilities, and G17 guests. G19, G20, G23, G24, G26, and G27 consume transactions.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/economy/. Everything else is
read-only. Root owns module/plugin/schedule/dependency/reflection registration
and aligns earlier symbols. Report collisions.
3. Frozen public contract
Money is signed integer hundredths of the displayed base currency. Arithmetic
must be checked; no floating-point money enters live state.
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
EconomyPlugin |
Plugin |
unit struct | Registers only accounting/service systems. |
Money |
value type | #[repr(transparent)] pub struct Money(pub i64) with checked checked_add/checked_sub |
Currency minor units; overflow rejects a transaction. |
ZooCash |
Resource |
pub struct ZooCash(pub Money) |
Sole authoritative player cash balance. |
AdmissionPrice |
Resource |
pub struct AdmissionPrice(pub Money) |
Current global gate price; bounds come from native scenario/rules. |
Wallet |
Component |
pub struct Wallet(pub Money) |
Authoritative cash carried by one guest. |
Price |
Component |
pub struct Price(pub Money) |
Current price on one purchasable/service entity. |
ServiceFacility |
Component |
pub struct ServiceFacility { pub definition: AssetId, pub capacity: u16, pub occupied: u16 } |
Capacity counters remain occupied <= capacity; entity is the facility. |
Inventory |
Component |
pub struct Inventory { pub available:u16, pub reserved:u16, pub capacity:u16 } |
Optional stocked-facility fact; available + reserved <= capacity. Initial capacity and per-service units come from A14. |
ServiceReservation |
Component |
pub struct ServiceReservation { pub facility: Entity, pub service: AssetId, pub inventory_units:u16 } |
Lives on customer; at most one active service reservation. Units move available→reserved atomically at reservation. |
ServiceProgress |
Component |
pub struct ServiceProgress { pub facility: Entity, pub remaining_ticks: u32 } |
Lives on customer only while service is underway; duration is preflight-compiled G32 ticks. |
TransactionKind |
enum | Admission, Purchase, Service, Construction, Sale, Wage, Upkeep, Donation, Reward, Refund |
Fixed semantic accounting categories. |
Account |
enum | Zoo, Entity(Entity), External |
Transaction endpoints; External is source/sink, not hidden cash state. |
TransactionRequest |
Message |
pub struct TransactionRequest { pub operation: Entity, pub debit: Account, pub credit: Account, pub amount: Money, pub kind: TransactionKind, pub subject: Option<Entity> } |
operation is the caller-owned pending-operation entity; amount positive; one requested atomic transfer. |
TransactionCompleted |
Message |
pub struct TransactionCompleted { pub operation: Entity, pub debit: Account, pub credit: Account, pub amount: Money, pub kind: TransactionKind, pub subject: Option<Entity> } |
Echoes the request operation exactly once after both sides commit. |
TransactionRejected |
Message |
pub struct TransactionRejected { pub operation: Entity, pub debit: Account, pub credit: Account, pub amount: Money, pub kind: TransactionKind, pub subject: Option<Entity>, pub reason: TransactionRejection } |
Echoes the request operation exactly once; balances are unchanged. |
TransactionRejection |
enum | NonPositive, InsufficientFunds, MissingAccount, CapacityUnavailable, Overflow, RuleDenied |
No string error path. |
ServiceRequest |
Message |
pub struct ServiceRequest { pub customer: Entity, pub facility: Entity, pub service: AssetId } |
Guest/staff requests actual authored service. |
ServiceCompleted |
Message |
pub struct ServiceCompleted { pub customer: Entity, pub facility: Entity, pub service: AssetId } |
Emitted after time and payment completion. |
ServicePaymentPending |
Component |
pub struct ServicePaymentPending { pub customer: Entity, pub facility: Entity, pub service: AssetId } |
Lives only on the G18-owned operation entity between transfer request and matching result. |
Root contract requests: align G10 facility/placed definitions and affordances,
G17 Guest/destination facts, G14 reached-target facts, and A14 price/duration/
capacity records. Other packages must use Money, Wallet, ZooCash, and
TransactionRequest directly—not mirror balances or create economy adapters.
4. ECS ownership
ZooCash and AdmissionPrice are genuinely zoo-global policy/facts. There is
no ledger vector, transaction manager, facility world, or per-frame finance
snapshot. Historical reports, if required, are bounded aggregates on their
natural reporting owner rather than retained transactions.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_service_facilities |
FixedUpdate / FixedGameSet::Act |
Commands, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&DefinitionId),(Added<DefinitionId>,Without<ServiceFacility>)> |
Inserts the definition's Price, ServiceFacility, and optional full Inventory directly on the placed facility |
Fresh placed facilities only; G22-restored facilities already carry ServiceFacility. Zero inventory capacity inserts no Inventory. |
admit_guests |
FixedUpdate / FixedGameSet::Economy |
MessageReader<GuestArrived>, Res<AdmissionPrice>, ResMut<ZooCash>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut Wallet,&mut GuestPhase),With<Guest>> |
Wallet/cash/phase | Matching gate arrival; checked direct admission transfer. |
reserve_services |
FixedUpdate / FixedGameSet::Act |
MessageReader<ServiceRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&Wallet,Option<&ServiceReservation>)>, Query<(&Price,&mut ServiceFacility,Option<&mut Inventory>)>, Commands |
Occupancy, available→reserved stock, reservation/progress | Valid reached customer with capacity, affordability and stock; all counters change atomically. |
advance_services |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Query<(Entity,&WorldMember,&mut ServiceProgress,&ServiceReservation)>, Query<(),With<ServiceFacility>>, Query<(),With<ServicePaymentPending>>, Commands, MessageWriter<TransactionRequest> |
Progress plus one payment operation carrying the customer's WorldMember, and transaction request |
One tick; no duplicate. |
apply_transactions |
FixedUpdate / FixedGameSet::Economy |
MessageReader<TransactionRequest>, ResMut<ZooCash>, Query<&mut Wallet>, MessageWriter<TransactionCompleted>, MessageWriter<TransactionRejected> |
Exactly two account balances atomically | Single authoritative writer; checked arithmetic; deterministic message order. |
finish_services |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, Query<&ServicePaymentPending>, Query<(Entity,&ServiceReservation),With<ServiceProgress>>, Query<(&mut ServiceFacility,Option<&mut Inventory>)>, Commands, MessageWriter<ServiceCompleted> |
Clears facts, consumes reserved stock, releases occupancy/operation and emits completion | completed.operation match. |
reject_service_payments |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionRejected>, Query<&ServicePaymentPending>, Query<(Entity,&ServiceReservation),With<ServiceProgress>>, Query<(&mut ServiceFacility,Option<&mut Inventory>)>, Commands |
Returns reserved→available stock and releases reservation/capacity/operation | rejected.operation match. |
cancel_invalid_services |
FixedUpdate / FixedGameSet::Cleanup |
Query<(Entity,&ServiceReservation),With<ServiceProgress>>, Query<(&mut ServiceFacility,Option<&mut Inventory>)>, RemovedComponents<Guest>, Commands |
Returns reserved stock, clears invalid reservation/progress and decrements live facility | Before despawn. |
restock_inventories |
FixedUpdate / FixedGameSet::Economy |
MessageReader<ZooDayAdvanced>, Res<Assets<WorldDefinitionsAsset>>, Query<(&ServiceFacility,&mut Inventory)> |
Saturating available toward capacity without touching reserved units |
Exactly once per zoo-day event using authored inventory_restock_per_zoo_day. |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch. No XML/Z2F/filesystem/source precedence belongs here.
6. Behavioral evidence and disposition
Primary code evidence exists in cpp-reconstruction/include/openzt2/economy/
and src/economy/ (ZTEconomyComponent, transaction/tracker/data), plus shipped
facility, product, admission, scenario, and economy definitions. Search analysis
TSVs for economy messages and live-observe price UI, service timing/refunds, and
insufficient-funds behavior. Mine commit d5657e5a only for facts in
domain/src/simulation/economy.rs, facilities, features/simulation/economy.rs,
and UI finance/money modules.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle: source currency precision/rounding; whether negative cash is mode- dependent; admission debit semantics; purchase/service charge point; capacity and cancellation; sale/refund percentages; wage/upkeep cadence; finance report categories. Native authored/scenario values replace all guesses.
7. Required behavior
- Every money transfer is positive, checked, atomic, deterministic, and emits exactly one completion or rejection. Insufficient funds never partially debit.
- Admission/service states change only after the appropriate transaction result. Facility deletion/customer departure releases capacity and follows authentic refund rules.
- Prices/capacities/durations originate in native definitions or scenario policy. Mod extension is data-defined through the fixed categories above.
- Stocked services reserve authored units before beginning, consume those units only on successful payment, return them on rejection/cancellation, and restock only by the compiled zoo-day amount.
- Construction/sales/wages/upkeep/donations/rewards all use the same transfer path, but this package does not own their domain decisions.
ZooCash, wallets, prices, occupancy, reservations, and progress persist; pending message buffers do not.
8. Performance contract
Steady accounting/service systems allocate zero. Transfers are O(messages) and account lookup O(1); service progression is O(active services), not all facilities × guests. Bevy message buffers are warmed/pre-sized before settled measurement. No ledger growth, strings, floats, filesystem, or asset payload clones. Hard tests cover zero-allocation successful/rejected transfers and a settled service tick. Mapped definition lookup remains allocation-free; no GPU resources are owned.
9. Acceptance criteria
Deliver the owned Rust subtree and exact exports. Tests cover checked money, atomic debit/credit, insufficient funds, missing entity, capacity, service completion/cancellation, and message uniqueness. Observable done: admission and completed commerce change guest/zoo balances once and UI consumers see the same facts. No stub, float money, manager, ledger world, or guessed price.
10. Required handoff
Report API/files, dependency and root wiring needs, registrations, direct consumers, evidence results/questions, seams, and allocation/page/gameplay checks. Root performs bulk compilation.
G19 — Donations
1. Identity and outcome
This package delivers guest donation decisions and donation-box/acceptor use as small ECS facts. A guest identifies an eligible authored subject, reaches an acceptor, and transfers money through G18 exactly once; the acceptor and zoo receive UI-visible totals. Donation decision and completion form one cohesive feature, while accounting remains owned by G18.
Integration wave: 3. Prerequisites: G17 guests and G18 economy.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/donations/. Everything else
is read-only. Root owns declarations, plugin/schedule/message/reflection wiring
and cross-package alignment.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
DonationsPlugin |
Plugin |
unit struct | Registers focused donation systems. |
DonationAcceptor |
Component |
pub struct DonationAcceptor { pub category: AssetId, pub beneficiary: Option<Entity> } |
Marks a real box/facility/show/tour acceptor. Beneficiary must be live or absent. |
DonationRng |
Component |
pub struct DonationRng(pub DeterministicRng) |
G32 domain-local stream on each guest, derived from zoo seed and PersistentId; sole donation roll source and persisted by G22. |
DonationCandidate |
Component |
pub struct DonationCandidate { pub acceptor: Entity, pub subject: Entity, pub amount: Money } |
Lives on guest between decision and reservation; positive amount. |
DonationProgress |
Component |
pub struct DonationProgress { pub acceptor: Entity, pub subject: Entity, pub amount: Money, pub remaining_ticks: u32 } |
Carries the accepted candidate facts through animation/payment without a lookup side table. |
DonationTotal |
Component |
pub struct DonationTotal { pub amount: Money, pub count: u32 } |
Cumulative acceptor fact; checked arithmetic, saturating count only if evidence requires. |
PendingDonationPayment |
Component |
pub struct PendingDonationPayment { pub guest: Entity, pub acceptor: Entity, pub subject: Entity, pub amount: Money } |
Lives only on the domain-owned transaction operation entity. |
DonationRequest |
Message |
pub struct DonationRequest { pub guest: Entity, pub acceptor: Entity, pub subject: Entity, pub amount: Money } |
Real request after guest decision/reach. |
DonationCompleted |
Message |
pub struct DonationCompleted { pub guest: Entity, pub acceptor: Entity, pub subject: Entity, pub amount: Money } |
Emitted once after matching G18 transaction succeeds. |
DonationRejected |
Message |
pub struct DonationRejected { pub guest: Entity, pub acceptor: Entity, pub reason: DonationRejection } |
Typed domain failure, no mutation. |
DonationRejection |
enum | InvalidAcceptor, InvalidSubject, Unreachable, InsufficientFunds, RuleDenied, AccountFailure |
Fixed reason vocabulary. |
Root contract requests: align G17 guest needs/satisfaction/destination and G14
reach/spatial facts; consume G18 Money, Wallet, Account,
TransactionRequest, and TransactionCompleted directly. A14/G26/G27 must
provide authored donation category/value facts without a local catalogue copy.
4. ECS ownership
No donation manager, candidate list resource, ledger copy, or guest snapshot is allowed. Candidate/progress lives on the donating guest and totals on the acceptor.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
choose_donation_candidates |
FixedUpdate / FixedGameSet::Think |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Res<SpatialGrid>, Query<(Entity,&GuestSatisfaction,&Wallet,&mut DonationRng),(With<Guest>,Without<DonationCandidate>,Without<DonationProgress>)>, Query<(Entity,&DonationAcceptor,&Transform)> , Commands |
Inserts DonationCandidate |
Due eligible guests. |
begin_donations |
FixedUpdate / FixedGameSet::Act |
MessageReader<Arrived>, Query<(Entity,&DonationCandidate),With<Guest>>, Query<&DonationAcceptor>, Query<(),With<PhotoSubject>>, Commands |
Replaces candidate with progress | Matching acceptor arrival. |
advance_donations |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Query<(Entity,&mut DonationProgress,&Wallet),With<Guest>>, Query<&DonationAcceptor>, Commands, MessageWriter<DonationRequest> |
Countdown/request from progress facts | Active donors. |
request_donation_transfers |
FixedUpdate / FixedGameSet::Economy |
MessageReader<DonationRequest>, Query<(&Wallet,&DonationProgress,&WorldMember),With<Guest>>, Query<&DonationAcceptor>, Query<(),With<PendingDonationPayment>>, Commands, MessageWriter<TransactionRequest>, MessageWriter<DonationRejected> |
Pending operation carrying guest WorldMember plus transaction request |
Valid request. |
complete_donations |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, Query<&PendingDonationPayment>, Query<Entity,With<DonationProgress>>, Query<&mut DonationTotal>, Commands, MessageWriter<DonationCompleted> |
Total/progress/operation/completion | Operation match. |
reject_donation_payments |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionRejected>, Query<&PendingDonationPayment>, Query<Entity,With<DonationProgress>>, Commands, MessageWriter<DonationRejected> |
Clears progress/operation; emits rejection | Operation match. |
cancel_invalid_donations |
FixedUpdate / FixedGameSet::Cleanup |
Query<(Entity,Option<&DonationCandidate>,Option<&DonationProgress>),With<Guest>>, Query<(),With<DonationAcceptor>>, RemovedComponents<Guest>, Commands, MessageWriter<DonationRejected> |
Clears transient components | Broken relation. |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch. No original formats or filesystem access.
6. Behavioral evidence and disposition
Search shipped donation-box, animal/viewable, show and tour definitions; search
analysis records/oracle for donation components/messages and
ZT_SET_CURRENT_SHOW_FOR_DONATIONS. Observe original eligibility, animation,
amount feedback, and box attribution. Mine commit d5657e5a only for behavioral
leads in domain/src/simulation/donation.rs,
features/simulation/donation.rs, guests, shows, and tours.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle candidate scoring/chance, subject-to-acceptor association, donation amount formula, distance/reach/animation timing, cooldown/repeat limits, show and tour multipliers, education/satisfaction effects, and attribution categories from native definitions and live behavior. Never invent fallback amounts.
7. Required behavior
- Only a live guest with sufficient wallet, an eligible viewed subject, and a reachable matching acceptor may donate.
- The G18 transaction is the sole balance mutation. Rejection clears or retains intent exactly as recovered and never increments totals.
- Acceptor deletion, subject deletion, guest departure, save/load, and mode transitions clean transient relationships. Totals and successfully completed outcome facts persist; candidates/progress persist only if G22 evidence says in-progress actions resume.
- Shows/tours contribute through direct authored category/value facts, not string tokens or a donation facade.
8. Performance contract
Zero allocations in steady systems. Candidate search uses bounded G14 spatial cells and due guests: O(nearby eligible pairs), never all guests × all boxes. Completion is O(transaction messages). No ledgers, strings, dynamic candidate lists, asset clones, or filesystem. Hard allocation tests cover a no-donation tick and full donation path after message buffers warm. No GPU ownership.
9. Acceptance criteria
Deliver exact exports under the owned path. Tests cover eligibility, insufficient wallet, invalid relationships, one transfer/one completion, checked totals, and cleanup. Observable done: an eligible guest reaches a matching box and donates; wallet/zoo/box totals agree exactly. No placeholder values or parallel economy.
10. Required handoff
Report files/API, root wiring/registration, dependencies, consumed symbols, resolved/open evidence, integration seams, and allocation/gameplay checks. Root performs bulk compilation.
G20 — Staff
1. Identity and outcome
This package delivers hiring, firing, wages, assignments, and executable work as staff and job entities. Keepers feed/clean/care; maintenance workers repair and empty waste; educators and other authored roles perform their actual jobs. There is no staff manager or central simulated workforce object.
Integration wave: 3. Prerequisites: A14 definitions, G10 facilities, G14 locomotion, G15 feeding, and G18 economy. G16 health requests treatment work.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/staff/; all else read-only.
Root owns declarations, registration, schedules, reflection, dependencies, and
cross-package symbol alignment. Report collisions.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
StaffPlugin |
Plugin |
unit struct | Registers staff employment/job systems only. |
Staff |
Component |
marker | Identifies one staff entity. |
StaffRole |
Component |
pub struct StaffRole(pub AssetId) |
Immutable authored role definition. |
Employment |
Component |
pub struct Employment { pub wage: Money, pub hired_tick: u64 } |
Wage uses G18 minor units and G32 calendar/ticks; cadence is definition/scenario-derived. |
StaffAssignment |
Component |
pub struct StaffAssignment { pub area: Option<Entity>, pub target: Option<Entity> } |
Optional player-authored restriction; referenced entities must be live. |
AvailableForWork |
Component |
marker | Staff may claim work; removed while off-duty/working/incapacitated. |
StaffJob |
Component |
pub struct StaffJob { pub kind: StaffJobKind, pub target: Entity, pub urgency: u16 } |
Lives on a dedicated job entity; target is live until cleanup. |
JobClaim |
Component |
pub struct JobClaim { pub staff: Entity } |
Lives on job entity; at most one claimant. |
CurrentJob |
Component |
pub struct CurrentJob { pub job: Entity } |
Lives on staff; inverse of one valid JobClaim. |
JobProgress |
Component |
pub struct JobProgress { pub remaining_ticks: u32 } |
Lives on job after staff reaches target; duration is A14/G32 ticks. |
PendingStaffHire |
Component |
pub struct PendingStaffHire { pub role: AssetId, pub position: Vec3 } |
Domain-owned payment operation; no staff entity exists before matching completion. |
PendingWagePayment |
Component |
pub struct PendingWagePayment { pub staff: Entity, pub due_tick: u64 } |
Correlates one evidenced wage charge to one employee/cadence. |
StaffJobRequest |
Message |
pub struct StaffJobRequest { pub kind: StaffJobKind, pub target: Entity, pub urgency: u16 } |
Natural asynchronous request from a dirty/broken/hungry/sick target. |
HireStaffRequest |
Message |
pub struct HireStaffRequest { pub role: AssetId, pub position: Vec3 } |
Player request; validates price/placement through owning domains. |
FireStaffRequest |
Message |
pub struct FireStaffRequest { pub staff: Entity } |
Player request; claimed work is released before despawn. |
StaffHired |
Message |
pub struct StaffHired { pub staff: Entity, pub role: AssetId } |
Emitted exactly once after spawn/payment. |
StaffFired |
Message |
pub struct StaffFired { pub staff: Entity } |
Emitted once before despawn. |
StaffJobCompleted |
Message |
pub struct StaffJobCompleted { pub staff: Entity, pub job: Entity, pub kind: StaffJobKind, pub target: Entity } |
Emitted only after target mutation succeeds. |
StaffJobCancelled |
Message |
pub struct StaffJobCancelled { pub kind: StaffJobKind, pub target: Entity } |
Emitted before invalid-job cleanup removes a still-live target's job, allowing its natural owner to re-evaluate unresolved work. |
StaffJob, StaffJobRequest, and StaffJobCompleted use A14's exact
StaffJobKind::{Feed,RefillWater,CleanHabitat,EmptyBin,SweepLitter,Repair,Treat, Educate,Entertain,Tranquilize,Capture,MaintainTank,OperateShow}; G20 declares no
second enum. Root aligns G14 navigation target/reached/spatial facts,
G15 feeder/container transactions, G16 treatment/recapture, G36 condition/waste,
G17 education reactions, G18 transaction types, and A14 staff/job definitions.
Do not introduce provider/facility traits or copies.
4. ECS ownership
Jobs are entities because they have lifecycle, contention, target, claim, and save relevance. There is no global queue; spatial lookup may index unclaimed job entities through G14's bounded index.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
create_staff_jobs |
FixedUpdate / FixedGameSet::Think |
MessageReader<StaffJobRequest>, Query<(&StaffJob,Option<&JobClaim>)>, Query<&WorldMember>, ResMut<PersistentIdAllocator>, Commands |
After target validation/ID allocation, spawns unique job entity with target's WorldMember and fresh PersistentId |
Deduplicate (kind,target); acknowledge no job before both identity facts exist. |
claim_staff_jobs |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldDefinitionsAsset>>, Res<SpatialGrid>, Query<(Entity,&StaffRole,&Transform,&StaffAssignment),(With<Staff>,With<AvailableForWork>,Without<CurrentJob>)>, Query<(Entity,&StaffJob),Without<JobClaim>>, Commands |
Claim/current job/availability | Priority/distance/entity order. |
begin_staff_job |
FixedUpdate / FixedGameSet::Act |
MessageReader<Arrived>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&CurrentJob),With<Staff>>, Query<&StaffJob>, Commands |
Inserts JobProgress |
Matching job target arrival. |
advance_feeding_jobs |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<(&mut FoodContainer,Option<&RefillPending>)>, Query<&mut DrinkContainer>, Commands, MessageWriter<RefillContainer>, MessageWriter<StaffJobCompleted> |
Progress/container requests | Feed/RefillWater only. |
advance_facility_operation_jobs |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<&mut WaterQuality>, Query<&mut ShowState>, Commands, MessageWriter<StaffJobCompleted> |
Progress and natural target components | MaintainTank/OperateShow only; G36 exclusively executes Repair. |
advance_health_jobs |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<(Option<&Disease>,Option<&Escaped>,Option<&Tranquilized>),With<Animal>>, Commands, MessageWriter<TreatmentRequest>, MessageWriter<TranquilizeRequest>, MessageWriter<StaffJobCompleted> |
Progress/health requests | Treat/Tranquilize/Capture. |
advance_guest_service_jobs |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Query<(Entity,&StaffJob,&mut JobProgress,&JobClaim)>, Query<(),With<Guest>>, Commands, MessageWriter<GuestReaction>, MessageWriter<StaffJobCompleted> |
Progress/reaction | Educate/Entertain. |
release_finished_jobs |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<StaffJobCompleted>, Query<(Entity,&CurrentJob),With<Staff>>, Query<&JobClaim>, Commands |
Job/current/availability | After target mutation. |
cancel_invalid_jobs |
FixedUpdate / FixedGameSet::Cleanup |
Query<(Entity,&StaffJob,Option<&JobClaim>)>, Query<(Entity,Option<&CurrentJob>),With<Staff>>, Query<(),With<WorldMember>>, Commands, MessageWriter<StaffJobCancelled> |
Emits cancellation for a still-live target, clears reciprocal facts and despawns job | Before target/staff despawn; absent targets cannot be requeued. |
request_staff_hiring |
FixedUpdate / FixedGameSet::Economy |
MessageReader<HireStaffRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<&PendingStaffHire>, Query<(Entity,&WorldRoot)>, Commands, MessageWriter<TransactionRequest> |
Spawns PendingStaffHire operation with root WorldMember and emits a transaction carrying it |
No staff spawn and no duplicate (role,position) request. |
complete_staff_hiring |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, Query<(Entity,&PendingStaffHire,&WorldMember)>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Commands, MessageWriter<StaffHired> |
Validates/allocates first, then spawns staff bundle with operation's WorldMember and fresh PersistentId, emits hired, despawns operation |
TransactionCompleted.operation must equal the operation entity. |
reject_staff_hiring |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionRejected>, Query<(Entity,&PendingStaffHire)>, Commands |
Despawns operation without staff spawn/success | TransactionRejected.operation must equal the operation entity. |
process_staff_firing |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<FireStaffRequest>, Query<(Entity,&StaffRole,&Employment,Option<&CurrentJob>),With<Staff>>, Query<&mut JobClaim>, Commands, MessageWriter<StaffFired> |
Releases job and despawns staff | After job execution. |
charge_staff_wages |
FixedUpdate / FixedGameSet::Economy |
MessageReader<ZooDayAdvanced>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&Employment,&WorldMember),With<Staff>>, Query<&PendingWagePayment>, Commands, MessageWriter<TransactionRequest> |
Spawns world-scoped PendingWagePayment and emits authored wage debit carrying it |
Exact original cadence; no second operation for the same (staff,due_tick). |
settle_staff_wages |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<(Entity,&PendingWagePayment)>, Query<&mut Employment>, Commands |
Applies only evidenced employment consequence and despawns operation; never emits hire/job success | Result operation must equal the operation entity. |
5. Preflight producer contract
Not applicable: engine consumes only native assets produced before launch. No source parsing, filesystem discovery, or original codecs.
6. Behavioral evidence and disposition
Search shipped staff entity/task/role definitions; analysis/oracle names for
keeper, maintenance, educator, veterinarian, assignment and hire/fire commands;
observe job priority, assignment areas, animation and wages. Mine commit
d5657e5a only for evidence in domain/src/simulation/staff.rs,
features/simulation/staff.rs, disease, facilities, feeding and AI/navigation.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle roles, job compatibility/scoring, duplicate suppression, assignment semantics, work durations/effects, operating requirements, wages, firing/refund, and unavailable states. Use native records/oracle/live behavior; no generic job bag or guessed work rate.
7. Required behavior
- Only compatible, available, reachable staff claim one job; one job has one claimant. Assignment restrictions and deterministic priorities apply.
- Work mutates the target fact owned by its domain only after arrival/duration. Target/staff deletion atomically releases reciprocal relationships.
- Hiring/firing/wages use G18 transactions exactly once. Staff identity, employment, assignment and active job are save relevant.
- New mod roles use fixed A14 role/job capability records; no runtime command strings. Staff locomotion remains G14-owned.
8. Performance contract
Zero allocations after warm-up. Claim search uses bounded spatial indexes and unclaimed changed jobs, O(nearby candidates), not staff × all jobs. Job entities avoid growing queues. Work systems are split by exact query and process only active progress. Allocation regressions cover idle, claim, work and cleanup; mapped lookups/handles allocate zero; no GPU ownership.
9. Acceptance criteria
Deliver exact API under owned path. Tests establish unique jobs/claims, priority/assignment, invalid cleanup, completed mutation, and single wage/hire/ fire transactions. Observable done: staff can be hired, assigned, autonomously complete authentic care/maintenance work, be paid, and be fired. No manager, whole-world dispatcher, stub, guessed rate, or duplicate target state.
10. Required handoff
Report files/API, dependencies, root registrations/wiring, consumed symbols, evidence, seams, and allocator/page/gameplay validation.
G21 — Information and Catalogues
1. Identity and outcome
This package connects selection to native UI panels, purchase catalogues, filters, buy information, entity status, and Zoopedia content. It projects live facts and immutable mapped records into the G02 UI without owning copies of the selected entity, catalogue, or simulation.
Integration wave: 3. Prerequisites: A02 UI, A09 localization, A14 definitions, G02 UI runtime, and G07 construction.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/information/; all other paths
read-only. Root owns registrations, loader/plugin wiring, schedule ordering,
dependencies, and cross-package alignment.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
InformationPlugin |
Plugin |
unit struct | Registers selection and information projection. |
SelectedEntity |
Resource |
pub struct SelectedEntity(pub Option<Entity>) |
Sole current world selection; cleared when entity is invalid. |
SelectionRequest |
Message |
pub struct SelectionRequest { pub entity: Option<Entity>, pub source: ActionSource } |
Device-independent selection request. |
SelectionChanged |
Message |
pub struct SelectionChanged { pub previous: Option<Entity>, pub current: Option<Entity> } |
Emitted exactly once per actual change. |
Inspectable |
Component |
pub struct Inspectable { pub definition: AssetId } |
Entity exposes mapped authored information plus live components. |
InfoPanel |
Component |
pub struct InfoPanel { pub subject: Entity } |
Lives on one UI root node; no copied domain fields. |
CataloguePanel |
Component |
pub struct CataloguePanel { pub category: CatalogueCategory, pub page: u16 } |
UI-node view state only. |
CatalogueFilter |
Component |
pub struct CatalogueFilter { pub text_key: Option<AssetId>, pub unlocked_only: bool, pub affordable_only: bool } |
Lives on catalogue UI node; text_key refers to precompiled normalized search token, never runtime lowercase String. |
CatalogueCommand |
Message |
pub enum CatalogueCommand { SetCategory(CatalogueCategory), SetPage(u16), SetUnlockedOnly(bool), SetAffordableOnly(bool), Choose(AssetId), OpenZoopedia(AssetId) } |
Fixed UI command result from A02/G02. |
PurchaseChoice |
Message |
pub struct PurchaseChoice { pub definition: AssetId } |
Hands selected definition directly to G07/G11/G20 natural owner. |
ZoopediaPage |
Component |
pub struct ZoopediaPage { pub subject: AssetId, pub section: u16 } |
UI node points into mapped localized native content. |
CataloguePanel/CatalogueCommand consume A14's exact CatalogueCategory
directly; this package declares no second category enum. A14 exposes direct
indexed catalogue/Zoopedia records; G02 exposes node binding targets/fixed
command results; G07/G11/G20
consume PurchaseChoice; G18/G23 expose direct cash/unlock facts; entity owners
provide small live status components. Do not create an EntityInfo map,
reflection property bag, UI model tree, or copied catalogue resource.
4. ECS ownership
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
apply_selection_requests |
Update / GameSet::Intent |
MessageReader<SelectionRequest>, Query<(),With<Inspectable>>, ResMut<SelectedEntity>, MessageWriter<SelectionChanged> |
Selection resource only | In game; deterministic last valid request. |
clear_removed_selection |
Update / GameSet::Ui |
RemovedComponents<Inspectable>, ResMut<SelectedEntity>, MessageWriter<SelectionChanged> |
Clears invalid selection | Before panel projection. |
bind_info_panel_subject |
Update / GameSet::Ui |
Res<SelectedEntity>, Query<(&mut InfoPanel,&mut Visibility)>, Commands |
Panel subject/visibility | SelectedEntity.is_changed(). |
project_static_information |
Update / GameSet::Ui |
Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<&Inspectable>, Query<(&InfoPanel,&InheritedVisibility),Changed<InfoPanel>>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiImageBinding,&mut ImageNode)> |
UI node text/image handles/visibility | Selection/locale change only; no owned copy. |
project_identity_name |
Update / GameSet::Ui |
Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Inspectable,Option<&Name>)>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiImageBinding,&mut ImageNode)> |
Identity/name/icon node values only | Subject, locale, or Name changed. |
project_animal_needs |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Hunger,&Thirst,&RestNeed,&PrivacyNeed,&SocialNeed,&ExerciseNeed,&StimulationNeed,&EnvironmentNeed,&KeeperCareNeed,&HealthNeed,&AnimalWelfare,&WelfareBand),With<Animal>>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Need bars and welfare label only | Relevant G12 component changed. |
project_animal_health |
Update / GameSet::Ui |
Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Vitality,Option<&Disease>,Option<&Treatment>,Option<&Tranquilized>,Option<&Escaped>,Option<&Rampaging>,Option<&Dead>),With<Animal>>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiVisibleBinding,&mut Visibility)> |
Health/disease/treatment node values and visibility only | Relevant G16 component changed. |
project_guest_status |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(&GuestPhase,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,&GuestEntertainment,&GuestSatisfaction,&GuestEducation,&VisitTime),With<Guest>>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Guest need/satisfaction/education/visit node values only | Relevant G17 component changed. |
project_staff_status |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(&StaffRole,&Employment,Option<&StaffAssignment>,Option<&CurrentJob>),With<Staff>>, Query<(&StaffJob,Option<&JobProgress>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Role/wage/assignment/current-job node values only | Relevant G20 component changed. |
project_facility_status |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&Price>,Option<&ServiceFacility>,Option<&Inventory>,Option<&Condition>,Option<&WasteContainer>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
G18 price/capacity/inventory and G36 condition/waste values only | Relevant facility fact changed; no generic component dispatch. |
project_sanitation_status |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&Litter>,Option<&HabitatWaste>)>, Res<ZooCleanliness>, Query<(&UiValueBinding,&mut UiValue)> |
Loose-waste amount and zoo cleanliness values only | Relevant G36 fact changed. |
project_habitat_status |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(&HabitatRegion,&HabitatSummary,Option<&TankGeometry>,Option<&WaterQuality>,Option<&TankCapacity>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Habitat area/biome/breach and tank quality/capacity nodes only | Relevant G09/G25 fact changed. |
project_show_status |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(&ShowStage,&ShowSchedule,&ShowState)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Schedule/capacity/current-state nodes only | Relevant G26 fact changed. |
project_transport_status |
Update / GameSet::Ui |
Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&TransportCircuit>,Option<&TransportStation>,Option<&TransportVehicle>,Option<&WaitingForTransport>,Option<&TransportRider>,Option<&TourScore>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Circuit/station/vehicle/trip node values only | Relevant G27 fact changed. |
project_scenario_status |
Update / GameSet::Ui |
Res<ActiveScenario>, Res<ZooClock>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(&Objective,&ObjectiveStatus,&ObjectiveProgress,Option<&ObjectiveDeadline>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Objective/status/progress/deadline nodes only | Relevant G24 fact or clock display boundary changed. |
project_progression_status |
Update / GameSet::Ui |
Res<Fame>, Res<ZooRating>, Res<UnlockSet>, Query<(&InfoPanel,&InheritedVisibility)>, Query<(Option<&ResearchProject>,Option<&AwardEarned>)>, Query<(&UiValueBinding,&mut UiValue)>, Query<(&UiTextBinding,&mut Text)> |
Fame/rating/research/award node values only | Relevant G23 fact changed. |
apply_catalogue_commands |
Update / GameSet::Intent |
MessageReader<CatalogueCommand>, Query<(&mut CataloguePanel,&mut CatalogueFilter)>, MessageWriter<PurchaseChoice>, MessageWriter<ShowUiRole> |
UI-node state/messages | In menu/in game where panel exists. |
project_catalogue_page |
Update / GameSet::Ui |
Res<Assets<WorldDefinitionsAsset>>, Res<ZooCash>, Res<UnlockSet>, Query<(&CataloguePanel,&CatalogueFilter),Or<(Changed<CataloguePanel>,Changed<CatalogueFilter>)>>, Query<(&UiNodeId,&mut Text,&mut Visibility)> |
Row bindings/visibility | O(page size), no catalogue cloning/sorting. |
project_zoopedia_page |
Update / GameSet::Ui |
Res<Assets<WorldDefinitionsAsset>>, Res<Assets<LocalizationAsset>>, Query<&ZoopediaPage,Changed<ZoopediaPage>>, Query<(&UiTextBinding,&mut Text)>, Query<(&UiImageBinding,&mut ImageNode)>, Query<(&UiVisibleBinding,&mut Visibility)> |
Text/media handles/visibility | No runtime markup parsing. |
5. Preflight producer contract
Not applicable: engine consumes only native assets produced before launch. Search normalization, indexes, UI commands, and Zoopedia markup are compiled by A02/A09/A14; this package never reads XML/Z2F or files.
6. Behavioral evidence and disposition
Search shipped UI/, entity definition, lang and Zoopedia source families;
analysis TSVs for select/info/buy/catalogue/Zoopedia commands; live-observe panel
tabs, filters, ordering and missing facts. Mine d5657e5a only for evidence in
features/interaction/entity_info.rs, UI binding/lists/zoopedia/finance, and
catalog records.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle original category/order/filter rules, page sizes, definition-to-panel selection, visible live fields/formatting, Zoopedia navigation, and purchase command semantics. Recover from A02/A14 source/oracle/live UI. Do not guess UI fields or revive generic property projection.
7. Required behavior
- Selection always references one live inspectable entity or none and works for
mouse/controller
ActionSource; removal clears it once. - Static data remains mapped; live status reads the real component directly. Hidden panels cause no projection work.
- Catalogues retain preflight deterministic ordering, filter with native indexes,
page into reusable rows, and emit stable
AssetIdchoices. - Unlock/affordability are read directly from G23/G18. Zoopedia uses precompiled fixed content commands; mods can add records without runtime XML.
- UI state may persist as options if G22 specifies; it never becomes game state.
8. Performance contract
Mapped lookup/traversal and handle clone allocate zero. Projection is event/
changed/visibility driven; catalogue work O(page size), info work O(visible
fields). Rows and text buffers are reused; no per-frame String, sorting,
reflection maps, asset clones, filesystem, or dynamic node rebuild. Allocator
tests cover settled hidden/visible panels and page navigation. GPU bytes remain
owned by A05/A02 handles, not duplicated here.
9. Acceptance criteria
Deliver owned Rust and exact exports. Tests cover valid/invalid selection, single change event, direct live fact projection, deterministic filtering/ paging, unlock/affordability, and stable purchase choice. Observable done: players inspect entities, browse/buy from source-driven catalogues, and navigate Zoopedia with authentic information. No bag, shadow UI model, guessed panel, or runtime source parser.
10. Required handoff
Report files/API, root registrations/wiring, dependencies, direct symbols, evidence, temporary seams, allocator/page/GPU/UI checks.
G22 — Persistence
1. Identity and outcome
This package delivers native player profiles/options and fast save/load of the
actual ECS facts. A save is a versioned consumer-shaped snapshot keyed by stable
entity IDs and AssetId; loading replaces the current persistent entities and
repairs relationships. It never parses original saves at runtime and never
retains a mirror world. Original save/map import belongs to A19 preflight.
Integration wave: 4. Prerequisites: G04–G21 and G23–G36 as direct component consumers. G01–G03 and G21 own shell/input/UI state and have no world-snapshot section.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/persistence/; all else
read-only. Root owns Cargo features/dependencies, module/plugin/schedule and
reflection/type registration. The worker delivers the complete concrete section
pipeline through G36 now; integration must not add a registration facade or a
later catch-all serializer. Worker may not edit source components to make them
serializable.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
PersistencePlugin |
Plugin |
unit struct | Registers request/I/O/apply pipeline only. |
SaveSlotId |
value type | #[repr(transparent)] pub struct SaveSlotId(pub u32) |
Platform storage slot key; not a filesystem path. |
SaveRequest |
Message |
pub struct SaveRequest { pub slot: SaveSlotId } |
Player/autosave request. |
LoadRequest |
Message |
pub struct LoadRequest { pub slot: SaveSlotId } |
Requests native snapshot load. |
DeleteSaveRequest |
Message |
pub struct DeleteSaveRequest { pub slot: SaveSlotId } |
Explicit player request to remove one native slot. |
SaveCompleted |
Message |
pub struct SaveCompleted { pub slot: SaveSlotId, pub bytes: u64 } |
Emitted after durable atomic replacement succeeds. |
LoadCompleted |
Message |
pub struct LoadCompleted { pub slot: SaveSlotId } |
Emitted after entity spawn, relationship repair, and validation. |
SaveDeleted |
Message |
pub struct SaveDeleted { pub slot: SaveSlotId } |
Emitted only after the slot is durably absent. |
DeleteGeneratedImage |
Message |
pub struct DeleteGeneratedImage { pub photo: Entity, pub image: Handle<Image> } |
G28 requests deletion of one profile-owned generated image; G22 performs the explicit platform-storage operation without exposing filesystem access to gameplay. |
PersistenceFailed |
Message |
pub struct PersistenceFailed { pub operation: PersistenceOperation, pub slot: SaveSlotId, pub reason: PersistenceFailure } |
Typed failure; current world remains valid. |
PersistenceOperation |
enum | Save, Load, Delete |
Fixed operation vocabulary. |
PersistenceFailure |
enum | Missing, Io, InvalidHeader, UnsupportedVersion, WrongProfile, CorruptSection, UnknownAsset, DuplicateEntityId, BrokenReference, CapacityExceeded |
No source-format fallback. |
SnapshotHeader |
POD struct | #[repr(C)] pub struct SnapshotHeader { pub magic: [u8; 8], pub version: u32, pub profile_id: AssetId, pub entity_count: u32, pub section_count: u32, pub payload_bytes: u64, pub checksum: [u8; 32] } |
Little-endian native save header validated before apply. |
SnapshotSection |
enum | World, Terrain, Topology, Placement, Maintenance, Animals, Welfare, Behavior, Locomotion, Feeding, Health, Guests, Economy, Donations, Staff, Progression, Scenario, Aquatic, Shows, Transport, Photos, ExtinctAnimals, GesturesPuzzles, SimulationTime, Environment |
Exact deterministic section order; no dynamic registration or opaque section names. |
SnapshotScratch |
Resource |
pub struct SnapshotScratch { pub bytes: Vec<u8>, pub saved_ids: Vec<PersistentId>, pub entity_map: Vec<(PersistentId, Entity)>, pub validated: bool } |
Before mutation it holds only snapshot bytes plus sorted saved IDs; no staged World, entities, components, or asset copies. validated is set only after every semantic validator succeeds. |
ProfileRecord |
value | pub struct ProfileRecord { pub id: AssetId, pub display_name: String, pub last_played_unix_ms: u64 } |
Native OpenZT2 profile metadata; bounded UTF-8 player text is loaded only during shell/profile mutation. |
ProfileIndex |
Resource |
pub struct ProfileIndex { pub entries: Vec<ProfileRecord>, pub selected: Option<AssetId> } |
Sole bounded installed-profile catalogue; sorted by stable ID and replaced atomically after explicit profile I/O. |
ProfileOptions |
Resource |
pub struct ProfileOptions { pub profile_id: AssetId, pub locale: AssetId, pub controller_enabled: bool } |
Genuine global user/profile policy only. A11 AudioMixSettings is the sole live volume authority and is serialized directly; fast preload is startup/mapping policy owned by A01/G35, not persistence state. |
LoadProfileIndex |
Message |
pub struct LoadProfileIndex; |
Boot/profile-screen request for native profile metadata. |
CreateProfile |
Message |
pub struct CreateProfile { pub display_name: String } |
Creates one OpenZT2 profile after bounded UTF-8/name uniqueness validation. |
SelectProfile |
Message |
pub struct SelectProfile { pub profile: AssetId } |
Selects one existing stable profile identity and loads its options/mix state. |
DeleteProfile |
Message |
pub struct DeleteProfile { pub profile: AssetId } |
Explicit removal request; active profile and owned saves require evidenced confirmation policy. |
ProfileIndexReady |
Message |
pub struct ProfileIndexReady; |
G01 may enumerate ProfileIndex after this fact. |
ProfileCreated |
Message |
pub struct ProfileCreated { pub profile: AssetId } |
Emitted after durable metadata/options creation. |
ProfileSelected |
Message |
pub struct ProfileSelected { pub profile: AssetId } |
Emitted after ProfileOptions, A11 audio, G03 bindings, and G34 display/graphics settings are loaded. |
ProfileDeleted |
Message |
pub struct ProfileDeleted { pub profile: AssetId } |
Emitted after profile metadata and policy-approved owned data are durably absent. |
ProfileOperationFailed |
Message |
pub struct ProfileOperationFailed { pub profile: Option<AssetId>, pub reason: ProfileFailure } |
Typed shell-visible failure with no partial selected state. |
ProfileFailure |
enum | Io, CorruptIndex, InvalidName, DuplicateName, Missing, Active, HasSaves, CapacityExceeded |
Fixed native-profile failure vocabulary. |
G22 consumes G04's naturally owned PersistentId: G04 assigns the nonzero,
world-unique stable identity at authoritative spawn, and G22 persists it. Every
snapshot-eligible entity already carries PersistentId and WorldMember
before its owning domain acknowledges creation, exactly as G04 requires.
Snapshot sections directly encode concrete G04–G20 and G23–G33 components; there is no public
serializer trait, registration facade, generic property bag, DynamicScene, or
retained World copy. Platform slot I/O is a narrow root-provided storage path
or Bevy task, not a public gameplay service graph.
Profile identity is one stable AssetId everywhere; G01 selection and G04
world-load requests already use that exact type. A UI row index is presentation
only and is resolved to the stable ID before any request is emitted.
Profile-index/create/select/delete systems are integration wave 2 so G01 has a
real profile source; full world snapshot systems remain wave 4.
4. ECS ownership
The snapshot exists only during an explicit save/load operation. Serialization
systems query concrete component tuples per domain into deterministic sections;
load systems spawn concrete bundles then repair PersistentId references.
G18 transaction-operation entities and every domain's Pending*Payment/
Pending*Transaction component are transient within one fixed-step economy to
cleanup chain. begin_save runs only after that chain is drained, so these are
neither snapshot records nor ambiguous resumable transfers.
The following are separate Bevy systems. The writer chain is serial only during an explicit save so section bytes remain canonical; its queries stay narrow and do not force unrelated gameplay systems or normal frames into one access set.
Save section systems
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
begin_save |
Update / GameSet::Intent |
MessageReader<SaveRequest>, Res<ProfileOptions>, ResMut<SnapshotScratch> |
Clears logical scratch and writes header placeholder | Explicit request; no operation active. |
write_world_section |
Update / GameSet::Intent |
Query<(&WorldRoot,&WorldBounds)>, ResMut<SnapshotScratch> |
The sole world-root record; root identity is structural and is not an entity-reference ID | After begin; exactly one root. |
write_terrain_section |
Update / GameSet::Intent |
Query<(&PersistentId,&TerrainChunk,&EditedTerrainSamples)>, ResMut<SnapshotScratch> |
Edited terrain overlays only | After world. |
write_topology_section |
Update / GameSet::Intent |
Query<(&PersistentId,&TopologyNode,Option<&Gate>)>, Query<(&PersistentId,&FenceEdge)>, Query<(&PersistentId,&PathTile,Option<&PathSupport>)>, Query<(&PersistentId,&Portal)>, ResMut<SnapshotScratch> |
Topology records and saved-ID links | After terrain. |
write_placement_section |
Update / GameSet::Intent |
Query<(&PersistentId,&Placeable,&Transform,&FootprintOccupancy,Option<&AuthoredEntrance>)>, ResMut<SnapshotScratch> |
Durable placed-object facts | After topology. |
write_maintenance_section |
Update / GameSet::Intent |
Query<(&PersistentId,Option<&Condition>,Option<&WasteContainer>,Option<&WasteProduction>),Or<(With<Condition>,With<WasteContainer>,With<WasteProduction>)>>, Query<(&PersistentId,&DefinitionId,&Transform,Option<&Litter>,Option<&HabitatWaste>),Or<(With<Litter>,With<HabitatWaste>)>>, ResMut<SnapshotScratch> |
G36 object facts, animal production deadlines, and persistent loose-waste entities | After placement; records sorted by PersistentId. |
write_animal_section |
Update / GameSet::Intent |
Query<(&PersistentId,&SpeciesHandle,&AnimalVariant,&AnimalSex,&AnimalLifeStage,&Age,Option<&Pregnancy>,Option<&Parents>,&ReproductionRng)>, ResMut<SnapshotScratch> |
Animal/lifecycle records and saved-ID links | After maintenance. |
write_welfare_section |
Update / GameSet::Intent |
Query<(&PersistentId,&Hunger,&Thirst,&RestNeed,&PrivacyNeed,&SocialNeed,&ExerciseNeed,&StimulationNeed,&EnvironmentNeed,&KeeperCareNeed,&HealthNeed,&HabitatSuitability,&AnimalWelfare,&WelfareBand),With<Animal>>, ResMut<SnapshotScratch> |
Welfare records | After animals. |
write_behavior_section |
Update / GameSet::Intent |
Query<(&PersistentId,&BehaviorHandle,Option<&ActiveTask>,&BehaviorRng)>, ResMut<SnapshotScratch> |
Active behavior facts, optional target saved ID, and RNG | After welfare. |
write_locomotion_section |
Update / GameSet::Intent |
Query<(&PersistentId,&NavAgent,&Velocity,Option<&Destination>,Option<&Route>,Option<&Docking>)>, ResMut<SnapshotScratch> |
In-progress locomotion and docking links; derived Steering, SpatialCell, and SpatialGrid excluded |
After behavior. |
write_feeding_section |
Update / GameSet::Intent |
Query<(&PersistentId,&FoodContainer)>, Query<(&PersistentId,&DrinkContainer)>, Query<(&PersistentId,&FeedingTask)>, Query<(&PersistentId,&DrinkingTask)>, Query<(&PersistentId,&ConsumptionReservation)>, Query<(&PersistentId,&RefillPending)>, ResMut<SnapshotScratch> |
Inventories and active feeding relations | After locomotion. |
write_health_section |
Update / GameSet::Intent |
Query<(&PersistentId,&Vitality,&DiseaseRng,Option<&Disease>,Option<&Treatment>,Option<&Tranquilized>,Option<&Escaped>,Option<&Rampaging>,Option<&Dead>)>, ResMut<SnapshotScratch> |
Health facts, RNG, and treatment provider saved IDs | After feeding. |
write_guest_section |
Update / GameSet::Intent |
Res<GuestArrivalState>, Query<(&PersistentId,&GuestArchetype,&GuestRng,&GuestPhase,&GuestHunger,&GuestThirst,&GuestEnergy,&GuestRestroom,&GuestComfort,&GuestSatisfaction,&GuestEducation,&VisitTime,Option<&Viewing>,Option<&GuestDestination>,&GuestMemories),With<Guest>>, ResMut<SnapshotScratch> |
Arrival policy cursor/RNG plus guest facts and saved-ID links | After health. |
write_economy_section |
Update / GameSet::Intent |
Res<ZooCash>, Res<AdmissionPrice>, Query<(&PersistentId,&Wallet)>, Query<(&PersistentId,&Price)>, Query<(&PersistentId,&ServiceFacility)>, Query<(&PersistentId,&Inventory)>, Query<(&PersistentId,&ServiceReservation)>, Query<(&PersistentId,&ServiceProgress)>, ResMut<SnapshotScratch> |
Zoo/entity economy, stock and service facts/links | After guests. |
write_donation_section |
Update / GameSet::Intent |
Query<(&PersistentId,&DonationAcceptor)>, Query<(&PersistentId,&DonationTotal)>, Query<(&PersistentId,&DonationRng)>, Query<(&PersistentId,&DonationCandidate)>, Query<(&PersistentId,&DonationProgress)>, ResMut<SnapshotScratch> |
Donation totals, RNG, and proven in-progress facts | After economy. |
write_staff_section |
Update / GameSet::Intent |
Query<(&PersistentId,&StaffRole,&Employment,Option<&StaffAssignment>,Option<&AvailableForWork>,Option<&CurrentJob>),With<Staff>>, Query<(&PersistentId,&StaffJob,Option<&JobClaim>,Option<&JobProgress>)>, ResMut<SnapshotScratch> |
Staff/job facts and saved-ID links | After donations. |
write_progression_section |
Update / GameSet::Intent |
Res<Fame>, Res<ZooRating>, Res<UnlockSet>, Query<(&PersistentId,&ResearchProject,Option<&ResearchPaused>)>, Query<(&PersistentId,&AwardEarned)>, Query<(&PersistentId,&AwardConditionProgress)>, ResMut<SnapshotScratch> |
Progression facts | After staff. |
write_scenario_section |
Update / GameSet::Intent |
Option<Res<ActiveScenario>>, Option<Res<CampaignProgress>>, Res<ChallengeOfferSchedule>, Query<(&PersistentId,&Objective,&ObjectiveStatus,&ObjectiveProgress,Option<&ObjectiveDeadline>)>, Query<(&PersistentId,&ScenarioRuleNode,&RuleSatisfied,Option<&RuleProgress>)>, Query<(&PersistentId,&TutorialStep)>, Query<(&PersistentId,&ChallengeOffer)>, ResMut<SnapshotScratch> |
Optional primary/campaign state, deterministic challenge schedule, offer state, objectives/rules and links | After progression. |
write_aquatic_section |
Update / GameSet::Intent |
Query<(&PersistentId,&Tank,&TankGeometry,&WaterQuality,&TankCapacity)>, Query<(&PersistentId,&AquaticAnimal,&AquaticHome),With<Animal>>, ResMut<SnapshotScratch> |
Aquatic facts and home links | After scenario. |
write_show_section |
Update / GameSet::Intent |
Query<(&PersistentId,&ShowStage,&ShowSchedule,&ShowState)>, Query<(&PersistentId,&TrickTraining,Option<&TrainingProgress>)>, Query<(&PersistentId,&Performing),With<Animal>>, Query<(&PersistentId,&WatchingShow),With<Guest>>, ResMut<SnapshotScratch> |
Show facts and participant links | After aquatic. |
write_transport_section |
Update / GameSet::Intent |
Query<(&PersistentId,&TransportCircuit)>, Query<(&PersistentId,&CircuitMember,Option<&TransportStation>,Option<&TrackSegment>,Option<&TransportVehicle>,Option<&RoutePosition>,Option<&StationDestination>)>, Query<(&PersistentId,Option<&WaitingForTransport>,Option<&TransportRider>,Option<&TourScore>),With<Guest>>, Query<(&PersistentId,&TourViewable)>, ResMut<SnapshotScratch> |
Transport graph/live-trip facts and links | After shows. |
write_photo_section |
Update / GameSet::Intent |
Query<(&PersistentId,&Photo,&PhotoSubjects,&AlbumMember)>, Query<(&PersistentId,&PhotoAlbum)>, ResMut<SnapshotScratch> |
Photo metadata and generated-image storage IDs, never pixels | After transport. |
write_extinct_section |
Update / GameSet::Intent |
Res<FossilCollection>, Query<(&PersistentId,&FossilSite,&FossilRng)>, Query<(&PersistentId,&FossilPiece)>, Query<(&PersistentId,&AssemblyTable,&FossilAssembly)>, Query<(&PersistentId,&CloneLab,&CloneRng,Option<&CloneProcess>)>, ResMut<SnapshotScratch> |
Fossil/cloning facts and RNG | After photos. |
write_puzzle_section |
Update / GameSet::Intent |
Query<(&PersistentId,&Puzzle)>, ResMut<SnapshotScratch> |
Durable puzzle progress; active pointer GestureStroke is excluded |
After extinct animals. |
write_simulation_time_section |
Update / GameSet::Intent |
Res<ZooClock>, Res<ZooCalendar>, Res<SimulationControl>, Res<ZooSeed>, ResMut<SnapshotScratch> |
Exact clock/calendar/control/seed | After puzzles. |
write_environment_section |
Update / GameSet::Intent |
Query<(&PersistentId,&WorldEnvironment,&Daylight,&Weather,Option<&WeatherTransition>,&Wind,&EnvironmentRandom)>, Query<(&PersistentId,&AmbientSpawner)>, ResMut<SnapshotScratch> |
Weather and deterministic spawner continuation; presentation markers and ambient animals excluded | Last section. |
finish_save |
Update / GameSet::Intent |
Res<ProfileOptions>, ResMut<SnapshotScratch>, Query<(Entity,&mut SlotIoTask)>, Commands, MessageWriter<SaveCompleted>, MessageWriter<PersistenceFailed> |
Finalizes header/checksum, starts or polls one atomic-replacement task, then clears logical scratch and despawns task | After environment; explicit I/O exception. |
Load, spawn, repair, and profile systems
The two private asynchronous operation components named below are fixed rather
than left as a storage abstraction: ProfileIoTask(Task<io::Result<ProfileIoCompletion>>)
and SlotIoTask(Task<io::Result<SlotIoCompletion>>). Their private completion
enums contain only the exact owned request payload and native bytes/records
needed by the matching completion system. They live on one operation entity,
are polled only by the named completion system, and are despawned after one
success or failure. They are not a public storage service or retained runtime.
Every entity-producing row after restore_world_snapshot_records also inserts G04
WorldMember { root } and appends (PersistentId, Entity) to the pre-reserved
sorted map in SnapshotScratch. Every repair row performs allocation-free
binary lookup in that map. All mandatory and optional reference policy was
already proven against saved_ids before despawn; repair cannot discover an
ordinary data failure after mutation. Derived G09
habitat/index facts and G21 UI selection are rebuilt by their natural systems.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
load_profile_index |
Update / GameSet::Intent |
MessageReader<LoadProfileIndex>, Query<(),With<ProfileIoTask>>, Commands, MessageWriter<ProfileOperationFailed> |
Spawns one bounded native profile-index read task | Integration wave 2; explicit request and no existing task. |
complete_profile_index_load |
Update / GameSet::Intent |
Query<(Entity,&mut ProfileIoTask)>, ResMut<ProfileIndex>, Commands, MessageWriter<ProfileIndexReady>, MessageWriter<ProfileOperationFailed> |
Atomically replaces sorted profile catalogue and despawns task | Ready task only; no partial index. |
create_profiles |
Update / GameSet::Intent |
MessageReader<CreateProfile>, Res<ProfileIndex>, Query<(),With<ProfileIoTask>>, Commands, MessageWriter<ProfileCreated>, MessageWriter<ProfileOperationFailed> |
Spawns one create task; its completion atomically creates stable ID/default options/default A11 mix | Explicit request; bounded unique name. |
select_profile |
Update / GameSet::Intent |
MessageReader<SelectProfile>, Res<ProfileIndex>, Query<(),With<ProfileIoTask>>, ResMut<ProfileOptions>, ResMut<AudioMixSettings>, ResMut<InputBindings>, ResMut<DisplaySettings>, ResMut<GraphicsSettings>, Commands, MessageWriter<ProfileSelected>, MessageWriter<ProfileOperationFailed> |
On ready task, atomically replaces selected ID and every natural live settings owner | Existing profile only; after index ready. |
delete_profiles |
Update / GameSet::Intent |
MessageReader<DeleteProfile>, Res<ProfileIndex>, Query<(),With<ProfileIoTask>>, Commands, MessageWriter<ProfileDeleted>, MessageWriter<ProfileOperationFailed> |
Spawns one delete task and atomically removes allowed profile metadata/data on completion | Existing inactive profile and evidenced save policy. |
begin_load |
Update / GameSet::Intent |
MessageReader<LoadRequest>, Res<ProfileOptions>, ResMut<SnapshotScratch>, Query<(),With<SlotIoTask>>, Commands, MessageWriter<PersistenceFailed> |
Spawns bounded slot read; on ready completion validates header/checksum/profile/section ranges and resets validated=false |
Explicit request; current world untouched. |
prevalidate_snapshot_ids |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, MessageWriter<PersistenceFailed> |
Traverses immutable section ranges in SnapshotScratch.bytes, builds sorted saved_ids, and rejects zero/duplicate IDs, wrong counts, overflow, or out-of-range records |
After structural validation; no ECS mutation. |
prevalidate_world_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> |
Validates the world section's single root, scenario/map IDs, bounds, and root invariants | No ECS mutation. |
prevalidate_terrain_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<TerrainAsset>>, MessageWriter<PersistenceFailed> |
Validates terrain chunk IDs, coordinates, sample ranges, ownership and saved IDs | After world validator. |
prevalidate_topology_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates topology record kinds, IDs, cells, state and saved-ID relations | After terrain validator. |
prevalidate_placement_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<ScenePrefabAsset>>, MessageWriter<PersistenceFailed> |
Validates placement definitions, transforms, occupancy and saved IDs | After topology validator. |
prevalidate_maintenance_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates maintenance bounds, definitions, transforms, deadlines, section coverage and saved IDs | After placement validator; no ECS mutation. |
prevalidate_animal_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, MessageWriter<PersistenceFailed> |
Validates animal identity/lifecycle/RNG/lineage and saved IDs | After maintenance validator. |
prevalidate_welfare_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, MessageWriter<PersistenceFailed> |
Validates animal coverage, G12 bounds, suitability, welfare and band consistency | After animal validator. |
prevalidate_behavior_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>>, MessageWriter<PersistenceFailed> |
Validates behavior indexes, task policy, RNG and saved IDs | After welfare validator. |
prevalidate_locomotion_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<NavigationAsset>>, MessageWriter<PersistenceFailed> |
Validates locomotion values, route capacity/cursor, docking and saved IDs | After behavior validator. |
prevalidate_feeding_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates feeding contents, bounds, exclusivity and saved-ID links | After locomotion validator. |
prevalidate_health_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates health bounds/states/causes/RNG and saved-ID provider links | After feeding validator. |
prevalidate_guest_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates arrival policy/next tick/RNG plus guest archetype/phase/fixed fields/memories/RNG and saved IDs | After health validator. |
prevalidate_economy_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates economy money, available + reserved <= capacity, reservation-unit consistency, service capacity/price/progress and saved-ID links |
After guest validator. |
prevalidate_donation_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates donation totals/progress/RNG and saved-ID links | After economy validator. |
prevalidate_staff_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates staff roles/wages/jobs/assignments/progress and reciprocal saved IDs | After donation validator. |
prevalidate_progression_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates fame/rating/unlock/research/award bounds and uniqueness | After staff validator. |
prevalidate_scenario_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> |
Validates optional primary/campaign state, challenge schedule/RNG/offer state, scenario-scoped objective/rule/status/deadline records and saved IDs | After progression validator. |
prevalidate_aquatic_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<TerrainAsset>>, MessageWriter<PersistenceFailed> |
Validates tank geometry/water/capacity/requirements and saved-ID home links | After scenario validator. |
prevalidate_show_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates show slots/state/tricks/proficiency and saved-ID participant links | After aquatic validator. |
prevalidate_transport_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<NavigationAsset>>, MessageWriter<PersistenceFailed> |
Validates transport graph/capacity/route values and saved-ID trip links | After show validator. |
prevalidate_photo_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> |
Validates photo score/subject/image/album records and saved IDs | After transport validator. |
prevalidate_extinct_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates extinct bitsets/IDs/counts/ticks/RNG and saved IDs | After photo validator. |
prevalidate_puzzle_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>>, MessageWriter<PersistenceFailed> |
Validates puzzle definition/step/state and rejects transient samples | After extinct validator. |
prevalidate_time_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<PersistenceFailed> |
Validates tick/day/calendar/speed/pause/seed consistency | After puzzle validator. |
prevalidate_environment_records |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<WorldScenarioAsset>>, MessageWriter<PersistenceFailed> |
Validates environment IDs/transitions/wind/spawner/RNG and exclusions | Last semantic validator. |
seal_snapshot_validation |
Update / GameSet::Intent |
ResMut<SnapshotScratch> |
Sets validated=true only if every prior validator left the operation intact |
No ECS mutation; a failure clears scratch and leaves current world intact. |
despawn_loaded_world |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Query<(Entity,&WorldMember)>, Query<Entity,With<WorldRoot>> |
Removes all old world members, then old root | Requires scratch.validated; shell/profile entities remain. |
restore_world_snapshot_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>> |
The sole WorldRoot and WorldBounds; installs a fresh G04 allocator for that root |
After despawn; uses only the already-validated world record and creates no persistent-ID map entry for the root. |
restore_persistent_id_cursor |
Update / GameSet::Intent |
Res<SnapshotScratch>, ResMut<PersistentIdAllocator>, Query<Entity,With<WorldRoot>> |
Calls G04 reserve_imported over the already sorted, duplicate-validated saved_ids, leaving next above the greatest imported ID |
After world root and before any entity spawn; no allocation or new validation. |
spawn_terrain_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<TerrainAsset>> |
PersistentId, TerrainChunk, EditedTerrainSamples |
After world. |
spawn_topology_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> |
PersistentId, TopologyNode, FenceEdge, PathTile, Gate, Portal, PathSupport with unresolved saved IDs |
After terrain. |
spawn_placement_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<ScenePrefabAsset>> |
PersistentId, Placeable, Transform, FootprintOccupancy, optional AuthoredEntrance |
After topology. |
spawn_maintenance_object_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> |
Inserts Condition/WasteContainer on mapped placed entities and spawns saved Litter/HabitatWaste entities with PersistentId, DefinitionId, Transform, and WorldMember |
After placement; all records already validated. |
spawn_animal_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>> |
G11 animal/lifecycle components with unresolved lineage IDs | After maintenance object records. |
restore_animal_waste_schedules |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>> |
Inserts saved G36 WasteProduction on mapped animal entities |
After animals; no loose-waste spawn or first-time validation. |
spawn_welfare_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch> |
Inserts all saved G12 need and welfare components on mapped animals | After animals. |
spawn_behavior_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>> |
BehaviorHandle, ActiveTask, BehaviorRng with unresolved target IDs |
After welfare. |
spawn_locomotion_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<NavigationAsset>> |
NavAgent, Velocity, Destination, Route, Docking with unresolved target IDs |
After behavior. |
spawn_feeding_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>> |
G15 container/task/reservation/refill components with unresolved entity IDs | After locomotion. |
spawn_health_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> |
G16 health components and DiseaseRng, with unresolved provider IDs |
After feeding. |
spawn_guest_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> |
Restores GuestArrivalState and G17 guest bundle with unresolved view/destination/memory entity IDs |
After health. |
spawn_economy_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooCash>, ResMut<AdmissionPrice> |
G18 wallet/price/facility/inventory/service components with unresolved facility IDs | After guests. |
spawn_donation_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> |
G19 components and DonationRng with unresolved entity IDs |
After economy. |
spawn_staff_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> |
G20 staff/job entities with unresolved assignment/claim IDs | After donations. |
spawn_progression_records |
Update / GameSet::Intent |
Commands, Res<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, ResMut<Fame>, ResMut<ZooRating>, ResMut<UnlockSet> |
G23 resources and research/award entities | After staff. |
spawn_scenario_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>> |
Restores optional G24 primary/campaign resources, ChallengeOfferSchedule, and scenario/offer entities with unresolved objective IDs |
After progression. |
spawn_aquatic_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<TerrainAsset>> |
G25 components with unresolved home IDs | After scenario. |
spawn_show_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>> |
G26 components with unresolved participant/stage IDs | After aquatic. |
spawn_transport_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<NavigationAsset>> |
G27 graph/trip components with unresolved IDs | After shows. |
spawn_photo_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldScenarioAsset>>, ResMut<Assets<Image>> |
G28 album/photo components and handles with unresolved subject/member IDs | After transport. |
spawn_extinct_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, ResMut<FossilCollection> |
G29 site/piece/assembly/lab/process components and RNG | After photos. |
spawn_puzzle_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<BehaviorProgramAsset>> |
PersistentId, Puzzle |
After extinct animals. |
restore_simulation_time |
Update / GameSet::Intent |
Res<SnapshotScratch>, ResMut<ZooClock>, ResMut<ZooCalendar>, ResMut<SimulationControl>, ResMut<ZooSeed> |
Exact saved G32 resources | After puzzles. |
spawn_environment_records |
Update / GameSet::Intent |
Commands, ResMut<SnapshotScratch>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<WorldScenarioAsset>> |
Saved G33 environment/spawner components with RNG; no presentation markers or ambient animals | After simulation time. |
repair_topology_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut FenceEdge>, Query<&mut Portal>, Query<&mut PathSupport> |
Resolves topology links | After all spawns. |
repair_parent_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut Pregnancy>, Query<&mut Parents> |
Resolves lineage links; deleted optional ancestors become None |
After topology repair. |
repair_behavior_targets |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut ActiveTask> |
Resolves optional task targets | After parent repair. |
repair_locomotion_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut Docking> |
Resolves docking targets | After behavior repair. |
repair_feeding_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut FeedingTask>, Query<&mut DrinkingTask>, Query<&mut ConsumptionReservation> |
Resolves source/consumer links | After locomotion repair. |
repair_treatment_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut Treatment> |
Resolves provider links | After feeding repair. |
repair_guest_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut Viewing>, Query<&mut GuestDestination>, Query<&mut GuestMemories> |
Resolves guest subject/destination/memory links | After treatment repair. |
repair_service_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut ServiceReservation>, Query<&mut ServiceProgress> |
Resolves facility links | After guest repair. |
repair_donation_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut DonationAcceptor>, Query<&mut DonationCandidate>, Query<&mut DonationProgress> |
Resolves beneficiary/acceptor/subject links | After service repair. |
repair_staff_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut StaffAssignment>, Query<&mut StaffJob>, Query<&mut JobClaim>, Query<&mut CurrentJob> |
Resolves area/target/staff/job links | After donation repair. |
repair_scenario_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut ScenarioRuleNode> |
Resolves objective links | After staff repair. |
repair_aquatic_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut AquaticHome> |
Resolves tank-home links | After scenario repair. |
repair_show_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut ShowSchedule>, Query<&mut TrickTraining>, Query<&mut TrainingProgress>, Query<&mut Performing>, Query<&mut WatchingShow> |
Resolves show participant/stage links | After aquatic repair. |
repair_transport_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut CircuitMember>, Query<&mut TrackSegment>, Query<&mut RoutePosition>, Query<&mut StationDestination>, Query<&mut WaitingForTransport>, Query<&mut TransportRider> |
Resolves circuit graph/trip links | After show repair. |
repair_photo_links |
Update / GameSet::Intent |
Res<SnapshotScratch>, Query<&mut PhotoSubjects>, Query<&mut AlbumMember> |
Resolves photo subject/album links | Last repair. |
finish_load |
Update / GameSet::Intent |
ResMut<SnapshotScratch>, MessageWriter<LoadCompleted> |
Clears validated scratch and emits completion | After deterministic spawn/repair and natural derived-index rebuilds; performs no first-time semantic validation. |
delete_save |
Update / GameSet::Intent |
MessageReader<DeleteSaveRequest>, Query<(Entity,&mut SlotIoTask)>, Commands, MessageWriter<SaveDeleted>, MessageWriter<PersistenceFailed> |
Starts or polls one atomic slot-deletion task | Explicit request; no world mutation. |
persist_profile_options |
Update / GameSet::Ui |
Res<ProfileOptions>, Res<AudioMixSettings>, Res<InputBindings>, Res<DisplaySettings>, Res<GraphicsSettings>, Query<(Entity,&mut ProfileIoTask)>, Commands, MessageWriter<ProfileOperationFailed> |
Starts or polls one atomic write containing values read from natural live owners | Any listed resource is_changed() and no active profile task; G22 retains no settings mirror. |
5. Preflight producer contract
Not applicable to gameplay. Engine consumes native assets. Original Zoo Tycoon 2 save/map import is exclusively A19 preflight. This package may perform only explicit OpenZT2 profile/save slot I/O; it must not discover packs, read Z2F/XML, merge mods, transcode, or interpret original saves.
6. Behavioral evidence and disposition
Use live original save/load/autosave behavior and shipped save examples only to
identify persisted semantics. Search analysis/oracle for saveable components,
entity reference and calendar/scenario state. Mine commit d5657e5a only for
field/evidence leads in domain/src/persistence/save.rs and
features/interaction/save.rs; never reuse its aggregate snapshots, generic
records, or source parser. A19 owns original conversion.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle exact save relevance, pause/autosave timing, in-progress action resume, deleted-reference policy, thumbnails/metadata, and profile option semantics. Clock/calendar/seed and every domain-local RNG state are mandatory concrete coverage; G33 weather/spawner continuation is saved while nonsavable ambient animals are recreated from those facts.
7. Required behavior
- Save iteration order is G04
PersistentId; sections/records are deterministic. Snapshot checksum/version/profile are verified before world mutation. - Every structural and semantic failure is detected before
despawn_loaded_world; failed load leaves the current world untouched. Successful load produces one live ECS world with direct components and Bevy asset handles reconstructed fromAssetId, not copied asset payloads. - All entity relationships serialize as
PersistentId, repair once, and reject broken mandatory references. Transient message/UI/render caches do not save. - Save writes are atomic (temporary file, sync as platform contract requires, replace). Delete/recovery policies are evidence-driven.
- OpenZT2 improvement: large 64-bit saves and instant native load; no compatibility promise for original binary saves inside engine.
8. Performance contract
Ordinary settled frames allocate zero and perform zero filesystem operations. Explicit save/load is exceptional: scratch buffers are reused, bounded by validated header/capacity, and allocation count/bytes are measured separately. Serialization is O(saved components + references); ID map is sorted once during the explicit operation and binary searched or built into pre-reserved storage. No retained mirror after completion. Bench thresholds on Jarvis: warm save and load must report exact heap allocations/bytes, wall time, bytes copied, and zero asset payload copies; mapped pack pages remain separately accounted. No GPU resources except optional thumbnail staging owned/reused by G28/presentation.
9. Acceptance criteria
Deliver exact exports and concrete focused section pipeline under owned path.
Tests cover deterministic bytes, checksum/version/profile rejection, duplicate
IDs, broken references, failed-load atomicity, exact relationship repair,
round-trip live facts, and empty scratch afterward. No DynamicScene, generic
bag, serializer trait facade, retained world, source parser, stub, or fake
success. Observable done: save, mutate zoo, load, and instantly recover the same
live world and options.
10. Required handoff
Report exact component coverage by section, files/API, Cargo needs, root registrations, symbols consumed, evidence, temporary integration seams, and allocation/I/O/page/gameplay benchmarks. No later generic section extension or registration mechanism is permitted.
G23 — Progression
1. Identity and outcome
This package delivers fame, zoo rating, awards, research, and unlocks as authoritative global progression facts plus per-project entities. Player actions change ratings, unlock native catalogue records, advance one or more authored research projects, and earn awards with visible feedback.
Integration wave: 4. Prerequisites: A03 species indexes, A14 definitions, G18–G22, G32 time, and G36 cleanliness facts.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/progression/; all else
read-only. Root owns declarations, plugin/schedule/reflection wiring,
dependencies, and persistence section integration.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ProgressionPlugin |
Plugin |
unit struct | Registers focused progression systems. |
Fame |
Resource |
pub struct Fame { pub half_stars: u8, pub maximum_reached: u8 } |
Half-star index; valid upper bound comes from A14, not hardcoded. Maximum never decreases. |
ZooRating |
Resource |
pub struct ZooRating { pub animal_welfare_permille: u16, pub guest_satisfaction_permille: u16, pub education_permille: u16, pub variety_permille: u16, pub scenery_permille: u16, pub finance_permille: u16, pub cleanliness_permille: u16, pub overall_permille: u16 } |
One exact field per A14 RatingInputKind; all 0..=1000, no float rating. |
RatingScratch |
Resource |
pub struct RatingScratch { pub species_words: Vec<u64> } |
Pre-sized reusable variety bitset only; cleared after changed-population evaluation and never authoritative. |
UnlockSet |
Resource |
pub struct UnlockSet { pub words: Vec<u64>, pub definition_count: u32 } |
Bounded bitset sized once to A14 catalogue; index space is A14 stable definition index. |
ResearchProject |
Component |
pub struct ResearchProject { pub definition: AssetId, pub elapsed_ticks: u64, pub required_ticks: u64 } |
One active project entity; positive duration is preflight-compiled G32 ticks. |
ResearchPaused |
Component |
marker | Project does not advance. |
PendingResearchPayment |
Component |
pub struct PendingResearchPayment { pub definition: AssetId } |
Domain-owned operation; a project does not exist before its matching payment completion. |
AwardEarned |
Component |
pub struct AwardEarned { pub definition: AssetId, pub earned_tick: u64 } |
Dedicated award entity; one per non-repeatable award. |
AwardConditionProgress |
Component |
pub struct AwardConditionProgress { pub award: AssetId, pub condition_index: u32, pub satisfied_ticks: u64 } |
Dedicated bounded condition entity only when A14 duration is nonzero; exact tick continuation persisted by G22. |
ProgressionFactChanged |
Message |
pub enum ProgressionFactChanged { Fame, Rating, Unlock(AssetId), Research(AssetId), Award(AssetId) } |
UI/scenario notification after actual mutation. |
StartResearchRequest |
Message |
pub struct StartResearchRequest { pub definition: AssetId } |
Validates prerequisites, availability, and cost. |
UnlockRequest |
Message |
pub struct UnlockRequest { pub operation: Entity, pub definition: AssetId } |
Typed scenario/system request with caller-owned correlation. |
UnlockApplied |
Message |
pub struct UnlockApplied { pub operation: Entity, pub definition: AssetId } |
Echoes operation once after bit mutation or idempotent already-unlocked acceptance. |
UnlockRejected |
Message |
pub struct UnlockRejected { pub operation: Entity, pub definition: AssetId } |
Echoes operation once; unlock set unchanged. |
ResearchCompleted |
Message |
pub struct ResearchCompleted { pub project: Entity, pub definition: AssetId } |
Emitted once after unlock effects commit. |
AwardGranted |
Message |
pub struct AwardGranted { pub award: Entity, pub definition: AssetId } |
Emitted once per earned award entity. |
is_unlocked |
function | pub fn is_unlocked(set: &UnlockSet, index: u32) -> bool |
O(1), allocation-free, returns false out of range. |
Root requests A14 stable definition-index lookup and compiled rating/fame/ research/award rules; direct G18 transactions, G17 guest outcomes, G11/G12 animal facts, and G22 persistence. Never copy catalogues or world summaries.
4. ECS ownership
Fame/rating/unlocks are genuinely zoo-global bounded facts. Projects/awards are
entities for lifecycle and save identity. UnlockSet allocates only at world
load/profile initialization and never grows afterward.
The private AwardCandidate { definition: AssetId } message is emitted by the
seven independent evaluators. One materializer owns uniqueness and entity
creation, so those evaluators remain parallel and never contend on the
PersistentIdAllocator.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
recompute_animal_welfare_rating |
FixedUpdate / FixedGameSet::Economy |
MessageReader<WelfareChanged>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldDefinitionsAsset>>, Query<&AnimalWelfare,(With<Animal>,Without<Dead>)>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
animal_welfare_permille only |
Relevant welfare/population change. |
recompute_guest_satisfaction_rating |
FixedUpdate / FixedGameSet::Economy |
MessageReader<GuestArrived>, MessageReader<GuestDeparted>, MessageReader<GuestReaction>, Res<Assets<WorldDefinitionsAsset>>, Query<&GuestSatisfaction,With<Guest>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
guest_satisfaction_permille only |
Relevant guest fact change. |
recompute_education_rating |
FixedUpdate / FixedGameSet::Economy |
MessageReader<GuestArrived>, MessageReader<GuestDeparted>, MessageReader<GuestReaction>, Res<Assets<WorldDefinitionsAsset>>, Query<&GuestEducation,With<Guest>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
education_permille only |
Relevant education/population change. |
recompute_variety_rating |
FixedUpdate / FixedGameSet::Economy |
MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<&SpeciesHandle,(With<Animal>,Without<Dead>)>, ResMut<RatingScratch>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
variety_permille only; scratch cleared |
Population change only; no allocation/sort. |
recompute_scenery_rating |
FixedUpdate / FixedGameSet::Economy |
MessageReader<ObjectPlaced>, RemovedComponents<Placeable>, Res<Assets<WorldDefinitionsAsset>>, Query<&Placeable>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
scenery_permille only |
Durable placement change. |
recompute_finance_rating |
FixedUpdate / FixedGameSet::Economy |
Res<ZooCash>, Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
finance_permille only |
ZooCash.is_changed(). |
recompute_cleanliness_rating |
FixedUpdate / FixedGameSet::Economy |
Res<ZooCleanliness>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
Copies the changed exact G36 permille into cleanliness_permille only |
resource_changed::<ZooCleanliness>; after G36 projection, no placed-object scan. |
recompute_overall_rating |
FixedUpdate / FixedGameSet::Economy |
Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooRating>, MessageWriter<ProgressionFactChanged> |
Reads seven input fields and writes overall_permille with checked integer rounding |
After seven input systems when ZooRating.is_changed(). |
update_fame |
FixedUpdate / FixedGameSet::Economy |
Res<Assets<WorldDefinitionsAsset>>, Res<ZooRating>, ResMut<Fame>, MessageWriter<ProgressionFactChanged> |
Current/max half-stars | After overall rating when ZooRating.is_changed(). |
request_research_payment |
FixedUpdate / FixedGameSet::Economy |
MessageReader<StartResearchRequest>, Res<Assets<WorldDefinitionsAsset>>, Res<UnlockSet>, Query<&ResearchProject>, Query<&PendingResearchPayment>, Query<(Entity,&WorldRoot)>, Commands, MessageWriter<TransactionRequest> |
Spawns PendingResearchPayment with root WorldMember and emits a transaction carrying it |
Valid request only; no project yet. |
complete_research_payment |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&PendingResearchPayment,&WorldMember)>, ResMut<PersistentIdAllocator>, Commands |
Validates/allocates first, then spawns ResearchProject with operation WorldMember and fresh PersistentId, and despawns operation |
TransactionCompleted.operation must equal the operation entity. |
reject_research_payment |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionRejected>, Query<(Entity,&PendingResearchPayment)>, Commands |
Despawns operation without project/unlock mutation | TransactionRejected.operation must equal the operation entity. |
advance_research |
FixedUpdate / FixedGameSet::Economy |
Res<ZooClock>, Query<(Entity,&mut ResearchProject),Without<ResearchPaused>>, MessageWriter<ResearchCompleted> |
Increments elapsed_ticks by one and completes at required_ticks |
Exactly one simulation tick; no float duration. |
apply_research_unlocks |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<ResearchCompleted>, Res<Assets<WorldDefinitionsAsset>>, ResMut<UnlockSet>, Query<(Entity,&ResearchProject)>, Commands, MessageWriter<ProgressionFactChanged> |
Bitset; despawns project | Atomic dependency closure; after completion. |
apply_unlock_requests |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<UnlockRequest>, Res<Assets<WorldDefinitionsAsset>>, ResMut<UnlockSet>, MessageWriter<UnlockApplied>, MessageWriter<UnlockRejected>, MessageWriter<ProgressionFactChanged> |
Exact target bit only | Deterministic request order; echoes every operation once. |
initialize_award_progress |
Update / GameSet::Intent |
MessageReader<WorldLoadFinished>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&WorldRoot)>, Query<&AwardConditionProgress>, ResMut<PersistentIdAllocator>, Commands |
For a new world, creates required duration-condition entities with root WorldMember and fresh PersistentId |
Missing definition/index pairs only; G22-restored progress is untouched. |
evaluate_animal_welfare_awards |
FixedUpdate / FixedGameSet::Think |
Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> |
Animal-welfare condition progress/candidates | ZooRating.is_changed(); exact A14 comparison/duration. |
evaluate_guest_satisfaction_awards |
FixedUpdate / FixedGameSet::Think |
Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> |
Guest-satisfaction condition progress/candidates | ZooRating.is_changed(); exact comparison/duration. |
evaluate_education_awards |
FixedUpdate / FixedGameSet::Think |
Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> |
Education condition progress/candidates | ZooRating.is_changed(); exact comparison/duration. |
evaluate_variety_awards |
FixedUpdate / FixedGameSet::Think |
Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> |
Variety condition progress/candidates | ZooRating.is_changed(); exact comparison/duration. |
evaluate_scenery_awards |
FixedUpdate / FixedGameSet::Think |
Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> |
Scenery condition progress/candidates | ZooRating.is_changed(); exact comparison/duration. |
evaluate_finance_awards |
FixedUpdate / FixedGameSet::Think |
Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> |
Finance condition progress/candidates | ZooRating.is_changed(); exact comparison/duration. |
evaluate_cleanliness_awards |
FixedUpdate / FixedGameSet::Think |
Res<ZooRating>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&mut AwardConditionProgress)>, MessageWriter<AwardCandidate>, MessageWriter<ProgressionFactChanged> |
Cleanliness condition progress/candidates | ZooRating.is_changed(); exact comparison/duration. |
grant_awards |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<AwardCandidate>, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<&AwardEarned>, Query<(Entity,&WorldRoot)>, ResMut<PersistentIdAllocator>, Commands, MessageWriter<AwardGranted>, MessageWriter<ProgressionFactChanged> |
Deduplicates candidates, then creates one award entity with root WorldMember and fresh PersistentId before acknowledgement |
After all seven evaluators; non-repeatable definition only. |
5. Preflight producer contract
Not applicable: only native assets are consumed. Preflight A14 compiles rules, stable indexes and dependency closure; no runtime XML/script/filesystem.
6. Behavioral evidence and disposition
Search shipped fame/rating/research/award/unlock definitions and UI; analysis
TSVs for ZT_PROCESS_FAME_UPDATE, ZT_SET_FAME_HALFSTARS,
ZT_RESEARCH_IS_UNLOCKED, ZT_RESEARCH_UNLOCK_ENTITY_KIND, and award commands;
live-observe thresholds/timing. Mine d5657e5a only for behavioral evidence in
progression/research modules and feedback records.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle rating inputs/formula/cadence, fame thresholds/max semantics, research cost/time/parallelism, prerequisite closure, unlock categories, repeatable awards and rewards. Use native records/oracle/live behavior, never guessed thresholds.
7. Required behavior
- Rating and fame update deterministically from live facts and native rules. Maximum fame is monotonic; current fame follows recovered behavior.
- Research validates prerequisites/cost, advances on simulation time, unlocks exact compiled targets, and emits completion once.
- Awards evaluate only affected rules, cannot duplicate non-repeatable awards, and apply rewards via their natural owner (G18/G23).
- All resources/components are save relevant. Mods add compiled records/indexes through A14 without runtime command strings.
8. Performance contract
Lookup is O(1) bit access and allocation-free. Rating/awards are changed/event- driven over compiled bounded rule ranges; research O(active projects). Bitset capacity is fixed before steady measurement. No scans of all entities per rule, strings, hash growth, payload clones, filesystem, or recurring allocations. Tests measure idle frame, unlock lookup, project tick, and affected award batch; no GPU ownership.
9. Acceptance criteria
Deliver exact exports. Tests cover bounds, monotonic max fame, threshold crossing, research validation/completion, unlock dependency closure, award uniqueness and persistence facts. Observable done: zoo actions alter rating/fame, research unlocks content, awards appear authentically. No guessed formulas, manager, shadow catalogue, or no-op unlock.
10. Required handoff
Report API/files, dependency/root wiring, A14 index contract, G22 sections, consumers, evidence, seams, and allocator/page/gameplay checks.
G24 — Scenarios, Campaigns, Tutorials, and Challenges
1. Identity and outcome
This package instantiates A19 scenario/objective/rule records into entities,
evaluates the fixed ScenarioRuleKind variants against live ECS facts, drives campaign/
challenge/tutorial transitions, and emits authentic win/loss/results. It does
not embed a scripting VM or duplicate the world into a scenario snapshot.
Integration wave: 4. Prerequisites: A19 world scenarios, G18 economy, G22 persistence, and G23 progression.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/scenario/; all else read-only.
Root owns declarations, plugin/schedule/state/reflection/persistence wiring,
dependencies, and alignment with A19's final archived rule enum.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ScenarioPlugin |
Plugin |
unit struct | Registers scenario lifecycle and focused evaluators. |
ActiveScenario |
Resource |
pub struct ActiveScenario { pub definition: AssetId, pub started_tick: u64, pub state: ScenarioState } |
One active scenario/campaign mission or none (resource absent); elapsed time is ZooClock.tick - started_tick. |
ScenarioState |
enum | Running, Won, Lost |
Terminal states never resume evaluation. |
Objective |
Component |
pub struct Objective { pub scenario: AssetId, pub record_index: u32 } |
Indexes one validated A19 objective within a primary scenario or concurrent challenge; text, ranges and root rule remain mapped. |
ScenarioRuleNode |
Component |
pub struct ScenarioRuleNode { pub objective: Entity, pub rule_index: u32 } |
One entity per reachable A19 ScenarioRule; composite children follow validated rule_links. |
RuleSatisfied |
Component |
pub struct RuleSatisfied(pub bool) |
Current rule result; leaf evaluator or composite aggregator for that exact A19 kind is sole writer. |
RuleProgress |
Component |
pub struct RuleProgress { pub current: i64, pub target: i64 } |
Display integers in A19's exact cents/half-star/permille/count/tick/score-milli unit. |
ObjectiveStatus |
Component + enum |
Inactive, Active, Satisfied, Failed |
Exactly one status; terminal according to definition. |
ObjectiveProgress |
Component |
pub struct ObjectiveProgress { pub current: i64, pub target: i64 } |
Display units defined by rule; target immutable during one objective unless authored. |
ObjectiveDeadline |
Component |
pub struct ObjectiveDeadline { pub end_tick: u64 } |
Absolute G32 zoo tick; absent means no deadline and integer comparison avoids drift. |
CampaignProgress |
Resource |
pub struct CampaignProgress { pub campaign: AssetId, pub completed_words: Vec<u64> } |
Bounded bitset sized once from A19 mission count. |
TutorialStep |
Component |
pub struct TutorialStep { pub definition: AssetId, pub step_index: u32 } |
Dedicated active tutorial objective entity; no UI script object. |
ChallengeState |
enum | Offered, Active |
Offer expires only in Offered; accepted challenge remains until its own terminal result. |
ChallengeOffer |
Component |
pub struct ChallengeOffer { pub definition: AssetId, pub expires_tick: u64, pub state: ChallengeState } |
Durable world entity for one offered/accepted A19 challenge policy. |
ChallengeOfferSchedule |
Resource |
pub struct ChallengeOfferSchedule { pub next_tick: u64, pub rng: DeterministicRng } |
One deterministic world-level offer cursor; policy selection is weighted over eligible A19 records and G22 persists it. |
PendingScenarioCashAction |
Component |
pub struct PendingScenarioCashAction { pub scenario: AssetId, pub action_index: u32 } |
Domain-owned G18 operation for one typed cash action; prevents duplicate application. |
PendingScenarioUnlockAction |
Component |
pub struct PendingScenarioUnlockAction { pub scenario: AssetId, pub action_index: u32, pub definition: AssetId } |
Correlates one A19 Unlock request to exact G23 acknowledgement. |
PendingScenarioSpawnAction |
Component |
pub struct PendingScenarioSpawnAction { pub scenario: AssetId, pub action_index: u32, pub prefab: AssetId, pub definition: AssetId } |
Correlates one A19 Spawn request to exact G04 spawn acknowledgement. |
PendingScenarioWeatherAction |
Component |
pub struct PendingScenarioWeatherAction { pub scenario: AssetId, pub action_index: u32, pub weather: AssetId } |
Correlates one A19 SetWeather request to exact G33 completion. |
PendingScenarioMessageAction |
Component |
pub struct PendingScenarioMessageAction { pub scenario: AssetId, pub action_index: u32, pub message: AssetId } |
Correlates one ShowMessage request to G02 acceptance/presentation acknowledgement. |
ScenarioTerminalPending |
Component |
pub struct ScenarioTerminalPending { pub scenario: AssetId, pub state: ScenarioState } |
Terminal intent entity; result is withheld until no pending typed action for this scenario remains. |
AcceptChallenge |
Message |
pub struct AcceptChallenge { pub offer: Entity } |
Player accepts a live eligible offer. |
ChallengeAccepted |
Message |
pub struct ChallengeAccepted { pub offer: Entity, pub definition: AssetId } |
Emitted after all challenge objective/rule entities have identity and the offer becomes active. |
ChallengeRejected |
Message |
pub struct ChallengeRejected { pub offer: Entity, pub reason: ChallengeRejection } |
Offer remains unchanged. |
ChallengeRejection |
enum | Missing, Expired, AlreadyActive, Ineligible, PersistentId(PersistentIdError) |
Closed acceptance failure vocabulary. |
ChallengeOfferGenerationFailed |
Message |
pub struct ChallengeOfferGenerationFailed { pub reason: PersistentIdError } |
Explicit failure to materialize an otherwise due offer. |
ScenarioResult |
Message |
pub struct ScenarioResult { pub scenario: AssetId, pub state: ScenarioState } |
Emitted exactly once after all terminal effects commit. |
ObjectiveChanged |
Message |
pub struct ObjectiveChanged { pub objective: Entity, pub status: ObjectiveStatus, pub current: i64, pub target: i64 } |
UI notification after actual changed facts. |
TutorialActionObserved |
Message |
pub struct TutorialActionObserved { pub action: GameAction, pub subject: Option<Entity> } |
Fixed action observation; never arbitrary command string. |
This package consumes A19 exactly as frozen: ObjectiveRecord.rule_index,
ScenarioRule { kind, subject, comparison, target, children }, the exact
ScenarioRuleKind enum, and every exact ScenarioAction variant. It compares
cents, half-star indexes, 0..=1000 permille, counts, ticks and score thousandths
without guessed conversion. There is no second rule enum/operand record,
expression language, callback registry, Lua VM, string dispatcher, or world
snapshot.
4. ECS ownership
The active scenario and bounded campaign bitset are genuinely global. Each objective/offer/tutorial step is an entity. Evaluators are split by fixed rule kind and query only relevant live components.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
instantiate_scenario |
Update / GameSet::Intent |
MessageReader<WorldLoadFinished>, Res<Assets<WorldScenarioAsset>>, Res<ZooClock>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<Objective>>, Commands |
For a new scenario only, creates active resource plus objective/reachable rule-node/tutorial entities, each with root WorldMember and fresh PersistentId |
No existing objective entities; G22-restored scenario facts are untouched. |
initialize_challenge_schedule |
Update / GameSet::Intent |
MessageReader<WorldLoadFinished>, Res<Assets<WorldScenarioAsset>>, Res<ZooClock>, Res<ZooSeed>, Option<Res<ChallengeOfferSchedule>>, Commands |
Inserts schedule at the earliest compiled first-offer tick with a G32 domain RNG | New world only; G22-restored schedule is untouched. |
generate_challenge_offers |
FixedUpdate / FixedGameSet::Think |
Res<ZooClock>, Res<Fame>, Res<UnlockSet>, Res<Assets<WorldScenarioAsset>>, ResMut<ChallengeOfferSchedule>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<ChallengeOffer>>, Commands, MessageWriter<ChallengeOfferGenerationFailed> |
At due tick selects one eligible weighted policy, then creates an Offered entity with root WorldMember and fresh PersistentId; advances schedule |
At most one offered/active challenge; map/prerequisite/fame eligibility and intervals are compiled A19 facts. |
accept_challenge_offers |
Update / GameSet::Intent |
MessageReader<AcceptChallenge>, Res<ZooClock>, Res<Assets<WorldScenarioAsset>>, ResMut<PersistentIdAllocator>, Query<(Entity,&mut ChallengeOffer,&WorldMember)>, Commands, MessageWriter<ChallengeAccepted>, MessageWriter<ChallengeRejected> |
Prevalidates and allocates all required IDs, creates objective/reachable rule-node entities with the offer's WorldMember, then changes offer to Active and acknowledges |
Exact offered entity, before expiry; no partial challenge on failure. |
evaluate_cash_rules |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldScenarioAsset>>, Res<ZooCash>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching CashCents rule facts only |
ZooCash.is_changed(); A19 cents comparison. |
evaluate_fame_rules |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldScenarioAsset>>, Res<Fame>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching FameHalfStars rule facts only |
Fame.is_changed(). |
evaluate_rating_rules |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldScenarioAsset>>, Res<ZooRating>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching ZooRatingPermille rule facts only |
ZooRating.is_changed(); exact permille. |
evaluate_species_count_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldScenarioAsset>>, Query<&SpeciesHandle,(With<Animal>,Without<Dead>)>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching SpeciesCount rule facts only |
Animal lifecycle change only. |
evaluate_animal_count_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldScenarioAsset>>, Query<(),(With<Animal>,Without<Dead>)>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching AnimalCount rule facts only |
Animal lifecycle change only. |
evaluate_guest_count_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<GuestArrived>, MessageReader<GuestDeparted>, Res<Assets<WorldScenarioAsset>>, Query<(),With<Guest>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching GuestCount rule facts only |
Guest lifecycle change only. |
evaluate_object_count_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<ObjectPlaced>, RemovedComponents<Placeable>, Res<Assets<WorldScenarioAsset>>, Query<&Placeable>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching ObjectCount rule facts only |
Durable placed-object change only. |
evaluate_research_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<ProgressionFactChanged>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<UnlockSet>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching ResearchComplete rule facts only |
Unlock/research change only. |
evaluate_elapsed_tick_rules |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldScenarioAsset>>, Res<ZooClock>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching ElapsedTicks rule facts only |
Due target tick only. |
evaluate_welfare_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<WelfareChanged>, MessageReader<AnimalAdopted>, MessageReader<AnimalBorn>, MessageReader<AnimalReleased>, MessageReader<AnimalDied>, Res<Assets<WorldScenarioAsset>>, Query<(&SpeciesHandle,&AnimalWelfare),(With<Animal>,Without<Dead>)>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching SpeciesWelfarePermille rule facts only |
Relevant welfare or population change only. |
evaluate_birth_count_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<AnimalBorn>, Res<Assets<WorldScenarioAsset>>, Query<&SpeciesHandle,With<Animal>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching AnimalBirthCount rule facts only |
Matching birth only. |
evaluate_adoption_count_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<AnimalAdopted>, Res<Assets<WorldScenarioAsset>>, Query<&SpeciesHandle,With<Animal>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching AnimalAdoptionCount rule facts only |
Matching adoption only. |
evaluate_release_count_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<AnimalReleased>, Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching AnimalReleaseCount rule facts only |
Matching release only. |
evaluate_successful_show_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<ShowCompleted>, Res<Assets<WorldScenarioAsset>>, Query<&ShowStage>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching SuccessfulShowCount rule facts only |
Matching successful show only. |
evaluate_photo_score_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<PhotoCaptured>, Res<Assets<WorldScenarioAsset>>, Query<&Photo>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching PhotoScoreMilli rule facts only |
Photo capture only. |
evaluate_photo_subject_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<PhotoCaptured>, Res<Assets<WorldScenarioAsset>>, Query<&PhotoSubjects>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching PhotoSubjectCount rule facts only |
Photo capture only. |
evaluate_photo_challenge_rules |
FixedUpdate / FixedGameSet::Think |
MessageReader<PhotoChallengeCompleted>, Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Matching PhotoChallengeComplete rule facts only |
Matching completed challenge only. |
aggregate_rule_nodes |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldScenarioAsset>>, Query<(Entity,&ScenarioRuleNode,&mut RuleSatisfied,&mut RuleProgress)> |
Composite All/Any/Not rule facts |
Leaves first, then reverse topological rule order. |
aggregate_objectives |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioRuleNode,&RuleSatisfied,Option<&RuleProgress>)>, Query<(Entity,&Objective,&mut ObjectiveStatus,&mut ObjectiveProgress)>, MessageWriter<ObjectiveChanged> |
Objective root status/progress | After leaf/composite evaluation; prerequisites honored. |
expire_deadlines_and_challenges |
FixedUpdate / FixedGameSet::Think |
Res<ZooClock>, Option<Res<ActiveScenario>>, Query<(Entity,&ObjectiveDeadline,&mut ObjectiveStatus,&mut ObjectiveProgress)>, Query<(Entity,&ChallengeOffer)>, Commands, MessageWriter<ObjectiveChanged> |
Failed objectives and only ChallengeState::Offered offers whose response tick expired |
Running primary objectives when present, plus offers in freeform/challenge worlds; integer tick comparison. |
advance_tutorial_steps |
Update / GameSet::Intent |
MessageReader<TutorialActionObserved>, Res<Assets<WorldScenarioAsset>>, Query<(Entity,&mut TutorialStep,&mut ObjectiveStatus,&mut ObjectiveProgress)>, Commands, MessageWriter<ObjectiveChanged> |
Step index/objective status | Action-driven. |
begin_scenario_resolution |
FixedUpdate / FixedGameSet::Cleanup |
Option<Res<ActiveScenario>>, Res<Assets<WorldScenarioAsset>>, Query<(&Objective,&ObjectiveStatus)>, Query<&ChallengeOffer>, Query<&ScenarioTerminalPending>, Query<(Entity,&WorldRoot)>, Commands |
For each primary/active-challenge scenario whose own objectives became terminal, spawns one transient ScenarioTerminalPending carrying root WorldMember |
Once per scenario after aggregate/deadlines; statuses from another scenario never participate. |
request_scenario_cash_actions |
FixedUpdate / FixedGameSet::Cleanup |
Res<Assets<WorldScenarioAsset>>, Query<(Entity,&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioCashAction>, Commands, MessageWriter<TransactionRequest> |
Spawns a world-scoped PendingScenarioCashAction and emits transaction carrying it |
Each A19 GrantCashCents action index at most once. |
complete_scenario_cash_actions |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, Query<(Entity,&PendingScenarioCashAction)>, Commands |
Despawns the exact applied action operation | TransactionCompleted.operation must equal the operation entity. |
reject_scenario_cash_actions |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionRejected>, Res<Assets<WorldScenarioAsset>>, Query<(Entity,&PendingScenarioCashAction)>, Commands |
Fails/retries only by authored policy and despawns operation; never marks success | TransactionRejected.operation must equal the operation entity. |
apply_scenario_admission_actions |
FixedUpdate / FixedGameSet::Cleanup |
Res<Assets<WorldScenarioAsset>>, Query<&ScenarioTerminalPending>, ResMut<AdmissionPrice> |
Admission price only | Each A19 SetAdmissionCents action index once; preflight rejects duplicates whose order would be ambiguous. |
request_scenario_unlock_actions |
FixedUpdate / FixedGameSet::Cleanup |
Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioUnlockAction>, Commands, MessageWriter<UnlockRequest> |
Spawns world-scoped PendingScenarioUnlockAction and sends its entity as UnlockRequest.operation |
Each Unlock action index once. |
acknowledge_scenario_unlocks |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<UnlockApplied>, MessageReader<UnlockRejected>, Query<(Entity,&PendingScenarioUnlockAction)>, Commands |
Despawns the exact acknowledged action operation | Acknowledgement operation must equal the operation entity. |
request_scenario_spawn_actions |
FixedUpdate / FixedGameSet::Cleanup |
Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<(Entity,&WorldRoot)>, Query<&PendingScenarioSpawnAction>, Commands, MessageWriter<SpawnScenarioObject> |
Spawns world-scoped PendingScenarioSpawnAction and sends its entity as SpawnScenarioObject.operation |
Each Spawn action index once. |
acknowledge_scenario_spawns |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<ScenarioObjectSpawned>, MessageReader<ScenarioObjectSpawnRejected>, Query<(Entity,&PendingScenarioSpawnAction)>, Commands |
Despawns the exact acknowledged action operation | Acknowledgement operation must equal the operation entity. |
request_scenario_weather_actions |
FixedUpdate / FixedGameSet::Cleanup |
Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioWeatherAction>, Commands, MessageWriter<SetWeather> |
Spawns world-scoped PendingScenarioWeatherAction and sends its entity as SetWeather.operation |
Each SetWeather action index once. |
acknowledge_scenario_weather |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<WeatherRequestApplied>, MessageReader<WeatherRequestRejected>, Query<(Entity,&PendingScenarioWeatherAction)>, Commands |
Despawns the exact acknowledged action operation | Acknowledgement operation must equal the operation entity. |
request_scenario_message_actions |
Update / GameSet::Ui |
Res<Assets<WorldScenarioAsset>>, Query<(&ScenarioTerminalPending,&WorldMember)>, Query<&PendingScenarioMessageAction>, Commands, MessageWriter<ShowNotification> |
Spawns world-scoped PendingScenarioMessageAction and sends its entity as ShowNotification.operation |
Each ShowMessage action index once. |
acknowledge_scenario_messages |
Update / GameSet::Ui |
MessageReader<NotificationPresented>, MessageReader<NotificationRejected>, Query<(Entity,&PendingScenarioMessageAction)>, Commands |
Despawns the exact acknowledged action operation | Acknowledgement operation must equal the operation entity. |
queue_scenario_terminal_actions |
FixedUpdate / FixedGameSet::Cleanup |
Res<Assets<WorldScenarioAsset>>, Query<&mut ScenarioTerminalPending> |
Updates terminal intent from mapped A19 Complete/Fail actions only |
Each action index once; contradictory actions rejected preflight. |
finalize_scenario_result |
FixedUpdate / FixedGameSet::Cleanup |
Option<ResMut<ActiveScenario>>, Option<ResMut<CampaignProgress>>, ResMut<ChallengeOfferSchedule>, Query<(Entity,&ScenarioTerminalPending)>, Query<(Entity,&ChallengeOffer)>, Query<&PendingScenarioCashAction>, Query<&PendingScenarioUnlockAction>, Query<&PendingScenarioSpawnAction>, Query<&PendingScenarioWeatherAction>, Query<&PendingScenarioMessageAction>, Commands, MessageWriter<ScenarioResult> |
Commits primary state/campaign bit or removes the matching active challenge and schedules its next offer; emits result and despawns terminal operation | Only when no pending action component carries the same scenario. |
5. Preflight producer contract
Not applicable: engine consumes only native A19 records. Preflight compiles original rule trees/scripts into the fixed exhaustive rule vocabulary, resolves source precedence and dependencies, and rejects unsupported commands. No XML, Lua, Z2F, filesystem, or original save parsing here.
6. Behavioral evidence and disposition
Search shipped campaign/scenario/challenge/tutorial XML/script/data families;
analysis TSVs for ZT_POPULATE_SCENARIO_UI, scenario selection, starting cash,
objective/challenge/tutorial types; live-observe mission setup, timers, results,
rewards, failure and tutorial gating. Mine d5657e5a only for behavior/evidence
in scenario, triggers, script-manager and UI modules—not architecture.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle every shipped rule/operand/aggregation, activation dependency, timer, repeatability, challenge generation/expiry, campaign unlock, reward and result presentation. Unsupported source commands block producer acceptance rather than becoming runtime strings or no-op success.
7. Required behavior
- Scenario starting facts are applied once through their natural owners. Objectives activate/evaluate/fail/satisfy in authored dependency order.
- Condition evaluators use current live facts and typed events; no mirrored counts. Deadlines use G32 integer ticks and authentic pause behavior.
- Win/loss is emitted once, rewards apply once, campaign bits persist, and load resumes nonterminal state exactly.
- Tutorials gate/advance on fixed action/domain facts. Challenges retain offer, accept, expiry and result lifecycle.
- Mods extend only A19's supported fixed rule/command vocabulary. Unknown rules are preflight errors, never ignored.
8. Performance contract
Settled systems allocate zero. Evaluators run only affected objective ranges using precompiled kind indexes: O(affected objectives), not rules × world. Campaign bitset capacity fixed at load. No strings, dynamic AST/VM, reflection, hash growth, filesystem, or asset clones. Allocation tests cover idle scenario, one affected condition/objective and terminal resolution; mapped traversal zero; no GPU ownership.
9. Acceptance criteria
Deliver exact API and exhaustive typed evaluators under owned path. Tests cover activation, each A19 rule/action variant, All/Any/Not aggregation, deadlines/pause, terminal uniqueness, reward uniqueness, challenge expiry, tutorial action, campaign persistence, unknown rule rejection at A19 boundary. Observable done: shipped scenarios/campaigns/ challenges/tutorials run to authentic results. No VM, script manager, snapshot, stub, or guessed objective.
10. Required handoff
Report covered A19 rules/actions, files/API, dependencies/root wiring, G22 sections, direct domain events, evidence gaps, seams, and allocator/page/gameplay checks.
G25 — Aquatic Systems
1. Identity and outcome
This package delivers tanks and marine animal care as real terrain/topology and animal facts: bounded water volumes, floor/wall manipulation, fill/drain, marine placement, capacity/depth suitability, containment, and aquatic care. The same ECS world drives rendering, navigation, welfare, and persistence.
Integration wave: 5. Prerequisites: A14–A18, G05 terrain, G09 habitat, G10 placement, G11–G16 animal systems.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/aquatic/; all else read-only.
Root owns declarations, plugin/schedule/reflection/persistence registration,
dependencies, and cross-package symbol alignment.
3. Frozen public contract
World distances/heights are finite meters; volumes are cubic meters; normalized
quality is 0.0..=1.0. All authored rates/limits come from native definitions.
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
AquaticPlugin |
Plugin |
unit struct | Registers tank/marine systems. |
Tank |
Component |
pub struct Tank { pub definition: AssetId } |
Marker plus immutable tank definition on habitat entity. |
TankGeometry |
Component |
pub struct TankGeometry { pub floor_height: f32, pub wall_height: f32, pub water_height: f32, pub area: f32, pub volume: f32 } |
Finite geometry; floor <= water <= wall; area/volume nonnegative and derived from G09 boundary. |
WaterQuality |
Component |
pub struct WaterQuality(pub u16) |
Current A14 tank-water quality permille, 0..=1000. |
TankCapacity |
Component |
pub struct TankCapacity { pub used: f32, pub required: f32 } |
Derived cubic-meter requirement for current marine population. |
AquaticAnimal |
Component |
pub struct AquaticAnimal { pub minimum_depth: f32, pub initial_space: f32, pub additional_space: f32 } |
Native species requirements; finite nonnegative values. |
AquaticHome |
Component |
pub struct AquaticHome(pub Entity) |
References one live tank habitat. |
TankOperation |
enum | RaiseFloor, LowerFloor, RaiseWall, LowerWall, Fill, Drain |
Fixed player tool vocabulary. |
TankEditRequest |
Message |
pub struct TankEditRequest { pub tank: Entity, pub operation: TankOperation, pub elapsed_ticks: u32 } |
Coalesced continuous-tool simulation ticks; authored rate is preflight-compiled and application is integer-tick deterministic. |
TankChanged |
Message |
pub struct TankChanged { pub tank: Entity } |
Emitted once after valid geometry/water mutation. |
MarinePlacementRejected |
Message |
pub struct MarinePlacementRejected { pub animal_definition: AssetId, pub tank: Entity, pub reason: MarinePlacementFailure } |
Typed rejection without spawn/payment completion. |
MarinePlacementFailure |
enum | NotTank, InsufficientDepth, InsufficientSpace, IncompatibleWater, InvalidGeometry |
Fixed validated reasons. |
Root requests direct G09 habitat boundary/membership and containment; G05 terrain/water/collision dirty-region facts; G07 transaction grouping; G11 animal spawn/species; G12 welfare; G14 aquatic navigation; A14/A16 definitions; and A18 water effects. Do not duplicate meshes, terrain, habitat membership, or animal state.
4. ECS ownership
Tank geometry lives on the habitat/tank entity; marine requirements/home live on the animal. There is no tank manager or separate water world.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
apply_tank_edits |
FixedUpdate / FixedGameSet::Act |
MessageReader<TankEditRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&Tank,&mut TankGeometry)>, Query<&EditTransaction>, MessageWriter<TankChanged> |
Geometry/water values | Valid continuous request; clamps/rejects by authored limits. |
recompute_tank_geometry |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<HabitatChanged>, MessageReader<TerrainChanged>, Res<Assets<WorldDefinitionsAsset>>, Query<(&Tank,&HabitatRegion,&mut TankGeometry)> |
Area/volume and dependent heights | Dirty tanks only; after terrain/topology. |
update_tank_capacity |
FixedUpdate / FixedGameSet::Think |
MessageReader<ContainmentChanged>, Query<(&AquaticAnimal,&AquaticHome),With<Animal>>, Query<&mut TankCapacity,With<Tank>> |
Used/required capacity | Changed membership only; residents are grouped by AquaticHome, never found by a nested world scan. |
update_aquatic_suitability |
FixedUpdate / FixedGameSet::Think |
Query<(&TankGeometry,&WaterQuality,&TankCapacity),Or<(Changed<TankGeometry>,Changed<WaterQuality>,Changed<TankCapacity>)>>, Query<(&AquaticAnimal,&AquaticHome,&mut HabitatSuitability),With<Animal>> |
G12 HabitatSuitability only |
Dirty tank and residents grouped by AquaticHome. |
decay_water_quality |
FixedUpdate / FixedGameSet::Think |
MessageReader<ZooDayAdvanced>, Res<Assets<WorldDefinitionsAsset>>, Query<(&TankCapacity,&mut WaterQuality),With<Tank>> |
Quality permille | Exactly once per emitted zoo-day event; no float elapsed accumulation. |
invalidate_broken_tanks |
FixedUpdate / FixedGameSet::Cleanup |
RemovedComponents<Tank>, RemovedComponents<HabitatRegion>, Query<(Entity,&AquaticHome),With<Animal>>, Commands, MessageWriter<ContainmentChanged> |
Clears AquaticHome and emits exact containment invalidation; G16 consumes that fact |
Before tank despawn. |
5. Preflight producer contract
Not applicable: engine consumes native definitions/chunks. Source tank XML, materials, entity records and commands are compiled by A14–A18; no runtime filesystem/source formats.
6. Behavioral evidence and disposition
Search shipped Marine Mania tank manager/manipulation UI, tank wall/gate/
support, marine species and water definitions. Search analysis TSVs for
ZTTankMgr, ZTTankCommandReader, ZT_SETTANKMODE, floor/wall/fill/drain
commands, and tank material tokens. Observe live manipulation speeds, limits,
pricing and animal response. Mine d5657e5a only for evidence in
domain/src/simulation/tank.rs, features/simulation/tank.rs, expansion and
terrain modules.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle water/floor offsets, minimum/show depth, dynamic manipulation speeds, space formula (initial plus additional population), wall material states, fill/drain cost, water-quality decay/care and invalid-tank consequences. Values come from native records/oracle/live behavior only.
7. Required behavior
- Tank edits maintain geometry invariants and participate in G07 undo/redo as one transaction. Collision/render dirty regions update once after commit.
- G11's adoption validator reads G25
Tank,TankGeometry,TankCapacity, and the A03 marine requirement record directly and emits G25's typedMarinePlacementRejectedbefore payment/spawn. G25 must not intercept and forwardAdoptAnimal. Population changes recompute the authored capacity formula. - Tank suitability directly affects G12 welfare; invalid containment produces authentic G16 consequences. Removal cleans animal/show/topology links.
- Mods extend compiled tank/species records, never string commands or XML.
- Geometry, quality, capacity and home are save relevant.
8. Performance contract
Steady systems allocate zero. Dirty-tank work is O(changed boundary cells + indexed residents), not tanks × animals. Fill/edit O(1) per request. GPU water/ mesh staging uses G05/A16/A18 reusable buffers with zero recurring allocations; this plugin owns no copied payload. Tests separately count heap, terrain mapping page faults, and changed GPU bytes.
9. Acceptance criteria
Deliver exact exports. Tests cover geometry invariants, native rates/limits, capacity formula, placement failures, dirty-only suitability, undo/redo facts, and relationship cleanup. Observable done: build/edit/fill/drain a tank, place marine animals, observe correct suitability/care. No tank manager, guessed offset, duplicate mesh/water world, or runtime parser.
10. Required handoff
Report files/API, root/dependency/schedule/reflection/G22 wiring, consumed symbols, evidence, seams, and allocator/page/GPU/gameplay checks.
G26 — Animal Shows
1. Identity and outcome
This package delivers show stages, editable schedules, performer/trick compatibility, training, execution, audience participation, and results as ordinary entities/components. Animals train and perform authored animations at real stages; guests watch and react; revenue/donations use G18/G19.
Integration wave: 5. Prerequisites: A06 animation, A10 behavior, A14 definitions, G11–G18. G25 supplies aquatic show-stage suitability where applicable.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/shows/; all else read-only.
Root owns registrations, schedule/reflection/persistence wiring, dependencies,
and cross-package alignment.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ShowsPlugin |
Plugin |
unit struct | Registers schedule/training/performance systems. |
ShowStage |
Component |
pub struct ShowStage { pub definition: AssetId } |
Real placed stage entity. |
ShowSchedule |
Component |
pub struct ShowSchedule { pub slots: Vec<ShowSlot>, pub capacity: u16 } |
Allocated once to native stage capacity; len <= capacity, no steady growth beyond reserved capacity. |
ShowSlot |
value struct | pub struct ShowSlot { pub performer: Entity, pub trick: AssetId, pub duration_ticks: u32 } |
Live performer and compatible trick; positive duration is preflight-compiled G32 ticks. |
ShowState |
Component + enum |
Idle, Preparing { remaining_ticks: u32 }, Running { slot: u16, elapsed_ticks: u32 }, Cooldown { remaining_ticks: u32 } |
Exactly one deterministic G32-tick stage lifecycle state. |
TrickTraining |
Component |
pub struct TrickTraining { pub performer: Entity, pub trick: AssetId, pub proficiency: f32 } |
Dedicated relation entity; proficiency finite 0..=1. |
TrainingProgress |
Component |
pub struct TrainingProgress { pub trainer: Entity, pub remaining_ticks: u32 } |
Temporary active training on relation entity; preflight-compiled G32 ticks. |
Performing |
Component |
pub struct Performing { pub stage: Entity, pub trick: AssetId } |
Lives on animal during current slot; excludes ordinary behavior. |
WatchingShow |
Component |
pub struct WatchingShow { pub stage: Entity } |
Lives on guest after seating/view validation. |
PendingShowTransaction |
Component |
pub struct PendingShowTransaction { pub stage: Entity, pub guest: Option<Entity>, pub outcome: AssetId } |
Domain-owned G18 operation for one evidenced show charge/reward. |
ShowEdit |
Message |
pub enum ShowEdit { Insert { stage: Entity, index: u16, performer: Entity, trick: AssetId }, Remove { stage: Entity, index: u16 }, Move { stage: Entity, from: u16, to: u16 }, Clear { stage: Entity } } |
Typed mixer edit; validates then mutates preallocated schedule. |
StartShowRequest |
Message |
pub struct StartShowRequest { pub stage: Entity } |
Starts eligible nonempty schedule. |
TrainingRequest |
Message |
pub struct TrainingRequest { pub performer: Entity, pub trick: AssetId, pub trainer: Entity } |
Staff/player request after compatibility validation. |
ShowCompleted |
Message |
pub struct ShowCompleted { pub stage: Entity, pub audience: u32, pub score_milli: i32 } |
Emitted once after final effects in exact A14 fixed score units. |
Root requests direct G11 animal/species, G13 behavior exclusion, G14 docking, G17 reactions, G18 transactions, G19 donation category, G20 training job, G25 tank/stage compatibility, A06 animation handles, and A14 show/trick/stage records. No show manager, animation copy, audience list, or UI mixer model.
4. ECS ownership
Schedules/states live on stage; training is a relation entity; performance and watching live on participants. Variable schedule capacity is native-derived and reserved once.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
apply_show_edits |
Update / GameSet::Intent |
MessageReader<ShowEdit>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut ShowSchedule,&ShowState),With<ShowStage>>, Query<(&SpeciesHandle,&AnimalLifeStage),With<Animal>>, Query<&TrickTraining> |
Preallocated schedule slots | Idle only unless evidence allows; rejects invalid edit. |
begin_show |
FixedUpdate / FixedGameSet::Act |
MessageReader<StartShowRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&ShowStage,&ShowSchedule,&mut ShowState)>, Query<(&SpeciesHandle,Option<&Performing>),With<Animal>>, Query<&TrickTraining>, MessageWriter<NavigateTo> |
Preparing state and participant navigation targets | Eligible stage only. |
advance_show_state |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Query<(Entity,&ShowSchedule,&mut ShowState),With<ShowStage>>, Query<(Entity,Option<&Performing>),With<Animal>>, MessageReader<Arrived>, MessageReader<AnimationMarker>, Commands |
Increments/decrements exact show-state tick fields and Performing |
One simulation tick after navigation/animation sampling. |
acquire_show_audience |
FixedUpdate / FixedGameSet::Think |
Res<SpatialGrid>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&GlobalTransform,&ShowSchedule,&ShowState),With<ShowStage>>, Query<(Entity,&GlobalTransform,Option<&WatchingShow>),With<Guest>>, Commands |
Inserts/removes WatchingShow |
No retained audience vector. |
apply_show_reactions |
FixedUpdate / FixedGameSet::Act |
Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&WatchingShow),With<Guest>>, Query<(&ShowSchedule,&ShowState),With<ShowStage>>, Query<&TrickTraining>, MessageWriter<GuestReaction> |
Emits reaction facts | At authored markers/end, not every frame. |
finish_show |
FixedUpdate / FixedGameSet::Cleanup |
Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&ShowSchedule,&mut ShowState),With<ShowStage>>, Query<(Entity,&WatchingShow),With<Guest>>, Query<(Entity,&Performing),With<Animal>>, Commands, MessageWriter<DonationRequest>, MessageWriter<ShowCompleted> |
Clears participant facts; idle/cooldown; emits nonfinancial outcomes | Exactly once when the final slot reaches its authored end marker. |
request_show_transactions |
FixedUpdate / FixedGameSet::Economy |
MessageReader<ShowCompleted>, Res<Assets<WorldDefinitionsAsset>>, Query<&WorldMember,With<ShowStage>>, Query<&PendingShowTransaction>, Commands, MessageWriter<TransactionRequest> |
Spawns one PendingShowTransaction carrying the stage's WorldMember and emits request carrying it |
One operation per authored financial outcome. |
settle_show_transactions |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, MessageReader<TransactionRejected>, Query<(Entity,&PendingShowTransaction)>, Commands, MessageWriter<GuestReaction> |
Applies only matching authored success/failure feedback and despawns operation | Result operation must equal the operation entity; rejection is never success. |
begin_training |
FixedUpdate / FixedGameSet::Act |
MessageReader<TrainingRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(&SpeciesHandle,&AnimalLifeStage,&WorldMember),With<Animal>>, Query<&StaffRole,With<Staff>>, Query<(Entity,&TrickTraining,Option<&TrainingProgress>)>, ResMut<PersistentIdAllocator>, Commands |
Updates an existing relation or, after ID allocation, spawns TrickTraining with the performer's WorldMember and fresh PersistentId; inserts TrainingProgress |
Valid request only; no acknowledgement before identity exists. |
advance_training |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, MessageReader<Arrived>, Query<(&mut TrickTraining,&mut TrainingProgress)>, Query<(),With<Animal>>, Query<(),With<Staff>>, Commands |
Decrements remaining_ticks; updates fixed authored proficiency at completion; clears progress |
One simulation tick; split from show execution. |
clean_invalid_show_links |
FixedUpdate / FixedGameSet::Cleanup |
RemovedComponents<ShowStage>, RemovedComponents<Animal>, RemovedComponents<Guest>, RemovedComponents<Staff>, Query<(Entity,&mut ShowSchedule)>, Query<(Entity,&TrickTraining,Option<&TrainingProgress>)>, Query<(Entity,&Performing)>, Query<(Entity,&WatchingShow)>, Commands |
Removes invalid links/slots | Before referenced despawn. |
5. Preflight producer contract
Not applicable: A06/A10/A14 compile show/trick/stage/animation contracts. No runtime XML, scripts, paths, filesystem or source command dispatch.
6. Behavioral evidence and disposition
Search shipped show stage, trick, training, AI/task, animation and mixer UI
families; analysis TSVs for ZT_SHOWMIXER_*, ZT_SET_SHOW_NAME, show donation
and gesture tokens; live-observe scheduling, eligibility, training, crowd and
rewards. Mine d5657e5a only for behavioral leads in show modules, interaction
dispatch/training, donations and animation evidence.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle stage capacity, compatible species/tricks, training formula, mixer edit semantics, preparation/cooldown, animation marker effects, audience capacity/ score, donation/revenue and cancellation. Native records supply all values.
7. Required behavior
- Mixer edits preserve valid order/capacity/compatibility and use preallocated storage. Performer deletion removes its slots and relations.
- Shows reserve/dock performers, execute exact trick/animation sequence, exclude ordinary behavior, acquire only valid viewers, and complete once.
- Training requires compatible trainer/performer/trick and changes proficiency according to authored rules. Results feed G17/G18/G19/G23/G24 directly.
- Schedule, training, proficiency, and current resumable state persist as evidence requires. Mods use fixed native records only.
8. Performance contract
Steady systems allocate zero. Schedule edits are explicit exceptional UI events but must remain within reserved capacity without allocation. Runtime is O(active stages + active participants + nearby indexed audience), not stages × guests. Animation/assets remain mapped/handled. GPU staging is A06/render-owned and reused. Tests count idle/running/training allocations and GPU bytes separately.
9. Acceptance criteria
Deliver exact exports. Tests cover schedule operations/capacity, compatibility, state transitions, participant cleanup, proficiency, audience/result uniqueness, and save facts. Observable done: create/edit/train/run an authentic show and see guest/economy effects. No manager, list copy, guessed capacity, script VM, or fake animations.
10. Required handoff
Report API/files, root/dependency/schedule/reflection/G22 wiring, native evidence, consumers, seams, and allocator/page/GPU/gameplay checks.
G27 — Transport and Tours
1. Identity and outcome
This package delivers ground/sky transport circuits and tours as ECS graph entities: stations, track segments, vehicles, riders, routes, queues, viewable subjects, tour scoring, and transport economics. Guests board real vehicles, travel a validated circuit, view authored subjects, pay, and disembark.
Integration wave: 5. Prerequisites: A14 definitions, A15 prefabs, A17 navigation, G14 locomotion, G17 guests, and G18 economy.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/transport_tours/; all other
paths read-only. Root owns registrations, schedules, reflection/persistence,
dependencies, and cross-package alignment.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
TransportToursPlugin |
Plugin |
unit struct | Registers circuits, vehicles, riders and tour scoring. |
TransportCircuit |
Component |
pub struct TransportCircuit { pub definition: AssetId, pub closed: bool } |
Circuit root entity; topology is relationships, not embedded vectors. |
CircuitMember |
Component |
pub struct CircuitMember(pub Entity) |
Station/track/vehicle belongs to one live circuit. |
TransportStation |
Component |
pub struct TransportStation { pub definition: AssetId, pub capacity: u16, pub queued: u16, pub occupied: u16 } |
Counters remain within native capacities. |
TrackSegment |
Component |
pub struct TrackSegment { pub definition: AssetId, pub from: Entity, pub to: Entity, pub length: f32 } |
Directed validated circuit edge; finite positive length. |
TransportVehicle |
Component |
pub struct TransportVehicle { pub definition: AssetId, pub seats: u16, pub occupied: u16, pub speed: f32 } |
Counters valid; speed finite nonnegative. |
RoutePosition |
Component |
pub struct RoutePosition { pub segment: Entity, pub distance: f32 } |
Distance clamped to segment length. |
StationDestination |
Component |
pub struct StationDestination(pub Entity) |
Vehicle's next live station. |
WaitingForTransport |
Component |
pub struct WaitingForTransport { pub station: Entity, pub circuit: Entity } |
Lives on guest; one queue relation. |
TransportRider |
Component |
pub struct TransportRider { pub vehicle: Entity, pub boarded_station: Entity } |
Lives on guest; excludes ordinary locomotion. |
TourScore |
Component |
pub struct TourScore { pub value: f32, pub observations: u32 } |
Lives on rider/guest during tour; finite native-derived sum. |
TourViewable |
Component |
pub struct TourViewable { pub definition: AssetId } |
Marks subject with compiled category/event score record. |
PendingTransportFare |
Component |
pub struct PendingTransportFare { pub guest: Entity, pub circuit: Entity, pub score: f32 } |
Domain-owned operation correlating one completed ride with one fare. |
OpenCircuitRequest |
Message |
pub struct OpenCircuitRequest { pub circuit: Entity, pub open: bool } |
Validates closed graph, stations and vehicles before opening. |
BoardTransportRequest |
Message |
pub struct BoardTransportRequest { pub guest: Entity, pub station: Entity } |
Actual guest boarding intent. |
TransportTripCompleted |
Message |
pub struct TransportTripCompleted { pub guest: Entity, pub circuit: Entity, pub score: f32 } |
Emitted once after disembark/payment/tour effects. |
Root requests G08 topology/build transaction facts, G14 spatial/navigation and docking, G17 destination/reaction, G18 price/service/transaction, A14/A17 station/track/vehicle/view records, and G19 donation category where proven. Do not create a circuit manager, passenger vector, route world, or copied graph.
4. ECS ownership
Graph nodes/edges/vehicles/riders are entities/components. Counts are derived
and maintained transactionally; rider membership is on guests, never a vehicle
Vec<Entity>.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
validate_changed_circuits |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<OpenCircuitRequest>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut TransportCircuit)>, Query<(Entity,&CircuitMember,Option<&TrackSegment>,Option<&TransportStation>,Option<&TransportVehicle>)> |
TransportCircuit.closed only after complete graph validation; invalid requests leave it closed |
On request or Changed<CircuitMember>/Changed<TrackSegment>/Changed<TransportStation>. |
queue_transport_riders |
FixedUpdate / FixedGameSet::Act |
MessageReader<BoardTransportRequest>, Query<(&Guest,&Wallet,Option<&WaitingForTransport>,Option<&TransportRider>)>, Query<(&CircuitMember,&mut TransportStation,&GlobalTransform)>, Query<&TransportCircuit>, Query<&GlobalTransform,With<Guest>>, Commands |
WaitingForTransport and TransportStation.queued |
Capacity/reach/price valid. |
board_transport_riders |
FixedUpdate / FixedGameSet::Act |
MessageReader<Arrived>, Query<(Entity,&mut TransportVehicle,&CircuitMember)>, Query<(Entity,&mut TransportStation)>, Query<(Entity,&WaitingForTransport),With<Guest>>, Commands |
TransportRider, TourScore, station/vehicle counters; removes waiting and ordinary navigation facts |
Deterministic queue order recovered from evidence. |
advance_transport_vehicles |
FixedUpdate / FixedGameSet::Navigate |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut TransportVehicle,&mut RoutePosition,&StationDestination,&mut Transform,&CircuitMember)>, Query<(&TrackSegment,&GlobalTransform)>, Query<&TransportCircuit> |
Route distance, transform and speed | Open valid circuits only; authored acceleration/spacing. |
score_tour_views |
FixedUpdate / FixedGameSet::Act |
Res<SpatialGrid>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&TransportRider,&mut TourScore),With<Guest>>, Query<&GlobalTransform,With<TransportVehicle>>, Query<(Entity,&TourViewable,&GlobalTransform)> , MessageWriter<GuestReaction> |
TourScore; emits exact guest reactions |
At authored observation cadence, using G14 cell ranges rather than a world scan. |
disembark_transport_riders |
FixedUpdate / FixedGameSet::Act |
MessageReader<Arrived>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&mut TransportVehicle)>, Query<&mut TransportStation>, Query<(Entity,&TransportRider,&TourScore,&WorldMember),With<Guest>>, Query<&PendingTransportFare>, Commands, MessageWriter<TransactionRequest> |
Clears rider/counts, restores navigation, spawns PendingTransportFare carrying the guest's WorldMember, and emits fare request carrying it |
One operation per ride. |
finish_transport_trips |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, Query<(Entity,&PendingTransportFare)>, Commands, MessageWriter<TransportTripCompleted>, MessageWriter<GuestReaction>, MessageWriter<DonationRequest> |
Emits result once and despawns operation | TransactionCompleted.operation must equal the operation entity. |
reject_transport_fares |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionRejected>, Query<(Entity,&PendingTransportFare)>, Commands, MessageWriter<GuestReaction> |
Applies only evidenced failed-fare consequence and despawns operation; no trip success | TransactionRejected.operation must equal the operation entity. |
clean_broken_transport_links |
FixedUpdate / FixedGameSet::Cleanup |
RemovedComponents<TransportCircuit>, RemovedComponents<TransportStation>, RemovedComponents<TrackSegment>, RemovedComponents<TransportVehicle>, Query<(Entity,&CircuitMember)>, Query<(Entity,&WaitingForTransport),With<Guest>>, Query<(Entity,&TransportRider),With<Guest>>, Query<&mut TransportStation>, Query<&mut TransportVehicle>, Commands |
Clears relationships and repairs counts | Before despawn; authentic evacuation/refund is an evidence gate. |
5. Preflight producer contract
Not applicable: A14/A15/A17 compile station endpoints, graph/track geometry, vehicle motion and tour scoring. No runtime XML/Z2F/filesystem/source merging.
6. Behavioral evidence and disposition
Search shipped ground/sky station, track, vehicle, gate, tour AI/viewable and
economy definitions; analysis/oracle for transportation/circuit/station/vehicle
classes and tour tasks. Observe construction validation, queues, vehicle spacing,
boarding, fares and tour reactions. Mine d5657e5a only for evidence in
transport/tour domain and feature modules.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle graph closure/direction, endpoint tolerances, slopes/heights, vehicle acceleration/follow distance, capacities, queue order, station dwell, fare timing, rider destination, view radius/score/categories/events and donation effects. All are native/evidence-derived.
7. Required behavior
- Open requires a valid authored circuit. Topology edits close/invalidate safely and participate in G07/G08 undo/redo.
- Boarding is reachable/capacity/affordability checked; reciprocal counters and rider facts change atomically. Rider cannot navigate independently.
- Vehicles follow graph/spacing rules; tour views use visibility/range and native scoring. Disembark, payment, reaction and result happen once.
- Removal/load cleans or repairs every relation. Circuit graph, counts, route, wait/rider/score are save relevant according to evidence.
- Mods supply compiled records/graph pieces only.
8. Performance contract
Zero settled allocations. Vehicle movement O(vehicles); boarding/disembark O( affected station queues/riders); view scoring O(nearby spatial cells). No graph clone, passenger vector, all-pairs search, strings, hash growth, filesystem, or payload clone. Render/GPU handles remain A04/A15-owned. Tests profile heap, mapped navigation pages and GPU instances separately.
9. Acceptance criteria
Deliver exact exports. Tests cover graph validity, capacity/counters, queue ordering, movement/spacing, view score, payment/result uniqueness, and broken link cleanup. Observable done: build/open a circuit, guests queue/ride/view/pay/ leave authentically. No manager, route mirror, guessed tolerance or stub.
10. Required handoff
Report API/files, root/dependency/schedule/reflection/G22 wiring, consumed symbols, evidence, seams, and allocator/page/GPU/gameplay checks.
G28 — Photos
1. Identity and outcome
This package delivers player camera capture, authentic subject/framing facts, photo entities/albums, and photo challenges. A capture reuses GPU readback capacity, creates one Bevy image asset and one metadata entity, and reports typed subject evidence to G24. It does not retain a second rendered world.
Integration wave: 5. Prerequisites: A02 UI, A05 textures, A19 challenges, G06 camera, and G24 scenario.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/photos/; all else read-only.
Root owns module/plugin/render-graph/schedule/reflection/persistence registration,
dependencies, and cross-package alignment.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
PhotosPlugin |
Plugin |
unit struct | Registers photo intent, metadata and completion systems. |
PhotoMode |
Component |
marker on the active camera | Capture input is active; G31 owns overall mode transition. |
PhotoSubject |
Component |
pub struct PhotoSubject { pub definition: AssetId } |
Marks a photographable live entity with native scoring definition. |
Photo |
Component |
pub struct Photo { pub image: Handle<Image>, pub captured_tick: u64, pub camera_position: Vec3, pub camera_rotation: Quat, pub score_milli: i32 } |
Lives on one album-entry entity, owns one generated image handle, and stores A19's exact integer-thousandths score. |
PhotoSubjects |
Component |
pub struct PhotoSubjects(pub ArrayVec<Entity, PHOTO_SUBJECT_CAPACITY>) |
Inline sorted/deduplicated subject IDs; no heap owner or runtime resize. |
PhotoAlbum |
Component |
pub struct PhotoAlbum { pub profile: AssetId } |
Album root entity; entries are child/relationship entities, not an embedded vector. |
AlbumMember |
Component |
pub struct AlbumMember(pub Entity) |
Photo entry references one live album. |
CapturePhotoRequest |
Message |
pub struct CapturePhotoRequest { pub camera: Entity } |
Player request from fixed input command. |
PhotoCaptured |
Message |
pub struct PhotoCaptured { pub photo: Entity, pub score_milli: i32 } |
Emitted once after GPU result and metadata commit. |
PhotoChallengeCompleted |
Message |
pub struct PhotoChallengeCompleted { pub photo: Entity, pub challenge: AssetId } |
Emitted once per newly satisfied A19 challenge after exact score/subject evaluation. |
PhotoCaptureFailed |
Message |
pub struct PhotoCaptureFailed { pub reason: PhotoFailure } |
Typed failure; no empty photo entity. |
PhotoFailure |
enum | NotInPhotoMode, ReadbackBusy, InvalidCamera, CapacityExceeded, PersistentId(PersistentIdError), Gpu, Storage |
Fixed failure vocabulary. |
PendingPhotoCapture |
Component |
pub struct PendingPhotoCapture { pub captured_tick: u64, pub camera_position: Vec3, pub camera_rotation: Quat, pub subjects: PhotoSubjects } |
Lives on the camera from accepted intent until the mapped GPU buffer is consumed; it is also the render-extraction request and owns no image bytes. |
PhotoReadback |
Resource |
pub struct PhotoReadback { pub buffer: Buffer, pub capacity_bytes: u64, pub busy: bool, pub ready: bool, pub width: u32, pub height: u32, pub row_bytes: u32 } |
One reusable GPU readback buffer and its fixed-size completion facts; may grow only outside settled frame and never per capture once target-profile capacity is established. |
DeletePhotoRequest |
Message |
pub struct DeletePhotoRequest { pub photo: Entity } |
Removes album entity/image persistence through explicit action. |
PhotoSubjects imports A19's one recovered PHOTO_SUBJECT_CAPACITY constant;
G28 declares no duplicate. Root aligns G06 active camera and frustum, render
graph extraction/readback, G31 mode, G24 typed photo objective observation, A19
native scoring records, and G22 generated-image persistence. Do not copy scene entities, camera
world, or texture payload into a Rust resource.
4. ECS ownership
Albums/photos are entities; generated pixels live in Bevy Assets<Image> and
GPU resources. The only resource is reusable staging, not album state.
The private zero-sized PendingPhotoScore component is inserted only on a
freshly captured photo and removed by score_photo; restored Photo entities
never carry it and therefore never replay scoring/challenge messages.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_photo_album |
Update / GameSet::Intent |
MessageReader<WorldLoadFinished>, Res<ProfileOptions>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<PhotoAlbum>>, Commands, MessageWriter<PhotoCaptureFailed> |
For a new world only, creates one PhotoAlbum entity with selected profile, root WorldMember, and allocator-issued PersistentId |
No existing album; G22 restores saved albums instead. |
request_photo_readback |
Update / GameSet::Intent |
MessageReader<CapturePhotoRequest>, Res<ZooClock>, Query<(Entity,&Camera,&GlobalTransform,&PhotoMode),Without<PendingPhotoCapture>>, ResMut<PhotoReadback>, Commands, MessageWriter<PhotoCaptureFailed> |
Inserts PendingPhotoCapture, sets PhotoReadback.busy=true, ready=false; render extraction reads that component directly |
Valid mode/camera and idle staging only. |
collect_visible_photo_subjects |
Update / GameSet::Presentation |
Res<SpatialGrid>, Res<Assets<WorldScenarioAsset>>, Query<(&Camera,&GlobalTransform,&mut PendingPhotoCapture)>, Query<(Entity,&PhotoSubject,&GlobalTransform,&InheritedVisibility)> |
PendingPhotoCapture.subjects only |
Once for a new pending capture; tests exact camera frustum, render visibility and authored occlusion rule over G14 candidate cells. |
complete_photo_capture |
Update / GameSet::Presentation |
ResMut<PhotoReadback>, Query<(Entity,&PendingPhotoCapture)>, Query<(Entity,&WorldMember),With<PhotoAlbum>>, ResMut<PersistentIdAllocator>, ResMut<Assets<Image>>, Commands, MessageWriter<PhotoCaptureFailed> |
Reads the already-mapped buffer; after ID allocation creates one Image and photo entity carrying PersistentId, the album's WorldMember, Photo, PhotoSubjects, AlbumMember, and private PendingPhotoScore; removes capture marker and clears busy/ready |
PhotoReadback.ready; explicit capture only; exceptional image/entity allocation measured. |
score_photo |
Update / GameSet::Intent |
Res<Assets<WorldScenarioAsset>>, Query<(Entity,&mut Photo,&PhotoSubjects,&AlbumMember),With<PendingPhotoScore>>, Query<&PhotoSubject>, Commands, MessageWriter<PhotoCaptured>, MessageWriter<PhotoChallengeCompleted> |
Photo.score_milli, removes pending score, and emits exact challenge/completion messages |
Once per fresh photo/challenge; restored photos never match. |
delete_photos |
Update / GameSet::Intent |
MessageReader<DeletePhotoRequest>, Query<(Entity,&Photo,&AlbumMember)>, ResMut<Assets<Image>>, Commands, MessageWriter<DeleteGeneratedImage> |
Removes image/entry and requests persistent generated-image deletion | Explicit valid request; DeleteGeneratedImage is the root-frozen G22 storage message, not a filesystem call. |
5. Preflight producer contract
Not applicable: A19 compiles photo scoring/challenge records and A02 commands. No source XML, filesystem discovery, Z2F, or image transcoding occurs in ordinary runtime; generated photo persistence is explicit gameplay output.
6. Behavioral evidence and disposition
Search shipped photo UI, camera mode, photographable entity and photo challenge
definitions; analysis TSVs/oracle for photo/camera/album/challenge command names;
live-observe framing, subject recognition, naming, score and album behavior.
Mine d5657e5a only for evidence in domain photo and Bevy presentation/photos,
camera, screenshot, save and scenario modules.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle output resolution/format, capture effects, visibility/occlusion, numeric
PHOTO_SUBJECT_CAPACITY, maximum
subjects, subject/framing/behavior scoring, challenge event, album limit,
thumbnail/full image and save/delete policy. All bounds/formulas derive from
native target profile/evidence.
7. Required behavior
- Capture is accepted only in valid photo mode with idle staging. It records the actual rendered camera output and proven visible subjects, not proximity alone.
- One completion creates one Image handle and metadata entity. Challenge/scenario receives typed subject/score facts once. Failure leaves no album entry.
- Album membership and generated image persistence survive save/load; deletion releases Bevy/GPU/storage ownership when last handle is gone.
- Controller capture uses G03/G31 action mapping. Mods extend native scoring records without runtime scripts.
8. Performance contract
Settled non-capture frames allocate zero and do no readback. Subject IDs use inline evidence-bounded storage validated by A19 preflight. Staging reaches target-profile capacity before measurement; captures are explicit exceptional operations with allocation/readback latency and bytes recorded. Subject search uses existing visibility/spatial results O(visible candidates). No scene clone, per-frame pixels, strings, filesystem read, or recurring GPU allocation. Track Rust heap, mapped definitions, readback bytes and generated texture VRAM separately.
9. Acceptance criteria
Deliver exact exports. Tests cover mode/busy failures, subject dedupe/capacity, single completion, scoring fact, album relationship and deletion ownership; render validation confirms actual pixels/occlusion. Observable done: enter photo mode, capture/view/delete a correctly scored photo and satisfy a challenge. No shadow renderer/world, guessed bound, blank success or recurring staging.
10. Required handoff
Report API/files, render/Cargo/root/G22/G24 wiring, native evidence, seams, and heap/page/readback/VRAM/gameplay measurements.
G29 — Extinct Animal Recovery
1. Identity and outcome
This package delivers fossil discovery, collection, puzzle assembly, cloning, and creation of extinct animals as ordinary progression and world entities. Recovered pieces unlock an authored skeleton assembly; completed specimens feed a cloning lab; successful cloning requests the normal G11 animal spawn path.
Integration wave: 5. Prerequisites: A03 species, A10 behavior, A14 definitions, G11 animal lifecycle, G23 progression, and G24 scenario.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/extinct_animals/; all else
read-only. Root owns registrations, schedules, reflection/persistence,
dependencies, and cross-package alignment.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ExtinctAnimalsPlugin |
Plugin |
unit struct | Registers fossil/assembly/cloning systems. |
FossilSite |
Component |
pub struct FossilSite { pub definition: AssetId, pub exhausted: bool } |
Placed/authored discovery location. |
FossilRng |
Component |
pub struct FossilRng(pub DeterministicRng) |
G32 stream on each site, derived from zoo seed and site PersistentId; sole discovery roll source. |
FossilPiece |
Component |
pub struct FossilPiece { pub set: AssetId, pub piece_index: u16 } |
Discovered world piece; index validated against native set. |
FossilCollection |
Resource |
pub struct FossilCollection { pub words: Vec<u64>, pub piece_count: u32 } |
Profile-wide bounded bitset sized once to native stable piece index. |
AssemblyTable |
Component |
pub struct AssemblyTable { pub definition: AssetId } |
Real facility entity. |
FossilAssembly |
Component |
pub struct FossilAssembly { pub set: AssetId, pub placed_words: Vec<u64>, pub piece_count: u16 } |
Lives on assembly entity; bitset allocated once to set size. |
CloneLab |
Component |
pub struct CloneLab { pub definition: AssetId } |
Real facility entity. |
CloneRng |
Component |
pub struct CloneRng(pub DeterministicRng) |
G32 stream on each lab, derived from zoo seed and lab PersistentId; sole cloning roll source. |
CloneProcess |
Component |
pub struct CloneProcess { pub species: AssetId, pub elapsed_ticks: u64, pub required_ticks: u64, pub attempt: u32 } |
Lives on lab while one authored clone attempt runs; A14 process duration is already ticks. |
PendingClonePayment |
Component |
pub struct PendingClonePayment { pub lab: Entity, pub species: AssetId } |
Domain-owned transaction operation; no CloneProcess exists before matching completion. |
PendingCloneSpawn |
Component |
pub struct PendingCloneSpawn { pub lab: Entity, pub species: AssetId } |
Lives on the caller-owned spawn-operation entity and correlates G11's natural spawn result. |
DiscoverFossilRequest |
Message |
pub struct DiscoverFossilRequest { pub site: Entity, pub tool: Entity } |
Valid special-mode discovery action. |
PlaceFossilPiece |
Message |
pub struct PlaceFossilPiece { pub assembly: Entity, pub piece_index: u16, pub slot_index: u16 } |
Typed assembly puzzle action; native layout validates slot. |
StartCloneRequest |
Message |
pub struct StartCloneRequest { pub lab: Entity, pub species: AssetId } |
Requires completed assembly/unlock/cost. |
FossilSetCompleted |
Message |
pub struct FossilSetCompleted { pub assembly: Entity, pub set: AssetId } |
Emitted exactly once on valid full assembly. |
CloneCompleted |
Message |
pub struct CloneCompleted { pub lab: Entity, pub species: AssetId, pub animal: Entity } |
Emitted after normal animal spawn succeeds. |
CloneFailed |
Message |
pub struct CloneFailed { pub lab: Entity, pub species: AssetId, pub attempt: u32 } |
Emitted only when authored probability/rules produce failure. |
Root requests A14 stable fossil piece/set/slot/lab definitions; G30 puzzle/gesture observations; G31 fossil/cloning modes; G18 costs; G23 unlocks; G24 typed events; and G11 adoption/spawn request. Do not copy species definitions or create an extinct-animal entity architecture separate from ordinary animals.
4. ECS ownership
Collection is genuinely profile-global bounded inventory. Site/table/lab/piece/ assembly/process are entities/components. Bitsets allocate once from native counts and do not grow in steady play.
The implementation-private CloneSpawnPending { operation: Entity } component
lives on a lab only between SpawnAnimalRequest and the matching G11 result. It
is a narrow correlation marker, not durable clone state, and G22 never saves it.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
discover_fossils |
FixedUpdate / FixedGameSet::Act |
MessageReader<DiscoverFossilRequest>, Res<ActiveImmersiveMode>, Res<Assets<WorldDefinitionsAsset>>, ResMut<FossilCollection>, ResMut<PersistentIdAllocator>, Query<(&GlobalTransform,&ModeTool)>, Query<(Entity,&mut FossilSite,&mut FossilRng,&GlobalTransform,&WorldMember)>, Commands |
Collection bit/site exhaustion and, after ID allocation, optional FossilPiece with site WorldMember and fresh PersistentId |
Valid authored action only; no visible piece before identity exists. |
place_fossil_pieces |
Update / GameSet::Intent |
MessageReader<PlaceFossilPiece>, Res<Assets<WorldDefinitionsAsset>>, Res<FossilCollection>, Query<(Entity,&AssemblyTable,&mut FossilAssembly)>, MessageWriter<FossilSetCompleted> |
Assembly placed bitset/count | Reject unavailable/wrong/occupied piece. |
request_clone_payments |
FixedUpdate / FixedGameSet::Economy |
MessageReader<StartCloneRequest>, Res<Assets<WorldDefinitionsAsset>>, Res<UnlockSet>, Res<FossilCollection>, Query<(Entity,&CloneLab,&WorldMember,Option<&CloneProcess>)>, Query<&PendingClonePayment>, Commands, MessageWriter<TransactionRequest> |
Spawns PendingClonePayment carrying lab WorldMember and emits transaction carrying it |
Valid request only; no process yet. |
complete_clone_payments |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionCompleted>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&PendingClonePayment)>, Query<&CloneLab>, Commands |
Inserts CloneProcess on the lab and despawns operation |
TransactionCompleted.operation must equal the operation entity. |
reject_clone_payments |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<TransactionRejected>, Query<(Entity,&PendingClonePayment)>, Commands |
Despawns operation without process/success | TransactionRejected.operation must equal the operation entity. |
advance_clone_processes |
FixedUpdate / FixedGameSet::Act |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Res<Assets<SpeciesCatalogAsset>>, Query<(Entity,&CloneLab,&mut CloneProcess,&mut CloneRng,&Transform,&HabitatMember,&WorldMember),Without<CloneSpawnPending>>, Commands, MessageWriter<SpawnAnimalRequest>, MessageWriter<CloneFailed> |
Elapsed ticks/attempt/failure; on authored success spawns a world-scoped PendingCloneSpawn operation, marks the lab with private CloneSpawnPending { operation }, and requests G11 creation in the lab's habitat at A14's authored offset |
Active labs only; no AdoptAnimal and no direct assembly of G11 components. |
finish_successful_clones |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<AnimalSpawned>, Query<(Entity,&PendingCloneSpawn)>, Query<(&CloneLab,&CloneProcess,&CloneSpawnPending)>, Commands, MessageWriter<CloneCompleted> |
Clears matching lab process/pending marker, despawns operation and emits result | Exact spawned.operation match only. |
reject_clone_spawns |
FixedUpdate / FixedGameSet::Cleanup |
MessageReader<AnimalSpawnRejected>, Query<(Entity,&PendingCloneSpawn)>, Query<(&mut CloneProcess,&CloneSpawnPending)>, Commands, MessageWriter<CloneFailed> |
Clears matching pending marker/operation and applies the recovered failure/refund policy | Exact operation match; never reports a successful clone. |
clean_invalid_extinct_links |
FixedUpdate / FixedGameSet::Cleanup |
RemovedComponents<FossilSite>, RemovedComponents<AssemblyTable>, RemovedComponents<CloneLab>, Query<(Entity,&FossilPiece)>, Query<(Entity,&FossilAssembly)>, Query<(Entity,&CloneProcess)>, Commands |
Clears transient relations/process according to authored refund | Before despawn; refund behavior is an explicit evidence gate. |
5. Preflight producer contract
Not applicable: A03/A10/A14 compile species, fossil layouts, discovery/cloning rules and fixed commands. No runtime XML, scripts, source assets or filesystem.
6. Behavioral evidence and disposition
Search shipped Extinct Animals fossil site/piece/set, fossil finder, assembly UI,
cloning lab and extinct species definitions; analysis TSVs/oracle for fossil,
assemble, cloning and minigame modes; live-observe discovery, puzzle, probability,
timing and spawn. Mine d5657e5a only for behavior/evidence in expansion,
scenario, gesture/modes and animal modules.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle discovery distribution/repeats/exhaustion, piece index/layout, assembly validation/reward, cloning prerequisites/cost/time/failure/progression, name/ variant/sex and the exact lab-relative A14 spawn offset. No guessed odds or generic puzzle bag.
7. Required behavior
- Discovery validates mode/tool/site and deterministic authored rules; inventory cannot duplicate a nonrepeatable piece.
- Assembly accepts only owned matching pieces in correct authored slots and completes once. Cloning requires completion/unlock/payment and uses evidence- backed outcome/timing.
- A successful clone is an ordinary G11 animal with normal G12–G16 behavior; only origin/species facts distinguish it. All durable facts persist.
- Mods add compiled sets/layouts/species/rules within fixed vocabularies.
8. Performance contract
Settled systems allocate zero. Bit lookup O(1); discovery/placement O(1); cloning O(active labs). Bitset capacities fixed at profile/world load. Explicit spawn may use normal Bevy entity allocation but no asset copy. No strings, map growth, filesystem or VM. Allocation tests cover idle, discovery, placement and clone tick; mapped/handle operations zero; no owned GPU resources.
9. Acceptance criteria
Deliver exact exports. Tests cover inventory bounds/uniqueness, slot validity, single completion, clone prerequisites/payment/timing/outcome, ordinary animal spawn and cleanup/persistence. Observable done: find/assemble fossils and clone an extinct animal authentically. No separate extinct world, guessed probability, stub, or runtime parser.
10. Required handoff
Report API/files, root/dependency/G22/G24/G30/G31 wiring, evidence, seams, and allocator/page/gameplay checks.
G30 — Gestures and Puzzles
1. Identity and outcome
This package delivers controller/mouse gesture capture, recognition against compiled A10 templates, and authored minigame/puzzle step progression. It turns input samples into fixed typed actions for training, fossils, cloning, tutorials, and other proven modes without adding a script VM or source-document runtime.
Integration wave: 5. Prerequisites: A02 UI, A10 behavior programs, G02 UI, G03 input, and G24 scenario.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/gestures_puzzles/; all else
read-only. Root owns registrations, schedules, reflection/persistence,
dependencies, and alignment with A10 command/template representation.
3. Frozen public contract
Gesture points are normalized viewport coordinates in [-1.0, 1.0] and carry
monotonic simulation-independent input seconds. Sample capacity comes from the
active compiled gesture template/profile and is reserved before capture.
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
GesturesPuzzlesPlugin |
Plugin |
unit struct | Registers input capture, recognition and puzzle progression. |
GestureSurface |
Component |
pub struct GestureSurface { pub definition: AssetId } |
UI/world interaction surface with compiled allowed templates. |
GestureStroke |
Component |
pub struct GestureStroke { pub surface: Entity, pub context: GestureContext, pub points: Vec<InputGesturePoint>, pub capacity: u16, pub active: bool } |
Lives on input/controller cursor entity; allocated once to native bound, never grows during stroke. |
InputGesturePoint |
POD struct | #[repr(C)] pub struct InputGesturePoint { pub position: Vec2, pub time_seconds: f32 } |
Finite normalized live input sample; distinct from A10's quantized GestureTemplatePoint. |
GestureContext |
enum | Training, Fossil, Cloning, Tutorial, Minigame |
Live interaction context, distinct from A10 GestureTemplateKind. |
BeginGesture |
Message |
pub struct BeginGesture { pub surface: Entity, pub source: ActionSource } |
Starts valid idle stroke. |
GestureSample |
Message |
pub struct GestureSample { pub position: Vec2, pub time_seconds: f32, pub source: ActionSource } |
Device-independent normalized sample. |
EndGesture |
Message |
pub struct EndGesture { pub source: ActionSource } |
Ends current source stroke. |
GestureRecognized |
Message |
pub struct GestureRecognized { pub surface: Entity, pub template: AssetId, pub command: PuzzleCommand, pub score: f32, pub source: ActionSource } |
Carries A10's exact copyable typed command; template ID is diagnostic identity only. |
GestureRejected |
Message |
pub struct GestureRejected { pub surface: Entity, pub reason: GestureFailure, pub source: ActionSource } |
Typed failure; no guessed nearest success. |
GestureFailure |
enum | Busy, InvalidSurface, TooFewSamples, CapacityExceeded, NoMatch, Cancelled |
Fixed reasons. |
Puzzle |
Component |
pub struct Puzzle { pub definition: AssetId, pub step: u16, pub state: PuzzleState } |
Dedicated puzzle entity points into compiled step table. |
PuzzleState |
enum | Inactive, Running, Completed, Failed |
Terminal state follows native rule. |
PuzzleAction |
Message |
pub struct PuzzleAction { pub puzzle: Entity, pub command: PuzzleCommand, pub subject: Option<Entity> } |
Carries A10's exact copyable command directly. |
PuzzleCompleted |
Message |
pub struct PuzzleCompleted { pub puzzle: Entity, pub definition: AssetId } |
Emitted once after final step effects. |
G30 consumes A10 GestureTemplateKind, GestureTemplatePoint,
GestureTemplate, PuzzleDefinition, PuzzleStep, and PuzzleCommand directly;
it declares no archived-template variants. Root aligns G03 pointer/controller
normalization, G02 surface/focus ownership, G24 tutorial observation, G26 training,
G29 fossil/cloning, and G31 active mode. Do not add arbitrary string commands,
callback traits, or copied gesture documents.
4. ECS ownership
Stroke belongs to the active input cursor; puzzle state belongs to puzzle entity. No gesture manager, minigame world, action registry, or unbounded sample queue.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
begin_gesture_strokes |
Update / GameSet::Input |
MessageReader<BeginGesture>, Res<Assets<BehaviorProgramAsset>>, Query<&GestureSurface>, Query<(Entity,&mut GestureStroke)>, Commands, MessageWriter<GestureRejected> |
Clears preallocated points and activates the source stroke | Valid focused surface/source only. |
append_gesture_samples |
Update / GameSet::Input |
MessageReader<GestureSample>, Query<&mut GestureStroke> |
Pushes into the matching active stroke within existing capacity | Rejects nonfinite/out-of-order/overflow; no reserve. |
finish_gesture_strokes |
Update / GameSet::Intent |
MessageReader<EndGesture>, Res<Assets<BehaviorProgramAsset>>, Query<&GestureSurface>, Query<&mut GestureStroke>, MessageWriter<GestureRecognized>, MessageWriter<GestureRejected> |
Deactivates stroke; emits one result | Deterministic normalization/scoring from the mapped A10 contract. |
route_gesture_commands |
Update / GameSet::Intent |
MessageReader<GestureRecognized>, Res<ActiveImmersiveMode>, Query<(Entity,&Puzzle,&GestureSurface)>, MessageWriter<PuzzleAction>, MessageWriter<EnterImmersiveMode>, MessageWriter<PlaceFossilPiece>, MessageWriter<StartCloneRequest>, MessageWriter<TrainingRequest> |
Exhaustively matches PuzzleCommand and emits one natural-owner typed request |
No AssetId/string command dispatch or callback registry. |
advance_puzzles |
FixedUpdate / FixedGameSet::Act |
MessageReader<PuzzleAction>, Res<Assets<BehaviorProgramAsset>>, Query<(Entity,&mut Puzzle)>, Query<(),With<WorldMember>>, Commands, MessageWriter<PuzzleCompleted> |
Puzzle step/state and mapped typed completion/failure effects | Valid expected action only. |
cancel_invalid_gestures |
FixedUpdate / FixedGameSet::Cleanup |
RemovedComponents<GestureSurface>, RemovedComponents<ControlledEntity>, Query<(Entity,&mut GestureStroke)>, Query<(Entity,&mut Puzzle)>, Commands, MessageWriter<GestureRejected> |
Clears active stroke/puzzle according to rule | Before relationship despawn. |
5. Preflight producer contract
Not applicable: A10 compiles source gesture paths/templates, normalization parameters, thresholds, commands and puzzle steps into fixed native records. Unsupported script commands are preflight errors. No XML/Lua/filesystem here.
6. Behavioral evidence and disposition
Search shipped gesture/test/training/fossil/cloning/minigame UI and behavior
families; analysis TSVs for ZT_SHOWGESTURE, ZT_QUICKGESTURETEST and related
commands; live-observe stroke tolerance/feedback and puzzle sequencing. Mine
d5657e5a only for behavioral leads in domain/interaction gesture, Bevy gesture
dispatch, training, modes and scenario.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle sampling/filtering/resampling, direction/rotation/scale invariance, scoring/threshold/tie-break, capacity, timeout/cancel, command mapping, puzzle step success/failure/retry/reward. Values and algorithms require native/oracle/ live evidence; no generic recognizer guess.
7. Required behavior
- Mouse/controller generate the same normalized stroke vocabulary. Invalid, overflowed or unmatched strokes reject clearly and never become nearest-match success.
- Recognition traverses mapped templates allocation-free and emits fixed command
AssetId; routing is exhaustive to natural domains. - Puzzles accept only expected authored actions, transition once, apply typed effects and persist resumable state where proven.
- Mods may add templates/steps using supported commands; unknown commands fail preflight.
8. Performance contract
Stroke storage allocates/reserves once from native capacity; capture, recognition and puzzle ticks allocate zero. Recognition complexity is O(samples × allowed template samples) with precomputed normalized templates and bounded candidates; SIMD may be used after correctness. No strings, source path parsing, map growth, filesystem or cloned templates. Bench heap, mapped pages and recognition latency; no GPU resources.
9. Acceptance criteria
Deliver exact exports. Tests use recovered fixtures to cover normalization, direction/threshold/tie-break, overflow/rejection, command routing, puzzle step/ terminal uniqueness, controller parity and save facts. Observable done: authentic gestures drive training/fossil/cloning/tutorial/minigame actions. No VM, registry, manager, guessed recognizer, or no-op command.
10. Required handoff
Report API/files, dependencies/root schedule wiring, command coverage, G22/G24/G26/G29/G31 wiring, evidence, seams, and allocator/page/latency validation.
G31 — Immersive and Special Modes
1. Identity and outcome
This package delivers coherent transitions into guest view, first-person, training, fossil, cloning, photo, show and other proven special tool/camera modes. It requests the existing G06 camera, attaches focused control markers to the controlled entity and mode-specific components; it never creates another renderer, scene, input world, or overarching runtime.
Integration wave: 5. Prerequisites: G03 input, G06 camera, G24 scenario, G26 shows, G28 photos, G29 extinct animals, and G30 gestures/puzzles.
2. Exclusive ownership
Exclusive path: reconstruction/game/src/plugins/immersive_modes/; all else
read-only. Root owns module/plugin/state/schedule/reflection/persistence wiring,
dependencies, and cross-package symbol alignment.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ImmersiveModesPlugin |
Plugin |
unit struct | Registers mode transition/control projection systems. |
ImmersiveMode |
enum | GuestView, FirstPerson, Training, FossilSearch, FossilAssembly, Cloning, Photo, ShowEdit |
Fixed proven high-level modes; ordinary construction remains G07. |
ActiveImmersiveMode |
Resource |
pub struct ActiveImmersiveMode { pub mode: ImmersiveMode, pub controller: Entity, pub subject: Option<Entity>, pub camera: Entity } |
Resource exists only while special mode is active; it identifies participants but copies no camera/input/domain state. |
PendingImmersiveEntry |
Component |
pub struct PendingImmersiveEntry { pub mode: ImmersiveMode, pub subject: Option<Entity>, pub failure: Option<ModeEntryFailure> } |
Lives on requesting controller for at most one intent pass; each validator consumes only its mode. |
ControlledEntity |
Component |
pub struct ControlledEntity { pub controller: Entity } |
Lives on guest/animal/vehicle/avatar being directly controlled. |
ModeTool |
Component |
pub struct ModeTool { pub definition: AssetId } |
Mode-specific cursor/tool entity using native definition. |
GuestViewControl / FirstPersonControl / TrainingControl / FossilSearchControl / FossilAssemblyControl / CloningControl / PhotoControl / ShowEditControl |
marker components | unit structs, exactly one attached to the active controller | Focused mode-local input filters; no copied G03 action-context resource or enum dispatcher. |
EnterImmersiveMode |
Message |
pub struct EnterImmersiveMode { pub mode: ImmersiveMode, pub controller: Entity, pub subject: Option<Entity> } |
Typed request; validates mode prerequisites before transition. |
ExitImmersiveMode |
Message |
pub struct ExitImmersiveMode { pub controller: Entity, pub reason: ModeExitReason } |
Requests removal of mode controls and G06 camera restoration. |
ModeExitReason |
enum | Completed, Cancelled, SubjectRemoved, Invalidated, GamePhaseChanged |
Fixed reason vocabulary. |
ModeEntryFailure |
enum | Busy, InvalidController, MissingSubject, WrongSubject, MissingTool, RuleDenied |
Fixed atomic rejection reasons. |
ImmersiveModeRejected |
Message |
pub struct ImmersiveModeRejected { pub mode: ImmersiveMode, pub reason: ModeEntryFailure } |
Emitted once by the exact mode validator; failed entry mutates nothing else. |
ImmersiveModeEntered |
Message |
pub struct ImmersiveModeEntered { pub mode: ImmersiveMode, pub subject: Option<Entity> } |
Emitted once after camera/input/tool state commits. |
ImmersiveModeExited |
Message |
pub struct ImmersiveModeExited { pub mode: ImmersiveMode, pub reason: ModeExitReason } |
Emitted once after full cleanup/return. |
G06 exclusively creates, reads, restores, and removes CameraReturnState on the
actual camera. G31 only emits G06 SetCameraMode and RestoreCameraMode
messages; it never queries or mutates that component. Root also aligns G06
camera requests, G02 focus surface, G14 controlled locomotion, and direct G26/
G28/G29/G30 components. Do not define camera/input/render facades or duplicate
their state.
4. ECS ownership
Only one small active-mode resource coordinates a global mutually exclusive mode. Actual camera/input/subject/tool facts remain on their natural entities.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
reserve_immersive_entry |
Update / GameSet::Intent |
MessageReader<EnterImmersiveMode>, Option<Res<ActiveImmersiveMode>>, Query<(),With<PendingImmersiveEntry>>, Commands, MessageWriter<ImmersiveModeRejected> |
Inserts one PendingImmersiveEntry on controller or rejects Busy |
In game; first valid request in message order. |
enter_guest_view |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)> selecting mode==GuestView, Query<(),With<Guest>>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts resource/ControlledEntity/GuestViewControl; sends SetCameraMode { mode: CameraMode::FirstPerson(subject), transition_seconds: evidenced value } |
Transition value is an evidence gate. |
enter_first_person |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::FirstPerson; Query<(),(With<NavAgent>,Without<Dead>)>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts FirstPersonControl/controlled facts; sends CameraMode::FirstPerson(subject) |
Exact transition value is an evidence gate. |
enter_training |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::Training; Query<(),With<Animal>>, Query<(),With<Staff>>, Query<&TrickTraining>, Res<Assets<WorldDefinitionsAsset>>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts TrainingControl and ModeTool; emits one SetCameraMode |
Exact CameraMode variant and transition_seconds are mandatory oracle/live-observation evidence gates; this row must not compile with a guessed value. |
enter_fossil_search |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::FossilSearch; Res<Assets<WorldDefinitionsAsset>>, Query<&FossilSite>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts FossilSearchControl/tool and sends evidenced camera request |
Exact camera variant and transition value are evidence gates. |
enter_fossil_assembly |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::FossilAssembly; Query<&FossilAssembly>, Query<&GestureSurface>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts FossilAssemblyControl/tool and sends evidenced camera request |
Exact relations, camera variant and transition value are evidence gates. |
enter_cloning |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::Cloning; Query<(&CloneLab,Option<&CloneProcess>)>, Query<&GestureSurface>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts CloningControl/tool and sends evidenced camera request |
Exact lab prerequisite, camera variant and transition value are evidence gates. |
enter_photo |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::Photo; Res<PhotoReadback>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts PhotoControl/PhotoMode; sends evidenced camera request |
Readback idle; exact camera variant and transition value are evidence gates. |
enter_show_edit |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, selecting mode==ImmersiveMode::ShowEdit; Query<(&ShowStage,&ShowSchedule)>, Query<&InfoPanel>, Query<Entity,With<ZooCamera>>, Commands, MessageWriter<SetCameraMode>, MessageWriter<ImmersiveModeEntered> |
Inserts ShowEditControl; sends evidenced camera request |
Valid stage/surface; exact camera variant and transition value are evidence gates. |
reject_pending_entry |
Update / GameSet::Intent |
Query<(Entity,&PendingImmersiveEntry)>, Commands, MessageWriter<ImmersiveModeRejected> |
Emits stored exact failure and removes pending component | After all eight validators. |
apply_guest_view_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ControlAxes>, Res<ActiveImmersiveMode>, Query<(&GlobalTransform,&NavAgent),With<GuestViewControl>>, MessageWriter<NavigateTo> |
Emits guest NavigateTo only; G06 consumes look axes itself |
Active GuestView. |
apply_first_person_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ControlAxes>, Res<ActiveImmersiveMode>, Query<(&GlobalTransform,&NavAgent),With<FirstPersonControl>>, MessageWriter<NavigateTo> |
Emits subject NavigateTo only |
Active FirstPerson. |
apply_training_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<&ModeTool,With<TrainingControl>>, MessageWriter<TrainingRequest>, MessageWriter<BeginGesture>, MessageWriter<GestureSample>, MessageWriter<EndGesture> |
Exact training/gesture messages only | Active Training. |
apply_fossil_search_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<(&Transform,&ModeTool),With<FossilSearchControl>>, Query<(Entity,&FossilSite,&Transform)>, MessageWriter<DiscoverFossilRequest> |
Fossil discovery requests only | Active FossilSearch; bounded G14 candidates supplied before query. |
apply_fossil_assembly_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<&ModeTool,With<FossilAssemblyControl>>, Query<(&FossilAssembly,&GestureSurface)>, MessageWriter<PlaceFossilPiece>, MessageWriter<PuzzleAction> |
Assembly/puzzle messages only | Active FossilAssembly. |
apply_cloning_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<&ModeTool,With<CloningControl>>, Query<(&CloneLab,Option<&CloneProcess>,&GestureSurface)>, MessageWriter<StartCloneRequest>, MessageWriter<PuzzleAction> |
Clone/puzzle messages only | Active Cloning. |
apply_photo_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<Entity,(With<ZooCamera>,With<PhotoMode>)>, MessageWriter<CapturePhotoRequest> |
Capture requests only | Active Photo. |
apply_show_edit_controls |
Update / GameSet::Input |
MessageReader<ActionRequest>, Res<ActiveImmersiveMode>, Query<(&ShowStage,&ShowSchedule)>, MessageWriter<ShowEdit>, MessageWriter<StartShowRequest> |
Show edit/start messages only | Active ShowEdit. |
update_guest_first_person_view |
Update / GameSet::Presentation |
Res<ActiveImmersiveMode>, Query<&GlobalTransform,With<ControlledEntity>>, Query<(&mut Transform,&CameraMode),With<ZooCamera>>, Res<Assets<WorldDefinitionsAsset>> |
Actual G06 camera Transform only |
GuestView/FirstPerson after subject movement; exact node offset is an evidence gate. |
invalidate_removed_mode_subject |
Update / GameSet::Intent |
Option<Res<ActiveImmersiveMode>>, RemovedComponents<ControlledEntity>, RemovedComponents<ModeTool>, MessageWriter<ExitImmersiveMode> |
Exit request only | Relevant participant removal. |
exit_immersive_modes |
Update / GameSet::Intent |
MessageReader<ExitImmersiveMode>, Option<Res<ActiveImmersiveMode>>, Query<Entity,With<ControlledEntity>>, Query<Entity,Or<(With<ModeTool>,With<GuestViewControl>,With<FirstPersonControl>,With<TrainingControl>,With<FossilSearchControl>,With<FossilAssemblyControl>,With<CloningControl>,With<PhotoControl>,With<ShowEditControl>)>>, Commands, MessageWriter<RestoreCameraMode>, MessageWriter<ImmersiveModeExited> |
Sends restore; removes G31 components/resource | Atomic exactly once; never accesses CameraReturnState. |
exit_modes_on_phase_change |
OnExit(GamePhase::InGame) |
Option<Res<ActiveImmersiveMode>>, Query<Entity,With<ControlledEntity>>, Query<Entity,Or<(With<ModeTool>,With<GuestViewControl>,With<FirstPersonControl>,With<TrainingControl>,With<FossilSearchControl>,With<FossilAssemblyControl>,With<CloningControl>,With<PhotoControl>,With<ShowEditControl>)>>, Commands, MessageWriter<RestoreCameraMode>, MessageWriter<ImmersiveModeExited> |
Sends restore and removes G31 state | Before world teardown. |
The eight entry validators and eight mode-control systems above are the frozen registration lists. They may share private pure math helpers, but must not be recombined into a match-based dispatcher or borrow components from inactive modes.
5. Preflight producer contract
Not applicable: mode/tool/camera node/action definitions are compiled by asset packages. No runtime XML, scripts, filesystem or original command strings.
6. Behavioral evidence and disposition
Search shipped guest view/first-person/camera modes, training, fossil, cloning,
photo and show mode UI/config families; analysis TSVs/oracle for mode classes and
set/cancel commands; live-observe entry restrictions, camera node/controls,
cursor/focus and return semantics. Mine d5657e5a only for evidence in domain
interaction modes/camera, guest_view, dispatch/modes/training, and presentation
camera.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Settle exact proven mode list, prerequisite/subject eligibility, time pause, camera nodes/FOV/collision, input mapping, subject control, UI visibility, completion/cancel/invalid return behavior and nested-mode prohibition. No mode or camera value may be guessed.
7. Required behavior
- Entry fully validates in its one narrow mode system; failure changes nothing. Successful entry asks G06's real camera to retain its own return fact and then configures the existing camera/input/subject.
- One special mode at a time. Mode controls query only relevant components and forward typed actions to natural feature owners.
- Exit for cancel/completion/removal/phase change removes all mode-owned facts, asks G06 to restore its precise prior camera state, clears mode-local controls/focus, and emits once.
- Guest/first-person uses real entity transforms and collision; training/photo/ fossil/cloning/show modes operate on their real feature entities.
- Active resumable state persists only where original save behavior proves it; otherwise load returns safely to ordinary overhead mode.
8. Performance contract
Mode-inactive settled cost is zero queries via run conditions and zero allocations. Active systems allocate zero and are O(1) plus their natural feature query. No render target/scene clone, strings, dynamic dispatch, source parsing, filesystem, or per-frame handle clone. Camera/GPU resources remain G06/render- owned. Tests count inactive/each active mode allocations and GPU bytes/page faults separately.
9. Acceptance criteria
Deliver exact exports plus separate narrow control systems. Tests cover atomic entry failure, mutual exclusion, each proven mode prerequisite/control route, subject removal, exact camera/focus restoration and unique exit. Observable done: enter/navigate/complete/cancel every original special mode with authentic camera/input semantics. No host runtime, renderer wrapper, mode manager, god dispatcher, guessed camera, or leaked marker.
10. Required handoff
Report API/files, exact private control system list, root/dependency/schedule/ reflection/G22 wiring, consumed symbols, evidence, seams, and allocator/page/ GPU/gameplay checks.
G32 — Simulation time and determinism
1. Identity and outcome
The zoo advances on one authoritative fixed clock with authentic pause and speed tiers, stable calendar boundaries, and deterministic random streams that remain reproducible across save/load and parallel system execution. This is a natural simulation primitive consumed by many domains; it is not progression state or a general runtime service. Integration wave 2. Prerequisites: A14 timing policy and A19 starting-world facts. G04 initializes it and G22 persists it.
2. Exclusive ownership
The worker exclusively owns game/src/plugins/simulation_time/. Every other
path is read-only. Root owns module/plugin registration, fixed-schedule cadence,
state wiring, reflection/persistence registration, and Cargo dependencies.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
SimulationTimePlugin |
Plugin |
pub struct SimulationTimePlugin; |
Registers only simulation control, clock, calendar-boundary, and deterministic-seed behavior. |
ZooClock |
Resource |
pub struct ZooClock { pub tick: u64, pub absolute_day: u32, pub tick_in_day: u32 } |
Sole authoritative zoo time; tick_in_day < timing.ticks_per_day; integers avoid accumulated float drift. |
ZooCalendar |
Resource |
pub struct ZooCalendar { pub year: u16, pub month: u8, pub day: u8 } |
Derived from absolute_day using the validated A14 calendar policy; never advanced independently. |
SimulationControl |
Resource |
pub struct SimulationControl { pub speed_tier: u8, pub paused: bool } |
One global player policy; tier indexes the mapped A14 speed table. |
ZooSeed |
Resource |
#[repr(transparent)] pub struct ZooSeed(pub u64); |
Save-stable world seed chosen once at world creation/import. |
RngDomain |
#[repr(u8)] enum |
Reproduction, Behavior, Disease, Guest, Donation, Fossil, Clone, Weather, AmbientSpawner |
Closed code-owned stream-separation vocabulary; adding a stochastic domain is an explicit schema/version change, never an asset ID. |
DeterministicRng |
value | #[repr(C)] pub struct DeterministicRng { state: u64, stream: u64 } with from_entity(seed: ZooSeed, persistent_id: PersistentId, domain: RngDomain) -> Self, from_raw([u64; 2]) -> Self, to_raw(self) -> [u64; 2], next_u32(&mut self) -> u32, unit_f32(&mut self) -> f32, and range_u32(&mut self, upper_exclusive: u32) -> Option<u32> |
Small copyable PCG-family state; explicit raw round-trip is the sole G22 persistence surface. Domain components own instances; exact algorithm/domain discriminants are frozen in native save schema. |
SetSimulationSpeed |
Message |
pub struct SetSimulationSpeed { pub tier: u8 } |
Player/UI request; invalid native tier is rejected without mutation. |
SetSimulationPaused |
Message |
pub struct SetSimulationPaused(pub bool); |
Explicit pause request; mode/scenario policy may reject it before this package. |
SimulationControlChanged |
Message |
pub struct SimulationControlChanged { pub speed_tier: u8, pub paused: bool } |
Emitted once after an actual accepted control change. |
ZooDayAdvanced |
Message |
pub struct ZooDayAdvanced { pub previous_day: u32, pub current_day: u32, pub calendar: ZooCalendar } |
Boundary fact emitted once per crossed zoo day, including multiple fixed ticks processed during catch-up. |
simulation_running |
run condition | pub fn simulation_running(control: Res<SimulationControl>) -> bool |
True exactly when in-game simulation is not paused. |
A14 must expose a mapped SimulationTimingDefinition containing evidenced fixed
frequency, ticks per zoo day, ordered speed multipliers, and calendar policy.
A19 must expose starting tick/day/calendar and optional imported seed. Each
stochastic domain owns a narrowly named component wrapping DeterministicRng
(for example behavior, disease, donation, reproduction); this package does not
collect those streams in a resource or component bag.
4. ECS ownership
Clock, calendar, control, and seed are genuinely world-global facts. RNG state belongs to the entity and stochastic domain that consumes it. There is no timer manager, global mutable random queue, callback scheduler, or list of domain timers.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_simulation_time |
Update / GameSet::Intent |
Commands, MessageReader<BeginWorldLoad>, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>> |
Inserts ZooClock, derived ZooCalendar, SimulationControl, and ZooSeed from the requested A19 scenario/start |
In GamePhase::Loading; before G04 entity streams are seeded. |
apply_simulation_control |
Update / GameSet::Intent |
MessageReader<SetSimulationSpeed>, MessageReader<SetSimulationPaused>, Res<Assets<WorldDefinitionsAsset>>, ResMut<SimulationControl>, ResMut<Time<Virtual>>, MessageWriter<SimulationControlChanged> |
Validated policy and virtual-time pause/relative speed that controls fixed-step accumulation only | Presentation/input systems must read Time<Real> and remain independent of zoo speed/pause. |
advance_zoo_clock |
FixedUpdate / FixedGameSet::Clock |
Res<Assets<WorldDefinitionsAsset>>, ResMut<ZooClock>, ResMut<ZooCalendar>, MessageWriter<ZooDayAdvanced> |
One integer tick and any crossed day/calendar boundary | simulation_running; first fixed simulation system. |
remove_simulation_time |
OnExit(GamePhase::InGame) |
Commands |
Removes world clock/calendar/control/seed | After persistence/world-exit consumers have finished. |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch. A14 preflight recovers and validates time scale, speed tiers, and calendar rules; A19 preflight converts original starting time and seed. This package performs no filesystem discovery, source parsing, conversion, or original-save handling.
6. Behavioral evidence and disposition
Search shipped options/UI/scenario records for speed tiers, pause restrictions,
time-of-day and calendar values; search the oracle and analysis maps for game
clock, calendar, pause, speed, random and seed functions. Mine commit d5657e5a
only for evidence in features/core/time.rs, features/core/random.rs, scenario,
economy, animal, disease and save paths. Live observation settles fixed cadence,
displayed date boundaries, speed multipliers, pause exceptions, and whether
catch-up crosses multiple day events.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence gates: exact original fixed frequency and zoo-day conversion; number, order and multipliers of speed tiers; calendar epoch/month rules; pause behavior in menus and immersive modes; original random distributions where observable; and imported-save seed availability. Do not substitute familiar simulation-game defaults. The PCG-family implementation is an explicit OpenZT2 determinism choice, while recovered probability formulas remain domain behavior.
7. Required behavior
- Paused simulation produces no fixed gameplay ticks or elapsed zoo time. Changing speed affects Bevy virtual time once and does not multiply domain deltas a second time.
- Integer tick/day/calendar transitions are deterministic, emit each crossed day once, and survive save/load exactly. Wages, research, scenarios, ageing, disease and other consumers use these facts instead of private wall clocks.
- Each stochastic entity/domain stream is derived from world seed, stable
persistent identity and a closed
RngDomaindiscriminant. Parallel scheduling cannot alter another domain's sequence. Components persist their current RNG state. Time<Virtual>controls fixed-step accumulation only. Presentation/input useTime<Real>. Gameplay durations and persisted progress are canonical integer ticks orZooDayAdvancedboundaries; no seconds/day float conversion exists.
8. Performance contract
All systems are O(1), allocate zero after warm-up, and perform no filesystem or
asset-payload copies. DeterministicRng methods inline without allocation,
locking, atomics, dynamic dispatch, modulo bias, or global contention. No
per-tick message is emitted; only material control/day boundaries use warmed
Bevy message buffers. Regression checks cover an idle paused frame, one normal
tick, a day boundary, speed change, RNG sequence/rejection sampling, and exact
save/resume continuation. The package owns no mapped payload copy or GPU bytes.
9. Acceptance criteria
Deliver ordinary formatted Rust under the owned directory with every symbol
above. Pure tests use evidence-backed timing fixtures to prove tick/day/calendar
boundaries, pause/speed behavior, deterministic independent streams, unbiased
bounded range generation, and exact resume state. No guessed time constants,
global RNG resource mutation, timer collection, callback queue, todo!, stub,
or fake success. Root validates domain consumers in bulk rather than repeatedly
building the workspace.
10. Required handoff
Report files/API, required dependencies and root state/schedule/reflection/G22 registrations, A14/A19 fields consumed, every domain RNG wrapper expected, resolved/open timing evidence, any integration seam, and allocator/timing/save validation. Root must ensure no domain retains a competing clock, pause flag, speed multiplier, calendar, or global random generator.
G33 — World environment and ambient life
1. Identity and outcome
Each loaded map presents its authored climate, time-of-day lighting, sky, fog, weather and bounded ambient fauna through ordinary Bevy entities and rendering components. Ambient birds, ground animals and water life are normal lightweight entities selected from native biome/time rules; there is no environment manager, source token state, or second rendered world. Integration wave 2. Prerequisites: A04–A08, A11, A14, A15, A19, G05, G12, G14 and G32.
2. Exclusive ownership
The worker exclusively owns game/src/plugins/environment/. Every other path
is read-only. Root owns module/plugin/render-feature registration, schedule and
state wiring, persistence policy, and dependency changes.
Bevy/wgpu exclusively own lights, cameras, fog resources, materials, sky/effect rendering and GPU state. G33 owns recovered environmental selection and timing policy only; it must not build a renderer, scene world, weather host, or copied presentation state.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
EnvironmentPlugin |
Plugin |
pub struct EnvironmentPlugin; |
Registers environment instantiation, presentation, weather and ambient-life systems only. |
WorldEnvironment |
Component |
pub struct WorldEnvironment { pub definition: AssetId } |
Exactly one entity for the loaded map; points to A14 climate/environment records. |
Daylight |
Component |
pub struct Daylight { pub fraction: f32 } |
Derived normalized day fraction in [0,1) from G32 clock and A14 timing policy. |
Weather |
Component |
pub struct Weather { pub definition: AssetId, pub elapsed_ticks: u64, pub duration_ticks: u64 } |
Current validated weather state on the environment entity; duration comes from native rules. |
WeatherTransition |
Component |
pub struct WeatherTransition { pub from: AssetId, pub to: AssetId, pub start_tick: u64, pub duration_ticks: u32 } |
Temporary authored interpolation; removed exactly at completion. |
EnvironmentLight |
Component |
pub struct EnvironmentLight { pub keyframe: u16 } |
Marks the actual Bevy directional-light entity projected from the active native light curve. |
EnvironmentSky |
Component |
pub struct EnvironmentSky { pub definition: AssetId } |
Marks the actual sky presentation entity/handle; no copied sky state resource. |
EnvironmentFog |
Component |
pub struct EnvironmentFog; |
Marks the authoritative G06 camera carrying Bevy DistanceFog; fog state is not copied to a child world. |
Wind |
Component |
pub struct Wind { pub direction: Vec2, pub speed_mps: f32 } |
Current finite world-XZ wind fact on the environment entity. |
EnvironmentRandom |
Component |
pub struct EnvironmentRandom(pub DeterministicRng); |
G32 domain-local weather stream on the environment entity; save-relevant when weather is stochastic. |
AmbientSpawner |
Component |
pub struct AmbientSpawner { pub definition: AssetId, pub next_tick: u64, pub random: DeterministicRng } |
One authored biome/class spawner entity; RNG is G32 domain-local state. |
AmbientAnimal |
Component |
pub struct AmbientAnimal { pub definition: AssetId, pub despawn_tick: u64 } |
Lightweight nonsavable ambient entity using normal A15 prefab/model/animation handles. |
SetWeather |
Message |
pub struct SetWeather { pub operation: Entity, pub definition: AssetId, pub transition_ticks: Option<u32> } |
Caller-owned operation gives exact asynchronous correlation; only native weather IDs accepted. |
WeatherRequestApplied |
Message |
pub struct WeatherRequestApplied { pub operation: Entity, pub definition: AssetId } |
Echoed once when the requested weather becomes authoritative. |
WeatherRequestRejected |
Message |
pub struct WeatherRequestRejected { pub operation: Entity, pub definition: AssetId } |
Echoed once for invalid/busy/rule-denied request; weather unchanged. |
WeatherChanged |
Message |
pub struct WeatherChanged { pub previous: AssetId, pub current: AssetId } |
Emitted once after a weather transition actually commits. |
A14 must expose typed EnvironmentDefinition, LightKeyframe, FogKeyframe,
SkyKeyframe, WeatherDefinition, WeatherTransitionRule,
AmbientSpawnDefinition, its exact AmbientClass, and their TableRange link tables, including
evidence-derived limits, probabilities, biome/daylight eligibility, lifetimes,
prefabs, audio and effects. A19 maps reference one environment definition. A11
owns audio playback, A18 owns effect playback, and this package attaches their
natural emitter/instance components rather than forwarding through facades.
4. ECS ownership
Global map environment is one real entity because its lighting/weather facts share lifecycle. Lights, sky and fog are actual Bevy presentation entities. Each ambient rule is a spawner entity and each ambient animal a normal entity. There is no environment resource, ambient population collection, copied render state, source-document object, or global random stream.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_world_environment |
Update / GameSet::Intent |
MessageReader<WorldLoadFinished>, Commands, Res<Assets<WorldScenarioAsset>>, Res<Assets<WorldDefinitionsAsset>>, Res<ZooSeed>, ResMut<PersistentIdAllocator>, Query<(Entity,&WorldRoot)>, Query<(),With<WorldEnvironment>> |
For a new world only, resolves WorldRoot.scenario and creates the authoritative environment plus bounded ambient-spawner entities, each with the root WorldMember and allocator-issued PersistentId |
No existing environment; G22-restored worlds already contain environment/spawner facts. |
hydrate_environment_presentation |
OnEnter(GamePhase::InGame) |
Commands, Query<(Entity,&WorldEnvironment,&WorldMember),Added<WorldEnvironment>>, Query<Entity,With<ZooCamera>>, Query<(),With<EnvironmentLight>>, Res<Assets<WorldDefinitionsAsset>> |
Creates only actual light/sky presentation entities carrying the environment's WorldMember, and attaches EnvironmentFog/Bevy fog to the authoritative camera |
Runs for both fresh and G22-restored environment facts; never creates simulation state. |
project_daylight |
Update / GameSet::Presentation |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(&mut Daylight, &WorldEnvironment, &Children)>, Query<(&mut DirectionalLight, &mut Transform), With<EnvironmentLight>>, Query<&mut DistanceFog, (With<ZooCamera>, With<EnvironmentFog>)> |
Interpolated Bevy light transform/color/illuminance and camera fog parameters | Only when source clock crosses a native presentation sample/keyframe interval. |
advance_weather |
FixedUpdate / FixedGameSet::Think |
Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity, &WorldEnvironment, &mut Weather, Option<&mut WeatherTransition>, &mut Wind, &mut EnvironmentRandom)>, Commands, MessageWriter<WeatherChanged> |
Current weather/transition/wind/RNG and attached A11/A18 emitters | Due environment only; G32 deterministic domain stream. |
apply_weather_requests |
FixedUpdate / FixedGameSet::Act |
MessageReader<SetWeather>, Res<Assets<WorldDefinitionsAsset>>, Query<(Entity,&WorldEnvironment,&Weather,Option<&WeatherTransition>)>, Commands, MessageWriter<WeatherRequestApplied>, MessageWriter<WeatherRequestRejected> |
Inserts validated transition or changes immediately; retains operation until transition completion and echoes exact operation once | After scenario intent; invalid/busy request emits rejection without mutation. |
spawn_ambient_animals |
FixedUpdate / FixedGameSet::Think |
Commands, Res<ZooClock>, Res<Assets<WorldDefinitionsAsset>>, Res<TerrainIndex>, Res<SpatialGrid>, Query<(&TerrainChunk,Option<&EditedTerrainSamples>)>, Query<(&WorldMember,&mut AmbientSpawner)>, Query<&SpatialCell> |
Spawns due A15 prefab with AmbientAnimal, the same WorldMember, and natural presentation/locomotion facts |
Due spawners only; uses G14 CSR cell ranges and respects compiled population bounds/surface rules. |
despawn_expired_ambient_animals |
FixedUpdate / FixedGameSet::Cleanup |
Commands, Res<ZooClock>, Query<(Entity, &AmbientAnimal)> |
Despawns expired ambient entities | Query filtered/indexed to ambient entities; no generic world removal path. |
apply_environmental_welfare |
FixedUpdate / FixedGameSet::Think |
Res<Assets<SpeciesCatalogAsset>>, Res<Assets<WorldDefinitionsAsset>>, Query<(&WorldEnvironment,&Weather,&Daylight),Or<(Changed<Weather>,Changed<Daylight>)>>, Query<(Entity,&SpeciesHandle,&HabitatMember),With<Animal>>, MessageWriter<AdjustEnvironment> |
Emits exact G12 AdjustEnvironment { animal, delta_q16 } messages only |
Only if evidence proves a gameplay effect; residents are grouped by habitat/environment and G12 is never mutated directly. |
remove_world_environment |
OnExit(GamePhase::InGame) |
Commands, Query<Entity,Or<(With<WorldEnvironment>,With<EnvironmentLight>,With<EnvironmentSky>,With<AmbientSpawner>,With<AmbientAnimal>)>> |
Despawns environment/presentation/spawner/ambient entities | Before world teardown completes; G04 world unload remains an idempotent backstop through WorldMember. |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch. A14 preflight resolves environment maps, light rigs, fog, sky, weather, ambient manager documents and source precedence into fixed typed records; A19 selects the final environment. This plugin never parses XML, Z2F, original render records or source weather tokens.
6. Behavioral evidence and disposition
Search shipped environment maps, light rigs, sky/fog/weather records, biome
ambient documents, ambient species/prefabs and sound/effect references. Search
the oracle and analysis maps for environment, lighting, time-of-day, weather and
ambient-AI classes/functions. Mine commit d5657e5a only for evidence in
features/presentation/environment.rs, features/simulation/ambient.rs, terrain,
audio and world rendering; its strings, managers, defaults and copied runtime
state are not reusable. Live comparison settles keyframe interpolation, light
direction/color, fog/sky layering, weather cadence, and ambient placement.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence gates: complete environment/light/fog/sky source vocabulary; day-fraction mapping and interpolation; weather types/transitions/gameplay effects; wind; ambient classes, eligible biomes/day periods, population bounds, spawn attempts, lifetimes, movement and save exclusion. The old code's fallback constants are not evidence and must not be copied.
7. Required behavior
- A loaded map produces its authored initial environment with no fallback light, sky, fog, weather or ambient rule guessed in Rust. Presentation follows G32 zoo time and remains frame-smooth without mutating the authoritative clock.
- Weather transitions interpolate only evidenced properties, attach normal A11 audio/A18 effects, emit one completion fact, and persist current state when original behavior requires it.
- Ambient spawning uses compiled eligibility, bounded population and G32 deterministic per-spawner streams. Spawned entities use actual terrain/water, ordinary transforms/prefabs/animation and focused locomotion; expiry removes them directly. They never enter zoo population/economy/save facts unless evidence explicitly classifies them as gameplay animals.
- Mods extend the fixed native environment/weather/ambient record vocabulary; unknown semantic fields block preflight conversion.
8. Performance contract
Settled systems allocate zero. Daylight work is O(environment presentation entities) only at relevant clock changes. Weather is O(active environments), and ambient spawning O(due spawners × bounded attempts) with O(1) terrain lookup and bounded spatial candidates. No per-frame strings, keyframe vectors, whole-world queries, source parsing, asset-payload clone, or environment graph. Spawn ECS allocation is an explicit bounded transition; A04–A08/A11/A18 own mapped/GPU/ audio storage. Regression checks cover unchanged presentation, weather tick, failed ambient attempt, expiry and population cap while tracking heap, mapped pages, GPU bytes and audio buffers separately.
9. Acceptance criteria
Deliver ordinary formatted Rust under the owned directory exporting every
symbol and focused system above. Evidence-backed fixtures prove keyframe
interpolation, weather transition uniqueness, terrain-class placement,
day/biome eligibility, deterministic bounded spawning, population cap, expiry
and cleanup. Observable completion is a representative map matching original
lighting/sky/fog/weather while ambient life appears and disappears authentically.
No guessed defaults, manager, copied environment resource, global RNG, source
token dispatcher, placeholder weather, todo!, or fake effect.
10. Required handoff
Report files/API, root Bevy render/schedule/reflection/G22 registrations, A03/ A04–A08/A11/A14/A15/A18/A19 and G04/G05/G12/G14/G32 symbols, resolved/open environment evidence, temporary seams, and allocator/page/GPU/audio/visual validation. Root must remove any overlap with terrain presentation while preserving G05 ownership of terrain data/editing and G33 ownership of map-wide environment presentation.
G34 — Display and graphics settings
1. Identity and outcome
The source-driven options screen changes native window, presentation quality
and UI scale through ordinary Bevy state. Audio settings remain
A11's AudioMixSettings; input bindings remain G03's InputBindings; G22
persists all three natural owners. This package does not create a settings host
or copy engine state. Integration wave 2. Prerequisites: G01, G02 and G06 plus
the prepared presentation contracts from A04–A08, A16 and A18.
2. Exclusive ownership
The worker exclusively owns reconstruction/game/src/plugins/settings/.
Every other path is read-only. Root owns module/plugin registration, schedule
ordering and G01/G02/G22 wiring.
Bevy window/UI/audio/input and Bevy/wgpu render resources are the only settings targets. G34 validates policy and applies changed values directly; it must not introduce a platform window backend, graphics abstraction, settings host, or mirrored renderer/device state.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
SettingsPlugin |
Plugin |
pub struct SettingsPlugin; |
Registers validation and changed-only application systems. |
DisplayMode |
enum | Windowed, BorderlessFullscreen |
Closed portable mode vocabulary. |
FramePacing |
enum | VSync, Immediate, Mailbox |
Maps to a supported Bevy/wgpu present mode; unsupported choices reject. |
DisplaySettings |
Resource |
pub struct DisplaySettings { pub width:u32, pub height:u32, pub mode:DisplayMode, pub pacing:FramePacing, pub ui_scale_permille:u16 } |
Sole live display policy; validated resolution and UI scale 500..=2000. |
ShadowQuality |
enum | Off, Low, High |
Selects pre-existing pipeline/resource policy; never compiles shaders. |
EffectQuality |
enum | Low, High |
Selects A18 compiled capacity/LOD policy. |
GraphicsSettings |
Resource |
pub struct GraphicsSettings { pub msaa_samples:u8, pub shadows:ShadowQuality, pub water_reflections:bool, pub effects:EffectQuality } |
Sole live quality policy; MSAA is exactly 1, 2, 4, or 8 when supported. |
GraphicsCapabilities |
Resource |
pub struct GraphicsCapabilities { pub msaa_mask:u16, pub present_mode_mask:u8, pub shadows:bool, pub water_reflections:bool } |
Immutable hardware/surface facts populated once from Bevy's render adapter and primary surface; not settings or copied render state. |
SetDisplaySettings |
Message |
pub struct SetDisplaySettings(pub DisplaySettings); |
One complete proposed display policy from G01/G02. |
SetGraphicsSettings |
Message |
pub struct SetGraphicsSettings(pub GraphicsSettings); |
One complete proposed quality policy. |
SettingsApplied |
Message |
pub struct SettingsApplied { pub display:bool, pub graphics:bool } |
Emitted once after actual accepted changes. |
SettingsRejected |
Message |
pub struct SettingsRejected { pub reason:SettingsFailure } |
Rejection leaves both live resources unchanged. |
SettingsFailure |
enum | InvalidResolution, InvalidScale, UnsupportedPresentMode, UnsupportedMsaa, UnsupportedFeature |
Fixed localizable failure vocabulary. |
G01 owns options-screen navigation, G02 owns UI projection, G22 owns durable profile I/O, A11 owns mix volumes, and G03 owns bindings. This package consumes those owners directly and must not aggregate them into another options object.
4. ECS ownership
The two resources are genuinely process-global presentation policy. Actual window, camera and renderer objects remain Bevy-owned.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
validate_display_settings |
Update / GameSet::Intent |
MessageReader<SetDisplaySettings>, ResMut<DisplaySettings>, Query<&Window,With<PrimaryWindow>>, MessageWriter<SettingsApplied>, MessageWriter<SettingsRejected> |
Replaces display policy only after full capability/range validation | Requests only; before apply. |
validate_graphics_settings |
Update / GameSet::Intent |
MessageReader<SetGraphicsSettings>, ResMut<GraphicsSettings>, Res<GraphicsCapabilities>, MessageWriter<SettingsApplied>, MessageWriter<SettingsRejected> |
Replaces quality policy only after complete device validation | Requests only; before apply. |
apply_window_settings |
Update / GameSet::Presentation |
Res<DisplaySettings>, Query<&mut Window,With<PrimaryWindow>> |
Bevy WindowResolution, WindowMode and PresentMode |
resource_changed::<DisplaySettings> only. |
apply_camera_render_settings |
Update / GameSet::Presentation |
Commands, Res<GraphicsSettings>, Query<Entity,With<ZooCamera>> |
Actual Bevy camera Msaa component |
Graphics settings changed; no camera copy. |
apply_ui_scale |
Update / GameSet::Ui |
Res<DisplaySettings>, Query<&mut Transform,(With<UiDocumentRoot>,Or<(Added<UiDocumentRoot>,Changed<Transform>)>)> |
Root UI transform scale derived from permille | Display settings changed or a UI root was added. |
apply_shadow_quality |
Update / GameSet::Presentation |
Res<GraphicsSettings>, Query<&mut DirectionalLight,With<EnvironmentLight>> |
Existing Bevy directional-light shadow enablement/quality fields only | Graphics settings changed only; no source/pipeline compilation. |
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch.
Target-profile texture/shader/codec decisions remain preflight-owned. Runtime
settings only choose among capabilities and variants already compiled into the
pack. A18's pooled effect systems and A16/G25 water presentation read changed
GraphicsSettings directly in their natural owner; G34 does not proxy or copy
those facts.
active profile.
6. Behavioral evidence and disposition
Search shipped options UI/XML, default option records and localization; search
the C++ oracle/analysis for display, resolution, fullscreen, antialiasing,
shadow, reflection and graphics-option functions. Mine commit d5657e5a only
for already-discovered option vocabulary and UI behavior. Live original behavior
settles apply/cancel confirmation, defaults and which changes are immediate.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
The original option set and ranges are evidence gates. OpenZT2 may expose modern resolution, frame-pacing and controller choices, but they must be labeled as extensions and capability-validated rather than presented as recovered rules.
7. Required behavior
- Invalid or unsupported proposals leave current resources and Bevy objects unchanged and produce one typed rejection.
- Accepted values update the actual primary
Window, active camera and Bevy presentation components once. They never trigger archive conversion, shader compilation, texture decode or recreation of the world. - G01's Apply/Cancel flow observes the result message. G22 serializes these two resources alongside A11/G03 settings and restores them before the first source-driven screen is presented.
- Mods may reference prepared quality variants but cannot extend this behavior with command IDs, source strings or runtime callbacks.
8. Performance contract
Idle settings cost is zero systems through change run conditions, zero allocations and zero filesystem work. Applying a change is O(one window + active cameras + affected presentation entities), occurs only on request, and reuses prepared pipelines/GPU resources. Record any unavoidable swapchain or render- target recreation separately from Rust heap, mapped pages and steady GPU bytes.
9. Acceptance criteria
Deliver ordinary formatted Rust under the owned directory with every symbol and
system above. Narrow tests cover range/capability rejection, atomic unchanged
state, accepted window mapping, changed-only application and persistence round-
trip handoff. No settings manager, aggregate options host, renderer wrapper,
runtime source parsing, shader compilation, todo!, stub or fake success.
10. Required handoff
Report files/API, exact Bevy 0.19 window/UI/MSAA APIs used, root registrations, G01/G02/G06/G22 and A04–A08/A11/A16/A18 symbols, evidence questions, temporary API-name seams, and allocator/swapchain/GPU/visual validation. Root must erase any upstream-name shim and preserve A11/G03 as the sole audio/input authorities.
G35 — Native content mapping and residency
1. Identity and outcome
The game opens and validates exactly one A01-resolved native profile, maps it
read-only once, exposes its byte ranges through Bevy's asset source/loaders, and
applies the selected PreloadMode without creating a heap copy. Off performs
no eager page touches, Core greedily faults the core gameplay closure, and
AllEnabled greedily faults the entire selected/resolved profile, all within a
safe available-memory budget. Integration wave 1. Prerequisites: frozen native
pack transport and the launcher-supplied profile path/mode.
2. Exclusive ownership
The implementation lives in reconstruction/game/src/plugins/content/.
The superseded game/src/content.rs is available only through Git history;
it is not a second content implementation. Crate/plugin registration and the
small native-pack API extension named below are central integration concerns.
No family loader or gameplay plugin is owned by this package.
Existing memmap2 and rkyv are the mandatory mapping/archive implementation.
G35 may add page-fault policy and Bevy asset-source integration only. It must not
copy the mapping into Arc<[u8]>, decode a second content graph, introduce an
asset store, or add another filesystem/cache layer.
3. Frozen public contract
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ContentPlugin |
Plugin |
pub struct ContentPlugin { profile:Arc<MappedPack>, preload:PreloadMode }; pub fn open(path:impl AsRef<Path>, expected_id:AssetId, expected_target:TargetProfile, preload:PreloadMode)->io::Result<Self> |
Sole game startup owner of opening/validating the launcher-selected resolved mapping; expected values come from A01's frozen arguments. |
ContentMap |
Resource |
pub struct ContentMap(pub Arc<MappedPack>); |
One immutable mapped profile retained only so Bevy family loaders/asset source share its lifetime; never decoded content state. |
ContentReady |
Message |
pub struct ContentReady { pub assets:u32, pub mapped_bytes:u64, pub prefaulted_bytes:u64, pub mode:PreloadMode } |
Emitted once after asset-source registration and requested eager touches complete. |
ContentLoadFailed |
Message |
pub struct ContentLoadFailed { pub reason:ContentFailure } |
Boot remains blocked; no fallback source path. |
ContentFailure |
enum | Open, InvalidPack, WrongProfile, WrongTarget, InsufficientBootstrapMemory, AssetSourceRegistration |
Fixed startup failures. |
ResidencyBudget |
value | pub struct ResidencyBudget { pub available_bytes:u64, pub safety_headroom_bytes:u64, pub touch_budget_bytes:u64 } |
Snapshot of startup OS memory facts; not retained live state after preload. |
PreloadReport |
value | pub struct PreloadReport { pub requested:PreloadMode, pub touched_bytes:u64, pub skipped_bytes:u64, pub major_faults:u64, pub minor_faults:u64 } |
Startup diagnostics only, then emitted/recorded by performance tooling. |
memory_budget |
function | pub fn memory_budget(total_bytes:u64, available_bytes:u64)->ResidencyBudget |
headroom=max(total_bytes/8, 2_GiB) and touch budget is available-headroom, saturating; explicit OpenZT2 policy. |
preload_groups |
function | pub fn preload_groups(mode:PreloadMode)->&'static [LoadGroup] |
Off=[]; Core=[Bootstrap,Menu,Zoo,Resident]; AllEnabled=[Bootstrap,Menu,Zoo,Resident,OnDemand,Stream]. |
Root extends MappedPack once with
pub fn pack_id(&self) -> AssetId, pub fn target_profile(&self) -> TargetProfile and
pub fn prefault_budgeted(&self, groups:&[LoadGroup], budget_bytes:u64)->u64.
The first two expose already-validated header values so startup can reject the
wrong resolved profile or target without re-reading its header. The prefault function
walks final catalogue order and touches at most the requested number of 4-KiB
payload bytes, including a partial final asset when needed. It allocates
nothing, retains nothing, and cannot see original pack/DLC/mod layers.
4. ECS ownership
ContentMap is genuine immutable global ownership of the mapping used by every
typed loader. No asset catalogue, decoded record graph, residency mirror, byte
cache or background content world is retained.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
install_content_source |
plugin build |
owned Arc<MappedPack>, App::register_asset_source, Commands/resource insertion equivalent |
Bevy asset source backed by mapped slices and one ContentMap |
Before any family loader or GamePhase::Boot system. |
prefault_content |
Startup / GameSet::Diagnostics |
Res<ContentMap>, plugin PreloadMode, OS sysinfo(2) memory snapshot, MessageWriter<ContentReady>, MessageWriter<ContentLoadFailed> |
Page touches only plus one report/message | Once; after source installation and before shell bootstrap requests. |
The asset reader returns borrowed SliceReader views into MappedPack; family
loaders acquire MappedAsset owners from the same Arc. Bevy Handle<T> and
Assets<T> remain the only asset lifecycle/cache authority.
5. Preflight producer contract
Not applicable: the engine consumes only the final native profile. A01 and the
family producers have already resolved overrides, dependencies, target formats
and LoadGroup. AllEnabled therefore means every asset in the selected final
profile, never unselected source archives.
6. Behavioral evidence and disposition
Use the current native/src/pack.rs, converter/src/pack.rs, allocation/mapping
regressions and Linux page-fault measurements. Commit d5657e5a may identify
previous startup asset demand but contributes no architecture. The original
game is not authority for the OpenZT2 preload improvement.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence questions are limited to measuring the headroom default on Jarvis and representative lower-memory machines. A policy change alters one pure formula; it cannot add prediction caches, source-pack semantics or a content manager.
7. Required behavior
- Opening performs the sole game-content file open, read-only mmap and complete
header/catalogue-range/catalogue-hash validation, then compares the launch
profile ID and target. It deliberately does not scan payload pages in
Off; each family performs its one typed rkyv/payload validation when first hydrated (or during requested prefault/bootstrap hydration). After that boundary, asset hydration performs noopen/read/preadagainst game content and copies no payload into a general heap buffer. Offtouches no pages proactively.Corefollows the exact core group order.AllEnabledfollows all final-profile groups. Both stop exactly at the memory budget and report skipped bytes rather than swapping the workstation.- Normal demand paging remains valid for untouched bytes. Streaming assets keep bounded decoder/GPU buffers owned by their family; this package owns none.
- A mapping or target failure blocks Boot explicitly. No original Z2F/XML path, converter invocation or fallback asset source exists in the game binary.
8. Performance contract
Map/open validation is O(catalogue); prefault is O(pages touched); lookup remains
the native pack's allocation-free binary lookup. Heap growth is one Arc/Bevy
registration only, never proportional to payload bytes. Regression checks
measure wall time, minor/major faults and /proc/<pid>/smaps_rollup mapped/RSS
separately from Rust heap. After ContentReady, game-content open/read/pread
syscalls are forbidden and settled asset-handle cloning allocates zero.
9. Acceptance criteria
Deliver ordinary formatted Rust under the owned directory with every symbol and
system above. Narrow tests cover pure budget/group selection, Off zero touches,
bounded partial Core/AllEnabled touches and one shared mapping owner. Root bulk
checks syscall/page-fault/allocation contracts. No copied pack bytes, asset
store, runtime converter, layer merge, host, manager, prediction graph, todo!,
stub or fake ready signal.
10. Required handoff
Report files/API, root deletion of old content.rs, native pack_id,
target_profile and prefault_budgeted additions, asset-source/plugin ordering,
profile/target checks,
OS memory API, A01 launch arguments, family-loader use, and exact heap/mapping/
page-fault/syscall results. Any temporary source-name shim is removed during
destructive integration.
G36 — Maintenance and Sanitation
1. Identity and outcome
This package makes deterioration, full waste containers, and loose litter real zoo facts. Placed objects wear only according to converted authored rules, completed services add only their authored wear and waste, maintenance staff receive deduplicated repair/empty/sweep work, and successful work mutates the small target component directly. Guests can observe litter, the information UI can present the selected fact, progression receives one changed-only cleanliness value, and native saves restore the same condition and waste.
These responsibilities form one natural greenfield-Bevy domain: the facts that make an object broken or dirty, the events that change those facts, and the three staff effects that resolve them. There is no maintenance manager, dirty entity bag, copied facility model, or package-local world.
Integration wave: 3. Logical prerequisites: A03 species, A14 world definitions, G04 world spawn and persistent identity, G09 habitats, G10 placement, G11 animals, G18 services, G20 staff jobs, and G32 simulation time. G21, G22, and G23 consume the resulting facts after root integration.
2. Exclusive ownership
The worker exclusively owns
reconstruction/game/src/plugins/maintenance/. Every other repository path is
read-only. Root owns crate declarations, plugin registration, schedule
ordering, reflection, dependencies, asset registration, and the adjacent
contract corrections listed below. A collision is reported; it never expands
the worker's ownership.
3. Frozen public contract
All amounts and thresholds are exact native integer units. Permille values are
always 0..=1000. Only this package mutates Condition, WasteContainer, or
Litter during normal gameplay; G22 may insert their validated saved values.
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
MaintenancePlugin |
Plugin |
pub struct MaintenancePlugin; |
Registers maintenance facts, their component hooks, focused mutation/job systems, and the derived cleanliness projection only. |
Condition |
Component |
#[repr(transparent)] pub struct Condition(pub u16); |
Current structural condition permille: 1000 is fully sound and 0 is exhausted. Present only when A14 has an authored maintenance definition. |
WasteContainer |
Component |
pub struct WasteContainer { pub units: u16, pub capacity: u16 } |
Bounded contained waste; units <= capacity, and capacity is the nonzero immutable A14 value for this entity definition. |
Litter |
Component |
pub struct Litter { pub amount_units: u16 } |
Positive guest/facility loose-waste amount on a dedicated world entity. Every live litter entity also has DefinitionId, Transform, WorldMember, and PersistentId. |
HabitatWaste |
Component |
pub struct HabitatWaste { pub amount_units: u16 } |
Positive animal waste on a dedicated world entity cleaned by CleanHabitat; it carries the same identity/world/transform facts as litter. |
WasteProduction |
Component |
pub struct WasteProduction { pub next_tick: u64 } |
Per-animal G32 deadline derived from its A03 species rule; saved so load cannot duplicate or skip production. |
ZooCleanliness |
Resource |
#[repr(transparent)] pub struct ZooCleanliness(pub u16); |
Derived zoo cleanliness permille. It is the sole G23 cleanliness input, changes only when a contributing live fact changes, and is rebuilt rather than saved. |
Root must freeze these exact additions in A14 rather than allowing the worker to define a second asset schema:
MaintenanceDefinition { id: AssetId, object: AssetId, initial_condition_permille: u16, deterioration_per_zoo_day_permille: u16, repair_below_permille: u16, waste_capacity_units: u16, empty_at_units: u16, litter_definition: Option<AssetId>, litter_local_offset_cm: [i16; 3], service_effects: TableRange };ServiceMaintenanceEffect { service: AssetId, condition_loss_permille: u16, contained_waste_units: u16, loose_litter_units: u16 }, indexed by a definition'sservice_effects;CleanlinessPolicy { condition_weight: u16, waste_weight: u16, litter_weight: u16, litter_reference_units: u32 }, exactly one in the resolved A14 archive;maintenance_definitionssorted byobject, owner-contiguousservice_maintenance_effects,cleanliness_policy(), and allocation-freefind_maintenance_by_object(AssetId)accessors on the mapped A14 owner;DefinitionFamily::Maintenanceand deterministic preflight routing for those records.
The A14 producer rejects permille overflow, zero capacity paired with nonzero
waste rules, empty_at_units > waste_capacity_units, duplicate service IDs
within one definition, nonzero loose-litter output without a resolved litter
definition, zero total cleanliness weight, or zero litter reference when its
weight is nonzero. A source object without evidenced maintenance behavior has
no MaintenanceDefinition; the converter must not synthesize defaults.
Root must also add G20
pub struct StaffJobCancelled { pub kind: StaffJobKind, pub target: Entity }
as a message emitted before cancellation removes a still-live target's job.
This is a natural job-lifecycle fact used to re-request unresolved work, not a
maintenance facade. G20 remains the sole owner of StaffJobRequest,
StaffJob, JobClaim, JobProgress, and StaffJobCompleted.
4. ECS ownership
Condition, WasteContainer, Litter, HabitatWaste, and WasteProduction
are the authoritative per-entity
facts. The plugin privately owns exactly
CleanlinessTotals { condition_sum:u64, condition_count:u32, waste_units:u64, waste_capacity:u64, litter_units:u64, dirty:bool } as a
resource. Component
add/remove hooks add or subtract their current contribution, including direct
G10 deletion and G04 world unload. Each mutation system replaces its old and
new contribution in the same call that changes the component. The totals are a
bounded derived reduction, not an entity registry or parallel world, and never
retain entity IDs. Loose-waste units are the checked sum of Litter and
HabitatWaste amounts.
MaintenancePlugin::build registers on_add/on_remove hooks for Condition,
WasteContainer, Litter, and HabitatWaste. Each hook reads only the affected
component through Bevy's DeferredWorld, mutates CleanlinessTotals, and marks
dirty; it performs no query, allocation, entity lookup table, or deferred
event. Systems that mutate an existing component borrow
ResMut<CleanlinessTotals> and apply the old→new delta in the same call.
Four private zero-sized components—RepairWork, EmptyBinWork,
SweepLitterWork, and CleanHabitatWork—classify newly added G20 job entities
once. They contain no
state and exist solely so the three execution systems have disjoint archetypal
queries instead of rescanning or write-locking every StaffJob.
For condition, an empty population scores 1000; otherwise the score is the
nearest-integer condition_sum / condition_count. Waste scores 1000 when
total capacity is zero and otherwise
1000 - min(1000, round(waste_units * 1000 / waste_capacity)). Litter scores
1000 - min(1000, round(litter_units * 1000 / litter_reference_units)). ZooCleanliness is the nearest-integer weighted
mean of those three scores using the A14 policy. Intermediate arithmetic uses
checked u64; invalid archive bounds never reach the engine.
| System | Schedule/set | Exact query and parameters | Writes | Run condition/order |
|---|---|---|---|---|
initialize_maintenance_facts |
FixedUpdate / FixedGameSet::Act |
Commands; Res<Assets<WorldDefinitionsAsset>>; Query<(Entity,&DefinitionId),(Added<DefinitionId>,Without<Condition>)> |
Inserts authored Condition and, when capacity is nonzero, WasteContainer { units:0, capacity } |
In game, after G04/G10 entity creation; missing maintenance definition is a valid no-op. |
deteriorate_maintainable_objects |
FixedUpdate / FixedGameSet::Think |
MessageReader<ZooDayAdvanced>, Res<Assets<WorldDefinitionsAsset>>, Query<(&DefinitionId,&mut Condition)>, ResMut<CleanlinessTotals> |
Saturating condition loss and exact aggregate delta | Once per crossed zoo day, multiplying the authored per-day loss by current_day - previous_day with checked arithmetic; never per frame. |
apply_service_maintenance_effects |
FixedUpdate / FixedGameSet::Act |
Commands, MessageReader<ServiceCompleted>, Res<Assets<WorldDefinitionsAsset>>, ResMut<PersistentIdAllocator>, Query<(&DefinitionId,&Transform,&WorldMember,Option<&mut Condition>,Option<&mut WasteContainer>)>, ResMut<CleanlinessTotals> |
Authored condition/waste deltas; zero or one litter entity per completion with DefinitionId, transformed authored offset, WorldMember, and synchronously allocated PersistentId |
After G18 service completion and before maintenance-request systems; an effect is applied only when both facility definition and exact service ID match. |
initialize_waste_production |
FixedUpdate / FixedGameSet::Act |
Commands, Res<ZooClock>, Res<Assets<SpeciesCatalogAsset>>, Query<&SpeciesHandle,(Added<SpeciesHandle>,Without<WasteProduction>)> |
Inserts the first exact WasteProduction deadline only for a positive A03 rule |
After G11 animal creation; no random/default cadence. |
produce_animal_waste |
FixedUpdate / FixedGameSet::Act |
Commands, Res<ZooClock>, Res<Assets<SpeciesCatalogAsset>>, ResMut<PersistentIdAllocator>, Query<(&SpeciesHandle,&GlobalTransform,&WorldMember,&mut WasteProduction),With<Animal>> |
At a due tick spawns one HabitatWaste entity with the species waste DefinitionId, transform, same WorldMember, fresh PersistentId, and advances the checked deadline |
Due animals only; validates/allocates before spawn and never loops once per missed tick after a large time jump. |
request_repairs |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldDefinitionsAsset>>; Query<(Entity,&DefinitionId,&Condition),Changed<Condition>>; MessageWriter<StaffJobRequest> |
One Repair request candidate |
Emits only while condition is strictly below the authored threshold; G20 deduplicates (kind,target). |
request_bin_emptying |
FixedUpdate / FixedGameSet::Think |
Res<Assets<WorldDefinitionsAsset>>; Query<(Entity,&DefinitionId,&WasteContainer),Changed<WasteContainer>>; MessageWriter<StaffJobRequest> |
One EmptyBin request candidate |
Emits only at or above the authored nonzero empty threshold; G20 deduplicates (kind,target). |
request_litter_sweeping |
FixedUpdate / FixedGameSet::Think |
Query<(Entity,&Litter),Added<Litter>>; MessageWriter<StaffJobRequest> |
One SweepLitter request candidate |
Positive validated amount only; G20 deduplicates (kind,target). |
request_habitat_cleaning |
FixedUpdate / FixedGameSet::Think |
Query<(Entity,&HabitatWaste),Added<HabitatWaste>>, MessageWriter<StaffJobRequest> |
One CleanHabitat request candidate |
Positive validated amount only; G20 deduplicates (kind,target). |
requeue_cancelled_maintenance_work |
FixedUpdate / FixedGameSet::Think |
MessageReader<StaffJobCancelled>, Query<&Condition>, Query<&WasteContainer>, Query<&Litter>, Query<&HabitatWaste>, Res<Assets<WorldDefinitionsAsset>>, MessageWriter<StaffJobRequest> |
Replacement request only if the direct target fact still meets its rule | After G20 cancellation and before job creation; dead or already-resolved targets produce nothing. |
classify_maintenance_jobs |
FixedUpdate / FixedGameSet::Think |
Commands, Query<(Entity,&StaffJob),Added<StaffJob>> |
Inserts exactly one private RepairWork, EmptyBinWork, SweepLitterWork, or CleanHabitatWork marker on the matching G20 job entity |
After G20 job creation and before claim/execution; every other job is untouched. |
advance_repair_jobs |
FixedUpdate / FixedGameSet::Act |
Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<RepairWork>,Without<EmptyBinWork>,Without<SweepLitterWork>,Without<CleanHabitatWork>)>, Query<&StaffRole>, Query<(&DefinitionId,&mut Condition)>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> |
Decrements remaining ticks; at zero applies the resolved A14 StaffJobEffect::Repair directly and emits one completion |
After G20 begin_staff_job; no G20 repair execution system runs in parallel. Preflight and job start reject a missing/incompatible effect rather than admitting an unexecutable job. |
advance_empty_bin_jobs |
FixedUpdate / FixedGameSet::Act |
Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<EmptyBinWork>,Without<RepairWork>,Without<SweepLitterWork>,Without<CleanHabitatWork>)>, Query<&StaffRole>, Query<&mut WasteContainer>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> |
Decrements remaining ticks; at zero applies resolved StaffJobEffect::Clean { amount }, clamped at zero, and emits one completion |
After job start and before G20 release. Zero amount is rejected by A14 preflight. |
advance_sweep_litter_jobs |
FixedUpdate / FixedGameSet::Act |
Commands, Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<SweepLitterWork>,Without<RepairWork>,Without<EmptyBinWork>,Without<CleanHabitatWork>)>, Query<&StaffRole>, Query<&mut Litter>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> |
Decrements remaining ticks; at zero applies resolved Clean { amount }, despawns litter at zero, then emits one completion |
After job start and before G20 release; target despawn is through deferred commands after aggregate subtraction. |
advance_clean_habitat_jobs |
FixedUpdate / FixedGameSet::Act |
Commands, Query<(Entity,&StaffJob,&JobClaim,&mut JobProgress),(With<CleanHabitatWork>,Without<RepairWork>,Without<EmptyBinWork>,Without<SweepLitterWork>)>, Query<&mut HabitatWaste>, Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, MessageWriter<StaffJobCompleted> |
Decrements remaining ticks; at zero applies resolved Clean { amount }, despawns exhausted waste, then emits one completion |
After job start and before G20 release; no G20 sanitation executor runs in parallel. |
project_zoo_cleanliness |
FixedUpdate / FixedGameSet::Economy |
Res<Assets<WorldDefinitionsAsset>>, ResMut<CleanlinessTotals>, ResMut<ZooCleanliness> |
New permille only when CleanlinessTotals.dirty and the computed value differs; clears dirty |
After all maintenance mutation systems and component hooks; G23 reads it later in the same set/order. |
Job urgency is the direct severity in the target's own unit: repair uses
1000 - condition, emptying uses rounded container fullness permille, and
sweeping uses min(amount_units, 1000). There is no priority tier table or
package-local queue. G20's deterministic job selection combines this request
urgency with the authored StaffJobDefinition.priority it already owns.
5. Preflight producer contract
Not applicable: the engine consumes only native assets produced before launch. This gameplay package adds no filesystem discovery, Z2F/XML parsing, source precedence, decompression, or conversion dependency. The A14-owned producer changes requested in section 3 are performed under A14 ownership.
6. Behavioral evidence and disposition
Search shipped entity/component/task definitions under ignored vendor/ for
trash containers/cans, recycling bins, loose trash, repair tasks, facility-use
mutations, maintenance-worker requests, and condition/status attributes. Search
the C++ oracle and analysis tables for ZTAIStaffMgr, maintenance-worker task
selection, SweepTrash, EmptyTrash, EmptyRecyclingBin, trash quantity,
facility condition, and cleanup events. Concrete starting points include
docs/analysis/function-perimeter.tsv entries for
populate_trash_level_display, maintenance-worker panels, and maintenance
filter handlers; docs/analysis/global-perimeter.tsv trash-quantity and
maintenance-worker bindings; and docs/analysis/rtti-classes.tsv staff cleanup
types. Observe the original for threshold crossing, job cancellation/retry,
litter placement/stacking, repair amount, and rating response.
Mine commit d5657e5a only for already-discovered behavior/evidence in the
historical reconstruction/domain/src/simulation/facility.rs and
reconstruction/domain/src/simulation/staff.rs paths and their former feature
callers. Those files do not exist in the current workspace. Their dynamic maps,
parsed strings, managers, and runtime contracts are not implementation material.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
Evidence gates before implementation: confirm which object families possess condition; exact deterioration cadence and scale; service-specific condition, contained-waste, and loose-litter effects; repair/empty thresholds and amounts; whether loose litter coalesces or remains individual; its authored transform and visual definition; guest reaction distance; and the exact original cleanliness weighting/reference. If original deterioration is not zoo-day based, report that contract conflict to root and freeze the recovered G32-tick cadence before coding. Never substitute guessed constants, generic defaults, or asset-instance hardcoding.
7. Required behavior
- A maintainable spawned object receives exactly its converted initial condition and optional empty container. Objects lacking a definition receive neither component.
- Deterioration and service use are saturating, deterministic integer operations. A completed service is applied once; rejection or an unknown service produces no maintenance mutation. A service effect that creates litter validates the whole target and allocates its persistent ID before any condition or waste field is changed, so failure cannot partially apply it.
- A request is created only from a direct unresolved fact. G20 owns job deduplication and assignment; cancellation rechecks the live target rather than retaining a shadow request registry.
- Repair, empty, and sweep effects use the exact claimed staff job definition. Completion is emitted only after the target mutation succeeds. A stale target or incompatible job is cancelled without mutation or fake success.
- Litter creation allocates
PersistentIdbefore spawn and attaches the facility'sWorldMember.root. Sweep despawns only when amount reaches zero. World unload and object deletion remove aggregate contributions through component lifecycle hooks. - Each evidenced A03 animal waste rule owns one exact next-tick deadline.
Production creates an ordinary persistent
HabitatWasteentity in that animal's world;CleanHabitatwork mutates/despawns only that direct fact. - G17 reads
Litterdirectly for authored visibility/reaction behavior; G21 reads the three facts directly for selected-entity UI. Neither receives a copied maintenance model. ZooCleanlinesschanges only after a contributing fact changes. G23 consumes it directly and does not rescan all placed entities.Condition,WasteContainer, litter/habitat-waste amount/identity/transform, and animalWasteProductiondeadlines are save-relevant. Private totals andZooCleanlinessare rebuilt.- OpenZT2 adds no behavior change in this slice; native integer storage and event-driven execution are architectural improvements only.
8. Performance contract
All settled systems and component hooks allocate zero. Service work is
O(completed services), request work is O(changed facts), one-time job
classification is O(new jobs), and active staff execution is
O(active jobs of the queried class) through disjoint marker archetypes.
Scheduled deterioration is
O(maintainable objects) only on a zoo-day boundary, never a per-frame world
scan. Cleanliness update and component add/remove are O(1) per fact and retain
no entity map. G20's existing job entities are the sole job queue.
There is no per-frame asset clone, string construction, hash growth, source access, generic dirty collection, or copied definition data. Litter growth is explicit ECS gameplay state, never a hidden queue or retained duplicate; integer and persistent-ID exhaustion reject the originating mutation rather than wrapping. Allocator regression coverage measures a quiet maintenance tick, one service effect without litter, request deduplication, each active job step after warm-up, and cleanliness projection at zero heap allocations. Litter spawn/despawn allocation is measured separately as an intentional ECS structural operation. This package maps no pages itself and owns no GPU resource; mapped A14 lookup and handle cloning remain allocation free.
9. Acceptance criteria
Deliver ordinary formatted Rust under the owned maintenance subtree exporting
exactly MaintenancePlugin, Condition, WasteContainer, Litter,
HabitatWaste, WasteProduction, and ZooCleanliness. Implement the listed
component hooks and focused systems
without a manager, registry, runtime source document, generic property bag,
duplicate staff queue, or retained entity summary. No todo!,
unimplemented!, silent stub, fake completion, guessed rule, include_str!
source assembly, or generated Rust concatenation is accepted.
Focused tests cover component bounds, service applied once, saturation, threshold request generation, cancelled-job requeue, stale-target rejection, repair/empty/sweep/clean-habitat mutation, litter and animal-waste lifecycle identity/deadline, aggregate add/remove, and exact integer cleanliness rounding. Observable done: facilities deteriorate and accumulate authored waste, litter appears as ordinary world entities, maintenance staff repair/empty/sweep it, guests/UI/rating see the live result, and save/load restores it. Facility commerce, staff assignment, guest reaction scoring, persistence I/O, and rendering the referenced litter prefab remain owned by their natural adjacent packages.
10. Required handoff
Report files/API, evidence values and unresolved gates, no new Cargo needs (or the exact unavoidable dependency), plugin/message/component/resource registrations, schedule ordering, component-hook registration, and consumed A03/A14/G04/G10/G11/G17/G18/G20/G32 symbols. Report allocation counts for quiet, service, and active-job paths; litter structural allocation separately; no mapped/GPU ownership.
Root integration must make these exact cross-file changes:
- Add A03's evidenced animal-waste fields and the A14 records, archive
tables/accessors,
DefinitionFamilyroute, conversion/validation, and contract tests specified in section 3. - Add/register/emit G20
StaffJobCancelled; removeEmptyBinandSweepLitter/CleanHabitatexecution from G20, removeRepairfrom its maintenance executor, and order G36's four direct-fact executors before G20release_finished_jobs.MaintainTankandOperateShowremain with their natural owners. - Replace G10 ownership references to condition/waste in G20, G21, and G23 with G36. G10 remains placement/footprint ownership only.
- Extend A02
ScalarBindingSourcewithFacilityWasteUnits,FacilityWasteCapacity,LitterAmountUnits,HabitatWasteAmountUnits, andZooCleanlinessPermille; make G21 project G36 facts directly and add the evidenced G17 litter reaction system without a copied view model. - Make G23
recompute_cleanliness_ratingconsume changedRes<ZooCleanliness>directly and remove its placed-object scan. - Insert
MaintenanceafterPlacementin G22SnapshotSection; add deterministic write/prevalidate/apply coverage for placed-objectCondition/WasteContainer, litter/habitat-wastePersistentId,DefinitionId, transform, amount/root relation, and animalWasteProduction. Validation occurs before world mutation. Do not serialize private totals orZooCleanliness. - Add the module/plugin to the root plugin group and align the manifest, integration wave, public-symbol audit, and final destructive cleanup. No temporary adapter or interface introduced solely for this package survives.
G37 — Interactive physics and attachments
1. Identity and outcome
Physical enrichment props, carried and thrown objects, ropes, authored attachments, collision-driven interactions, and fitting surfaces use Avian's native Bevy ECS physics. OpenZT2 supplies recovered game semantics around that solver; it does not contain a physics engine. Integration wave 3. Prerequisites: A14, A15, A17, G04, G05, G10, G13–G15, and G32.
2. Exclusive ownership and mandatory dependency
The package worker owns game/src/plugins/physics/ only. Root adds avian3d = 0.7 with the minimum
3D/f32/Parry features, registers PhysicsPlugins on the fixed schedule, sets
world scale/substeps, and orders OpenZT2 systems against PhysicsSystems.
Avian exclusively owns rigid-body integration, collider representation,
broad/narrow phase, the contact graph, solver rows, restitution/friction,
sleeping, continuous collision detection, joints, constraints, and spatial
queries. The worker must not create PhysicsBody, ColliderShape,
PhysicsVelocity, PhysicsLayer, PhysicsSleeping, PhysicsScratch, a custom
solver, a body registry, or a mirrored physics world. If Avian misses a measured
requirement, report it for configuration or a narrow fork; do not restore the
bespoke design archived at commit 68a4c481.
3. Frozen OpenZT2 contract
Avian components such as RigidBody, Collider, LinearVelocity,
AngularVelocity, CollisionLayers, Friction, Restitution, Sleeping,
FixedJoint, DistanceJoint, CollisionEventsEnabled, and external
force/impulse components are used directly and are not wrapped.
| Symbol | Kind | Exact shape or signature | Owner and invariant |
|---|---|---|---|
ZooPhysicsPlugin |
plugin | unit struct implementing Plugin |
Registers only OpenZT2 conversion, interaction, contact-fact, fitting and cleanup systems. |
PhysicsMaterialId |
component | pub struct PhysicsMaterialId(pub AssetId); |
Authored A14 material identity; Avian friction/restitution components hold live solver values. |
CarriedBy |
component | { carrier:Entity, socket:AssetId } |
Domain relationship; carried bodies use evidenced Avian body/sensor state. |
AttachedTo |
component | { parent:Entity, socket:AssetId, local:Transform, policy:AttachmentPolicy } |
Domain attachment intent; rigid physical attachment uses an Avian joint rather than duplicate transform integration. |
AttachmentPolicy |
enum | Rigid, FollowPosition, FollowPositionAndYaw |
Closed recovered vocabulary. |
FittingSurface |
component | { kind:FittingSurfaceKind, normal_tolerance:f32 } |
OpenZT2 placement/fitting policy, not collision geometry. |
FittingSurfaceKind |
enum | Terrain, Tile, Entity |
Closed authored surface class. |
ApplyPhysicsImpulse |
message | { entity:Entity, impulse_ns:Vec3, point_world:Vec3 } |
Converted once into Avian's impulse component. |
AttachPhysicsObject |
message | { object:Entity, parent:Entity, socket:AssetId, policy:AttachmentPolicy } |
Validated domain request; creates the natural Avian joint or carried relation. |
DetachPhysicsObject |
message | { object:Entity, inherited_velocity:Vec3 } |
Removes relation/joint once and restores evidenced body state. |
PhysicsContactFact |
message | { a:Entity, b:Entity, point:Vec3, normal:Vec3, impulse_ns:f32, kind:ContactFactKind } |
Emitted only for authored gameplay consumers; not a copy of every Avian contact. |
ContactFactKind |
enum | Impact, Enter, Exit |
Fixed gameplay vocabulary. |
PhysicsInteractionFailed |
message | { entity:Entity, reason:PhysicsFailure } |
Explicit invalid request. |
PhysicsFailure |
enum | MissingBody, InvalidTarget, InvalidSocket, LayerRejected, CapacityExceeded |
Fixed failure vocabulary. |
A14 owns physical material/layer/socket policy. A15 emits prefab records that G04 translates directly into Avian components and G37 domain components. G14 owns intentional locomotion; Avian owns collision response.
4. ECS ownership and scheduling
There is one entity for each physical object. Avian components on that entity are authoritative mechanical state. OpenZT2 components contain only zoo-game facts Avian cannot know.
| System | Schedule/order | Exact work |
|---|---|---|
apply_attachment_requests |
fixed, before PhysicsSystems::Prepare |
Validate sockets/endpoints; insert/remove direct Avian joints and the one domain relationship. |
apply_physics_impulses |
fixed, before PhysicsSystems::Prepare |
Convert typed requests into Avian impulse components; reject non-dynamic targets. |
apply_authored_contact_policy |
Avian collision hook | Apply only evidenced one-way/material/layer policy that cannot be represented by ordinary Avian components. |
emit_gameplay_contact_facts |
fixed, after PhysicsSystems::Writeback |
Read enabled collision events/contact data and emit only subscribed game facts. |
fit_eligible_objects |
changed transforms, before physics prepare | Apply recovered terrain/tile/entity fitting policy to eligible fixed/kinematic bodies. |
cleanup_physics_relationships |
fixed cleanup | Remove dead-endpoint joints/relations and restore evidenced detach state exactly once. |
Avian's ContactGraph and solver resources are legitimate bounded algorithm
state. OpenZT2 must not copy them into a resource or event log.
5. Preflight producer contract
Not applicable in this package. A14/A15 preflight compile source shapes into the small Avian-supported primitive vocabulary, material/layer values, mass/density, CCD/sleep policy, sockets, and joint parameters. Unsupported or ambiguous source shapes fail conversion; runtime mesh decomposition is forbidden.
6. Behavioral evidence and disposition
Recover BFPhysObj, BFPhysComponent, BFPhysicsComponent,
BFRealPhysicsComponent, BFFakePhysicsComponent, BFRopeComponent,
collision/fitting-surface identities, BFBehAttachObject, BFBehDetachObject,
BFBehWhap, and kick data from shipped definitions, TSVs, oracle, and live
behavior. Establish units, layers, mass/material values, sleep/CCD policy,
socket semantics, detach velocity, rope parameters, fitting and gameplay-visible
contacts. Solver internals do not need original parity; observable response does.
Commit
19dd222fintentionally deleted the former Rust architecture. Do not restore its hosts, managers, aggregate runtime resources, generic bags, duplicated worlds, runtime source parsing, or compatibility layers.
7. Required behavior
Dynamic props collide, settle, wake, and respond at the fixed zoo timestep. Carried/attached objects follow one typed relationship and detach once. Ropes use Avian distance/fixed constraints and cannot retain dead endpoints. Terrain and fitting surfaces affect only eligible bodies. Gameplay systems consume focused facts, never Avian's entire contact graph.
8. Performance contract
Settled Avian stepping plus G37 integration targets zero heap allocations after body/contact/joint capacities stabilize. Enable collision events only for entities with gameplay consumers. Primitive colliders are preflight-produced; no runtime decomposition. Tracy records Avian broad phase, narrow phase, solver, substeps and OpenZT2 hooks separately. Allocation regressions cover a settled populated zoo, sleeping bodies, repeated contacts, rope constraints, and attachment churn after reserved capacity. A failing mandatory-dependency test must name the Avian allocation/copy before any fork is proposed.
9. Acceptance criteria
Deliver only the OpenZT2 contract above, using Avian components directly. Fixtures cover impulses, sleep/wake, layers, attachments, ropes, fitting, endpoint deletion, fixed-step replay, and bounded collision-fact emission. No custom broad phase, contact solver, collider type, body registry, copied contact collection, guessed material policy, or generic physics wrapper is accepted.
10. Required handoff
Report minimal Avian features/version, root plugin/schedule configuration, A14/A15 schema needs, Avian components inserted by G04, collision hooks, saved domain/Avian facts, recovered constants, remaining evidence, and Tracy/ allocation/contact-count results.