#include "CustomObject.h"
#include <libultraship/bridge/consolevariablebridge.h>
#include "port/UI/cvar_prefixes.h"
#include "port/Rando/Logic/Logic.h"
#include "port/Rando/ObjectBehavior/ObjectBehavior.h"
#include "port/Enhancements/Events/Hooks/Events.h"
#include "actor.h"
#include "include/core1/sns.h"
#include "spdlog/spdlog.h"
extern "C" {
extern u8 D_80385FF0[0xE];
typedef struct chjiggy_s {
u32 unk0;
u32 index;
} ActorLocal_Jiggy;
typedef struct {
enum mumbotoken_e uid;
} ActorLocal_MumboToken;
typedef struct {
enum honeycomb_e uid;
s32 unk4;
} ActorLocal_EmptyHoneycomb;
s32 item_adjustByDiffWithHud(enum item_e item, s32 diff);
void fxSparkle_musicNote(s16 position[3]);
void player_getPosition(f32 dst[3]);
void ml_vec3f_to_vec3h(s16 dst[3], f32 src[3]);
void gcparade_beginFinalParade(void);
void coMusicPlayer_playMusic(enum comusic_e track_id, s32 volume);
void marker_despawn(ActorMarker* marker);
Actor* actor_new(s32 position[3], s32 yaw, ActorInfo* actorInfo, u32 flags);
extern ActorInfo chJinjoBlue;
extern ActorInfo chJinjoGreen;
extern ActorInfo chJinjoYellow;
extern ActorInfo chJinjoPink;
extern ActorInfo chJinjoOrange;
extern ActorInfo chJiggy;
extern ActorInfo chEmptyHoneycomb;
extern ActorInfo chMumboToken;
extern ActorInfo sumusicNote;
extern ActorInfo chExtraLife;
extern ActorInfo chHoneycomb;
extern ActorInfo chBlueEgg;
extern ActorInfo chRedFeather;
extern ActorInfo chGoldFeather;
extern ActorInfo chSnsEgg;
extern ActorInfo chIceKey;
}
typedef struct {
int32_t position[3];
RandoCheckId randoCheckId;
bool isSpawned;
} QueuedRandoProp;
std::vector<QueuedRandoProp> randoActorQueue;
std::vector<RandoCheckId> randoSpawnedCheckIds;
bool shouldRemoveEX = false;
std::map<actor_e, std::pair<ActorInfo, int32_t>> actorInfoMap = {
{ ACTOR_2D_MUMBO_TOKEN, { chMumboToken, ACTOR_FLAG_UNKNOWN_6 } },
{ ACTOR_46_JIGGY, { chJiggy, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_7 | ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_47_EMPTY_HONEYCOMB, { chEmptyHoneycomb, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_49_EXTRA_LIFE, { chExtraLife, ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_51_MUSIC_NOTE, { sumusicNote, ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_5E_JINJO_YELLOW, { chJinjoYellow, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_8 } },
{ ACTOR_5F_JINJO_ORANGE, { chJinjoOrange, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_8 } },
{ ACTOR_60_JINJO_BLUE, { chJinjoBlue, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_8 } },
{ ACTOR_61_JINJO_PINK, { chJinjoPink, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_8 } },
{ ACTOR_62_JINJO_GREEN, { chJinjoGreen, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_8 } },
{ ACTOR_50_HONEYCOMB, { chHoneycomb, ACTOR_FLAG_UNKNOWN_6 | ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_52_BLUE_EGG, { chBlueEgg, ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_129_RED_FEATHER, { chRedFeather, ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_370_GOLD_FEATHER, { chGoldFeather, ACTOR_FLAG_UNKNOWN_21 } },
{ ACTOR_25E_SNS_EGG, { chSnsEgg, ACTOR_FLAG_UNKNOWN_9 | ACTOR_FLAG_UNKNOWN_10 | ACTOR_FLAG_UNKNOWN_15 } },
{ ACTOR_25D_ICE_KEY, { chIceKey, ACTOR_FLAG_UNKNOWN_9 | ACTOR_FLAG_UNKNOWN_10 | ACTOR_FLAG_UNKNOWN_15 } },
};
int32_t currentMap = -1;
void CustomObject::ResetRandoSpawnQueue() {
for (auto& queue : randoActorQueue) {
queue.isSpawned = false;
}
}
void CustomObject::ClearRandoActorListEX() {
if ((gsworld_getMap() == MAP_7_TTC_TREASURE_TROVE_COVE && currentMap == gsworld_getMap()) ||
(gsworld_getMap() == MAP_1B_MMM_MAD_MONSTER_MANSION && currentMap == gsworld_getMap())) {
CustomObject::ResetRandoSpawnQueue();
randoSpawnedCheckIds.clear();
}
if (currentMap != gsworld_getMap()) {
currentMap = gsworld_getMap();
randoActorQueue.clear();
randoSpawnedCheckIds.clear();
}
}
bool CustomObject::CheckSpawnedIdList(RandoCheckId randoCheckId) {
for (auto& spawn : randoSpawnedCheckIds) {
if (spawn == randoCheckId) {
return true;
}
}
return false;
}
void CustomObject::RemoveSpawnedIdFromList(RandoCheckId randoCheckId) {
for (int i = 0; i < randoSpawnedCheckIds.size(); i++) {
if (randoSpawnedCheckIds[i] == randoCheckId) {
randoSpawnedCheckIds.erase(randoSpawnedCheckIds.begin() + i);
break;
}
}
}
Actor* CustomObject::SetCustomActorParametersEX(RandoCheckId randoCheckId, Actor* customActor) {
RandoSaveCheck randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId];
customActor->marker->randoCheckId = randoCheckId;
RandoItemType itemType = Rando::StaticData::Items[randoSaveCheck.randoItemId].randoItemType;
switch (itemType) {
case RITYPE_EMPTY_HONEYCOMB:
ActorLocal_EmptyHoneycomb* honeycombLocal;
honeycombLocal = (ActorLocal_EmptyHoneycomb*)&customActor->local;
honeycombLocal->uid = (honeycomb_e)Rando::Logic::GetShuffledObject(randoCheckId).randoCollectionId;
break;
case RITYPE_JIGGY:
ActorLocal_Jiggy* jiggyLocal;
jiggyLocal = (ActorLocal_Jiggy*)&customActor->local;
jiggyLocal->index = randoSaveCheck.randoCollectionId;
break;
case RITYPE_MUMBO_TOKEN:
ActorLocal_MumboToken* tokenLocal;
tokenLocal = (ActorLocal_MumboToken*)&customActor->local;
tokenLocal->uid = (mumbotoken_e)Rando::Logic::GetShuffledObject(randoCheckId).randoCollectionId;
break;
case RITYPE_SNS_EGG:
case RITYPE_SNS_KEY:
customActor->actorTypeSpecificField = randoSaveCheck.randoCollectionId;
customActor->scale = 0.5f;
if (itemType == RITYPE_SNS_KEY) {
customActor->position_y += 50.0f;
}
break;
default:
break;
}
return customActor;
}
Actor* CustomObject::SpawnCustomActorEX(RandoCheckId randoCheckId, int32_t position[3], ActorInfo* actorInfo,
int32_t flags) {
if (randoCheckId == RC_UNKNOWN) {
return NULL;
}
Actor* customActor = actor_new(position, 0, actorInfo, flags);
if (customActor != NULL) {
if (actorInfo->actorId == ACTOR_51_MUSIC_NOTE) {
customActor->unk124_6 = 0;
}
customActor = SetCustomActorParametersEX(randoCheckId, customActor);
randoSpawnedCheckIds.push_back(randoCheckId);
}
return customActor;
}
void CustomObject::FlushRandoSpawnQueue() {
if (gsworld_getMap() == MAP_91_FILE_SELECT) {
return;
}
if (randoActorQueue.empty()) {
return;
}
for (auto& queue : randoActorQueue) {
if (queue.isSpawned) {
continue;
}
if (CustomObject::CheckSpawnedIdList(queue.randoCheckId)) {
queue.isSpawned = true;
continue;
}
RandoSaveCheck randoSaveCheck = Rando::Logic::GetShuffledObject(queue.randoCheckId);
if (randoSaveCheck.randoCheckId == RC_UNKNOWN) {
queue.isSpawned = true;
continue;
}
actor_e randoActorId = Rando::StaticData::GetActorIdByRandoItemId(randoSaveCheck.randoItemId);
if (randoSaveCheck.eligible) {
randoActorId = GetRandomJunkActorId(randoSaveCheck);
}
if (randoActorId == ACTOR_1_UNKNOWN) {
queue.isSpawned = true;
continue;
}
Actor* customActor = CustomObject::SpawnCustomActorEX(randoSaveCheck.randoCheckId, queue.position,
&actorInfoMap.at(randoActorId).first,
actorInfoMap.at(randoActorId).second);
if (customActor != NULL && randoSaveCheck.eligible &&
RANDO_SAVE_OPTIONS[RO_SPAWN_JUNK].optionValue == RO_GENERIC_ON) {
customActor->marker->unk14_21 = true;
customActor->scale = 1.0f;
}
queue.isSpawned = true;
}
}
void CustomObject::AddPropToSpawnQueueEX(int32_t position[3], RandoCheckId randoCheckId) {
randoActorQueue.push_back({ { position[0], position[1], position[2] }, randoCheckId, false });
}
Actor* CustomObject::ShouldCreateCustomActorEX(RandoCheckId randoCheckId, int32_t position[3], bool isProp,
Actor* refActor) {
if (randoCheckId == RC_UNKNOWN) {
return NULL;
}
if (CustomObject::CheckSpawnedIdList(randoCheckId)) {
return NULL;
}
RandoSaveCheck randoSaveCheck = Rando::Logic::GetShuffledObject(randoCheckId);
actor_e randoActorId = (actor_e)Rando::StaticData::Items[randoSaveCheck.randoItemId].actorId;
if (!randoSaveCheck.isShuffled) {
return NULL;
}
if (isProp) {
CustomObject::AddPropToSpawnQueueEX(position, randoCheckId);
return NULL;
}
if (randoSaveCheck.eligible) {
if (refActor != nullptr) {
randoActorId = (actor_e)refActor->modelCacheIndex;
} else {
randoActorId = GetRandomJunkActorId(randoSaveCheck);
}
}
if (randoSaveCheck.randoCheckId == RC_UNKNOWN) {
return NULL;
}
if (randoActorId == ACTOR_1_UNKNOWN) {
return NULL;
}
return CustomObject::SpawnCustomActorEX(randoCheckId, position, &actorInfoMap.at(randoActorId).first,
actorInfoMap.at(randoActorId).second);
}
void CustomObject::ResolveCustomActorCollisionEX(RandoCheckId randoCheckId) {
RandoSaveCheck randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId];
if (randoSaveCheck.randoCheckId == RC_UNKNOWN) {
return;
}
int32_t spawnPosition[3];
int16_t sparklePos[3];
f32 playerPosF[3];
player_getPosition(playerPosF);
ml_vec3f_to_vec3h(sparklePos, playerPosF);
spawnPosition[0] = (int32_t)playerPosF[0];
spawnPosition[1] = (int32_t)playerPosF[1];
spawnPosition[2] = (int32_t)playerPosF[2];
Rando::StaticData::RandoStaticItem randoItem = Rando::StaticData::Items[randoSaveCheck.randoItemId];
RandoItemType itemType = randoItem.randoItemType;
int32_t currentLevel = map_getLevel(gsworld_getMap());
switch (itemType) {
case RITYPE_JIGGY:
if (CVarGetInteger(CVAR_ENHANCEMENT("Cutscenes.SkipJiggyDance"), 0)) {
fxSparkle_musicNote(sparklePos);
}
break;
case RITYPE_JINJO:
if (randoItem.worldId == currentLevel) {
int32_t jinjoMarkerId = GetJinjoActorMarkerId((actor_e)randoItem.actorId);
item_adjustByDiffWithHud(ITEM_12_JINJOS, (1 << ((jinjoMarkerId + 6) & 0x1F)));
} else {
if (Rando::Logic::ShouldSpawnJinjoJiggy(randoItem.worldId)) {
RandoCheckId jiggyCheckId = Rando::StaticData::GetJinjoJiggyCheckByLevelId(randoItem.worldId);
if (jiggyCheckId != RC_UNKNOWN) {
Actor* customActor = ShouldCreateCustomActorEX(jiggyCheckId, spawnPosition, false);
if (customActor != NULL) {
ApplyCustomActorPhysics(jiggyCheckId, customActor, true);
}
}
}
}
break;
case RITYPE_MUSIC_NOTE:
D_80385FF0[randoItem.worldId]++;
if (randoItem.worldId == currentLevel) {
item_set(ITEM_C_NOTE, D_80385FF0[map_getLevel(gsworld_getMap())]);
}
UpdateSaveDataNoteScores();
fxSparkle_musicNote(sparklePos);
coMusicPlayer_playMusic(COMUSIC_9_NOTE_COLLECTED, 16000);
break;
case RITYPE_SNS_EGG:
case RITYPE_SNS_KEY:
if (Rando::Logic::GetTotalSnsItemsCollected() >= 7) {
gcparade_beginFinalParade();
}
break;
default:
break;
}
}
void CustomObject::CheckObtainedEX(RandoCheckId randoCheckId, bool isInit) {
for (auto& pool : Rando::Logic::shuffledPool) {
if (pool.randoCheckId == randoCheckId && !pool.eligible) {
pool.eligible = true;
shouldRemoveEX = true;
RANDO_SAVE_CHECKS[pool.randoCheckId].eligible = true;
CustomObject::RemoveSpawnedIdFromList(randoCheckId);
if (isInit) {
CustomObject::ResolveCustomActorCollisionEX(randoCheckId);
} else {
Rando::StaticData::SendCollisionNotification(pool.randoCheckId);
}
Rando::StaticData::ModifyRandoInfFlagState(randoCheckId);
Rando::Logic::RefreshReachableRegions();
if (!isInit) {
CALL_EVENT(OnRandoCheckObtained, (int32_t)randoCheckId, (int32_t)gsworld_getMap());
}
break;
}
}
}
void CustomObject::ObjectCollectedEX(Prop* prop) {
shouldRemoveEX = false;
CustomObject::CheckObtainedEX((RandoCheckId)prop->actorProp.marker->randoCheckId);
if (shouldRemoveEX) {
RandoSaveCheck randoSaveCheck = RANDO_SAVE_CHECKS[(RandoCheckId)prop->actorProp.marker->randoCheckId];
CustomObject::ResolveCustomActorCollisionEX(randoSaveCheck.randoCheckId);
if (Rando::StaticData::GetActorIdByRandoItemId(randoSaveCheck.randoItemId) == ACTOR_51_MUSIC_NOTE) {
marker_despawn(prop->actorProp.marker);
}
}
}