diff --git a/data/scripts/follower.inc b/data/scripts/follower.inc index c3e32fe3f6..4907eb0c5f 100644 --- a/data/scripts/follower.inc +++ b/data/scripts/follower.inc @@ -1,5 +1,5 @@ gText_FollowerLovesYou:: - .string "{STR_VAR_1} is regarding you with\nadoration!$" + .string "123456789012345678901234567890123\n$" gText_FollowerLostInThought:: .string "{STR_VAR_1} seems lost in thought.$" @@ -146,6 +146,18 @@ EventScript_FollowerBurnPainful:: waitmoncry return +@ Message address must be loaded into bank 0 +EventScript_FollowerGeneric:: @ same as Std_MsgboxDefault + 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 new file mode 100644 index 0000000000..b8e43e08d0 --- /dev/null +++ b/emotions.txt @@ -0,0 +1,191 @@ +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. +Neutral (Special): +(CELEBI) danced happily ( Pokemon Exclusive ). +(CELEBI) danced beautiful ( Pokemon Exclusive ). +Sniff sniff, something smells good! ( Day care center ). +{STR_VAR_1} seems dazzled after seeing the sky. +Your pokemon is surveying the shelves restlessly ( In pokemart ). +Your pokemon is smelling the scent of the flowers. (When next to flowers in Cherrygrove City). +Your pokemon is staring intently at the sea! (On the beach). +Why is it doing warm-up exercises? (Cerulean Gym). +{STR_VAR_1} is running along the side of the pool! (Cerulean Gym). +{STR_VAR_1} is vigorously producing fire (must be a fire type). +{STR_VAR_1} spat fire (must be a fire type). +{STR_VAR_1} is staring intently at the shelves. +(TYPHLOSION) emitted fire and shouted ( Pokemon Exclusive ). +{STR_VAR_1} is vigorously breathing fire ( must be fire type). +(MAGIKARP) is leaping around more than usual (lake of rage, During team rockets broadcast) ( Pokemon Exclusive ). +{STR_VAR_1} Greeted Slowbro (Cerulean city, in front of Slowbro). +{STR_VAR_1} seems happy to have taken a shower! (Celadon gym) (Grass,Water type). +{STR_VAR_1} is fidgeting in front of everyone (maybe baby pokemon). +{STR_VAR_1} roared! +Your pokemon is staring intently at the sea! +{STR_VAR_1} is looking at the surging sea. +{STR_VAR_1} is playing on the sand. +{STR_VAR_1} is staring fixedly at the sea. +{STR_VAR_1} Cried out, looking at the tall mountain! +{STR_VAR_1} is stomping on the grass! +{STR_VAR_1} seems highly interested in the tree. +{STR_VAR_1} is playing around, Picking bits of grass. +{STR_VAR_1} is looking outside and frolicking! +{STR_VAR_1} spun around in a circle! +Sad (Special): +{STR_VAR_1} is going to fall down! (when HP is red). +{STR_VAR_1} is not happy. (During rain if the pokemon is a fire type). +{STR_VAR_1} seems to be about to fall over! (When HP is red.). +{STR_VAR_1} is trying very hard to keep up with you... (When HP is yellow or paralyzed). +{STR_VAR_1}'s burn looks painful! (when burnt). +Upset (Special): +{STR_VAR_1} is taking shelter in the grass from the rain. +{STR_VAR_1} is splashing around in the wet grass. +It seems to have eaten something strange. It's making an odd face... (Olivine Cafe). +{STR_VAR_1} is staring at the crumbling floor (burnt tower). +{STR_VAR_1} seems to think that (Player) has disappeared! ( Morty's Gym ). +{STR_VAR_1} seems to feel a little claustrophobic. +{STR_VAR_1} is a bit nervous about the narrow space! +{STR_VAR_1} is splashing around in the grass (must be fire type). +Your pokemon doesn't like splashing around on the ground (must be fire type). +{STR_VAR_1} is taking shelter in the grass from the rain (must be fire type). +{STR_VAR_1} seems very cold (when frozen). +{STR_VAR_1} seems uneasy and is poking (PLAYER). +{STR_VAR_1} is trembling with fear (Cemetery). +{STR_VAR_1} seems somehow sad... (Cemetery). +{STR_VAR_1} is growling softly (Dragon type in Dragons Den). +Angry (Special): +{STR_VAR_1} is staring at the Persian statue and glaring. +{STR_VAR_1} is glaring at the PERSIAN statue! +{STR_VAR_1} is glaring at your foe! +{STR_VAR_1} is intimidating your foe! +Pensive (Special): +{STR_VAR_1} is staring intently at the goods on display! +{STR_VAR_1} is staring at its reflection in the water. +{STR_VAR_1} is staring intently at the reflection of its face. +{STR_VAR_1} is gnawing at the ice. +{STR_VAR_1} seems interested in Amphy... +{STR_VAR_1} has a sleepy look on its face... (Near Jigglypuff in Radio Tower.). +{STR_VAR_1} seems to relax as it hears the sound of rustling leaves... +{STR_VAR_1} seems to be listening to the sound of rustling leaves. +Your pokemon turned to face the other way, showing a defiant expression. +{STR_VAR_1} is preoccupied by the ceiling, which seems like it may collapse (burnt tower). +{STR_VAR_1} focused with a sharp gaze! (On the way to Victory Road.). +{STR_VAR_1} emitted fire and shouted! (must be a fire type). +{STR_VAR_1} is concerned about the swaying pillar (sprout tower). +Your pokemon is drooling a little (Olivine Cafe). +{STR_VAR_1} seems very interested in the bicycles (bike shop). +{STR_VAR_1} is preoccupied by the floor, which seems like it may collapse (burnt tower). +{STR_VAR_1} is nervous ( in a gym before beating gym leader ) ( must have type disadvantage ). +Music: +{STR_VAR_1} is showing off its agility! +{STR_VAR_1} is moving around happily! +Woah! {STR_VAR_1} suddenly started dancing in happiness! +{STR_VAR_1} is steadily keeping up with you! +{STR_VAR_1} is very happy about the rain. +{STR_VAR_1} seems to want to play with (Player). +{STR_VAR_1} is pulling out the grass. +{STR_VAR_1} is happy skipping about. +{STR_VAR_1} is playing in the puddle. +{STR_VAR_1} is gazing restlessly at the building ( in goldenrod). +{STR_VAR_1} is singing and humming. +{STR_VAR_1} is looking around restlessly at the Forest. +{STR_VAR_1} is playfully nibbling at the ground. +(CYNDAQUIL) blew out a fireball. ( Pokemon Exclusive ). +(CYNDAQUIL) blew out a couple of fireballs. ( Pokemon Exclusive ). +(CHIKORITA) is waving its leaf around ( Pokemon Exclusive ). +(CHIKORITA) is making its leaf twitch ( Pokemon Exclusive ). +(TODADILE) is moving its jaw ( Pokemon Exclusive ). +(TODADILE) is opening and closing its mouth ( Pokemon Exclusive ). +{STR_VAR_1} is nipping at your feet! +{STR_VAR_1} is sniffing around the room. (Inside Hero's Room). +{STR_VAR_1} is dancing around the pillar! (Seems to occur more for Bellsprout)(sprout tower). +{STR_VAR_1} swayed around, dancing in a strange manner.(Seems to occur more for Bellsprout)(sprout tower). +{STR_VAR_1} swayed and danced around as it pleased (Seems to occur more for Bellsprout)(sprout tower). +Your Pokemon is happily looking at (Player)'s footprints! (Sandy area). +Your Pokemon is playing around and splashing in the water! (Water's edge). +Your Pokemon is blowing sand in the air! (Sandy area). +{STR_VAR_1} is looking up at the sky. +{STR_VAR_1} is poking at garbage (Goldenrods underground path). +Your pokemon is rolling a screw from a bicycle around. +{STR_VAR_1} is looking up at the sky. +{STR_VAR_1} seems to be happy about the rain! ( during rain, must not have type disadvantage to water). +{STR_VAR_1} is clawing the grass! +{STR_VAR_1} turns around and looks at you. +{STR_VAR_1} is listening to the sound of the waterfall ( Mt. Mortar). +{STR_VAR_1} is looking up the tall mountain... (Mt. Mortar). +{STR_VAR_1} seems concerned about the waterfall. +{STR_VAR_1} is working hard to show off its mighty power! +{STR_VAR_1} pulled back to run! +Whoa! {STR_VAR_1} suddenly danced in happiness! +{STR_VAR_1} is rolling around in the grass. +{STR_VAR_1} seems to want to return to the lab (after you receive the Mystery egg). +Your pokemon is staring at the various items. +{STR_VAR_1} is wandering around enjoying the forest scenery. +{STR_VAR_1} is playing around in the fallen leaves. +{STR_VAR_1} is playing around with a leaf. +{STR_VAR_1} is playing around, touching the leaves. +Your pokemon is happily gazing at the beautiful, starry sky! +{STR_VAR_1} is pulling out the grass. +{STR_VAR_1} seems to be enjoying this a little bit! +{STR_VAR_1} is looking up at the ceiling. +{STR_VAR_1} is cheerful! +{STR_VAR_1} is swaying with the boat! +{STR_VAR_1} is dancing along with the rolling of the ship. +{STR_VAR_1} doesn't want to get off the boat yet! +{STR_VAR_1} is listening to the sound of the machine. +{STR_VAR_1} seems happy at the sight of water on the window! +Your pokemon is blowing sand in the air! +Your pokemon is staring spellbound at the night sky! +Your pokemon seems to be enjoying sliding around! +{STR_VAR_1} is touching the ice. +{STR_VAR_1} is steadily observing the flow of the river. +{STR_VAR_1} is noticing the scent of the grass. +{STR_VAR_1} is playing around, plucking bits of grass. +{STR_VAR_1} is happy to see what's out doors! +{STR_VAR_1} seems to want to touch the machine! (Power plant). +{STR_VAR_1} is listening intently to the sound of the waves. +{STR_VAR_1} is jumping around in a carefree way! +Waah! your pokemon suddenly splashed water! (on a shore). +Your pokemon seems to be smelling a nostalgically familiar scent... +Surprised (Special): +Your pokemon has a flower petal on its face! (Newbark Town). +{STR_VAR_1} seems to have gotten caught in the clumps of grass. +{STR_VAR_1} slipped on the floor and seems likely to fall! +Your pokemon is very concerned about the room below ( Burnt tower ). +Careful! seems like the floor could collapse, and it might fall. +{STR_VAR_1} seems to think that (Player) has disappeared! ( Morty's Gym ). +{STR_VAR_1} gazed surprisingly at the rock! +Huh? your pokemon found something in top of the mountain ( outside Mt. Mortar) (!). +{STR_VAR_1} is cautious about the confined area! +{STR_VAR_1} seems to be very surprised that it is raining! +{STR_VAR_1} got tangled up in the branches and almost fell down! +{STR_VAR_1} looked up at the sky and shouted loudly. +{STR_VAR_1} seems to have found something! +{STR_VAR_1} is feeling nervous and a touch claustrophobic. (while inside an elevator). +{STR_VAR_1} almost forgot it was holding that (Held item). +{STR_VAR_1} is happy at the sight of the water on the window! +{STR_VAR_1} was surprised by the sounds in the thicket! +Seems the breeze is coming from here. +Oh! its slipping and came over here for support (on ice). +Your pokemon almost slipped and fell over! (on ice). +A cold wind suddenly blew by! +Your pokemon seems surprised to touch ice. (TODO: Regice) +{STR_VAR_1} seems to have gotten a bit of zap! (Power plant). +{STR_VAR_1} is dazzled by the shiny brightness of the bridge! (Standing on Nugget Bridge). +{STR_VAR_1} is slipping on the water and almost fell over! +Curious (Special): +{STR_VAR_1} feels something... (burnt tower basement). +{STR_VAR_1} seems to be hearing a strange sound. (Rocket hideout in Mahogany). +{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 new file mode 100644 index 0000000000..1d0fc10e50 --- /dev/null +++ b/follower_emotions.py @@ -0,0 +1,50 @@ +""" Processes & outputs follower emotion messages """ +import sys +import re +import textwrap + +blank_regex = re.compile(r'\(?_+\)?') + + +# Converts a series of message lines to a better format +def convert_messages(infile, outfile='emotions.txt'): + with open(infile, 'r') as f_in, open(outfile, 'w') as f_out: + for line in f_in: + line = line.rstrip('\n') + if line and line[0] == '-': + line = line[1:] + line = line.lstrip() + if not line: + continue + line = blank_regex.sub('{STR_VAR_1}', line) + if line[-1] not in ('.', '?', '!', ':'): + line += '.' + print(line) + f_out.write('\n' + line) + +# Prepares a string for field-message display, performing line-wrapping, etc +# Does not add a terminator, as this is done by _("") +def prepare_string(s): + lines = textwrap.wrap(s, width=36) # Width of message window + s = lines[0] + for i, line in enumerate(lines[1:]): + ending = r'\p' if i % 2 else r'\n' + s += ending + line + return s + + +# Exports up to n messages in C format to outfile +def export_messages(infile, outfile, n=None, indent=2): + with open(infile, 'r') as f_in: + lines = f_in.readlines() + if n is not None: + lines = lines[:n] + with open(outfile, 'w') as f_out: + codelines = [' '*indent + f'(const char []) _("{prepare_string(s)}"),' for s in lines] + f_out.write('\n'.join(codelines)) + print(f'{len(lines)} lines written') + return len(lines) + + +if __name__ == '__main__': + export_messages('emotions.txt', 'emotions.h', n=7) diff --git a/include/data.h b/include/data.h index 2f8c447463..a706986cef 100644 --- a/include/data.h +++ b/include/data.h @@ -4,6 +4,15 @@ #include "constants/moves.h" #define SPECIES_SHINY_TAG 500 +#define N_FOLLOWER_HAPPY_MESSAGES 31 +#define N_FOLLOWER_NEUTRAL_MESSAGES 15 +#define N_FOLLOWER_SAD_MESSAGES 3 +#define N_FOLLOWER_UPSET_MESSAGES 3 +#define N_FOLLOWER_ANGRY_MESSAGES 5 +#define N_FOLLOWER_PENSIVE_MESSAGES 20 +#define N_FOLLOWER_LOVE_MESSAGES 10 +#define N_FOLLOWER_SURPRISE_MESSAGES 20 +#define N_FOLLOWER_CURIOUS_MESSAGES 7 struct MonCoords { @@ -69,6 +78,13 @@ struct Trainer #define TRAINER_ENCOUNTER_MUSIC(trainer)((gTrainers[trainer].encounterMusic_gender & 0x7F)) +struct FollowerMessagePool +{ + const char * const * messages; + const u8 * script; + u16 length; +}; + extern const u16 gMinigameDigits_Pal[]; extern const u32 gMinigameDigits_Gfx[]; @@ -115,4 +131,15 @@ extern const u8 gTrainerClassNames[][13]; extern const u8 gSpeciesNames[][POKEMON_NAME_LENGTH + 1]; extern const u8 gMoveNames[MOVES_COUNT][MOVE_NAME_LENGTH + 1]; +// Follower text messages +extern const char * const gFollowerHappyMessages[]; +extern const char * const gFollowerNeutralMessages[]; +extern const char * const gFollowerSadMessages[]; +extern const char * const gFollowerUpsetMessages[]; +extern const char * const gFollowerAngryMessages[]; +extern const char * const gFollowerPensiveMessages[]; +extern const char * const gFollowerLoveMessages[]; +extern const char * const gFollowerSurpriseMessages[]; +extern const char * const gFollowerCuriousMessages[]; + #endif // GUARD_DATA_H diff --git a/include/event_scripts.h b/include/event_scripts.h index 0f12c689bd..5adb830e65 100644 --- a/include/event_scripts.h +++ b/include/event_scripts.h @@ -16,6 +16,8 @@ extern const u8 EventScript_FollowerAboutToFall[]; extern const u8 EventScript_FollowerTryingToKeepUp[]; extern const u8 EventScript_FollowerIsShivering[]; extern const u8 EventScript_FollowerBurnPainful[]; +extern const u8 EventScript_FollowerGeneric[]; +extern const u8 EventScript_FollowerLove[]; extern const u8 EnterPokeballMovement[]; extern const u8 EventScript_TestSignpostMsg[]; diff --git a/src/data.c b/src/data.c index 6a4295440c..56add9c935 100644 --- a/src/data.c +++ b/src/data.c @@ -431,3 +431,4 @@ static const u32 sUnused[] = #include "data/trainers.h" #include "data/text/species_names.h" #include "data/text/move_names.h" +#include "data/text/follower_messages.h" diff --git a/src/data/text/follower_messages.h b/src/data/text/follower_messages.h new file mode 100644 index 0000000000..517e486626 --- /dev/null +++ b/src/data/text/follower_messages.h @@ -0,0 +1,149 @@ +// 'Generic', unconditional happy messages +const char * const gFollowerHappyMessages[] = { + (const char []) _("{STR_VAR_1} began poking you in the\nstomach."), + (const char []) _("{STR_VAR_1} is happy but shy."), + (const char []) _("{STR_VAR_1} is coming along happily."), + (const char []) _("{STR_VAR_1} is composed."), + (const char []) _("{STR_VAR_1} seems to be feeling\ngreat about walking with you!"), + (const char []) _("{STR_VAR_1} is glowing with health."), + (const char []) _("{STR_VAR_1} looks very happy."), + (const char []) _("{STR_VAR_1} put in extra effort."), + (const char []) _("{STR_VAR_1} is smelling the scents\nof the surrounding air."), + (const char []) _("{STR_VAR_1} is jumping for joy!"), + (const char []) _("{STR_VAR_1} is still feeling great!"), + (const char []) _("Your pokemon has caught the scent of\nsmoke."), + (const char []) _("{STR_VAR_1} is poking at your belly."), + (const char []) _("Your pokemon stretched out its body\nand is relaxing."), + (const char []) _("{STR_VAR_1} looks like it wants to\nlead!"), + (const char []) _("{STR_VAR_1} is doing it's best to\nkeep up with you."), + (const char []) _("{STR_VAR_1} is happily cuddling up\nto you!"), + (const char []) _("{STR_VAR_1} is full of life!"), + (const char []) _("{STR_VAR_1} seems to be very happy!"), + (const char []) _("{STR_VAR_1} is so happy that it\ncan't stand still!"), + (const char []) _("{STR_VAR_1} nodded slowly."), + (const char []) _("{STR_VAR_1} is very eager!"), + (const char []) _("{STR_VAR_1} is wandering around and\nlistening to the different sounds."), + (const char []) _("{STR_VAR_1} looks very interested."), + (const char []) _("{STR_VAR_1} is somehow forcing\nitself to keep going."), + (const char []) _("{STR_VAR_1} gave you a sunny look!"), + (const char []) _("{STR_VAR_1} gives you a happy look\nand a smile."), + (const char []) _("Your pokemon is smelling the scent\nof flowers."), + (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!"), +}; + +// Unconditional neutral messages +const char * const gFollowerNeutralMessages[] = { + (const char []) _("{STR_VAR_1} is steadily poking at\nthe ground."), + (const char []) _("{STR_VAR_1} is standing guard."), + (const char []) _("{STR_VAR_1} is staring patiently at\nnothing at all."), + (const char []) _("{STR_VAR_1} is wandering around."), + (const char []) _("Your pokemon yawned loudly!"), + (const char []) _("Your pokemon is looking around\nrestlessly."), + (const char []) _("{STR_VAR_1} is steadily poking at\nthe ground."), + (const char []) _("{STR_VAR_1} is looking this way and\nsmiling."), + (const char []) _("{STR_VAR_1} is gazing around\nrestlessly."), + (const char []) _("{STR_VAR_1} let out a battle cry."), + (const char []) _("{STR_VAR_1} danced a wonderful\ndance!"), + (const char []) _("{STR_VAR_1} is very eager."), + (const char []) _("{STR_VAR_1} is staring intently into\nthe distance."), + (const char []) _("{STR_VAR_1} is on the lookout!"), + (const char []) _("{STR_VAR_1} looked off into the\ndistance and barked!"), +}; + +// Unconditional sad messages +const char * const gFollowerSadMessages[] = { + (const char []) _("{STR_VAR_1} is dizzy."), + (const char []) _("{STR_VAR_1} is stepping on your\nfeet!"), + (const char []) _("{STR_VAR_1} seems a little tired."), +}; + +// Unconditional upset messages +const char * const gFollowerUpsetMessages[] = { + (const char []) _("{STR_VAR_1} seems unhappy somehow..."), + (const char []) _("{STR_VAR_1} is making an unhappy\nface."), + (const char []) _(".....Your pokemon seems a little\ncold."), +}; + +// Unconditional angry messages +const char * const gFollowerAngryMessages[] = { + (const char []) _("{STR_VAR_1} let out a roar!"), + (const char []) _("{STR_VAR_1} is making a face like\nits angry!"), + (const char []) _("{STR_VAR_1} seems to be angry for\nsome reason."), + (const char []) _("Your pokemon turned to face the\nother way, showing a defiant\pexpression."), + (const char []) _("{STR_VAR_1} cried out."), +}; + +// Unconditional pensive messages +const char * const gFollowerPensiveMessages[] = { + (const char []) _("{STR_VAR_1} is looking down\nsteadily."), + (const char []) _("{STR_VAR_1} is surveying the area."), + (const char []) _("{STR_VAR_1} is peering down."), + (const char []) _("{STR_VAR_1} is somehow fighting off\nsleep..."), + (const char []) _("{STR_VAR_1} seems to be wandering\naround."), + (const char []) _("{STR_VAR_1} is looking around\nabsentmindedly."), + (const char []) _("{STR_VAR_1} yawned very loudly!"), + (const char []) _("{STR_VAR_1} is relaxing comfortably."), + (const char []) _("{STR_VAR_1} is staring steadfastly\nat your face."), + (const char []) _("{STR_VAR_1} is staring intently at\nyour face."), + (const char []) _("{STR_VAR_1} is focusing its\nattention on you."), + (const char []) _("{STR_VAR_1} is staring into the\ndepths."), + (const char []) _("{STR_VAR_1} is sniffing at the\nground."), + (const char []) _("Your pokemon is staring intently at\nnothing."), + (const char []) _("{STR_VAR_1} focused with a sharp\ngaze!"), + (const char []) _("{STR_VAR_1} is concentrating."), + (const char []) _("{STR_VAR_1} faced this way and\nnodded."), + (const char []) _("{STR_VAR_1} seems a bit nervous..."), + (const char []) _("{STR_VAR_1} is looking at your\nfootprints."), + (const char []) _("{STR_VAR_1} is staring straight into\nyour eyes."), +}; + +// All 'love' messages are unconditional +const char * const gFollowerLoveMessages[] = { + (const char []) _("{STR_VAR_1} suddenly started walking\ncloser!"), + (const char []) _("{STR_VAR_1} cheeks are becoming\nrosy!"), + (const char []) _("Woah! {STR_VAR_1} suddenly hugged\nyou!"), + (const char []) _("Woah! {STR_VAR_1} is suddenly\nplayful!"), + (const char []) _("{STR_VAR_1} is rubbing against your\nlegs!"), + (const char []) _("{STR_VAR_1} blushes."), + (const char []) _("Ah! {STR_VAR_1} cuddles you!"), + (const char []) _("{STR_VAR_1} is regarding you with\nadoration!"), + (const char []) _("{STR_VAR_1} got closer to you."), + (const char []) _("{STR_VAR_1} is keeping close to your\nfeet."), +}; + +// Unconditional surprised messages +const char * const gFollowerSurpriseMessages[] = { + (const char []) _("{STR_VAR_1} is in danger of falling\nover!"), + (const char []) _("{STR_VAR_1} bumped into you!"), + (const char []) _("{STR_VAR_1} doesn't seem to be used\nto its own name yet."), + (const char []) _("{STR_VAR_1} is peering down."), + (const char []) _("Your pokemon stumbled and nearly\nfell!"), + (const char []) _("{STR_VAR_1} feels something and is\nhowling!"), + (const char []) _("{STR_VAR_1} seems refreshed!"), + (const char []) _("{STR_VAR_1} suddenly turned around\nand started barking!"), + (const char []) _("{STR_VAR_1} suddenly turned around!"), + (const char []) _("Your pokemon was surprised that you\nsuddenly spoke to it!"), + (const char []) _("Sniff sniff, something smells really\ngood!"), + (const char []) _("{STR_VAR_1} feels refreshed."), + (const char []) _("{STR_VAR_1} is wobbling and seems\nabout to fall over."), + (const char []) _("{STR_VAR_1} is in danger of falling\nover."), + (const char []) _("{STR_VAR_1} is walking along\ncautiously."), + (const char []) _("{STR_VAR_1} is getting tense with\nnervous energy."), + (const char []) _("{STR_VAR_1} sensed something strange\nand was surprised!"), + (const char []) _("{STR_VAR_1} is scared and snuggled\nup to you!"), + (const char []) _("{STR_VAR_1} is feeling an unusual\npresence..."), + (const char []) _("{STR_VAR_1} is getting tense with\nnervous energy."), +}; + +// Unconditional curious messages +const char * const gFollowerCuriousMessages[] = { + (const char []) _("Your pokemon is looking around\nrestlessly for something."), + (const char []) _("Your pokemon wasn't watching where\nit was going and ran into you!"), + (const char []) _("Sniff, sniff! Is there something\nnearby?"), + (const char []) _("{STR_VAR_1} is rolling a pebble\naround playfully."), + (const char []) _("{STR_VAR_1} is wandering around and\nsearching for something."), + (const char []) _("{STR_VAR_1} is sniffing at you."), + (const char []) _("{STR_VAR_1} seems to be a little\nhesitant..."), +}; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 78db0c4c32..a054279c8b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1705,6 +1705,49 @@ static bool8 SpeciesHasType(u16 species, u8 type) { return gBaseStats[species].type1 == type || gBaseStats[species].type2 == type; } +// Returns a random index according to a list of weights +static u8 RandomWeightedIndex(u8 *weights, u8 length) { + u8 i; + u16 random_value; + u16 cum_weight = 0; + for (i = 0; i < length; i++) + cum_weight += weights[i]; + random_value = Random() % cum_weight; + cum_weight = 0; + for (i = 0; i < length; i++) { + cum_weight += weights[i]; + if (random_value <= cum_weight) + return i; + } +} + +enum { + FOLLOWER_EMOTION_HAPPY = 0, + FOLLOWER_EMOTION_NEUTRAL, // Also called "No emotion" + FOLLOWER_EMOTION_SAD, + FOLLOWER_EMOTION_UPSET, + FOLLOWER_EMOTION_ANGRY, + FOLLOWER_EMOTION_PENSIVE, + FOLLOWER_EMOTION_LOVE, + FOLLOWER_EMOTION_SURPRISE, + FOLLOWER_EMOTION_CURIOUS, + FOLLOWER_EMOTION_LENGTH, +}; + +// 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_FollowerLove, N_FOLLOWER_LOVE_MESSAGES}, + [FOLLOWER_EMOTION_SURPRISE] = {gFollowerSurpriseMessages, EventScript_FollowerGeneric, N_FOLLOWER_SURPRISE_MESSAGES}, + [FOLLOWER_EMOTION_CURIOUS] = {gFollowerCuriousMessages, EventScript_FollowerGeneric, N_FOLLOWER_CURIOUS_MESSAGES}, +}; + +// Call an applicable follower message script bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big switch for follower messages { u16 value; @@ -1717,6 +1760,8 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big u8 n_choices = 0; struct ObjectEvent *objEvent = GetFollowerObject(); struct Pokemon *mon = GetFirstLiveMon(); + u8 emotion; + u8 emotion_weight[FOLLOWER_EMOTION_LENGTH] = {0}; if (mon == NULL) { ScriptCall(ctx, EventScript_FollowerLovesYou); return FALSE; @@ -1751,24 +1796,46 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big message_choices[n_choices++] = EventScript_FollowerIsShivering; else if (mon->status & 0x10) // STATUS1_BURN message_choices[n_choices++] = EventScript_FollowerBurnPainful; - // 5. Location-based messages - map_region = GetCurrentRegionMapSectionId(); // defined in region_map_sections.h - if (GetMonData(mon, MON_DATA_MET_LOCATION) == map_region) - message_choices[n_choices++] = EventScript_FollowerMetLocation; - // 6. Friendship-based messages + // 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); - if (friendship <= 80) - message_choices[n_choices++] = EventScript_FollowerSkeptical; - else if (friendship <= 170) - message_choices[n_choices++] = EventScript_FollowerAppraising; - else if (friendship < 255) - message_choices[n_choices++] = EventScript_FollowerHappyWalk; - else // Max friendship - message_choices[n_choices++] = EventScript_FollowerLovesYou; - if (!n_choices) - ScriptCall(ctx, EventScript_FollowerLovesYou); // Default in case of no choices - else - ScriptCall(ctx, message_choices[Random() % min(n_choices, ARRAY_COUNT(message_choices))]); + 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; + // Neutral weights + emotion_weight[FOLLOWER_EMOTION_NEUTRAL] = 15; + // Sad weights + emotion_weight[FOLLOWER_EMOTION_SAD] = 5; + health_percent = mon->hp * 100 / mon->maxHP; + if (health_percent < 50 || mon->status & 0x40) // STATUS1_PARALYSIS + emotion_weight[FOLLOWER_EMOTION_SAD] = 30; + // Upset weights + emotion_weight[FOLLOWER_EMOTION_UPSET] = friendship < 80 ? 15 : 5; + // Angry weights + emotion_weight[FOLLOWER_EMOTION_ANGRY] = friendship < 80 ? 15 : 5; + // Pensive weights + emotion_weight[FOLLOWER_EMOTION_PENSIVE] = 15; + // Love weights + if (friendship > 170) + emotion_weight[FOLLOWER_EMOTION_LOVE] = 30; + else if (friendship > 80) + emotion_weight[FOLLOWER_EMOTION_LOVE] = 20; + // Surprise weights + // TODO: Scale this with how long the follower has been out + emotion_weight[FOLLOWER_EMOTION_SURPRISE] = 10; + // Curious weights + // 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); + ctx->data[0] = (u32) followerBasicMessages[emotion].messages[Random() % followerBasicMessages[emotion].length]; + ScriptCall(ctx, followerBasicMessages[emotion].script); return FALSE; }