Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/event-triggers-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,27 @@ Confirmed offsets and dispatch order for the Sakuya descent + Water Lily grant +

The bypass installed by `src/okami-apclient/eventfix/kamiki_village.cpp` replaces `FUN_1804c64a0` with a stub that calls `clearCutsceneModeBits()`, `grantBrush(BrushOverlay::water_lily)`, and `transitionStageSubState(0xe)`. The natural chain's per-map state writes (e.g. `KamikiVillage` worldStateBits 11 / 149 / 163) are set elsewhere (trigger volumes, post-bloom scripts); the bypass does not interact with them.

## Gale Shrine galestorm tutorial bypass

The handler for the tutorial is FUN_1804E9CC0,

```cpp
if (((DAT_180b6b2ac >> 0x1e & 1) == 0) && ((*(uint *)(WORLD_STATE_POINTER + 0x45c) & 0x100) == 0))
{
cVar2 = FUN_1803f3380(PTR_DAT_1807a8cb0,5,0,1);
if (cVar2 != '\0') {
set_world_state_bit(0x80017); //set flag "stuck in platform"
puVar1 = PTR_DAT_1807a8cb0;
enter_cutsence_mode(PTR_DAT_1807a8cb0);
uVar3 = schedule_callback(puVar1 + 0x20,FUN_1804ec0c0,0xffffffff);
register_callback(puVar1,uVar3);
return;
}
}
```
We replace the FUN_1804ec0c0 with a stub that exits cutscene mode, grants the galestorm check to the player, and sets some flags to mark the tutorial as complete. The door stays closed, but it doesn't have collision as you're never supposed to be able to move while it's closed, so you can just cross it.


## Stage architecture (one level above TICK)

The CoN TICK has zero static call sites in main.dll. Its only xref is its `.pdata` exception-unwind entry. Despite that, scripts and TICKs in this engine are *not* loaded from external files: there is no separate script bytecode language. Scripts are compiled C++ callbacks baked into main.dll, scheduled by ID via `FUN_1803f35f0(ctx, script_id, ...)` and similar. So the TICK gets installed at runtime through some indirection that we haven't fully traced statically (likely a stage-id -> function-pointer table populated during stage init), but the answer lives somewhere inside main.dll, not in an on-disk script file. The on-disk room files (`data_pc/stN/rXXX.bin`) carry SCA trigger volumes, MSD strings, and per-room asset data, not callback pointers. That said, the *parallel* machinery (the stage descriptor object the TICK eventually drives) is fully visible in the binary.
Expand Down
2 changes: 2 additions & 0 deletions src/okami-apclient/eventfix/eventfix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "cave_of_nagi.hpp"
#include "common.hpp"
#include "gale_shrine.hpp"
#include "kamiki_village.hpp"
#include "registry.hpp"

Expand Down Expand Up @@ -48,6 +49,7 @@ void initialize()
int installed = 0;
installAll(cave_of_nagi::getBypasses(), installed);
installAll(kamiki_village::getBypasses(), installed);
installAll(gale_shrine::getBypasses(), installed);

wolf::logInfo("[eventfix] installed %d bypass hooks", installed);
}
Expand Down
37 changes: 37 additions & 0 deletions src/okami-apclient/eventfix/gale_shrine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "gale_shrine.hpp"

#include <okami/brushes.hpp>
#include <wolf_framework.hpp>

#include "common.hpp"

namespace eventfix::gale_shrine
{

namespace
{

void __fastcall stubGalestormTutoBypass()
{
eventfix::clearCutsceneModeBits();
eventfix::grantBrush(okami::BrushOverlay::galestorm);
// Set this flag to load the "healed" state on next reload
eventfix::setStateBit(0x80023);
// Set these to mark tutorial as completed
eventfix::setStateBit(0x80026);
eventfix::setStateBit(0x80033);
wolf::logInfo("[eventfix] Galestorm tutorial removed; Granted bursh power");
}

constexpr EventBypass kBypasses[] = {
{"Galestrom tutorail lock", 0x4EC0C0, stubGalestormTutoBypass},
};

} // namespace

std::span<const EventBypass> getBypasses()
{
return kBypasses;
}

} // namespace eventfix::gale_shrine
14 changes: 14 additions & 0 deletions src/okami-apclient/eventfix/gale_shrine.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <span>

#include "registry.hpp"

namespace eventfix::gale_shrine
{

// Bypass records for Gale Shrine forced-tutorial softlock. See
// docs/event-triggers-runtime.md for the runtime model and chain.
std::span<const EventBypass> getBypasses();

} // namespace eventfix::gale_shrine
1 change: 1 addition & 0 deletions src/okami-apclient/sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set(okami-apclient_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/eventfix/cave_of_nagi.cpp
${CMAKE_CURRENT_SOURCE_DIR}/eventfix/common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/eventfix/eventfix.cpp
${CMAKE_CURRENT_SOURCE_DIR}/eventfix/gale_shrine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/eventfix/kamiki_village.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gamestate_accessors.cpp
${CMAKE_CURRENT_SOURCE_DIR}/itempatch.cpp
Expand Down
Loading