diff --git a/.github/README.md b/.github/README.md index ad763cf7d4..0f240e88a9 100644 --- a/.github/README.md +++ b/.github/README.md @@ -64,14 +64,14 @@ pokeemerald-expansion是基于pret的[pokeemerald](https://github.com/pret/pokee - 检查你的当前版本。 - 你可以在调试菜单的`Utilities -> Expansion Version`选项中检查。 - 如果该选项不可用,你可能拥有的是1.6.2或更早的版本。在那种情况下,请查看[更改日志](docs/CHANGELOG.md)以确定你的版本,基于你仓库中可用的功能。 -- ***重要提示***:如果你落后了几个版本,我们建议你一次更新一个小版本,而不是直接跳到最新的补丁版本(例如,1.5.3 -> 1.6.2 -> 1.7.4等等。查看在线文档网站](https://rh-hideout.github.io/pokeemerald-expansion/CHANGELOG.html)以查看每个步骤的最新版本。) +- ***重要提示***:如果你落后了几个版本,我们建议你一次更新一个小版本,而不是直接跳到最新的补丁版本(例如,1.5.3 -> 1.6.2 -> 1.7.4等等。[查看在线文档网站](https://rh-hideout.github.io/pokeemerald-expansion/CHANGELOG.html)以查看每个步骤的最新版本。) - 一旦你设置了远程仓库,请运行命令`git pull RHH expansion/X.Y.Z`,将X、Y和Z替换为你想要更新到的相应版本的数字(例如,要更新到1.11.1,请使用`git pull RHH expansion/1.11.1`)。 - ***重要提示***:如果你落后了几个版本,我们建议你一次更新一个小版本,而不是直接跳到最新的补丁版本(例如,1.5.3 -> 1.6.2 -> 1.7.4等等) - 另外,你可以更新到扩展的未发布版本。 - ***master(稳定版)***:它包含将在下一个补丁版本中发布的***bug修复***。要合并,请使用`git pull RHH master`。 - ***upcoming(不稳定版,可能存在bug)***:它包含将在下一个小版本中发布的***功能***。要合并,请使用`git pull RHH upcoming`。 -### 请考虑在你的项目中归功于整个贡献者名单](https://github.com/rh-hideout/pokeemerald-expansion/wiki/Credits),因为他们都为开发这个项目付出了辛勤的努力 :) +### 请考虑在你的项目中归功于整个[贡献者名单](https://github.com/rh-hideout/pokeemerald-expansion/wiki/Credits),因为他们都为开发这个项目付出了辛勤的努力 :) ## 谁在维护这个项目? @@ -246,4 +246,4 @@ pokeemerald-expansion是基于pret的[pokeemerald](https://github.com/pret/pokee - 《黑2/白2》及以后的驱虫喷雾系统,也支持《Let's Go!皮卡丘/伊布》中的香水。 - 第六世代及以后的努力值上限。 - 包含了pret的所有错误修复。 - - 修复了地图上的下雪天气效果。 \ No newline at end of file + - 修复了地图上的下雪天气效果。 diff --git a/Makefile b/Makefile index f2128cc25d..aaa8e9db4a 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ endif PREFIX := arm-none-eabi- OBJCOPY := $(PREFIX)objcopy OBJDUMP := $(PREFIX)objdump +READELF := $(PREFIX)readelf AS := $(PREFIX)as LD := $(PREFIX)ld @@ -86,6 +87,7 @@ endif ELF := $(ROM:.gba=.elf) MAP := $(ROM:.gba=.map) SYM := $(ROM:.gba=.sym) +NOCASHSYM := $(ROM:.gba=.sym) # Commonly used directories C_SUBDIR = src @@ -294,6 +296,8 @@ endif syms: $(SYM) +nosyms: $(NOCASHSYM) + clean: tidy clean-tools clean-check-tools clean-generated clean-assets @$(MAKE) clean -C libagbsyscall @@ -465,3 +469,7 @@ $(ROM): $(ELF) # Symbol file (`make syms`) $(SYM): $(ELF) $(OBJDUMP) -t $< | sort -u | grep -E "^0[2389]" | $(PERL) -p -e 's/^(\w{8}) (\w).{6} \S+\t(\w{8}) (\S+)$$/\1 \2 \3 \4/g' > $@ + +# Symbol file (`make nosyms) +$(NOCASHSYM): $(ELF) + $(READELF) -Ws $< | tail -n +4 | awk '$$4 !~/(FILE|NOTYPE)/ && $$8 !~/^(\$$|\.)/ {print $$2,$$8}' | sort -k 1 | awk '!seen[$$1]++' > $@ diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a00654d6c7..53ed13f9b6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8399,7 +8399,7 @@ BattleScript_BattlerAbilityStatRaiseOnSwitchIn:: BattleScript_ScriptingAbilityStatRaise:: copybyte gBattlerAbility, sBATTLER call BattleScript_AbilityPopUp - copybyte sSAVED_DMG, gBattlerAttacker + saveattacker copybyte gBattlerAttacker, sBATTLER statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN, NULL setgraphicalstatchangevalues @@ -8407,7 +8407,7 @@ BattleScript_ScriptingAbilityStatRaise:: waitanimation printstring STRINGID_ATTACKERABILITYSTATRAISE waitmessage B_WAIT_TIME_LONG - copybyte gBattlerAttacker, sSAVED_DMG + restoreattacker return BattleScript_WeakArmorActivates:: diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 572d076fab..27e3706e3f 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -265,13 +265,12 @@ BattleScript_ActionWallyThrow: end2 BattleScript_TrainerASlideMsgRet:: - handletrainerslidemsg BS_SCRIPTING, 0 trainerslidein BS_OPPONENT1 - handletrainerslidemsg BS_SCRIPTING, 1 + handletrainerslidemsg BS_SCRIPTING, PRINT_SLIDE_MESSAGE waitstate trainerslideout BS_OPPONENT1 waitstate - handletrainerslidemsg BS_SCRIPTING, 2 + handletrainerslidemsg BS_SCRIPTING, RESTORE_BATTLER_SLIDE_CONTROL return BattleScript_TrainerASlideMsgEnd2:: @@ -279,13 +278,12 @@ BattleScript_TrainerASlideMsgEnd2:: end2 BattleScript_TrainerBSlideMsgRet:: - handletrainerslidemsg BS_SCRIPTING, 0 trainerslidein BS_OPPONENT2 - handletrainerslidemsg BS_SCRIPTING, 1 + handletrainerslidemsg BS_SCRIPTING, PRINT_SLIDE_MESSAGE waitstate trainerslideout BS_OPPONENT2 waitstate - handletrainerslidemsg BS_SCRIPTING, 2 + handletrainerslidemsg BS_SCRIPTING, RESTORE_BATTLER_SLIDE_CONTROL return BattleScript_TrainerBSlideMsgEnd2:: diff --git a/docs/install/windows/WSL_ZH.md b/docs/install/windows/WSL_ZH.md index 991de2a25c..8fc5ede07b 100644 --- a/docs/install/windows/WSL_ZH.md +++ b/docs/install/windows/WSL_ZH.md @@ -113,4 +113,40 @@ mkdir decomps ```bash cd ~/decomps +``` + +## WSL更新或安装依赖时错误解决 + +更新WSL安装依赖项时如果出现以下错误提示 + +```console +E: Sub-process /usr/bin/dpkg returned an error code (1) +``` + +可先执行以下命令 + +```bash +sudo mv /var/lib/dpkg/info /var/lib/dpkg/info_old +sudo mkdir /var/lib/dpkg/info +``` + +等待命令执行完毕后再次运行更新WSL安装依赖项命令。更新或安装完毕后运行以下命令以恢复pkg信息 + +```bash +sudo mv /var/lib/dpkg/info_old/* /var/lib/dpkg/info/ +sudo rmdir /var/lib/dpkg/info_old +``` + +如果出现以下提示,并列出可移除的包名(以libllvm17t64为例) + +```console +The following package was automatically installed and is no longer required: + libllvm17t64 +``` + +可执行以下命令解决该提示 + +```bash +sudo apt autoremove +sudo apt install libllvm17t64 ``` \ No newline at end of file diff --git a/include/battle.h b/include/battle.h index 4620112194..8874857efb 100644 --- a/include/battle.h +++ b/include/battle.h @@ -736,19 +736,19 @@ struct BattleStruct u8 debugBattler; u8 magnitudeBasePower; u8 presentBasePower; - u8 roostTypes[MAX_BATTLERS_COUNT][2]; + u8 roostTypes[MAX_BATTLERS_COUNT][NUM_BATTLE_SIDES]; u8 savedBattlerTarget[5]; u8 savedBattlerAttacker[5]; u8 savedTargetCount:4; u8 savedAttackerCount:4; bool8 ateBoost[MAX_BATTLERS_COUNT]; - u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler + u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][NUM_BATTLE_SIDES]; // two per battler struct ZMoveData zmove; struct DynamaxData dynamax; struct BattleGimmickData gimmick; const u8 *trainerSlideMsg; enum BattleIntroStates introState:8; - u8 ateBerry[2]; // array id determined by side, each party pokemon as bit + u8 ateBerry[NUM_BATTLE_SIDES]; // array id determined by side, each party pokemon as bit u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct u16 tracedAbility[MAX_BATTLERS_COUNT]; @@ -804,13 +804,14 @@ struct BattleStruct u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side u8 monCausingSleepClause[NUM_BATTLE_SIDES]; // Stores which pokemon on a given side is causing Sleep Clause to be active as the mon's index in the party u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects + s16 savedcheekPouchDamage; // Cheek Pouch can happen in the middle of an attack execution so we need to store the current dmg u8 cheekPouchActivated:1; u8 padding2:3; u8 pursuitStoredSwitch; // Stored id for the Pursuit target's switch s32 battlerExpReward; u16 prevTurnSpecies[MAX_BATTLERS_COUNT]; // Stores species the AI has in play at start of turn - s32 moveDamage[MAX_BATTLERS_COUNT]; - s32 critChance[MAX_BATTLERS_COUNT]; + s16 moveDamage[MAX_BATTLERS_COUNT]; + s16 critChance[MAX_BATTLERS_COUNT]; u16 moveResultFlags[MAX_BATTLERS_COUNT]; u8 missStringId[MAX_BATTLERS_COUNT]; u8 noResultString[MAX_BATTLERS_COUNT]; @@ -821,7 +822,6 @@ struct BattleStruct u8 numSpreadTargets:2; u8 bypassMoldBreakerChecks:1; // for ABILITYEFFECT_IMMUNITY u8 noTargetPresent:1; - u8 usedEjectItem; u8 usedMicleBerry; struct MessageStatus slideMessageStatus; u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT]; diff --git a/include/constants/battle.h b/include/constants/battle.h index 3bc59e8773..86226013d5 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -605,4 +605,10 @@ enum StartingStatus STARTING_STATUS_SWAMP_OPPONENT, }; +enum SlideMsgStates +{ + PRINT_SLIDE_MESSAGE, + RESTORE_BATTLER_SLIDE_CONTROL, +}; + #endif // GUARD_CONSTANTS_BATTLE_H diff --git a/python_tools/test.py b/python_tools/test.py index db53d85da8..3da0158781 100644 --- a/python_tools/test.py +++ b/python_tools/test.py @@ -1,3 +1,4 @@ +#!/usr/bin/python python3 # 原始字符串 text = "全副武装的样子。\n即使是极巨化宝可梦的\n攻击也能轻易抵挡。" diff --git a/python_tools/test_follow.py b/python_tools/test_follow.py index a37a02974c..07b1f309bd 100644 --- a/python_tools/test_follow.py +++ b/python_tools/test_follow.py @@ -1,9 +1,19 @@ +#!/usr/bin/python python3 +import os import openpyxl import re +# 获取py文件所在文件夹绝对路径 +pydir = os.path.dirname(os.path.realpath(__file__)) + +#获取excel相对py文件路径 +excel_path = os.path.join(pydir, "src", "精灵跟随.xlsx") + +# 获取C文件所在文件夹相对py文件路径 +cdir = os.path.join(os.path.dirname(pydir), "src") + # 文件路径 -excel_path = r"c:\Users\Nox\Documents\GitHub\pokeemerald-expansion-Chinese\python_tools\src\精灵跟随.xlsx" -c_file_path = r"c:\Users\Nox\Documents\GitHub\pokeemerald-expansion-Chinese\src\follower_helper.c" +c_file_path = os.path.join(cdir, "follower_helper.c") # 读取 Excel 文件 wb = openpyxl.load_workbook(excel_path) diff --git a/python_tools/test_string_extract.py b/python_tools/test_string_extract.py index 1d26e42607..8ad841ac67 100644 --- a/python_tools/test_string_extract.py +++ b/python_tools/test_string_extract.py @@ -1,8 +1,16 @@ +#!/usr/bin/python python3 +import os import re +# 获取py文件所在文件夹绝对路径 +pydir = os.path.dirname(os.path.realpath(__file__)) + +# 获取C文件所在文件夹相对py文件路径 +cdir = os.path.join(os.path.dirname(pydir), "src") + # 文件路径 -input_file = r"c:\Users\Nox\Documents\GitHub\pokeemerald-expansion-Chinese\src\strings.c" -output_file = r"c:\Users\Nox\Documents\GitHub\pokeemerald-expansion-Chinese\extracted_strings.txt" +input_file = os.path.join(cdir, "strings.c") +output_file = os.path.join(os.path.dirname(pydir), "extracted_strings.txt") # 正则表达式匹配变量名和 _("...") 之间的内容 pattern = re.compile(r'const\s+u8\s+(\w+)\[\]\s*=\s*_\("([^"]*)"\)') diff --git a/python_tools/translate_battle_message.py b/python_tools/translate_battle_message.py index 9b895933ba..f41b913609 100644 --- a/python_tools/translate_battle_message.py +++ b/python_tools/translate_battle_message.py @@ -1,10 +1,13 @@ +#!/usr/bin/python python3 import re import os from openpyxl import load_workbook +pydir = os.path.dirname(os.path.abspath(__file__)) + # 文件路径 -c_file_path = os.path.dirname(os.path.abspath(__file__))+"/../src/battle_message.c" -xlsx_file_path = os.path.dirname(os.path.abspath(__file__))+"/src/战斗文本.xlsx" +c_file_path = os.path.join(os.path.dirname(pydir), "src", "battle_message.c") +xlsx_file_path = os.path.join(pydir, "src", "战斗文本.xlsx") # 读取 xlsx 文件并解析为字典 def load_translations(xlsx_file_path): diff --git a/python_tools/translate_debug.py b/python_tools/translate_debug.py index ac685988f8..8e7624d8d9 100644 --- a/python_tools/translate_debug.py +++ b/python_tools/translate_debug.py @@ -1,3 +1,4 @@ +#!/usr/bin/python python3 import re import openpyxl import os @@ -45,8 +46,8 @@ def replace_in_c_file(c_file, replacement_dict): # 主函数 def main(): current_folder = os.path.dirname(os.path.abspath(__file__)) - xlsx_file = current_folder+"/src/debug文本.xlsx" # Excel 文件路径 - c_file = current_folder+"/../src/battle_debug.c" # C 文件路径 + xlsx_file = os.path.join(current_folder, "src", "debug文本.xlsx") # Excel 文件路径 + c_file = os.path.join(os.path.dirname(current_folder), "src", "battle_debug.c") # C 文件路径 # 加载替换字典 replacement_dict = load_replacement_dict(xlsx_file) diff --git a/python_tools/translate_decoration.py b/python_tools/translate_decoration.py index 282f3ed249..1697847d53 100644 --- a/python_tools/translate_decoration.py +++ b/python_tools/translate_decoration.py @@ -1,11 +1,12 @@ +#!/usr/bin/python python3 import openpyxl import re import os # 文件路径 base_dir = os.path.dirname(os.path.abspath(__file__)) -h_file_path = os.path.join(base_dir, "../src/data/decoration/header.h") -xlsx_path = os.path.join(base_dir, "src/装饰物品.xlsx") +h_file_path = os.path.join(os.path.dirname(base_dir), "src", "data", "decoration", "header.h") +xlsx_path = os.path.join(base_dir, "src", "装饰物品.xlsx") # 加载Excel文件 wb = openpyxl.load_workbook(xlsx_path) diff --git a/python_tools/translate_decoration2.py b/python_tools/translate_decoration2.py index 2ea84296fc..906f1c91c6 100644 --- a/python_tools/translate_decoration2.py +++ b/python_tools/translate_decoration2.py @@ -1,10 +1,12 @@ +#!/usr/bin/python python3 import openpyxl import re import os # 文件路径 -h_file_path = os.path.dirname(os.path.abspath(__file__))+"/../src/data/decoration/description.h" -xlsx_path = os.path.dirname(os.path.abspath(__file__))+"/src/装饰物品.xlsx" +base_dir = os.path.dirname(os.path.abspath(__file__)) +h_file_path = os.path.join(os.path.dirname(base_dir), "src", "data", "decoration", "description.h") +xlsx_path = os.path.join(base_dir, "src", "装饰物品.xlsx") # 加载Excel文件 wb = openpyxl.load_workbook(xlsx_path) diff --git a/python_tools/translate_items.py b/python_tools/translate_items.py index 32d9f346c0..f7d59b2863 100644 --- a/python_tools/translate_items.py +++ b/python_tools/translate_items.py @@ -1,3 +1,4 @@ +#!/usr/bin/python python3 import os import re import pandas as pd @@ -60,13 +61,13 @@ def replace_item_info(content, df): return content def log(message): - with open(current_folder+"\log.txt", "a", encoding="utf-8") as log_file: + with open(os.path.join(current_folder, "log.txt"), "a", encoding="utf-8") as log_file: log_file.write(message + "\n") print(message) if __name__ == "__main__": - work_file = current_folder +"\..\src\data\items.h" - df = pd.read_excel(current_folder +r'\src\道具.xlsx') + work_file = os.path.join(os.path.dirname(current_folder), "src", "data", "items.h") + df = pd.read_excel(os.path.join(current_folder, "src", "道具.xlsx")) df.set_index('道具', inplace=True) diff --git a/python_tools/translate_pokemon.py b/python_tools/translate_pokemon.py index 58c98edbe0..f96ad05c85 100644 --- a/python_tools/translate_pokemon.py +++ b/python_tools/translate_pokemon.py @@ -1,3 +1,4 @@ +#!/usr/bin/python python3 import os import re import pandas as pd @@ -56,14 +57,13 @@ def replace_species_info(content, df): return content def log(message): - with open(current_folder+"\log.txt", "a", encoding="utf-8") as log_file: + with open(os.path.join(current_folder, "log.txt"), "a", encoding="utf-8") as log_file: log_file.write(message + "\n") print(message) if __name__ == "__main__": - - work_folder = current_folder +"\..\src\data\pokemon\species_info" - df = pd.read_excel(current_folder +r'\src\图鉴.xlsx') + work_folder = os.path.join(os.path.dirname(current_folder), "src", "data", "pokemon", "species_info") + df = pd.read_excel(os.path.join(current_folder, "src", "图鉴.xlsx")) df.set_index('name', inplace=True) for filename in os.listdir(work_folder): diff --git a/python_tools/translate_skills.py b/python_tools/translate_skills.py index 4e8a215fb5..a3b21ccf25 100644 --- a/python_tools/translate_skills.py +++ b/python_tools/translate_skills.py @@ -1,3 +1,4 @@ +#!/usr/bin/python python3 import os import re import pandas as pd @@ -60,14 +61,13 @@ def replace_move_info(content, df): return content def log(message): - with open(current_folder+"\log.txt", "a", encoding="utf-8") as log_file: + with open(os.path.join(current_folder, "log.txt"), "a", encoding="utf-8") as log_file: log_file.write(message + "\n") print(message) if __name__ == "__main__": - - work_file = current_folder +"\..\src\data\moves_info.h" - df = pd.read_excel(current_folder +r'\src\招式.xlsx') + work_file = os.path.join(os.path.dirname(current_folder), "src", "data", "moves_info.h") + df = pd.read_excel(os.path.join(current_folder, "src", "招式.xlsx")) df.set_index('招式', inplace=True) diff --git a/python_tools/translate_string.py b/python_tools/translate_string.py index 1783699a0a..5156ed61c3 100644 --- a/python_tools/translate_string.py +++ b/python_tools/translate_string.py @@ -1,4 +1,4 @@ - +#!/usr/bin/python python3 import os import re import pandas as pd @@ -44,13 +44,12 @@ def replace_move_info(content, df): return content def log(message): - with open(current_folder+"\log.txt", "a", encoding="utf-8") as log_file: + with open(os.path.join(current_folder, "log.txt"), "a", encoding="utf-8") as log_file: log_file.write(message + "\n") print(message) if __name__ == "__main__": - - work_file = current_folder +"\..\src\strings.c" - df = pd.read_excel(current_folder + r'\src\文本.xlsx') + work_file = os.path.join(os.path.dirname(current_folder), "src", "strings.c") + df = pd.read_excel(os.path.join(current_folder, "src", "文本.xlsx")) # 检查并清理重复的“变量名” df = df.drop_duplicates(subset='变量名', keep='first') diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3704eb1b01..03680aa5fc 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1677,6 +1677,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks if (effect == EFFECT_DRAGON_DARTS + && !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(battlerDef), gCurrentMove) && !recalcDragonDarts // So we don't jump back and forth between targets && CanTargetPartner(gBattlerAttacker, battlerDef) && !TargetFullyImmuneToCurrMove(gBattlerAttacker, BATTLE_PARTNER(battlerDef))) @@ -6496,8 +6497,7 @@ static void Cmd_moveend(void) else if (moveRecoil > 0 && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerAttacker) - && IsBattlerTurnDamaged(gBattlerTarget) - && gBattleScripting.savedDmg != 0) // Some checks may be redundant alongside this one + && IsBattlerTurnDamaged(gBattlerTarget)) { gBattleStruct->moveDamage[gBattlerAttacker] = max(1, gBattleScripting.savedDmg * max(1, moveRecoil) / 100); BattleScriptPushCursor(); @@ -7006,6 +7006,7 @@ static void Cmd_moveend(void) else { if (moveEffect == EFFECT_DRAGON_DARTS + && !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), gCurrentMove) && !(gBattleStruct->moveResultFlags[BATTLE_PARTNER(gBattlerTarget)] & MOVE_RESULT_MISSED) // didn't miss the other target && CanTargetPartner(gBattlerAttacker, gBattlerTarget) && !TargetFullyImmuneToCurrMove(gBattlerAttacker, BATTLE_PARTNER(gBattlerTarget))) @@ -7157,6 +7158,10 @@ static void Cmd_moveend(void) if (!(ejectPackBattlers & 1u << battler)) continue; + // Hit escape moves activate before Eject Pack for user + if (moveEffect == EFFECT_HIT_ESCAPE && gBattlerAttacker == battler) + continue; + gBattleScripting.battler = battler; gLastUsedItem = gBattleMons[battler].item; @@ -9048,7 +9053,7 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId) && !IsBattlerAtMaxHp(battler)) { gBattleStruct->cheekPouchActivated = TRUE; - gBattleScripting.savedDmg = gBattleStruct->moveDamage[battler]; + gBattleStruct->savedcheekPouchDamage = gBattleStruct->moveDamage[battler]; gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 3; if (gBattleStruct->moveDamage[battler] == 0) gBattleStruct->moveDamage[battler] = 1; @@ -9066,7 +9071,7 @@ static void TryRestoreDamageAfterCheeckPouch(u32 battler) { if (gBattleStruct->cheekPouchActivated) { - gBattleStruct->moveDamage[battler] = gBattleScripting.savedDmg; + gBattleStruct->moveDamage[battler] = gBattleStruct->savedcheekPouchDamage; gBattleStruct->cheekPouchActivated = FALSE; } } @@ -10602,6 +10607,7 @@ static void Cmd_various(void) if ((battlerAbility == ABILITY_MOXIE || battlerAbility == ABILITY_CHILLING_NEIGH || battlerAbility == ABILITY_AS_ONE_ICE_RIDER) + && IsBattlerAlive(battler) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -10625,6 +10631,7 @@ static void Cmd_various(void) if ((battlerAbility == ABILITY_GRIM_NEIGH || battlerAbility == ABILITY_AS_ONE_SHADOW_RIDER) + && IsBattlerAlive(battler) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -10663,7 +10670,8 @@ static void Cmd_various(void) VARIOUS_ARGS(); i = GetHighestStatId(battler); - if (GetBattlerAbility(battler) == ABILITY_BEAST_BOOST + if (IsBattlerAlive(battler) + && GetBattlerAbility(battler) == ABILITY_BEAST_BOOST && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, i, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -10701,6 +10709,7 @@ static void Cmd_various(void) { VARIOUS_ARGS(); if (GetMoveEffect(gCurrentMove) == EFFECT_FELL_STINGER + && IsBattlerAlive(gBattlerAttacker) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -11119,21 +11128,13 @@ static void Cmd_various(void) case VARIOUS_HANDLE_TRAINER_SLIDE_MSG: { VARIOUS_ARGS(u8 case_); - if (cmd->case_ == 0) - { - // Save sprite IDs, because trainer slide in will overwrite gBattlerSpriteIds variable. - gBattleScripting.savedDmg = (gBattlerSpriteIds[battler] & 0xFF) | (gBattlerSpriteIds[BATTLE_PARTNER(battler)] << 8); - } - else if (cmd->case_ == 1) + if (cmd->case_ == PRINT_SLIDE_MESSAGE) { BtlController_EmitPrintString(battler, BUFFER_A, STRINGID_TRAINERSLIDE); MarkBattlerForControllerExec(battler); } - else + else if (cmd->case_ == RESTORE_BATTLER_SLIDE_CONTROL) { - gBattlerSpriteIds[BATTLE_PARTNER(battler)] = gBattleScripting.savedDmg >> 8; - gBattlerSpriteIds[battler] = gBattleScripting.savedDmg & 0xFF; - gBattleScripting.savedDmg = 0; if (IsBattlerAlive(battler)) { SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species); diff --git a/src/battle_util.c b/src/battle_util.c index 72d203609e..9bf82b0670 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -227,15 +227,28 @@ static bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef, u32 a return FALSE; } +static inline bool32 IsDragonDartsSecondHit(u32 effect) +{ + if (effect != EFFECT_DRAGON_DARTS) + return FALSE; + + if (gMultiHitCounter == 1) + return TRUE; + + return FALSE; +} + bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) { u32 ability = GetBattlerAbility(battlerAtk); u32 effect = GetMoveEffect(move); if (gSideTimers[defSide].followmeTimer == 0 - || !IsBattlerAlive(gSideTimers[defSide].followmeTarget) - || effect == EFFECT_SNIPE_SHOT || effect == EFFECT_SKY_DROP - || ability == ABILITY_PROPELLER_TAIL || ability == ABILITY_STALWART) + || (!IsBattlerAlive(gSideTimers[defSide].followmeTarget) && !IsDragonDartsSecondHit(effect)) + || effect == EFFECT_SNIPE_SHOT + || effect == EFFECT_SKY_DROP + || ability == ABILITY_PROPELLER_TAIL + || ability == ABILITY_STALWART) return FALSE; if (effect == EFFECT_PURSUIT && IsPursuitTargetSet()) @@ -3582,6 +3595,9 @@ static void CancellerWeatherPrimal(u32 *effect) } if (*effect == 1) { + gBattleScripting.moveEffect = 0; + gProtectStructs[gBattlerAttacker].chargingTurn = FALSE; + CancelMultiTurnMoves(gBattlerAttacker); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; @@ -3696,6 +3712,7 @@ static void CancellerMultihitMoves(u32 *effect) gMultiHitCounter = GetMoveStrikeCount(gCurrentMove); if (GetMoveEffect(gCurrentMove) == EFFECT_DRAGON_DARTS + && !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), gCurrentMove) && CanTargetPartner(gBattlerAttacker, gBattlerTarget) && TargetFullyImmuneToCurrMove(gBattlerAttacker, gBattlerTarget)) gBattlerTarget = BATTLE_PARTNER(gBattlerTarget); @@ -7355,6 +7372,7 @@ u32 RestoreWhiteHerbStats(u32 battler) } if (effect != 0) { + gLastUsedItem = gBattleMons[battler].item; gBattleScripting.battler = battler; gPotentialItemEffectBattler = battler; } @@ -7679,10 +7697,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) case HOLD_EFFECT_RESTORE_STATS: effect = RestoreWhiteHerbStats(battler); if (effect != 0) - { - gBattlerAttacker = battler; BattleScriptExecute(BattleScript_WhiteHerbEnd2); - } break; case HOLD_EFFECT_CONFUSE_SPICY: if (B_BERRIES_INSTANT >= GEN_4) @@ -7898,10 +7913,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) case HOLD_EFFECT_RESTORE_STATS: effect = RestoreWhiteHerbStats(battler); if (effect != 0) - { - gBattlerAttacker = battler; BattleScriptExecute(BattleScript_WhiteHerbEnd2); - } break; case HOLD_EFFECT_BLACK_SLUDGE: if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) @@ -8190,7 +8202,8 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) break; case HOLD_EFFECT_LIFE_ORB: if (IsBattlerAlive(gBattlerAttacker) - && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveDamage[gBattlerTarget]) // Needs the second check in case of Substitute + && !IsBattleMoveStatus(gCurrentMove) + && (IsBattlerTurnDamaged(gBattlerTarget) || !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) // Needs the second check in case of Substitute && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD && !gProtectStructs[gBattlerAttacker].confusionSelfDmg @@ -8311,7 +8324,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) effect = TrySetEnigmaBerry(battler); break; case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move - if (IsBattlerAlive(battler) + if (IsBattlerAlive(gBattlerAttacker) && IsBattlerTurnDamaged(gBattlerTarget) && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && IsBattleMovePhysical(gCurrentMove) @@ -8331,7 +8344,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) } break; case HOLD_EFFECT_ROWAP_BERRY: // consume and damage attacker if used special move - if (IsBattlerAlive(battler) + if (IsBattlerAlive(gBattlerAttacker) && IsBattlerTurnDamaged(gBattlerTarget) && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && IsBattleMoveSpecial(gCurrentMove) diff --git a/src/data/pokemon/egg_moves.h b/src/data/pokemon/egg_moves.h index 23c233de58..392d2a0549 100644 --- a/src/data/pokemon/egg_moves.h +++ b/src/data/pokemon/egg_moves.h @@ -4278,6 +4278,13 @@ static const u16 sBasculinEggMoveLearnset[] = { MOVE_HEAD_SMASH, MOVE_UNAVAILABLE, }; +#if P_HISUIAN_FORMS +static const u16 sBasculinWhiteStripedEggMoveLearnset[] = { + MOVE_ENDEAVOR, + MOVE_LAST_RESPECTS, + MOVE_UNAVAILABLE, +}; +#endif //P_HISUIAN_FORMS #endif //P_FAMILY_BASCULIN #if P_FAMILY_SANDILE diff --git a/src/data/pokemon/species_info/gen_5_families.h b/src/data/pokemon/species_info/gen_5_families.h index 7662b7ced9..27b7a61d05 100644 --- a/src/data/pokemon/species_info/gen_5_families.h +++ b/src/data/pokemon/species_info/gen_5_families.h @@ -4377,6 +4377,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = ) .levelUpLearnset = sBasculinWhiteStripedLevelUpLearnset, .teachableLearnset = sBasculinWhiteStripedTeachableLearnset, + .eggMoveLearnset = sBasculinWhiteStripedEggMoveLearnset, .formSpeciesIdTable = sBasculinFormSpeciesIdTable, .evolutions = EVOLUTION({EVO_RECOIL_DAMAGE_MALE, 294, SPECIES_BASCULEGION_M}, {EVO_RECOIL_DAMAGE_FEMALE, 294, SPECIES_BASCULEGION_F}), diff --git a/test/battle/ability/beast_boost.c b/test/battle/ability/beast_boost.c index 8ce0c55b9e..9b07c51f1d 100644 --- a/test/battle/ability/beast_boost.c +++ b/test/battle/ability/beast_boost.c @@ -36,3 +36,23 @@ SINGLE_BATTLE_TEST("Beast Boost boosts the most proficient stat when knocking ou } } } + +SINGLE_BATTLE_TEST("Beast Boost doesn't trigger if user is fainted") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_DESTINY_BOND) == EFFECT_DESTINY_BOND); + ASSUME(GetMovePower(MOVE_SCRATCH) > 0); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_KARTANA) { Ability(ABILITY_BEAST_BOOST); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DESTINY_BOND); MOVE(opponent, MOVE_SCRATCH); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DESTINY_BOND, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + NOT ABILITY_POPUP(opponent, ABILITY_BEAST_BOOST); + SEND_IN_MESSAGE("Wynaut"); + MESSAGE("2 sent out Wobbuffet!"); + } +} diff --git a/test/battle/ability/desolate_land.c b/test/battle/ability/desolate_land.c index 5bf1358503..e058b7b514 100644 --- a/test/battle/ability/desolate_land.c +++ b/test/battle/ability/desolate_land.c @@ -65,3 +65,21 @@ SINGLE_BATTLE_TEST("Desolate Land does not block a move if pokemon is asleep and MESSAGE("The opposing Wobbuffet is fast asleep."); } } + +SINGLE_BATTLE_TEST("Desolate Land will not create a softlock when move in semi invulnerable position is blocked") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + } WHEN { + TURN { MOVE(player, MOVE_DIVE); } + TURN { SWITCH(opponent, 1); SKIP_TURN(player); } + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, player); + ABILITY_POPUP(opponent, ABILITY_DESOLATE_LAND); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + } +} diff --git a/test/battle/hold_effect/jaboca_berry.c b/test/battle/hold_effect/jaboca_berry.c index 2eb10df77a..cdb5cbfacd 100644 --- a/test/battle/hold_effect/jaboca_berry.c +++ b/test/battle/hold_effect/jaboca_berry.c @@ -58,3 +58,18 @@ SINGLE_BATTLE_TEST("Jaboca Berry tirggers before Bug Bite can steal it") NOT MESSAGE("Wynaut stole and ate the opposing Wobbuffet's Jaboca Berry!"); } } + +SINGLE_BATTLE_TEST("Jaboca Berry is triggered even if berry user dies") +{ + GIVEN { + ASSUME(GetMoveCategory(MOVE_SWIFT) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Item(ITEM_JABOCA_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Jaboca Berry!"); + } +} diff --git a/test/battle/hold_effect/life_orb.c b/test/battle/hold_effect/life_orb.c index e69de29bb2..f27ce29d03 100644 --- a/test/battle/hold_effect/life_orb.c +++ b/test/battle/hold_effect/life_orb.c @@ -0,0 +1,33 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Life Orb activates if it hits a Substitute") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + } +} + +SINGLE_BATTLE_TEST("Life Orb does not activate if using a status move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GROWL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, player); + NONE_OF { + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + } + } +} diff --git a/test/battle/hold_effect/restore_stats.c b/test/battle/hold_effect/restore_stats.c index e7f020910f..f0f7bc5908 100644 --- a/test/battle/hold_effect/restore_stats.c +++ b/test/battle/hold_effect/restore_stats.c @@ -217,3 +217,21 @@ SINGLE_BATTLE_TEST("White Herb has correct interactions with Intimidate triggere } } } + +DOUBLE_BATTLE_TEST("White Herb is correctly displayed") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT) { Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerRight, MOVE_SUPERPOWER, target: opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + MESSAGE("Wynaut returned its stats to normal using its White Herb!"); + } THEN { + EXPECT(playerLeft->item == ITEM_NONE); + EXPECT(playerLeft->statStages[STAT_DEF] = DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/hold_effect/rowap_berry.c b/test/battle/hold_effect/rowap_berry.c index 0604fefcb7..93ac49f608 100644 --- a/test/battle/hold_effect/rowap_berry.c +++ b/test/battle/hold_effect/rowap_berry.c @@ -57,3 +57,18 @@ SINGLE_BATTLE_TEST("Rowap Berry is not triggered by a physical move") } } } + +SINGLE_BATTLE_TEST("Rowap Berry is triggered even if berry user dies") +{ + GIVEN { + ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Item(ITEM_ROWAP_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_SWIFT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWIFT, player); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Rowap Berry!"); + } +} diff --git a/test/battle/move_effect/dragon_darts.c b/test/battle/move_effect/dragon_darts.c index 9e178f67c8..1d94968a09 100644 --- a/test/battle/move_effect/dragon_darts.c +++ b/test/battle/move_effect/dragon_darts.c @@ -76,6 +76,7 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes an opponent twice if the other one is F PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; } PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } + GIVEN { ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); PLAYER(SPECIES_WOBBUFFET); @@ -252,3 +253,61 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes right ally twice if one strike misses") MESSAGE("The Pokémon was hit 2 time(s)!"); } } + +DOUBLE_BATTLE_TEST("Dragon Darts strikes will be both redirected to Follow Me user") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + HP_BAR(opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + HP_BAR(opponentRight); + MESSAGE("The Pokémon was hit 2 time(s)!"); + } +} + +DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a fairy type follow me user") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CLEFAIRY); + } WHEN { + TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + MESSAGE("The Pokémon was hit 2 time(s)!"); + } + } +} + +DOUBLE_BATTLE_TEST("Dragon Darts fails to strike the second target if first target fainted and follow me was active") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + } WHEN { + TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + HP_BAR(opponentRight); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft); + MESSAGE("The Pokémon was hit 2 time(s)!"); + } + } +} diff --git a/test/battle/move_effect/hit_escape.c b/test/battle/move_effect/hit_escape.c index 83280f9f50..d0ef3d40f3 100644 --- a/test/battle/move_effect/hit_escape.c +++ b/test/battle/move_effect/hit_escape.c @@ -179,3 +179,19 @@ SINGLE_BATTLE_TEST("Hit Escape: Electric Seed boost is received by the right pok EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); } } + +SINGLE_BATTLE_TEST("Hit Escape: U-turn triggers before Eject Pack") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }; + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOODRA_HISUI) { Ability(ABILITY_GOOEY); }; + } WHEN { + TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); + HP_BAR(opponent); + SEND_IN_MESSAGE("Wynaut"); + } +} diff --git a/tools/mapjson/json11.cpp b/tools/mapjson/json11.cpp index 3ac5f392a0..fcacdbeb39 100644 --- a/tools/mapjson/json11.cpp +++ b/tools/mapjson/json11.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace json11 {