Added createfollowernpc macro to make a new follower from a gfx id (#7196)
This commit is contained in:
parent
b5956d8eba
commit
bfb0c2fa73
@ -2570,7 +2570,7 @@
|
||||
|
||||
@ Follower NPCs
|
||||
|
||||
@ Sets an NPC up to follow the player.
|
||||
@ Sets an existing NPC up to follow the player.
|
||||
@ Follower flags are defined in include/constants/follower_npc.h
|
||||
@ If you want to specify a battle partner without specifying a custom script, you can set the script parameter to 0.
|
||||
.macro setfollowernpc localId:req, flags:req, script=0, battlePartner=0
|
||||
@ -2598,6 +2598,19 @@
|
||||
.endif
|
||||
.endm
|
||||
|
||||
@ Creates a new follower NPC with the specified graphics id.
|
||||
.macro createfollowernpc gfx:req, flags:req, script=0, battlePartner=0
|
||||
.if FNPC_ENABLE_NPC_FOLLOWERS
|
||||
callnative ScriptCreateFollowerNPC
|
||||
.2byte \gfx
|
||||
.2byte \flags
|
||||
.4byte \script
|
||||
.2byte \battlePartner
|
||||
.else
|
||||
.error "createfollowernpc unavailable with FNPC_ENABLE_NPC_FOLLOWERS defined as FALSE"
|
||||
.endif
|
||||
.endm
|
||||
|
||||
@ Remove the follower NPC (assumes there will only ever be one).
|
||||
.macro destroyfollowernpc
|
||||
.if FNPC_ENABLE_NPC_FOLLOWERS
|
||||
|
||||
@ -23,9 +23,6 @@ enum FollowerNPCDataTypes
|
||||
FNPC_DATA_OBJ_ID,
|
||||
FNPC_DATA_CURRENT_SPRITE,
|
||||
FNPC_DATA_DELAYED_STATE,
|
||||
FNPC_DATA_MAP_ID,
|
||||
FNPC_DATA_MAP_NUM,
|
||||
FNPC_DATA_MAP_GROUP,
|
||||
FNPC_DATA_EVENT_FLAG,
|
||||
FNPC_DATA_GFX_ID,
|
||||
FNPC_DATA_FOLLOWER_FLAGS,
|
||||
|
||||
@ -204,11 +204,11 @@ struct Time
|
||||
/*0x04*/ s8 seconds;
|
||||
};
|
||||
|
||||
struct NPCFollowerMapData
|
||||
struct NPCFollowerPadding
|
||||
{
|
||||
u8 id;
|
||||
u8 number;
|
||||
u8 group;
|
||||
u8 padding1;
|
||||
u8 padding2;
|
||||
u8 padding3;
|
||||
};
|
||||
|
||||
struct NPCFollower
|
||||
@ -220,7 +220,7 @@ struct NPCFollower
|
||||
u8 objId;
|
||||
u8 currentSprite;
|
||||
u8 delayedState;
|
||||
struct NPCFollowerMapData map;
|
||||
struct NPCFollowerPadding padding;
|
||||
struct Coords16 log;
|
||||
const u8 *script;
|
||||
u16 flag;
|
||||
|
||||
@ -49,6 +49,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 void CreateFollowerNPC(u32 gfx, u32 followerFlags, const u8 *scriptPtr);
|
||||
static u32 GetFollowerNPCSprite(void);
|
||||
static bool32 IsStateMovement(u32 state);
|
||||
static u32 GetPlayerFaceToDoorDirection(struct ObjectEvent *player, struct ObjectEvent *follower);
|
||||
@ -92,15 +93,6 @@ void SetFollowerNPCData(enum FollowerNPCDataTypes type, u32 value)
|
||||
case FNPC_DATA_DELAYED_STATE:
|
||||
gSaveBlock3Ptr->NPCfollower.delayedState = value;
|
||||
break;
|
||||
case FNPC_DATA_MAP_ID:
|
||||
gSaveBlock3Ptr->NPCfollower.map.id = value;
|
||||
break;
|
||||
case FNPC_DATA_MAP_NUM:
|
||||
gSaveBlock3Ptr->NPCfollower.map.number = value;
|
||||
break;
|
||||
case FNPC_DATA_MAP_GROUP:
|
||||
gSaveBlock3Ptr->NPCfollower.map.group = value;
|
||||
break;
|
||||
case FNPC_DATA_EVENT_FLAG:
|
||||
gSaveBlock3Ptr->NPCfollower.flag = value;
|
||||
break;
|
||||
@ -161,12 +153,6 @@ u32 GetFollowerNPCData(enum FollowerNPCDataTypes type)
|
||||
return gSaveBlock3Ptr->NPCfollower.currentSprite;
|
||||
case FNPC_DATA_DELAYED_STATE:
|
||||
return gSaveBlock3Ptr->NPCfollower.delayedState;
|
||||
case FNPC_DATA_MAP_ID:
|
||||
return gSaveBlock3Ptr->NPCfollower.map.id;
|
||||
case FNPC_DATA_MAP_NUM:
|
||||
return gSaveBlock3Ptr->NPCfollower.map.number;
|
||||
case FNPC_DATA_MAP_GROUP:
|
||||
return gSaveBlock3Ptr->NPCfollower.map.group;
|
||||
case FNPC_DATA_EVENT_FLAG:
|
||||
return gSaveBlock3Ptr->NPCfollower.flag;
|
||||
case FNPC_DATA_GFX_ID:
|
||||
@ -187,77 +173,97 @@ void ClearFollowerNPCData(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, const u8 *ptr)
|
||||
static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, const u8 *scriptPtr)
|
||||
{
|
||||
struct ObjectEventTemplate npc;
|
||||
struct ObjectEvent *follower;
|
||||
u32 npcX, npcY;
|
||||
u32 faceDirection;
|
||||
u32 eventObjId;
|
||||
u32 eventObjId = GetObjectEventIdByLocalId(localId);
|
||||
u32 npcX = gObjectEvents[eventObjId].currentCoords.x;
|
||||
u32 npcY = gObjectEvents[eventObjId].currentCoords.y;
|
||||
const u8 *script;
|
||||
u32 flag;
|
||||
|
||||
// Only allow 1 follower NPC at a time.
|
||||
if (PlayerHasFollowerNPC())
|
||||
flag = GetObjectEventFlagIdByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
|
||||
// If the object does not have an event flag, don't create follower.
|
||||
if (flag == 0)
|
||||
return;
|
||||
|
||||
for (eventObjId = 0; eventObjId < OBJECT_EVENTS_COUNT; eventObjId++)
|
||||
if (setScript == TRUE)
|
||||
// Set the custom script.
|
||||
script = scriptPtr;
|
||||
else
|
||||
// Use the object's original script.
|
||||
script = GetObjectEventScriptPointerByObjectEventId(eventObjId);
|
||||
|
||||
RemoveObjectEvent(&gObjectEvents[eventObjId]);
|
||||
FlagSet(flag);
|
||||
|
||||
npc = *GetObjectEventTemplateByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
|
||||
npc.movementType = 0;
|
||||
npc.script = script;
|
||||
npc.localId = OBJ_EVENT_ID_NPC_FOLLOWER;
|
||||
SetFollowerNPCData(FNPC_DATA_OBJ_ID, TrySpawnObjectEventTemplate(&npc, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, npcX, npcY));
|
||||
follower = &gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)];
|
||||
MoveObjectEventToMapCoords(follower, npcX, npcY);
|
||||
ObjectEventTurn(follower, gObjectEvents[eventObjId].facingDirection);
|
||||
follower->movementType = MOVEMENT_TYPE_NONE;
|
||||
gSprites[follower->spriteId].callback = MovementType_None;
|
||||
|
||||
SetFollowerNPCData(FNPC_DATA_IN_PROGRESS, TRUE);
|
||||
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 the player is biking and the follower flags prohibit biking, force the player to dismount the bike.
|
||||
if (!CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE)
|
||||
&& TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE))
|
||||
SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_ON_FOOT);
|
||||
|
||||
// Set the follower sprite to match the player state.
|
||||
if (!TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_ON_FOOT))
|
||||
FollowerNPC_HandleSprite();
|
||||
}
|
||||
|
||||
static void CreateFollowerNPC(u32 gfx, u32 followerFlags, const u8 *scriptPtr)
|
||||
{
|
||||
struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||
struct ObjectEvent *follower;
|
||||
struct ObjectEventTemplate npc =
|
||||
{
|
||||
if (!gObjectEvents[eventObjId].active || gObjectEvents[eventObjId].isPlayer)
|
||||
continue;
|
||||
.localId = OBJ_EVENT_ID_NPC_FOLLOWER,
|
||||
.graphicsId = gfx,
|
||||
.x = player->currentCoords.x,
|
||||
.y = player->currentCoords.y,
|
||||
.elevation = PlayerGetElevation(),
|
||||
.script = scriptPtr
|
||||
};
|
||||
|
||||
if (gObjectEvents[eventObjId].localId == localId)
|
||||
{
|
||||
flag = GetObjectEventFlagIdByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
|
||||
// If the object does not have an event flag, don't create follower.
|
||||
if (flag == 0)
|
||||
return;
|
||||
SetFollowerNPCData(FNPC_DATA_OBJ_ID, TrySpawnObjectEventTemplate(&npc, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, player->currentCoords.x, player->currentCoords.y));
|
||||
follower = &gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)];
|
||||
follower->movementType = MOVEMENT_TYPE_NONE;
|
||||
gSprites[follower->spriteId].callback = MovementType_None;
|
||||
|
||||
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 (setScript == TRUE)
|
||||
// Set the custom script.
|
||||
script = ptr;
|
||||
else
|
||||
// Use the object's original script.
|
||||
script = GetObjectEventScriptPointerByObjectEventId(eventObjId);
|
||||
// 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)
|
||||
&& TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE))
|
||||
SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_ON_FOOT);
|
||||
|
||||
npcX = gObjectEvents[eventObjId].currentCoords.x;
|
||||
npcY = gObjectEvents[eventObjId].currentCoords.y;
|
||||
faceDirection = gObjectEvents[eventObjId].facingDirection;
|
||||
SetFollowerNPCData(FNPC_DATA_MAP_ID, gObjectEvents[eventObjId].localId);
|
||||
RemoveObjectEvent(&gObjectEvents[eventObjId]);
|
||||
FlagSet(flag);
|
||||
// Set the follower sprite to match the player state.
|
||||
if (!TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_ON_FOOT))
|
||||
FollowerNPC_HandleSprite();
|
||||
|
||||
npc = *GetObjectEventTemplateByLocalIdAndMap(GetFollowerNPCData(FNPC_DATA_MAP_ID), gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
|
||||
npc.movementType = 0;
|
||||
npc.script = script;
|
||||
npc.localId = OBJ_EVENT_ID_NPC_FOLLOWER;
|
||||
SetFollowerNPCData(FNPC_DATA_OBJ_ID, TrySpawnObjectEventTemplate(&npc, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, npcX, npcY));
|
||||
follower = &gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)];
|
||||
MoveObjectEventToMapCoords(follower, npcX, npcY);
|
||||
ObjectEventTurn(follower, faceDirection);
|
||||
follower->movementType = MOVEMENT_TYPE_NONE;
|
||||
gSprites[follower->spriteId].callback = MovementType_None;
|
||||
|
||||
SetFollowerNPCData(FNPC_DATA_IN_PROGRESS, TRUE);
|
||||
SetFollowerNPCData(FNPC_DATA_GFX_ID, follower->graphicsId);
|
||||
SetFollowerNPCData(FNPC_DATA_MAP_NUM, gSaveBlock1Ptr->location.mapNum);
|
||||
SetFollowerNPCData(FNPC_DATA_MAP_GROUP, gSaveBlock1Ptr->location.mapGroup);
|
||||
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 the player is biking and the follower flags prohibit biking, force the player to dismount the bike.
|
||||
if (!CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE)
|
||||
&& TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE))
|
||||
SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_ON_FOOT);
|
||||
|
||||
// Set the follower sprite to match the player state.
|
||||
if (!TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_ON_FOOT))
|
||||
FollowerNPC_HandleSprite();
|
||||
}
|
||||
}
|
||||
HideNPCFollower();
|
||||
SetFollowerNPCData(FNPC_DATA_WARP_END, FNPC_WARP_REAPPEAR);
|
||||
}
|
||||
|
||||
static u32 GetFollowerNPCSprite(void)
|
||||
@ -951,13 +957,13 @@ void SetFollowerNPCSprite(u32 spriteIndex)
|
||||
SetFollowerNPCData(FNPC_DATA_CURRENT_SPRITE, spriteIndex);
|
||||
oldSpriteId = follower->spriteId;
|
||||
newGraphicsId = GetFollowerNPCSprite();
|
||||
clone = *GetObjectEventTemplateByLocalIdAndMap(OBJ_EVENT_ID_NPC_FOLLOWER, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
|
||||
|
||||
backupFollower = *follower;
|
||||
backupFollower.graphicsId = newGraphicsId;
|
||||
DestroySprite(&gSprites[oldSpriteId]);
|
||||
RemoveObjectEvent(&gObjectEvents[GetFollowerNPCObjectId()]);
|
||||
|
||||
clone = *GetObjectEventTemplateByLocalIdAndMap(GetFollowerNPCData(FNPC_DATA_MAP_ID), GetFollowerNPCData(FNPC_DATA_MAP_NUM), GetFollowerNPCData(FNPC_DATA_MAP_GROUP));
|
||||
clone.graphicsId = newGraphicsId;
|
||||
clone.movementType = 0;
|
||||
clone.localId = OBJ_EVENT_ID_NPC_FOLLOWER;
|
||||
@ -1128,20 +1134,20 @@ void NPCFollow(struct ObjectEvent *npc, u32 state, bool32 ignoreScriptActive)
|
||||
|
||||
void CreateFollowerNPCAvatar(void)
|
||||
{
|
||||
struct ObjectEvent *player;
|
||||
struct ObjectEventTemplate clone;
|
||||
|
||||
if (!PlayerHasFollowerNPC())
|
||||
return;
|
||||
|
||||
player = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||
clone = *GetObjectEventTemplateByLocalIdAndMap(GetFollowerNPCData(FNPC_DATA_MAP_ID), GetFollowerNPCData(FNPC_DATA_MAP_NUM), GetFollowerNPCData(FNPC_DATA_MAP_GROUP));
|
||||
|
||||
clone.graphicsId = GetFollowerNPCSprite();
|
||||
clone.x = player->currentCoords.x - 7;
|
||||
clone.y = player->currentCoords.y - 7;
|
||||
clone.movementType = 0;
|
||||
clone.localId = OBJ_EVENT_ID_NPC_FOLLOWER;
|
||||
struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||
struct ObjectEventTemplate clone =
|
||||
{
|
||||
.localId = OBJ_EVENT_ID_NPC_FOLLOWER,
|
||||
.graphicsId = GetFollowerNPCSprite(),
|
||||
.x = player->currentCoords.x - 7,
|
||||
.y = player->currentCoords.y - 7,
|
||||
.elevation = player->currentElevation,
|
||||
.script = GetFollowerNPCScriptPointer(),
|
||||
.movementType = MOVEMENT_TYPE_FACE_DOWN
|
||||
};
|
||||
|
||||
switch (GetPlayerFacingDirection())
|
||||
{
|
||||
@ -1157,7 +1163,7 @@ void CreateFollowerNPCAvatar(void)
|
||||
}
|
||||
|
||||
// Create NPC and store ID.
|
||||
SetFollowerNPCData(FNPC_DATA_OBJ_ID, TrySpawnObjectEventTemplate(&clone, GetFollowerNPCData(FNPC_DATA_MAP_NUM), GetFollowerNPCData(FNPC_DATA_MAP_GROUP), clone.x, clone.y));
|
||||
SetFollowerNPCData(FNPC_DATA_OBJ_ID, TrySpawnObjectEventTemplate(&clone, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, clone.x, clone.y));
|
||||
if (GetFollowerNPCData(FNPC_DATA_OBJ_ID) == OBJECT_EVENTS_COUNT)
|
||||
{
|
||||
ClearFollowerNPCData();
|
||||
@ -1622,17 +1628,33 @@ void ScriptSetFollowerNPC(struct ScriptContext *ctx)
|
||||
u32 battlePartner = ScriptReadHalfword(ctx);
|
||||
const u8 *script = (const u8 *)ScriptReadWord(ctx);
|
||||
|
||||
if (PlayerHasFollowerNPC())
|
||||
return;
|
||||
|
||||
SetFollowerNPCData(FNPC_DATA_BATTLE_PARTNER, battlePartner);
|
||||
TurnNPCIntoFollower(localId, flags, setScript, script);
|
||||
}
|
||||
|
||||
void ScriptCreateFollowerNPC(struct ScriptContext *ctx)
|
||||
{
|
||||
u32 gfx = ScriptReadHalfword(ctx);
|
||||
u32 flags = ScriptReadHalfword(ctx);
|
||||
const u8 *script = (const u8 *)ScriptReadWord(ctx);
|
||||
u32 battlePartner = ScriptReadHalfword(ctx);
|
||||
|
||||
if (PlayerHasFollowerNPC())
|
||||
return;
|
||||
|
||||
SetFollowerNPCData(FNPC_DATA_BATTLE_PARTNER, battlePartner);
|
||||
CreateFollowerNPC(gfx, flags, script);
|
||||
}
|
||||
|
||||
void ScriptDestroyFollowerNPC(struct ScriptContext *ctx)
|
||||
{
|
||||
if (!PlayerHasFollowerNPC())
|
||||
return;
|
||||
|
||||
RemoveObjectEvent(&gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)]);
|
||||
FlagSet(GetFollowerNPCData(FNPC_DATA_EVENT_FLAG));
|
||||
ClearFollowerNPCData();
|
||||
UpdateFollowingPokemon();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user