Merge branch 'followers' into lighting

This commit is contained in:
Ariel A 2025-01-01 00:37:09 -05:00
commit a515cb3c6b
16 changed files with 230 additions and 58 deletions

View File

@ -75,6 +75,20 @@ A: Configuration for the follower system is mostly in [event_objects.h](include/
// Followers will emerge from the pokeball they are stored in,
// instead of a normal pokeball
#define OW_MON_POKEBALLS TRUE
// New/old handling for followers during scripts;
// TRUE: Script collisions hide follower, FLAG_SAFE_FOLLOWER_MOVEMENT on by default
// (scripted player movement moves follower too!)
// FALSE: Script collisions unhandled, FLAG_SAFE_FOLLOWER_MOVEMENT off by default
#define OW_MON_SCRIPT_MOVEMENT TRUE
// If set, the only pokemon allowed to follow you
// will be those matching species, met location,
// and/or met level;
// These accept vars, too: VAR_TEMP_1, etc
#define OW_MON_ALLOWED_SPECIES (0)
#define OW_MON_ALLOWED_MET_LVL (0)
#define OW_MON_ALLOWED_MET_LOC (0)
```
### `(lighting)` Q: How do I mark certain colors in a palette as light-blended?

View File

@ -1946,3 +1946,11 @@
setvar VAR_0x8006, \item
special CreateEnemyEventMon
.endm
@ hide any follower pokemon if present,
@ putting them into their pokeball;
@ by default waits for their movement to finish
.macro hidefollower wait=1
callfunc ScrFunc_hidefollower
.2byte \wait
.endm

View File

@ -376,6 +376,24 @@ EventScript_EndSurface::
releaseall
end
EventScript_DigCommon:
callfunc ScrFunc_IsFollowerFieldMoveUser
.2byte VAR_0x8004
setfieldeffectargument 3, VAR_0x8004 @ skip pose if true
dofieldeffect FLDEFF_USE_DIG
waitstate
EventScript_DigSealedChamber:: @ fallthrough
setflag FLAG_SAFE_FOLLOWER_MOVEMENT
call_if_eq VAR_0x8004, TRUE, EventScript_FollowerFieldMove
callnative DoBrailleDigEffect
releaseall
end
@ Use Dig from party menu
EventScript_UseDig::
lockall
goto EventScript_DigCommon
Text_CantDive:
.string "The sea is deep here. A POKéMON\n"
.string "may be able to go underwater.$"

View File

@ -33,6 +33,7 @@ EventScript_PkmnCenterNurse_IllTakeYourPkmn2::
return
EventScript_PkmnCenterNurse_TakeAndHealPkmn::
hidefollower 0
applymovement VAR_0x800B, Movement_PkmnCenterNurse_Turn
waitmovement 0
dofieldeffect FLDEFF_POKECENTER_HEAL

View File

@ -8,11 +8,13 @@ Std_MsgboxNPC:
return
Std_MsgboxSign:
setflag FLAG_SAFE_FOLLOWER_MOVEMENT
lockall
message NULL
waitmessage
waitbuttonpress
releaseall
clearflag FLAG_SAFE_FOLLOWER_MOVEMENT
return
Std_MsgboxDefault:

View File

@ -290,6 +290,33 @@
// instead of a normal pokeball
#define OW_MON_POKEBALLS TRUE
// New/old handling for followers during scripts;
// TRUE: Script collisions hide follower, FLAG_SAFE_FOLLOWER_MOVEMENT on by default
// (scripted player movement moves follower too!)
// FALSE: Script collisions unhandled, FLAG_SAFE_FOLLOWER_MOVEMENT off by default
#define OW_MON_SCRIPT_MOVEMENT TRUE
// If set, the only pokemon allowed to follow you
// will be those matching species, met location,
// and/or met level;
// These accept vars, too: VAR_TEMP_1, etc
#define OW_MON_ALLOWED_SPECIES (0)
#define OW_MON_ALLOWED_MET_LVL (0)
#define OW_MON_ALLOWED_MET_LOC (0)
// Examples:
// Yellow Pikachu:
// #define OW_MON_ALLOWED_SPECIES (SPECIES_PIKACHU)
// #define OW_MON_ALLOWED_MET_LVL (0)
// #define OW_MON_ALLOWED_MET_LOC (MAPSEC_PALLET_TOWN)
// Hoenn Starter:
// #define OW_MON_ALLOWED_SPECIES (0)
// #define OW_MON_ALLOWED_MET_LVL (5)
// #define OW_MON_ALLOWED_MET_LOC (MAPSEC_ROUTE_101)
// Species set in VAR_XXXX:
// #define OW_MON_ALLOWED_SPECIES (VAR_XXXX)
// #define OW_MON_ALLOWED_MET_LVL (0)
// #define OW_MON_ALLOWED_MET_LOC (0)
#define SHADOW_SIZE_S 0
#define SHADOW_SIZE_M 1
#define SHADOW_SIZE_L 2

View File

@ -1643,7 +1643,9 @@
#define FLAG_ENABLE_MULTI_CORRIDOR_DOOR (SPECIAL_FLAGS_START + 0x2)
#define FLAG_SPECIAL_FLAG_UNUSED_0x4003 (SPECIAL_FLAGS_START + 0x3) // Unused Flag
#define FLAG_STORING_ITEMS_IN_PYRAMID_BAG (SPECIAL_FLAGS_START + 0x4)
#define FLAG_SAFE_FOLLOWER_MOVEMENT (SPECIAL_FLAGS_START + 0x5) // When set, applymovement does not put the follower inside a pokeball
// When set, `applymovement` does not hide follower pokemon;
// Also, scripted movements on the player will move follower(s), too
#define FLAG_SAFE_FOLLOWER_MOVEMENT (SPECIAL_FLAGS_START + 0x5)
// FLAG_SPECIAL_FLAG_0x4005 - 0x407F also exist and are unused
#define SPECIAL_FLAGS_END (SPECIAL_FLAGS_START + 0x7F)
#define NUM_SPECIAL_FLAGS (SPECIAL_FLAGS_END - SPECIAL_FLAGS_START + 1)

View File

@ -164,6 +164,7 @@ u8 GetWalkInPlaceFastMovementAction(u32);
u8 GetWalkInPlaceNormalMovementAction(u32);
u8 GetWalkInPlaceSlowMovementAction(u32);
u8 GetCollisionAtCoords(struct ObjectEvent *, s16 x, s16 y, u32 dir);
u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, bool32 addCoords);
void MoveCoords(u8 direction, s16 *x, s16 *y);
bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *);
u8 ObjectEventClearHeldMovementIfFinished(struct ObjectEvent *);

View File

@ -404,6 +404,7 @@ extern const u8 EventScript_FailSweetScent[];
extern const u8 EventScript_UseFlash[];
extern const u8 EventScript_UseCut[];
extern const u8 EventScript_UseRockSmash[];
extern const u8 EventScript_UseDig[];
//player pc
extern const u8 LittlerootTown_BrendansHouse_2F_EventScript_TurnOffPlayerPC[];

View File

@ -25,7 +25,8 @@ git push build --force-with-lease
if [[ $retVal -eq 0 ]]; then
commit_msg=$(git log -1 --pretty=%B)
if [[ "$commit_msg" == "$temp_commit_msg" ]]; then
git reset "$old_head" &>/dev/null
# Keep i(N)tent to add
git reset --mixed -N "$old_head" &>/dev/null
fi
fi
set -x

View File

@ -87,7 +87,6 @@ void DoBrailleDigEffect(void)
DrawWholeMapView();
PlaySE(SE_BANG);
FlagSet(FLAG_SYS_BRAILLE_DIG);
UnlockPlayerFieldControls();
}
bool8 CheckRelicanthWailord(void)

View File

@ -362,7 +362,7 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = {
[MOVEMENT_TYPE_COPY_PLAYER_CLOCKWISE_IN_GRASS] = TRUE,
};
const u8 gInitialMovementTypeFacingDirections[] = {
const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = {
[MOVEMENT_TYPE_NONE] = DIR_SOUTH,
[MOVEMENT_TYPE_LOOK_AROUND] = DIR_SOUTH,
[MOVEMENT_TYPE_WANDER_AROUND] = DIR_SOUTH,
@ -444,6 +444,7 @@ const u8 gInitialMovementTypeFacingDirections[] = {
[MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_UP] = DIR_NORTH,
[MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = DIR_WEST,
[MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = DIR_EAST,
[MOVEMENT_TYPE_FOLLOW_PLAYER] = DIR_SOUTH,
};
#define OBJ_EVENT_PAL_TAG_BRENDAN 0x1100
@ -1812,8 +1813,16 @@ u8 CreateVirtualObject(u8 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevatio
struct Pokemon *GetFirstLiveMon(void) { // Return address of first conscious party mon or NULL
u32 i;
for (i = 0; i < PARTY_SIZE; i++) {
struct Pokemon *mon = &gPlayerParty[i];
if ((OW_MON_ALLOWED_SPECIES && GetMonData(mon, MON_DATA_SPECIES_OR_EGG) != VarGet(OW_MON_ALLOWED_SPECIES))
|| (OW_MON_ALLOWED_MET_LVL && GetMonData(mon, MON_DATA_MET_LEVEL) != VarGet(OW_MON_ALLOWED_MET_LVL))
|| (OW_MON_ALLOWED_MET_LOC && GetMonData(mon, MON_DATA_MET_LOCATION) != VarGet(OW_MON_ALLOWED_MET_LOC))
) {
continue;
}
if (gPlayerParty[i].hp > 0 && !(gPlayerParty[i].box.isEgg || gPlayerParty[i].box.isBadEgg))
return &gPlayerParty[i];
return &gPlayerParty[i];
}
return NULL;
}
@ -5344,6 +5353,7 @@ bool8 MovementType_FollowPlayer_Moving(struct ObjectEvent *objectEvent, struct S
if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) {
#endif
objectEvent->singleMovementActive = 0;
objectEvent->facingDirectionLocked = FALSE;
if (sprite->sTypeFuncId) // restore nonzero state
sprite->sTypeFuncId = 1;
} else if (objectEvent->movementActionId < MOVEMENT_ACTION_EXIT_POKEBALL) {
@ -5378,9 +5388,7 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri
s16 y;
s16 targetX;
s16 targetY;
#ifdef MB_SIDEWAYS_STAIRS_RIGHT_SIDE
u32 playerAction = gObjectEvents[gPlayerAvatar.objectEventId].movementActionId;
#endif
targetX = gObjectEvents[gPlayerAvatar.objectEventId].previousCoords.x;
targetY = gObjectEvents[gPlayerAvatar.objectEventId].previousCoords.y;
@ -5396,8 +5404,8 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri
if (objectEvent->invisible) {
// Animate exiting pokeball
// Player is jumping, but follower is invisible
if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) {
// don't emerge if player is jumping or moving via script
if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2 || ArePlayerFieldControlsLocked()) {
sprite->sTypeFuncId = 0; // return to shadowing state
return FALSE;
}
@ -5415,46 +5423,37 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri
// Follow player
direction = GetDirectionToFace(x, y, targetX, targetY);
// During a script, if player sidesteps or backsteps,
// mirror player's direction instead
if (ArePlayerFieldControlsLocked()
&& gObjectEvents[gPlayerAvatar.objectEventId].facingDirection != gObjectEvents[gPlayerAvatar.objectEventId].movementDirection
) {
direction = gObjectEvents[gPlayerAvatar.objectEventId].movementDirection;
objectEvent->facingDirectionLocked = TRUE;
}
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 (GetLedgeJumpDirection(x, y, direction) != DIR_NONE) {
#endif
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)
else if (playerAction >= MOVEMENT_ACTION_WALK_SLOW_DOWN && playerAction <= MOVEMENT_ACTION_WALK_SLOW_RIGHT) {
if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) // on sideways stairs
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));
} else {
objectEvent->movementActionId = GetWalkNormalMovementAction(direction);
if (OW_MON_BOBBING == TRUE)
sprite->y2 = -1;
}
}
sprite->sActionFuncId = 0;
#else
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));
} else if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) {
// If *player* jumps, make step take twice as long
ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction));
} else if (gSprites[gPlayerAvatar.spriteId].data[4] == MOVE_SPEED_FAST_1) {
objectEvent->movementActionId = GetWalkFastMovementAction(direction);
} else {
ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkNormalMovementAction(direction));
objectEvent->movementActionId = GetWalkNormalMovementAction(direction);
if (OW_MON_BOBBING == TRUE)
sprite->y2 = -1;
}
#endif
sprite->sActionFuncId = 0;
objectEvent->singleMovementActive = 1;
sprite->sTypeFuncId = 2;
return TRUE;
@ -5979,13 +5978,17 @@ static bool8 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent,
return FALSE;
}
static bool8 DoesObjectCollideWithObjectAt(struct ObjectEvent *objectEvent, s16 x, s16 y)
{
u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, bool32 addCoords) {
u8 i;
struct ObjectEvent *curObject;
if (objectEvent->localId == OBJ_EVENT_ID_FOLLOWER)
return FALSE; // follower cannot collide with other objects, but they can collide with it
return OBJECT_EVENTS_COUNT; // follower cannot collide with other objects, but they can collide with it
if (addCoords) {
x += objectEvent->currentCoords.x;
y += objectEvent->currentCoords.y;
}
for (i = 0; i < OBJECT_EVENTS_COUNT; i++)
{
@ -5995,11 +5998,16 @@ static bool8 DoesObjectCollideWithObjectAt(struct ObjectEvent *objectEvent, s16
if ((curObject->currentCoords.x == x && curObject->currentCoords.y == y) || (curObject->previousCoords.x == x && curObject->previousCoords.y == y))
{
if (AreElevationsCompatible(objectEvent->currentElevation, curObject->currentElevation))
return TRUE;
return i;
}
}
}
return FALSE;
return OBJECT_EVENTS_COUNT;
}
static bool8 DoesObjectCollideWithObjectAt(struct ObjectEvent *objectEvent, s16 x, s16 y)
{
return (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) < OBJECT_EVENTS_COUNT);
}
bool8 IsBerryTreeSparkling(u8 localId, u8 mapNum, u8 mapGroup)
@ -6128,6 +6136,16 @@ bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent)
return FALSE;
}
static const u8 sActionIdToCopyableMovement[] = {
[MOVEMENT_ACTION_FACE_DOWN ... MOVEMENT_ACTION_FACE_RIGHT] = COPY_MOVE_FACE,
[MOVEMENT_ACTION_WALK_SLOW_DOWN ... MOVEMENT_ACTION_WALK_NORMAL_RIGHT] = COPY_MOVE_WALK,
[MOVEMENT_ACTION_JUMP_2_DOWN ... MOVEMENT_ACTION_JUMP_2_RIGHT] = COPY_MOVE_JUMP2,
[MOVEMENT_ACTION_WALK_FAST_DOWN ... MOVEMENT_ACTION_WALK_FAST_RIGHT] = COPY_MOVE_WALK,
[MOVEMENT_ACTION_RIDE_WATER_CURRENT_DOWN ... MOVEMENT_ACTION_PLAYER_RUN_RIGHT] = COPY_MOVE_WALK,
[MOVEMENT_ACTION_NONE] = COPY_MOVE_NONE,
};
bool8 ObjectEventSetHeldMovement(struct ObjectEvent *objectEvent, u8 movementActionId)
{
if (ObjectEventIsMovementOverridden(objectEvent))
@ -6138,6 +6156,16 @@ bool8 ObjectEventSetHeldMovement(struct ObjectEvent *objectEvent, u8 movementAct
objectEvent->heldMovementActive = TRUE;
objectEvent->heldMovementFinished = FALSE;
gSprites[objectEvent->spriteId].sActionFuncId = 0;
// When player is moved via script, set copyable movement
// for any followers via a lookup table
if (ArePlayerFieldControlsLocked() &&
objectEvent->isPlayer &&
FlagGet(FLAG_SAFE_FOLLOWER_MOVEMENT))
{
objectEvent->extra.playerCopyableMovement = sActionIdToCopyableMovement[objectEvent->movementActionId];
}
return FALSE;
}
@ -6160,6 +6188,15 @@ void ObjectEventClearHeldMovement(struct ObjectEvent *objectEvent)
objectEvent->heldMovementFinished = FALSE;
gSprites[objectEvent->spriteId].sTypeFuncId = 0;
gSprites[objectEvent->spriteId].sActionFuncId = 0;
// When player is moved via script, set copyable movement
// for any followers via a lookup table
if (ArePlayerFieldControlsLocked() &&
objectEvent->isPlayer &&
FlagGet(FLAG_SAFE_FOLLOWER_MOVEMENT))
{
objectEvent->extra.playerCopyableMovement = sActionIdToCopyableMovement[objectEvent->movementActionId];
}
}
u8 ObjectEventCheckHeldMovementStatus(struct ObjectEvent *objectEvent)
@ -9446,10 +9483,10 @@ static void DoTracksGroundEffect_SlitherTracks(struct ObjectEvent *objEvent, str
// each byte in that row is for the next direction of the bike in the order
// of down, up, left, right.
static const u8 slitherTracks_Transitions[4][4] = {
1, 2, 7, 8,
1, 2, 6, 5,
5, 8, 3, 4,
6, 7, 3, 4,
{1, 2, 7, 8},
{1, 2, 6, 5},
{5, 8, 3, 4},
{6, 7, 3, 4},
};
if (objEvent->currentCoords.x != objEvent->previousCoords.x || objEvent->currentCoords.y != objEvent->previousCoords.y)

View File

@ -1,11 +1,13 @@
#include "global.h"
#include "braille_puzzles.h"
#include "event_scripts.h"
#include "field_effect.h"
#include "field_player_avatar.h"
#include "fldeff.h"
#include "item_use.h"
#include "overworld.h"
#include "party_menu.h"
#include "script.h"
#include "sprite.h"
#include "constants/field_effects.h"
@ -31,8 +33,8 @@ bool8 SetUpFieldMove_Dig(void)
static void FieldCallback_Dig(void)
{
Overworld_ResetStateAfterDigEscRope();
FieldEffectStart(FLDEFF_USE_DIG);
gFieldEffectArguments[0] = GetCursorSelectionMonId();
ScriptContext_SetupScript(EventScript_UseDig);
}
bool8 FldEff_UseDig(void)
@ -53,7 +55,8 @@ static void StartDigFieldEffect(void)
FieldEffectActiveListRemove(FLDEFF_USE_DIG);
if (ShouldDoBrailleDigEffect())
{
DoBrailleDigEffect();
// EventScript_DigSealedChamber handles DoBrailleDigEffect call
ScriptContext_Enable();
}
else
{

View File

@ -1006,6 +1006,20 @@ bool8 ScrCmd_fadeinbgm(struct ScriptContext *ctx)
return FALSE;
}
struct ObjectEvent * ScriptHideFollower(void) {
struct ObjectEvent *obj = GetFollowerObject();
if (obj == NULL || obj->invisible)
return NULL;
ClearObjectEventMovement(obj, &gSprites[obj->spriteId]);
gSprites[obj->spriteId].animCmdIndex = 0; // Reset start frame of animation
// Note: ScriptMovement_ returns TRUE on error
if (ScriptMovement_StartObjectMovementScript(obj->localId, obj->mapGroup, obj->mapNum, EnterPokeballMovement))
return NULL;
return obj;
}
bool8 ScrCmd_applymovement(struct ScriptContext *ctx)
{
u16 localId = VarGet(ScriptReadHalfword(ctx));
@ -1019,17 +1033,11 @@ bool8 ScrCmd_applymovement(struct ScriptContext *ctx)
}
ScriptMovement_StartObjectMovementScript(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, movementScript);
sMovingNpcId = localId;
objEvent = GetFollowerObject();
// Force follower into pokeball
if (localId != OBJ_EVENT_ID_FOLLOWER
&& !FlagGet(FLAG_SAFE_FOLLOWER_MOVEMENT)
&& (movementScript < Common_Movement_FollowerSafeStart || movementScript > Common_Movement_FollowerSafeEnd)
&& (objEvent = GetFollowerObject())
&& !objEvent->invisible)
if (localId != OBJ_EVENT_ID_FOLLOWER &&
!FlagGet(FLAG_SAFE_FOLLOWER_MOVEMENT)
&& (movementScript < Common_Movement_FollowerSafeStart || movementScript > Common_Movement_FollowerSafeEnd))
{
ClearObjectEventMovement(objEvent, &gSprites[objEvent->spriteId]);
gSprites[objEvent->spriteId].animCmdIndex = 0; // Reset start frame of animation
ScriptMovement_StartObjectMovementScript(OBJ_EVENT_ID_FOLLOWER, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, EnterPokeballMovement);
ScriptHideFollower();
}
return FALSE;
}
@ -1249,8 +1257,11 @@ bool8 ScrCmd_lockall(struct ScriptContext *ctx)
}
else
{
struct ObjectEvent *followerObj = GetFollowerObject();
FreezeObjects_WaitForPlayer();
SetupNativeScript(ctx, IsFreezePlayerFinished);
if (FlagGet(FLAG_SAFE_FOLLOWER_MOVEMENT) && followerObj) // Unfreeze follower object (conditionally)
UnfreezeObjectEvent(followerObj);
return TRUE;
}
}
@ -2378,3 +2389,23 @@ bool8 ScrCmd_warpwhitefade(struct ScriptContext *ctx)
ResetInitialPlayerAvatarState();
return TRUE;
}
bool8 ScrFunc_hidefollower(struct ScriptContext *ctx) {
bool16 wait = VarGet(ScriptReadHalfword(ctx));
struct ObjectEvent *obj;
if ((obj = ScriptHideFollower()) != NULL && wait) {
sMovingNpcId = obj->localId;
sMovingNpcMapGroup = obj->mapGroup;
sMovingNpcMapNum = obj->mapNum;
SetupNativeScript(ctx, WaitForMovementFinish);
}
// Just in case, prevent `applymovement`
// from hiding the follower again
if (obj)
FlagSet(FLAG_SAFE_FOLLOWER_MOVEMENT);
// execute next script command with no delay
return TRUE;
}

View File

@ -4,6 +4,7 @@
#include "mystery_gift.h"
#include "util.h"
#include "constants/event_objects.h"
#include "constants/flags.h"
#include "constants/map_scripts.h"
#define RAM_SCRIPT_MAGIC 51
@ -243,6 +244,8 @@ void ScriptContext_SetupScript(const u8 *ptr)
InitScriptContext(&sGlobalScriptContext, gScriptCmdTable, gScriptCmdTableEnd);
SetupBytecodeScript(&sGlobalScriptContext, ptr);
LockPlayerFieldControls();
if (OW_MON_SCRIPT_MOVEMENT)
FlagSet(FLAG_SAFE_FOLLOWER_MOVEMENT);
sGlobalScriptContextStatus = CONTEXT_RUNNING;
}

View File

@ -1,6 +1,7 @@
#include "global.h"
#include "script_movement.h"
#include "event_object_movement.h"
#include "event_scripts.h"
#include "task.h"
#include "util.h"
#include "constants/event_objects.h"
@ -205,13 +206,34 @@ static void ScriptMovement_MoveObjects(u8 taskId)
}
}
// from event_object_movement
#define sTypeFuncId data[1]
#define sTimer data[5]
static void ScriptMovement_TakeStep(u8 taskId, u8 moveScrId, u8 objEventId, const u8 *movementScript)
{
u8 nextMoveActionId;
struct ObjectEvent *obj = &gObjectEvents[objEventId];
if (ObjectEventIsHeldMovementActive(&gObjectEvents[objEventId])
&& !ObjectEventClearHeldMovementIfFinished(&gObjectEvents[objEventId]))
if (ObjectEventIsHeldMovementActive(obj) &&
!ObjectEventClearHeldMovementIfFinished(obj))
{
// If, while undergoing scripted movement,
// a non-player object collides with an active follower pokemon,
// put that follower into a pokeball
// (sTimer helps limit this expensive check to once per step)
if (OW_MON_SCRIPT_MOVEMENT &&
gSprites[obj->spriteId].sTimer == 1 &&
(objEventId = GetObjectObjectCollidesWith(obj, 0, 0, TRUE)) < OBJECT_EVENTS_COUNT &&
// switch `obj` to follower
((obj = &gObjectEvents[objEventId])->movementType == MOVEMENT_TYPE_FOLLOW_PLAYER) &&
gSprites[obj->spriteId].sTypeFuncId != 0)
{
ClearObjectEventMovement(obj, &gSprites[obj->spriteId]);
ScriptMovement_StartObjectMovementScript(obj->localId, obj->mapNum, obj->mapGroup, EnterPokeballMovement);
}
return;
}
nextMoveActionId = *movementScript;
if (nextMoveActionId == MOVEMENT_ACTION_STEP_END)
@ -229,3 +251,5 @@ static void ScriptMovement_TakeStep(u8 taskId, u8 moveScrId, u8 objEventId, cons
}
}
#undef sTypeFuncId
#undef sTimer