Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- The Wizrobe softlock is caused by an intersection of a few different problems. Specifically, it's a conflict between how the actor is programmed and how the Secret Shrine room is set up. To start, we need to understand how the Wizrobe chooses which platforms to spawn at.
- The Wizrobe chooses his current platform in EnWiz_SelectPlatform. The logic is frankly slightly overcomplicated, but basically he's looking at every EnWizBrock actor (Wizrobe platform) in the room to see how many there are, then he picks a random one that isn't the one he just was at. This condition (on line 592 of current MM decomp master) guarantees that he won't randomly pick the platform he just was at:
- curPlatformIndex = Rand_ZeroFloat(i);
- while ((this->curPlatformIndex == curPlatformIndex) || ((s16)i == curPlatformIndex)) {
- curPlatformIndex = Rand_ZeroFloat(i);
- if (1) {}
- }
- As a consequence, if there are N platforms in the room, then the Wizrobe has a 1 / (N - 1) chance of picking any particular platform, assuming the particular platform in question *isn't* the one he was just on.
- The softlock happens when you perform the "quick kill" on the Wizrobe, i.e., you kill it before the start of the second phase cutscene. So what happens when the Wizrobe transitions from the first phase to the second phase? To start with, you have to damage the Wizrobe in its first phase. This will end up calling into EnWiz_Damaged, which checks to see if his health is 0. Here's the relevant code (on line 1132):
- if (this->actor.colChkInfo.health <= 0) {
- if (this->fightState == EN_WIZ_FIGHT_STATE_FIRST_PHASE) {
- this->fightState = EN_WIZ_FIGHT_STATE_SECOND_PHASE_CUTSCENE;
- if ((this->type == EN_WIZ_TYPE_FIRE) || (this->type == EN_WIZ_TYPE_FIRE_NO_BGM)) {
- this->actor.colChkInfo.health = 8;
- } else {
- this->actor.colChkInfo.health = 6;
- }
- EnWiz_SetupDisappear(this);
- } else {
- EnWiz_SetupDead(this);
- }
- }
- To summarize the code above, if he has 0 health, and he's in his first phase, he will transition to his second phase, get some health back, and disappear. If he has 0 health in any other state of the fight, he'll die instead, but let's ignore that for now.
- The Wizrobe disappearing and reappearing is caused by a chain of function calls, namely EnWiz_SetupDisappear -> EnWiz_Disappear -> EnWiz_SetupAppear. At at the top of EnWiz_SetupAppear (on line 689), you can see we call EnWiz_SelectPlatform, the same function we mentioned before. Calling EnWiz_SelectPlatform has two consequences:
- - He will randomly select a platform in the room to set as his "current platform" (this is the logic we talked about above)
- - There's a special case (on line 603) for when he is in the "second phase cutscene" state. In this state, he will actually ignore what his "current platform" is and instead appear at the "first platform" in the room. You can see the relevant code here:
- case EN_WIZ_FIGHT_STATE_SECOND_PHASE_CUTSCENE:
- Math_Vec3f_Copy(&this->actor.world.pos, &this->platforms[0]->world.pos);
- for (i = 0, ghostAlpha = 128; i < this->platformCount; i++, ghostAlpha -= 10) {
- Math_Vec3f_Copy(&this->ghostPos[i], &this->actor.world.pos);
- this->ghostAlpha[i] = ghostAlpha;
- }
- break;
- To summarize, when the Wizrobe appears before the second phase cutscene, his idea of his "current platform" can be *different* than the platform he is actually on. He will always be on the "first platform", but his "current platform" can randomly be something different. It is this difference that causes the softlock.
- To explain further, it's important to know the layout of the Secret Shrine Wizrobe room. There are four Wizrobe platforms in the room; the first and second platforms are elevated off the ground on raised ledges, whereas the third and fourth platforms are on the floor. We said before that when he appears before the second phase cutscene, his "current platform" can be different from the platform he is actually on, and it is specifically the difference in elevation between these two platforms that causes the softlock. If the Wizrobe's "current platform" is randomly selected to be one of the ground platforms, and you quick kill him on the ledge, the softlock happens. But why?
- When you kill a Wizrobe, a few different functions run. The important one is EnWiz_Damaged, which launches the Wizrobe into the air when he dies, then waits for him to come back down before transitioning him to the "dead" state. This check, starting on line 1123, is checking to see if he's made it back down to his "current platform" (ignore the "this->timer" part of the check; in the quick kill case, this is always false):
- if ((this->timer == 1) || ((this->actor.velocity.y < 0.0f) && (this->actor.world.pos.y < (this->platforms[this->curPlatformIndex]->world.pos.y + 11.0f))))
- You might be able to see the problem here. Since we're checking the y-position of what the Wizrobe *thinks* is its "current platform", it might be lower than the platform it is *actually* on, and if it *is* lower, then the y-position it's looking for is actually *inside the floor*. Thus, the Wizrobe gets stuck forever, permanently waiting for itself to fall onto a y-position it cannot reach.
- How often does this softlock happen? Well, it depends on the last platform it was at before you do the quick kill. If the last platform was an elevated platform, then it can randomly pick one of the elevated platforms or two of the floor platforms as its next random "current platform"; if it picks either of the "floor platforms", then performing a quick kill will softlock. If the last platform was a floor platform, then it can randomly pick two of the elevated platforms or one of the floor platforms as its next random "current platform". In other words, a quick kill either has a random 2/3 or 1/3 chance of softlocking the game in the Secret Shrine, depending on whether the last platform the Wizrobe was on was elevated or on the floor.
- How could we fix the softlock? It is actually extremely simple. We just need to make sure the "current platform" and the actual platform don't get desynced. We could patch it very easily by doing something like this (on line 603):
- case EN_WIZ_FIGHT_STATE_SECOND_PHASE_CUTSCENE:
- this->curcurPlatformIndex = 0;
- Math_Vec3f_Copy(&this->actor.world.pos, &this->platforms[0]->world.pos);
- for (i = 0, ghostAlpha = 128; i < this->platformCount; i++, ghostAlpha -= 10) {
- Math_Vec3f_Copy(&this->ghostPos[i], &this->actor.world.pos);
- this->ghostAlpha[i] = ghostAlpha;
- }
- break;
Advertisement
Add Comment
Please, Sign In to add comment