Crusader_Decomp/docs/usecode-alarmhat-analysis.md

9.3 KiB

ALARMHAT Analysis

Purpose

This note records the current evidence-backed read of exported ALARMHAT pseudocode and what the class most likely means in gameplay.

The goal is not to force a final rename. The goal is to state what the script definitely does, what it probably does, and where the remaining uncertainty sits.

Sources used

  • exported pseudocode: USECODE/EUSECODE_extracted/pseudocode/ALARMHAT/slot_0A_equip.txt
  • class/event index rows for ALARMHAT
  • raw linear disassembly in K:/ghidra/crusader-disasm/crusader_disasm.txt
  • nearby alarm-family comparators:
    • ALARMBOX
    • ALRMTRIG
    • ALARM_NS
    • ALARM_EW

Short version

ALARMHAT is not a general many-event alarm controller. In the extracted corpus it has one live body: slot 0x0A (equip).

That body behaves like an alarm-family state controller attached to an item. It does two different things depending on the current frame of the item:

  1. in frame 0, it searches nearby shape 0x04D0 objects and equips qualifying ones with mode 0x17
  2. in non-zero frames, it first requires the item to be on-screen, then performs a nearby actor/family scan, and if that passes it searches nearby shape 0x04D0 objects and equips qualifying ones with mode 0x15

The likely gameplay read is: ALARMHAT is a local alarm-state driver that flips nearby helper objects or actors into one of two equipment/activation states, with the second state gated by player-visible activity near the item.

Structural facts

From class_event_index.tsv:

  • class: ALARMHAT
  • class id: 0x0561
  • only decoded non-zero slot: 0x0A equip
  • body window: 0x00D4..0x025F
  • body length: 395 bytes

From the debug trailer/local names in the exported body:

  • locals currently render as referent, var, item, and npc

Those local names are useful hints, but the behavior matters more than the variable spelling.

Direct script behavior

The body begins with the standard alarm-family setup:

set_info(0x0211, *(arg_06));
process_exclude();

That matches ALARMBOX::equip, which also begins with set_info(0x0211, *(arg_06)); process_exclude();.

After that, the script splits on Item.getFrame(arg_06).

Branch 1: frame == 0

The raw disassembly shows a loop-selector sequence equivalent to:

  • search nearby items
  • constrain by item->shape == 0x04D0
  • use the current item as the search origin

For each matching object:

  • call Item.getFrame(item)
  • only continue on frame 0
  • call Item::I_equip(pid, 0x17, item)
  • suspend

So the first branch is not sounding an alarm by itself. It is driving nearby shape 0x04D0 objects into a specific equip/activation mode.

Branch 2: frame != 0

This branch only runs if Item::I_isOnScreen(arg_06) passes.

It then performs another nearby search, this time using item->family with family value 6. Inside that scan it uses:

  • Actor::I_isNPC(...)
  • Item::I_getZ(...)
  • a vertical-band test of origin_z - 10 < candidate_z < origin_z + 10

The exact truth sense of the Actor::I_isNPC step is still slightly uncertain because we are reading it through the current pseudo-IR condition simplifier, but the script is clearly trying to qualify nearby actor-like entities in the same local vertical band before proceeding.

If that actor/family gate succeeds, the script runs a second nearby shape search for item->shape == 0x04D0, again requiring frame 0 on the found object, then calls:

Item::I_equip(pid, 0x15, item)

followed by suspend.

So the non-zero-frame branch is the more selective mode: it only arms the nearby 0x04D0 helper objects once a local visibility/actor-proximity condition is satisfied.

What shape 0x04D0 likely is

The old disassembly corpus labels usecode class 0x04D0 as MONSTER.

That does not prove every shape-id use of 0x04D0 is literally a monster actor object, but it is a strong clue that ALARMHAT is not interacting with an arbitrary visual prop. It is probably targeting a helper/actor class in the monster or hostile-response lane.

This is the strongest reason not to read ALARMHAT as just a decorative siren hat sprite. The script is actively scanning for nearby 0x04D0 objects and equipping them.

Follow-up: what controls immediate spawn versus waiting

The nearby ALARMHAT logic is only one half of the 0x04D0 story. The matching MONSTER class now adds one verified activation rule:

  • MONSTER::enterFastArea only checks 0x04D0 objects when Item.getFrame(arg_06) == 0
  • inside that frame-0 lane it reads Item.getMapNum(arg_06) and only auto-runs MONSTER.equip(pid, 0, arg_06) when (mapNum & 0x08) == 0

