# Egg Identification Investigation ## Goal Add a reliable egg browser to the map renderer and clarify what the game means by an egg "ID". ## Short Answer - Fresh-game startup is hardcoded to map `1`, egg `0x1e`. - That startup egg is not read from `CRUSADER.CFG` or another external mission map. - For Crusader teleport eggs, the destination number that gameplay matches against is the low byte of the item `quality` field. - The broader egg families use different payload fields depending on egg type, so the renderer should not assume that every egg-family item uses the same number source. ## Fresh-Game Default Start Egg The existing reverse-engineering note in [docs/first-mission-map-selection.md](k:/ghidra/Crusader_Decomp/docs/first-mission-map-selection.md) already establishes the startup path: - normal new game calls `Teleporter_CreateProcessDirect(1, 0x1e, 1)` - the controlling call site is in `Game_Start` - this is a code-selected default, not a config-file mapping That means the known first-map spawn uses teleport egg id `0x1e` on map `1`. ## ScummVM Crusader Egg Model The ScummVM Crusader engine in `engines/ultima/ultima8` maps Crusader egg families as follows: - family `3` = `SF_GLOBEGG` - family `4` = `SF_UNKEGG` (usecode trigger egg) - family `7` = `SF_MONSTEREGG` - family `8` = `SF_TELEPORTEGG` Relevant files: - [shape_info.h](k:/misc/scummvm/engines/ultima/ultima8/gfx/shape_info.h) - [item_factory.cpp](k:/misc/scummvm/engines/ultima/ultima8/world/item_factory.cpp) - [egg.cpp](k:/misc/scummvm/engines/ultima/ultima8/world/egg.cpp) - [egg.h](k:/misc/scummvm/engines/ultima/ultima8/world/egg.h) - [monster_egg.h](k:/misc/scummvm/engines/ultima/ultima8/world/monster_egg.h) - [monster_egg.cpp](k:/misc/scummvm/engines/ultima/ultima8/world/monster_egg.cpp) - [teleport_egg.h](k:/misc/scummvm/engines/ultima/ultima8/world/teleport_egg.h) - [teleport_egg.cpp](k:/misc/scummvm/engines/ultima/ultima8/world/teleport_egg.cpp) - [current_map.cpp](k:/misc/scummvm/engines/ultima/ultima8/world/current_map.cpp) One structural detail matters here: generic trigger eggs and teleport eggs inherit from `Egg`, but `MonsterEgg` is its own `Item` subclass instead of an `Egg` subclass. That helps explain why Crusader monster eggs do not line up perfectly with the generic egg-field conventions. ## Which Field Holds The Number? There is not one universal answer for every egg-family item. ### Teleport eggs Teleport eggs are the important case for spawn/start locations. ScummVM uses: - `TeleportEgg::getTeleportId()` = `(_quality & 0xFF)` - `MainActor::teleport(mapNum, teleport_id)` to move to a destination egg - `CurrentMap::findDestination(id)` to find a teleport egg target with matching low-byte quality It also distinguishes two teleport egg roles: - `frame != 1` = active teleporter trigger - `frame == 1` = destination marker So for renderer purposes, the gameplay-relevant teleport egg id is the low byte of `quality`, not `mapNum`. ### Generic/usecode eggs ScummVM's generic egg intrinsics expose: - `Egg::I_getEggId()` -> `getMapNum()` So family `4` eggs use `mapNum` as their generic egg id in the engine interface. ### Monster eggs ScummVM's monster egg accessor exposes: - `MonsterEgg::I_getMonId()` -> `getMapNum() >> 3` That is a separate meaning from teleport ids. There is an important Crusader-specific wrinkle for renderer work: - the generic ScummVM `MonsterEgg` model stores monster shape in `quality & 0x7FF` and activity in the low three bits of `mapNum` - but exported No Remorse `0x024F` frame `0` monster eggs often have `quality = 0` and still carry non-zero `npcNum` values that line up with useful DTABLE rows - concrete exported examples include Remorse map 69 (`npcNum = 2` and `npcNum = 6`) and Remorse map 2 (`rawNpcNum = 5`) - No Remorse frame `1` `0x024F` entries also exist, but the checked cases were zeroed placeholders with no useful `npcNum`, `mapNum`, or `quality` payload - exported No Regret `0x024F` currently diverges earlier: its shape definition resolves to `family: 0`, `kind: terrain` rather than `family: 7`, `kind: egg`, so the renderer should not assume that Remorse monster-egg semantics carry over unchanged So the renderer keeps two interpretations side by side for family `7` eggs: - the egg-family label remains `monster id = mapNum >> 3`, matching the engine intrinsic surface - the NPC preview for evidence-backed `0x024F` frame `0` eggs comes from `npcNum`, because that field matches observed spawn identity better than the zero-heavy `quality` field in current Remorse exports ### Glob eggs Glob eggs expand glob contents using the item `quality` value as the glob reference. ## Renderer Decision The new map-renderer egg UI treats all egg-family items as eggs, but labels each one with the field that matches its ScummVM family behavior: - family `8`: teleport id = `quality & 0xFF` - family `4`: egg id = `mapNum` - family `7`: monster id = `mapNum >> 3` - family `3`: glob id = `quality` That keeps the viewer useful for teleport/start-point work without flattening all egg families into one misleading scheme. For the NPC preview overlay, the renderer now makes one additional evidence-backed exception: - `0x024F` frame `0` monster eggs with a non-zero `npcNum` get the same DTABLE-backed blue NPC preview as `0x04D0` spawners That exception is intentionally narrower than the generic egg browser labels. It reflects the currently verified Remorse data, not a blanket claim that every family `7` egg in every game uses `npcNum` as its authoritative spawn row. ## Current Cross-Game State Of `0x024F` - No Remorse: exported shape definition resolves to `family: 7`, `kind: egg`, and scene items produce `egg.type = monster-spawn` - No Regret: exported shape definition currently resolves to `family: 0`, `kind: terrain`, and scene items do not produce egg metadata That is the strongest current reason to keep the new monster-egg preview support scoped to the evidence-backed Remorse path instead of enabling it globally for both games. ## Weapon Room `250` The current renderer catalog data already contains egg-oriented notes that include `250` in the known egg-id lists for Crusader egg shapes, which is consistent with the user-observed convention that egg `250` is the weapons room/test room marker. Those catalog references are evidence for the workflow and UI, but the renderer should still treat them as conventions layered on top of the raw egg-family decoding above. ## Outcome For The Feature The egg browser added to the renderer now: - lists every egg-family item in the loaded map - shows the decoded id appropriate to that egg family - can center the camera on a chosen egg and pin-select it - can draw zoom-stable id labels over eggs in the viewport ## Remaining Uncertainty Two things are still worth keeping in mind: - family `7` Crusader handling is still marked in ScummVM as partly suspicious because those records can also behave container-like - `250 = weapons room` is a strong workflow convention, but this investigation did not add a new executable-side proof beyond the catalog evidence and known reverse-engineering notes