diff --git a/data/scripts/follower.inc b/data/scripts/follower.inc index 4907eb0c5f..cf8c063c1b 100644 --- a/data/scripts/follower.inc +++ b/data/scripts/follower.inc @@ -55,6 +55,12 @@ callfunc ScrFunc_bufferlivemonspeciesname .byte \out .endm +.macro emote obj:req id:req +callfunc ScrFunc_emote +.byte \obj +.byte \id +.endm + EventScript_Follower:: lock faceplayer @@ -73,6 +79,7 @@ EventScript_Follower:: EventScript_FollowerFly:: callfunc ScrFunc_followerfly EventScript_FollowerEnd:: + waitfieldeffect FLDEFF_EMOTE release end @@ -147,17 +154,13 @@ EventScript_FollowerBurnPainful:: return @ Message address must be loaded into bank 0 -EventScript_FollowerGeneric:: @ same as Std_MsgboxDefault +EventScript_FollowerGeneric:: @ similar to Std_MsgboxDefault + waitfieldeffect FLDEFF_EMOTE message 0x0 waitmessage waitbuttonpress return -EventScript_FollowerLove:: - applymovement 0xFE ContestHall_Movement_Heart - waitmovement 0xFE - goto EventScript_FollowerGeneric - EnterPokeballMovement:: .byte 0x9F @ EnterPokeball step_end diff --git a/emotions.txt b/emotions.txt index b8e43e08d0..dad46cf113 100644 --- a/emotions.txt +++ b/emotions.txt @@ -1,14 +1,13 @@ Happy (Special): -{STR_VAR_1} greeted the two. (when standing in front of 2 trainers). -{STR_VAR_1} is very eager! ( gym ) must have type advantage. -{STR_VAR_1} is very composed and sure of itself (in a gym with a type advantage). -{STR_VAR_1} greeted Amphy! {STR_VAR_1} greeted your mom. Your pokemon is staring intently at the mountain peak. {STR_VAR_1} is staring straight at the pokemon league. {STR_VAR_1} is staring at the sea. {STR_VAR_1} greeted everyone! -Your pokemon seems happy about the great weather. +{STR_VAR_1} greeted the two. (when standing in front of 2 trainers). +{STR_VAR_1} is very eager! ( gym ) must have type advantage. +{STR_VAR_1} is very composed and sure of itself (in a gym with a type advantage). +{STR_VAR_1} greeted Amphy! Neutral (Special): (CELEBI) danced happily ( Pokemon Exclusive ). (CELEBI) danced beautiful ( Pokemon Exclusive ). @@ -187,5 +186,3 @@ Curious (Special): {STR_VAR_1} seems to be hearing a strange sound. (Team rocket hide out). {STR_VAR_1} is concerned about the other side of the fence, it seems. {STR_VAR_1} is looking at the machine in a strange manner. (Power plant). -Poisoned: -{STR_VAR_1} is shivering with the effects of being poisoned. diff --git a/follower_emotions.py b/follower_emotions.py index 1d0fc10e50..6b8393019b 100644 --- a/follower_emotions.py +++ b/follower_emotions.py @@ -47,4 +47,4 @@ def export_messages(infile, outfile, n=None, indent=2): if __name__ == '__main__': - export_messages('emotions.txt', 'emotions.h', n=7) + export_messages('emotions.txt', 'emotions.h', n=1) diff --git a/graphics/misc/emotes.pal b/graphics/misc/emotes.pal new file mode 100644 index 0000000000..295fc02237 --- /dev/null +++ b/graphics/misc/emotes.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +255 255 0 +151 48 0 +74 75 87 +88 88 97 +164 72 167 +248 63 2 +78 128 207 +48 160 0 +237 131 0 +205 144 203 +168 168 178 +251 167 159 +240 176 183 +219 181 221 +165 225 69 +247 249 246 diff --git a/graphics/misc/emotes.png b/graphics/misc/emotes.png new file mode 100644 index 0000000000..2460eaf999 Binary files /dev/null and b/graphics/misc/emotes.png differ diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index dc1085f7ca..c16a25c21f 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -35,6 +35,7 @@ #define FLDEFF_USE_FLY 31 #define FLDEFF_FLY_IN 32 #define FLDEFF_QUESTION_MARK_ICON 33 +#define FLDEFF_EMOTE 33 // shares a slot with FLDEFF_QUESTION_MARK_ICON #define FLDEFF_FEET_IN_FLOWING_WATER 34 #define FLDEFF_BIKE_TIRE_TRACKS 35 #define FLDEFF_SAND_DISGUISE 36 diff --git a/include/data.h b/include/data.h index a706986cef..1c258dce17 100644 --- a/include/data.h +++ b/include/data.h @@ -13,6 +13,7 @@ #define N_FOLLOWER_LOVE_MESSAGES 10 #define N_FOLLOWER_SURPRISE_MESSAGES 20 #define N_FOLLOWER_CURIOUS_MESSAGES 7 +#define N_FOLLOWER_POISONED_MESSAGES 1 struct MonCoords { @@ -141,5 +142,6 @@ extern const char * const gFollowerPensiveMessages[]; extern const char * const gFollowerLoveMessages[]; extern const char * const gFollowerSurpriseMessages[]; extern const char * const gFollowerCuriousMessages[]; +extern const char * const gFollowerPoisonedMessages[]; #endif // GUARD_DATA_H diff --git a/spritesheet_rules.mk b/spritesheet_rules.mk index dfda9e6361..3b279745be 100644 --- a/spritesheet_rules.mk +++ b/spritesheet_rules.mk @@ -1,5 +1,6 @@ OBJEVENTGFXDIR := graphics/object_events/pics FLDEFFGFXDIR := graphics/field_effects/pics +MISCGFXDIR := graphics/misc $(OBJEVENTGFXDIR)/people/brendan/walking.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 2 -mheight 4 @@ -1913,3 +1914,6 @@ $(OBJEVENTGFXDIR)/pokemon/jirachi.4bpp: %.4bpp: %.png $(OBJEVENTGFXDIR)/pokemon/deoxys.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 4 -mheight 4 + +$(MISCGFXDIR)/emotes.4bpp: %.4bpp: %.png + $(GFX) $< $@ -mwidth 2 -mheight 2 diff --git a/src/data/object_events/object_event_graphics.h b/src/data/object_events/object_event_graphics.h index 66e6dd9258..51ad7d3937 100755 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -780,3 +780,4 @@ const u16 gObjectEventPal_HoOh[] = INCBIN_U16("graphics/object_events/palettes/h const u16 gObjectEventPal_Lugia[] = INCBIN_U16("graphics/object_events/palettes/lugia.gbapal"); const u16 gObjectEventPaletteLight[] = INCBIN_U16("graphics/object_events/palettes/light.gbapal"); +const u16 gObjectEventPaletteEmotes[] = INCBIN_U16("graphics/misc/emotes.gbapal"); diff --git a/src/data/text/follower_messages.h b/src/data/text/follower_messages.h index 517e486626..ea3c140437 100644 --- a/src/data/text/follower_messages.h +++ b/src/data/text/follower_messages.h @@ -31,6 +31,8 @@ const char * const gFollowerHappyMessages[] = { (const char []) _("{STR_VAR_1} seems very happy to see\nyou!"), (const char []) _("{STR_VAR_1} faced this way and\ngrinned."), (const char []) _("{STR_VAR_1} happily cuddled up to\nyou!"), + // Conditional messages begin here, index 31 + (const char []) _("Your pokemon seems happy about the\ngreat weather."), }; // Unconditional neutral messages @@ -147,3 +149,7 @@ const char * const gFollowerCuriousMessages[] = { (const char []) _("{STR_VAR_1} is sniffing at you."), (const char []) _("{STR_VAR_1} seems to be a little\nhesitant..."), }; + +const char * const gFollowerPoisonedMessages[] = { + (const char []) _("{STR_VAR_1} is shivering with the\neffects of being poisoned."), +}; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index a054279c8b..b27f988d56 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -450,8 +450,9 @@ const u8 gInitialMovementTypeFacingDirections[] = { #define OBJ_EVENT_PAL_TAG_LUGIA 0x1121 #define OBJ_EVENT_PAL_TAG_RS_BRENDAN 0x1122 #define OBJ_EVENT_PAL_TAG_RS_MAY 0x1123 -#define OBJ_EVENT_PAL_TAG_DYNAMIC 0x1124 -#define OBJ_EVENT_PAL_TAG_LIGHT 0x8001 +#define OBJ_EVENT_PAL_TAG_DYNAMIC 0x1124 +#define OBJ_EVENT_PAL_TAG_EMOTES 0x1125 +#define OBJ_EVENT_PAL_TAG_LIGHT 0x8001 #define OBJ_EVENT_PAL_TAG_NONE 0x11FF #include "data/object_events/object_event_graphics_info_pointers.h" @@ -500,6 +501,7 @@ static const struct SpritePalette sObjectEventSpritePalettes[] = { {gObjectEventPal_RubySapphireMay, OBJ_EVENT_PAL_TAG_RS_MAY}, {gObjectEventPal_Npc1, OBJ_EVENT_PAL_TAG_DYNAMIC}, {gObjectEventPaletteLight, OBJ_EVENT_PAL_TAG_LIGHT}, + {gObjectEventPaletteEmotes, OBJ_EVENT_PAL_TAG_EMOTES}, {NULL, 0x0000}, }; @@ -1731,6 +1733,8 @@ enum { FOLLOWER_EMOTION_LOVE, FOLLOWER_EMOTION_SURPRISE, FOLLOWER_EMOTION_CURIOUS, + FOLLOWER_EMOTION_MUSIC, + FOLLOWER_EMOTION_POISONED, FOLLOWER_EMOTION_LENGTH, }; @@ -1742,21 +1746,45 @@ static const struct FollowerMessagePool followerBasicMessages[] = { [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_FollowerLove, N_FOLLOWER_LOVE_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_POISONED] = {gFollowerPoisonedMessages, EventScript_FollowerGeneric, N_FOLLOWER_POISONED_MESSAGES}, +}; + +// Display an emote above an object event +// Note that this is not a movement action +static void ObjectEventEmote(struct ObjectEvent *objEvent, u8 emotion) { + emotion %= FOLLOWER_EMOTION_LENGTH; + ObjectEventGetLocalIdAndMap(objEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]); + gFieldEffectArguments[7] = emotion; + FieldEffectStart(FLDEFF_EMOTE); +} + +// Script-accessible version of the above +bool8 ScrFunc_emote(struct ScriptContext *ctx) { + u8 localId = ScriptReadByte(ctx); + u8 emotion = ScriptReadByte(ctx) % FOLLOWER_EMOTION_LENGTH; + u8 i = GetObjectEventIdByLocalId(localId); + if (i < OBJECT_EVENTS_COUNT) + ObjectEventEmote(&gObjectEvents[i], emotion); + return FALSE; +} + +struct SpecialEmote { // Used for storing conditional emotes + const u8 * script; + u16 index; + u8 emotion; }; // Call an applicable follower message script bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big switch for follower messages { - u16 value; u16 species; u32 behavior; s16 health_percent; - u8 map_region; u8 friendship; - const u8 *message_choices[10] = {0}; + struct SpecialEmote cond_emotes[16] = {0}; u8 n_choices = 0; struct ObjectEvent *objEvent = GetFollowerObject(); struct Pokemon *mon = GetFirstLiveMon(); @@ -1771,44 +1799,22 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big ScriptJump(ctx, EventScript_FollowerEnd); behavior = MapGridGetMetatileBehaviorAt(objEvent->currentCoords.x, objEvent->currentCoords.y); species = GetMonData(mon, MON_DATA_SPECIES); - // 1. Puddle splash or wet feet - if (MetatileBehavior_IsPuddle(behavior) || MetatileBehavior_IsShallowFlowingWater(behavior)) { - if (SpeciesHasType(species, TYPE_FIRE)) - message_choices[n_choices++] = EventScript_FollowerUnhappyToBeWet; - else if (SpeciesToGraphicsInfo(species, 0)->tracks) // if follower is grounded - message_choices[n_choices++] = EventScript_FollowerSplashesAbout; - } - // 2. Weather-based messages - if (GetCurrentWeather() == WEATHER_RAIN || GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM) { - if (SpeciesHasType(species, TYPE_FIRE)) - message_choices[n_choices++] = EventScript_FollowerUnhappyFace; - else if (SpeciesHasType(species, TYPE_WATER) || SpeciesHasType(species, TYPE_GRASS)) - message_choices[n_choices++] = EventScript_FollowerHappyRain; - } - // 3. Health & status-based messages - health_percent = mon->hp * 100 / mon->maxHP; - if (health_percent <= 20) - message_choices[n_choices++] = EventScript_FollowerAboutToFall; - else if (health_percent < 50 || mon->status & 0x40) // STATUS1_PARALYSIS - message_choices[n_choices++] = EventScript_FollowerTryingToKeepUp; - // 4. More status messages - if (mon->status & (0x20 | 0x8)) // STATUS1_FREEZE | STATUS1_POISON - message_choices[n_choices++] = EventScript_FollowerIsShivering; - else if (mon->status & 0x10) // STATUS1_BURN - message_choices[n_choices++] = EventScript_FollowerBurnPainful; - // TODO: What influences a follower's emotion ? - // Happy, neutral, sad, upset, angry, daydream, love, surprise, quizzical - // Friendship: boost to 'happy' and 'love' - // Weather: Based on type - // Status/low health: boost to unhappy, poisoned - // Happy weights - // TODO: Add sprites from https://www.spriters-resource.com/ds_dsi/pokemonheartgoldsoulsilver/sheet/30497/ ? friendship = GetMonData(mon, MON_DATA_FRIENDSHIP); + // // 1. Puddle splash or wet feet + // if (MetatileBehavior_IsPuddle(behavior) || MetatileBehavior_IsShallowFlowingWater(behavior)) { + // if (SpeciesHasType(species, TYPE_FIRE)) + // message_choices[n_choices++] = EventScript_FollowerUnhappyToBeWet; + // else if (SpeciesToGraphicsInfo(species, 0)->tracks) // if follower is grounded + // message_choices[n_choices++] = EventScript_FollowerSplashesAbout; + // } + // Happy weights emotion_weight[FOLLOWER_EMOTION_HAPPY] = 10; if (friendship > 170) emotion_weight[FOLLOWER_EMOTION_HAPPY] = 30; else if (friendship > 80) emotion_weight[FOLLOWER_EMOTION_HAPPY] = 20; + if (GetCurrentWeather() == WEATHER_SUNNY || GetCurrentWeather() == WEATHER_SUNNY_CLOUDS) + cond_emotes[n_choices++] = (struct SpecialEmote) {.emotion=FOLLOWER_EMOTION_HAPPY, .index=31}; // Neutral weights emotion_weight[FOLLOWER_EMOTION_NEUTRAL] = 15; // Sad weights @@ -1834,6 +1840,28 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big // TODO: Increase this if there is an item nearby, or if the pokemon has pickup emotion_weight[FOLLOWER_EMOTION_CURIOUS] = 5; emotion = RandomWeightedIndex(emotion_weight, FOLLOWER_EMOTION_LENGTH); + if (mon->status & 0x8) // STATUS1_POISON + emotion = FOLLOWER_EMOTION_POISONED; + ObjectEventEmote(objEvent, emotion); + if (Random() & 1) { // With 50% chance, select special message using reservoir sampling + u8 i, j = 1; + struct SpecialEmote *choice = 0; + for (i = 0; i < n_choices; i++) { + if (cond_emotes[i].emotion == emotion) { + if (Random() < 0x10000 / (j++)) // Replace item with 1/j chance + choice = &cond_emotes[i]; + } + } + if (choice) { // Only continue if a script was actually chosen + if (choice->script) + ScriptCall(ctx, choice->script); + else { + ctx->data[0] = (u32) followerBasicMessages[emotion].messages[choice->index]; + ScriptCall(ctx, followerBasicMessages[emotion].script); + } + return FALSE; + } + } ctx->data[0] = (u32) followerBasicMessages[emotion].messages[Random() % followerBasicMessages[emotion].length]; ScriptCall(ctx, followerBasicMessages[emotion].script); return FALSE; @@ -2119,7 +2147,7 @@ static u8 UpdateSpritePalette(const struct SpritePalette * spritePalette, struct } // Find and update based on template's paletteTag -// TODO: Should this logic happen in CreateSpriteAt? +// TODO: Add a better way to associate tags -> palettes besides listing them in sObjectEventSpritePalettes u8 UpdateSpritePaletteByTemplate(const struct SpriteTemplate * template, struct Sprite * sprite) { u8 i = FindObjectEventPaletteIndexByTag(template->paletteTag); if (i == 0xFF) @@ -7260,6 +7288,7 @@ bool8 MovementAction_EmoteExclamationMark_Step0(struct ObjectEvent *objectEvent, bool8 MovementAction_EmoteQuestionMark_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ObjectEventGetLocalIdAndMap(objectEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]); + gFieldEffectArguments[7] = -1; FieldEffectStart(FLDEFF_QUESTION_MARK_ICON); sprite->data[2] = 1; return TRUE; diff --git a/src/trainer_see.c b/src/trainer_see.c index 082b92f2cc..ec4c8e461e 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -62,6 +62,9 @@ EWRAM_DATA u8 gApproachingTrainerId = 0; static const u8 sEmotion_ExclamationMarkGfx[] = INCBIN_U8("graphics/misc/emotion_exclamation.4bpp"); static const u8 sEmotion_QuestionMarkGfx[] = INCBIN_U8("graphics/misc/emotion_question.4bpp"); static const u8 sEmotion_HeartGfx[] = INCBIN_U8("graphics/misc/emotion_heart.4bpp"); +// TODO: Credit https://www.spriters-resource.com/ds_dsi/pokemonheartgoldsoulsilver/sheet/30497/ +static const u8 sEmotion_Gfx[] = INCBIN_U8("graphics/misc/emotes.4bpp"); + static u8 (*const sDirectionalApproachDistanceFuncs[])(struct ObjectEvent *trainerObj, s16 range, s16 x, s16 y) = { @@ -147,6 +150,109 @@ 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 +}; + +static const union AnimCmd sSpriteAnim_Emotes0[] = { + ANIMCMD_FRAME(0*2, 30), + ANIMCMD_FRAME(0*2+1, 30), + ANIMCMD_FRAME(0*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes1[] = { + ANIMCMD_FRAME(1*2, 30), + ANIMCMD_FRAME(1*2+1, 30), + ANIMCMD_FRAME(1*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes2[] = { + ANIMCMD_FRAME(2*2, 30), + ANIMCMD_FRAME(2*2+1, 30), + ANIMCMD_FRAME(2*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes3[] = { + ANIMCMD_FRAME(3*2, 30), + ANIMCMD_FRAME(3*2+1, 30), + ANIMCMD_FRAME(3*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes4[] = { + ANIMCMD_FRAME(4*2, 30), + ANIMCMD_FRAME(4*2+1, 30), + ANIMCMD_FRAME(4*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes5[] = { + ANIMCMD_FRAME(5*2, 30), + ANIMCMD_FRAME(5*2+1, 30), + ANIMCMD_FRAME(5*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes6[] = { + ANIMCMD_FRAME(6*2, 30), + ANIMCMD_FRAME(6*2+1, 30), + ANIMCMD_FRAME(6*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes7[] = { + ANIMCMD_FRAME(7*2, 30), + ANIMCMD_FRAME(7*2+1, 30), + ANIMCMD_FRAME(7*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes8[] = { + ANIMCMD_FRAME(8*2, 30), + ANIMCMD_FRAME(8*2+1, 30), + ANIMCMD_FRAME(8*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes9[] = { + ANIMCMD_FRAME(9*2, 30), + ANIMCMD_FRAME(9*2+1, 30), + ANIMCMD_FRAME(9*2, 30), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_Emotes10[] = { + ANIMCMD_FRAME(10*2, 30), + ANIMCMD_FRAME(10*2+1, 30), + ANIMCMD_FRAME(10*2, 30), + ANIMCMD_END +}; + static const union AnimCmd sSpriteAnim_Icons1[] = { ANIMCMD_FRAME(0, 60), @@ -165,12 +271,28 @@ static const union AnimCmd *const sSpriteAnimTable_Icons[] = sSpriteAnim_Icons2 }; -#define OBJ_EVENT_PAL_TAG_17 0x1110 // TODO: Move this into event_object_movement.h +static const union AnimCmd *const sSpriteAnimTable_Emotes[] = { + sSpriteAnim_Emotes0, + sSpriteAnim_Emotes1, + sSpriteAnim_Emotes2, + sSpriteAnim_Emotes3, + sSpriteAnim_Emotes4, + sSpriteAnim_Emotes5, + sSpriteAnim_Emotes6, + sSpriteAnim_Emotes7, + sSpriteAnim_Emotes8, + sSpriteAnim_Emotes9, + sSpriteAnim_Emotes10, +}; + +// TODO: Move these declarations into even_object_movement.h +#define OBJ_EVENT_PAL_TAG_MAY 0x1110 +#define OBJ_EVENT_PAL_TAG_EMOTES 0x1125 static const struct SpriteTemplate sSpriteTemplate_ExclamationQuestionMark = { .tileTag = 0xffff, - .paletteTag = OBJ_EVENT_PAL_TAG_17, + .paletteTag = OBJ_EVENT_PAL_TAG_MAY, .oam = &sOamData_Icons, .anims = sSpriteAnimTable_Icons, .images = sSpriteImageTable_ExclamationQuestionMark, @@ -189,6 +311,16 @@ static const struct SpriteTemplate sSpriteTemplate_HeartIcon = .callback = SpriteCB_TrainerIcons }; +static const struct SpriteTemplate sSpriteTemplate_Emote = { + .tileTag = 0xffff, + .paletteTag = OBJ_EVENT_PAL_TAG_EMOTES, + .oam = &sOamData_Icons, + .anims = sSpriteAnimTable_Emotes, + .images = sSpriteImageTable_Emotes, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCB_TrainerIcons +}; + // code bool8 CheckForTrainersWantingBattle(void) { @@ -710,7 +842,17 @@ u8 FldEff_ExclamationMarkIcon(void) u8 FldEff_QuestionMarkIcon(void) { - u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x52); + u8 spriteId; + if (gFieldEffectArguments[7] >= 0) { // Use follower emotes + u8 emotion = gFieldEffectArguments[7]; + spriteId = CreateSpriteAtEnd(&sSpriteTemplate_Emote, 0, 0, 0x52); + if (spriteId == MAX_SPRITES) + return 0; + SetIconSpriteData(&gSprites[spriteId], FLDEFF_EMOTE, emotion); // Set animation based on emotion + UpdateSpritePaletteByTemplate(&sSpriteTemplate_Emote, &gSprites[spriteId]); + return 0; + } + spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x52); if (spriteId != MAX_SPRITES) { SetIconSpriteData(&gSprites[spriteId], FLDEFF_QUESTION_MARK_ICON, 1); @@ -723,7 +865,6 @@ u8 FldEff_QuestionMarkIcon(void) u8 FldEff_HeartIcon(void) { u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_HeartIcon, 0, 0, 0x52); - if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId];