Better system for Follower NPC running frames (#7222)

Co-authored-by: hedara90 <90hedara@gmail.com>
This commit is contained in:
Bivurnum 2025-06-27 03:12:35 -05:00 committed by GitHub
parent b60c897b4a
commit b1200117a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 30 additions and 16 deletions

View File

@ -27,8 +27,6 @@ The object ***MUST*** have an event flag or the NPC follower will not be created
## Follower Flags
These are required to tell the game what behavior you want the NPC follower to have. They are defined in [include/constants/follower_npc.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/upcoming/include/constants/follower_npc.h). The second list of flags is the same as the first, but with shortened names to make them easier to type when scripting. The first 7 flags in the list are individual behaviors, whereas the remaining three are bundles of flags. For example, if you use `FNPC_SURF` in `setfollowernpc`, the NPC follower will be able to Surf behind the player. If you use `FNPC_ALL_WATER` instead, the NPC follower will be able to Dive and go up Waterfalls in addition to being able to Surf. Feel free to add your own custom bundles of flags to the file to meet your needs.
If the NPC doesn't have unique running frames, you should not use the `FOLLOWER_NPC_FLAG_HAS_RUNNING_FRAMES`(`FNPC_RUNNING`) flag for them, as this will cause visual glitching. If the flag is not used, the follower will simply use their regular walking animation frames, just sped up. The only objects currently in the game that have unique running frames are the player and rival characters, so the running frames flag should be used for those.
To make sure the NPC follower uses the correct animation frames, you should add an entry to `gFollowerAlternateSprites` in [include/follower_npc_alternate_sprites.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/upcoming/include/follower_npc_alternate_sprites.h). Only do this if your object has distinct animation frames for different behaviors (running, biking, surfing, etc). Follow the templates for Rival May and Rival Brendan that already exist there.
## Follower Movements

View File

@ -2,7 +2,6 @@
#define GUARD_CONSTANTS_FOLLOWER_NPC_H
// NPC Follower Flags
#define FOLLOWER_NPC_FLAG_HAS_RUNNING_FRAMES 0x1 // Only use this if the NPC has running anim frames. Part of FOLLOWER_NPC_FLAG_ALL_LAND.
#define FOLLOWER_NPC_FLAG_CAN_BIKE 0x2 // Player is allowed to use the Bike. Part of FOLLOWER_NPC_FLAG_ALL_LAND.
#define FOLLOWER_NPC_FLAG_CAN_LEAVE_ROUTE 0x4 // Player is allowed to use Fly/Teleport/EscapeRope/etc. Part of FOLLOWER_NPC_FLAG_ALL_LAND.
#define FOLLOWER_NPC_FLAG_CAN_SURF 0x8 // Player is allowed to Surf. Part of FOLLOWER_NPC_FLAG_ALL_WATER.
@ -10,12 +9,11 @@
#define FOLLOWER_NPC_FLAG_CAN_DIVE 0x20 // Player is allowed to use Dive. Part of FOLLOWER_NPC_FLAG_ALL_WATER.
#define FOLLOWER_NPC_FLAG_CLEAR_ON_WHITE_OUT 0x80 // The NPC follower will be destroyed if the player whites out.
#define FOLLOWER_NPC_FLAG_ALL_LAND FOLLOWER_NPC_FLAG_HAS_RUNNING_FRAMES | FOLLOWER_NPC_FLAG_CAN_BIKE | FOLLOWER_NPC_FLAG_CAN_LEAVE_ROUTE
#define FOLLOWER_NPC_FLAG_ALL_LAND FOLLOWER_NPC_FLAG_CAN_BIKE | FOLLOWER_NPC_FLAG_CAN_LEAVE_ROUTE
#define FOLLOWER_NPC_FLAG_ALL_WATER FOLLOWER_NPC_FLAG_CAN_SURF | FOLLOWER_NPC_FLAG_CAN_WATERFALL | FOLLOWER_NPC_FLAG_CAN_DIVE
#define FOLLOWER_NPC_FLAG_ALL FOLLOWER_NPC_FLAG_ALL_LAND | FOLLOWER_NPC_FLAG_ALL_WATER | FOLLOWER_NPC_FLAG_CLEAR_ON_WHITE_OUT
// Shorter flag names for ease of use in setfollowernpc script macro
#define FNPC_RUNNING FOLLOWER_NPC_FLAG_HAS_RUNNING_FRAMES
#define FNPC_BIKE FOLLOWER_NPC_FLAG_CAN_BIKE
#define FNPC_LEAVE_ROUTE FOLLOWER_NPC_FLAG_CAN_LEAVE_ROUTE
#define FNPC_SURF FOLLOWER_NPC_FLAG_CAN_SURF
@ -27,8 +25,4 @@
#define FNPC_ALL_WATER FOLLOWER_NPC_FLAG_ALL_WATER
#define FNPC_ALL FOLLOWER_NPC_FLAG_ALL
#define FNPC_NONE 0
#define FNPC_ALWAYS 2
#endif // GUARD_CONSTANTS_FOLLOWER_NPC_H

View File

@ -12,6 +12,7 @@ struct FollowerNPCSpriteGraphics
u16 acroBikeId;
u16 surfId;
u16 underwaterId;
bool8 hasRunningFrames;
};
enum FollowerNPCDataTypes
@ -77,6 +78,11 @@ enum FollowerNPCHandleEscalatorFinishTaskStates{
MOVEMENT_FINISH
};
#define FOLLOWER_NPC_FLAG_HAS_RUNNING_FRAMES 0x1
#define FNPC_NONE 0
#define FNPC_ALWAYS 2
void SetFollowerNPCData(enum FollowerNPCDataTypes type, u32 value);
const u8 *GetFollowerNPCScriptPointer(void);
u32 GetFollowerNPCData(enum FollowerNPCDataTypes type);

