diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25e332491e..dd145771ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build ROM on: push: - branches: [ romhack ] + branches: [ followers ] pull_request: jobs: diff --git a/.gitignore b/.gitignore index 94c424e695..315e6c28da 100644 --- a/.gitignore +++ b/.gitignore @@ -36,8 +36,11 @@ porymap.project.cfg .fuse_hidden* .ccls-cache/* .ropeproject/ -overworld/ +/overworld/ +/icons/ *.sna *.diff *.sym *.js +agbcc/ +/*.zip diff --git a/README.md b/README.md index c524149913..c3648a0443 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,18 @@ This is a fork of the [matching decompilation](https://github.com/pret/pokeemerald) at [PRET](https://github.com/pret). -The general philosophy of this fork is to maintain vanilla behavior & compatibility where possible, especially in terms of data structures. For that reason, this fork does not increase the size of the save data structure or the object event structure, nor does it add a `nature` field to the Pokémon structure. +This fork tries to maintain vanilla compatibility whenever possible. It doesn't increase the size of any save data structure or the object event structure. -There are several branches, each with additional features compared to vanilla: +There are several branches, each with one main feature: -**romhack** branch: +**followers** branch: * [HGSS-style pokémon followers](https://bulbapedia.bulbagarden.net/wiki/Walking_Pok%C3%A9mon#Pok.C3.A9mon_HeartGold_and_SoulSilver) for all 386 pokémon, including emotes, the 28 Unown forms and a majority of follower messages. * Dynamic overworld palettes & reflections compatible with vanilla berry trees. -* A way to change a pokemon's nature while mangling its PID as little as possible. +* Function to change a pokemon's nature while preserving most properties of its PID. * Function to detect newer emulators/new GBA hardware. **icons** branch: -* Everything from the **romhack** branch. +* Everything from the **followers** branch. * All pokemon icons updated to Gen 6, based on [this repo](https://github.com/msikma/pokesprite/tree/master/icons/pokemon/regular) * This includes compatibility with the PC, trade, contests, mail, Battle Dome. Examples: ![PC](https://i.imgur.com/wzwJfd1.png) @@ -22,7 +22,7 @@ There are several branches, each with additional features compared to vanilla: * Icons share palettes with front sprites, meaning that shiny pokemon will also have shiny icons! **lighting** branch: -* Everything from the **romhack** branch. +* Everything from the **followers** branch. * Day/night shading compatible with weather. * GSC-style window lights. * WIP interframe-blended lamp lights at night, i.e in Rustboro. diff --git a/asm/macros/movement.inc b/asm/macros/movement.inc index 62618379b6..8ce8330371 100644 --- a/asm/macros/movement.inc +++ b/asm/macros/movement.inc @@ -162,5 +162,7 @@ create_movement_action figure_8, MOVEMENT_ACTION_FIGURE_8 create_movement_action fly_up, MOVEMENT_ACTION_FLY_UP create_movement_action fly_down, MOVEMENT_ACTION_FLY_DOWN + create_movement_action exit_pokeball, MOVEMENT_ACTION_EXIT_POKEBALL + create_movement_action enter_pokeball, MOVEMENT_ACTION_ENTER_POKEBALL create_movement_action step_end, MOVEMENT_ACTION_STEP_END diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000000..c1aa53cc13 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,7 @@ +-Wimplicit -Wparentheses -Werror -O2 +-lgcc +-lc +-Iinclude +-Igflib +-undef +-Itools/agbcc/include diff --git a/data/maps/SootopolisCity/scripts.inc b/data/maps/SootopolisCity/scripts.inc index 69f74880ec..af6f68555c 100644 --- a/data/maps/SootopolisCity/scripts.inc +++ b/data/maps/SootopolisCity/scripts.inc @@ -495,6 +495,7 @@ SootopolisCity_EventScript_RayquazaSceneFromPokeCenter:: removeobject LOCALID_GROUDON removeobject LOCALID_KYOGRE addobject LOCALID_RAYQUAZA + hideobjectat LOCALID_RAYQUAZA, MAP_SOOTOPOLIS_CITY setvar VAR_0x8004, TRUE special Script_DoRayquazaScene waitstate diff --git a/data/scripts/follower.inc b/data/scripts/follower.inc index a8303e5ba3..77e2da5cb4 100644 --- a/data/scripts/follower.inc +++ b/data/scripts/follower.inc @@ -74,7 +74,7 @@ EventScript_FollowerJump:: return EnterPokeballMovement:: - .byte 0x9F @ EnterPokeball + enter_pokeball step_end @ Movement scripts below, movements are defined in movement.inc diff --git a/include/follower_helper.h b/include/follower_helper.h index 54b4d4d164..a03e08b5d3 100644 --- a/include/follower_helper.h +++ b/include/follower_helper.h @@ -16,9 +16,14 @@ enum { FOLLOWER_EMOTION_LENGTH, }; +// This struct is optimized for size +// Each "section" can be used to combine multiple conditions, +// i.e, species and map +// Just set the flags accordingly and use the right union member struct __attribute__((packed)) FollowerMsgInfoExtended { const u8 *text; const u8 *script; + union __attribute__((packed)) { u16 species:10; struct __attribute__((packed)) { @@ -55,7 +60,8 @@ struct __attribute__((packed)) FollowerMsgInfoExtended { } wt; u16 wtFlags:2; // 1 = weather matching, 2 = song, 3 = time u16 weight:3; - u16 textSpread:1; // if set, `text` is an array of texts instead + // if set, `text` is treated as an array of up to 4 texts instead + u16 textSpread:1; union __attribute__((packed)) { struct __attribute__((packed)) { @@ -63,9 +69,63 @@ struct __attribute__((packed)) FollowerMsgInfoExtended { u16 distance:6; } mb; } near; - u16 nearFlags:2; // 1 = mb within '+' shape distance away + u16 nearFlags:2; // 1 = mb within '+'-shaped distance away }; -extern const struct FollowerMsgInfoExtended gFollowerConditionalMessages[]; +enum { + ST_FLAGS_SPECIES = 1, + ST_FLAGS_TYPE, + ST_FLAGS_STATUS, +}; + +enum { + MM_FLAGS_MAPSEC = 1, + MM_FLAGS_MAP, + MM_FLAGS_MB, // (m)etatile (b)ehavior +}; + +enum { + WT_FLAGS_WEATHER = 1, + WT_FLAGS_MUSIC, + WT_FLAGS_TIME, +}; + +#define NEAR_FLAGS_MB 1 + +enum { + COND_MSG_CELEBI, + COND_MSG_FIRE, + COND_MSG_EVER_GRANDE, + COND_MSG_ROUTE_112, + COND_MSG_DAY_CARE, + COND_MSG_MART, + COND_MSG_VICTORY_ROAD, + COND_MSG_BIKE_SHOP, + COND_MSG_MACHINES, + COND_MSG_SAILING, + COND_MSG_PUDDLE, + COND_MSG_SAND, + COND_MSG_GRASS, + COND_MSG_FOOTPRINTS, + COND_MSG_ELEVATOR, + COND_MSG_ICE_ROOM, + COND_MSG_ROUTE_117, + COND_MSG_DRAGON_GROWL, + COND_MSG_FEAR, + COND_MSG_FIRE_RAIN, + COND_MSG_FROZEN, + COND_MSG_SEASIDE, + COND_MSG_WATERFALL, + COND_MSG_RAIN, + COND_MSG_REFLECTION, + COND_MSG_LEAVES, + COND_MSG_ICE, + COND_MSG_BURN, + COND_MSG_DAY, + COND_MSG_NIGHT, + COND_MSG_COUNT, +}; + +extern const struct FollowerMsgInfoExtended gFollowerConditionalMessages[COND_MSG_COUNT]; #endif //GUARD_FOLLOWER_HELPER_H diff --git a/src/data/object_events/movement_type_func_tables.h b/src/data/object_events/movement_type_func_tables.h index c8cce4d103..c30f7daaf7 100755 --- a/src/data/object_events/movement_type_func_tables.h +++ b/src/data/object_events/movement_type_func_tables.h @@ -408,17 +408,17 @@ u8 (*const gMovementTypeFuncs_FollowPlayer[])(struct ObjectEvent *, struct Sprit }; bool8 (*const gFollowPlayerMovementFuncs[])(struct ObjectEvent *, struct Sprite *, u8, bool8(u8)) = { - FollowablePlayerMovement_Idle, - FollowablePlayerMovement_Idle, - FollowablePlayerMovement_Step, - FollowablePlayerMovement_GoSpeed1, - FollowablePlayerMovement_GoSpeed2, - FollowablePlayerMovement_Slide, - fph_IM_DIFFERENT, - FollowablePlayerMovement_GoSpeed4, - FollowablePlayerMovement_Jump, - FollowablePlayerMovement_Idle, - FollowablePlayerMovement_Idle, + [COPY_MOVE_NONE] = FollowablePlayerMovement_Idle, + [COPY_MOVE_FACE] = FollowablePlayerMovement_Idle, + [COPY_MOVE_WALK] = FollowablePlayerMovement_Step, + [COPY_MOVE_WALK_FAST] = FollowablePlayerMovement_GoSpeed1, + [COPY_MOVE_WALK_FASTER] = FollowablePlayerMovement_GoSpeed2, + [COPY_MOVE_SLIDE] = FollowablePlayerMovement_Slide, + [COPY_MOVE_JUMP_IN_PLACE] = fph_IM_DIFFERENT, + [COPY_MOVE_JUMP] = FollowablePlayerMovement_GoSpeed4, + [COPY_MOVE_JUMP2] = FollowablePlayerMovement_Step, + [COPY_MOVE_EMPTY_1] = FollowablePlayerMovement_Idle, + [COPY_MOVE_EMPTY_2] = FollowablePlayerMovement_Idle, }; u8 (*const gMovementTypeFuncs_CopyPlayerInGrass[])(struct ObjectEvent *, struct Sprite *) = { diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 818f950a8c..7eef22d7eb 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2089,7 +2089,7 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big } // Match scripted conditional messages // With 50% chance, try to match scripted conditional messages - for (i = (Random() & 1) ? 30 : 0, j = 1; i < 30; i++) { + for (i = (Random() & 1) ? COND_MSG_COUNT : 0, j = 1; i < COND_MSG_COUNT; i++) { const struct FollowerMsgInfoExtended *info = &gFollowerConditionalMessages[i]; if (info->stFlags == 1 && species != info->st.species) continue; @@ -5092,7 +5092,7 @@ bool8 MovementType_FollowPlayer_Shadow(struct ObjectEvent *objectEvent, struct S MoveObjectEventToMapCoords(objectEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); objectEvent->triggerGroundEffectsOnMove = FALSE; // Stop endless reflection spawning } - sprite->data[1] = 1; // Enter active state; if the player moves the follower will appear + sprite->sTypeFuncId = 1; // Enter active state; if the player moves the follower will appear return TRUE; } @@ -5102,7 +5102,7 @@ bool8 MovementType_FollowPlayer_Active(struct ObjectEvent *objectEvent, struct S return FALSE; } else if (!IsFollowerVisible()) { if (objectEvent->invisible) { // Return to shadowing state - sprite->data[1] = 0; + sprite->sTypeFuncId = 0; return FALSE; } // Animate entering pokeball @@ -5110,7 +5110,7 @@ bool8 MovementType_FollowPlayer_Active(struct ObjectEvent *objectEvent, struct S ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_ENTER_POKEBALL); objectEvent->singleMovementActive = 1; sprite->animCmdIndex = 0; // Needed for animCmdIndex weirdness - sprite->data[1] = 2; // movement action sets state to 0 + sprite->sTypeFuncId = 2; // movement action sets state to 0 return TRUE; } // TODO: Remove dependence on PlayerGetCopyableMovement @@ -5128,8 +5128,8 @@ bool8 MovementType_FollowPlayer_Moving(struct ObjectEvent *objectEvent, struct S if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { #endif objectEvent->singleMovementActive = 0; - if (sprite->data[1]) { // restore nonzero state - sprite->data[1] = 1; + if (sprite->sTypeFuncId) { // restore nonzero state + sprite->sTypeFuncId = 1; } } else if (objectEvent->movementActionId != MOVEMENT_ACTION_EXIT_POKEBALL) { UpdateFollowerTransformEffect(objectEvent, sprite); @@ -5142,7 +5142,7 @@ bool8 FollowablePlayerMovement_Idle(struct ObjectEvent *objectEvent, struct Spri u8 direction; if (!objectEvent->singleMovementActive) { // walk in place ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection)); - sprite->data[1] = 1; + sprite->sTypeFuncId = 1; objectEvent->singleMovementActive = 1; return TRUE; } else if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { // finish movement action @@ -5180,7 +5180,7 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EXIT_POKEBALL); objectEvent->singleMovementActive = 1; sprite->animCmdIndex = 0; // Needed because of weird animCmdIndex stuff - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } else if (x == targetX && y == targetY) { // don't move if already in the player's last position return FALSE; @@ -5188,14 +5188,19 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri // Follow player direction = GetDirectionToFace(x, y, targetX, targetY); - #ifdef MB_SIDEWAYS_STAIRS_RIGHT_SIDE // https://github.com/ghoulslash/pokeemerald/tree/sideways_stairs MoveCoords(direction, &x, &y); + #ifdef MB_SIDEWAYS_STAIRS_RIGHT_SIDE // https://github.com/ghoulslash/pokeemerald/tree/sideways_stairs GetCollisionAtCoords(objectEvent, x, y, direction); // Sets directionOverwrite for stairs - if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) { // Set follow speed according to player's speed + if (GetLedgeJumpDirection(x, y, direction) != DIR_NONE) // InitJumpRegular will set the proper speed + ObjectEventSetSingleMovement(objectEvent, sprite, GetJump2MovementAction(direction)); + else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) { // Set follow speed according to player's speed if (playerAction >= MOVEMENT_ACTION_RUN_DOWN_SLOW && playerAction <= MOVEMENT_ACTION_RUN_RIGHT_SLOW) objectEvent->movementActionId = GetWalkNormalMovementAction(direction); else objectEvent->movementActionId = GetWalkFastMovementAction(direction); + + } else if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) { + ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction)); } else { if (playerAction >= MOVEMENT_ACTION_WALK_SLOW_DOWN && playerAction <= MOVEMENT_ACTION_WALK_SLOW_RIGHT) ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction)); @@ -5204,13 +5209,18 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri } sprite->sActionFuncId = 0; #else - if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) // Set follow speed according to player's speed + if (GetLedgeJumpDirection(x, y, direction) != DIR_NONE) // InitJumpRegular will set the proper speed + ObjectEventSetSingleMovement(objectEvent, sprite, GetJump2MovementAction(direction)); + else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) // Set follow speed according to player's speed ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkFastMovementAction(direction)); + // If *player* jumps, make step take twice as long + else if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) + ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction)); else ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkNormalMovementAction(direction)); #endif objectEvent->singleMovementActive = 1; - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } @@ -5229,7 +5239,7 @@ bool8 FollowablePlayerMovement_GoSpeed1(struct ObjectEvent *objectEvent, struct ObjectEventSetSingleMovement(objectEvent, sprite, GetFaceDirectionMovementAction(direction)); } objectEvent->singleMovementActive = TRUE; - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } @@ -5248,7 +5258,7 @@ bool8 FollowablePlayerMovement_GoSpeed2(struct ObjectEvent *objectEvent, struct ObjectEventSetSingleMovement(objectEvent, sprite, GetFaceDirectionMovementAction(direction)); } objectEvent->singleMovementActive = TRUE; - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } @@ -5267,7 +5277,7 @@ bool8 FollowablePlayerMovement_Slide(struct ObjectEvent *objectEvent, struct Spr ObjectEventSetSingleMovement(objectEvent, sprite, GetFaceDirectionMovementAction(direction)); } objectEvent->singleMovementActive = TRUE; - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } @@ -5279,7 +5289,7 @@ bool8 fph_IM_DIFFERENT(struct ObjectEvent *objectEvent, struct Sprite *sprite, u direction = GetCopyDirection(gInitialMovementTypeFacingDirections[objectEvent->movementType], objectEvent->directionSequenceIndex, direction); ObjectEventSetSingleMovement(objectEvent, sprite, GetJumpInPlaceMovementAction(direction)); objectEvent->singleMovementActive = TRUE; - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } @@ -5298,7 +5308,7 @@ bool8 FollowablePlayerMovement_GoSpeed4(struct ObjectEvent *objectEvent, struct ObjectEventSetSingleMovement(objectEvent, sprite, GetFaceDirectionMovementAction(direction)); } objectEvent->singleMovementActive = TRUE; - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } @@ -5314,7 +5324,7 @@ bool8 FollowablePlayerMovement_Jump(struct ObjectEvent *objectEvent, struct Spri MoveCoordsInDirection(direction, &x, &y, 2, 2); ObjectEventSetSingleMovement(objectEvent, sprite, GetJump2MovementAction(direction)); objectEvent->singleMovementActive = TRUE; - sprite->data[1] = 2; + sprite->sTypeFuncId = 2; return TRUE; } @@ -6403,6 +6413,8 @@ enum { JUMP_TYPE_HIGH, JUMP_TYPE_LOW, JUMP_TYPE_NORMAL, + JUMP_TYPE_FAST, + JUMP_TYPE_FASTER, }; static void InitJump(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 direction, u8 distance, u8 type) @@ -6426,6 +6438,13 @@ static void InitJump(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 static void InitJumpRegular(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 direction, u8 distance, u8 type) { + // For follower only, match the anim duration of the player's movement, whether dashing, walking or jumping + if (objectEvent->localId == OBJ_EVENT_ID_FOLLOWER + && type == JUMP_TYPE_HIGH + && distance == JUMP_DISTANCE_FAR + // In some areas (i.e Meteor Falls), the player can jump as the follower jumps, so preserve type in this case + && PlayerGetCopyableMovement() != COPY_MOVE_JUMP2) + type = TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) ? JUMP_TYPE_FASTER : JUMP_TYPE_FAST; InitJump(objectEvent, sprite, direction, distance, type); SetStepAnimHandleAlternation(objectEvent, sprite, GetMoveDirectionAnimNum(objectEvent->facingDirection)); DoShadowFieldEffect(objectEvent); @@ -9655,7 +9674,16 @@ static u8 DoJumpSpriteMovement(struct Sprite *sprite) if (sprite->sDistance != JUMP_DISTANCE_IN_PLACE) Step1(sprite, sprite->sDirection); - sprite->y2 = GetJumpY(sprite->sTimer >> distanceToShift[sprite->sDistance], sprite->sJumpType); + if (sprite->sJumpType == JUMP_TYPE_FASTER) { + Step3(sprite, sprite->sDirection); + sprite->y2 = GetJumpY(sprite->sTimer >> distanceToShift[sprite->sDistance], JUMP_TYPE_NORMAL); + sprite->sTimer += 3; + } else if (sprite->sJumpType == JUMP_TYPE_FAST) { + Step1(sprite, sprite->sDirection); + sprite->y2 = GetJumpY(sprite->sTimer >> distanceToShift[sprite->sDistance], JUMP_TYPE_NORMAL); + sprite->sTimer++; + } else + sprite->y2 = GetJumpY(sprite->sTimer >> distanceToShift[sprite->sDistance], sprite->sJumpType); sprite->sTimer++; diff --git a/src/field_effect.c b/src/field_effect.c index bebf8e4051..a37f57d554 100644 --- a/src/field_effect.c +++ b/src/field_effect.c @@ -27,6 +27,7 @@ #include "trig.h" #include "util.h" #include "constants/field_effects.h" +#include "constants/event_objects.h" #include "constants/event_object_movement.h" #include "constants/metatile_behaviors.h" #include "constants/rgb.h" @@ -1421,7 +1422,7 @@ void FieldCB_FallWarpExit(void) Overworld_PlaySpecialMapMusic(); WarpFadeInScreen(); ScriptContext2_Enable(); - FreezeObjectEvents(); // TODO: How does this interact with follower pokemon? + FreezeObjectEvents(); CreateTask(Task_FallWarpFieldEffect, 0); gFieldCallback = NULL; } @@ -1553,6 +1554,15 @@ static bool8 FallWarpEffect_End(struct Task *task) #define tState data[0] #define tGoingUp data[1] +static void HideFollowerForFieldEffect(void) { + struct ObjectEvent *followerObj = GetFollowerObject(); + if (!followerObj || followerObj->invisible) + return; + ClearObjectEventMovement(followerObj, &gSprites[followerObj->spriteId]); + gSprites[followerObj->spriteId].animCmdIndex = 0; // Avoids a visual glitch with follower's animation frame + ObjectEventSetHeldMovement(followerObj, MOVEMENT_ACTION_ENTER_POKEBALL); +} + void StartEscalatorWarp(u8 metatileBehavior, u8 priority) { u8 taskId; @@ -1573,9 +1583,10 @@ static void Task_EscalatorWarpOut(u8 taskId) static bool8 EscalatorWarpOut_Init(struct Task *task) { - FreezeObjectEvents(); // TODO: Follower pokemon interaction + FreezeObjectEvents(); CameraObjectReset2(); StartEscalator(task->tGoingUp); + HideFollowerForFieldEffect(); // Hide follower before warping task->tState++; return FALSE; } @@ -1954,7 +1965,7 @@ static void Task_LavaridgeGymB1FWarp(u8 taskId) static bool8 LavaridgeGymB1FWarpEffect_Init(struct Task *task, struct ObjectEvent *objectEvent, struct Sprite *sprite) { - FreezeObjectEvents(); // TODO: Follower pokemon interaction + FreezeObjectEvents(); CameraObjectReset2(); SetCameraPanningCallback(NULL); gPlayerAvatar.preventStep = TRUE; @@ -1962,6 +1973,8 @@ static bool8 LavaridgeGymB1FWarpEffect_Init(struct Task *task, struct ObjectEven objectEvent->noShadow = TRUE; task->data[1] = 1; task->data[0]++; + if (objectEvent->localId == OBJ_EVENT_ID_PLAYER) // Hide follower before warping + HideFollowerForFieldEffect(); return TRUE; } @@ -2072,7 +2085,7 @@ static void Task_LavaridgeGymB1FWarpExit(u8 taskId) static bool8 LavaridgeGymB1FWarpExitEffect_Init(struct Task *task, struct ObjectEvent *objectEvent, struct Sprite *sprite) { CameraObjectReset2(); - FreezeObjectEvents(); // TODO: Follower pokemon interaction + FreezeObjectEvents(); gPlayerAvatar.preventStep = TRUE; objectEvent->invisible = TRUE; task->data[0]++; @@ -2148,12 +2161,14 @@ static void Task_LavaridgeGym1FWarp(u8 taskId) static bool8 LavaridgeGym1FWarpEffect_Init(struct Task *task, struct ObjectEvent *objectEvent, struct Sprite *sprite) { - FreezeObjectEvents(); // TODO: Follower pokemon interaction + FreezeObjectEvents(); CameraObjectReset2(); gPlayerAvatar.preventStep = TRUE; objectEvent->fixedPriority = 1; objectEvent->noShadow = TRUE; task->data[0]++; + if (objectEvent->localId == OBJ_EVENT_ID_PLAYER) // Hide follower before warping + HideFollowerForFieldEffect(); return FALSE; } @@ -2238,7 +2253,8 @@ void SpriteCB_AshPuff(struct Sprite *sprite) void StartEscapeRopeFieldEffect(void) { ScriptContext2_Enable(); - FreezeObjectEvents(); // TODO: Follower pokemon interaction + FreezeObjectEvents(); + HideFollowerForFieldEffect(); // hide follower before warping CreateTask(Task_EscapeRopeWarpOut, 80); } @@ -3001,15 +3017,10 @@ static void Task_SurfFieldEffect(u8 taskId) static void SurfFieldEffect_Init(struct Task *task) { - struct ObjectEvent *followerObject = GetFollowerObject(); ScriptContext2_Enable(); FreezeObjectEvents(); // Put follower into pokeball before using Surf - if (followerObject && !followerObject->invisible) { - ClearObjectEventMovement(followerObject, &gSprites[followerObject->spriteId]); - gSprites[followerObject->spriteId].animCmdIndex = 0; // Needed because of weird animCmdIndex stuff - ObjectEventSetHeldMovement(followerObject, MOVEMENT_ACTION_ENTER_POKEBALL); - } + HideFollowerForFieldEffect(); gPlayerAvatar.preventStep = TRUE; SetPlayerAvatarStateMask(PLAYER_AVATAR_FLAG_SURFING); PlayerGetDestCoords(&task->tDestX, &task->tDestY); diff --git a/src/follower_helper.c b/src/follower_helper.c index 9b0350e0c8..f146db6a74 100644 --- a/src/follower_helper.c +++ b/src/follower_helper.c @@ -73,206 +73,235 @@ static const u8 sCondMsg45[] = _("Your POKéMON is staring spellbound\nat the ni static const u8 sCondMsg46[] = _("Your POKéMON is happily gazing at\nthe beautiful, starry sky!"); static const u8* const sNightTexts[] = {sCondMsg45, sCondMsg46, NULL}; -// Note that the size of this array must also be correct in event_object_movement -const struct FollowerMsgInfoExtended gFollowerConditionalMessages[30] = { +// See the struct definition in follower_helper.h for more info +const struct FollowerMsgInfoExtended gFollowerConditionalMessages[COND_MSG_COUNT] = { + [COND_MSG_CELEBI] = { .text = (u8*)sCelebiTexts, .textSpread = 1, .script = EventScript_FollowerDance, .st = {.species = SPECIES_CELEBI}, - .stFlags = 1, // MATCH_SPECIES + .stFlags = ST_FLAGS_SPECIES, .emotion = FOLLOWER_EMOTION_NEUTRAL, }, + [COND_MSG_FIRE] = { .text = (u8*)sFireTexts, .st = {.types = {.type1 = TYPE_FIRE, .type2 = TYPE_FIRE}}, - .stFlags = 2, // MATCH_TYPES + .stFlags = ST_FLAGS_TYPE, .emotion = FOLLOWER_EMOTION_NEUTRAL, .textSpread = 1, }, + [COND_MSG_EVER_GRANDE] = { .text = sCondMsg06, .script = EventScript_FollowerFaceUp, .mm = {.map = {.mapNum = MAP_NUM(EVER_GRANDE_CITY), .mapGroup = MAP_GROUP(EVER_GRANDE_CITY)}}, - .mmFlags = 2, // MATCH_MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_HAPPY, }, + [COND_MSG_ROUTE_112] = { .text = sCondMsg07, .mm = {.map = {.mapNum = MAP_NUM(ROUTE112), .mapGroup = MAP_GROUP(ROUTE112)}}, - .mmFlags = 2, // MATCH_MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_HAPPY, }, + [COND_MSG_DAY_CARE] = { .text = sCondMsg08, .script = EventScript_FollowerNostalgia, .mm = {.map = {.mapNum = MAP_NUM(ROUTE117_POKEMON_DAY_CARE), .mapGroup = MAP_GROUP(ROUTE117_POKEMON_DAY_CARE)}}, - .mmFlags = 2, // MATCH_MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_NEUTRAL, }, + [COND_MSG_MART] = { .text = (u8*)sShopTexts, .textSpread = 1, .script = EventScript_FollowerLookAround, .wt = {.song = MUS_POKE_MART}, - .wtFlags = 2, // MATCH_SONG + .wtFlags = WT_FLAGS_MUSIC, .emotion = FOLLOWER_EMOTION_NEUTRAL, }, + [COND_MSG_VICTORY_ROAD] = { .text = sCondMsg11, .wt = {.song = MUS_VICTORY_ROAD}, - .wtFlags = 2, // MATCH_SONG + .wtFlags = WT_FLAGS_MUSIC, .emotion = FOLLOWER_EMOTION_PENSIVE, }, + [COND_MSG_BIKE_SHOP] = { .text = sCondMsg12, .mm = {.map = {.mapNum = MAP_NUM(MAUVILLE_CITY_BIKE_SHOP), .mapGroup = MAP_GROUP(MAUVILLE_CITY_BIKE_SHOP)}}, - .mmFlags = 2, // MATCH_MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_PENSIVE, }, + [COND_MSG_MACHINES] = { .text = (u8*)sMachineTexts, .mm = {.map = {.mapNum = MAP_NUM(NEW_MAUVILLE_INSIDE), .mapGroup = MAP_GROUP(NEW_MAUVILLE_INSIDE)}}, - .mmFlags = 2, // MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_MUSIC, .textSpread = 1, }, + [COND_MSG_SAILING] = { .text = (u8*)sBoatTexts, .script = EventScript_FollowerLookAround, .wt = {.song = MUS_SAILING}, - .wtFlags = 2, // MATCH_SONG + .wtFlags = WT_FLAGS_MUSIC, .emotion = FOLLOWER_EMOTION_MUSIC, .textSpread = 1, }, + [COND_MSG_PUDDLE] = { .text = sCondMsg18, .script = EventScript_FollowerHopping, .mm = {.mb = {.behavior1 = MB_SHALLOW_WATER, .behavior2 = MB_PUDDLE}}, - .mmFlags = 3, // MB + .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_SAND] = { .text = sCondMsg19, .mm = {.mb = {.behavior1 = MB_SAND, .behavior2 = MB_DEEP_SAND}}, - .mmFlags = 3, // MB + .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_GRASS] = { .text = sCondMsg20, .mm = {.mb = {.behavior1 = MB_TALL_GRASS, .behavior2 = MB_LONG_GRASS}}, - .mmFlags = 3, // MB + .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_FOOTPRINTS] = { .text = sCondMsg21, .mm = {.mb = {.behavior1 = MB_SAND, .behavior2 = MB_FOOTPRINTS}}, - .mmFlags = 3, // MB + .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_ELEVATOR] = { .text = (u8*)sElevatorTexts, .textSpread = 1, .mm = {.map = {.mapNum = MAP_NUM(LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR), .mapGroup = MAP_GROUP(LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR)}}, - .mmFlags = 2, // MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_SURPRISE, }, + [COND_MSG_ICE_ROOM] = { .text = (u8*)sColdTexts, .textSpread = 1, .mm = {.map = {.mapNum = MAP_NUM(SHOAL_CAVE_LOW_TIDE_ICE_ROOM), .mapGroup = MAP_GROUP(SHOAL_CAVE_LOW_TIDE_ICE_ROOM)}}, - .mmFlags = 2, // MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_SURPRISE, }, + [COND_MSG_ROUTE_117] = { .text = sCondMsg27, .mm = {.map = {.mapNum = MAP_NUM(ROUTE117), .mapGroup = MAP_GROUP(ROUTE117)}}, - .mmFlags = 2, // MAP + .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_SURPRISE, }, + [COND_MSG_DRAGON_GROWL] = { .text = sCondMsg28, .st = {.types = {.type1 = TYPE_DRAGON, .type2 = TYPE_DRAGON}}, - .stFlags = 2, // MATCH_TYPES + .stFlags = ST_FLAGS_TYPE, .mm = {.mapSec = {.mapSec = MAPSEC_SKY_PILLAR}}, - .mmFlags = 1, // MAP_SEC + .mmFlags = MM_FLAGS_MAPSEC, .emotion = FOLLOWER_EMOTION_UPSET, }, + [COND_MSG_FEAR] = { .text = (u8*)sFearTexts, .textSpread = 1, .st = {.types = {.type1 = TYPE_GHOST, .type2 = TYPE_NOT_TYPE1}}, - .stFlags = 2, // TYPE + .stFlags = ST_FLAGS_TYPE, .mm = {.mapSec = {.mapSec = MAPSEC_MT_PYRE}}, - .mmFlags = 1, // MAP_SEC + .mmFlags = MM_FLAGS_MAPSEC, .wt = {.song = MUS_MT_PYRE}, - .wtFlags = 2, // SONG + .wtFlags = WT_FLAGS_MUSIC, .emotion = FOLLOWER_EMOTION_UPSET, }, + [COND_MSG_FIRE_RAIN] = { .text = sCondMsg31, .st = {.types = {.type1 = TYPE_FIRE, .type2 = TYPE_FIRE}}, - .stFlags = 2, // TYPE + .stFlags = ST_FLAGS_TYPE, .wt = {.weather = {.weather1 = WEATHER_RAIN, .weather2 = WEATHER_RAIN_THUNDERSTORM}}, - .wtFlags = 1, // WEATHER + .wtFlags = WT_FLAGS_WEATHER, .emotion = FOLLOWER_EMOTION_UPSET, }, + [COND_MSG_FROZEN] = { .text = sCondMsg32, .st = {.status = STATUS1_FREEZE}, - .stFlags = 3, // STATUS + .stFlags = ST_FLAGS_STATUS, .emotion = FOLLOWER_EMOTION_UPSET, }, + [COND_MSG_SEASIDE] = { .text = (u8*)sSeaTexts, .textSpread = 1, .script = EventScript_FollowerFaceResult, .near = {.mb = {.behavior = MB_OCEAN_WATER, .distance = 5}}, - .nearFlags = 1, // mb + .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_WATERFALL] = { .text = sCondMsg36, .script = EventScript_FollowerFaceResult, .near = {.mb = {.behavior = MB_WATERFALL, .distance = 5}}, - .nearFlags = 1, // mb + .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_RAIN] = { .text = sCondMsg37, .st = {.types = {.type1 = TYPE_FIRE, .type2 = TYPE_NOT_TYPE1}}, - .stFlags = 2, // TYPE + .stFlags = ST_FLAGS_TYPE, .wt = {.weather = {.weather1 = WEATHER_RAIN, .weather2 = WEATHER_RAIN_THUNDERSTORM}}, - .wtFlags = 1, // WEATHER + .wtFlags = WT_FLAGS_WEATHER, .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_REFLECTION] = { .text = sCondMsg38, .script = EventScript_FollowerFaceResult, .near = {.mb = {.behavior = MB_POND_WATER, .distance = 1}}, - .nearFlags = 1, // mb + .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_PENSIVE, }, + [COND_MSG_LEAVES] = { .text = sCondMsg39, .mm = {.mapSec = {.mapSec = MAPSEC_PETALBURG_WOODS}}, - .mmFlags = 1, // MAP_SEC + .mmFlags = MM_FLAGS_MAPSEC, .emotion = FOLLOWER_EMOTION_PENSIVE, }, + [COND_MSG_ICE] = { .text = (u8*)sIceTexts, .textSpread = 1, .script = EventScript_FollowerFaceResult, .near = {.mb = {.behavior = MB_ICE, .distance = 1}}, - .nearFlags = 1, // mb + .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_PENSIVE, }, + [COND_MSG_BURN] = { .text = sCondMsg42, .st = {.status STATUS1_BURN}, - .stFlags 3, // STATUS + .stFlags = ST_FLAGS_STATUS, .emotion = FOLLOWER_EMOTION_SAD, }, + [COND_MSG_DAY] = { .text = (u8*)sDayTexts, .textSpread = 1, @@ -280,6 +309,7 @@ const struct FollowerMsgInfoExtended gFollowerConditionalMessages[30] = { .wtFlags = 3, // time .emotion = FOLLOWER_EMOTION_MUSIC, }, + [COND_MSG_NIGHT] = { .text = (u8*)sNightTexts, .textSpread = 1, diff --git a/src/rayquaza_scene.c b/src/rayquaza_scene.c index 5572ef8338..df03bfaebb 100644 --- a/src/rayquaza_scene.c +++ b/src/rayquaza_scene.c @@ -1,5 +1,6 @@ #include "global.h" #include "rayquaza_scene.h" +#include "event_object_movement.h" #include "sprite.h" #include "task.h" #include "graphics.h" @@ -15,6 +16,7 @@ #include "sound.h" #include "constants/songs.h" #include "constants/rgb.h" +#include "constants/event_objects.h" #include "random.h" /* @@ -1294,9 +1296,13 @@ void DoRayquazaScene(u8 animId, bool8 endEarly, void (*exitCallback)(void)) static void CB2_InitRayquazaScene(void) { + u32 i; SetVBlankHBlankCallbacksToNull(); ClearScheduledBgCopiesToVram(); ScanlineEffect_Stop(); + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + if (gObjectEvents[i].graphicsId == OBJ_EVENT_GFX_RAYQUAZA) + gObjectEvents[i].invisible = FALSE; FreeAllSpritePalettes(); ResetPaletteFade(); ResetSpriteData();