From 6b2c2935312770f3da5c2cd670f1cc2c70fbfa0d Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:03:25 -0400 Subject: [PATCH] Anchor: fix ccw jiggy podium to be hidden for players in anchor and only perform logic when connected --- src/lair/jigsawpicture.c | 51 +++++++++++++++------ src/port/Enhancements/Events/Hooks/Events.h | 2 + src/port/Network/Anchor/HookHandlers.cpp | 4 ++ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/lair/jigsawpicture.c b/src/lair/jigsawpicture.c index a58b77c37..cfb5be6ed 100644 --- a/src/lair/jigsawpicture.c +++ b/src/lair/jigsawpicture.c @@ -25,12 +25,13 @@ typedef struct { void jigsawPicture_setState(Actor *this, s32 next_state); void updateJigsawPictureActor(Actor *this); +Actor *jigsawPicture_draw(ActorMarker *marker, Gfx **gfx, Mtx **mtx, Vtx **vtx); static s32 sBuzzedPedestalField = 0; /* .data */ -ActorInfo JIGSAW_PICTURE_ACTOR = { 0x1EB, 0x3B7, 0x48B, 0x1, NULL, updateJigsawPictureActor, actor_update_func_80326224, actor_draw, 0, 0, 0.0f, 0}; -ActorInfo JIGSAW_PICTURE_ACTOR_2 = { 0x1EB, 0x3BC, 0x538, 0x1, NULL, updateJigsawPictureActor, actor_update_func_80326224, actor_draw, 0, 0, 0.0f, 0}; +ActorInfo JIGSAW_PICTURE_ACTOR = { 0x1EB, 0x3B7, 0x48B, 0x1, NULL, updateJigsawPictureActor, actor_update_func_80326224, jigsawPicture_draw, 0, 0, 0.0f, 0}; +ActorInfo JIGSAW_PICTURE_ACTOR_2 = { 0x1EB, 0x3BC, 0x538, 0x1, NULL, updateJigsawPictureActor, actor_update_func_80326224, jigsawPicture_draw, 0, 0, 0.0f, 0}; Struct_lair_86F0_0 D_803947F8[0xb] ={ { 1, 0x1, FILEPROG_5D_MM_PUZZLE_PIECES_PLACED}, { 2, 0x2, FILEPROG_5E_TTC_PUZZLE_PIECES_PLACED}, @@ -429,6 +430,26 @@ void addPieces(Actor *this, s32 arg1) { fileProgressFlag_set(FILEPROG_DE_USED_ALL_YOUR_PUZZLE_PIECES, 1); } +// [port] unk38_31 marks a podium that is spawned but not yet revealed by its switch (see boggy3). +Actor *jigsawPicture_draw(ActorMarker *marker, Gfx **gfx, Mtx **mtx, Vtx **vtx) { + Actor *this = marker_getActor(marker); + + if (this->unk38_31) return this; + + return actor_draw(marker, gfx, mtx, vtx); +} + +static void revealCcwPuzzlePodium(Actor *this) { + this->unk38_31 = false; + this->marker->propPtr->unk8_3 = true; + if (!fileProgressFlag_get(FILEPROG_54_CCW_PUZZLE_PODIUM_ACTIVE)) { + __bundle_spawnFromFirstActor(BUNDLE_20__UNKNOWN, this); + func_80324CFC(0.0f, COMUSIC_43_ENTER_LEVEL_GLITTER, 0x7FFF); + func_80324D2C(2.1f, COMUSIC_43_ENTER_LEVEL_GLITTER); + func_8030E6D4(SFX_113_PAD_APPEARS); + } +} + void updateJigsawPictureActor(Actor *this) { ActorLocal_lair_86F0 *local; s32 sp7C[6]; //buttons @@ -462,23 +483,27 @@ void updateJigsawPictureActor(Actor *this) { this->volatile_initialized = true; if (this->actorTypeSpecificField == 9) { this->unk1C[0] = 8.0f; - this->unk1C[1] = 0.0f; + if (!fileProgressFlag_get(FILEPROG_53_CCW_PUZZLE_PODIUM_SWITCH_PRESSED)) { + // [port] Anchor suppresses the despawn: a despawned podium only comes back when the + // cube re-streams, which would strand a teammate standing here when the flag arrives. + if (EventSystem_Should(VB_CCW_PODIUM_DESPAWN, true, this)) { + marker_despawn(this->marker); + } else { + this->unk38_31 = true; + this->marker->propPtr->unk8_3 = false; + } + return; + } + revealCcwPuzzlePodium(this); } } - if (this->actorTypeSpecificField == 9) { + // [port] Waiting on the switch: reveal in place the moment the flag lands, ours or a teammate's. + if (this->unk38_31) { if (!fileProgressFlag_get(FILEPROG_53_CCW_PUZZLE_PODIUM_SWITCH_PRESSED)) { return; } - if (this->unk1C[1] == 0.0f) { - this->unk1C[1] = 1.0f; - if (!fileProgressFlag_get(FILEPROG_54_CCW_PUZZLE_PODIUM_ACTIVE)) { - __bundle_spawnFromFirstActor(BUNDLE_20__UNKNOWN, this); - func_80324CFC(0.0f, COMUSIC_43_ENTER_LEVEL_GLITTER, 0x7FFF); - func_80324D2C(2.1f, COMUSIC_43_ENTER_LEVEL_GLITTER); - func_8030E6D4(SFX_113_PAD_APPEARS); - } - } + revealCcwPuzzlePodium(this); } if ((this->actorTypeSpecificField == 9) && !fileProgressFlag_get(FILEPROG_54_CCW_PUZZLE_PODIUM_ACTIVE)) { diff --git a/src/port/Enhancements/Events/Hooks/Events.h b/src/port/Enhancements/Events/Hooks/Events.h index bb2970576..5d3b4b373 100644 --- a/src/port/Enhancements/Events/Hooks/Events.h +++ b/src/port/Enhancements/Events/Hooks/Events.h @@ -85,6 +85,8 @@ typedef enum VBehaviorID { VB_DOOR_OPEN_CAMERA, // Suppresses door-open camera lock when the flag came from a teammate, not us. VB_CC_RINGS_SNAP_WATER, // CC rings water snap on run teardown: suppressed when a teammate finished the rings. VB_LEVELDOOR_REMOTE_OPEN_DONE, // Lair door remote-open "already handled" test. + VB_CCW_PODIUM_DESPAWN, // Despawn the unrevealed CCW puzzle podium: suppressed so a teammate's switch press can + // reveal it live. // Romhack port gates VB_JIGGYSCORE_LEVEL_TOTAL, diff --git a/src/port/Network/Anchor/HookHandlers.cpp b/src/port/Network/Anchor/HookHandlers.cpp index 525028997..f7dc3d47f 100644 --- a/src/port/Network/Anchor/HookHandlers.cpp +++ b/src/port/Network/Anchor/HookHandlers.cpp @@ -306,6 +306,10 @@ void Anchor::RegisterHooks() { COND_VB_SHOULD(VB_CC_RINGS_SNAP_WATER, EVENT_PRIORITY_NORMAL, isConnected, { *should = false; }); + // Vanilla despawns the CCW podium until its switch is pressed, and only rebuilds it when the cube + // re-streams. Keep the actor alive (hidden) instead, so a teammate's press reveals it in place. + COND_VB_SHOULD(VB_CCW_PODIUM_DESPAWN, EVENT_PRIORITY_NORMAL, isConnected, { *should = false; }); + // Lair door remote-open: Door of Grunty's open flag (0xE2) is already set on arrival; key off // visual state (fully open == 0x1B) instead. Other lair doors stay flag-based. COND_VB_SHOULD(VB_LEVELDOOR_REMOTE_OPEN_DONE, EVENT_PRIORITY_NORMAL, isConnected, {