diff options
Diffstat (limited to 'mm/2s2h/Rando/MiscBehavior/Traps.cpp')
| -rw-r--r-- | mm/2s2h/Rando/MiscBehavior/Traps.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/mm/2s2h/Rando/MiscBehavior/Traps.cpp b/mm/2s2h/Rando/MiscBehavior/Traps.cpp new file mode 100644 index 000000000..6a7528dc2 --- /dev/null +++ b/mm/2s2h/Rando/MiscBehavior/Traps.cpp @@ -0,0 +1,282 @@ +#include "Traps.h" +#include <libultraship/bridge/consolevariablebridge.h> +#include "MiscBehavior.h" +#include "Rando/ActorBehavior/ActorBehavior.h" +#include "2s2h/DeveloperTools/SaveEditor.h" + +extern "C" { +#include "variables.h" +#include "functions.h" +#include "overlays/actors/ovl_En_Time_Tag/z_en_time_tag.h" +void func_80833B18(PlayState* play, Player* thisx, s32 arg2, f32 speed, f32 velocityY, s16 arg5, + s32 invincibilityTimer); +void EnTimeTag_KickOut_Transition(EnTimeTag* enTimeTag, PlayState* play); +} + +#define MORNING_TIME 0x4000 +#define DAY_LENGTH 0x10000 +// Adjust so that 6 A.M. is 0 and 5:59 A.M. is 0xFFFF +#define ZERO_DAY_START(time) (((u16)(time - MORNING_TIME) % DAY_LENGTH)) + +extern void UpdateGameTime(u16 gameTime); + +int roll = TRAP_FREEZE; +const u16 timeSkipInterval = 4000; + +std::map<TrapTypes, const char*> trapToCvarMap = { + { TRAP_FREEZE, "gRando.Traps.Freeze" }, { TRAP_BLAST, "gRando.Traps.Blast" }, + { TRAP_SHOCK, "gRando.Traps.Shock" }, { TRAP_JINX, "gRando.Traps.Jinx" }, + { TRAP_WALLET, "gRando.Traps.Wallet" }, { TRAP_ENEMY, "gRando.Traps.Enemy" }, + { TRAP_TIME, "gRando.Traps.Time" }, +}; + +std::vector<TrapTypes> getEnabledTrapTypes() { + std::vector<TrapTypes> enabledTrapTypes; + for (auto& trap : trapToCvarMap) { + if (CVarGetInteger(trap.second, 0)) { + enabledTrapTypes.push_back(trap.first); + } + } + if (enabledTrapTypes.size() == 0) { + enabledTrapTypes.push_back(TRAP_FREEZE); + } + return enabledTrapTypes; +}; + +int RollTrapType() { + auto enabledTraps = getEnabledTrapTypes(); + roll = enabledTraps[rand() % enabledTraps.size()]; + return roll; +} + +// clang-format off +std::vector<std::string> defaultTrapMessages = { + "This item is available in the %bRando DLC%w.", + "This is what happens when %gCaladius%w is left unsupervised.", + "Oh no!", + "Uh oh!", + "KEKW", + "We've been trying to reach you about your Horse's %gextended warranty%w.", + "Admit it, you wish this was %gGreg%w.", + "Error 404: Item Not Found", + "Get dunked on!", + "This %rTrap%w is brought to you by today's sponsor...\n%gRaid Shadow Legends%w!", +}; + +std::vector<std::string> freezeTrapMessages = { + "%rOcarina of Time%w called, they want their %bIce Trap%w back.", + "Greetings from %bSnowhead%w! Wish you were here.", + "This item was too %bcool%w for you anyway.", + "Let me see your best %bKing Zora%w impersonation.", + "There's no business like %bSnow%w business!", + "How much does a polar bear weigh? Enough to break the %bice%w.", + "You found the %yTrifo%w... Wait, nevermind...", + "Quick Time Event! Don't die.", +}; + +std::vector<std::string> blastTrapMessages = { + "Coming to you live from the %yThunderdome%w!", + "There was supposed to be an Earth shattering %yKaboom%w!", + "Pardon me while I %yburst%w.", + "This is not the greatest %yblast%w in the world, this is just a tribute.", + "Hey look, this %yitem%w is ticking!", + "This is sure to make %gPamela%w leave the house.", + "You found a %yDodongo Snack%w!", +}; + +std::vector<std::string> shockTrapMessages = { + "We're losing him!\n%gCLEAR%w", + "It's got what %gplants%w need.", + "On todays episode of Grey's Anatomy...", + "I'm giving it all I've got, captain!", + "Now simulating a CD in a Microwave.", + "Hang in there, for %gMeryl%w." +}; + +std::vector<std::string> jinxTrapMessages = { + "Looks like someone's got a case of the %bMondays%w. :( ", + "Your wrist hurts! The doctor says no more swords.", +}; + +std::vector<std::string> walletTrapMessages = { + "Spare some %rchange%w?", + "%rBreaking News%w: the Moon hasn't crashed, but Termina's economy sure has." +}; + +std::vector<std::string> enemyTrapMessages = { + "You made a new friend!", + // Like Like Specific, will need to adjust if new enemies are added. + "Someone likes you! They %rLike Like%w you!", + "You don't need a shield anyway.", + "This item sucks.", +}; + +std::vector<std::string> timeTrapMessages = { + "%yTime%w flashes before your eyes!", + "You have played the %ySun's Song%w!", + "The %rGoddess of Time%w smites you!", + "Spent an hour and a half admiring this %bfake item%w.", + "You found a great place to take a nap!", + "Break time! Not like the world is ending right?", + "I just need you to tell me how to get to the %ytime machine%w.", + "Mweep", +}; + +std::map<TrapTypes, std::vector<std::string>> trapMessageList = { + { TRAP_FREEZE, freezeTrapMessages }, + { TRAP_BLAST, blastTrapMessages }, + { TRAP_SHOCK, shockTrapMessages }, + { TRAP_JINX, jinxTrapMessages }, + { TRAP_WALLET, walletTrapMessages }, + { TRAP_ENEMY, enemyTrapMessages }, + { TRAP_TIME, timeTrapMessages }, +}; +// clang-format on + +std::string GetTrapMessage() { + RollTrapType(); + auto findIt = trapMessageList.find((TrapTypes)roll); + if (findIt == trapMessageList.end()) { + return defaultTrapMessages[rand() % defaultTrapMessages.size()]; + } else { + return findIt->second[rand() % findIt->second.size()]; + } +} + +void Rando::MiscBehavior::OfferTrapItem() { + if (!gPlayState) { + return; + } + + switch (roll) { + case TRAP_FREEZE: + GameInteractor::Instance->events.emplace_back( + GIEventTrap{ .action = []() { func_80833B18(gPlayState, GET_PLAYER(gPlayState), 3, 0, 0, 0, 0); } }); + break; + case TRAP_BLAST: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_BOM, GET_PLAYER(gPlayState)->actor.world.pos.x, + GET_PLAYER(gPlayState)->actor.world.pos.y, GET_PLAYER(gPlayState)->actor.world.pos.z, 1, 0, + 0, 0); + } }); + break; + case TRAP_SHOCK: + GameInteractor::Instance->events.emplace_back( + GIEventTrap{ .action = []() { func_80833B18(gPlayState, GET_PLAYER(gPlayState), 4, 0, 0, 0, 0); } }); + break; + case TRAP_JINX: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + Actor_PlaySfx(&GET_PLAYER(gPlayState)->actor, NA_SE_EN_BUBLE_BITE); + gSaveContext.jinxTimer = 1200; + } }); + break; + case TRAP_WALLET: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + int16_t currentRupees = gSaveContext.save.saveInfo.playerData.rupees; + if (currentRupees != 0) { + Vec3f positional = GET_PLAYER(gPlayState)->actor.world.pos; + positional.y = GET_PLAYER(gPlayState)->actor.world.pos.y + 100.0f; + Item00Type rupee = ITEM00_RUPEE_GREEN; + int16_t spawnedRupees = 0; + int16_t remainingRupees = currentRupees; + for (int i = spawnedRupees; spawnedRupees < remainingRupees;) { + if (currentRupees >= 20) { + rupee = ITEM00_RUPEE_RED; + spawnedRupees += 20; + Rupees_ChangeBy(-20); + currentRupees -= 20; + } else if (currentRupees >= 5) { + rupee = ITEM00_RUPEE_BLUE; + spawnedRupees += 5; + Rupees_ChangeBy(-5); + currentRupees -= 5; + } else if (currentRupees >= 1) { + rupee = ITEM00_RUPEE_GREEN; + spawnedRupees += 1; + Rupees_ChangeBy(-1); + currentRupees -= 1; + } + EnItem00* rupeeActor = (EnItem00*)Item_DropCollectible(gPlayState, &positional, rupee); + rupeeActor->actor.speed = Rand_CenteredFloat(5.0f); + rupeeActor->unk152 = 600; // Extending Time before Despawning + } + } + } }); + break; + case TRAP_ENEMY: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_RR, GET_PLAYER(gPlayState)->actor.world.pos.x, + GET_PLAYER(gPlayState)->actor.world.pos.y, GET_PLAYER(gPlayState)->actor.world.pos.z, 0, 0, + 0, 1); + } }); + break; + case TRAP_TIME: + GameInteractor::Instance->events.emplace_back(GIEventTrap{ .action = []() { + u16 previous_time = gSaveContext.save.time; + u16 new_time = gSaveContext.save.time + timeSkipInterval; + if (previous_time < MORNING_TIME && new_time >= MORNING_TIME) { + // Handles case where Night -> Day + if (gSaveContext.save.day != 3) { + gSaveContext.save.day++; + gSaveContext.save.eventDayCount++; + UpdateGameTime(new_time); + Interface_NewDay(gPlayState, CURRENT_DAY); + // Load environment values for new day + Environment_NewDay(&gPlayState->envCtx); + // Clear weather from day 2 + gWeatherMode = WEATHER_MODE_CLEAR; + gPlayState->envCtx.lightningState = LIGHTNING_OFF; + } else { + // Handles Moonfall case, prevents skipping past it by setting time right before Moonfall. + UpdateGameTime(MORNING_TIME - (timeSkipInterval / 10)); + } + } else { + // Every other case + UpdateGameTime(new_time); + } + TransitionFade_SetColor(&gPlayState->unk_18E48, 0x000000); + R_TRANS_FADE_FLASH_ALPHA_STEP = -1; + Player_PlaySfx(GET_PLAYER(gPlayState), NA_SE_SY_TRANSFORM_MASK_FLASH); + + // Handle kickouts, if needed + EnTimeTag* enTimeTag = (EnTimeTag*)Actor_FindNearby(gPlayState, &GET_PLAYER(gPlayState)->actor, + ACTOR_EN_TIME_TAG, ACTORCAT_ITEMACTION, 99999.9f); + if (enTimeTag != nullptr) { + TimeTagType timeTagType = (TimeTagType)TIMETAG_GET_TYPE(&enTimeTag->actor); + if (timeTagType == TIMETAG_KICKOUT_DOOR || timeTagType >= TIMETAG_KICKOUT_FINAL_HOURS) { + s16 kickoutHour = TIMETAG_KICKOUT_HOUR(&enTimeTag->actor); + s16 kickoutMinute = TIMETAG_KICKOUT_MINUTE(&enTimeTag->actor); + s32 kickoutTime = CLOCK_TIME(kickoutHour, kickoutMinute); + kickoutTime = ZERO_DAY_START(kickoutTime); + previous_time = ZERO_DAY_START(previous_time); + new_time = ZERO_DAY_START(new_time); + // If we were here before the kickout time, and now it's after, then get out of my house + if (previous_time <= kickoutTime && new_time >= kickoutTime) { + // Unless this is the Stock Pot Inn, and the room key is obtained + if (!(gPlayState->sceneId == SCENE_YADOYA && + Flags_GetRandoInf(RANDO_INF_OBTAINED_ROOM_KEY))) { + // This comes from EnTimeTag_KickOut_WaitForTime + Player_SetCsActionWithHaltedActors(gPlayState, &enTimeTag->actor, PLAYER_CSACTION_WAIT); + Message_StartTextbox(gPlayState, 0x1883 + TIMETAG_KICKOUT_GET_TEXT(&enTimeTag->actor), + NULL); + enTimeTag->actionFunc = EnTimeTag_KickOut_Transition; + } + } + } + } + } }); + break; + default: + break; + } +} + +void Rando::ActorBehavior::InitTrapsBehavior() { + // Selectively disable object dependency for actors spawned by traps + COND_VB_SHOULD(VB_ENABLE_OBJECT_DEPENDENCY, IS_RANDO && RANDO_SAVE_OPTIONS[RO_SHUFFLE_TRAPS], { + ObjectId objectId = (ObjectId)va_arg(args, int); + if (objectId == OBJECT_RR) { // Like-Like + *should = false; + } + }); +}
\ No newline at end of file |