View File

@ -19,6 +19,7 @@ static const struct FollowerNPCSpriteGraphics gFollowerNPCAlternateSprites[] =
.acroBikeId = OBJ_EVENT_GFX_RIVAL_MAY_ACRO_BIKE,
.surfId = OBJ_EVENT_GFX_RIVAL_MAY_SURFING,
.underwaterId = OBJ_EVENT_GFX_MAY_UNDERWATER,
.hasRunningFrames = TRUE,
},
{
.normalId = OBJ_EVENT_GFX_RIVAL_BRENDAN_NORMAL,
@ -26,6 +27,7 @@ static const struct FollowerNPCSpriteGraphics gFollowerNPCAlternateSprites[] =
.acroBikeId = OBJ_EVENT_GFX_RIVAL_BRENDAN_ACRO_BIKE,
.surfId = OBJ_EVENT_GFX_RIVAL_BRENDAN_SURFING,
.underwaterId = OBJ_EVENT_GFX_BRENDAN_UNDERWATER,
.hasRunningFrames = TRUE,
},
};

View File

@ -3437,11 +3437,6 @@ static void DebugAction_CreateFollowerNPC(u8 taskId)
DestroyFollowerNPC();
SetFollowerNPCData(FNPC_DATA_BATTLE_PARTNER, PARTNER_STEVEN);
CreateFollowerNPC(gfx, FNPC_ALL, Debug_Follower_NPC_Event_Script);
if (gfx != OBJ_EVENT_GFX_RIVAL_BRENDAN_NORMAL && gfx != OBJ_EVENT_GFX_RIVAL_MAY_NORMAL)
{
u32 flags = GetFollowerNPCData(FNPC_DATA_FOLLOWER_FLAGS);
SetFollowerNPCData(FNPC_DATA_FOLLOWER_FLAGS, (flags &= ~FNPC_RUNNING));
}
UnlockPlayerFieldControls();
}

View File

@ -50,6 +50,7 @@ static void SetFollowerNPCScriptPointer(const u8 *script);
static void PlayerLogCoordinates(struct ObjectEvent *player);
static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, const u8 *script);
static u32 GetFollowerNPCSprite(void);
static bool32 FollowerNPCHasRunningFrames(void);
static bool32 IsStateMovement(u32 state);
static u32 GetPlayerFaceToDoorDirection(struct ObjectEvent *player, struct ObjectEvent *follower);
static u32 ReturnFollowerNPCDelayedState(u32 direction);
@ -212,9 +213,12 @@ static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, c
SetFollowerNPCData(FNPC_DATA_GFX_ID, follower->graphicsId);
SetFollowerNPCScriptPointer(script);
SetFollowerNPCData(FNPC_DATA_EVENT_FLAG, flag);
SetFollowerNPCData(FNPC_DATA_FOLLOWER_FLAGS, followerFlags);
SetFollowerNPCData(FNPC_DATA_SURF_BLOB, FNPC_SURF_BLOB_NONE);
SetFollowerNPCData(FNPC_DATA_COME_OUT_DOOR, FNPC_DOOR_NONE);
if (FollowerNPCHasRunningFrames())
followerFlags |= FOLLOWER_NPC_FLAG_HAS_RUNNING_FRAMES;
SetFollowerNPCData(FNPC_DATA_FOLLOWER_FLAGS, followerFlags);
// If the player is biking and the follower flags prohibit biking, force the player to dismount the bike.
if (!CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE)
@ -257,6 +261,18 @@ static u32 GetFollowerNPCSprite(void)
return GetFollowerNPCData(FNPC_DATA_GFX_ID);
}
static bool32 FollowerNPCHasRunningFrames(void)
{
for (u32 i = 0; i < NELEMS(gFollowerNPCAlternateSprites); i++)
{
if (gFollowerNPCAlternateSprites[i].normalId == GetFollowerNPCData(FNPC_DATA_GFX_ID)
&& gFollowerNPCAlternateSprites[i].hasRunningFrames == TRUE)
return TRUE;
}
return FALSE;
}
static bool32 IsStateMovement(u32 state)
{
switch (state)
@ -696,10 +712,13 @@ void CreateFollowerNPC(u32 gfx, u32 followerFlags, const u8 *scriptPtr)
SetFollowerNPCData(FNPC_DATA_IN_PROGRESS, TRUE);
SetFollowerNPCData(FNPC_DATA_GFX_ID, follower->graphicsId);
SetFollowerNPCData(FNPC_DATA_FOLLOWER_FLAGS, followerFlags);
SetFollowerNPCData(FNPC_DATA_SURF_BLOB, FNPC_SURF_BLOB_NONE);
SetFollowerNPCData(FNPC_DATA_COME_OUT_DOOR, FNPC_DOOR_NONE);
SetFollowerNPCScriptPointer(scriptPtr);
if (FollowerNPCHasRunningFrames())
followerFlags |= FOLLOWER_NPC_FLAG_HAS_RUNNING_FRAMES;
SetFollowerNPCData(FNPC_DATA_FOLLOWER_FLAGS, followerFlags);
// If the player is biking and the follower flags prohibit biking, force the player to dismount the bike.
if (!CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE)