From 875c4edd50399a507ea92d43cc2cbbee93c75a0b Mon Sep 17 00:00:00 2001 From: Ariel Antonitis Date: Sun, 18 Apr 2021 01:38:08 -0400 Subject: [PATCH] Added some conditional messages. Added emote graphics. --- data/scripts/follower.inc | 15 +- emotions.txt | 11 +- follower_emotions.py | 2 +- graphics/misc/emotes.pal | 19 +++ graphics/misc/emotes.png | Bin 0 -> 4058 bytes include/constants/field_effects.h | 1 + include/data.h | 2 + spritesheet_rules.mk | 4 + .../object_events/object_event_graphics.h | 1 + src/data/text/follower_messages.h | 6 + src/event_object_movement.c | 107 ++++++++----- src/trainer_see.c | 149 +++++++++++++++++- 12 files changed, 260 insertions(+), 57 deletions(-) create mode 100644 graphics/misc/emotes.pal create mode 100644 graphics/misc/emotes.png 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 0000000000000000000000000000000000000000..2460eaf999a8c1b67178882dce8d51853c6104a8 GIT binary patch literal 4058 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.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];