diff --git a/data/maps/BattleFrontier_BattlePalaceLobby/scripts.inc b/data/maps/BattleFrontier_BattlePalaceLobby/scripts.inc index 21e651fb53..33d3735884 100644 --- a/data/maps/BattleFrontier_BattlePalaceLobby/scripts.inc +++ b/data/maps/BattleFrontier_BattlePalaceLobby/scripts.inc @@ -650,3 +650,4 @@ BattleFrontier_BattlePalaceLobby_Text_ExplainRulesWhenInDanger: .string "nature when it is in trouble.\p" .string "If a POKéMON begins behaving oddly\n" .string "in a pinch, watch it carefully.$" + diff --git a/data/maps/BattleFrontier_BattlePikeLobby/scripts.inc b/data/maps/BattleFrontier_BattlePikeLobby/scripts.inc index 3e9db4d4ca..b37100bc0a 100644 --- a/data/maps/BattleFrontier_BattlePikeLobby/scripts.inc +++ b/data/maps/BattleFrontier_BattlePikeLobby/scripts.inc @@ -434,3 +434,4 @@ BattleFrontier_BattlePikeLobby_Text_ExplainMonOrderRules: .string "changed.\p" .string "The sequence must be set before\n" .string "starting your challenge.$" + diff --git a/data/maps/BattleFrontier_BattlePyramidLobby/scripts.inc b/data/maps/BattleFrontier_BattlePyramidLobby/scripts.inc index d07dc86470..1768411616 100644 --- a/data/maps/BattleFrontier_BattlePyramidLobby/scripts.inc +++ b/data/maps/BattleFrontier_BattlePyramidLobby/scripts.inc @@ -904,3 +904,4 @@ BattleFrontier_BattlePyramidLobby_Text_ExplainBagRules: .string "of ten kinds of items.\p" .string "The contents of the BATTLE BAG are\n" .string "lost if you fail in your quest.$" + diff --git a/data/maps/RusturfTunnel/scripts.inc b/data/maps/RusturfTunnel/scripts.inc index fbbdecde34..47f7dd9b4d 100644 --- a/data/maps/RusturfTunnel/scripts.inc +++ b/data/maps/RusturfTunnel/scripts.inc @@ -530,3 +530,4 @@ RusturfTunnel_Text_MikePostBattle: .string "They halted development here to\n" .string "protect POKéMON, right?\l" .string "There's a feel-good story!$" + diff --git a/data/scripts/follower.inc b/data/scripts/follower.inc index dbdb113bdd..f0ef6dbb3b 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 0776220dd9..6362210320 100644 --- a/gflib/sprite.c +++ b/gflib/sprite.c @@ -52,7 +52,6 @@ static void SortSprites(u32 *spritePriorities, s32 n); 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 3ce6dfc76f..87bd17f448 100644 --- a/gflib/sprite.h +++ b/gflib/sprite.h @@ -304,6 +304,7 @@ void SetOamMatrixRotationScaling(u8 matrixNum, s16 xScale, s16 yScale, u16 rotat u16 LoadSpriteSheet(const struct SpriteSheet *sheet); u16 LoadSpriteSheetByTemplate(const struct SpriteTemplate *template, u32 frame, s32 offset); 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/misc/animated_ball.png b/graphics/object_events/pics/misc/animated_ball.png index 534e66c7b4..03216113ba 100644 Binary files a/graphics/object_events/pics/misc/animated_ball.png and b/graphics/object_events/pics/misc/animated_ball.png differ diff --git a/graphics/object_events/pics/misc/ball_beast.png b/graphics/object_events/pics/misc/ball_beast.png new file mode 100644 index 0000000000..f3733d11ae Binary files /dev/null and b/graphics/object_events/pics/misc/ball_beast.png differ diff --git a/graphics/object_events/pics/misc/ball_cherish.png b/graphics/object_events/pics/misc/ball_cherish.png new file mode 100644 index 0000000000..a93cab30c5 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_cherish.png differ diff --git a/graphics/object_events/pics/misc/ball_dive.png b/graphics/object_events/pics/misc/ball_dive.png new file mode 100644 index 0000000000..1349ffa3ff Binary files /dev/null and b/graphics/object_events/pics/misc/ball_dive.png differ diff --git a/graphics/object_events/pics/misc/ball_dream.png b/graphics/object_events/pics/misc/ball_dream.png new file mode 100644 index 0000000000..875e72a702 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_dream.png differ diff --git a/graphics/object_events/pics/misc/ball_dusk.png b/graphics/object_events/pics/misc/ball_dusk.png new file mode 100644 index 0000000000..824557003d Binary files /dev/null and b/graphics/object_events/pics/misc/ball_dusk.png differ diff --git a/graphics/object_events/pics/misc/ball_fast.png b/graphics/object_events/pics/misc/ball_fast.png new file mode 100644 index 0000000000..3e3ca467ea Binary files /dev/null and b/graphics/object_events/pics/misc/ball_fast.png differ diff --git a/graphics/object_events/pics/misc/ball_friend.png b/graphics/object_events/pics/misc/ball_friend.png new file mode 100644 index 0000000000..f22d7f78a2 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_friend.png differ diff --git a/graphics/object_events/pics/misc/ball_great.png b/graphics/object_events/pics/misc/ball_great.png new file mode 100644 index 0000000000..5a70a505fe Binary files /dev/null and b/graphics/object_events/pics/misc/ball_great.png differ diff --git a/graphics/object_events/pics/misc/ball_heal.png b/graphics/object_events/pics/misc/ball_heal.png new file mode 100644 index 0000000000..bcb2272e51 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_heal.png differ diff --git a/graphics/object_events/pics/misc/ball_heavy.png b/graphics/object_events/pics/misc/ball_heavy.png new file mode 100644 index 0000000000..5b28410694 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_heavy.png differ diff --git a/graphics/object_events/pics/misc/ball_level.png b/graphics/object_events/pics/misc/ball_level.png new file mode 100644 index 0000000000..9050ad5588 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_level.png differ diff --git a/graphics/object_events/pics/misc/ball_love.png b/graphics/object_events/pics/misc/ball_love.png new file mode 100644 index 0000000000..9584b6c797 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_love.png differ diff --git a/graphics/object_events/pics/misc/ball_lure.png b/graphics/object_events/pics/misc/ball_lure.png new file mode 100644 index 0000000000..f3f6c1ffd9 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_lure.png differ diff --git a/graphics/object_events/pics/misc/ball_luxury.png b/graphics/object_events/pics/misc/ball_luxury.png new file mode 100644 index 0000000000..f04e816cb3 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_luxury.png differ diff --git a/graphics/object_events/pics/misc/ball_master.png b/graphics/object_events/pics/misc/ball_master.png new file mode 100644 index 0000000000..1db9b71fba Binary files /dev/null and b/graphics/object_events/pics/misc/ball_master.png differ diff --git a/graphics/object_events/pics/misc/ball_moon.png b/graphics/object_events/pics/misc/ball_moon.png new file mode 100644 index 0000000000..f13f34e769 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_moon.png differ diff --git a/graphics/object_events/pics/misc/ball_nest.png b/graphics/object_events/pics/misc/ball_nest.png new file mode 100644 index 0000000000..48a409183b Binary files /dev/null and b/graphics/object_events/pics/misc/ball_nest.png differ diff --git a/graphics/object_events/pics/misc/ball_net.png b/graphics/object_events/pics/misc/ball_net.png new file mode 100644 index 0000000000..186d76650e Binary files /dev/null and b/graphics/object_events/pics/misc/ball_net.png differ diff --git a/graphics/object_events/pics/misc/ball_park.png b/graphics/object_events/pics/misc/ball_park.png new file mode 100644 index 0000000000..5cc3f1a9b0 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_park.png differ diff --git a/graphics/object_events/pics/misc/ball_premier.png b/graphics/object_events/pics/misc/ball_premier.png new file mode 100644 index 0000000000..8876cfe22d Binary files /dev/null and b/graphics/object_events/pics/misc/ball_premier.png differ diff --git a/graphics/object_events/pics/misc/ball_quick.png b/graphics/object_events/pics/misc/ball_quick.png new file mode 100644 index 0000000000..0f6c64b1b1 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_quick.png differ diff --git a/graphics/object_events/pics/misc/ball_repeat.png b/graphics/object_events/pics/misc/ball_repeat.png new file mode 100644 index 0000000000..36186b094c Binary files /dev/null and b/graphics/object_events/pics/misc/ball_repeat.png differ diff --git a/graphics/object_events/pics/misc/ball_safari.png b/graphics/object_events/pics/misc/ball_safari.png new file mode 100644 index 0000000000..f73846a9b8 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_safari.png differ diff --git a/graphics/object_events/pics/misc/ball_sport.png b/graphics/object_events/pics/misc/ball_sport.png new file mode 100644 index 0000000000..1c32d2609c Binary files /dev/null and b/graphics/object_events/pics/misc/ball_sport.png differ diff --git a/graphics/object_events/pics/misc/ball_strange.png b/graphics/object_events/pics/misc/ball_strange.png new file mode 100644 index 0000000000..ee09679dc2 Binary files /dev/null and b/graphics/object_events/pics/misc/ball_strange.png differ diff --git a/graphics/object_events/pics/misc/ball_timer.png b/graphics/object_events/pics/misc/ball_timer.png new file mode 100644 index 0000000000..878d6ea5ba Binary files /dev/null and b/graphics/object_events/pics/misc/ball_timer.png differ diff --git a/graphics/object_events/pics/misc/ball_ultra.png b/graphics/object_events/pics/misc/ball_ultra.png new file mode 100644 index 0000000000..171281666f Binary files /dev/null and b/graphics/object_events/pics/misc/ball_ultra.png differ diff --git a/graphics/object_events/pics/pokemon/wailord.png b/graphics/object_events/pics/pokemon/wailord.png new file mode 100644 index 0000000000..a7032a856a Binary files /dev/null and b/graphics/object_events/pics/pokemon/wailord.png differ diff --git a/graphics/object_events/pics/pokemon_old/substitute.png b/graphics/object_events/pics/pokemon_old/substitute.png new file mode 100644 index 0000000000..6be1e2ddcb Binary files /dev/null and b/graphics/object_events/pics/pokemon_old/substitute.png differ diff --git a/graphics/pokemon/question_mark/follower.png b/graphics/pokemon/question_mark/follower.png new file mode 100644 index 0000000000..6be1e2ddcb Binary files /dev/null and b/graphics/pokemon/question_mark/follower.png differ diff --git a/graphics/pokemon/wailord/follower.png b/graphics/pokemon/wailord/follower.png index cbf005ab31..a7032a856a 100644 Binary files a/graphics/pokemon/wailord/follower.png and b/graphics/pokemon/wailord/follower.png differ diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index e53f45c609..e7641f9826 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -278,6 +278,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/constants/event_objects.h b/include/constants/event_objects.h index a96a23617f..0ac58ff49e 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -300,6 +300,10 @@ // (You should not use 48x48 sprites/tables for compressed gfx) // 16x32, 32x32, 64x64 etc are fine +// Followers will emerge from the pokeball they are stored in, +// instead of a normal pokeball +#define OW_MON_POKEBALLS TRUE + #define SHADOW_SIZE_S 0 #define SHADOW_SIZE_M 1 #define SHADOW_SIZE_L 2 @@ -392,6 +396,44 @@ #define OBJ_EVENT_PAL_TAG_RS_BRENDAN 0x1122 #define OBJ_EVENT_PAL_TAG_RS_MAY 0x1123 #define OBJ_EVENT_PAL_TAG_DYNAMIC 0x1124 + +#if OW_MON_POKEBALLS +// Vanilla +#define OBJ_EVENT_PAL_TAG_BALL_MASTER 0x1150 +#define OBJ_EVENT_PAL_TAG_BALL_ULTRA 0x1151 +#define OBJ_EVENT_PAL_TAG_BALL_GREAT 0x1152 +#define OBJ_EVENT_PAL_TAG_BALL_SAFARI 0x1153 +#define OBJ_EVENT_PAL_TAG_BALL_NET 0x1154 +#define OBJ_EVENT_PAL_TAG_BALL_DIVE 0x1155 +#define OBJ_EVENT_PAL_TAG_BALL_NEST 0x1156 +#define OBJ_EVENT_PAL_TAG_BALL_REPEAT 0x1157 +#define OBJ_EVENT_PAL_TAG_BALL_TIMER 0x1158 +#define OBJ_EVENT_PAL_TAG_BALL_LUXURY 0x1159 +#define OBJ_EVENT_PAL_TAG_BALL_PREMIER 0x115A +// Gen IV/Sinnoh +#define OBJ_EVENT_PAL_TAG_BALL_DUSK 0x115B +#define OBJ_EVENT_PAL_TAG_BALL_HEAL 0x115C +#define OBJ_EVENT_PAL_TAG_BALL_QUICK 0x115D +#define OBJ_EVENT_PAL_TAG_BALL_CHERISH 0x115E +#define OBJ_EVENT_PAL_TAG_BALL_PARK 0x115F +// Gen II/Johto Apricorns +#define OBJ_EVENT_PAL_TAG_BALL_FAST 0x1160 +#define OBJ_EVENT_PAL_TAG_BALL_LEVEL 0x1161 +#define OBJ_EVENT_PAL_TAG_BALL_LURE 0x1162 +#define OBJ_EVENT_PAL_TAG_BALL_HEAVY 0x1163 +#define OBJ_EVENT_PAL_TAG_BALL_LOVE 0x1164 +#define OBJ_EVENT_PAL_TAG_BALL_FRIEND 0x1165 +#define OBJ_EVENT_PAL_TAG_BALL_MOON 0x1166 +#define OBJ_EVENT_PAL_TAG_BALL_SPORT 0x1167 +// Gen V +#define OBJ_EVENT_PAL_TAG_BALL_DREAM 0x1168 +// Gen VII +#define OBJ_EVENT_PAL_TAG_BALL_BEAST 0x1169 +// 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) diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index 7732a5be8e..a6a883c92f 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -113,7 +113,6 @@ #define FLDEFFOBJ_BUBBLES 34 #define FLDEFFOBJ_SMALL_SPARKLE 35 #define FLDEFFOBJ_RAYQUAZA 36 - #define FLDEFFOBJ_TRACKS_SLITHER 37 #define FLDEFFOBJ_TRACKS_SPOT 38 #define FLDEFFOBJ_TRACKS_BUG 39 @@ -131,11 +130,10 @@ #define FLDEFF_PAL_TAG_HOF_MONITOR 0x1010 #define FLDEFF_PAL_TAG_UNKNOWN 0x1011 - // Duplicates of event_object_movement tags -#define FLDEFF_PAL_TAG_MAY 0x1110 // OBJ_EVENT_PAL_TAG_MAY -#define FLDEFF_PAL_TAG_BRENDAN 0x1100 // OBJ_EVENT_PAL_TAG_BRENDAN -#define FLDEFF_PAL_TAG_NPC_1 0x1103 // OBJ_EVENT_PAL_TAG_NPC_1 -#define FLDEFF_PAL_TAG_NPC_2 0x1104 // OBJ_EVENT_PAL_TAG_NPC_2 +#define FLDEFF_PAL_TAG_MAY 0x1110 // OBJ_EVENT_PAL_TAG_MAY +#define FLDEFF_PAL_TAG_BRENDAN 0x1100 // OBJ_EVENT_PAL_TAG_BRENDAN +#define FLDEFF_PAL_TAG_NPC_1 0x1103 // OBJ_EVENT_PAL_TAG_NPC_1 +#define FLDEFF_PAL_TAG_NPC_2 0x1104 // OBJ_EVENT_PAL_TAG_NPC_2 #endif // GUARD_FIELD_EFFECT_CONSTANTS_H diff --git a/include/decompress.h b/include/decompress.h index 8b4f8106ad..9e8c30286d 100644 --- a/include/decompress.h +++ b/include/decompress.h @@ -8,6 +8,8 @@ extern u8 ALIGNED(4) gDecompressionBuffer[0x4000]; void LZDecompressWram(const u32 *src, void *dest); void LZDecompressVram(const u32 *src, void *dest); +bool32 IsLZ77Data(const void *ptr, u32 minSize, u32 maxSize); + u16 LoadCompressedSpriteSheet(const struct CompressedSpriteSheet *src); u16 LoadCompressedSpriteSheetByTemplate(const struct SpriteTemplate *template, s32 offset); void LoadCompressedSpriteSheetOverrideBuffer(const struct CompressedSpriteSheet *src, void *buffer); diff --git a/include/event_object_movement.h b/include/event_object_movement.h index cf88225c43..706ebc2808 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -293,6 +293,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 64ef44c269..c9a9ef4ae6 100644 --- a/include/follower_helper.h +++ b/include/follower_helper.h @@ -16,81 +16,67 @@ enum { FOLLOWER_EMOTION_LENGTH, }; -// This struct is optimized for size -// Each "section" can be used to combine multiple conditions, -// i.e, species and map -// Just set the flags accordingly and use the right union member -struct __attribute__((packed)) FollowerMsgInfoExtended { +// Can be either 3 bytes, a u16 and a byte, or a 24-bit value +union __attribute__((packed)) MsgConditionData { + u8 bytes[3]; + struct __attribute__((packed)) { + u16 hw; + u8 b; + } split; + u32 raw:24; +}; // size = 0x3 + +struct __attribute__((packed)) MsgCondition { + u32 type:8; + union MsgConditionData data; +}; // size = 0x4 + +struct FollowerMsgInfoExtended { const u8 *text; const u8 *script; - union __attribute__((packed)) { - u16 species:10; - struct __attribute__((packed)) { - u16 type1:5; - u16 type2:5; // if >= NUMBER_OF_MON_TYPES, inverts checking for type1 - } types; - u16 status:10; - } st; - u16 stFlags:2; // 0 = no matching, 1 = species matching, 2 = type matching, 3 = status - u16 emotion:4; // emotion for this message - - union __attribute__((packed)) { - struct __attribute__((packed)) { - u16 mapSec:8; - } mapSec; - struct __attribute__((packed)) { - u16 mapNum:8; - u16 mapGroup:6; - } map; - struct __attribute__((packed)) { - u16 behavior1:8; - u16 behavior2:6; // not full; only goes up to 0x3F - } mb; - } mm; - u16 mmFlags:2; // 1 = map sec, 2 = map, 3 = metatile behavior - - union __attribute__((packed)) { - struct __attribute__((packed)) { - u16 weather1:5; - u16 weather2:5; - } weather; - u16 song:10; - u16 timeOfDay:10; - } wt; - u16 wtFlags:2; // 1 = weather matching, 2 = song, 3 = time - u16 weight:3; + u32 emotion:4; + u32 weight:3; // if set, `text` is treated as an array of up to 4 texts instead - u16 textSpread:1; + // which one is displayed is chosen at random + u32 textSpread:1; + u32 orFlag:1; // if set, *any* condition can match, rather than all - union __attribute__((packed)) { - struct __attribute__((packed)) { - u16 behavior:8; - u16 distance:6; - } mb; - } near; - u16 nearFlags:2; // 1 = mb within '+'-shaped distance away -}; + struct MsgCondition conditions[5]; +}; // size = 8 + 4 + 5*4 = 32, 0x20 -enum { - ST_FLAGS_SPECIES = 1, - ST_FLAGS_TYPE, - ST_FLAGS_STATUS, -}; +// Follower message conditions +#define MSG_COND_NONE 0 +#define MSG_COND_SPECIES 1 +#define MSG_COND_TYPE 2 +#define MSG_COND_STATUS 3 +#define MSG_COND_MAPSEC 4 +#define MSG_COND_MAP 5 +#define MSG_COND_ON_MB 6 +#define MSG_COND_WEATHER 7 +#define MSG_COND_MUSIC 8 +#define MSG_COND_TIME_OF_DAY 9 +#define MSG_COND_NEAR_MB 10 -enum { - MM_FLAGS_MAPSEC = 1, - MM_FLAGS_MAP, - MM_FLAGS_MB, // (m)etatile (b)ehavior -}; +#define MATCH_U24(type, value) {type, {.raw = value}} +#define MATCH_U16(type, value1, value2) {type, {.split = {.hw = value1, .b = value2}}} +#define MATCH_U8(type, v1, v2, v3) {type, {.bytes = {v1, v2, v3}}} -enum { - WT_FLAGS_WEATHER = 1, - WT_FLAGS_MUSIC, - WT_FLAGS_TIME, -}; - -#define NEAR_FLAGS_MB 1 +#define MATCH_SPECIES(species) MATCH_U24(MSG_COND_SPECIES, species) +#define MATCH_TYPES(type1, type2) MATCH_U8(MSG_COND_TYPE, type1, type2, 0) +// Checks that follower has *neither* of the two types +#define MATCH_NOT_TYPES(type1, type2) MATCH_U8(MSG_COND_TYPE, type1, type2, TYPE_NONE) +#define MATCH_STATUS(status) MATCH_U24(MSG_COND_STATUS, status) +#define MATCH_MAPSEC(mapsec) MATCH_U24(MSG_COND_MAPSEC, mapsec) +#define MATCH_MAP_RAW(mapGroup, mapNum) MATCH_U8(MSG_COND_MAP, mapGroup, mapNum, 0) +#define MATCH_MAP(map) MATCH_U8(MSG_COND_MAP, MAP_GROUP(map), MAP_NUM(map), 0) +// Matches one of two metatile behaviors follower is standing on +#define MATCH_ON_MB(mb1, mb2) MATCH_U8(MSG_COND_ON_MB, mb1, mb2, 0) +#define MATCH_WEATHER(weather1, weather2) MATCH_U8(MSG_COND_WEATHER, weather1, weather2, 0) +#define MATCH_MUSIC(song) MATCH_U24(MSG_COND_MUSIC, song) +#define MATCH_TIME_OF_DAY(time) MATCH_U24(MSG_COND_TIME_OF_DAY, time) +// Matches metatile behavior within a '+' shape of size `distance` +#define MATCH_NEAR_MB(mb, distance) MATCH_U8(MSG_COND_NEAR_MB, mb, distance, 0) enum { COND_MSG_CELEBI, @@ -125,5 +111,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 2c2d46243d..928bcc7629 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -264,6 +264,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/include/pokemon.h b/include/pokemon.h index d24225ef0b..1b82018da6 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -613,6 +613,7 @@ extern const struct MoveInfo gMovesInfo[]; extern const u8 gFacilityClassToPicIndex[]; extern const u8 gFacilityClassToTrainerClass[]; extern const struct SpeciesInfo gSpeciesInfo[]; +extern const void* const gFollowerPalettes[NUM_SPECIES][2]; extern const u32 gExperienceTables[][MAX_LEVEL + 1]; extern const u8 gPPUpGetMask[]; extern const u8 gPPUpClearMask[]; diff --git a/spritesheet_rules.mk b/spritesheet_rules.mk index 2d7994124f..6cf5bd41e9 100644 --- a/spritesheet_rules.mk +++ b/spritesheet_rules.mk @@ -731,6 +731,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 $(POKEMONGFXDIR)/bulbasaur/follower.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 4 -mheight 4 @@ -3990,5 +3993,8 @@ $(POKEMONGFXDIR)/marowak/alolan/follower.4bpp: %.4bpp: %.png $(MISCGFXDIR)/emotes.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 2 -mheight 2 +# All pokeballs are 16x32 +$(OBJEVENTGFXDIR)/misc/ball_%.4bpp: $(OBJEVENTGFXDIR)/misc/ball_%.png ; $(GFX) $< $@ -mwidth 2 -mheight 4 + graphics/door_anims/battle_tower_multi_corridor.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 2 -mheight 4 diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index be102cc56a..366861db80 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -28,13 +28,13 @@ static const struct SpriteFrameImage sPicTable_ShadowExtraLarge[] = { obj_frame_tiles(gFieldEffectObjectPic_ShadowExtraLarge), }; -const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowSmall = {0xFFFF, TAG_WEATHER_START, &gObjectEventBaseOam_8x8, sAnimTable_Shadow, sPicTable_ShadowSmall, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; +const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowSmall = {TAG_NONE, TAG_WEATHER_START, &gObjectEventBaseOam_8x8, sAnimTable_Shadow, sPicTable_ShadowSmall, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; -const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowMedium = {0xFFFF, TAG_WEATHER_START, &gObjectEventBaseOam_16x8, sAnimTable_Shadow, sPicTable_ShadowMedium, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; +const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowMedium = {TAG_NONE, TAG_WEATHER_START, &gObjectEventBaseOam_16x8, sAnimTable_Shadow, sPicTable_ShadowMedium, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; -const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowLarge = {0xFFFF, TAG_WEATHER_START, &gObjectEventBaseOam_32x8, sAnimTable_Shadow, sPicTable_ShadowLarge, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; +const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowLarge = {TAG_NONE, TAG_WEATHER_START, &gObjectEventBaseOam_32x8, sAnimTable_Shadow, sPicTable_ShadowLarge, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; -const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowExtraLarge = {0xFFFF, TAG_WEATHER_START, &gObjectEventBaseOam_64x32, sAnimTable_Shadow, sPicTable_ShadowExtraLarge, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; +const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowExtraLarge = {TAG_NONE, TAG_WEATHER_START, &gObjectEventBaseOam_64x32, sAnimTable_Shadow, sPicTable_ShadowExtraLarge, gDummySpriteAffineAnimTable, UpdateShadowFieldEffect}; static const struct SpriteFrameImage sPicTable_TallGrass[] = { overworld_frame(gFieldEffectObjectPic_TallGrass, 2, 2, 0), diff --git a/src/data/graphics/pokemon.h b/src/data/graphics/pokemon.h index f20a73850f..164d207379 100644 --- a/src/data/graphics/pokemon.h +++ b/src/data/graphics/pokemon.h @@ -12,9 +12,9 @@ const u8 gMonIcon_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mark/ico #if P_FOOTPRINTS const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mark/footprint.1bpp"); #endif //P_FOOTPRINTS -//#if P_FOLLOWERS - const u32 gObjectEventPic_None[] = INCBIN_COMP("graphics/pokemon/porygon/follower.4bpp"); -//#endif //P_FOLLOWERS +#if P_FOLLOWERS + const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_mark/follower.4bpp"); +#endif //P_FOLLOWERS #if P_FAMILY_BULBASAUR const u32 gMonFrontPic_Bulbasaur[] = INCBIN_U32("graphics/pokemon/bulbasaur/anim_front.4bpp.lz"); diff --git a/src/data/object_events/object_event_anims.h b/src/data/object_events/object_event_anims.h index 6483eec40b..bde3c3b0d5 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), @@ -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[] = { }; 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 32459c8c1b..e185339d8d 100755 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -381,4 +381,75 @@ 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/pokemon/question_mark/follower.gbapal"); + const u16 gObjectEventPaletteEmotes[] = INCBIN_U16("graphics/misc/emotes.gbapal"); + +#if OW_MON_POKEBALLS +const u32 gObjectEventPic_MasterBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_master.4bpp"); +const u32 gObjectEventPic_UltraBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_ultra.4bpp"); +const u32 gObjectEventPic_GreatBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_great.4bpp"); +const u32 gObjectEventPic_SafariBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_safari.4bpp"); +const u32 gObjectEventPic_NetBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_net.4bpp"); +const u32 gObjectEventPic_DiveBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_dive.4bpp"); +const u32 gObjectEventPic_NestBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_nest.4bpp"); +const u32 gObjectEventPic_RepeatBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_repeat.4bpp"); +const u32 gObjectEventPic_TimerBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_timer.4bpp"); +const u32 gObjectEventPic_LuxuryBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_luxury.4bpp"); +const u32 gObjectEventPic_PremierBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_premier.4bpp"); +const u32 gObjectEventPic_DuskBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_dusk.4bpp"); +const u32 gObjectEventPic_HealBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_heal.4bpp"); +const u32 gObjectEventPic_QuickBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_quick.4bpp"); +const u32 gObjectEventPic_CherishBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_cherish.4bpp"); +const u32 gObjectEventPic_ParkBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_park.4bpp"); +const u32 gObjectEventPic_FastBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_fast.4bpp"); +const u32 gObjectEventPic_LevelBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_level.4bpp"); +const u32 gObjectEventPic_LureBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_lure.4bpp"); +const u32 gObjectEventPic_HeavyBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_heavy.4bpp"); +const u32 gObjectEventPic_LoveBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_love.4bpp"); +const u32 gObjectEventPic_FriendBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_friend.4bpp"); +const u32 gObjectEventPic_MoonBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_moon.4bpp"); +const u32 gObjectEventPic_SportBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_sport.4bpp"); +const u32 gObjectEventPic_DreamBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_dream.4bpp"); +const u32 gObjectEventPic_BeastBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_beast.4bpp"); +#ifdef ITEM_STRANGE_BALL +const u32 gObjectEventPic_StrangeBall[] = INCBIN_U32("graphics/object_events/pics/misc/ball_strange.4bpp"); +#endif +#endif + +#if OW_MON_POKEBALLS +// Palettes are small, so always include all of the palettes (no #ifdef) +// Vanilla +const u16 gObjectEventPal_MasterBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_master.gbapal"); +const u16 gObjectEventPal_UltraBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_ultra.gbapal"); +const u16 gObjectEventPal_GreatBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_great.gbapal"); +const u16 gObjectEventPal_SafariBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_safari.gbapal"); +const u16 gObjectEventPal_NetBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_net.gbapal"); +const u16 gObjectEventPal_DiveBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_dive.gbapal"); +const u16 gObjectEventPal_NestBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_nest.gbapal"); +const u16 gObjectEventPal_RepeatBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_repeat.gbapal"); +const u16 gObjectEventPal_TimerBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_timer.gbapal"); +const u16 gObjectEventPal_LuxuryBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_luxury.gbapal"); +const u16 gObjectEventPal_PremierBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_premier.gbapal"); +// Gen IV/Sinnoh +const u16 gObjectEventPal_DuskBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_dusk.gbapal"); +const u16 gObjectEventPal_HealBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_heal.gbapal"); +const u16 gObjectEventPal_QuickBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_quick.gbapal"); +const u16 gObjectEventPal_CherishBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_cherish.gbapal"); +const u16 gObjectEventPal_ParkBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_park.gbapal"); +// Gen II/Johto Apricorns +const u16 gObjectEventPal_FastBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_fast.gbapal"); +const u16 gObjectEventPal_LevelBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_level.gbapal"); +const u16 gObjectEventPal_LureBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_lure.gbapal"); +const u16 gObjectEventPal_HeavyBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_heavy.gbapal"); +const u16 gObjectEventPal_LoveBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_love.gbapal"); +const u16 gObjectEventPal_FriendBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_friend.gbapal"); +const u16 gObjectEventPal_MoonBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_moon.gbapal"); +const u16 gObjectEventPal_SportBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_sport.gbapal"); +// Gen V +const u16 gObjectEventPal_DreamBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_dream.gbapal"); +// Gen VII +const u16 gObjectEventPal_BeastBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_beast.gbapal"); +// Gen VIII +const u16 gObjectEventPal_StrangeBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_strange.gbapal"); +#endif diff --git a/src/data/object_events/object_event_graphics_info_followers.h b/src/data/object_events/object_event_graphics_info_followers.h new file mode 100644 index 0000000000..12ded86610 --- /dev/null +++ b/src/data/object_events/object_event_graphics_info_followers.h @@ -0,0 +1,67 @@ +#if OW_MON_POKEBALLS + +#define POKEBALL_GFX_INFO(NAME) \ + [BALL_##NAME] = { \ + .tileTag = TAG_NONE, \ + .paletteTag = OBJ_EVENT_PAL_TAG_BALL_##NAME, \ + .size = 256, \ + .width = 16, \ + .height = 32, \ + .shadowSize = SHADOW_SIZE_M, \ + .inanimate = TRUE, \ + .oam = &gObjectEventBaseOam_16x32, \ + .subspriteTables = sOamTables_16x32, \ + .anims = sAnimTable_Following, \ + .images = sPicTable_Ball_##NAME, \ + .affineAnims = gDummySpriteAffineAnimTable, \ + } + + +const struct ObjectEventGraphicsInfo gPokeballGraphics[POKEBALL_COUNT] = { + // Vanilla + POKEBALL_GFX_INFO(MASTER), + POKEBALL_GFX_INFO(ULTRA), + POKEBALL_GFX_INFO(GREAT), + POKEBALL_GFX_INFO(SAFARI), + POKEBALL_GFX_INFO(NET), + POKEBALL_GFX_INFO(DIVE), + POKEBALL_GFX_INFO(NEST), + POKEBALL_GFX_INFO(REPEAT), + POKEBALL_GFX_INFO(TIMER), + POKEBALL_GFX_INFO(LUXURY), + POKEBALL_GFX_INFO(PREMIER), + // Gen IV/Sinnoh pokeballs + #ifdef ITEM_DUSK_BALL + POKEBALL_GFX_INFO(DUSK), + POKEBALL_GFX_INFO(HEAL), + POKEBALL_GFX_INFO(QUICK), + POKEBALL_GFX_INFO(CHERISH), + #endif + #ifdef ITEM_PARK_BALL + POKEBALL_GFX_INFO(PARK), + #endif + // Gen II/Johto Apricorn pokeballs + #ifdef ITEM_FAST_BALL + POKEBALL_GFX_INFO(FAST), + POKEBALL_GFX_INFO(LEVEL), + POKEBALL_GFX_INFO(LURE), + POKEBALL_GFX_INFO(HEAVY), + POKEBALL_GFX_INFO(LOVE), + POKEBALL_GFX_INFO(FRIEND), + POKEBALL_GFX_INFO(MOON), + POKEBALL_GFX_INFO(SPORT), + #endif + // Gen V + #ifdef ITEM_DREAM_BALL + POKEBALL_GFX_INFO(DREAM), + #endif + // Gen VII + #ifdef ITEM_BEAST_BALL + POKEBALL_GFX_INFO(BEAST), + #endif + // Gen VIII + #ifdef ITEM_STRANGE_BALL + POKEBALL_GFX_INFO(STRANGE), + #endif +}; +#endif diff --git a/src/data/object_events/object_event_pic_tables.h b/src/data/object_events/object_event_pic_tables.h index 4464875a89..682b57f236 100755 --- a/src/data/object_events/object_event_pic_tables.h +++ b/src/data/object_events/object_event_pic_tables.h @@ -1113,9 +1113,109 @@ static const struct SpriteFrameImage sPicTable_Brandon[] = { }; static const struct SpriteFrameImage sPicTable_AnimatedBall[] = { - overworld_ascending_frames(gObjectEventPic_AnimatedBall, 2, 4), + overworld_frame(gObjectEventPic_AnimatedBall, 2, 4, 0), + overworld_frame(gObjectEventPic_AnimatedBall, 2, 4, 1), + overworld_frame(gObjectEventPic_AnimatedBall, 2, 4, 2), + overworld_frame(gObjectEventPic_AnimatedBall, 2, 4, 3), + overworld_frame(gObjectEventPic_AnimatedBall, 2, 4, 4), + overworld_frame(gObjectEventPic_AnimatedBall, 2, 4, 0), }; +#if OW_MON_POKEBALLS + +#define POKEBALL_PIC_FRAMES(name) \ + overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 0), \ + overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 1), \ + overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 2), \ + overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 3), \ + overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 4), \ + overworld_frame(gObjectEventPic_##name##Ball, 2, 4, 0) + +static const struct SpriteFrameImage sPicTable_Ball_MASTER[] = { + POKEBALL_PIC_FRAMES(Master), +}; +static const struct SpriteFrameImage sPicTable_Ball_ULTRA[] = { + POKEBALL_PIC_FRAMES(Ultra), +}; +static const struct SpriteFrameImage sPicTable_Ball_GREAT[] = { + POKEBALL_PIC_FRAMES(Great), +}; +static const struct SpriteFrameImage sPicTable_Ball_SAFARI[] = { + POKEBALL_PIC_FRAMES(Safari), +}; +static const struct SpriteFrameImage sPicTable_Ball_NET[] = { + POKEBALL_PIC_FRAMES(Net), +}; +static const struct SpriteFrameImage sPicTable_Ball_DIVE[] = { + POKEBALL_PIC_FRAMES(Dive), +}; +static const struct SpriteFrameImage sPicTable_Ball_NEST[] = { + POKEBALL_PIC_FRAMES(Nest), +}; +static const struct SpriteFrameImage sPicTable_Ball_REPEAT[] = { + POKEBALL_PIC_FRAMES(Repeat), +}; +static const struct SpriteFrameImage sPicTable_Ball_TIMER[] = { + POKEBALL_PIC_FRAMES(Timer), +}; +static const struct SpriteFrameImage sPicTable_Ball_LUXURY[] = { + POKEBALL_PIC_FRAMES(Luxury), +}; +static const struct SpriteFrameImage sPicTable_Ball_PREMIER[] = { + POKEBALL_PIC_FRAMES(Premier), +}; +static const struct SpriteFrameImage sPicTable_Ball_DUSK[] = { + POKEBALL_PIC_FRAMES(Dusk), +}; +static const struct SpriteFrameImage sPicTable_Ball_HEAL[] = { + POKEBALL_PIC_FRAMES(Heal), +}; +static const struct SpriteFrameImage sPicTable_Ball_QUICK[] = { + POKEBALL_PIC_FRAMES(Quick), +}; +static const struct SpriteFrameImage sPicTable_Ball_CHERISH[] = { + POKEBALL_PIC_FRAMES(Cherish), +}; +static const struct SpriteFrameImage sPicTable_Ball_PARK[] = { + POKEBALL_PIC_FRAMES(Park), +}; +static const struct SpriteFrameImage sPicTable_Ball_FAST[] = { + POKEBALL_PIC_FRAMES(Fast), +}; +static const struct SpriteFrameImage sPicTable_Ball_LEVEL[] = { + POKEBALL_PIC_FRAMES(Level), +}; +static const struct SpriteFrameImage sPicTable_Ball_LURE[] = { + POKEBALL_PIC_FRAMES(Lure), +}; +static const struct SpriteFrameImage sPicTable_Ball_HEAVY[] = { + POKEBALL_PIC_FRAMES(Heavy), +}; +static const struct SpriteFrameImage sPicTable_Ball_LOVE[] = { + POKEBALL_PIC_FRAMES(Love), +}; +static const struct SpriteFrameImage sPicTable_Ball_FRIEND[] = { + POKEBALL_PIC_FRAMES(Friend), +}; +static const struct SpriteFrameImage sPicTable_Ball_MOON[] = { + POKEBALL_PIC_FRAMES(Moon), +}; +static const struct SpriteFrameImage sPicTable_Ball_SPORT[] = { + POKEBALL_PIC_FRAMES(Sport), +}; +static const struct SpriteFrameImage sPicTable_Ball_DREAM[] = { + POKEBALL_PIC_FRAMES(Dream), +}; +static const struct SpriteFrameImage sPicTable_Ball_BEAST[] = { + POKEBALL_PIC_FRAMES(Beast), +}; +#ifdef ITEM_STRANGE_BALL +static const struct SpriteFrameImage sPicTable_Ball_STRANGE[] = { + POKEBALL_PIC_FRAMES(Strange), +}; +#endif +#endif + static const struct SpriteFrameImage sPicTable_DeoxysOld[] = { overworld_frame(gObjectEventPic_DeoxysOld, 4, 4, 0), overworld_frame(gObjectEventPic_DeoxysOld, 4, 4, 0), diff --git a/src/data/object_events/object_event_pic_tables_followers.h b/src/data/object_events/object_event_pic_tables_followers.h index d8087c31cf..a2d4268ece 100644 --- a/src/data/object_events/object_event_pic_tables_followers.h +++ b/src/data/object_events/object_event_pic_tables_followers.h @@ -1,9 +1,7 @@ - -static const struct SpriteFrameImage sPicTable_None[] = { - overworld_ascending_frames(gObjectEventPic_None, 4, 4), -}; - #if P_FOLLOWERS +static const struct SpriteFrameImage sPicTable_Substitute[] = { + overworld_ascending_frames(gObjectEventPic_Substitute, 4, 4), +}; #if P_FAMILY_BULBASAUR static const struct SpriteFrameImage sPicTable_Bulbasaur[] = { diff --git a/src/data/pokemon/species_info.h b/src/data/pokemon/species_info.h index 43a939985e..0b8386ae9d 100644 --- a/src/data/pokemon/species_info.h +++ b/src/data/pokemon/species_info.h @@ -291,7 +291,7 @@ const struct SpeciesInfo gSpeciesInfo[] = .backAnimId = BACK_ANIM_NONE, PALETTES(CircledQuestionMark), ICON(QuestionMark, 0), - .followerData = {TAG_NONE, OBJ_EVENT_PAL_TAG_DYNAMIC, OBJ_EVENT_PAL_TAG_NONE, 512, 32, 32, 2, SHADOW_SIZE_M, FALSE, COMP, TRACKS_FOOT, &gObjectEventBaseOam_32x32, sOamTables_32x32, sAnimTable_Following, sPicTable_None, gDummySpriteAffineAnimTable}, + .followerData = {TAG_NONE, OBJ_EVENT_PAL_TAG_SUBSTITUTE, OBJ_EVENT_PAL_TAG_NONE, 512, 32, 32, 2, SHADOW_SIZE_M, FALSE, COMP, TRACKS_FOOT, &gObjectEventBaseOam_32x32, sOamTables_32x32, sAnimTable_Following, sPicTable_Substitute, gDummySpriteAffineAnimTable}, LEARNSETS(None), }, @@ -380,3 +380,16 @@ const struct SpeciesInfo gSpeciesInfo[] = }, */ }; + + + +// Standalone follower palettes +// If not NULL, entries here override the front-sprite-based pals +// used by OBJ_EVENT_PAL_TAG_DYNAMIC +// Palette data may be compressed, or not +const void* const gFollowerPalettes[NUM_SPECIES][2] = +{ + // Must have at least one entry, or ARRAY_COUNT comparison fails + // (SPECIES_NONE does not use OBJ_EVENT_PAL_TAG_DYNAMIC anyway) + [SPECIES_NONE] = {gMonPalette_CircledQuestionMark, gMonShinyPalette_CircledQuestionMark}, +}; 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/decompress.c b/src/decompress.c index df3f9ef844..6143f669bf 100644 --- a/src/decompress.c +++ b/src/decompress.c @@ -18,6 +18,24 @@ void LZDecompressVram(const u32 *src, void *dest) LZ77UnCompVram(src, dest); } +// Checks if `ptr` is likely LZ77 data +// Checks word-alignment, min/max size, and header byte +bool32 IsLZ77Data(const void *ptr, u32 minSize, u32 maxSize) { + const u8 *data = ptr; + u32 size; + // Compressed data must be word aligned + if (((u32)ptr) & 3) + return FALSE; + // Check LZ77 header byte + // See https://problemkaputt.de/gbatek.htm#biosdecompressionfunctions + if (data[0] != 0x10) + return FALSE; + + // Read 24-bit uncompressed size + size = data[1] | (data[2] << 8) | (data[3] << 16); + return (size >= minSize && size <= maxSize); +} + u16 LoadCompressedSpriteSheet(const struct CompressedSpriteSheet *src) { struct SpriteSheet dest; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 31d0e4ae38..c536eeee63 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1,5 +1,6 @@ #include "global.h" #include "malloc.h" +#include "battle_anim.h" #include "battle_pyramid.h" #include "battle_util.h" #include "berry.h" @@ -19,11 +20,13 @@ #include "fieldmap.h" #include "follower_helper.h" #include "gpu_regs.h" +#include "graphics.h" #include "mauville_old_man.h" #include "metatile_behavior.h" #include "overworld.h" #include "palette.h" #include "pokemon.h" +#include "pokeball.h" #include "random.h" #include "region_map.h" #include "script.h" @@ -188,7 +191,7 @@ static u8 DoJumpSpecialSpriteMovement(struct Sprite *); static void CreateLevitateMovementTask(struct ObjectEvent *); static void DestroyLevitateMovementTask(u8); static bool8 GetFollowerInfo(u16 *species, u8 *form, u8 *shiny); -static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool8 shiny); +static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool32 shiny); static const struct ObjectEventGraphicsInfo * SpeciesToGraphicsInfo(u16 species, u8 form); static bool8 NpcTakeStep(struct Sprite *); static bool8 IsElevationMismatchAt(u8, s16, s16); @@ -453,6 +456,7 @@ const u8 gInitialMovementTypeFacingDirections[] = { #include "data/object_events/base_oam.h" #include "data/object_events/object_event_subsprites.h" #include "data/object_events/object_event_graphics_info.h" +#include "data/object_events/object_event_graphics_info_followers.h" static const struct SpritePalette sObjectEventSpritePalettes[] = { {gObjectEventPal_Npc1, OBJ_EVENT_PAL_TAG_NPC_1}, @@ -490,6 +494,39 @@ static const struct SpritePalette sObjectEventSpritePalettes[] = { {gObjectEventPal_Lugia, OBJ_EVENT_PAL_TAG_LUGIA}, {gObjectEventPal_RubySapphireBrendan, OBJ_EVENT_PAL_TAG_RS_BRENDAN}, {gObjectEventPal_RubySapphireMay, OBJ_EVENT_PAL_TAG_RS_MAY}, +#if OW_MON_POKEBALLS + {gObjectEventPal_MasterBall, OBJ_EVENT_PAL_TAG_BALL_MASTER}, + {gObjectEventPal_UltraBall, OBJ_EVENT_PAL_TAG_BALL_ULTRA}, + {gObjectEventPal_GreatBall, OBJ_EVENT_PAL_TAG_BALL_GREAT}, + {gObjectEventPal_SafariBall, OBJ_EVENT_PAL_TAG_BALL_SAFARI}, + {gObjectEventPal_NetBall, OBJ_EVENT_PAL_TAG_BALL_NET}, + {gObjectEventPal_DiveBall, OBJ_EVENT_PAL_TAG_BALL_DIVE}, + {gObjectEventPal_NestBall, OBJ_EVENT_PAL_TAG_BALL_NEST}, + {gObjectEventPal_RepeatBall, OBJ_EVENT_PAL_TAG_BALL_REPEAT}, + {gObjectEventPal_TimerBall, OBJ_EVENT_PAL_TAG_BALL_TIMER}, + {gObjectEventPal_LuxuryBall, OBJ_EVENT_PAL_TAG_BALL_LUXURY}, + {gObjectEventPal_PremierBall, OBJ_EVENT_PAL_TAG_BALL_PREMIER}, + {gObjectEventPal_DuskBall, OBJ_EVENT_PAL_TAG_BALL_DUSK}, + {gObjectEventPal_HealBall, OBJ_EVENT_PAL_TAG_BALL_HEAL}, + {gObjectEventPal_QuickBall, OBJ_EVENT_PAL_TAG_BALL_QUICK}, + {gObjectEventPal_CherishBall, OBJ_EVENT_PAL_TAG_BALL_CHERISH}, + {gObjectEventPal_ParkBall, OBJ_EVENT_PAL_TAG_BALL_PARK}, + {gObjectEventPal_FastBall, OBJ_EVENT_PAL_TAG_BALL_FAST}, + {gObjectEventPal_LevelBall, OBJ_EVENT_PAL_TAG_BALL_LEVEL}, + {gObjectEventPal_LureBall, OBJ_EVENT_PAL_TAG_BALL_LURE}, + {gObjectEventPal_HeavyBall, OBJ_EVENT_PAL_TAG_BALL_HEAVY}, + {gObjectEventPal_LoveBall, OBJ_EVENT_PAL_TAG_BALL_LOVE}, + {gObjectEventPal_FriendBall, OBJ_EVENT_PAL_TAG_BALL_FRIEND}, + {gObjectEventPal_MoonBall, OBJ_EVENT_PAL_TAG_BALL_MOON}, + {gObjectEventPal_SportBall, OBJ_EVENT_PAL_TAG_BALL_SPORT}, + {gObjectEventPal_DreamBall, OBJ_EVENT_PAL_TAG_BALL_DREAM}, + {gObjectEventPal_BeastBall, OBJ_EVENT_PAL_TAG_BALL_BEAST}, + // Gen VIII + #ifdef ITEM_STRANGE_BALL + {gObjectEventPal_StrangeBall, OBJ_EVENT_PAL_TAG_BALL_STRANGE}, + #endif +#endif + {gObjectEventPal_Substitute, OBJ_EVENT_PAL_TAG_SUBSTITUTE}, {gObjectEventPaletteEmotes, OBJ_EVENT_PAL_TAG_EMOTES}, #ifdef BUGFIX {NULL, OBJ_EVENT_PAL_TAG_NONE}, @@ -1419,6 +1456,31 @@ void RemoveAllObjectEventsExceptPlayer(void) } } +// 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; +} + u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid, struct Sprite *sprite) { u16 tag = info->tileTag; @@ -1458,9 +1520,16 @@ u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid, } // Going from sheet -> !sheet, reset tile number // (sheet stays loaded) + // Note: It's possible to load a non-sheet gfx + // larger than the allocated prefix space, + // in which case we would have to realloc + // TODO: Realloc usingSheet -> !usingSheet larger gfx } else if (sprite && sprite->usingSheet) { sprite->oam.tileNum = sprite->sheetTileStart; sprite->usingSheet = FALSE; + // Not usingSheet and info size differs; realloc tiles + } else if (sprite && !sprite->sheetTileStart && sprite->oam.size != info->oam->size) { + ReallocSpriteTiles(sprite, info->images->size); } return tag; } @@ -1663,6 +1732,7 @@ u8 CreateObjectGraphicsSprite(u16 graphicsId, void (*callback)(struct Sprite *), // Use shininess info from follower object // in future this should be passed in paletteNum = LoadDynamicFollowerPaletteFromGraphicsId(graphicsId, obj ? obj->shiny : FALSE, spriteTemplate); + spriteTemplate->paletteTag = GetSpritePaletteTagByPaletteNum(paletteNum); } else if (spriteTemplate->paletteTag != TAG_NONE) { @@ -1742,7 +1812,7 @@ u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevati // Return address of first conscious party mon or NULL struct Pokemon * GetFirstLiveMon(void) { - u8 i; + u32 i; for (i = 0; i < PARTY_SIZE; i++) { if (gPlayerParty[i].hp > 0 && !(gPlayerParty[i].box.isEgg || gPlayerParty[i].box.isBadEgg)) @@ -1754,7 +1824,7 @@ struct Pokemon * GetFirstLiveMon(void) // Return follower ObjectEvent or NULL struct ObjectEvent * GetFollowerObject(void) { - u8 i; + u32 i; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && gObjectEvents[i].active) @@ -1777,35 +1847,51 @@ static const struct ObjectEventGraphicsInfo * SpeciesToGraphicsInfo(u16 species, graphicsInfo = &gSpeciesInfo[species].followerData; break; } - // Try to avoid OOB access - if (OW_GFX_COMPRESS) - { - if (graphicsInfo->tileTag == 0 && species < NUM_SPECIES) - return &gSpeciesInfo[SPECIES_NONE].followerData; - else if (graphicsInfo->tileTag != TAG_NONE && species >= NUM_SPECIES) - return &gSpeciesInfo[SPECIES_NONE].followerData; - else - return graphicsInfo; - } + // Try to avoid OOB or undefined access + if (graphicsInfo->tileTag == 0 && species < NUM_SPECIES) + return &gSpeciesInfo[SPECIES_NONE].followerData; + else if (graphicsInfo->tileTag != TAG_NONE && species >= NUM_SPECIES) + return &gSpeciesInfo[SPECIES_NONE].followerData; else - return graphicsInfo->tileTag == TAG_NONE ? graphicsInfo : &gSpeciesInfo[SPECIES_NONE].followerData; + return graphicsInfo; } // Find, or load, the palette for the specified pokemon info -static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool8 shiny) +static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool32 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 u32 *palette = GetMonSpritePalFromSpecies(species, shiny, FALSE); //ETODO - if ((paletteNum = IndexOfSpritePaletteTag(species)) == 0xFF) + // palette already loaded + if ((paletteNum = IndexOfSpritePaletteTag(species)) < 16) + return paletteNum; + + // Use standalone palette, unless entry is OOB or NULL (fallback to front-sprite-based) + if (gFollowerPalettes[species][shiny & 1]) { + struct SpritePalette spritePalette = {.tag = shiny ? (species + SPECIES_SHINY_TAG) : species}; + spritePalette.data = gFollowerPalettes[species][shiny & 1]; + + // Check if pal data must be decompressed + if (IsLZ77Data(spritePalette.data, PLTT_SIZE_4BPP, PLTT_SIZE_4BPP)) { + // IsLZ77Data guarantees word-alignment, so casting this is safe + LZ77UnCompWram((u32*)spritePalette.data, gDecompressionBuffer); + spritePalette.data = (void*)gDecompressionBuffer; + } + paletteNum = LoadSpritePalette(&spritePalette); + } + else + { + // Use matching front sprite's normal/shiny palettes // Load compressed palette LoadCompressedSpritePaletteWithTag(palette, species); paletteNum = IndexOfSpritePaletteTag(species); // Tag is always present - if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog - UpdateSpritePaletteWithWeather(paletteNum); + } + + if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog + UpdateSpritePaletteWithWeather(paletteNum); return paletteNum; } @@ -1832,7 +1918,8 @@ static void FollowerSetGraphics(struct ObjectEvent *objEvent, u16 species, u8 fo } } -// Like FollowerSetGraphics, but does not reposition sprite; intended to be used for mid-movement form changes, etc. +// 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) { u32 species = OW_SPECIES(objEvent); @@ -1842,11 +1929,17 @@ static void RefreshFollowerGraphics(struct ObjectEvent *objEvent) struct Sprite *sprite = &gSprites[objEvent->spriteId]; u32 i = FindObjectEventPaletteIndexByTag(graphicsInfo->paletteTag); - // Forbid changing form to a new size/shape - // TODO: Reposition sprite, reallocate tiles if form size changes - if (sprite->oam.shape != graphicsInfo->oam->shape - || sprite->oam.size != graphicsInfo->oam->size) - return; + if (graphicsInfo->oam->size != sprite->oam.size) { + #if LARGE_OW_SUPPORT && !OW_GFX_COMPRESS + ReallocSpriteTiles(sprite, graphicsInfo->images->size); + #endif + // Add difference in Y vectors + sprite->y += -(graphicsInfo->height >> 1) - sprite->centerToCornerVecY; + } + + if (OW_GFX_COMPRESS) + LoadSheetGraphicsInfo(graphicsInfo, objEvent->graphicsId, sprite); + sprite->oam.shape = graphicsInfo->oam->shape; sprite->oam.size = graphicsInfo->oam->size; sprite->images = graphicsInfo->images; @@ -1869,8 +1962,6 @@ static void RefreshFollowerGraphics(struct ObjectEvent *objEvent) if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog UpdateSpritePaletteWithWeather(sprite->oam.paletteNum); } - if (OW_GFX_COMPRESS) - LoadSheetGraphicsInfo(graphicsInfo, objEvent->graphicsId, sprite); } static u16 GetOverworldCastformSpecies(void) @@ -1928,61 +2019,58 @@ void UpdateFollowingPokemon(void) 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)) - { - if (objEvent == NULL) - { - // Spawn follower - struct ObjectEventTemplate template = - { - .localId = OBJ_EVENT_ID_FOLLOWER, - .graphicsId = OBJ_EVENT_GFX_MON_BASE + species, - .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, - // store form info in template - .trainerRange_berryTreeId = (form & 0x1F) | (shiny << 5), - }; - objEvent = &gObjectEvents[SpawnSpecialObjectEvent(&template)]; - objEvent->invisible = TRUE; - } - sprite = &gSprites[objEvent->spriteId]; - // Follower appearance changed; move to player and set invisible - if (species != OW_SPECIES(objEvent) || shiny != objEvent->shiny || form != OW_FORM(objEvent)) - { - if (SpeciesToGraphicsInfo(species, 0)->height != SpeciesToGraphicsInfo(OW_SPECIES(objEvent), 0)->height - || SpeciesToGraphicsInfo(species, 0)->width != SpeciesToGraphicsInfo(OW_SPECIES(objEvent), 0)->width) - { - RemoveFollowingPokemon(); - UpdateFollowingPokemon(); - } - else - { - MoveObjectEventToMapCoords(objEvent, - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); - FollowerSetGraphics(objEvent, species, form, shiny); - objEvent->invisible = TRUE; - } - } - sprite->data[6] = 0; // set animation data - } - else + // 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 + u32 objId = gPlayerAvatar.objectEventId; + struct ObjectEventTemplate template = + { + .localId = OBJ_EVENT_ID_FOLLOWER, + .graphicsId = OBJ_EVENT_GFX_MON_BASE + species, + .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, + // store form info in template + .trainerRange_berryTreeId = (form & 0x1F) | (shiny << 5), + }; + 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 != OW_SPECIES(objEvent) || shiny != objEvent->shiny || form != OW_FORM(objEvent)) + { + MoveObjectEventToMapCoords( + objEvent, + gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, + gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y + ); + FollowerSetGraphics(objEvent, species, form, shiny); + objEvent->invisible = TRUE; + } + sprite->data[6] = 0; // set animation data } // Remove follower object. Idempotent. void RemoveFollowingPokemon(void) -{ +{ struct ObjectEvent *objectEvent = GetFollowerObject(); if (objectEvent == NULL) return; @@ -1990,20 +2078,12 @@ void RemoveFollowingPokemon(void) } // Determine whether follower *should* be visible -static bool8 IsFollowerVisible(void) +static bool32 IsFollowerVisible(void) { - u8 currentMBBehavior = gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior; - u8 previousMBBehavior = gObjectEvents[gPlayerAvatar.objectEventId].previousMetatileBehavior; - - if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_ACRO_BIKE | PLAYER_AVATAR_FLAG_MACH_BIKE)) - return FALSE; - if (MetatileBehavior_IsSurfableWaterOrUnderwater(currentMBBehavior) || MetatileBehavior_IsSurfableWaterOrUnderwater(previousMBBehavior)) - return FALSE; - if (MetatileBehavior_IsForcedMovementTile(currentMBBehavior) || MetatileBehavior_IsForcedMovementTile(previousMBBehavior)) - return FALSE; - if (gWeatherPtr->currWeather == WEATHER_UNDERWATER || gWeatherPtr->currWeather == WEATHER_UNDERWATER_BUBBLES) - return FALSE; - return TRUE; + return + !(TestPlayerAvatarFlags(FOLLOWER_INVISIBLE_FLAGS) + || MetatileBehavior_IsSurfableWaterOrUnderwater(gObjectEvents[gPlayerAvatar.objectEventId].previousMetatileBehavior) + || MetatileBehavior_IsForcedMovementTile(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior)); } static bool8 SpeciesHasType(u16 species, u8 type) @@ -2014,40 +2094,22 @@ static bool8 SpeciesHasType(u16 species, u8 type) // Returns a random index according to a list of weights static u8 RandomWeightedIndex(u8 *weights, u8 length) { - u8 i; + u32 i; u16 random_value; - u16 cum_weight = 0; - + u16 weightSum = 0; for (i = 0; i < length; i++) - cum_weight += weights[i]; - - random_value = Random() % cum_weight; - cum_weight = 0; + weightSum += weights[i]; + random_value = Random() % weightSum; + weightSum = 0; for (i = 0; i < length; i++) { - cum_weight += weights[i]; - if (random_value <= cum_weight) + weightSum += weights[i]; + if (random_value <= weightSum) return i; } - return length; + return 0; } -// 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) @@ -2064,7 +2126,6 @@ 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; @@ -2081,29 +2142,106 @@ struct SpecialEmote static u32 FindMetatileBehaviorWithinRange(s32 x, s32 y, u32 mb, u8 distance) { s32 i; - for (i = y+1; i <= y + distance; i++) + + for (i = y + 1; i <= y + distance; i++) if (MapGridGetMetatileBehaviorAt(x, i) == mb) return DIR_SOUTH; - for (i = y-1; i >= y - distance; i--) + + for (i = y - 1; i >= y - distance; i--) if (MapGridGetMetatileBehaviorAt(x, i) == mb) return DIR_NORTH; - for (i = x+1; i <= x + distance; i++) + + for (i = x + 1; i <= x + distance; i++) if (MapGridGetMetatileBehaviorAt(i, y) == mb) return DIR_EAST; - for (i = x-1; i >= x - distance; i--) + + for (i = x - 1; i >= x - distance; i--) if (MapGridGetMetatileBehaviorAt(i, y) == mb) return DIR_WEST; return DIR_NONE; } +// Check a single follower message condition +bool32 CheckMsgCondition(const struct MsgCondition *cond, struct Pokemon *mon, u32 species, struct ObjectEvent *obj) { + u32 multi; + if (species == SPECIES_NONE) + species = GetMonData(mon, MON_DATA_SPECIES); + + switch (cond->type) + { + case MSG_COND_SPECIES: + return (cond->data.raw == species); + case MSG_COND_TYPE: + multi = (SpeciesHasType(species, cond->data.bytes[0]) || + SpeciesHasType(species, cond->data.bytes[1])); + // if bytes[2] == TYPE_NONE, + // invert; check that mon has *neither* type! + if (cond->data.bytes[2] == 0) + return multi; + else + return !multi; + break; + case MSG_COND_STATUS: + return (cond->data.raw & mon->status); + case MSG_COND_MAPSEC: + return (cond->data.raw == gMapHeader.regionMapSectionId); + case MSG_COND_MAP: + return (gSaveBlock1Ptr->location.mapGroup == cond->data.bytes[0] && + gSaveBlock1Ptr->location.mapNum == cond->data.bytes[1]); + case MSG_COND_ON_MB: + return (obj->currentMetatileBehavior == cond->data.bytes[0] || + obj->currentMetatileBehavior == cond->data.bytes[1]); + case MSG_COND_WEATHER: + multi = GetCurrentWeather(); + return (multi == cond->data.bytes[0] || multi == cond->data.bytes[1]); + case MSG_COND_MUSIC: + return (cond->data.raw == GetCurrentMapMusic()); + // Added on `lighting` branch + // case MSG_COND_TIME_OF_DAY: + // break; + case MSG_COND_NEAR_MB: + multi = FindMetatileBehaviorWithinRange( + obj->currentCoords.x, obj->currentCoords.y, + cond->data.bytes[0], cond->data.bytes[1]); + if (multi) + gSpecialVar_Result = multi; + return multi; + case MSG_COND_NONE: + // fallthrough + default: + return TRUE; + } +} + +// Check if follower info can be displayed in the current situation; +// i.e, if all its conditions match +bool32 CheckMsgInfo(const struct FollowerMsgInfoExtended *info, struct Pokemon *mon, u32 species, struct ObjectEvent *obj) { + u32 i; + + // any condition matches + if (info->orFlag) { + for (i = 0; i < ARRAY_COUNT(info->conditions) && info->conditions[i].type; i++) + if (CheckMsgCondition(&info->conditions[i], mon, species, obj)) + return TRUE; + return FALSE; + // all conditions must match + } else { + for (i = 0; i < ARRAY_COUNT(info->conditions) && info->conditions[i].type; i++) + if (!CheckMsgCondition(&info->conditions[i], mon, species, obj)) + return FALSE; + return TRUE; + } +} + // 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; + u32 species; + s32 multi; + struct SpecialEmote condEmotes[16] = {0}; + u32 condCount = 0; + u32 emotion; struct ObjectEvent *objEvent = GetFollowerObject(); struct Pokemon *mon = GetFirstLiveMon(); u8 emotion_weight[FOLLOWER_EMOTION_LENGTH] = @@ -2114,20 +2252,19 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big [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_SURPRISE] = 10, + [FOLLOWER_EMOTION_CURIOUS] = 10, [FOLLOWER_EMOTION_MUSIC] = 15, }; u32 i, j; bool32 pickedCondition = FALSE; - if (mon == NULL) + if (mon == NULL) // failsafe { 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)) - ScriptJump(ctx, EventScript_FollowerEnd); + // 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) @@ -2143,24 +2280,24 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big emotion_weight[FOLLOWER_EMOTION_HAPPY] = 30; emotion_weight[FOLLOWER_EMOTION_LOVE] = 30; } - // Conditional messages follow + // Special C-based conditions follower // Weather-related if (GetCurrentWeather() == WEATHER_SUNNY_CLOUDS) - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_HAPPY, .index = 31}; + 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; - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_SAD, .index = 4}; - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_SAD, .index = 5}; + 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; - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_SAD, .index = 6}; + condEmotes[condCount++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_SAD, .index = 6}; } - // Gym type advantage/disadvantage scripts + // Gym type advantage/disadvantage if (GetCurrentMapMusic() == MUS_GYM || GetCurrentMapMusic() == MUS_RG_GYM) { switch (gMapHeader.regionMapSectionId) @@ -2210,49 +2347,33 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big { multi = GetTypeEffectiveness(mon, multi); if (multi <= UQ_4_12(0.5)) - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_HAPPY, .index = 32}; + condEmotes[condCount++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_HAPPY, .index = 32}; else if (multi >= UQ_4_12(2.0)) - cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_SAD, .index = 7}; + condEmotes[condCount++] = (struct SpecialEmote) {.emotion = FOLLOWER_EMOTION_SAD, .index = 7}; } } + emotion = RandomWeightedIndex(emotion_weight, FOLLOWER_EMOTION_LENGTH); if ((mon->status & STATUS1_PSN_ANY) && GetMonAbility(mon) != ABILITY_POISON_HEAL) 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++) + + // end special conditions + + // 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 (cond_emotes[i].emotion == emotion && (Random() < 0x10000 / (j++))) // Replace item with 1/j chance - multi = cond_emotes[i].index; + if (condEmotes[i].emotion == emotion && (Random() < 0x10000 / (j++))) // Replace each item with 1/j chance + multi = condEmotes[i].index; } - // Match scripted conditional messages - // With 50% chance, try to match scripted conditional messages + // (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) + if (!CheckMsgInfo(info, mon, species, objEvent)) 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)) { @@ -2260,7 +2381,8 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big pickedCondition = TRUE; } } - if (pickedCondition) // conditional message was chosen + // condition message was chosen + if (pickedCondition) { emotion = gFollowerConditionalMessages[multi].emotion; ObjectEventEmote(objEvent, emotion); @@ -2275,19 +2397,15 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big } ctx->data[0] = i ? ((u32*)gFollowerConditionalMessages[multi].text)[Random() % i] : 0; } - ScriptCall(ctx, gFollowerConditionalMessages[multi].script ? gFollowerConditionalMessages[multi].script : followerBasicMessages[emotion].script); + 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) 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; -} - -bool8 ScrFunc_followerfly(struct ScriptContext *ctx) -{ - SetMainCallback2(CB2_OpenFlyMap); + 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; } @@ -2469,7 +2587,6 @@ static u8 UpdateSpritePalette(const struct SpritePalette * spritePalette, struct } // 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); @@ -2485,13 +2602,22 @@ static void ObjectEventSetGraphics(struct ObjectEvent *objectEvent, const struct u32 i = FindObjectEventPaletteIndexByTag(graphicsInfo->paletteTag); if (i != 0xFF) UpdateSpritePalette(&sObjectEventSpritePalettes[i], sprite); + + #if LARGE_OW_SUPPORT && !OW_GFX_COMPRESS + // If gfx size changes, we need to reallocate tiles + if (graphicsInfo->oam->size != sprite->oam.size) + ReallocSpriteTiles(sprite, graphicsInfo->images->size); + #endif + + #if OW_GFX_COMPRESS + LoadSheetGraphicsInfo(graphicsInfo, objectEvent->graphicsId, sprite); + #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; - if (OW_GFX_COMPRESS) - LoadSheetGraphicsInfo(graphicsInfo, objectEvent->graphicsId, sprite); objectEvent->inanimate = graphicsInfo->inanimate; SetSpritePosToMapCoords(objectEvent->currentCoords.x, objectEvent->currentCoords.y, &sprite->x, &sprite->y); sprite->centerToCornerVecX = -(graphicsInfo->width >> 1); @@ -2559,9 +2685,7 @@ static void SetBerryTreeGraphics(struct ObjectEvent *objectEvent, u8 berryId, u8 sprite->x += 8; sprite->y += 16 + sprite->centerToCornerVecY; if (objectEvent->trackedByCamera) - { CameraObjectReset1(); - } } static void get_berry_tree_graphics(struct ObjectEvent *objectEvent, struct Sprite *sprite) @@ -5045,7 +5169,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; @@ -5057,10 +5181,11 @@ 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 (GET_BASE_SPECIES_ID(OW_SPECIES(objectEvent)) == SPECIES_CASTFORM - && OW_SPECIES(objectEvent) != GetOverworldCastformSpecies()) + && OW_SPECIES(objectEvent) != (multi = GetOverworldCastformSpecies())) { sprite->data[7] = TRANSFORM_TYPE_WEATHER << 8; return TRUE; @@ -5139,7 +5264,11 @@ bool8 MovementType_FollowPlayer_Shadow(struct ObjectEvent *objectEvent, struct S { // Shadow player's position objectEvent->invisible = TRUE; - MoveObjectEventToMapCoords(objectEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); + MoveObjectEventToMapCoords( + objectEvent, + gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, + gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y + ); objectEvent->triggerGroundEffectsOnMove = FALSE; // Stop endless reflection spawning return FALSE; } @@ -5147,7 +5276,11 @@ bool8 MovementType_FollowPlayer_Shadow(struct ObjectEvent *objectEvent, struct S // This way the player cannot talk to the invisible follower before it appears if (objectEvent->invisible) { - MoveObjectEventToMapCoords(objectEvent, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y); + MoveObjectEventToMapCoords( + objectEvent, + gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, + gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y + ); objectEvent->triggerGroundEffectsOnMove = FALSE; // Stop endless reflection spawning } sprite->sTypeFuncId = 1; // Enter active state; if the player moves the follower will appear @@ -5171,7 +5304,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); } @@ -5188,19 +5320,14 @@ bool8 MovementType_FollowPlayer_Moving(struct ObjectEvent *objectEvent, struct S { #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 ((sprite->data[5] & 7) == 2) + if (OW_MON_BOBBING == TRUE && (sprite->data[5] & 7) == 2) sprite->y2 ^= -1; - #endif } return FALSE; } @@ -5219,11 +5346,8 @@ bool8 FollowablePlayerMovement_Idle(struct ObjectEvent *objectEvent, struct Spri { // finish movement action objectEvent->singleMovementActive = 0; - } - #if OW_MON_BOBBING == TRUE - else if ((sprite->data[3] & 7) == 2) + } else if (OW_MON_BOBBING == TRUE && (sprite->data[3] & 7) == 2) sprite->y2 ^= -1; - #endif UpdateFollowerTransformEffect(objectEvent, sprite); return FALSE; } @@ -5244,11 +5368,9 @@ 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 + 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); @@ -5266,9 +5388,8 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EXIT_POKEBALL); objectEvent->singleMovementActive = 1; sprite->sTypeFuncId = 2; - #if OW_MON_BOBBING == TRUE - sprite->y2 = 0; - #endif + if (OW_MON_BOBBING == TRUE) + sprite->y2 = 0; return TRUE; } else if (x == targetX && y == targetY) @@ -5282,7 +5403,9 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri 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 + 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)) { @@ -5291,7 +5414,6 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri objectEvent->movementActionId = GetWalkNormalMovementAction(direction); else objectEvent->movementActionId = GetWalkFastMovementAction(direction); - } else if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) { @@ -5300,28 +5422,31 @@ bool8 FollowablePlayerMovement_Step(struct ObjectEvent *objectEvent, struct Spri else { if (playerAction >= MOVEMENT_ACTION_WALK_SLOW_DOWN && playerAction <= MOVEMENT_ACTION_WALK_SLOW_RIGHT) + { ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction)); - else { + } + else + { objectEvent->movementActionId = GetWalkNormalMovementAction(direction); - #if OW_MON_BOBBING == TRUE - sprite->y2 = -1; - #endif + if (OW_MON_BOBBING == TRUE) + sprite->y2 = -1; } } sprite->sActionFuncId = 0; #else - if (GetLedgeJumpDirection(x, y, direction) != DIR_NONE) // InitJumpRegular will set the proper speed + 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 + } else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) { + // Set follow speed according to player's speed ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkFastMovementAction(direction)); - // If *player* jumps, make step take twice as long - else if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) + } else if (PlayerGetCopyableMovement() == COPY_MOVE_JUMP2) { + // If *player* jumps, make step take twice as long ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkSlowMovementAction(direction)); - else { + } else { ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkNormalMovementAction(direction)); - #if OW_MON_BOBBING == TRUE - sprite->y2 = -1; - #endif + if (OW_MON_BOBBING == TRUE) + sprite->y2 = -1; } #endif objectEvent->singleMovementActive = 1; @@ -5760,10 +5885,6 @@ bool8 ScrFunc_IsFollowerFieldMoveUser(struct ScriptContext *ctx) u16 followIndex = ((u32)follower - (u32)gPlayerParty) / sizeof(struct Pokemon); *var = userIndex == followIndex; } - else - { - return FALSE; - } return FALSE; } @@ -6896,26 +7017,51 @@ static u8 LoadFillColorPalette(u16 color, u16 paletteTag, struct Sprite *sprite) return UpdateSpritePalette(&dynamicPalette, sprite); } +static void ObjectEventSetPokeballGfx(struct ObjectEvent *objEvent) { + #if OW_MON_POKEBALLS + u32 ball = BALL_POKE; + if (objEvent->localId == OBJ_EVENT_ID_FOLLOWER) { + struct Pokemon *mon = GetFirstLiveMon(); + if (mon) + ball = ItemIdToBallId(GetMonData(mon, MON_DATA_POKEBALL)); + } + + if (ball != BALL_POKE && ball < POKEBALL_COUNT) { + const struct ObjectEventGraphicsInfo *info = &gPokeballGraphics[ball]; + if (info->tileTag == TAG_NONE) { + ObjectEventSetGraphics(objEvent, info); + return; + } + } + #endif + 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; u16 graphicsId = objectEvent->graphicsId; 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; - ObjectEventSetGraphicsId(objectEvent, OBJ_EVENT_GFX_ANIMATED_BALL); + // 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->graphicsId = graphicsId; objectEvent->inanimate = FALSE; return MovementAction_ExitPokeball_Step1(objectEvent, sprite); @@ -6967,9 +7113,9 @@ 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; @@ -6977,26 +7123,23 @@ bool8 MovementAction_ExitPokeball_Step1(struct ObjectEvent *objectEvent, struct 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, OW_SPECIES(objectEvent), OW_FORM(objectEvent), objectEvent->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)) - { + 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] >> 4); - #if LARGE_OW_SUPPORT - } - #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); @@ -7008,10 +7151,14 @@ 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); } @@ -7019,38 +7166,34 @@ bool8 MovementAction_EnterPokeball_Step0(struct ObjectEvent *objectEvent, struct bool8 MovementAction_EnterPokeball_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 graphicsId = objectEvent->graphicsId; - sprite->data[3]--; - if (sprite->data[3] == 0) + if (--sprite->sDuration == 0) { - sprite->data[2] = 2; + sprite->sActionFuncId = 2; return FALSE; } - else if (sprite->data[3] == 11) + 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; + sprite->affineAnims = sAffineAnims_PokeballFollower; + sprite->oam.affineMode = ST_OAM_AFFINE_NORMAL; + InitSpriteAffineAnim(sprite); + StartSpriteAffineAnim(sprite, sprite->sSpeedFlip); } - else if (sprite->data[3] == 7) + 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; - ObjectEventSetGraphicsId(objectEvent, OBJ_EVENT_GFX_ANIMATED_BALL); + ObjectEventSetPokeballGfx(objectEvent); objectEvent->graphicsId = graphicsId; objectEvent->inanimate = FALSE; } @@ -7061,12 +7204,15 @@ bool8 MovementAction_EnterPokeball_Step2(struct ObjectEvent *objectEvent, struct { FollowerSetGraphics(objectEvent, OW_SPECIES(objectEvent), OW_FORM(objectEvent), objectEvent->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); @@ -9073,19 +9219,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]; diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 60180d80bb..5015fc04d8 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -334,7 +334,7 @@ u32 FldEff_Shadow(void) objectEventId = GetObjectEventIdByLocalIdAndMap(gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]); graphicsInfo = GetObjectEventGraphicsInfo(gObjectEvents[objectEventId].graphicsId); if (graphicsInfo->shadowSize == SHADOW_SIZE_NONE) // don't create a shadow at all - return 0; + return 0; spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[sShadowEffectTemplateIds[graphicsInfo->shadowSize]], 0, 0, 0x94); if (spriteId != MAX_SPRITES) { diff --git a/src/follower_helper.c b/src/follower_helper.c index 2dbf51dadf..b2f44e7d1a 100644 --- a/src/follower_helper.c +++ b/src/follower_helper.c @@ -73,225 +73,222 @@ const struct FollowerMsgInfoExtended gFollowerConditionalMessages[COND_MSG_COUNT .text = (u8*)sCelebiTexts, .textSpread = 1, .script = EventScript_FollowerDance, - .st = {.species = SPECIES_CELEBI}, - .stFlags = ST_FLAGS_SPECIES, .emotion = FOLLOWER_EMOTION_NEUTRAL, + .conditions = {MATCH_SPECIES(SPECIES_CELEBI)}, }, [COND_MSG_FIRE] = { .text = (u8*)sFireTexts, - .st = {.types = {.type1 = TYPE_FIRE, .type2 = TYPE_FIRE}}, - .stFlags = ST_FLAGS_TYPE, - .emotion = FOLLOWER_EMOTION_NEUTRAL, .textSpread = 1, + .emotion = FOLLOWER_EMOTION_NEUTRAL, + .conditions = {MATCH_TYPES(TYPE_FIRE, TYPE_FIRE)}, }, [COND_MSG_EVER_GRANDE] = { .text = sCondMsg06, .script = EventScript_FollowerFaceUp, - .mm = {.map = {.mapNum = MAP_NUM(EVER_GRANDE_CITY), .mapGroup = MAP_GROUP(EVER_GRANDE_CITY)}}, - .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_HAPPY, + .conditions = {MATCH_MAP(EVER_GRANDE_CITY)}, }, [COND_MSG_ROUTE_112] = { .text = sCondMsg07, - .mm = {.map = {.mapNum = MAP_NUM(ROUTE112), .mapGroup = MAP_GROUP(ROUTE112)}}, - .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_HAPPY, + .conditions = {MATCH_MAP(ROUTE112)}, }, [COND_MSG_DAY_CARE] = { .text = sCondMsg08, .script = EventScript_FollowerNostalgia, - .mm = {.map = {.mapNum = MAP_NUM(ROUTE117_POKEMON_DAY_CARE), .mapGroup = MAP_GROUP(ROUTE117_POKEMON_DAY_CARE)}}, - .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_NEUTRAL, + .conditions = {MATCH_MAP(ROUTE117_POKEMON_DAY_CARE)}, }, [COND_MSG_MART] = { .text = (u8*)sShopTexts, .textSpread = 1, .script = EventScript_FollowerLookAround, - .wt = {.song = MUS_POKE_MART}, - .wtFlags = WT_FLAGS_MUSIC, .emotion = FOLLOWER_EMOTION_NEUTRAL, + .conditions = {MATCH_MUSIC(MUS_POKE_MART)}, }, [COND_MSG_VICTORY_ROAD] = { .text = sCondMsg11, - .wt = {.song = MUS_VICTORY_ROAD}, - .wtFlags = WT_FLAGS_MUSIC, .emotion = FOLLOWER_EMOTION_PENSIVE, + .conditions = {MATCH_MUSIC(MUS_VICTORY_ROAD)}, }, [COND_MSG_BIKE_SHOP] = { .text = sCondMsg12, - .mm = {.map = {.mapNum = MAP_NUM(MAUVILLE_CITY_BIKE_SHOP), .mapGroup = MAP_GROUP(MAUVILLE_CITY_BIKE_SHOP)}}, - .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_PENSIVE, + .conditions = {MATCH_MAP(MAUVILLE_CITY_BIKE_SHOP)}, }, [COND_MSG_MACHINES] = { .text = (u8*)sMachineTexts, - .mm = {.map = {.mapNum = MAP_NUM(NEW_MAUVILLE_INSIDE), .mapGroup = MAP_GROUP(NEW_MAUVILLE_INSIDE)}}, - .mmFlags = MM_FLAGS_MAP, - .emotion = FOLLOWER_EMOTION_MUSIC, .textSpread = 1, + .emotion = FOLLOWER_EMOTION_MUSIC, + .orFlag = 1, // match any of these maps + .conditions = { + MATCH_MAP(NEW_MAUVILLE_INSIDE), + MATCH_MAP(SLATEPORT_CITY_STERNS_SHIPYARD_1F), + MATCH_MAP(SLATEPORT_CITY_STERNS_SHIPYARD_2F), + } }, [COND_MSG_SAILING] = { .text = (u8*)sBoatTexts, - .script = EventScript_FollowerLookAround, - .wt = {.song = MUS_SAILING}, - .wtFlags = WT_FLAGS_MUSIC, - .emotion = FOLLOWER_EMOTION_MUSIC, .textSpread = 1, + .emotion = FOLLOWER_EMOTION_MUSIC, + .script = EventScript_FollowerLookAround, + .conditions = {MATCH_MUSIC(MUS_SAILING)}, }, [COND_MSG_PUDDLE] = { .text = sCondMsg18, .script = EventScript_FollowerHopping, - .mm = {.mb = {.behavior1 = MB_SHALLOW_WATER, .behavior2 = MB_PUDDLE}}, - .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = {MATCH_ON_MB(MB_SHALLOW_WATER, MB_PUDDLE)}, }, [COND_MSG_SAND] = { .text = sCondMsg19, - .mm = {.mb = {.behavior1 = MB_SAND, .behavior2 = MB_DEEP_SAND}}, - .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = {MATCH_ON_MB(MB_SAND, MB_DEEP_SAND)}, }, [COND_MSG_GRASS] = { .text = sCondMsg20, - .mm = {.mb = {.behavior1 = MB_TALL_GRASS, .behavior2 = MB_LONG_GRASS}}, - .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = {MATCH_ON_MB(MB_TALL_GRASS, MB_LONG_GRASS)}, }, [COND_MSG_FOOTPRINTS] = { .text = sCondMsg21, - .mm = {.mb = {.behavior1 = MB_SAND, .behavior2 = MB_FOOTPRINTS}}, - .mmFlags = MM_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = {MATCH_ON_MB(MB_SAND, MB_FOOTPRINTS)}, }, [COND_MSG_ELEVATOR] = { .text = (u8*)sElevatorTexts, .textSpread = 1, - .mm = {.map = {.mapNum = MAP_NUM(LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR), .mapGroup = MAP_GROUP(LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR)}}, - .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_SURPRISE, + .conditions = {MATCH_MAP(LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR)}, }, [COND_MSG_ICE_ROOM] = { .text = (u8*)sColdTexts, .textSpread = 1, - .mm = {.map = {.mapNum = MAP_NUM(SHOAL_CAVE_LOW_TIDE_ICE_ROOM), .mapGroup = MAP_GROUP(SHOAL_CAVE_LOW_TIDE_ICE_ROOM)}}, - .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_SURPRISE, + .conditions = {MATCH_MAP(SHOAL_CAVE_LOW_TIDE_ICE_ROOM)}, }, [COND_MSG_ROUTE_117] = { .text = sCondMsg27, - .mm = {.map = {.mapNum = MAP_NUM(ROUTE117), .mapGroup = MAP_GROUP(ROUTE117)}}, - .mmFlags = MM_FLAGS_MAP, .emotion = FOLLOWER_EMOTION_SURPRISE, + .conditions = {MATCH_MAP(ROUTE117)}, }, [COND_MSG_DRAGON_GROWL] = { .text = sCondMsg28, - .st = {.types = {.type1 = TYPE_DRAGON, .type2 = TYPE_DRAGON}}, - .stFlags = ST_FLAGS_TYPE, - .mm = {.mapSec = {.mapSec = MAPSEC_SKY_PILLAR}}, - .mmFlags = MM_FLAGS_MAPSEC, .emotion = FOLLOWER_EMOTION_UPSET, + .conditions = { + MATCH_TYPES(TYPE_DRAGON, TYPE_DRAGON), + MATCH_MAPSEC(MAPSEC_SKY_PILLAR), + } }, [COND_MSG_FEAR] = { .text = (u8*)sFearTexts, .textSpread = 1, - .st = {.types = {.type1 = TYPE_GHOST, .type2 = TYPE_NOT_TYPE1}}, - .stFlags = ST_FLAGS_TYPE, - .mm = {.mapSec = {.mapSec = MAPSEC_MT_PYRE}}, - .mmFlags = MM_FLAGS_MAPSEC, - .wt = {.song = MUS_MT_PYRE}, - .wtFlags = WT_FLAGS_MUSIC, .emotion = FOLLOWER_EMOTION_UPSET, + .conditions = { + MATCH_NOT_TYPES(TYPE_GHOST, TYPE_GHOST), + MATCH_MAPSEC(MAPSEC_MT_PYRE), + MATCH_MUSIC(MUS_MT_PYRE), + } }, [COND_MSG_FIRE_RAIN] = { .text = sCondMsg31, - .st = {.types = {.type1 = TYPE_FIRE, .type2 = TYPE_FIRE}}, - .stFlags = ST_FLAGS_TYPE, - .wt = {.weather = {.weather1 = WEATHER_RAIN, .weather2 = WEATHER_RAIN_THUNDERSTORM}}, - .wtFlags = WT_FLAGS_WEATHER, .emotion = FOLLOWER_EMOTION_UPSET, + .conditions = { + MATCH_TYPES(TYPE_FIRE, TYPE_FIRE), + MATCH_WEATHER(WEATHER_RAIN, WEATHER_RAIN_THUNDERSTORM), + } }, [COND_MSG_FROZEN] = { .text = sCondMsg32, - .st = {.status = STATUS1_FREEZE}, - .stFlags = ST_FLAGS_STATUS, .emotion = FOLLOWER_EMOTION_UPSET, + .conditions = { + MATCH_STATUS(STATUS1_FREEZE), + } }, [COND_MSG_SEASIDE] = { .text = (u8*)sSeaTexts, .textSpread = 1, .script = EventScript_FollowerFaceResult, - .near = {.mb = {.behavior = MB_OCEAN_WATER, .distance = 5}}, - .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = {MATCH_NEAR_MB(MB_OCEAN_WATER, 5)}, }, [COND_MSG_WATERFALL] = { .text = sCondMsg36, .script = EventScript_FollowerFaceResult, - .near = {.mb = {.behavior = MB_WATERFALL, .distance = 5}}, - .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = {MATCH_NEAR_MB(MB_WATERFALL, 5)}, }, [COND_MSG_RAIN] = { .text = sCondMsg37, - .st = {.types = {.type1 = TYPE_FIRE, .type2 = TYPE_NOT_TYPE1}}, - .stFlags = ST_FLAGS_TYPE, - .wt = {.weather = {.weather1 = WEATHER_RAIN, .weather2 = WEATHER_RAIN_THUNDERSTORM}}, - .wtFlags = WT_FLAGS_WEATHER, .emotion = FOLLOWER_EMOTION_MUSIC, + .conditions = { + MATCH_NOT_TYPES(TYPE_FIRE, TYPE_FIRE), + MATCH_WEATHER(WEATHER_RAIN, WEATHER_RAIN_THUNDERSTORM) + } }, [COND_MSG_REFLECTION] = { .text = sCondMsg38, .script = EventScript_FollowerFaceResult, - .near = {.mb = {.behavior = MB_POND_WATER, .distance = 1}}, - .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_PENSIVE, + .conditions = {MATCH_NEAR_MB(MB_POND_WATER, 1)}, }, [COND_MSG_LEAVES] = { .text = sCondMsg39, - .mm = {.mapSec = {.mapSec = MAPSEC_PETALBURG_WOODS}}, - .mmFlags = MM_FLAGS_MAPSEC, .emotion = FOLLOWER_EMOTION_PENSIVE, + .conditions = {MATCH_MAPSEC(MAPSEC_PETALBURG_WOODS)}, }, [COND_MSG_ICE] = { .text = (u8*)sIceTexts, .textSpread = 1, .script = EventScript_FollowerFaceResult, - .near = {.mb = {.behavior = MB_ICE, .distance = 1}}, - .nearFlags = NEAR_FLAGS_MB, .emotion = FOLLOWER_EMOTION_PENSIVE, + .conditions = {MATCH_NEAR_MB(MB_ICE, 1)}, }, [COND_MSG_BURN] = { .text = sCondMsg42, - .st = {.status = STATUS1_BURN}, - .stFlags = ST_FLAGS_STATUS, .emotion = FOLLOWER_EMOTION_SAD, + .conditions = {MATCH_STATUS(STATUS1_BURN)}, }, }; + +// 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 dbb1f0da0a..9d75627805 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -1016,9 +1016,9 @@ bool8 ScrCmd_applymovement(struct ScriptContext *ctx) } ScriptMovement_StartObjectMovementScript(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, movementScript); sMovingNpcId = localId; + // Force follower into pokeball if (localId != OBJ_EVENT_ID_FOLLOWER && !FlagGet(FLAG_SAFE_FOLLOWER_MOVEMENT)) { - // Force follower into pokeball objEvent = GetFollowerObject(); // return early if no follower or in shadowing state if (objEvent == NULL || gSprites[objEvent->spriteId].data[1] == 0) @@ -2181,7 +2181,7 @@ bool8 ScrCmd_playmoncry(struct ScriptContext *ctx) return FALSE; } -bool8 ScrFunc_playfirstmoncry(struct ScriptContext *ctx) +bool8 ScrFunc_playfirstmoncry(struct ScriptContext *ctx) { u16 species = GetMonData(GetFirstLiveMon(), MON_DATA_SPECIES); PlayCry_Script(species, 0); diff --git a/src/trainer_see.c b/src/trainer_see.c index e68e1bf6d9..8e95c7e5bd 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -161,101 +161,112 @@ 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[] = { +static const union AnimCmd sSpriteAnim_Emotes0[] = +{ ANIMCMD_FRAME(0*2, 30), ANIMCMD_FRAME(0*2+1, 25), ANIMCMD_FRAME(0*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes1[] = { +static const union AnimCmd sSpriteAnim_Emotes1[] = +{ ANIMCMD_FRAME(1*2, 30), ANIMCMD_FRAME(1*2+1, 25), ANIMCMD_FRAME(1*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes2[] = { +static const union AnimCmd sSpriteAnim_Emotes2[] = +{ ANIMCMD_FRAME(2*2, 30), ANIMCMD_FRAME(2*2+1, 25), ANIMCMD_FRAME(2*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes3[] = { +static const union AnimCmd sSpriteAnim_Emotes3[] = +{ ANIMCMD_FRAME(3*2, 30), ANIMCMD_FRAME(3*2+1, 25), ANIMCMD_FRAME(3*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes4[] = { +static const union AnimCmd sSpriteAnim_Emotes4[] = +{ ANIMCMD_FRAME(4*2, 30), ANIMCMD_FRAME(4*2+1, 25), ANIMCMD_FRAME(4*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes5[] = { +static const union AnimCmd sSpriteAnim_Emotes5[] = +{ ANIMCMD_FRAME(5*2, 30), ANIMCMD_FRAME(5*2+1, 25), ANIMCMD_FRAME(5*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes6[] = { +static const union AnimCmd sSpriteAnim_Emotes6[] = +{ ANIMCMD_FRAME(6*2, 30), ANIMCMD_FRAME(6*2+1, 25), ANIMCMD_FRAME(6*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes7[] = { +static const union AnimCmd sSpriteAnim_Emotes7[] = +{ ANIMCMD_FRAME(7*2, 30), ANIMCMD_FRAME(7*2+1, 25), ANIMCMD_FRAME(7*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes8[] = { +static const union AnimCmd sSpriteAnim_Emotes8[] = +{ ANIMCMD_FRAME(8*2, 30), ANIMCMD_FRAME(8*2+1, 25), ANIMCMD_FRAME(8*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes9[] = { +static const union AnimCmd sSpriteAnim_Emotes9[] = +{ ANIMCMD_FRAME(9*2, 30), ANIMCMD_FRAME(9*2+1, 25), ANIMCMD_FRAME(9*2, 30), ANIMCMD_END }; -static const union AnimCmd sSpriteAnim_Emotes10[] = { +static const union AnimCmd sSpriteAnim_Emotes10[] = +{ ANIMCMD_FRAME(10*2, 30), ANIMCMD_FRAME(10*2+1, 25), ANIMCMD_FRAME(10*2, 30), @@ -336,8 +347,9 @@ static const struct SpriteTemplate sSpriteTemplate_HeartIcon = .callback = SpriteCB_TrainerIcons }; -static const struct SpriteTemplate sSpriteTemplate_Emote = { - .tileTag = 0xffff, +static const struct SpriteTemplate sSpriteTemplate_Emote = +{ + .tileTag = TAG_NONE, .paletteTag = OBJ_EVENT_PAL_TAG_EMOTES, .oam = &sOamData_Icons, .anims = sSpriteAnimTable_Emotes,