From efa1600ad058c7c6e2876aac8311f6fe68950ffe Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 31 Aug 2020 21:10:34 -0700 Subject: [PATCH] Truly deal with the NPC dialog softlock problem Reported by somario360: After I (Luigi) gave the baby penguin to the mother, the start spawned, but I was stuck in the looking up animation. My friend (Mario) talked to Bowser after defeating him, Bowser turned into a key, but he was stuck in the looking up animation after (I was able to grab the key though, but Bowser was slightly visible) (Also don't mind 0 stars, he loves doing the lobby BLJ) The issue is the state machine moved on without removing the reading dialog action. There was no straight forward way to deal with this. Custom functions were written for each call to dialog to ensure that we should stay reading the dialog. --- src/game/behaviors/boo.inc.c | 4 +++- src/game/behaviors/bowser.inc.c | 8 ++++++-- src/game/behaviors/camera_lakitu.inc.c | 4 +++- src/game/behaviors/capswitch.inc.c | 6 +++--- src/game/behaviors/eyerok.inc.c | 8 ++++++-- src/game/behaviors/koopa.inc.c | 8 ++++++-- src/game/behaviors/racing_penguin.inc.c | 8 ++++++-- src/game/behaviors/sl_snowman_wind.inc.c | 4 +++- src/game/behaviors/tuxie.inc.c | 16 +++++++++------- src/game/behaviors/ukiki.inc.c | 11 ++++++++--- src/game/behaviors/whomp.inc.c | 8 ++++++-- src/game/behaviors/wiggler.inc.c | 9 +++++++-- src/game/mario_actions_cutscene.c | 13 ++++++++++++- src/game/mario_misc.c | 2 +- src/game/obj_behaviors_2.c | 5 ++--- src/game/object_helpers.c | 17 +++++++++++++++-- src/game/object_helpers.h | 7 +++++-- 17 files changed, 101 insertions(+), 37 deletions(-) diff --git a/src/game/behaviors/boo.inc.c b/src/game/behaviors/boo.inc.c index 0c41f1ea6..36b7b25e2 100644 --- a/src/game/behaviors/boo.inc.c +++ b/src/game/behaviors/boo.inc.c @@ -492,6 +492,8 @@ static void boo_act_3(void) { } } +u8 boo_act_4_continue_dialog(void) { return o->oAction == 4; } + // Called when a Go on a Ghost Hunt boo dies static void boo_act_4(void) { s32 dialogID; @@ -504,7 +506,7 @@ static void boo_act_4(void) { } struct MarioState* marioState = nearest_mario_state_to_object(o); - if (marioState->playerIndex != 0 || cur_obj_update_dialog(&gMarioStates[0], 2, 2, dialogID, 0)) { + if (marioState->playerIndex != 0 || cur_obj_update_dialog(&gMarioStates[0], 2, 2, dialogID, 0, boo_act_4_continue_dialog)) { create_sound_spawner(SOUND_OBJ_DYING_ENEMY1); obj_mark_for_deletion(o); diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c index 6fc93a1f3..dd869c8c7 100644 --- a/src/game/behaviors/bowser.inc.c +++ b/src/game/behaviors/bowser.inc.c @@ -925,6 +925,8 @@ void bowser_dead_hide(void) { o->oGravity = 0; } +u8 bowser_dead_not_bits_end_continue_dialog(void) { return o->oAction == 4 && o->oSubAction == 3; } + s32 bowser_dead_not_bits_end(void) { struct MarioState* marioState = nearest_mario_state_to_object(o); @@ -934,7 +936,7 @@ s32 bowser_dead_not_bits_end(void) { func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40); o->oBowserUnkF8++; } - if (marioState->playerIndex == 0 && cur_obj_update_dialog(marioState, 2, 18, sBowserDefeatedDialogText[o->oBehParams2ndByte], 0)) { + if (marioState->playerIndex == 0 && cur_obj_update_dialog(marioState, 2, 18, sBowserDefeatedDialogText[o->oBehParams2ndByte], 0, bowser_dead_not_bits_end_continue_dialog)) { o->oBowserUnkF8++; cur_obj_play_sound_2(SOUND_GENERAL2_BOWSER_EXPLODE); sequence_player_unlower(SEQ_PLAYER_LEVEL, 60); @@ -950,6 +952,8 @@ s32 bowser_dead_not_bits_end(void) { return ret; } +u8 bowser_dead_bits_end_continue_dialog(void) { return o->oAction == 4 && o->oBowserUnkF8 < 2; } + s32 bowser_dead_bits_end(void) { struct MarioState* marioState = nearest_mario_state_to_object(o); UNUSED s32 unused; @@ -964,7 +968,7 @@ s32 bowser_dead_bits_end(void) { func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40); o->oBowserUnkF8++; } - if (marioState->playerIndex == 0 && cur_obj_update_dialog(marioState, 2, 18, dialogID, 0)) { + if (marioState->playerIndex == 0 && cur_obj_update_dialog(marioState, 2, 18, dialogID, 0, bowser_dead_bits_end_continue_dialog)) { cur_obj_set_model(MODEL_BOWSER2); sequence_player_unlower(SEQ_PLAYER_LEVEL, 60); sequence_player_fade_out(0, 1); diff --git a/src/game/behaviors/camera_lakitu.inc.c b/src/game/behaviors/camera_lakitu.inc.c index e49deb61e..cb9ca4fd7 100644 --- a/src/game/behaviors/camera_lakitu.inc.c +++ b/src/game/behaviors/camera_lakitu.inc.c @@ -57,6 +57,8 @@ static void camera_lakitu_intro_act_spawn_cloud(void) { } } +u8 camera_lakitu_intro_act_show_dialog_continue_dialog(void) { return o->oCameraLakituFinishedDialog != TRUE; } + /** * Circle down to mario, show the dialog, then fly away. */ @@ -115,7 +117,7 @@ static void camera_lakitu_intro_act_show_dialog(void) { } } } - } else if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, DIALOG_UNK2_FLAG_0, CUTSCENE_DIALOG, DIALOG_034) != 0) { + } else if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, DIALOG_UNK2_FLAG_0, CUTSCENE_DIALOG, DIALOG_034, camera_lakitu_intro_act_show_dialog_continue_dialog) != 0) { o->oCameraLakituFinishedDialog = TRUE; } } diff --git a/src/game/behaviors/capswitch.inc.c b/src/game/behaviors/capswitch.inc.c index 284f3ebb2..30355828b 100644 --- a/src/game/behaviors/capswitch.inc.c +++ b/src/game/behaviors/capswitch.inc.c @@ -30,6 +30,8 @@ void cap_switch_act_1(void) { } } +u8 cap_switch_act_2_continue_dialog(void) { return o->oAction == 2 && o->oTimer >= 5; } + void cap_switch_act_2(void) { capSwitchForcePress = FALSE; s32 sp1C; @@ -44,10 +46,8 @@ void cap_switch_act_2(void) { } else { struct MarioState* marioState = nearest_mario_state_to_object(o); if (marioState == &gMarioStates[0]) { - sp1C = cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 1, 0x0C, CUTSCENE_CAP_SWITCH_PRESS, 0); + sp1C = cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 1, 0x0C, CUTSCENE_CAP_SWITCH_PRESS, 0, cap_switch_act_2_continue_dialog); if (sp1C) { o->oAction = 3; } - } else { - o->oAction = 3; } } } diff --git a/src/game/behaviors/eyerok.inc.c b/src/game/behaviors/eyerok.inc.c index 42e2b1e82..a1398f7c0 100644 --- a/src/game/behaviors/eyerok.inc.c +++ b/src/game/behaviors/eyerok.inc.c @@ -62,8 +62,10 @@ static void eyerok_boss_act_wake_up(void) { } } +u8 eyerok_boss_act_show_intro_text_continue_dialog(void) { return o->oAction == EYEROK_BOSS_ACT_SHOW_INTRO_TEXT; } + static void eyerok_boss_act_show_intro_text(void) { - if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, DIALOG_117)) { + if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, DIALOG_117, eyerok_boss_act_show_intro_text_continue_dialog)) { o->oAction = EYEROK_BOSS_ACT_FIGHT; } } @@ -115,9 +117,11 @@ static void eyerok_boss_act_fight(void) { } } +u8 eyerok_boss_act_die_continue_dialog(void) { return o->oAction == EYEROK_BOSS_ACT_DIE; } + static void eyerok_boss_act_die(void) { if (o->oTimer == 60) { - if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, DIALOG_118)) { + if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, DIALOG_118, eyerok_boss_act_die_continue_dialog)) { spawn_default_star(0.0f, -900.0f, -3700.0f); } else { o->oTimer -= 1; diff --git a/src/game/behaviors/koopa.inc.c b/src/game/behaviors/koopa.inc.c index b9d8bbeda..59a19568e 100644 --- a/src/game/behaviors/koopa.inc.c +++ b/src/game/behaviors/koopa.inc.c @@ -580,6 +580,8 @@ static void koopa_the_quick_force_start_race(void) { o->oFlags |= OBJ_FLAG_ACTIVE_FROM_AFAR; } +u8 koopa_the_quick_act_show_init_text_continue_dialog(void) { return o->oAction == KOOPA_THE_QUICK_ACT_SHOW_INIT_TEXT; } + /** * Display the dialog asking mario if he wants to race. Begin the race or * return to the waiting action. @@ -588,7 +590,7 @@ static void koopa_the_quick_act_show_init_text(void) { struct MarioState* marioState = nearest_mario_state_to_object(o); s32 response = 0; if (marioState == &gMarioStates[0]) { - response = obj_update_race_proposition_dialog(&gMarioStates[0], sKoopaTheQuickProperties[o->oKoopaTheQuickRaceIndex].initText); + response = obj_update_race_proposition_dialog(&gMarioStates[0], sKoopaTheQuickProperties[o->oKoopaTheQuickRaceIndex].initText, koopa_the_quick_act_show_init_text_continue_dialog); } if (response == 1) { @@ -778,6 +780,8 @@ static void koopa_the_quick_act_stop(void) { } } +u8 koopa_the_quick_act_after_race_continue_dialog(void) { return o->oAction == KOOPA_THE_QUICK_ACT_AFTER_RACE && o->parentObj->oKoopaRaceEndpointUnk100 > 0; } + /** * Wait for mario to approach, then show text indicating the status of the race. * If mario got to the finish line first and didn't use the cannon, then spawn @@ -812,7 +816,7 @@ static void koopa_the_quick_act_after_race(void) { } } else if (o->parentObj->oKoopaRaceEndpointUnk100 > 0) { if (marioState == &gMarioStates[0]) { - s32 dialogResponse = cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 1, CUTSCENE_DIALOG, o->parentObj->oKoopaRaceEndpointUnk100); + s32 dialogResponse = cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 1, CUTSCENE_DIALOG, o->parentObj->oKoopaRaceEndpointUnk100, koopa_the_quick_act_after_race_continue_dialog); if (dialogResponse != 0) { o->parentObj->oKoopaRaceEndpointUnk100 = -1; o->oTimer = 0; diff --git a/src/game/behaviors/racing_penguin.inc.c b/src/game/behaviors/racing_penguin.inc.c index 60e965da4..77f1c1245 100644 --- a/src/game/behaviors/racing_penguin.inc.c +++ b/src/game/behaviors/racing_penguin.inc.c @@ -51,11 +51,13 @@ static void racing_penguin_force_start_race(void) { penguinForceStartRace = FALSE; } +u8 racing_penguin_act_show_init_text_continue_dialog(void) { return o->oAction == RACING_PENGUIN_ACT_SHOW_INIT_TEXT; } + static void racing_penguin_act_show_init_text(void) { s32 response; struct Object *child; - response = obj_update_race_proposition_dialog(&gMarioStates[0], sRacingPenguinData[o->oBehParams2ndByte].text); + response = obj_update_race_proposition_dialog(&gMarioStates[0], sRacingPenguinData[o->oBehParams2ndByte].text, racing_penguin_act_show_init_text_continue_dialog); if (response == 1) { child = cur_obj_nearest_object_with_behavior(bhvPenguinRaceFinishLine); child->parentObj = o; @@ -151,6 +153,8 @@ static void racing_penguin_act_finish_race(void) { } } +u8 racing_penguin_act_show_final_text_continue_dialog(void) { return o->oAction == RACING_PENGUIN_ACT_SHOW_FINAL_TEXT && o->oRacingPenguinFinalTextbox > 0; } + static void racing_penguin_act_show_final_text(void) { s32 textResult; @@ -181,7 +185,7 @@ static void racing_penguin_act_show_final_text(void) { o->oForwardVel = 4.0f; } } else if (o->oRacingPenguinFinalTextbox > 0) { - if ((textResult = cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 1, CUTSCENE_DIALOG, o->oRacingPenguinFinalTextbox)) != 0) { + if ((textResult = cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 1, CUTSCENE_DIALOG, o->oRacingPenguinFinalTextbox, racing_penguin_act_show_final_text_continue_dialog)) != 0) { o->oRacingPenguinFinalTextbox = -1; o->oTimer = 0; } diff --git a/src/game/behaviors/sl_snowman_wind.inc.c b/src/game/behaviors/sl_snowman_wind.inc.c index da518f856..6651c48e6 100644 --- a/src/game/behaviors/sl_snowman_wind.inc.c +++ b/src/game/behaviors/sl_snowman_wind.inc.c @@ -1,5 +1,7 @@ // sl_snowman_wind.c.inc +u8 bhv_sl_snowman_wind_loop_continue_dialog(void) { return o->oSubAction == SL_SNOWMAN_WIND_ACT_TALKING; } + void bhv_sl_snowman_wind_loop(void) { UNUSED s32 unusedVar = 0; s16 marioAngleFromWindSource; @@ -21,7 +23,7 @@ void bhv_sl_snowman_wind_loop(void) { // Mario has come close, begin dialog. } else if (o->oSubAction == SL_SNOWMAN_WIND_ACT_TALKING) { - if (cur_obj_update_dialog(&gMarioStates[0], 2, 2, DIALOG_153, 0)) + if (cur_obj_update_dialog(&gMarioStates[0], 2, 2, DIALOG_153, 0, bhv_sl_snowman_wind_loop_continue_dialog)) o->oSubAction++; // Blowing, spawn wind particles (SL_SNOWMAN_WIND_ACT_BLOWING) diff --git a/src/game/behaviors/tuxie.inc.c b/src/game/behaviors/tuxie.inc.c index 280d1318d..accfda363 100644 --- a/src/game/behaviors/tuxie.inc.c +++ b/src/game/behaviors/tuxie.inc.c @@ -43,6 +43,8 @@ void tuxies_mother_act_2(void) { } } +u8 tuxies_mother_act_1_continue_dialog(void) { return (o->oAction == 1 && o->oSubAction == 0); } + void tuxies_mother_act_1(void) { // only local can interact with mother struct MarioState* marioState = &gMarioStates[0]; @@ -60,7 +62,7 @@ void tuxies_mother_act_1(void) { dialogID = DIALOG_058; else dialogID = DIALOG_059; - if (cur_obj_update_dialog_with_cutscene(marioState, 2, 1, CUTSCENE_DIALOG, dialogID)) { + if (cur_obj_update_dialog_with_cutscene(marioState, 2, 1, CUTSCENE_DIALOG, dialogID, tuxies_mother_act_1_continue_dialog)) { if (dialogID == DIALOG_058) o->oSubAction = 1; else @@ -102,6 +104,8 @@ void tuxies_mother_act_1(void) { } } +u8 tuxies_mother_act_0_continue_dialog(void) { return (o->oAction == 0 && o->oSubAction == 1); } + void tuxies_mother_act_0(void) { // only local can interact with mother struct MarioState* marioState = &gMarioStates[0]; @@ -116,11 +120,9 @@ void tuxies_mother_act_0(void) { cur_obj_init_animation_with_sound(3); if (sp28 < 500.0f) sp2C = 1; - if (sp24 != NULL && sp28 < 300.0f && sp24->oHeldState != HELD_FREE) { - if (sp24->heldByPlayerIndex == 0) { - o->oAction = 1; - sp24->oSmallPenguinUnk88 = 1; - } + if (sp24->heldByPlayerIndex == 0 && sp24 != NULL && sp28 < 300.0f && sp24->oHeldState != HELD_FREE) { + o->oAction = 1; + sp24->oSmallPenguinUnk88 = 1; o->prevObj = sp24; } else { switch (o->oSubAction) { @@ -130,7 +132,7 @@ void tuxies_mother_act_0(void) { o->oSubAction++; break; case 1: - if (cur_obj_update_dialog_with_cutscene(marioState, 2, 1, CUTSCENE_DIALOG, DIALOG_057)) + if (cur_obj_update_dialog_with_cutscene(marioState, 2, 1, CUTSCENE_DIALOG, DIALOG_057, tuxies_mother_act_0_continue_dialog)) o->oSubAction++; break; case 2: diff --git a/src/game/behaviors/ukiki.inc.c b/src/game/behaviors/ukiki.inc.c index 5693cabdb..6003f96ff 100644 --- a/src/game/behaviors/ukiki.inc.c +++ b/src/game/behaviors/ukiki.inc.c @@ -333,6 +333,8 @@ static Trajectory sCageUkikiPath[] = { TRAJECTORY_END(), }; +u8 ukiki_act_go_to_cage_continue_dialog(void) { return o->oAction == UKIKI_ACT_GO_TO_CAGE && o->oSubAction == UKIKI_SUB_ACT_CAGE_TALK_TO_MARIO; } + /** * Travel to the cage, wait for Mario, jump to it, and ride it to * our death. Ukiki is a tad suicidal. @@ -383,7 +385,7 @@ void ukiki_act_go_to_cage(void) { case UKIKI_SUB_ACT_CAGE_TALK_TO_MARIO: cur_obj_init_animation_with_sound(UKIKI_ANIM_HANDSTAND); - if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 3, 1, CUTSCENE_DIALOG, DIALOG_080)) { + if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 3, 1, CUTSCENE_DIALOG, DIALOG_080, ukiki_act_go_to_cage_continue_dialog)) { o->oSubAction++; } break; @@ -554,6 +556,9 @@ void cage_ukiki_held_loop(void) { } } +u8 hat_ukiki_held_loop_1(void) { return o->oHeldState == HELD_HELD && o->oUkikiTextState == UKIKI_TEXT_STEAL_HAT; } +u8 hat_ukiki_held_loop_2(void) { return o->oHeldState == HELD_HELD && o->oUkikiTextState == UKIKI_TEXT_HAS_HAT; } + /** * Called by the main behavior function for the hat ukiki whenever it is held. */ @@ -567,7 +572,7 @@ void hat_ukiki_held_loop(void) { break; case UKIKI_TEXT_STEAL_HAT: - if (cur_obj_update_dialog(&gMarioStates[0], 2, 2, DIALOG_100, 0)) { + if (cur_obj_update_dialog(&gMarioStates[0], 2, 2, DIALOG_100, 0, hat_ukiki_held_loop_1)) { o->oInteractionSubtype |= INT_SUBTYPE_DROP_IMMEDIATELY; o->oUkikiTextState = UKIKI_TEXT_STOLE_HAT; } @@ -577,7 +582,7 @@ void hat_ukiki_held_loop(void) { break; case UKIKI_TEXT_HAS_HAT: - if (cur_obj_update_dialog(&gMarioStates[0], 2, 18, DIALOG_101, 0)) { + if (cur_obj_update_dialog(&gMarioStates[0], 2, 18, DIALOG_101, 0, hat_ukiki_held_loop_2)) { mario_retrieve_cap(); set_mario_npc_dialog(&gMarioStates[0], 0); o->oUkikiHasHat &= ~UKIKI_HAT_ON; diff --git a/src/game/behaviors/whomp.inc.c b/src/game/behaviors/whomp.inc.c index 62757db60..678e6c516 100644 --- a/src/game/behaviors/whomp.inc.c +++ b/src/game/behaviors/whomp.inc.c @@ -14,6 +14,8 @@ void whomp_play_sfx_from_pound_animation(void) { cur_obj_play_sound_2(SOUND_OBJ_POUNDING1); } +u8 whomp_act_0_continue_dialog(void) { return o->oAction == 0; } + void whomp_act_0(void) { struct MarioState* marioState = nearest_mario_state_to_object(o); struct Object* player = marioState->marioObj; @@ -32,7 +34,7 @@ void whomp_act_0(void) { cur_obj_set_pos_to_home(); o->oHealth = 3; } - } else if (marioState == &gMarioStates[0] && cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 1, CUTSCENE_DIALOG, DIALOG_114)) { + } else if (marioState == &gMarioStates[0] && cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 1, CUTSCENE_DIALOG, DIALOG_114, whomp_act_0_continue_dialog)) { o->oAction = 2; network_send_object(o); } @@ -230,10 +232,12 @@ void whomp_act_6(void) { } } +u8 whomp_act_8_continue_dialog(void) { return o->oAction == 8; } + void whomp_act_8(void) { if (o->oBehParams2ndByte != 0) { struct MarioState* marioState = nearest_mario_state_to_object(o); - if (marioState == &gMarioStates[0] && cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 2, CUTSCENE_DIALOG, DIALOG_115)) { + if (marioState == &gMarioStates[0] && cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 2, CUTSCENE_DIALOG, DIALOG_115, whomp_act_8_continue_dialog)) { obj_set_angle(o, 0, 0, 0); cur_obj_hide(); cur_obj_become_intangible(); diff --git a/src/game/behaviors/wiggler.inc.c b/src/game/behaviors/wiggler.inc.c index 23a31b895..e01f0a632 100644 --- a/src/game/behaviors/wiggler.inc.c +++ b/src/game/behaviors/wiggler.inc.c @@ -209,6 +209,8 @@ void wiggler_init_segments(void) { } } + u8 wiggler_act_walk_continue_dialog(void) { return o->oAction == WIGGLER_ACT_WALK && o->oWigglerTextStatus < WIGGLER_TEXT_STATUS_COMPLETED_DIALOG; } + /** * Show text if necessary. Then walk toward mario if not at full health, and * otherwise wander in random directions. @@ -228,7 +230,7 @@ static void wiggler_act_walk(void) { // If Mario is positioned below the wiggler, assume he entered through the // lower cave entrance, so don't display text. - if (gMarioObject->oPosY < o->oPosY || cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, DIALOG_150) != 0) { + if (gMarioObject->oPosY < o->oPosY || cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, DIALOG_150, wiggler_act_walk_continue_dialog) != 0) { o->oWigglerTextStatus = WIGGLER_TEXT_STATUS_COMPLETED_DIALOG; } } else { @@ -284,6 +286,9 @@ static void wiggler_act_walk(void) { } } } + +u8 wiggler_act_jumped_on_continue_dialog(void) { return o->oAction == WIGGLER_ACT_JUMPED_ON && o->header.gfx.scale[1] >= 4.0f && o->oTimer > 30; } + /** * Squish and unsquish, then show text and enter either the walking or shrinking * action. @@ -304,7 +309,7 @@ static void wiggler_act_jumped_on(void) { // defeated) or go back to walking if (o->header.gfx.scale[1] >= 4.0f) { if (o->oTimer > 30) { - if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, attackText[o->oHealth - 2]) != 0) { + if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 2, 0, CUTSCENE_DIALOG, attackText[o->oHealth - 2], wiggler_act_jumped_on_continue_dialog) != 0) { // Because we don't want the wiggler to disappear after being // defeated, we leave its health at 1 if (--o->oHealth == 1) { diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 65a59558c..1a07fbe14 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -358,6 +358,8 @@ s32 set_mario_npc_dialog(struct MarioState* m, s32 actionArg) { if (m->playerIndex == 0 && actionArg == 0) { localDialogNPCBehavior = NULL; + continueDialogFunction = NULL; + continueDialogFunctionObject = NULL; } // in dialog @@ -395,8 +397,17 @@ s32 act_reading_npc_dialog(struct MarioState *m) { s32 headTurnAmount = 0; s16 angleToNPC; + if (m->playerIndex != 0) { return FALSE; } + if (m->playerIndex == 0) { - if (m->usedObj == NULL || m->usedObj->activeFlags == ACTIVE_FLAG_DEACTIVATED || m->usedObj->behavior != localDialogNPCBehavior) { + u8 continueDialogCallback = TRUE; + if (continueDialogFunction != NULL && continueDialogFunctionObject != NULL) { + struct Object* tmp = gCurrentObject; + gCurrentObject = continueDialogFunctionObject; + continueDialogCallback = continueDialogFunction(); + gCurrentObject = tmp; + } + if (!continueDialogCallback || m->usedObj == NULL || m->usedObj->activeFlags == ACTIVE_FLAG_DEACTIVATED || m->usedObj->behavior != localDialogNPCBehavior) { set_mario_npc_dialog(m, 0); } } diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c index b3e40c477..d7495c111 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -124,7 +124,7 @@ static void toad_message_opaque(void) { } static void toad_message_talking(void) { - if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 3, 1, CUTSCENE_DIALOG, gCurrentObject->oToadMessageDialogId) + if (cur_obj_update_dialog_with_cutscene(&gMarioStates[0], 3, 1, CUTSCENE_DIALOG, gCurrentObject->oToadMessageDialogId, NULL) != 0) { gCurrentObject->oToadMessageRecentlyTalked = 1; gCurrentObject->oToadMessageState = TOAD_MESSAGE_FADING; diff --git a/src/game/obj_behaviors_2.c b/src/game/obj_behaviors_2.c index 97969d6cf..c2f73a9ad 100644 --- a/src/game/obj_behaviors_2.c +++ b/src/game/obj_behaviors_2.c @@ -97,9 +97,8 @@ static s16 obj_get_pitch_from_vel(void) { * If the player declines the race, then disable time stop and allow Mario to * move again. */ -static s32 obj_update_race_proposition_dialog(struct MarioState* m, s16 dialogID) { - s32 dialogResponse = - cur_obj_update_dialog_with_cutscene(m, 2, DIALOG_UNK2_FLAG_0 | DIALOG_UNK2_LEAVE_TIME_STOP_ENABLED, CUTSCENE_RACE_DIALOG, dialogID); +static s32 obj_update_race_proposition_dialog(struct MarioState* m, s16 dialogID, u8 (*inContinueDialogFunction)(void)) { + s32 dialogResponse = cur_obj_update_dialog_with_cutscene(m, 2, DIALOG_UNK2_FLAG_0 | DIALOG_UNK2_LEAVE_TIME_STOP_ENABLED, CUTSCENE_RACE_DIALOG, dialogID, inContinueDialogFunction); if (dialogResponse == 2) { set_mario_npc_dialog(m, 0); diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index c1a457e1e..d3c0fa19f 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -27,6 +27,9 @@ #include "spawn_object.h" #include "spawn_sound.h" +u8 (*continueDialogFunction)(void) = NULL; +struct Object* continueDialogFunctionObject = NULL; + s8 D_8032F0A0[] = { 0xF8, 0x08, 0xFC, 0x04 }; s16 D_8032F0A4[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; static s8 sLevelsWithRooms[] = { LEVEL_BBH, LEVEL_CASTLE, LEVEL_HMC, -1 }; @@ -2663,6 +2666,8 @@ s32 cur_obj_can_mario_activate_textbox_2(struct MarioState* m, f32 radius, f32 h } static void cur_obj_end_dialog(struct MarioState* m, s32 dialogFlags, s32 dialogResult) { + if (m->playerIndex != 0) { return 0; } + o->oDialogResponse = dialogResult; o->oDialogState++; @@ -2671,10 +2676,14 @@ static void cur_obj_end_dialog(struct MarioState* m, s32 dialogFlags, s32 dialog } } -s32 cur_obj_update_dialog(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 dialogID, UNUSED s32 unused) { +s32 cur_obj_update_dialog(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 dialogID, UNUSED s32 unused, u8 (*inContinueDialogFunction)(void)) { s32 dialogResponse = 0; UNUSED s32 doneTurning = TRUE; + if (m->playerIndex != 0) { return 0; } + continueDialogFunctionObject = gCurrentObject; + continueDialogFunction = inContinueDialogFunction; + switch (o->oDialogState) { #ifdef VERSION_JP case DIALOG_UNK1_ENABLE_TIME_STOP: @@ -2748,10 +2757,14 @@ s32 cur_obj_update_dialog(struct MarioState* m, s32 actionArg, s32 dialogFlags, return dialogResponse; } -s32 cur_obj_update_dialog_with_cutscene(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 cutsceneTable, s32 dialogID) { +s32 cur_obj_update_dialog_with_cutscene(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 cutsceneTable, s32 dialogID, u8 (*inContinueDialogFunction)(void)) { s32 dialogResponse = 0; s32 doneTurning = TRUE; + if (m->playerIndex != 0) { return 0; } + continueDialogFunctionObject = gCurrentObject; + continueDialogFunction = inContinueDialogFunction; + switch (o->oDialogState) { #ifdef VERSION_JP case DIALOG_UNK2_ENABLE_TIME_STOP: diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 7fd97d922..6b1ada53f 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -67,6 +67,9 @@ struct SpawnParticlesInfo /*0x10*/ f32 sizeRange; }; +extern u8 (*continueDialogFunction)(void); +extern struct Object* continueDialogFunctionObject; + Gfx *geo_update_projectile_pos_from_parent(s32 callContext, UNUSED struct GraphNode *node, Mat4 mtx); Gfx *geo_update_layer_transparency(s32 callContext, struct GraphNode *node, UNUSED void *context); #ifdef AVOID_UB @@ -277,8 +280,8 @@ void set_time_stop_flags(s32 flags); void clear_time_stop_flags(s32 flags); s32 cur_obj_can_mario_activate_textbox(struct MarioState* m, f32 radius, f32 height, UNUSED s32 unused); s32 cur_obj_can_mario_activate_textbox_2(struct MarioState* m, f32 radius, f32 height); -s32 cur_obj_update_dialog(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 dialogID, UNUSED s32 unused); -s32 cur_obj_update_dialog_with_cutscene(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 cutsceneTable, s32 dialogID); +s32 cur_obj_update_dialog(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 dialogID, UNUSED s32 unused, u8 (*inContinueDialogFunction)(void)); +s32 cur_obj_update_dialog_with_cutscene(struct MarioState* m, s32 actionArg, s32 dialogFlags, s32 cutsceneTable, s32 dialogID, u8 (*inContinueDialogFunction)(void)); s32 cur_obj_has_model(u16 modelID); void cur_obj_align_gfx_with_floor(void); s32 mario_is_within_rectangle(s16 minX, s16 maxX, s16 minZ, s16 maxZ);