From 69a9e6a365745ea536c6c3c64a142d4c469254ae Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:21:48 -0700 Subject: [PATCH 001/108] Localization: remove all `"_female": null` entries in /en (#3918) --- src/locales/en/achv.json | 36 +-- src/locales/en/dialogue-double-battle.json | 4 +- src/locales/en/dialogue-final-boss.json | 2 +- src/locales/en/dialogue.json | 252 ++++++++++----------- 4 files changed, 147 insertions(+), 147 deletions(-) diff --git a/src/locales/en/achv.json b/src/locales/en/achv.json index 0ed746c77b3..fae786e034a 100644 --- a/src/locales/en/achv.json +++ b/src/locales/en/achv.json @@ -10,19 +10,19 @@ }, "10K_MONEY": { "name": "Money Haver", - "name_female": null + "name_female": "Money Haver" }, "100K_MONEY": { "name": "Rich", - "name_female": null + "name_female": "Rich" }, "1M_MONEY": { "name": "Millionaire", - "name_female": null + "name_female": "Millionaire" }, "10M_MONEY": { "name": "One Percenter", - "name_female": null + "name_female": "One Percenter" }, "DamageAchv": { "description": "Inflict {{damageAmount}} damage in one hit" @@ -32,11 +32,11 @@ }, "1000_DMG": { "name": "Harder Hitter", - "name_female": null + "name_female": "Harder Hitter" }, "2500_DMG": { "name": "That's a Lotta Damage!", - "name_female": null + "name_female": "That's a Lotta Damage!" }, "10000_DMG": { "name": "One Punch Man", @@ -47,19 +47,19 @@ }, "250_HEAL": { "name": "Novice Healer", - "name_female": null + "name_female": "Novice Healer" }, "1000_HEAL": { "name": "Big Healer", - "name_female": null + "name_female": "Big Healer" }, "2500_HEAL": { "name": "Cleric", - "name_female": null + "name_female": "Cleric" }, "10000_HEAL": { "name": "Recovery Master", - "name_female": null + "name_female": "Recovery Master" }, "LevelAchv": { "description": "Level up a Pokémon to Lv{{level}}" @@ -69,7 +69,7 @@ }, "LV_250": { "name": "Elite", - "name_female": null + "name_female": "Elite" }, "LV_1000": { "name": "To Go Even Further Beyond" @@ -79,23 +79,23 @@ }, "10_RIBBONS": { "name": "Pokémon League Champion", - "name_female": null + "name_female": "Pokémon League Champion" }, "25_RIBBONS": { "name": "Great League Champion", - "name_female": null + "name_female": "Great League Champion" }, "50_RIBBONS": { "name": "Ultra League Champion", - "name_female": null + "name_female": "Ultra League Champion" }, "75_RIBBONS": { "name": "Rogue League Champion", - "name_female": null + "name_female": "Rogue League Champion" }, "100_RIBBONS": { "name": "Master League Champion", - "name_female": null + "name_female": "Master League Champion" }, "TRANSFER_MAX_BATTLE_STAT": { "name": "Teamwork", @@ -147,7 +147,7 @@ }, "SHINY_PARTY": { "name": "That's Dedication", - "name_female": null, + "name_female": "That's Dedication", "description": "Have a full party of shiny Pokémon" }, "HATCH_MYTHICAL": { @@ -176,7 +176,7 @@ }, "CLASSIC_VICTORY": { "name": "Undefeated", - "name_female": null, + "name_female": "Undefeated", "description": "Beat the game in classic mode" }, "UNEVOLVED_CLASSIC_VICTORY": { diff --git a/src/locales/en/dialogue-double-battle.json b/src/locales/en/dialogue-double-battle.json index 9484aa2edcc..4190af49d15 100644 --- a/src/locales/en/dialogue-double-battle.json +++ b/src/locales/en/dialogue-double-battle.json @@ -58,7 +58,7 @@ "iris_alder_double": { "encounter": { "1": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?", - "1_female": null + "1_female": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?" }, "victory": { "1": "Iris: A loss like this is not easy to take...\n$Alder: But we will only get stronger with every loss!" @@ -75,7 +75,7 @@ "marnie_piers_double": { "encounter": { "1": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing...", - "1_female": null + "1_female": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing..." }, "victory": { "1": "Piers: Now that was a great concert!\n$Marnie: Brother..." diff --git a/src/locales/en/dialogue-final-boss.json b/src/locales/en/dialogue-final-boss.json index 3abe4cd8831..6f99aae3e0c 100644 --- a/src/locales/en/dialogue-final-boss.json +++ b/src/locales/en/dialogue-final-boss.json @@ -1,6 +1,6 @@ { "encounter": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", - "encounter_female": null, + "encounter_female": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", "firstStageWin": "I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.\n$Do not disappoint me.", "secondStageWin": "…Magnificent.", "key_ordinal_one": "st", diff --git a/src/locales/en/dialogue.json b/src/locales/en/dialogue.json index e96a42daf1d..1f8919f11b5 100644 --- a/src/locales/en/dialogue.json +++ b/src/locales/en/dialogue.json @@ -3,31 +3,31 @@ "encounter": { "1": "Hey, wanna battle?", "2": "Are you a new trainer too?", - "2_female": null, + "2_female": "Are you a new trainer too?", "3": "Hey, I haven't seen you before. Let's battle!", "4": "I just lost, so I'm trying to find more Pokémon.\nWait! You look weak! Come on, let's battle!", - "4_female": null, + "4_female": "I just lost, so I'm trying to find more Pokémon.\nWait! You look weak! Come on, let's battle!", "5": "Have we met or not? I don't really remember. Well, I guess it's nice to meet you anyway!", "6": "All right! Let's go!", "7": "All right! Here I come! I'll show you my power!", "8": "Haw haw haw... I'll show you how hawesome my Pokémon are!", "9": "No need to waste time saying hello. Bring it on whenever you're ready!", - "9_female": null, + "9_female": "No need to waste time saying hello. Bring it on whenever you're ready!", "10": "Don't let your guard down, or you may be crying when a kid beats you.", "11": "I've raised my Pokémon with great care. You're not allowed to hurt them!", "12": "Glad you made it! It won't be an easy job from here.", - "12_female": null, + "12_female": "Glad you made it! It won't be an easy job from here.", "13": "The battles continue forever! Welcome to the world with no end!", - "13_female": null + "13_female": "The battles continue forever! Welcome to the world with no end!" }, "victory": { "1": "Wow! You're strong!", - "1_female": null, + "1_female": "Wow! You're strong!", "2": "I didn't stand a chance, huh?", "3": "I'll find you again when I'm older and beat you!", "4": "Ugh. I don't have any more Pokémon.", "5": "No way… NO WAY! How could I lose again…", - "5_female": null, + "5_female": "No way… NO WAY! How could I lose again…", "6": "No! I lost!", "7": "Whoa! You are incredible! I'm amazed and surprised!", "8": "Could it be… How… My Pokémon and I are the strongest, though…", @@ -42,12 +42,12 @@ "encounter": { "1": "Let's have a battle, shall we?", "2": "You look like a new trainer. Let's have a battle!", - "2_female": null, + "2_female": "You look like a new trainer. Let's have a battle!", "3": "I don't recognize you. How about a battle?", "4": "Let's have a fun Pokémon battle!", "5": "I'll show you the ropes of how to really use Pokémon!", "6": "A serious battle starts from a serious beginning! Are you sure you're ready?", - "6_female": null, + "6_female": "A serious battle starts from a serious beginning! Are you sure you're ready?", "7": "You're only young once. And you only get one shot at a given battle. Soon, you'll be nothing but a memory.", "8": "You'd better go easy on me, OK? Though I'll be seriously fighting!", "9": "School is boring. I've got nothing to do. Yawn. I'm only battling to kill the time." @@ -55,15 +55,15 @@ "victory": { "1": "That was impressive! I've got a lot to learn.", "2": "I didn't think you'd beat me that bad…", - "2_female": null, + "2_female": "I didn't think you'd beat me that bad…", "3": "I hope we get to have a rematch some day.", "4": "That was pretty amazingly fun! You've totally exhausted me…", "5": "You actually taught me a lesson! You're pretty amazing!", "6": "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", - "6_female": null, + "6_female": "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", "7": "I don't need memories like this. Deleting memory…", "8": "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", - "8_female": null, + "8_female": "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", "9": "I'm actually getting tired of battling… There's gotta be something new to do…" } }, @@ -154,7 +154,7 @@ "ace_trainer": { "encounter": { "1": "You seem quite confident.", - "1_female": null, + "1_female": "You seem quite confident.", "2": "Your Pokémon… Show them to me…", "3": "Because I'm an Ace Trainer, people think I'm strong.", "4": "Are you aware of what it takes to be an Ace Trainer?" @@ -163,9 +163,9 @@ "1": "Yes… You have good Pokémon…", "2": "What?! But I'm a battling genius!", "3": "Of course, you are the main character!", - "3_female": null, + "3_female": "Of course, you are the main character!", "4": "OK! OK! You could be an Ace Trainer!", - "4_female": null + "4_female": "OK! OK! You could be an Ace Trainer!" }, "defeat": { "1": "I am devoting my body and soul to Pokémon battles!", @@ -187,7 +187,7 @@ "1": "Get ready, because when we team up, it's double the trouble!", "2": "Two hearts, one strategy – let's see if you can keep up with our twin power!", "3": "Hope you're ready for double trouble, because we're about to bring the heat!", - "3_female": null + "3_female": "Hope you're ready for double trouble, because we're about to bring the heat!" }, "victory": { "1": "We may have lost this round, but our bond remains unbreakable!", @@ -216,7 +216,7 @@ "encounter": { "1": "I praise your courage in challenging me! For I am the one with the strongest kick!", "2": "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?", - "2_female": null + "2_female": "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?" }, "victory": { "1": "Oh. The Pokémon did the fighting. My strong kick didn't help a bit.", @@ -328,7 +328,7 @@ "defeat": { "1": "New age simply refers to twentieth century classical composers, right?", "2": "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself.", - "2_female": null + "2_female": "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself." } }, "psychic": { @@ -360,7 +360,7 @@ "baker": { "encounter": { "1": "Hope you're ready to taste defeat!", - "1_female": null + "1_female": "Hope you're ready to taste defeat!" }, "victory": { "1": "I'll bake a comeback." @@ -391,7 +391,7 @@ "1": "Matey, you're walking the plank if you lose!", "2": "Come on then! My sailor's pride is at stake!", "3": "Ahoy there! Are you seasick?", - "3_female": null + "3_female": "Ahoy there! Are you seasick?" }, "victory": { "1": "Argh! Beaten by a kid!", @@ -419,7 +419,7 @@ }, "victory": { "1": "Tch, you really are strong. It's too bad.\n$If you were to join Team Rocket, you could become an Executive.", - "1_female": null, + "1_female": "Tch, you really are strong. It's too bad.\n$If you were to join Team Rocket, you could become an Executive.", "2": "I... I'm shattered...", "3": "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" } @@ -458,7 +458,7 @@ "1": "Hehehe! You might have beaten me, but you don't stand a chance against the boss!\n$If you get lost now, you won't have to face a sound whipping!", "2": "Hehehe... So, I lost, too...", "3": "Ahya! How could this be? For an Admin like me to lose to some random trainer...", - "3_female": null + "3_female": "Ahya! How could this be? For an Admin like me to lose to some random trainer..." } }, "courtney": { @@ -478,13 +478,13 @@ "1": "Ahahahaha! You're going to meddle in Team Aqua's affairs?\n$You're either absolutely fearless, simply ignorant, or both!\n$You're so cute, you're disgusting! I'll put you down", "2": "What's this? Who's this spoiled brat?", "3": "Cool your jets. Be patient. I'll crush you shortly.", - "3_female": null + "3_female": "Cool your jets. Be patient. I'll crush you shortly." }, "victory": { "1": "Ahahahaha! We got meddled with unexpectedly! We're out of options.\n$We'll have to pull out. But this isn't the last you'll see of Team Aqua!\n$We have other plans! Don't you forget it!", "2": "Ahhh?! Did I go too easy on you?!", "3": "Uh. Are you telling me you've upped your game even more during the fight?\n$You're a brat with a bright future… My Pokémon and I don't have any strength left to fight…\n$Go on… Go and be destroyed by Archie.", - "3_female": null + "3_female": "Uh. Are you telling me you've upped your game even more during the fight?\n$You're a brat with a bright future… My Pokémon and I don't have any strength left to fight…\n$Go on… Go and be destroyed by Archie." } }, "matt": { @@ -497,7 +497,7 @@ "1": "Muwuhahaha! That battle was fun even though I lost!", "2": "I can feel it! I can feel it, all right! The strength coming offa you!\n$More! I still want more! But looks like we're outta time...", "3": "Oho! That's a loss I can be proud of!", - "3_female": null + "3_female": "Oho! That's a loss I can be proud of!" } }, "mars": { @@ -505,7 +505,7 @@ "1": "I'm Mars, one of Team Galactic's top Commanders.", "2": "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", "3": "Feeling nervous? You should be!", - "3_female": null + "3_female": "Feeling nervous? You should be!" }, "victory": { "1": "This can't be happening! How did I lose?!", @@ -540,25 +540,25 @@ "zinzolin": { "encounter": { "1": "You could become a threat to Team Plasma, so we will eliminate you here and now!", - "1_female": null, + "1_female": "You could become a threat to Team Plasma, so we will eliminate you here and now!", "2": "You don't have the sense to know when to quit, it seems. It's an act of mercy on my part to bring an end to this now!", "3": "You're an impressive Trainer to have made it this far. But it ends here.", - "3_female": null + "3_female": "You're an impressive Trainer to have made it this far. But it ends here." }, "victory": { "1": "Ghetsis... I have failed you...", "2": "It's bitter cold. I'm shivering. I'm suffering. Yet, we will stand victorious.", "3": "Hmph. You're a smarter Trainer than I expected, but not smart enough.", - "3_female": null + "3_female": "Hmph. You're a smarter Trainer than I expected, but not smart enough." } }, "rood": { "encounter": { "1": "You are a threat to Team Plasma. We cannot let you walk away from here and now!", - "1_female": null, + "1_female": "You are a threat to Team Plasma. We cannot let you walk away from here and now!", "2": "It seems you don't know when to give up. I'll make sure no one interferes with our plans!", "3": "You are a remarkable Trainer to have made it this far. But this is where it ends.", - "3_female": null + "3_female": "You are a remarkable Trainer to have made it this far. But this is where it ends." }, "victory": { "1": "Ghetsis... I have failed my mission...", @@ -569,15 +569,15 @@ "xerosic": { "encounter": { "1": "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", - "1_female": null, + "1_female": "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", "2": "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", - "2_female": null, + "2_female": "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", "3": "I've been waiting for you! I need to do a little research on you! Come, let us begin!" }, "victory": { "1": "Ah, you're quite strong. Oh yes—very strong, indeed.", "2": "Ding-ding-ding! You did it! To the victor go the spoils!", - "2_female": null, + "2_female": "Ding-ding-ding! You did it! To the victor go the spoils!", "3": "Wonderful! Amazing! You have tremendous skill and bravery!" } }, @@ -585,7 +585,7 @@ "encounter": { "1": "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", "2": "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", - "2_female": null, + "2_female": "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", "3": "I've anticipated your arrival. It's time for a little test. Shall we begin?" }, "victory": { @@ -598,11 +598,11 @@ "encounter": { "1": "Prepare for trouble!", "2": "We're pulling a big job here! Get lost, kid!", - "2_female": null, + "2_female": "We're pulling a big job here! Get lost, kid!", "3": "Hand over your Pokémon, or face the wrath of Team Rocket!", "4": "You're about to experience the true terror of Team Rocket!", "5": "Hey, kid! Me am a Team Rocket member kind of guy!", - "5_female": null + "5_female": "Hey, kid! Me am a Team Rocket member kind of guy!" }, "victory": { "1": "Team Rocket blasting off again!", @@ -624,7 +624,7 @@ "1": "Huh? I lost?!", "2": "I can't believe I lost! I even skipped lunch for this", "3": "No way! You're just a kid!", - "3_female": null, + "3_female": "No way! You're just a kid!", "4": "Urrrgh... I should've ducked into our hideout right away...", "5": "You beat me... Do you think the boss will dock my pay for this?" } @@ -652,7 +652,7 @@ "3": "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", "4": "Get ready to lose!", "5": "Hope you're ready for a cosmic beatdown!", - "5_female": null + "5_female": "Hope you're ready for a cosmic beatdown!" }, "victory": { "1": "Shut down...", @@ -682,7 +682,7 @@ "encounter": { "1": "Your Pokémon are no match for the elegance of Team Flare.", "2": "Hope you brought your sunglasses, because things are about to get bright!", - "2_female": null, + "2_female": "Hope you brought your sunglasses, because things are about to get bright!", "3": "Team Flare will cleanse the world of imperfection!", "4": "Prepare to face the brilliance of Team Flare!", "5": "Fashion is most important to us!" @@ -784,7 +784,7 @@ }, "defeat": { "1": "Mark my words. Not being able to measure your own strength shows that you are still a child.", - "1_female": null + "1_female": "Mark my words. Not being able to measure your own strength shows that you are still a child." } }, "rocket_boss_giovanni_2": { @@ -845,7 +845,7 @@ "galactic_boss_cyrus_1": { "encounter": { "1": "You were compelled to come here by such vacuous sentimentality.\n$I will make you regret paying heed to your heart!", - "1_female": null + "1_female": "You were compelled to come here by such vacuous sentimentality.\n$I will make you regret paying heed to your heart!" }, "victory": { "1": "Interesting. And quite curious." @@ -995,7 +995,7 @@ "misty": { "encounter": { "1": "My policy is an all out offensive with Water-type Pokémon!", - "1_female": null, + "1_female": "My policy is an all out offensive with Water-type Pokémon!", "2": "Hiya, I'll show you the strength of my aquatic Pokémon!", "3": "My dream was to go on a journey and battle powerful trainers…\nWill you be a sufficient challenge?" }, @@ -1013,14 +1013,14 @@ "lt_surge": { "encounter": { "1": "My Electric Pokémon saved me during the war! I'll show you how!", - "1_female": null, + "1_female": "My Electric Pokémon saved me during the war! I'll show you how!", "2": "Ten-hut! I'll shock you into surrender!", "3": "I'll zap you just like I do to all my enemies in battle!" }, "victory": { "1": "Whoa! Your team's the real deal, kid!", "2": "Aaargh, you're strong! Even my electric tricks lost against you.", - "2_female": null, + "2_female": "Aaargh, you're strong! Even my electric tricks lost against you.", "3": "That was an absolutely shocking loss!" }, "defeat": { @@ -1045,7 +1045,7 @@ "defeat": { "1": "I was afraid I would doze off…", "2": "Oh my, it seems my Grass Pokémon overwhelmed you.", - "2_female": null, + "2_female": "Oh my, it seems my Grass Pokémon overwhelmed you.", "3": "That battle was such a soothing experience.", "4": "Oh… Is that all?" } @@ -1106,7 +1106,7 @@ "1": "I, the leader of Team Rocket, will make you feel a world of pain!", "2": "My training here will be vital before I am to face my old associates again.", "3": "I do not think you are prepared for the level of failure you are about to experience!", - "3_female": null + "3_female": "I do not think you are prepared for the level of failure you are about to experience!" }, "victory": { "1": "WHAT! Me, lose?! There is nothing I wish to say to you!", @@ -1139,7 +1139,7 @@ "brawly": { "encounter": { "1": "Oh man, a challenger!\nLet's see what you can do!", - "1_female": null, + "1_female": "Oh man, a challenger!\nLet's see what you can do!", "2": "You seem like a big splash.\nLet's battle!", "3": "Time to create a storm!\nLet's go!" }, @@ -1167,7 +1167,7 @@ }, "defeat": { "1": "Recharge your batteries and challenge me again sometime!\nWahahahaha!", - "1_female": null, + "1_female": "Recharge your batteries and challenge me again sometime!\nWahahahaha!", "2": "I hope you found our battle electrifying!\nWahahahaha!", "3": "Aren't you shocked I won?\nWahahahaha!" } @@ -1214,7 +1214,7 @@ }, "victory": { "1": "You're the first Trainer I've seen with more grace than I.\nExcellently played.", - "1_female": null, + "1_female": "You're the first Trainer I've seen with more grace than I.\nExcellently played.", "2": "Oh, my Flying Pokémon have plummeted!\nVery well.", "3": "Though I may have fallen, my Pokémon will continue to fly!" }, @@ -1227,7 +1227,7 @@ "tate": { "encounter": { "1": "Hehehe…\nWere you surprised to see me without my sister?", - "1_female": null, + "1_female": "Hehehe…\nWere you surprised to see me without my sister?", "2": "I can see what you're thinking…\nYou want to battle!", "3": "How can you defeat someone…\nWho knows your every move?" }, @@ -1245,7 +1245,7 @@ "liza": { "encounter": { "1": "Fufufu…\nWere you surprised to see me without my brother?", - "1_female": null, + "1_female": "Fufufu…\nWere you surprised to see me without my brother?", "2": "I can determine what you desire…\nYou want to battle, don't you?", "3": "How can you defeat someone…\nWho's one with their Pokémon?" }, @@ -1317,10 +1317,10 @@ "nessa": { "encounter": { "1": "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", - "1_female": null, + "1_female": "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", "2": "I'm not here to chat. I'm here to win!", "3": "This is a little gift from my Pokémon… I hope you can take it!", - "3_female": null + "3_female": "This is a little gift from my Pokémon… I hope you can take it!" }, "victory": { "1": "You and your Pokémon are just too much…", @@ -1341,7 +1341,7 @@ }, "victory": { "1": "You… You're pretty good, huh?", - "1_female": null, + "1_female": "You… You're pretty good, huh?", "2": "If you find Gordie around, be sure to give him a right trashing, would you?", "3": "I think you took breaking the ice a little too literally…" }, @@ -1355,12 +1355,12 @@ "encounter": { "1": "You look strong! Shoots! Let's start!", "2": "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", - "2_female": null, + "2_female": "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", "3": "Oh ho, so I'm facing you! That's off the wall." }, "victory": { "1": "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", - "1_female": null, + "1_female": "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", "2": "You don't just look strong, you're strong fo' reals! Eh, I was swept away, too!", "3": "You're strong as a gnarly wave!" }, @@ -1373,7 +1373,7 @@ "shauntal": { "encounter": { "1": "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", - "1_female": null, + "1_female": "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", "2": "I absolutely love writing about Trainers who come here and the Pokémon they train.\nCould I use you and your Pokémon as a subject?", "3": "Every person who works with Pokémon has a story to tell.\nWhat story is about to be told?" }, @@ -1391,7 +1391,7 @@ "marshal": { "encounter": { "1": "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", - "1_female": null, + "1_female": "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", "2": "Victory, decisive victory, is my intention! Challenger, here I come!", "3": "In myself, I seek to develop the strength of a fighter and shatter any weakness in myself!\nPrevailing with the force of my convictions!" }, @@ -1411,7 +1411,7 @@ "1": "You remind me of an old friend. That makes me excited about this Pokémon battle!", "2": "Pokémon battles have no meaning if you don't think why you battle.\n$Or better said, it makes battling together with Pokémon meaningless.", "3": "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you.", - "3_female": null + "3_female": "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you." }, "victory": { "1": "Thank you! I saw what was missing in me.", @@ -1427,65 +1427,65 @@ "chili": { "encounter": { "1": "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", - "1_female": null, + "1_female": "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", "2": "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", - "2_female": null, + "2_female": "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", "3": "I'm going to show you what me and my blazing Fire types can do!", - "3_female": null + "3_female": "I'm going to show you what me and my blazing Fire types can do!" }, "victory": { "1": "You got me. I am… burned… out…", - "1_female": null, + "1_female": "You got me. I am… burned… out…", "2": "Whoa ho! You're on fire!", - "2_female": null, + "2_female": "Whoa ho! You're on fire!", "3": "Augh! You got me!" }, "defeat": { "1": "I'm on fire! Play with me, and you'll get burned!", - "1_female": null, + "1_female": "I'm on fire! Play with me, and you'll get burned!", "2": "When you play with fire, you get burned!", "3": "I mean, c'mon, your opponent was me! You didn't have a chance!", - "3_female": null + "3_female": "I mean, c'mon, your opponent was me! You didn't have a chance!" } }, "cilan": { "encounter": { "1": "Nothing personal... No hard feelings... Me and my Grass-type Pokémon will...\n$Um... We're gonna battle come what may.", - "1_female": null, + "1_female": "Nothing personal... No hard feelings... Me and my Grass-type Pokémon will...\n$Um... We're gonna battle come what may.", "2": "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", - "2_female": null, + "2_female": "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", "3": "OK… So, um, I'm Cilan, I like Grass-type Pokémon.", - "3_female": null + "3_female": "OK… So, um, I'm Cilan, I like Grass-type Pokémon." }, "victory": { "1": "Er… Is it over now?", - "1_female": null, + "1_female": "Er… Is it over now?", "2": "…What a surprise. You are very strong, aren't you? \n$I guess my brothers wouldn't have been able to defeat you either…", - "2_female": null, + "2_female": "…What a surprise. You are very strong, aren't you? \n$I guess my brothers wouldn't have been able to defeat you either…", "3": "…Huh. Looks like my timing was, um, off?" }, "defeat": { "1": "Huh? Did I win?", - "1_female": null, + "1_female": "Huh? Did I win?", "2": "I guess… \n$I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.", - "2_female": null, + "2_female": "I guess… \n$I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.", "3": "It…it was quite a thrilling experience…", - "3_female": null + "3_female": "It…it was quite a thrilling experience…" } }, "roark": { "encounter": { "1": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", - "1_female": null, + "1_female": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", "2": "Here goes! These are my rocking Pokémon, my pride and joy!", "3": "Rock-type Pokémon are simply the best!", "4": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", - "4_female": null + "4_female": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!" }, "victory": { "1": "W-what? That can't be! My buffed-up Pokémon!", "2": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", - "2_female": null, + "2_female": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", "3": "With skill like yours, it's natural for you to win.", "4": "Wh-what?! It can't be! Even that wasn't enough?", "5": "I blew it." @@ -1508,7 +1508,7 @@ "victory": { "1": "I'm not good enough yet…", "2": "I see… Your journey has taken you to far-away places and you have witnessed much more than I.\n$I envy you for that…", - "2_female": null, + "2_female": "I see… Your journey has taken you to far-away places and you have witnessed much more than I.\n$I envy you for that…", "3": "How is this possible…", "4": "I don't think our potentials are so different.\n$But you seem to have something more than that… So be it.", "5": "Guess I need more training.", @@ -1568,13 +1568,13 @@ }, "defeat": { "1": "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?", - "1_female": null + "1_female": "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?" } }, "ramos": { "encounter": { "1": "Did yeh enjoy the garden playground I made with all these sturdy plants o' mine?\n$Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?", - "1_female": null + "1_female": "Did yeh enjoy the garden playground I made with all these sturdy plants o' mine?\n$Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?" }, "victory": { "1": "Yeh believe in yer Pokémon… And they believe in yeh, too… It was a fine battle, sprout." @@ -1605,7 +1605,7 @@ "victory": { "1": "I must say, I'm warmed up to you! I might even admire you a little.", "2": "Wow! You're great! You've earned my respect! \n$I think your focus and will bowled us over totally. ", - "2_female": null + "2_female": "Wow! You're great! You've earned my respect! \n$I think your focus and will bowled us over totally. " }, "defeat": { "1": "I sensed your will to win, but I don't lose!", @@ -1618,7 +1618,7 @@ }, "victory": { "1": "Amazing! You're very good, aren't you?", - "1_female": null + "1_female": "Amazing! You're very good, aren't you?" }, "defeat": { "1": "Yes! My Pokémon and I are perfectly good!" @@ -1660,7 +1660,7 @@ "clay": { "encounter": { "1": "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!", - "1_female": null + "1_female": "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!" }, "victory": { "1": "Man oh man… It feels good to go all out and still be defeated!" @@ -1675,7 +1675,7 @@ }, "victory": { "1": "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!", - "1_female": null + "1_female": "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!" }, "defeat": { "1": "You come back to see me again now, ya hear?" @@ -1742,7 +1742,7 @@ }, "victory": { "1": "Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. \n$I find much joy in having met you and your Pokémon. You have proven yourself worthy.", - "1_female": null + "1_female": "Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. \n$I find much joy in having met you and your Pokémon. You have proven yourself worthy." }, "defeat": { "1": "A grand illusion!" @@ -1751,14 +1751,14 @@ "lorelei": { "encounter": { "1": "No one can best me when it comes to icy Pokémon! Freezing moves are powerful!\n$Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?", - "1_female": null + "1_female": "No one can best me when it comes to icy Pokémon! Freezing moves are powerful!\n$Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?" }, "victory": { "1": "How dare you!" }, "defeat": { "1": "There's nothing you can do once you're frozen.", - "1_female": null + "1_female": "There's nothing you can do once you're frozen." } }, "will": { @@ -1775,11 +1775,11 @@ "malva": { "encounter": { "1": "I feel like my heart might just burst into flames. \n$I'm burning up with my hatred for you, runt!", - "1_female": null + "1_female": "I feel like my heart might just burst into flames. \n$I'm burning up with my hatred for you, runt!" }, "victory": { "1": "What news… So a new challenger has defeated Malva!", - "1_female": null + "1_female": "What news… So a new challenger has defeated Malva!" }, "defeat": { "1": "I am delighted! Yes, delighted that I could squash you beneath my heel." @@ -1802,7 +1802,7 @@ }, "victory": { "1": "I certainly found an interesting Trainer to face!", - "1_female": null + "1_female": "I certainly found an interesting Trainer to face!" }, "defeat": { "1": "Ahaha. What an interesting battle." @@ -1814,11 +1814,11 @@ }, "victory": { "1": "Not bad, kiddo.", - "1_female": null + "1_female": "Not bad, kiddo." }, "defeat": { "1": "Nahahaha! You really are something else, kiddo!", - "1_female": null + "1_female": "Nahahaha! You really are something else, kiddo!" } }, "bruno": { @@ -1838,7 +1838,7 @@ }, "victory": { "1": "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win.", - "1_female": null + "1_female": "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win." }, "defeat": { "1": "Thanks! Thanks to our battle, I was also able to make progress in my research!" @@ -1869,11 +1869,11 @@ "lenora": { "encounter": { "1": "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!", - "1_female": null + "1_female": "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!" }, "victory": { "1": "My theory about you was correct. You're more than just talented… You're motivated! I salute you!", - "1_female": null + "1_female": "My theory about you was correct. You're more than just talented… You're motivated! I salute you!" }, "defeat": { "1": "Ah ha ha! If you lose, make sure to analyze why, and use that knowledge in your next battle!" @@ -1899,7 +1899,7 @@ }, "defeat": { "1": "Hey, c'mon! Get serious! You gotta put more out there!", - "1_female": null + "1_female": "Hey, c'mon! Get serious! You gotta put more out there!" } }, "olivia": { @@ -1938,7 +1938,7 @@ "flint": { "encounter": { "1": "Hope you're warmed up, cause here comes the Big Bang!", - "1_female": null + "1_female": "Hope you're warmed up, cause here comes the Big Bang!" }, "victory": { "1": "Incredible! Your moves are so hot, they make mine look lukewarm!" @@ -1961,7 +1961,7 @@ "caitlin": { "encounter": { "1": "It's me who appeared when the flower opened up. You who have been waiting…\n$You look like a Pokémon Trainer with refined strength and deepened kindness. \n$What I look for in my opponent is superb strength… \n$Please unleash your power to the fullest!", - "1_female": null + "1_female": "It's me who appeared when the flower opened up. You who have been waiting…\n$You look like a Pokémon Trainer with refined strength and deepened kindness. \n$What I look for in my opponent is superb strength… \n$Please unleash your power to the fullest!" }, "victory": { "1": "My Pokémon and I learned so much! I offer you my thanks." @@ -1984,15 +1984,15 @@ "wikstrom": { "encounter": { "1": "Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! \n$Let the battle begin! En garde!", - "1_female": null + "1_female": "Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! \n$Let the battle begin! En garde!" }, "victory": { "1": "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!", - "1_female": null + "1_female": "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!" }, "defeat": { "1": "What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! \n$Winning against such a worthy opponent doth give my soul wings--thus do I soar!", - "1_female": null + "1_female": "What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! \n$Winning against such a worthy opponent doth give my soul wings--thus do I soar!" } }, "acerola": { @@ -2024,14 +2024,14 @@ }, "victory": { "1": "You got me. You are magnificent!", - "1_female": null, + "1_female": "You got me. You are magnificent!", "2": "I never expected another trainer to beat me… I'm surprised.", - "2_female": null + "2_female": "I never expected another trainer to beat me… I'm surprised." }, "defeat": { "1": "That was close. Want to try again?", "2": "It's not that you are weak. Don't let it bother you.", - "2_female": null + "2_female": "It's not that you are weak. Don't let it bother you." } }, "karen": { @@ -2057,7 +2057,7 @@ }, "victory": { "1": "The power of Grass has wilted… What an incredible Challenger!", - "1_female": null + "1_female": "The power of Grass has wilted… What an incredible Challenger!" }, "defeat": { "1": "This'll really leave you in shock and awe." @@ -2077,7 +2077,7 @@ "drasna": { "encounter": { "1": "You must be a strong Trainer. Yes, quite strong indeed…\n$That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!", - "1_female": null + "1_female": "You must be a strong Trainer. Yes, quite strong indeed…\n$That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!" }, "victory": { "1": "Oh, dear me. That sure was a quick battle… I do hope you'll come back again sometime!" @@ -2111,7 +2111,7 @@ "blue": { "encounter": { "1": "You must be pretty good to get this far.", - "1_female": null + "1_female": "You must be pretty good to get this far." }, "victory": { "1": "I've only lost to him and now to you… Him? Hee, hee…" @@ -2159,7 +2159,7 @@ }, "victory": { "1": "This is the emergence of a new Champion.", - "1_female": null + "1_female": "This is the emergence of a new Champion." }, "defeat": { "1": "I successfully defended my Championship." @@ -2248,7 +2248,7 @@ }, "victory": { "1": "Waaah! Waaah! You're so mean!", - "1_female": null + "1_female": "Waaah! Waaah! You're so mean!" }, "defeat": { "1": "And that's that!" @@ -2257,7 +2257,7 @@ "chuck": { "encounter": { "1": "Hah! You want to challenge me? Are you brave or just ignorant?", - "1_female": null + "1_female": "Hah! You want to challenge me? Are you brave or just ignorant?" }, "victory": { "1": "You're strong! Would you please make me your apprentice?" @@ -2269,7 +2269,7 @@ "katy": { "encounter": { "1": "Don't let your guard down unless you would like to find yourself knocked off your feet!", - "1_female": null + "1_female": "Don't let your guard down unless you would like to find yourself knocked off your feet!" }, "victory": { "1": "All of my sweet little Pokémon dropped like flies!" @@ -2303,7 +2303,7 @@ "maylene": { "encounter": { "1": "I've come to challenge you now, and I won't hold anything back. \n$Please prepare yourself for battle!", - "1_female": null + "1_female": "I've come to challenge you now, and I won't hold anything back. \n$Please prepare yourself for battle!" }, "victory": { "1": "I admit defeat…" @@ -2326,7 +2326,7 @@ "byron": { "encounter": { "1": "Trainer! You're young, just like my son, Roark. \n$With more young Trainers taking charge, the future of Pokémon is bright! \n$So, as a wall for young people, I'll take your challenge!", - "1_female": null + "1_female": "Trainer! You're young, just like my son, Roark. \n$With more young Trainers taking charge, the future of Pokémon is bright! \n$So, as a wall for young people, I'll take your challenge!" }, "victory": { "1": "Hmm! My sturdy Pokémon--defeated!" @@ -2349,7 +2349,7 @@ "volkner": { "encounter": { "1": "Since you've come this far, you must be quite strong…\n$I hope you're the Trainer who'll make me remember how fun it is to battle!", - "1_female": null + "1_female": "Since you've come this far, you must be quite strong…\n$I hope you're the Trainer who'll make me remember how fun it is to battle!" }, "victory": { "1": "You've got me beat…\n$Your desire and the noble way your Pokémon battled for you… \n$I even felt thrilled during our match. That was a very good battle." @@ -2452,7 +2452,7 @@ "valerie": { "encounter": { "1": "Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. \n$Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. \n$The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong.", - "1_female": null + "1_female": "Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. \n$Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. \n$The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong." }, "victory": { "1": "I hope that you will find things worth smiling about tomorrow…" @@ -2500,7 +2500,7 @@ }, "victory": { "1": "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon.", - "1_female": null + "1_female": "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon." }, "defeat": { "1": "Too bad for you, I guess." @@ -2509,7 +2509,7 @@ "bede": { "encounter": { "1": "I suppose I should prove beyond doubt just how pathetic you are and how strong I am.", - "1_female": null + "1_female": "I suppose I should prove beyond doubt just how pathetic you are and how strong I am." }, "victory": { "1": "I see… Well, that's fine. I wasn't really trying all that hard anyway." @@ -2554,7 +2554,7 @@ "brassius": { "encounter": { "1": "I assume you are ready? Let our collaborative work of art begin!", - "1_female": null + "1_female": "I assume you are ready? Let our collaborative work of art begin!" }, "victory": { "1": "Ahhh…vant-garde!" @@ -2566,11 +2566,11 @@ "iono": { "encounter": { "1": "How're ya feelin' about this battle?\n$...\n$Let's get this show on the road! How strong is our challenger? \n$I 'unno! Let's find out together!", - "1_female": null + "1_female": "How're ya feelin' about this battle?\n$...\n$Let's get this show on the road! How strong is our challenger? \n$I 'unno! Let's find out together!" }, "victory": { "1": "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!", - "1_female": null + "1_female": "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!" }, "defeat": { "1": "Your eyeballs are MINE!" @@ -2593,7 +2593,7 @@ }, "victory": { "1": "You're cool, my friend—you move my SOUL!", - "1_female": null + "1_female": "You're cool, my friend—you move my SOUL!" }, "defeat": { "1": "Later, baby!" @@ -2627,9 +2627,9 @@ "nessa_elite": { "encounter": { "1": "The tides are turning in my favor. Ready to get swept away?", - "1_female": null, + "1_female": "The tides are turning in my favor. Ready to get swept away?", "2": "Let's make some waves with this battle! I hope you're prepared!", - "2_female": null + "2_female": "Let's make some waves with this battle! I hope you're prepared!" }, "victory": { "1": "You navigated those waters perfectly... Well done!", @@ -2657,7 +2657,7 @@ "allister_elite": { "encounter": { "1": "Shadows fall... Are you ready to face your fears?", - "1_female": null, + "1_female": "Shadows fall... Are you ready to face your fears?", "2": "Let's see if you can handle the darkness that I command." }, "victory": { @@ -2681,7 +2681,7 @@ "defeat": { "1": "Another storm weathered, another victory claimed! Well fought!", "2": "You got caught in my storm! Better luck next time!", - "2_female": null + "2_female": "You got caught in my storm! Better luck next time!" } }, "alder": { From 55e0d65ac8055b9b3fb6748289ae44796956b32c Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:23:25 -0700 Subject: [PATCH 002/108] [Refactor] Improvements on getOrder() (#3547) * Moved getOrder() into TurnStartPhase * Cleaned up bypass speed checks * Revert "Cleaned up bypass speed checks" This reverts commit 11150254f568b8575211693919c9c5deeb3d8dcd. * Added comments. * Fixed up some inconsistencies * changed isSameBracket check * changed isSameBracket check p2 * changed isSameBracket check p3 * changed isSameBracket check p3 * Fixed up conditionals + stall/M.m * Seems OK * Update battle-spec.ts * Updated tests to use new functions introduced. Less intuitive, but faster. * Update src/phases.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Moved getOrder() into TurnStartPhase * Cleaned up bypass speed checks * Revert "Cleaned up bypass speed checks" This reverts commit 11150254f568b8575211693919c9c5deeb3d8dcd. * Added comments. * Fixed up some inconsistencies * changed isSameBracket check * changed isSameBracket check p2 * changed isSameBracket check p3 * changed isSameBracket check p3 * Fixed up conditionals + stall/M.m * Seems OK * Update battle-spec.ts * Updated tests to use new functions introduced. Less intuitive, but faster. * Removed import * i hate git * Moved getOrder() into TurnStartPhase * Seems OK * missing import * Added Snooze's review * Added test fixes and removed unwanted edit. * fixed dynamax cannon test * typedocs fixes * Updating battle-order.test.ts * merge fixes * ughhh * Update src/test/abilities/mycelium_might.test.ts Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * tsdocs :) * Fixed tests * Update src/phases/turn-start-phase.ts Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * Update src/phases/turn-start-phase.ts Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * innerthunder's fixes * commas * Mocked stats instead of directly changing them --------- Co-authored-by: Frutescens Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> --- src/data/ability.ts | 4 +- src/phases/field-phase.ts | 35 +------ src/phases/turn-start-phase.ts | 110 ++++++++++++++++------ src/test/abilities/mycelium_might.test.ts | 51 +++++----- src/test/abilities/stall.test.ts | 48 +++++----- src/test/battle/battle-order.test.ts | 105 +++++++++++++-------- src/test/utils/gameManager.ts | 4 +- 7 files changed, 203 insertions(+), 154 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 8b7a7772efe..3d5b32f4ce8 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -5025,7 +5025,7 @@ export function initAbilities() { .attr(AlwaysHitAbAttr) .attr(DoubleBattleChanceAbAttr), new Ability(Abilities.STALL, 4) - .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.5), + .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.2), new Ability(Abilities.TECHNICIAN, 4) .attr(MovePowerBoostAbAttr, (user, target, move) => { const power = new Utils.NumberHolder(move.power); @@ -5713,7 +5713,7 @@ export function initAbilities() { .partial() // Healing not blocked by Heal Block .ignorable(), new Ability(Abilities.MYCELIUM_MIGHT, 9) - .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS, -0.5) + .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS, -0.2) .attr(PreventBypassSpeedChanceAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS) .attr(MoveAbilityBypassAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS), new Ability(Abilities.MINDS_EYE, 9) diff --git a/src/phases/field-phase.ts b/src/phases/field-phase.ts index a9622271f14..02d1f1395d3 100644 --- a/src/phases/field-phase.ts +++ b/src/phases/field-phase.ts @@ -1,42 +1,9 @@ -import { BattlerIndex } from "#app/battle.js"; -import { TrickRoomTag } from "#app/data/arena-tag.js"; -import { Stat } from "#app/enums/stat.js"; -import Pokemon from "#app/field/pokemon.js"; import { BattlePhase } from "./battle-phase"; -import * as Utils from "#app/utils.js"; +import Pokemon from "#app/field/pokemon"; type PokemonFunc = (pokemon: Pokemon) => void; export abstract class FieldPhase extends BattlePhase { - getOrder(): BattlerIndex[] { - const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; - const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; - - // We shuffle the list before sorting so speed ties produce random results - let orderedTargets: Pokemon[] = playerField.concat(enemyField); - // We seed it with the current turn to prevent an inconsistency where it - // was varying based on how long since you last reloaded - this.scene.executeWithSeedOffset(() => { - orderedTargets = Utils.randSeedShuffle(orderedTargets); - }, this.scene.currentBattle.turn, this.scene.waveSeed); - - orderedTargets.sort((a: Pokemon, b: Pokemon) => { - const aSpeed = a?.getBattleStat(Stat.SPD) || 0; - const bSpeed = b?.getBattleStat(Stat.SPD) || 0; - - return bSpeed - aSpeed; - }); - - const speedReversed = new Utils.BooleanHolder(false); - this.scene.arena.applyTags(TrickRoomTag, speedReversed); - - if (speedReversed.value) { - orderedTargets = orderedTargets.reverse(); - } - - return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); - } - executeForAll(func: PokemonFunc): void { const field = this.scene.getField(true).filter(p => p.summonData); field.forEach(pokemon => func(pokemon)); diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index e4064fc784a..b2545e9ee30 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -1,12 +1,12 @@ -import BattleScene from "#app/battle-scene.js"; -import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr, ChangeMovePriorityAbAttr } from "#app/data/ability.js"; -import { allMoves, applyMoveAttrs, IncrementMovePriorityAttr, MoveHeaderAttr } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Stat } from "#app/enums/stat.js"; -import { PokemonMove } from "#app/field/pokemon.js"; -import { BypassSpeedChanceModifier } from "#app/modifier/modifier.js"; -import { Command } from "#app/ui/command-ui-handler.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr, ChangeMovePriorityAbAttr } from "#app/data/ability"; +import { allMoves, applyMoveAttrs, IncrementMovePriorityAttr, MoveHeaderAttr } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import Pokemon, { PokemonMove } from "#app/field/pokemon"; +import { BypassSpeedChanceModifier } from "#app/modifier/modifier"; +import { Command } from "#app/ui/command-ui-handler"; +import * as Utils from "#app/utils"; import { AttemptCapturePhase } from "./attempt-capture-phase"; import { AttemptRunPhase } from "./attempt-run-phase"; import { BerryPhase } from "./berry-phase"; @@ -17,18 +17,59 @@ import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; import { SwitchSummonPhase } from "./switch-summon-phase"; import { TurnEndPhase } from "./turn-end-phase"; import { WeatherEffectPhase } from "./weather-effect-phase"; +import { BattlerIndex } from "#app/battle"; +import { TrickRoomTag } from "#app/data/arena-tag"; export class TurnStartPhase extends FieldPhase { constructor(scene: BattleScene) { super(scene); } - start() { - super.start(); + /** + * This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array. + * It also checks for Trick Room and reverses the array if it is present. + * @returns {@linkcode BattlerIndex[]} the battle indices of all pokemon on the field ordered by speed + */ + getSpeedOrder(): BattlerIndex[] { + const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; + const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; - const field = this.scene.getField(); - const order = this.getOrder(); + // We shuffle the list before sorting so speed ties produce random results + let orderedTargets: Pokemon[] = playerField.concat(enemyField); + // We seed it with the current turn to prevent an inconsistency where it + // was varying based on how long since you last reloaded + this.scene.executeWithSeedOffset(() => { + orderedTargets = Utils.randSeedShuffle(orderedTargets); + }, this.scene.currentBattle.turn, this.scene.waveSeed); + orderedTargets.sort((a: Pokemon, b: Pokemon) => { + const aSpeed = a?.getBattleStat(Stat.SPD) || 0; + const bSpeed = b?.getBattleStat(Stat.SPD) || 0; + + return bSpeed - aSpeed; + }); + + // Next, a check for Trick Room is applied. If Trick Room is present, the order is reversed. + const speedReversed = new Utils.BooleanHolder(false); + this.scene.arena.applyTags(TrickRoomTag, speedReversed); + + if (speedReversed.value) { + orderedTargets = orderedTargets.reverse(); + } + + return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER)); + } + + /** + * This takes the result of getSpeedOrder and applies priority / bypass speed attributes to it. + * This also considers the priority levels of various commands and changes the result of getSpeedOrder based on such. + * @returns {@linkcode BattlerIndex[]} the final sequence of commands for this turn + */ + getCommandOrder(): BattlerIndex[] { + let moveOrder = this.getSpeedOrder(); + // The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw + // The ability Mycelium Might disables Quick Claw's activation when using a status move + // This occurs before the main loop because of battles with more than two Pokemon const battlerBypassSpeed = {}; this.scene.getField(true).filter(p => p.summonData).map(p => { @@ -42,8 +83,9 @@ export class TurnStartPhase extends FieldPhase { battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; }); - const moveOrder = order.slice(0); - + // The function begins sorting orderedTargets based on command priority, move priority, and possible speed bypasses. + // Non-FIGHT commands (SWITCH, BALL, RUN) have a higher command priority and will always occur before any FIGHT commands. + moveOrder = moveOrder.slice(0); moveOrder.sort((a, b) => { const aCommand = this.scene.currentBattle.turnCommands[a]; const bCommand = this.scene.currentBattle.turnCommands[b]; @@ -55,37 +97,50 @@ export class TurnStartPhase extends FieldPhase { return -1; } } else if (aCommand?.command === Command.FIGHT) { - const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? - const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? + const aMove = allMoves[aCommand.move!.move]; + const bMove = allMoves[bCommand!.move!.move]; + // The game now considers priority and applies the relevant move and ability attributes const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, false, aMove, aPriority); //TODO: is the bang correct here? - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, false, bMove, bPriority); //TODO: is the bang correct here? + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, false, aMove, aPriority); + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, false, bMove, bPriority); + // The game now checks for differences in priority levels. + // If the moves share the same original priority bracket, it can check for differences in battlerBypassSpeed and return the result. + // This conditional is used to ensure that Quick Claw can still activate with abilities like Stall and Mycelium Might (attack moves only) + // Otherwise, the game returns the user of the move with the highest priority. + const isSameBracket = Math.ceil(aPriority.value) - Math.ceil(bPriority.value) === 0; if (aPriority.value !== bPriority.value) { - const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); - const hasSpeedDifference = battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value; - if (bracketDifference === 0 && hasSpeedDifference) { + if (isSameBracket && battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; } return aPriority.value < bPriority.value ? 1 : -1; } } + // If there is no difference between the move's calculated priorities, the game checks for differences in battlerBypassSpeed and returns the result. if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; } - const aIndex = order.indexOf(a); - const bIndex = order.indexOf(b); + const aIndex = moveOrder.indexOf(a); + const bIndex = moveOrder.indexOf(b); return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; }); + return moveOrder; + } + + start() { + super.start(); + + const field = this.scene.getField(); + const moveOrder = this.getCommandOrder(); let orderIndex = 0; @@ -150,10 +205,9 @@ export class TurnStartPhase extends FieldPhase { } } - this.scene.pushPhase(new WeatherEffectPhase(this.scene)); - for (const o of order) { + for (const o of moveOrder) { if (field[o].status && field[o].status.isPostTurn()) { this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o)); } diff --git a/src/test/abilities/mycelium_might.test.ts b/src/test/abilities/mycelium_might.test.ts index 83396f7950f..d5bea185f59 100644 --- a/src/test/abilities/mycelium_might.test.ts +++ b/src/test/abilities/mycelium_might.test.ts @@ -1,6 +1,6 @@ import { BattleStat } from "#app/data/battle-stat"; -import { MovePhase } from "#app/phases/move-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - describe("Abilities - Mycelium Might", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -35,7 +34,7 @@ describe("Abilities - Mycelium Might", () => { }); /** - * Bulbapedia References: + * References: * https://bulbapedia.bulbagarden.net/wiki/Mycelium_Might_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 @@ -44,22 +43,22 @@ describe("Abilities - Mycelium Might", () => { it("will move last in its priority bracket and ignore protective abilities", async () => { await game.startBattle([Species.REGIELEKI]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); + const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex(); const enemyIndex = enemyPokemon?.getBattlerIndex(); game.move.select(Moves.BABY_DOLL_EYES); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (without Mycelium Might) goes first despite having lower speed than the player Pokemon. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); - // The player Pokemon (with Mycelium Might) goes last despite having higher speed than the opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + expect(speedOrder).toEqual([playerIndex, enemyIndex]); + expect(commandOrder).toEqual([enemyIndex, playerIndex]); await game.phaseInterceptor.to(TurnEndPhase); + // Despite the opponent's ability (Clear Body), its attack stat is still reduced. expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); @@ -67,39 +66,41 @@ describe("Abilities - Mycelium Might", () => { game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([Species.REGIELEKI]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); + const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex(); const enemyIndex = enemyPokemon?.getBattlerIndex(); game.move.select(Moves.BABY_DOLL_EYES); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because its move is still within a higher priority bracket than its opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The enemy Pokemon goes second because its move is in a lower priority bracket. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + expect(speedOrder).toEqual([playerIndex, enemyIndex]); + expect(commandOrder).toEqual([playerIndex, enemyIndex]); await game.phaseInterceptor.to(TurnEndPhase); + // Despite the opponent's ability (Clear Body), its attack stat is still reduced. expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); it("will not affect non-status moves", async () => { await game.startBattle([Species.REGIELEKI]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.QUICK_ATTACK); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because it has a higher speed and did not use a status move. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The enemy Pokemon (without M.M.) goes second because its speed is lower. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + // This means that the commandOrder should be identical to the speedOrder + expect(speedOrder).toEqual([playerIndex, enemyIndex]); + expect(commandOrder).toEqual([playerIndex, enemyIndex]); }, 20000); }); diff --git a/src/test/abilities/stall.test.ts b/src/test/abilities/stall.test.ts index d8dbe9d0e06..7baf7c846f0 100644 --- a/src/test/abilities/stall.test.ts +++ b/src/test/abilities/stall.test.ts @@ -1,11 +1,10 @@ -import { MovePhase } from "#app/phases/move-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - +import { TurnStartPhase } from "#app/phases/turn-start-phase"; describe("Abilities - Stall", () => { let phaserGame: Phaser.Game; @@ -32,7 +31,7 @@ describe("Abilities - Stall", () => { }); /** - * Bulbapedia References: + * References: * https://bulbapedia.bulbagarden.net/wiki/Stall_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority **/ @@ -40,55 +39,56 @@ describe("Abilities - Stall", () => { it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { await game.startBattle([Species.SHUCKLE]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.QUICK_ATTACK); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (without Stall) goes first despite having lower speed than the opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (with Stall) goes last despite having higher speed than the player Pokemon. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + expect(speedOrder).toEqual([enemyIndex, playerIndex]); + expect(commandOrder).toEqual([playerIndex, enemyIndex]); }, 20000); it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => { await game.startBattle([Species.SHUCKLE]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (with Stall) goes first because its move is still within a higher priority bracket than its opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon goes second because its move is in a lower priority bracket. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + expect(speedOrder).toEqual([enemyIndex, playerIndex]); + expect(commandOrder).toEqual([enemyIndex, playerIndex]); }, 20000); it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async () => { game.override.ability(Abilities.STALL); await game.startBattle([Species.SHUCKLE]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); + const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(MovePhase, false); - // The opponent Pokemon (with Stall) goes first because it has a higher speed. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + await game.phaseInterceptor.to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); + // The opponent Pokemon (with Stall) goes first because it has a higher speed. // The player Pokemon (with Stall) goes second because its speed is lower. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + expect(speedOrder).toEqual([enemyIndex, playerIndex]); + expect(commandOrder).toEqual([enemyIndex, playerIndex]); }, 20000); }); diff --git a/src/test/battle/battle-order.test.ts b/src/test/battle/battle-order.test.ts index 0129ecad254..e19168962dc 100644 --- a/src/test/battle/battle-order.test.ts +++ b/src/test/battle/battle-order.test.ts @@ -1,4 +1,3 @@ -import { Stat } from "#app/data/pokemon-stat"; import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { SelectTargetPhase } from "#app/phases/select-target-phase"; import { TurnStartPhase } from "#app/phases/turn-start-phase"; @@ -7,8 +6,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Battle order", () => { let phaserGame: Phaser.Game; @@ -37,30 +35,42 @@ describe("Battle order", () => { await game.startBattle([ Species.BULBASAUR, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 50; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set playerPokemon's speed to 50 + vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); + + const playerPokemonIndex = playerPokemon.getBattlerIndex(); + const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order[0]).toBe(2); - expect(order[1]).toBe(0); + const order = phase.getCommandOrder(); + expect(order[0]).toBe(enemyPokemonIndex); + expect(order[1]).toBe(playerPokemonIndex); }, 20000); it("Player faster than opponent 150 vs 50", async () => { await game.startBattle([ Species.BULBASAUR, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 150; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 50; + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set playerPokemon's speed to 150 + vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50 game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); + + const playerPokemonIndex = playerPokemon.getBattlerIndex(); + const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order[0]).toBe(0); - expect(order[1]).toBe(2); + const order = phase.getCommandOrder(); + expect(order[0]).toBe(playerPokemonIndex); + expect(order[1]).toBe(enemyPokemonIndex); }, 20000); it("double - both opponents faster than player 50/50 vs 150/150", async () => { @@ -69,20 +79,25 @@ describe("Battle order", () => { Species.BULBASAUR, Species.BLASTOISE, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 50; - game.scene.getParty()[1].stats[Stat.SPD] = 50; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; - game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + + playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50])); // set both playerPokemons' speed to 50 + enemyPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150])); // set both enemyPokemons' speed to 150 + const playerIndices = playerPokemon.map(p => p?.getBattlerIndex()); + const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex()); game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(2)); - expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(3)); - expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(2)); - expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(3)); + const order = phase.getCommandOrder(); + expect(order.slice(0, 2).includes(enemyIndices[0])).toBe(true); + expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); + expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true); + expect(order.slice(2, 4).includes(playerIndices[1])).toBe(true); }, 20000); it("double - speed tie except 1 - 100/100 vs 100/150", async () => { @@ -91,19 +106,25 @@ describe("Battle order", () => { Species.BULBASAUR, Species.BLASTOISE, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 100; - game.scene.getParty()[1].stats[Stat.SPD] = 100; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; - game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100])); //set both playerPokemons' speed to 100 + vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set enemyPokemon's speed to 100 + vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 + const playerIndices = playerPokemon.map(p => p?.getBattlerIndex()); + const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex()); game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(0)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(1)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(2)); + const order = phase.getCommandOrder(); + expect(order[0]).toBe(enemyIndices[1]); + expect(order.slice(1, 4).includes(enemyIndices[0])).toBe(true); + expect(order.slice(1, 4).includes(playerIndices[0])).toBe(true); + expect(order.slice(1, 4).includes(playerIndices[1])).toBe(true); }, 20000); it("double - speed tie 100/150 vs 100/150", async () => { @@ -112,19 +133,25 @@ describe("Battle order", () => { Species.BULBASAUR, Species.BLASTOISE, ]); - game.scene.getParty()[0].stats[Stat.SPD] = 100; - game.scene.getParty()[1].stats[Stat.SPD] = 150; - game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; - game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set one playerPokemon's speed to 100 + vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other playerPokemon's speed to 150 + vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set one enemyPokemon's speed to 100 + vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other enemyPokemon's speed to 150 + const playerIndices = playerPokemon.map(p => p?.getBattlerIndex()); + const enemyIndices = enemyPokemon.map(p => p?.getBattlerIndex()); game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); - expect(order.indexOf(1)).toBeLessThan(order.indexOf(0)); - expect(order.indexOf(1)).toBeLessThan(order.indexOf(2)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(0)); - expect(order.indexOf(3)).toBeLessThan(order.indexOf(2)); + const order = phase.getCommandOrder(); + expect(order.slice(0, 2).includes(playerIndices[1])).toBe(true); + expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); + expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true); + expect(order.slice(2, 4).includes(enemyIndices[0])).toBe(true); }, 20000); }); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 60d07065090..5818244db8f 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -381,7 +381,7 @@ export default class GameManager { } /** - * Intercepts `TurnStartPhase` and mocks the getOrder's return value {@linkcode TurnStartPhase.getOrder} + * Intercepts `TurnStartPhase` and mocks the getSpeedOrder's return value {@linkcode TurnStartPhase.getSpeedOrder} * Used to modify the turn order. * @param {BattlerIndex[]} order The turn order to set * @example @@ -392,7 +392,7 @@ export default class GameManager { async setTurnOrder(order: BattlerIndex[]): Promise { await this.phaseInterceptor.to(TurnStartPhase, false); - vi.spyOn(this.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue(order); + vi.spyOn(this.scene.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order); } /** From 335d32e0d7afd9c4baab6eb3c837bf9b704647f1 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Sun, 1 Sep 2024 18:24:37 -0400 Subject: [PATCH 003/108] [UI] [QoL] [Enhancement] Exclude redundant species from certain filters in starter select (#3910) * Exclude species without HA from HA filters in starter select * Remove candyless starters from passive/cost reduction filters --- src/ui/dropdown.ts | 8 +++++++ src/ui/starter-select-ui-handler.ts | 33 ++++++++++++++++------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/ui/dropdown.ts b/src/ui/dropdown.ts index 1fef7259108..08d55b03cdb 100644 --- a/src/ui/dropdown.ts +++ b/src/ui/dropdown.ts @@ -23,6 +23,14 @@ export enum SortDirection { DESC = 1 } +export enum SortCriteria { + NUMBER = 0, + COST = 1, + CANDY = 2, + IV = 3, + NAME = 4 +} + export class DropDownLabel { public state: DropDownState; public text: string; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 267a62104e3..ff76a467d25 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -39,12 +39,13 @@ import { Species } from "#enums/species"; import {Button} from "#enums/buttons"; import { EggSourceType } from "#app/enums/egg-source-types.js"; import AwaitableUiHandler from "./awaitable-ui-handler"; -import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType } from "./dropdown"; +import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "./dropdown"; import { StarterContainer } from "./starter-container"; import { DropDownColumn, FilterBar } from "./filter-bar"; import { ScrollBar } from "./scroll-bar"; import { SelectChallengePhase } from "#app/phases/select-challenge-phase.js"; import { TitlePhase } from "#app/phases/title-phase.js"; +import { Abilities } from "#app/enums/abilities"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -501,11 +502,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // sort filter const sortOptions = [ - new DropDownOption(this.scene, 0, new DropDownLabel(i18next.t("filterBar:sortByNumber"), undefined, DropDownState.ON)), - new DropDownOption(this.scene, 1, new DropDownLabel(i18next.t("filterBar:sortByCost"))), - new DropDownOption(this.scene, 2, new DropDownLabel(i18next.t("filterBar:sortByCandies"))), - new DropDownOption(this.scene, 3, new DropDownLabel(i18next.t("filterBar:sortByIVs"))), - new DropDownOption(this.scene, 4, new DropDownLabel(i18next.t("filterBar:sortByName"))) + new DropDownOption(this.scene, SortCriteria.NUMBER, new DropDownLabel(i18next.t("filterBar:sortByNumber"), undefined, DropDownState.ON)), + new DropDownOption(this.scene, SortCriteria.COST, new DropDownLabel(i18next.t("filterBar:sortByCost"))), + new DropDownOption(this.scene, SortCriteria.CANDY, new DropDownLabel(i18next.t("filterBar:sortByCandies"))), + new DropDownOption(this.scene, SortCriteria.IV, new DropDownLabel(i18next.t("filterBar:sortByIVs"))), + new DropDownOption(this.scene, SortCriteria.NAME, new DropDownLabel(i18next.t("filterBar:sortByName"))) ]; this.filterBar.addFilter(DropDownColumn.SORT, i18next.t("filterBar:sortFilter"), new DropDown(this.scene, 0, 0, sortOptions, this.updateStarters, DropDownType.SINGLE)); this.filterBarContainer.add(this.filterBar); @@ -2363,6 +2364,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // First, ensure you have the caught attributes for the species else default to bigint 0 const caughtAttr = this.scene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0); const starterData = this.scene.gameData.starterData[container.species.speciesId]; + const isStarterProgressable = speciesEggMoves.hasOwnProperty(container.species.speciesId); // Gen filter const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(container.species.generation); @@ -2398,7 +2400,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.ON) { return isPassiveUnlocked; } else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.EXCLUDE) { - return !isPassiveUnlocked; + return isStarterProgressable && !isPassiveUnlocked; } else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.UNLOCKABLE) { return isPassiveUnlockable; } else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.OFF) { @@ -2413,7 +2415,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.ON) { return isCostReduced; } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.EXCLUDE) { - return !isCostReduced; + return isStarterProgressable && !isCostReduced; } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.UNLOCKABLE) { return isCostReductionUnlockable; } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.OFF) { @@ -2450,12 +2452,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }); // HA Filter + const speciesHasHiddenAbility = container.species.abilityHidden !== container.species.ability1 && container.species.abilityHidden !== Abilities.NONE; const hasHA = starterData.abilityAttr & AbilityAttr.ABILITY_HIDDEN; const fitsHA = this.filterBar.getVals(DropDownColumn.MISC).some(misc => { if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.ON) { return hasHA; } else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.EXCLUDE) { - return !hasHA; + return speciesHasHiddenAbility && !hasHA; } else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.OFF) { return true; } @@ -2467,7 +2470,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (misc.val === "EGG" && misc.state === DropDownState.ON) { return isEggPurchasable; } else if (misc.val === "EGG" && misc.state === DropDownState.EXCLUDE) { - return !isEggPurchasable; + return isStarterProgressable && !isEggPurchasable; } else if (misc.val === "EGG" && misc.state === DropDownState.OFF) { return true; } @@ -2498,19 +2501,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler { switch (sort.val) { default: break; - case 0: + case SortCriteria.NUMBER: return (a.species.speciesId - b.species.speciesId) * -sort.dir; - case 1: + case SortCriteria.COST: return (a.cost - b.cost) * -sort.dir; - case 2: + case SortCriteria.CANDY: const candyCountA = this.scene.gameData.starterData[a.species.speciesId].candyCount; const candyCountB = this.scene.gameData.starterData[b.species.speciesId].candyCount; return (candyCountA - candyCountB) * -sort.dir; - case 3: + case SortCriteria.IV: const avgIVsA = this.scene.gameData.dexData[a.species.speciesId].ivs.reduce((a, b) => a + b, 0) / this.scene.gameData.dexData[a.species.speciesId].ivs.length; const avgIVsB = this.scene.gameData.dexData[b.species.speciesId].ivs.reduce((a, b) => a + b, 0) / this.scene.gameData.dexData[b.species.speciesId].ivs.length; return (avgIVsA - avgIVsB) * -sort.dir; - case 4: + case SortCriteria.NAME: return a.species.name.localeCompare(b.species.name) * -sort.dir; } return 0; From a41133a55a60d84d73faca460d9b9ec8e57ce448 Mon Sep 17 00:00:00 2001 From: cadi Date: Mon, 2 Sep 2024 07:26:45 +0900 Subject: [PATCH 004/108] blocking inputs while showing texts to prevent moveing cursor (#2371) Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/ui/party-ui-handler.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index e7c4069c16e..a61037548e6 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -166,6 +166,8 @@ export default class PartyUiHandler extends MessageUiHandler { private iconAnimHandler: PokemonIconAnimHandler; + private blockInput: boolean; + private static FilterAll = (_pokemon: PlayerPokemon) => null; public static FilterNonFainted = (pokemon: PlayerPokemon) => { @@ -317,7 +319,7 @@ export default class PartyUiHandler extends MessageUiHandler { processInput(button: Button): boolean { const ui = this.getUi(); - if (this.pendingPrompt) { + if (this.pendingPrompt || this.blockInput) { return false; } @@ -485,7 +487,9 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); if (this.cursor >= this.scene.currentBattle.getBattlerCount() || !pokemon.isAllowedInBattle()) { + this.blockInput = true; this.showText(i18next.t("partyUiHandler:releaseConfirmation", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.blockInput = false; ui.setModeWithoutClear(Mode.CONFIRM, () => { ui.setMode(Mode.PARTY); this.doRelease(this.cursor); From 7755f798fd0f88f5fffc2451e558882ecf27cd93 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Sun, 1 Sep 2024 18:21:11 -0700 Subject: [PATCH 005/108] [Ability] Implement Tera Shell (#3856) * Implement Tera Shell * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> * Add comments and fixed damage condition to `applyPreDefend` * Fix speed tie breaking things in tera shell test * Change deprecated `startBattle` calls --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> --- src/data/ability.ts | 47 ++++++++++- src/field/pokemon.ts | 7 +- src/locales/en/ability-trigger.json | 1 + src/test/abilities/tera_shell.test.ts | 111 ++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 src/test/abilities/tera_shell.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index 3d5b32f4ce8..e40a88a04b2 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -8,7 +8,7 @@ import { Weather, WeatherType } from "./weather"; import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags"; import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { Gender } from "./gender"; -import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr } from "./move"; +import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { Stat, getStatName } from "./pokemon-stat"; import { BerryModifier, PokemonHeldItemModifier } from "../modifier/modifier"; @@ -475,6 +475,47 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } } +/** + * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Tera_Shell_(Ability) | Tera Shell} + * When the source is at full HP, incoming attacks will have a maximum 0.5x type effectiveness multiplier. + * @extends PreDefendAbAttr + */ +export class FullHpResistTypeAbAttr extends PreDefendAbAttr { + /** + * Reduces a type multiplier to 0.5 if the source is at full HP. + * @param pokemon {@linkcode Pokemon} the Pokemon with this ability + * @param passive n/a + * @param simulated n/a (this doesn't change game state) + * @param attacker n/a + * @param move {@linkcode Move} the move being used on the source + * @param cancelled n/a + * @param args `[0]` a container for the move's current type effectiveness multiplier + * @returns `true` if the move's effectiveness is reduced; `false` otherwise + */ + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { + const typeMultiplier = args[0]; + if (!(typeMultiplier && typeMultiplier instanceof Utils.NumberHolder)) { + return false; + } + + if (move && move.hasAttr(FixedDamageAttr)) { + return false; + } + + if (pokemon.isFullHp() && typeMultiplier.value > 0.5) { + typeMultiplier.value = 0.5; + return true; + } + return false; + } + + getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + return i18next.t("abilityTriggers:fullHpResistType", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) + }); + } +} + export class PostDefendAbAttr extends AbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; @@ -5761,10 +5802,10 @@ export function initAbilities() { .attr(NoTransformAbilityAbAttr) .attr(NoFusionAbilityAbAttr), new Ability(Abilities.TERA_SHELL, 9) + .attr(FullHpResistTypeAbAttr) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) - .ignorable() - .unimplemented(), + .ignorable(), new Ability(Abilities.TERAFORM_ZERO, 9) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 9fcbdd8dc6c..457ff874095 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -22,7 +22,7 @@ import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoo import { WeatherType } from "../data/weather"; import { TempBattleStat } from "../data/temp-battle-stat"; import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr } from "../data/ability"; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr } from "../data/ability"; import PokemonData from "../system/pokemon-data"; import { BattlerIndex } from "../battle"; import { Mode } from "../ui/ui"; @@ -1276,6 +1276,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } + // Apply Tera Shell's effect to attacks after all immunities are accounted for + if (!ignoreAbility && move.category !== MoveCategory.STATUS) { + applyPreDefendAbAttrs(FullHpResistTypeAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier); + } + return (!cancelledHolder.value ? typeMultiplier.value : 0) as TypeDamageMultiplier; } diff --git a/src/locales/en/ability-trigger.json b/src/locales/en/ability-trigger.json index 307ab70b85c..4f1d4dac766 100644 --- a/src/locales/en/ability-trigger.json +++ b/src/locales/en/ability-trigger.json @@ -12,6 +12,7 @@ "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", + "fullHpResistType": "{{pokemonNameWithAffix}} made its shell gleam!\nIt's distorting type matchups!", "moveImmunity": "It doesn't affect {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} sucked up the liquid ooze!", "postDefendTypeChange": "{{pokemonNameWithAffix}}'s {{abilityName}}\nmade it the {{typeName}} type!", diff --git a/src/test/abilities/tera_shell.test.ts b/src/test/abilities/tera_shell.test.ts new file mode 100644 index 00000000000..f9cb2935619 --- /dev/null +++ b/src/test/abilities/tera_shell.test.ts @@ -0,0 +1,111 @@ +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { HitResult } from "#app/field/pokemon.js"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const TIMEOUT = 10 * 1000; // 10 second timeout + +describe("Abilities - Tera Shell", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .ability(Abilities.TERA_SHELL) + .moveset([Moves.SPLASH]) + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.INSOMNIA) + .enemyMoveset(Array(4).fill(Moves.MACH_PUNCH)) + .startingLevel(100) + .enemyLevel(100); + }); + + it( + "should change the effectiveness of non-resisted attacks when the source is at full HP", + async () => { + await game.classicMode.startBattle([Species.SNORLAX]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "getMoveEffectiveness"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0.5); + + await game.toNextTurn(); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(2); + }, TIMEOUT + ); + + it( + "should not override type immunities", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); + + await game.classicMode.startBattle([Species.SNORLAX]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "getMoveEffectiveness"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0); + }, TIMEOUT + ); + + it( + "should not override type multipliers less than 0.5x", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.QUICK_ATTACK)); + + await game.classicMode.startBattle([Species.AGGRON]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "getMoveEffectiveness"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(playerPokemon.getMoveEffectiveness).toHaveLastReturnedWith(0.25); + }, TIMEOUT + ); + + it( + "should not affect the effectiveness of fixed-damage moves", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.DRAGON_RAGE)); + + await game.classicMode.startBattle([Species.CHARIZARD]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "apply"); + + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("BerryPhase", false); + expect(playerPokemon.apply).toHaveLastReturnedWith(HitResult.EFFECTIVE); + expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp() - 40); + }, TIMEOUT + ); +}); From 97e3250f62d956268a0076ade875e5bb9cd38af1 Mon Sep 17 00:00:00 2001 From: chaosgrimmon <31082757+chaosgrimmon@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:31:25 -0400 Subject: [PATCH 006/108] [Sprite] Chien-Pao sprite cleanup (#3961) * Delete public/images/pokemon/exp/shiny/1002b.png * Delete public/images/pokemon/exp/shiny/1002s.png * Delete public/images/pokemon/exp/shiny/1002sb.png --- public/images/pokemon/exp/shiny/1002b.png | Bin 3018 -> 0 bytes public/images/pokemon/exp/shiny/1002s.png | Bin 4517 -> 0 bytes public/images/pokemon/exp/shiny/1002sb.png | Bin 2924 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 public/images/pokemon/exp/shiny/1002b.png delete mode 100644 public/images/pokemon/exp/shiny/1002s.png delete mode 100644 public/images/pokemon/exp/shiny/1002sb.png diff --git a/public/images/pokemon/exp/shiny/1002b.png b/public/images/pokemon/exp/shiny/1002b.png deleted file mode 100644 index 85dfb1c4bd6b6b3fb8312e71372bf37f6758c24e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3018 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 diff --git a/public/images/pokemon/exp/shiny/1002s.png b/public/images/pokemon/exp/shiny/1002s.png deleted file mode 100644 index 835b3dcd73bde83d1eb1492aec1c6bd61a6a9608..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4517 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 diff --git a/public/images/pokemon/exp/shiny/1002sb.png b/public/images/pokemon/exp/shiny/1002sb.png deleted file mode 100644 index f87e2fc42397c82c99e00917b9c5dbd2e973ec67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2924 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 L+Y{<>-q-&N5*a0} From 3bcee779e296be330ba3c6168ff462d8f7f38d23 Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:39:12 +0800 Subject: [PATCH 007/108] [Move] Fully implement dragon cheer (#3959) --- src/data/battler-tags.ts | 21 +++++++++++++++++++++ src/data/move.ts | 5 ++--- src/enums/battler-tag-type.ts | 3 ++- src/field/pokemon.ts | 13 ++++++++++--- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 8c05d296e76..38db36e86f6 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1507,6 +1507,25 @@ export class CritBoostTag extends BattlerTag { } } +/** + * Tag for the effects of Dragon Cheer, which boosts the critical hit ratio of the user's allies. + * @extends {CritBoostTag} + */ +export class DragonCheerTag extends CritBoostTag { + /** The types of the user's ally when the tag is added */ + public typesOnAdd: Type[]; + + constructor() { + super(BattlerTagType.CRIT_BOOST, Moves.DRAGON_CHEER); + } + + onAdd(pokemon: Pokemon): void { + super.onAdd(pokemon); + + this.typesOnAdd = pokemon.getTypes(true); + } +} + export class SaltCuredTag extends BattlerTag { private sourceIndex: number; @@ -1923,6 +1942,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new TypeBoostTag(tagType, sourceMove, Type.FIRE, 1.5, false); case BattlerTagType.CRIT_BOOST: return new CritBoostTag(tagType, sourceMove); + case BattlerTagType.DRAGON_CHEER: + return new DragonCheerTag(); case BattlerTagType.ALWAYS_CRIT: case BattlerTagType.IGNORE_ACCURACY: return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove); diff --git a/src/data/move.ts b/src/data/move.ts index d50dc7e2074..bbc33241454 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -9143,9 +9143,8 @@ export function initMoves() { new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9) .attr(OpponentHighHpPowerAttr, 100), new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9) - .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true) - .target(MoveTarget.NEAR_ALLY) - .partial(), + .attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true) + .target(MoveTarget.NEAR_ALLY), new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) .soundBased() .partial(), diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index b133b442801..73580a107b2 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -69,5 +69,6 @@ export enum BattlerTagType { GULP_MISSILE_ARROKUDA = "GULP_MISSILE_ARROKUDA", GULP_MISSILE_PIKACHU = "GULP_MISSILE_PIKACHU", BEAK_BLAST_CHARGING = "BEAK_BLAST_CHARGING", - SHELL_TRAP = "SHELL_TRAP" + SHELL_TRAP = "SHELL_TRAP", + DRAGON_CHEER = "DRAGON_CHEER", } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 457ff874095..00d5f5d3dd2 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -18,7 +18,7 @@ import { Status, StatusEffect, getRandomStatus } from "../data/status-effect"; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions"; import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms"; import { BattleStat } from "../data/battle-stat"; -import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag } from "../data/battler-tags"; +import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag, DragonCheerTag, CritBoostTag } from "../data/battler-tags"; import { WeatherType } from "../data/weather"; import { TempBattleStat } from "../data/temp-battle-stat"; import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag"; @@ -2069,9 +2069,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { critLevel.value += 1; } } - if (source.getTag(BattlerTagType.CRIT_BOOST)) { - critLevel.value += 2; + + const critBoostTag = source.getTag(CritBoostTag); + if (critBoostTag) { + if (critBoostTag instanceof DragonCheerTag) { + critLevel.value += critBoostTag.typesOnAdd.includes(Type.DRAGON) ? 2 : 1; + } else { + critLevel.value += 2; + } } + console.log(`crit stage: +${critLevel.value}`); const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))]; isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance); From 64368b62bc983ad6a475a021cfd22b372ab274ee Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:39:42 +0800 Subject: [PATCH 008/108] [Ability] Add form change support for Flower Gift (#3941) * add form change support for flower gift * fix nits --- src/data/ability.ts | 34 ++++-- src/data/move.ts | 4 +- src/data/pokemon-forms.ts | 9 +- src/field/arena.ts | 10 +- src/test/abilities/flower_gift.test.ts | 154 +++++++++++++++++++++++++ 5 files changed, 193 insertions(+), 18 deletions(-) create mode 100644 src/test/abilities/flower_gift.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index e40a88a04b2..4d3d32e22fa 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2428,7 +2428,7 @@ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAtt /** * Triggers weather-based form change when summoned into an active weather. - * Used by Forecast. + * Used by Forecast and Flower Gift. * @extends PostSummonAbAttr */ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { @@ -2451,7 +2451,10 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { * @returns whether the form change was triggered */ applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST) { + const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); + const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { if (simulated) { return simulated; } @@ -3124,37 +3127,41 @@ export class PostWeatherChangeAbAttr extends AbAttr { /** * Triggers weather-based form change when weather changes. - * Used by Forecast. + * Used by Forecast and Flower Gift. * @extends PostWeatherChangeAbAttr */ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { private ability: Abilities; + private formRevertingWeathers: WeatherType[]; - constructor(ability: Abilities) { + constructor(ability: Abilities, formRevertingWeathers: WeatherType[]) { super(false); this.ability = ability; + this.formRevertingWeathers = formRevertingWeathers; } /** * Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the * weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} - * @param {Pokemon} pokemon the Pokemon that changed the weather + * @param {Pokemon} pokemon the Pokemon with this ability * @param passive n/a * @param weather n/a * @param args n/a * @returns whether the form change was triggered */ applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { - if (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST) { + const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST); + const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { if (simulated) { return simulated; } - const formRevertingWeathers: WeatherType[] = [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]; const weatherType = pokemon.scene.arena.weather?.weatherType; - if (weatherType && formRevertingWeathers.includes(weatherType)) { + if (weatherType && this.formRevertingWeathers.includes(weatherType)) { pokemon.scene.arena.triggerWeatherBasedFormChangesToNormal(); } else { pokemon.scene.arena.triggerWeatherBasedFormChanges(); @@ -4744,7 +4751,8 @@ function setAbilityRevealed(pokemon: Pokemon): void { */ function getPokemonWithWeatherBasedForms(scene: BattleScene) { return scene.getField(true).filter(p => - p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM + (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM) + || (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM) ); } @@ -4943,7 +4951,7 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(NoFusionAbilityAbAttr) .attr(PostSummonFormChangeByWeatherAbAttr, Abilities.FORECAST) - .attr(PostWeatherChangeFormChangeAbAttr, Abilities.FORECAST), + .attr(PostWeatherChangeFormChangeAbAttr, Abilities.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]), new Ability(Abilities.STICKY_HOLD, 3) .attr(BlockItemTheftAbAttr) .bypassFaint() @@ -5136,8 +5144,10 @@ export function initAbilities() { .conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.SPDEF, 1.5) .attr(UncopiableAbilityAbAttr) .attr(NoFusionAbilityAbAttr) - .ignorable() - .partial(), + .attr(PostSummonFormChangeByWeatherAbAttr, Abilities.FLOWER_GIFT) + .attr(PostWeatherChangeFormChangeAbAttr, Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]) + .partial() // Should also boosts stats of ally + .ignorable(), new Ability(Abilities.BAD_DREAMS, 4) .attr(PostTurnHurtIfSleepingAbAttr), new Ability(Abilities.PICKPOCKET, 5) diff --git a/src/data/move.ts b/src/data/move.ts index bbc33241454..2ffa8a36de9 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5888,9 +5888,9 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr { target.summonData.ability = tempAbilityId; user.scene.queueMessage(i18next.t("moveTriggers:swappedAbilitiesWithTarget", {pokemonName: getPokemonNameWithAffix(user)})); - // Swaps Forecast from Castform + // Swaps Forecast/Flower Gift from Castform/Cherrim user.scene.arena.triggerWeatherBasedFormChangesToNormal(); - // Swaps Forecast to Castform (edge case) + // Swaps Forecast/Flower Gift to Castform/Cherrim (edge case) user.scene.arena.triggerWeatherBasedFormChanges(); return true; diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index e4417f8e8bb..1420d8800aa 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -359,7 +359,7 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger { /** * Class used for triggering form changes based on weather. - * Used by Castform. + * Used by Castform and Cherrim. * @extends SpeciesFormChangeTrigger */ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger { @@ -392,7 +392,7 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger { /** * Class used for reverting to the original form when the weather runs out * or when the user loses the ability/is suppressed. - * Used by Castform. + * Used by Castform and Cherrim. * @extends SpeciesFormChangeTrigger */ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChangeTrigger { @@ -930,6 +930,11 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeActiveTrigger(), true), new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeActiveTrigger(), true), ], + [Species.CHERRIM]: [ + new SpeciesFormChange(Species.CHERRIM, "overcast", "sunshine", new SpeciesFormChangeWeatherTrigger(Abilities.FLOWER_GIFT, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]), true), + new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]), true), + new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeActiveTrigger(), true), + ], }; export function initPokemonForms() { diff --git a/src/field/arena.ts b/src/field/arena.ts index 7622b9a014f..e8defbd1a8e 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -339,7 +339,10 @@ export class Arena { */ triggerWeatherBasedFormChanges(): void { this.scene.getField(true).forEach( p => { - if (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM) { + const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM); + const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { new ShowAbilityPhase(this.scene, p.getBattlerIndex()); this.scene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger); } @@ -351,7 +354,10 @@ export class Arena { */ triggerWeatherBasedFormChangesToNormal(): void { this.scene.getField(true).forEach( p => { - if (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM) { + const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM); + const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM); + + if (isCastformWithForecast || isCherrimWithFlowerGift) { new ShowAbilityPhase(this.scene, p.getBattlerIndex()); return this.scene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger); } diff --git a/src/test/abilities/flower_gift.test.ts b/src/test/abilities/flower_gift.test.ts new file mode 100644 index 00000000000..f8c1141386d --- /dev/null +++ b/src/test/abilities/flower_gift.test.ts @@ -0,0 +1,154 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import { WeatherType } from "#app/enums/weather-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Flower Gift", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const OVERCAST_FORM = 0; + const SUNSHINE_FORM = 1; + + /** + * Tests reverting to normal form when Cloud Nine/Air Lock is active on the field + * @param {GameManager} game The game manager instance + * @param {Abilities} ability The ability that is active on the field + */ + const testRevertFormAgainstAbility = async (game: GameManager, ability: Abilities) => { + game.override.starterForms({ [Species.CASTFORM]: SUNSHINE_FORM }).enemyAbility(ability); + await game.classicMode.startBattle([Species.CASTFORM]); + + game.move.select(Moves.SPLASH); + + expect(game.scene.getPlayerPokemon()?.formIndex).toBe(OVERCAST_FORM); + }; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.SPLASH, Moves.RAIN_DANCE, Moves.SUNNY_DAY, Moves.SKILL_SWAP]) + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(SPLASH_ONLY) + .enemyAbility(Abilities.BALL_FETCH); + }); + + // TODO: Uncomment expect statements when the ability is implemented - currently does not increase stats of allies + it("increases the Attack and Special Defense stats of the Pokémon with this Ability and its allies by 1.5× during Harsh Sunlight", async () => { + game.override.battleType("double"); + await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]); + + const [ cherrim ] = game.scene.getPlayerField(); + const cherrimAtkStat = cherrim.getBattleStat(Stat.ATK); + const cherrimSpDefStat = cherrim.getBattleStat(Stat.SPDEF); + + // const magikarpAtkStat = magikarp.getBattleStat(Stat.ATK);; + // const magikarpSpDefStat = magikarp.getBattleStat(Stat.SPDEF); + + game.move.select(Moves.SUNNY_DAY, 0); + game.move.select(Moves.SPLASH, 1); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + expect(cherrim.getBattleStat(Stat.ATK)).toBe(Math.floor(cherrimAtkStat * 1.5)); + expect(cherrim.getBattleStat(Stat.SPDEF)).toBe(Math.floor(cherrimSpDefStat * 1.5)); + // expect(magikarp.getBattleStat(Stat.ATK)).toBe(Math.floor(magikarpAtkStat * 1.5)); + // expect(magikarp.getBattleStat(Stat.SPDEF)).toBe(Math.floor(magikarpSpDefStat * 1.5)); + }); + + it("changes the Pokemon's form during Harsh Sunlight", async () => { + game.override.weather(WeatherType.HARSH_SUN); + await game.classicMode.startBattle([Species.CHERRIM]); + + const cherrim = game.scene.getPlayerPokemon()!; + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + game.move.select(Moves.SPLASH); + }); + + it("reverts to Overcast Form if a Pokémon on the field has Air Lock", async () => { + await testRevertFormAgainstAbility(game, Abilities.AIR_LOCK); + }); + + it("reverts to Overcast Form if a Pokémon on the field has Cloud Nine", async () => { + await testRevertFormAgainstAbility(game, Abilities.CLOUD_NINE); + }); + + it("reverts to Overcast Form when the Pokémon loses Flower Gift, changes form under Harsh Sunlight/Sunny when it regains it", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.SKILL_SWAP)).weather(WeatherType.HARSH_SUN); + + await game.classicMode.startBattle([Species.CHERRIM]); + + const cherrim = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.SKILL_SWAP); + + await game.phaseInterceptor.to("TurnStartPhase"); + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(cherrim.formIndex).toBe(OVERCAST_FORM); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + }); + + it("reverts to Overcast Form when the Flower Gift is suppressed, changes form under Harsh Sunlight/Sunny when it regains it", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)).weather(WeatherType.HARSH_SUN); + + await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]); + + const cherrim = game.scene.getPlayerPokemon()!; + + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(cherrim.summonData.abilitySuppressed).toBe(true); + expect(cherrim.formIndex).toBe(OVERCAST_FORM); + + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("MovePhase"); + + expect(cherrim.summonData.abilitySuppressed).toBe(false); + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + }); + + it("should be in Overcast Form after the user is switched out", async () => { + game.override.weather(WeatherType.SUNNY); + + await game.classicMode.startBattle([Species.CASTFORM, Species.MAGIKARP]); + const cherrim = game.scene.getPlayerPokemon()!; + + expect(cherrim.formIndex).toBe(SUNSHINE_FORM); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + expect(cherrim.formIndex).toBe(OVERCAST_FORM); + }); +}); From 0c28da75b4a57236fae46e2ade4a95cbc8d09f02 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:42:23 -0700 Subject: [PATCH 009/108] [Bug] Fix Opponent pokemon sprite disappears after using Roar and Dragon Tail #481 (#3927) --- src/data/battle-anims.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index a2f6e41f4ae..da4e7f6a33b 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -788,10 +788,10 @@ export abstract class BattleAnim { targetSprite.pipelineData["tone"] = [ 0.0, 0.0, 0.0, 0.0 ]; targetSprite.setAngle(0); if (!this.isHideUser() && userSprite) { - userSprite.setVisible(true); + this.user?.getSprite().setVisible(true); // using this.user to fix context loss due to isOppAnim swap (#481) } if (!this.isHideTarget() && (targetSprite !== userSprite || !this.isHideUser())) { - targetSprite.setVisible(true); + this.target?.getSprite().setVisible(true); // using this.target to fix context loss due to isOppAnim swap (#481) } for (const ms of Object.values(spriteCache).flat()) { if (ms) { From c070f110e5ebc2f53362ebe7dd3adb1a7d1d80bb Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:44:58 -0400 Subject: [PATCH 010/108] [Localization(fr)] Additional nature entries in pokemon-summary (#3869) Co-authored-by: Lugiad --- src/locales/fr/pokemon-summary.json | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/locales/fr/pokemon-summary.json b/src/locales/fr/pokemon-summary.json index f0b4f5a474f..01e712c8468 100644 --- a/src/locales/fr/pokemon-summary.json +++ b/src/locales/fr/pokemon-summary.json @@ -13,5 +13,32 @@ "metFragment": { "normal": "rencontré au N.{{level}},\n{{biome}}.", "apparently": "apparemment rencontré au N.{{level}},\n{{biome}}." + }, + "natureFragment": { + "Hardy": "{{nature}}", + "Lonely": "{{nature}}", + "Brave": "{{nature}}", + "Adamant": "{{nature}}", + "Naughty": "{{nature}}", + "Bold": "{{nature}}", + "Docile": "{{nature}}", + "Relaxed": "{{nature}}", + "Impish": "{{nature}}", + "Lax": "{{nature}}", + "Timid": "{{nature}}", + "Hasty": "{{nature}}", + "Serious": "{{nature}}", + "Jolly": "{{nature}}", + "Naive": "{{nature}}", + "Modest": "{{nature}}", + "Mild": "{{nature}}", + "Quiet": "{{nature}}", + "Bashful": "{{nature}}", + "Rash": "{{nature}}", + "Calm": "{{nature}}", + "Gentle": "{{nature}}", + "Sassy": "{{nature}}", + "Careful": "{{nature}}", + "Quirky": "{{nature}}" } -} \ No newline at end of file +} From 49c3158fd1e5d2ceb53312daf311d23e8a1396a1 Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:45:42 -0400 Subject: [PATCH 011/108] [Localization(ko)] Change ghost type challenge acv for achv-female (#3866) Co-authored-by: sodam Co-authored-by: Enoch Co-authored-by: KimJeongSun --- src/locales/ko/achv.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/ko/achv.json b/src/locales/ko/achv.json index 8546dff949c..b9fd327ef3b 100644 --- a/src/locales/ko/achv.json +++ b/src/locales/ko/achv.json @@ -225,7 +225,7 @@ "name": "독침붕처럼 쏴라" }, "MONO_GHOST": { - "name": "누굴 부를 거야?" + "name": "무서운 게 딱 좋아!" }, "MONO_STEEL": { "name": "강철 심장" @@ -265,4 +265,4 @@ "name": "상성 전문가(였던 것)", "description": "거꾸로 배틀 챌린지 모드 클리어." } -} \ No newline at end of file +} From 14e0c66ed9d46f248f26ee7cc0e378e87792cfd1 Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:46:53 -0400 Subject: [PATCH 012/108] [Localisation] [ES] Finished and reviewed terrain, modifier, trainer-names, splash messages (#3848) * Translate splash-messages.json via GitLocalize * Translate splash-messages.json via GitLocalize * Translate trainer-names.json via GitLocalize * Translate modifier.json via GitLocalize * Translate modifier.json via GitLocalize * Translate modifier.json via GitLocalize * Translate terrain.json via GitLocalize * Update src/locales/es/trainer-names.json Co-authored-by: Asdar --------- Co-authored-by: LilyAlternis Co-authored-by: Asdar Co-authored-by: Rafa Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/locales/es/modifier.json | 13 ++++++-- src/locales/es/splash-messages.json | 12 ++++---- src/locales/es/terrain.json | 17 +++++++++- src/locales/es/trainer-names.json | 48 ++++++++++++++--------------- 4 files changed, 56 insertions(+), 34 deletions(-) diff --git a/src/locales/es/modifier.json b/src/locales/es/modifier.json index 593b3df2f0f..a94e41a4574 100644 --- a/src/locales/es/modifier.json +++ b/src/locales/es/modifier.json @@ -1,3 +1,12 @@ { - "bypassSpeedChanceApply": "¡Gracias {{itemName}} {{pokemonName}} puede tener prioridad!" -} \ No newline at end of file + "surviveDamageApply": "{{pokemonNameWithAffix}} ha usado {{typeName}} y ha logrado resistir!", + "turnHealApply": "{{pokemonNameWithAffix}} ha recuperado unos pocos PS gracias a {{typeName}}!", + "hitHealApply": "{{pokemonNameWithAffix}} ha recuperado unos pocos PS gracias a {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} ha sido revivido gracias a su {{typeName}}!", + "pokemonResetNegativeStatStageApply": "Las estadísticas bajadas de {{pokemonNameWithAffix}} fueron restauradas gracias a {{typeName}}!", + "moneyInterestApply": "Recibiste intereses de ₽{{moneyAmount}}\ngracias a {{typeName}}!", + "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} fue absorbido\npor {{pokemonName}}'s {{typeName}}!", + "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} fue robado por {{pokemonName}}'s {{typeName}}!", + "enemyTurnHealApply": "¡{{pokemonNameWithAffix}}\nrecuperó algunos PS!", + "bypassSpeedChanceApply": "¡Gracias a su {{itemName}}, {{pokemonName}} puede tener prioridad!" +} diff --git a/src/locales/es/splash-messages.json b/src/locales/es/splash-messages.json index 90ce3593b89..b1d4820b06e 100644 --- a/src/locales/es/splash-messages.json +++ b/src/locales/es/splash-messages.json @@ -3,12 +3,12 @@ "joinTheDiscord": "¡Únete al Discord!", "infiniteLevels": "¡Niveles infinitos!", "everythingStacks": "¡Todo se acumula!", - "optionalSaveScumming": "¡Trampas guardando (¡opcionales!)!", + "optionalSaveScumming": "¡Trampas de guardado opcionales!", "biomes": "¡35 biomas!", "openSource": "¡Código abierto!", "playWithSpeed": "¡Juega a velocidad 5x!", - "liveBugTesting": "¡Arreglo de bugs sobre la marcha!", - "heavyInfluence": "¡Influencia Alta en RoR2!", + "liveBugTesting": "¡Testeo de bugs en directo!", + "heavyInfluence": "¡Mucha Influencia de RoR2!", "pokemonRiskAndPokemonRain": "¡Pokémon Risk y Pokémon Rain!", "nowWithMoreSalt": "¡Con un 33% más de polémica!", "infiniteFusionAtHome": "¡Infinite Fusion en casa!", @@ -17,16 +17,16 @@ "mubstitute": "¡Mubstituto!", "thatsCrazy": "¡De locos!", "oranceJuice": "¡Zumo de narancia!", - "questionableBalancing": "¡Balance cuestionable!", + "questionableBalancing": "¡Cambios en balance cuestionables!", "coolShaders": "¡Shaders impresionantes!", "aiFree": "¡Libre de IA!", "suddenDifficultySpikes": "¡Saltos de dificultad repentinos!", "basedOnAnUnfinishedFlashGame": "¡Basado en un juego Flash inacabado!", - "moreAddictiveThanIntended": "¡Más adictivo de la cuenta!", + "moreAddictiveThanIntended": "¡Más adictivo de lo previsto!", "mostlyConsistentSeeds": "¡Semillas CASI consistentes!", "achievementPointsDontDoAnything": "¡Los Puntos de Logro no hacen nada!", "youDoNotStartAtLevel": "¡No empiezas al nivel 2000!", - "dontTalkAboutTheManaphyEggIncident": "¡No hablen del incidente del Huevo Manaphy!", + "dontTalkAboutTheManaphyEggIncident": "¡No se habla del Incidente Manaphy!", "alsoTryPokengine": "¡Prueba también Pokéngine!", "alsoTryEmeraldRogue": "¡Prueba también Emerald Rogue!", "alsoTryRadicalRed": "¡Prueba también Radical Red!", diff --git a/src/locales/es/terrain.json b/src/locales/es/terrain.json index 9e26dfeeb6e..912f5186180 100644 --- a/src/locales/es/terrain.json +++ b/src/locales/es/terrain.json @@ -1 +1,16 @@ -{} \ No newline at end of file +{ + "misty": "Niebla", + "mistyStartMessage": "¡La niebla ha envuelto el terreno de combate!", + "mistyClearMessage": "La niebla se ha disipado.", + "mistyBlockMessage": "¡El campo de niebla ha protegido a {{pokemonNameWithAffix}} ", + "electric": "Eléctrico", + "electricStartMessage": "¡Se ha formado un campo de corriente eléctrica en el terreno\nde combate!", + "electricClearMessage": "El campo de corriente eléctrica ha desaparecido.\t", + "grassy": "Hierba", + "grassyStartMessage": "¡El terreno de combate se ha cubierto de hierba!", + "grassyClearMessage": "La hierba ha desaparecido.", + "psychic": "Psíquico", + "psychicStartMessage": "¡El terreno de combate se ha vuelto muy extraño!", + "psychicClearMessage": "Ha desaparecido la extraña sensación que se percibía en el terreno\nde combate.", + "defaultBlockMessage": "¡El campo {{terrainName}} ha protegido a {{pokemonNameWithAffix}} " +} diff --git a/src/locales/es/trainer-names.json b/src/locales/es/trainer-names.json index c6758366db7..ce09a0c9037 100644 --- a/src/locales/es/trainer-names.json +++ b/src/locales/es/trainer-names.json @@ -1,7 +1,7 @@ { "brock": "Brock", "misty": "Misty", - "lt_surge": "Tt. Surge", + "lt_surge": "Teniente Surge", "erika": "Erika", "janine": "Sachiko", "sabrina": "Sabrina", @@ -23,7 +23,7 @@ "winona": "Alana", "tate": "Vito", "liza": "Leti", - "juan": "Galán", + "juan": "Galano", "roark": "Roco", "gardenia": "Gardenia", "maylene": "Brega", @@ -34,7 +34,7 @@ "volkner": "Lectro", "cilan": "Millo", "chili": "Zeo", - "cress": "Maiz", + "cress": "Maíz", "cheren": "Cheren", "lenora": "Aloe", "roxie": "Hiedra", @@ -57,7 +57,7 @@ "nessa": "Cathy", "kabu": "Naboru", "bea": "Judith", - "allister": "Allistair", + "allister": "Alistair", "opal": "Sally", "bede": "Berto", "gordie": "Morris", @@ -123,30 +123,28 @@ "leon": "Lionel", "rival": "Finn", "rival_female": "Ivy", - "archer": "Archer", - "ariana": "Ariana", - "proton": "Proton", + "archer": "Atlas", + "ariana": "Atenea", + "proton": "Protón", "petrel": "Petrel", - "tabitha": "Tabitha", - "courtney": "Courtney", - "shelly": "Shelly", - "matt": "Matt", - "mars": "Mars", - "jupiter": "Jupiter", - "saturn": "Saturn", - "zinzolin": "Zinzolin", - "rood": "Rood", - "xerosic": "Xerosic", - "bryony": "Bryony", + "tabitha": "Tatiano", + "courtney": "Carola", + "shelly": "Silvina", + "matt": "Matías", + "mars": "Venus", + "jupiter": "Ceres", + "saturn": "Saturno", + "zinzolin": "Menek", + "rood": "Ruga", + "xerosic": "Xero", + "bryony": "Begonia", + "maxie": "Magno", + "archie": "Aquiles", + "cyrus": "Helio", + "ghetsis": "Ghechis", + "lysandre": "Lysson", "faba": "Fabio", - - "maxie": "Maxie", - "archie": "Archie", - "cyrus": "Cyrus", - "ghetsis": "Ghetsis", - "lysandre": "Lysandre", "lusamine": "Samina", - "blue_red_double": "Azul y Rojo", "red_blue_double": "Rojo y Azul", "tate_liza_double": "Vito y Leti", From dcb03f4ee99635a56d8b7e9316f2c0f4a29c0b92 Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Mon, 2 Sep 2024 04:47:22 +0200 Subject: [PATCH 013/108] [Test] Add test for final boss fight phase switch (#3847) * implement test for final boss encounter phase switch * Update Eternatus tests & helper function * fix endless_boss test following GameManager update --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/test/endless_boss.test.ts | 10 ++-- src/test/final_boss.test.ts | 98 +++++++++++++++++++++++++++++++---- src/test/utils/gameManager.ts | 29 +++++------ src/ui/ui.ts | 3 +- 4 files changed, 110 insertions(+), 30 deletions(-) diff --git a/src/test/endless_boss.test.ts b/src/test/endless_boss.test.ts index e983be245b6..8a564695e42 100644 --- a/src/test/endless_boss.test.ts +++ b/src/test/endless_boss.test.ts @@ -32,7 +32,7 @@ describe("Endless Boss", () => { it(`should spawn a minor boss every ${EndlessBossWave.Minor} waves in END biome in Endless`, async () => { game.override.startingWave(EndlessBossWave.Minor); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Minor); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -44,7 +44,7 @@ describe("Endless Boss", () => { it(`should spawn a major boss every ${EndlessBossWave.Major} waves in END biome in Endless`, async () => { game.override.startingWave(EndlessBossWave.Major); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Major); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -56,7 +56,7 @@ describe("Endless Boss", () => { it(`should spawn a minor boss every ${EndlessBossWave.Minor} waves in END biome in Spliced Endless`, async () => { game.override.startingWave(EndlessBossWave.Minor); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.SPLICED_ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.SPLICED_ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Minor); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -68,7 +68,7 @@ describe("Endless Boss", () => { it(`should spawn a major boss every ${EndlessBossWave.Major} waves in END biome in Spliced Endless`, async () => { game.override.startingWave(EndlessBossWave.Major); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.SPLICED_ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.SPLICED_ENDLESS); expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Major); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -80,7 +80,7 @@ describe("Endless Boss", () => { it(`should NOT spawn major or minor boss outside wave ${EndlessBossWave.Minor}s in END biome`, async () => { game.override.startingWave(EndlessBossWave.Minor - 1); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.ENDLESS); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.ENDLESS); expect(game.scene.currentBattle.waveIndex).not.toBe(EndlessBossWave.Minor); expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS); diff --git a/src/test/final_boss.test.ts b/src/test/final_boss.test.ts index 0f59572619b..5d006998a0b 100644 --- a/src/test/final_boss.test.ts +++ b/src/test/final_boss.test.ts @@ -1,8 +1,13 @@ +import { StatusEffect } from "#app/data/status-effect"; +import { Abilities } from "#app/enums/abilities"; import { Biome } from "#app/enums/biome"; +import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { GameModes } from "#app/game-mode"; +import { TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; +import { SPLASH_ONLY } from "./utils/testUtils"; const FinalWave = { Classic: 200, @@ -20,7 +25,13 @@ describe("Final Boss", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.startingWave(FinalWave.Classic).startingBiome(Biome.END).disableCrits(); + game.override + .startingWave(FinalWave.Classic) + .startingBiome(Biome.END) + .disableCrits() + .enemyMoveset(SPLASH_ONLY) + .moveset([ Moves.SPLASH, Moves.WILL_O_WISP, Moves.DRAGON_PULSE ]) + .startingLevel(10000); }); afterEach(() => { @@ -28,7 +39,7 @@ describe("Final Boss", () => { }); it("should spawn Eternatus on wave 200 in END biome", async () => { - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -37,7 +48,7 @@ describe("Final Boss", () => { it("should NOT spawn Eternatus before wave 200 in END biome", async () => { game.override.startingWave(FinalWave.Classic - 1); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); expect(game.scene.currentBattle.waveIndex).not.toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); @@ -46,7 +57,7 @@ describe("Final Boss", () => { it("should NOT spawn Eternatus outside of END biome", async () => { game.override.startingBiome(Biome.FOREST); - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).not.toBe(Biome.END); @@ -54,12 +65,81 @@ describe("Final Boss", () => { }); it("should not have passive enabled on Eternatus", async () => { - await game.runToFinalBossEncounter(game, [Species.BIDOOF], GameModes.CLASSIC); + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); - const eternatus = game.scene.getEnemyPokemon(); - expect(eternatus?.species.speciesId).toBe(Species.ETERNATUS); - expect(eternatus?.hasPassive()).toBe(false); + const eternatus = game.scene.getEnemyPokemon()!; + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.hasPassive()).toBe(false); + }); + + it("should change form on direct hit down to last boss fragment", async () => { + await game.runToFinalBossEncounter([Species.KYUREM], GameModes.CLASSIC); + await game.phaseInterceptor.to("CommandPhase"); + + // Eternatus phase 1 + const eternatus = game.scene.getEnemyPokemon()!; + const phase1Hp = eternatus.getMaxHp(); + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.formIndex).toBe(0); + expect(eternatus.bossSegments).toBe(4); + expect(eternatus.bossSegmentIndex).toBe(3); + + game.move.select(Moves.DRAGON_PULSE); + await game.toNextTurn(); + + // Eternatus phase 2: changed form, healed and restored its shields + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.hp).toBeGreaterThan(phase1Hp); + expect(eternatus.hp).toBe(eternatus.getMaxHp()); + expect(eternatus.formIndex).toBe(1); + expect(eternatus.bossSegments).toBe(5); + expect(eternatus.bossSegmentIndex).toBe(4); + const miniBlackHole = eternatus.getHeldItems().find(m => m instanceof TurnHeldItemTransferModifier); + expect(miniBlackHole).toBeDefined(); + expect(miniBlackHole?.stackCount).toBe(1); + }); + + it("should change form on status damage down to last boss fragment", async () => { + game.override.ability(Abilities.NO_GUARD); + + await game.runToFinalBossEncounter([Species.BIDOOF], GameModes.CLASSIC); + await game.phaseInterceptor.to("CommandPhase"); + + // Eternatus phase 1 + const eternatus = game.scene.getEnemyPokemon()!; + const phase1Hp = eternatus.getMaxHp(); + expect(eternatus.species.speciesId).toBe(Species.ETERNATUS); + expect(eternatus.formIndex).toBe(0); + expect(eternatus.bossSegments).toBe(4); + expect(eternatus.bossSegmentIndex).toBe(3); + + game.move.select(Moves.WILL_O_WISP); + await game.toNextTurn(); + expect(eternatus.status?.effect).toBe(StatusEffect.BURN); + + const tickDamage = phase1Hp - eternatus.hp; + const lastShieldHp = Math.ceil(phase1Hp / eternatus.bossSegments); + // Stall until the burn is one hit away from breaking the last shield + while (eternatus.hp - tickDamage > lastShieldHp) { + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + } + + expect(eternatus.bossSegmentIndex).toBe(1); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + // Eternatus phase 2: changed form, healed and restored its shields + expect(eternatus.hp).toBeGreaterThan(phase1Hp); + expect(eternatus.hp).toBe(eternatus.getMaxHp()); + expect(eternatus.status).toBeFalsy(); + expect(eternatus.formIndex).toBe(1); + expect(eternatus.bossSegments).toBe(5); + expect(eternatus.bossSegmentIndex).toBe(4); + const miniBlackHole = eternatus.getHeldItems().find(m => m instanceof TurnHeldItemTransferModifier); + expect(miniBlackHole).toBeDefined(); + expect(miniBlackHole?.stackCount).toBe(1); }); - it.todo("should change form on direct hit down to last boss fragment", () => {}); }); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 5818244db8f..a9a71d16bf7 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -6,6 +6,7 @@ import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import Trainer from "#app/field/trainer"; import { GameModes, getGameMode } from "#app/game-mode"; import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; +import overrides from "#app/overrides"; import { CommandPhase } from "#app/phases/command-phase"; import { EncounterPhase } from "#app/phases/encounter-phase"; import { FaintPhase } from "#app/phases/faint-phase"; @@ -148,28 +149,26 @@ export default class GameManager { * @param species * @param mode */ - async runToFinalBossEncounter(game: GameManager, species: Species[], mode: GameModes) { + async runToFinalBossEncounter(species: Species[], mode: GameModes) { console.log("===to final boss encounter==="); - await game.runToTitle(); + await this.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { - game.scene.gameMode = getGameMode(mode); - const starters = generateStarter(game.scene, species); - const selectStarterPhase = new SelectStarterPhase(game.scene); - game.scene.pushPhase(new EncounterPhase(game.scene, false)); + this.onNextPrompt("TitlePhase", Mode.TITLE, () => { + this.scene.gameMode = getGameMode(mode); + const starters = generateStarter(this.scene, species); + const selectStarterPhase = new SelectStarterPhase(this.scene); + this.scene.pushPhase(new EncounterPhase(this.scene, false)); selectStarterPhase.initBattle(starters); }); - game.onNextPrompt("EncounterPhase", Mode.MESSAGE, async () => { - // This will skip all entry dialogue (I can't figure out a way to sequentially handle the 8 chained messages via 1 prompt handler) - game.setMode(Mode.MESSAGE); - const encounterPhase = game.scene.getCurrentPhase() as EncounterPhase; + // This will consider all battle entry dialog as seens and skip them + vi.spyOn(this.scene.ui, "shouldSkipDialogue").mockReturnValue(true); - // No need to end phase, this will do it for you - encounterPhase.doEncounterCommon(false); - }); + if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) { + this.removeEnemyHeldItems(); + } - await game.phaseInterceptor.to(EncounterPhase, true); + await this.phaseInterceptor.to(EncounterPhase); console.log("===finished run to final boss encounter==="); } diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 7108a8dd480..8ec91b59480 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -317,10 +317,11 @@ export default class UI extends Phaser.GameObjects.Container { if (i18next.exists(keyOrText) ) { const i18nKey = keyOrText; hasi18n = true; + text = i18next.t(i18nKey, { context: genderStr }); // override text with translation // Skip dialogue if the player has enabled the option and the dialogue has been already seen - if (battleScene.skipSeenDialogues && battleScene.gameData.getSeenDialogues()[i18nKey] === true) { + if (this.shouldSkipDialogue(i18nKey)) { console.log(`Dialogue ${i18nKey} skipped`); callback(); return; From 709066bd1ab9619059259348b84515c8e9b9f27e Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:57:07 -0700 Subject: [PATCH 014/108] [Move] Finish Alluring Voice, Burning Jealousy and Lash Out (#3508) * Implement Alluring Voice and Burning Jealousy * Fix Alluring Voice and add tests * Replace `BattlerTag.STATS_BOOSTED` with `PokemonTurnData` field * Work around bug with turn data * Remove unused variable * Replace nearby instances of `integer` with `number` * Fix imports * Implement Lash Out * Rename `battleStats(In|De)crease` -> `battleStats(In|De)creased` * Fix copy/paste error Co-authored-by: schmidtc1 <62030095+schmidtc1@users.noreply.github.com> * Update tests --------- Co-authored-by: ElliottSimmonds Co-authored-by: schmidtc1 <62030095+schmidtc1@users.noreply.github.com> Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com> --- src/data/move.ts | 61 ++++++++++++-- src/field/pokemon.ts | 32 ++++---- src/phases/stat-change-phase.ts | 36 ++++++--- src/test/moves/alluring_voice.test.ts | 54 +++++++++++++ src/test/moves/burning_jealousy.test.ts | 103 ++++++++++++++++++++++++ src/test/moves/lash_out.test.ts | 52 ++++++++++++ 6 files changed, 307 insertions(+), 31 deletions(-) create mode 100644 src/test/moves/alluring_voice.test.ts create mode 100644 src/test/moves/burning_jealousy.test.ts create mode 100644 src/test/moves/lash_out.test.ts diff --git a/src/data/move.ts b/src/data/move.ts index 2ffa8a36de9..95d306a61ba 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -6037,6 +6037,57 @@ export class DestinyBondAttr extends MoveEffectAttr { } } +/** + * Attribute to apply a battler tag to the target if they have had their stats boosted this turn. + * @extends AddBattlerTagAttr + */ +export class AddBattlerTagIfBoostedAttr extends AddBattlerTagAttr { + constructor(tag: BattlerTagType) { + super(tag, false, false, 2, 5); + } + + /** + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} being used + * @param {any[]} args N/A + * @returns true + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (target.turnData.battleStatsIncreased) { + super.apply(user, target, move, args); + } + return true; + } +} + +/** + * Attribute to apply a status effect to the target if they have had their stats boosted this turn. + * @extends MoveEffectAttr + */ +export class StatusIfBoostedAttr extends MoveEffectAttr { + public effect: StatusEffect; + + constructor(effect: StatusEffect) { + super(true, MoveEffectTrigger.HIT); + this.effect = effect; + } + + /** + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} N/A + * @param {any[]} args N/A + * @returns true + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (target.turnData.battleStatsIncreased) { + target.trySetStatus(this.effect, true, user); + } + return true; + } +} + export class LastResortAttr extends MoveAttr { getCondition(): MoveConditionFunc { return (user: Pokemon, target: Pokemon, move: Move) => { @@ -8694,10 +8745,10 @@ export function initMoves() { new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) .attr(StatChangeAttr, BattleStat.SPATK, -1), new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8) - .target(MoveTarget.ALL_NEAR_ENEMIES) - .partial(), + .attr(StatusIfBoostedAttr, StatusEffect.BURN) + .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.LASH_OUT, Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8) - .partial(), + .attr(MovePowerMultiplierAttr, (user, target, move) => user.turnData.battleStatsDecreased ? 2 : 1), new AttackMove(Moves.POLTERGEIST, Type.GHOST, MoveCategory.PHYSICAL, 110, 90, 5, -1, 0, 8) .attr(AttackedByItemAttr) .makesContact(false), @@ -9146,8 +9197,8 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true) .target(MoveTarget.NEAR_ALLY), new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) - .soundBased() - .partial(), + .attr(AddBattlerTagIfBoostedAttr, BattlerTagType.CONFUSED) + .soundBased(), new AttackMove(Moves.TEMPER_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9) .attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1), new AttackMove(Moves.SUPERCELL_SLAM, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 00d5f5d3dd2..b1fcd512e35 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -4286,7 +4286,7 @@ export interface TurnMove { targets?: BattlerIndex[]; result: MoveResult; virtual?: boolean; - turn?: integer; + turn?: number; } export interface QueuedMove { @@ -4298,17 +4298,17 @@ export interface QueuedMove { export interface AttackMoveResult { move: Moves; result: DamageResult; - damage: integer; + damage: number; critical: boolean; - sourceId: integer; + sourceId: number; sourceBattlerIndex: BattlerIndex; } export class PokemonSummonData { - public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ]; + public battleStats: number[] = [ 0, 0, 0, 0, 0, 0, 0 ]; public moveQueue: QueuedMove[] = []; public disabledMove: Moves = Moves.NONE; - public disabledTurns: integer = 0; + public disabledTurns: number = 0; public tags: BattlerTag[] = []; public abilitySuppressed: boolean = false; public abilitiesApplied: Abilities[] = []; @@ -4318,14 +4318,14 @@ export class PokemonSummonData { public ability: Abilities = Abilities.NONE; public gender: Gender; public fusionGender: Gender; - public stats: integer[]; + public stats: number[]; public moveset: (PokemonMove | null)[]; // If not initialized this value will not be populated from save data. public types: Type[] = []; } export class PokemonBattleData { - public hitCount: integer = 0; + public hitCount: number = 0; public endured: boolean = false; public berriesEaten: BerryType[] = []; public abilitiesApplied: Abilities[] = []; @@ -4334,21 +4334,23 @@ export class PokemonBattleData { export class PokemonBattleSummonData { /** The number of turns the pokemon has passed since entering the battle */ - public turnCount: integer = 1; + public turnCount: number = 1; /** The list of moves the pokemon has used since entering the battle */ public moveHistory: TurnMove[] = []; } export class PokemonTurnData { - public flinched: boolean; - public acted: boolean; - public hitCount: integer; - public hitsLeft: integer; - public damageDealt: integer = 0; - public currDamageDealt: integer = 0; - public damageTaken: integer = 0; + public flinched: boolean = false; + public acted: boolean = false; + public hitCount: number; + public hitsLeft: number; + public damageDealt: number = 0; + public currDamageDealt: number = 0; + public damageTaken: number = 0; public attacksReceived: AttackMoveResult[] = []; public order: number; + public battleStatsIncreased: boolean = false; + public battleStatsDecreased: boolean = false; } export enum AiType { diff --git a/src/phases/stat-change-phase.ts b/src/phases/stat-change-phase.ts index 856d0a33eea..3116c49e8ef 100644 --- a/src/phases/stat-change-phase.ts +++ b/src/phases/stat-change-phase.ts @@ -1,14 +1,14 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyPreStatChangeAbAttrs, ProtectStatAbAttr, applyAbAttrs, StatChangeMultiplierAbAttr, StatChangeCopyAbAttr, applyPostStatChangeAbAttrs, PostStatChangeAbAttr } from "#app/data/ability.js"; -import { MistTag, ArenaTagSide } from "#app/data/arena-tag.js"; -import { BattleStat, getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js"; -import Pokemon from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { PokemonResetNegativeStatStageModifier } from "#app/modifier/modifier.js"; -import { handleTutorial, Tutorial } from "#app/tutorial.js"; +import { BattlerIndex } from "#app/battle"; +import BattleScene from "#app/battle-scene"; +import { applyAbAttrs, applyPostStatChangeAbAttrs, applyPreStatChangeAbAttrs, PostStatChangeAbAttr, ProtectStatAbAttr, StatChangeCopyAbAttr, StatChangeMultiplierAbAttr } from "#app/data/ability"; +import { ArenaTagSide, MistTag } from "#app/data/arena-tag"; +import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat"; +import Pokemon from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { PokemonResetNegativeStatStageModifier } from "#app/modifier/modifier"; +import { handleTutorial, Tutorial } from "#app/tutorial"; +import * as Utils from "#app/utils"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; import { PokemonPhase } from "./pokemon-phase"; export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void; @@ -72,7 +72,7 @@ export class StatChangePhase extends PokemonPhase { } const battleStats = this.getPokemon().summonData.battleStats; - const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats![stat] + levels.value, 6) : Math.max(battleStats![stat] + levels.value, -6)) - battleStats![stat]); + const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats[stat] + levels.value, 6) : Math.max(battleStats[stat] + levels.value, -6)) - battleStats[stat]); this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels); @@ -85,6 +85,20 @@ export class StatChangePhase extends PokemonPhase { } for (const stat of filteredStats) { + if (levels.value > 0 && pokemon.summonData.battleStats[stat] < 6) { + if (!pokemon.turnData) { + // Temporary fix for missing turn data struct on turn 1 + pokemon.resetTurnData(); + } + pokemon.turnData.battleStatsIncreased = true; + } else if (levels.value < 0 && pokemon.summonData.battleStats[stat] > -6) { + if (!pokemon.turnData) { + // Temporary fix for missing turn data struct on turn 1 + pokemon.resetTurnData(); + } + pokemon.turnData.battleStatsDecreased = true; + } + pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6); } diff --git a/src/test/moves/alluring_voice.test.ts b/src/test/moves/alluring_voice.test.ts new file mode 100644 index 00000000000..e6ece39524a --- /dev/null +++ b/src/test/moves/alluring_voice.test.ts @@ -0,0 +1,54 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Alluring Voice", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.ICE_SCALES) + .enemyMoveset(Array(4).fill(Moves.HOWL)) + .startingLevel(10) + .enemyLevel(10) + .starterSpecies(Species.FEEBAS) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.ALLURING_VOICE]); + + }); + + it("should confuse the opponent if their stats were raised", async () => { + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.ALLURING_VOICE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to(BerryPhase); + + expect(enemy.getTag(BattlerTagType.CONFUSED)?.tagType).toBe("CONFUSED"); + }, TIMEOUT); +}); diff --git a/src/test/moves/burning_jealousy.test.ts b/src/test/moves/burning_jealousy.test.ts new file mode 100644 index 00000000000..2281fe74acb --- /dev/null +++ b/src/test/moves/burning_jealousy.test.ts @@ -0,0 +1,103 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { StatusEffect } from "#app/enums/status-effect"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Burning Jealousy", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.ICE_SCALES) + .enemyMoveset(Array(4).fill(Moves.HOWL)) + .startingLevel(10) + .enemyLevel(10) + .starterSpecies(Species.FEEBAS) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.BURNING_JEALOUSY, Moves.GROWL]); + + }); + + it("should burn the opponent if their stats were raised", async () => { + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.BURNING_JEALOUSY); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy.status?.effect).toBe(StatusEffect.BURN); + }, TIMEOUT); + + it("should still burn the opponent if their stats were both raised and lowered in the same turn", async () => { + game.override + .starterSpecies(0) + .battleType("double"); + await game.classicMode.startBattle([Species.FEEBAS, Species.ABRA]); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.BURNING_JEALOUSY); + game.move.select(Moves.GROWL, 1); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2]); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy.status?.effect).toBe(StatusEffect.BURN); + }, TIMEOUT); + + it("should ignore stats raised by imposter", async () => { + game.override + .enemySpecies(Species.DITTO) + .enemyAbility(Abilities.IMPOSTER) + .enemyMoveset(SPLASH_ONLY); + await game.classicMode.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.BURNING_JEALOUSY); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy.status?.effect).toBeUndefined(); + }, TIMEOUT); + + it.skip("should ignore weakness policy", async () => { // TODO: Make this test if WP is implemented + await game.classicMode.startBattle(); + }, TIMEOUT); + + it("should be boosted by Sheer Force even if opponent didn't raise stats", async () => { + game.override + .ability(Abilities.SHEER_FORCE) + .enemyMoveset(SPLASH_ONLY); + vi.spyOn(allMoves[Moves.BURNING_JEALOUSY], "calculateBattlePower"); + await game.classicMode.startBattle(); + + game.move.select(Moves.BURNING_JEALOUSY); + await game.phaseInterceptor.to("BerryPhase"); + + expect(allMoves[Moves.BURNING_JEALOUSY].calculateBattlePower).toHaveReturnedWith(allMoves[Moves.BURNING_JEALOUSY].power * 5461 / 4096); + }, TIMEOUT); +}); diff --git a/src/test/moves/lash_out.test.ts b/src/test/moves/lash_out.test.ts new file mode 100644 index 00000000000..78f4b712cf0 --- /dev/null +++ b/src/test/moves/lash_out.test.ts @@ -0,0 +1,52 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Lash Out", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.FUR_COAT) + .enemyMoveset(Array(4).fill(Moves.GROWL)) + .startingLevel(10) + .enemyLevel(10) + .starterSpecies(Species.FEEBAS) + .ability(Abilities.BALL_FETCH) + .moveset([Moves.LASH_OUT]); + + }); + + it("should deal double damage if the user's stats were lowered this turn", async () => { + vi.spyOn(allMoves[Moves.LASH_OUT], "calculateBattlePower"); + await game.classicMode.startBattle(); + + game.move.select(Moves.LASH_OUT); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("BerryPhase"); + + expect(allMoves[Moves.LASH_OUT].calculateBattlePower).toHaveReturnedWith(150); + }, TIMEOUT); +}); From 80828359e2b135e7b6e2c7c2df990fa6b2d9be8a Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:28:10 -0400 Subject: [PATCH 015/108] [Localization(en)] Fix Roark Dialouge (#3873) * Translate dialogue-male.json via GitLocalize * Added female * ! * Update src/locales/en/dialogue-male.json * Remove empty files * Added missing comma --------- Co-authored-by: Jannik Tappert Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> --- src/locales/de/dialogue.json | 8 ++++---- src/locales/en/dialogue.json | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/locales/de/dialogue.json b/src/locales/de/dialogue.json index e5bcb81ce52..8a3dbb8880e 100644 --- a/src/locales/de/dialogue.json +++ b/src/locales/de/dialogue.json @@ -1403,19 +1403,19 @@ "1": "Ich muss dein Potenzial als Trainer und die Stärke der Pokémon sehen, die mit dir kämpfen!", "2": "Los geht's! Dies sind meine Gesteins-Pokémon, mein ganzer Stolz!", "3": "Gesteins-Pokémon sind einfach die besten!", - "4": "Ich muss dein Potenzial als Trainer und die Stärke der Pokémon sehen, die mit dir kämpfen!" + "4": "Tag für Tag grabe ich hier nach Fossilien.\n$Die viele Arbeit hat meine Pokémon felsenfest gemacht\nund das wirst du jetzt im Kampf zu spüren bekommen!" }, "victory": { "1": "W-was? Das kann nicht sein! Meine total tranierten Pokémon!", "2": "…Wir haben die Kontrolle verloren. Beim nächsten Mal fordere ich dich\n$zu einem Fossilien-Ausgrabungswettbewerb heraus.", "3": "Mit deinem Können ist es nur natürlich, dass du gewinnst.", - "4": "W-was?! Das kann nicht sein! Selbst das war nicht genug?", - "5": "Ich habe es vermasselt." + "4": "W-was?! Das kann nicht sein! Selbst das war nicht genug?" }, "defeat": { "1": "Siehst du? Ich bin stolz auf meinen steinigen Kampfstil!", "2": "Danke! Der Kampf hat mir Vertrauen gegeben, dass ich vielleicht meinen Vater besiegen kann!", - "3": "Ich fühle mich, als hätte ich gerade einen wirklich hartnäckigen Felsen durchbrochen!" + "3": "Na, was sagst du jetzt? Meine felsenfesten Pokémon waren hart genug für dich, was?", + "4": "Ich wusste, dass ich gewinnen würde!" } }, "morty": { diff --git a/src/locales/en/dialogue.json b/src/locales/en/dialogue.json index 1f8919f11b5..90b03a176d1 100644 --- a/src/locales/en/dialogue.json +++ b/src/locales/en/dialogue.json @@ -742,7 +742,7 @@ "plumeria": { "encounter": { "1": " ...Hmph. You don't look like anything special to me.", - "2": "It takes these dumb Grunts way too long to deal with you kids..", + "2": "It takes these dumb Grunts way too long to deal with you kids...", "3": "Mess with anyone in Team Skull, and I'll show you how serious I can get." }, "victory": { @@ -1479,21 +1479,21 @@ "1_female": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", "2": "Here goes! These are my rocking Pokémon, my pride and joy!", "3": "Rock-type Pokémon are simply the best!", - "4": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", - "4_female": "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!" + "4": "Every day, I toughened up my Pokémon by digging up Fossils nonstop.\n$Could I show you how tough I made them in a battle?", + "4_female": "Every day, I toughened up my Pokémon by digging up Fossils nonstop.\n$Could I show you how tough I made them in a battle?" }, "victory": { "1": "W-what? That can't be! My buffed-up Pokémon!", "2": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", "2_female": "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", "3": "With skill like yours, it's natural for you to win.", - "4": "Wh-what?! It can't be! Even that wasn't enough?", - "5": "I blew it." + "4": "Wh-what?! It can't be! Even that wasn't enough?" }, "defeat": { "1": "See? I'm proud of my rocking battle style!", "2": "Thanks! The battle gave me confidence that I may be able to beat my dad!", - "3": "I feel like I just smashed through a really stubborn boulder!" + "3": "See? These are my rocking Pokémon, my pride and joy!", + "4": "I knew I would win!" } }, "morty": { From 56b39032b9cf272e6b63c31eec2b43f834ef85be Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:30:27 +0800 Subject: [PATCH 016/108] [Dev] add script to create test boilerplate file (#3954) * add script to create test boilerplate file * add more docs * add timeout to template --- create-test-boilerplate.js | 101 +++++++++++++++++++++++++++++++++++++ package.json | 3 +- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 create-test-boilerplate.js diff --git a/create-test-boilerplate.js b/create-test-boilerplate.js new file mode 100644 index 00000000000..bf68258f321 --- /dev/null +++ b/create-test-boilerplate.js @@ -0,0 +1,101 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +/** + * This script creates a test boilerplate file for a move or ability. + * @param {string} type - The type of test to create. Either "move" or "ability". + * @param {string} fileName - The name of the file to create. + * @example npm run create-test move tackle + */ + +// Get the directory name of the current module file +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Get the arguments from the command line +const args = process.argv.slice(2); +const type = args[0]; // "move" or "ability" +let fileName = args[1]; // The file name + +if (!type || !fileName) { + console.error('Please provide both a type ("move" or "ability") and a file name.'); + process.exit(1); +} + +// Convert fileName from to snake_case if camelCase is given +fileName = fileName.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase(); + +// Format the description for the test case +const formattedName = fileName + .replace(/_/g, ' ') + .replace(/\b\w/g, char => char.toUpperCase()); + +// Determine the directory based on the type +let dir; +let description; +if (type === 'move') { + dir = path.join(__dirname, 'src', 'test', 'moves'); + description = `Moves - ${formattedName}`; +} else if (type === 'ability') { + dir = path.join(__dirname, 'src', 'test', 'abilities'); + description = `Abilities - ${formattedName}`; +} else { + console.error('Invalid type. Please use "move" or "ability".'); + process.exit(1); +} + +// Ensure the directory exists +if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); +} + +// Create the file with the given name +const filePath = path.join(dir, `${fileName}.test.ts`); + +if (fs.existsSync(filePath)) { + console.error(`File "${fileName}.test.ts" already exists.`); + process.exit(1); +} + +// Define the content template +const content = `import { Abilities } from "#enums/abilities"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it } from "vitest"; + +describe("${description}", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(SPLASH_ONLY); + }); + + it("test case", async () => { + // await game.classicMode.startBattle(); + // game.move.select(); + }, TIMEOUT); +}); +`; + +// Write the template content to the file +fs.writeFileSync(filePath, content, 'utf8'); + +console.log(`File created at: ${filePath}`); \ No newline at end of file diff --git a/package.json b/package.json index b85ac639a1b..83e82585d1e 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "eslint-ci": "eslint .", "docs": "typedoc", "depcruise": "depcruise src", - "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg" + "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg", + "create-test": "node ./create-test-boilerplate.js" }, "devDependencies": { "@eslint/js": "^9.3.0", From 1fd662111e0b668e9f98a7b2c333cd67b9f54c37 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:32:31 -0700 Subject: [PATCH 017/108] [Test] Tests now default to using "Set" battle style (#3728) * Tests now default to using "Set" battle style * Fix typo --- src/test/abilities/ability_timing.test.ts | 21 +-- src/test/abilities/disguise.test.ts | 42 ++--- src/test/abilities/intimidate.test.ts | 205 ++++++++-------------- src/test/evolution.test.ts | 27 ++- src/test/evolutions/evolutions.test.ts | 48 ----- src/test/{utils => }/misc.test.ts | 6 +- src/test/moves/spikes.test.ts | 96 ++++------ src/test/utils/gameManager.ts | 2 +- src/test/utils/helpers/settingsHelper.ts | 16 +- 9 files changed, 173 insertions(+), 290 deletions(-) delete mode 100644 src/test/evolutions/evolutions.test.ts rename src/test/{utils => }/misc.test.ts (90%) diff --git a/src/test/abilities/ability_timing.test.ts b/src/test/abilities/ability_timing.test.ts index 3238f880992..fb3d3af1a6b 100644 --- a/src/test/abilities/ability_timing.test.ts +++ b/src/test/abilities/ability_timing.test.ts @@ -1,13 +1,11 @@ +import { BattleStyle } from "#app/enums/battle-style"; import { CommandPhase } from "#app/phases/command-phase"; -import { MessagePhase } from "#app/phases/message-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; import i18next, { initI18n } from "#app/plugins/i18n"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -28,19 +26,18 @@ describe("Ability Timing", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.enemySpecies(Species.PIDGEY); - game.override.enemyAbility(Abilities.INTIMIDATE); - game.override.enemyMoveset(SPLASH_ONLY); - - game.override.ability(Abilities.BALL_FETCH); - game.override.moveset([Moves.SPLASH, Moves.ICE_BEAM]); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.INTIMIDATE) + .ability(Abilities.BALL_FETCH); }); - it("should trigger after switch check", async() => { + it("should trigger after switch check", async () => { initI18n(); i18next.changeLanguage("en"); + game.settings.battleStyle = BattleStyle.SWITCH; await game.classicMode.runToSummon([Species.EEVEE, Species.FEEBAS]); game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { @@ -48,7 +45,7 @@ describe("Ability Timing", () => { game.endPhase(); }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); - await game.phaseInterceptor.to(MessagePhase); + await game.phaseInterceptor.to("MessagePhase"); const message = game.textInterceptor.getLatestMessage(); expect(message).toContain("Attack fell"); }, 5000); diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index 85141fdb491..bbb0a20dc1a 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -1,11 +1,5 @@ import { BattleStat } from "#app/data/battle-stat"; import { StatusEffect } from "#app/data/status-effect"; -import { CommandPhase } from "#app/phases/command-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import { Mode } from "#app/ui/ui"; import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -33,13 +27,12 @@ describe("Abilities - Disguise", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - - game.override.enemySpecies(Species.MIMIKYU); - game.override.enemyMoveset(SPLASH_ONLY); - - game.override.starterSpecies(Species.REGIELEKI); - game.override.moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]); + game.override + .battleType("single") + .enemySpecies(Species.MIMIKYU) + .enemyMoveset(SPLASH_ONLY) + .starterSpecies(Species.REGIELEKI) + .moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]); }, TIMEOUT); it("takes no damage from attacking move and transforms to Busted form, takes 1/8 max HP damage from the disguise breaking", async () => { @@ -53,7 +46,7 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.SHADOW_SNEAK); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(mimikyu.hp).equals(maxHp - disguiseDamage); expect(mimikyu.formIndex).toBe(bustedForm); @@ -68,7 +61,7 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.VACUUM_WAVE); - await game.phaseInterceptor.to(MoveEndPhase); + await game.phaseInterceptor.to("MoveEndPhase"); expect(mimikyu.formIndex).toBe(disguisedForm); }, TIMEOUT); @@ -87,12 +80,12 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.SURGING_STRIKES); // First hit - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(mimikyu.hp).equals(maxHp - disguiseDamage); expect(mimikyu.formIndex).toBe(disguisedForm); // Second hit - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(mimikyu.hp).lessThan(maxHp - disguiseDamage); expect(mimikyu.formIndex).toBe(bustedForm); }, TIMEOUT); @@ -105,7 +98,7 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.TOXIC_THREAD); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(mimikyu.formIndex).toBe(disguisedForm); expect(mimikyu.status?.effect).toBe(StatusEffect.POISON); @@ -125,7 +118,7 @@ describe("Abilities - Disguise", () => { game.move.select(Moves.SPLASH); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(mimikyu.formIndex).toBe(bustedForm); expect(mimikyu.hp).equals(maxHp - disguiseDamage); @@ -133,7 +126,7 @@ describe("Abilities - Disguise", () => { await game.toNextTurn(); game.doSwitchPokemon(1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(mimikyu.formIndex).toBe(bustedForm); }, TIMEOUT); @@ -194,15 +187,6 @@ describe("Abilities - Disguise", () => { await game.toNextTurn(); game.move.select(Moves.SPLASH); await game.doKillOpponents(); - game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { // TODO: Make tests run in set mode instead of switch mode - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); - - game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); await game.phaseInterceptor.to("PartyHealPhase"); expect(mimikyu1.formIndex).toBe(disguisedForm); diff --git a/src/test/abilities/intimidate.test.ts b/src/test/abilities/intimidate.test.ts index 93b663d06da..bc5b5ab1a7d 100644 --- a/src/test/abilities/intimidate.test.ts +++ b/src/test/abilities/intimidate.test.ts @@ -1,12 +1,8 @@ import { BattleStat } from "#app/data/battle-stat"; import { Status, StatusEffect } from "#app/data/status-effect"; import { GameModes, getGameMode } from "#app/game-mode"; -import { CommandPhase } from "#app/phases/command-phase"; -import { DamagePhase } from "#app/phases/damage-phase"; import { EncounterPhase } from "#app/phases/encounter-phase"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; @@ -33,36 +29,26 @@ describe("Abilities - Intimidate", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.enemySpecies(Species.RATTATA); - game.override.enemyAbility(Abilities.INTIMIDATE); - game.override.enemyPassiveAbility(Abilities.HYDRATION); - game.override.ability(Abilities.INTIMIDATE); - game.override.startingWave(3); - game.override.enemyMoveset(SPLASH_ONLY); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.INTIMIDATE) + .ability(Abilities.INTIMIDATE) + .moveset([Moves.SPLASH, Moves.AERIAL_ACE]) + .enemyMoveset(SPLASH_ONLY); }); it("single - wild with switch", async () => { - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.MIGHTYENA); + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); + game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA); + await game.phaseInterceptor.to("CommandPhase"); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(0); @@ -73,25 +59,16 @@ describe("Abilities - Intimidate", () => { it("single - boss should only trigger once then switch", async () => { game.override.startingWave(10); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); + game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA); + await game.phaseInterceptor.to("CommandPhase"); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(0); @@ -102,25 +79,16 @@ describe("Abilities - Intimidate", () => { it("single - trainer should only trigger once with switch", async () => { game.override.startingWave(5); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); + game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase); - expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA); + await game.phaseInterceptor.to("CommandPhase"); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(0); @@ -130,21 +98,14 @@ describe("Abilities - Intimidate", () => { }, 200000); it("double - trainer should only trigger once per pokemon", async () => { - game.override.battleType("double"); - game.override.startingWave(5); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); + game.override + .battleType("double") + .startingWave(5); + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); + const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2); @@ -157,20 +118,11 @@ describe("Abilities - Intimidate", () => { it("double - wild: should only trigger once per pokemon", async () => { game.override.battleType("double"); - game.override.startingWave(3); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); + const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2); @@ -182,21 +134,14 @@ describe("Abilities - Intimidate", () => { }, 20000); it("double - boss: should only trigger once per pokemon", async () => { - game.override.battleType("double"); - game.override.startingWave(10); - await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - game.onNextPrompt( - "CheckSwitchPhase", - Mode.CONFIRM, - () => { - game.setMode(Mode.MESSAGE); - game.endPhase(); - }, - () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase) - ); - await game.phaseInterceptor.to(CommandPhase, false); + game.override + .battleType("double") + .startingWave(10); + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2); + const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2); @@ -208,104 +153,101 @@ describe("Abilities - Intimidate", () => { }, 20000); it("single - wild next wave opp triger once, us: none", async () => { - game.override.startingWave(2); - game.override.moveset([Moves.AERIAL_ACE]); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + await game.startBattle([Species.MIGHTYENA]); + let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); game.move.select(Moves.AERIAL_ACE); - await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(DamagePhase); - await game.killPokemon(game.scene.currentBattle.enemyParty[0]); - expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); + await game.phaseInterceptor.to("DamagePhase"); + await game.doKillOpponents(); await game.toNextWave(); + battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); + battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); }, 20000); it("single - wild next turn - no retrigger on next turn", async () => { - game.override.startingWave(2); - game.override.moveset([Moves.SPLASH]); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + await game.startBattle([Species.MIGHTYENA]); + let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); + game.move.select(Moves.SPLASH); await game.toNextTurn(); + battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); + battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); }, 20000); it("single - trainer should only trigger once and each time he switch", async () => { - game.override.moveset([Moves.SPLASH]); - game.override.enemyMoveset([Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH]); - game.override.startingWave(5); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + game.override + .enemyMoveset(Array(4).fill(Moves.VOLT_SWITCH)) + .startingWave(5); + await game.startBattle([Species.MIGHTYENA]); + let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); + game.move.select(Moves.SPLASH); await game.toNextTurn(); + battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2); + battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); + game.move.select(Moves.SPLASH); await game.toNextTurn(); + battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-3); + battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); }, 200000); it("single - trainer should only trigger once whatever turn we are", async () => { - game.override.moveset([Moves.SPLASH]); - game.override.enemyMoveset(SPLASH_ONLY); game.override.startingWave(5); - await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; + await game.startBattle([Species.MIGHTYENA]); + + const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; + const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; + expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); + game.move.select(Moves.SPLASH); await game.toNextTurn(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; - expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - game.move.select(Moves.AERIAL_ACE); - console.log("===to new turn==="); - await game.toNextTurn(); - battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); }, 20000); it("double - wild vs only 1 on player side", async () => { game.override.battleType("double"); - game.override.startingWave(3); await game.classicMode.runToSummon([Species.MIGHTYENA]); - await game.phaseInterceptor.to(CommandPhase, false); + await game.phaseInterceptor.to("CommandPhase", false); + const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-1); @@ -315,7 +257,6 @@ describe("Abilities - Intimidate", () => { it("double - wild vs only 1 alive on player side", async () => { game.override.battleType("double"); - game.override.startingWave(3); await game.runToTitle(); game.onNextPrompt("TitlePhase", Mode.TITLE, () => { @@ -330,9 +271,11 @@ describe("Abilities - Intimidate", () => { await game.phaseInterceptor.run(EncounterPhase); - await game.phaseInterceptor.to(CommandPhase, false); + await game.phaseInterceptor.to("CommandPhase", false); + const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats; expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-1); diff --git a/src/test/evolution.test.ts b/src/test/evolution.test.ts index f9123cf6d9a..5844e92ab8d 100644 --- a/src/test/evolution.test.ts +++ b/src/test/evolution.test.ts @@ -2,9 +2,10 @@ import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } fr import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; +import * as Utils from "#app/utils"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { SPLASH_ONLY } from "./utils/testUtils"; describe("Evolution", () => { @@ -148,4 +149,28 @@ describe("Evolution", () => { expect(cyndaquil.hp).toBeGreaterThan(hpBefore); expect(cyndaquil.hp).toBeLessThan(cyndaquil.getMaxHp()); }, TIMEOUT); + + it("should handle rng-based split evolution", async () => { + /* this test checks to make sure that tandemaus will + * evolve into a 3 family maushold 25% of the time + * and a 4 family maushold the other 75% of the time + * This is done by using the getEvolution method in pokemon.ts + * getEvolution will give back the form that the pokemon can evolve into + * It does this by checking the pokemon conditions in pokemon-forms.ts + * For tandemaus, the conditions are random due to a randSeedInt(4) + * If the value is 0, it's a 3 family maushold, whereas if the value is + * 1, 2 or 3, it's a 4 family maushold + */ + await game.startBattle([Species.TANDEMAUS]); // starts us off with a tandemaus + const playerPokemon = game.scene.getPlayerPokemon()!; + playerPokemon.level = 25; // tandemaus evolves at level 25 + vi.spyOn(Utils, "randSeedInt").mockReturnValue(0); // setting the random generator to be 0 to force a three family maushold + const threeForm = playerPokemon.getEvolution()!; + expect(threeForm.evoFormKey).toBe("three"); // as per pokemon-forms, the evoFormKey for 3 family mausholds is "three" + for (let f = 1; f < 4; f++) { + vi.spyOn(Utils, "randSeedInt").mockReturnValue(f); // setting the random generator to 1, 2 and 3 to force 4 family mausholds + const fourForm = playerPokemon.getEvolution()!; + expect(fourForm.evoFormKey).toBe(null); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is null + } + }, TIMEOUT); }); diff --git a/src/test/evolutions/evolutions.test.ts b/src/test/evolutions/evolutions.test.ts deleted file mode 100644 index 2028764115c..00000000000 --- a/src/test/evolutions/evolutions.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as Utils from "#app/utils"; -import { Species } from "#enums/species"; -import GameManager from "#test/utils/gameManager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; - -describe("Evolution tests", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - }); - - it("tandemaus evolution form test", async () => { - /* this test checks to make sure that tandemaus will - * evolve into a 3 family maushold 25% of the time - * and a 4 family maushold the other 75% of the time - * This is done by using the getEvolution method in pokemon.ts - * getEvolution will give back the form that the pokemon can evolve into - * It does this by checking the pokemon conditions in pokemon-forms.ts - * For tandemaus, the conditions are random due to a randSeedInt(4) - * If the value is 0, it's a 3 family maushold, whereas if the value is - * 1, 2 or 3, it's a 4 family maushold - */ - await game.startBattle([Species.TANDEMAUS]); // starts us off with a tandemaus - const playerPokemon = game.scene.getPlayerPokemon()!; - playerPokemon.level = 25; // tandemaus evolves at level 25 - vi.spyOn(Utils, "randSeedInt").mockReturnValue(0); // setting the random generator to be 0 to force a three family maushold - const threeForm = playerPokemon.getEvolution()!; - expect(threeForm.evoFormKey).toBe("three"); // as per pokemon-forms, the evoFormKey for 3 family mausholds is "three" - for (let f = 1; f < 4; f++) { - vi.spyOn(Utils, "randSeedInt").mockReturnValue(f); // setting the random generator to 1, 2 and 3 to force 4 family mausholds - const fourForm = playerPokemon.getEvolution()!; - expect(fourForm.evoFormKey).toBe(null); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is null - } - }, 5000); -}); diff --git a/src/test/utils/misc.test.ts b/src/test/misc.test.ts similarity index 90% rename from src/test/utils/misc.test.ts rename to src/test/misc.test.ts index d7c10144ead..1052a282a64 100644 --- a/src/test/utils/misc.test.ts +++ b/src/test/misc.test.ts @@ -30,7 +30,7 @@ describe("Test misc", () => { return response.json(); }).then(data => { spy(); // Call the spy function - expect(data).toEqual({"username":"greenlamp", "lastSessionSlot":0}); + expect(data).toEqual({ "username": "greenlamp", "lastSessionSlot": 0 }); }); expect(spy).toHaveBeenCalled(); }); @@ -43,7 +43,7 @@ describe("Test misc", () => { return response.json(); }).then(data => { spy(); // Call the spy function - expect(data).toEqual({"username":"greenlamp", "lastSessionSlot":0}); + expect(data).toEqual({ "username": "greenlamp", "lastSessionSlot": 0 }); }); expect(spy).toHaveBeenCalled(); }); @@ -54,7 +54,7 @@ describe("Test misc", () => { expect(response.ok).toBe(true); expect(response.status).toBe(200); - expect(data).toEqual({"username":"greenlamp", "lastSessionSlot":0}); + expect(data).toEqual({ "username": "greenlamp", "lastSessionSlot": 0 }); }); it("test apifetch mock sync", async () => { diff --git a/src/test/moves/spikes.test.ts b/src/test/moves/spikes.test.ts index c4096111c6f..05ea717ebbe 100644 --- a/src/test/moves/spikes.test.ts +++ b/src/test/moves/spikes.test.ts @@ -1,10 +1,10 @@ -import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; describe("Moves - Spikes", () => { @@ -23,93 +23,61 @@ describe("Moves - Spikes", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.scene.battleStyle = 1; - game.override.battleType("single"); - game.override.enemySpecies(Species.RATTATA); - game.override.enemyAbility(Abilities.HYDRATION); - game.override.enemyPassiveAbility(Abilities.HYDRATION); - game.override.ability(Abilities.HYDRATION); - game.override.passiveAbility(Abilities.HYDRATION); - game.override.startingWave(3); - game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); - game.override.moveset([Moves.SPIKES, Moves.SPLASH, Moves.ROAR]); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .ability(Abilities.BALL_FETCH) + .enemyMoveset(SPLASH_ONLY) + .moveset([Moves.SPIKES, Moves.SPLASH, Moves.ROAR]); }); - it("single - wild - stay on field - no damage", async () => { - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, true); - const initialHp = game.scene.getParty()[0].hp; - expect(game.scene.getParty()[0].hp).toBe(initialHp); + it("should not damage the team that set them", async () => { + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + game.move.select(Moves.SPIKES); await game.toNextTurn(); + game.move.select(Moves.SPLASH); await game.toNextTurn(); - expect(game.scene.getParty()[0].hp).toBe(initialHp); - }, 20000); - - it("single - wild - take some damage", async () => { - // player set spikes on the field and switch back to back - // opponent do splash for 2 turns - // nobody should take damage - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, false); - - const initialHp = game.scene.getParty()[0].hp; - game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase, false); game.doSwitchPokemon(1); - await game.phaseInterceptor.run(CommandPhase); - await game.phaseInterceptor.to(CommandPhase, false); + await game.toNextTurn(); - expect(game.scene.getParty()[0].hp).toBe(initialHp); + game.doSwitchPokemon(1); + await game.toNextTurn(); + + const player = game.scene.getParty()[0]; + expect(player.hp).toBe(player.getMaxHp()); }, 20000); - it("trainer - wild - force switch opponent - should take damage", async () => { + it("should damage opposing pokemon that are forced to switch in", async () => { game.override.startingWave(5); - // player set spikes on the field and do splash for 3 turns - // opponent do splash for 4 turns - // nobody should take damage - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, true); - const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + game.move.select(Moves.SPIKES); await game.toNextTurn(); + game.move.select(Moves.ROAR); await game.toNextTurn(); - expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); + + const enemy = game.scene.getEnemyParty()[0]; + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); }, 20000); - it("trainer - wild - force switch by himself opponent - should take damage", async () => { + it("should damage opposing pokemon that choose to switch in", async () => { game.override.startingWave(5); - game.override.startingLevel(5000); - game.override.enemySpecies(0); - // turn 1: player set spikes, opponent do splash - // turn 2: player do splash, opponent switch pokemon - // opponent pokemon should trigger spikes and lose HP - await game.classicMode.runToSummon([ - Species.MIGHTYENA, - Species.POOCHYENA, - ]); - await game.phaseInterceptor.to(CommandPhase, true); - const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; + await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); + game.move.select(Moves.SPIKES); await game.toNextTurn(); - game.forceOpponentToSwitch(); game.move.select(Moves.SPLASH); + game.forceOpponentToSwitch(); await game.toNextTurn(); - expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); + + const enemy = game.scene.getEnemyParty()[0]; + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); }, 20000); }); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index a9a71d16bf7..6724e05521c 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -139,7 +139,7 @@ export default class GameManager { this.scene.hpBarSpeed = 3; this.scene.enableTutorials = false; this.scene.gameData.gender = PlayerGender.MALE; // set initial player gender - + this.scene.battleStyle = this.settings.battleStyle; } /** diff --git a/src/test/utils/helpers/settingsHelper.ts b/src/test/utils/helpers/settingsHelper.ts index 76ffafdbe10..8fca2a34d47 100644 --- a/src/test/utils/helpers/settingsHelper.ts +++ b/src/test/utils/helpers/settingsHelper.ts @@ -1,16 +1,30 @@ import { PlayerGender } from "#app/enums/player-gender"; +import { BattleStyle } from "#app/enums/battle-style"; import { GameManagerHelper } from "./gameManagerHelper"; /** * Helper to handle settings for tests */ export class SettingsHelper extends GameManagerHelper { + private _battleStyle: BattleStyle = BattleStyle.SET; + + get battleStyle(): BattleStyle { + return this._battleStyle; + } + + /** + * Change the battle style to Switch or Set mode (tests default to {@linkcode BattleStyle.SET}) + * @param mode {@linkcode BattleStyle.SWITCH} or {@linkcode BattleStyle.SET} + */ + set battleStyle(mode: BattleStyle.SWITCH | BattleStyle.SET) { + this._battleStyle = mode; + } /** * Disable/Enable type hints settings * @param enable true to enabled, false to disabled */ - typeHints(enable: boolean) { + typeHints(enable: boolean): void { this.game.scene.typeHints = enable; this.log(`Type Hints ${enable? "enabled" : "disabled"}` ); } From e29f1fe5fd5f8aceb0a69ae1b25305ad2267aeaa Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:39:26 -0700 Subject: [PATCH 018/108] [Bug] Fix some trapping moves' interactions with Ghost-type Pokemon (#3936) * Fix secondary effects to trapping moves not applying to Ghost types * Docs for `isTrapped` * more `isTrapped` cleanup * Remove .js from imports --- src/data/battler-tags.ts | 23 ++++++++++- src/data/move.ts | 2 +- src/enums/battler-tag-type.ts | 1 + src/field/pokemon.ts | 26 +++++++++++- src/phases/command-phase.ts | 68 ++++++++++++++----------------- src/phases/enemy-command-phase.ts | 14 ++----- 6 files changed, 82 insertions(+), 52 deletions(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 38db36e86f6..2e280634d5d 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -212,7 +212,7 @@ export class TrappedTag extends BattlerTag { canAdd(pokemon: Pokemon): boolean { const isGhost = pokemon.isOfType(Type.GHOST); - const isTrapped = pokemon.getTag(BattlerTagType.TRAPPED); + const isTrapped = pokemon.getTag(TrappedTag); return !isTrapped && !isGhost; } @@ -245,6 +245,23 @@ export class TrappedTag extends BattlerTag { } } +/** + * BattlerTag implementing No Retreat's trapping effect. + * This is treated separately from other trapping effects to prevent + * Ghost-type Pokemon from being able to reuse the move. + * @extends TrappedTag + */ +class NoRetreatTag extends TrappedTag { + constructor(sourceId: number) { + super(BattlerTagType.NO_RETREAT, BattlerTagLapseType.CUSTOM, 0, Moves.NO_RETREAT, sourceId); + } + + /** overrides {@linkcode TrappedTag.apply}, removing the Ghost-type condition */ + canAdd(pokemon: Pokemon): boolean { + return !pokemon.getTag(TrappedTag); + } +} + /** * BattlerTag that represents the {@link https://bulbapedia.bulbagarden.net/wiki/Flinch Flinch} status condition */ @@ -864,7 +881,7 @@ export abstract class DamagingTrapTag extends TrappedTag { } canAdd(pokemon: Pokemon): boolean { - return !pokemon.isOfType(Type.GHOST) && !pokemon.findTag(t => t instanceof DamagingTrapTag); + return !pokemon.getTag(TrappedTag); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -1883,6 +1900,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new DrowsyTag(); case BattlerTagType.TRAPPED: return new TrappedTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); + case BattlerTagType.NO_RETREAT: + return new NoRetreatTag(sourceId); case BattlerTagType.BIND: return new BindTag(turnCount, sourceId); case BattlerTagType.WRAP: diff --git a/src/data/move.ts b/src/data/move.ts index 95d306a61ba..3f87fc68b89 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -8553,7 +8553,7 @@ export function initMoves() { .partial(), new SelfStatusMove(Moves.NO_RETREAT, Type.FIGHTING, -1, 5, -1, 0, 8) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) - .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, false, 1) + .attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false) .condition((user, target, move) => user.getTag(TrappedTag)?.sourceMove !== Moves.NO_RETREAT), // fails if the user is currently trapped by No Retreat new StatusMove(Moves.TAR_SHOT, Type.ROCK, 100, 15, -1, 0, 8) .attr(StatChangeAttr, BattleStat.SPD, -1) diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 73580a107b2..20ceb1b331f 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -71,4 +71,5 @@ export enum BattlerTagType { BEAK_BLAST_CHARGING = "BEAK_BLAST_CHARGING", SHELL_TRAP = "SHELL_TRAP", DRAGON_CHEER = "DRAGON_CHEER", + NO_RETREAT = "NO_RETREAT", } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b1fcd512e35..d8acddecedf 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -18,11 +18,11 @@ import { Status, StatusEffect, getRandomStatus } from "../data/status-effect"; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions"; import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms"; import { BattleStat } from "../data/battle-stat"; -import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag, DragonCheerTag, CritBoostTag } from "../data/battler-tags"; +import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag } from "../data/battler-tags"; import { WeatherType } from "../data/weather"; import { TempBattleStat } from "../data/temp-battle-stat"; import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr } from "../data/ability"; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "../data/ability"; import PokemonData from "../system/pokemon-data"; import { BattlerIndex } from "../battle"; import { Mode } from "../ui/ui"; @@ -1210,6 +1210,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.MAGNET_RISEN) && !this.getTag(SemiInvulnerableTag)); } + /** + * Determines whether this Pokemon is prevented from running or switching due + * to effects from moves and/or abilities. + * @param trappedAbMessages `string[]` If defined, ability trigger messages + * (e.g. from Shadow Tag) are forwarded through this array. + * @param simulated `boolean` if `true`, applies abilities via simulated calls. + * @returns + */ + isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean { + if (this.isOfType(Type.GHOST)) { + return false; + } + + const trappedByAbility = new Utils.BooleanHolder(false); + + this.scene.getEnemyField()!.forEach(enemyPokemon => + applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trappedByAbility, this, trappedAbMessages, simulated) + ); + + return (trappedByAbility.value || !!this.getTag(TrappedTag)); + } + /** * Calculates the type of a move when used by this Pokemon after * type-changing move and ability attributes have applied. diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 68ede826d95..9681a6eeee8 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -1,21 +1,18 @@ -import BattleScene from "#app/battle-scene.js"; -import { TurnCommand, BattleType } from "#app/battle.js"; -import { applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "#app/data/ability.js"; -import { TrappedTag, EncoreTag } from "#app/data/battler-tags.js"; -import { MoveTargetSet, getMoveTargets } from "#app/data/move.js"; -import { speciesStarters } from "#app/data/pokemon-species.js"; -import { Type } from "#app/data/type.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Biome } from "#app/enums/biome.js"; -import { Moves } from "#app/enums/moves.js"; -import { PokeballType } from "#app/enums/pokeball.js"; -import { FieldPosition, PlayerPokemon } from "#app/field/pokemon.js"; -import { getPokemonNameWithAffix } from "#app/messages.js"; -import { Command } from "#app/ui/command-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import BattleScene from "#app/battle-scene"; +import { TurnCommand, BattleType } from "#app/battle"; +import { TrappedTag, EncoreTag } from "#app/data/battler-tags"; +import { MoveTargetSet, getMoveTargets } from "#app/data/move"; +import { speciesStarters } from "#app/data/pokemon-species"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Biome } from "#app/enums/biome"; +import { Moves } from "#app/enums/moves"; +import { PokeballType } from "#app/enums/pokeball"; +import { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { Command } from "#app/ui/command-ui-handler"; +import { Mode } from "#app/ui/ui"; import i18next from "i18next"; -import * as Utils from "#app/utils.js"; import { FieldPhase } from "./field-phase"; import { SelectTargetPhase } from "./select-target-phase"; @@ -77,7 +74,6 @@ export class CommandPhase extends FieldPhase { handleCommand(command: Command, cursor: integer, ...args: any[]): boolean { const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; - const enemyField = this.scene.getEnemyField(); let success: boolean; switch (command) { @@ -184,14 +180,9 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { - const trapTag = playerPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; - const trapped = new Utils.BooleanHolder(false); const batonPass = isSwitch && args[0] as boolean; const trappedAbMessages: string[] = []; - if (!batonPass) { - enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, trappedAbMessages, true)); - } - if (batonPass || (!trapTag && !trapped.value)) { + if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) { this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } : { command: Command.RUN }; @@ -199,14 +190,27 @@ export class CommandPhase extends FieldPhase { if (!isSwitch && this.fieldIndex) { this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } - } else if (trapTag) { - if (trapTag.sourceMove === Moves.INGRAIN && trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId)?.isOfType(Type.GHOST)) { - success = true; + } else if (trappedAbMessages.length > 0) { + if (!isSwitch) { + this.scene.ui.setMode(Mode.MESSAGE); + } + this.scene.ui.showText(trappedAbMessages[0], null, () => { + this.scene.ui.showText("", 0); + if (!isSwitch) { + this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); + } + }, null, true); + } else { + const trapTag = playerPokemon.getTag(TrappedTag); + + // trapTag should be defined at this point, but just in case... + if (!trapTag) { this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } : { command: Command.RUN }; break; } + if (!isSwitch) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); @@ -224,16 +228,6 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); } }, null, true); - } else if (trapped.value && trappedAbMessages.length > 0) { - if (!isSwitch) { - this.scene.ui.setMode(Mode.MESSAGE); - } - this.scene.ui.showText(trappedAbMessages[0], null, () => { - this.scene.ui.showText("", 0); - if (!isSwitch) { - this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); - } - }, null, true); } } break; diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts index 5277b2666c7..d9bb08d6fae 100644 --- a/src/phases/enemy-command-phase.ts +++ b/src/phases/enemy-command-phase.ts @@ -1,9 +1,6 @@ -import BattleScene from "#app/battle-scene.js"; -import { BattlerIndex } from "#app/battle.js"; -import { applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "#app/data/ability.js"; -import { TrappedTag } from "#app/data/battler-tags.js"; -import { Command } from "#app/ui/command-ui-handler.js"; -import * as Utils from "#app/utils.js"; +import BattleScene from "#app/battle-scene"; +import { BattlerIndex } from "#app/battle"; +import { Command } from "#app/ui/command-ui-handler"; import { FieldPhase } from "./field-phase"; /** @@ -45,10 +42,7 @@ export class EnemyCommandPhase extends FieldPhase { if (trainer && !enemyPokemon.getMoveQueue().length) { const opponents = enemyPokemon.getOpponents(); - const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; - const trapped = new Utils.BooleanHolder(false); - opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, [""], true)); - if (!trapTag && !trapped.value) { + if (!enemyPokemon.isTrapped()) { const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); if (partyMemberScores.length) { From 781bfd831f1b137bf2d0834917cd3177c67ae689 Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:55:07 -0400 Subject: [PATCH 019/108] [Localization] pokemon names, party-ui-handler, pokemon-info and pokemon-info-container localizations for italian (#3836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translate pokemon-info-container.json via GitLocalize * Translate pokemon-info.json via GitLocalize * Translate party-ui-handler.json via GitLocalize * Translate pokemon.json via GitLocalize --------- Co-authored-by: Niccolò --- src/locales/it/party-ui-handler.json | 87 ++++++++++++---------- src/locales/it/pokemon-info-container.json | 5 +- src/locales/it/pokemon.json | 24 +++++- 3 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/locales/it/party-ui-handler.json b/src/locales/it/party-ui-handler.json index f5582e5b630..95466779727 100644 --- a/src/locales/it/party-ui-handler.json +++ b/src/locales/it/party-ui-handler.json @@ -1,42 +1,47 @@ { - "SEND_OUT": "Manda in campo", - "SUMMARY": "Sommario", - "CANCEL": "Annulla", - "RELEASE": "Rilascia", - "APPLY": "Applica", - "TEACH": "Insegna", - "SPLICE": "Unisci", - "UNSPLICE": "Dividi", - "ACTIVATE": "Attiva", - "DEACTIVATE": "Disattiva", - "TRANSFER": "Trasferisci", - "ALL": "Tutto", - "PASS_BATON": "Staffetta", - "UNPAUSE_EVOLUTION": "Consenti evoluzione", - "REVIVE": "Revitalizza", - "RENAME": "Rinomina", - "choosePokemon": "Scegli un Pokémon.", - "doWhatWithThisPokemon": "Hai selezionato questo Pokémon.", - "noEnergy": "{{pokemonName}} non ha più energie\nper lottare!", - "hasEnergy": "{{pokemonName}} ha ancora energie\nper lottare!", - "cantBeUsed": "{{pokemonName}} non può essere usato\nin questa sfida!", - "tooManyItems": "{{pokemonName}} possiede già\nquest'oggetto in abbondanza!", - "anyEffect": "Non avrebbe alcun effetto.", - "unpausedEvolutions": "{{pokemonName}} può di nuovo evolversi.", - "unspliceConfirmation": "Vuoi davvero dividere {{fusionName}}\nda {{pokemonName}}? {{fusionName}} andrà perduto.", - "wasReverted": "{{fusionName}} è tornato ad essere {{pokemonName}}.", - "releaseConfirmation": "Vuoi davvero liberare {{pokemonName}}?", - "releaseInBattle": "Non puoi liberare un Pokémon che sta combattendo!", - "selectAMove": "Scegli una mossa.", - "changeQuantity": "Scegli un oggetto da trasferire.\nUsa < e > per cambiarne la quantità.", - "selectAnotherPokemonToSplice": "Scegli un altro Pokémon da unire.", - "cancel": "Annulla", - "goodbye": "Addio, {{pokemonName}}!", - "byebye": "Ciao ciao, {{pokemonName}}!", - "farewell": "Arrivederci, {{pokemonName}}!", - "soLong": "È stato bello, {{pokemonName}}!", - "thisIsWhereWePart": "Le nostre strade si dividono, {{pokemonName}}!", - "illMissYou": "Mi mancherai, {{pokemonName}}!", - "illNeverForgetYou": "Non ti dimenticherò, {{pokemonName}}!", - "untilWeMeetAgain": "Alla prossima, {{pokemonName}}!" - } \ No newline at end of file + "SEND_OUT": "Manda in campo", + "SUMMARY": "Sommario", + "CANCEL": "Annulla", + "RELEASE": "Rilascia", + "APPLY": "Applica", + "TEACH": "Insegna", + "SPLICE": "Unisci", + "UNSPLICE": "Dividi", + "ACTIVATE": "Attiva", + "DEACTIVATE": "Disattiva", + "TRANSFER": "Trasferisci", + "ALL": "Tutto", + "PASS_BATON": "Staffetta", + "UNPAUSE_EVOLUTION": "Consenti evoluzione", + "REVIVE": "Revitalizza", + "RENAME": "Rinomina", + "choosePokemon": "Scegli un Pokémon.", + "doWhatWithThisPokemon": "Hai selezionato questo Pokémon.", + "noEnergy": "{{pokemonName}} non ha più energie\nper lottare!", + "hasEnergy": "{{pokemonName}} ha ancora energie\nper lottare!", + "cantBeUsed": "{{pokemonName}} non può essere usato\nin questa sfida!", + "tooManyItems": "{{pokemonName}} possiede già\nquest'oggetto in abbondanza!", + "anyEffect": "Non avrebbe alcun effetto.", + "unpausedEvolutions": "{{pokemonName}} può di nuovo evolversi.", + "unspliceConfirmation": "Vuoi davvero dividere {{fusionName}}\nda {{pokemonName}}? {{fusionName}} andrà perduto.", + "wasReverted": "{{fusionName}} è tornato ad essere {{pokemonName}}.", + "releaseConfirmation": "Vuoi davvero liberare {{pokemonName}}?", + "releaseInBattle": "Non puoi liberare un Pokémon che sta combattendo!", + "selectAMove": "Scegli una mossa.", + "changeQuantity": "Scegli un oggetto da trasferire.\nUsa < e > per cambiarne la quantità.", + "selectAnotherPokemonToSplice": "Scegli un altro Pokémon da unire.", + "cancel": "Annulla", + "able": "Sì!", + "notAble": "No!", + "learned": "La conosce!", + "goodbye": "Addio, {{pokemonName}}!", + "byebye": "Ciao ciao, {{pokemonName}}!", + "farewell": "Arrivederci, {{pokemonName}}!", + "soLong": "È stato bello, {{pokemonName}}!", + "thisIsWhereWePart": "Le nostre strade si dividono, {{pokemonName}}!", + "illMissYou": "Mi mancherai, {{pokemonName}}!", + "illNeverForgetYou": "Non ti dimenticherò, {{pokemonName}}!", + "untilWeMeetAgain": "Alla prossima, {{pokemonName}}!", + "sayonara": "Sayonara, {{pokemonName}}!", + "smellYaLater": "Ci becchiamo, {{pokemonName}}!" +} diff --git a/src/locales/it/pokemon-info-container.json b/src/locales/it/pokemon-info-container.json index c3cc8d49ce1..f3de9081ebc 100644 --- a/src/locales/it/pokemon-info-container.json +++ b/src/locales/it/pokemon-info-container.json @@ -2,5 +2,6 @@ "moveset": "Set di mosse", "gender": "Genere:", "ability": "Abilità:", - "nature": "Natura:" -} \ No newline at end of file + "nature": "Natura:", + "form": "Forma:" +} diff --git a/src/locales/it/pokemon.json b/src/locales/it/pokemon.json index 9e26dfeeb6e..dcc4cc7f310 100644 --- a/src/locales/it/pokemon.json +++ b/src/locales/it/pokemon.json @@ -1 +1,23 @@ -{} \ No newline at end of file +{ + "type_null": "Tipo Zero", + "great_tusk": "Grandizanne", + "scream_tail": "Codaurlante", + "brute_bonnet": "Fungofurioso", + "flutter_mane": "Crinealato", + "slither_wing": "Alirasenti", + "sandy_shocks": "Peldisabbia", + "iron_treads": "Solcoferreo", + "iron_bundle": "Saccoferreo", + "iron_hands": "Manoferrea", + "iron_jugulis": "Colloferreo", + "iron_moth": "Falenaferrea", + "iron_thorns": "Spineferree", + "roaring_moon": "Lunaruggente", + "iron_valiant": "Eroeferreo", + "walking_wake": "Acquecrespe", + "iron_leaves": "Fogliaferrea", + "gouging_fire": "Vampeaguzze", + "raging_bolt": "Furiatonante", + "iron_boulder": "Massoferreo", + "iron_crown": "Capoferreo" +} From 08fe9e1e2134045b1f542875168b0062489410a5 Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:57:28 -0400 Subject: [PATCH 020/108] [Localization(fr)] Translate trainer-classes.json (#3882) Co-authored-by: Lugiad --- src/locales/fr/trainer-classes.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/locales/fr/trainer-classes.json b/src/locales/fr/trainer-classes.json index cebdadf09e9..b7027cf544f 100644 --- a/src/locales/fr/trainer-classes.json +++ b/src/locales/fr/trainer-classes.json @@ -101,8 +101,8 @@ "workers": "Ouvriers", "youngster": "Gamin", "rocket_grunt": "Sbire de la Team Rocket", - "rocket_grunt_female": "Sbire de la Team Rocket", "rocket_grunts": "Sbires de la Team Rocket", + "rocket_grunt_female": "Sbire de la Team Rocket", "magma_grunt": "Sbire de la Team Magma", "magma_grunt_female": "Sbire de la Team Magma", "magma_grunts": "Sbires de la Team Magma", @@ -123,6 +123,7 @@ "aether_grunts": "Employés de la Fondation Æther", "skull_grunt": "Sbire de la Team Skull", "skull_grunt_female": "Sbire de la Team Skull", + "skull_grunts": "Sbires de la Team Skull", "macro_grunt": "Employé de Macro Cosmos", "macro_grunt_female": "Employée de Macro Cosmos", "macro_grunts": "Employés de Macro Cosmos" From 8edb9ca65b1b8a02c1a33421cf04605e22891a2a Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:57:48 -0400 Subject: [PATCH 021/108] [Localizaiton(pt_br)] Translate pokemon-summary.json via GitLocalize (#3868) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Ricardo --- src/locales/pt_BR/pokemon-summary.json | 29 +++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/locales/pt_BR/pokemon-summary.json b/src/locales/pt_BR/pokemon-summary.json index 62add589847..4c427dbac4f 100644 --- a/src/locales/pt_BR/pokemon-summary.json +++ b/src/locales/pt_BR/pokemon-summary.json @@ -13,5 +13,32 @@ "metFragment": { "normal": "encontrado no Nv.{{level}},\n{{biome}}.", "apparently": "aparentemente encontrado no Nv.{{level}},\n{{biome}}." + }, + "natureFragment": { + "Hardy": "{{nature}}", + "Lonely": "{{nature}}", + "Brave": "{{nature}}", + "Adamant": "{{nature}}", + "Naughty": "{{nature}}", + "Bold": "{{nature}}", + "Docile": "{{nature}}", + "Relaxed": "{{nature}}", + "Impish": "{{nature}}", + "Lax": "{{nature}}", + "Timid": "{{nature}}", + "Hasty": "{{nature}}", + "Serious": "{{nature}}", + "Jolly": "{{nature}}", + "Naive": "{{nature}}", + "Modest": "{{nature}}", + "Mild": "{{nature}}", + "Quiet": "{{nature}}", + "Bashful": "{{nature}}", + "Rash": "{{nature}}", + "Calm": "{{nature}}", + "Gentle": "{{nature}}", + "Sassy": "{{nature}}", + "Careful": "{{nature}}", + "Quirky": "{{nature}}" } -} \ No newline at end of file +} From c4c9cf939eaa9118bf0350bae495a6bdd6068401 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:09:46 -0700 Subject: [PATCH 022/108] [Bug] Moved RNG call if the Pokemon's ability hasn't been determined (#3966) Co-authored-by: frutescens --- src/field/pokemon.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d8acddecedf..16ad96f61cc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -133,9 +133,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); } - const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); - const randAbilityIndex = Utils.randSeedInt(2); - this.species = species; this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; @@ -146,6 +143,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.abilityIndex = abilityIndex; // Use the provided ability index if it is defined } else { // If abilityIndex is not provided, determine it based on species and hidden ability + const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); + const randAbilityIndex = Utils.randSeedInt(2); if (species.abilityHidden && hasHiddenAbility) { // If the species has a hidden ability and the hidden ability is present this.abilityIndex = 2; From 0671a244a86c6f9711c64586d3932f3bc7db9b0b Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:16:54 -0400 Subject: [PATCH 023/108] [Localisation] [ES] Reviewed and finished ability.json and bgm-name.json (#3920) * Translate ability.json via GitLocalize * Translate ability.json via GitLocalize * Translate bgm-name.json via GitLocalize --------- Co-authored-by: Asdar Co-authored-by: LilyAlternis --- src/locales/es/ability.json | 752 +++++++++++++++++------------------ src/locales/es/bgm-name.json | 218 +++++----- 2 files changed, 487 insertions(+), 483 deletions(-) diff --git a/src/locales/es/ability.json b/src/locales/es/ability.json index 807cc755c94..598694f441e 100644 --- a/src/locales/es/ability.json +++ b/src/locales/es/ability.json @@ -1,1242 +1,1242 @@ { "stench": { - "name": "Hedor", + "name": "Hedor", "description": "Puede amedrentar a un Pokémon al atacarlo debido al mal olor que emana." }, "drizzle": { - "name": "Llovizna", + "name": "Llovizna", "description": "Hace que llueva al entrar en combate." }, "speedBoost": { - "name": "Impulso", + "name": "Impulso", "description": "Aumenta su Velocidad en cada turno." }, "battleArmor": { - "name": "Armadura Batalla", + "name": "Armadura Batalla", "description": "La robusta coraza que lo protege bloquea los golpes críticos." }, "sturdy": { - "name": "Robustez", + "name": "Robustez", "description": "El Pokémon no puede debilitarse de un solo golpe cuando tiene los PS al máximo. También evita los movimientos fulminantes." }, "damp": { - "name": "Humedad", + "name": "Humedad", "description": "Aumenta la humedad del entorno y evita que se puedan utilizar movimientos explosivos, tales como Autodestrucción." }, "limber": { - "name": "Flexibilidad", + "name": "Flexibilidad", "description": "Evita ser paralizado gracias a la flexibilidad de su cuerpo." }, "sandVeil": { - "name": "Velo Arena", + "name": "Velo Arena", "description": "Aumenta su Evasión durante las tormentas de arena." }, "static": { - "name": "Elec. Estática", + "name": "Elec. Estática", "description": "La electricidad estática que lo envuelve puede paralizar al Pokémon que lo ataque con un movimiento de contacto." }, "voltAbsorb": { - "name": "Absorbe Elec", + "name": "Absorbe Elec", "description": "Si lo alcanza un movimiento de tipo Eléctrico, recupera PS en vez de sufrir daño." }, "waterAbsorb": { - "name": "Absorbe Agua", + "name": "Absorbe Agua", "description": "Si lo alcanza un movimiento de tipo Agua, recupera PS en vez de sufrir daño." }, "oblivious": { - "name": "Despiste", + "name": "Despiste", "description": "Su indiferencia evita que sea provocado, caiga presa del enamoramiento o sufra los efectos de Intimidación." }, "cloudNine": { - "name": "Aclimatación", + "name": "Aclimatación", "description": "Anula todos los efectos del tiempo atmosférico." }, "compoundEyes": { - "name": "Ojo Compuesto", + "name": "Ojo Compuesto", "description": "Aumenta la precisión de sus movimientos." }, "insomnia": { - "name": "Insomnio", + "name": "Insomnio", "description": "Su resistencia al sueño le impide quedarse dormido." }, "colorChange": { - "name": "Cambio Color", + "name": "Cambio Color", "description": "Adopta el tipo del último movimiento del que es blanco." }, "immunity": { - "name": "Inmunidad", + "name": "Inmunidad", "description": "Su sistema inmunitario evita el envenenamiento." }, "flashFire": { - "name": "Absorbe Fuego", + "name": "Absorbe Fuego", "description": "Si lo alcanza algún movimiento de tipo Fuego, potencia sus propios movimientos de dicho tipo." }, "shieldDust": { - "name": "Polvo Escudo", + "name": "Polvo Escudo", "description": "El polvo de escamas que lo envuelve lo protege de los efectos secundarios de los ataques recibidos." }, "ownTempo": { - "name": "Ritmo Propio", + "name": "Ritmo Propio", "description": "Como le gusta hacer las cosas a su manera, no le afecta la confusión ni sufre los efectos de Intimidación." }, "suctionCups": { - "name": "Ventosas", + "name": "Ventosas", "description": "Sus ventosas se aferran al suelo, con lo cual anula movimientos y objetos que fuercen el cambio de Pokémon." }, "intimidate": { - "name": "Intimidación", + "name": "Intimidación", "description": "Al entrar en combate, amilana al rival de tal manera que reduce su Ataque." }, "shadowTag": { - "name": "Sombra Trampa", + "name": "Sombra Trampa", "description": "Pisa la sombra del rival para impedir que huya o lo cambien por otro." }, "roughSkin": { - "name": "Piel Tosca", + "name": "Piel Tosca", "description": "Hiere con su piel áspera al Pokémon que lo ataque con un movimiento de contacto." }, "wonderGuard": { - "name": "Superguarda", + "name": "Superguarda", "description": "Gracias a un poder misterioso, solo le hacen daño los movimientos supereficaces." }, "levitate": { - "name": "Levitación", + "name": "Levitación", "description": "Su capacidad de flotar sobre el suelo le proporciona inmunidad frente a los movimientos de tipo Tierra." }, "effectSpore": { - "name": "Efecto Espora", + "name": "Efecto Espora", "description": "Puede dormir, envenenar o paralizar al Pokémon que lo ataque con un movimiento de contacto." }, "synchronize": { - "name": "Sincronía", + "name": "Sincronía", "description": "Contagia el envenenamiento, las quemaduras o la parálisis al Pokémon que le cause ese estado." }, "clearBody": { - "name": "Cuerpo Puro", + "name": "Cuerpo Puro", "description": "Evita que se reduzcan sus características a causa de movimientos o habilidades de otros Pokémon." }, "naturalCure": { - "name": "Cura Natural", + "name": "Cura Natural", "description": "Sus problemas de estado desaparecen cuando se retira del combate." }, "lightningRod": { - "name": "Pararrayos", + "name": "Pararrayos", "description": "Atrae y neutraliza los movimientos de tipo Eléctrico, que además le aumentan el Ataque Especial." }, "sereneGrace": { - "name": "Dicha", + "name": "Dicha", "description": "Aumenta la probabilidad de que los movimientos causen efectos secundarios." }, "swiftSwim": { - "name": "Nado Rápido", + "name": "Nado Rápido", "description": "Aumenta su Velocidad cuando llueve." }, "chlorophyll": { - "name": "Clorofila", + "name": "Clorofila", "description": "Aumenta su Velocidad cuando hace sol." }, "illuminate": { - "name": "Iluminación", + "name": "Iluminación", "description": "Al iluminar el entorno, evita que su Precisión se reduzca." }, "trace": { - "name": "Calco", + "name": "Calco", "description": "Copia la habilidad del rival al entrar en combate." }, "hugePower": { - "name": "Potencia", + "name": "Potencia", "description": "Duplica la potencia de sus ataques físicos." }, "poisonPoint": { - "name": "Punto Tóxico", + "name": "Punto Tóxico", "description": "Puede envenenar al Pokémon que lo ataque con un movimiento de contacto." }, "innerFocus": { - "name": "Fuerza Mental", + "name": "Fuerza Mental", "description": "Gracias a su profunda concentración, no se amedrenta ante los ataques de otros Pokémon ni sufre los efectos de Intimidación." }, "magmaArmor": { - "name": "Escudo Magma", + "name": "Escudo Magma", "description": "Gracias al magma candente que lo envuelve, no puede ser congelado." }, "waterVeil": { - "name": "Velo Agua", + "name": "Velo Agua", "description": "Evita las quemaduras gracias a la capa de agua que lo envuelve." }, "magnetPull": { - "name": "Imán", + "name": "Imán", "description": "Su magnetismo atrae a los Pokémon de tipo Acero y les impide huir o ser cambiados por otros." }, "soundproof": { - "name": "Insonorizar", + "name": "Insonorizar", "description": "Su aislamiento acústico lo protege de movimientos que usan sonido." }, "rainDish": { - "name": "Cura Lluvia", + "name": "Cura Lluvia", "description": "Recupera PS de forma gradual cuando llueve." }, "sandStream": { - "name": "Chorro Arena", + "name": "Chorro Arena", "description": "Crea una tormenta de arena al entrar en combate." }, "pressure": { - "name": "Presión", + "name": "Presión", "description": "Presiona al rival de tal manera que este consume más PP al usar sus movimientos." }, "thickFat": { - "name": "Sebo", + "name": "Sebo", "description": "Gracias a la gruesa capa de grasa que lo protege, reduce a la mitad el daño que recibe de ataques de tipo Fuego o Hielo." }, "earlyBird": { - "name": "Madrugar", + "name": "Madrugar", "description": "Si se duerme, tardará la mitad de tiempo en despertarse." }, "flameBody": { - "name": "Cuerpo Llama", + "name": "Cuerpo Llama", "description": "Puede quemar al Pokémon que lo ataque con un movimiento de contacto." }, "runAway": { - "name": "Fuga", + "name": "Fuga", "description": "Puede escapar de cualquier Pokémon salvaje." }, "keenEye": { - "name": "Vista Lince", + "name": "Vista Lince", "description": "Su aguda vista evita que su Precisión se reduzca." }, "hyperCutter": { - "name": "Corte Fuerte", + "name": "Corte Fuerte", "description": "Evita que otros Pokémon le reduzcan el Ataque." }, "pickup": { - "name": "Recogida", - "description": "Puede recoger objetos que otros Pokémon hayan usado, o bien aquellos que encuentre en plena aventura." + "name": "Recogida", + "description": "Puede que recoja un objeto del enemigo tras cada batalla, al azar." }, "truant": { - "name": "Pereza", + "name": "Pereza", "description": "Al ejecutar un movimiento, descansará en el turno siguiente." }, "hustle": { - "name": "Entusiasmo", - "description": "Aumenta su Ataque, pero reduce su Precisión." + "name": "Entusiasmo", + "description": "Aumenta su ataque, pero reduce su precisión." }, "cuteCharm": { - "name": "Gran Encanto", + "name": "Gran Encanto", "description": "Puede causar enamoramiento al Pokémon que lo ataque con un movimiento de contacto." }, "plus": { - "name": "Más", - "description": "Aumenta su Ataque Especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." + "name": "Más", + "description": "Aumenta su ataque especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." }, "minus": { - "name": "Menos", - "description": "Aumenta su Ataque Especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." + "name": "Menos", + "description": "Aumenta su ataque especial si un Pokémon aliado tiene la habilidad Más o la habilidad Menos." }, "forecast": { - "name": "Predicción", + "name": "Predicción", "description": "Cambia a tipo Agua, Fuego o Hielo en función del tiempo atmosférico." }, "stickyHold": { - "name": "Viscosidad", + "name": "Viscosidad", "description": "Los objetos se quedan pegados a su cuerpo, por lo que no pueden robárselos." }, "shedSkin": { - "name": "Mudar", + "name": "Mudar", "description": "Puede curar sus problemas de estado al mudar la piel." }, "guts": { - "name": "Agallas", - "description": "Si sufre un problema de estado, se arma de valor y aumenta su Ataque." + "name": "Agallas", + "description": "Si sufre un problema de estado, se arma de valor y aumenta su ataque." }, "marvelScale": { - "name": "Escama Especial", - "description": "Si sufre un problema de estado, sus escamas especiales reaccionan y aumenta su Defensa." + "name": "Escama Especial", + "description": "Si sufre un problema de estado, sus escamas especiales reaccionan y aumenta su defensa." }, "liquidOoze": { - "name": "Viscosecreción", + "name": "Viscosecreción", "description": "Exuda una secreción viscosa y tóxica de intenso hedor que hiere a quienes intentan drenarle PS." }, "overgrow": { - "name": "Espesura", + "name": "Espesura", "description": "Potencia sus movimientos de tipo Planta cuando le quedan pocos PS." }, "blaze": { - "name": "Mar Llamas", + "name": "Mar Llamas", "description": "Potencia sus movimientos de tipo Fuego cuando le quedan pocos PS." }, "torrent": { - "name": "Torrente", + "name": "Torrente", "description": "Potencia sus movimientos de tipo Agua cuando le quedan pocos PS." }, "swarm": { - "name": "Enjambre", + "name": "Enjambre", "description": "Potencia sus movimientos de tipo Bicho cuando le quedan pocos PS." }, "rockHead": { - "name": "Cabeza Roca", + "name": "Cabeza Roca", "description": "No pierde PS al usar movimientos que también hieren al usuario." }, "drought": { - "name": "Sequía", + "name": "Sequía", "description": "El tiempo pasa a ser soleado al entrar en combate." }, "arenaTrap": { - "name": "Trampa Arena", + "name": "Trampa Arena", "description": "Evita que el rival huya o sea cambiado por otro." }, "vitalSpirit": { - "name": "Espíritu Vital", + "name": "Espíritu Vital", "description": "Su determinación le impide quedarse dormido." }, "whiteSmoke": { - "name": "Humo Blanco", + "name": "Humo Blanco", "description": "El humo blanco que lo protege evita que otros Pokémon le reduzcan las características." }, "purePower": { - "name": "Energía Pura", + "name": "Energía Pura", "description": "Duplica la potencia de sus ataques físicos gracias al yoga." }, "shellArmor": { - "name": "Caparazón", + "name": "Caparazón", "description": "La robusta coraza que lo protege bloquea los golpes críticos." }, "airLock": { - "name": "Esclusa de Aire", + "name": "Esclusa de Aire", "description": "Neutraliza todos los efectos del tiempo atmosférico." }, "tangledFeet": { - "name": "Tumbos", - "description": "Aumenta su Evasión si está confuso." + "name": "Tumbos", + "description": "Aumenta su evasión si está confuso." }, "motorDrive": { - "name": "Electromotor", - "description": "Si lo alcanza un movimiento de tipo Eléctrico, aumenta su Velocidad en vez de sufrir daño." + "name": "Electromotor", + "description": "Si lo alcanza un movimiento de tipo Eléctrico, aumenta su velocidad en vez de sufrir daño." }, "rivalry": { - "name": "Rivalidad", + "name": "Rivalidad", "description": "Si el objetivo es del mismo sexo, su competitividad le lleva a infligir más daño. Si es del sexo contrario, en cambio, el daño será menor." }, "steadfast": { - "name": "Impasible", - "description": "Cada vez que se amedrenta, aumenta su Velocidad debido a su voluntad inquebrantable." + "name": "Impasible", + "description": "Cada vez que se amedrenta, aumenta su velocidad debido a su voluntad inquebrantable." }, "snowCloak": { - "name": "Manto Níveo", - "description": "Aumenta su Evasión cuando nieva." + "name": "Manto Níveo", + "description": "Aumenta su evasión cuando nieva." }, "gluttony": { - "name": "Gula", + "name": "Gula", "description": "Cuando sus PS se ven reducidos a la mitad, engulle la baya que normalmente solo se comería cuando le quedasen pocos PS." }, "angerPoint": { - "name": "Irascible", - "description": "Si recibe un golpe crítico, monta en cólera y su Ataque aumenta al máximo." + "name": "Irascible", + "description": "Si recibe un golpe crítico, monta en cólera y su ataque aumenta al máximo." }, "unburden": { - "name": "Liviano", - "description": "Aumenta su Velocidad si usa o pierde el objeto que lleva." + "name": "Liviano", + "description": "Aumenta su velocidad si usa o pierde el objeto que lleva." }, "heatproof": { - "name": "Ignífugo", + "name": "Ignífugo", "description": "Su cuerpo, resistente al calor, reduce a la mitad el daño recibido por movimientos de tipo Fuego." }, "simple": { - "name": "Simple", + "name": "Simple", "description": "Duplica los cambios en las características." }, "drySkin": { - "name": "Piel Seca", + "name": "Piel Seca", "description": "Pierde PS si hace sol y los recupera si llueve o recibe un movimiento de tipo Agua. Los movimientos de tipo Fuego, por su parte, le hacen más daño de lo normal." }, "download": { - "name": "Descarga", - "description": "Compara la Defensa y la Defensa Especial del rival para ver cuál es inferior y aumenta su propio Ataque o Ataque Especial según sea lo más eficaz." + "name": "Descarga", + "description": "Compara la defensa y la defensa especial del rival para ver cuál es inferior y aumenta su propio ataque o ataque especial según sea lo más eficaz." }, "ironFist": { - "name": "Puño Férreo", + "name": "Puño Férreo", "description": "Aumenta la potencia de los movimientos con los puños." }, "poisonHeal": { - "name": "Antídoto", + "name": "Antídoto", "description": "Si resulta envenenado, recupera PS en vez de perderlos." }, "adaptability": { - "name": "Adaptable", + "name": "Adaptable", "description": "Potencia aún más los movimientos cuyo tipo coincida con el suyo." }, "skillLink": { - "name": "Encadenado", + "name": "Encadenado", "description": "Ejecuta siempre los movimientos de ataque múltiple con el número máximo de golpes." }, "hydration": { - "name": "Hidratación", + "name": "Hidratación", "description": "Cura los problemas de estado si está lloviendo." }, "solarPower": { - "name": "Poder Solar", - "description": "Si hace sol, aumenta su Ataque Especial, pero pierde PS en cada turno." + "name": "Poder Solar", + "description": "Si hace sol, aumenta su ataque especial, pero pierde PS en cada turno." }, "quickFeet": { - "name": "Pies Rápidos", - "description": "Aumenta su Velocidad si sufre problemas de estado." + "name": "Pies Rápidos", + "description": "Aumenta su velocidad si sufre problemas de estado." }, "normalize": { - "name": "Normalidad", + "name": "Normalidad", "description": "Hace que todos sus movimientos se vuelvan de tipo Normal y aumenta ligeramente su potencia." }, "sniper": { - "name": "Francotirador", + "name": "Francotirador", "description": "Potencia los golpes críticos que asesta aún más de lo normal." }, "magicGuard": { - "name": "Muro Mágico", + "name": "Muro Mágico", "description": "Solo recibe daño de ataques." }, "noGuard": { - "name": "Indefenso", + "name": "Indefenso", "description": "Al quedar ambos expuestos, tanto sus movimientos como los del Pokémon que lo ataque acertarán siempre." }, "stall": { - "name": "Rezagado", + "name": "Rezagado", "description": "Ejecuta su movimiento tras todos los demás." }, "technician": { - "name": "Experto", + "name": "Experto", "description": "Aumenta la potencia de sus movimientos débiles." }, "leafGuard": { - "name": "Defensa Hoja", + "name": "Defensa Hoja", "description": "Evita los problemas de estado si hace sol." }, "klutz": { - "name": "Zoquete", + "name": "Zoquete", "description": "No puede usar objetos equipados." }, "moldBreaker": { - "name": "Rompemoldes", + "name": "Rompemoldes", "description": "Sus movimientos no se ven afectados por la habilidad del objetivo." }, "superLuck": { - "name": "Afortunado", + "name": "Afortunado", "description": "Su buena suerte aumenta la probabilidad de asestar golpes críticos." }, "aftermath": { - "name": "Detonación", + "name": "Detonación", "description": "Daña al Pokémon que le ha dado el golpe de gracia con un movimiento de contacto." }, "anticipation": { - "name": "Anticipación", + "name": "Anticipación", "description": "Prevé los movimientos peligrosos del rival." }, "forewarn": { - "name": "Alerta", + "name": "Alerta", "description": "Revela uno de los movimientos del rival al entrar en combate." }, "unaware": { - "name": "Ignorante", + "name": "Ignorante", "description": "Pasa por alto los cambios en las características de un Pokémon al atacarlo o recibir daño." }, "tintedLens": { - "name": "Cromolente", + "name": "Cromolente", "description": "Potencia los movimientos que no son muy eficaces, que infligen ahora un daño normal." }, "filter": { - "name": "Filtro", + "name": "Filtro", "description": "Mitiga el daño que le infligen los movimientos supereficaces." }, "slowStart": { - "name": "Inicio Lento", - "description": "Reduce a la mitad su Ataque y su Velocidad durante cinco turnos." + "name": "Inicio Lento", + "description": "Reduce a la mitad su ataque y su velocidad durante cinco turnos." }, "scrappy": { - "name": "Intrépido", + "name": "Intrépido", "description": "Alcanza a Pokémon de tipo Fantasma con movimientos de tipo Normal o Lucha. Además, no sufre los efectos de Intimidación." }, "stormDrain": { - "name": "Colector", - "description": "Atrae y neutraliza los movimientos de tipo Agua, que además le aumentan el Ataque Especial." + "name": "Colector", + "description": "Atrae y neutraliza los movimientos de tipo Agua, que además le aumentan el ataque especial." }, "iceBody": { - "name": "Gélido", + "name": "Gélido", "description": "Recupera PS de forma gradual cuando nieva." }, "solidRock": { - "name": "Roca Sólida", + "name": "Roca Sólida", "description": "Mitiga el daño que le infligen los movimientos supereficaces." }, "snowWarning": { - "name": "Nevada", + "name": "Nevada", "description": "Invoca una nevada al entrar en combate." }, "honeyGather": { - "name": "Recogemiel", - "description": "The Pokémon gathers Honey after a battle. The Honey is then sold for money." + "name": "Recogemiel", + "description": "El Pokémon recoge miel tras cada batalla. La miel se vende inmediatamente por ₽." }, "frisk": { - "name": "Cacheo", + "name": "Cacheo", "description": "Cuando entra en combate, el Pokémon puede comprobar la habilidad de un Pokémon rival." }, "reckless": { - "name": "Audaz", + "name": "Audaz", "description": "Potencia los movimientos que también dañan al usuario." }, "multitype": { - "name": "Multitipo", + "name": "Multitipo", "description": "Cambia su tipo al de la tabla que lleve." }, "flowerGift": { - "name": "Don Floral", - "description": "Si hace sol, aumenta su Ataque y su Defensa Especial, así como los de sus aliados." + "name": "Don Floral", + "description": "Si hace sol, aumenta su ataque y su defensa Especial, así como los de sus aliados." }, "badDreams": { - "name": "Mal Sueño", + "name": "Mal Sueño", "description": "Inflige daño a cualquier rival que esté dormido." }, "pickpocket": { - "name": "Hurto", + "name": "Hurto", "description": "Roba el objeto del Pokémon que lo ataque con un movimiento de contacto." }, "sheerForce": { - "name": "Potencia Bruta", + "name": "Potencia Bruta", "description": "Aumenta la potencia de sus movimientos en detrimento de los efectos secundarios, que se ven anulados." }, "contrary": { - "name": "Respondón", + "name": "Respondón", "description": "Invierte los cambios en las características: bajan cuando les toca subir y suben cuando les toca bajar." }, "unnerve": { - "name": "Nerviosismo", + "name": "Nerviosismo", "description": "Pone nervioso al rival y le impide comer bayas." }, "defiant": { - "name": "Competitivo", - "description": "Aumenta mucho su Ataque cuando el rival le reduce cualquiera de sus características." + "name": "Competitivo", + "description": "Aumenta mucho su ataque cuando el rival le reduce cualquiera de sus características." }, "defeatist": { - "name": "Flaqueza", - "description": "Cuando sus PS se ven reducidos a la mitad, se cansa tanto que su Ataque y su Ataque Especial también se ven reducidos a la mitad." + "name": "Flaqueza", + "description": "Cuando sus PS se ven reducidos a la mitad, se cansa tanto que su ataque y su ataque Especial también se ven reducidos a la mitad." }, "cursedBody": { - "name": "Cuerpo Maldito", + "name": "Cuerpo Maldito", "description": "Puede anular el movimiento usado en su contra." }, "healer": { - "name": "Alma Cura", + "name": "Alma Cura", "description": "A veces cura los problemas de estado de un aliado." }, "friendGuard": { - "name": "Compiescolta", + "name": "Compiescolta", "description": "Reduce el daño que sufren los aliados." }, "weakArmor": { - "name": "Armadura Frágil", - "description": "Al recibir daño de un ataque físico, se reduce su Defensa, pero aumenta mucho su Velocidad." + "name": "Armadura Frágil", + "description": "Al recibir daño de un ataque físico, se reduce su defensa, pero aumenta mucho su velocidad." }, "heavyMetal": { - "name": "Metal Pesado", + "name": "Metal Pesado", "description": "Duplica su peso." }, "lightMetal": { - "name": "Metal Liviano", + "name": "Metal Liviano", "description": "Reduce a la mitad su peso." }, "multiscale": { - "name": "Multiescamas", + "name": "Multiescamas", "description": "Reduce el daño que sufre si sus PS están al máximo." }, "toxicBoost": { - "name": "Ímpetu Tóxico", + "name": "Ímpetu Tóxico", "description": "Aumenta la potencia de sus ataques físicos cuando está envenenado." }, "flareBoost": { - "name": "Ímpetu Ardiente", + "name": "Ímpetu Ardiente", "description": "Aumenta la potencia de sus ataques especiales cuando sufre quemaduras." }, "harvest": { - "name": "Cosecha", + "name": "Cosecha", "description": "Puede reutilizar varias veces una misma baya." }, "telepathy": { - "name": "Telepatía", + "name": "Telepatía", "description": "Elude los ataques de los aliados durante el combate." }, "moody": { - "name": "Veleta", + "name": "Veleta", "description": "Aumenta mucho una característica en cada turno, pero reduce otra." }, "overcoat": { - "name": "Funda", + "name": "Funda", "description": "No recibe daño de las tormentas de arena ni sufre los efectos causados por polvos o esporas." }, "poisonTouch": { - "name": "Toque Tóxico", + "name": "Toque Tóxico", "description": "Puede envenenar al Pokémon al que ataque con un movimiento de contacto." }, "regenerator": { - "name": "Regeneración", + "name": "Regeneración", "description": "Recupera unos pocos PS cuando se retira del combate." }, "bigPecks": { - "name": "Sacapecho", - "description": "Impide que otros Pokémon le reduzcan la Defensa." + "name": "Sacapecho", + "description": "Impide que otros Pokémon le reduzcan la defensa." }, "sandRush": { - "name": "Ímpetu Arena", - "description": "Aumenta su Velocidad durante las tormentas de arena." + "name": "Ímpetu Arena", + "description": "Aumenta su velocidad durante las tormentas de arena." }, "wonderSkin": { - "name": "Piel Milagro", + "name": "Piel Milagro", "description": "Presenta una mayor resistencia ante los movimientos de estado." }, "analytic": { - "name": "Cálculo Final", + "name": "Cálculo Final", "description": "Aumenta la potencia de su movimiento si es el último en atacar." }, "illusion": { - "name": "Ilusión", + "name": "Ilusión", "description": "Adopta el aspecto del último Pokémon del equipo al entrar en combate para desconcertar al rival." }, "imposter": { - "name": "Impostor", + "name": "Impostor", "description": "Se transforma en el Pokémon que tiene enfrente." }, "infiltrator": { - "name": "Allanamiento", + "name": "Allanamiento", "description": "Ataca sorteando las barreras o el sustituto del objetivo." }, "mummy": { - "name": "Momia", + "name": "Momia", "description": "Contagia la habilidad Momia al Pokémon que lo ataque con un movimiento de contacto." }, "moxie": { - "name": "Autoestima", - "description": "Al debilitar a un objetivo, su confianza se refuerza de tal manera que aumenta su Ataque." + "name": "Autoestima", + "description": "Al debilitar a un objetivo, su confianza se refuerza de tal manera que aumenta su ataque." }, "justified": { - "name": "Justiciero", - "description": "Si lo alcanza un movimiento de tipo Siniestro, aumenta el Ataque debido a su integridad." + "name": "Justiciero", + "description": "Si lo alcanza un movimiento de tipo Siniestro, aumenta el ataque debido a su integridad." }, "rattled": { - "name": "Cobardía", - "description": "Si lo alcanza un ataque de tipo Siniestro, Bicho o Fantasma, o si sufre los efectos de Intimidación, el miedo hace que aumente su Velocidad." + "name": "Cobardía", + "description": "Si lo alcanza un ataque de tipo Siniestro, Bicho o Fantasma, o si sufre los efectos de Intimidación, el miedo hace que aumente su velocidad." }, "magicBounce": { - "name": "Espejo Mágico", + "name": "Espejo Mágico", "description": "Puede devolver los movimientos de estado sin verse afectado por ellos." }, "sapSipper": { - "name": "Herbívoro", - "description": "Si lo alcanza un movimiento de tipo Planta, aumenta su Ataque en vez de sufrir daño." + "name": "Herbívoro", + "description": "Si lo alcanza un movimiento de tipo Planta, aumenta su ataque en vez de sufrir daño." }, "prankster": { - "name": "Bromista", + "name": "Bromista", "description": "Sus movimientos de estado tienen prioridad alta." }, "sandForce": { - "name": "Poder Arena", + "name": "Poder Arena", "description": "Potencia los movimientos de tipo Tierra, Acero y Roca durante las tormentas de arena." }, "ironBarbs": { - "name": "Punta Acero", + "name": "Punta Acero", "description": "Inflige daño con sus púas de acero al Pokémon que lo ataque con un movimiento de contacto." }, "zenMode": { - "name": "Modo Daruma", + "name": "Modo Daruma", "description": "Cambia de forma si sus PS se ven reducidos a la mitad o menos." }, "victoryStar": { - "name": "Tinovictoria", - "description": "Aumenta su Precisión y la de sus aliados." + "name": "Tinovictoria", + "description": "Aumenta su precisión y la de sus aliados." }, "turboblaze": { - "name": "Turbollama", + "name": "Turbollama", "description": "Sus movimientos no se ven afectados por la habilidad del objetivo." }, "teravolt": { - "name": "Terravoltaje", + "name": "Terravoltaje", "description": "Sus movimientos no se ven afectados por la habilidad del objetivo." }, "aromaVeil": { - "name": "Velo Aroma", + "name": "Velo Aroma", "description": "Se protege a sí mismo y a sus aliados de efectos que impiden usar movimientos." }, "flowerVeil": { - "name": "Velo Flor", + "name": "Velo Flor", "description": "Evita que los Pokémon de tipo Planta aliados sufran problemas de estado o que les reduzcan sus características." }, "cheekPouch": { - "name": "Carrillo", + "name": "Carrillo", "description": "Recupera PS al comer cualquier baya." }, "protean": { - "name": "Mutatipo", + "name": "Mutatipo", "description": "Al entrar en combate, cambia su tipo al del primer movimiento que va a usar." }, "furCoat": { - "name": "Pelaje Recio", + "name": "Pelaje Recio", "description": "Reduce a la mitad el daño que recibe de ataques físicos." }, "magician": { - "name": "Prestidigitador", + "name": "Prestidigitador", "description": "Roba el objeto del Pokémon al que alcance con un movimiento." }, "bulletproof": { - "name": "Antibalas", + "name": "Antibalas", "description": "No le afectan las bombas ni algunos proyectiles." }, "competitive": { - "name": "Tenacidad", - "description": "Aumenta mucho su Ataque Especial cuando el rival le reduce cualquiera de sus características." + "name": "Tenacidad", + "description": "Aumenta mucho su ataque especial cuando el rival le reduce cualquiera de sus características." }, "strongJaw": { - "name": "Mandíbula Fuerte", + "name": "Mandíbula Fuerte", "description": "Su robusta mandíbula le confiere una mordedura mucho más potente." }, "refrigerate": { - "name": "Piel Helada", + "name": "Piel Helada", "description": "Convierte los movimientos de tipo Normal en tipo Hielo y aumenta ligeramente su potencia." }, "sweetVeil": { - "name": "Velo Dulce", + "name": "Velo Dulce", "description": "No cae dormido y evita también que sus aliados se duerman." }, "stanceChange": { - "name": "Cambio Táctico", + "name": "Cambio Táctico", "description": "Adopta la Forma Filo al lanzar un ataque, o bien la Forma Escudo si usa el movimiento Escudo Real." }, "galeWings": { - "name": "Alas Vendaval", + "name": "Alas Vendaval", "description": "Da prioridad a los movimientos de tipo Volador si sus PS están al máximo." }, "megaLauncher": { - "name": "Megadisparador", + "name": "Megadisparador", "description": "Aumenta la potencia de algunos movimientos de pulsos y auras." }, "grassPelt": { - "name": "Manto Frondoso", - "description": "Aumenta su Defensa si hay un campo de hierba en el terreno de combate." + "name": "Manto Frondoso", + "description": "Aumenta su defensa si hay un campo de hierba en el terreno de combate." }, "symbiosis": { - "name": "Simbiosis", + "name": "Simbiosis", "description": "Pasa su objeto a un aliado cuando este use el suyo." }, "toughClaws": { - "name": "Garra Dura", + "name": "Garra Dura", "description": "Aumenta la potencia de los movimientos de contacto." }, "pixilate": { - "name": "Piel Feérica", + "name": "Piel Feérica", "description": "Convierte los movimientos de tipo Normal en tipo Hada y aumenta ligeramente su potencia." }, "gooey": { - "name": "Baba", - "description": "Reduce la Velocidad del Pokémon que lo ataque con un movimiento de contacto." + "name": "Baba", + "description": "Reduce la velocidad del Pokémon que lo ataque con un movimiento de contacto." }, "aerilate": { - "name": "Piel Celeste", + "name": "Piel Celeste", "description": "Convierte los movimientos de tipo Normal en tipo Volador y aumenta ligeramente su potencia." }, "parentalBond": { - "name": "Amor Filial", + "name": "Amor Filial", "description": "Une fuerzas con su cría y ataca dos veces." }, "darkAura": { - "name": "Aura Oscura", + "name": "Aura Oscura", "description": "Aumenta la potencia de los movimientos de tipo Siniestro de todos los Pokémon." }, "fairyAura": { - "name": "Aura Feérica", + "name": "Aura Feérica", "description": "Aumenta la potencia de los movimientos de tipo Hada de todos los Pokémon." }, "auraBreak": { - "name": "Rompeaura", + "name": "Rompeaura", "description": "Invierte los efectos de las habilidades de auras, por lo que reduce la potencia de ciertos movimientos en vez de aumentarla." }, "primordialSea": { - "name": "Mar del Albor", + "name": "Mar del Albor", "description": "Altera el clima para anular los ataques de tipo Fuego." }, "desolateLand": { - "name": "Tierra del Ocaso", + "name": "Tierra del Ocaso", "description": "Altera el clima para anular los ataques de tipo Agua." }, "deltaStream": { - "name": "Ráfaga Delta", + "name": "Ráfaga Delta", "description": "Altera el clima para anular las vulnerabilidades del tipo Volador." }, "stamina": { - "name": "Firmeza", - "description": "Aumenta su Defensa al recibir un ataque." + "name": "Firmeza", + "description": "Aumenta su defensa al recibir un ataque." }, "wimpOut": { - "name": "Huida", + "name": "Huida", "description": "Se asusta y abandona el terreno de combate cuando sus PS se ven reducidos a la mitad." }, "emergencyExit": { - "name": "Retirada", + "name": "Retirada", "description": "Abandona el terreno de combate cuando sus PS se ven reducidos a la mitad para evitar males mayores." }, "waterCompaction": { - "name": "Hidrorrefuerzo", - "description": "Aumenta mucho su Defensa si lo alcanza un movimiento de tipo Agua." + "name": "Hidrorrefuerzo", + "description": "Aumenta mucho su defensa si lo alcanza un movimiento de tipo Agua." }, "merciless": { - "name": "Ensañamiento", + "name": "Ensañamiento", "description": "Hace que sus movimientos asesten siempre un golpe crítico si el objetivo está envenenado." }, "shieldsDown": { - "name": "Escudo Limitado", + "name": "Escudo Limitado", "description": "Rompe su coraza cuando sus PS se ven reducidos a la mitad y adopta una forma ofensiva." }, "stakeout": { - "name": "Vigilante", + "name": "Vigilante", "description": "Si el objetivo de su ataque es sustituido por otro, duplica el daño que infligirá." }, "waterBubble": { - "name": "Pompa", + "name": "Pompa", "description": "Reduce el daño que le provocan los movimientos de tipo Fuego y es inmune a las quemaduras." }, "steelworker": { - "name": "Acero Templado", + "name": "Acero Templado", "description": "Potencia los movimientos de tipo Acero." }, "berserk": { - "name": "Cólera", - "description": "Aumenta su Ataque Especial si sus PS se ven reducidos a la mitad debido a algún ataque." + "name": "Cólera", + "description": "Aumenta su ataque especial si sus PS se ven reducidos a la mitad debido a algún ataque." }, "slushRush": { - "name": "Quitanieves", - "description": "Aumenta su Velocidad cuando nieva." + "name": "Quitanieves", + "description": "Aumenta su velocidad cuando nieva." }, "longReach": { - "name": "Remoto", + "name": "Remoto", "description": "Puede usar cualquier movimiento sin entrar en contacto con su objetivo." }, "liquidVoice": { - "name": "Voz Fluida", + "name": "Voz Fluida", "description": "Hace que todos sus movimientos que usan sonido pasen a ser de tipo Agua." }, "triage": { - "name": "Primer Auxilio", + "name": "Primer Auxilio", "description": "Da prioridad a los movimientos que restauran PS." }, "galvanize": { - "name": "Piel Eléctrica", + "name": "Piel Eléctrica", "description": "Convierte los movimientos de tipo Normal en tipo Eléctrico y aumenta ligeramente su potencia." }, "surgeSurfer": { - "name": "Cola Surf", - "description": "Duplica su Velocidad si hay un campo eléctrico en el terreno de combate." + "name": "Cola Surf", + "description": "Duplica su velocidad si hay un campo eléctrico en el terreno de combate." }, "schooling": { - "name": "Banco", + "name": "Banco", "description": "Forma bancos con sus congéneres cuando tiene muchos PS, lo cual le otorga más fuerza. Cuando le quedan pocos PS, el banco se dispersa." }, "disguise": { - "name": "Disfraz", + "name": "Disfraz", "description": "Puede eludir un ataque valiéndose de la tela que le cubre el cuerpo una vez por combate." }, "battleBond": { - "name": "Fuerte Afecto", - "description": "Al derrotar a un Pokémon, los vínculos con su Entrenador se refuerzan y aumentan su Ataque, su Ataque Especial y su Velocidad." + "name": "Fuerte Afecto", + "description": "Al derrotar a un Pokémon, los vínculos con su Entrenador se refuerzan y aumentan su ataque, su ataque especial y su velocidad." }, "powerConstruct": { - "name": "Agrupamiento", + "name": "Agrupamiento", "description": "Cuando sus PS se ven reducidos a la mitad, las células se reagrupan y adopta su Forma Completa." }, "corrosion": { - "name": "Corrosión", + "name": "Corrosión", "description": "Puede envenenar incluso a Pokémon de tipo Acero o Veneno." }, "comatose": { - "name": "Letargo Perenne", + "name": "Letargo Perenne", "description": "No despierta jamás de su profundo letargo e incluso ataca dormido." }, "queenlyMajesty": { - "name": "Regia Presencia", + "name": "Regia Presencia", "description": "Intimida al rival y le impide usar movimientos con prioridad contra él y sus aliados." }, "innardsOut": { - "name": "Revés", + "name": "Revés", "description": "Al caer debilitado, inflige al atacante un daño equivalente a los PS que le quedaran antes de recibir el golpe de gracia." }, "dancer": { - "name": "Pareja de Baile", + "name": "Pareja de Baile", "description": "Puede copiar inmediatamente cualquier movimiento de baile que haya usado otro Pokémon presente en el combate." }, "battery": { - "name": "Batería", + "name": "Batería", "description": "Potencia los ataques especiales de los aliados." }, "fluffy": { - "name": "Peluche", + "name": "Peluche", "description": "Reduce a la mitad el daño recibido por los movimientos de contacto, pero duplica el que le infligen los de tipo Fuego." }, "dazzling": { - "name": "Cuerpo Vívido", + "name": "Cuerpo Vívido", "description": "Desconcierta al rival y le impide usar movimientos con prioridad contra él y sus aliados." }, "soulHeart": { - "name": "Coránima", - "description": "Aumenta su Ataque Especial cada vez que un Pokémon cae debilitado." + "name": "Coránima", + "description": "Aumenta su ataque especial cada vez que un Pokémon cae debilitado." }, "tanglingHair": { - "name": "Rizos Rebeldes", - "description": "Reduce la Velocidad del Pokémon que lo ataque con un movimiento de contacto." + "name": "Rizos Rebeldes", + "description": "Reduce la velocidad del Pokémon que lo ataque con un movimiento de contacto." }, "receiver": { - "name": "Receptor", + "name": "Receptor", "description": "Adquiere la habilidad de un aliado debilitado." }, "powerOfAlchemy": { - "name": "Reacción Química", + "name": "Reacción Química", "description": "Reacciona copiando la habilidad de un aliado debilitado." }, "beastBoost": { - "name": "Ultraimpulso", + "name": "Ultraimpulso", "description": "Al derrotar a un Pokémon, aumenta su característica más fuerte." }, "rksSystem": { - "name": "Sistema Alfa", + "name": "Sistema Alfa", "description": "Cambia su tipo según el disco que lleve instalado." }, "electricSurge": { - "name": "Electrogénesis", + "name": "Electrogénesis", "description": "Crea un campo eléctrico al entrar en combate." }, "psychicSurge": { - "name": "Psicogénesis", + "name": "Psicogénesis", "description": "Crea un campo psíquico al entrar en combate." }, "mistySurge": { - "name": "Nebulogénesis", + "name": "Nebulogénesis", "description": "Crea un campo de niebla al entrar en combate." }, "grassySurge": { - "name": "Herbogénesis", + "name": "Herbogénesis", "description": "Crea un campo de hierba al entrar en combate." }, "fullMetalBody": { - "name": "Guardia Metálica", + "name": "Guardia Metálica", "description": "Evita que se reduzcan sus características a causa de movimientos o habilidades de otros Pokémon." }, "shadowShield": { - "name": "Guardia Espectro", + "name": "Guardia Espectro", "description": "Reduce el daño que sufre si sus PS están al máximo." }, "prismArmor": { - "name": "Armadura Prisma", + "name": "Armadura Prisma", "description": "Mitiga el daño que le infligen los movimientos supereficaces." }, "neuroforce": { - "name": "Fuerza Cerebral", + "name": "Fuerza Cerebral", "description": "Potencia los ataques supereficaces." }, "intrepidSword": { - "name": "Espada Indómita", - "description": "Aumenta su Ataque al entrar en combate por primera vez." + "name": "Espada Indómita", + "description": "Aumenta su ataque al entrar en combate por primera vez." }, "dauntlessShield": { - "name": "Escudo Recio", - "description": "Aumenta su Defensa al entrar en combate por primera vez." + "name": "Escudo Recio", + "description": "Aumenta su defensa al entrar en combate por primera vez." }, "libero": { - "name": "Líbero", + "name": "Líbero", "description": "Al entrar en combate, cambia su tipo al del primer movimiento que va a usar." }, "ballFetch": { - "name": "Recogebolas", - "description": "Si no lleva equipado ningún objeto, recupera la Poké Ball del primer intento de captura fallido." + "name": "Recogebolas", + "description": "Recupera la Poké Ball del primer intento de captura fallido." }, "cottonDown": { - "name": "Pelusa", - "description": "Al ser alcanzado por un ataque, suelta una pelusa de algodón que reduce la Velocidad de todos los demás Pokémon." + "name": "Pelusa", + "description": "Al ser alcanzado por un ataque, suelta una pelusa de algodón que reduce la velocidad de todos los demás Pokémon." }, "propellerTail": { - "name": "Hélice Caudal", + "name": "Hélice Caudal", "description": "Ignora los efectos de las habilidades o los movimientos que permiten a un Pokémon centrar la atención sobre sí." }, "mirrorArmor": { - "name": "Coraza Reflejo", + "name": "Coraza Reflejo", "description": "Refleja los efectos que reducen las características." }, "gulpMissile": { - "name": "Tragamisil", + "name": "Tragamisil", "description": "Tras usar Surf o Buceo, emerge con una presa en la boca. Al recibir daño, ataca escupiéndola." }, "stalwart": { - "name": "Acérrimo", + "name": "Acérrimo", "description": "Ignora los efectos de las habilidades o los movimientos que permiten a un Pokémon centrar la atención sobre sí." }, "steamEngine": { - "name": "Combustible", - "description": "Si lo alcanza un movimiento de tipo Fuego o Agua, aumenta muchísimo su Velocidad." + "name": "Combustible", + "description": "Si lo alcanza un movimiento de tipo Fuego o Agua, aumenta muchísimo su velocidad." }, "punkRock": { - "name": "Punk Rock", + "name": "Punk Rock", "description": "Potencia los movimientos que usan sonido y reduce a la mitad el daño que le infligen dichos movimientos." }, "sandSpit": { - "name": "Expulsarena", + "name": "Expulsarena", "description": "Provoca una tormenta de arena al recibir un ataque." }, "iceScales": { - "name": "Escama de Hielo", + "name": "Escama de Hielo", "description": "Las gélidas escamas que protegen su cuerpo reducen a la mitad el daño que le infligen los ataques especiales." }, "ripen": { - "name": "Maduración", + "name": "Maduración", "description": "Hace madurar las bayas, por lo que duplica sus efectos." }, "iceFace": { - "name": "Cara de Hielo", + "name": "Cara de Hielo", "description": "Absorbe el daño de un ataque físico con el hielo de la cabeza, tras lo cual cambia de forma. El hielo se regenerará la próxima vez que nieve." }, "powerSpot": { - "name": "Fuente Energía", + "name": "Fuente Energía", "description": "Potencia los movimientos de los Pokémon adyacentes." }, "mimicry": { - "name": "Mimetismo", + "name": "Mimetismo", "description": "Cambia su tipo según el campo que haya en el terreno de combate." }, "screenCleaner": { - "name": "Antibarrera", + "name": "Antibarrera", "description": "Anula los efectos de Pantalla de Luz, Reflejo y Velo Aurora tanto de rivales como de aliados al entrar en combate." }, "steelySpirit": { - "name": "Alma Acerada", + "name": "Alma Acerada", "description": "Potencia los movimientos de tipo Acero del Pokémon y sus aliados." }, "perishBody": { - "name": "Cuerpo Mortal", + "name": "Cuerpo Mortal", "description": "Si lo alcanza un movimiento de contacto, se debilitará al cabo de 3 turnos, así como el atacante, a menos que abandonen el terreno de combate." }, "wanderingSpirit": { - "name": "Alma Errante", + "name": "Alma Errante", "description": "Si lo alcanza un movimiento de contacto, intercambia su habilidad con la del atacante." }, "gorillaTactics": { - "name": "Monotema", - "description": "Aumenta su Ataque, pero solo puede usar el primer movimiento escogido." + "name": "Monotema", + "description": "Aumenta su ataque, pero solo puede usar el primer movimiento escogido." }, "neutralizingGas": { - "name": "Gas Reactivo", + "name": "Gas Reactivo", "description": "Anula los efectos de las habilidades de los demás Pokémon presentes mientras esté en el terreno de combate." }, "pastelVeil": { - "name": "Velo Pastel", + "name": "Velo Pastel", "description": "Se protege a sí mismo y a sus aliados del envenenamiento." }, "hungerSwitch": { - "name": "Mutapetito", + "name": "Mutapetito", "description": "Alterna entre su Forma Saciada y Forma Voraz al final de cada turno." }, "quickDraw": { - "name": "Mano Rápida", + "name": "Mano Rápida", "description": "A veces, puede atacar el primero." }, "unseenFist": { - "name": "Puño Invisible", + "name": "Puño Invisible", "description": "Si usa un movimiento de contacto, puede infligir daño al objetivo aunque este se proteja." }, "curiousMedicine": { - "name": "Medicina Extraña", + "name": "Medicina Extraña", "description": "Al entrar en combate, rezuma una substancia medicinal por la caracola que revierte los cambios en las características de los aliados." }, "transistor": { - "name": "Transistor", + "name": "Transistor", "description": "Potencia los movimientos de tipo Eléctrico." }, "dragonsMaw": { - "name": "Mandíbula Dragón", + "name": "Mandíbula Dragón", "description": "Potencia los movimientos de tipo Dragón." }, "chillingNeigh": { - "name": "Relincho Blanco", - "description": "Al derrotar a un objetivo, emite un relincho gélido y aumenta su Ataque." + "name": "Relincho Blanco", + "description": "Al derrotar a un objetivo, emite un relincho gélido y aumenta su ataque." }, "grimNeigh": { - "name": "Relincho Negro", - "description": "Al derrotar a un objetivo, emite un relincho aterrador y aumenta su Ataque Especial." + "name": "Relincho Negro", + "description": "Al derrotar a un objetivo, emite un relincho aterrador y aumenta su ataque especial." }, "asOneGlastrier": { - "name": "Unidad Ecuestre", + "name": "Unidad Ecuestre", "description": "El Pokémon tiene dos habilidades: Relincho Negro de Spectrier y Nerviosismo de Calyrex." }, "asOneSpectrier": { - "name": "Unidad Ecuestre", + "name": "Unidad Ecuestre", "description": "El Pokémon tiene dos habilidades: Relincho Negro de Spectrier y Nerviosismo de Calyrex." }, "lingeringAroma": { - "name": "Olor Persistente", + "name": "Olor Persistente", "description": "Contagia la habilidad Olor Persistente al Pokémon que lo ataque con un movimiento de contacto." }, "seedSower": { - "name": "Disemillar", + "name": "Disemillar", "description": "Crea un campo de hierba al recibir un ataque." }, "thermalExchange": { - "name": "Termoconversión", - "description": "Evita las quemaduras y, si lo alcanza un movimiento de tipo Fuego, aumenta su Ataque." + "name": "Termoconversión", + "description": "Evita las quemaduras y, si lo alcanza un movimiento de tipo Fuego, aumenta su ataque." }, "angerShell": { - "name": "Coraza Ira", - "description": "Cuando un ataque reduce sus PS a la mitad, un arrebato de cólera reduce su Defensa y su Defensa Especial, pero aumenta su Ataque, su Ataque Especial y su Velocidad." + "name": "Coraza Ira", + "description": "Cuando un ataque reduce sus PS a la mitad, un arrebato de cólera reduce su defensa y su defensa especial, pero aumenta su ataque, su ataque especial y su velocidad." }, "purifyingSalt": { - "name": "Sal Purificadora", + "name": "Sal Purificadora", "description": "Su sal pura lo protege de los problemas de estado y reduce a la mitad el daño que recibe de ataques de tipo Fantasma." }, "wellBakedBody": { - "name": "Cuerpo Horneado", - "description": "Si lo alcanza un movimiento de tipo Fuego, aumenta mucho su Defensa en vez de sufrir daño." + "name": "Cuerpo Horneado", + "description": "Si lo alcanza un movimiento de tipo Fuego, aumenta mucho su defensa en vez de sufrir daño." }, "windRider": { - "name": "Surcavientos", - "description": "Si sopla un Viento Afín o lo alcanza un movimiento que usa viento, aumenta su Ataque. Tampoco recibe daño de este último." + "name": "Surcavientos", + "description": "Si sopla un Viento Afín o lo alcanza un movimiento que usa viento, aumenta su ataque. Tampoco recibe daño de este último." }, "guardDog": { - "name": "Perro Guardián", - "description": "Aumenta su Ataque si sufre los efectos de Intimidación. También anula movimientos y objetos que fuercen el cambio de Pokémon." + "name": "Perro Guardián", + "description": "Aumenta su ataque si sufre los efectos de Intimidación. También anula movimientos y objetos que fuercen el cambio de Pokémon." }, "rockyPayload": { - "name": "Transportarrocas", + "name": "Transportarrocas", "description": "Potencia los movimientos de tipo Roca." }, "windPower": { - "name": "Energía Eólica", + "name": "Energía Eólica", "description": "Su cuerpo se carga de electricidad si lo alcanza un movimiento que usa viento, lo que potencia su siguiente movimiento de tipo Eléctrico." }, "zeroToHero": { - "name": "Cambio Heroico", + "name": "Cambio Heroico", "description": "Adopta la Forma Heroica cuando se retira del combate." }, "commander": { - "name": "Comandar", + "name": "Comandar", "description": "Si al entrar en combate coincide con un Dondozo aliado, se cuela en el interior de su boca para tomar el control." }, "electromorphosis": { - "name": "Dinamo", + "name": "Dinamo", "description": "Su cuerpo se carga de electricidad al recibir daño, lo que potencia su siguiente movimiento de tipo Eléctrico." }, "protosynthesis": { - "name": "Paleosíntesis", + "name": "Paleosíntesis", "description": "Si hace sol o lleva un tanque de Energía Potenciadora, aumenta su característica más alta." }, "quarkDrive": { - "name": "Carga Cuark", + "name": "Carga Cuark", "description": "Si hay un campo eléctrico en el terreno de combate o lleva un tanque de Energía Potenciadora, aumenta su característica más alta." }, "goodAsGold": { - "name": "Cuerpo Áureo", + "name": "Cuerpo Áureo", "description": "Su robusto cuerpo de oro inoxidable lo hace inmune frente a movimientos de estado de otros Pokémon." }, "vesselOfRuin": { - "name": "Caldero Debacle", - "description": "Reduce el Ataque Especial de todos los demás Pokémon con el poder de su caldero maldito." + "name": "Caldero Debacle", + "description": "Reduce el ataque especial de todos los demás Pokémon con el poder de su caldero maldito." }, "swordOfRuin": { - "name": "Espada Debacle", - "description": "Reduce la Defensa de todos los demás Pokémon con el poder de su espada maldita." + "name": "Espada Debacle", + "description": "Reduce la defensa de todos los demás Pokémon con el poder de su espada maldita." }, "tabletsOfRuin": { - "name": "Tablilla Debacle", - "description": "Reduce el Ataque de todos los demás Pokémon con el poder de sus tablillas malditas." + "name": "Tablilla Debacle", + "description": "Reduce el ataque de todos los demás Pokémon con el poder de sus tablillas malditas." }, "beadsOfRuin": { - "name": "Abalorio Debacle", - "description": "Reduce la Defensa Especial de todos los demás Pokémon con el poder de sus abalorios malditos." + "name": "Abalorio Debacle", + "description": "Reduce la defensa especial de todos los demás Pokémon con el poder de sus abalorios malditos." }, "orichalcumPulse": { - "name": "Latido Oricalco", - "description": "El tiempo pasa a ser soleado cuando entra en combate. Si hace mucho sol, su Ataque aumenta gracias a su pulso primigenio." + "name": "Latido Oricalco", + "description": "El tiempo pasa a ser soleado cuando entra en combate. Si hace mucho sol, su ataque aumenta gracias a su pulso primigenio." }, "hadronEngine": { - "name": "Motor Hadrónico", - "description": "Crea un campo eléctrico al entrar en combate. Si hay un campo eléctrico, su Ataque Especial aumenta gracias a su motor futurista." + "name": "Motor Hadrónico", + "description": "Crea un campo eléctrico al entrar en combate. Si hay un campo eléctrico, su ataque especial aumenta gracias a su motor futurista." }, "opportunist": { - "name": "Oportunista", + "name": "Oportunista", "description": "Copia las mejoras en las características del rival, aprovechándose de la situación." }, "cudChew": { - "name": "Rumia", + "name": "Rumia", "description": "Cuando ingiere una baya, la regurgita al final del siguiente turno y se la come por segunda vez." }, "sharpness": { - "name": "Cortante", + "name": "Cortante", "description": "Aumenta la potencia de los movimientos cortantes." }, "supremeOverlord": { - "name": "General Supremo", - "description": "Al entrar en combate, su Ataque y su Ataque Especial aumentan un poco por cada miembro del equipo que haya sido derrotado hasta el momento." + "name": "General Supremo", + "description": "Al entrar en combate, su ataque y su ataque especial aumentan un poco por cada miembro del equipo que haya sido derrotado hasta el momento." }, "costar": { - "name": "Unísono", + "name": "Unísono", "description": "Al entrar en combate, copia los cambios en las características de su aliado." }, "toxicDebris": { - "name": "Capa Tóxica", + "name": "Capa Tóxica", "description": "Al recibir daño de un ataque físico, lanza una trampa de púas tóxicas a los pies del rival." }, "armorTail": { - "name": "Cola Armadura", + "name": "Cola Armadura", "description": "La extraña cola que le envuelve la cabeza impide al rival usar movimientos con prioridad contra él y sus aliados." }, "earthEater": { - "name": "Geofagia", + "name": "Geofagia", "description": "Si lo alcanza un movimiento de tipo Tierra, recupera PS en vez de sufrir daño." }, "myceliumMight": { - "name": "Poder Fúngico", + "name": "Poder Fúngico", "description": "El Pokémon siempre actúa con lentitud cuando usa movimientos de estado, pero estos no se ven afectados por la habilidad del objetivo." }, "mindsEye": { - "name": "Ojo Mental", - "description": "Alcanza a Pokémon de tipo Fantasma con movimientos de tipo Normal o Lucha. Su Precisión no se puede reducir e ignora los cambios en la Evasión del objetivo." + "name": "Ojo Mental", + "description": "Alcanza a Pokémon de tipo Fantasma con movimientos de tipo Normal o Lucha. Su precisión no se puede reducir e ignora los cambios en la evasión del objetivo." }, "supersweetSyrup": { - "name": "Néctar Dulce", - "description": "Al entrar en combate por primera vez, esparce un aroma dulzón a néctar que reduce la Evasión del rival." + "name": "Néctar Dulce", + "description": "Al entrar en combate por primera vez, esparce un aroma dulzón a néctar que reduce la evasión del rival." }, "hospitality": { - "name": "Hospitalidad", + "name": "Hospitalidad", "description": "Al entrar en combate, restaura algunos PS de su aliado como muestra de hospitalidad." }, "toxicChain": { - "name": "Cadena Tóxica", + "name": "Cadena Tóxica", "description": "Gracias al poder de su cadena impregnada de toxinas, puede envenenar gravemente al Pokémon al que ataque." }, "embodyAspectTeal": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara turquesa y aumenta su velocidad." }, "embodyAspectWellspring": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara fuente y aumenta su defensa especial." }, "embodyAspectHearthflame": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara horno y aumenta su ataque." }, "embodyAspectCornerstone": { - "name": "Evocarrecuerdos", - "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la Máscara Cimiento y aumenta su Defensa." + "name": "Evocarrecuerdos", + "description": "Al evocar viejos recuerdos, el Pokémon hace brillar la máscara cimiento y aumenta su defensa." }, "teraShift": { - "name": "Teracambio", + "name": "Teracambio", "description": "Al entrar en combate, adopta la Forma Teracristal tras absorber la energía de su alrededor." }, "teraShell": { - "name": "Teracaparazón", + "name": "Teracaparazón", "description": "Su caparazón encierra energía de todos los tipos. Gracias a ello, si sus PS están al máximo, el movimiento que lo alcance no será muy eficaz." }, "teraformZero": { - "name": "Teraformación 0", + "name": "Teraformación 0", "description": "Cuando Terapagos adopta la Forma Astral, anula todos los efectos del tiempo atmosférico y de los campos que haya en el terreno gracias a su poder oculto." }, "poisonPuppeteer": { - "name": "Títere Tóxico", + "name": "Títere Tóxico", "description": "Los rivales que Pecharunt envenene con sus movimientos también sufrirán confusión." } } diff --git a/src/locales/es/bgm-name.json b/src/locales/es/bgm-name.json index be617b79567..f0e0ab7e852 100644 --- a/src/locales/es/bgm-name.json +++ b/src/locales/es/bgm-name.json @@ -1,95 +1,99 @@ { "music": "Música: ", "missing_entries": "{{name}}", - "battle_kanto_champion": "B2W2 - ¡Vs Campeón de Kanto!", - "battle_johto_champion": "B2W2 - ¡Vs Campeón de Johto!", - "battle_hoenn_champion_g5": "B2W2 - ¡Vs Campeón de Hoenn!", - "battle_hoenn_champion_g6": "ORAS - ¡Vs Campeón de Hoenn!", - "battle_sinnoh_champion": "B2W2 - ¡Vs Campeón de Sinnoh!", - "battle_champion_alder": "BW - ¡Vs Campeón de Teselia!", - "battle_champion_iris": "B2W2 - ¡Vs Campeón de Teselia!", - "battle_kalos_champion": "XY - ¡Vs Campeón de Kalos!", - "battle_alola_champion": "USUM - ¡Vs Campeón de Alola!", - "battle_galar_champion": "SWSH - ¡Vs Campeón de Galar!", - "battle_champion_geeta": "SV - ¡Vs Campeona Ságita!", - "battle_champion_nemona": "SV - ¡Vs Campeona Mencía!", - "battle_champion_kieran": "SV - ¡Vs Campeón Cass!", - "battle_hoenn_elite": "ORAS - ¡Vs Alto Mando!", - "battle_unova_elite": "BW - ¡Vs Alto Mando!", - "battle_kalos_elite": "XY - ¡Vs Alto Mando!", - "battle_alola_elite": "SM - ¡Vs Alto Mando!", - "battle_galar_elite": "SWSH - Torneo de Finalistas", - "battle_paldea_elite": "SV - ¡Vs Alto Mando!", - "battle_bb_elite": "SV - ¡Vs Alto Mando de la Academia Arándano!", + "battle_kanto_champion": "B2W2 - ¡Vs. Campeón de Kanto!", + "battle_johto_champion": "B2W2 - ¡Vs. Campeón de Johto!", + "battle_hoenn_champion_g5": "B2W2 - ¡Vs. Campeón de Hoenn!", + "battle_hoenn_champion_g6": "ORAS - ¡Vs. Campeón de Hoenn!", + "battle_sinnoh_champion": "B2W2 - ¡Vs. Campeón de Sinnoh!", + "battle_champion_alder": "BW - ¡Vs. Campeón de Teselia!", + "battle_champion_iris": "B2W2 - ¡Vs. Campeón de Teselia!", + "battle_kalos_champion": "XY - ¡Vs. Campeón de Kalos!", + "battle_alola_champion": "USUM - ¡Vs. Campeón de Alola!", + "battle_galar_champion": "SWSH - ¡Vs. Campeón de Galar!", + "battle_champion_geeta": "SV - ¡Vs. Campeona Ságita!", + "battle_champion_nemona": "SV - ¡Vs. Campeona Mencía!", + "battle_champion_kieran": "SV - ¡Vs. Campeón Cass!", + "battle_hoenn_elite": "ORAS - ¡Vs. Alto Mando!", + "battle_unova_elite": "BW - ¡Vs. Alto Mando!", + "battle_kalos_elite": "XY - ¡Vs. Alto Mando!", + "battle_alola_elite": "SM - ¡Vs. Alto Mando!", + "battle_galar_elite": "SWSH - Torneo de finalistas", + "battle_paldea_elite": "SV - ¡Vs. Alto Mando!", + "battle_bb_elite": "SV - ¡Vs. Alto Mando de la Academia Arándano!", "battle_final_encounter": "PMD RTDX - Dominio de Rayquaza", - "battle_final": "BW - ¡Vs Ghechis!", - "battle_kanto_gym": "B2W2 - ¡Vs Líder de Kanto!", - "battle_johto_gym": "B2W2 - ¡Vs Líder de Johto!", - "battle_hoenn_gym": "B2W2 - ¡Vs Líder de Hoenn!", - "battle_sinnoh_gym": "B2W2 - ¡Vs Líder de Sinnoh!", - "battle_unova_gym": "BW - ¡Vs Líder de Teselia!", - "battle_kalos_gym": "XY - ¡Vs Líder de Kalos!", - "battle_galar_gym": "SWSH - ¡Vs Líder de Galar!", - "battle_paldea_gym": "SV - ¡Vs Líder de Paldea!", - "battle_legendary_kanto": "XY - ¡Vs Legendarios de Kanto!", - "battle_legendary_raikou": "HGSS - ¡Vs Raikou!", - "battle_legendary_entei": "HGSS - ¡Vs Entei!", - "battle_legendary_suicune": "HGSS - ¡Vs Suicune!", - "battle_legendary_lugia": "HGSS - ¡Vs Lugia!", - "battle_legendary_ho_oh": "HGSS - ¡Vs Ho-oh!", - "battle_legendary_regis_g5": "B2W2 - ¡Vs Regis!", - "battle_legendary_regis_g6": "ORAS - ¡Vs Regis!", - "battle_legendary_gro_kyo": "ORAS - ¡Vs Groudon/Kyogre!", - "battle_legendary_rayquaza": "ORAS - ¡Vs Rayquaza!", - "battle_legendary_deoxys": "ORAS - ¡Vs Deoxys!", - "battle_legendary_lake_trio": "ORAS - ¡Vs Trío del Lago!", - "battle_legendary_sinnoh": "ORAS - ¡Vs Legendarios de Sinnoh!", - "battle_legendary_dia_pal": "ORAS - ¡Vs Dialga/Palkia!", - "battle_legendary_giratina": "ORAS - ¡Vs Giratina!", - "battle_legendary_arceus": "HGSS - ¡Vs Arceus!", - "battle_legendary_unova": "BW - ¡Vs Legendarios de Teselia!", - "battle_legendary_kyurem": "BW - ¡Vs Kyurem!", - "battle_legendary_res_zek": "BW - ¡Vs Reshiram/Zekrom!", - "battle_legendary_xern_yvel": "XY - ¡Vs Xerneas/Yveltal!", - "battle_legendary_tapu": "SM - ¡Vs Tapus!", - "battle_legendary_sol_lun": "SM - ¡Vs Solgaleo/Lunala!", - "battle_legendary_ub": "SM - ¡Vs Ultraentes!", - "battle_legendary_dusk_dawn": "USUM - ¡Vs Necrozma Melena Crepuscular/Alas del Alba!", - "battle_legendary_ultra_nec": "USUM - ¡Vs Ultra-Necrozma!", - "battle_legendary_zac_zam": "SWSH - ¡Vs Zacian/Zamazenta!", - "battle_legendary_glas_spec": "SWSH - ¡Vs Glastrier/Spectrier!", - "battle_legendary_calyrex": "SWSH - ¡Vs Calyrex!", - "battle_legendary_birds_galar": "SWSH - ¡Vs Aves Legendarias de Galar!", - "battle_legendary_ruinous": "SV - ¡Vs Tesoros Funestos!", - "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", - "battle_legendary_loyal_three": "SV - ¡Vs Compatrones!", - "battle_legendary_ogerpon": "SV - ¡Vs Ogerpon!", - "battle_legendary_terapagos": "SV - ¡Vs Terapagos!", - "battle_legendary_pecharunt": "SV - ¡Vs Pecharunt!", - "battle_rival": "BW - ¡Vs Rival!", + "battle_final": "BW - ¡Vs. Ghechis!", + "battle_kanto_gym": "B2W2 - ¡Vs. Líder de Kanto!", + "battle_johto_gym": "B2W2 - ¡Vs. Líder de Johto!", + "battle_hoenn_gym": "B2W2 - ¡Vs. Líder de Hoenn!", + "battle_sinnoh_gym": "B2W2 - ¡Vs. Líder de Sinnoh!", + "battle_unova_gym": "BW - ¡Vs. Líder de Teselia!", + "battle_kalos_gym": "XY - ¡Vs. Líder de Kalos!", + "battle_galar_gym": "SWSH - ¡Vs. Líder de Galar!", + "battle_paldea_gym": "SV - ¡Vs. Líder de Paldea!", + "battle_legendary_kanto": "XY - ¡Vs. Legendarios de Kanto!", + "battle_legendary_raikou": "HGSS - ¡Vs. Raikou!", + "battle_legendary_entei": "HGSS - ¡Vs. Entei!", + "battle_legendary_suicune": "HGSS - ¡Vs. Suicune!", + "battle_legendary_lugia": "HGSS - ¡Vs. Lugia!", + "battle_legendary_ho_oh": "HGSS - ¡Vs. Ho-oh!", + "battle_legendary_regis_g5": "B2W2 - ¡Vs. Regis!", + "battle_legendary_regis_g6": "ORAS - ¡Vs. Regis!", + "battle_legendary_gro_kyo": "ORAS - ¡Vs. Groudon/Kyogre!", + "battle_legendary_rayquaza": "ORAS - ¡Vs. Rayquaza!", + "battle_legendary_deoxys": "ORAS - ¡Vs. Deoxys!", + "battle_legendary_lake_trio": "ORAS - ¡Vs. trío del Lago!", + "battle_legendary_sinnoh": "ORAS - ¡Vs. legendarios de Sinnoh!", + "battle_legendary_dia_pal": "ORAS - ¡Vs. Dialga/Palkia!", + "battle_legendary_origin_forme": "LA - ¡Vs. Dialga & Palkia, Forma Origen!", + "battle_legendary_giratina": "ORAS - ¡Vs. Giratina!", + "battle_legendary_arceus": "HGSS - ¡Vs. Arceus!", + "battle_legendary_unova": "BW - ¡Vs. legendarios de Teselia!", + "battle_legendary_kyurem": "BW - ¡Vs. Kyurem!", + "battle_legendary_res_zek": "BW - ¡Vs. Reshiram/Zekrom!", + "battle_legendary_xern_yvel": "XY - ¡Vs. Xerneas/Yveltal!", + "battle_legendary_tapu": "SM - ¡Vs. Tapus!", + "battle_legendary_sol_lun": "SM - ¡Vs. Solgaleo/Lunala!", + "battle_legendary_ub": "SM - ¡Vs. Ultraentes!", + "battle_legendary_dusk_dawn": "USUM - ¡Vs. Necrozma Melena Crepuscular/Alas del Alba!", + "battle_legendary_ultra_nec": "USUM - ¡Vs. Ultra-Necrozma!", + "battle_legendary_zac_zam": "SWSH - ¡Vs. Zacian/Zamazenta!", + "battle_legendary_glas_spec": "SWSH - ¡Vs. Glastrier/Spectrier!", + "battle_legendary_calyrex": "SWSH - ¡Vs. Calyrex!", + "battle_legendary_riders": "SWSH - ¡Vs. Calyrex Jinete!", + "battle_legendary_birds_galar": "SWSH - ¡Vs. Aves Legendarias de Galar!", + "battle_legendary_ruinous": "SV - ¡Vs. Tesoros Funestos!", + "battle_legendary_kor_mir": "SV - ¡Batalla en el área Zero!", + "battle_legendary_loyal_three": "SV - ¡Vs. Compatrones!", + "battle_legendary_ogerpon": "SV - ¡Vs. Ogerpon!", + "battle_legendary_terapagos": "SV - ¡Vs. Terapagos!", + "battle_legendary_pecharunt": "SV - ¡Vs. Pecharunt!", + "battle_rival": "BW - ¡Vs. Rival!", "battle_rival_2": "BW - ¡Vs N!", - "battle_rival_3": "BW - ¡Vs N (Liga Pokémon)!", - "battle_trainer": "BW - ¡Vs Entrenador!", - "battle_wild": "BW - ¡Vs Pokémon Salvaje!", - "battle_wild_strong": "BW - ¡Vs Pokémon Salvaje Raro!", - "end_summit": "PMD RTDX - Techo del Cielo", - "battle_rocket_grunt": "HGSS Team Rocket Battle", - "battle_aqua_magma_grunt": "ORAS Team Aqua & Magma Battle", - "battle_galactic_grunt": "BDSP Team Galactic Battle", + "battle_rival_3": "BW - ¡Vs. N (Liga Pokémon)!", + "battle_trainer": "BW - ¡Vs. entrenador!", + "battle_wild": "BW - ¡Vs. Pokémon salvaje!", + "battle_wild_strong": "BW - ¡Vs. Pokémon salvaje raro!", + "end_summit": "PMD RTDX - Techo del cielo", + "battle_rocket_grunt": "HGSS - ¡Vs. Team Rocket!", + "battle_aqua_magma_grunt": "ORAS - ¡Vs. Equipo Aqua & Magma!", + "battle_galactic_grunt": "BDSP - ¡Vs. Equipo Galaxia!", "battle_plasma_grunt": "BW - ¡Vs Equipo Plasma!", - "battle_flare_grunt": "XY Team Flare Battle", - "battle_aether_grunt": "SM Aether Foundation Battle", - "battle_skull_grunt": "SM Team Skull Battle", - "battle_macro_grunt": "SWSH Trainer Battle", - "battle_galactic_admin": "BDSP Team Galactic Admin Battle", - "battle_skull_admin": "SM Team Skull Admin Battle", - "battle_oleana": "SWSH Oleana Battle", - "battle_rocket_boss": "USUM Giovanni Battle", - "battle_aqua_magma_boss": "ORAS Archie & Maxie Battle", - "battle_galactic_boss": "BDSP Cyrus Battle", - "battle_plasma_boss": "B2W2 Ghetsis Battle", - "battle_flare_boss": "XY Lysandre Battle", - + "battle_flare_grunt": "XY - ¡Vs. Team Flare!", + "battle_aether_grunt": "SM - ¡Vs. Fundación Æther!", + "battle_skull_grunt": "SM - ¡Vs. Team Skull!", + "battle_macro_grunt": "SWSH - ¡Vs. entrenador!", + "battle_galactic_admin": "BDSP - ¡Vs. Comandante del Equipo Galaxia!", + "battle_skull_admin": "SM - ¡Vs. Comandante del Team Skull!", + "battle_oleana": "SWSH - ¡Vs. Olivia!", + "battle_rocket_boss": "USUM - ¡Vs. Giovanni!", + "battle_aqua_magma_boss": "ORAS - ¡Vs. Aquiles & Magno!", + "battle_galactic_boss": "BDSP - ¡Vs. Helio!", + "battle_plasma_boss": "B2W2 - ¡Vs. Ghechis Armonia!", + "battle_flare_boss": "XY - ¡Vs. Lysson!", + "battle_aether_boss": "SM - ¡Vs. Samina!", + "battle_skull_boss": "SM - ¡Vs. Guzmán!", + "battle_macro_boss": "SWSH - ¡Vs. Rose!", "abyss": "PMD EoS - Cráter Oscuro", "badlands": "PMD EoS - Valle Desolado", "beach": "PMD EoS - Risco Calado", @@ -105,40 +109,40 @@ "graveyard": "PMD EoS - Bosque Misterio", "ice_cave": "PMD EoS - Gran Iceberg", "island": "PMD EoS - Costa Escarpada", - "jungle": "Lmz - Jungle", - "laboratory": "Firel - Laboratory", + "jungle": "Lmz - Jungla", + "laboratory": "Firel - Laboratorio", "lake": "PMD EoS - Cueva Cristal", "meadow": "PMD EoS - Bosque de la Cumbre del Cielo", - "metropolis": "Firel - Metropolis", + "metropolis": "Firel - Metrópolis", "mountain": "PMD EoS - Monte Cuerno", "plains": "PMD EoS - Pradera de la Cumbre del Cielo", "power_plant": "PMD EoS - Pradera Destello", "ruins": "PMD EoS - Sima Hermética", - "sea": "Andr06 - Marine Mystique", - "seabed": "Firel - Seabed", - "slum": "Andr06 - Sneaky Snom", + "sea": "Andr06 - Misticismo marino", + "seabed": "Firel - Lecho del mar", + "slum": "Andr06 - Snom sigiloso", "snowy_forest": "PMD EoS - Campo nevado de la Cumbre del Cielo", - "space": "Firel - Aether", + "space": "Firel - Æther ", "swamp": "PMD EoS - Mar Circundante", "tall_grass": "PMD EoS - Bosque Niebla", "temple": "PMD EoS - Cueva Regia", "town": "PMD EoS - Tema del territorio aleatorio 3", "volcano": "PMD EoS - Cueva Vapor", "wasteland": "PMD EoS - Corazón Tierra Oculta", - "encounter_ace_trainer": "BW - Desafío Combate (Entrenador Guay)", - "encounter_backpacker": "BW - Desafío Combate (Mochilero)", - "encounter_clerk": "BW - Desafío Combate (Empresario)", - "encounter_cyclist": "BW - Desafío Combate (Ciclista)", - "encounter_lass": "BW - Desafío Combate (Chica)", - "encounter_parasol_lady": "BW - Desafío Combate (Dama parasol)", - "encounter_pokefan": "BW - Desafío Combate (Pokéfan)", - "encounter_psychic": "BW - Desafío Combate (Médium)", - "encounter_rich": "BW - Desafío Combate (Aristócrata)", - "encounter_rival": "BW - Desafío Combate (Cheren)", - "encounter_roughneck": "BW - Desafío Combate (Calvo)", - "encounter_scientist": "BW - Desafío Combate (Científico)", - "encounter_twins": "BW - Desafío Combate (Gemelas)", - "encounter_youngster": "BW - Desafío Combate (Joven)", + "encounter_ace_trainer": "BW - ¡Vs. entrenador guay!", + "encounter_backpacker": "BW - ¡Vs. mochilero!", + "encounter_clerk": "BW - ¡Vs. empresario!", + "encounter_cyclist": "BW - ¡Vs. ciclista!", + "encounter_lass": "BW - ¡Vs. chica joven!", + "encounter_parasol_lady": "BW - ¡Vs. dama parasol!", + "encounter_pokefan": "BW - ¡Vs. poké-fan!", + "encounter_psychic": "BW -¡Vs. médium!", + "encounter_rich": "BW - ¡Vs. aristócrata!", + "encounter_rival": "BW - ¡Vs. Cheren!", + "encounter_roughneck": "BW - ¡Vs. tío chungo!", + "encounter_scientist": "BW - ¡Vs. científico!", + "encounter_twins": "BW - ¡Vs. gemelas!", + "encounter_youngster": "BW - ¡Vs. chico joven!", "heal": "BW - Cura Pokémon", "menu": "PMD EoS - ¡Bienvenidos al mundo de los Pokémon!", "title": "PMD EoS - Tema del menú principal" From dd033f4ec1636adff8488545bd35bdcc85715aa7 Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:17:05 -0400 Subject: [PATCH 024/108] [Localization] [pt_BR] Updated many translations (#3917) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translate tutorial.json via GitLocalize * Translate challenges.json via GitLocalize * Translate menu-ui-handler.json via GitLocalize --------- Co-authored-by: José Ricardo --- src/locales/pt_BR/challenges.json | 2 +- src/locales/pt_BR/menu-ui-handler.json | 8 ++++---- src/locales/pt_BR/tutorial.json | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/locales/pt_BR/challenges.json b/src/locales/pt_BR/challenges.json index 6b20a92f4f6..8402ad106b6 100644 --- a/src/locales/pt_BR/challenges.json +++ b/src/locales/pt_BR/challenges.json @@ -33,4 +33,4 @@ "value.0": "Desligado", "value.1": "Ligado" } -} \ No newline at end of file +} diff --git a/src/locales/pt_BR/menu-ui-handler.json b/src/locales/pt_BR/menu-ui-handler.json index cc087c8335e..df654976d68 100644 --- a/src/locales/pt_BR/menu-ui-handler.json +++ b/src/locales/pt_BR/menu-ui-handler.json @@ -14,8 +14,8 @@ "importSlotSelect": "Selecione um slot para importar.", "exportSession": "Exportar sessão", "exportSlotSelect": "Selecione um slot para exportar.", - "importRunHistory":"Importar Histórico de Jogos", - "exportRunHistory":"Exportar Histórico de Jogos", + "importRunHistory": "Importar Histórico de Jogos", + "exportRunHistory": "Exportar Histórico de Jogos", "importData": "Importar dados", "exportData": "Exportar dados", "consentPreferences": "Opções de Privacidade", @@ -25,5 +25,5 @@ "unlinkGoogle": "Desconectar Google", "cancel": "Cancelar", "losingProgressionWarning": "Você vai perder todo o progresso desde o início da batalha. Confirmar?", - "noEggs": "Você não está chocando\nnenhum ovo no momento!" -} \ No newline at end of file + "noEggs": "Você não está chocando nenhum ovo\nno momento!" +} diff --git a/src/locales/pt_BR/tutorial.json b/src/locales/pt_BR/tutorial.json index e347ca6fbee..92ea0dd080f 100644 --- a/src/locales/pt_BR/tutorial.json +++ b/src/locales/pt_BR/tutorial.json @@ -1,10 +1,10 @@ { - "intro": "Bem-vindo ao PokéRogue! Este é um jogo Pokémon feito por fãs focado em batalhas com elementos roguelite.\n$Este jogo não é monetizado e não reivindicamos propriedade de Pokémon nem dos ativos protegidos\n$por direitos autorais usados.\n$O jogo é um trabalho em andamento, mas é totalmente jogável.\n$Para relatórios de bugs, use a comunidade no Discord.\n$Se o jogo estiver rodando lentamente, certifique-se de que a 'Aceleração de hardware' esteja ativada \n$nas configurações do seu navegador.", + "intro": "Bem-vindo ao PokéRogue!\n$Este é um fangame Pokémon focado em batalhas com elementos roguelite.\n$Este jogo não é monetizado e não reivindicamos propriedade do Pokémon nem dos ativos protegidos$por direitos autorais usados.\n$O jogo é um trabalho em andamento,\nmas totalmente jogável.\n$Para relatórios de bugs, use a comunidade do Discord.\n$Se o jogo rodar lentamente, certifique-se de que\na 'Aceleração de Hardware' esteja ativada$nas configurações do seu navegador.", "accessMenu": "Para acessar o menu, pressione M ou Esc.\n$O menu contém configurações e diversas funções.", - "menu": "A partir deste menu, você pode acessar as configurações. \n$Nas configurações, você pode alterar a velocidade do jogo,\n$o estilo da janela, entre outras opções. \n$Existem também vários outros recursos disponíveis aqui.\n$Não deixe de conferir todos eles!", - "starterSelect": "Aqui você pode escolher seus iniciais apertando a tecla Z ou\na Barra de Espaço.\n$Esses serão os primeiro Pokémon da sua equipe.\n$Cada inicial tem seu custo. Sua equipe pode ter até 6\nmembros, desde que a soma dos custos não ultrapasse 10. \n$Você pode escolher o gênero, a habilidade\ne até a forma do seu inicial.\n$Essas opções dependem das variantes dessa\nespécie que você já capturou ou chocou. \n$Os IVs de cada inicial são os melhores de todos os Pokémon\ndaquela espécie que você já capturou ou chocou.\n$Sempre capture vários Pokémon de várias espécies!", + "menu": "A partir deste menu, você pode\\nacessar as configurações.\n$A partir das configurações, você\npode alterar a velocidade do jogo,\n$o estilo da janela e outras opções.\n$Há também vários outros recursos aqui.\nCertifique-se de verificar todos eles!", + "starterSelect": "Nesta tela, você pode selecionar seus iniciais\npressionando Z ou a barra de espaço.\n$Esses serão os primeiros membros da sua equipe.\n$Cada inicial tem um custo. Sua equipe pode ter até 6 membros,\ndesde que desde que o custo total não exceda 10.\n$Você pode escolher o gênero, a habilidade\ne até a forma do seu inicial.\n$Essas opções dependem das variantes dessa\nespécie que você já capturou ou chocou.\n$Os IVs de cada inicial são os melhores de todos os Pokémon\ndaquela espécie que você já capturou ou chocou.\n$Sempre capture vários Pokémon de todas as espécies!", "pokerus": "Todo dia, 3 Pokémon iniciais ficam com uma borda roxa.\n$Caso veja um inicial que você possui com uma dessa, tente\nadicioná-lo a sua equipe. Lembre-se de olhar seu sumário!", "statChange": "As mudanças de atributos se mantém após a batalha desde que o Pokémon não seja trocado.\n$Seus Pokémon voltam a suas Poké Bolas antes de batalhas contra treinadores e de entrar em um novo bioma.\n$Para ver as mudanças de atributos dos Pokémon em campo, mantena C ou Shift pressionado durante a batalha.", "selectItem": "Após cada batalha, você pode escolher entre 3 itens aleatórios.\n$Você pode escolher apenas um deles.\n$Esses itens variam entre consumíveis, itens de segurar e itens passivos permanentes.\n$A maioria dos efeitos de itens não consumíveis podem ser acumulados.\n$Alguns itens só aparecerão se puderem ser usados, como os itens de evolução.\n$Você também pode transferir itens de segurar entre os Pokémon utilizando a opção \"Alterar\".\n$A opção de transferir irá aparecer no canto inferior direito assim que você obter um item de segurar.\n$Você pode comprar itens consumíveis com dinheiro, e sua variedade aumentará conforme você for mais longe.\n$Certifique-se de comprá-los antes de escolher seu item aleatório. Ao escolhê-lo, a próxima batalha começará.", - "eggGacha": "Aqui você pode trocar seus vouchers\npor ovos de Pokémon.\n$Ovos ficam mais próximos de chocar após cada batalha.\nOvos mais raros demoram mais para chocar.\n$Pokémon chocados não serão adicionados a sua equipe,\nmas sim aos seus iniciais.\n$Pokémon chocados geralmente possuem IVs melhores\nque Pokémon selvagens.\n$Alguns Pokémon só podem ser obtidos através de seus ovos.\n$Temos 3 máquinas, cada uma com seu bônus específico,\nentão escolha a que mais lhe convém!" + "eggGacha": "Nesta tela, você pode trocar seus vouchers por ovos\nde Pokémon.\n$Ovos ficam mais próximos de chocar após cada batalha.\nOvos mais raros demoram mais tempo para chocar.\n$Pokémon chocados não serão adicionados a sua equipe,\nmas sim aos seus iniciais.\n$Pokémon chocados de ovos geralmente têm IVs melhores\ndo que Pokémon selvagens.\n$Alguns Pokémon só podem ser obtidos através de ovos.\n$Existem 3 máquinas para usar com diferentes bônus, então\nescolha a que mais lhe convém!" } From a894438e24166f51ef7258c70f03194d5833eeea Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:18:49 -0400 Subject: [PATCH 025/108] [Localisation] [JA] Several files translated (#3916) * Translate ability-trigger.json via GitLocalize * Translate game-stats-ui-handler.json via GitLocalize * Translate modifier-select-ui-handler.json via GitLocalize * Translate pokemon-form-battle.json via GitLocalize * Translate battle-info.json via GitLocalize * Translate menu.json via GitLocalize * Translate battler-tags.json via GitLocalize * Translate pokemon-info.json via GitLocalize * Translate achv.json via GitLocalize * Translate berry.json via GitLocalize * Translate achv-female.json via GitLocalize * Translate challenges.json via GitLocalize * Translate menu-ui-handler.json via GitLocalize * Translate egg.json via GitLocalize * Translate pokemon.json via GitLocalize * Translate pokemon.json via GitLocalize * Translate starter-select-ui-handler.json via GitLocalize * Translate command-ui-handler.json via GitLocalize * Translate settings.json via GitLocalize * Update achv-female.json * Update menu-ui-handler.json * Update settings.json * Update achv.json * Update achv.json * Update achv.json * Delete src/locales/ja/achv-female.json * Update achv.json Compared in-game, with these changes it should all look good. * Update challenges.json * Update game-mode.json * Update menu.json * Update settings.json * Translate game-stats-ui-handler.json via GitLocalize * Update game-stats-ui-handler.json * Update settings.json --------- Co-authored-by: gitlocalize-app[bot] <55277160+gitlocalize-app[bot]@users.noreply.github.com> Co-authored-by: Enoch Co-authored-by: Chapybara-jp --- src/locales/ja/ability-trigger.json | 3 +- src/locales/ja/achv.json | 23 ++++--- src/locales/ja/battler-tags.json | 14 +++- src/locales/ja/berry.json | 24 +++---- src/locales/ja/challenges.json | 22 +++--- src/locales/ja/command-ui-handler.json | 4 +- src/locales/ja/egg.json | 22 +++--- src/locales/ja/game-mode.json | 6 +- src/locales/ja/game-stats-ui-handler.json | 30 ++++---- src/locales/ja/menu-ui-handler.json | 17 +++-- src/locales/ja/menu.json | 67 +++++++++++------- .../ja/modifier-select-ui-handler.json | 10 +-- src/locales/ja/pokemon-info.json | 29 ++++---- src/locales/ja/pokemon.json | 8 +-- src/locales/ja/settings.json | 68 +++++++++++++++---- src/locales/ja/starter-select-ui-handler.json | 9 +-- 16 files changed, 218 insertions(+), 138 deletions(-) diff --git a/src/locales/ja/ability-trigger.json b/src/locales/ja/ability-trigger.json index f9d1cc60e4c..ec77d4d97d3 100644 --- a/src/locales/ja/ability-trigger.json +++ b/src/locales/ja/ability-trigger.json @@ -12,7 +12,6 @@ "blockItemTheft": "{{pokemonNameWithAffix}}の {{abilityName}}で\n道具を うばわれない!", "typeImmunityHeal": "{{pokemonNameWithAffix}}は {{abilityName}}で\n体力を 回復した!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}は {{abilityName}}で\nダメージを 受けない。", - "postDefendDisguise": "{{pokemonNameWithAffix}}の\nばけのかわが はがれた!", "moveImmunity": "{{pokemonNameWithAffix}}には\n効果が ないようだ…", "reverseDrain": "{{pokemonNameWithAffix}}は\nヘドロえきを 吸い取った!", "postDefendTypeChange": "{{pokemonNameWithAffix}}は {{abilityName}}で\n{{typeName}}タイプに なった!", @@ -60,4 +59,4 @@ "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}の わざわいのおふだ\nまわりの {{statName}}が 弱まった!", "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}の わざわいのたまで\nまわりの {{statName}}が 弱まった!", "preventBerryUse": "{{pokemonNameWithAffix}}は 緊張して\nきのみが 食べられなくなった!" -} \ No newline at end of file +} diff --git a/src/locales/ja/achv.json b/src/locales/ja/achv.json index 0dc5dc8185a..809375e5c7e 100644 --- a/src/locales/ja/achv.json +++ b/src/locales/ja/achv.json @@ -6,7 +6,7 @@ "name": "なし" }, "MoneyAchv": { - "description": "一回の ランで ₽{{moneyAmount}}を 稼ぐ" + "description": "一回の ランで {{moneyAmount}}円を 稼ぐ" }, "10K_MONEY": { "name": "お金を持つ人" @@ -21,7 +21,7 @@ "name": "超富裕層" }, "DamageAchv": { - "description": "一撃で {{damageAmount}}ダメージを 与える" + "description": "一撃で HP{{damageAmount}}の ダメージを 与える" }, "250_DMG": { "name": "力持ち" @@ -33,10 +33,11 @@ "name": "カカロット" }, "10000_DMG": { - "name": "ワンパンマン" + "name": "ワンパンマン", + "name_female": "ワンパンウーマン" }, "HealAchv": { - "description": "一つの 技や 特性や 持っているアイテムで {{healAmount}}{{HP}}を 一気に 回復する" + "description": "一つの 技や 特性や 持っているアイテムで\n{{healAmount}}{{HP}}を 一気に 回復する" }, "250_HEAL": { "name": "回復発見者" @@ -82,7 +83,7 @@ }, "TRANSFER_MAX_BATTLE_STAT": { "name": "同力", - "description": "少なくとも 一つの 能力を 最大まで あげて 他の 手持ちポケモンに バトンタッチする" + "description": "少なくとも 一つの 能力を 最大まで あげて\n他の 手持ちポケモンに バトンタッチする" }, "MAX_FRIENDSHIP": { "name": "マブ達", @@ -106,7 +107,7 @@ }, "SPLICE": { "name": "インフィニット・フュジョン", - "description": "いでんしのくさびで 二つの ポケモンを 吸収合体させる" + "description": "遺伝子のくさびで 二つの ポケモンを 吸収合体させる" }, "MINI_BLACK_HOLE": { "name": "アイテムホーリック", @@ -161,8 +162,8 @@ "description": "クラシックモードを クリアする" }, "UNEVOLVED_CLASSIC_VICTORY": { - "name": "Bring Your Child To Work Day", - "description": "Beat the game in Classic Mode with at least one unevolved party member." + "name": "はじめてのおつかい", + "description": "少なくとも 一つの 進化していない 手持ちポケモンで\nクラシックモードを クリアする" }, "MONO_GEN_ONE": { "name": "原始", @@ -260,5 +261,9 @@ "FRESH_START": { "name": "一発で!", "description": "出直しチャレンジを クリアする" + }, + "INVERSE_BATTLE": { + "name": "カガミよミガカ", + "description": "反転バトルチャレンジを クリアする\nるすアリク をジンレャチルトバ転反" } -} \ No newline at end of file +} diff --git a/src/locales/ja/battler-tags.json b/src/locales/ja/battler-tags.json index beef485ffd2..25412c971e9 100644 --- a/src/locales/ja/battler-tags.json +++ b/src/locales/ja/battler-tags.json @@ -1,4 +1,12 @@ { + "trappedDesc": "捕らわれること", + "flinchedDesc": "ひるむこと", + "confusedDesc": "混乱", + "infatuatedDesc": "メロメロ", + "seedDesc": "種を植えつくこと", + "nightmareDesc": "あくむ", + "ingrainDesc": "根", + "drowsyDesc": "ねむけ", "rechargingLapse": "{{pokemonNameWithAffix}}は 攻撃の 反動で 動けない!", "trappedOnAdd": "{{pokemonNameWithAffix}}は もう 逃げられない!", "trappedOnRemove": "{{pokemonNameWithAffix}}は\n{{moveName}}の 効果が 解けた!", @@ -13,9 +21,9 @@ "infatuatedOnAdd": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に メロメロに なった!", "infatuatedOnOverlap": "{{pokemonNameWithAffix}}は すでに メロメロだ!", "infatuatedLapse": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に メロメロだ!", - "infatuatedLapseImmobilize": "{{pokemonNameWithAffix}}は\nメロメロで わざが 出せなかった!", + "infatuatedLapseImmobilize": "{{pokemonNameWithAffix}}は メロメロで 技が出せなかった!", "infatuatedOnRemove": "{{pokemonNameWithAffix}}は メロメロ状態が 治った!", - "seededOnAdd": "{{pokemonNameWithAffix}}に 種を 植(う)えつけた!", + "seededOnAdd": "{{pokemonNameWithAffix}}に 種を 植えつけた!", "seededLapse": "やどりぎが {{pokemonNameWithAffix}}の 体力を うばう!", "seededLapseShed": "{{pokemonNameWithAffix}}は ヘドロえきを 吸い取った!", "nightmareOnAdd": "{{pokemonNameWithAffix}}は あくむを 見始めた!", @@ -60,4 +68,4 @@ "cursedOnAdd": "{{pokemonNameWithAffix}}は 自分の 体力を 削って\n{{pokemonName}}に のろいを かけた!", "cursedLapse": "{{pokemonNameWithAffix}}は のろわれている!", "stockpilingOnAdd": "{{pokemonNameWithAffix}}は {{stockpiledCount}}つ たくわえた!" -} \ No newline at end of file +} diff --git a/src/locales/ja/berry.json b/src/locales/ja/berry.json index 641901583b2..73d13d5e8f0 100644 --- a/src/locales/ja/berry.json +++ b/src/locales/ja/berry.json @@ -1,46 +1,46 @@ { "SITRUS": { "name": "オボンのみ", - "effect": "HP 50%いかのとき HPを 25パーセント かいふくする" + "effect": "持たせると HPが 50%以下になるとき HPを 25% 回復する" }, "LUM": { "name": "ラムのみ", - "effect": "すべての じょうたい いじょうと こんらんを かいふくする" + "effect": "持たせると 状態異常や 混乱になるとき 回復する\n" }, "ENIGMA": { "name": "ナゾのみ", - "effect": "こうかばつぐんの わざを うけたとき HPを 25パーセント かいふくする" + "effect": "持たせると 効果バツグンの 技を 受けたとき HPを 25%回復する" }, "LIECHI": { "name": "チイラのみ", - "effect": "HP 25%いかのとき こうげきが あがる" + "effect": "持たせると HPが 25%以下に なるとき 攻撃が あがる" }, "GANLON": { "name": "リュガのみ", - "effect": "HP 25%いかのとき ぼうぎょが あがる" + "effect": "持たせると HPが 25%以下に なるとき 防御が あがる\n" }, "PETAYA": { "name": "ヤタピのみ", - "effect": "HP 25%いかのとき とくこうが あがる" + "effect": "持たせると HPが 25%以下に なるとき 特攻が あがる\n" }, "APICOT": { "name": "ズアのみ", - "effect": "HP 25%いかのとき とくぼうが あがる" + "effect": "持たせると HPが 25%以下に なるとき 特防が あがる\n" }, "SALAC": { "name": "カムラのみ", - "effect": "HP 25%いかのとき すばやさが あがる" + "effect": "持たせると HPが 25%以下に なるとき 素早さが あがる" }, "LANSAT": { "name": "サンのみ", - "effect": "HP 25%いかのとき こうげきが きゅうしょに あたりやすくなる" + "effect": "持たせると HPが 25%以下に なるとき 攻撃が 急所に 当たりやすくなる" }, "STARF": { "name": "スターのみ", - "effect": "HP 25%いかのとき のうりょくの どれか 1つが ぐーんと あがる" + "effect": "持たせると HPが 25%以下に なるとき どれか 1つの 能力が ぐーんと あがる" }, "LEPPA": { "name": "ヒメリのみ", - "effect": "PPが 0に なった わざの PPを 10だけ かいふくする" + "effect": "持たせると PPが 0になる 技のPPを 10回復する" } -} \ No newline at end of file +} diff --git a/src/locales/ja/challenges.json b/src/locales/ja/challenges.json index 54225ebf766..99c6e978d49 100644 --- a/src/locales/ja/challenges.json +++ b/src/locales/ja/challenges.json @@ -1,10 +1,10 @@ { - "title": "チャレンジを 設定", + "title": "チャレンジを 設定", "illegalEvolution": "{{pokemon}}は このチャレンジで\n対象外の ポケモンに なってしまった!", "singleGeneration": { "name": "単一世代", - "desc": "{{gen}}世代からの ポケモンしか 使えません", - "desc_default": "選んだ 世代からの ポケモンしか 使えません", + "desc": "{{gen}}世代からの ポケモンしか 使えません", + "desc_default": "選んだ 世代からの ポケモンしか 使えません", "gen_1": "1", "gen_2": "2", "gen_3": "3", @@ -17,14 +17,20 @@ }, "singleType": { "name": "単一タイプ", - "desc": "{{type}}タイプの ポケモンしか 使えません", - "desc_default": "選んだ タイプの ポケモンしか 使えません" + "desc": "{{type}}タイプの ポケモンしか 使えません", + "desc_default": "選んだ タイプの ポケモンしか 使えません" }, "freshStart": { "name": "出直し", - "shortName": "出直し", - "desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の 最初のパートナーしか 使えません", + "desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の スターターしか 使えません", + "value.0": "オフ", + "value.1": "オン" + }, + "inverseBattle": { + "name": "反転バトル", + "shortName": "反バ", + "desc": "タイプ相性が 反転で、なんの タイプも 「効果はなし」が ありません\n他の チャレンジの 実績が 無効に されます", "value.0": "オフ", "value.1": "オン" } -} \ No newline at end of file +} diff --git a/src/locales/ja/command-ui-handler.json b/src/locales/ja/command-ui-handler.json index 0b2020a9517..6248a19785f 100644 --- a/src/locales/ja/command-ui-handler.json +++ b/src/locales/ja/command-ui-handler.json @@ -3,5 +3,5 @@ "ball": "ボール", "pokemon": "ポケモン", "run": "にげる", - "actionMessage": "{{pokemonName}}は どうする?" -} \ No newline at end of file + "actionMessage": "{{pokemonName}}は どうする?" +} diff --git a/src/locales/ja/egg.json b/src/locales/ja/egg.json index b0cb7b7de61..91b1442c56c 100644 --- a/src/locales/ja/egg.json +++ b/src/locales/ja/egg.json @@ -4,23 +4,23 @@ "ultraTier": "超レア", "masterTier": "伝説", "defaultTier": "ふつう", - "hatchWavesMessageSoon": "なかから おとが きこえてくる! もうすぐ うまれそう!", - "hatchWavesMessageClose": "ときどき うごいている みたい。 うまれるまで もう ちょっとかな?", - "hatchWavesMessageNotClose": "なにが うまれてくるのかな? うまれるまで まだまだ じかんが かかりそう。", - "hatchWavesMessageLongTime": "この タマゴは うまれるまで かなり じかんが かかりそう。", + "hatchWavesMessageSoon": "中から 音が 聞こえてくる! もうすぐ 生まれそう!", + "hatchWavesMessageClose": "時々 動いている みたい。生まれるまで もう ちょっとかな?", + "hatchWavesMessageNotClose": "なにが 生まれてくるのかな? 生まれるまで まだまだ 時間が かかりそう。", + "hatchWavesMessageLongTime": "この タマゴは 生まれるまで かなり 時間が かかりそう。", "gachaTypeLegendary": "伝説確率アップ", - "gachaTypeMove": "レアなタマゴわざ確率アップ", + "gachaTypeMove": "レアなタマゴ技確率アップ", "gachaTypeShiny": "色違い確率アップ", "selectMachine": "ガチャマシンを選択", "notEnoughVouchers": "タマゴクーポンが足りません!", "tooManyEggs": "タマゴが一杯です!", "pull": "回引く", "pulls": "回引く", - "sameSpeciesEgg": "{{species}}は このタマゴから うまれる!", - "hatchFromTheEgg": "{{pokemonName}}は タマゴから うまれた!", - "eggMoveUnlock": "タマゴわざ {{moveName}}を おぼえた!", - "rareEggMoveUnlock": "レアなタマゴわざ {{moveName}}を おぼえた!!", - "moveUPGacha": "わざ UP!", + "sameSpeciesEgg": "{{species}}は このタマゴから 生まれる!", + "hatchFromTheEgg": "{{pokemonName}}は タマゴから 生まれた!", + "eggMoveUnlock": "タマゴ技: {{moveName}}を 覚えた!", + "rareEggMoveUnlock": "レアなタマゴ技: {{moveName}}を 覚えた!!", + "moveUPGacha": "技 UP!", "shinyUPGacha": "色違い UP!", "legendaryUPGacha": "UP!" -} \ No newline at end of file +} diff --git a/src/locales/ja/game-mode.json b/src/locales/ja/game-mode.json index 36559e5cce3..dc04b36932f 100644 --- a/src/locales/ja/game-mode.json +++ b/src/locales/ja/game-mode.json @@ -1,8 +1,8 @@ { "classic": "クラシック", "endless": "エンドレス", - "endlessSpliced": "エンドレス (Spliced)", + "endlessSpliced": "エンドレス(吸収合体)", "dailyRun": "デイリーラン", - "unknown": "Unknown", + "unknown": "???", "challenge": "チャレンジ" -} \ No newline at end of file +} diff --git a/src/locales/ja/game-stats-ui-handler.json b/src/locales/ja/game-stats-ui-handler.json index 25bb21f701a..2fff802734a 100644 --- a/src/locales/ja/game-stats-ui-handler.json +++ b/src/locales/ja/game-stats-ui-handler.json @@ -1,6 +1,6 @@ { "stats": "統計", - "playTime": "プレー時間", + "playTime": "プレイ時間", "totalBattles": "合計バトル数", "starters": "スターター数", "shinyStarters": "色違いスターター数", @@ -12,31 +12,31 @@ "dailyRunAttempts": "デイリーラン", "dailyRunWins": "デイリーラン勝利", "endlessRuns": "エンドレスラン", - "highestWaveEndless": "エンドレス最高ウェーブ", + "highestWaveEndless": "エンドレス最高波", "highestMoney": "最大貯金", "highestDamage": "最大ダメージ", "highestHPHealed": "最大HP回復", "pokemonEncountered": "遭遇したポケモン", "pokemonDefeated": "倒したポケモン", "pokemonCaught": "捕まえたポケモン", - "eggsHatched": "ふかしたタマゴ", - "subLegendsSeen": "見つけた順伝説", - "subLegendsCaught": "捕まえた順伝説", - "subLegendsHatched": "ふかした順伝説", - "legendsSeen": "見つけた伝説", - "legendsCaught": "捕まえた伝説", - "legendsHatched": "ふかした伝説", + "eggsHatched": "孵化したタマゴ", + "subLegendsSeen": "見つけた順伝説ポケモン", + "subLegendsCaught": "捕まえた準伝説ポケモン", + "subLegendsHatched": "孵化した準伝説ポケモン", + "legendsSeen": "見つけた伝説ポケモン", + "legendsCaught": "捕まえた伝説ポケモン", + "legendsHatched": "孵化した伝説ポケモン", "mythicalsSeen": "見つけた幻ポケモン", "mythicalsCaught": "捕まえた幻ポケモン", - "mythicalsHatched": "ふかした幻ポケモン", - "shiniesSeen": "見つけた色違い", - "shiniesCaught": "捕まえた色違い", - "shiniesHatched": "ふかした色違い", - "pokemonFused": "合体したポケモン", + "mythicalsHatched": "孵化した幻ポケモン", + "shiniesSeen": "見つけた色違いポケモン", + "shiniesCaught": "捕まえた色違いポケモン", + "shiniesHatched": "孵化した色違いポケモン", + "pokemonFused": "吸収合体したポケモン", "trainersDefeated": "倒したトレーナー", "eggsPulled": "引いたタマゴ", "rareEggsPulled": "引いたレアタマゴ", "epicEggsPulled": "引いた超レアタマゴ", "legendaryEggsPulled": "引いた伝説タマゴ", "manaphyEggsPulled": "引いたマナフィタマゴ" -} \ No newline at end of file +} diff --git a/src/locales/ja/menu-ui-handler.json b/src/locales/ja/menu-ui-handler.json index beb014b84a9..1930c3999c6 100644 --- a/src/locales/ja/menu-ui-handler.json +++ b/src/locales/ja/menu-ui-handler.json @@ -2,19 +2,22 @@ "GAME_SETTINGS": "設定", "ACHIEVEMENTS": "実績", "STATS": "統計", + "RUN_HISTORY": "ラン歴", "EGG_LIST": "タマゴリスト", "EGG_GACHA": "タマゴガチャ", "MANAGE_DATA": "データ管理", "COMMUNITY": "コミュニティ", - "SAVE_AND_QUIT": "保存して終了", + "SAVE_AND_QUIT": "セーブして終了", "LOG_OUT": "ログアウト", "slot": "スロット {{slotNumber}}", - "importSession": "セッションのインポート", + "importSession": "セッションをインポート", "importSlotSelect": "インポート先の スロットを 選んでください", - "exportSession": "セッションのエクスポート", + "exportSession": "セッションをエクスポート", "exportSlotSelect": "エクスポート元の スロットを 選んでください", - "importData": "データのインポート", - "exportData": "データのエクスポート", + "importRunHistory": "ラン歴をインポート", + "exportRunHistory": "ラン歴をエクスポート", + "importData": "データをインポート", + "exportData": "データをエクスポート", "consentPreferences": "同意設定", "linkDiscord": "Discord連携", "unlinkDiscord": "Discord連携解除", @@ -22,5 +25,5 @@ "unlinkGoogle": "Google連携解除", "cancel": "キャンセル", "losingProgressionWarning": "戦闘開始からの データが 保存されません。\nよろしいですか?", - "noEggs": "現在 タマゴを ふかしていません!" -} \ No newline at end of file + "noEggs": "現在は タマゴを 孵化していません!" +} diff --git a/src/locales/ja/menu.json b/src/locales/ja/menu.json index ce6f9ae0672..0e7701578bf 100644 --- a/src/locales/ja/menu.json +++ b/src/locales/ja/menu.json @@ -1,38 +1,55 @@ { "cancel": "キャンセル", "continue": "つづきから", - "loadGame": "ロードセーブ", + "dailyRun": "日替わりラン(ベータ版)", + "loadGame": "セーブを読み込む", "newGame": "はじめから", - "username": "ユーザーめい", + "settings": "設定", + "selectGameMode": "ゲームモードを 選んでください。", + "logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要が ありません!", + "username": "ユーザー名", "password": "パスワード", "login": "ログイン", - "orUse": "Or use", - "register": "かいいん とうろく", - "emptyUsername": "ユーザー名は空にできません", - "invalidLoginUsername": "入力したユーザー名は無効です", - "invalidRegisterUsername": "ユーザー名には英文字、数字、アンダースコアのみを含める必要があります", + "orUse": "他の\nログイン方法", + "register": "登録", + "emptyUsername": "ユーザー名を 空にする ことは できません", + "invalidLoginUsername": "入力されたユーザー名は無効です", + "invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむ必要が あります", "invalidLoginPassword": "入力したパスワードは無効です", - "invalidRegisterPassword": "パスワードは6文字以上でなければなりません", - "usernameAlreadyUsed": "ユーザー名は既に使用されています", - "accountNonExistent": "ユーザーは存在しません", - "unmatchingPassword": "パスワードが一致しません", - "passwordNotMatchingConfirmPassword": "パスワードは確認パスワードと一致する必要があります", + "invalidRegisterPassword": "パスワードは 6文字以上 でなければなりません", + "usernameAlreadyUsed": "入力したユーザー名は すでに 使用されています", + "accountNonExistent": "入力したユーザーは 存在しません", + "unmatchingPassword": "入力したパスワードが 一致しません", + "passwordNotMatchingConfirmPassword": "パスワードは パスワード確認と 一致する 必要があります", "confirmPassword": "パスワード確認", - "registrationAgeWarning": "登録することで、あなたが13歳以上であることを確認します。", + "registrationAgeWarning": "登録では 13歳以上 であることを 確認します。", "backToLogin": "ログインへ", - "failedToLoadSaveData": "保存データの読み込みに失敗しました。ページを再読み込みしてください。\nこれが続く場合は、管理者に連絡してください。", - "sessionSuccess": "セッションが正常に読み込まれました。", - "failedToLoadSession": "セッションデータを読み込むことができませんでした。\nデータが破損している可能性があります。", - "boyOrGirl": "おとこのこ?\nそれとも おんなのこ?", - "evolving": "…おや!?\n{{pokemonName}}のようすが…!", - "stoppedEvolving": "{{pokemonName}}のへんかがとまった", - "evolutionDone": "おめでとう!\n{{pokemonName}}は{{evolvedPokemonName}}にしんかした!", - "dailyRankings": "ほんじつのランキング", - "weeklyRankings": "しゅうのランキング", + "failedToLoadSaveData": "セーブデータの 読み込みは 不可能でした。ページを 再読み込み してください。\n長い間に続く 場合は 管理者に 連絡してください。", + "sessionSuccess": "セッションが 正常に 読み込まれました。", + "failedToLoadSession": "セッションデータを 読み込むことが できませんでした。\nデータが 破損している 可能性が あります。", + "boyOrGirl": "男の子?\nそれとも 女の子?", + "evolving": "…おや!?\n{{pokemonName}}の 様子が…!", + "stoppedEvolving": "あれ…? {{pokemonName}}の 変化が 止まった!", + "pauseEvolutionsQuestion": "{{pokemonName}}の 進化を 休止しますか?\n後で 手持ち画面から 進化を また 可能にできます。", + "evolutionsPaused": "{{pokemonName}}の 進化を 休止しました。", + "evolutionDone": "おめでとう!\n{{pokemonName}}は {{evolvedPokemonName}}に 進化した!", + "dailyRankings": "今日のランキング", + "weeklyRankings": "今週のランキング", "noRankings": "ランキングなし", "positionIcon": "#", - "loading": "よみこみちゅう…", + "usernameScoreboard": "ユーザー名", + "score": "スコア", + "wave": "波", + "loading": "読み込み中…", + "loadingAsset": "読み込み中:{{assetName}}", "playersOnline": "オンラインのプレイヤー", "yes": "はい", - "no": "いいえ" -} \ No newline at end of file + "no": "いいえ", + "disclaimer": "免責", + "disclaimerDescription": "このゲームは 未完成作品です。\nセーブデータの 損失を含める ゲーム性に関する 問題が 起きる可能性が あります。\nなお、ゲームは 予告なく変更される 可能性もあり、さらに更新され、完成されるとも 限りません。", + "choosePokemon": "ポケモンを選ぶ", + "renamePokemon": "ニックネームを変える", + "rename": "変える", + "nickname": "ニックネーム", + "errorServerDown": "おや!\nサーバーとの 接続中に 問題が 発生しました。\nゲームは 自動的に 再接続されます から\nウィンドウは 開いたままに しておいても よろしいです。" +} diff --git a/src/locales/ja/modifier-select-ui-handler.json b/src/locales/ja/modifier-select-ui-handler.json index 9370f01491e..d7428c8e373 100644 --- a/src/locales/ja/modifier-select-ui-handler.json +++ b/src/locales/ja/modifier-select-ui-handler.json @@ -1,12 +1,12 @@ { "transfer": "アイテム移行", "reroll": "選択肢変更", - "lockRarities": "レア度の固定", - "checkTeam": "チームを確認", - "transferDesc": "ポケモンの 手持ちアイテムを 移行する", + "lockRarities": "レア度を固定", + "checkTeam": "手持ちを確認", + "transferDesc": "手持ちポケモンの 持たせるアイテムを 移行する", "rerollDesc": "お金を 使って アイテムの 選択肢を 変更する", - "lockRaritiesDesc": "選択肢を 変更するときの レア度を 固定する\n(選択肢変更金額を影響する)", - "checkTeamDesc": "チームの 状態を 確認する\nフォルムチェンジアイテムを 有効・無効にする", + "lockRaritiesDesc": "選択肢を 変更するときの レア度を 固定する\n(選択肢変更の価格は変わる)", + "checkTeamDesc": "手持ちポケモンの 状態を 確認する\nフォルムチェンジアイテムを 有効・無効にする", "rerollCost": "{{formattedMoney}}円", "itemCost": "{{formattedMoney}}円" } diff --git a/src/locales/ja/pokemon-info.json b/src/locales/ja/pokemon-info.json index 9b7a7506953..456b4949839 100644 --- a/src/locales/ja/pokemon-info.json +++ b/src/locales/ja/pokemon-info.json @@ -2,21 +2,22 @@ "Stat": { "HP": "HP", "HPshortened": "HP", - "ATK": "こうげき", - "ATKshortened": "こうげき", - "DEF": "ぼうぎょ", - "DEFshortened": "ぼうぎょ", - "SPATK": "とくこう", - "SPATKshortened": "とくこう", - "SPDEF": "とくぼう", - "SPDEFshortened": "とくぼう", - "SPD": "すばやさ", - "SPDshortened": "すばやさ", - "ACC": "めいちゅう", - "EVA": "かいひ" + "ATK": "攻撃", + "ATKshortened": "攻撃", + "DEF": "防御", + "DEFshortened": "防御", + "SPATK": "特攻", + "SPATKshortened": "特攻", + "SPDEF": "特防", + "SPDEFshortened": "特防", + "SPD": "素早さ", + "SPDshortened": "素早さ", + "ACC": "命中", + "EVA": "回避", + "HPStat": "HP" }, "Type": { - "UNKNOWN": "Unknown", + "UNKNOWN": "???", "NORMAL": "ノーマル", "FIGHTING": "かくとう", "FLYING": "ひこう", @@ -37,4 +38,4 @@ "FAIRY": "フェアリー", "STELLAR": "ステラ" } -} \ No newline at end of file +} diff --git a/src/locales/ja/pokemon.json b/src/locales/ja/pokemon.json index 6c182c09f86..e6fcd02a750 100644 --- a/src/locales/ja/pokemon.json +++ b/src/locales/ja/pokemon.json @@ -437,7 +437,7 @@ "bronzor": "ドーミラー", "bronzong": "ドータクン", "bonsly": "ウソハチ", - "mime_jr.": "マネネ", + "mime_jr": "マネネ", "happiny": "ピンプク", "chatot": "ペラップ", "spiritomb": "ミカルゲ", @@ -770,7 +770,7 @@ "sandygast": "スナバァ", "palossand": "シロデスナ", "pyukumuku": "ナマコブシ", - "type:_null": "タイプ:ヌル", + "type_null": "タイプ:ヌル", "silvally": "シルヴァディ", "minior": "メテノ", "komala": "ネッコアラ", @@ -863,7 +863,7 @@ "obstagoon": "タチフサグマ", "perrserker": "ニャイキング", "cursola": "サニゴーン", - "sirfetch_d": "ネギガナイト", + "sirfetchd": "ネギガナイト", "mr_rime": "バリコオル", "runerigus": "デスバーン", "milcery": "マホミル", @@ -1081,4 +1081,4 @@ "paldea_tauros": "ケンタロス", "paldea_wooper": "ウパー", "bloodmoon_ursaluna": "ガチグマ" -} \ No newline at end of file +} diff --git a/src/locales/ja/settings.json b/src/locales/ja/settings.json index c88792979f6..4cb10c670de 100644 --- a/src/locales/ja/settings.json +++ b/src/locales/ja/settings.json @@ -6,12 +6,14 @@ "audio": "音声", "gamepad": "コントローラー", "keyboard": "キーボード", - "gameSpeed": "ゲームスピード", - "hpBarSpeed": "HPバーの増減スピード", - "expGainsSpeed": "EXPバーの増加スピード", - "expPartyDisplay": "パーティの経験値取得表示", + "gameSpeed": "ゲームの速さ", + "hpBarSpeed": "HPバー増減の速さ", + "expGainsSpeed": "経験値バー増加の速さ", + "expPartyDisplay": "手持ちの経験値取得表示", + "skipSeenDialogues": "もう見た話をスキップ", "battleStyle": "試合のルール", - "enableRetries": "リトライを有効にする", + "enableRetries": "再挑戦を有効にする", + "hideIvs": "個体値スキャナーを隠す", "tutorials": "チュートリアル", "touchControls": "タッチ操作", "vibrations": "振動", @@ -35,33 +37,71 @@ "moneyFormat": "お金の表示形式", "damageNumbers": "ダメージ表示", "simple": "シンプル", - "fancy": "Fancy", + "fancy": "オシャレ", "abbreviated": "省略", - "moveAnimations": "戦闘アニメ", + "moveAnimations": "戦闘アニメーション", "showStatsOnLevelUp": "レベルアップ時のステータス表示", + "candyUpgradeNotification": "飴アプグレ通知", "passivesOnly": "パッシブのみ", + "candyUpgradeDisplay": "飴アプグレ表示", "icon": "アイコン", "animation": "アニメーション", - "moveInfo": "技の情報表示", + "moveInfo": "技情報", + "showMovesetFlyout": "技情報表示", + "showArenaFlyout": "戦場情報表示", + "showTimeOfDayWidget": "時刻指標", + "timeOfDayAnimation": "時刻指標アニメーション", + "bounce": "跳ねる", + "timeOfDay_back": "跳ね返る", + "spriteSet": "スプライト設定", + "consistent": "一貫", + "mixedAnimated": "アニメーションミックス", + "fusionPaletteSwaps": "吸収合体ポケモンの色違い", "playerGender": "プレイヤーの性別", - "typeHints": "相性のヒント", + "typeHints": "タイプ相性ヒント", "masterVolume": "マスターボリューム", - "bgmVolume": "BGMのボリューム", - "seVolume": "SEのボリューム", + "bgmVolume": "BGMボリューム", + "fieldVolume": "フィールドボリューム", + "seVolume": "SEボリューム", + "uiVolume": "UIボリューム", + "musicPreference": "BGM設定", + "mixed": "ミックス", + "gamepadPleasePlug": "コントローラーを 接続してください\nまたは、ボタンを 押してください", + "delete": "削除", + "keyboardPleasePress": "キーを押してください", "reset": "リセット", "requireReload": "再読み込みが必要", "action": "決定", "back": "戻る", + "pressToBind": "押下でキーバインド", + "pressButton": "ボタンを押してください", "buttonUp": "上", "buttonDown": "下", "buttonLeft": "左", "buttonRight": "右", "buttonAction": "決定", "buttonMenu": "メニュー", - "buttonSubmit": "Submit", + "buttonSubmit": "提出", "buttonCancel": "キャンセル", - "alt": " (代替)", + "buttonStats": "能力変化表示", + "buttonCycleForm": "フォルム変更", + "buttonCycleShiny": "色違い変更", + "buttonCycleGender": "性別変更", + "buttonCycleAbility": "特性変更", + "buttonCycleNature": "性格変更", + "buttonCycleVariant": "色変更", + "buttonSpeedUp": "速さを上げる", + "buttonSlowDown": "速さを下げる", + "alt": "(代替)", "mute": "ミュート", "controller": "コントローラー", - "gamepadSupport": "コントローラーサポート" + "gamepadSupport": "コントローラーサポート", + "showBgmBar": "BGMの名前を表示", + "moveTouchControls": "タッチ移動操作", + "shopOverlayOpacity": "ショップオーバレイ不透明度", + "shopCursorTarget": "ショップカーソル初位置", + "items": "アイテム", + "reroll": "選択肢変更", + "shop": "ショップ", + "checkTeam": "手持ちを確認" } diff --git a/src/locales/ja/starter-select-ui-handler.json b/src/locales/ja/starter-select-ui-handler.json index 84eaa8598e9..cab5c500df6 100644 --- a/src/locales/ja/starter-select-ui-handler.json +++ b/src/locales/ja/starter-select-ui-handler.json @@ -1,5 +1,5 @@ { - "confirmStartTeam": "この条件で チャレンジを 始めますか?", + "confirmStartTeam": "この手持ちで 始めますか?", "confirmExit": "終了しますか?", "invalidParty": "手持ちは チャレンジの 条件で 認められない!", "gen1": "1世代", @@ -16,8 +16,8 @@ "passive": "パッシブ:", "nature": "性格:", "eggMoves": "タマゴ技", - "start": "始める", "addToParty": "手持ちに入れる", + "removeFromParty": "手持ちから除く", "toggleIVs": "個体値を表示", "manageMoves": "技を並び替える", "manageNature": "性格を変える", @@ -36,9 +36,10 @@ "cycleAbility": ": 特性変更", "cycleNature": ": 性格変更", "cycleVariant": ": 色変更", + "goFilter": ": フィルタ へ ", "enablePassive": "パッシブ - オン", "disablePassive": "パッシブ - オフ", - "locked": "開放されていない", + "locked": "非開放", "disabled": "無効", "uncaught": "捕まっていない" -} \ No newline at end of file +} From 22d31bc704572c746dddfab82d8f7d0842862138 Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:19:02 -0400 Subject: [PATCH 026/108] [Localisation] [ES] Review and finished pokemon-form and pokemon-form-battle (#3903) * Translate pokemon-form.json via GitLocalize * Translate pokemon-form.json via GitLocalize * Translate pokemon-form-battle.json via GitLocalize * Translate pokemon-form-battle.json via GitLocalize * Update src/locales/es/pokemon-form.json Co-authored-by: Asdar --------- Co-authored-by: Rafa Co-authored-by: LilyAlternis Co-authored-by: Asdar Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> --- src/locales/es/pokemon-form-battle.json | 10 +++++-- src/locales/es/pokemon-form.json | 39 +++++++++++++++++++------ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/locales/es/pokemon-form-battle.json b/src/locales/es/pokemon-form-battle.json index 7af01f8f093..5266baa7049 100644 --- a/src/locales/es/pokemon-form-battle.json +++ b/src/locales/es/pokemon-form-battle.json @@ -4,5 +4,11 @@ "mega-y": "Mega {{pokemonName}} Y", "primal": "{{pokemonName}} Primigenio", "gigantamax": "G-Max {{pokemonName}}", - "eternamax": "E-Max {{pokemonName}}" -} \ No newline at end of file + "eternamax": "E-Max {{pokemonName}}", + "megaChange": "¡{{preName}} ha mega-evolucionado a {{pokemonName}}!", + "gigantamaxChange": "¡{{preName}} ha gigamaxizado a {{pokemonName}}!", + "eternamaxChange": "¡{{preName}} ha eternamaxizado a {{pokemonName}}!", + "revertChange": "¡{{pokemonName}} ha revertido a su forma original!", + "formChange": "¡{{preName}} ha cambiado de forma!", + "disguiseChange": "¡El disfraz ha actuado como señuelo!\t" +} diff --git a/src/locales/es/pokemon-form.json b/src/locales/es/pokemon-form.json index c46521d78da..2f70038ad2d 100644 --- a/src/locales/es/pokemon-form.json +++ b/src/locales/es/pokemon-form.json @@ -7,6 +7,7 @@ "pikachuToughCosplay": "Enmascarada", "pikachuPartner": "Compañero", "eeveePartner": "Compañero", + "pichuSpiky": "Picoreja", "unownA": "A", "unownB": "B", "unownC": "C", @@ -49,6 +50,8 @@ "rotomFrost": "Frío", "rotomFan": "Ventilador", "rotomMow": "Corte", + "giratinaAltered": "Modificada", + "shayminLand": "Tierra", "basculinRedStriped": "Raya Roja", "basculinBlueStriped": "Raya Azul", "basculinWhiteStriped": "Raya Blanca", @@ -56,6 +59,10 @@ "deerlingSummer": "Verano", "deerlingAutumn": "Otoño", "deerlingWinter": "Invierno", + "tornadusIncarnate": "Avatar", + "thundurusIncarnate": "Avatar", + "landorusIncarnate": "Avatar", + "keldeoOrdinary": "Habitual", "meloettaAria": "Lírica", "meloettaPirouette": "Danza", "froakieBattleBond": "Fuerte Afecto", @@ -87,12 +94,12 @@ "furfrouHeart": "Corazón", "furfrouStar": "Estrella", "furfrouDiamond": "Diamante", - "furfrouDebutante": "Debutante", - "furfrouMatron": "Matrón", - "furfrouDandy": "Dandi", - "furfrouLaReine": "La Reine", + "furfrouDebutante": "Señorita", + "furfrouMatron": "Dama", + "furfrouDandy": "Caballero", + "furfrouLaReine": "Aristócrata", "furfrouKabuki": "Kabuki", - "furfrouPharaoh": "Faraón", + "furfrouPharaoh": "Faraónico", "pumpkabooSmall": "Pequeño", "pumpkabooLarge": "Grande", "pumpkabooSuper": "Enorme", @@ -127,11 +134,15 @@ "magearnaOriginal": "Vetusto", "marshadowZenith": "Cénit", "sinisteaPhony": "Falsificada", - "sinisteaAntique": "Auténtica", + "sinisteaAntique": "Genuina", "eiscueNoIce": "Cara Deshielo", "indeedeeMale": "Macho", "indeedeeFemale": "Hembra", + "morpekoFullBelly": "Saciada", + "zacianHeroOfManyBattles": "Guerrero avezado", + "zamazentaHeroOfManyBattles": "Guerrero avezado", "zarudeDada": "Papá", + "enamorusIncarnate": "Avatar", "squawkabillyGreenPlumage": "Plumaje Verde", "squawkabillyBluePlumage": "Plumaje Azul", "squawkabillyYellowPlumage": "Plumaje Amarillo", @@ -141,9 +152,19 @@ "tatsugiriStretchy": "Estirada", "gimmighoulChest": "Cofre", "gimmighoulRoaming": "Andante", - "poltchageistCounterfeit": "Imitación", - "poltchageistArtisan": "Original", + "koraidonApexBuild": "Forma Plena", + "koraidonLimitedBuild": "Forma Limitada", + "koraidonSprintingBuild": "Forma Carrera", + "koraidonSwimmingBuild": "Forma Nado", + "koraidonGlidingBuild": "Forma Planeo", + "miraidonUltimateMode": "Modo Pleno", + "miraidonLowPowerMode": "Modo Limitado", + "miraidonDriveMode": "Modo Conducción", + "miraidonAquaticMode": "Modo Flote", + "miraidonGlideMode": "Modo Planeo", + "poltchageistCounterfeit": "Fraudulenta", + "poltchageistArtisan": "Opulenta", "paldeaTaurosCombat": "Combatiente", "paldeaTaurosBlaze": "Ardiente", "paldeaTaurosAqua": "Acuático" -} \ No newline at end of file +} From 4553c1c34ffdbbdda2696bbd69315b3b5a294950 Mon Sep 17 00:00:00 2001 From: PrabbyDD <147005742+PrabbyDD@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:20:16 -0700 Subject: [PATCH 027/108] [Bug] Fix Octolock Ignores Clear Body, White Smoke, Big Pecks #3876 Pecks, Clear Body, and White Smoke Adding tests for octolock --- src/data/battler-tags.ts | 2 +- src/test/moves/octolock.test.ts | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 2e280634d5d..92df6fc294f 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -767,7 +767,7 @@ export class OctolockTag extends TrappedTag { const shouldLapse = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (shouldLapse) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.DEF, BattleStat.SPDEF], -1)); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [BattleStat.DEF, BattleStat.SPDEF], -1)); return true; } diff --git a/src/test/moves/octolock.test.ts b/src/test/moves/octolock.test.ts index 389e4a4c4cf..34dad13b0d9 100644 --- a/src/test/moves/octolock.test.ts +++ b/src/test/moves/octolock.test.ts @@ -61,6 +61,48 @@ describe("Moves - Octolock", () => { expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-2); }); + it("If target pokemon has Big Pecks, Octolock should only reduce spdef by 1", { timeout: 10000 }, async () => { + game.override.enemyAbility(Abilities.BIG_PECKS); + await game.startBattle([Species.GRAPPLOCT]); + + const enemyPokemon = game.scene.getEnemyField(); + + // use Octolock and advance to init phase of next turn to check for stat changes + game.move.select(Moves.OCTOLOCK); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(0); + expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-1); + }); + + it("If target pokemon has White Smoke, Octolock should not reduce any stats", { timeout: 10000 }, async () => { + game.override.enemyAbility(Abilities.WHITE_SMOKE); + await game.startBattle([Species.GRAPPLOCT]); + + const enemyPokemon = game.scene.getEnemyField(); + + // use Octolock and advance to init phase of next turn to check for stat changes + game.move.select(Moves.OCTOLOCK); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(0); + expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(0); + }); + + it("If target pokemon has Clear Body, Octolock should not reduce any stats", { timeout: 10000 }, async () => { + game.override.enemyAbility(Abilities.CLEAR_BODY); + await game.startBattle([Species.GRAPPLOCT]); + + const enemyPokemon = game.scene.getEnemyField(); + + // use Octolock and advance to init phase of next turn to check for stat changes + game.move.select(Moves.OCTOLOCK); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(0); + expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(0); + }); + it("Traps the target pokemon", { timeout: 10000 }, async () => { await game.startBattle([Species.GRAPPLOCT]); From 69a9916b4c2780fd478dd861f1b0e554613ae2e0 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:21:56 -0700 Subject: [PATCH 028/108] [Bug] Moves copied by Dancer should not consume PP (#3623) * Moves copied by Dancer should not consume PP * Add test for Dancer (unfinished) * Delete src/test/abilities/dancer.test.ts This test is not finished lol * Add test --- src/data/ability.ts | 4 +- src/phases/move-phase.ts | 16 ++++---- src/test/abilities/dancer.test.ts | 64 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 src/test/abilities/dancer.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index 4d3d32e22fa..818ab07637f 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -3695,10 +3695,10 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { const target = this.getTarget(dancer, source, targets); - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true)); + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true, true)); } else if (move.getMove() instanceof SelfStatusMove) { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true)); + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true, true)); } } return true; diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index c446660b16f..e2893d587a7 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -1,9 +1,9 @@ import BattleScene from "#app/battle-scene.js"; import { BattlerIndex } from "#app/battle.js"; -import { applyAbAttrs, RedirectMoveAbAttr, BlockRedirectAbAttr, IncreasePpAbAttr, applyPreAttackAbAttrs, PokemonTypeChangeAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr } from "#app/data/ability.js"; +import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs, BlockRedirectAbAttr, IncreasePpAbAttr, PokemonTypeChangeAbAttr, PostMoveUsedAbAttr, RedirectMoveAbAttr } from "#app/data/ability.js"; import { CommonAnim } from "#app/data/battle-anims.js"; -import { CenterOfAttentionTag, BattlerTagLapseType } from "#app/data/battler-tags.js"; -import { MoveFlags, BypassRedirectAttr, allMoves, CopyMoveAttr, applyMoveAttrs, BypassSleepAttr, HealStatusEffectAttr, ChargeAttr, PreMoveMessageAttr } from "#app/data/move.js"; +import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags.js"; +import { allMoves, applyMoveAttrs, BypassRedirectAttr, BypassSleepAttr, ChargeAttr, CopyMoveAttr, HealStatusEffectAttr, MoveFlags, PreMoveMessageAttr } from "#app/data/move.js"; import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms.js"; import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect.js"; import { Type } from "#app/data/type.js"; @@ -13,10 +13,10 @@ import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { Moves } from "#app/enums/moves.js"; import { StatusEffect } from "#app/enums/status-effect.js"; import { MoveUsedEvent } from "#app/events/battle-scene.js"; -import Pokemon, { PokemonMove, MoveResult, TurnMove } from "#app/field/pokemon.js"; +import Pokemon, { MoveResult, PokemonMove, TurnMove } from "#app/field/pokemon.js"; import { getPokemonNameWithAffix } from "#app/messages.js"; -import i18next from "i18next"; import * as Utils from "#app/utils.js"; +import i18next from "i18next"; import { BattlePhase } from "./battle-phase"; import { CommonAnimPhase } from "./common-anim-phase"; import { MoveEffectPhase } from "./move-effect-phase"; @@ -38,8 +38,8 @@ export class MovePhase extends BattlePhase { this.pokemon = pokemon; this.targets = targets; this.move = move; - this.followUp = !!followUp; - this.ignorePp = !!ignorePp; + this.followUp = followUp ?? false; + this.ignorePp = ignorePp ?? false; this.failed = false; this.cancelled = false; } @@ -194,7 +194,7 @@ export class MovePhase extends BattlePhase { return this.end(); } - if (!moveQueue.length || !moveQueue.shift()?.ignorePP) { // using .shift here clears out two turn moves once they've been used + if ((!moveQueue.length || !moveQueue.shift()?.ignorePP) && !this.ignorePp) { // using .shift here clears out two turn moves once they've been used this.move.usePp(ppUsed); this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); } diff --git a/src/test/abilities/dancer.test.ts b/src/test/abilities/dancer.test.ts new file mode 100644 index 00000000000..d80f497f8b2 --- /dev/null +++ b/src/test/abilities/dancer.test.ts @@ -0,0 +1,64 @@ +import { BattlerIndex } from "#app/battle"; +import { MovePhase } from "#app/phases/move-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Abilities - Dancer", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("double") + .moveset([Moves.SWORDS_DANCE, Moves.SPLASH]) + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.DANCER) + .enemyMoveset(Array(4).fill(Moves.VICTORY_DANCE)); + }); + + // Reference Link: https://bulbapedia.bulbagarden.net/wiki/Dancer_(Ability) + + it("triggers when dance moves are used, doesn't consume extra PP", async () => { + await game.classicMode.startBattle([Species.ORICORIO, Species.FEEBAS]); + + const [oricorio] = game.scene.getPlayerField(); + + game.move.select(Moves.SPLASH); + game.move.select(Moves.SWORDS_DANCE, 1); + await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2]); + await game.phaseInterceptor.to("MovePhase"); + // immediately copies ally move + await game.phaseInterceptor.to("MovePhase", false); + let currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(oricorio); + expect(currentPhase.move.moveId).toBe(Moves.SWORDS_DANCE); + await game.phaseInterceptor.to("MoveEndPhase"); + await game.phaseInterceptor.to("MovePhase"); + // immediately copies enemy move + await game.phaseInterceptor.to("MovePhase", false); + currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(oricorio); + expect(currentPhase.move.moveId).toBe(Moves.VICTORY_DANCE); + await game.phaseInterceptor.to("BerryPhase"); + + // doesn't use PP if copied move is also in moveset + expect(oricorio.moveset[0]?.ppUsed).toBe(0); + }, TIMEOUT); +}); From f3c41edf5e35266fb2ca752eac988c04ab1d881d Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:24:12 -0700 Subject: [PATCH 029/108] [Bug] Don't reset turn count or used moves array at the start of a new wave (fakeout and gigaton hammer) (#3606) * Don't reset turn count or used moves array at the start of a new wave * Add tests --- src/phases/battle-end-phase.ts | 6 -- src/test/moves/fake_out.test.ts | 83 +++++++++++++++++++++++++++ src/test/moves/gigaton_hammer.test.ts | 81 ++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 src/test/moves/fake_out.test.ts create mode 100644 src/test/moves/gigaton_hammer.test.ts diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index a9999370cdd..06315668a8b 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -23,12 +23,6 @@ export class BattleEndPhase extends BattlePhase { this.scene.unshiftPhase(new GameOverPhase(this.scene, true)); } - for (const pokemon of this.scene.getField()) { - if (pokemon) { - pokemon.resetBattleSummonData(); - } - } - for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) { applyPostBattleAbAttrs(PostBattleAbAttr, pokemon); } diff --git a/src/test/moves/fake_out.test.ts b/src/test/moves/fake_out.test.ts new file mode 100644 index 00000000000..92331ebb758 --- /dev/null +++ b/src/test/moves/fake_out.test.ts @@ -0,0 +1,83 @@ +import GameManager from "#app/test/utils/gameManager"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; + +describe("Moves - Fake Out", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.CORVIKNIGHT) + .starterSpecies(Species.FEEBAS) + .moveset([Moves.FAKE_OUT, Moves.SPLASH]) + .enemyMoveset(SPLASH_ONLY) + .disableCrits(); + }); + + it("can only be used on the first turn a pokemon is sent out", async() => { + await game.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + await game.toNextTurn(); + + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + const postTurnOneHp = enemy.hp; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + await game.toNextTurn(); + + expect(enemy.hp).toBe(postTurnOneHp); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.doKillOpponents(); + await game.toNextWave(); + + const newEnemy = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + await game.toNextTurn(); + + expect(newEnemy.hp).toBe(newEnemy.getMaxHp()); + }, 20000); + + it("can be used again if recalled and sent back out", async() => { + game.override.startingWave(4); + await game.startBattle(); + + const enemy1 = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp()); + + await game.doKillOpponents(); + await game.toNextWave(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + await game.toNextTurn(); + + const enemy2 = game.scene.getEnemyPokemon()!; + + expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); + }, 20000); +}); diff --git a/src/test/moves/gigaton_hammer.test.ts b/src/test/moves/gigaton_hammer.test.ts new file mode 100644 index 00000000000..aca95a76332 --- /dev/null +++ b/src/test/moves/gigaton_hammer.test.ts @@ -0,0 +1,81 @@ +import { BattlerIndex } from "#app/battle.js"; +import GameManager from "#app/test/utils/gameManager"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; + +describe("Moves - Gigaton Hammer", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .starterSpecies(Species.FEEBAS) + .moveset([Moves.GIGATON_HAMMER]) + .startingLevel(10) + .enemyLevel(100) + .enemyMoveset(SPLASH_ONLY) + .disableCrits(); + }); + + it("can't be used two turns in a row", async() => { + await game.startBattle(); + + const enemy1 = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp()); + + await game.doKillOpponents(); + await game.toNextWave(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + await game.toNextTurn(); + + const enemy2 = game.scene.getEnemyPokemon()!; + + expect(enemy2.hp).toBe(enemy2.getMaxHp()); + }, 20000); + + it("can be used again if recalled and sent back out", async() => { + game.override.startingWave(4); + await game.startBattle(); + + const enemy1 = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp()); + + await game.doKillOpponents(); + await game.toNextWave(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + await game.toNextTurn(); + + const enemy2 = game.scene.getEnemyPokemon()!; + + expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); + }, 20000); +}); From 2d5bd57c44292dae075bfaddbb8543b6573f2752 Mon Sep 17 00:00:00 2001 From: "gitlocalize-app[bot]" <55277160+gitlocalize-app[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:25:07 -0400 Subject: [PATCH 030/108] [Localization] [FR] pokemon-info.json and dialogue-misc.json completion (#3761) * Translate pokemon-info.json via GitLocalize * Update dialogue-misc.json --------- Co-authored-by: Lugiad --- src/locales/fr/dialogue-misc.json | 6 ++++-- src/locales/fr/pokemon-info.json | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/locales/fr/dialogue-misc.json b/src/locales/fr/dialogue-misc.json index d932d283d37..359c2dfb46b 100644 --- a/src/locales/fr/dialogue-misc.json +++ b/src/locales/fr/dialogue-misc.json @@ -1,4 +1,6 @@ { "ending": "@c{smile}Oh ? T’as gagné ?@d{96} @c{smile_eclosed}J’aurais dû le savoir.\nMais de voilà de retour.\n$@c{smile}C’est terminé.@d{64} T’as brisé ce cycle infernal.\n$@c{serious_smile_fists}T’as aussi accompli ton rêve non ?\nTu n’as pas connu la moindre défaite.\n$@c{neutral}Je suis le seul à me souvenir de ce que t’as fait.@d{96}\nJe pense que ça ira, non ?\n$@c{serious_smile_fists}Ta légende vivra à jamais dans nos cœurs.\n$@c{smile_eclosed}Bref, j’en ai un peu marre de ce endroit, pas toi ? Rentrons à la maison.\n$@c{serious_smile_fists}On se fera un p’tit combat une fois rentrés ?\nSi t’es d’accord.", - "ending_female": "@c{shock}T’es revenu ?@d{32} Ça veut dire…@d{96} que t’as gagné ?!\n@c{smile_ehalf}J’aurais dû le savoir.\n$@c{smile_eclosed}Bien sûr… J’ai toujours eu ce sentiment.\n@c{smile}C’est fini maitenant hein ? T’as brisé ce cycle.\n$@c{smile_ehalf}T’as aussi accompli ton rêve non ?\nTu n’as pas connu la moindre défaite.\n$Je serai la seule à me souvenir de ce que t’as fait.\n@c{angry_mopen}Je tâcherai de ne pas oublier !\n$@c{smile_wave_wink}J’déconne !@d{64} @c{smile}Jamais j’oublierai.@d{32}\nTa légende vivra à jamais dans nos cœurs.\n$@c{smile_wave}Bon,@d{64} il se fait tard…@d{96} je crois ?\nDifficile à dire ici.\n$Rentrons, @c{smile_wave_wink}et demain on se fera un p’tit combat, comme au bon vieux temps ?" -} \ No newline at end of file + "ending_female": "@c{shock}T’es revenu ?@d{32} Ça veut dire…@d{96} que t’as gagné ?!\n@c{smile_ehalf}J’aurais dû le savoir.\n$@c{smile_eclosed}Bien sûr… J’ai toujours eu ce sentiment.\n@c{smile}C’est fini maitenant hein ? T’as brisé ce cycle.\n$@c{smile_ehalf}T’as aussi accompli ton rêve non ?\nTu n’as pas connu la moindre défaite.\n$Je serai la seule à me souvenir de ce que t’as fait.\n@c{angry_mopen}Je tâcherai de ne pas oublier !\n$@c{smile_wave_wink}J’déconne !@d{64} @c{smile}Jamais j’oublierai.@d{32}\nTa légende vivra à jamais dans nos cœurs.\n$@c{smile_wave}Bon,@d{64} il se fait tard…@d{96} je crois ?\nDifficile à dire ici.\n$Rentrons, @c{smile_wave_wink}et demain on se fera un p’tit combat, comme au bon vieux temps ?", + "ending_endless": "Félicitations ! Vous avez atteint la fin actuelle.\nPlus de contenu à venir bientôt !", + "ending_name": "Les devs" +} diff --git a/src/locales/fr/pokemon-info.json b/src/locales/fr/pokemon-info.json index 1e55f332432..1160ec95b75 100644 --- a/src/locales/fr/pokemon-info.json +++ b/src/locales/fr/pokemon-info.json @@ -13,7 +13,8 @@ "SPD": "Vitesse", "SPDshortened": "Vit", "ACC": "Précison", - "EVA": "Esquive" + "EVA": "Esquive", + "HPStat": "PV" }, "Type": { "UNKNOWN": "Inconnu", @@ -37,4 +38,4 @@ "FAIRY": "Fée", "STELLAR": "Stellaire" } -} \ No newline at end of file +} From f54846f735e2c22f195348cb3ae9f347d7c5cfe3 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:26:20 -0700 Subject: [PATCH 031/108] [Move] Implement Safeguard (#3447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implemented safeguard and tests * Update tests * Add i18n placeholders * Implement Safeguard for non-volatile statuses * Implement protection from confusion and Yawn * Replace `target instanceof EnemyPokemon` with `target.isPlayer()` * Minor capitalization change * First batch of i18n Adds fr, pt_BR, zh_CN, zh_TW Co-authored-by: Lugiad' Co-authored-by: José Ricardo Fleury Oliveira Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> * Add more translations + de, es, ko Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Asdar Co-authored-by: sodam <66295123+sodaMelon@users.noreply.github.com> * Fix broken character in es translation * Update test with new function definition * Add Italian translation Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> * Add move category check for message display Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * Update phase imports in Safeguard test * Fix test imports * Update tests --------- Co-authored-by: Joshua Keegan Co-authored-by: Lugiad' Co-authored-by: José Ricardo Fleury Oliveira Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Asdar Co-authored-by: sodam <66295123+sodaMelon@users.noreply.github.com> Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> --- src/data/ability.ts | 2 +- src/data/arena-tag.ts | 17 ++++ src/data/move.ts | 22 +++- src/enums/arena-tag-type.ts | 1 + src/field/pokemon.ts | 5 + src/locales/de/arena-tag.json | 8 +- src/locales/de/move-trigger.json | 3 +- src/locales/es/arena-tag.json | 9 +- src/locales/es/move-trigger.json | 3 +- src/locales/fr/arena-tag.json | 8 +- src/locales/fr/move-trigger.json | 3 +- src/locales/it/arena-tag.json | 9 +- src/locales/it/move-trigger.json | 3 +- src/locales/ko/arena-tag.json | 8 +- src/locales/ko/move-trigger.json | 3 +- src/locales/pt_BR/arena-tag.json | 8 +- src/locales/pt_BR/move-trigger.json | 3 +- src/locales/zh_CN/arena-tag.json | 8 +- src/locales/zh_CN/move-trigger.json | 3 +- src/locales/zh_TW/arena-tag.json | 8 +- src/locales/zh_TW/move-trigger.json | 3 +- src/test/moves/safeguard.test.ts | 150 ++++++++++++++++++++++++++++ 22 files changed, 268 insertions(+), 19 deletions(-) create mode 100644 src/test/moves/safeguard.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index 818ab07637f..40312eaa8be 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -850,7 +850,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { } export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { - private chance: integer; + public chance: integer; private effects: StatusEffect[]; constructor(chance: integer, ...effects: StatusEffect[]) { diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index a60ea5c2981..09cc7a5b97c 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -905,6 +905,21 @@ class HappyHourTag extends ArenaTag { } } +class SafeguardTag extends ArenaTag { + constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side); + } + + onAdd(arena: Arena): void { + arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); + } + + onRemove(arena: Arena): void { + arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); + } +} + + export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { case ArenaTagType.MIST: @@ -950,6 +965,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new TailwindTag(turnCount, sourceId, side); case ArenaTagType.HAPPY_HOUR: return new HappyHourTag(turnCount, sourceId, side); + case ArenaTagType.SAFEGUARD: + return new SafeguardTag(turnCount, sourceId, side); default: return null; } diff --git a/src/data/move.ts b/src/data/move.ts index 3f87fc68b89..1dc715f264a 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1953,6 +1953,13 @@ export class StatusEffectAttr extends MoveEffectAttr { return false; } } + + if (user !== target && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) { + if (move.category === MoveCategory.STATUS) { + user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)})); + } + return false; + } if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect); @@ -4659,6 +4666,17 @@ export class ConfuseAttr extends AddBattlerTagAttr { constructor(selfTarget?: boolean) { super(BattlerTagType.CONFUSED, selfTarget, false, 2, 5); } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!this.selfTarget && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) { + if (move.category === MoveCategory.STATUS) { + user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)})); + } + return false; + } + + return super.apply(user, target, move, args); + } } export class RechargeAttr extends AddBattlerTagAttr { @@ -7014,7 +7032,7 @@ export function initMoves() { .attr(FriendshipPowerAttr, true), new StatusMove(Moves.SAFEGUARD, Type.NORMAL, -1, 25, -1, 0, 2) .target(MoveTarget.USER_SIDE) - .unimplemented(), + .attr(AddArenaTagAttr, ArenaTagType.SAFEGUARD, 5, true, true), new StatusMove(Moves.PAIN_SPLIT, Type.NORMAL, -1, 20, -1, 0, 2) .attr(HpSplitAttr) .condition(failOnBossCondition), @@ -7203,7 +7221,7 @@ export function initMoves() { .attr(RemoveScreensAttr), new StatusMove(Moves.YAWN, Type.NORMAL, -1, 10, -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) - .condition((user, target, move) => !target.status), + .condition((user, target, move) => !target.status && !target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)), new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3) .attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferrable).length > 0 ? 1.5 : 1) .attr(RemoveHeldItemAttr, false), diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index 1265b815bf4..1c79750c91a 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -22,5 +22,6 @@ export enum ArenaTagType { CRAFTY_SHIELD = "CRAFTY_SHIELD", TAILWIND = "TAILWIND", HAPPY_HOUR = "HAPPY_HOUR", + SAFEGUARD = "SAFEGUARD", NO_CRIT = "NO_CRIT" } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 16ad96f61cc..c970c99e7d3 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2787,6 +2787,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const types = this.getTypes(true, true); + const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + if (sourcePokemon && sourcePokemon !== this && this.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) { + return false; + } + switch (effect) { case StatusEffect.POISON: case StatusEffect.TOXIC: diff --git a/src/locales/de/arena-tag.json b/src/locales/de/arena-tag.json index 454effae60c..3bed4fefbd0 100644 --- a/src/locales/de/arena-tag.json +++ b/src/locales/de/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "Der Rückenwind auf deiner Seite hat sich gelegt!", "tailwindOnRemoveEnemy": "Der Rückenwind auf gegnerischer Seite hat sich gelegt!", "happyHourOnAdd": "Goldene Zeiten sind angebrochen!", - "happyHourOnRemove": "Die goldenen Zeiten sind vorbei!" + "happyHourOnRemove": "Die goldenen Zeiten sind vorbei!", + "safeguardOnAdd": "Das ganze Feld wird von einem Schleier umhüllt!", + "safeguardOnAddPlayer": "Das Team des Anwenders wird von einem Schleier umhüllt!", + "safeguardOnAddEnemy": "Das gegnerische Team wird von einem Schleier umhüllt!", + "safeguardOnRemove": "Der mystische Schleier, der das ganze Feld umgab, hat sich gelüftet!", + "safeguardOnRemovePlayer": "Der mystische Schleier, der dein Team umgab, hat sich gelüftet!", + "safeguardOnRemoveEnemy": "Der mystische Schleier, der das gegnerische Team umgab, hat sich gelüftet!" } \ No newline at end of file diff --git a/src/locales/de/move-trigger.json b/src/locales/de/move-trigger.json index 5b2b2471df9..163e8014d8b 100644 --- a/src/locales/de/move-trigger.json +++ b/src/locales/de/move-trigger.json @@ -61,5 +61,6 @@ "suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!", "revivalBlessing": "{{pokemonName}} ist wieder fit und kampfbereit!", "swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!", - "exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!" + "exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!", + "safeguard": "{{targetName}} wird durch Bodyguard geschützt!" } \ No newline at end of file diff --git a/src/locales/es/arena-tag.json b/src/locales/es/arena-tag.json index 9e26dfeeb6e..913876ddf87 100644 --- a/src/locales/es/arena-tag.json +++ b/src/locales/es/arena-tag.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "safeguardOnAdd": "¡Todos los Pokémon están protegidos por Velo Sagrado!", + "safeguardOnAddPlayer": "¡Tu equipo se ha protegido con Velo Sagrado!", + "safeguardOnAddEnemy": "¡El equipo enemigo se ha protegido con Velo Sagrado!", + "safeguardOnRemove": "¡Velo Sagrado dejó de hacer efecto!", + "safeguardOnRemovePlayer": "El efecto de Velo Sagrado en tu equipo se ha disipado.", + "safeguardOnRemoveEnemy": "El efecto de Velo Sagrado en el equipo enemigo se ha disipado." +} \ No newline at end of file diff --git a/src/locales/es/move-trigger.json b/src/locales/es/move-trigger.json index b570f029377..52a6f86d930 100644 --- a/src/locales/es/move-trigger.json +++ b/src/locales/es/move-trigger.json @@ -7,5 +7,6 @@ "usedUpAllElectricity": "¡{{pokemonName}} ha descargado toda su electricidad!", "stoleItem": "¡{{pokemonName}} robó el objeto\n{{itemName}} de {{targetName}}!", "statEliminated": "¡Los cambios en estadísticas fueron eliminados!", - "revivalBlessing": "¡{{pokemonName}} ha revivido!" + "revivalBlessing": "¡{{pokemonName}} ha revivido!", + "safeguard": "¡{{targetName}} está protegido por Velo Sagrado!" } \ No newline at end of file diff --git a/src/locales/fr/arena-tag.json b/src/locales/fr/arena-tag.json index 16355816ae4..c3c705290fa 100644 --- a/src/locales/fr/arena-tag.json +++ b/src/locales/fr/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "Le vent arrière soufflant\nsur votre équipe s’arrête !", "tailwindOnRemoveEnemy": "Le vent arrière soufflant\nsur l’équipe ennemie s’arrête !", "happyHourOnAdd": "L’ambiance est euphorique !", - "happyHourOnRemove": "L’ambiance se calme !" + "happyHourOnRemove": "L’ambiance se calme !", + "safeguardOnAdd": "Un voile mystérieux recouvre\ntout le terrain !", + "safeguardOnAddPlayer": "Un voile mystérieux recouvre\nvotre équipe !", + "safeguardOnAddEnemy": "Un voile mystérieux recouvre\nl’équipe ennemie !", + "safeguardOnRemove": "Le terrain n’est plus protégé\npar le voile mystérieux !", + "safeguardOnRemovePlayer": "Votre équipe n’est plus protégée\npar le voile mystérieux !", + "safeguardOnRemoveEnemy": "L’équipe ennemie n’est plus protégée\npar le voile mystérieux !" } \ No newline at end of file diff --git a/src/locales/fr/move-trigger.json b/src/locales/fr/move-trigger.json index 43cf09d5bf6..5c814745a8e 100644 --- a/src/locales/fr/move-trigger.json +++ b/src/locales/fr/move-trigger.json @@ -61,5 +61,6 @@ "suppressAbilities": "Le talent de {{pokemonName}}\na été rendu inactif !", "revivalBlessing": "{{pokemonName}} a repris connaissance\net est prêt à se battre de nouveau !", "swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !", - "exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !" + "exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !", + "safeguard": "{{targetName}} est protégé\npar la capacité Rune Protect !" } \ No newline at end of file diff --git a/src/locales/it/arena-tag.json b/src/locales/it/arena-tag.json index 9e26dfeeb6e..a1c5ee5b3c9 100644 --- a/src/locales/it/arena-tag.json +++ b/src/locales/it/arena-tag.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "safeguardOnAdd": "Un velo mistico ricopre il campo!", + "safeguardOnAddPlayer": "Un velo mistico ricopre la tua squadra!", + "safeguardOnAddEnemy": "Un velo mistico ricopre la squadra avversaria!", + "safeguardOnRemove": "Il campo non è più protetto da Salvaguardia!", + "safeguardOnRemovePlayer": "La tua squadra non è più protetta da Salvaguardia!", + "safeguardOnRemoveEnemy": "La squadra avversaria non è più protetta da Salvaguardia!" +} \ No newline at end of file diff --git a/src/locales/it/move-trigger.json b/src/locales/it/move-trigger.json index e852c2fb52a..58b7b1a4c5b 100644 --- a/src/locales/it/move-trigger.json +++ b/src/locales/it/move-trigger.json @@ -61,5 +61,6 @@ "suppressAbilities": "L’abilità di {{pokemonName}}\nperde ogni efficacia!", "revivalBlessing": "{{pokemonName}} torna in forze!", "swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!", - "exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!" + "exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!", + "safeguard": "Salvaguardia protegge {{targetName}}!" } \ No newline at end of file diff --git a/src/locales/ko/arena-tag.json b/src/locales/ko/arena-tag.json index 61586508a94..ce9922ab3bf 100644 --- a/src/locales/ko/arena-tag.json +++ b/src/locales/ko/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "우리 편의\n순풍이 멈췄다!", "tailwindOnRemoveEnemy": "상대의\n순풍이 멈췄다!", "happyHourOnAdd": "모두 행복한 기분에\n휩싸였다!", - "happyHourOnRemove": "기분이 원래대로 돌아왔다." + "happyHourOnRemove": "기분이 원래대로 돌아왔다.", + "safeguardOnAdd": "필드 전체가 신비의 베일에 둘러싸였다!", + "safeguardOnAddPlayer": "우리 편은 신비의 베일에 둘러싸였다!", + "safeguardOnAddEnemy": "상대 편은 신비의 베일에 둘러싸였다!", + "safeguardOnRemove": "필드를 감싸던 신비의 베일이 없어졌다!", + "safeguardOnRemovePlayer": "우리 편을 감싸던 신비의 베일이 없어졌다!", + "safeguardOnRemoveEnemy": "상대 편을 감싸던 신비의 베일이 없어졌다!" } \ No newline at end of file diff --git a/src/locales/ko/move-trigger.json b/src/locales/ko/move-trigger.json index 61dffa122a3..f0e0fbd6a56 100644 --- a/src/locales/ko/move-trigger.json +++ b/src/locales/ko/move-trigger.json @@ -61,5 +61,6 @@ "suppressAbilities": "{{pokemonName}}의\n특성이 효과를 발휘하지 못하게 되었다!", "revivalBlessing": "{{pokemonName}}[[는]]\n정신을 차려 싸울 수 있게 되었다!", "swapArenaTags": "{{pokemonName}}[[는]]\n서로의 필드 효과를 교체했다!", - "exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!" + "exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!", + "safeguard": "{{targetName}}[[는]] 신비의 베일이 지켜 주고 있다!" } \ No newline at end of file diff --git a/src/locales/pt_BR/arena-tag.json b/src/locales/pt_BR/arena-tag.json index 20ef208c8fc..7ab1ecea721 100644 --- a/src/locales/pt_BR/arena-tag.json +++ b/src/locales/pt_BR/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "O Tailwind de sua equipe acabou!", "tailwindOnRemoveEnemy": "O Tailwind da equipe adversária acabou!", "happyHourOnAdd": "Todos foram envolvidos por uma atmosfera alegre!", - "happyHourOnRemove": "A atmosfera retornou ao normal." + "happyHourOnRemove": "A atmosfera retornou ao normal.", + "safeguardOnAdd": "O campo de batalha está envolto num véu místico!", + "safeguardOnAddPlayer": "Sua equipe se envolveu num véu místico!", + "safeguardOnAddEnemy": "A equipe adversária se envolveu num véu místico!", + "safeguardOnRemove": "O campo não está mais protegido por Safeguard!", + "safeguardOnRemovePlayer": "Sua equipe não está mais protegido por Safeguard!", + "safeguardOnRemoveEnemy": "A equipe adversária não está mais protegido por Safeguard!" } \ No newline at end of file diff --git a/src/locales/pt_BR/move-trigger.json b/src/locales/pt_BR/move-trigger.json index 416740dba0d..ea320412a24 100644 --- a/src/locales/pt_BR/move-trigger.json +++ b/src/locales/pt_BR/move-trigger.json @@ -61,5 +61,6 @@ "suppressAbilities": "A habilidade de {{pokemonName}}\nfoi suprimida!", "revivalBlessing": "{{pokemonName}} foi reanimado!", "swapArenaTags": "{{pokemonName}} trocou os efeitos de batalha que afetam cada lado do campo!", - "exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!" + "exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!", + "safeguard": "{{targetName}} está protegido por Safeguard!" } \ No newline at end of file diff --git a/src/locales/zh_CN/arena-tag.json b/src/locales/zh_CN/arena-tag.json index 5a36b3ae1f7..74ad38ba9bf 100644 --- a/src/locales/zh_CN/arena-tag.json +++ b/src/locales/zh_CN/arena-tag.json @@ -47,5 +47,11 @@ "tailwindOnRemovePlayer": "我方的顺风停止了!", "tailwindOnRemoveEnemy": "敌方的顺风停止了!", "happyHourOnAdd": "大家被欢乐的\n气氛包围了!", - "happyHourOnRemove": "气氛回复到平常了。" + "happyHourOnRemove": "气氛回复到平常了。", + "safeguardOnAdd": "整个场地被\n神秘之幕包围了!", + "safeguardOnAddPlayer": "我方被\n神秘之幕包围了!", + "safeguardOnAddEnemy": "对手被\n神秘之幕包围了!", + "safeguardOnRemove": "包围整个场地的\n神秘之幕消失了!", + "safeguardOnRemovePlayer": "包围我方的\n神秘之幕消失了!", + "safeguardOnRemoveEnemy": "包围对手的\n神秘之幕消失了!" } \ No newline at end of file diff --git a/src/locales/zh_CN/move-trigger.json b/src/locales/zh_CN/move-trigger.json index 5a76f402783..44705d54e76 100644 --- a/src/locales/zh_CN/move-trigger.json +++ b/src/locales/zh_CN/move-trigger.json @@ -61,5 +61,6 @@ "suppressAbilities": "{{pokemonName}}的特性\n变得无效了!", "revivalBlessing": "{{pokemonName}}复活了!", "swapArenaTags": "{{pokemonName}}\n交换了双方的场地效果!", - "exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!" + "exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!", + "safeguard": "{{targetName}}\n正受到神秘之幕的保护!" } \ No newline at end of file diff --git a/src/locales/zh_TW/arena-tag.json b/src/locales/zh_TW/arena-tag.json index b60946a3b77..78246d9c44f 100644 --- a/src/locales/zh_TW/arena-tag.json +++ b/src/locales/zh_TW/arena-tag.json @@ -1,5 +1,11 @@ { "noCritOnAddPlayer": "{{moveName}}保護了你的\n隊伍不被擊中要害!", "noCritOnAddEnemy": "{{moveName}}保護了對方的\n隊伍不被擊中要害!", - "noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!" + "noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!", + "safeguardOnAdd": "整個場地被\n神秘之幕包圍了!", + "safeguardOnAddPlayer": "我方被\n神秘之幕包圍了!", + "safeguardOnAddEnemy": "對手被\n神秘之幕包圍了!", + "safeguardOnRemove": "包圍整個場地的\n神秘之幕消失了!", + "safeguardOnRemovePlayer": "包圍我方的\n神秘之幕消失了!", + "safeguardOnRemoveEnemy": "包圍對手的\n神秘之幕消失了!" } \ No newline at end of file diff --git a/src/locales/zh_TW/move-trigger.json b/src/locales/zh_TW/move-trigger.json index 03ca6841a7f..60dcc1eab7a 100644 --- a/src/locales/zh_TW/move-trigger.json +++ b/src/locales/zh_TW/move-trigger.json @@ -61,5 +61,6 @@ "suppressAbilities": "{{pokemonName}}的特性\n變得無效了!", "revivalBlessing": "{{pokemonName}}復活了!", "swapArenaTags": "{{pokemonName}}\n交換了雙方的場地效果!", - "exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!" + "exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!", + "safeguard": "{{targetName}}\n正受到神秘之幕的保護!" } \ No newline at end of file diff --git a/src/test/moves/safeguard.test.ts b/src/test/moves/safeguard.test.ts new file mode 100644 index 00000000000..94a7aa6031e --- /dev/null +++ b/src/test/moves/safeguard.test.ts @@ -0,0 +1,150 @@ +import { BattlerIndex } from "#app/battle"; +import { allAbilities, PostDefendContactApplyStatusEffectAbAttr } from "#app/data/ability"; +import { Abilities } from "#app/enums/abilities"; +import { StatusEffect } from "#app/enums/status-effect"; +import GameManager from "#app/test/utils/gameManager"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Safeguard", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.DRATINI) + .enemyMoveset(Array(4).fill(Moves.SAFEGUARD)) + .enemyAbility(Abilities.BALL_FETCH) + .enemyLevel(5) + .starterSpecies(Species.DRATINI) + .moveset([Moves.NUZZLE, Moves.SPORE, Moves.YAWN, Moves.SPLASH]) + .ability(Abilities.BALL_FETCH); + }); + + it("protects from damaging moves with additional effects", async () => { + await game.startBattle(); + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.NUZZLE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemy.status).toBeUndefined(); + }, TIMEOUT); + + it("protects from status moves", async () => { + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPORE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyPokemon.status).toBeUndefined(); + }, TIMEOUT); + + it("protects from confusion", async () => { + game.override.moveset([Moves.CONFUSE_RAY]); + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.CONFUSE_RAY); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyPokemon.summonData.tags).toEqual([]); + }, TIMEOUT); + + it("protects ally from status", async () => { + game.override.battleType("double"); + + await game.startBattle(); + + game.move.select(Moves.SPORE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.NUZZLE, 1, BattlerIndex.ENEMY_2); + + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2]); + + await game.phaseInterceptor.to("BerryPhase"); + + const enemyPokemon = game.scene.getEnemyField(); + + expect(enemyPokemon[0].status).toBeUndefined(); + expect(enemyPokemon[1].status).toBeUndefined(); + }, TIMEOUT); + + it("protects from Yawn", async () => { + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.YAWN); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyPokemon.summonData.tags).toEqual([]); + }, TIMEOUT); + + it("doesn't protect from already existing Yawn", async () => { + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.YAWN); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextTurn(); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(enemyPokemon.status?.effect).toEqual(StatusEffect.SLEEP); + }, TIMEOUT); + + it("doesn't protect from self-inflicted via Rest or Flame Orb", async () => { + game.override.enemyHeldItems([{name: "FLAME_ORB"}]); + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + + expect(enemyPokemon.status?.effect).toEqual(StatusEffect.BURN); + + game.override.enemyMoveset(Array(4).fill(Moves.REST)); + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(enemyPokemon.status?.effect).toEqual(StatusEffect.SLEEP); + }, TIMEOUT); + + it("protects from ability-inflicted status", async () => { + game.override.ability(Abilities.STATIC); + vi.spyOn(allAbilities[Abilities.STATIC].getAttrs(PostDefendContactApplyStatusEffectAbAttr)[0], "chance", "get").mockReturnValue(100); + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.toNextTurn(); + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(enemyPokemon.status).toBeUndefined(); + }, TIMEOUT); +}); From 1e432fc74b958e4629c4b33de85ee103444069ed Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:26:35 -0700 Subject: [PATCH 032/108] [Bug] Fixed pre-set volume oversight (#3963) Co-authored-by: frutescens --- src/battle-scene.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 2a920864850..e761d8fca39 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1791,6 +1791,7 @@ export default class BattleScene extends SceneBase { config = config ?? {}; try { const keyDetails = key.split("/"); + config["volume"] = config["volume"] ?? 1; switch (keyDetails[0]) { case "level_up_fanfare": case "item_fanfare": @@ -1800,11 +1801,11 @@ export default class BattleScene extends SceneBase { case "evolution_fanfare": // These sounds are loaded in as BGM, but played as sound effects // When these sounds are updated in updateVolume(), they are treated as BGM however because they are placed in the BGM Cache through being called by playSoundWithoutBGM() - config["volume"] = this.masterVolume * this.bgmVolume; + config["volume"] *= (this.masterVolume * this.bgmVolume); break; case "battle_anims": case "cry": - config["volume"] = this.masterVolume * this.fieldVolume; + config["volume"] *= (this.masterVolume * this.fieldVolume); //PRSFX sound files are unusually loud if (keyDetails[1].startsWith("PRSFX- ")) { config["volume"] *= 0.5; @@ -1812,10 +1813,10 @@ export default class BattleScene extends SceneBase { break; case "ui": //As of, right now this applies to the "select", "menu_open", "error" sound effects - config["volume"] = this.masterVolume * this.uiVolume; + config["volume"] *= (this.masterVolume * this.uiVolume); break; case "se": - config["volume"] = this.masterVolume * this.seVolume; + config["volume"] *= (this.masterVolume * this.seVolume); break; } this.sound.play(key, config); From 84ef7f0683737eb8f35505420acb3071c4593ca5 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:26:47 -0700 Subject: [PATCH 033/108] [Balance] Double base shiny odds, adjusted Shiny Charm to match (#3964) * Double shiny odds "anyone wanna double the base shiny odds for me" - damo, 2024 * Adjust Shiny Charm to compensate for increased base odds * Remove magic number * Update tsdoc and remove unneeded `console.log()` * Clarify tsdoc --- src/field/pokemon.ts | 18 +++++++++--------- src/modifier/modifier.ts | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index c970c99e7d3..8594d5b769b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1527,13 +1527,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Function that tries to set a Pokemon shiny based on the trainer's trainer ID and secret ID + * Function that tries to set a Pokemon shiny based on the trainer's trainer ID and secret ID. * Endless Pokemon in the end biome are unable to be set to shiny * - * The exact mechanic is that it calculates E as the XOR of the player's trainer ID and secret ID - * F is calculated as the XOR of the first 16 bits of the Pokemon's ID with the last 16 bits - * The XOR of E and F are then compared to the thresholdOverride (default case 32) to see whether or not to generate a shiny - * @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance + * The exact mechanic is that it calculates E as the XOR of the player's trainer ID and secret ID. + * F is calculated as the XOR of the first 16 bits of the Pokemon's ID with the last 16 bits. + * The XOR of E and F are then compared to the {@linkcode shinyThreshold} (or {@linkcode thresholdOverride} if set) to see whether or not to generate a shiny. + * The base shiny odds are {@linkcode baseShinyChance} / 65536 + * @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) * @returns true if the Pokemon has been set as a shiny, false otherwise */ trySetShiny(thresholdOverride?: integer): boolean { @@ -1548,7 +1549,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const E = this.scene.gameData.trainerId ^ this.scene.gameData.secretId; const F = rand1 ^ rand2; - const shinyThreshold = new Utils.IntegerHolder(32); + /** `64/65536 -> 1/1024` */ + const baseShinyChance = 64; + const shinyThreshold = new Utils.IntegerHolder(baseShinyChance); if (thresholdOverride === undefined) { if (this.scene.eventManager.isEventActive()) { shinyThreshold.value *= this.scene.eventManager.getShinyMultiplier(); @@ -1561,9 +1564,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } this.shiny = (E ^ F) < shinyThreshold.value; - if ((E ^ F) < 32) { - console.log("REAL SHINY!!"); - } if (this.shiny) { this.initShinySparkle(); diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 99f4540f493..ca0b18ce18b 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -12,7 +12,7 @@ import { getPokemonNameWithAffix } from "../messages"; import * as Utils from "../utils"; import { TempBattleStat } from "../data/temp-battle-stat"; import { getBerryEffectFunc, getBerryPredicate } from "../data/berry"; -import { BattlerTagType} from "#enums/battler-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import { StatusEffect, getStatusEffectHealText } from "../data/status-effect"; import { achvs } from "../system/achv"; @@ -2193,7 +2193,7 @@ export class ShinyRateBoosterModifier extends PersistentModifier { } apply(args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value *= Math.pow(2, 2 + this.getStackCount()); + (args[0] as Utils.IntegerHolder).value *= Math.pow(2, 1 + this.getStackCount()); return true; } From 0cbdaab28e6902e91bbc5019e87c1f930d87291b Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:32:22 -0700 Subject: [PATCH 034/108] [UI][Misc] Force users to have an active challenge (#3953) * I hope this is good enough * renamed variable to better name * Remove random newline * When player is ready cool box * Fixed cancel behavior * standardized action/cancel behavior * Added comments --------- Co-authored-by: frutescens Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/locales/en/challenges.json | 1 + src/ui/challenges-select-ui-handler.ts | 181 ++++++++++++++----------- 2 files changed, 101 insertions(+), 81 deletions(-) diff --git a/src/locales/en/challenges.json b/src/locales/en/challenges.json index f189266cea2..7d330401407 100644 --- a/src/locales/en/challenges.json +++ b/src/locales/en/challenges.json @@ -1,6 +1,7 @@ { "title": "Challenge Modifiers", "illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", + "noneSelected": "None Selected", "singleGeneration": { "name": "Mono Gen", "desc": "You can only use Pokémon from Generation {{gen}}.", diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index f1ba0da6c51..e08736d2b70 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -5,13 +5,13 @@ import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import {Button} from "#enums/buttons"; import i18next from "i18next"; -import { Challenge } from "#app/data/challenge.js"; +import { Challenge } from "#app/data/challenge"; import * as Utils from "../utils"; -import { Challenges } from "#app/enums/challenges.js"; +import { Challenges } from "#app/enums/challenges"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { Color, ShadowColor } from "#app/enums/color.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; +import { Color, ShadowColor } from "#app/enums/color"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { TitlePhase } from "#app/phases/title-phase"; /** * Handles all the UI for choosing optional challenges. @@ -33,7 +33,10 @@ export default class GameChallengesUiHandler extends UiHandler { private cursorObj: Phaser.GameObjects.NineSlice | null; + private startBg: Phaser.GameObjects.NineSlice; private startCursor: Phaser.GameObjects.NineSlice; + private startText: Phaser.GameObjects.Text; + private hasSelectedChallenge: boolean; private optionsWidth: number; @@ -104,20 +107,20 @@ export default class GameChallengesUiHandler extends UiHandler { this.descriptionText.setShadow(4, 5, ShadowColor.ORANGE); this.descriptionText.setOrigin(0, 0); - const startBg = addWindow(this.scene, 0, 0, descriptionBg.width, 24); - startBg.setName("window-start-bg"); - startBg.setOrigin(0, 0); - startBg.setPositionRelative(descriptionBg, 0, descriptionBg.height); + this.startBg = addWindow(this.scene, 0, 0, descriptionBg.width, 24); + this.startBg.setName("window-start-bg"); + this.startBg.setOrigin(0, 0); + this.startBg.setPositionRelative(descriptionBg, 0, descriptionBg.height); - const startText = addTextObject(this.scene, 0, 0, i18next.t("common:start"), TextStyle.SETTINGS_LABEL); - startText.setName("text-start"); - startText.setOrigin(0, 0); - startText.setPositionRelative(startBg, (startBg.width - startText.displayWidth) / 2, 4); + this.startText = addTextObject(this.scene, 0, 0, i18next.t("challenges:noneSelected"), TextStyle.SETTINGS_LABEL); + this.startText.setName("text-start"); + this.startText.setOrigin(0, 0); + this.startText.setPositionRelative(this.startBg, (this.startBg.width - this.startText.displayWidth) / 2, 4); this.startCursor = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, descriptionBg.width - 8, 16, 1, 1, 1, 1); this.startCursor.setName("9s-start-cursor"); this.startCursor.setOrigin(0, 0); - this.startCursor.setPositionRelative(startBg, 4, 3); + this.startCursor.setPositionRelative(this.startBg, 4, 3); this.startCursor.setVisible(false); this.valuesContainer = this.scene.add.container(0, 0); @@ -157,8 +160,8 @@ export default class GameChallengesUiHandler extends UiHandler { this.challengesContainer.add(this.optionsBg); this.challengesContainer.add(descriptionBg); this.challengesContainer.add(this.descriptionText); - this.challengesContainer.add(startBg); - this.challengesContainer.add(startText); + this.challengesContainer.add(this.startBg); + this.challengesContainer.add(this.startText); this.challengesContainer.add(this.startCursor); this.challengesContainer.add(this.valuesContainer); @@ -216,6 +219,21 @@ export default class GameChallengesUiHandler extends UiHandler { this.monoTypeValue.setVisible(false); } + // This checks if a challenge has been selected by the user and updates the text/its opacity accordingly. + this.hasSelectedChallenge = this.scene.gameMode.challenges.some(c => c.value !== 0); + if (this.hasSelectedChallenge) { + + this.startText.setText(i18next.t("common:start")); + this.startText.setAlpha(1); + this.startText.setPositionRelative(this.startBg, (this.startBg.width - this.startText.displayWidth) / 2, 4); + } else { + + this.startText.setText(i18next.t("challenges:noneSelected")); + this.startText.setAlpha(0.5); + this.startText.setPositionRelative(this.startBg, (this.startBg.width - this.startText.displayWidth) / 2, 4); + } + this.challengesContainer.update(); + // const totalDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getDifficulty(), 0); // const totalMinDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getMinDifficulty(), 0); // this.difficultyText.text = `${totalDifficulty}` + (totalMinDifficulty ? `/${totalMinDifficulty}` : ""); @@ -227,6 +245,8 @@ export default class GameChallengesUiHandler extends UiHandler { this.startCursor.setVisible(false); this.challengesContainer.setVisible(true); + // Should always be false at the start + this.hasSelectedChallenge = this.scene.gameMode.challenges.some(c => c.value !== 0); this.setCursor(0); this.initLabels(); @@ -257,6 +277,7 @@ export default class GameChallengesUiHandler extends UiHandler { if (button === Button.CANCEL) { if (this.startCursor.visible) { + // If the user presses cancel when the start cursor has been activated, the game deactivates the start cursor and allows typical challenge selection behavior this.startCursor.setVisible(false); this.cursorObj?.setVisible(true); } else { @@ -266,83 +287,82 @@ export default class GameChallengesUiHandler extends UiHandler { } success = true; } else if (button === Button.SUBMIT || button === Button.ACTION) { - if (this.startCursor.visible) { - const totalDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getDifficulty(), 0); - const totalMinDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getMinDifficulty(), 0); - if (totalDifficulty >= totalMinDifficulty) { + if (this.hasSelectedChallenge) { + if (this.startCursor.visible) { this.scene.unshiftPhase(new SelectStarterPhase(this.scene)); this.scene.getCurrentPhase()?.end(); - success = true; } else { - success = false; + this.startCursor.setVisible(true); + this.cursorObj?.setVisible(false); } - } else { - this.startCursor.setVisible(true); - this.cursorObj?.setVisible(false); success = true; + } else { + success = false; } } else { - switch (button) { - case Button.UP: - if (this.cursor === 0) { - if (this.scrollCursor === 0) { - // When at the top of the menu and pressing UP, move to the bottommost item. - if (this.scene.gameMode.challenges.length > rowsToDisplay) { // If there are more than 9 challenges, scroll to the bottom - // First, set the cursor to the last visible element, preparing for the scroll to the end. - const successA = this.setCursor(rowsToDisplay - 1); - // Then, adjust the scroll to display the bottommost elements of the menu. - const successB = this.setScrollCursor(this.scene.gameMode.challenges.length - rowsToDisplay); - success = successA && successB; // success is just there to play the little validation sound effect - } else { // If there are 9 or less challenges, just move to the bottom one - success = this.setCursor(this.scene.gameMode.challenges.length - 1); + if (this.cursorObj?.visible && !this.startCursor.visible) { + switch (button) { + case Button.UP: + if (this.cursor === 0) { + if (this.scrollCursor === 0) { + // When at the top of the menu and pressing UP, move to the bottommost item. + if (this.scene.gameMode.challenges.length > rowsToDisplay) { // If there are more than 9 challenges, scroll to the bottom + // First, set the cursor to the last visible element, preparing for the scroll to the end. + const successA = this.setCursor(rowsToDisplay - 1); + // Then, adjust the scroll to display the bottommost elements of the menu. + const successB = this.setScrollCursor(this.scene.gameMode.challenges.length - rowsToDisplay); + success = successA && successB; // success is just there to play the little validation sound effect + } else { // If there are 9 or less challenges, just move to the bottom one + success = this.setCursor(this.scene.gameMode.challenges.length - 1); + } + } else { + success = this.setScrollCursor(this.scrollCursor - 1); } } else { - success = this.setScrollCursor(this.scrollCursor - 1); + success = this.setCursor(this.cursor - 1); } - } else { - success = this.setCursor(this.cursor - 1); - } - if (success) { - this.updateText(); - } - break; - case Button.DOWN: - if (this.cursor === rowsToDisplay - 1) { - if (this.scrollCursor < this.scene.gameMode.challenges.length - rowsToDisplay) { - // When at the bottom and pressing DOWN, scroll if possible. - success = this.setScrollCursor(this.scrollCursor + 1); + if (success) { + this.updateText(); + } + break; + case Button.DOWN: + if (this.cursor === rowsToDisplay - 1) { + if (this.scrollCursor < this.scene.gameMode.challenges.length - rowsToDisplay) { + // When at the bottom and pressing DOWN, scroll if possible. + success = this.setScrollCursor(this.scrollCursor + 1); + } else { + // When at the bottom of a scrolling menu and pressing DOWN, move to the topmost item. + // First, set the cursor to the first visible element, preparing for the scroll to the top. + const successA = this.setCursor(0); + // Then, adjust the scroll to display the topmost elements of the menu. + const successB = this.setScrollCursor(0); + success = successA && successB; // success is just there to play the little validation sound effect + } + } else if (this.scene.gameMode.challenges.length < rowsToDisplay && this.cursor === this.scene.gameMode.challenges.length - 1) { + // When at the bottom of a non-scrolling menu and pressing DOWN, move to the topmost item. + success = this.setCursor(0); } else { - // When at the bottom of a scrolling menu and pressing DOWN, move to the topmost item. - // First, set the cursor to the first visible element, preparing for the scroll to the top. - const successA = this.setCursor(0); - // Then, adjust the scroll to display the topmost elements of the menu. - const successB = this.setScrollCursor(0); - success = successA && successB; // success is just there to play the little validation sound effect + success = this.setCursor(this.cursor + 1); } - } else if (this.scene.gameMode.challenges.length < rowsToDisplay && this.cursor === this.scene.gameMode.challenges.length - 1) { - // When at the bottom of a non-scrolling menu and pressing DOWN, move to the topmost item. - success = this.setCursor(0); - } else { - success = this.setCursor(this.cursor + 1); + if (success) { + this.updateText(); + } + break; + case Button.LEFT: + // Moves the option cursor left, if possible. + success = this.getActiveChallenge().decreaseValue(); + if (success) { + this.updateText(); + } + break; + case Button.RIGHT: + // Moves the option cursor right, if possible. + success = this.getActiveChallenge().increaseValue(); + if (success) { + this.updateText(); + } + break; } - if (success) { - this.updateText(); - } - break; - case Button.LEFT: - // Moves the option cursor left, if possible. - success = this.getActiveChallenge().decreaseValue(); - if (success) { - this.updateText(); - } - break; - case Button.RIGHT: - // Moves the option cursor right, if possible. - success = this.getActiveChallenge().increaseValue(); - if (success) { - this.updateText(); - } - break; } } @@ -350,7 +370,6 @@ export default class GameChallengesUiHandler extends UiHandler { if (success) { ui.playSelect(); } - return success; } From 744c8f884555d51068defa99a2eb02b054ce757c Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:06:20 -0700 Subject: [PATCH 035/108] [Test] Update/modernize/fix some tests (#3968) --- src/test/abilities/hustle.test.ts | 36 +++++++++++++-------------- src/test/abilities/wind_rider.test.ts | 36 ++++++++++++++------------- src/test/moves/fake_out.test.ts | 17 ++++++------- src/test/moves/gigaton_hammer.test.ts | 13 +++++----- 4 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index b7c3b723c4b..276edb691c9 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -1,8 +1,6 @@ import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; -import { DamagePhase } from "#app/phases/damage-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; @@ -26,18 +24,18 @@ describe("Abilities - Hustle", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.ability(Abilities.HUSTLE); - game.override.moveset([Moves.TACKLE, Moves.GIGA_DRAIN, Moves.FISSURE]); - game.override.startingLevel(5); - game.override.disableCrits(); - game.override.enemyLevel(5); - game.override.enemyMoveset(SPLASH_ONLY); - game.override.enemySpecies(Species.SHUCKLE); - game.override.enemyAbility(Abilities.BALL_FETCH); + game.override + .ability(Abilities.HUSTLE) + .moveset([Moves.TACKLE, Moves.GIGA_DRAIN, Moves.FISSURE]) + .disableCrits() + .battleType("single") + .enemyMoveset(SPLASH_ONLY) + .enemySpecies(Species.SHUCKLE) + .enemyAbility(Abilities.BALL_FETCH); }); it("increases the user's Attack stat by 50%", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const atk = pikachu.stats[Stat.ATK]; @@ -45,25 +43,25 @@ describe("Abilities - Hustle", () => { game.move.select(Moves.TACKLE); await game.move.forceHit(); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); - expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5); + expect(pikachu.getBattleStat).toHaveReturnedWith(Math.floor(atk * 1.5)); }); it("lowers the accuracy of the user's physical moves by 20%", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; vi.spyOn(pikachu, "getAccuracyMultiplier"); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(MoveEffectPhase); + await game.phaseInterceptor.to("MoveEffectPhase"); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(0.8); }); it("does not affect non-physical moves", async () => { - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const spatk = pikachu.stats[Stat.SPATK]; @@ -71,7 +69,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getAccuracyMultiplier"); game.move.select(Moves.GIGA_DRAIN); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(pikachu.getBattleStat).toHaveReturnedWith(spatk); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1); @@ -81,7 +79,7 @@ describe("Abilities - Hustle", () => { game.override.startingLevel(100); game.override.enemyLevel(30); - await game.startBattle([Species.PIKACHU]); + await game.classicMode.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -89,7 +87,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy"); game.move.select(Moves.FISSURE); - await game.phaseInterceptor.to(DamagePhase); + await game.phaseInterceptor.to("DamagePhase"); expect(enemyPokemon.turnData.damageTaken).toBe(enemyPokemon.getMaxHp()); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1); diff --git a/src/test/abilities/wind_rider.test.ts b/src/test/abilities/wind_rider.test.ts index 97e2e6456dc..e11b3b39723 100644 --- a/src/test/abilities/wind_rider.test.ts +++ b/src/test/abilities/wind_rider.test.ts @@ -1,5 +1,4 @@ import { BattleStat } from "#app/data/battle-stat"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -24,22 +23,23 @@ describe("Abilities - Wind Rider", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.enemySpecies(Species.SHIFTRY); - game.override.enemyAbility(Abilities.WIND_RIDER); - game.override.moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]); - game.override.enemyMoveset(SPLASH_ONLY); + game.override + .battleType("single") + .enemySpecies(Species.SHIFTRY) + .enemyAbility(Abilities.WIND_RIDER) + .moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]) + .enemyMoveset(SPLASH_ONLY); }); it("takes no damage from wind moves and its Attack is increased by one stage when hit by one", async () => { - await game.startBattle([Species.MAGIKARP]); + await game.classicMode.startBattle([Species.MAGIKARP]); const shiftry = game.scene.getEnemyPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); game.move.select(Moves.PETAL_BLIZZARD); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(shiftry.isFullHp()).toBe(true); expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); @@ -49,14 +49,14 @@ describe("Abilities - Wind Rider", () => { game.override.ability(Abilities.WIND_RIDER); game.override.enemySpecies(Species.MAGIKARP); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); game.move.select(Moves.TAILWIND); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); }); @@ -65,7 +65,7 @@ describe("Abilities - Wind Rider", () => { game.override.ability(Abilities.WIND_RIDER); game.override.enemySpecies(Species.MAGIKARP); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const magikarp = game.scene.getEnemyPokemon()!; const shiftry = game.scene.getPlayerPokemon()!; @@ -74,16 +74,18 @@ describe("Abilities - Wind Rider", () => { game.move.select(Moves.TAILWIND); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); }); it("does not increase Attack when Tailwind is present on opposing side", async () => { - game.override.enemySpecies(Species.MAGIKARP); + game.override + .enemySpecies(Species.MAGIKARP) + .ability(Abilities.WIND_RIDER); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const magikarp = game.scene.getEnemyPokemon()!; const shiftry = game.scene.getPlayerPokemon()!; @@ -92,7 +94,7 @@ describe("Abilities - Wind Rider", () => { game.move.select(Moves.TAILWIND); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -101,7 +103,7 @@ describe("Abilities - Wind Rider", () => { it("does not interact with Sandstorm", async () => { game.override.enemySpecies(Species.MAGIKARP); - await game.startBattle([Species.SHIFTRY]); + await game.classicMode.startBattle([Species.SHIFTRY]); const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -109,7 +111,7 @@ describe("Abilities - Wind Rider", () => { game.move.select(Moves.SANDSTORM); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(shiftry.hp).lessThan(shiftry.getMaxHp()); diff --git a/src/test/moves/fake_out.test.ts b/src/test/moves/fake_out.test.ts index 92331ebb758..ac09917daea 100644 --- a/src/test/moves/fake_out.test.ts +++ b/src/test/moves/fake_out.test.ts @@ -1,5 +1,4 @@ import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -32,28 +31,28 @@ describe("Moves - Fake Out", () => { }); it("can only be used on the first turn a pokemon is sent out", async() => { - await game.startBattle(); + await game.classicMode.startBattle(); const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + game.move.select(Moves.FAKE_OUT); await game.toNextTurn(); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); const postTurnOneHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + game.move.select(Moves.FAKE_OUT); await game.toNextTurn(); expect(enemy.hp).toBe(postTurnOneHp); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.toNextWave(); const newEnemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + game.move.select(Moves.FAKE_OUT); await game.toNextTurn(); expect(newEnemy.hp).toBe(newEnemy.getMaxHp()); @@ -61,11 +60,11 @@ describe("Moves - Fake Out", () => { it("can be used again if recalled and sent back out", async() => { game.override.startingWave(4); - await game.startBattle(); + await game.classicMode.startBattle(); const enemy1 = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + game.move.select(Moves.FAKE_OUT); await game.phaseInterceptor.to("MoveEndPhase"); expect(enemy1.hp).toBeLessThan(enemy1.getMaxHp()); @@ -73,7 +72,7 @@ describe("Moves - Fake Out", () => { await game.doKillOpponents(); await game.toNextWave(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FAKE_OUT)); + game.move.select(Moves.FAKE_OUT); await game.toNextTurn(); const enemy2 = game.scene.getEnemyPokemon()!; diff --git a/src/test/moves/gigaton_hammer.test.ts b/src/test/moves/gigaton_hammer.test.ts index aca95a76332..9379e9d98b2 100644 --- a/src/test/moves/gigaton_hammer.test.ts +++ b/src/test/moves/gigaton_hammer.test.ts @@ -1,6 +1,5 @@ import { BattlerIndex } from "#app/battle.js"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -35,11 +34,11 @@ describe("Moves - Gigaton Hammer", () => { }); it("can't be used two turns in a row", async() => { - await game.startBattle(); + await game.classicMode.startBattle(); const enemy1 = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + game.move.select(Moves.GIGATON_HAMMER); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEndPhase"); @@ -48,7 +47,7 @@ describe("Moves - Gigaton Hammer", () => { await game.doKillOpponents(); await game.toNextWave(); - game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + game.move.select(Moves.GIGATON_HAMMER); await game.toNextTurn(); const enemy2 = game.scene.getEnemyPokemon()!; @@ -58,11 +57,11 @@ describe("Moves - Gigaton Hammer", () => { it("can be used again if recalled and sent back out", async() => { game.override.startingWave(4); - await game.startBattle(); + await game.classicMode.startBattle(); const enemy1 = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + game.move.select(Moves.GIGATON_HAMMER); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEndPhase"); @@ -71,7 +70,7 @@ describe("Moves - Gigaton Hammer", () => { await game.doKillOpponents(); await game.toNextWave(); - game.doAttack(getMovePosition(game.scene, 0, Moves.GIGATON_HAMMER)); + game.move.select(Moves.GIGATON_HAMMER); await game.toNextTurn(); const enemy2 = game.scene.getEnemyPokemon()!; From 434b82311232bbd498619152eee77598b1d65ec4 Mon Sep 17 00:00:00 2001 From: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:15:52 -0500 Subject: [PATCH 036/108] [Enhancement/Item] Separate form change items into non-rare and rare (#3957) * Separate form change items into non-rare and rare * Remove unnecessary !! --- public/images/items.json | 3378 ++++++++--------- public/images/items.png | Bin 57239 -> 56430 bytes .../{blank_memory.png => normal_memory.png} | Bin src/data/pokemon-forms.ts | 26 +- src/locales/de/modifier-type.json | 2 +- src/locales/en/modifier-type.json | 2 +- src/locales/es/modifier-type.json | 2 +- src/locales/fr/modifier-type.json | 2 +- src/locales/it/modifier-type.json | 2 +- src/locales/ko/modifier-type.json | 2 +- src/locales/pt_BR/modifier-type.json | 2 +- src/locales/zh_CN/modifier-type.json | 2 +- src/locales/zh_TW/modifier-type.json | 2 +- src/modifier/modifier-type.ts | 11 +- 14 files changed, 1718 insertions(+), 1715 deletions(-) rename public/images/items/{blank_memory.png => normal_memory.png} (100%) diff --git a/public/images/items.json b/public/images/items.json index bb86b46aa4d..442b93d657b 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -2971,7 +2971,7 @@ } }, { - "filename": "blank_memory", + "filename": "bug_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -2992,7 +2992,7 @@ } }, { - "filename": "bug_memory", + "filename": "charcoal", "rotated": false, "trimmed": true, "sourceSize": { @@ -3013,7 +3013,7 @@ } }, { - "filename": "charcoal", + "filename": "dark_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -3034,7 +3034,7 @@ } }, { - "filename": "dark_memory", + "filename": "dire_hit", "rotated": false, "trimmed": true, "sourceSize": { @@ -3433,7 +3433,7 @@ } }, { - "filename": "dire_hit", + "filename": "dna_splicers", "rotated": false, "trimmed": true, "sourceSize": { @@ -3727,7 +3727,7 @@ } }, { - "filename": "dna_splicers", + "filename": "dragon_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -3979,7 +3979,7 @@ } }, { - "filename": "dragon_memory", + "filename": "electirizer", "rotated": false, "trimmed": true, "sourceSize": { @@ -4168,7 +4168,7 @@ } }, { - "filename": "electirizer", + "filename": "electric_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4189,7 +4189,7 @@ } }, { - "filename": "electric_memory", + "filename": "enigma_berry", "rotated": false, "trimmed": true, "sourceSize": { @@ -4231,7 +4231,7 @@ } }, { - "filename": "enigma_berry", + "filename": "fairy_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4252,7 +4252,7 @@ } }, { - "filename": "fairy_memory", + "filename": "fighting_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4272,27 +4272,6 @@ "h": 22 } }, - { - "filename": "fighting_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 193, - "y": 212, - "w": 22, - "h": 22 - } - }, { "filename": "fire_memory", "rotated": false, @@ -4309,7 +4288,7 @@ }, "frame": { "x": 193, - "y": 234, + "y": 212, "w": 22, "h": 22 } @@ -4330,7 +4309,7 @@ }, "frame": { "x": 193, - "y": 256, + "y": 234, "w": 22, "h": 22 } @@ -4350,8 +4329,8 @@ "h": 22 }, "frame": { - "x": 215, - "y": 212, + "x": 193, + "y": 256, "w": 22, "h": 22 } @@ -4372,7 +4351,7 @@ }, "frame": { "x": 215, - "y": 234, + "y": 212, "w": 22, "h": 22 } @@ -4393,7 +4372,7 @@ }, "frame": { "x": 215, - "y": 256, + "y": 234, "w": 22, "h": 22 } @@ -4412,6 +4391,27 @@ "w": 22, "h": 22 }, + "frame": { + "x": 215, + "y": 256, + "w": 22, + "h": 22 + } + }, + { + "filename": "guard_spec", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, "frame": { "x": 195, "y": 278, @@ -4420,7 +4420,7 @@ } }, { - "filename": "guard_spec", + "filename": "hard_meteorite", "rotated": false, "trimmed": true, "sourceSize": { @@ -4428,15 +4428,15 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 7, "y": 5, - "w": 22, + "w": 20, "h": 22 }, "frame": { "x": 217, "y": 278, - "w": 22, + "w": 20, "h": 22 } }, @@ -4476,8 +4476,8 @@ "h": 22 }, "frame": { - "x": 236, - "y": 127, + "x": 258, + "y": 105, "w": 22, "h": 22 } @@ -4497,8 +4497,8 @@ "h": 22 }, "frame": { - "x": 258, - "y": 105, + "x": 236, + "y": 127, "w": 22, "h": 22 } @@ -4525,7 +4525,7 @@ } }, { - "filename": "poison_memory", + "filename": "normal_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4546,7 +4546,7 @@ } }, { - "filename": "protector", + "filename": "poison_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4588,7 +4588,7 @@ } }, { - "filename": "psychic_memory", + "filename": "protector", "rotated": false, "trimmed": true, "sourceSize": { @@ -4609,7 +4609,7 @@ } }, { - "filename": "rock_memory", + "filename": "psychic_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4650,48 +4650,6 @@ "h": 23 } }, - { - "filename": "hard_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 237, - "y": 235, - "w": 20, - "h": 22 - } - }, - { - "filename": "liechi_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 237, - "y": 257, - "w": 22, - "h": 21 - } - }, { "filename": "potion", "rotated": false, @@ -4734,6 +4692,27 @@ "h": 21 } }, + { + "filename": "liechi_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 302, + "y": 128, + "w": 22, + "h": 21 + } + }, { "filename": "reviver_seed", "rotated": false, @@ -4749,12 +4728,54 @@ "h": 20 }, "frame": { - "x": 302, - "y": 128, + "x": 342, + "y": 107, "w": 23, "h": 20 } }, + { + "filename": "shell_bell", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 23, + "h": 20 + }, + "frame": { + "x": 365, + "y": 107, + "w": 23, + "h": 20 + } + }, + { + "filename": "rock_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 237, + "y": 235, + "w": 22, + "h": 22 + } + }, { "filename": "scroll_of_darkness", "rotated": false, @@ -4770,8 +4791,8 @@ "h": 22 }, "frame": { - "x": 342, - "y": 107, + "x": 237, + "y": 257, "w": 22, "h": 22 } @@ -4791,75 +4812,12 @@ "h": 22 }, "frame": { - "x": 364, - "y": 107, + "x": 237, + "y": 279, "w": 22, "h": 22 } }, - { - "filename": "shed_shell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 386, - "y": 107, - "w": 22, - "h": 22 - } - }, - { - "filename": "oval_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 19 - }, - "frame": { - "x": 408, - "y": 108, - "w": 18, - "h": 19 - } - }, - { - "filename": "sitrus_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 20, - "h": 22 - }, - "frame": { - "x": 239, - "y": 278, - "w": 20, - "h": 22 - } - }, { "filename": "upgrade", "rotated": false, @@ -4882,7 +4840,7 @@ } }, { - "filename": "starf_berry", + "filename": "shed_shell", "rotated": false, "trimmed": true, "sourceSize": { @@ -4903,7 +4861,7 @@ } }, { - "filename": "steel_memory", + "filename": "starf_berry", "rotated": false, "trimmed": true, "sourceSize": { @@ -4924,7 +4882,7 @@ } }, { - "filename": "thick_club", + "filename": "steel_memory", "rotated": false, "trimmed": true, "sourceSize": { @@ -4945,7 +4903,7 @@ } }, { - "filename": "thunder_stone", + "filename": "big_nugget", "rotated": false, "trimmed": true, "sourceSize": { @@ -4953,16 +4911,37 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 6, + "y": 6, + "w": 20, + "h": 20 }, "frame": { - "x": 257, - "y": 234, - "w": 22, - "h": 22 + "x": 388, + "y": 107, + "w": 20, + "h": 20 + } + }, + { + "filename": "oval_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 19 + }, + "frame": { + "x": 408, + "y": 108, + "w": 18, + "h": 19 } }, { @@ -4987,7 +4966,28 @@ } }, { - "filename": "tm_bug", + "filename": "sitrus_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 20, + "h": 22 + }, + "frame": { + "x": 281, + "y": 168, + "w": 20, + "h": 22 + } + }, + { + "filename": "thick_club", "rotated": false, "trimmed": true, "sourceSize": { @@ -5001,14 +5001,14 @@ "h": 22 }, "frame": { - "x": 281, - "y": 168, + "x": 301, + "y": 149, "w": 22, "h": 22 } }, { - "filename": "tm_dark", + "filename": "thunder_stone", "rotated": false, "trimmed": true, "sourceSize": { @@ -5029,7 +5029,7 @@ } }, { - "filename": "tm_dragon", + "filename": "tm_bug", "rotated": false, "trimmed": true, "sourceSize": { @@ -5050,7 +5050,7 @@ } }, { - "filename": "tm_electric", + "filename": "tm_dark", "rotated": false, "trimmed": true, "sourceSize": { @@ -5064,14 +5064,14 @@ "h": 22 }, "frame": { - "x": 279, + "x": 259, "y": 234, "w": 22, "h": 22 } }, { - "filename": "tm_fairy", + "filename": "tm_dragon", "rotated": false, "trimmed": true, "sourceSize": { @@ -5092,7 +5092,7 @@ } }, { - "filename": "tm_fighting", + "filename": "tm_electric", "rotated": false, "trimmed": true, "sourceSize": { @@ -5112,6 +5112,48 @@ "h": 22 } }, + { + "filename": "tm_fairy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 281, + "y": 234, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_fighting", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 281, + "y": 256, + "w": 22, + "h": 22 + } + }, { "filename": "tm_fire", "rotated": false, @@ -5128,11 +5170,53 @@ }, "frame": { "x": 281, - "y": 256, + "y": 278, "w": 22, "h": 22 } }, + { + "filename": "lum_berry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 301, + "y": 171, + "w": 20, + "h": 19 + } + }, + { + "filename": "metal_coat", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 5, + "w": 19, + "h": 22 + }, + "frame": { + "x": 302, + "y": 190, + "w": 19, + "h": 22 + } + }, { "filename": "tm_flying", "rotated": false, @@ -5148,56 +5232,14 @@ "h": 22 }, "frame": { - "x": 281, - "y": 278, + "x": 301, + "y": 212, "w": 22, "h": 22 } }, { - "filename": "baton", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 408, - "y": 127, - "w": 18, - "h": 18 - } - }, - { - "filename": "golden_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 17, - "h": 20 - }, - "frame": { - "x": 325, - "y": 128, - "w": 17, - "h": 20 - } - }, - { - "filename": "shell_bell", + "filename": "tm_ghost", "rotated": false, "trimmed": true, "sourceSize": { @@ -5206,19 +5248,19 @@ }, "spriteSourceSize": { "x": 5, - "y": 7, - "w": 23, - "h": 20 + "y": 5, + "w": 22, + "h": 22 }, "frame": { - "x": 342, - "y": 129, - "w": 23, - "h": 20 + "x": 303, + "y": 234, + "w": 22, + "h": 22 } }, { - "filename": "deep_sea_scale", + "filename": "tm_grass", "rotated": false, "trimmed": true, "sourceSize": { @@ -5227,15 +5269,36 @@ }, "spriteSourceSize": { "x": 5, - "y": 6, + "y": 5, "w": 22, - "h": 20 + "h": 22 }, "frame": { - "x": 365, - "y": 129, + "x": 303, + "y": 256, "w": 22, - "h": 20 + "h": 22 + } + }, + { + "filename": "tm_ground", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 303, + "y": 278, + "w": 22, + "h": 22 } }, { @@ -5253,14 +5316,14 @@ "h": 21 }, "frame": { - "x": 387, - "y": 129, + "x": 324, + "y": 128, "w": 21, "h": 21 } }, { - "filename": "candy", + "filename": "tm_ice", "rotated": false, "trimmed": true, "sourceSize": { @@ -5268,15 +5331,498 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 11, - "w": 18, + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 323, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_normal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 321, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_poison", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 345, + "y": 127, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_psychic", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 345, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_rock", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 343, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_steel", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 367, + "y": 127, + "w": 22, + "h": 22 + } + }, + { + "filename": "tm_water", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 367, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "water_memory", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 365, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "water_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 389, + "y": 127, + "w": 22, + "h": 22 + } + }, + { + "filename": "full_heal", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 4, + "w": 15, + "h": 23 + }, + "frame": { + "x": 411, + "y": 127, + "w": 15, + "h": 23 + } + }, + { + "filename": "x_accuracy", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 389, + "y": 149, + "w": 22, + "h": 22 + } + }, + { + "filename": "leftovers", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 15, + "h": 22 + }, + "frame": { + "x": 411, + "y": 150, + "w": 15, + "h": 22 + } + }, + { + "filename": "x_attack", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 387, + "y": 171, + "w": 22, + "h": 22 + } + }, + { + "filename": "super_potion", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 5, + "w": 17, + "h": 23 + }, + "frame": { + "x": 409, + "y": 172, + "w": 17, + "h": 23 + } + }, + { + "filename": "power_herb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 321, + "y": 193, + "w": 20, + "h": 19 + } + }, + { + "filename": "x_defense", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 323, + "y": 212, + "w": 22, + "h": 22 + } + }, + { + "filename": "razor_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 20, + "h": 19 + }, + "frame": { + "x": 341, + "y": 193, + "w": 20, + "h": 19 + } + }, + { + "filename": "x_sp_atk", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 325, + "y": 234, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_sp_def", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 325, + "y": 256, + "w": 22, + "h": 22 + } + }, + { + "filename": "x_speed", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 5, + "w": 22, + "h": 22 + }, + "frame": { + "x": 325, + "y": 278, + "w": 22, + "h": 22 + } + }, + { + "filename": "deep_sea_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 361, + "y": 193, + "w": 22, + "h": 20 + } + }, + { + "filename": "fairy_feather", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 22, + "h": 20 + }, + "frame": { + "x": 383, + "y": 193, + "w": 22, + "h": 20 + } + }, + { + "filename": "shiny_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 405, + "y": 195, + "w": 21, + "h": 21 + } + }, + { + "filename": "mystery_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, "h": 18 }, "frame": { - "x": 408, - "y": 145, - "w": 18, + "x": 345, + "y": 212, + "w": 16, "h": 18 } }, @@ -5295,35 +5841,14 @@ "h": 17 }, "frame": { - "x": 195, - "y": 300, + "x": 361, + "y": 213, "w": 23, "h": 17 } }, { - "filename": "relic_crown", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 23, - "h": 18 - }, - "frame": { - "x": 218, - "y": 300, - "w": 23, - "h": 18 - } - }, - { - "filename": "fairy_feather", + "filename": "masterpiece_teacup", "rotated": false, "trimmed": true, "sourceSize": { @@ -5333,13 +5858,286 @@ "spriteSourceSize": { "x": 5, "y": 7, + "w": 21, + "h": 18 + }, + "frame": { + "x": 384, + "y": 213, + "w": 21, + "h": 18 + } + }, + { + "filename": "zoom_lens", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 21, + "h": 21 + }, + "frame": { + "x": 405, + "y": 216, + "w": 21, + "h": 21 + } + }, + { + "filename": "sweet_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, "w": 22, + "h": 21 + }, + "frame": { + "x": 347, + "y": 230, + "w": 22, + "h": 21 + } + }, + { + "filename": "syrupy_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 347, + "y": 251, + "w": 22, + "h": 21 + } + }, + { + "filename": "tart_apple", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 21 + }, + "frame": { + "x": 347, + "y": 272, + "w": 22, + "h": 21 + } + }, + { + "filename": "eviolite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 369, + "y": 230, + "w": 15, + "h": 15 + } + }, + { + "filename": "sharp_meteorite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 21, + "h": 18 + }, + "frame": { + "x": 384, + "y": 231, + "w": 21, + "h": 18 + } + }, + { + "filename": "unremarkable_teacup", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 7, + "w": 21, + "h": 18 + }, + "frame": { + "x": 405, + "y": 237, + "w": 21, + "h": 18 + } + }, + { + "filename": "prism_scale", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 8, + "w": 15, + "h": 15 + }, + "frame": { + "x": 369, + "y": 245, + "w": 15, + "h": 15 + } + }, + { + "filename": "metronome", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 5, + "w": 17, + "h": 22 + }, + "frame": { + "x": 369, + "y": 260, + "w": 17, + "h": 22 + } + }, + { + "filename": "quick_claw", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 386, + "y": 249, + "w": 19, + "h": 21 + } + }, + { + "filename": "blue_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, "h": 20 }, "frame": { - "x": 241, - "y": 300, - "w": 22, + "x": 405, + "y": 255, + "w": 20, + "h": 20 + } + }, + { + "filename": "candy_jar", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 386, + "y": 270, + "w": 19, + "h": 20 + } + }, + { + "filename": "golden_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 369, + "y": 282, + "w": 17, "h": 20 } }, @@ -5358,14 +6156,56 @@ "h": 20 }, "frame": { - "x": 263, - "y": 300, + "x": 347, + "y": 293, "w": 22, "h": 20 } }, { - "filename": "big_nugget", + "filename": "everstone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 8, + "w": 20, + "h": 17 + }, + "frame": { + "x": 405, + "y": 275, + "w": 20, + "h": 17 + } + }, + { + "filename": "hard_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 19, + "h": 20 + }, + "frame": { + "x": 386, + "y": 290, + "w": 19, + "h": 20 + } + }, + { + "filename": "gb", "rotated": false, "trimmed": true, "sourceSize": { @@ -5379,7 +6219,217 @@ "h": 20 }, "frame": { - "x": 285, + "x": 405, + "y": 292, + "w": 20, + "h": 20 + } + }, + { + "filename": "lucky_egg", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 17, + "h": 20 + }, + "frame": { + "x": 369, + "y": 302, + "w": 17, + "h": 20 + } + }, + { + "filename": "miracle_seed", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 7, + "w": 19, + "h": 19 + }, + "frame": { + "x": 386, + "y": 310, + "w": 19, + "h": 19 + } + }, + { + "filename": "magnet", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 405, + "y": 312, + "w": 20, + "h": 20 + } + }, + { + "filename": "relic_crown", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 4, + "y": 7, + "w": 23, + "h": 18 + }, + "frame": { + "x": 195, + "y": 300, + "w": 23, + "h": 18 + } + }, + { + "filename": "spell_tag", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 6, + "w": 19, + "h": 21 + }, + "frame": { + "x": 218, + "y": 300, + "w": 19, + "h": 21 + } + }, + { + "filename": "tera_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 5, + "y": 6, + "w": 22, + "h": 20 + }, + "frame": { + "x": 237, + "y": 301, + "w": 22, + "h": 20 + } + }, + { + "filename": "mb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 259, + "y": 300, + "w": 20, + "h": 20 + } + }, + { + "filename": "pb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 279, + "y": 300, + "w": 20, + "h": 20 + } + }, + { + "filename": "pb_gold", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 299, + "y": 300, + "w": 20, + "h": 20 + } + }, + { + "filename": "rb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 6, + "y": 6, + "w": 20, + "h": 20 + }, + "frame": { + "x": 319, "y": 300, "w": 20, "h": 20 @@ -5407,7 +6457,7 @@ } }, { - "filename": "lucky_egg", + "filename": "soothe_bell", "rotated": false, "trimmed": true, "sourceSize": { @@ -5415,16 +6465,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 7, - "y": 6, + "x": 8, + "y": 5, "w": 17, - "h": 20 + "h": 22 }, "frame": { "x": 178, "y": 305, "w": 17, - "h": 20 + "h": 22 } }, { @@ -5443,13 +6493,13 @@ }, "frame": { "x": 195, - "y": 317, + "y": 318, "w": 23, "h": 17 } }, { - "filename": "sweet_apple", + "filename": "smooth_meteorite", "rotated": false, "trimmed": true, "sourceSize": { @@ -5457,20 +6507,20 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 7, "y": 6, - "w": 22, - "h": 21 + "w": 20, + "h": 20 }, "frame": { "x": 218, - "y": 318, - "w": 22, - "h": 21 + "y": 321, + "w": 20, + "h": 20 } }, { - "filename": "syrupy_apple", + "filename": "strange_ball", "rotated": false, "trimmed": true, "sourceSize": { @@ -5478,58 +6528,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, + "x": 6, "y": 6, - "w": 22, - "h": 21 + "w": 20, + "h": 20 }, "frame": { - "x": 240, - "y": 320, - "w": 22, - "h": 21 - } - }, - { - "filename": "tart_apple", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 21 - }, - "frame": { - "x": 262, - "y": 320, - "w": 22, - "h": 21 - } - }, - { - "filename": "shiny_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 284, - "y": 320, - "w": 21, - "h": 21 + "x": 238, + "y": 321, + "w": 20, + "h": 20 } }, { @@ -5554,7 +6562,7 @@ } }, { - "filename": "tm_ghost", + "filename": "ub", "rotated": false, "trimmed": true, "sourceSize": { @@ -5562,541 +6570,16 @@ "h": 32 }, "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 + "x": 6, + "y": 6, + "w": 20, + "h": 20 }, "frame": { "x": 135, "y": 325, - "w": 22, - "h": 22 - } - }, - { - "filename": "zoom_lens", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 21, - "h": 21 - }, - "frame": { - "x": 157, - "y": 322, - "w": 21, - "h": 21 - } - }, - { - "filename": "metronome", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 178, - "y": 325, - "w": 17, - "h": 22 - } - }, - { - "filename": "tm_grass", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 136, - "y": 347, - "w": 22, - "h": 22 - } - }, - { - "filename": "blue_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, "w": 20, "h": 20 - }, - "frame": { - "x": 158, - "y": 343, - "w": 20, - "h": 20 - } - }, - { - "filename": "super_potion", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 23 - }, - "frame": { - "x": 141, - "y": 369, - "w": 17, - "h": 23 - } - }, - { - "filename": "tm_ground", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 158, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "metal_coat", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 19, - "h": 22 - }, - "frame": { - "x": 141, - "y": 392, - "w": 19, - "h": 22 - } - }, - { - "filename": "tera_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 22, - "h": 20 - }, - "frame": { - "x": 195, - "y": 334, - "w": 22, - "h": 20 - } - }, - { - "filename": "tm_ice", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 217, - "y": 339, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_normal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 239, - "y": 341, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_poison", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 261, - "y": 341, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_psychic", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 283, - "y": 341, - "w": 22, - "h": 22 - } - }, - { - "filename": "tm_rock", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 160, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "lum_berry", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 160, - "y": 407, - "w": 20, - "h": 19 - } - }, - { - "filename": "power_herb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 180, - "y": 407, - "w": 20, - "h": 19 - } - }, - { - "filename": "altarianite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 178, - "y": 347, - "w": 16, - "h": 16 - } - }, - { - "filename": "leftovers", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 15, - "h": 22 - }, - "frame": { - "x": 180, - "y": 363, - "w": 15, - "h": 22 - } - }, - { - "filename": "tm_steel", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 182, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "razor_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 20, - "h": 19 - }, - "frame": { - "x": 200, - "y": 407, - "w": 20, - "h": 19 - } - }, - { - "filename": "tm_water", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 195, - "y": 354, - "w": 22, - "h": 22 - } - }, - { - "filename": "water_memory", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 217, - "y": 361, - "w": 22, - "h": 22 - } - }, - { - "filename": "water_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 239, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_accuracy", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 261, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_attack", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 283, - "y": 363, - "w": 22, - "h": 22 - } - }, - { - "filename": "soothe_bell", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 5, - "w": 17, - "h": 22 - }, - "frame": { - "x": 204, - "y": 385, - "w": 17, - "h": 22 } }, { @@ -6114,537 +6597,12 @@ "h": 19 }, "frame": { - "x": 220, - "y": 407, + "x": 155, + "y": 322, "w": 20, "h": 19 } }, - { - "filename": "x_defense", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 221, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_sp_atk", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 243, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "miracle_seed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 19, - "h": 19 - }, - "frame": { - "x": 240, - "y": 407, - "w": 19, - "h": 19 - } - }, - { - "filename": "x_sp_def", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 265, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "x_speed", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 22, - "h": 22 - }, - "frame": { - "x": 287, - "y": 385, - "w": 22, - "h": 22 - } - }, - { - "filename": "masterpiece_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, - "h": 18 - }, - "frame": { - "x": 259, - "y": 407, - "w": 21, - "h": 18 - } - }, - { - "filename": "sharp_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 21, - "h": 18 - }, - "frame": { - "x": 280, - "y": 407, - "w": 21, - "h": 18 - } - }, - { - "filename": "dark_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 301, - "y": 407, - "w": 18, - "h": 18 - } - }, - { - "filename": "relic_gold", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 15, - "h": 11 - }, - "frame": { - "x": 0, - "y": 414, - "w": 15, - "h": 11 - } - }, - { - "filename": "full_heal", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 4, - "w": 15, - "h": 23 - }, - "frame": { - "x": 301, - "y": 212, - "w": 15, - "h": 23 - } - }, - { - "filename": "quick_claw", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 301, - "y": 235, - "w": 19, - "h": 21 - } - }, - { - "filename": "spell_tag", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 19, - "h": 21 - }, - "frame": { - "x": 303, - "y": 256, - "w": 19, - "h": 21 - } - }, - { - "filename": "candy_jar", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 303, - "y": 277, - "w": 19, - "h": 20 - } - }, - { - "filename": "gb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 297, - "w": 20, - "h": 20 - } - }, - { - "filename": "magnet", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 317, - "w": 20, - "h": 20 - } - }, - { - "filename": "mb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 337, - "w": 20, - "h": 20 - } - }, - { - "filename": "pb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 305, - "y": 357, - "w": 20, - "h": 20 - } - }, - { - "filename": "hard_stone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 19, - "h": 20 - }, - "frame": { - "x": 309, - "y": 377, - "w": 19, - "h": 20 - } - }, - { - "filename": "pb_gold", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 302, - "y": 148, - "w": 20, - "h": 20 - } - }, - { - "filename": "rb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 322, - "y": 148, - "w": 20, - "h": 20 - } - }, - { - "filename": "smooth_meteorite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 303, - "y": 168, - "w": 20, - "h": 20 - } - }, - { - "filename": "strange_ball", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 342, - "y": 149, - "w": 20, - "h": 20 - } - }, - { - "filename": "ub", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 6, - "w": 20, - "h": 20 - }, - "frame": { - "x": 362, - "y": 149, - "w": 20, - "h": 20 - } - }, - { - "filename": "flame_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 323, - "y": 168, - "w": 18, - "h": 18 - } - }, - { - "filename": "unremarkable_teacup", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 21, - "h": 18 - }, - "frame": { - "x": 341, - "y": 169, - "w": 21, - "h": 18 - } - }, { "filename": "wl_ability_urge", "rotated": false, @@ -6660,8 +6618,8 @@ "h": 18 }, "frame": { - "x": 362, - "y": 169, + "x": 175, + "y": 327, "w": 20, "h": 18 } @@ -6681,12 +6639,33 @@ "h": 18 }, "frame": { - "x": 382, - "y": 150, + "x": 136, + "y": 345, "w": 20, "h": 18 } }, + { + "filename": "baton", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 156, + "y": 341, + "w": 18, + "h": 18 + } + }, { "filename": "wl_awakening", "rotated": false, @@ -6702,8 +6681,8 @@ "h": 18 }, "frame": { - "x": 382, - "y": 168, + "x": 174, + "y": 345, "w": 20, "h": 18 } @@ -6723,33 +6702,12 @@ "h": 18 }, "frame": { - "x": 402, - "y": 163, + "x": 195, + "y": 335, "w": 20, "h": 18 } }, - { - "filename": "everstone", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 6, - "y": 8, - "w": 20, - "h": 17 - }, - "frame": { - "x": 402, - "y": 181, - "w": 20, - "h": 17 - } - }, { "filename": "wl_custom_spliced", "rotated": false, @@ -6765,8 +6723,8 @@ "h": 18 }, "frame": { - "x": 382, - "y": 186, + "x": 215, + "y": 341, "w": 20, "h": 18 } @@ -6786,8 +6744,8 @@ "h": 18 }, "frame": { - "x": 402, - "y": 198, + "x": 235, + "y": 341, "w": 20, "h": 18 } @@ -6807,33 +6765,12 @@ "h": 18 }, "frame": { - "x": 303, - "y": 188, + "x": 194, + "y": 353, "w": 20, "h": 18 } }, - { - "filename": "light_ball", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 323, - "y": 186, - "w": 18, - "h": 18 - } - }, { "filename": "wl_ether", "rotated": false, @@ -6849,8 +6786,8 @@ "h": 18 }, "frame": { - "x": 341, - "y": 187, + "x": 214, + "y": 359, "w": 20, "h": 18 } @@ -6870,14 +6807,14 @@ "h": 18 }, "frame": { - "x": 361, - "y": 187, + "x": 234, + "y": 359, "w": 20, "h": 18 } }, { - "filename": "light_stone", + "filename": "candy", "rotated": false, "trimmed": true, "sourceSize": { @@ -6886,13 +6823,13 @@ }, "spriteSourceSize": { "x": 7, - "y": 7, + "y": 11, "w": 18, "h": 18 }, "frame": { - "x": 323, - "y": 204, + "x": 156, + "y": 359, "w": 18, "h": 18 } @@ -6912,8 +6849,8 @@ "h": 18 }, "frame": { - "x": 341, - "y": 205, + "x": 174, + "y": 363, "w": 20, "h": 18 } @@ -6933,8 +6870,8 @@ "h": 18 }, "frame": { - "x": 361, - "y": 205, + "x": 194, + "y": 371, "w": 20, "h": 18 } @@ -6954,8 +6891,8 @@ "h": 18 }, "frame": { - "x": 381, - "y": 204, + "x": 214, + "y": 377, "w": 20, "h": 18 } @@ -6975,12 +6912,33 @@ "h": 18 }, "frame": { - "x": 401, - "y": 216, + "x": 234, + "y": 377, "w": 20, "h": 18 } }, + { + "filename": "relic_gold", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 9, + "y": 11, + "w": 15, + "h": 11 + }, + "frame": { + "x": 141, + "y": 363, + "w": 15, + "h": 11 + } + }, { "filename": "wl_item_drop", "rotated": false, @@ -6996,8 +6954,8 @@ "h": 18 }, "frame": { - "x": 381, - "y": 222, + "x": 141, + "y": 377, "w": 20, "h": 18 } @@ -7017,8 +6975,8 @@ "h": 18 }, "frame": { - "x": 401, - "y": 234, + "x": 141, + "y": 395, "w": 20, "h": 18 } @@ -7038,8 +6996,8 @@ "h": 18 }, "frame": { - "x": 320, - "y": 222, + "x": 161, + "y": 381, "w": 20, "h": 18 } @@ -7059,8 +7017,8 @@ "h": 18 }, "frame": { - "x": 340, - "y": 223, + "x": 161, + "y": 399, "w": 20, "h": 18 } @@ -7080,96 +7038,12 @@ "h": 18 }, "frame": { - "x": 360, - "y": 223, + "x": 181, + "y": 389, "w": 20, "h": 18 } }, - { - "filename": "ampharosite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 16 - }, - "frame": { - "x": 320, - "y": 240, - "w": 16, - "h": 16 - } - }, - { - "filename": "mystery_egg", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 16, - "h": 18 - }, - "frame": { - "x": 322, - "y": 256, - "w": 16, - "h": 18 - } - }, - { - "filename": "toxic_orb", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 7, - "y": 7, - "w": 18, - "h": 18 - }, - "frame": { - "x": 322, - "y": 274, - "w": 18, - "h": 18 - } - }, - { - "filename": "eviolite", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 8, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 336, - "y": 241, - "w": 15, - "h": 15 - } - }, { "filename": "wl_max_revive", "rotated": false, @@ -7185,8 +7059,8 @@ "h": 18 }, "frame": { - "x": 351, - "y": 241, + "x": 181, + "y": 407, "w": 20, "h": 18 } @@ -7206,8 +7080,8 @@ "h": 18 }, "frame": { - "x": 325, - "y": 292, + "x": 201, + "y": 395, "w": 20, "h": 18 } @@ -7227,12 +7101,54 @@ "h": 18 }, "frame": { - "x": 325, - "y": 310, + "x": 221, + "y": 395, "w": 20, "h": 18 } }, + { + "filename": "dark_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 241, + "y": 395, + "w": 18, + "h": 18 + } + }, + { + "filename": "flame_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 255, + "y": 341, + "w": 18, + "h": 18 + } + }, { "filename": "wl_reset_urge", "rotated": false, @@ -7248,8 +7164,8 @@ "h": 18 }, "frame": { - "x": 325, - "y": 328, + "x": 254, + "y": 359, "w": 20, "h": 18 } @@ -7269,12 +7185,33 @@ "h": 18 }, "frame": { - "x": 325, - "y": 346, + "x": 254, + "y": 377, "w": 20, "h": 18 } }, + { + "filename": "light_ball", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 259, + "y": 395, + "w": 18, + "h": 18 + } + }, { "filename": "wl_super_potion", "rotated": false, @@ -7290,12 +7227,96 @@ "h": 18 }, "frame": { - "x": 371, - "y": 241, + "x": 259, + "y": 320, "w": 20, "h": 18 } }, + { + "filename": "light_stone", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 279, + "y": 320, + "w": 18, + "h": 18 + } + }, + { + "filename": "toxic_orb", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 7, + "y": 7, + "w": 18, + "h": 18 + }, + "frame": { + "x": 297, + "y": 320, + "w": 18, + "h": 18 + } + }, + { + "filename": "altarianite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 315, + "y": 320, + "w": 16, + "h": 16 + } + }, + { + "filename": "ampharosite", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 8, + "y": 8, + "w": 16, + "h": 16 + }, + "frame": { + "x": 273, + "y": 338, + "w": 16, + "h": 16 + } + }, { "filename": "audinite", "rotated": false, @@ -7311,8 +7332,8 @@ "h": 16 }, "frame": { - "x": 328, - "y": 364, + "x": 289, + "y": 338, "w": 16, "h": 16 } @@ -7332,33 +7353,12 @@ "h": 16 }, "frame": { - "x": 328, - "y": 380, + "x": 274, + "y": 354, "w": 16, "h": 16 } }, - { - "filename": "prism_scale", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 32, - "h": 32 - }, - "spriteSourceSize": { - "x": 9, - "y": 8, - "w": 15, - "h": 15 - }, - "frame": { - "x": 338, - "y": 259, - "w": 15, - "h": 15 - } - }, { "filename": "beedrillite", "rotated": false, @@ -7374,8 +7374,8 @@ "h": 16 }, "frame": { - "x": 353, - "y": 259, + "x": 274, + "y": 370, "w": 16, "h": 16 } @@ -7395,8 +7395,8 @@ "h": 16 }, "frame": { - "x": 369, - "y": 259, + "x": 290, + "y": 354, "w": 16, "h": 16 } @@ -7416,8 +7416,8 @@ "h": 16 }, "frame": { - "x": 385, - "y": 259, + "x": 290, + "y": 370, "w": 16, "h": 16 } @@ -7437,8 +7437,8 @@ "h": 16 }, "frame": { - "x": 401, - "y": 252, + "x": 305, + "y": 338, "w": 16, "h": 16 } @@ -7458,8 +7458,8 @@ "h": 16 }, "frame": { - "x": 401, - "y": 268, + "x": 306, + "y": 354, "w": 16, "h": 16 } @@ -7479,8 +7479,8 @@ "h": 16 }, "frame": { - "x": 340, - "y": 275, + "x": 306, + "y": 370, "w": 16, "h": 16 } @@ -7500,8 +7500,8 @@ "h": 16 }, "frame": { - "x": 356, - "y": 275, + "x": 331, + "y": 320, "w": 16, "h": 16 } @@ -7521,8 +7521,8 @@ "h": 16 }, "frame": { - "x": 372, - "y": 275, + "x": 321, + "y": 336, "w": 16, "h": 16 } @@ -7542,8 +7542,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 291, + "x": 322, + "y": 352, "w": 16, "h": 16 } @@ -7563,8 +7563,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 307, + "x": 322, + "y": 368, "w": 16, "h": 16 } @@ -7584,8 +7584,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 291, + "x": 337, + "y": 336, "w": 16, "h": 16 } @@ -7605,8 +7605,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 323, + "x": 338, + "y": 352, "w": 16, "h": 16 } @@ -7626,8 +7626,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 307, + "x": 338, + "y": 368, "w": 16, "h": 16 } @@ -7647,8 +7647,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 339, + "x": 347, + "y": 313, "w": 16, "h": 16 } @@ -7668,8 +7668,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 323, + "x": 277, + "y": 386, "w": 16, "h": 16 } @@ -7689,8 +7689,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 339, + "x": 293, + "y": 386, "w": 16, "h": 16 } @@ -7710,8 +7710,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 291, + "x": 309, + "y": 386, "w": 16, "h": 16 } @@ -7731,8 +7731,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 307, + "x": 325, + "y": 384, "w": 16, "h": 16 } @@ -7752,8 +7752,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 323, + "x": 341, + "y": 384, "w": 16, "h": 16 } @@ -7773,8 +7773,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 339, + "x": 277, + "y": 402, "w": 16, "h": 16 } @@ -7794,8 +7794,8 @@ "h": 16 }, "frame": { - "x": 345, - "y": 355, + "x": 293, + "y": 402, "w": 16, "h": 16 } @@ -7815,8 +7815,8 @@ "h": 16 }, "frame": { - "x": 361, - "y": 355, + "x": 309, + "y": 402, "w": 16, "h": 16 } @@ -7836,8 +7836,8 @@ "h": 16 }, "frame": { - "x": 377, - "y": 355, + "x": 325, + "y": 400, "w": 16, "h": 16 } @@ -7857,8 +7857,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 371, + "x": 341, + "y": 400, "w": 16, "h": 16 } @@ -7878,8 +7878,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 371, + "x": 353, + "y": 329, "w": 16, "h": 16 } @@ -7899,8 +7899,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 371, + "x": 369, + "y": 322, "w": 16, "h": 16 } @@ -7920,8 +7920,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 387, + "x": 354, + "y": 345, "w": 16, "h": 16 } @@ -7941,8 +7941,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 387, + "x": 354, + "y": 361, "w": 16, "h": 16 } @@ -7962,8 +7962,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 387, + "x": 385, + "y": 329, "w": 16, "h": 16 } @@ -7983,8 +7983,8 @@ "h": 16 }, "frame": { - "x": 328, - "y": 396, + "x": 401, + "y": 332, "w": 16, "h": 16 } @@ -8004,8 +8004,8 @@ "h": 16 }, "frame": { - "x": 344, - "y": 403, + "x": 357, + "y": 377, "w": 16, "h": 16 } @@ -8025,8 +8025,8 @@ "h": 16 }, "frame": { - "x": 360, - "y": 403, + "x": 357, + "y": 393, "w": 16, "h": 16 } @@ -8046,8 +8046,8 @@ "h": 16 }, "frame": { - "x": 376, - "y": 403, + "x": 357, + "y": 409, "w": 16, "h": 16 } @@ -8067,8 +8067,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 284, + "x": 370, + "y": 345, "w": 16, "h": 16 } @@ -8088,8 +8088,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 300, + "x": 370, + "y": 361, "w": 16, "h": 16 } @@ -8109,8 +8109,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 316, + "x": 373, + "y": 377, "w": 16, "h": 16 } @@ -8130,8 +8130,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 332, + "x": 373, + "y": 393, "w": 16, "h": 16 } @@ -8151,8 +8151,8 @@ "h": 16 }, "frame": { - "x": 393, - "y": 348, + "x": 373, + "y": 409, "w": 16, "h": 16 } @@ -8172,8 +8172,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 284, + "x": 386, + "y": 348, "w": 16, "h": 16 } @@ -8193,8 +8193,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 300, + "x": 402, + "y": 348, "w": 16, "h": 16 } @@ -8214,8 +8214,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 316, + "x": 389, + "y": 364, "w": 16, "h": 16 } @@ -8235,8 +8235,8 @@ "h": 16 }, "frame": { - "x": 409, - "y": 332, + "x": 389, + "y": 380, "w": 16, "h": 16 } @@ -8247,6 +8247,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:6dd58685cd89890a4361a424b59bfe65:ff9159978f3a103ee1d656fb76e37457:110e074689c9edd2c54833ce2e4d9270$" + "smartupdate": "$TexturePacker:SmartUpdate:c004184e48566e1da6f13477a3348fd3:dc1a5489f7821641aade35ba290bbea7:110e074689c9edd2c54833ce2e4d9270$" } } diff --git a/public/images/items.png b/public/images/items.png index 23c87cf3204bcb5b3b69eaa1775a9ce81d0952c7..4c366e4d72adba5aa9f5e67b8b62942f1660268d 100644 GIT binary patch literal 56430 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%0A^RWoL5zJe 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

s})d$Z7HUxwiiq(1{m^qowcwoh>b`{s$ z?k~#EfV#=I{Mo=nBSFLgys_f>7g(j%P`5vo_jX*m6J`kEENDrcepyqxB=o;O5c|MU88Zc2lv zbu(i>+o>ZKU+(`+`Leq7Mt)R-GzYvcUIIp>b1G>$H6I=yi{Xh6c+F#r2t1MuA_2MJ z7)Z3y=$g6mtL%rizW!NW61E{v2h5034dM5Owi@wiedGoQe-s5ybF>RRD&>}>l(-FG zBuKq>Xw3fN{l8#2fjrCP#I#3}0R!#nHgJPj%!TR?|HJbirp7YH76h z$hpJ+Wp23&m-p7rPK*C&p|l)!ZZ=Ajo=T$truA+3udEkO^7p8T;5~fHl0Evma1AR1 zcT9?14p+4zM*+CSOjy)|PrM^~9soVkf4ySGgbm%WT@)jdAy0F{A0ev7* zm7*RtqAX7bxt)nB2Lg_PxEZ1SUmXw#DHsO4;C8bLml*S~CTt2El_0`I3o~oOL8*O2^Tw{p2SQEn?_F`9%F?W3OPYkAjndm^- zi0v0K2JzcVv1C8`+2!$RVGR5Y9$8TU4j?|#t##5OazYcPx2;ux*R#{hCn6T?jQ9Rt zyasNPNVSBj8+b|BP4-i*5lfMxMtXI(-ZcF=5~;A%O&u@pQLU&J7TAUjm|c0I=u(=> zK5yTaXQ%iXCp4c6h}cz3hWsV$J0iFgYgBPB-VVp8{uF^kESjSO?Y! ?jZg+%Jw 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�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 IM6N<$f@McVP5=M^ diff --git a/public/images/items/blank_memory.png b/public/images/items/normal_memory.png similarity index 100% rename from public/images/items/blank_memory.png rename to public/images/items/normal_memory.png diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 1420d8800aa..bc815b91f3a 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -66,34 +66,34 @@ export enum FormChangeItem { BLUE_ORB = 50, RED_ORB, - SHARP_METEORITE, - HARD_METEORITE, - SMOOTH_METEORITE, ADAMANT_CRYSTAL, LUSTROUS_GLOBE, GRISEOUS_CORE, REVEAL_GLASS, - GRACIDEA, MAX_MUSHROOMS, DARK_STONE, LIGHT_STONE, PRISON_BOTTLE, - N_LUNARIZER, - N_SOLARIZER, RUSTED_SWORD, RUSTED_SHIELD, ICY_REINS_OF_UNITY, SHADOW_REINS_OF_UNITY, - WELLSPRING_MASK, - HEARTHFLAME_MASK, - CORNERSTONE_MASK, + ULTRANECROZIUM_Z, + + SHARP_METEORITE = 100, + HARD_METEORITE, + SMOOTH_METEORITE, + GRACIDEA, SHOCK_DRIVE, BURN_DRIVE, CHILL_DRIVE, DOUSE_DRIVE, - ULTRANECROZIUM_Z, - - FIST_PLATE = 100, + N_SOLARIZER, + N_LUNARIZER, + WELLSPRING_MASK, + HEARTHFLAME_MASK, + CORNERSTONE_MASK, + FIST_PLATE, SKY_PLATE, TOXIC_PLATE, EARTH_PLATE, @@ -129,7 +129,7 @@ export enum FormChangeItem { DRAGON_MEMORY, DARK_MEMORY, FAIRY_MEMORY, - BLANK_MEMORY // TODO: Find a potential use for this + NORMAL_MEMORY // TODO: Find a potential use for this } export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean; diff --git a/src/locales/de/modifier-type.json b/src/locales/de/modifier-type.json index c9927636c2a..9298a78614a 100644 --- a/src/locales/de/modifier-type.json +++ b/src/locales/de/modifier-type.json @@ -604,6 +604,6 @@ "DRAGON_MEMORY": "Drachen-Disc", "DARK_MEMORY": "Unlicht-Disc", "FAIRY_MEMORY": "Feen-Disc", - "BLANK_MEMORY": "Leere-Disc" + "NORMAL_MEMORY": "Normal-Disc" } } \ No newline at end of file diff --git a/src/locales/en/modifier-type.json b/src/locales/en/modifier-type.json index ed1ef900878..15b9fb8f46d 100644 --- a/src/locales/en/modifier-type.json +++ b/src/locales/en/modifier-type.json @@ -450,6 +450,6 @@ "DRAGON_MEMORY": "Dragon Memory", "DARK_MEMORY": "Dark Memory", "FAIRY_MEMORY": "Fairy Memory", - "BLANK_MEMORY": "Blank Memory" + "NORMAL_MEMORY": "Normal Memory" } } diff --git a/src/locales/es/modifier-type.json b/src/locales/es/modifier-type.json index 95325788bf4..9c36b8da767 100644 --- a/src/locales/es/modifier-type.json +++ b/src/locales/es/modifier-type.json @@ -604,6 +604,6 @@ "DRAGON_MEMORY": "Disco dragón", "DARK_MEMORY": "Disco siniestro", "FAIRY_MEMORY": "Disco hada", - "BLANK_MEMORY": "Disco en blanco" + "NORMAL_MEMORY": "Disco normal" } } diff --git a/src/locales/fr/modifier-type.json b/src/locales/fr/modifier-type.json index 6d5cfb098ef..935deeb5f62 100644 --- a/src/locales/fr/modifier-type.json +++ b/src/locales/fr/modifier-type.json @@ -450,6 +450,6 @@ "DRAGON_MEMORY": "ROM Dragon", "DARK_MEMORY": "ROM Ténèbres", "FAIRY_MEMORY": "ROM Fée", - "BLANK_MEMORY": "ROM Vierge" + "NORMAL_MEMORY": "ROM Normal" } } diff --git a/src/locales/it/modifier-type.json b/src/locales/it/modifier-type.json index f5cee70bbc9..b466e5bb9a3 100644 --- a/src/locales/it/modifier-type.json +++ b/src/locales/it/modifier-type.json @@ -604,6 +604,6 @@ "DRAGON_MEMORY": "ROM Drago", "DARK_MEMORY": "ROM Buio", "FAIRY_MEMORY": "ROM Folletto", - "BLANK_MEMORY": "ROM Vuota" + "NORMAL_MEMORY": "ROM Normale" } } \ No newline at end of file diff --git a/src/locales/ko/modifier-type.json b/src/locales/ko/modifier-type.json index 3d282dfffd1..a5b3405b33f 100644 --- a/src/locales/ko/modifier-type.json +++ b/src/locales/ko/modifier-type.json @@ -604,6 +604,6 @@ "DRAGON_MEMORY": "드래곤메모리", "DARK_MEMORY": "다크메모리", "FAIRY_MEMORY": "페어리메모리", - "BLANK_MEMORY": "빈메모리" + "NORMAL_MEMORY": "일반메모리" } } \ No newline at end of file diff --git a/src/locales/pt_BR/modifier-type.json b/src/locales/pt_BR/modifier-type.json index 1787360b51e..b02281a53b8 100644 --- a/src/locales/pt_BR/modifier-type.json +++ b/src/locales/pt_BR/modifier-type.json @@ -604,6 +604,6 @@ "DRAGON_MEMORY": "Memória do Dragão", "DARK_MEMORY": "Memória Sombria", "FAIRY_MEMORY": "Memória de Fada", - "BLANK_MEMORY": "Memória Vazia" + "NORMAL_MEMORY": "Memória Normal" } } diff --git a/src/locales/zh_CN/modifier-type.json b/src/locales/zh_CN/modifier-type.json index e9172985092..4a982b77cea 100644 --- a/src/locales/zh_CN/modifier-type.json +++ b/src/locales/zh_CN/modifier-type.json @@ -604,6 +604,6 @@ "DRAGON_MEMORY": "龙存储碟", "DARK_MEMORY": "黑暗存储碟", "FAIRY_MEMORY": "妖精存储碟", - "BLANK_MEMORY": "空白存储碟" + "NORMAL_MEMORY": "一般存储碟" } } \ No newline at end of file diff --git a/src/locales/zh_TW/modifier-type.json b/src/locales/zh_TW/modifier-type.json index a7ddaea077b..847ede7001e 100644 --- a/src/locales/zh_TW/modifier-type.json +++ b/src/locales/zh_TW/modifier-type.json @@ -600,6 +600,6 @@ "DRAGON_MEMORY": "龍記憶碟", "DARK_MEMORY": "黑暗記憶碟", "FAIRY_MEMORY": "妖精記憶碟", - "BLANK_MEMORY": "空白記憶碟" + "NORMAL_MEMORY": "一般記憶碟" } } \ No newline at end of file diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 3afeb79ff2b..e693ca01052 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1043,7 +1043,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { } class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator { - constructor() { + constructor(rare: boolean) { super((party: Pokemon[], pregenArgs?: any[]) => { if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in FormChangeItem)) { return new FormChangeItemModifierType(pregenArgs[0] as FormChangeItem); @@ -1083,7 +1083,8 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator { } } return formChangeItemTriggers; - }).flat().flatMap(fc => fc.item))]; + }).flat()) + ].flat().flatMap(fc => fc.item).filter(i => (i && i < 100) === rare); // convert it into a set to remove duplicate values, which can appear when the same species with a potential form change is in the party. if (!formChangeItemPool.length) { @@ -1282,7 +1283,8 @@ export const modifierTypes = { EVOLUTION_ITEM: () => new EvolutionItemModifierTypeGenerator(false), RARE_EVOLUTION_ITEM: () => new EvolutionItemModifierTypeGenerator(true), - FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(), + FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(false), + RARE_FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(true), MEGA_BRACELET: () => new ModifierType("modifierType:ModifierType.MEGA_BRACELET", "mega_bracelet", (type, _args) => new Modifiers.MegaEvolutionAccessModifier(type)), DYNAMAX_BAND: () => new ModifierType("modifierType:ModifierType.DYNAMAX_BAND", "dynamax_band", (type, _args) => new Modifiers.GigantamaxAccessModifier(type)), @@ -1595,6 +1597,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.PP_MAX, 3), new WeightedModifierType(modifierTypes.MINT, 4), new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32), 32), + new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { if (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.EVIOLITE]) { @@ -1661,7 +1664,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), new WeightedModifierType(modifierTypes.LOCK_CAPSULE, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), - new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), + new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36), new WeightedModifierType(modifierTypes.DYNAMAX_BAND, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36), new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, 3), From 634bfb7900b66d9be28b801d685efacc3d8762cd Mon Sep 17 00:00:00 2001 From: chaosgrimmon <31082757+chaosgrimmon@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:16:47 -0400 Subject: [PATCH 037/108] [Sprite] Pelipper variant cleanup (#3969) * [Sprite] Sprite fix Pelipper front * [Sprite] Map epic Pelipper front * Delete public/images/pokemon/variant/279_1.png * Delete public/images/pokemon/variant/279_2.png * Delete public/images/pokemon/variant/279_3.png * Delete public/images/pokemon/variant/279_1.json * Delete public/images/pokemon/variant/279_2.json * Delete public/images/pokemon/variant/279_3.json * [Sprite] Use epic Pelipper palette * [Sprite] Index base shiny Pelipper front --- public/images/pokemon/279.png | Bin 29086 -> 12765 bytes public/images/pokemon/shiny/279.png | Bin 29364 -> 12770 bytes public/images/pokemon/variant/279.json | 53 +- public/images/pokemon/variant/279_1.json | 3611 ----------------- public/images/pokemon/variant/279_1.png | Bin 30209 -> 0 bytes public/images/pokemon/variant/279_2.json | 3611 ----------------- public/images/pokemon/variant/279_2.png | Bin 30073 -> 0 bytes public/images/pokemon/variant/279_3.json | 3611 ----------------- public/images/pokemon/variant/279_3.png | Bin 30216 -> 0 bytes .../images/pokemon/variant/_masterlist.json | 2 +- 10 files changed, 33 insertions(+), 10855 deletions(-) delete mode 100644 public/images/pokemon/variant/279_1.json delete mode 100644 public/images/pokemon/variant/279_1.png delete mode 100644 public/images/pokemon/variant/279_2.json delete mode 100644 public/images/pokemon/variant/279_2.png delete mode 100644 public/images/pokemon/variant/279_3.json delete mode 100644 public/images/pokemon/variant/279_3.png diff --git a/public/images/pokemon/279.png b/public/images/pokemon/279.png index a50bf952433e9939b30fe5c2f998ac332ef165bd..e7243586515191f5102dc93b9fd82302532b21d1 100644 GIT binary patch delta 10202 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 diff --git a/public/images/pokemon/shiny/279.png b/public/images/pokemon/shiny/279.png index c43634e8602852f2e741820d757eb417a8eb6ba0..26dba5479139e2d149c388db9ccb05d307eb1142 100644 GIT binary patch delta 10207 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 V;9YAG@b8y=in3}lwNmDx{|AK9>m~pI delta 26932 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 JW!lcS{||}@Hn{)* diff --git a/public/images/pokemon/variant/279.json b/public/images/pokemon/variant/279.json index a7b97ac3161..5c193f7939b 100644 --- a/public/images/pokemon/variant/279.json +++ b/public/images/pokemon/variant/279.json @@ -1,38 +1,49 @@ { "0": { + "bc4524": "af5457", "31638c": "324a26", - "101010": "101010", "5aa5ce": "40683c", - "a5e6ff": "b6d9ac", - "7bceef": "789c6e", - "ced6ef": "c09e99", - "737384": "774644", + "ce4252": "af2c4f", "ffffff": "f1dcd8", "8c4231": "420b0c", - "ffde4a": "c66f68", - "c57b31": "551917", "ffffad": "f4bfb6", + "ffde4a": "c66f68", + "7bceef": "789c6e", + "a5e6ff": "b6d9ac", + "737384": "774644", "f7b531": "af5457", - "ce4252": "af2c4f", - "bc4524": "af5457", - "00e5e7": "00e5e7" + "c57b31": "551917", + "ced6ef": "c09e99" }, "1": { + "bc4524": "3d325e", "31638c": "143a72", - "101010": "101010", "5aa5ce": "4060bc", - "a5e6ff": "b4b3ff", - "7bceef": "657ddf", - "ced6ef": "a8b5dd", - "737384": "737384", + "ce4252": "b75558", "ffffff": "e5ecff", "8c4231": "17103f", - "ffde4a": "534e72", - "c57b31": "2a1f50", "ffffad": "87879b", + "ffde4a": "534e72", + "7bceef": "657ddf", + "a5e6ff": "b4b3ff", "f7b531": "3d325e", - "ce4252": "b75558", - "bc4524": "3d325e", - "00e5e7": "00e5e7" + "c57b31": "2a1f50", + "ced6ef": "a8b5dd" + }, + "2": { + "ce4252": "215991", + "ffde4a": "f16f40", + "ffffad": "ffb274", + "737384": "884c43", + "c57b31": "761c03", + "7bceef": "be3d2f", + "8c4231": "5a0700", + "5aa5ce": "892722", + "8c4232": "761c03", + "ffffff": "f5e1d1", + "a5e6ff": "dd533a", + "f7b531": "bc4524", + "ced6ef": "d19e92", + "31638c": "610f0e" } -} \ No newline at end of file +} diff --git a/public/images/pokemon/variant/279_1.json b/public/images/pokemon/variant/279_1.json deleted file mode 100644 index 3f884262e14..00000000000 --- a/public/images/pokemon/variant/279_1.json +++ /dev/null @@ -1,3611 +0,0 @@ -{ - "textures": [ - { - "image": "279_1.png", - "format": "RGBA8888", - "size": { - "w": 422, - "h": 422 - }, - "scale": 1, - "frames": [ - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0131.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 96, - "h": 53 - }, - "frame": { - "x": 0, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0168.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 96, - "h": 53 - }, - "frame": { - "x": 96, - "y": 0, - "w": 96, - "h": 53 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0161.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 96, - "h": 52 - }, - "frame": { - "x": 192, - "y": 0, - "w": 96, - "h": 52 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 94, - "h": 53 - }, - "frame": { - "x": 288, - "y": 0, - "w": 94, - "h": 53 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0132.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0133.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0134.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 93, - "h": 53 - }, - "frame": { - "x": 192, - "y": 52, - "w": 93, - "h": 53 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0135.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0136.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 93, - "h": 53 - }, - "frame": { - "x": 285, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0137.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0138.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0139.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0140.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0141.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0142.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 53, - "w": 93, - "h": 53 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0143.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0144.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 186, - "y": 105, - "w": 93, - "h": 53 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0166.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 7, - "w": 93, - "h": 53 - }, - "frame": { - "x": 279, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0167.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 0, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 93, - "h": 53 - }, - "frame": { - "x": 93, - "y": 106, - "w": 93, - "h": 53 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 9, - "w": 82, - "h": 60 - }, - "frame": { - "x": 186, - "y": 158, - "w": 82, - "h": 60 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 94, - "h": 52 - }, - "frame": { - "x": 268, - "y": 159, - "w": 94, - "h": 52 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0128.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 57, - "h": 68 - }, - "frame": { - "x": 362, - "y": 159, - "w": 57, - "h": 68 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0165.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 81, - "h": 60 - }, - "frame": { - "x": 268, - "y": 211, - "w": 81, - "h": 60 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0162.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 68, - "h": 63 - }, - "frame": { - "x": 349, - "y": 227, - "w": 68, - "h": 63 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 81, - "h": 60 - }, - "frame": { - "x": 0, - "y": 159, - "w": 81, - "h": 60 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0130.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 82, - "h": 59 - }, - "frame": { - "x": 81, - "y": 159, - "w": 82, - "h": 59 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0145.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0146.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0147.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 93, - "h": 52 - }, - "frame": { - "x": 81, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0169.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 68, - "h": 63 - }, - "frame": { - "x": 0, - "y": 219, - "w": 68, - "h": 63 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0148.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0149.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0150.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 7, - "w": 93, - "h": 52 - }, - "frame": { - "x": 174, - "y": 218, - "w": 93, - "h": 52 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0151.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0152.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 8, - "w": 93, - "h": 52 - }, - "frame": { - "x": 68, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 67, - "h": 63 - }, - "frame": { - "x": 0, - "y": 282, - "w": 67, - "h": 63 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0153.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0154.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0155.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 9, - "w": 93, - "h": 52 - }, - "frame": { - "x": 161, - "y": 270, - "w": 93, - "h": 52 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0156.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0157.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0158.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 10, - "w": 93, - "h": 52 - }, - "frame": { - "x": 254, - "y": 271, - "w": 93, - "h": 52 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0127.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0170.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 3, - "w": 63, - "h": 66 - }, - "frame": { - "x": 347, - "y": 290, - "w": 63, - "h": 66 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0159.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0160.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 2, - "y": 11, - "w": 93, - "h": 52 - }, - "frame": { - "x": 67, - "y": 322, - "w": 93, - "h": 52 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0171.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 1, - "w": 63, - "h": 66 - }, - "frame": { - "x": 0, - "y": 345, - "w": 63, - "h": 66 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 63, - "h": 66 - }, - "frame": { - "x": 160, - "y": 322, - "w": 63, - "h": 66 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0129.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 0, - "w": 67, - "h": 62 - }, - "frame": { - "x": 223, - "y": 323, - "w": 67, - "h": 62 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0163.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 9, - "w": 57, - "h": 67 - }, - "frame": { - "x": 290, - "y": 323, - "w": 57, - "h": 67 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - }, - { - "filename": "0164.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 96, - "h": 76 - }, - "spriteSourceSize": { - "x": 14, - "y": 7, - "w": 62, - "h": 66 - }, - "frame": { - "x": 347, - "y": 356, - "w": 62, - "h": 66 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:37124082e206aa3cddb045440ab1e9cf:8d64288649efa3066ed9b372190e868f:b1d275d5bba320dd22d2f2c7e56d52ec$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/279_1.png b/public/images/pokemon/variant/279_1.png deleted file mode 100644 index f2aa7b8ac3a88f0b6bdd32ccabe9a5e548c405cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30209 zcmagFbyQW)-#@x(5e4aP=|(yPq`ONg=?>vYw@8CXcOObXx=RI=jzgy)br3uU0~PT#}P>^0hpAgBDU@fEwnz}a@-!%aBG7d0d7kVl0^L_c@>qvB)j zEU-`>9RX<0z`A=w!wZs21=LK`fcZG*Ou|dju6A6yt84aYsklDiK0l?+n1K(_%81)J zzFaUNC;cHJX_&**qWL4ESwMY^S7nUBIE>#Q+4Gq5Rls?s4W8%;|2qltVb8G*o1