Quirks And Anomalies¶
A running log of empirical findings about the Pioneers of Pagonia game database that don't fit neatly into the structured docs but are worth keeping on the radar — for modders who hit them, for future contributors who'd otherwise be surprised, and as cross-checks during version updates.
Each entry says: what was observed, where it shows up, what it likely means, and what (if anything) to do about it.
The 12 dangling "unit" and "faction" references¶
The validation pass consistently reports 12 unresolved GUID references that are not null GUIDs — they're real-looking GUIDs that point at no entity in the shipped XML set. They have been there since at least 1.2.2 and remain through the current 1.4.0 baseline. (The 1.3.1 update added 18 further, transient NoMVP.* orphans on top of these, for 30 unresolved references total — see Validation Baseline. The 12 described here are the stable ones.)
These 12 references resolve to only two distinct GUIDs:
| GUID | Times referenced | XML element context | Source files |
|---|---|---|---|
ac941c5f-8266-4456-80f6-d32c777017dc |
7 | <Unit> (and one <Item>, one <Content>) |
core/gdb/buildings.gd.xml, core/gdb/landingparties.gd.xml, core/gdb/objectives.gd.xml |
d860e55d-71de-4200-b7e1-46f2143d6ebd |
5 | <CustomFaction> |
core/gdb/narration.gd.xml |
Neither GUID appears in generated/entities.json. The 1.2.2 → 1.3.0 diff:
- 1.2.2 also reported exactly 12 "other unresolved", but only 2 of those 12 GUIDs survived into 1.3.0 — the rest got reshuffled. The total stayed at 12 by coincidence (or by intent — the same engine-magic shape was reused).
- The two surviving GUIDs are exactly the two listed above. They're stable across the version change.
Working hypothesis. These are engine-special "magic GUIDs" that the engine recognises outside the XML dataset: probably a wildcard / placeholder Unit token (use-any-unit, no-unit, generic-NPC?) and a wildcard CustomFaction marker (no-faction, neutral). The element contexts (<Unit>, <CustomFaction>) suggest they're consumed as "slot reservations" rather than concrete references.
Implication for modders. Don't try to "fix" these by pointing them at real entities — they're not bugs. If your validator flags them, treat them as known engine constants. If you ship a mod that references an existing core entity that uses one of these GUIDs internally, leave the reference alone.
Only one file lives outside <pak>/gdb/¶
Every shipped *.gd.xml lives under <pak>/gdb/<file>.gd.xml — except one:
That's the only exception across core, decorations1, dlc1, and tools. core's own campaign-map data lives under core/gdb/campaign map 1 - tutorial.gd.xml etc. — same content type, different convention.
Likely meaning. EE introduced the <pak>/maps/ subfolder convention with the Meadowsong DLC. If EE keeps this convention, future DLC modules would likely also use <dlc-name>/maps/<...>.gd.xml for map-specific gdb content rather than mixing it into the main gdb/ folder. core's own campaign files probably stay under core/gdb/ for backwards compatibility.
The paker's classify command already handles this — files under <m>/maps/ count as map-specific content, not as standalone overlay overrides. See docs/mod-distribution.md → The Pak Loading Model.
tools.pak's unusual layout¶
Every shipped pak places its <m>.gd.bin index under <m>/. Except tools.pak, which puts it at the pak root:
tools.pak
├── tools.gd.bin (at pak root — not under tools/!)
├── tools/files.json (rest of the skeleton is under tools/)
├── tools/manifest.json
├── tools/memory.bin
└── tools/gdb/magmaview.gd.xml
tools/files.json points at tools.gd.bin (root path) rather than tools/tools.gd.bin. The engine loads it correctly.
Why it matters. Our classifier (pagonia-paker classify) deliberately accepts both layouts — <m>/<m>.gd.bin and root-level <m>.gd.bin — so a custom mod can choose either shape. If the engine ever supports only one layout, the tools pak's behaviour will tell us which one is canonical.
Implication for modders. When writing a Pattern B overlay pak by hand, putting the .gd.bin under <m>/ is the safer choice (matches core/dlc1/decorations1 majority). The root-level placement works because tools.pak does it, but expect less testing/diagnostic coverage from third-party tools.
The single 1.2.2 InheritanceMode="Template" use¶
In pre-Meadowsong 1.2.2, the InheritanceMode attribute machinery already shipped with the engine — but it was used by exactly one entity across the entire database:
<Entity Name="DecorativeBuilding"
Guid="9edc01cd-15b7-48a4-bfca-26ae9a3702af"
InheritanceMode="Template"
InheritedGuid="0913f2c3-524b-436b-af6b-1af8617a110a">
(File: decorations1/gdb/decorations.gd.xml)
This entity survived unchanged into 1.3.0 — same GUID, same InheritedGuid, same file. Meadowsong didn't redefine it.
Why it's interesting. It looks like the test-bed entity EE used to prove the InheritanceMode machinery worked before opening the floodgates with Meadowsong (which jumped to 49 uses across Template / Replace / Incremental). The fact that decorations1 rather than core hosts it suggests decorations1 was the lower-risk pak to ship the feature in.
Implication. None practical for modders. But if you see a contributor wondering "when did InheritanceMode appear?" — point them at DecorativeBuilding as the earliest known use.
Each InheritedGuid target is hit exactly once¶
Across all 49 InheritanceMode uses in 1.3.0, the InheritedGuid distribution is perfectly flat: 49 distinct target entities, each targeted by exactly one inheriting entity. No two inheriting entities compete for the same parent.
This is a very clean property:
- No conflict between two
Replaceentities competing for the same core target. - No "Template + Incremental on the same base" combination shipped.
- No two extensions stacking on the same core entity.
Implication for modders. In shipped content the engine never has to resolve "two mods both Replace the same entity" or "two mods both Incremental the same entity" — those scenarios don't occur in EE's own data. But the resolution rule is no longer unknown. EE confirmed it on 2026-06-06 (mod-distribution.md → Dev review follow-up):
- two mods
Replacethe same entity → last-loaded mod wins (load order decides); - two mods
Incrementalthe same entity → both apply (additions stack, no clobber).
So the rule is settled; what's still absent is shipped example data to regression-test against. That makes cross-mod conflict detection on the entity-relation level a now-buildable feature for our tooling (we can warn deterministically), not a blocked one — community mods are still the first real corpus to validate against.
InheritanceMode="Unload" ships but is unused¶
A data scan of every shipped pak finds InheritanceMode in exactly three flavours — Template (18), Replace (14), Incremental (19) — and zero occurrences of Unload. For a while we read that absence literally: "the entity-level Unload mode doesn't ship; the encounter-level null-GUID <ReplaceSelf> pattern is the only removal mechanism."
That inference was wrong, and it's an instructive miss. EE confirmed on 2026-06-06 that InheritanceMode="Unload" has been implemented in the engine since Meadowsong — it's simply never used in EE's own content. A pure data scan is structurally blind to an engine feature with no data uses: there's nothing in the bytes to find. The only way to learn it exists is a dev statement (or decompiling the loader).
What it does (per the dev): a proxy entity A unloads another entity B, and the merged database then simply doesn't contain B — as if it never existed. The catch: it's only safe when no other entity C still references B (B's GUID must appear nowhere else). Because the game validates references in some subsystems but not others, an unsafe unload may crash in one place and silently dangle in another — so an effective unload usually means unloading B and all its dependents together, and finding the safe set is currently trial-and-error.
Implication for modders. Unload is a real, available primitive — but the sharpest-edged one. Reach for Incremental / Template first; treat Unload (and Replace) as last resorts, and expect to test in-game. See DLC Patch And Override Model → Patch Behaviour Patterns.
Methodology lesson for this repo. Where our docs infer a negative ("X doesn't exist") purely from a data scan, flag it as "not observed in shipped data" rather than "not supported" — the two are different, and Unload is the proof.
Reference resolution rate drops with each new version¶
The fraction of references that resolve to a real entity is going down over time:
| Version | Total refs | Resolved | Null GUID | "Other" unresolved | Resolution rate |
|---|---|---|---|---|---|
| 1.2.2-11216+189567 | 26,162 | 21,208 | 4,942 | 12 | 81.06% |
| 1.3.0-11768+193445 | 31,776 | 24,451 | 7,313 | 12 | 76.95% |
| 1.3.1-11826+193733 | 31,761 | 24,420 | 7,311 | 30 | 76.89% |
| 1.3.2-11873+194094 | 31,763 | 24,422 | 7,311 | 30 | 76.89% |
| 1.4.0-11893+194274 | 31,763 | 24,422 | 7,311 | 30 | 76.89% |
| 1.4.0-11944+194631 | 31,763 | 24,422 | 7,311 | 30 | 76.89% |
| 1.4.0-11975+194885 | 31,763 | 24,422 | 7,311 | 30 | 76.89% |
| 1.4.0-12032+195221 | 31,755 | 24,414 | 7,311 | 30 | 76.88% |
The null-GUID share grew from 18.89% (1.2.2) to 23.01% (1.3.0 Meadowsong) and held at 23.02% in the 1.3.1 hotfix — Meadowsong added 2,371 new null-GUID references (+47%) while only adding 615 new entities (+15%); the 1.3.1 hotfix barely moved the totals, the 1.3.2 "Free Beer" update moved them less still (+2 entities, +2 resolved refs, the "Other" column steady at 30, resolution rate flat at 76.89%), and the 1.4.0 "Pagonia Editor Update" beta held everything flat (+4 entities, but reference totals, resolved/null counts, and the 76.89% rate are all unchanged — the editor is the story, not the shipped data); its Beta Update #1 (11944) moved nothing further — three dlc1 value/schema edits, every count identical — and Beta Update #2 (11975) added a single core localization entity (+8 loca tags for the GDB-mod-dependency / co-op-join UI) but no GUID references at all, so the resolution rate stayed flat at 76.89%. The official 1.4.0 release (12032) is the first build since 1.3.2 to move the rate at all — barely, to 76.88%: it added one core entity (the Type mod-browser filter) and removed 8 resolved GUID references when the four SteelSmithRecipe weapon recipes were reworked into a richer animated step sequence that no longer duplicates the product reference per step. The null and "Other" columns are untouched, so this is a recipe-shape edit, not reference rot. Note the "Other" unresolved column jumping 12 → 30 in 1.3.1 is not reference rot: the 1.3.1 NoMVP.* resource cleanup deleted six placeholder resource entities but left four of them still referenced by recipes, adding 18 dangling references. See VALIDATION_BASELINE.md → Unresolved Non-Null GUID References.
Likely meaning. Meadowsong-era XML uses more optional-with-default-null fields than 1.2.2 XML did. The encounter-level <ReplaceSelf><ReplaceWithEntity>null</ReplaceWithEntity></ReplaceSelf> "unload" pattern is one example, but the bulk of the increase is probably new component shapes that ship with empty-by-default sub-fields.
Implication for modders. Don't be alarmed if your validator reports tens of thousands of null-GUID references — they're the engine's "field exists but is intentionally empty" pattern, not bugs. The "Other unresolved" count is the more interesting metric to watch for actual reference rot.
Meadowsong added 33 new component types (and removed 1 typo)¶
The valueTypes vocabulary (the <Aspect*>, <Vis*>, <Effect*>, etc. elements that hang off <Entity><Values>) grew from 281 types in 1.2.2 to 313 in 1.3.0 — net 33 new, 1 retired.
The retired one is UiGlobalUnitTraits → renamed to UiGlobalUnitTrait (singular). Plain spelling cleanup, same intent.
Of the 33 additions, 26 are dlc1-exclusive (never used by core entities — clean DLC content additions) and 7 are also used by core entities, which is more interesting: core got extended to interoperate with the DLC. The 7:
| Component | What it likely does | First appearance |
|---|---|---|
AspectAnimalSpawnLocation |
Marks a building as a site where farm animals spawn | Added to core's Hut (so player houses spawn animals now!) and dlc1's Infected Hut |
UiCampaign |
Campaign-UI state shape | Used by both core's Campaign Map and dlc1's DLC1.Campaign |
UiGameHint + UiGameHintReferences |
New in-game-hints system | core + dlc1 contribute hints |
UiGlobalUnitTrait |
The renamed singular form (was UiGlobalUnitTraits in 1.2.2) |
core + dlc1 |
UiMarketplaceGlobals |
Market-stall globals | core + dlc1 |
UniqueUnitSpawn |
Spawn-unique-units mechanic | core + dlc1 |
UnitRaidParameters |
Bulk-added to 57 existing core NPC units — every campaign NPC now has raid parameters | core (heavy adoption) + dlc1 |
The dlc1-only 26 cluster around four Meadowsong systems: animal husbandry (AspectAnimalFarm, AspectFarmAnimal, AspectFeedingStation, AspectGuardAnimal), infection / the Withering (AspectInfected*, EffectInfected, EffectInfectionImmunity, InfectionChance*), shrine/sanctuary (ShrineManaResource), and DLC-aware progression (DLC1AchievementParameters, DLC1CM1AchievementParameters, several *AreaAbility types for the Sanctuary's effect zones).
Implication for modders. When EE ships a major update, expect new valueTypes to appear. If your mod targets Hut by GUID and the engine adds AspectAnimalSpawnLocation to it, your mod still works — your component edits don't conflict with the new aspect. But the merged entity now has more shape than your local copy understands. Treat new aspects as additive context, not as overrides.
Most "entity changed" diffs are one bulk component addition¶
The 1.2.2 → 1.3.0 diff reports 68 entities with metadata changes (same GUID, something different about them). Breaking that down:
| Change kind | Count |
|---|---|
| components changed | 57 |
| name changed (renamed) | 11 |
Of the 57 component-change entries, the overwhelming majority are a single bulk rollout: 57 existing core NPC entities (campaign-map bosses, soldiers, ghosts, werewolves, etc. across CM3 through CM7) all gained the new UnitRaidParameters component in the same release. So functionally the 57 = "every campaign NPC now supports the raid-parameter system", not 57 independent edits.
If you're diffing two game versions and find "57 entities changed", check whether they share a single new component. If yes, it's likely one cross-cutting design change. If no, the diff is doing real per-entity work.
EE's internal "NoMVP." prefix on pre-release entities¶
Among the 11 rename diffs, one stands out:
And among the 10 removed entities:
Observed pattern. EE uses NoMVP. as an internal prefix to mark entities present in the codebase but not yet in shipping content ("not part of the MVP"). When a feature actually ships publicly — like the Brewery + Beer in the Free Beer update that landed with Meadowsong — the prefix gets stripped. The recipe shape changed enough that the old NoMVP.BeerRecipe entity was retired and replaced with a fresh entity rather than renamed in-place. Confirmed by direct evidence: the rename NoMVP.Beer → Beer and the deletion of NoMVP.BeerRecipe lined up exactly with the public Beer feature shipping; the NoMVP. prefix appears on no other entity surviving into 1.3.0.
Implication for modders. If you find entities prefixed NoMVP. in any future game version, treat them as not-yet-shipped placeholder content. They may disappear or get renamed in the next release. Don't ship a mod that targets a NoMVP. entity by name or GUID.
Same-GUID renames worth flagging¶
Renames preserve GUIDs but change the human-readable name. The full 1.2.2 → 1.3.0 rename list:
| 1.2.2 name | 1.3.0 name | Note |
|---|---|---|
| Foreman Jadrick Speaker | Foreman Jadrik Speaker | typo fix |
| Maira Speaker | Scholar Maira Speaker | title added |
| PorcinoDepositBase | MushroomDepositBase | specific → generic |
| PorciniDeposit_001 | MushroomsDeposit_001 | specific → generic |
| PorciniDeposit_002 | MushroomsDeposit_002 | specific → generic |
| PorciniDeposit_003 | MushroomsDeposit_003 | specific → generic |
| PorciniDeposit_004 | MushroomsDeposit_004 | specific → generic |
| Campaign | Campaign Map | refinement |
| Alcoholic Drinks Market Stall | Beverage Market Stall | scope broadened |
| Beverage Meal Ingredients | Processed Ingredients | scope broadened |
| NoMVP.Beer | Beer | shipping (see above) |
Implication for modders. Targeting entities by GUID is robust across renames. Targeting them by display name is fragile — every renamed entity above would break a name-only patch. This is why our mod patch format uses GUIDs as the primary target identifier.
Core never references DLC content (one-way dependency)¶
A reference-direction probe across all 31,776 GUID-like references in 1.3.0:
| Direction | Resolved references |
|---|---|
core → dlc1 |
0 |
core → decorations1 |
0 |
core → tools |
0 |
anything → core |
many |
Confirms that the layered package model is strictly one-way: non-core packages reference core, never the other way around. Architecturally important — it means a mod can extend core without core needing to know the mod exists, and removing a DLC can never leave dangling references in core.
This is the empirical foundation under the "Pattern B mods don't need core to be modified" claim in docs/mod-distribution.md. Now it's a measured fact, not just a working assumption.
tools.pak is the structural outlier (twice over)¶
We already noted that tools.pak puts its .gd.bin at the pak root rather than under tools/. Probing further: it's also the only shipped pak with byte[1] = 0x01 in its .gd.bin header.
| Pak | .gd.bin location |
Header bytes (hex) | byte[1] |
|---|---|---|---|
| core | core/core.gd.bin |
03 00 02 2A 00 00 00 |
0x00 |
| dlc1 | dlc1/dlc1.gd.bin |
03 00 02 0E 00 00 00 |
0x00 |
| decorations1 | decorations1/decorations1.gd.bin |
03 00 02 01 00 00 00 |
0x00 |
| tools | tools.gd.bin (root!) |
03 01 02 01 00 00 00 |
0x01 |
tools.pak was structurally different on both axes in 1.2.2 too. The semantics of byte[1] remain unverified — it's possibly a minor-format-version flag, a feature bit, or an authoring-tool fingerprint. Either way: when modders write their own paks, use byte[1] = 0x00; 0x01 may signal "this pak was produced by the EE toolchain in a specific way" that community paks don't need to mimic.
memory.bin carries per-pak memory-allocation hints¶
Every shipped pak ships a 28-byte <m>/memory.bin blob. Decoding the bytes:
01 00 00 00 00 00 6-byte header (constant)
<8 bytes> uint64 #1
<8 bytes> uint64 #2
<8 bytes> uint64 #3 (only 6 bytes here actually; last 2 padding?)
Concrete observed values:
| Pak | First uint | Second uint | Third uint |
|---|---|---|---|
| core | (varies, large) | (varies) | (varies) |
| dlc1 | 0x000098b3 (39,091) | 0x00003cfc (15,612) | 0x0000265b (9,819) |
| tools | 0x0e f7 (3,831) | 0x03 da (986) | 0x01 13 (275) |
| user-map 4r70_DnD | 0x000f (15) | 0x0004 (4) | 0x0002 (2) |
The numbers scale roughly with pak size and content complexity. Working hypothesis: per-category memory-allocation hints the engine uses to pre-size buffers when loading the pak (textures vs. audio vs. xml byte counts, for example). For a fresh scaffolded mod pak, writing 28 zeros has worked so far — the engine probably either ignores them, recalculates on first load, or accepts an over-allocation hint.
Implication for modders. The bundled paker's Pattern B scaffold writes 28 zero bytes, and this hasn't broken anything observably. Don't try to engineer the values without verification — the field is undocumented.
Dead / cut content: fully-authored but unreachable¶
Roughly 60 entities ship with real, player-facing assets (icons, meshes, complete recipe bodies, recruitment costs) yet have no reachable path in normal play. They are not bugs and not the null-GUID "intentionally empty field" pattern — they are cut, superseded, or never-wired-up content that EE left in the shipped database. They cluster into a handful of recognisable abandoned features.
Methodology. Candidates were surfaced fast from the generated entity catalog (generated/catalog/resource-flow.csv carries ProducedBy / ConsumedBy / ConstructionCostFor / RecruitmentCostFor / UsageCount; building-production.csv is the real building→recipe wiring), then each was re-verified adversarially against the raw .gd.xml. The catalog is only a projection — it tracks production / construction / recruitment / storage / treasure, but not references from objectives, landing parties, techtree, abilities, campaign maps, or mapgen — so the XML is authoritative. An entity counts as "cut" only when every reference to its GUID lives inside an IsAbstract="true" parent (or a dev/test container), and no reachability root (techtree unlock, a non-abstract building's <ProductionRecipes>, map placement, concrete landing party, NPC spawn) touches it.
Two verification gotchas worth recording. (1) The indexed
Greptool silently returns zero hits for some.gd.xmlGUID searches (a UTF-16/BOM artifact on the largeproductionrecipes.gd.xml); rawgrep/rgvia shell is authoritative — several entities here were nearly mis-filed as "not found" because of it. (2)building-production.csvdoes not filter abstract buildings, so an abstract (un-buildable) building's recipes look "produced" in the catalog. That is exactly how the cut Stone Mason chain hid: the catalog shows Border Stone as produced by "Stone Mason", but the building itself isIsAbstract.
Cut transport / mechanisation feature (cogwheel + carts + wheels)¶
The clearest abandoned feature: a transport / mechanisation chain — a cogwheel, wooden and iron-bound wheels, a handcart and a wheelbarrow. The Wooden Cogwheel is fully modelled (icon, mesh, pile mesh, carry attachment) but IsAbstract; its only producing recipe is assigned to no building, and it is requested only by the abstract Test all landing party and orphan objective templates. The wheel/cart recipes carry EE's NoMVP. prefix (its internal "not part of the MVP" marker — see the NoMVP section above) and were superseded before ship.
| Entity | GUID | Defining file | Evidence |
|---|---|---|---|
| Wooden Cogwheel (resource) | 80ca9b4e-06cd-4a53-9cfb-a63af8a00a08 |
core/gdb/resources.gd.xml |
All refs abstract; producing recipe in no building, not in techtree |
| WoodenCogWheelRecipe | 3c7515a2-752a-48c4-913f-b23ea8d9997d |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; assigned to no building |
| NoMVP.WoddenWheelRecipe | ec10ab0a-f42c-485c-9efb-4d0fb594d90a |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; child of BaseRecipe1In1Out |
| NoMVP.IronboundWheelRecipe | 96398754-6d6b-4164-84de-bf0dec514936 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| NoMVP.HandcartRecipe | 78faccbf-fc66-4c2c-a76c-3095d8cf3756 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| NoMVP.WheelbarrowRecipe | 23fcda6d-a4e9-48da-b7f1-19cf2f4306a0 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; inputs point at deleted NoMVP resources |
Unbuilt copper-tools tier¶
A complete first-generation copper tool tier — one fully-authored recipe per tool — that no building or techtree ever wires up. The game instead ships iron tools and a later workshop "3-copper" tier (Axe3CopperRecipe, Pincers3CopperRecipe, … are live); the single-tool copper recipes below, plus the orphaned Pickaxe3CopperRecipe, are the cut remnants. Their shared intermediate good, Hardwood Boards, is dead alongside them (consumed only by these cut recipes; the live chain uses Hardwood Trunk).
| Entity | GUID | Defining file | Evidence |
|---|---|---|---|
| AxeCopperRecipe | 7f870c83-ab09-4acc-9644-5766ba2e0a04 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; superseded by Axe3CopperRecipe |
| HammerCopperRecipe | 632fb9a3-2e6d-4bf7-9dd6-dc3ad88d00d1 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| ChiselCopperRecipe | 984199e8-028d-4816-aa73-7f123e363208 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def (unlike live ChiselIronRecipe) |
| KnifeCopperRecipe | 4d8c53ee-5d75-4314-b314-c29818fe4b69 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| PickaxeCopperRecipe | b67670de-ff96-40f2-8d73-5e0364a64392 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| PincersCopperRecipe | 2e74ff04-1a57-4992-ab5f-558abffb3093 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; live tier is Pincers3CopperRecipe |
| SawCopperRecipe | 793c130c-496e-4894-a365-642f59d4b53b |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; live saw is SawIronRecipe |
| ShovelCopperRecipe | 86d28a9e-46fb-4c5a-9ac8-3e84ac942b97 |
core/gdb/productionrecipes.gd.xml |
No reachable ref (GUID returns 0 hits — likely already removed) |
| SickleCopperRecipe | 42a83f80-1616-411c-b1d5-39b0d5ac0d7a |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| Pickaxe3CopperRecipe | 2522ca7a-d04a-42c6-a0fe-ee437038e544 |
core/gdb/productionrecipes.gd.xml |
Single self-def; orphaned 3-copper variant (siblings are live) |
| Hardwood Boards (resource) | aa3e19cc-14d9-439f-b4d9-ceedfaa620f2 |
core/gdb/resources.gd.xml |
Consumed only by cut copper-tool recipes; live chain uses Hardwood Trunk |
| HardwoodBoardsRecipe | 184dd446-7031-459a-9c95-270cfa97f8e2 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
Cut "Stone Mason" building and its dressed/border-stone chain¶
An entire production building was cut: Stone Mason ships as an IsAbstract entity with full assets (icon_build_stonemason_001.png, name, description) but is not buildable. Its two recipes — DressedStoneRecipe (Stone Block) and BorderStoneRecipe (Border Stone) — and the Border Stone resource go with it. Note the live game still has the Stone Block resource: it survived because the Quarry / Master Quarry produce it directly, so only the Stone Mason path is dead, not its output good.
| Entity | GUID | Defining file | Evidence |
|---|---|---|---|
| Stone Mason (building) | 8ba32781-9df4-426e-b32b-924d06479df9 |
core/gdb/buildings.gd.xml |
IsAbstract; not in techtree, not placed on any map; no reachable ref |
| DressedStoneRecipe | 1d493222-c92d-4937-8801-dbdbf3b653a2 |
core/gdb/productionrecipes.gd.xml |
Run only by the abstract Stone Mason; Stone Block comes from the Quarry instead |
| BorderStoneRecipe | ecd1d7b5-671a-4451-b1e6-da13228c5866 |
core/gdb/productionrecipes.gd.xml |
Abstract self-def + one <Recipe> ref, both inside the abstract Stone Mason |
| Border Stone (resource) | 7486fadc-0bc5-4b88-ab3c-ed736549ff68 |
core/gdb/resources.gd.xml |
Fully modelled; every ref inside abstract entities; no live consumer |
Cut elemental-sorceress upgrade line¶
The spiritual training building (Arcane Academy) trains a base sorceress, but the elemental upgrade variants — Mountain II/III and Water II/III — were cut, and they take their cut recruitment goods (gem staffs and wands) and the staff/wand production recipes down with them. The catalog shows the cross-links (e.g. Ruby Staff is RecruitCostFor Sorceress Mountain III, Sapphire Wand for Sorceress Water II) — but every entity in the chain is unreachable.
| Entity | GUID | Defining file | Evidence |
|---|---|---|---|
| Sorceress Mountain II (unit) | 1c370f86-3b3a-46fa-9310-526e3d27b6d4 |
core/gdb/units.gd.xml |
Single abstract self-def; never spawned or recruited |
| Sorceress Mountain III (unit) | 179a94aa-5b09-4994-8939-eaa8a9098328 |
core/gdb/units.gd.xml |
Single abstract self-def |
| Sorceress Water II (unit) | 29c09296-1d1b-4087-b1ae-ac8d54d34d1f |
core/gdb/units.gd.xml |
Single abstract self-def |
| Sorceress Water III (unit) | fa678790-18eb-4183-b9ab-db86523b891b |
core/gdb/units.gd.xml |
Single abstract self-def |
| Ruby Staff (resource) | bff6017c-6ed4-4ad1-8440-062bb98fc8af |
core/gdb/resources.gd.xml |
All refs abstract; recruit-cost for cut Sorceress Mountain III |
| Sapphire Staff (resource) | c0ea5d13-f32f-4ed9-9902-7d92f24dffeb |
core/gdb/resources.gd.xml |
All refs abstract; recruit-cost for cut Sorceress Water III |
| Iron Wand (resource) | 583dfb98-33d4-4ee1-acbe-e676d1ea8b11 |
core/gdb/resources.gd.xml |
Produced/consumed only by cut staff/wand recipes |
| Ruby Wand (resource) | bf16030f-9b00-4024-8201-556efc8fe760 |
core/gdb/resources.gd.xml |
All refs abstract; recruit-cost for cut Sorceress Mountain II |
| Sapphire Wand (resource) | 48f7675f-d379-470c-9799-6def7c632cf8 |
core/gdb/resources.gd.xml |
All refs abstract; recruit-cost for cut Sorceress Water II |
| IronStaffRecipe | 06482375-1229-4f5c-9397-80224a025d5e |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| IronWandRecipe | 57a89026-cfb0-4c8d-b104-623f44d09602 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| EmeraldStaffRecipe | d8353c68-6702-4079-bd6a-d0f86fdbd38e |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| DiamondStaffRecipe | e53fd615-fa1b-403f-872c-a463bebe373e |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| RubyStaffRecipe | 91a530ef-4750-4586-8f5d-8212d112dc73 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| StaffRuby | 4450a182-fd22-4483-b1d0-2619be22223e |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| StaffSapphire | 9658c9a8-d802-4c0a-a14c-591fe8252711 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| WandRuby | ec07af1d-f5bb-4cf7-83b5-49163bde8b89 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| WandSapphire | 699b3bb5-aad2-4e05-ad77-8dc5e7c68f61 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
| NoMVP.SapphireStaffRecipe | c9d9944a-3786-4d17-a051-9f6a49b3b28e |
core/gdb/productionrecipes.gd.xml |
Single abstract NoMVP self-def |
Superseded revisions (*Old, Deprecated, variant)¶
A textbook revision pattern: a recipe or building is reworked, the new version keeps the clean name, and the predecessor is renamed …Old / … Deprecated and left dangling. In each case the live twin is wired into a real building (and often a campaign map) while the Old GUID appears only at its own self-definition.
| Entity | GUID | Defining file | Evidence |
|---|---|---|---|
| Copper Hut old (building) | ec98dbc8-a92f-4f11-b080-07f314900876 |
core/gdb/buildings.gd.xml |
IsAbstract; refs only within its own block; live Copper Hut is separate |
| Artisan Blacksmith 3x4 Deprecated (building) | 2323e08a-8727-4a54-9888-8469ebc8f23e |
core/gdb/buildings.gd.xml |
IsAbstract; single self-def, no external ref |
| Guild Hall 3x3 Deprecated (building) | 301c11c1-8a39-4d56-b6e0-09517f4f6c51 |
core/gdb/buildings.gd.xml |
IsAbstract; single self-def, no external ref |
| IronChainMailRecipeOld | a5382b19-9d60-49f8-958f-493e058e1433 |
core/gdb/productionrecipes.gd.xml |
Single self-def; live IronChainMailRecipe in building + campaign map 2 |
| IronShieldRecipeOld | ae7b6381-4bae-450d-ab85-2fbc10203262 |
core/gdb/productionrecipes.gd.xml |
Single self-def; live IronShieldRecipe in building + campaign map 2 |
| SteelDaggersRecipeOld | ea74ce7e-6f51-4963-9cbd-9acd0b634fd3 |
core/gdb/productionrecipes.gd.xml |
Single self-def; live SteelDaggersRecipe in smith + campaign map 4 |
| SteelHalberdRecipeOld | 0522135b-4353-4e16-a00a-463ae77b97f6 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; live SteelHalberdRecipe exists |
| SteelMaceRecipeOld | 752e8098-b66b-4172-8b9c-15835210711e |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; replaced by SteelMaceRecipe |
| SteelStaffRecipeOld | d391ee15-5bd2-406d-8135-8d4e6950f853 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; live SteelStaffRecipe in building |
| BronzeSwordRecipe | 90938792-54c8-4374-bbfe-de3cfc96da19 |
core/gdb/productionrecipes.gd.xml |
Single self-def; superseded by BronzeSword1CopperRecipe |
| ReinforcedLeatherArmorClothRecipe | 4a821cbf-2c0b-4a29-a110-715c46ba28de |
core/gdb/productionrecipes.gd.xml |
Single self-def; superseded "cloth" armor variant |
| WoodenSpearRecipe | 333a3462-46df-4592-989b-47c57da8ea21 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def; superseded by StoneSpearRecipe |
| MacheteRecipe | 0d13639b-ad9e-45c8-871d-938c01fa7449 |
core/gdb/productionrecipes.gd.xml |
Single abstract self-def |
Miscellaneous cut leftovers¶
| Entity | GUID | Defining file | Evidence |
|---|---|---|---|
| NoMVP.GoldCoinsRecipe | a585e9c2-6656-4370-9c78-7e8387541c2f |
core/gdb/productionrecipes.gd.xml |
Single abstract NoMVP self-def; live GoldCoinsRecipe ships |
| NoMVP.GoldIngotRecipe | 015acb0f-9113-4df1-b2c1-5e2c381b0e4d |
core/gdb/productionrecipes.gd.xml |
Single abstract NoMVP self-def; live GoldIngotRecipe ships |
| Enhanced Robe (resource) | 1de66cab-46f5-4b53-b675-a03264eb8458 |
core/gdb/resources.gd.xml |
IsAbstract with assets; UsageCount=0, no producer/consumer |
| TotemFlash (ability) | 38d849eb-1afa-4161-9336-9871e7f44a04 |
core/gdb/abilities.gd.xml |
Concrete ability (InstantKill VFX) referenced by no unit/encounter |
| DecorativeBuilding006 "Village Tree" | dd28349a-3856-4093-83af-12ae6a8a71e7 |
core/gdb/decorations.gd.xml |
Single abstract self-def; not placed by mapgen/terrainprops |
| DecorativeBuilding008 "Bird Bath" | 964b8004-ce22-4d7c-bd91-c3fde30fe58f |
core/gdb/decorations.gd.xml |
Abstract; re-shipped later as the DLC1 Birdbath |
| DCL1DecorativeBuilding33 FestivalGround (building) | d784a9d4-0f4e-442e-b01a-2c54290d2195 |
dlc1/gdb/buildings.gd.xml |
Concrete, real assets, but no techtree/map placement |
| Veteran Adventurer B (unit) | 5d7a7041-9f1d-478b-a02b-e27c00312bd5 |
core/gdb/units.gd.xml |
Single self-def; the unused half of a NoMVP A/B ranger pair |
| CheckifObsolete.Woodworks_Worker (unit) | 9d670423-7917-49df-830e-e59b70c81db9 |
core/gdb/units.gd.xml |
Abstract; the name is EE's own "check if obsolete" dev marker |
What yielded nothing, and what this means for modders¶
- Player-buildable buildings: no fully-cut buildable building exists — every concrete building reaches a techtree node or a map. The cut buildings above are all
IsAbstract(Stone Mason, the twoDeprecated/oldrevisions) or an unplaced decorative DLC entity. The ~44 other "not in techtree" buildings are system pseudo-entities (ranges, markers, focus points, toggle-order buttons,Tag Impress *groups) — not content. - Implication. Treat
IsAbstract="true"entities,NoMVP.*,*Old,* Deprecated, andCheckifObsolete.*as internal/cut — do not target them by GUID or name in a mod; they may vanish or get renamed without notice (consistent with theNoMVP.Beer → Beerprecedent above). If you want to revive one (e.g. re-enable the copper-tool tier), the recipe bodies are intact — you only need to assign the recipe to a building and add a techtree unlock.
Reproducing and extending this audit¶
The fast path is catalog-first: read the relevant generated/catalog/*.csv before grepping raw XML. resource-flow.csv (ProducedBy/ConsumedBy/UsageCount) and building-production.csv (real building→recipe wiring) answer most reachability questions in one pass; only then confirm survivors against the .gd.xml (remembering the two gotchas above: shell-grep, not the indexed tool, on productionrecipes.gd.xml; and that building-production.csv keeps abstract buildings). If "is X reachable / used?" becomes a recurring question, it is worth promoting to a generated artifact — e.g. a reachability.csv that flags each entity reachable | abstract-only | orphan with its decisive reference — so future audits are a single table lookup rather than a re-derivation.
How to refresh this log¶
Every game-version update worth a snapshot is also worth a quick pass through these probes. Re-run the queries in this doc against the new game-gdb/:
grep -rh 'InheritanceMode="[^"]*"' game-gdb/ | sort | uniq -c | sort -rnto enumerate inheritance modesfind game-gdb -name "*.gd.xml" -type f | grep -v "/gdb/"for files outside the canonical conventionnode generated/_curiosities.jswould give the dangling-reference list if you have that local script around (it's transient — gitignored undergenerated/)- for the dead / cut content set: re-diff
generated/catalog/recipes.csvagainstbuilding-production.csv(recipes assigned to no building) and scanresource-flow.csvforUsageCount0 / abstract-only resources, then re-verify survivors against the raw XML. Watch forNoMVP.*/*Old/* Deprecated/CheckifObsolete.*naming markers appearing or disappearing across versions
When a new quirk shows up, add it here. When an old one resolves itself in a new version, mark it resolved rather than deleting the entry — the history of "what was once weird" is valuable context.