[Engine] Split material identity from cell state behind a property table#14
Open
Kakapio wants to merge 1 commit into
Open
[Engine] Split material identity from cell state behind a property table#14Kakapio wants to merge 1 commit into
Kakapio wants to merge 1 commit into
Conversation
Replace the nested Particle enum tree (Common/Special/Liquid/Solid with
Direction embedded in liquid variants) with:
- Material: a flat identity enum. All static behavior lives in one
const properties() row per material: sprite index, phase (with
per-liquid viscosity), and world-gen rule (common depth band, or
special spawn weight + vein flag). Adding a particle is now one
variant + one row + a sprite.
- CellState + Particle { material, state }: dynamic state (flow
direction) is separate, so equality and hashing are exact again.
The old discriminant-only Eq/Hash hack on Liquid - where
Water(Left) == Water(Right) for maps but not for matching - is gone;
identity comparisons now say `.material` explicitly.
- Directional interaction rules: a const match on (source, target)
material pairs replaces the HashMap with commutative double-hash
keys. Symmetric behavior (water+lava both ways) is spelled out, and
the acid-into-water no-op is now explicit and documented instead of
an accident of commutative lookup.
- Phase-driven dispatch: Chunk::simulate picks a simulator by
Material::phase(), and FluidSimulator reads viscosity from the
table - a new phase (powder, gas) plugs in without touching match
arms scattered across files. The ParticleType and WorldGenType
traits, the duplicated inherent depth methods, and the dead
Liquid world-gen impl are all deleted.
Behavior-preserving: both insta snapshots (seeded terrain, settled
pool) pass unchanged, and RNG draw order is identical, so same-seed
worlds are bit-for-bit the same as before.
Tests: 27 passing; new unit tests for sprite-index uniqueness, exact
particle equality, directional rules, and full-depth common-band
coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Item 4 from the engine review: the biggest extensibility lever. The nested
Particleenum tree (Common/Special(Ore|Gem)/Liquid(Direction)/Solid) conflated what a particle is with how it currently behaves, and adding a particle meant touching five-plus files (enum variant, sprite match, depth methods, spawn chance, interaction rules). This PR collapses all of it into a single registry. Net −157 lines.What changed
Material+ oneproperties()row per materialA flat identity enum whose entire static behavior lives in one
const fntable: sprite index,Phase(StaticorLiquid { viscosity }), and an optionalWorldGenRule(Commondepth band, orSpecialspawn weight + vein flag). Adding a particle is now one variant + one row + an atlas sprite. The generator, renderer, and simulator all read the table; theParticleType/WorldGenTypetraits, the duplicated inherent depth methods onCommon/Special, and the deadLiquidworld-gen impl are deleted.Particle { material, state }— honest equalityFlow direction moves out of the enum variants into
CellState. Equality and hashing are now exact (derived), killing the footgun whereWater(Left) == Water(Right)for HashMaps but not for pattern matching. Code that wants identity semantics — rule lookups, change tracking, deferred-step re-validation — now compares.materialexplicitly, which is what it always meant.Directional interaction rules, no more HashMap
interaction_rule(source, target)is aconst fnmatch on material pairs, replacing theLazyLock<HashMap>whose commutative key spun up twoDefaultHashers per lookup in the hot path. Symmetry is now spelled out ((Water, Lava) | (Lava, Water)), and the asymmetric acid-into-water no-op — previously an accident of commutative lookup against directional Preserve semantics — is explicit and documented, with a note pointing at source-transforming rules as the future fix.Phase-driven dispatch
Chunk::simulatepicks a simulator bymaterial.is_liquid()(the one dispatch point), andFluidSimulatorreads viscosity from the table vialet Phase::Liquid { viscosity } = …. A future powder or gas phase plugs in here without touching scattered match arms.Behavior parity — verified, not hoped
Material::iter()keeps Gold before Ruby), and the settled-pool simulation endpoint is identical.Tests
27 passing. New in this PR:
sprite_indices_are_unique_and_nonzero— the atlas-collision insurance the old scattered indices never hadparticle_equality_is_exact— documents the new equality semanticscommon_bands_cover_all_depths— terrain generation can never panic on a depth gap🤖 Generated with Claude Code