summaryrefslogtreecommitdiff
path: root/mm/2s2h/Rando/MiscBehavior/Traps.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mm/2s2h/Rando/MiscBehavior/Traps.cpp')
-rw-r--r--mm/2s2h/Rando/MiscBehavior/Traps.cpp282
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