diff --git a/asm/macros/event.inc b/asm/macros/event.inc index e2e04333e..8010c6991 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2266,3 +2266,9 @@ callnative ScriptHideItemDescription .endm + @ Hides any follower Pokémon if present, putting them into their Poké Ball; by default waits for their movement to finish. + .macro hidefollower wait=1 + callnative ScrFunc_hidefollower + .2byte \wait + .endm + diff --git a/data/scripts/pkmn_center_nurse.inc b/data/scripts/pkmn_center_nurse.inc index 726d0beaa..8dee70527 100644 --- a/data/scripts/pkmn_center_nurse.inc +++ b/data/scripts/pkmn_center_nurse.inc @@ -19,7 +19,8 @@ EventScript_PkmnCenterNurse_HealPkmn:: end EventScript_PkmnCenterNurse_TakeAndHealPkmn:: - applymovement VAR_LAST_TALKED, Movement_PkmnCenterNurse_Turn @ Changed from Common_Movement_WalkInPlaceFasterLeft to force the follower to enter their Poké Ball + hidefollower 0 + applymovement VAR_LAST_TALKED, Movement_PkmnCenterNurse_Turn waitmovement 0 dofieldeffect FLDEFF_POKECENTER_HEAL waitfieldeffect FLDEFF_POKECENTER_HEAL diff --git a/include/config/overworld.h b/include/config/overworld.h index bb81f364a..bce02be53 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -4,7 +4,7 @@ // Movement config #define OW_RUNNING_INDOORS GEN_LATEST // In Gen4+, players are allowed to run indoors. #define OW_AUTO_SIGNPOST FALSE // When enabled, if the tile that the player is facing has MB_SIGNPOST, MB_POKEMART_SIGN, or MB_POKEMON_CENTER_SIGN, the player will automatically read the signpost, as seen in FRLG. -#define SLOW_MOVEMENT_ON_STAIRS FALSE // If enabled, the player will move slower up/down stairs like in FR +#define SLOW_MOVEMENT_ON_STAIRS TRUE // If enabled, the player will move slower up/down stairs like in FR // Other settings #define OW_POISON_DAMAGE GEN_LATEST // In Gen4, Pokémon no longer faint from Poison in the overworld. In Gen5+, they no longer take damage at all. diff --git a/include/constants/metatile_behaviors.h b/include/constants/metatile_behaviors.h index 1c5894fe1..490367182 100644 --- a/include/constants/metatile_behaviors.h +++ b/include/constants/metatile_behaviors.h @@ -58,6 +58,12 @@ #define MB_SLIDE_NORTH 0x46 #define MB_SLIDE_SOUTH 0x47 #define MB_TRICK_HOUSE_PUZZLE_8_FLOOR 0x48 +#define MB_SIDEWAYS_STAIRS_RIGHT_SIDE 0x49 +#define MB_SIDEWAYS_STAIRS_LEFT_SIDE 0x4A +#define MB_SIDEWAYS_STAIRS_RIGHT_SIDE_TOP 0x4B +#define MB_SIDEWAYS_STAIRS_LEFT_SIDE_TOP 0x4C +#define MB_SIDEWAYS_STAIRS_RIGHT_SIDE_BOTTOM 0x4D +#define MB_SIDEWAYS_STAIRS_LEFT_SIDE_BOTTOM 0x4E // #define MB_EASTWARD_CURRENT 0x50 #define MB_WESTWARD_CURRENT 0x51 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 84ddf818a..77eb28761 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -206,6 +206,8 @@ u8 DoJumpSpecialSpriteMovement(struct Sprite *); void TurnVirtualObject(u8 virtualObjId, u8 direction); const u8 *GetObjectEventScriptPointerByObjectEventId(u8 objectEventId); u8 GetFirstInactiveObjectEventId(void); +u8 GetObjectEventIdByLocalId(u8); +bool32 IsFollowerVisible(void); u8 GetCollisionFlagsAtCoords(struct ObjectEvent * objectEvent, s16 x, s16 y, u8 direction); void OverrideTemplateCoordsForObjectEvent(const struct ObjectEvent *objectEvent); void CancelPlayerForcedMovement(void); diff --git a/include/field_player_avatar.h b/include/field_player_avatar.h index b6c70ba31..d58514fae 100644 --- a/include/field_player_avatar.h +++ b/include/field_player_avatar.h @@ -57,12 +57,15 @@ void PlayerTurnInPlace(u8 direction); void PlayerJumpLedge(u8 direction); void PlayerShakeHeadOrWalkInPlace(void); void player_step(u8 direction, u16 newKeys, u16 heldKeys); -bool32 PlayerIsMovingOnRockStairs(u8 direction); +bool32 ObjectMovingOnRockStairs(struct ObjectEvent *objectEvent, u8 direction); void SetPlayerAvatarWatering(u8 direction); u8 GetPlayerAvatarFlags(void); void UpdatePlayerAvatarTransitionState(void); void InitPlayerAvatar(s16 x, s16 y, u8 direction, u8 gender); void PlayerUseAcroBikeOnBumpySlope(u8 direction); u8 GetRSAvatarGraphicsIdByGender(u8 gender); +//sideways stairs +u8 GetRightSideStairsDirection(u8 direction); +u8 GetLeftSideStairsDirection(u8 direction); #endif //GUARD_FIELD_PLAYER_AVATAR_H diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index c7bc814e8..061e592af 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -355,6 +355,8 @@ enum COLLISION_ISOLATED_HORIZONTAL_RAIL, COLLISION_VERTICAL_RAIL, COLLISION_HORIZONTAL_RAIL, + COLLISION_SIDEWAYS_STAIRS_TO_RIGHT, + COLLISION_SIDEWAYS_STAIRS_TO_LEFT, COLLISION_COUNT }; diff --git a/include/metatile_behavior.h b/include/metatile_behavior.h index 7f090ea62..36d721f91 100644 --- a/include/metatile_behavior.h +++ b/include/metatile_behavior.h @@ -136,5 +136,13 @@ bool8 MetatileBehavior_IsTrainerTowerMonitor(u8 metatileBehavior); bool8 MetatileBehavior_IsSecretBaseJumpMat(u8 metatileBehavior); bool8 MetatileBehavior_IsSecretBaseSpinMat(u8 metatileBehavior); bool8 MetatileBehavior_IsSnow(u8 metatileBehavior); +bool8 MetatileBehavior_IsSidewaysStairsRightSide(u8); +bool8 MetatileBehavior_IsSidewaysStairsLeftSide(u8); +bool8 MetatileBehavior_IsSidewaysStairsRightSideTop(u8 metatileBehavior); +bool8 MetatileBehavior_IsSidewaysStairsLeftSideTop(u8 metatileBehavior); +bool8 MetatileBehavior_IsSidewaysStairsRightSideBottom(u8 metatileBehavior); +bool8 MetatileBehavior_IsSidewaysStairsLeftSideBottom(u8 metatileBehavior); +bool8 MetatileBehavior_IsSidewaysStairsRightSideAny(u8 metatileBehavior); +bool8 MetatileBehavior_IsSidewaysStairsLeftSideAny(u8 metatileBehavior); #endif // GUARD_METATILE_BEHAVIOR diff --git a/src/bike.c b/src/bike.c index 772e65a28..724c8d10b 100644 --- a/src/bike.c +++ b/src/bike.c @@ -55,6 +55,19 @@ static u8 BikeInputHandler_Normal(u8 *direction_p, u16 newKeys, u16 heldKeys) struct ObjectEvent *playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 direction = GetPlayerMovementDirection(); + // fix direction when moving on sideways stairs + switch (direction) + { + case DIR_SOUTHWEST: + case DIR_NORTHWEST: + direction = DIR_WEST; + break; + case DIR_SOUTHEAST: + case DIR_NORTHEAST: + direction = DIR_EAST; + break; + } + gPlayerAvatar.bikeFrameCounter = 0; if (MetatileBehavior_IsCyclingRoadPullDownTile(playerObjEvent->currentMetatileBehavior) == TRUE) { @@ -188,7 +201,7 @@ static void BikeTransition_MoveDirection(u8 direction) if (collision == COLLISION_COUNT) PlayerWalkFast(direction); - else if (PlayerIsMovingOnRockStairs(direction)) + else if (ObjectMovingOnRockStairs(playerObjEvent, direction)) PlayerWalkFast(direction); else PlayerRideWaterCurrent(direction); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 44f588bbd..1ea922877 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -78,7 +78,6 @@ static void DoGroundEffects_OnSpawn(struct ObjectEvent *, struct Sprite *); static void DoGroundEffects_OnBeginStep(struct ObjectEvent *, struct Sprite *); static void DoGroundEffects_OnFinishStep(struct ObjectEvent *, struct Sprite *); static void CreateReflectionEffectSprites(void); -static u8 GetObjectEventIdByLocalId(u8); static u8 GetObjectEventIdByLocalIdAndMapInternal(u8, u8, u8); static bool8 GetAvailableObjectEventId(u16, u8, u8, u8 *); static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *); @@ -739,10 +738,10 @@ static const u8 sFaceDirectionAnimNums[] = { [DIR_NORTH] = ANIM_STD_FACE_NORTH, [DIR_WEST] = ANIM_STD_FACE_WEST, [DIR_EAST] = ANIM_STD_FACE_EAST, - [DIR_SOUTHWEST] = ANIM_STD_FACE_SOUTH, - [DIR_SOUTHEAST] = ANIM_STD_FACE_SOUTH, - [DIR_NORTHWEST] = ANIM_STD_FACE_NORTH, - [DIR_NORTHEAST] = ANIM_STD_FACE_NORTH, + [DIR_SOUTHWEST] = ANIM_STD_FACE_WEST, + [DIR_SOUTHEAST] = ANIM_STD_FACE_EAST, + [DIR_NORTHWEST] = ANIM_STD_FACE_WEST, + [DIR_NORTHEAST] = ANIM_STD_FACE_EAST, }; static const u8 sMoveDirectionAnimNums[] = { @@ -751,10 +750,10 @@ static const u8 sMoveDirectionAnimNums[] = { [DIR_NORTH] = ANIM_STD_GO_NORTH, [DIR_WEST] = ANIM_STD_GO_WEST, [DIR_EAST] = ANIM_STD_GO_EAST, - [DIR_SOUTHWEST] = ANIM_STD_GO_SOUTH, - [DIR_SOUTHEAST] = ANIM_STD_GO_SOUTH, - [DIR_NORTHWEST] = ANIM_STD_GO_NORTH, - [DIR_NORTHEAST] = ANIM_STD_GO_NORTH, + [DIR_SOUTHWEST] = ANIM_STD_GO_WEST, + [DIR_SOUTHEAST] = ANIM_STD_GO_EAST, + [DIR_NORTHWEST] = ANIM_STD_GO_WEST, + [DIR_NORTHEAST] = ANIM_STD_GO_EAST, }; static const u8 sMoveDirectionFastAnimNums[] = { @@ -763,10 +762,10 @@ static const u8 sMoveDirectionFastAnimNums[] = { [DIR_NORTH] = ANIM_STD_GO_FAST_NORTH, [DIR_WEST] = ANIM_STD_GO_FAST_WEST, [DIR_EAST] = ANIM_STD_GO_FAST_EAST, - [DIR_SOUTHWEST] = ANIM_STD_GO_FAST_SOUTH, - [DIR_SOUTHEAST] = ANIM_STD_GO_FAST_SOUTH, - [DIR_NORTHWEST] = ANIM_STD_GO_FAST_NORTH, - [DIR_NORTHEAST] = ANIM_STD_GO_FAST_NORTH, + [DIR_SOUTHWEST] = ANIM_STD_GO_FAST_WEST, + [DIR_SOUTHEAST] = ANIM_STD_GO_FAST_EAST, + [DIR_NORTHWEST] = ANIM_STD_GO_FAST_WEST, + [DIR_NORTHEAST] = ANIM_STD_GO_FAST_EAST, }; static const u8 sMoveDirectionFasterAnimNums[] = { @@ -775,10 +774,10 @@ static const u8 sMoveDirectionFasterAnimNums[] = { [DIR_NORTH] = ANIM_STD_GO_FASTER_NORTH, [DIR_WEST] = ANIM_STD_GO_FASTER_WEST, [DIR_EAST] = ANIM_STD_GO_FASTER_EAST, - [DIR_SOUTHWEST] = ANIM_STD_GO_FASTER_SOUTH, - [DIR_SOUTHEAST] = ANIM_STD_GO_FASTER_SOUTH, - [DIR_NORTHWEST] = ANIM_STD_GO_FASTER_NORTH, - [DIR_NORTHEAST] = ANIM_STD_GO_FASTER_NORTH, + [DIR_SOUTHWEST] = ANIM_STD_GO_FASTER_WEST, + [DIR_SOUTHEAST] = ANIM_STD_GO_FASTER_EAST, + [DIR_NORTHWEST] = ANIM_STD_GO_FASTER_WEST, + [DIR_NORTHEAST] = ANIM_STD_GO_FASTER_EAST, }; static const u8 sMoveDirectionFastestAnimNums[] = { @@ -787,10 +786,10 @@ static const u8 sMoveDirectionFastestAnimNums[] = { [DIR_NORTH] = ANIM_STD_GO_FASTEST_NORTH, [DIR_WEST] = ANIM_STD_GO_FASTEST_WEST, [DIR_EAST] = ANIM_STD_GO_FASTEST_EAST, - [DIR_SOUTHWEST] = ANIM_STD_GO_FASTEST_SOUTH, - [DIR_SOUTHEAST] = ANIM_STD_GO_FASTEST_SOUTH, - [DIR_NORTHWEST] = ANIM_STD_GO_FASTEST_NORTH, - [DIR_NORTHEAST] = ANIM_STD_GO_FASTEST_NORTH, + [DIR_SOUTHWEST] = ANIM_STD_GO_FASTEST_WEST, + [DIR_SOUTHEAST] = ANIM_STD_GO_FASTEST_EAST, + [DIR_NORTHWEST] = ANIM_STD_GO_FASTEST_WEST, + [DIR_NORTHEAST] = ANIM_STD_GO_FASTEST_EAST, }; static const u8 sJumpSpecialDirectionAnimNums[] = { @@ -799,10 +798,10 @@ static const u8 sJumpSpecialDirectionAnimNums[] = { [DIR_NORTH] = ANIM_GET_ON_OFF_POKEMON_NORTH, [DIR_WEST] = ANIM_GET_ON_OFF_POKEMON_WEST, [DIR_EAST] = ANIM_GET_ON_OFF_POKEMON_EAST, - [DIR_SOUTHWEST] = ANIM_GET_ON_OFF_POKEMON_SOUTH, - [DIR_SOUTHEAST] = ANIM_GET_ON_OFF_POKEMON_SOUTH, - [DIR_NORTHWEST] = ANIM_GET_ON_OFF_POKEMON_NORTH, - [DIR_NORTHEAST] = ANIM_GET_ON_OFF_POKEMON_NORTH, + [DIR_SOUTHWEST] = ANIM_GET_ON_OFF_POKEMON_WEST, + [DIR_SOUTHEAST] = ANIM_GET_ON_OFF_POKEMON_EAST, + [DIR_NORTHWEST] = ANIM_GET_ON_OFF_POKEMON_WEST, + [DIR_NORTHEAST] = ANIM_GET_ON_OFF_POKEMON_EAST, }; static const u8 sAcroBunnyHopBackWheelDirectionAnimNums[] = { @@ -811,10 +810,10 @@ static const u8 sAcroBunnyHopBackWheelDirectionAnimNums[] = { [DIR_NORTH] = ANIM_BUNNY_HOP_BACK_WHEEL_NORTH, [DIR_WEST] = ANIM_BUNNY_HOP_BACK_WHEEL_WEST, [DIR_EAST] = ANIM_BUNNY_HOP_BACK_WHEEL_EAST, - [DIR_SOUTHWEST] = ANIM_BUNNY_HOP_BACK_WHEEL_SOUTH, - [DIR_SOUTHEAST] = ANIM_BUNNY_HOP_BACK_WHEEL_SOUTH, - [DIR_NORTHWEST] = ANIM_BUNNY_HOP_BACK_WHEEL_NORTH, - [DIR_NORTHEAST] = ANIM_BUNNY_HOP_BACK_WHEEL_NORTH, + [DIR_SOUTHWEST] = ANIM_BUNNY_HOP_BACK_WHEEL_WEST, + [DIR_SOUTHEAST] = ANIM_BUNNY_HOP_BACK_WHEEL_EAST, + [DIR_NORTHWEST] = ANIM_BUNNY_HOP_BACK_WHEEL_WEST, + [DIR_NORTHEAST] = ANIM_BUNNY_HOP_BACK_WHEEL_EAST, }; static const u8 sAcroBunnyHopFrontWheelDirectionAnimNums[] = { @@ -823,10 +822,10 @@ static const u8 sAcroBunnyHopFrontWheelDirectionAnimNums[] = { [DIR_NORTH] = ANIM_BUNNY_HOP_FRONT_WHEEL_NORTH, [DIR_WEST] = ANIM_BUNNY_HOP_FRONT_WHEEL_WEST, [DIR_EAST] = ANIM_BUNNY_HOP_FRONT_WHEEL_EAST, - [DIR_SOUTHWEST] = ANIM_BUNNY_HOP_FRONT_WHEEL_SOUTH, - [DIR_SOUTHEAST] = ANIM_BUNNY_HOP_FRONT_WHEEL_SOUTH, - [DIR_NORTHWEST] = ANIM_BUNNY_HOP_FRONT_WHEEL_NORTH, - [DIR_NORTHEAST] = ANIM_BUNNY_HOP_FRONT_WHEEL_NORTH, + [DIR_SOUTHWEST] = ANIM_BUNNY_HOP_FRONT_WHEEL_WEST, + [DIR_SOUTHEAST] = ANIM_BUNNY_HOP_FRONT_WHEEL_EAST, + [DIR_NORTHWEST] = ANIM_BUNNY_HOP_FRONT_WHEEL_WEST, + [DIR_NORTHEAST] = ANIM_BUNNY_HOP_FRONT_WHEEL_EAST, }; static const u8 sAcroStandingWheelieBackWheelDirectionAnimNums[] = { @@ -835,10 +834,10 @@ static const u8 sAcroStandingWheelieBackWheelDirectionAnimNums[] = { [DIR_NORTH] = ANIM_STANDING_WHEELIE_BACK_WHEEL_NORTH, [DIR_WEST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_WEST, [DIR_EAST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_EAST, - [DIR_SOUTHWEST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_SOUTH, - [DIR_SOUTHEAST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_SOUTH, - [DIR_NORTHWEST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_NORTH, - [DIR_NORTHEAST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_NORTH, + [DIR_SOUTHWEST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_WEST, + [DIR_SOUTHEAST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_EAST, + [DIR_NORTHWEST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_WEST, + [DIR_NORTHEAST] = ANIM_STANDING_WHEELIE_BACK_WHEEL_EAST, }; static const u8 sSpinDirectionAnimNums[] = { @@ -859,10 +858,10 @@ static const u8 sAcroStandingWheelieFrontWheelDirectionAnimNums[] = { [DIR_NORTH] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_NORTH, [DIR_WEST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_WEST, [DIR_EAST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_EAST, - [DIR_SOUTHWEST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_SOUTH, - [DIR_SOUTHEAST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_SOUTH, - [DIR_NORTHWEST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_NORTH, - [DIR_NORTHEAST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_NORTH, + [DIR_SOUTHWEST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_WEST, + [DIR_SOUTHEAST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_EAST, + [DIR_NORTHWEST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_WEST, + [DIR_NORTHEAST] = ANIM_STANDING_WHEELIE_FRONT_WHEEL_EAST, }; static const u8 sAcroMovingWheelieDirectionAnimNums[] = { @@ -871,10 +870,10 @@ static const u8 sAcroMovingWheelieDirectionAnimNums[] = { [DIR_NORTH] = ANIM_MOVING_WHEELIE_NORTH, [DIR_WEST] = ANIM_MOVING_WHEELIE_WEST, [DIR_EAST] = ANIM_MOVING_WHEELIE_EAST, - [DIR_SOUTHWEST] = ANIM_MOVING_WHEELIE_SOUTH, - [DIR_SOUTHEAST] = ANIM_MOVING_WHEELIE_SOUTH, - [DIR_NORTHWEST] = ANIM_MOVING_WHEELIE_NORTH, - [DIR_NORTHEAST] = ANIM_MOVING_WHEELIE_NORTH, + [DIR_SOUTHWEST] = ANIM_MOVING_WHEELIE_WEST, + [DIR_SOUTHEAST] = ANIM_MOVING_WHEELIE_EAST, + [DIR_NORTHWEST] = ANIM_MOVING_WHEELIE_WEST, + [DIR_NORTHEAST] = ANIM_MOVING_WHEELIE_EAST, }; static const u8 sFishingDirectionAnimNums[] = { @@ -919,10 +918,10 @@ static const u8 sRunningDirectionAnimNums[] = { [DIR_NORTH] = ANIM_RUN_NORTH, [DIR_WEST] = ANIM_RUN_WEST, [DIR_EAST] = ANIM_RUN_EAST, - [DIR_SOUTHWEST] = ANIM_RUN_SOUTH, - [DIR_SOUTHEAST] = ANIM_RUN_SOUTH, - [DIR_NORTHWEST] = ANIM_RUN_NORTH, - [DIR_NORTHEAST] = ANIM_RUN_NORTH, + [DIR_SOUTHWEST] = ANIM_RUN_WEST, + [DIR_SOUTHEAST] = ANIM_RUN_EAST, + [DIR_NORTHWEST] = ANIM_RUN_WEST, + [DIR_NORTHEAST] = ANIM_RUN_EAST, }; static const u8 sTrainerFacingDirectionMovementTypes[] = { @@ -961,6 +960,10 @@ static const struct Coords16 sDirectionToVectors[] = { [DIR_SOUTHEAST] = { 1, 1}, [DIR_NORTHWEST] = {-1, -1}, [DIR_NORTHEAST] = { 1, -1}, + {-2, 1}, + { 2, 1}, + {-2, -1}, + { 2, -1} }; static const u8 sFaceDirectionMovementActions[] = { @@ -969,6 +972,10 @@ static const u8 sFaceDirectionMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_FACE_UP, [DIR_WEST] = MOVEMENT_ACTION_FACE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_FACE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_FACE_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_FACE_RIGHT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_FACE_LEFT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_FACE_RIGHT }; static const u8 sFaceDirectionFastMovementActions[] = { @@ -1065,6 +1072,10 @@ static const u8 sPlayerRunSlowMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_PLAYER_RUN_UP_SLOW, [DIR_WEST] = MOVEMENT_ACTION_PLAYER_RUN_LEFT_SLOW, [DIR_EAST] = MOVEMENT_ACTION_PLAYER_RUN_RIGHT_SLOW, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_PLAYER_RUN_LEFT_SLOW, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_PLAYER_RUN_RIGHT_SLOW, + [DIR_NORTHWEST] = MOVEMENT_ACTION_PLAYER_RUN_LEFT_SLOW, + [DIR_NORTHEAST] = MOVEMENT_ACTION_PLAYER_RUN_RIGHT_SLOW, }; static const u8 sSpinMovementActions[] = { @@ -1129,6 +1140,10 @@ static const u8 sWalkInPlaceSlowMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_WALK_IN_PLACE_SLOW_UP, [DIR_WEST] = MOVEMENT_ACTION_WALK_IN_PLACE_SLOW_LEFT, [DIR_EAST] = MOVEMENT_ACTION_WALK_IN_PLACE_SLOW_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_SLOW_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_SLOW_LEFT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_SLOW_RIGHT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_SLOW_RIGHT }; static const u8 sWalkInPlaceNormalMovementActions[] = { @@ -1137,6 +1152,10 @@ static const u8 sWalkInPlaceNormalMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_UP, [DIR_WEST] = MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_LEFT, [DIR_EAST] = MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_LEFT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT }; static const u8 sWalkInPlaceFastMovementActions[] = { @@ -1145,6 +1164,10 @@ static const u8 sWalkInPlaceFastMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_WALK_IN_PLACE_FAST_UP, [DIR_WEST] = MOVEMENT_ACTION_WALK_IN_PLACE_FAST_LEFT, [DIR_EAST] = MOVEMENT_ACTION_WALK_IN_PLACE_FAST_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_FAST_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_FAST_LEFT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_FAST_RIGHT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_FAST_RIGHT }; static const u8 sWalkInPlaceFasterMovementActions[] = { @@ -1153,6 +1176,10 @@ static const u8 sWalkInPlaceFasterMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_UP, [DIR_WEST] = MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_LEFT, [DIR_EAST] = MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_LEFT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_RIGHT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_RIGHT }; static const u8 sAcroWheelieFaceDirectionMovementActions[] = { @@ -1161,6 +1188,10 @@ static const u8 sAcroWheelieFaceDirectionMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_WHEELIE_FACE_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_WHEELIE_FACE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_WHEELIE_FACE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_FACE_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_FACE_LEFT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_FACE_RIGHT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_FACE_RIGHT }; static const u8 sAcroPopWheelieFaceDirectionMovementActions[] = { @@ -1169,6 +1200,10 @@ static const u8 sAcroPopWheelieFaceDirectionMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_RIGHT, }; static const u8 sAcroEndWheelieFaceDirectionMovementActions[] = { @@ -1177,6 +1212,10 @@ static const u8 sAcroEndWheelieFaceDirectionMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_END_WHEELIE_FACE_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_END_WHEELIE_FACE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_END_WHEELIE_FACE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_END_WHEELIE_FACE_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_END_WHEELIE_FACE_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_END_WHEELIE_FACE_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_END_WHEELIE_FACE_RIGHT, }; static const u8 sAcroWheelieHopFaceDirectionMovementActions[] = { @@ -1185,6 +1224,10 @@ static const u8 sAcroWheelieHopFaceDirectionMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_FACE_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_FACE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_FACE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_FACE_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_FACE_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_FACE_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_FACE_RIGHT, }; static const u8 sAcroWheelieHopMovementActions[] = { @@ -1193,6 +1236,10 @@ static const u8 sAcroWheelieHopMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_HOP_RIGHT, }; static const u8 sAcroWheelieJumpMovementActions[] = { @@ -1201,6 +1248,10 @@ static const u8 sAcroWheelieJumpMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_WHEELIE_JUMP_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_WHEELIE_JUMP_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_WHEELIE_JUMP_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_JUMP_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_JUMP_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_JUMP_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_JUMP_RIGHT, }; static const u8 sAcroWheelieInPlaceMovementActions[] = { @@ -1209,6 +1260,10 @@ static const u8 sAcroWheelieInPlaceMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_WHEELIE_IN_PLACE_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_WHEELIE_IN_PLACE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_WHEELIE_IN_PLACE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_IN_PLACE_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_IN_PLACE_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_IN_PLACE_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_IN_PLACE_RIGHT, }; static const u8 sAcroPopWheelieMoveMovementActions[] = { @@ -1217,6 +1272,10 @@ static const u8 sAcroPopWheelieMoveMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_MOVE_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_MOVE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_MOVE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_MOVE_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_MOVE_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_MOVE_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_POP_WHEELIE_MOVE_RIGHT, }; static const u8 sAcroWheelieMoveMovementActions[] = { @@ -1225,6 +1284,10 @@ static const u8 sAcroWheelieMoveMovementActions[] = { [DIR_NORTH] = MOVEMENT_ACTION_ACRO_WHEELIE_MOVE_UP, [DIR_WEST] = MOVEMENT_ACTION_ACRO_WHEELIE_MOVE_LEFT, [DIR_EAST] = MOVEMENT_ACTION_ACRO_WHEELIE_MOVE_RIGHT, + [DIR_SOUTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_MOVE_LEFT, + [DIR_NORTHWEST] = MOVEMENT_ACTION_ACRO_WHEELIE_MOVE_LEFT, + [DIR_SOUTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_MOVE_RIGHT, + [DIR_NORTHEAST] = MOVEMENT_ACTION_ACRO_WHEELIE_MOVE_RIGHT, }; static const u8 gOppositeDirections[] = { @@ -1347,7 +1410,7 @@ static u8 GetObjectEventIdByLocalIdAndMapInternal(u8 localId, u8 mapNum, u8 mapG return OBJECT_EVENTS_COUNT; } -static u8 GetObjectEventIdByLocalId(u8 localId) +u8 GetObjectEventIdByLocalId(u8 localId) { u8 i; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) @@ -2309,7 +2372,7 @@ void RemoveFollowingPokemon(void) } // Determine whether follower *should* be visible -static bool32 IsFollowerVisible(void) +bool32 IsFollowerVisible(void) { return !(TestPlayerAvatarFlags(FOLLOWER_INVISIBLE_FLAGS) || MetatileBehavior_IsSurfableAndNotWaterfall(gObjectEvents[gPlayerAvatar.objectEventId].previousMetatileBehavior) @@ -5607,17 +5670,13 @@ bool8 MovementType_FollowPlayer_Active(struct ObjectEvent *objectEvent, struct S bool8 MovementType_FollowPlayer_Moving(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - #ifdef MB_SIDEWAYS_STAIRS_RIGHT_SIDE // Copied from ObjectEventExecSingleMovementAction - if (gMovementActionFuncs[objectEvent->movementActionId][sprite->sActionFuncId](objectEvent, sprite)) + if (sMovementActionFuncs[objectEvent->movementActionId][sprite->sActionFuncId](objectEvent, sprite)) { objectEvent->movementActionId = MOVEMENT_ACTION_NONE; sprite->sActionFuncId = 0; - #else - if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) - { - #endif - objectEvent->singleMovementActive = 0; + objectEvent->singleMovementActive = FALSE; + objectEvent->facingDirectionLocked = FALSE; if (sprite->sTypeFuncId) // restore nonzero state sprite->sTypeFuncId = 1; } @@ -5660,9 +5719,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; @@ -5680,13 +5737,15 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri { // 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; } + MoveObjectEventToMapCoords(objectEvent, targetX, targetY); ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EXIT_POKEBALL); - objectEvent->singleMovementActive = 1; + objectEvent->singleMovementActive = TRUE; sprite->sTypeFuncId = 2; if (OW_FOLLOWERS_BOBBING == TRUE) sprite->y2 = 0; @@ -5700,63 +5759,45 @@ 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) { // InitJumpRegular will set the proper speed ObjectEventSetSingleMovement(objectEvent, sprite, GetJump2MovementAction(direction)); - else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) + } + else if (playerAction >= MOVEMENT_ACTION_WALK_SLOW_DOWN && playerAction <= MOVEMENT_ACTION_WALK_SLOW_RIGHT) { - // Set follow speed according to player's speed - if (playerAction >= MOVEMENT_ACTION_RUN_DOWN_SLOW && playerAction <= MOVEMENT_ACTION_RUN_RIGHT_SLOW) + 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_FOLLOWERS_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, GetWalkSlowerMovementAction(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_FOLLOWERS_BOBBING == TRUE) sprite->y2 = -1; } - #endif - objectEvent->singleMovementActive = 1; + sprite->sActionFuncId = 0; + objectEvent->singleMovementActive = TRUE; sprite->sTypeFuncId = 2; return TRUE; } @@ -6313,15 +6354,53 @@ static u8 GetCollisionInDirection(struct ObjectEvent *objectEvent, u8 direction) return GetCollisionAtCoords(objectEvent, x, y, direction); } -u8 GetCollisionAtCoords(struct ObjectEvent *objectEvent, s16 x, s16 y, u32 dir) +u8 GetSidewaysStairsCollision(struct ObjectEvent *objectEvent, u8 dir, u8 currentBehavior, u8 nextBehavior, u8 collision) { - u8 direction = dir; + if ((dir == DIR_SOUTH || dir == DIR_NORTH) && collision != COLLISION_NONE) + return collision; -#if OW_FLAG_NO_COLLISION != 0 - if (FlagGet(OW_FLAG_NO_COLLISION)) - return COLLISION_NONE; -#endif + // cant descend stairs into water + if (MetatileBehavior_IsSurfableAndNotWaterfall(nextBehavior)) + return collision; + if (MetatileBehavior_IsSidewaysStairsLeftSide(nextBehavior)) + { + //moving ONTO left side stair + if (dir == DIR_WEST && currentBehavior != nextBehavior) + return collision; //moving onto top part of left-stair going left, so no diagonal + else + return COLLISION_SIDEWAYS_STAIRS_TO_LEFT; // move diagonally + } + else if (MetatileBehavior_IsSidewaysStairsRightSide(nextBehavior)) + { + //moving ONTO right side stair + if (dir == DIR_EAST && currentBehavior != nextBehavior) + return collision; //moving onto top part of right-stair going right, so no diagonal + else + return COLLISION_SIDEWAYS_STAIRS_TO_RIGHT; + } + else if (MetatileBehavior_IsSidewaysStairsLeftSideAny(currentBehavior)) + { + //moving OFF of any left side stair + if (dir == DIR_WEST && nextBehavior != currentBehavior) + return COLLISION_SIDEWAYS_STAIRS_TO_LEFT; //moving off of left stairs onto non-stair -> move diagonal + else + return collision; //moving off of left side stair to east -> move east + } + else if (MetatileBehavior_IsSidewaysStairsRightSideAny(currentBehavior)) + { + //moving OFF of any right side stair + if (dir == DIR_EAST && nextBehavior != currentBehavior) + return COLLISION_SIDEWAYS_STAIRS_TO_RIGHT; //moving off right stair onto non-stair -> move diagonal + else + return collision; + } + + return collision; +} + +static u8 GetVanillaCollision(struct ObjectEvent *objectEvent, s16 x, s16 y, u32 direction) +{ if (IsCoordOutsideObjectEventMovementRange(objectEvent, x, y)) return COLLISION_OUTSIDE_RANGE; else if (MapGridGetCollisionAt(x, y) || GetMapBorderIdAt(x, y) == CONNECTION_INVALID || IsMetatileDirectionallyImpassable(objectEvent, x, y, direction)) @@ -6335,6 +6414,90 @@ u8 GetCollisionAtCoords(struct ObjectEvent *objectEvent, s16 x, s16 y, u32 dir) return COLLISION_NONE; } +static bool8 ObjectEventOnLeftSideStair(struct ObjectEvent *objectEvent, s16 x, s16 y, u8 direction) +{ + switch (direction) + { + case DIR_EAST: + MoveCoords(DIR_NORTH, &x, &y); + return DoesObjectCollideWithObjectAt(objectEvent, x, y); + case DIR_WEST: + MoveCoords(DIR_SOUTH, &x, &y); + return DoesObjectCollideWithObjectAt(objectEvent, x, y); + default: + return FALSE; //north/south taken care of in GetVanillaCollision + } +} + +static bool8 ObjectEventOnRightSideStair(struct ObjectEvent *objectEvent, s16 x, s16 y, u8 direction) +{ + switch (direction) + { + case DIR_EAST: + MoveCoords(DIR_SOUTH, &x, &y); + return DoesObjectCollideWithObjectAt(objectEvent, x, y); + case DIR_WEST: + MoveCoords(DIR_NORTH, &x, &y); + return DoesObjectCollideWithObjectAt(objectEvent, x, y); + default: + return FALSE; //north/south taken care of in GetVanillaCollision + } +} + +u8 GetCollisionAtCoords(struct ObjectEvent *objectEvent, s16 x, s16 y, u32 dir) +{ + u8 currentBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); + u8 nextBehavior = MapGridGetMetatileBehaviorAt(x, y); + u8 collision; + + #if OW_FLAG_NO_COLLISION != 0 + if (FlagGet(OW_FLAG_NO_COLLISION)) + return COLLISION_NONE; + #endif + + objectEvent->directionOverwrite = DIR_NONE; + + //sideways stairs checks + if (MetatileBehavior_IsSidewaysStairsLeftSideTop(nextBehavior) && dir == DIR_EAST) + return COLLISION_IMPASSABLE; //moving onto left-side top edge east from regular ground -> nope + else if (MetatileBehavior_IsSidewaysStairsRightSideTop(nextBehavior) && dir == DIR_WEST) + return COLLISION_IMPASSABLE; //moving onto left-side top edge east from regular ground -> nope + else if (MetatileBehavior_IsSidewaysStairsRightSideBottom(nextBehavior) && (dir == DIR_EAST || dir == DIR_SOUTH)) + return COLLISION_IMPASSABLE; //moving into right-side bottom edge from regular ground -> nah + else if (MetatileBehavior_IsSidewaysStairsLeftSideBottom(nextBehavior) && (dir == DIR_WEST || dir == DIR_SOUTH)) + return COLLISION_IMPASSABLE; //moving onto left-side bottom edge from regular ground -> nah + else if ((MetatileBehavior_IsSidewaysStairsLeftSideTop(currentBehavior) || MetatileBehavior_IsSidewaysStairsRightSideTop(currentBehavior)) + && dir == DIR_NORTH) + return COLLISION_IMPASSABLE; //trying to move north off of top-most tile onto same level doesn't work + else if (!(MetatileBehavior_IsSidewaysStairsLeftSideTop(currentBehavior) || MetatileBehavior_IsSidewaysStairsRightSideTop(currentBehavior)) + && dir == DIR_SOUTH && (MetatileBehavior_IsSidewaysStairsLeftSideTop(nextBehavior) || MetatileBehavior_IsSidewaysStairsRightSideTop(nextBehavior))) + return COLLISION_IMPASSABLE; //trying to move south onto top stair tile at same level from non-stair -> no + else if (!(MetatileBehavior_IsSidewaysStairsLeftSideBottom(currentBehavior) || MetatileBehavior_IsSidewaysStairsRightSideBottom(currentBehavior)) + && dir == DIR_NORTH && (MetatileBehavior_IsSidewaysStairsLeftSideBottom(nextBehavior) || MetatileBehavior_IsSidewaysStairsRightSideBottom(nextBehavior))) + return COLLISION_IMPASSABLE; //trying to move north onto top stair tile at same level from non-stair -> no + + // regular checks + collision = GetVanillaCollision(objectEvent, x, y, dir); + + //sideways stairs direction change checks + collision = GetSidewaysStairsCollision(objectEvent, dir, currentBehavior, nextBehavior, collision); + switch (collision) + { + case COLLISION_SIDEWAYS_STAIRS_TO_LEFT: + if (ObjectEventOnLeftSideStair(objectEvent, x, y, dir)) + return COLLISION_OBJECT_EVENT; + objectEvent->directionOverwrite = GetLeftSideStairsDirection(dir); + return COLLISION_NONE; + case COLLISION_SIDEWAYS_STAIRS_TO_RIGHT: + if (ObjectEventOnRightSideStair(objectEvent, x, y, dir)) + return COLLISION_OBJECT_EVENT; + objectEvent->directionOverwrite = GetRightSideStairsDirection(dir); + return COLLISION_NONE; + } + + return collision; +} + u8 GetCollisionFlagsAtCoords(struct ObjectEvent *objectEvent, s16 x, s16 y, u8 direction) { u8 flags = 0; @@ -6535,6 +6698,42 @@ bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent) return FALSE; } +static u8 TryUpdateMovementActionOnStairs(struct ObjectEvent *objectEvent, u8 movementActionId) +{ + if (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER) + return movementActionId; // handled separately + + if (!ObjectMovingOnRockStairs(objectEvent, objectEvent->movementDirection)) + return movementActionId; + + switch (movementActionId) + { + case MOVEMENT_ACTION_WALK_NORMAL_DOWN: + return MOVEMENT_ACTION_WALK_SLOW_DOWN; + case MOVEMENT_ACTION_WALK_NORMAL_UP: + return MOVEMENT_ACTION_WALK_SLOW_UP; + case MOVEMENT_ACTION_WALK_NORMAL_LEFT: + return MOVEMENT_ACTION_WALK_SLOW_LEFT; + case MOVEMENT_ACTION_WALK_NORMAL_RIGHT: + return MOVEMENT_ACTION_WALK_SLOW_RIGHT; + default: + return movementActionId; + } +} + +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, + // Not a typo; follower needs to take an action with a duration == JUMP's, + // and JUMP2 here will lead to WALK_SLOW later + [MOVEMENT_ACTION_JUMP_DOWN ... MOVEMENT_ACTION_JUMP_RIGHT] = COPY_MOVE_JUMP2, + + [MOVEMENT_ACTION_NONE] = COPY_MOVE_NONE, +}; + bool8 ObjectEventSetHeldMovement(struct ObjectEvent *objectEvent, u8 movementActionId) { if (QL_GetPlaybackState() == QL_PLAYBACK_STATE_RUNNING) @@ -6542,16 +6741,29 @@ bool8 ObjectEventSetHeldMovement(struct ObjectEvent *objectEvent, u8 movementAct else if (ObjectEventIsMovementOverridden(objectEvent)) return TRUE; + movementActionId = TryUpdateMovementActionOnStairs(objectEvent, movementActionId); + UnfreezeObjectEvent(objectEvent); objectEvent->movementActionId = movementActionId; objectEvent->heldMovementActive = TRUE; objectEvent->heldMovementFinished = FALSE; gSprites[objectEvent->spriteId].data[2] = 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->playerCopyableMovement = sActionIdToCopyableMovement[objectEvent->movementActionId]; + } + return FALSE; } void ObjectEventForceSetHeldMovement(struct ObjectEvent *objectEvent, u8 movementActionId) { + movementActionId = TryUpdateMovementActionOnStairs(objectEvent, movementActionId); ObjectEventClearHeldMovementIfActive(objectEvent); ObjectEventSetHeldMovement(objectEvent, movementActionId); } @@ -6569,6 +6781,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->playerCopyableMovement = sActionIdToCopyableMovement[objectEvent->movementActionId]; + } } u8 ObjectEventCheckHeldMovementStatus(struct ObjectEvent *objectEvent) @@ -6591,7 +6812,7 @@ u8 ObjectEventClearHeldMovementIfFinished(struct ObjectEvent *objectEvent) u8 ObjectEventGetHeldMovementActionId(struct ObjectEvent *objectEvent) { if (objectEvent->heldMovementActive) - return objectEvent->movementActionId; + return TryUpdateMovementActionOnStairs(objectEvent, objectEvent->movementActionId); return MOVEMENT_ACTION_NONE; } @@ -6641,7 +6862,7 @@ u8 name(u32 idx)\ u8 animIds[sizeof(table)];\ direction = idx;\ memcpy(animIds, (table), sizeof(table));\ - if (direction > DIR_EAST) direction = 0;\ + if (direction > sizeof(table)) direction = 0;\ return animIds[direction];\ } @@ -6726,6 +6947,7 @@ static u32 GetCopyDirection(u8 copyInitDir, u32 playerInitDir, u32 playerMoveDir static void ObjectEventExecHeldMovementAction(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + objectEvent->movementActionId = TryUpdateMovementActionOnStairs(objectEvent, objectEvent->movementActionId); if (sMovementActionFuncs[objectEvent->movementActionId][sprite->data[2]](objectEvent, sprite)) { objectEvent->heldMovementFinished = TRUE; @@ -6734,6 +6956,7 @@ static void ObjectEventExecHeldMovementAction(struct ObjectEvent *objectEvent, s static void QuestLogObjectEventExecHeldMovementAction(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + objectEvent->movementActionId = TryUpdateMovementActionOnStairs(objectEvent, objectEvent->movementActionId); if (sMovementActionFuncs[objectEvent->movementActionId][sprite->data[2]](objectEvent, sprite)) { objectEvent->heldMovementFinished = TRUE; @@ -6744,6 +6967,7 @@ static void QuestLogObjectEventExecHeldMovementAction(struct ObjectEvent *object static bool8 ObjectEventExecSingleMovementAction(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + objectEvent->movementActionId = TryUpdateMovementActionOnStairs(objectEvent, objectEvent->movementActionId); if (sMovementActionFuncs[objectEvent->movementActionId][sprite->data[2]](objectEvent, sprite)) { objectEvent->movementActionId = MOVEMENT_ACTION_NONE; @@ -6755,7 +6979,7 @@ static bool8 ObjectEventExecSingleMovementAction(struct ObjectEvent *objectEvent static void ObjectEventSetSingleMovement(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 movementActionId) { - objectEvent->movementActionId = movementActionId; + objectEvent->movementActionId = TryUpdateMovementActionOnStairs(objectEvent, movementActionId); sprite->data[2] = 0; if (gQuestLogPlaybackState == QL_PLAYBACK_STATE_RECORDING) @@ -7003,7 +7227,10 @@ static bool8 MovementAction_WalkSlowerUp_Step1(struct ObjectEvent *objectEvent, static bool8 MovementAction_WalkSlowerLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitWalkSlower(objectEvent, sprite, DIR_WEST); + if (objectEvent->directionOverwrite) + InitWalkSlower(objectEvent, sprite, objectEvent->directionOverwrite); + else + InitWalkSlower(objectEvent, sprite, DIR_WEST); return MovementAction_WalkSlowerLeft_Step1(objectEvent, sprite); } @@ -7019,7 +7246,10 @@ static bool8 MovementAction_WalkSlowerLeft_Step1(struct ObjectEvent *objectEvent static bool8 MovementAction_WalkSlowerRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitWalkSlower(objectEvent, sprite, DIR_EAST); + if (objectEvent->directionOverwrite) + InitWalkSlower(objectEvent, sprite, objectEvent->directionOverwrite); + else + InitWalkSlower(objectEvent, sprite, DIR_EAST); return MovementAction_WalkSlowerRight_Step1(objectEvent, sprite); } @@ -7105,7 +7335,10 @@ static bool8 MovementAction_WalkSlowDown_Step1(struct ObjectEvent *objectEvent, static bool8 MovementAction_WalkSlowLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitWalkSlow(objectEvent, sprite, DIR_WEST); + if (objectEvent->directionOverwrite) + InitWalkSlow(objectEvent, sprite, objectEvent->directionOverwrite); + else + InitWalkSlow(objectEvent, sprite, DIR_WEST); return MovementAction_WalkSlowLeft_Step1(objectEvent, sprite); } @@ -7121,7 +7354,10 @@ static bool8 MovementAction_WalkSlowLeft_Step1(struct ObjectEvent *objectEvent, static bool8 MovementAction_WalkSlowRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitWalkSlow(objectEvent, sprite, DIR_EAST); + if (objectEvent->directionOverwrite) + InitWalkSlow(objectEvent, sprite, objectEvent->directionOverwrite); + else + InitWalkSlow(objectEvent, sprite, DIR_EAST); return MovementAction_WalkSlowRight_Step1(objectEvent, sprite); } @@ -7169,7 +7405,10 @@ static bool8 MovementAction_WalkNormalUp_Step1(struct ObjectEvent *objectEvent, static bool8 MovementAction_WalkNormalLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_NORMAL); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_NORMAL); + else + InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_NORMAL); return MovementAction_WalkNormalLeft_Step1(objectEvent, sprite); } @@ -7185,7 +7424,10 @@ static bool8 MovementAction_WalkNormalLeft_Step1(struct ObjectEvent *objectEvent static bool8 MovementAction_WalkNormalRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_NORMAL); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_NORMAL); + else + InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_NORMAL); return MovementAction_WalkNormalRight_Step1(objectEvent, sprite); } @@ -7459,7 +7701,10 @@ static bool8 MovementAction_WalkFastUp_Step1(struct ObjectEvent *objectEvent, st static bool8 MovementAction_WalkFastLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_1); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FAST_1); + else + InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_1); return MovementAction_WalkFastLeft_Step1(objectEvent, sprite); } @@ -7475,7 +7720,10 @@ static bool8 MovementAction_WalkFastLeft_Step1(struct ObjectEvent *objectEvent, static bool8 MovementAction_WalkFastRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_1); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FAST_1); + else + InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_1); return MovementAction_WalkFastRight_Step1(objectEvent, sprite); } @@ -7951,13 +8199,19 @@ static bool8 MovementAction_WalkInPlaceFasterUp_Step0(struct ObjectEvent *object static bool8 MovementAction_WalkInPlaceFasterLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMoveInPlace(objectEvent, sprite, DIR_WEST, GetMoveDirectionFasterAnimNum(DIR_WEST), 4); + if (objectEvent->directionOverwrite) + InitMoveInPlace(objectEvent, sprite, objectEvent->directionOverwrite, GetMoveDirectionFasterAnimNum(DIR_WEST), 4); + else + InitMoveInPlace(objectEvent, sprite, DIR_WEST, GetMoveDirectionFasterAnimNum(DIR_WEST), 4); return MovementAction_WalkInPlace_Step1(objectEvent, sprite); } static bool8 MovementAction_WalkInPlaceFasterRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMoveInPlace(objectEvent, sprite, DIR_EAST, GetMoveDirectionFasterAnimNum(DIR_EAST), 4); + if (objectEvent->directionOverwrite) + InitMoveInPlace(objectEvent, sprite, objectEvent->directionOverwrite, GetMoveDirectionFasterAnimNum(DIR_EAST), 4); + else + InitMoveInPlace(objectEvent, sprite, DIR_EAST, GetMoveDirectionFasterAnimNum(DIR_EAST), 4); return MovementAction_WalkInPlace_Step1(objectEvent, sprite); } @@ -7995,7 +8249,10 @@ static bool8 MovementAction_RideWaterCurrentUp_Step1(struct ObjectEvent *objectE static bool8 MovementAction_RideWaterCurrentLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_2); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FAST_2); + else + InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_2); return MovementAction_RideWaterCurrentLeft_Step1(objectEvent, sprite); } @@ -8011,7 +8268,10 @@ static bool8 MovementAction_RideWaterCurrentLeft_Step1(struct ObjectEvent *objec static bool8 MovementAction_RideWaterCurrentRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_2); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FAST_2); + else + InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_2); return MovementAction_RideWaterCurrentRight_Step1(objectEvent, sprite); } @@ -8059,7 +8319,10 @@ static bool8 MovementAction_WalkFasterUp_Step1(struct ObjectEvent *objectEvent, static bool8 MovementAction_WalkFasterLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FASTER); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FASTER); + else + InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FASTER); return MovementAction_WalkFasterLeft_Step1(objectEvent, sprite); } @@ -8075,7 +8338,10 @@ static bool8 MovementAction_WalkFasterLeft_Step1(struct ObjectEvent *objectEvent static bool8 MovementAction_WalkFasterRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FASTER); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FASTER); + else + InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FASTER); return MovementAction_WalkFasterRight_Step1(objectEvent, sprite); } @@ -8123,7 +8389,10 @@ static bool8 MovementAction_SlideUp_Step1(struct ObjectEvent *objectEvent, struc static bool8 MovementAction_SlideLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FASTEST); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FASTEST); + else + InitMovementNormal(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FASTEST); return MovementAction_SlideLeft_Step1(objectEvent, sprite); } @@ -8139,7 +8408,10 @@ static bool8 MovementAction_SlideLeft_Step1(struct ObjectEvent *objectEvent, str static bool8 MovementAction_SlideRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FASTEST); + if (objectEvent->directionOverwrite) + InitMovementNormal(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FASTEST); + else + InitMovementNormal(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FASTEST); return MovementAction_SlideRight_Step1(objectEvent, sprite); } @@ -8187,7 +8459,10 @@ static bool8 MovementAction_PlayerRunUp_Step1(struct ObjectEvent *objectEvent, s static bool8 MovementAction_PlayerRunLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - StartRunningAnim(objectEvent, sprite, DIR_WEST); + if (objectEvent->directionOverwrite) + StartRunningAnim(objectEvent, sprite, objectEvent->directionOverwrite); + else + StartRunningAnim(objectEvent, sprite, DIR_WEST); return MovementAction_PlayerRunLeft_Step1(objectEvent, sprite); } @@ -8203,7 +8478,10 @@ static bool8 MovementAction_PlayerRunLeft_Step1(struct ObjectEvent *objectEvent, static bool8 MovementAction_PlayerRunRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - StartRunningAnim(objectEvent, sprite, DIR_EAST); + if (objectEvent->directionOverwrite) + StartRunningAnim(objectEvent, sprite, objectEvent->directionOverwrite); + else + StartRunningAnim(objectEvent, sprite, DIR_EAST); return MovementAction_PlayerRunRight_Step1(objectEvent, sprite); } @@ -9180,7 +9458,10 @@ static bool8 MovementAction_AcroWheelieHopUp_Step1(struct ObjectEvent *objectEve static bool8 MovementAction_AcroWheelieHopLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroWheelieJump(objectEvent, sprite, DIR_WEST, JUMP_DISTANCE_NORMAL, JUMP_TYPE_LOW); + if (objectEvent->directionOverwrite) + InitAcroWheelieJump(objectEvent, sprite, objectEvent->directionOverwrite, JUMP_DISTANCE_NORMAL, JUMP_TYPE_LOW); + else + InitAcroWheelieJump(objectEvent, sprite, DIR_WEST, JUMP_DISTANCE_NORMAL, JUMP_TYPE_LOW); return MovementAction_AcroWheelieHopLeft_Step1(objectEvent, sprite); } @@ -9197,7 +9478,10 @@ static bool8 MovementAction_AcroWheelieHopLeft_Step1(struct ObjectEvent *objectE static bool8 MovementAction_AcroWheelieHopRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroWheelieJump(objectEvent, sprite, DIR_EAST, JUMP_DISTANCE_NORMAL, JUMP_TYPE_LOW); + if (objectEvent->directionOverwrite) + InitAcroWheelieJump(objectEvent, sprite, objectEvent->directionOverwrite, JUMP_DISTANCE_NORMAL, JUMP_TYPE_LOW); + else + InitAcroWheelieJump(objectEvent, sprite, DIR_EAST, JUMP_DISTANCE_NORMAL, JUMP_TYPE_LOW); return MovementAction_AcroWheelieHopRight_Step1(objectEvent, sprite); } @@ -9248,7 +9532,10 @@ static bool8 MovementAction_AcroWheelieJumpUp_Step1(struct ObjectEvent *objectEv static bool8 MovementAction_AcroWheelieJumpLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroWheelieJump(objectEvent, sprite, DIR_WEST, JUMP_DISTANCE_FAR, JUMP_TYPE_HIGH); + if (objectEvent->directionOverwrite) + InitAcroWheelieJump(objectEvent, sprite, objectEvent->directionOverwrite, JUMP_DISTANCE_FAR, JUMP_TYPE_HIGH); + else + InitAcroWheelieJump(objectEvent, sprite, DIR_WEST, JUMP_DISTANCE_FAR, JUMP_TYPE_HIGH); return MovementAction_AcroWheelieJumpLeft_Step1(objectEvent, sprite); } @@ -9265,7 +9552,10 @@ static bool8 MovementAction_AcroWheelieJumpLeft_Step1(struct ObjectEvent *object static bool8 MovementAction_AcroWheelieJumpRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroWheelieJump(objectEvent, sprite, DIR_EAST, JUMP_DISTANCE_FAR, JUMP_TYPE_HIGH); + if (objectEvent->directionOverwrite) + InitAcroWheelieJump(objectEvent, sprite, objectEvent->directionOverwrite, JUMP_DISTANCE_FAR, JUMP_TYPE_HIGH); + else + InitAcroWheelieJump(objectEvent, sprite, DIR_EAST, JUMP_DISTANCE_FAR, JUMP_TYPE_HIGH); return MovementAction_AcroWheelieJumpRight_Step1(objectEvent, sprite); } @@ -9294,13 +9584,19 @@ static bool8 MovementAction_AcroWheelieInPlaceUp_Step0(struct ObjectEvent *objec static bool8 MovementAction_AcroWheelieInPlaceLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMoveInPlace(objectEvent, sprite, DIR_WEST, GetAcroWheeliePedalDirectionAnimNum(DIR_WEST), 8); + if (objectEvent->directionOverwrite) + InitMoveInPlace(objectEvent, sprite, objectEvent->directionOverwrite, GetAcroWheeliePedalDirectionAnimNum(objectEvent->directionOverwrite), 8); + else + InitMoveInPlace(objectEvent, sprite, DIR_WEST, GetAcroWheeliePedalDirectionAnimNum(DIR_WEST), 8); return MovementAction_WalkInPlace_Step1(objectEvent, sprite); } static bool8 MovementAction_AcroWheelieInPlaceRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitMoveInPlace(objectEvent, sprite, DIR_EAST, GetAcroWheeliePedalDirectionAnimNum(DIR_EAST), 8); + if (objectEvent->directionOverwrite) + InitMoveInPlace(objectEvent, sprite, objectEvent->directionOverwrite, GetAcroWheeliePedalDirectionAnimNum(objectEvent->directionOverwrite), 8); + else + InitMoveInPlace(objectEvent, sprite, DIR_EAST, GetAcroWheeliePedalDirectionAnimNum(DIR_EAST), 8); return MovementAction_WalkInPlace_Step1(objectEvent, sprite); } @@ -9345,7 +9641,10 @@ static bool8 MovementAction_AcroPopWheelieMoveUp_Step1(struct ObjectEvent *objec static bool8 MovementAction_AcroPopWheelieMoveLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroPopWheelie(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_1); + if (objectEvent->directionOverwrite) + InitAcroPopWheelie(objectEvent, sprite, objectEvent->directionOverwrite, 1); + else + InitAcroPopWheelie(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_1); return MovementAction_AcroPopWheelieMoveLeft_Step1(objectEvent, sprite); } @@ -9361,7 +9660,10 @@ static bool8 MovementAction_AcroPopWheelieMoveLeft_Step1(struct ObjectEvent *obj static bool8 MovementAction_AcroPopWheelieMoveRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroPopWheelie(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_1); + if (objectEvent->directionOverwrite) + InitAcroPopWheelie(objectEvent, sprite, objectEvent->directionOverwrite, 1); + else + InitAcroPopWheelie(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_1); return MovementAction_AcroPopWheelieMoveRight_Step1(objectEvent, sprite); } @@ -9415,7 +9717,10 @@ static bool8 MovementAction_AcroWheelieMoveUp_Step1(struct ObjectEvent *objectEv static bool8 MovementAction_AcroWheelieMoveLeft_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroWheelieMove(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_1); + if (objectEvent->directionOverwrite) + InitAcroWheelieMove(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FAST_1); + else + InitAcroWheelieMove(objectEvent, sprite, DIR_WEST, MOVE_SPEED_FAST_1); return MovementAction_AcroWheelieMoveLeft_Step1(objectEvent, sprite); } @@ -9431,7 +9736,10 @@ static bool8 MovementAction_AcroWheelieMoveLeft_Step1(struct ObjectEvent *object static bool8 MovementAction_AcroWheelieMoveRight_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - InitAcroWheelieMove(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_1); + if (objectEvent->directionOverwrite) + InitAcroWheelieMove(objectEvent, sprite, objectEvent->directionOverwrite, MOVE_SPEED_FAST_1); + else + InitAcroWheelieMove(objectEvent, sprite, DIR_EAST, MOVE_SPEED_FAST_1); return MovementAction_AcroWheelieMoveRight_Step1(objectEvent, sprite); } @@ -9818,6 +10126,9 @@ static void GetGroundEffectFlags_LongGrassOnBeginStep(struct ObjectEvent *objEve static void GetGroundEffectFlags_Tracks(struct ObjectEvent *objEvent, u32 *flags) { + if (objEvent->directionOverwrite) + return; + if (MetatileBehavior_IsDeepSand(objEvent->previousMetatileBehavior)) { *flags |= GROUND_EFFECT_FLAG_DEEP_SAND; @@ -10353,12 +10664,13 @@ static void DoTracksGroundEffect_BikeTireTracks(struct ObjectEvent *objEvent, st if (objEvent->currentCoords.x != objEvent->previousCoords.x || objEvent->currentCoords.y != objEvent->previousCoords.y) { + u8 movementDir = (objEvent->previousMovementDirection > DIR_EAST) ? (objEvent->previousMovementDirection - DIR_EAST) : objEvent->previousMovementDirection; + gFieldEffectArguments[0] = objEvent->previousCoords.x; gFieldEffectArguments[1] = objEvent->previousCoords.y; gFieldEffectArguments[2] = 149; gFieldEffectArguments[3] = 2; - gFieldEffectArguments[4] = - bikeTireTracks_Transitions[objEvent->previousMovementDirection][objEvent->facingDirection - 5]; + gFieldEffectArguments[4] = bikeTireTracks_Transitions[movementDir][objEvent->facingDirection - 5]; switch(groundEffect) { diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index e958a93df..609e1ce8d 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -464,8 +464,39 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 { u8 objectEventId; const u8 *script; + s16 currX = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x; + s16 currY = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y; + u8 currBehavior = MapGridGetMetatileBehaviorAt(currX, currY); + + switch (direction) + { + case DIR_EAST: + if (MetatileBehavior_IsSidewaysStairsLeftSideAny(metatileBehavior)) + // sideways stairs left-side to your right -> check northeast + objectEventId = GetObjectEventIdByPosition(currX + 1, currY - 1, position->elevation); + else if (MetatileBehavior_IsSidewaysStairsRightSideAny(currBehavior)) + // on top of right-side stairs -> check southeast + objectEventId = GetObjectEventIdByPosition(currX + 1, currY + 1, position->elevation); + else + // check in front of player + objectEventId = GetObjectEventIdByPosition(position->x, position->y, position->elevation); + break; + case DIR_WEST: + if (MetatileBehavior_IsSidewaysStairsRightSideAny(metatileBehavior)) + // facing sideways stairs right side -> check northwest + objectEventId = GetObjectEventIdByPosition(currX - 1, currY - 1, position->elevation); + else if (MetatileBehavior_IsSidewaysStairsLeftSideAny(currBehavior)) + // on top of left-side stairs -> check southwest + objectEventId = GetObjectEventIdByPosition(currX - 1, currY + 1, position->elevation); + else + // check in front of player + objectEventId = GetObjectEventIdByPosition(position->x, position->y, position->elevation); + break; + default: + objectEventId = GetObjectEventIdByPosition(position->x, position->y, position->elevation); + break; + } - objectEventId = GetObjectEventIdByPosition(position->x, position->y, position->elevation); if (objectEventId == OBJECT_EVENTS_COUNT || gObjectEvents[objectEventId].localId == OBJ_EVENT_ID_PLAYER) { if (MetatileBehavior_IsCounter(metatileBehavior) != TRUE) diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 61da84ef6..eb2f4c60b 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -308,7 +308,22 @@ static bool8 ForcedMovement_None(void) static u8 DoForcedMovement(u8 direction, MovementAction movementAction) { struct PlayerAvatar *playerAvatar = &gPlayerAvatar; - u8 collision = CheckForPlayerAvatarCollision(direction); + u8 collision; + + // Check for sideways stairs onto ice movement. + switch (direction) + { + case DIR_NORTHWEST: + case DIR_SOUTHWEST: + direction = DIR_WEST; + break; + case DIR_NORTHEAST: + case DIR_SOUTHEAST: + direction = DIR_EAST; + break; + } + + collision = CheckForPlayerAvatarCollision(direction); playerAvatar->flags |= PLAYER_AVATAR_FLAG_FORCED; if (collision) @@ -541,7 +556,7 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys) if ((heldKeys & B_BUTTON) && FlagGet(FLAG_SYS_B_DASH) && !IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior)) { - if (PlayerIsMovingOnRockStairs(direction)) + if (ObjectMovingOnRockStairs(&gObjectEvents[gPlayerAvatar.objectEventId], direction)) PlayerRunSlow(direction); else PlayerRun(direction); @@ -555,21 +570,25 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys) } else { - if (PlayerIsMovingOnRockStairs(direction)) + if (ObjectMovingOnRockStairs(&gObjectEvents[gPlayerAvatar.objectEventId], direction)) PlayerWalkSlow(direction); else PlayerWalkNormal(direction); } } -bool32 PlayerIsMovingOnRockStairs(u8 direction) +bool32 ObjectMovingOnRockStairs(struct ObjectEvent *objectEvent, u8 direction) { - struct ObjectEvent * objectEvent; +#if SLOW_MOVEMENT_ON_STAIRS == TRUE s16 x, y; - objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId]; x = objectEvent->currentCoords.x; y = objectEvent->currentCoords.y; + + // TODO followers on sideways stairs + if (IsFollowerVisible() && GetFollowerObject() != NULL && (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER)) + return FALSE; + switch (direction) { case DIR_NORTH: @@ -577,9 +596,21 @@ bool32 PlayerIsMovingOnRockStairs(u8 direction) case DIR_SOUTH: MoveCoords(DIR_SOUTH, &x, &y); return MetatileBehavior_IsRockStairs(MapGridGetMetatileBehaviorAt(x, y)); + case DIR_WEST: + case DIR_EAST: + case DIR_NORTHEAST: + case DIR_NORTHWEST: + case DIR_SOUTHWEST: + case DIR_SOUTHEAST: + // directionOverwrite is only used for sideways stairs motion + if (objectEvent->directionOverwrite) + return TRUE; default: return FALSE; } +#else + return FALSE; +#endif } static u8 CheckForPlayerAvatarCollision(u8 direction) @@ -2385,3 +2416,34 @@ static u8 TeleportAnim_RotatePlayer(struct ObjectEvent *object, s16 *a1) #undef tDeltaY #undef tRotationTimer #undef tState + +//sideways stairs +u8 GetRightSideStairsDirection(u8 direction) +{ + switch (direction) + { + case DIR_WEST: + return DIR_NORTHWEST; + case DIR_EAST: + return DIR_SOUTHEAST; + default: + if (direction > DIR_EAST) + direction -= DIR_EAST; + return direction; + } +} + +u8 GetLeftSideStairsDirection(u8 direction) +{ + switch (direction) + { + case DIR_WEST: + return DIR_SOUTHWEST; + case DIR_EAST: + return DIR_NORTHEAST; + default: + if (direction > DIR_EAST) + direction -= DIR_EAST; + return direction; + } +} diff --git a/src/metatile_behavior.c b/src/metatile_behavior.c index c01280df4..a18ef247c 100644 --- a/src/metatile_behavior.c +++ b/src/metatile_behavior.c @@ -1042,3 +1042,71 @@ bool8 MetatileBehavior_IsTrainerTowerMonitor(u8 metatileBehavior) else return FALSE; } + +bool8 MetatileBehavior_IsSidewaysStairsRightSide(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_RIGHT_SIDE || metatileBehavior == MB_SIDEWAYS_STAIRS_RIGHT_SIDE_BOTTOM) + return TRUE; + else + return FALSE; +} + +bool8 MetatileBehavior_IsSidewaysStairsLeftSide(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_LEFT_SIDE || metatileBehavior == MB_SIDEWAYS_STAIRS_LEFT_SIDE_BOTTOM) + return TRUE; + else + return FALSE; +} + +bool8 MetatileBehavior_IsSidewaysStairsRightSideTop(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_RIGHT_SIDE_TOP) + return TRUE; + else + return FALSE; +} + +bool8 MetatileBehavior_IsSidewaysStairsLeftSideTop(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_LEFT_SIDE_TOP) + return TRUE; + else + return FALSE; +} + +bool8 MetatileBehavior_IsSidewaysStairsRightSideBottom(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_RIGHT_SIDE_BOTTOM) + return TRUE; + else + return FALSE; +} + +bool8 MetatileBehavior_IsSidewaysStairsLeftSideBottom(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_LEFT_SIDE_BOTTOM) + return TRUE; + else + return FALSE; +} + +bool8 MetatileBehavior_IsSidewaysStairsRightSideAny(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_RIGHT_SIDE + || metatileBehavior == MB_SIDEWAYS_STAIRS_RIGHT_SIDE_BOTTOM + || metatileBehavior == MB_SIDEWAYS_STAIRS_RIGHT_SIDE_TOP) + return TRUE; + else + return FALSE; +} + +bool8 MetatileBehavior_IsSidewaysStairsLeftSideAny(u8 metatileBehavior) +{ + if (metatileBehavior == MB_SIDEWAYS_STAIRS_LEFT_SIDE + || metatileBehavior == MB_SIDEWAYS_STAIRS_LEFT_SIDE_BOTTOM + || metatileBehavior == MB_SIDEWAYS_STAIRS_LEFT_SIDE_TOP) + return TRUE; + else + return FALSE; +} diff --git a/src/scrcmd.c b/src/scrcmd.c index ca733d3e4..00b576f0c 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -1251,6 +1251,21 @@ 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)); @@ -1260,24 +1275,21 @@ bool8 ScrCmd_applymovement(struct ScriptContext * ctx) Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); // When applying script movements to follower, it may have frozen animation that must be cleared - if (localId == OBJ_EVENT_ID_FOLLOWER && (objEvent = GetFollowerObject()) && objEvent->frozen) + if ((localId == OBJ_EVENT_ID_FOLLOWER && (objEvent = GetFollowerObject()) && objEvent->frozen) + || ((objEvent = &gObjectEvents[GetObjectEventIdByLocalId(localId)]) && IS_OW_MON_OBJ(objEvent))) { ClearObjectEventMovement(objEvent, &gSprites[objEvent->spriteId]); gSprites[objEvent->spriteId].animCmdIndex = 0; // Reset start frame of animation } + + gObjectEvents[GetObjectEventIdByLocalId(localId)].directionOverwrite = DIR_NONE; 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) + && !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; } @@ -1291,6 +1303,7 @@ bool8 ScrCmd_applymovementat(struct ScriptContext * ctx) Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + gObjectEvents[GetObjectEventIdByLocalId(localId)].directionOverwrite = DIR_NONE; ScriptMovement_StartObjectMovementScript(localId, mapNum, mapGroup, movementScript); sMovingNpcId = localId; return FALSE; @@ -2967,3 +2980,25 @@ void SetMovingNpcId(u16 npcId) { sMovingNpcId = npcId; } + +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; +}