Current safest read:

  • frame 0 = the only state that participates in the automatic enterFastArea spawn lane
  • mapNum bit 0x08 = suppresses that automatic lane without changing DTABLE row selection
  • frame 1 = skips enterFastArea entirely and is therefore more likely to be used in paired or externally signaled setups

That fits the recurring authored pairs already visible in exported map data:

  • Remorse map 246: frame-0 item:162 uses mapNum = 8, npcNum = 0, while colocated frame-1 item:163 uses mapNum = 1, npcNum = 8
  • Remorse map 9: frame-0 item:338 uses mapNum = 0, npcNum = 0, while colocated frame-1 item:339 uses mapNum = 7, npcNum = 2

This follow-up also narrows one tempting overread: quality low byte is not the primary spawn now vs wait control in MONSTER::enterFastArea. The current Regret ALARMHAT body does still compare nearby 0x04D0 Item.getQLo(...) values against difficulty lanes 0/1/2, so low quality remains relevant as a secondary filter, just not as the automatic-enter-area gate.

The wider exported corpus now supports keeping that claim local but real. Additional 0x04D0-adjacent bodies in ITEM.slot_2D, FUSPAC.slot_01, and MISS8.slot_20 also scan nearby 0x04D0 objects and branch on frame and/or Item.getQLo(...). Current safest synthesis is therefore:

  • frame plus mapNum bit 0x08 controls the automatic MONSTER.enterFastArea lane
  • quality low byte is still a real local signal key used by several authored trigger/alarm/helper families
  • but that low byte is not yet proven to be a single universal direct-link field across every 0x04D0 interaction

Comparison to the rest of the alarm family

ALARMHAT fits the broader alarm family, but it is not identical to the other alarm classes.

ALARM_NS and ALARM_EW

These are tiny enterFastArea stubs. They mainly stamp info, exclude themselves from processing, and gate on a simple intrinsic.

Those look like directional/environment trigger markers.

ALRMTRIG

This class is a compact trigger/spawner. Its equip body branches on map/state and spawns class_0A18_slot_20(...) with different mode values.

That looks more like an alarm-event relay.

ALARMBOX

This is the closest comparator.

ALARMBOX::equip:

  • uses the same set_info(0x0211) + process_exclude() prologue
  • branches by local state and frame
  • spawns different helper slots for low/high alarm states
  • can also spawn class_0A18_slot_20(...)

So ALARMBOX reads like a more explicit alarm control box, while ALARMHAT reads like a local accessory/controller that pushes nearby helper objects into one of two equip states.

Gameplay interpretation

The safest gameplay-facing read is:

ALARMHAT is an alarm-state accessory or controller item that toggles nearby hostile/helper objects between two alarm-response modes.

The second mode only activates when the item is visible on screen and when a nearby actor-like entity qualifies within a narrow Z band, which strongly suggests local encounter awareness rather than a map-global trigger.

In practical gameplay terms, the object likely does something like one of these:

  • wake up or arm nearby hostile responders
  • switch nearby helper entities between idle and alert states
  • mark a local alarm point as actively triggered once the player or another actor is nearby

The name ALARMHAT therefore probably reflects object art or designer shorthand rather than a full behavior description. The script behavior is closer to alarm accessory that equips nearby monster/helper actors into a response mode than to play a siren sound or flash a light.

Confidence and uncertainty

High confidence:

  • ALARMHAT has one live exported body, slot 0x0A equip
  • it uses the same 0x0211 alarm-family setup as ALARMBOX
  • it branches on its own frame
  • it performs nearby searches on shape 0x04D0
  • it calls Item::I_equip(...) on qualifying found objects with two different mode values: 0x17 and 0x15
  • the second mode is gated by on-screen and nearby actor/family checks

Moderate confidence:

  • shape 0x04D0 is a monster/helper/hostile-response class rather than an inert prop
  • the non-zero-frame branch is the alert or escalation state

Lower confidence / still open:

  • the exact semantic meaning of equip modes 0x17 versus 0x15
  • the precise truth sense of the Actor::I_isNPC-named intrinsic inside the family-6 scan
  • whether ALARMHAT is physically a wearable-looking prop, a mounted alarm device, or a small controller object whose art happened to be named HAT

Current best rename-level takeaway

Do not rename it yet.

But the current best mental model is:

ALARMHAT = local alarm-state driver that equips nearby MONSTER/helper objects into one of two response modes depending on frame/state and nearby actor visibility.