diff --git a/data/scripts/follower.inc b/data/scripts/follower.inc index 3a81e6fabf..6d1d3f0024 100644 --- a/data/scripts/follower.inc +++ b/data/scripts/follower.inc @@ -30,17 +30,6 @@ EventScript_Follower:: bufferlivemonnickname 0 playfirstmoncry callfunc ScrFunc_getfolloweraction - checkpartymove MOVE_FLY - compare VAR_RESULT 6 - goto_if_eq EventScript_FollowerEnd - bufferlivemonnickname 0 - msgbox gText_WantsToFly, MSGBOX_YESNO - switch VAR_RESULT - case NO, EventScript_FollowerEnd - case YES, EventScript_FollowerFly - case MULTI_B_PRESSED, EventScript_FollowerEnd -EventScript_FollowerFly:: - callfunc ScrFunc_followerfly EventScript_FollowerEnd:: waitfieldeffect FLDEFF_EMOTE release diff --git a/gflib/sprite.c b/gflib/sprite.c index 4540f59cc0..0b3447187c 100644 --- a/gflib/sprite.c +++ b/gflib/sprite.c @@ -56,7 +56,6 @@ static void AddSpritesToOamBuffer(void); static u8 CreateSpriteAt(u8 index, const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority); static void ResetOamMatrices(void); static void ResetSprite(struct Sprite *sprite); -static s16 AllocSpriteTiles(u16 tileCount); static void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images); static void ResetAllSprites(void); static void BeginAnim(struct Sprite *sprite); diff --git a/gflib/sprite.h b/gflib/sprite.h index 86527bc4c0..2b2b083103 100644 --- a/gflib/sprite.h +++ b/gflib/sprite.h @@ -297,6 +297,7 @@ void InitSpriteAffineAnim(struct Sprite *sprite); void SetOamMatrixRotationScaling(u8 matrixNum, s16 xScale, s16 yScale, u16 rotation); u16 LoadSpriteSheet(const struct SpriteSheet *sheet); void LoadSpriteSheets(const struct SpriteSheet *sheets); +s16 AllocSpriteTiles(u16 tileCount); u16 AllocTilesForSpriteSheet(struct SpriteSheet *sheet); void AllocTilesForSpriteSheets(struct SpriteSheet *sheets); void LoadTilesForSpriteSheet(const struct SpriteSheet *sheet); diff --git a/graphics/object_events/pics/pokemon/substitute.png b/graphics/object_events/pics/pokemon/substitute.png new file mode 100644 index 0000000000..6be1e2ddcb Binary files /dev/null and b/graphics/object_events/pics/pokemon/substitute.png differ diff --git a/graphics/object_events/pics/pokemon/wailord.png b/graphics/object_events/pics/pokemon/wailord.png index a1fc3a087a..a7032a856a 100644 Binary files a/graphics/object_events/pics/pokemon/wailord.png and b/graphics/object_events/pics/pokemon/wailord.png differ diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index f7c2d36284..cac4333689 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -276,6 +276,11 @@ #define ANIM_RUN_WEST (ANIM_STD_COUNT + 2) #define ANIM_RUN_EAST (ANIM_STD_COUNT + 3) +#define ANIM_EXIT_POKEBALL_FAST_SOUTH (ANIM_STD_COUNT + 0) +#define ANIM_EXIT_POKEBALL_FAST_NORTH (ANIM_STD_COUNT + 1) +#define ANIM_EXIT_POKEBALL_FAST_WEST (ANIM_STD_COUNT + 2) +#define ANIM_EXIT_POKEBALL_FAST_EAST (ANIM_STD_COUNT + 3) + #define ANIM_BUNNY_HOP_BACK_WHEEL_SOUTH (ANIM_STD_COUNT + 0) #define ANIM_BUNNY_HOP_BACK_WHEEL_NORTH (ANIM_STD_COUNT + 1) #define ANIM_BUNNY_HOP_BACK_WHEEL_WEST (ANIM_STD_COUNT + 2) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 1b4f1e1d9f..9c7ab50ede 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -289,6 +289,7 @@ void MovementType_FollowPlayer(struct Sprite *); u8 GetSlideMovementAction(u32); u8 GetJumpMovementAction(u32); u8 GetJump2MovementAction(u32); +u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); u8 CreateCopySpriteAt(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); u8 MovementType_WanderAround_Step0(struct ObjectEvent *, struct Sprite *); diff --git a/include/follower_helper.h b/include/follower_helper.h index 115242fdb9..d8ab0621b1 100644 --- a/include/follower_helper.h +++ b/include/follower_helper.h @@ -125,5 +125,6 @@ enum { }; extern const struct FollowerMsgInfoExtended gFollowerConditionalMessages[COND_MSG_COUNT]; +extern const struct FollowerMessagePool gFollowerBasicMessages[FOLLOWER_EMOTION_LENGTH]; #endif //GUARD_FOLLOWER_HELPER_H diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 0c07148ba2..9f1a210a27 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -272,6 +272,11 @@ enum { #define PLAYER_AVATAR_FLAG_FORCED_MOVE (1 << 6) #define PLAYER_AVATAR_FLAG_DASH (1 << 7) +#define PLAYER_AVATAR_FLAG_BIKE (PLAYER_AVATAR_FLAG_MACH_BIKE | PLAYER_AVATAR_FLAG_ACRO_BIKE) +// Player avatar flags for which follower pokemon are hidden +#define FOLLOWER_INVISIBLE_FLAGS (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER | \ + PLAYER_AVATAR_FLAG_BIKE | PLAYER_AVATAR_FLAG_FORCED_MOVE) + enum { ACRO_BIKE_NORMAL, diff --git a/remote_build.sh b/remote_build.sh index 03e3e54b1a..835520290d 100644 --- a/remote_build.sh +++ b/remote_build.sh @@ -11,7 +11,23 @@ remote_path=$(git remote get-url build | sed -nr -e "s/ssh:\/\/\w+@?\w*://p") # make_cmd=$(git config --local remake.make) make_product=$(git config --local remake.src) make_dest=$(git config --local remake.dest) -set -x +old_head=$(git rev-parse --short @) +set +e +# Make a temp commit for unstaged changes +temp_commit_msg="temp build" +git commit -aem "$temp_commit_msg" +retVal=$? +set -e +# set -x git push build --force-with-lease -ssh $remote_host "cd $remote_path && git reset --hard && git checkout $git_branch && $make_cmd" -scp "$remote_host:$remote_path/$make_product" $make_dest +# { set +x; } 2>/dev/null +# Reset temp commit +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 + fi +fi +set -x +ssh -o "VisualHostKey=no" $remote_host "cd $remote_path && git reset --hard && git checkout $git_branch && $make_cmd" +scp -o "VisualHostKey=no" "$remote_host:$remote_path/$make_product" $make_dest diff --git a/spritesheet_rules.mk b/spritesheet_rules.mk index 902eb1c892..5eb25228a7 100644 --- a/spritesheet_rules.mk +++ b/spritesheet_rules.mk @@ -687,6 +687,9 @@ $(FLDEFFGFXDIR)/secret_power_tree.4bpp: %.4bpp: %.png $(FLDEFFGFXDIR)/record_mix_lights.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 4 -mheight 1 +$(OBJEVENTGFXDIR)/pokemon/substitute.4bpp: %.4bpp: %.png + $(GFX) $< $@ -mwidth 4 -mheight 4 + $(OBJEVENTGFXDIR)/pokemon/bulbasaur.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 4 -mheight 4 diff --git a/src/data/object_events/object_event_anims.h b/src/data/object_events/object_event_anims.h index 8bb579e01b..1faf48e759 100755 --- a/src/data/object_events/object_event_anims.h +++ b/src/data/object_events/object_event_anims.h @@ -217,6 +217,12 @@ static const union AnimCmd sAnim_FaceEast2F[] = ANIMCMD_JUMP(0), }; +static const union AnimCmd sAnim_FaceEast2F_Asym[] = +{ + ANIMCMD_FRAME(6, 16), + ANIMCMD_JUMP(0), +}; + static const union AnimCmd sAnim_GoSouth[] = { ANIMCMD_FRAME(3, 8), @@ -246,11 +252,11 @@ static const union AnimCmd sAnim_GoNorth[] = static const union AnimCmd sAnim_GoNorth2F[] = { - ANIMCMD_FRAME(2, 6), - ANIMCMD_FRAME(3, 6), - ANIMCMD_FRAME(3, 6), - ANIMCMD_FRAME(2, 6), - ANIMCMD_JUMP(0), + ANIMCMD_FRAME(2, 6), + ANIMCMD_FRAME(3, 6), + ANIMCMD_FRAME(3, 6), + ANIMCMD_FRAME(2, 6), + ANIMCMD_JUMP(0), }; static const union AnimCmd sAnim_GoWest[] = @@ -289,6 +295,15 @@ static const union AnimCmd sAnim_GoEast2F[] = ANIMCMD_JUMP(0), }; +static const union AnimCmd sAnim_GoEast2F_Asym[] = +{ + ANIMCMD_FRAME(6, 6), + ANIMCMD_FRAME(7, 6), + ANIMCMD_FRAME(7, 6), + ANIMCMD_FRAME(6, 6), + ANIMCMD_JUMP(0), +}; + static const union AnimCmd sAnim_GoFastSouth[] = { ANIMCMD_FRAME(3, 4), @@ -361,6 +376,15 @@ static const union AnimCmd sAnim_GoFastEast2F[] = ANIMCMD_JUMP(0), }; +static const union AnimCmd sAnim_GoFastEast2F_Asym[] = +{ + ANIMCMD_FRAME(6, 4), + ANIMCMD_FRAME(7, 4), + ANIMCMD_FRAME(7, 4), + ANIMCMD_FRAME(6, 4), + ANIMCMD_JUMP(0), +}; + static const union AnimCmd sAnim_GoFasterSouth[] = { ANIMCMD_FRAME(3, 2), @@ -422,6 +446,19 @@ static const union AnimCmd sAnim_ExitPokeballEast[] = ANIMCMD_JUMP(0), }; +static const union AnimCmd sAnim_ExitPokeballEast_Asym[] = +{ + ANIMCMD_FRAME(0, 1), + ANIMCMD_FRAME(0, 3), + ANIMCMD_FRAME(0, 1), + ANIMCMD_FRAME(1, 1), + ANIMCMD_FRAME(2, 1), + ANIMCMD_FRAME(3, 1), + ANIMCMD_FRAME(4, 1), + ANIMCMD_FRAME(6, 8), + ANIMCMD_JUMP(0), +}; + static const union AnimCmd sAnim_EnterSouth[] = { ANIMCMD_FRAME(0, 8), @@ -497,6 +534,18 @@ static const union AnimCmd sAnim_EnterEast[] = ANIMCMD_JUMP(0), }; +static const union AnimCmd sAnim_EnterEast_Asym[] = +{ + ANIMCMD_FRAME(6, 8), + ANIMCMD_FRAME(4, 1), + ANIMCMD_FRAME(3, 1), + ANIMCMD_FRAME(2, 1), + ANIMCMD_FRAME(1, 1), + ANIMCMD_FRAME(0, 1), + ANIMCMD_FRAME(0, 3), + ANIMCMD_JUMP(0), +}; + static const union AnimCmd sAnim_GoFastestSouth[] = { ANIMCMD_FRAME(3, 1), @@ -554,6 +603,18 @@ static const union AnimCmd sAnim_ExitPokeballFastEast[] = ANIMCMD_JUMP(0), }; +static const union AnimCmd sAnim_ExitPokeballFastEast_Asym[] = +{ + ANIMCMD_FRAME(0, 1), + ANIMCMD_FRAME(1, 1), + ANIMCMD_FRAME(2, 1), + ANIMCMD_FRAME(3, 1), + ANIMCMD_FRAME(4, 1), + ANIMCMD_FRAME(6, 2), + ANIMCMD_FRAME(6, 1), + ANIMCMD_JUMP(0), +}; + static const union AnimCmd sAnim_GoFastestNorth[] = { ANIMCMD_FRAME(5, 1), @@ -1084,31 +1145,59 @@ static const union AnimCmd *const sAnimTable_Standard[] = { }; static const union AnimCmd *const sAnimTable_Following[] = { - sAnim_FaceSouth, - sAnim_FaceNorth2F, - sAnim_FaceWest2F, - sAnim_FaceEast2F, - sAnim_GoSouth2F, - sAnim_GoNorth2F, - sAnim_GoWest2F, - sAnim_GoEast2F, - sAnim_GoFastSouth2F, - sAnim_GoFastNorth2F, - sAnim_GoFastWest2F, - sAnim_GoFastEast2F, - sAnim_EnterSouth, - sAnim_EnterNorth, - sAnim_EnterWest, - sAnim_EnterEast, - sAnim_ExitPokeballSouth, - sAnim_ExitPokeballNorth, - sAnim_ExitPokeballWest, - sAnim_ExitPokeballEast, - // ANIM_STD_COUNT = - sAnim_ExitPokeballFastSouth, - sAnim_ExitPokeballFastNorth, - sAnim_ExitPokeballFastWest, - sAnim_ExitPokeballFastEast, + [ANIM_STD_FACE_SOUTH] = sAnim_FaceSouth, + [ANIM_STD_FACE_NORTH] = sAnim_FaceNorth2F, + [ANIM_STD_FACE_WEST] = sAnim_FaceWest2F, + [ANIM_STD_FACE_EAST] = sAnim_FaceEast2F, + [ANIM_STD_GO_SOUTH] = sAnim_GoSouth2F, + [ANIM_STD_GO_NORTH] = sAnim_GoNorth2F, + [ANIM_STD_GO_WEST] = sAnim_GoWest2F, + [ANIM_STD_GO_EAST] = sAnim_GoEast2F, + [ANIM_STD_GO_FAST_SOUTH] = sAnim_GoFastSouth2F, + [ANIM_STD_GO_FAST_NORTH] = sAnim_GoFastNorth2F, + [ANIM_STD_GO_FAST_WEST] = sAnim_GoFastWest2F, + [ANIM_STD_GO_FAST_EAST] = sAnim_GoFastEast2F, + // 'Faster' and above used for entering/exiting pokeball + [ANIM_STD_GO_FASTER_SOUTH] = sAnim_EnterSouth, + [ANIM_STD_GO_FASTER_NORTH] = sAnim_EnterNorth, + [ANIM_STD_GO_FASTER_WEST] = sAnim_EnterWest, + [ANIM_STD_GO_FASTER_EAST] = sAnim_EnterEast, + [ANIM_STD_GO_FASTEST_SOUTH] = sAnim_ExitPokeballSouth, + [ANIM_STD_GO_FASTEST_NORTH] = sAnim_ExitPokeballNorth, + [ANIM_STD_GO_FASTEST_WEST] = sAnim_ExitPokeballWest, + [ANIM_STD_GO_FASTEST_EAST] = sAnim_ExitPokeballEast, + [ANIM_EXIT_POKEBALL_FAST_SOUTH] = sAnim_ExitPokeballFastSouth, + [ANIM_EXIT_POKEBALL_FAST_NORTH] = sAnim_ExitPokeballFastNorth, + [ANIM_EXIT_POKEBALL_FAST_WEST] = sAnim_ExitPokeballFastWest, + [ANIM_EXIT_POKEBALL_FAST_EAST] = sAnim_ExitPokeballFastEast, +}; + +// Like the above, but has separate frames for facing right +static const union AnimCmd *const sAnimTable_Following_Asym[] = { + [ANIM_STD_FACE_SOUTH] = sAnim_FaceSouth, + [ANIM_STD_FACE_NORTH] = sAnim_FaceNorth2F, + [ANIM_STD_FACE_WEST] = sAnim_FaceWest2F, + [ANIM_STD_FACE_EAST] = sAnim_FaceEast2F_Asym, + [ANIM_STD_GO_SOUTH] = sAnim_GoSouth2F, + [ANIM_STD_GO_NORTH] = sAnim_GoNorth2F, + [ANIM_STD_GO_WEST] = sAnim_GoWest2F, + [ANIM_STD_GO_EAST] = sAnim_GoEast2F_Asym, + [ANIM_STD_GO_FAST_SOUTH] = sAnim_GoFastSouth2F, + [ANIM_STD_GO_FAST_NORTH] = sAnim_GoFastNorth2F, + [ANIM_STD_GO_FAST_WEST] = sAnim_GoFastWest2F, + [ANIM_STD_GO_FAST_EAST] = sAnim_GoFastEast2F_Asym, + [ANIM_STD_GO_FASTER_SOUTH] = sAnim_EnterSouth, + [ANIM_STD_GO_FASTER_NORTH] = sAnim_EnterNorth, + [ANIM_STD_GO_FASTER_WEST] = sAnim_EnterWest, + [ANIM_STD_GO_FASTER_EAST] = sAnim_EnterEast_Asym, + [ANIM_STD_GO_FASTEST_SOUTH] = sAnim_ExitPokeballSouth, + [ANIM_STD_GO_FASTEST_NORTH] = sAnim_ExitPokeballNorth, + [ANIM_STD_GO_FASTEST_WEST] = sAnim_ExitPokeballWest, + [ANIM_STD_GO_FASTEST_EAST] = sAnim_ExitPokeballEast_Asym, + [ANIM_EXIT_POKEBALL_FAST_SOUTH] = sAnim_ExitPokeballFastSouth, + [ANIM_EXIT_POKEBALL_FAST_NORTH] = sAnim_ExitPokeballFastNorth, + [ANIM_EXIT_POKEBALL_FAST_WEST] = sAnim_ExitPokeballFastWest, + [ANIM_EXIT_POKEBALL_FAST_EAST] = sAnim_ExitPokeballFastEast_Asym, }; static const union AnimCmd *const sAnimTable_HoOh[] = { diff --git a/src/data/object_events/object_event_graphics.h b/src/data/object_events/object_event_graphics.h index 3a658aa15e..4d1a2cae1c 100755 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -392,6 +392,7 @@ const u32 gObjectEventPic_StrangeBall[] = INCBIN_U32("graphics/object_events/pic #endif #endif +const u32 gObjectEventPic_Substitute[] = INCBIN_U32("graphics/object_events/pics/pokemon/substitute.4bpp"); const u32 gObjectEventPic_Bulbasaur[] = INCBIN_U32("graphics/object_events/pics/pokemon/bulbasaur.4bpp"); const u32 gObjectEventPic_Ivysaur[] = INCBIN_U32("graphics/object_events/pics/pokemon/ivysaur.4bpp"); const u32 gObjectEventPic_Venusaur[] = INCBIN_U32("graphics/object_events/pics/pokemon/venusaur.4bpp"); @@ -836,6 +837,8 @@ const u32 gObjectEventPic_RayquazaCutscene[] = INCBIN_U32("graphics/object_event const u16 gObjectEventPal_HoOh[] = INCBIN_U16("graphics/object_events/palettes/ho_oh.gbapal"); const u16 gObjectEventPal_Lugia[] = INCBIN_U16("graphics/object_events/palettes/lugia.gbapal"); +const u16 gObjectEventPal_Substitute[] = INCBIN_U16("graphics/object_events/pics/pokemon/substitute.gbapal"); + const u16 gObjectEventPaletteEmotes[] = INCBIN_U16("graphics/misc/emotes.gbapal"); #if OW_MON_POKEBALLS diff --git a/src/data/object_events/object_event_graphics_info_followers.h b/src/data/object_events/object_event_graphics_info_followers.h index 6eeccde765..744df41706 100644 --- a/src/data/object_events/object_event_graphics_info_followers.h +++ b/src/data/object_events/object_event_graphics_info_followers.h @@ -1,5 +1,7 @@ // Species-indexed pokemon object event table const struct ObjectEventGraphicsInfo gPokemonObjectGraphics[] = { + // placeholder gfx, used when other gfx aren't found + [SPECIES_NONE] = {0xFFFF, OBJ_EVENT_PAL_TAG_SUBSTITUTE, OBJ_EVENT_PAL_TAG_NONE, 512, 32, 32, 2, SHADOW_SIZE_M, FALSE, FALSE, TRACKS_NONE, &gObjectEventBaseOam_32x32, sOamTables_32x32, sAnimTable_Following, sPicTable_Substitute, gDummySpriteAffineAnimTable}, [SPECIES_BULBASAUR] = {0xFFFF, OBJ_EVENT_PAL_TAG_DYNAMIC, OBJ_EVENT_PAL_TAG_NONE, 512, 32, 32, 2, SHADOW_SIZE_M, FALSE, FALSE, TRACKS_FOOT, &gObjectEventBaseOam_32x32, sOamTables_32x32, sAnimTable_Following, sPicTable_Bulbasaur, gDummySpriteAffineAnimTable}, [SPECIES_IVYSAUR] = {0xFFFF, OBJ_EVENT_PAL_TAG_DYNAMIC, OBJ_EVENT_PAL_TAG_NONE, 512, 32, 32, 2, SHADOW_SIZE_M, FALSE, FALSE, TRACKS_FOOT, &gObjectEventBaseOam_32x32, sOamTables_32x32, sAnimTable_Following, sPicTable_Ivysaur, gDummySpriteAffineAnimTable}, [SPECIES_VENUSAUR] = {0xFFFF, OBJ_EVENT_PAL_TAG_DYNAMIC, OBJ_EVENT_PAL_TAG_NONE, 512, 32, 32, 2, SHADOW_SIZE_M, FALSE, FALSE, TRACKS_FOOT, &gObjectEventBaseOam_32x32, sOamTables_32x32, sAnimTable_Following, sPicTable_Venusaur, gDummySpriteAffineAnimTable}, diff --git a/src/data/object_events/object_event_pic_tables.h b/src/data/object_events/object_event_pic_tables.h index bcd5f64e90..b2d2d15689 100755 --- a/src/data/object_events/object_event_pic_tables.h +++ b/src/data/object_events/object_event_pic_tables.h @@ -2117,6 +2117,14 @@ static const struct SpriteFrameImage sPicTable_Ball_STRANGE[] = { #endif +static const struct SpriteFrameImage sPicTable_Substitute[] = { + overworld_frame(gObjectEventPic_Substitute, 4, 4, 0), + overworld_frame(gObjectEventPic_Substitute, 4, 4, 1), + overworld_frame(gObjectEventPic_Substitute, 4, 4, 2), + overworld_frame(gObjectEventPic_Substitute, 4, 4, 3), + overworld_frame(gObjectEventPic_Substitute, 4, 4, 4), + overworld_frame(gObjectEventPic_Substitute, 4, 4, 5), +}; static const struct SpriteFrameImage sPicTable_Bulbasaur[] = { overworld_frame(gObjectEventPic_Bulbasaur, 4, 4, 0), overworld_frame(gObjectEventPic_Bulbasaur, 4, 4, 1), diff --git a/src/data/text/follower_messages.h b/src/data/text/follower_messages.h index 3fbec109cc..7678992f22 100644 --- a/src/data/text/follower_messages.h +++ b/src/data/text/follower_messages.h @@ -50,7 +50,7 @@ static const u8 sHappyMsg27[] = _("Your POKéMON is smelling the scent\nof flowe static const u8 sHappyMsg28[] = _("{STR_VAR_1} seems very happy to see\nyou!"); static const u8 sHappyMsg29[] = _("{STR_VAR_1} faced this way and\ngrinned."); static const u8 sHappyMsg30[] = _("{STR_VAR_1} happily cuddled up to\nyou!"); - // Conditional messages begin here, index 31 +// Conditional messages begin here, index 31 static const u8 sHappyMsg31[] = _("Your POKéMON seems happy about the\ngreat weather."); static const u8 sHappyMsg32[] = _("{STR_VAR_1} is very composed and\nsure of itself!"); @@ -106,7 +106,7 @@ const struct FollowerMsgInfo gFollowerNeutralMessages[] = { static const u8 sSadMsg00[] = _("{STR_VAR_1} is dizzy."); static const u8 sSadMsg01[] = _("{STR_VAR_1} is stepping on your\nfeet!"); static const u8 sSadMsg02[] = _("{STR_VAR_1} seems a little tired."); - // Conditional messages begin, index 3 +// Conditional messages begin, index 3 static const u8 sSadMsg03[] = _("{STR_VAR_1} is not happy."); static const u8 sSadMsg04[] = _("{STR_VAR_1} is going to fall down!\n"); static const u8 sSadMsg05[] = _("{STR_VAR_1} seems to be about to\nfall over!"); @@ -123,7 +123,7 @@ const struct FollowerMsgInfo gFollowerSadMessages[] = { static const u8 sUpsetMsg00[] = _("{STR_VAR_1} seems unhappy somehow…"); static const u8 sUpsetMsg01[] = _("{STR_VAR_1} is making an unhappy\nface."); static const u8 sUpsetMsg02[] = _("…Your POKéMON seems a little\ncold."); - // Conditional messages, index 3 +// Conditional messages, index 3 static const u8 sUpsetMsg03[] = _("{STR_VAR_1} is taking shelter in the\ngrass from the rain."); const struct FollowerMsgInfo gFollowerUpsetMessages[] = { @@ -225,7 +225,7 @@ static const u8 sSurpriseMsg16[] = _("{STR_VAR_1} sensed something strange\nand static const u8 sSurpriseMsg17[] = _("{STR_VAR_1} is scared and snuggled\nup to you!"); static const u8 sSurpriseMsg18[] = _("{STR_VAR_1} is feeling an unusual\npresence…"); static const u8 sSurpriseMsg19[] = _("{STR_VAR_1} is getting tense with\nnervous energy."); - // Conditional messages, index 20 +// Conditional messages, index 20 static const u8 sSurpriseMsg20[] = _("{STR_VAR_1} seems to be very\nsurprised that it is raining!"); const struct FollowerMsgInfo gFollowerSurpriseMessages[] = { diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 0c2116437f..a428dd2aed 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -520,6 +520,8 @@ const u8 gInitialMovementTypeFacingDirections[] = { // Gen VIII #define OBJ_EVENT_PAL_TAG_BALL_STRANGE 0x116A #endif +// Used as a placeholder follower graphic +#define OBJ_EVENT_PAL_TAG_SUBSTITUTE 0x7611 #define OBJ_EVENT_PAL_TAG_EMOTES 0x8002 // Not a real OW palette tag; used for the white flash applied to followers #define OBJ_EVENT_PAL_TAG_WHITE (OBJ_EVENT_PAL_TAG_NONE - 1) @@ -620,6 +622,7 @@ static const struct SpritePalette sObjectEventSpritePalettes[] = { {gObjectEventPal_StrangeBall, OBJ_EVENT_PAL_TAG_BALL_STRANGE}, #endif #endif + {gObjectEventPal_Substitute, OBJ_EVENT_PAL_TAG_SUBSTITUTE}, {gObjectEventPaletteEmotes, OBJ_EVENT_PAL_TAG_EMOTES}, {NULL, OBJ_EVENT_PAL_TAG_NONE}, }; @@ -1578,47 +1581,47 @@ static u8 TrySetupObjectEventSprite(const struct ObjectEventTemplate *objectEven static u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemplate, u8 mapNum, u8 mapGroup, s16 cameraX, s16 cameraY) { - u8 objectEventId; - struct SpriteTemplate spriteTemplate; - struct SpriteFrameImage spriteFrameImage; - const struct ObjectEventGraphicsInfo *graphicsInfo; - const struct SubspriteTable *subspriteTables = NULL; + u8 objectEventId; + struct SpriteTemplate spriteTemplate; + struct SpriteFrameImage spriteFrameImage; + const struct ObjectEventGraphicsInfo *graphicsInfo; + const struct SubspriteTable *subspriteTables = NULL; - graphicsInfo = GetObjectEventGraphicsInfo(objectEventTemplate->graphicsId); - MakeSpriteTemplateFromObjectEventTemplate(objectEventTemplate, &spriteTemplate, &subspriteTables); - spriteFrameImage.size = graphicsInfo->size; - spriteTemplate.images = &spriteFrameImage; - objectEventId = TrySetupObjectEventSprite(objectEventTemplate, &spriteTemplate, mapNum, mapGroup, cameraX, cameraY); - if (objectEventId == OBJECT_EVENTS_COUNT) - return OBJECT_EVENTS_COUNT; + graphicsInfo = GetObjectEventGraphicsInfo(objectEventTemplate->graphicsId); + MakeSpriteTemplateFromObjectEventTemplate(objectEventTemplate, &spriteTemplate, &subspriteTables); + spriteFrameImage.size = graphicsInfo->size; + spriteTemplate.images = &spriteFrameImage; + objectEventId = TrySetupObjectEventSprite(objectEventTemplate, &spriteTemplate, mapNum, mapGroup, cameraX, cameraY); + if (objectEventId == OBJECT_EVENTS_COUNT) + return OBJECT_EVENTS_COUNT; - gSprites[gObjectEvents[objectEventId].spriteId].images = graphicsInfo->images; - if (subspriteTables) - SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); + gSprites[gObjectEvents[objectEventId].spriteId].images = graphicsInfo->images; + if (subspriteTables) + SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); - // Set species based on script header - if (objectEventTemplate->graphicsId == OBJ_EVENT_GFX_OW_MON && objectEventTemplate->script) { - const u8 *script = objectEventTemplate->script; - if (script[0] == 0x7d) { // bufferspeciesname - u16 species; - u8 form; - bool8 shiny; - gObjectEvents[objectEventId].extra.asU16 = script[2] | script[3] << 8; - species = gObjectEvents[objectEventId].extra.mon.species; - form = gObjectEvents[objectEventId].extra.mon.form; - shiny = gObjectEvents[objectEventId].extra.mon.shiny; - FollowerSetGraphics(&gObjectEvents[objectEventId], species, form, shiny); + // Set species based on script header + if (objectEventTemplate->graphicsId == OBJ_EVENT_GFX_OW_MON && objectEventTemplate->script) { + const u8 *script = objectEventTemplate->script; + if (script[0] == 0x7d) { // bufferspeciesname + u16 species; + u8 form; + bool8 shiny; + gObjectEvents[objectEventId].extra.asU16 = script[2] | script[3] << 8; + species = gObjectEvents[objectEventId].extra.mon.species; + form = gObjectEvents[objectEventId].extra.mon.form; + shiny = gObjectEvents[objectEventId].extra.mon.shiny; + FollowerSetGraphics(&gObjectEvents[objectEventId], species, form, shiny); + } + // Set runtime species based on VAR_TEMP_4, if template has a dynamic graphics ID + } else if (objectEventTemplate->graphicsId >= OBJ_EVENT_GFX_VARS && VarGetObjectEventGraphicsId(objectEventTemplate->graphicsId - OBJ_EVENT_GFX_VARS) == OBJ_EVENT_GFX_OW_MON) { + gObjectEvents[objectEventId].extra.asU16 = VarGet(VAR_TEMP_4); + FollowerSetGraphics(&gObjectEvents[objectEventId], + gObjectEvents[objectEventId].extra.mon.species, + gObjectEvents[objectEventId].extra.mon.form, + gObjectEvents[objectEventId].extra.mon.form); } - // Set runtime species based on VAR_TEMP_4, if template has a dynamic graphics ID - } else if (objectEventTemplate->graphicsId >= OBJ_EVENT_GFX_VARS && VarGetObjectEventGraphicsId(objectEventTemplate->graphicsId - OBJ_EVENT_GFX_VARS) == OBJ_EVENT_GFX_OW_MON) { - gObjectEvents[objectEventId].extra.asU16 = VarGet(VAR_TEMP_4); - FollowerSetGraphics(&gObjectEvents[objectEventId], - gObjectEvents[objectEventId].extra.mon.species, - gObjectEvents[objectEventId].extra.mon.form, - gObjectEvents[objectEventId].extra.mon.form); - } - return objectEventId; + return objectEventId; } u8 SpawnSpecialObjectEvent(struct ObjectEventTemplate *objectEventTemplate) @@ -1781,85 +1784,126 @@ 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 - u8 i; - for (i=0; i 0 && !(gPlayerParty[i].box.isEgg || gPlayerParty[i].box.isBadEgg)) - return &gPlayerParty[i]; - } - return NULL; + u32 i; + for (i = 0; i < PARTY_SIZE; i++) { + if (gPlayerParty[i].hp > 0 && !(gPlayerParty[i].box.isEgg || gPlayerParty[i].box.isBadEgg)) + return &gPlayerParty[i]; + } + return NULL; } struct ObjectEvent * GetFollowerObject(void) { // Return follower ObjectEvent or NULL - u8 i; - for (i=0; i < OBJECT_EVENTS_COUNT; i++) { - if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && gObjectEvents[i].active) - return &gObjectEvents[i]; - } - return NULL; + u32 i; + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { + if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && gObjectEvents[i].active) + return &gObjectEvents[i]; + } + return NULL; } // Return graphicsInfo for a pokemon species & form static const struct ObjectEventGraphicsInfo * SpeciesToGraphicsInfo(u16 species, u8 form) { - const struct ObjectEventGraphicsInfo *graphicsInfo; - switch (species) { + const struct ObjectEventGraphicsInfo *graphicsInfo; + switch (species) + { case SPECIES_UNOWN: // Letters >A are defined as species >= NUM_SPECIES, so are not contiguous with A - form %= NUM_UNOWN_FORMS; - graphicsInfo = &gPokemonObjectGraphics[form ? SPECIES_UNOWN_B + form - 1 : species]; - break; + form %= NUM_UNOWN_FORMS; + graphicsInfo = &gPokemonObjectGraphics[form ? SPECIES_UNOWN_B + form - 1 : species]; + break; case SPECIES_CASTFORM: // Sunny, rainy, snowy forms stored separately - graphicsInfo = &gCastformObjectGraphics[form % NUM_CASTFORM_FORMS]; - break; + graphicsInfo = &gCastformObjectGraphics[form % NUM_CASTFORM_FORMS]; + break; default: - graphicsInfo = &gPokemonObjectGraphics[species]; - break; - } - return graphicsInfo->tileTag == 0xFFFF ? graphicsInfo : &gPokemonObjectGraphics[SPECIES_PORYGON]; // avoid OOB access + graphicsInfo = &gPokemonObjectGraphics[species]; + break; + } + // Try to avoid OOB or undefined access + if (graphicsInfo->tileTag == 0 && species < NUM_SPECIES) + return &gPokemonObjectGraphics[SPECIES_NONE]; + else if (graphicsInfo->tileTag != TAG_NONE && species >= NUM_SPECIES) + return &gPokemonObjectGraphics[SPECIES_NONE]; + else + return graphicsInfo; } // Find, or load, the palette for the specified pokemon info static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool8 shiny) { - u8 paletteNum; + u32 paletteNum; // Note that the shiny palette tag is `species + SPECIES_SHINY_TAG`, which must be increased with more pokemon // so that palette tags do not overlap const struct CompressedSpritePalette *spritePalette = &(shiny ? gMonShinyPaletteTable : gMonPaletteTable)[species]; if ((paletteNum = IndexOfSpritePaletteTag(spritePalette->tag)) == 0xFF) { // Load compressed palette - LoadCompressedSpritePalette(spritePalette); - paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present - if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog - UpdateSpritePaletteWithWeather(paletteNum); + LoadCompressedSpritePalette(spritePalette); + paletteNum = IndexOfSpritePaletteTag(spritePalette->tag); // Tag is always present + if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog + UpdateSpritePaletteWithWeather(paletteNum); } return paletteNum; } // Set graphics & sprite for a follower object event by species & shininess. static void FollowerSetGraphics(struct ObjectEvent *objEvent, u16 species, u8 form, bool8 shiny) { - const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form); - objEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; - ObjectEventSetGraphics(objEvent, graphicsInfo); - objEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; - objEvent->extra.mon.species = species; - objEvent->extra.mon.form = form; - objEvent->extra.mon.shiny = shiny; - if (graphicsInfo->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { // Use palette from species palette table - struct Sprite *sprite = &gSprites[objEvent->spriteId]; - // Free palette if otherwise unused - sprite->inUse = FALSE; - FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); - sprite->inUse = TRUE; - sprite->oam.paletteNum = LoadDynamicFollowerPalette(species, form, shiny); - } else if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog - UpdateSpritePaletteWithWeather(gSprites[objEvent->spriteId].oam.paletteNum); + const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form); + objEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; + ObjectEventSetGraphics(objEvent, graphicsInfo); + objEvent->graphicsId = OBJ_EVENT_GFX_OW_MON; + objEvent->extra.mon.species = species; + objEvent->extra.mon.form = form; + objEvent->extra.mon.shiny = shiny; + if (graphicsInfo->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { // Use palette from species palette table + struct Sprite *sprite = &gSprites[objEvent->spriteId]; + // Free palette if otherwise unused + sprite->inUse = FALSE; + FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); + sprite->inUse = TRUE; + sprite->oam.paletteNum = LoadDynamicFollowerPalette(species, form, shiny); + } else if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog + UpdateSpritePaletteWithWeather(gSprites[objEvent->spriteId].oam.paletteNum); } -// Like FollowerSetGraphics, but does not reposition sprite; intended to be used for mid-movement form changes, etc. -// TODO: Reposition sprite if size changes +// Free a sprite's current tiles and reallocate with a new size +// Used when changing to a gfx info with a larger size +static s16 ReallocSpriteTiles(struct Sprite *sprite, u32 byteSize) { + s16 i; + bool32 wasVisible = sprite->invisible; + sprite->invisible = TRUE; + + i = CopySprite(sprite, sprite->x, sprite->y, 0xFF); + if (i < MAX_SPRITES) { + DestroySprite(&gSprites[i]); + i = AllocSpriteTiles(byteSize / TILE_SIZE_4BPP); + if (i >= 0) { + // Fill the allocated area with zeroes + // To avoid visual glitches if the frame hasn't been copied yet + CpuFastFill16(0, (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * i, byteSize); + sprite->oam.tileNum = i; + } + } else { + i = -1; + } + + sprite->invisible = wasVisible; + return i; +} + +// Like FollowerSetGraphics, but does not recenter sprite on a metatile +// Intended to be used for mid-movement form changes, etc. static void RefreshFollowerGraphics(struct ObjectEvent *objEvent) { u16 species = objEvent->extra.mon.species; u8 form = objEvent->extra.mon.form; u8 shiny = objEvent->extra.mon.shiny; const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form); struct Sprite *sprite = &gSprites[objEvent->spriteId]; - u8 i = FindObjectEventPaletteIndexByTag(graphicsInfo->paletteTag); + u32 i = FindObjectEventPaletteIndexByTag(graphicsInfo->paletteTag); + + #if LARGE_OW_SUPPORT + // If gfx size changes, we need to reallocate tiles + if (graphicsInfo->oam->size != sprite->oam.size) { + ReallocSpriteTiles(sprite, graphicsInfo->images->size); + // Add difference in Y vectors + sprite->y += -(graphicsInfo->height >> 1) - sprite->centerToCornerVecY; + } + #endif sprite->oam.shape = graphicsInfo->oam->shape; sprite->oam.size = graphicsInfo->oam->size; @@ -1924,118 +1968,112 @@ static bool8 GetFollowerInfo(u16 *species, u8 *form, u8 *shiny) { } void UpdateFollowingPokemon(void) { // Update following pokemon if any - struct ObjectEvent *objEvent = GetFollowerObject(); - struct Sprite *sprite; - u16 species; - bool8 shiny; - u8 form; - // Avoid spawning large (>32x32) follower pokemon inside buildings - if (GetFollowerInfo(&species, &form, &shiny) && !(gMapHeader.mapType == MAP_TYPE_INDOOR && SpeciesToGraphicsInfo(species, 0)->height > 32) && !FlagGet(FLAG_TEMP_HIDE_FOLLOWER)) { + struct ObjectEvent *objEvent = GetFollowerObject(); + struct Sprite *sprite; + u16 species; + bool8 shiny; + u8 form; + // Don't spawn follower if: + // 1. GetFollowerInfo returns FALSE + // 2. Map is indoors and gfx is larger than 32x32 + // 3. flag is set + if (!GetFollowerInfo(&species, &form, &shiny) || + (gMapHeader.mapType == MAP_TYPE_INDOOR && SpeciesToGraphicsInfo(species, 0)->oam->size > ST_OAM_SIZE_2) || + FlagGet(FLAG_TEMP_HIDE_FOLLOWER)) + { + RemoveFollowingPokemon(); + return; + } + if (objEvent == NULL) { // Spawn follower - struct ObjectEventTemplate template = { - .localId = OBJ_EVENT_ID_FOLLOWER, - .graphicsId = OBJ_EVENT_GFX_OW_MON, - .flagId = 0, - .x = gSaveBlock1Ptr->pos.x, - .y = gSaveBlock1Ptr->pos.y, - // If player active, copy player elevation - .elevation = gObjectEvents[gPlayerAvatar.objectEventId].active ? gObjectEvents[gPlayerAvatar.objectEventId].currentElevation : 3, - .movementType = MOVEMENT_TYPE_FOLLOW_PLAYER, - }; - objEvent = &gObjectEvents[SpawnSpecialObjectEvent(&template)]; - objEvent->invisible = TRUE; + u32 objId = gPlayerAvatar.objectEventId; + struct ObjectEventTemplate template = { + .localId = OBJ_EVENT_ID_FOLLOWER, + .graphicsId = OBJ_EVENT_GFX_OW_MON, + .flagId = 0, + .x = gSaveBlock1Ptr->pos.x, + .y = gSaveBlock1Ptr->pos.y, + // If player active, copy player elevation + .elevation = gObjectEvents[objId].active ? gObjectEvents[objId].currentElevation : 3, + .movementType = MOVEMENT_TYPE_FOLLOW_PLAYER, + }; + if ((objId = SpawnSpecialObjectEvent(&template)) >= OBJECT_EVENTS_COUNT) + return; + objEvent = &gObjectEvents[objId]; + objEvent->invisible = TRUE; } sprite = &gSprites[objEvent->spriteId]; // Follower appearance changed; move to player and set invisible - if (species != objEvent->extra.mon.species || shiny != objEvent->extra.mon.shiny || form != objEvent->extra.mon.form) { - MoveObjectEventToMapCoords(objEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); - objEvent->invisible = TRUE; + if (species != objEvent->extra.mon.species || + shiny != objEvent->extra.mon.shiny || + form != objEvent->extra.mon.form) + { + MoveObjectEventToMapCoords(objEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); + objEvent->invisible = TRUE; } FollowerSetGraphics(objEvent, species, form, shiny); sprite->data[6] = 0; // set animation data objEvent->extra.mon.species = species; objEvent->extra.mon.shiny = shiny; objEvent->extra.mon.form = form; - } else { - RemoveFollowingPokemon(); - } } void RemoveFollowingPokemon(void) { // Remove follower object. Idempotent. - struct ObjectEvent *objectEvent = GetFollowerObject(); - if (objectEvent == NULL) - return; - RemoveObjectEvent(objectEvent); + struct ObjectEvent *objectEvent = GetFollowerObject(); + if (objectEvent == NULL) + return; + RemoveObjectEvent(objectEvent); } -static bool8 IsFollowerVisible(void) { // Determine whether follower *should* be visible - return - !(TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_ACRO_BIKE | PLAYER_AVATAR_FLAG_MACH_BIKE) - || MetatileBehavior_IsSurfableWaterOrUnderwater(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) - || MetatileBehavior_IsSurfableWaterOrUnderwater(gObjectEvents[gPlayerAvatar.objectEventId].previousMetatileBehavior) - || MetatileBehavior_IsForcedMovementTile(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) - || MetatileBehavior_IsForcedMovementTile(gObjectEvents[gPlayerAvatar.objectEventId].previousMetatileBehavior) - || gWeatherPtr->currWeather == WEATHER_UNDERWATER - || gWeatherPtr->currWeather == WEATHER_UNDERWATER_BUBBLES); +static bool32 IsFollowerVisible(void) { // Determine whether follower *should* be visible + return + !(TestPlayerAvatarFlags(FOLLOWER_INVISIBLE_FLAGS) + || MetatileBehavior_IsSurfableWaterOrUnderwater(gObjectEvents[gPlayerAvatar.objectEventId].previousMetatileBehavior) + || MetatileBehavior_IsForcedMovementTile(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior)); } static bool8 SpeciesHasType(u16 species, u8 type) { - return gSpeciesInfo[species].types[0] == type || gSpeciesInfo[species].types[1] == type; + return gSpeciesInfo[species].types[0] == type || gSpeciesInfo[species].types[1] == type; } // Returns a random index according to a list of weights static u8 RandomWeightedIndex(u8 *weights, u8 length) { - u8 i; - u16 random_value; - u16 cum_weight = 0; - for (i = 0; i < length; i++) - cum_weight += weights[i]; - random_value = Random() % cum_weight; - cum_weight = 0; - for (i = 0; i < length; i++) { - cum_weight += weights[i]; - if (random_value <= cum_weight) - return i; - } + u32 i; + u16 random_value; + u16 weightSum = 0; + for (i = 0; i < length; i++) + weightSum += weights[i]; + random_value = Random() % weightSum; + weightSum = 0; + for (i = 0; i < length; i++) { + weightSum += weights[i]; + if (random_value <= weightSum) + return i; + } } -// Pool of "unconditional" follower messages TODO: Should this be elsewhere ? -static const struct FollowerMessagePool followerBasicMessages[] = { - [FOLLOWER_EMOTION_HAPPY] = {gFollowerHappyMessages, EventScript_FollowerGeneric, N_FOLLOWER_HAPPY_MESSAGES}, - [FOLLOWER_EMOTION_NEUTRAL] = {gFollowerNeutralMessages, EventScript_FollowerGeneric, N_FOLLOWER_NEUTRAL_MESSAGES}, - [FOLLOWER_EMOTION_SAD] = {gFollowerSadMessages, EventScript_FollowerGeneric, N_FOLLOWER_SAD_MESSAGES}, - [FOLLOWER_EMOTION_UPSET] = {gFollowerUpsetMessages, EventScript_FollowerGeneric, N_FOLLOWER_UPSET_MESSAGES}, - [FOLLOWER_EMOTION_ANGRY] = {gFollowerAngryMessages, EventScript_FollowerGeneric, N_FOLLOWER_ANGRY_MESSAGES}, - [FOLLOWER_EMOTION_PENSIVE] = {gFollowerPensiveMessages, EventScript_FollowerGeneric, N_FOLLOWER_PENSIVE_MESSAGES}, - [FOLLOWER_EMOTION_LOVE] = {gFollowerLoveMessages, EventScript_FollowerGeneric, N_FOLLOWER_LOVE_MESSAGES}, - [FOLLOWER_EMOTION_SURPRISE] = {gFollowerSurpriseMessages, EventScript_FollowerGeneric, N_FOLLOWER_SURPRISE_MESSAGES}, - [FOLLOWER_EMOTION_CURIOUS] = {gFollowerCuriousMessages, EventScript_FollowerGeneric, N_FOLLOWER_CURIOUS_MESSAGES}, - [FOLLOWER_EMOTION_MUSIC] = {gFollowerMusicMessages, EventScript_FollowerGeneric, N_FOLLOWER_MUSIC_MESSAGES}, - [FOLLOWER_EMOTION_POISONED] = {gFollowerPoisonedMessages, EventScript_FollowerGeneric, N_FOLLOWER_POISONED_MESSAGES}, -}; - // Display an emote above an object event // Note that this is not a movement action static void ObjectEventEmote(struct ObjectEvent *objEvent, u8 emotion) { - emotion %= FOLLOWER_EMOTION_LENGTH; - ObjectEventGetLocalIdAndMap(objEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]); - gFieldEffectArguments[7] = emotion; - FieldEffectStart(FLDEFF_EMOTE); + emotion %= FOLLOWER_EMOTION_LENGTH; + ObjectEventGetLocalIdAndMap(objEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]); + gFieldEffectArguments[7] = emotion; + FieldEffectStart(FLDEFF_EMOTE); } // Script-accessible version of the above bool8 ScrFunc_emote(struct ScriptContext *ctx) { - u8 localId = ScriptReadByte(ctx); - u8 emotion = ScriptReadByte(ctx) % FOLLOWER_EMOTION_LENGTH; - u8 i = GetObjectEventIdByLocalId(localId); - if (i < OBJECT_EVENTS_COUNT) - ObjectEventEmote(&gObjectEvents[i], emotion); - return FALSE; + u8 localId = ScriptReadByte(ctx); + u8 emotion = ScriptReadByte(ctx) % FOLLOWER_EMOTION_LENGTH; + u8 i = GetObjectEventIdByLocalId(localId); + if (i < OBJECT_EVENTS_COUNT) + ObjectEventEmote(&gObjectEvents[i], emotion); + return FALSE; } struct SpecialEmote { // Used for storing conditional emotes - u16 index; - u8 emotion; + u16 index; + u8 emotion; }; // Find and return direction of metatile behavior within distance @@ -2060,184 +2098,198 @@ static u32 FindMetatileBehaviorWithinRange(s32 x, s32 y, u32 mb, u8 distance) { // Call an applicable follower message script bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big switch for follower messages { - u16 species; - s32 multi, multi2; - struct SpecialEmote cond_emotes[16] = {0}; - u8 emotion, n_choices = 0; - struct ObjectEvent *objEvent = GetFollowerObject(); - struct Pokemon *mon = GetFirstLiveMon(); - u8 emotion_weight[FOLLOWER_EMOTION_LENGTH] = { - [FOLLOWER_EMOTION_HAPPY] = 10, - [FOLLOWER_EMOTION_NEUTRAL] = 15, - [FOLLOWER_EMOTION_SAD] = 5, - [FOLLOWER_EMOTION_UPSET] = 15, - [FOLLOWER_EMOTION_ANGRY] = 15, - [FOLLOWER_EMOTION_PENSIVE] = 15, - [FOLLOWER_EMOTION_SURPRISE] = 10, // TODO: Scale this with how long the follower has been out? - [FOLLOWER_EMOTION_CURIOUS] = 10, // TODO: Increase this if there is an item nearby? - [FOLLOWER_EMOTION_MUSIC] = 15, - }; - u32 i, j; - bool32 pickedCondition = FALSE; - if (mon == NULL) { - ScriptCall(ctx, EventScript_FollowerLovesYou); - return FALSE; - } - // If map is not flyable, set the script to jump past the fly check TODO: Should followers ask to fly? - if (TRUE || !Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType)) + u32 species; + s32 multi, multi2; + struct SpecialEmote condEmotes[16] = {0}; + u32 condCount = 0; + u32 emotion; + struct ObjectEvent *objEvent = GetFollowerObject(); + struct Pokemon *mon = GetFirstLiveMon(); + u8 emotion_weight[FOLLOWER_EMOTION_LENGTH] = { + [FOLLOWER_EMOTION_HAPPY] = 10, + [FOLLOWER_EMOTION_NEUTRAL] = 15, + [FOLLOWER_EMOTION_SAD] = 5, + [FOLLOWER_EMOTION_UPSET] = 15, + [FOLLOWER_EMOTION_ANGRY] = 15, + [FOLLOWER_EMOTION_PENSIVE] = 15, + [FOLLOWER_EMOTION_SURPRISE] = 10, + [FOLLOWER_EMOTION_CURIOUS] = 10, + [FOLLOWER_EMOTION_MUSIC] = 15, + }; + u32 i, j; + bool32 pickedCondition = FALSE; + if (mon == NULL) { // failsafe + ScriptCall(ctx, EventScript_FollowerLovesYou); + return FALSE; + } + // Set the script to the very end; we'll be calling another script dynamically ScriptJump(ctx, EventScript_FollowerEnd); - species = GetMonData(mon, MON_DATA_SPECIES); - multi = GetMonData(mon, MON_DATA_FRIENDSHIP); - if (multi > 80) { - emotion_weight[FOLLOWER_EMOTION_HAPPY] = 20; - emotion_weight[FOLLOWER_EMOTION_UPSET] = 5; - emotion_weight[FOLLOWER_EMOTION_ANGRY] = 5; - emotion_weight[FOLLOWER_EMOTION_LOVE] = 20; - emotion_weight[FOLLOWER_EMOTION_MUSIC] = 20; - } - if (multi > 170) { - emotion_weight[FOLLOWER_EMOTION_HAPPY] = 30; - emotion_weight[FOLLOWER_EMOTION_LOVE] = 30; - } - // Conditional messages follow - // Weather-related - if (GetCurrentWeather() == WEATHER_SUNNY_CLOUDS) - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_HAPPY, .index=31}; - // Health & status-related - multi = mon->hp * 100 / mon->maxHP; - if (multi < 20) { - emotion_weight[FOLLOWER_EMOTION_SAD] = 30; - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=4}; - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=5}; - } - if (multi < 50 || mon->status & STATUS1_PARALYSIS) { - emotion_weight[FOLLOWER_EMOTION_SAD] = 30; - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=6}; - } - // Gym type advantage/disadvantage scripts - if (GetCurrentMapMusic() == MUS_GYM || GetCurrentMapMusic() == MUS_RG_GYM) { - switch (gMapHeader.regionMapSectionId) - { - case MAPSEC_RUSTBORO_CITY: - case MAPSEC_PEWTER_CITY: - multi = TYPE_ROCK; - break; - case MAPSEC_DEWFORD_TOWN: - multi = TYPE_FIGHTING; - break; - case MAPSEC_MAUVILLE_CITY: - case MAPSEC_VERMILION_CITY: - multi = TYPE_ELECTRIC; - break; - case MAPSEC_LAVARIDGE_TOWN: - case MAPSEC_CINNABAR_ISLAND: - multi = TYPE_FIRE; - break; - case MAPSEC_PETALBURG_CITY: - multi = TYPE_NORMAL; - break; - case MAPSEC_FORTREE_CITY: - multi = TYPE_FLYING; - break; - case MAPSEC_MOSSDEEP_CITY: - case MAPSEC_SAFFRON_CITY: - multi = TYPE_PSYCHIC; - break; - case MAPSEC_SOOTOPOLIS_CITY: - case MAPSEC_CERULEAN_CITY: - multi = TYPE_WATER; - break; - case MAPSEC_CELADON_CITY: - multi = TYPE_GRASS; - break; - case MAPSEC_FUCHSIA_CITY: - multi = TYPE_POISON; - break; - case MAPSEC_VIRIDIAN_CITY: - multi = TYPE_GROUND; - break; - default: - multi = NUMBER_OF_MON_TYPES; + species = GetMonData(mon, MON_DATA_SPECIES); + multi = GetMonData(mon, MON_DATA_FRIENDSHIP); + if (multi > 80) { + emotion_weight[FOLLOWER_EMOTION_HAPPY] = 20; + emotion_weight[FOLLOWER_EMOTION_UPSET] = 5; + emotion_weight[FOLLOWER_EMOTION_ANGRY] = 5; + emotion_weight[FOLLOWER_EMOTION_LOVE] = 20; + emotion_weight[FOLLOWER_EMOTION_MUSIC] = 20; } - if (multi < NUMBER_OF_MON_TYPES) { - multi = GetTypeEffectiveness(mon, multi); - if (multi & (MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_NO_EFFECT)) - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_HAPPY, .index=32}; - else if (multi & MOVE_RESULT_SUPER_EFFECTIVE) - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=7}; + if (multi > 170) { + emotion_weight[FOLLOWER_EMOTION_HAPPY] = 30; + emotion_weight[FOLLOWER_EMOTION_LOVE] = 30; + } + // Special C-based conditions follower + // Weather-related + if (GetCurrentWeather() == WEATHER_SUNNY_CLOUDS) + condEmotes[condCount++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_HAPPY, .index=31}; + // Health & status-related + multi = mon->hp * 100 / mon->maxHP; + if (multi < 20) { + emotion_weight[FOLLOWER_EMOTION_SAD] = 30; + condEmotes[condCount++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=4}; + condEmotes[condCount++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=5}; + } + if (multi < 50 || mon->status & STATUS1_PARALYSIS) { + emotion_weight[FOLLOWER_EMOTION_SAD] = 30; + condEmotes[condCount++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=6}; + } + // Gym type advantage/disadvantage + if (GetCurrentMapMusic() == MUS_GYM || GetCurrentMapMusic() == MUS_RG_GYM) { + switch (gMapHeader.regionMapSectionId) + { + case MAPSEC_RUSTBORO_CITY: + case MAPSEC_PEWTER_CITY: + multi = TYPE_ROCK; + break; + case MAPSEC_DEWFORD_TOWN: + multi = TYPE_FIGHTING; + break; + case MAPSEC_MAUVILLE_CITY: + case MAPSEC_VERMILION_CITY: + multi = TYPE_ELECTRIC; + break; + case MAPSEC_LAVARIDGE_TOWN: + case MAPSEC_CINNABAR_ISLAND: + multi = TYPE_FIRE; + break; + case MAPSEC_PETALBURG_CITY: + multi = TYPE_NORMAL; + break; + case MAPSEC_FORTREE_CITY: + multi = TYPE_FLYING; + break; + case MAPSEC_MOSSDEEP_CITY: + case MAPSEC_SAFFRON_CITY: + multi = TYPE_PSYCHIC; + break; + case MAPSEC_SOOTOPOLIS_CITY: + case MAPSEC_CERULEAN_CITY: + multi = TYPE_WATER; + break; + case MAPSEC_CELADON_CITY: + multi = TYPE_GRASS; + break; + case MAPSEC_FUCHSIA_CITY: + multi = TYPE_POISON; + break; + case MAPSEC_VIRIDIAN_CITY: + multi = TYPE_GROUND; + break; + default: + multi = NUMBER_OF_MON_TYPES; + } + if (multi < NUMBER_OF_MON_TYPES) { + multi = GetTypeEffectiveness(mon, multi); + if (multi & (MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_NO_EFFECT)) + condEmotes[condCount++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_HAPPY, .index=32}; + else if (multi & MOVE_RESULT_SUPER_EFFECTIVE) + condEmotes[condCount++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_SAD, .index=7}; + } } - } - emotion = RandomWeightedIndex(emotion_weight, FOLLOWER_EMOTION_LENGTH); - #ifdef BATTLE_ENGINE - if ((mon->status & STATUS1_PSN_ANY) && GetMonAbility(mon) != ABILITY_POISON_HEAL) - #else - if (mon->status & STATUS1_PSN_ANY) - #endif - emotion = FOLLOWER_EMOTION_POISONED; - multi = Random() % followerBasicMessages[emotion].length; - // With 50% chance, select special message using reservoir sampling - for (i = (Random() & 1) ? n_choices : 0, j = 1; i < n_choices; i++) { - if (cond_emotes[i].emotion == emotion && (Random() < 0x10000 / (j++))) // Replace item with 1/j chance - multi = cond_emotes[i].index; - } - // Match scripted conditional messages - // With 50% chance, try to match scripted conditional messages - for (i = (Random() & 1) ? COND_MSG_COUNT : 0, j = 1; i < COND_MSG_COUNT; i++) { - const struct FollowerMsgInfoExtended *info = &gFollowerConditionalMessages[i]; - if (info->stFlags == 1 && species != info->st.species) - continue; - if (info->stFlags == 2 && (info->st.types.type2 >= NUMBER_OF_MON_TYPES ? SpeciesHasType(species, info->st.types.type1) : !(SpeciesHasType(species, info->st.types.type1) || SpeciesHasType(species, info->st.types.type2)))) - continue; - if (info->stFlags == 3 && !(mon->status & info->st.status)) - continue; - if (info->mmFlags == 1 && gMapHeader.regionMapSectionId != info->mm.mapSec.mapSec) - continue; - if (info->mmFlags == 2 && !(gSaveBlock1Ptr->location.mapNum == info->mm.map.mapNum && gSaveBlock1Ptr->location.mapGroup == info->mm.map.mapGroup)) - continue; - if (info->mmFlags == 3 && !(objEvent->currentMetatileBehavior == info->mm.mb.behavior1 || objEvent->currentMetatileBehavior == info->mm.mb.behavior2)) - continue; - if (info->wtFlags == 1 && !(GetCurrentWeather() == info->wt.weather.weather1 || GetCurrentWeather() == info->wt.weather.weather2)) - continue; - if (info->wtFlags == 2 && GetCurrentMapMusic() != info->wt.song) - continue; - if (info->nearFlags == 1) { - if ((multi2 = FindMetatileBehaviorWithinRange(objEvent->currentCoords.x, objEvent->currentCoords.y, info->near.mb.behavior, info->near.mb.distance))) - gSpecialVar_Result = multi2; - else - continue; - } + emotion = RandomWeightedIndex(emotion_weight, FOLLOWER_EMOTION_LENGTH); + #ifdef BATTLE_ENGINE + if ((mon->status & STATUS1_PSN_ANY) && GetMonAbility(mon) != ABILITY_POISON_HEAL) + #else + if (mon->status & STATUS1_PSN_ANY) + #endif + emotion = FOLLOWER_EMOTION_POISONED; - // replace choice with weight/j chance - if (Random() < (0x10000 / (j++)) * (info->weight ? info->weight : 1)) { - multi = i; - pickedCondition = TRUE; - } - } - if (pickedCondition) { // conditional message was chosen - emotion = gFollowerConditionalMessages[multi].emotion; - ObjectEventEmote(objEvent, emotion); - ctx->data[0] = (u32) gFollowerConditionalMessages[multi].text; - // text choices are spread across array; pick a random one - if (gFollowerConditionalMessages[multi].textSpread) { - for (i = 0; i < 4; i++) - if (!((u32*)gFollowerConditionalMessages[multi].text)[i]) - break; - ctx->data[0] = i ? ((u32*)gFollowerConditionalMessages[multi].text)[Random() % i] : 0; - } - ScriptCall(ctx, gFollowerConditionalMessages[multi].script ? gFollowerConditionalMessages[multi].script : followerBasicMessages[emotion].script); - return FALSE; - } - ObjectEventEmote(objEvent, emotion); - ctx->data[0] = (u32) followerBasicMessages[emotion].messages[multi].text; // Load message text - ScriptCall(ctx, followerBasicMessages[emotion].messages[multi].script ? - followerBasicMessages[emotion].messages[multi].script : followerBasicMessages[emotion].script); - return FALSE; -} + // end special conditions -bool8 ScrFunc_followerfly(struct ScriptContext *ctx) { - SetMainCallback2(CB2_OpenFlyMap); - return FALSE; + // roll for basic/unconditional message + multi = Random() % gFollowerBasicMessages[emotion].length; + // (50% chance) Select special condition using reservoir sampling + for (i = (Random() & 1) ? condCount : 0, j = 1; i < condCount; i++) { + if (condEmotes[i].emotion == emotion && (Random() < 0x10000 / (j++))) // Replace each item with 1/j chance + multi = condEmotes[i].index; + } + // (50% chance) Match *scripted* conditional messages, from follower_helper.c + for (i = (Random() & 1) ? COND_MSG_COUNT : 0, j = 1; i < COND_MSG_COUNT; i++) { + const struct FollowerMsgInfoExtended *info = &gFollowerConditionalMessages[i]; + if (info->stFlags == 1 && species != info->st.species) + continue; + if (info->stFlags == 2 && + (info->st.types.type2 >= NUMBER_OF_MON_TYPES ? + SpeciesHasType(species, info->st.types.type1) : + !(SpeciesHasType(species, info->st.types.type1) || SpeciesHasType(species, info->st.types.type2))) + ) + continue; + if (info->stFlags == 3 && !(mon->status & info->st.status)) + continue; + if (info->mmFlags == 1 && gMapHeader.regionMapSectionId != info->mm.mapSec.mapSec) + continue; + if (info->mmFlags == 2 && + !(gSaveBlock1Ptr->location.mapNum == info->mm.map.mapNum && + gSaveBlock1Ptr->location.mapGroup == info->mm.map.mapGroup) + ) + continue; + if (info->mmFlags == 3 && + !(objEvent->currentMetatileBehavior == info->mm.mb.behavior1 || + objEvent->currentMetatileBehavior == info->mm.mb.behavior2) + ) + continue; + if (info->wtFlags == 1 && + !(GetCurrentWeather() == info->wt.weather.weather1 || + GetCurrentWeather() == info->wt.weather.weather2) + ) + continue; + if (info->wtFlags == 2 && GetCurrentMapMusic() != info->wt.song) + continue; + if (info->nearFlags == 1) { + if ((multi2 = FindMetatileBehaviorWithinRange(objEvent->currentCoords.x, objEvent->currentCoords.y, info->near.mb.behavior, info->near.mb.distance))) + gSpecialVar_Result = multi2; + else + continue; + } + + // replace choice with weight/j chance + if (Random() < (0x10000 / (j++)) * (info->weight ? info->weight : 1)) { + multi = i; + pickedCondition = TRUE; + } + } + // condition message was chosen + if (pickedCondition) { + emotion = gFollowerConditionalMessages[multi].emotion; + ObjectEventEmote(objEvent, emotion); + ctx->data[0] = (u32) gFollowerConditionalMessages[multi].text; + // text choices are spread across array; pick a random one + if (gFollowerConditionalMessages[multi].textSpread) { + for (i = 0; i < 4; i++) + if (!((u32*)gFollowerConditionalMessages[multi].text)[i]) + break; + ctx->data[0] = i ? ((u32*)gFollowerConditionalMessages[multi].text)[Random() % i] : 0; + } + ScriptCall(ctx, gFollowerConditionalMessages[multi].script ? gFollowerConditionalMessages[multi].script : gFollowerBasicMessages[emotion].script); + return FALSE; + } + // otherwise, a basic or C-based message was picked + ObjectEventEmote(objEvent, emotion); + ctx->data[0] = (u32) gFollowerBasicMessages[emotion].messages[multi].text; // Load message text + ScriptCall(ctx, gFollowerBasicMessages[emotion].messages[multi].script ? + gFollowerBasicMessages[emotion].messages[multi].script : + gFollowerBasicMessages[emotion].script); + return FALSE; } void TrySpawnObjectEvents(s16 cameraX, s16 cameraY) @@ -2371,9 +2423,10 @@ static void SpawnObjectEventOnReturnToField(u8 objectEventId, s16 x, s16 y) sprite->coordOffsetEnabled = TRUE; sprite->sObjEventId = objectEventId; objectEvent->spriteId = i; - if (objectEvent->graphicsId == OBJ_EVENT_GFX_OW_MON) { // Set pokemon graphics - FollowerSetGraphics(objectEvent, objectEvent->extra.mon.species, objectEvent->extra.mon.form, objectEvent->extra.mon.shiny); - } + + if (objectEvent->graphicsId == OBJ_EVENT_GFX_OW_MON) // Set pokemon graphics + FollowerSetGraphics(objectEvent, objectEvent->extra.mon.species, objectEvent->extra.mon.form, objectEvent->extra.mon.shiny); + if (!objectEvent->inanimate && objectEvent->movementType != MOVEMENT_TYPE_PLAYER) StartSpriteAnim(sprite, GetFaceDirectionAnimNum(objectEvent->facingDirection)); @@ -2405,43 +2458,47 @@ static void SetPlayerAvatarObjectEventIdAndObjectId(u8 objectEventId, u8 spriteI // Update sprite's palette, freeing old palette if necessary static u8 UpdateSpritePalette(const struct SpritePalette * spritePalette, struct Sprite * sprite) { - // Free palette if otherwise unused - sprite->inUse = FALSE; - FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); - sprite->inUse = TRUE; - return sprite->oam.paletteNum = LoadSpritePalette(spritePalette); + // Free palette if otherwise unused + sprite->inUse = FALSE; + FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum); + sprite->inUse = TRUE; + return sprite->oam.paletteNum = LoadSpritePalette(spritePalette); } // Find and update based on template's paletteTag -// TODO: Add a better way to associate tags -> palettes besides listing them in sObjectEventSpritePalettes u8 UpdateSpritePaletteByTemplate(const struct SpriteTemplate * template, struct Sprite * sprite) { - u8 i = FindObjectEventPaletteIndexByTag(template->paletteTag); - if (i == 0xFF) - return i; - return UpdateSpritePalette(&sObjectEventSpritePalettes[i], sprite); + u8 i = FindObjectEventPaletteIndexByTag(template->paletteTag); + if (i == 0xFF) + return i; + return UpdateSpritePalette(&sObjectEventSpritePalettes[i], sprite); } // Set graphics *by info* static void ObjectEventSetGraphics(struct ObjectEvent *objectEvent, const struct ObjectEventGraphicsInfo *graphicsInfo) { - struct Sprite *sprite = &gSprites[objectEvent->spriteId]; - u8 i = FindObjectEventPaletteIndexByTag(graphicsInfo->paletteTag); - if (i != 0xFF) - UpdateSpritePalette(&sObjectEventSpritePalettes[i], sprite); - sprite->oam.shape = graphicsInfo->oam->shape; - sprite->oam.size = graphicsInfo->oam->size; - sprite->images = graphicsInfo->images; - sprite->anims = graphicsInfo->anims; - sprite->subspriteTables = graphicsInfo->subspriteTables; - objectEvent->inanimate = graphicsInfo->inanimate; - SetSpritePosToMapCoords(objectEvent->currentCoords.x, objectEvent->currentCoords.y, &sprite->x, &sprite->y); - sprite->centerToCornerVecX = -(graphicsInfo->width >> 1); - sprite->centerToCornerVecY = -(graphicsInfo->height >> 1); - sprite->x += 8; - sprite->y += 16 + sprite->centerToCornerVecY; - if (objectEvent->trackedByCamera) - { - CameraObjectReset1(); - } + struct Sprite *sprite = &gSprites[objectEvent->spriteId]; + u32 i = FindObjectEventPaletteIndexByTag(graphicsInfo->paletteTag); + if (i != 0xFF) + UpdateSpritePalette(&sObjectEventSpritePalettes[i], sprite); + + #if LARGE_OW_SUPPORT + // If gfx size changes, we need to reallocate tiles + if (graphicsInfo->oam->size != sprite->oam.size) + ReallocSpriteTiles(sprite, graphicsInfo->images->size); + #endif + + sprite->oam.shape = graphicsInfo->oam->shape; + sprite->oam.size = graphicsInfo->oam->size; + sprite->images = graphicsInfo->images; + sprite->anims = graphicsInfo->anims; + sprite->subspriteTables = graphicsInfo->subspriteTables; + objectEvent->inanimate = graphicsInfo->inanimate; + SetSpritePosToMapCoords(objectEvent->currentCoords.x, objectEvent->currentCoords.y, &sprite->x, &sprite->y); + sprite->centerToCornerVecX = -(graphicsInfo->width >> 1); + sprite->centerToCornerVecY = -(graphicsInfo->height >> 1); + sprite->x += 8; + sprite->y += 16 + sprite->centerToCornerVecY; + if (objectEvent->trackedByCamera) + CameraObjectReset1(); } void ObjectEventSetGraphicsId(struct ObjectEvent *objectEvent, u8 graphicsId) @@ -2483,26 +2540,24 @@ void PlayerObjectTurn(struct PlayerAvatar *playerAvatar, u8 direction) } static void SetBerryTreeGraphics(struct ObjectEvent *objectEvent, u8 berryId, u8 berryStage) { - const u8 graphicsId = gBerryTreeObjectEventGraphicsIdTablePointers[berryId][berryStage]; - const struct ObjectEventGraphicsInfo *graphicsInfo = GetObjectEventGraphicsInfo(graphicsId); - struct Sprite *sprite = &gSprites[objectEvent->spriteId]; - UpdateSpritePalette(&sObjectEventSpritePalettes[gBerryTreePaletteSlotTablePointers[berryId][berryStage]-2], sprite); - sprite->oam.shape = graphicsInfo->oam->shape; - sprite->oam.size = graphicsInfo->oam->size; - sprite->images = gBerryTreePicTablePointers[berryId]; - sprite->anims = graphicsInfo->anims; - sprite->subspriteTables = graphicsInfo->subspriteTables; - objectEvent->inanimate = graphicsInfo->inanimate; - objectEvent->graphicsId = graphicsId; - SetSpritePosToMapCoords(objectEvent->currentCoords.x, objectEvent->currentCoords.y, &sprite->x, &sprite->y); - sprite->centerToCornerVecX = -(graphicsInfo->width >> 1); - sprite->centerToCornerVecY = -(graphicsInfo->height >> 1); - sprite->x += 8; - sprite->y += 16 + sprite->centerToCornerVecY; - if (objectEvent->trackedByCamera) - { - CameraObjectReset1(); - } + const u8 graphicsId = gBerryTreeObjectEventGraphicsIdTablePointers[berryId][berryStage]; + const struct ObjectEventGraphicsInfo *graphicsInfo = GetObjectEventGraphicsInfo(graphicsId); + struct Sprite *sprite = &gSprites[objectEvent->spriteId]; + UpdateSpritePalette(&sObjectEventSpritePalettes[gBerryTreePaletteSlotTablePointers[berryId][berryStage]-2], sprite); + sprite->oam.shape = graphicsInfo->oam->shape; + sprite->oam.size = graphicsInfo->oam->size; + sprite->images = gBerryTreePicTablePointers[berryId]; + sprite->anims = graphicsInfo->anims; + sprite->subspriteTables = graphicsInfo->subspriteTables; + objectEvent->inanimate = graphicsInfo->inanimate; + objectEvent->graphicsId = graphicsId; + SetSpritePosToMapCoords(objectEvent->currentCoords.x, objectEvent->currentCoords.y, &sprite->x, &sprite->y); + sprite->centerToCornerVecX = -(graphicsInfo->width >> 1); + sprite->centerToCornerVecY = -(graphicsInfo->height >> 1); + sprite->x += 8; + sprite->y += 16 + sprite->centerToCornerVecY; + if (objectEvent->trackedByCamera) + CameraObjectReset1(); } static void get_berry_tree_graphics(struct ObjectEvent *objectEvent, struct Sprite *sprite) @@ -4962,7 +5017,7 @@ bool8 CopyablePlayerMovement_Jump2(struct ObjectEvent *objectEvent, struct Sprit return TRUE; } -static bool8 EndFollowerTransformEffect(struct ObjectEvent *objectEvent, struct Sprite *sprite) { +static bool32 EndFollowerTransformEffect(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (!sprite) return FALSE; SetGpuReg(REG_OFFSET_MOSAIC, 0); @@ -4973,7 +5028,7 @@ static bool8 EndFollowerTransformEffect(struct ObjectEvent *objectEvent, struct return FALSE; } -static bool8 TryStartFollowerTransformEffect(struct ObjectEvent *objectEvent, struct Sprite *sprite) { +static bool32 TryStartFollowerTransformEffect(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u32 multi; if (objectEvent->extra.mon.species == SPECIES_CASTFORM && objectEvent->extra.mon.form != (multi = GetOverworldCastformForm())) { sprite->data[7] = TRANSFORM_TYPE_PERMANENT << 8; @@ -5035,10 +5090,10 @@ bool8 MovementType_FollowPlayer_Shadow(struct ObjectEvent *objectEvent, struct S { ClearObjectEventMovement(objectEvent, sprite); if (!IsFollowerVisible()) { // Shadow player's position - objectEvent->invisible = TRUE; - MoveObjectEventToMapCoords(objectEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); - objectEvent->triggerGroundEffectsOnMove = FALSE; // Stop endless reflection spawning - return FALSE; + objectEvent->invisible = TRUE; + MoveObjectEventToMapCoords(objectEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); + objectEvent->triggerGroundEffectsOnMove = FALSE; // Stop endless reflection spawning + return FALSE; } // Move follower to player, in case we end up in the shadowing state for only 1 frame // This way the player cannot talk to the invisible follower before it appears @@ -5064,7 +5119,6 @@ bool8 MovementType_FollowPlayer_Active(struct ObjectEvent *objectEvent, struct S sprite->sTypeFuncId = 2; // movement action sets state to 0 return TRUE; } - // TODO: Remove dependence on PlayerGetCopyableMovement return gFollowPlayerMovementFuncs[PlayerGetCopyableMovement()](objectEvent, sprite, GetPlayerMovementDirection(), NULL); } @@ -5079,12 +5133,11 @@ bool8 MovementType_FollowPlayer_Moving(struct ObjectEvent *objectEvent, struct S if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { #endif objectEvent->singleMovementActive = 0; - if (sprite->sTypeFuncId) { // restore nonzero state + if (sprite->sTypeFuncId) // restore nonzero state sprite->sTypeFuncId = 1; - } } else if (objectEvent->movementActionId < MOVEMENT_ACTION_EXIT_POKEBALL) { UpdateFollowerTransformEffect(objectEvent, sprite); - #if OW_MON_BOBBING == TRUE + #if OW_MON_BOBBING if ((sprite->data[5] & 7) == 2) sprite->y2 ^= -1; #endif @@ -5105,7 +5158,7 @@ bool8 FollowablePlayerMovement_Idle(struct ObjectEvent *objectEvent, struct Spri { // finish movement action objectEvent->singleMovementActive = 0; } - #if OW_MON_BOBBING == TRUE + #if OW_MON_BOBBING else if ((sprite->data[3] & 7) == 2) sprite->y2 ^= -1; #endif @@ -5129,29 +5182,29 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri x = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x; y = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y; - if ((x == targetX && y == targetY) || !IsFollowerVisible()) { // don't move on player collision or if not visible - return FALSE; - } + if ((x == targetX && y == targetY) || !IsFollowerVisible()) // don't move on player collision or if not visible + return FALSE; + x = objectEvent->currentCoords.x; y = objectEvent->currentCoords.y; ClearObjectEventMovement(objectEvent, sprite); if (objectEvent->invisible) { // Animate exiting pokeball - // Player is jumping, but follower is invisible - if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) { - sprite->sTypeFuncId = 0; // return to shadowing state - return FALSE; - } - MoveObjectEventToMapCoords(objectEvent, targetX, targetY); - ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EXIT_POKEBALL); - objectEvent->singleMovementActive = 1; - sprite->sTypeFuncId = 2; - #if OW_MON_BOBBING == TRUE - sprite->y2 = 0; - #endif - return TRUE; + // Player is jumping, but follower is invisible + if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) { + sprite->sTypeFuncId = 0; // return to shadowing state + return FALSE; + } + MoveObjectEventToMapCoords(objectEvent, targetX, targetY); + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EXIT_POKEBALL); + objectEvent->singleMovementActive = 1; + sprite->sTypeFuncId = 2; + #if OW_MON_BOBBING + sprite->y2 = 0; + #endif + return TRUE; } else if (x == targetX && y == targetY) { // don't move if already in the player's last position - return FALSE; + return FALSE; } // Follow player @@ -5174,7 +5227,7 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction)); else { objectEvent->movementActionId = GetWalkNormalMovementAction(direction); - #if OW_MON_BOBBING == TRUE + #if OW_MON_BOBBING sprite->y2 = -1; #endif } @@ -5190,7 +5243,7 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction)); else { ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkNormalMovementAction(direction)); - #if OW_MON_BOBBING == TRUE + #if OW_MON_BOBBING sprite->y2 = -1; #endif } @@ -5627,7 +5680,7 @@ bool8 ScrFunc_IsFollowerFieldMoveUser(struct ScriptContext *ctx) { if (follower && obj && !obj->invisible) { u16 followIndex = ((u32)follower - (u32)gPlayerParty) / sizeof(struct Pokemon); *var = userIndex == followIndex; - } else + } return FALSE; } @@ -6747,10 +6800,10 @@ bool8 MovementAction_WalkInPlaceSlowDown_Step0(struct ObjectEvent *objectEvent, // Update sprite with a palette filled with a solid color static u8 LoadFillColorPalette(u16 color, u16 paletteTag, struct Sprite *sprite) { - u16 paletteData[16]; - struct SpritePalette dynamicPalette = {.tag = paletteTag, .data = paletteData}; - CpuFill16(color, paletteData, PLTT_SIZE_4BPP); - return UpdateSpritePalette(&dynamicPalette, sprite); + u16 paletteData[16]; + struct SpritePalette dynamicPalette = {.tag = paletteTag, .data = paletteData}; + CpuFill16(color, paletteData, PLTT_SIZE_4BPP); + return UpdateSpritePalette(&dynamicPalette, sprite); } static void ObjectEventSetPokeballGfx(struct ObjectEvent *objEvent) { @@ -6773,19 +6826,24 @@ static void ObjectEventSetPokeballGfx(struct ObjectEvent *objEvent) { ObjectEventSetGraphicsId(objEvent, OBJ_EVENT_GFX_ANIMATED_BALL); } +#define sDuration data[3] +#define sSpeedFlip data[6] + bool8 MovementAction_ExitPokeball_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - u8 direction = gObjectEvents[gPlayerAvatar.objectEventId].facingDirection; + u32 direction = gObjectEvents[gPlayerAvatar.objectEventId].facingDirection; objectEvent->invisible = FALSE; if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) { // If player is dashing, the pokemon must come out faster - StartSpriteAnimInDirection(objectEvent, sprite, direction, GetMoveDirectionFastestAnimNum(direction) + 4); - sprite->data[3] = 8; // duration - sprite->data[6] = 0; // fast speed + StartSpriteAnimInDirection(objectEvent, sprite, direction, GetJumpSpecialDirectionAnimNum(direction)); + sprite->sDuration = 8; + sprite->sSpeedFlip = 0; // fast speed } else { StartSpriteAnimInDirection(objectEvent, sprite, direction, GetMoveDirectionFastestAnimNum(direction)); - sprite->data[3] = 16; // duration - sprite->data[6] = 1; // slow speed + sprite->sDuration = 16; + sprite->sSpeedFlip = 1; // normal speed } - sprite->data[6] |= (direction == DIR_EAST ? 1 : 0) << 4; + // If mon's right-facing sprite is h-flipped, we need to use a different affine anim + if (direction == DIR_EAST && sprite->anims[ANIM_STD_FACE_EAST]->frame.hFlip) + sprite->sSpeedFlip |= 1 << 4; ObjectEventSetPokeballGfx(objectEvent); objectEvent->inanimate = FALSE; return MovementAction_ExitPokeball_Step1(objectEvent, sprite); @@ -6837,32 +6895,30 @@ static const union AffineAnimCmd *const sAffineAnims_PokeballFollower[] = bool8 MovementAction_ExitPokeball_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - u8 duration = sprite->data[6] & 0xF; - sprite->data[3]--; - if (sprite->data[3] == 0) + // for different speeds, anim steps occur on different frame #s + u32 animStepFrame = (sprite->sSpeedFlip & 1) ? 7 : 3; // 0 -> 3, 1 -> 7 + if (--sprite->sDuration == 0) { sprite->sActionFuncId = 2; sprite->animCmdIndex = 0; sprite->animPaused = TRUE; return TRUE; // Set graphics, palette, and affine animation - } else if ((duration == 0 && sprite->data[3] == 3) || (duration == 1 && sprite->data[3] == 7)) { + } else if (sprite->sDuration == animStepFrame) { FollowerSetGraphics(objectEvent, objectEvent->extra.mon.species, objectEvent->extra.mon.form, objectEvent->extra.mon.shiny); LoadFillColorPalette(RGB_WHITE, OBJ_EVENT_PAL_TAG_WHITE, sprite); // Initialize affine animation sprite->affineAnims = sAffineAnims_PokeballFollower; #if LARGE_OW_SUPPORT - if (IS_POW_OF_TWO(-sprite->centerToCornerVecX)) { - #endif - sprite->affineAnims = sAffineAnims_PokeballFollower; - sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; - InitSpriteAffineAnim(sprite); - StartSpriteAffineAnim(sprite, sprite->data[6] >> 4); - #if LARGE_OW_SUPPORT - } + if (!IS_POW_OF_TWO(-sprite->centerToCornerVecX)) + return FALSE; #endif + sprite->affineAnims = sAffineAnims_PokeballFollower; + sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; + InitSpriteAffineAnim(sprite); + StartSpriteAffineAnim(sprite, sprite->sSpeedFlip >> 4); // Restore original palette & disable affine - } else if ((duration == 0 && sprite->data[3] == 1) || (duration == 1 && sprite->data[3] == 3)) { + } else if (sprite->sDuration == (animStepFrame >> 1)) { sprite->affineAnimEnded = TRUE; FreeSpriteOamMatrix(sprite); sprite->oam.affineMode = ST_OAM_AFFINE_OFF; @@ -6872,36 +6928,37 @@ bool8 MovementAction_ExitPokeball_Step1(struct ObjectEvent *objectEvent, struct } bool8 MovementAction_EnterPokeball_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - u8 direction = objectEvent->facingDirection; + u32 direction = objectEvent->facingDirection; StartSpriteAnimInDirection(objectEvent, sprite, direction, GetMoveDirectionFasterAnimNum(direction)); - sprite->data[3] = 16; // duration - sprite->data[6] = direction == DIR_EAST ? 3 : 2; // affine animation number + sprite->sDuration = 16; + // If mon's right-facing sprite is h-flipped, we need to use a different affine anim + if (direction == DIR_EAST && sprite->anims[ANIM_STD_FACE_EAST]->frame.hFlip) + sprite->sSpeedFlip = 3; + else + sprite->sSpeedFlip = 2; EndFollowerTransformEffect(objectEvent, sprite); return MovementAction_EnterPokeball_Step1(objectEvent, sprite); } bool8 MovementAction_EnterPokeball_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - sprite->data[3]--; - if (sprite->data[3] == 0) { - sprite->data[2] = 2; + if (--sprite->sDuration == 0) { + sprite->sActionFuncId = 2; return FALSE; - } else if (sprite->data[3] == 11) { // Set palette to white & start affine + } else if (sprite->sDuration == 11) { // Set palette to white & start affine LoadFillColorPalette(RGB_WHITE, OBJ_EVENT_PAL_TAG_WHITE, sprite); + sprite->subspriteTableNum = 0; #if LARGE_OW_SUPPORT // Only do affine if sprite width is power of 2 // (effect looks weird on sprites composed of subsprites like 48x48, etc) - if (IS_POW_OF_TWO(-sprite->centerToCornerVecX)) { + if (!IS_POW_OF_TWO(-sprite->centerToCornerVecX)) + return FALSE; #endif - sprite->affineAnims = sAffineAnims_PokeballFollower; - sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; - InitSpriteAffineAnim(sprite); - StartSpriteAffineAnim(sprite, sprite->data[6]); - #if LARGE_OW_SUPPORT - } - #endif - sprite->subspriteTableNum = 0; - } else if (sprite->data[3] == 7) { // Free white palette and change to pokeball, disable affine + sprite->affineAnims = sAffineAnims_PokeballFollower; + sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; + InitSpriteAffineAnim(sprite); + StartSpriteAffineAnim(sprite, sprite->sSpeedFlip); + } else if (sprite->sDuration == 7) { // Free white palette and change to pokeball, disable affine sprite->affineAnimEnded = TRUE; FreeSpriteOamMatrix(sprite); sprite->oam.affineMode = ST_OAM_AFFINE_OFF; @@ -6915,12 +6972,15 @@ bool8 MovementAction_EnterPokeball_Step2(struct ObjectEvent *objectEvent, struct { FollowerSetGraphics(objectEvent, objectEvent->extra.mon.species, objectEvent->extra.mon.form, objectEvent->extra.mon.shiny); objectEvent->invisible = TRUE; - sprite->data[1] = 0; - sprite->data[6] = 0; + sprite->sTypeFuncId = 0; + sprite->sSpeedFlip = 0; sprite->animPaused = TRUE; return TRUE; } +#undef sDuration +#undef sSpeedFlip + bool8 MovementAction_WalkInPlaceSlowUp_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { InitMoveInPlace(objectEvent, sprite, DIR_NORTH, GetMoveDirectionAnimNum(DIR_NORTH), 32); @@ -8927,19 +8987,16 @@ static void UpdateObjectEventElevationAndPriority(struct ObjectEvent *objEvent, return; ObjectEventUpdateElevation(objEvent, sprite); - #if LARGE_OW_SUPPORT if (objEvent->localId == OBJ_EVENT_ID_FOLLOWER) { + #if LARGE_OW_SUPPORT // keep subspriteMode synced with player's // so that it disappears under bridges when they do sprite->subspriteMode |= gSprites[gPlayerAvatar.spriteId].subspriteMode & SUBSPRITES_IGNORE_PRIORITY; + #endif + // if transitioning between elevations, use the player's elevation if (!objEvent->currentElevation) objEvent = &gObjectEvents[gPlayerAvatar.objectEventId]; } - #else - // if transitioning between elevations, use the player's elevation - if (!objEvent->currentElevation && objEvent->localId == OBJ_EVENT_ID_FOLLOWER) - objEvent = &gObjectEvents[gPlayerAvatar.objectEventId]; - #endif sprite->subspriteTableNum = sElevationToSubspriteTableNum[objEvent->previousElevation]; sprite->oam.priority = sElevationToPriority[objEvent->previousElevation]; @@ -9320,7 +9377,7 @@ static void (*const sGroundEffectFuncs[])(struct ObjectEvent *objEvent, struct S }; static void GroundEffect_Shadow(struct ObjectEvent *objEvent, struct Sprite *sprite) { - SetUpShadow(objEvent, sprite); + SetUpShadow(objEvent, sprite); } static void DoFlaggedGroundEffects(struct ObjectEvent *objEvent, struct Sprite *sprite, u32 flags) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 7dd55a6262..2d6b7978a4 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -55,10 +55,10 @@ u32 FldEff_Shadow(void); #define sIsStillReflection data[7] void SetUpShadow(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - gFieldEffectArguments[0] = objectEvent->localId; - gFieldEffectArguments[1] = gSaveBlock1Ptr->location.mapNum; - gFieldEffectArguments[2] = gSaveBlock1Ptr->location.mapGroup; - FldEff_Shadow(); + gFieldEffectArguments[0] = objectEvent->localId; + gFieldEffectArguments[1] = gSaveBlock1Ptr->location.mapNum; + gFieldEffectArguments[2] = gSaveBlock1Ptr->location.mapGroup; + FldEff_Shadow(); } void SetUpReflection(struct ObjectEvent *objectEvent, struct Sprite *sprite, bool8 stillReflection) diff --git a/src/follower_helper.c b/src/follower_helper.c index 2dbf51dadf..ace1eec45c 100644 --- a/src/follower_helper.c +++ b/src/follower_helper.c @@ -295,3 +295,18 @@ const struct FollowerMsgInfoExtended gFollowerConditionalMessages[COND_MSG_COUNT .emotion = FOLLOWER_EMOTION_SAD, }, }; + +// Pool of "unconditional" follower messages +const struct FollowerMessagePool gFollowerBasicMessages[FOLLOWER_EMOTION_LENGTH] = { + [FOLLOWER_EMOTION_HAPPY] = {gFollowerHappyMessages, EventScript_FollowerGeneric, N_FOLLOWER_HAPPY_MESSAGES}, + [FOLLOWER_EMOTION_NEUTRAL] = {gFollowerNeutralMessages, EventScript_FollowerGeneric, N_FOLLOWER_NEUTRAL_MESSAGES}, + [FOLLOWER_EMOTION_SAD] = {gFollowerSadMessages, EventScript_FollowerGeneric, N_FOLLOWER_SAD_MESSAGES}, + [FOLLOWER_EMOTION_UPSET] = {gFollowerUpsetMessages, EventScript_FollowerGeneric, N_FOLLOWER_UPSET_MESSAGES}, + [FOLLOWER_EMOTION_ANGRY] = {gFollowerAngryMessages, EventScript_FollowerGeneric, N_FOLLOWER_ANGRY_MESSAGES}, + [FOLLOWER_EMOTION_PENSIVE] = {gFollowerPensiveMessages, EventScript_FollowerGeneric, N_FOLLOWER_PENSIVE_MESSAGES}, + [FOLLOWER_EMOTION_LOVE] = {gFollowerLoveMessages, EventScript_FollowerGeneric, N_FOLLOWER_LOVE_MESSAGES}, + [FOLLOWER_EMOTION_SURPRISE] = {gFollowerSurpriseMessages, EventScript_FollowerGeneric, N_FOLLOWER_SURPRISE_MESSAGES}, + [FOLLOWER_EMOTION_CURIOUS] = {gFollowerCuriousMessages, EventScript_FollowerGeneric, N_FOLLOWER_CURIOUS_MESSAGES}, + [FOLLOWER_EMOTION_MUSIC] = {gFollowerMusicMessages, EventScript_FollowerGeneric, N_FOLLOWER_MUSIC_MESSAGES}, + [FOLLOWER_EMOTION_POISONED] = {gFollowerPoisonedMessages, EventScript_FollowerGeneric, N_FOLLOWER_POISONED_MESSAGES}, +}; diff --git a/src/scrcmd.c b/src/scrcmd.c index abef4a85fe..bb163f650a 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2074,9 +2074,9 @@ bool8 ScrCmd_playmoncry(struct ScriptContext *ctx) bool8 ScrFunc_playfirstmoncry(struct ScriptContext *ctx) { - u16 species = GetMonData(GetFirstLiveMon(), MON_DATA_SPECIES); - PlayCry_Script(species, 0); - return FALSE; + u16 species = GetMonData(GetFirstLiveMon(), MON_DATA_SPECIES); + PlayCry_Script(species, 0); + return FALSE; } bool8 ScrCmd_waitmoncry(struct ScriptContext *ctx) diff --git a/src/trainer_see.c b/src/trainer_see.c index 95b11447f2..ffa365ef95 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -152,28 +152,28 @@ static const struct SpriteFrameImage sSpriteImageTable_HeartIcon[] = static const struct SpriteFrameImage sSpriteImageTable_Emotes[] = { - { .data = (u8 *)sEmotion_Gfx+0*0x80, .size = 0x80}, // FOLLOWER_EMOTION_HAPPY - { .data = (u8 *)sEmotion_Gfx+1*0x80, .size = 0x80}, // FOLLOWER_EMOTION_HAPPY - { .data = (u8 *)sEmotion_Gfx+2*0x80, .size = 0x80}, // FOLLOWER_EMOTION_NEUTRAL - { .data = (u8 *)sEmotion_Gfx+3*0x80, .size = 0x80}, // FOLLOWER_EMOTION_NEUTRAL - { .data = (u8 *)sEmotion_Gfx+4*0x80, .size = 0x80}, // FOLLOWER_EMOTION_SAD - { .data = (u8 *)sEmotion_Gfx+5*0x80, .size = 0x80}, // FOLLOWER_EMOTION_SAD - { .data = (u8 *)sEmotion_Gfx+6*0x80, .size = 0x80}, // FOLLOWER_EMOTION_UPSET - { .data = (u8 *)sEmotion_Gfx+7*0x80, .size = 0x80}, // FOLLOWER_EMOTION_UPSET - { .data = (u8 *)sEmotion_Gfx+8*0x80, .size = 0x80}, // FOLLOWER_EMOTION_ANGRY - { .data = (u8 *)sEmotion_Gfx+9*0x80, .size = 0x80}, // FOLLOWER_EMOTION_ANGRY - { .data = (u8 *)sEmotion_Gfx+10*0x80, .size = 0x80}, // FOLLOWER_EMOTION_PENSIVE - { .data = (u8 *)sEmotion_Gfx+11*0x80, .size = 0x80}, // FOLLOWER_EMOTION_PENSIVE - { .data = (u8 *)sEmotion_Gfx+12*0x80, .size = 0x80}, // FOLLOWER_EMOTION_LOVE - { .data = (u8 *)sEmotion_Gfx+13*0x80, .size = 0x80}, // FOLLOWER_EMOTION_LOVE - { .data = (u8 *)sEmotion_Gfx+14*0x80, .size = 0x80}, // FOLLOWER_EMOTION_SURPRISE - { .data = (u8 *)sEmotion_Gfx+15*0x80, .size = 0x80}, // FOLLOWER_EMOTION_SURPRISE - { .data = (u8 *)sEmotion_Gfx+16*0x80, .size = 0x80}, // FOLLOWER_EMOTION_CURIOUS - { .data = (u8 *)sEmotion_Gfx+17*0x80, .size = 0x80}, // FOLLOWER_EMOTION_CURIOUS - { .data = (u8 *)sEmotion_Gfx+18*0x80, .size = 0x80}, // FOLLOWER_EMOTION_MUSIC - { .data = (u8 *)sEmotion_Gfx+19*0x80, .size = 0x80}, // FOLLOWER_EMOTION_MUSIC - { .data = (u8 *)sEmotion_Gfx+20*0x80, .size = 0x80}, // FOLLOWER_EMOTION_POISONED - { .data = (u8 *)sEmotion_Gfx+21*0x80, .size = 0x80}, // FOLLOWER_EMOTION_POISONED + overworld_frame(sEmotion_Gfx, 2, 2, 0), // FOLLOWER_EMOTION_HAPPY + overworld_frame(sEmotion_Gfx, 2, 2, 1), // FOLLOWER_EMOTION_HAPPY + overworld_frame(sEmotion_Gfx, 2, 2, 2), // FOLLOWER_EMOTION_NEUTRAL + overworld_frame(sEmotion_Gfx, 2, 2, 3), // FOLLOWER_EMOTION_NEUTRAL + overworld_frame(sEmotion_Gfx, 2, 2, 4), // FOLLOWER_EMOTION_SAD + overworld_frame(sEmotion_Gfx, 2, 2, 5), // FOLLOWER_EMOTION_SAD + overworld_frame(sEmotion_Gfx, 2, 2, 6), // FOLLOWER_EMOTION_UPSET + overworld_frame(sEmotion_Gfx, 2, 2, 7), // FOLLOWER_EMOTION_UPSET + overworld_frame(sEmotion_Gfx, 2, 2, 8), // FOLLOWER_EMOTION_ANGRY + overworld_frame(sEmotion_Gfx, 2, 2, 9), // FOLLOWER_EMOTION_ANGRY + overworld_frame(sEmotion_Gfx, 2, 2, 10), // FOLLOWER_EMOTION_PENSIVE + overworld_frame(sEmotion_Gfx, 2, 2, 11), // FOLLOWER_EMOTION_PENSIVE + overworld_frame(sEmotion_Gfx, 2, 2, 12), // FOLLOWER_EMOTION_LOVE + overworld_frame(sEmotion_Gfx, 2, 2, 13), // FOLLOWER_EMOTION_LOVE + overworld_frame(sEmotion_Gfx, 2, 2, 14), // FOLLOWER_EMOTION_SURPRISE + overworld_frame(sEmotion_Gfx, 2, 2, 15), // FOLLOWER_EMOTION_SURPRISE + overworld_frame(sEmotion_Gfx, 2, 2, 16), // FOLLOWER_EMOTION_CURIOUS + overworld_frame(sEmotion_Gfx, 2, 2, 17), // FOLLOWER_EMOTION_CURIOUS + overworld_frame(sEmotion_Gfx, 2, 2, 18), // FOLLOWER_EMOTION_MUSIC + overworld_frame(sEmotion_Gfx, 2, 2, 19), // FOLLOWER_EMOTION_MUSIC + overworld_frame(sEmotion_Gfx, 2, 2, 20), // FOLLOWER_EMOTION_POISONED + overworld_frame(sEmotion_Gfx, 2, 2, 21), // FOLLOWER_EMOTION_POISONED }; static const union AnimCmd sSpriteAnim_Emotes0[] = {