document how wave events work

This commit is contained in:
Martin Michelsen
2025-11-23 17:35:52 -08:00
parent b53847d1b5
commit 474ad99396
+55 -4
View File
@@ -223,6 +223,10 @@ public:
} __packed_ws__(EnemySetEntry, 0x48);
struct EventsSectionHeader { // Section type 3 (EVENTS)
// The events section has three zones: the header (this structure), the
// event entries, and the action stream. The header specifies where to find
// each one in the section, and how many entries there are. The offsets
// here are relative to the beginning of the header.
/* 00 */ le_uint32_t action_stream_offset;
/* 04 */ le_uint32_t entries_offset;
/* 08 */ le_uint32_t entry_count;
@@ -235,17 +239,64 @@ public:
} __packed_ws__(EventsSectionHeader, 0x10);
struct Event1Entry { // Section type 3 (EVENTS) if format == 0
// A wave event consists of an event (this struct) and an action stream,
// which is a short script that runs when all enemies in the wave are
// killed. Generally, events work like this:
// 1. The event is triggered (e.g. via a quest script or trigger object).
// This sets flag 0004 on the wave event.
// 2. The client constructs a TSetEvtDestroy object, which despite its
// name, is also responsible for constructing enemies. This sets flag
// 0002 on the wave event. This object waits for the delay specified
// in this structure (in frames), then constructs the wave's enemies.
// 3. The player kills all the enemies.
// 4. The TSetEvtDestroy object sets flag 0010 on the event.
// 5. The TSetEvtDestroy object sets flag 0008 on the event and runs the
// post-wave actions. (This happens one frame after the above.) See
// the implementation of MapFile::disassemble_action_stream for
// details on the format of post-wave actions. It then clears flag
// 0004 (but not 0002).
// The event ID identifies this event on the current floor. It is not
// required that all wave events have unique IDs; if multiple events have
// the same ID, they will all trigger at the same time when any one of them
// is triggered (since wave events can only be triggered by ID).
/* 00 */ le_uint32_t event_id = 0;
// Bits in flags:
// 0004 = is active
// The flags field specifies the state of the event. This field is synced
// to a joining player as part of the 6x6E command during the game loading
// sequence. Known bits:
// 0002 = wave object constructor has been called (this flag is not
// synced via 6x6E)
// 0004 = is active (has been triggered)
// 0008 = post-wave actions have been run
// 0010 = all enemies killed
/* 04 */ le_uint16_t flags = 0; // Used by PSO at runtime, unused in DAT file
/* 06 */ le_uint16_t event_type = 0;
/* 04 */ le_uint16_t flags = 0; // Used by PSO at runtime, unused in file
// It seems Sega originally wanted to support multiple types of events, and
// the event_type field controls which constructor is called when the event
// is triggered by a 6x67 command. It seems they never actually used this
// though; there are only two valid values: 0 makes the event do nothing
// (no object is constructed at all) and 1 uses the normal control object
// (TSetEvtDestroy). There is no bounds check here, so any other value
// causes undefined behavior.
/* 06 */ le_uint16_t event_type = 1;
// The room and wave_number fields specify which enemies should be
// constructed when this event triggers. All enemies whose room and
// wave_number fields match these two fields are constructed at the same
// time when the event triggers (or after the delay below).
/* 08 */ le_uint16_t room = 0;
/* 0A */ le_uint16_t wave_number = 0;
// The delay field specified how long (in frames) to wait after the event's
// trigger time before constructing all the enemies.
/* 0C */ le_uint32_t delay = 0;
// This field specifies where in the action stream data to start running
// commands for this event, when all enemies are defeated. This is relative
// to the beginning of the action stream, not the events section header.
/* 10 */ le_uint32_t action_stream_offset = 0;
/* 14 */
uint64_t semantic_hash(uint8_t floor) const;