1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
#include "ActorBehavior.h"
#include <libultraship/bridge/consolevariablebridge.h>
#include "2s2h/ObjectExtension/ActorListIndex.h"
#include "2s2h/CustomItem/CustomItem.h"
extern "C" {
#include "functions.h"
#include "variables.h"
#include "overlays/actors/ovl_Obj_Mure3/z_obj_mure3.h"
}
std::map<std::tuple<u16, u8, s16>, RandoCheckId> freestandingMap = {
{ { SCENE_HAKASHITA, 0, 0 }, RC_BENEATH_THE_GRAVEYARD_NIGHT_2_FREESTANDING_RUPEE_01 },
{ { SCENE_22DEKUCITY, 1, 17 }, RC_DEKU_PALACE_FREESTANDING_RUPEE_12 },
};
EnItem00* spawnReplacementItem(Vec3f& pos, Rando::StaticData::RandoStaticCheck& randoStaticCheck) {
s16 itemParams = CustomItem::KILL_ON_TOUCH;
// Freestanding PoH & HC cannot be picked up by boomerangs
if (randoStaticCheck.randoCheckType == RCTYPE_FREESTANDING) {
itemParams |= CustomItem::ABLE_TO_ZORA_RANG;
}
// The heart piece in the bio baba grotto beehive needs to be tossed to fall to the ground
if (randoStaticCheck.randoCheckId == RC_TERMINA_FIELD_BIO_BABA_GROTTO) {
itemParams |= CustomItem::TOSS_ON_SPAWN;
}
// If it hasn't been collected yet, spawn a dummy item
return CustomItem::Spawn(
pos.x, pos.y, pos.z, 0, itemParams, randoStaticCheck.randoCheckId,
[](Actor* actor, PlayState* play) {
auto& randoStaticCheck = Rando::StaticData::Checks[(RandoCheckId)CUSTOM_ITEM_PARAM];
switch (randoStaticCheck.flagType) {
case FLAG_NONE:
if (RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId].shuffled) {
RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId].eligible = true;
}
break;
case FLAG_CYCL_SCENE_COLLECTIBLE:
Flags_SetCollectible(play, randoStaticCheck.flag);
break;
default:
break;
}
},
[](Actor* actor, PlayState* play) {
auto& randoSaveCheck = RANDO_SAVE_CHECKS[CUSTOM_ITEM_PARAM];
Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY);
Rando::DrawItem(Rando::ConvertItem(randoSaveCheck.randoItemId, (RandoCheckId)CUSTOM_ITEM_PARAM),
(RandoCheckId)CUSTOM_ITEM_PARAM, actor);
});
}
void Rando::ActorBehavior::InitEnItem00Behavior() {
// Identify freestanding based on the spawner's scene ID, room, and actor list index. The resulting RC is the base
// value, with each child incrementing to it to get its own RC. The RCs must be contiguous for this to work.
COND_VB_SHOULD(VB_OBJ_MURE3_DROP_COLLECTIBLE, IS_RANDO, {
Actor* actor = va_arg(args, Actor*);
auto it = freestandingMap.find({ gPlayState->sceneId, actor->room, GetActorListIndex(actor) });
if (it != freestandingMap.end()) {
s32 i = va_arg(args, s32);
RandoCheckId randoCheckId = static_cast<RandoCheckId>(it->second + i);
auto randoStaticCheck = Rando::StaticData::Checks[randoCheckId];
auto randoSaveCheck = RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId];
if (randoSaveCheck.shuffled && !randoSaveCheck.cycleObtained) {
ObjMure3* objMure3 = (ObjMure3*)actor;
Vec3f spawnPos;
spawnPos.y = objMure3->actor.world.pos.y;
if (i < 6) { // Ring of Rupees
s16 yRot = objMure3->actor.world.rot.y + 0x2AAA * i;
spawnPos.x = (Math_SinS(yRot) * 40.0f) + objMure3->actor.world.pos.x;
spawnPos.z = (Math_CosS(yRot) * 40.0f) + objMure3->actor.world.pos.z;
objMure3->unk148[i] = spawnReplacementItem(spawnPos, randoStaticCheck);
objMure3->unk148[i]->actor.room = actor->room;
} else { // Center Rupee
spawnPos.x = objMure3->actor.world.pos.x;
spawnPos.z = objMure3->actor.world.pos.z;
objMure3->unk160 = (Actor*)spawnReplacementItem(spawnPos, randoStaticCheck);
objMure3->unk160->room = actor->room;
}
*should = false;
}
}
});
// For freestandings that are identifiable based on collectible flags, pre-empt their normal spawns and spawn a
// custom item in their places.
COND_ID_HOOK(ShouldActorInit, ACTOR_EN_ITEM00, IS_RANDO, [](Actor* actor, bool* should) {
EnItem00* item00 = (EnItem00*)actor;
// If it's one of our items ignore it
if (item00->actor.params == ITEM00_NOTHING) {
return;
}
auto randoStaticCheck = Rando::StaticData::GetCheckFromFlag(FLAG_CYCL_SCENE_COLLECTIBLE,
ENITEM00_GET_7F00(actor), gPlayState->sceneId);
if (randoStaticCheck.randoCheckId == RC_UNKNOWN) {
return;
}
// Pots handle their own items, ignore them
if (randoStaticCheck.randoCheckType == RCTYPE_POT) {
return;
}
auto randoSaveCheck = RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId];
if (!randoSaveCheck.shuffled || randoSaveCheck.cycleObtained) {
return;
}
// Prevent the original item from spawning
*should = false;
EnItem00* replacementItem = spawnReplacementItem(actor->world.pos, randoStaticCheck);
// Wonder Items are supposed to be invisible
if (randoStaticCheck.randoCheckType == RCTYPE_WONDER_ITEM) {
replacementItem->actor.draw = NULL;
}
});
}
|