mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-25 06:41:30 -04:00
Rework to use updated ItemService
This commit is contained in:
@@ -405,6 +405,12 @@
|
||||
- 0xFFFF # Yes, we've obtained the reward, do nothing
|
||||
- Jovani Give 20 Reward
|
||||
|
||||
- name: Jovani Give 20 Reward
|
||||
type: event
|
||||
event: 8
|
||||
parameters: 0x00010075
|
||||
next node index: 0x77E
|
||||
|
||||
#################################################
|
||||
# HINT SIGNS #
|
||||
#################################################
|
||||
@@ -611,4 +617,4 @@
|
||||
index: 21133
|
||||
type: message
|
||||
inf index: Cave of Ordeals Hint Sign Text
|
||||
next flow index: 0xFFFF
|
||||
next flow index: 0xFFFF
|
||||
|
||||
@@ -33,11 +33,6 @@
|
||||
- Ordona Province
|
||||
- Npc
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 1
|
||||
Index: 0x18F
|
||||
Next Node Index: 0x116
|
||||
Parameters: 0x0001004A
|
||||
Name Lookup:
|
||||
- Uli Cradle Delivery
|
||||
Event Flag: 0x0301
|
||||
@@ -49,11 +44,6 @@
|
||||
- Npc
|
||||
- Ordona Province
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 1
|
||||
Index: 0x6DF
|
||||
Next Node Index: 0x44A
|
||||
Parameters: 0x00010065
|
||||
Name Lookup:
|
||||
- Ordon Cat Rescue
|
||||
Event Flag: 0x1408
|
||||
@@ -64,14 +54,11 @@
|
||||
- Overworld
|
||||
- Ordona Province
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 1
|
||||
Index: 0x4D2
|
||||
Next Node Index: 0x2FA
|
||||
Parameters: 0x0001004B
|
||||
Shop:
|
||||
- Stage: 65
|
||||
Item: 0x4B
|
||||
Name Lookup:
|
||||
- Sera Shop Slingshot
|
||||
Event Flag: 0x4902
|
||||
|
||||
- Name: Ordon Shield
|
||||
@@ -292,11 +279,6 @@
|
||||
- Npc
|
||||
- Ordona Province
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 1
|
||||
Index: 0x345
|
||||
Next Node Index: 0x216
|
||||
Parameters: 0x00010021
|
||||
Name Lookup:
|
||||
- Herding Goats Reward
|
||||
Event Flag: 0x4240
|
||||
@@ -513,11 +495,6 @@
|
||||
- Faron Woods
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 6
|
||||
Index: 0x72
|
||||
Next Node Index: 0x54
|
||||
Parameters: 0x0001009D
|
||||
Name Lookup:
|
||||
- Coro Bottle
|
||||
Event Flag: 0x1A08
|
||||
@@ -1347,14 +1324,11 @@
|
||||
- Kakariko Village
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 2
|
||||
Index: 0x439
|
||||
Next Node Index: 0x2E8
|
||||
Parameters: 0x0001003E
|
||||
Shop:
|
||||
- Stage: 68
|
||||
Item: 0x3E
|
||||
Name Lookup:
|
||||
- Kakariko Village Malo Mart Hawkeye
|
||||
Switch Flag:
|
||||
Stage: 68
|
||||
Flag: 0x33
|
||||
@@ -1366,14 +1340,11 @@
|
||||
- Kakariko Village
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 2
|
||||
Index: 0x427
|
||||
Next Node Index: 0x2DB
|
||||
Parameters: 0x0001002C
|
||||
Shop:
|
||||
- Stage: 68
|
||||
Item: 0x2C
|
||||
Name Lookup:
|
||||
- Kakariko Village Malo Mart Hylian Shield
|
||||
Switch Flag:
|
||||
Stage: 68
|
||||
Flag: 0x39
|
||||
@@ -1385,14 +1356,11 @@
|
||||
- Kakariko Village
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 2
|
||||
Index: 0x449
|
||||
Next Node Index: 0x2F3
|
||||
Parameters: 0x00010061
|
||||
Shop:
|
||||
- Stage: 68
|
||||
Item: 0x61
|
||||
Name Lookup:
|
||||
- Kakariko Village Malo Mart Red Potion
|
||||
Switch Flag:
|
||||
Stage: 68
|
||||
Flag: 0x4
|
||||
@@ -1404,14 +1372,11 @@
|
||||
- Kakariko Village
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 2
|
||||
Index: 0x41C
|
||||
Next Node Index: 0x2D3
|
||||
Parameters: 0x0001002B
|
||||
Shop:
|
||||
- Stage: 68
|
||||
Item: 0x2B
|
||||
Name Lookup:
|
||||
- Kakariko Village Malo Mart Wooden Shield
|
||||
Switch Flag:
|
||||
Stage: 68
|
||||
Flag: 0x5
|
||||
@@ -1516,17 +1481,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
# Win the game without ever using the hawkeye
|
||||
- Group: 2
|
||||
Index: 0x124
|
||||
Next Node Index: 0xA6
|
||||
Parameters: 0x00010021
|
||||
# Win the game, but have used hawkeye before
|
||||
- Group: 2
|
||||
Index: 0xE0
|
||||
Next Node Index: 0x71
|
||||
Parameters: 0x00010021
|
||||
Name Lookup:
|
||||
- Talo Sharpshooting
|
||||
Event Flag: 0x0920
|
||||
@@ -1539,11 +1493,6 @@
|
||||
- Kakariko Village
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 2
|
||||
Index: 0x19A
|
||||
Next Node Index: 0xF5
|
||||
Parameters: 0x00010080
|
||||
Name Lookup:
|
||||
- Renados Letter
|
||||
Event Flag: 0x0F80
|
||||
@@ -1568,11 +1517,6 @@
|
||||
- Kakariko Graveyard
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 2
|
||||
Index: 0x55
|
||||
Next Node Index: 0x25
|
||||
Parameters: 0x00010031
|
||||
Name Lookup:
|
||||
- Rutelas Blessing
|
||||
Event Flag: 0x0804
|
||||
@@ -1585,11 +1529,6 @@
|
||||
- Kakariko Graveyard
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 2
|
||||
Index: 0x339
|
||||
Next Node Index: 0x1ED
|
||||
Parameters: 0x0001003D
|
||||
Name Lookup:
|
||||
- Gift From Ralis
|
||||
Event Flag: 0x3B80
|
||||
@@ -1958,11 +1897,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 6
|
||||
Index: 0x610
|
||||
Next Node Index: 0x439
|
||||
Parameters: 0x000100E9
|
||||
Name Lookup:
|
||||
- Skybook From Impaz
|
||||
Event Flag: 0x5F80
|
||||
@@ -1998,11 +1932,6 @@
|
||||
- Hidden Village
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 6
|
||||
Index: 0x32
|
||||
Next Node Index: 0x2A
|
||||
Parameters: 0x00010083
|
||||
Name Lookup:
|
||||
- Ilia Charm
|
||||
Event Flag: 0x2280
|
||||
@@ -2471,11 +2400,6 @@
|
||||
- Castle Town
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 4
|
||||
Index: 0x276
|
||||
Next Node Index: 0x1A5
|
||||
Parameters: 0x00010055
|
||||
Name Lookup:
|
||||
- STAR Prize 1
|
||||
Event Flag: 0x2308
|
||||
@@ -2488,11 +2412,6 @@
|
||||
- Castle Town
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 4
|
||||
Index: 0x269
|
||||
Next Node Index: 0x1A1
|
||||
Parameters: 0x00010056
|
||||
Name Lookup:
|
||||
- STAR Prize 2
|
||||
Event Flag: 0x2301
|
||||
@@ -2504,14 +2423,11 @@
|
||||
- Castle Town
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 4
|
||||
Index: 0x610
|
||||
Next Node Index: 0x5AE
|
||||
Parameters: 0x00010030
|
||||
Shop:
|
||||
- Stage: 73
|
||||
Item: 0x30
|
||||
Name Lookup:
|
||||
- Castle Town Malo Mart Magic Armor
|
||||
Switch Flag:
|
||||
Stage: 73
|
||||
Flag: 0x2B
|
||||
@@ -2567,17 +2483,6 @@
|
||||
- Castle Town
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
# Flow if less than 60 souls
|
||||
- Group: 4
|
||||
Index: 0x841
|
||||
Next Node Index: 0x773
|
||||
Parameters: 0x00010075
|
||||
# Custom flow if 60 souls
|
||||
- Group: 9
|
||||
Index: Jovani Give 20 Reward
|
||||
Next Node Index: 0x77E
|
||||
Parameters: 0x00010075
|
||||
Name Lookup:
|
||||
- Jovani 20 Poe Soul Reward
|
||||
Event Flag: 0x5510
|
||||
@@ -2592,11 +2497,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 4
|
||||
Index: 0x852
|
||||
Next Node Index: 0x781
|
||||
Parameters: 0x00010007
|
||||
Name Lookup:
|
||||
- Jovani 60 Poe Soul Reward
|
||||
Event Flag: 0x3820
|
||||
@@ -2630,11 +2530,6 @@
|
||||
- Castle Town
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 4
|
||||
Index: 0x14D
|
||||
Next Node Index: 0xC6
|
||||
Parameters: 0x00010081
|
||||
Name Lookup:
|
||||
- Telma Invoice
|
||||
Event Flag: 0x2180
|
||||
@@ -3157,11 +3052,6 @@
|
||||
- Lake Hylia
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 8
|
||||
Index: 0x180
|
||||
Next Node Index: 0xF7
|
||||
Parameters: 0x00010090
|
||||
Name Lookup:
|
||||
- Auru Gift To Fyer
|
||||
Event Flag: 0x2520
|
||||
@@ -3686,11 +3576,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 8
|
||||
Index: 0x1FC
|
||||
Next Node Index: 0x152
|
||||
Parameters: 0x00010021
|
||||
Name Lookup:
|
||||
- Plumm Fruit Balloon Minigame
|
||||
Event Flag: 0x2380
|
||||
@@ -3780,11 +3665,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 8
|
||||
Index: 0x2E4
|
||||
Next Node Index: 0x208
|
||||
Parameters: 0x00010051
|
||||
Name Lookup:
|
||||
- Iza Helping Hand
|
||||
Event Flag: 0x0B01
|
||||
@@ -3798,11 +3678,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 8
|
||||
Index: 0x2EE
|
||||
Next Node Index: 0x20F
|
||||
Parameters: 0x0001004F
|
||||
Name Lookup:
|
||||
- Iza Raging Rapids Minigame
|
||||
Event Flag: 0x5908
|
||||
@@ -3815,9 +3690,8 @@
|
||||
- ARC
|
||||
- REL
|
||||
Metadata:
|
||||
FLW Message:
|
||||
- Group: 0
|
||||
Message Id: 1822
|
||||
Name Lookup:
|
||||
- Fishing Hole Bottle
|
||||
Event Flag: 0x3908
|
||||
|
||||
- Name: Fishing Hole Heart Piece
|
||||
@@ -3833,11 +3707,8 @@
|
||||
- Stage: 62
|
||||
Flag: 0x80
|
||||
# Or picked up with the fishing rod during fishing
|
||||
FLW Message:
|
||||
- Group: 7
|
||||
Message Id: 7564
|
||||
- Group: 7
|
||||
Message Id: 7578
|
||||
Name Lookup:
|
||||
- Fishing Hole Heart Piece
|
||||
|
||||
|
||||
- Name: Zoras Domain Warp Portal
|
||||
@@ -4035,11 +3906,6 @@
|
||||
- Zoras Domain
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 7
|
||||
Index: 0x8
|
||||
Next Node Index: 0x1
|
||||
Parameters: 0x00010051
|
||||
Name Lookup:
|
||||
- Zoras Domain Underwater Goron
|
||||
Event Flag: 0x3D10
|
||||
@@ -4129,11 +3995,6 @@
|
||||
- Snowpeak Province
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 6
|
||||
Index: 0x282
|
||||
Next Node Index: 0x1C6
|
||||
Parameters: 0x00010091
|
||||
Name Lookup:
|
||||
- Ashei Sketch
|
||||
Event Flag: 0x2940
|
||||
@@ -4224,11 +4085,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 6
|
||||
Index: 0x2F8
|
||||
Next Node Index: 0x237
|
||||
Parameters: 0x00010021
|
||||
Name Lookup:
|
||||
- Snowboard Racing Prize
|
||||
Event Flag: 0x3B10
|
||||
@@ -4876,11 +4732,6 @@
|
||||
- ARC
|
||||
- Remote Location
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 5
|
||||
Index: 0x20A
|
||||
Next Node Index: 0x18F
|
||||
Parameters: 0x00010073
|
||||
Name Lookup:
|
||||
- Cave of Ordeals Great Fairy Reward
|
||||
Event Flag: 0x3E40
|
||||
@@ -5107,9 +4958,8 @@
|
||||
- ARC
|
||||
Goal Name: Diababa
|
||||
Metadata:
|
||||
FLW Message:
|
||||
- Group: 5
|
||||
Message Id: 5001
|
||||
Name Lookup:
|
||||
- Forest Temple Dungeon Reward
|
||||
Item Flag:
|
||||
Stage: 7
|
||||
Flag: 0x9E
|
||||
@@ -5149,11 +4999,6 @@
|
||||
- Big Key
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 5
|
||||
Index: 0x237
|
||||
Next Node Index: 0x19B
|
||||
Parameters: 0x000100F9
|
||||
Name Lookup:
|
||||
- Goron Mines Gor Amato Key Shard
|
||||
Event Flag: 0x3008 # late
|
||||
@@ -5273,11 +5118,6 @@
|
||||
- Big Key
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 5
|
||||
Index: 0x259
|
||||
Next Node Index: 0x1AD
|
||||
Parameters: 0x000100FA
|
||||
Name Lookup:
|
||||
- Goron Mines Gor Ebizo Key Shard
|
||||
Event Flag: 0x3702 # late
|
||||
@@ -5338,11 +5178,6 @@
|
||||
- Big Key
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 5
|
||||
Index: 0x248
|
||||
Next Node Index: 0x1A4
|
||||
Parameters: 0x000100FD
|
||||
Name Lookup:
|
||||
- Goron Mines Gor Liggs Key Shard
|
||||
Event Flag: 0x3701 # late
|
||||
@@ -5391,9 +5226,8 @@
|
||||
- ARC
|
||||
Goal Name: Fyrus
|
||||
Metadata:
|
||||
FLW Message:
|
||||
- Group: 5
|
||||
Message Id: 6011
|
||||
Name Lookup:
|
||||
- Goron Mines Dungeon Reward
|
||||
Event Flag: 0x0701
|
||||
|
||||
# LAKEBED TEMPLE
|
||||
@@ -5716,9 +5550,8 @@
|
||||
- ARC
|
||||
Goal Name: Morpheel
|
||||
Metadata:
|
||||
FLW Message:
|
||||
- Group: 5
|
||||
Message Id: 7001
|
||||
Name Lookup:
|
||||
- Lakebed Temple Dungeon Reward
|
||||
Event Flag: 0x0904 # late
|
||||
|
||||
# ARBITERS GROUNDS
|
||||
@@ -6064,11 +5897,6 @@
|
||||
- Dungeon Map
|
||||
- ARC
|
||||
Metadata:
|
||||
FLW Event:
|
||||
- Group: 5
|
||||
Index: 0x3C8
|
||||
Next Node Index: 0x265
|
||||
Parameters: 0x00010023
|
||||
Name Lookup:
|
||||
- Snowpeak Ruins Mansion Map
|
||||
Event Flag: 0x0B10 # late
|
||||
@@ -6285,9 +6113,8 @@
|
||||
- ARC
|
||||
Goal Name: Blizzeta
|
||||
Metadata:
|
||||
FLW Message:
|
||||
- Group: 5
|
||||
Message Id: 9301
|
||||
Name Lookup:
|
||||
- Snowpeak Ruins Dungeon Reward
|
||||
Event Flag: 0x2008
|
||||
|
||||
- Name: Temple of Time Lobby Lantern Chest
|
||||
@@ -6520,9 +6347,8 @@
|
||||
- ARC
|
||||
Goal Name: Armogohma
|
||||
Metadata:
|
||||
FLW Message:
|
||||
- Group: 5
|
||||
Message Id: 9401
|
||||
Name Lookup:
|
||||
- Temple of Time Dungeon Reward
|
||||
Item Flag:
|
||||
Stage: 10
|
||||
Flag: 0x9E
|
||||
@@ -6847,9 +6673,8 @@
|
||||
- ARC
|
||||
Goal Name: Argorok
|
||||
Metadata:
|
||||
FLW Message:
|
||||
- Group: 5
|
||||
Message Id: 11001
|
||||
Name Lookup:
|
||||
- City in the Sky Dungeon Reward
|
||||
Event Flag: 0x2002
|
||||
|
||||
# PALACE OF TWILIGHT
|
||||
@@ -7667,4 +7492,4 @@
|
||||
- Overworld
|
||||
- Non-Item Location
|
||||
Metadata:
|
||||
- None
|
||||
- None
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "d/actor/d_a_npc_zrc.h"
|
||||
#include "d/actor/d_a_npc_zrz.h"
|
||||
#include "d/actor/d_a_obj_bosswarp.h"
|
||||
#include "d/actor/d_a_obj_master_sword.h"
|
||||
#include "d/actor/d_a_obj_swBallC.h"
|
||||
#include "d/actor/d_a_obj_zra_rock.h"
|
||||
#include "d/actor/d_a_tag_kmsg.h"
|
||||
@@ -145,9 +144,6 @@ DEFINE_HOOK(&daNpc_ykW_c::isDelete, daNpc_ykW_c__isDelete);
|
||||
|
||||
DEFINE_HOOK_SYMBOL("daE_MD_Create", int(fopAc_ac_c*), daE_MD_c__create);
|
||||
|
||||
DEFINE_HOOK(&fopAcM_orderMapToolEvent, orderMapToolEvent);
|
||||
DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMasterSword_c__execute);
|
||||
|
||||
DEFINE_HOOK(&dScnName_c::changeGameScene, dScnName_c__changeGameScene);
|
||||
|
||||
DEFINE_HOOK(&daNpc_zrZ_c::isDelete, daNpc_zrZ_c__isDelete);
|
||||
@@ -178,13 +174,6 @@ DEFINE_HOOK(&daNpc_zrC_c::isDelete, daNpc_zrC_c__isDelete);
|
||||
|
||||
DEFINE_HOOK(&daObjZraRock_c::create, daObjZraRock_c__create);
|
||||
|
||||
#ifdef _MSVC_LANG
|
||||
#define phase_1_dScnPly_sig "?phase_1@@YAHPAUdScnPly_c@@@Z" // sig is wrong?
|
||||
#else
|
||||
#define phase_1_dScnPly_sig "_Z7phase_1P9dScnPly_c"
|
||||
#endif
|
||||
// DEFINE_HOOK_SYMBOL(phase_1_dScnPly_sig, int(dScnPly_c*), phase_1__dScnPly_c);
|
||||
|
||||
DEFINE_HOOK(&dMenu_Ring_c::textScaleHIO, dMenu_Ring_c__textScaleHIO);
|
||||
|
||||
#ifdef _MSVC_LANG
|
||||
@@ -1806,7 +1795,8 @@ HookAction hookPreProcCoGetItem(ModContext*, void* args, void*, void*) {
|
||||
}
|
||||
|
||||
// Don't show special text in rando
|
||||
if (dComIfGs_getPohSpiritNum() == 20) {
|
||||
const u8 nextPoeCount = dComIfGs_getPohSpiritNum() + 1;
|
||||
if (nextPoeCount == 20 || nextPoeCount == 60) {
|
||||
i_this->field_0x32cc = i_this->mProcVar2.field_0x300c + 0x65;
|
||||
}
|
||||
|
||||
@@ -2226,38 +2216,6 @@ void hookPostEMdCreate(ModContext*, void* args, void*, void*) {
|
||||
hookEMdCreate_prevSkipInfo = 0;
|
||||
}
|
||||
|
||||
HookAction hookPreOrderMapToolEvent(ModContext*, void* args, void*, void*) {
|
||||
auto eventId = mods::arg<u8>(args, 1);
|
||||
|
||||
// skip starting the master sword cutscene so we can handle the checks manually
|
||||
if (daAlink_c::checkStageName("F_SP117") && eventId == 2) {
|
||||
// Set the necessary flags to de-spawn the MS and set the save file event flag.
|
||||
dComIfGs_onTmpBit(0x820);
|
||||
dComIfGs_onEventBit(0x2120);
|
||||
return HOOK_SKIP_ORIGINAL;
|
||||
}
|
||||
|
||||
return HOOK_CONTINUE;
|
||||
}
|
||||
|
||||
HookAction hookPreMasterSwordExecute(ModContext*, void* args, void* retval, void*) {
|
||||
auto* i_this = mods::arg<daObjMasterSword_c*>(args, 0);
|
||||
|
||||
(i_this->*i_this->mActionFunc[1])();
|
||||
dComIfG_Ccsp()->Set(&i_this->mCyl);
|
||||
|
||||
i_this->mBtk.play();
|
||||
i_this->mBrk.play();
|
||||
|
||||
if (dComIfGs_isTmpBit(dSv_event_tmp_flag_c::tempBitLabels[73])) {
|
||||
// Don't automatically give the master sword in randomizer
|
||||
fopAcM_delete(i_this);
|
||||
}
|
||||
|
||||
*static_cast<int*>(retval) = 1;
|
||||
return HOOK_SKIP_ORIGINAL;
|
||||
}
|
||||
|
||||
void hookPost_dScnName_c__changeGameScene(ModContext* ctx, void* args, void* retval, void* userdata) {
|
||||
if (!mDoRst::isReset() && !fopOvlpM_IsPeek()) {
|
||||
randomizer::session::registerStartingLocation();
|
||||
@@ -2579,32 +2537,6 @@ void hookPostObjZraRockCreate(ModContext*, void* args, void* retval, void*) {
|
||||
}
|
||||
}
|
||||
|
||||
bool g_horseCallFirstBit = false;
|
||||
u8 g_slot21Item = 0;
|
||||
HookAction hookPreScnPlyPhase1(ModContext*, void*, void*, void*) {
|
||||
if (!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 &&
|
||||
dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12)
|
||||
{
|
||||
// Save item info for rando to restore after function runs
|
||||
g_horseCallFirstBit = dComIfGs_isItemFirstBit(dItemNo_HORSE_FLUTE_e);
|
||||
g_slot21Item = dComIfGs_getItem(SLOT_21, false);
|
||||
}
|
||||
|
||||
return HOOK_CONTINUE;
|
||||
}
|
||||
|
||||
void hookPostScnPlyPhase1(ModContext*, void*, void*, void*) {
|
||||
if (!strcmp(dComIfGp_getStartStageName(), "F_SP104") && dComIfGp_getStartStageRoomNo() == 1 &&
|
||||
dComIfGp_getStartStagePoint() == 23 && dComIfGp_getStartStageLayer() == 12)
|
||||
{
|
||||
// Restore item info
|
||||
if (!g_horseCallFirstBit) {
|
||||
dComIfGs_offItemFirstBit(dItemNo_HORSE_FLUTE_e);
|
||||
}
|
||||
dComIfGs_setItem(SLOT_21, g_slot21Item);
|
||||
}
|
||||
}
|
||||
|
||||
J2DPicture* g_dpadIcon{};
|
||||
void hookPostMenuRingTextScaleHIO(ModContext*, void* args, void*, void*) {
|
||||
auto menuRing = mods::arg<dMenu_Ring_c*>(args, 0);
|
||||
@@ -3230,9 +3162,6 @@ ModResult initialize() {
|
||||
ADD_HOOK_PRE(daE_MD_c__create, hookPreEMdCreate);
|
||||
ADD_HOOK_POST(daE_MD_c__create, hookPostEMdCreate);
|
||||
|
||||
ADD_HOOK_PRE(orderMapToolEvent, hookPreOrderMapToolEvent);
|
||||
ADD_HOOK_PRE(daObjMasterSword_c__execute, hookPreMasterSwordExecute);
|
||||
|
||||
ADD_HOOK_POST(dScnName_c__changeGameScene, hookPost_dScnName_c__changeGameScene);
|
||||
|
||||
ADD_HOOK_POST(daNpc_zrZ_c__isDelete, hookPostNpcZrzIsDelete);
|
||||
@@ -3263,9 +3192,6 @@ ModResult initialize() {
|
||||
|
||||
ADD_HOOK_POST(daObjZraRock_c__create, hookPostObjZraRockCreate);
|
||||
|
||||
// ADD_HOOK_PRE(phase_1__dScnPly_c, hookPreScnPlyPhase1);
|
||||
// ADD_HOOK_POST(phase_1__dScnPly_c, hookPostScnPlyPhase1);
|
||||
|
||||
ADD_HOOK_POST(dMenu_Ring_c__textScaleHIO, hookPostMenuRingTextScaleHIO);
|
||||
ADD_HOOK_POST(dMenu_Ring_c__destructor, hookPostMenuRingDestructor);
|
||||
ADD_HOOK_POST(dMenu_Ring_c__setActiveCursor, hookPostMenuRingSetActiveCursor);
|
||||
@@ -3373,9 +3299,6 @@ ModResult uninstall() {
|
||||
|
||||
mods::hook::uninstall<daE_MD_c__create>(svc_hook);
|
||||
|
||||
mods::hook::uninstall<orderMapToolEvent>(svc_hook);
|
||||
mods::hook::uninstall<daObjMasterSword_c__execute>(svc_hook);
|
||||
|
||||
mods::hook::uninstall<dScnName_c__changeGameScene>();
|
||||
|
||||
mods::hook::uninstall<daNpc_zrZ_c__isDelete>(svc_hook);
|
||||
@@ -3400,8 +3323,6 @@ ModResult uninstall() {
|
||||
|
||||
mods::hook::uninstall<daObjZraRock_c__create>(svc_hook);
|
||||
|
||||
//mods::hook::uninstall<phase_1__dScnPly_c>();
|
||||
|
||||
mods::hook::uninstall<dMenu_Ring_c__textScaleHIO>(svc_hook);
|
||||
mods::hook::uninstall<dMenu_Ring_c__destructor>(svc_hook);
|
||||
mods::hook::uninstall<dMenu_Ring_c__setActiveCursor>(svc_hook);
|
||||
|
||||
@@ -485,10 +485,6 @@ void randomizer_item_func_MIRROR_PIECE_4() {
|
||||
dComIfGs_onCollectMirror(3);
|
||||
}
|
||||
|
||||
void randomizer_item_func_POU_SPIRIT() {
|
||||
dComIfGs_addPohSpiritNum();
|
||||
}
|
||||
|
||||
void randomizer_item_func_AIR_LETTER() {
|
||||
u8 letterCount = getAncientDocumentNum();
|
||||
setAncientDocumentNum(letterCount + 1);
|
||||
@@ -764,7 +760,7 @@ static void (*item_func_ptr_randomizer[256])() = {
|
||||
/* 0xDD */ item_func_noentry,
|
||||
/* 0xDE */ item_func_noentry,
|
||||
/* 0xDF */ item_func_noentry,
|
||||
/* 0xE0 */ randomizer_item_func_POU_SPIRIT,
|
||||
/* 0xE0 */ item_func_POU_SPIRIT,
|
||||
/* 0xE1 */ item_func_ENDING_BLOW,
|
||||
/* 0xE2 */ item_func_SHIELD_ATTACK,
|
||||
/* 0xE3 */ item_func_BACK_SLICE,
|
||||
@@ -1768,4 +1764,4 @@ void restore_item_data_tables() {
|
||||
s_applied = false;
|
||||
}
|
||||
|
||||
} // namespace randomizer::item
|
||||
} // namespace randomizer::item
|
||||
|
||||
+586
-106
@@ -1,134 +1,614 @@
|
||||
#include "messages.hpp"
|
||||
|
||||
#include "JSystem/JMessage/control.h"
|
||||
#include "d/d_msg_class.h"
|
||||
#include "d/d_save.h"
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "randomizer_context.hpp"
|
||||
#include "custom_flow_ids.hpp"
|
||||
#include "randomizer_context.hpp"
|
||||
#include "stages.h"
|
||||
#include "tools.h"
|
||||
|
||||
#include "d/actor/d_a_alink.h"
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "d/d_item.h"
|
||||
#include "d/d_kankyo.h"
|
||||
#include "d/d_save.h"
|
||||
#include "d/d_stage.h"
|
||||
|
||||
#include <mods/bits.hpp>
|
||||
#include <mods/svc/flow.hpp>
|
||||
#include <mods/svc/log.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
static JMSMesgEntry_c defaultJMSMesgEntry{
|
||||
.string_offset = 0,
|
||||
.message_id = 0,
|
||||
.event_label_id = 0,
|
||||
.speaker = 0x24,
|
||||
.box_kind = 0x00,
|
||||
.draw_type = 0x00,
|
||||
.box_position = 0x00,
|
||||
.item_no = 0xFF,
|
||||
.line_alignment = 0x00,
|
||||
.speaker_mood = 0x00,
|
||||
.camera_attr = 0x00,
|
||||
.talk_anim = 0x02,
|
||||
.face_anim = 0x03,
|
||||
.lines_per_page = 0x04,
|
||||
};
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// Format certain messages that need to have dynamic info in them
|
||||
char* GetFormatedTextOverride(u32 key, std::string& text) {
|
||||
// Store formatted message in static buffer so it never goes away.
|
||||
// This is fine as long as we only ever need to format messages
|
||||
// for textboxes, but will cause issues if we need to use it for
|
||||
// other UI elements
|
||||
static std::array<char, 256> buf;
|
||||
u32 value{};
|
||||
char* outIt;
|
||||
namespace randomizer::messages {
|
||||
namespace {
|
||||
|
||||
constexpr uint16_t kMessageGroupCount = 9;
|
||||
constexpr uint16_t kLegacyQueryEventFlag = 53;
|
||||
constexpr uint16_t kLegacyQueryChangeTime = 54;
|
||||
constexpr uint16_t kLegacyQueryReturnToSpawn = 55;
|
||||
constexpr uint8_t kLegacyEventNoOp = 43;
|
||||
constexpr uint8_t kLegacyEventChangeTime = 44;
|
||||
constexpr uint8_t kLegacyEventReturnToSpawn = 45;
|
||||
constexpr uint8_t kLegacyEventSetTrackerFlag = 46;
|
||||
constexpr uint8_t kLegacyEventRemoveTradeItem = 47;
|
||||
|
||||
using LegacyIdMap = std::unordered_map<uint16_t, uint16_t>;
|
||||
using MessageIdMaps = std::array<LegacyIdMap, kMessageGroupCount>;
|
||||
|
||||
mods::flow::Query s_eventFlagQuery;
|
||||
mods::flow::Query s_changeTimeQuery;
|
||||
mods::flow::Query s_returnToSpawnQuery;
|
||||
mods::flow::Event s_changeTimeEvent;
|
||||
mods::flow::Event s_returnToSpawnEvent;
|
||||
mods::flow::Event s_removeTradeItemEvent;
|
||||
std::vector<mods::flow::Graph> s_graphs;
|
||||
std::vector<mods::flow::RegisteredMessage> s_messages;
|
||||
std::vector<mods::flow::MessageOverride> s_overrides;
|
||||
|
||||
uint32_t read_parameter(const uint8_t parameters[4]) {
|
||||
return static_cast<uint32_t>(parameters[0]) << 24 | static_cast<uint32_t>(parameters[1]) << 16 |
|
||||
static_cast<uint32_t>(parameters[2]) << 8 | parameters[3];
|
||||
}
|
||||
|
||||
uint16_t query_event_flag(ModContext*, const FlowQueryContext* query, void*) {
|
||||
if (query == nullptr || query->parameter >= std::size(dSv_event_flag_c::saveBitLabels)) {
|
||||
return 0;
|
||||
}
|
||||
return dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[query->parameter]) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint16_t query_change_time(ModContext*, const FlowQueryContext*, void*) {
|
||||
if (!dKy_darkworld_check() && !daAlink_c::checkForestOldCentury() &&
|
||||
(daAlink_c::checkField() || daAlink_c::checkCastleTown()) &&
|
||||
!daAlink_c::checkStageName("R_SP161"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t query_return_to_spawn(ModContext*, const FlowQueryContext*, void*) {
|
||||
const int stageId = getStageID();
|
||||
if (stageId <= Darkhammer) {
|
||||
return randomizer_GetContext().mReturnToPlaceOverrides.contains(stageId) ? 0 : 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
void event_change_time(ModContext*, const FlowEventContext*, void*) {
|
||||
if (daPy_py_c::checkNowWolf()) {
|
||||
g_randomizerState.setHasPendingToDChange(true);
|
||||
} else {
|
||||
g_randomizerState.handleTimeOfDayChange();
|
||||
}
|
||||
}
|
||||
|
||||
void event_return_to_spawn(ModContext*, const FlowEventContext* event, void*) {
|
||||
if (event != nullptr) {
|
||||
randomizer_returnToSpawn(read_parameter(event->parameters) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void event_remove_trade_item(ModContext*, const FlowEventContext* event, void*) {
|
||||
if (event == nullptr) {
|
||||
return;
|
||||
}
|
||||
const uint8_t item = static_cast<uint8_t>(read_parameter(event->parameters));
|
||||
if (item == dItemNo_LETTER_e || item == dItemNo_BILL_e || item == dItemNo_WOOD_STATUE_e ||
|
||||
item == dItemNo_IRIAS_PENDANT_e)
|
||||
{
|
||||
offWarashibeItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encoded_text(const std::string& text) {
|
||||
std::vector<uint8_t> result{text.begin(), text.end()};
|
||||
if (result.empty() || result.back() != 0) {
|
||||
result.push_back(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MessageEntryData default_message_entry() {
|
||||
MessageEntryData entry{};
|
||||
entry.bytes[8] = 0x24;
|
||||
entry.bytes[12] = 0xff;
|
||||
entry.bytes[16] = 0x02;
|
||||
entry.bytes[17] = 0x03;
|
||||
entry.bytes[18] = 0x04;
|
||||
return entry;
|
||||
}
|
||||
|
||||
MessageEntryData message_entry(const RandomizerContext& context, uint32_t legacyKey) {
|
||||
MessageEntryData entry = default_message_entry();
|
||||
const auto found = context.mAttributeOverrides.find(legacyKey);
|
||||
if (found != context.mAttributeOverrides.end()) {
|
||||
std::memcpy(entry.bytes, found->second.data(), sizeof(entry.bytes));
|
||||
std::fill_n(entry.bytes, 6, uint8_t{});
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> text_keys(const RandomizerContext& context, uint16_t group) {
|
||||
std::unordered_set<uint32_t> unique;
|
||||
for (const auto& [language, overrides] : context.mTextOverrides) {
|
||||
for (const auto& [key, text] : overrides) {
|
||||
if (key >> 16 == group) {
|
||||
unique.insert(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<uint32_t> result{unique.begin(), unique.end()};
|
||||
std::ranges::sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
ModResult register_custom_messages(const RandomizerContext& context, MessageIdMaps& messageIds) {
|
||||
const auto keys = text_keys(context, CUSTOM_BMG_GROUP);
|
||||
for (uint16_t group = 0; group < kMessageGroupCount; ++group) {
|
||||
for (const uint32_t key : keys) {
|
||||
std::vector<mods::flow::MessageVariant> variants;
|
||||
for (const auto& [language, overrides] : context.mTextOverrides) {
|
||||
const auto found = overrides.find(key);
|
||||
if (found == overrides.end() || language < MESSAGE_LANGUAGE_ENGLISH ||
|
||||
language > MESSAGE_LANGUAGE_ITALIAN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
variants.emplace_back(static_cast<MessageLanguage>(language),
|
||||
message_entry(context, key), encoded_text(found->second));
|
||||
}
|
||||
if (variants.empty()) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
auto message = mods::flow::register_message(group, variants);
|
||||
if (!message) {
|
||||
return message.result();
|
||||
}
|
||||
messageIds[group].emplace(static_cast<uint16_t>(key), message.id());
|
||||
s_messages.push_back(std::move(message));
|
||||
}
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
bool formatted_override(
|
||||
ModContext*, const MessageOverrideContext* message, MessageTextData* outText, void*) {
|
||||
if (message == nullptr || outText == nullptr || !randomizer_IsActive()) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t key = static_cast<uint32_t>(message->group) << 16 | message->message_id;
|
||||
const auto language = static_cast<int>(message->language);
|
||||
const auto languageIt = randomizer_GetContext().mTextOverrides.find(language);
|
||||
if (languageIt == randomizer_GetContext().mTextOverrides.end()) {
|
||||
return false;
|
||||
}
|
||||
const auto textIt = languageIt->second.find(key);
|
||||
if (textIt == languageIt->second.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t value = 0;
|
||||
// For item counts, execItemGet hasn't run yet, so add one to the count
|
||||
switch (key) {
|
||||
case (0 << 16) | 325: // Group 0, id 325
|
||||
case 325: // Group 0, id 325
|
||||
// Poe Soul get item text
|
||||
value = dComIfGs_getPohSpiritNum() + 1;
|
||||
outIt = fmt::vformat_to(buf.data(), text, fmt::make_format_args(value));
|
||||
break;
|
||||
case (0 << 16) | 335: // Group 0, id 335
|
||||
case 335: // Group 0, id 335
|
||||
// Sky book characters get item text
|
||||
value = getAncientDocumentNum() + 1;
|
||||
outIt = fmt::vformat_to(buf.data(), text, fmt::make_format_args(value));
|
||||
break;
|
||||
default:
|
||||
// No override, return original text
|
||||
return text.data();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Null-terminate
|
||||
size_t len = std::distance(buf.data(), outIt);
|
||||
buf[len] = '\0';
|
||||
|
||||
// Return overriden text
|
||||
return buf.data();
|
||||
thread_local std::vector<uint8_t> formatted;
|
||||
formatted = encoded_text(fmt::format(fmt::runtime(textIt->second), value));
|
||||
outText->text = formatted.data();
|
||||
outText->text_size = formatted.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 getLanguageForOverride() {
|
||||
u8 language = dSv_player_config_c::LANGUAGE_ENGLISH;
|
||||
|
||||
// TODO: add service or something to check game language
|
||||
/*if (dusk::version::isRegionPal()) {
|
||||
language = dComIfGs_getPalLanguage();
|
||||
}*//* else if (dusk::version::isRegionJpn()) {
|
||||
language = randomizer::Text::JAPANESE;
|
||||
}*/
|
||||
|
||||
return language;
|
||||
}
|
||||
|
||||
void HandleTextOverrides(JMessage::TControl* control, JMessage::TProcessor const* pProcessor, int groupID, int index) {
|
||||
if (randomizer_IsActive()) {
|
||||
// Get the entry for this message
|
||||
auto entry = static_cast<JMSMesgEntry_c*>(pProcessor->getMessageEntry_messageCode(groupID, index));
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the message id is >= 5000 then it's part of the stage file's message group
|
||||
// Otherwise it's part of group 0
|
||||
auto msgId = entry->message_id.host();
|
||||
u16 group = 0;
|
||||
if (msgId >= 5000) {
|
||||
group = dComIfGp_getStageStagInfo()->mMsgGroup;
|
||||
}
|
||||
|
||||
u32 key = (group << 16) | msgId;
|
||||
auto& textOverrides = randomizer_GetContext().mTextOverrides;
|
||||
u8 language = getLanguageForOverride();
|
||||
if (textOverrides.at(language).contains(key)) {
|
||||
control->pMessageText_begin_ = GetFormatedTextOverride(key, textOverrides[language][key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleCustomText(JMessage::TControl* control, u16 msgId) {
|
||||
if (randomizer_IsActive()) {
|
||||
u32 key = (CUSTOM_BMG_GROUP << 16) | msgId;
|
||||
auto& textOverrides = randomizer_GetContext().mTextOverrides;
|
||||
u8 language = getLanguageForOverride();
|
||||
if (textOverrides.at(language).contains(key)) {
|
||||
control->pMessageText_begin_ = GetFormatedTextOverride(key, textOverrides[language][key]);
|
||||
|
||||
// Get the attributes for this text-box if they were specified
|
||||
auto& attributeOverrides = randomizer_GetContext().mAttributeOverrides;
|
||||
if (attributeOverrides.contains(key)) {
|
||||
control->pEntry_ = reinterpret_cast<void*>(&attributeOverrides[key]);
|
||||
// Otherwise, use the default entry
|
||||
} else {
|
||||
defaultJMSMesgEntry.message_id = msgId;
|
||||
control->pEntry_ = &defaultJMSMesgEntry;
|
||||
ModResult register_native_overrides(const RandomizerContext& context) {
|
||||
for (uint16_t group = 0; group < kMessageGroupCount; ++group) {
|
||||
for (const uint32_t key : text_keys(context, group)) {
|
||||
for (const auto& [language, overrides] : context.mTextOverrides) {
|
||||
const auto found = overrides.find(key);
|
||||
if (found == overrides.end() || language < MESSAGE_LANGUAGE_ENGLISH ||
|
||||
language > MESSAGE_LANGUAGE_ITALIAN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
mods::flow::MessageOverride message;
|
||||
if (key == 325 || key == 335) {
|
||||
message = mods::flow::override_message_fn(group, static_cast<uint16_t>(key),
|
||||
static_cast<MessageLanguage>(language), formatted_override);
|
||||
} else {
|
||||
const auto text = encoded_text(found->second);
|
||||
message = mods::flow::override_message(group, static_cast<uint16_t>(key),
|
||||
static_cast<MessageLanguage>(language), std::span{text});
|
||||
}
|
||||
if (!message) {
|
||||
return message.result();
|
||||
}
|
||||
s_overrides.push_back(std::move(message));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
// Used in special cases
|
||||
char* GetTextOverride(s16 groupID, u32 messageId) {
|
||||
u32 key = (groupID << 16) | messageId;
|
||||
auto& textOverrides = randomizer_GetContext().mTextOverrides;
|
||||
u8 language = getLanguageForOverride();
|
||||
if (textOverrides.at(language).contains(key)) {
|
||||
return GetFormatedTextOverride(key, textOverrides[language][key]);
|
||||
std::vector<uint16_t> custom_node_ids(const RandomizerContext& context) {
|
||||
std::vector<uint16_t> result;
|
||||
for (const auto& [key, node] : context.mFlowPatches) {
|
||||
if (key >> 16 == CUSTOM_BMG_GROUP) {
|
||||
result.push_back(static_cast<uint16_t>(key));
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
std::ranges::sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
ModResult allocate_shared_node_ids(const std::array<FlowGraphHandle, kMessageGroupCount>& handles,
|
||||
size_t count, std::array<std::vector<uint16_t>, kMessageGroupCount>& allocated,
|
||||
LegacyIdMap& nodeIds, const std::vector<uint16_t>& legacyIds) {
|
||||
if (count == 0) {
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
std::array<std::unordered_set<uint16_t>, kMessageGroupCount> allocatedSets;
|
||||
std::vector<uint16_t> common;
|
||||
while (common.size() < count) {
|
||||
for (size_t group = 0; group < handles.size(); ++group) {
|
||||
uint16_t id = 0;
|
||||
const ModResult result = svc_flow->allocate_node(mod_ctx, handles[group], &id);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
allocated[group].push_back(id);
|
||||
allocatedSets[group].insert(id);
|
||||
}
|
||||
|
||||
common.clear();
|
||||
for (const uint16_t candidate : allocatedSets.front()) {
|
||||
const bool present = std::ranges::all_of(
|
||||
allocatedSets, [candidate](const auto& ids) { return ids.contains(candidate); });
|
||||
if (present) {
|
||||
common.push_back(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ranges::sort(common);
|
||||
for (size_t i = 0; i < legacyIds.size(); ++i) {
|
||||
nodeIds.emplace(legacyIds[i], common[i]);
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult remap_target(uint16_t legacy, const LegacyIdMap& nodeIds, uint16_t& out) {
|
||||
if (legacy == mods::flow::kEnd || legacy < BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
out = legacy;
|
||||
return MOD_OK;
|
||||
}
|
||||
const auto found = nodeIds.find(legacy);
|
||||
if (found == nodeIds.end()) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
out = found->second;
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult remap_query(FlowNodeData& node) {
|
||||
const uint16_t legacy = mods::read_bits<uint16_t>(node.bytes + 2);
|
||||
FlowQueryId query = legacy;
|
||||
switch (legacy) {
|
||||
case kLegacyQueryEventFlag:
|
||||
query = s_eventFlagQuery.id();
|
||||
break;
|
||||
case kLegacyQueryChangeTime:
|
||||
query = s_changeTimeQuery.id();
|
||||
break;
|
||||
case kLegacyQueryReturnToSpawn:
|
||||
query = s_returnToSpawnQuery.id();
|
||||
break;
|
||||
default:
|
||||
if (legacy >= FLOW_QUERY_BUILTIN_COUNT) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
mods::write_bits(node.bytes + 2, query);
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
ModResult remap_event(FlowNodeData& node) {
|
||||
switch (node.bytes[1]) {
|
||||
case kLegacyEventNoOp:
|
||||
node.bytes[1] = FLOW_EVENT_UNUSED_42;
|
||||
break;
|
||||
case kLegacyEventChangeTime:
|
||||
node.bytes[1] = s_changeTimeEvent.id();
|
||||
break;
|
||||
case kLegacyEventReturnToSpawn:
|
||||
node.bytes[1] = s_returnToSpawnEvent.id();
|
||||
break;
|
||||
case kLegacyEventSetTrackerFlag:
|
||||
// Older seed files may contain the retired pre-grant tracker event.
|
||||
node.bytes[1] = FLOW_EVENT_UNUSED_42;
|
||||
break;
|
||||
case kLegacyEventRemoveTradeItem:
|
||||
node.bytes[1] = s_removeTradeItemEvent.id();
|
||||
break;
|
||||
default:
|
||||
if (node.bytes[1] >= FLOW_EVENT_BUILTIN_COUNT) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
FlowNodeData raw_node(uint64_t value) {
|
||||
FlowNodeData node{};
|
||||
std::memcpy(node.bytes, &value, sizeof(node.bytes));
|
||||
return node;
|
||||
}
|
||||
|
||||
ModResult remap_node(const RandomizerContext& context, uint32_t legacyKey, uint16_t group,
|
||||
FlowGraphHandle handle, const LegacyIdMap& nodeIds, const MessageIdMaps& messageIds,
|
||||
FlowNodeData& node) {
|
||||
switch (node.bytes[0]) {
|
||||
case 1: {
|
||||
const uint16_t legacyMessage = mods::read_bits<uint16_t>(node.bytes + 2);
|
||||
if (legacyMessage >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
const auto found = messageIds[group].find(legacyMessage);
|
||||
if (found == messageIds[group].end()) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
mods::write_bits(node.bytes + 2, found->second);
|
||||
}
|
||||
uint16_t target = 0;
|
||||
const ModResult result =
|
||||
remap_target(mods::read_bits<uint16_t>(node.bytes + 4), nodeIds, target);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
mods::write_bits(node.bytes + 4, target);
|
||||
return MOD_OK;
|
||||
}
|
||||
case 2: {
|
||||
ModResult result = remap_query(node);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
const auto overrides = context.mFlowPatchesBranchOverrides.find(legacyKey);
|
||||
if (overrides == context.mFlowPatchesBranchOverrides.end()) {
|
||||
return MOD_OK;
|
||||
}
|
||||
if (overrides->second.empty() || overrides->second.size() != node.bytes[1] ||
|
||||
overrides->second.size() > std::numeric_limits<uint16_t>::max())
|
||||
{
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
std::vector<uint16_t> targets;
|
||||
targets.reserve(overrides->second.size());
|
||||
for (const uint16_t legacyTarget : overrides->second) {
|
||||
uint16_t target = 0;
|
||||
result = remap_target(legacyTarget, nodeIds, target);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
targets.push_back(target);
|
||||
}
|
||||
uint16_t firstEdge = 0;
|
||||
result = svc_flow->add_edges(
|
||||
mod_ctx, handle, targets.data(), static_cast<uint16_t>(targets.size()), &firstEdge);
|
||||
if (result == MOD_OK) {
|
||||
mods::write_bits(node.bytes + 6, firstEdge);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case 3: {
|
||||
ModResult result = remap_event(node);
|
||||
if (result != MOD_OK || node.bytes[1] == FLOW_EVENT_JUMP_FLOW) {
|
||||
return result;
|
||||
}
|
||||
const uint16_t legacyEdge = mods::read_bits<uint16_t>(node.bytes + 2);
|
||||
if (legacyEdge < BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
return MOD_OK;
|
||||
}
|
||||
uint16_t target = 0;
|
||||
result = remap_target(legacyEdge, nodeIds, target);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
uint16_t edge = 0;
|
||||
result = svc_flow->add_edges(mod_ctx, handle, &target, 1, &edge);
|
||||
if (result == MOD_OK) {
|
||||
mods::write_bits(node.bytes + 2, edge);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
void remap_actor_record(std::vector<uint8_t>& bytes, const LegacyIdMap& nodeIds) {
|
||||
if (bytes.size() < sizeof(stage_actor_data_class)) {
|
||||
return;
|
||||
}
|
||||
stage_actor_data_class actor{};
|
||||
std::memcpy(&actor, bytes.data(), sizeof(actor));
|
||||
if (std::memcmp(actor.name, "Obj_kn2", 7) != 0) {
|
||||
return;
|
||||
}
|
||||
const uint16_t legacy = static_cast<uint16_t>(actor.base.angle.x);
|
||||
const auto found = nodeIds.find(legacy);
|
||||
if (found == nodeIds.end()) {
|
||||
return;
|
||||
}
|
||||
actor.base.angle.x = static_cast<int16_t>(found->second);
|
||||
std::memcpy(bytes.data(), &actor, sizeof(actor));
|
||||
}
|
||||
|
||||
void remap_actor_flows(RandomizerContext& context, const LegacyIdMap& nodeIds) {
|
||||
for (auto& [stage, patches] : context.mObjectPatches) {
|
||||
for (auto& [crc, bytes] : patches) {
|
||||
remap_actor_record(bytes, nodeIds);
|
||||
}
|
||||
}
|
||||
for (auto& [stage, additions] : context.mObjectAdditions) {
|
||||
for (auto& bytes : additions) {
|
||||
remap_actor_record(bytes, nodeIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModResult register_graphs(RandomizerContext& context, const MessageIdMaps& messageIds) {
|
||||
const auto legacyIds = custom_node_ids(context);
|
||||
std::array<FlowGraphHandle, kMessageGroupCount> handles{};
|
||||
std::array<mods::flow::Graph, kMessageGroupCount> guards;
|
||||
for (uint16_t group = 0; group < kMessageGroupCount; ++group) {
|
||||
ModResult result = svc_flow->begin_graph(mod_ctx, group, &handles[group]);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
guards[group] = mods::flow::Graph{handles[group], MOD_OK};
|
||||
}
|
||||
|
||||
std::array<std::vector<uint16_t>, kMessageGroupCount> allocated;
|
||||
LegacyIdMap nodeIds;
|
||||
ModResult result =
|
||||
allocate_shared_node_ids(handles, legacyIds.size(), allocated, nodeIds, legacyIds);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unordered_map<uint16_t, uint16_t> dynamicToLegacy;
|
||||
for (const auto& [legacy, dynamic] : nodeIds) {
|
||||
dynamicToLegacy.emplace(dynamic, legacy);
|
||||
}
|
||||
|
||||
const FlowNodeData unused = mods::flow::event(FLOW_EVENT_UNUSED_42, 0, {});
|
||||
for (uint16_t group = 0; group < kMessageGroupCount; ++group) {
|
||||
for (const uint16_t dynamicId : allocated[group]) {
|
||||
FlowNodeData node = unused;
|
||||
const auto legacy = dynamicToLegacy.find(dynamicId);
|
||||
if (legacy != dynamicToLegacy.end()) {
|
||||
const uint32_t key = static_cast<uint32_t>(CUSTOM_BMG_GROUP) << 16 | legacy->second;
|
||||
const auto raw = context.mFlowPatches.find(key);
|
||||
if (raw == context.mFlowPatches.end()) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
node = raw_node(raw->second);
|
||||
result = remap_node(context, key, group, handles[group], nodeIds, messageIds, node);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = svc_flow->fill_node(mod_ctx, handles[group], dynamicId, &node);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [key, value] : context.mFlowPatches) {
|
||||
if (key >> 16 != group) {
|
||||
continue;
|
||||
}
|
||||
FlowNodeData node = raw_node(value);
|
||||
result = remap_node(context, key, group, handles[group], nodeIds, messageIds, node);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
result =
|
||||
svc_flow->patch_node(mod_ctx, handles[group], static_cast<uint16_t>(key), &node);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const FlowGraphHandle handle : handles) {
|
||||
result = svc_flow->commit_graph(mod_ctx, handle);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
for (auto& graph : guards) {
|
||||
s_graphs.push_back(std::move(graph));
|
||||
}
|
||||
remap_actor_flows(context, nodeIds);
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ModResult initialize() {
|
||||
s_eventFlagQuery = mods::flow::register_query("randomizer event flag", query_event_flag);
|
||||
s_changeTimeQuery = mods::flow::register_query("randomizer change time", query_change_time);
|
||||
s_returnToSpawnQuery =
|
||||
mods::flow::register_query("randomizer return to spawn", query_return_to_spawn);
|
||||
s_changeTimeEvent = mods::flow::register_event("randomizer change time", event_change_time);
|
||||
s_returnToSpawnEvent =
|
||||
mods::flow::register_event("randomizer return to spawn", event_return_to_spawn);
|
||||
s_removeTradeItemEvent =
|
||||
mods::flow::register_event("randomizer remove trade item", event_remove_trade_item);
|
||||
|
||||
if (!s_eventFlagQuery) {
|
||||
return s_eventFlagQuery.result();
|
||||
}
|
||||
if (!s_changeTimeQuery) {
|
||||
return s_changeTimeQuery.result();
|
||||
}
|
||||
if (!s_returnToSpawnQuery) {
|
||||
return s_returnToSpawnQuery.result();
|
||||
}
|
||||
if (!s_changeTimeEvent) {
|
||||
return s_changeTimeEvent.result();
|
||||
}
|
||||
if (!s_returnToSpawnEvent) {
|
||||
return s_returnToSpawnEvent.result();
|
||||
}
|
||||
return s_removeTradeItemEvent ? MOD_OK : s_removeTradeItemEvent.result();
|
||||
}
|
||||
|
||||
ModResult activate(RandomizerContext& context) {
|
||||
deactivate();
|
||||
|
||||
MessageIdMaps messageIds;
|
||||
ModResult result = register_custom_messages(context, messageIds);
|
||||
if (result == MOD_OK) {
|
||||
result = register_native_overrides(context);
|
||||
}
|
||||
if (result == MOD_OK) {
|
||||
result = register_graphs(context, messageIds);
|
||||
}
|
||||
if (result != MOD_OK) {
|
||||
mods::log::error("failed to register randomizer flow data: {}", static_cast<int>(result));
|
||||
deactivate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void deactivate() {
|
||||
s_graphs.clear();
|
||||
s_overrides.clear();
|
||||
s_messages.clear();
|
||||
}
|
||||
|
||||
} // namespace randomizer::messages
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <dolphin/types.h>
|
||||
#include <mods/api.h>
|
||||
|
||||
// Forward declaration
|
||||
namespace JMessage {
|
||||
struct TProcessor;
|
||||
struct TControl;
|
||||
}
|
||||
class RandomizerContext;
|
||||
|
||||
void HandleTextOverrides(JMessage::TControl* control, JMessage::TProcessor const* pProcessor, int groupID, int index);
|
||||
namespace randomizer::messages {
|
||||
|
||||
bool HandleCustomText(JMessage::TControl* control, u16 msgId);
|
||||
ModResult initialize();
|
||||
ModResult activate(RandomizerContext& context);
|
||||
void deactivate();
|
||||
|
||||
char* GetTextOverride(s16 groupID, u32 messageId);
|
||||
} // namespace randomizer::messages
|
||||
|
||||
@@ -76,13 +76,6 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
|
||||
|
||||
out["mTwilitInsectOverrides"] = mTwilitInsectOverrides;
|
||||
|
||||
for (const auto& [key, data] : this->mFlowItemMessageOverrides) {
|
||||
auto node = out["mFlowItemMessageOverrides"][key];
|
||||
node["itemId"] = data.itemId;
|
||||
node["stage"] = data.stage;
|
||||
node["flag"] = data.flag;
|
||||
}
|
||||
|
||||
for (const auto& [name, data] : this->mItemLocations) {
|
||||
auto node = out["mItemLocations"][name];
|
||||
node["itemId"] = data.itemId;
|
||||
@@ -248,12 +241,6 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
|
||||
itemData.flag = node["flag"].as<u16>();
|
||||
};
|
||||
|
||||
// FLW Override items
|
||||
for (const auto& flwNode : in["mFlowItemMessageOverrides"]) {
|
||||
u32 key = flwNode.first.as<u32>();
|
||||
retrieveItemData(this->mFlowItemMessageOverrides[key], flwNode.second);
|
||||
}
|
||||
|
||||
// Items we call by location name
|
||||
for (const auto& locationNode : in["mItemLocations"]) {
|
||||
const auto& locationName = nameLookupOverride(locationNode.first.as<std::string>());
|
||||
@@ -792,7 +779,11 @@ void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo,
|
||||
}
|
||||
}
|
||||
|
||||
void randomizer_setTempFlag(RandomizerContext::itemLocationData data) {
|
||||
void randomizer_setTempFlag(const RandomizerContext::itemLocationData& data) {
|
||||
if (data.flag == 0xFFFF) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If stage is 0xFF, then this is an event flag
|
||||
if (data.stage == 0xFF) {
|
||||
g_randomizerState.mTrackerTempEventFlag = data.flag;
|
||||
@@ -809,14 +800,6 @@ void randomizer_setTempFlag(RandomizerContext::itemLocationData data) {
|
||||
}
|
||||
}
|
||||
|
||||
void randomizer_setTempFlagForLocation(const std::string& locationName) {
|
||||
randomizer_setTempFlag(randomizer_GetContext().mItemLocations[nameLookupOverride(locationName)]);
|
||||
}
|
||||
|
||||
void randomizer_setTempFlagForFLWOverride(u32 key) {
|
||||
randomizer_setTempFlag(randomizer_GetContext().mFlowItemMessageOverrides[key]);
|
||||
}
|
||||
|
||||
bool randomizer_checkTempleOfTimeRequirement() {
|
||||
auto swordRequirement = randomizer_GetContext().mSettings[RandomizerContext::TEMPLE_OF_TIME_SWORD_REQUIREMENT];
|
||||
u8 roomNo = dComIfGp_getStartStageRoomNo();
|
||||
@@ -1206,71 +1189,6 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
||||
}
|
||||
};
|
||||
|
||||
// Items that we determine the text of and then give during a FLW message
|
||||
if (location->HasCategories("FLW Message")) {
|
||||
for (const auto& flwMessageNode : metaData["FLW Message"]) {
|
||||
u8 group = flwMessageNode["Group"].as<u8>();
|
||||
u16 messageId = flwMessageNode["Message Id"].as<u16>();
|
||||
u32 key = (group << 16) | messageId;
|
||||
randoData.mFlowItemMessageOverrides[key].itemId = location->GetCurrentItem()->GetID();
|
||||
getNodeFlags(randoData.mFlowItemMessageOverrides[key], metaData);
|
||||
}
|
||||
}
|
||||
|
||||
// Items that are given by FLW events. Override the item in the existing event with the
|
||||
// randomized item
|
||||
if (location->HasCategories("FLW Event")) {
|
||||
for (const auto& flwEventNode : metaData["FLW Event"]) {
|
||||
u8 group = flwEventNode["Group"].as<u8>();
|
||||
u16 index = handleCustomFlowID(flwEventNode["Index"].as<std::string>());
|
||||
mesg_flow_node_event event{};
|
||||
event.type = 3; // event type node
|
||||
event.event_idx = 8;
|
||||
event.next_node_idx = handleCustomFlowID(flwEventNode["Next Node Index"].as<std::string>());
|
||||
auto params = flwEventNode["Parameters"].as<u32>();
|
||||
// Zero out the spot for the item id
|
||||
params &= 0xFFFFFF00;
|
||||
// Put in the item id
|
||||
params |= location->GetCurrentItem()->GetID();
|
||||
// Set the params in the correct order
|
||||
event.params[0] = (params >> 24) & 0xFF;
|
||||
event.params[1] = (params >> 16) & 0xFF;
|
||||
event.params[2] = (params >> 8) & 0xFF;
|
||||
event.params[3] = params & 0xFF;
|
||||
|
||||
// Construct another FLW node to set the associated flag in a temporary variable
|
||||
// right before we receive the item. This ensures that a tracker/AP can pick up
|
||||
// on the fact that we've received the item.
|
||||
auto newFlwIndex = handleCustomFlowID(location->GetName() + " Flag Set Node");
|
||||
mesg_flow_node_event flagEvent{};
|
||||
flagEvent.type = 3; // event type node
|
||||
flagEvent.event_idx = 46; // Set temporary randomizer flag
|
||||
|
||||
u8 stage{0xFF};
|
||||
u16 flag{0xFFFF};
|
||||
if (metaData["Event Flag"]) {
|
||||
flag = metaData["Event Flag"].as<u16>();
|
||||
} else if (metaData["Switch Flag"]) {
|
||||
stage = metaData["Switch Flag"]["Stage"].as<u8>();
|
||||
flag = metaData["Switch Flag"]["Flag"].as<u8>();
|
||||
}
|
||||
flagEvent.params[0] = 0;
|
||||
flagEvent.params[1] = stage;
|
||||
flagEvent.params[2] = (flag >> 8) & 0xFF;
|
||||
flagEvent.params[3] = flag & 0xFF;
|
||||
|
||||
// Store the modified FLW nodes. The flag event takes the place of the original
|
||||
// index we're modifying and then leads into the custom index of the event that
|
||||
// sets up the item id
|
||||
flagEvent.next_node_idx = newFlwIndex;
|
||||
u32 key = (group << 16) | index;
|
||||
randoData.mFlowPatches[key] = std::bit_cast<u64>(flagEvent);
|
||||
|
||||
key = (CUSTOM_BMG_GROUP << 16) | newFlwIndex;
|
||||
randoData.mFlowPatches[key] = std::bit_cast<u64>(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Items that we lookup just by calling their location name
|
||||
if (location->HasCategories("Name Lookup")) {
|
||||
for (const auto& locationNameNode : metaData["Name Lookup"]) {
|
||||
|
||||
@@ -51,7 +51,6 @@ public:
|
||||
std::unordered_map<u16, u8> mGoldenWolfOverrides{};
|
||||
std::unordered_map<u16, u8> mShopOverrides{};
|
||||
std::unordered_map<u16, u16> mTwilitInsectOverrides{}; // Just used in tracker for now
|
||||
std::unordered_map<u32, itemLocationData> mFlowItemMessageOverrides{};
|
||||
std::unordered_map<std::string, itemLocationData> mItemLocations{};
|
||||
|
||||
u8 mStartHour{0};
|
||||
@@ -181,12 +180,6 @@ public:
|
||||
u8 mTimeChange{};
|
||||
bool mRoomReloadingState{false};
|
||||
|
||||
// Used to store an item id for a flow message override so that we can give the item
|
||||
// once the textbox is closed instead of when the message appears. This lines up
|
||||
// more naturally with the timing of when the game normally gives items and affects
|
||||
// things like the sound of the rupee counter going up.
|
||||
u8 mFlowMessageItemId{0};
|
||||
|
||||
int mFoolishItemCount{0};
|
||||
bool mUpdateTracker{false};
|
||||
bool mShowTracker{false};
|
||||
@@ -219,11 +212,7 @@ void randomizer_checkAndOverrideEntranceData(
|
||||
* variable. This allows the tracker/Archipelago to know a location has been checked
|
||||
* when the item is received instead of some indeterminate amount of time afterward.
|
||||
*/
|
||||
void randomizer_setTempFlag(RandomizerContext::itemLocationData);
|
||||
|
||||
void randomizer_setTempFlagForLocation(const std::string& locationName);
|
||||
|
||||
void randomizer_setTempFlagForFLWOverride(u32 key);
|
||||
void randomizer_setTempFlag(const RandomizerContext::itemLocationData&);
|
||||
|
||||
bool randomizer_checkTempleOfTimeRequirement();
|
||||
|
||||
|
||||
@@ -10,12 +10,20 @@
|
||||
#include "tools.h"
|
||||
#include "stages.h"
|
||||
#include "item.hpp"
|
||||
#include "messages.hpp"
|
||||
#include "verify_item_functions.h"
|
||||
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "d/d_item.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
|
||||
#include <mods/items.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace randomizer::session {
|
||||
ServiceManager svc_mng;
|
||||
std::string g_pending_seed_hash{};
|
||||
@@ -32,6 +40,18 @@ struct DerivedKey {
|
||||
u16 key;
|
||||
};
|
||||
|
||||
std::optional<int> parse_stage_check(const char* name, std::string_view prefix) {
|
||||
if (std::strncmp(name, prefix.data(), prefix.size()) != 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const char* stage = name + prefix.size();
|
||||
if (*stage == '\0' || std::strchr(stage, ':') != nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const int stageId = getStageID(stage);
|
||||
return stageId >= 0 ? std::optional{stageId} : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<DerivedKey> parse_derived(const char* name, std::string_view prefix) {
|
||||
if (std::strncmp(name, prefix.data(), prefix.size()) != 0) {
|
||||
return std::nullopt;
|
||||
@@ -64,14 +84,14 @@ bool resolve_check(ModContext*, const ItemCheckInfo* info, uint8_t* out_item, vo
|
||||
auto& ctx = randomizer_GetContext();
|
||||
|
||||
if (auto it = ctx.mItemLocations.find(info->name); it != ctx.mItemLocations.end()) {
|
||||
*out_item = static_cast<uint8_t>(it->second.itemId);
|
||||
*out_item = static_cast<uint8_t>(verifyProgressiveItem(it->second.itemId));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto key = parse_derived(info->name, "chest:")) {
|
||||
if (auto key = parse_derived(info->name, ITEM_CHECK_CHEST_PREFIX)) {
|
||||
return lookup_override(ctx.mTreasureChestOverrides, key->key, out_item, false);
|
||||
}
|
||||
if (auto key = parse_derived(info->name, "freestanding:")) {
|
||||
if (auto key = parse_derived(info->name, ITEM_CHECK_FREESTANDING_PREFIX)) {
|
||||
if (key->stage_id == Ook) {
|
||||
if (auto it = ctx.mItemLocations.find("Forest Temple Gale Boomerang");
|
||||
it != ctx.mItemLocations.end()) {
|
||||
@@ -82,18 +102,23 @@ bool resolve_check(ModContext*, const ItemCheckInfo* info, uint8_t* out_item, vo
|
||||
}
|
||||
return lookup_override(ctx.mFreestandingItemOverrides, key->key, out_item, true);
|
||||
}
|
||||
if (auto key = parse_derived(info->name, "poe:")) {
|
||||
return lookup_override(ctx.mPoeOverrides, key->key, out_item, false);
|
||||
if (auto key = parse_derived(info->name, ITEM_CHECK_POE_PREFIX)) {
|
||||
return lookup_override(ctx.mPoeOverrides, key->key, out_item, true);
|
||||
}
|
||||
if (auto key = parse_derived(info->name, "shop:")) {
|
||||
if (auto stageId = parse_stage_check(info->name, ITEM_CHECK_BOSS_PREFIX)) {
|
||||
const u16 key = static_cast<u16>((*stageId << 8) | 0x9F);
|
||||
return lookup_override(ctx.mFreestandingItemOverrides, key, out_item, true);
|
||||
}
|
||||
if (auto key = parse_derived(info->name, ITEM_CHECK_SHOP_PREFIX)) {
|
||||
return lookup_override(ctx.mShopOverrides, key->key, out_item, true);
|
||||
}
|
||||
if (auto key = parse_derived(info->name, "sky:")) {
|
||||
if (auto key = parse_derived(info->name, ITEM_CHECK_SKY_PREFIX)) {
|
||||
return lookup_override(ctx.mSkyCharacterOverrides, key->key, out_item, true);
|
||||
}
|
||||
|
||||
if (std::strncmp(info->name, "bug:", 4) == 0) {
|
||||
const u8 insect = static_cast<u8>(std::atoi(info->name + 4));
|
||||
constexpr std::string_view bugPrefix{ITEM_CHECK_BUG_PREFIX};
|
||||
if (std::strncmp(info->name, bugPrefix.data(), bugPrefix.size()) == 0) {
|
||||
const u8 insect = static_cast<u8>(std::atoi(info->name + bugPrefix.size()));
|
||||
if (auto it = ctx.mBugRewardOverrides.find(insect); it != ctx.mBugRewardOverrides.end()) {
|
||||
*out_item = static_cast<uint8_t>(verifyProgressiveItem(it->second));
|
||||
return true;
|
||||
@@ -110,17 +135,20 @@ void observe_give(ModContext*, const ItemGiveInfo* info, void*) {
|
||||
}
|
||||
|
||||
auto& ctx = randomizer_GetContext();
|
||||
if (ctx.mItemLocations.contains(info->check_name)) {
|
||||
randomizer_setTempFlagForLocation(info->check_name);
|
||||
if (const auto it = ctx.mItemLocations.find(info->check_name);
|
||||
it != ctx.mItemLocations.end())
|
||||
{
|
||||
randomizer_setTempFlag(it->second);
|
||||
}
|
||||
|
||||
if (std::strcmp(info->check_name, "dungeon_reward:D_MN10") == 0) {
|
||||
dComIfGs_onItem(0x9E, -1);
|
||||
} else if (auto key = parse_derived(info->check_name, "freestanding:");
|
||||
key && key->stage_id == Ook)
|
||||
if (auto key = parse_derived(info->check_name, ITEM_CHECK_FREESTANDING_PREFIX);
|
||||
key && key->stage_id == Ook)
|
||||
{
|
||||
dComIfGs_onItem(0x9D, -1);
|
||||
randomizer_setTempFlagForLocation("Forest Temple Gale Boomerang");
|
||||
if (const auto it = ctx.mItemLocations.find("Forest Temple Gale Boomerang");
|
||||
it != ctx.mItemLocations.end())
|
||||
{
|
||||
randomizer_setTempFlag(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +160,11 @@ bool activateSeed(const char* hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (messages::activate(ctx) != MOD_OK) {
|
||||
ctx = RandomizerContext{};
|
||||
return false;
|
||||
}
|
||||
|
||||
item::apply_item_data_tables();
|
||||
|
||||
svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver);
|
||||
@@ -158,6 +191,7 @@ void deactivateSeed() {
|
||||
}
|
||||
s_stage_edits.clear();
|
||||
|
||||
messages::deactivate();
|
||||
item::restore_item_data_tables();
|
||||
randomizer_GetContext() = RandomizerContext{};
|
||||
g_randomizerState = RandomizerState{};
|
||||
@@ -438,6 +472,11 @@ ModResult onGameModeUpdate(void*, ModError* error) {
|
||||
ModResult initialize(const ServiceManager& services) {
|
||||
svc_mng = services;
|
||||
|
||||
const ModResult messageResult = messages::initialize();
|
||||
if (messageResult != MOD_OK) {
|
||||
return messageResult;
|
||||
}
|
||||
|
||||
const GameModeDesc gameModeDesc = {
|
||||
.struct_size = sizeof(GameModeDesc),
|
||||
.game_mode_id = "randomizer",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "stages.h"
|
||||
#include "verify_item_functions.h"
|
||||
|
||||
#include <mods/items.h>
|
||||
#include <mods/svc/log.hpp>
|
||||
|
||||
bool playerIsInRoomStage(s32 room, const char* stage)
|
||||
@@ -505,40 +506,57 @@ randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world:
|
||||
std::string nameLookupOverride(const std::string& locationName) {
|
||||
// [generator name], [service name]
|
||||
static std::unordered_map<std::string, std::string> nameLookup = {
|
||||
{"Arbiters Grounds Dungeon Reward", "dungeon_reward:D_MN10"},
|
||||
{"Ashei Sketch", "ashei_sketch"},
|
||||
{"Auru Gift To Fyer", "auru_memo"},
|
||||
{"Cave of Ordeals Great Fairy Reward", "fairy_reward:D_SB01"},
|
||||
{"Charlo Donation Blessing", "prayer_reward"},
|
||||
{"Coro Bottle", "coro_bottle"},
|
||||
{"Gift From Ralis", "coral_earring"},
|
||||
{"Goron Mines Gor Amato Key Shard", "key_shard_1:D_MN04"},
|
||||
{"Goron Mines Gor Ebizo Key Shard", "key_shard_2:D_MN04"},
|
||||
{"Goron Mines Gor Liggs Key Shard", "key_shard_3:D_MN04"},
|
||||
{"Herding Goats Reward", "goats_reward"},
|
||||
{"Hyrule Castle King Bulblin Key", "bulblin_key:D_MN09"},
|
||||
{"Ilia Charm", "ilia_charm"},
|
||||
{"Iza Helping Hand", "iza_reward_1"},
|
||||
{"Iza Raging Rapids Minigame", "iza_reward_2"},
|
||||
{"Jovani 20 Poe Soul Reward", "jovani_reward_1"},
|
||||
{"Jovani 60 Poe Soul Reward", "jovani_reward_2"},
|
||||
{"Ordon Cat Rescue", "sera_reward"},
|
||||
{"Ordon Shield", "ordon_shield"},
|
||||
{"Ordon Sword", "ordon_sword"},
|
||||
{"Plumm Fruit Balloon Minigame", "plumm_minigame_reward"},
|
||||
{"Renados Letter", "renado_letter"},
|
||||
{"Rutelas Blessing", "zora_armor"},
|
||||
{"Skybook From Impaz", "skybook"},
|
||||
{"Snowboard Racing Prize", "snowboard_race_reward"},
|
||||
{"Snowpeak Ruins Ball and Chain", "ball_and_chain:D_MN11"},
|
||||
{"Snowpeak Ruins Mansion Map", "dungeon_map:D_MN11"},
|
||||
{"STAR Prize 1", "star_reward_1"},
|
||||
{"STAR Prize 2", "star_reward_2"},
|
||||
{"Talo Sharpshooting", "archery_reward:F_SP109"},
|
||||
{"Telma Invoice", "telma_invoice"},
|
||||
{"Uli Cradle Delivery", "uli_cradle_reward"},
|
||||
{"Wooden Statue", "wood_statue"},
|
||||
{"Zoras Domain Underwater Goron", "goron_reward:F_SP113"},
|
||||
{"Arbiters Grounds Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_ARBITERS},
|
||||
{"Ashei Sketch", ITEM_CHECK_ASHEI_SKETCH},
|
||||
{"Auru Gift To Fyer", ITEM_CHECK_AURU_MEMO},
|
||||
{"Cave of Ordeals Great Fairy Reward", ITEM_CHECK_FAIRY_REWARD},
|
||||
{"Charlo Donation Blessing", ITEM_CHECK_PRAYER_REWARD},
|
||||
{"Coro Bottle", ITEM_CHECK_CORO_BOTTLE},
|
||||
{"Fishing Hole Bottle", ITEM_CHECK_FISHING_BOTTLE},
|
||||
{"Fishing Hole Heart Piece", ITEM_CHECK_FISHING_HEART_PIECE},
|
||||
{"Forest Temple Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_FOREST},
|
||||
{"Gift From Ralis", ITEM_CHECK_CORAL_EARRING},
|
||||
{"Goron Mines Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_GORON},
|
||||
{"Goron Mines Gor Amato Key Shard", ITEM_CHECK_KEY_SHARD_1},
|
||||
{"Goron Mines Gor Ebizo Key Shard", ITEM_CHECK_KEY_SHARD_2},
|
||||
{"Goron Mines Gor Liggs Key Shard", ITEM_CHECK_KEY_SHARD_3},
|
||||
{"Herding Goats Reward", ITEM_CHECK_GOATS_REWARD},
|
||||
{"Hyrule Castle King Bulblin Key", ITEM_CHECK_BULBLIN_KEY},
|
||||
{"Ilia Charm", ITEM_CHECK_ILIA_CHARM},
|
||||
{"Ilia Memory Reward", ITEM_CHECK_ILIA_MEMORY},
|
||||
{"Iza Helping Hand", ITEM_CHECK_IZA_REWARD_1},
|
||||
{"Iza Raging Rapids Minigame", ITEM_CHECK_IZA_REWARD_2},
|
||||
{"Jovani 20 Poe Soul Reward", ITEM_CHECK_JOVANI_REWARD_1},
|
||||
{"Jovani 60 Poe Soul Reward", ITEM_CHECK_JOVANI_REWARD_2},
|
||||
{"Kakariko Village Malo Mart Hawkeye", "shop:R_SP109:62"},
|
||||
{"Kakariko Village Malo Mart Hylian Shield", "shop:R_SP109:44"},
|
||||
{"Kakariko Village Malo Mart Red Potion", "shop:R_SP109:97"},
|
||||
{"Kakariko Village Malo Mart Wooden Shield", "shop:R_SP109:43"},
|
||||
{"Castle Town Malo Mart Magic Armor", "shop:R_SP160:48"},
|
||||
{"Lakebed Temple Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_LAKEBED},
|
||||
{"Ordon Cat Rescue", ITEM_CHECK_SERA_REWARD},
|
||||
{"Ordon Shield", ITEM_CHECK_ORDON_SHIELD},
|
||||
{"Ordon Sword", ITEM_CHECK_ORDON_SWORD},
|
||||
{"Plumm Fruit Balloon Minigame", ITEM_CHECK_PLUMM_REWARD},
|
||||
{"Renados Letter", ITEM_CHECK_RENADO_LETTER},
|
||||
{"Rutelas Blessing", ITEM_CHECK_ZORA_ARMOR},
|
||||
{"Sacred Grove Pedestal Master Sword", ITEM_CHECK_MASTER_SWORD},
|
||||
{"Sacred Grove Pedestal Shadow Crystal", ITEM_CHECK_SHADOW_CRYSTAL},
|
||||
{"Sera Shop Slingshot", "shop:R_SP01:75"},
|
||||
{"Skybook From Impaz", ITEM_CHECK_SKYBOOK},
|
||||
{"Snowboard Racing Prize", ITEM_CHECK_SNOWBOARD_REWARD},
|
||||
{"Snowpeak Ruins Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_SNOWPEAK},
|
||||
{"Snowpeak Ruins Ball and Chain", ITEM_CHECK_BALL_AND_CHAIN},
|
||||
{"Snowpeak Ruins Mansion Map", ITEM_CHECK_DUNGEON_MAP_SNOWPEAK},
|
||||
{"STAR Prize 1", ITEM_CHECK_STAR_REWARD_1},
|
||||
{"STAR Prize 2", ITEM_CHECK_STAR_REWARD_2},
|
||||
{"Talo Sharpshooting", ITEM_CHECK_ARCHERY_REWARD},
|
||||
{"Telma Invoice", ITEM_CHECK_TELMA_INVOICE},
|
||||
{"Temple of Time Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_TIME},
|
||||
{"Uli Cradle Delivery", ITEM_CHECK_ULI_CRADLE_REWARD},
|
||||
{"Wooden Statue", ITEM_CHECK_WOOD_STATUE},
|
||||
{"City in the Sky Dungeon Reward", ITEM_CHECK_DUNGEON_REWARD_CITY},
|
||||
{"Zoras Domain Underwater Goron", ITEM_CHECK_GORON_REWARD},
|
||||
};
|
||||
|
||||
for (auto& [oldName, newName] : nameLookup) {
|
||||
@@ -646,12 +664,6 @@ int getLocationItem(randomizer::logic::location::Location* location) {
|
||||
auto key = (stage << 8) | flag;
|
||||
return context.mTwilitInsectOverrides[key];
|
||||
}
|
||||
if (auto& flwNode = locationMeta["FLW Message"]) {
|
||||
auto group = flwNode[0]["Group"].as<u16>();
|
||||
auto messageId = flwNode[0]["Message Id"].as<u16>();
|
||||
u32 key = (group << 16) | messageId;
|
||||
return context.mFlowItemMessageOverrides[key].itemId;
|
||||
}
|
||||
if (auto& nameNode = locationMeta["Name Lookup"]) {
|
||||
auto name = nameLookupOverride(nameNode[0].as<std::string>());
|
||||
return context.mItemLocations[name].itemId;
|
||||
@@ -848,4 +860,4 @@ u8 getAreaKeyNum(int i_stageNo) {
|
||||
}
|
||||
|
||||
return dComIfGs_getSaveData()->getSave(i_stageNo).getBit().getKeyNum();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user