From f8efab059efe5d1322056b9756bc159ce7b8a70d Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 13:13:04 +0200 Subject: [PATCH 01/13] port emergency exit tests --- .../abilities/emergency_exit/multihit.c | 107 +++++++++++++++ .../no_activation_if_healed_above_50.c | 103 ++++++++++++++ .../only_fastest_user_switches.c | 126 ++++++++++++++++++ .../abilities/emergency_exit/recoil.c | 102 ++++++++++++++ .../emergency_exit/residual_damage.c | 102 ++++++++++++++ .../residual_damage_after_item_heal.c | 103 ++++++++++++++ .../emergency_exit/residual_damage_weather.c | 102 ++++++++++++++ .../abilities/emergency_exit/singles.c | 2 + 8 files changed, 747 insertions(+) create mode 100644 data/battle_tests/abilities/emergency_exit/multihit.c create mode 100644 data/battle_tests/abilities/emergency_exit/no_activation_if_healed_above_50.c create mode 100644 data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c create mode 100644 data/battle_tests/abilities/emergency_exit/recoil.c create mode 100644 data/battle_tests/abilities/emergency_exit/residual_damage.c create mode 100644 data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c create mode 100644 data/battle_tests/abilities/emergency_exit/residual_damage_weather.c diff --git a/data/battle_tests/abilities/emergency_exit/multihit.c b/data/battle_tests/abilities/emergency_exit/multihit.c new file mode 100644 index 0000000000..bdbc58f0a1 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/multihit.c @@ -0,0 +1,107 @@ +// Test: Emergency Exit - activates after last hit of a multi-hit move and falling under 50% max-hp +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_BRELOOM, + .level = 50, + .form = 0, + .ability = ABILITY_SKILL_LINK, + .item = ITEM_NONE, + .moves = { MOVE_BULLET_SEED, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 149, //150 + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/no_activation_if_healed_above_50.c b/data/battle_tests/abilities/emergency_exit/no_activation_if_healed_above_50.c new file mode 100644 index 0000000000..07985b3f65 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/no_activation_if_healed_above_50.c @@ -0,0 +1,103 @@ +// Test: Emergency Exit - does not switch out when going below 50% max-HP but healed via item above 50% max-HP +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_STARAPTOR, + .level = 40, + .form = 0, + .ability = ABILITY_RECKLESS, + .item = ITEM_NONE, + .moves = { MOVE_BRAVE_BIRD, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 55, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_SITRUS_BERRY, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 86, 90, 90, 90, 92, 92, 92, 96, 96, 96, 98, 98, 98, 102, 102, 104 } }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor was damaged by the recoil!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod restored its health using its Sitrus Berry!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod used Sleep Talk!" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c b/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c new file mode 100644 index 0000000000..698344244a --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c @@ -0,0 +1,126 @@ +// Test: Emergency Exit - only fastest user switches +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_DOUBLES, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_STARAPTOR, + .level = 40, + .form = 0, + .ability = ABILITY_RECKLESS, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_DONPHAN, + .level = 100, + .form = 0, + .ability = ABILITY_STURDY, + .item = ITEM_NONE, + .moves = { MOVE_EARTHQUAKE, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 5, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_FOCUS_SASH, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_GOLISOPOD, + .level = 6, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_FOCUS_SASH, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Donphan used Earthquake!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod used Sleep Talk!" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/recoil.c b/data/battle_tests/abilities/emergency_exit/recoil.c new file mode 100644 index 0000000000..3ca9876231 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/recoil.c @@ -0,0 +1,102 @@ +// Test: Emergency Exit - Emergency Exit will trigger due to recoil damage +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_STARAPTOR, + .level = 50, + .form = 0, + .ability = ABILITY_POISON_HEAL, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_WAVE_CRASH, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 76, //150 + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod was damaged by the recoil!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage.c b/data/battle_tests/abilities/emergency_exit/residual_damage.c new file mode 100644 index 0000000000..607c8b60a9 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/residual_damage.c @@ -0,0 +1,102 @@ +// Test: Emergency Exit - activates when taking residual damage and falling under 50% max-hp - Burn +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_STARAPTOR, + .level = 40, + .form = 0, + .ability = ABILITY_RECKLESS, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 76, //150 + .status = STATUS_BURN, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c b/data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c new file mode 100644 index 0000000000..dfbd4977d8 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c @@ -0,0 +1,103 @@ +// Test: Emergency Exit - activates when healing from under 50% max-hp and taking residual damage to under 50% max-hp - Burn +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_MISMAGIUS, + .level = 90, + .form = 0, + .ability = ABILITY_LEVITATE, + .item = ITEM_NONE, + .moves = { MOVE_NIGHT_SHADE, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 55, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_ORAN_BERRY, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = STATUS_BURN, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 86, 90, 90, 90, 92, 92, 92, 96, 96, 96, 98, 98, 98, 102, 102, 104 } }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor was damaged by the recoil!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod restored its health using its Sitrus Berry!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod used Sleep Talk!" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c new file mode 100644 index 0000000000..dd91f851c3 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c @@ -0,0 +1,102 @@ +// Test: Emergency Exit - activates when taking residual damage and falling under 50% max-hp - Burn +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_SANDSTORM, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_STARAPTOR, + .level = 40, + .form = 0, + .ability = ABILITY_RECKLESS, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 76, //150 + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/singles.c b/data/battle_tests/abilities/emergency_exit/singles.c index e9d8856f51..aae71a8bfe 100644 --- a/data/battle_tests/abilities/emergency_exit/singles.c +++ b/data/battle_tests/abilities/emergency_exit/singles.c @@ -97,6 +97,8 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 96, 96, 98, 98, 98, 102, 102, 102, 104, 104, 108, 108, 108, 110, 110, 114 } }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor was damaged by the recoil!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod restored its health using its Oran Berry!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, } } From 8fa18ee79e421e0a3e29db624b3818da89319c93 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 14:26:28 +0200 Subject: [PATCH 02/13] deal with recoil --- .../{singles.c => item_heal_stays_below_50.c} | 3 +- .../abilities/emergency_exit/multihit.c | 6 +- .../multihit_drop_with_last_hit.c | 107 ++++++++++++++++++ .../only_fastest_user_switches.c | 6 +- .../abilities/emergency_exit/recoil.c | 3 +- .../emergency_exit/residual_damage.c | 1 + .../emergency_exit/residual_damage_weather.c | 1 + include/battle.h | 2 + src/individual/BattleController_MoveEnd.c | 1 + src/individual/ServerDoPostMoveEffects.c | 41 ++++--- 10 files changed, 151 insertions(+), 20 deletions(-) rename data/battle_tests/abilities/emergency_exit/{singles.c => item_heal_stays_below_50.c} (94%) create mode 100644 data/battle_tests/abilities/emergency_exit/multihit_drop_with_last_hit.c diff --git a/data/battle_tests/abilities/emergency_exit/singles.c b/data/battle_tests/abilities/emergency_exit/item_heal_stays_below_50.c similarity index 94% rename from data/battle_tests/abilities/emergency_exit/singles.c rename to data/battle_tests/abilities/emergency_exit/item_heal_stays_below_50.c index aae71a8bfe..bb754c2102 100644 --- a/data/battle_tests/abilities/emergency_exit/singles.c +++ b/data/battle_tests/abilities/emergency_exit/item_heal_stays_below_50.c @@ -1,4 +1,4 @@ -// Test: Emergency Exit - Recoil, Oran Berry, Switch +// Test: Emergency Exit - switches out when going below 50% max-HP but healing via held item is not enough to go back above the threshold #include "../../battle_tests.h" BEGIN_TEST { @@ -98,6 +98,7 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor was damaged by the recoil!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod restored its health using its Oran Berry!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, } diff --git a/data/battle_tests/abilities/emergency_exit/multihit.c b/data/battle_tests/abilities/emergency_exit/multihit.c index bdbc58f0a1..4b447f38c8 100644 --- a/data/battle_tests/abilities/emergency_exit/multihit.c +++ b/data/battle_tests/abilities/emergency_exit/multihit.c @@ -1,4 +1,4 @@ -// Test: Emergency Exit - activates after last hit of a multi-hit move and falling under 50% max-hp +// Test: Emergency Exit - activates after last hit of a multi-hit move and falling under 50% max-hp #include "../../battle_tests.h" BEGIN_TEST { @@ -32,7 +32,7 @@ BEGIN_TEST .ability = ABILITY_EMERGENCY_EXIT, .item = ITEM_NONE, .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, - .hp = 149, //150 + .hp = 95, //150 .status = 0, .condition2 = 0, .moveEffectFlags = 0, @@ -100,8 +100,8 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, } } END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/multihit_drop_with_last_hit.c b/data/battle_tests/abilities/emergency_exit/multihit_drop_with_last_hit.c new file mode 100644 index 0000000000..afd0426c22 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/multihit_drop_with_last_hit.c @@ -0,0 +1,107 @@ +// Test: Emergency Exit - activates after last hit of a multi-hit move and falling under 50% max-hp on last hit +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_BRELOOM, + .level = 50, + .form = 0, + .ability = ABILITY_SKILL_LINK, + .item = ITEM_NONE, + .moves = { MOVE_BULLET_SEED, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 149, //150 + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 18 } }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + } +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c b/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c index 698344244a..cc7fbdb796 100644 --- a/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c +++ b/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c @@ -54,7 +54,7 @@ BEGIN_TEST .form = 0, .ability = ABILITY_EMERGENCY_EXIT, .item = ITEM_FOCUS_SASH, - .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .moves = { MOVE_DRAGON_DANCE, MOVE_NONE, MOVE_NONE, MOVE_NONE }, .hp = FULL_HP, .status = 0, .condition2 = 0, @@ -74,7 +74,6 @@ BEGIN_TEST }, { .species = SPECIES_NONE }, { .species = SPECIES_NONE }, - { .species = SPECIES_NONE }, { .species = SPECIES_NONE } }, .playerScript = { { { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, @@ -118,7 +117,10 @@ BEGIN_TEST } }, .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Donphan used Earthquake!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "It's not very effective on the opposing Golisopod or Golisopod." }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "It's not very effective on the opposing Golisopod or Golisopod." }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod used Sleep Talk!" }, } diff --git a/data/battle_tests/abilities/emergency_exit/recoil.c b/data/battle_tests/abilities/emergency_exit/recoil.c index 3ca9876231..73246da4f7 100644 --- a/data/battle_tests/abilities/emergency_exit/recoil.c +++ b/data/battle_tests/abilities/emergency_exit/recoil.c @@ -11,7 +11,7 @@ BEGIN_TEST .species = SPECIES_STARAPTOR, .level = 50, .form = 0, - .ability = ABILITY_POISON_HEAL, + .ability = ABILITY_RECKLESS, .item = ITEM_NONE, .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, .hp = FULL_HP, @@ -96,6 +96,7 @@ BEGIN_TEST .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod was damaged by the recoil!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, } } diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage.c b/data/battle_tests/abilities/emergency_exit/residual_damage.c index 607c8b60a9..7c13ce7898 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage.c @@ -95,6 +95,7 @@ BEGIN_TEST } }, .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, } diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c index dd91f851c3..7cf0bb8005 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c @@ -95,6 +95,7 @@ BEGIN_TEST } }, .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, } diff --git a/include/battle.h b/include/battle.h index ffaba692f4..1eed39d6f9 100644 --- a/include/battle.h +++ b/include/battle.h @@ -1089,6 +1089,8 @@ struct BattleStruct { HealingWishQueue healingWishQueue; int field_condition2; // gen5+ field conditions int magicRoomCounter; + + int recoilDamage; }; enum { diff --git a/src/individual/BattleController_MoveEnd.c b/src/individual/BattleController_MoveEnd.c index 865ec0ad3e..1029b7c919 100644 --- a/src/individual/BattleController_MoveEnd.c +++ b/src/individual/BattleController_MoveEnd.c @@ -131,6 +131,7 @@ void LONG_CALL BattleController_MoveEndInternal(struct BattleSystem *bsys, struc ctx->futureSightSTAB = 0; ctx->currentMoveSwitchStatus = CURRENT_MOVE_NO_SWITCH; ctx->moveConditionsFlags[ctx->attack_client].mindBlownOrSteelBeam = FALSE; + ctx->recoilDamage = 0; ctx->moveContext.hitFoesCount = 0; ctx->moveContext.hitSubstituteCount = 0; diff --git a/src/individual/ServerDoPostMoveEffects.c b/src/individual/ServerDoPostMoveEffects.c index b4c9ab7330..28e85ac074 100644 --- a/src/individual/ServerDoPostMoveEffects.c +++ b/src/individual/ServerDoPostMoveEffects.c @@ -2104,17 +2104,21 @@ int LONG_CALL Activate_RecoilDamage(void *bsys UNUSED, struct BattleStruct *ctx) switch (moveEffect) { case MOVE_EFFECT_RECOIL_QUARTER: // wild charge seq_no = BATTLE_SUBSCRIPT_RECOIL_1_4; + ctx->recoilDamage = ctx->store_damage[ctx->attack_client] / 4; break; case MOVE_EFFECT_RECOIL_PARALYZE_HIT: // volt tackle case MOVE_EFFECT_RECOIL_BURN_HIT: // flare blitz case MOVE_EFFECT_RECOIL_THIRD: // double edge, brave bird seq_no = BATTLE_SUBSCRIPT_RECOIL_1_3; + ctx->recoilDamage = ctx->store_damage[ctx->attack_client] / 3; break; case MOVE_EFFECT_RECOIL_HALF: // head smash, light of ruin seq_no = BATTLE_SUBSCRIPT_RECOIL_1_2; + ctx->recoilDamage = ctx->store_damage[ctx->attack_client] / 2; break; case MOVE_EFFECT_RECOIL_HALF_MAX_HP: // chloroplast seq_no = BATTLE_SUBSCRIPT_RECOIL_HALF_MAX_HP; + ctx->recoilDamage = (-1)* (ctx->battlemon[ctx->attack_client].maxhp + 1) / 2; break; default: break; @@ -2655,19 +2659,30 @@ int LONG_CALL Activate_WimpOut_EmergencyExit(void *bsys, struct BattleStruct *ct continue; } - if (MoldBreakerAbilityCheck(ctx, ctx->attack_client, client_no, ABILITY_WIMP_OUT) - || MoldBreakerAbilityCheck(ctx, ctx->attack_client, client_no, ABILITY_EMERGENCY_EXIT)) { - if ((ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) - && ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2) - && ((ctx->oneSelfFlag[client_no].physical_damage) || (ctx->oneSelfFlag[client_no].special_damage)) - // EE doesn't activate if the Pokémon gets attacked by a sheer force boosted move - && !((GetBattlerAbility(ctx, ctx->attack_client) == ABILITY_SHEER_FORCE) && (ctx->battlemon[ctx->attack_client].sheer_force_flag == 1)) - && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2)) - && ( - // checks if the pokémon has gone below half HP from the current damage instance - // physical_damage and special_damage contain the relevant damage value that was just dealt, but the value is negative - ((ctx->battlemon[client_no].hp - (ctx->oneSelfFlag[client_no].physical_damage)) > (s32)ctx->battlemon[client_no].maxhp / 2) - || ((ctx->battlemon[client_no].hp - (ctx->oneSelfFlag[client_no].special_damage)) > (s32)ctx->battlemon[client_no].maxhp / 2))) { + if ((ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) + && ((GetBattlerAbility(ctx, client_no) == ABILITY_WIMP_OUT) // not affected by Mold Breaker + || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) + && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2))) { + BOOL canActivate = FALSE; + if (ctx->attack_client == client_no) { + if ((ctx->battlemon[client_no].hp - ctx->recoilDamage) > (s32)(ctx->battlemon[client_no].maxhp / 2)) + { + canActivate = TRUE; + } + } else { + int overallDamage = ctx->store_damage[ctx->defence_client]; + if ((ctx->oneSelfFlag[client_no].physical_damage || ctx->oneSelfFlag[client_no].special_damage) + // EE doesn't activate if the Pokémon gets attacked by a sheer force boosted move + && !((GetBattlerAbility(ctx, ctx->attack_client) == ABILITY_SHEER_FORCE) && (ctx->battlemon[ctx->attack_client].sheer_force_flag == 1)) + && ( + // checks if the pokémon has gone below half HP from the current damage instance + (ctx->battlemon[client_no].hp - overallDamage) > (s32)ctx->battlemon[client_no].maxhp / 2)) { + canActivate = TRUE; + } + } + + if (canActivate) + { ctx->addeffect_type = SIDE_EFFECT_TYPE_ABILITY; ctx->state_client = client_no; ctx->battlerIdTemp = client_no; From 1886de0edbd7cad0a4769373ec9a207b2cd7a746 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 15:57:07 +0200 Subject: [PATCH 03/13] residual damage --- asm/include/battle_commands.inc | 8 +- .../subscripts/subscript_0026_BURN_DAMAGE.s | 4 + .../subscript_0104_WEATHER_CONTINUES.s | 12 +- .../only_fastest_user_switches.c | 3 +- ...sidual_damage.c => residual_damage_burn.c} | 5 +- .../residual_damage_sticky_barb.c | 103 ++++++++++++++++++ .../emergency_exit/residual_damage_weather.c | 9 +- src/battle/battle_script_commands.c | 32 +++++- src/individual/ServerDoPostMoveEffects.c | 1 + src/individual/ServerFieldConditionCheck.c | 2 + 10 files changed, 169 insertions(+), 10 deletions(-) rename data/battle_tests/abilities/emergency_exit/{residual_damage.c => residual_damage_burn.c} (97%) create mode 100644 data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c diff --git a/asm/include/battle_commands.inc b/asm/include/battle_commands.inc index d8a75473a8..d8b09edb9e 100644 --- a/asm/include/battle_commands.inc +++ b/asm/include/battle_commands.inc @@ -1633,7 +1633,13 @@ .long (((\arg_2 - .) / 4) - 1) .endm - .equ BASE_ENGINE_BTL_SCR_CMDS_MAX, 298 + .macro TryActivateEmergencyExit arg_0, arg_1 + .long 298 + .long \arg_0 + .long (((\arg_1 - .) / 4) - 1) + .endm + + .equ BASE_ENGINE_BTL_SCR_CMDS_MAX, 299 // .macro YourCustomCommand // .long (BASE_ENGINE_BTL_SCR_CMDS_MAX + 1) diff --git a/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s b/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s index b3a49bf152..be97284d5e 100644 --- a/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s +++ b/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s @@ -20,6 +20,10 @@ _020: Wait UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK GoToSubscript BATTLE_SUBSCRIPT_UPDATE_HP + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + +_noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK _041: diff --git a/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s b/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s index 1eada97b30..ac4df05ac1 100644 --- a/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s +++ b/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s @@ -29,7 +29,17 @@ _083: CompareVarToValue OPCODE_FLAG_SET, BSCRIPT_VAR_FIELD_CONDITION, FIELD_CONDITION_SUN_ALL, _095 // {1} is buffeted by the {0}! PrintMessage 285, TAG_MOVE_NICKNAME, BATTLER_CATEGORY_MSG_TEMP, BATTLER_CATEGORY_MSG_TEMP - GoTo _112 + Wait + WaitButtonABTime 30 + Call BATTLE_SUBSCRIPT_UPDATE_HP + CompareVarToValue OPCODE_FLAG_NOT, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_FAINTED, _noFaint + Call BATTLE_SUBSCRIPT_FAINT_MON + GoTo _140 + +_noFaint: + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _140 + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + GoTo _140 _095: CompareVarToValue OPCODE_EQU, BSCRIPT_VAR_TEMP_DATA, 2, _107 diff --git a/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c b/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c index cc7fbdb796..d97c366915 100644 --- a/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c +++ b/data/battle_tests/abilities/emergency_exit/only_fastest_user_switches.c @@ -118,7 +118,8 @@ BEGIN_TEST .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Donphan used Earthquake!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "It's not very effective on the opposing Golisopod or Golisopod." }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "It's not very effective on the opposing Golisopod or Golisopod." }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod hung on using its Focus Sash!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod hung on using its Focus Sash!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn.c similarity index 97% rename from data/battle_tests/abilities/emergency_exit/residual_damage.c rename to data/battle_tests/abilities/emergency_exit/residual_damage_burn.c index 7c13ce7898..e0e4a833ba 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn.c @@ -94,10 +94,11 @@ BEGIN_TEST { ACTION_NONE, 0 }, } }, .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod is hurt by its burn!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, - } + }, + .knownFailing = TRUE, } END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c b/data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c new file mode 100644 index 0000000000..9280c3d920 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c @@ -0,0 +1,103 @@ +// Test: Emergency Exit - activates when taking residual damage and falling under 50% max-hp - Sticky Barb +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_STARAPTOR, + .level = 40, + .form = 0, + .ability = ABILITY_RECKLESS, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_STICKY_BARB, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 76, // 150 + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + }, + .knownFailing = TRUE, +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c index 7cf0bb8005..eca55ffc17 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c @@ -1,4 +1,4 @@ -// Test: Emergency Exit - activates when taking residual damage and falling under 50% max-hp - Burn +// Test: Emergency Exit - activates when taking residual damage and falling under 50% max-hp - Sandstorm #include "../../battle_tests.h" BEGIN_TEST { @@ -32,7 +32,7 @@ BEGIN_TEST .ability = ABILITY_EMERGENCY_EXIT, .item = ITEM_NONE, .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, - .hp = 76, //150 + .hp = 76, // 150 .status = 0, .condition2 = 0, .moveEffectFlags = 0, @@ -94,10 +94,11 @@ BEGIN_TEST { ACTION_NONE, 0 }, } }, .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod is buffeted by the Sandstorm!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor's Attack fell!" }, - } + }, + .knownFailing = TRUE, } END_TEST diff --git a/src/battle/battle_script_commands.c b/src/battle/battle_script_commands.c index 660f6b1db8..ca9cedb18c 100644 --- a/src/battle/battle_script_commands.c +++ b/src/battle/battle_script_commands.c @@ -140,6 +140,7 @@ BOOL btl_scr_cmd_127_ActivateHealingWish(void *bsys UNUSED, struct BattleStruct BOOL btl_scr_cmd_128_IsFieldCondition2On(void *bsys UNUSED, struct BattleStruct *ctx); BOOL btl_scr_cmd_129_SetFieldCondition2(void *bsys UNUSED, struct BattleStruct *ctx); BOOL btl_scr_cmd_12A_GoToIfMoveConditionFlagSet(void *bsys, struct BattleStruct *ctx); +BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *ctx); BOOL BtlCmd_GoToMoveScript(struct BattleSystem *bsys, struct BattleStruct *ctx); BOOL BtlCmd_WeatherHPRecovery(void *bw, struct BattleStruct *sp); BOOL BtlCmd_CalcWeatherBallParams(void *bw, struct BattleStruct *sp); @@ -483,6 +484,7 @@ const u8 *BattleScrCmdNames[] = { "IsFieldCondition2On", "SetFieldCondition2", "GoToIfMoveConditionFlagSet", + "TryActivateEmergencyExit" // "YourCustomCommand", }; @@ -490,7 +492,7 @@ u32 cmdAddress = 0; #pragma GCC diagnostic pop #endif // DEBUG_BATTLE_SCRIPT_COMMANDS -#define BASE_ENGINE_BTL_SCR_CMDS_MAX 0x12A +#define BASE_ENGINE_BTL_SCR_CMDS_MAX 0x12B // clang-format off const btl_scr_cmd_func NewBattleScriptCmdTable[] = { @@ -568,6 +570,7 @@ const btl_scr_cmd_func NewBattleScriptCmdTable[] = { [0x128 - START_OF_NEW_BTL_SCR_CMDS] = btl_scr_cmd_128_IsFieldCondition2On, [0x129 - START_OF_NEW_BTL_SCR_CMDS] = btl_scr_cmd_129_SetFieldCondition2, [0x12A - START_OF_NEW_BTL_SCR_CMDS] = btl_scr_cmd_12A_GoToIfMoveConditionFlagSet, + [0x12B - START_OF_NEW_BTL_SCR_CMDS] = btl_scr_cmd_12B_TryActivateEmergencyExit, // [BASE_ENGINE_BTL_SCR_CMDS_MAX - START_OF_NEW_BTL_SCR_CMDS + 1] = btl_scr_cmd_custom_01_your_custom_command, }; @@ -5883,3 +5886,30 @@ BOOL btl_scr_cmd_12A_GoToIfMoveConditionFlagSet(void *bsys, struct BattleStruct return FALSE; } + + +BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *ctx) +{ + IncrementBattleScriptPtr(ctx, 1); + u32 side = read_battle_script_param(ctx); + u32 client_no = GrabClientFromBattleScriptParam(bsys, ctx, side); + + int noActivation = read_battle_script_param(ctx); + + if ((ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) + && ((GetBattlerAbility(ctx, client_no) == ABILITY_WIMP_OUT) // not affected by Mold Breaker + || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) + && ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) + && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2)) + && ((ctx->battlemon[client_no].hp - ctx->hp_calc_work) > (s32)ctx->battlemon[client_no].maxhp / 2)) { + ctx->addeffect_type = SIDE_EFFECT_TYPE_ABILITY; + ctx->state_client = client_no; + ctx->battlerIdTemp = client_no; + } + else + { + IncrementBattleScriptPtr(ctx, noActivation); + } + + return FALSE; +} diff --git a/src/individual/ServerDoPostMoveEffects.c b/src/individual/ServerDoPostMoveEffects.c index 28e85ac074..52ae49c271 100644 --- a/src/individual/ServerDoPostMoveEffects.c +++ b/src/individual/ServerDoPostMoveEffects.c @@ -2662,6 +2662,7 @@ int LONG_CALL Activate_WimpOut_EmergencyExit(void *bsys, struct BattleStruct *ct if ((ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) && ((GetBattlerAbility(ctx, client_no) == ABILITY_WIMP_OUT) // not affected by Mold Breaker || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) + && ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2))) { BOOL canActivate = FALSE; if (ctx->attack_client == client_no) { diff --git a/src/individual/ServerFieldConditionCheck.c b/src/individual/ServerFieldConditionCheck.c index 4cbf1afda1..c2066c9230 100644 --- a/src/individual/ServerFieldConditionCheck.c +++ b/src/individual/ServerFieldConditionCheck.c @@ -1970,6 +1970,8 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) sp->playerSideHasFaintedTeammateThisTurn = 0; sp->enemySideHasFaintedTeammateThisTurn = 0; ret = 2; + + sp->currentMoveSwitchStatus = CURRENT_MOVE_NO_SWITCH; break; } } From 770216c18055d192281a3d1195c58c20ab792232 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 15:57:31 +0200 Subject: [PATCH 04/13] remove useless test --- .../residual_damage_after_item_heal.c | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c b/data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c deleted file mode 100644 index dfbd4977d8..0000000000 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_after_item_heal.c +++ /dev/null @@ -1,103 +0,0 @@ -// Test: Emergency Exit - activates when healing from under 50% max-hp and taking residual damage to under 50% max-hp - Burn -#include "../../battle_tests.h" -BEGIN_TEST -{ - .battleType = BATTLE_TYPE_TRAINER, - .weather = FIELD_CONDITION_NONE, - .fieldCondition = 0, - .terrain = TERRAIN_NONE, - .playerParty = { - { - .species = SPECIES_MISMAGIUS, - .level = 90, - .form = 0, - .ability = ABILITY_LEVITATE, - .item = ITEM_NONE, - .moves = { MOVE_NIGHT_SHADE, MOVE_NONE, MOVE_NONE, MOVE_NONE }, - .hp = FULL_HP, - .status = 0, - .condition2 = 0, - .moveEffectFlags = 0, - }, - { .species = SPECIES_NONE }, - { .species = SPECIES_NONE }, - { .species = SPECIES_NONE }, - { .species = SPECIES_NONE }, - { .species = SPECIES_NONE } - }, - .enemyParty = { { - .species = SPECIES_GOLISOPOD, - .level = 55, - .form = 0, - .ability = ABILITY_EMERGENCY_EXIT, - .item = ITEM_ORAN_BERRY, - .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, - .hp = FULL_HP, - .status = STATUS_BURN, - .condition2 = 0, - .moveEffectFlags = 0, - }, - { - .species = SPECIES_LITTEN, - .level = 50, - .form = 0, - .ability = ABILITY_INTIMIDATE, - .item = ITEM_NONE, - .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, - .hp = FULL_HP, - .status = 0, - .condition2 = 0, - .moveEffectFlags = 0, - }, - { .species = SPECIES_NONE }, - { .species = SPECIES_NONE }, - { .species = SPECIES_NONE }, - { .species = SPECIES_NONE } }, - .playerScript = { { - { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - }, - { - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - } }, - .enemyScript = { { - { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - }, - { - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, - } }, - .expectations = { - { .expectationType = EXPECTATION_TYPE_HP_BAR, .battlerIDOrPartySlot = BATTLER_ENEMY_FIRST, .expectationValue.hpTaken = { 86, 90, 90, 90, 92, 92, 92, 96, 96, 96, 98, 98, 98, 102, 102, 104 } }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Staraptor was damaged by the recoil!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod restored its health using its Sitrus Berry!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod used Sleep Talk!" }, - } -} -END_TEST From 9efcf09de584f4c87a52374273c0e1947dde670b Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 17:36:26 +0200 Subject: [PATCH 05/13] add EE to sticky barb, leech, poison script --- asm/include/battle_commands.inc | 2 +- .../subscripts/subscript_0023_POISON_DAMAGE.s | 4 ++++ .../subscripts/subscript_0069_LEECH_SEED_EFFECT.s | 7 +++++++ .../subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s | 4 ++++ .../subscripts/subscript_0498_HANDLE_WIMP_OUT.s | 1 - src/battle/battle_script_commands.c | 3 ++- 6 files changed, 18 insertions(+), 3 deletions(-) diff --git a/asm/include/battle_commands.inc b/asm/include/battle_commands.inc index d8b09edb9e..e37db9e451 100644 --- a/asm/include/battle_commands.inc +++ b/asm/include/battle_commands.inc @@ -1634,7 +1634,7 @@ .endm .macro TryActivateEmergencyExit arg_0, arg_1 - .long 298 + .long 299 .long \arg_0 .long (((\arg_1 - .) / 4) - 1) .endm diff --git a/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s b/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s index 1bdcb8b1ef..11b0d0015d 100644 --- a/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s +++ b/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s @@ -28,6 +28,10 @@ _038: Wait UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK GoToSubscript BATTLE_SUBSCRIPT_UPDATE_HP + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + +_noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK _055: diff --git a/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s b/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s index fcc85dede1..f4bd6bf194 100644 --- a/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s +++ b/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s @@ -30,6 +30,13 @@ _050: PrintMessage 296, TAG_NICKNAME, BATTLER_CATEGORY_MSG_DEFENDER Wait WaitButtonABTime 30 + UpdateMonDataFromVar OPCODE_GET, BATTLER_CATEGORY_MSG_DEFENDER, BMON_DATA_MAXHP, BSCRIPT_VAR_HP_CALC + DivideVarByValue BSCRIPT_VAR_HP_CALC, 8 + UpdateVar OPCODE_MUL, BSCRIPT_VAR_HP_CALC, -1 + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation //needs ctx->hp_calc_work + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + +_noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK End diff --git a/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s b/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s index bfae6b9e2b..c76bea6237 100644 --- a/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s +++ b/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s @@ -12,5 +12,9 @@ _000: PrintMessage 1132, TAG_NICKNAME_ITEM, BATTLER_CATEGORY_MSG_TEMP, BATTLER_CATEGORY_MSG_BATTLER_TEMP Wait WaitButtonABTime 30 + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + +_noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK End diff --git a/data/battle_scripts/subscripts/subscript_0498_HANDLE_WIMP_OUT.s b/data/battle_scripts/subscripts/subscript_0498_HANDLE_WIMP_OUT.s index 5580b28182..70206c1199 100644 --- a/data/battle_scripts/subscripts/subscript_0498_HANDLE_WIMP_OUT.s +++ b/data/battle_scripts/subscripts/subscript_0498_HANDLE_WIMP_OUT.s @@ -18,7 +18,6 @@ _155: Wait HealthbarSlideOut BATTLER_CATEGORY_MSG_BATTLER_TEMP Wait - UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS_2, BATTLE_STATUS2_UTURN UpdateVar OPCODE_FLAG_OFF, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_SYNCRONIZE UpdateVar OPCODE_SET, BSCRIPT_VAR_SIDE_EFFECT_MON_SELF_TURN_STATUS_FLAGS, SELF_TURN_FLAG_CLEAR SetCurrentMoveSwitchingStatus CURRENT_MOVE_SWITCH_PENDING diff --git a/src/battle/battle_script_commands.c b/src/battle/battle_script_commands.c index ca9cedb18c..f0678eb432 100644 --- a/src/battle/battle_script_commands.c +++ b/src/battle/battle_script_commands.c @@ -5896,7 +5896,8 @@ BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *c int noActivation = read_battle_script_param(ctx); - if ((ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) + if (ctx->battlemon[client_no].hp + && (ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) && ((GetBattlerAbility(ctx, client_no) == ABILITY_WIMP_OUT) // not affected by Mold Breaker || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) && ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) From 1913615cbc7fe8c6aa86efdfdab0bc30df2a407a Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 17:48:38 +0200 Subject: [PATCH 06/13] resolved switches after each block --- .../residual_damage_burn_speed_boost.c | 127 ++++++++++++++++++ src/individual/ServerFieldConditionCheck.c | 32 ++++- 2 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c new file mode 100644 index 0000000000..55d9187187 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c @@ -0,0 +1,127 @@ +// Test: Emergency Exit - Residual Damage from Burn, Resolve Switch, Speed Boost triggers +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_DOUBLES, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_STARAPTOR, + .level = 40, + .form = 0, + .ability = ABILITY_RECKLESS, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_BLAZIKEN, + .level = 40, + .form = 0, + .ability = ABILITY_SPEED_BOOST, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 76, //150 + .status = STATUS_BURN, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_SHUCKLE, + .level = 40, + .form = 0, + .ability = ABILITY_GLUTTONY, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod is hurt by its burn!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Blaziken's Speed Boost" }, + }, + .knownFailing = TRUE, +} +END_TEST diff --git a/src/individual/ServerFieldConditionCheck.c b/src/individual/ServerFieldConditionCheck.c index c2066c9230..9ef9cde71c 100644 --- a/src/individual/ServerFieldConditionCheck.c +++ b/src/individual/ServerFieldConditionCheck.c @@ -288,8 +288,14 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) debug_printf("In ENDTURN_RESOLVE_SWITCHES_1\n"); #endif - sp->fcc_seq_no++; + if (sp->currentMoveSwitchStatus == CURRENT_MOVE_SWITCH_PENDING) { + LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_SHOW_PARTY_LIST); + sp->next_server_seq_no = sp->server_seq_no; + sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; + return; + } + break; } // TODO @@ -630,7 +636,13 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) debug_printf("In ENDTURN_RESOLVE_SWITCHES_2\n"); #endif - + sp->fcc_seq_no++; + if (sp->currentMoveSwitchStatus == CURRENT_MOVE_SWITCH_PENDING) { + LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_SHOW_PARTY_LIST); + sp->next_server_seq_no = sp->server_seq_no; + sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; + return; + } sp->fcc_seq_no++; break; } @@ -1229,7 +1241,13 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) debug_printf("In ENDTURN_RESOLVE_SWITCHES_3\n"); #endif - + sp->fcc_seq_no++; + if (sp->currentMoveSwitchStatus == CURRENT_MOVE_SWITCH_PENDING) { + LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_SHOW_PARTY_LIST); + sp->next_server_seq_no = sp->server_seq_no; + sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; + return; + } sp->fcc_seq_no++; break; } @@ -1812,7 +1830,13 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) debug_printf("In ENDTURN_RESOLVE_SWITCHES_4\n"); #endif - + sp->fcc_seq_no++; + if (sp->currentMoveSwitchStatus == CURRENT_MOVE_SWITCH_PENDING) { + LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_SHOW_PARTY_LIST); + sp->next_server_seq_no = sp->server_seq_no; + sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; + return; + } sp->fcc_seq_no++; break; } From 72245cf76d2bf9c865985b6641eba51681b39054 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 18:07:33 +0200 Subject: [PATCH 07/13] poison/burn damage script ended early --- data/battle_scripts/subscripts/subscript_0010_SHOW_PARTY_LIST.s | 2 ++ data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s | 2 +- data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s | 2 +- .../abilities/emergency_exit/residual_damage_burn_speed_boost.c | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/data/battle_scripts/subscripts/subscript_0010_SHOW_PARTY_LIST.s b/data/battle_scripts/subscripts/subscript_0010_SHOW_PARTY_LIST.s index 12c50b18b3..b8e5291173 100644 --- a/data/battle_scripts/subscripts/subscript_0010_SHOW_PARTY_LIST.s +++ b/data/battle_scripts/subscripts/subscript_0010_SHOW_PARTY_LIST.s @@ -22,6 +22,7 @@ _001: WaitTime 72 HealthbarSlideIn BATTLER_CATEGORY_SWITCHED_MON Wait + SetCurrentMoveSwitchingStatus CURRENT_MOVE_NO_SWITCH Call BATTLE_SUBSCRIPT_HEALING_WISH Call BATTLE_SUBSCRIPT_HAZARDS_CHECK CompareVarToValue OPCODE_FLAG_NOT, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_FAINTED, _030 @@ -29,4 +30,5 @@ _001: _030: GoToIfAnySwitches _001 + Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK End diff --git a/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s b/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s index 11b0d0015d..eaedd5cb7b 100644 --- a/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s +++ b/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s @@ -27,7 +27,7 @@ _038: PlayBattleAnimation BATTLER_CATEGORY_MSG_TEMP, BATTLE_ANIMATION_POISONED Wait UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK - GoToSubscript BATTLE_SUBSCRIPT_UPDATE_HP + Call BATTLE_SUBSCRIPT_UPDATE_HP TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT diff --git a/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s b/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s index be97284d5e..6463a8beeb 100644 --- a/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s +++ b/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s @@ -19,7 +19,7 @@ _020: PlayBattleAnimation BATTLER_CATEGORY_MSG_TEMP, BATTLE_ANIMATION_BURNED Wait UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK - GoToSubscript BATTLE_SUBSCRIPT_UPDATE_HP + Call BATTLE_SUBSCRIPT_UPDATE_HP TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c index 55d9187187..ff350333d0 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c @@ -120,7 +120,7 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Blaziken's Speed Boost" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Blaziken's Speed rose!" }, }, .knownFailing = TRUE, } From 431b16d0db858b740fa4f08082715d1e583e5080 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Sun, 11 Oct 2026 19:07:22 +0200 Subject: [PATCH 08/13] lint --- .../emergency_exit/residual_damage_burn_speed_boost.c | 5 ++--- src/battle/battle_script_commands.c | 5 +---- src/individual/ServerDoPostMoveEffects.c | 10 ++++------ src/individual/ServerFieldConditionCheck.c | 8 ++++---- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c index ff350333d0..ad5040421c 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_speed_boost.c @@ -86,7 +86,7 @@ BEGIN_TEST { ACTION_NONE, 0 }, }, { - { ACTION_NONE, 0 }, + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, @@ -106,7 +106,7 @@ BEGIN_TEST { ACTION_NONE, 0 }, }, { - { ACTION_NONE, 0 }, + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, @@ -122,6 +122,5 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Blaziken's Speed rose!" }, }, - .knownFailing = TRUE, } END_TEST diff --git a/src/battle/battle_script_commands.c b/src/battle/battle_script_commands.c index f0678eb432..4379a4d2b1 100644 --- a/src/battle/battle_script_commands.c +++ b/src/battle/battle_script_commands.c @@ -5887,7 +5887,6 @@ BOOL btl_scr_cmd_12A_GoToIfMoveConditionFlagSet(void *bsys, struct BattleStruct return FALSE; } - BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *ctx) { IncrementBattleScriptPtr(ctx, 1); @@ -5906,9 +5905,7 @@ BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *c ctx->addeffect_type = SIDE_EFFECT_TYPE_ABILITY; ctx->state_client = client_no; ctx->battlerIdTemp = client_no; - } - else - { + } else { IncrementBattleScriptPtr(ctx, noActivation); } diff --git a/src/individual/ServerDoPostMoveEffects.c b/src/individual/ServerDoPostMoveEffects.c index 52ae49c271..d5d6ba402f 100644 --- a/src/individual/ServerDoPostMoveEffects.c +++ b/src/individual/ServerDoPostMoveEffects.c @@ -2118,7 +2118,7 @@ int LONG_CALL Activate_RecoilDamage(void *bsys UNUSED, struct BattleStruct *ctx) break; case MOVE_EFFECT_RECOIL_HALF_MAX_HP: // chloroplast seq_no = BATTLE_SUBSCRIPT_RECOIL_HALF_MAX_HP; - ctx->recoilDamage = (-1)* (ctx->battlemon[ctx->attack_client].maxhp + 1) / 2; + ctx->recoilDamage = (-1) * (ctx->battlemon[ctx->attack_client].maxhp + 1) / 2; break; default: break; @@ -2661,13 +2661,12 @@ int LONG_CALL Activate_WimpOut_EmergencyExit(void *bsys, struct BattleStruct *ct if ((ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) && ((GetBattlerAbility(ctx, client_no) == ABILITY_WIMP_OUT) // not affected by Mold Breaker - || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) + || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) && ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2))) { BOOL canActivate = FALSE; if (ctx->attack_client == client_no) { - if ((ctx->battlemon[client_no].hp - ctx->recoilDamage) > (s32)(ctx->battlemon[client_no].maxhp / 2)) - { + if ((ctx->battlemon[client_no].hp - ctx->recoilDamage) > (s32)(ctx->battlemon[client_no].maxhp / 2)) { canActivate = TRUE; } } else { @@ -2682,8 +2681,7 @@ int LONG_CALL Activate_WimpOut_EmergencyExit(void *bsys, struct BattleStruct *ct } } - if (canActivate) - { + if (canActivate) { ctx->addeffect_type = SIDE_EFFECT_TYPE_ABILITY; ctx->state_client = client_no; ctx->battlerIdTemp = client_no; diff --git a/src/individual/ServerFieldConditionCheck.c b/src/individual/ServerFieldConditionCheck.c index 9ef9cde71c..11896d2e5d 100644 --- a/src/individual/ServerFieldConditionCheck.c +++ b/src/individual/ServerFieldConditionCheck.c @@ -295,7 +295,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; return; } - + break; } // TODO @@ -643,7 +643,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; return; } - sp->fcc_seq_no++; + break; } case ENDTURN_AQUA_RING: { @@ -1248,7 +1248,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; return; } - sp->fcc_seq_no++; + break; } case ENDTURN_SECOND_EVENT_BLOCK: { @@ -1837,7 +1837,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) sp->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; return; } - sp->fcc_seq_no++; + break; } // TODO From 0b40fb9b6a2dce5e72fb95f210b02fb440147cb9 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Mon, 12 Oct 2026 08:30:24 +0200 Subject: [PATCH 09/13] rename wimp out script add failing leech seed test --- .../subscripts/subscript_0023_POISON_DAMAGE.s | 2 +- .../subscripts/subscript_0026_BURN_DAMAGE.s | 2 +- .../subscript_0069_LEECH_SEED_EFFECT.s | 3 +- .../subscript_0104_WEATHER_CONTINUES.s | 2 +- ...ript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s | 2 +- .../emergency_exit/residual_damage_burn.c | 1 - .../residual_damage_burn_leech_seed.c | 130 ++++++++++++++++++ .../residual_damage_sticky_barb.c | 2 +- .../emergency_exit/residual_damage_weather.c | 1 - include/constants/battle_script_constants.h | 2 +- src/battle/battle_script_commands.c | 1 + src/individual/ServerDoPostMoveEffects.c | 3 +- 12 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c diff --git a/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s b/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s index eaedd5cb7b..ff380537d7 100644 --- a/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s +++ b/data/battle_scripts/subscripts/subscript_0023_POISON_DAMAGE.s @@ -29,7 +29,7 @@ _038: UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK Call BATTLE_SUBSCRIPT_UPDATE_HP TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation - Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT _noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK diff --git a/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s b/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s index 6463a8beeb..4b64bdfe4d 100644 --- a/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s +++ b/data/battle_scripts/subscripts/subscript_0026_BURN_DAMAGE.s @@ -21,7 +21,7 @@ _020: UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK Call BATTLE_SUBSCRIPT_UPDATE_HP TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation - Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT _noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK diff --git a/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s b/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s index f4bd6bf194..dd53bc257d 100644 --- a/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s +++ b/data/battle_scripts/subscripts/subscript_0069_LEECH_SEED_EFFECT.s @@ -30,11 +30,12 @@ _050: PrintMessage 296, TAG_NICKNAME, BATTLER_CATEGORY_MSG_DEFENDER Wait WaitButtonABTime 30 + UpdateVarFromVar OPCODE_SET, BSCRIPT_VAR_MSG_BATTLER_TEMP, BSCRIPT_VAR_MSG_DEFENDER UpdateMonDataFromVar OPCODE_GET, BATTLER_CATEGORY_MSG_DEFENDER, BMON_DATA_MAXHP, BSCRIPT_VAR_HP_CALC DivideVarByValue BSCRIPT_VAR_HP_CALC, 8 UpdateVar OPCODE_MUL, BSCRIPT_VAR_HP_CALC, -1 TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation //needs ctx->hp_calc_work - Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT _noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK diff --git a/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s b/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s index ac4df05ac1..984ba7a8e7 100644 --- a/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s +++ b/data/battle_scripts/subscripts/subscript_0104_WEATHER_CONTINUES.s @@ -38,7 +38,7 @@ _083: _noFaint: TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _140 - Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT GoTo _140 _095: diff --git a/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s b/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s index c76bea6237..73434b5f39 100644 --- a/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s +++ b/data/battle_scripts/subscripts/subscript_0215_LOSE_HP_FROM_ITEM_WITH_MESSAGE.s @@ -13,7 +13,7 @@ _000: Wait WaitButtonABTime 30 TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation - Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT _noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_burn.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn.c index e0e4a833ba..190143876a 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_burn.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn.c @@ -99,6 +99,5 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, }, - .knownFailing = TRUE, } END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c new file mode 100644 index 0000000000..461bf8dff4 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c @@ -0,0 +1,130 @@ +// Test: Emergency Exit - Residual Damage from Leech Seed, Resolve Switch, Burn damage loop without Golisopod, Speed Boost triggers +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_DOUBLES, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_FERROTHORN, + .level = 40, + .form = 0, + .ability = ABILITY_OVERGROW, + .item = ITEM_NONE, + .moves = { MOVE_LEECH_SEED, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 50, + .status = STATUS_BURN, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_BLAZIKEN, + .level = 40, + .form = 0, + .ability = ABILITY_SPEED_BOOST, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 76, //150 + .status = STATUS_BURN, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_SHUCKLE, + .level = 40, + .form = 0, + .ability = ABILITY_GLUTTONY, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = STATUS_BURN, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod was seeded!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod’s health is sapped by Leech Seed!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "TGolisopod’s EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_DOES_NOT_CONTAIN, .expectationValue.message = "The opposing Golisopod is hurt by its burn!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Shuckle is hurt by its burn!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Blaziken's Speed rose!" }, + }, + .knownFailing = TRUE, +} +END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c b/data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c index 9280c3d920..6af023a0d7 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_sticky_barb.c @@ -94,10 +94,10 @@ BEGIN_TEST { ACTION_NONE, 0 }, } }, .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod is hurt by its Sticky Barb!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, }, - .knownFailing = TRUE, } END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c index eca55ffc17..5f0b27ab8d 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_weather.c @@ -99,6 +99,5 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, }, - .knownFailing = TRUE, } END_TEST diff --git a/include/constants/battle_script_constants.h b/include/constants/battle_script_constants.h index 4bb435fec2..9b2d117b85 100644 --- a/include/constants/battle_script_constants.h +++ b/include/constants/battle_script_constants.h @@ -504,7 +504,7 @@ #define BATTLE_SUBSCRIPT_BATCH_EFFECTIVENESS (495) #define BATTLE_SUBSCRIPT_BATCH_FOLLOWUP (496) #define BATTLE_SUBSCRIPT_SUBSTITUTE_FADES (497) -#define BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT (498) +#define BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT (498) #define BATTLE_SUBSCRIPT_RECOIL_HALF_MAX_HP (499) #define BATTLE_SUBSCRIPT_FUTURE_SIGHT_DAMAGE (500) #define BATTLE_SUBSCRIPT_HANDLE_HOSPITALITY (501) diff --git a/src/battle/battle_script_commands.c b/src/battle/battle_script_commands.c index 4379a4d2b1..dc08ec9697 100644 --- a/src/battle/battle_script_commands.c +++ b/src/battle/battle_script_commands.c @@ -5901,6 +5901,7 @@ BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *c || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) && ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2)) + // EE can be triggered by any residual damage, such as Burn, Sandstorm, etc && ((ctx->battlemon[client_no].hp - ctx->hp_calc_work) > (s32)ctx->battlemon[client_no].maxhp / 2)) { ctx->addeffect_type = SIDE_EFFECT_TYPE_ABILITY; ctx->state_client = client_no; diff --git a/src/individual/ServerDoPostMoveEffects.c b/src/individual/ServerDoPostMoveEffects.c index d5d6ba402f..a25485011d 100644 --- a/src/individual/ServerDoPostMoveEffects.c +++ b/src/individual/ServerDoPostMoveEffects.c @@ -2666,6 +2666,7 @@ int LONG_CALL Activate_WimpOut_EmergencyExit(void *bsys, struct BattleStruct *ct && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2))) { BOOL canActivate = FALSE; if (ctx->attack_client == client_no) { + // EE can be triggered by recoil if attacker. Recoil damage is stored earlier when applied if ((ctx->battlemon[client_no].hp - ctx->recoilDamage) > (s32)(ctx->battlemon[client_no].maxhp / 2)) { canActivate = TRUE; } @@ -2686,7 +2687,7 @@ int LONG_CALL Activate_WimpOut_EmergencyExit(void *bsys, struct BattleStruct *ct ctx->state_client = client_no; ctx->battlerIdTemp = client_no; - LoadBattleSubSeqScript(ctx, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT); + LoadBattleSubSeqScript(ctx, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT); ctx->next_server_seq_no = ctx->server_seq_no; ctx->server_seq_no = CONTROLLER_COMMAND_RUN_SCRIPT; return TRUE; From 2aa2b78b02822b8512c7ca857408bc88661008c8 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Mon, 12 Oct 2026 11:53:32 +0200 Subject: [PATCH 10/13] EE from nightmare, curse, bind EE from hazards hook checkSpikes, checkStealthRocks --- .../subscripts/subscript_0059_BIND_EFFECT.s | 6 +- .../subscript_0094_NIGHTMARE_EFFECT.s | 6 +- .../subscripts/subscript_0098_CURSE_DAMAGE.s | 6 +- .../subscripts/subscript_0099_HAZARDS_CHECK.s | 8 ++ .../residual_damage_burn_leech_seed.c | 4 +- .../abilities/emergency_exit/stealth_rocks.c | 116 ++++++++++++++++++ hooks | 2 + include/battle.h | 8 ++ rom.ld | 1 + src/battle/battle_script_commands.c | 72 ++++++++++- src/battle/other_battle_calculators.c | 11 ++ src/individual/ServerDoPostMoveEffects.c | 3 +- src/individual/ServerFieldConditionCheck.c | 15 +-- 13 files changed, 242 insertions(+), 16 deletions(-) create mode 100644 data/battle_tests/abilities/emergency_exit/stealth_rocks.c diff --git a/data/battle_scripts/subscripts/subscript_0059_BIND_EFFECT.s b/data/battle_scripts/subscripts/subscript_0059_BIND_EFFECT.s index cee87d2ff2..886989a165 100644 --- a/data/battle_scripts/subscripts/subscript_0059_BIND_EFFECT.s +++ b/data/battle_scripts/subscripts/subscript_0059_BIND_EFFECT.s @@ -57,7 +57,11 @@ _DealDamage: Wait WaitButtonABTime 30 UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK - GoToSubscript BATTLE_SUBSCRIPT_UPDATE_HP + Call BATTLE_SUBSCRIPT_UPDATE_HP + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT + +_noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK _End: diff --git a/data/battle_scripts/subscripts/subscript_0094_NIGHTMARE_EFFECT.s b/data/battle_scripts/subscripts/subscript_0094_NIGHTMARE_EFFECT.s index b4c73595d6..e195874b76 100644 --- a/data/battle_scripts/subscripts/subscript_0094_NIGHTMARE_EFFECT.s +++ b/data/battle_scripts/subscripts/subscript_0094_NIGHTMARE_EFFECT.s @@ -15,7 +15,11 @@ _000: Wait WaitButtonABTime 30 UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK - GoToSubscript BATTLE_SUBSCRIPT_UPDATE_HP + Call BATTLE_SUBSCRIPT_UPDATE_HP + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT + +_noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK _033: diff --git a/data/battle_scripts/subscripts/subscript_0098_CURSE_DAMAGE.s b/data/battle_scripts/subscripts/subscript_0098_CURSE_DAMAGE.s index a84abacfad..c1a4a2508d 100644 --- a/data/battle_scripts/subscripts/subscript_0098_CURSE_DAMAGE.s +++ b/data/battle_scripts/subscripts/subscript_0098_CURSE_DAMAGE.s @@ -15,7 +15,11 @@ _000: Wait WaitButtonABTime 30 UpdateVar OPCODE_FLAG_ON, BSCRIPT_VAR_BATTLE_STATUS, BATTLE_STATUS_NO_BLINK - GoToSubscript BATTLE_SUBSCRIPT_UPDATE_HP + Call BATTLE_SUBSCRIPT_UPDATE_HP + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noActivation + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT + +_noActivation: Call BATTLE_SUBSCRIPT_SWITCH_IN_ABILITY_CHECK _033: diff --git a/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s b/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s index 26b573f9e6..53f40f5187 100644 --- a/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s +++ b/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s @@ -51,6 +51,10 @@ _checkSpikes: PrintMessage 429, TAG_NICKNAME, BATTLER_CATEGORY_SWITCHED_MON Wait WaitButtonABTime 30 + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noWimpOutFromSpikes + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT + +_noWimpOutFromSpikes: GoTo _landingPad _checkIfShouldDoTSpikes: @@ -124,4 +128,8 @@ _checkStealthRock: PrintMessage 1079, TAG_NICKNAME, BATTLER_CATEGORY_SWITCHED_MON Wait WaitButtonABTime 30 + TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noWimpOutFromRocks + Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT + +_noWimpOutFromRocks: GoTo _landingPad diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c index 461bf8dff4..7fe88c46a8 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c @@ -117,8 +117,8 @@ BEGIN_TEST } }, .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod was seeded!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod’s health is sapped by Leech Seed!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "TGolisopod’s EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod's health is sapped by Leech Seed!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "TGolisopod's EmergencyExit" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_DOES_NOT_CONTAIN, .expectationValue.message = "The opposing Golisopod is hurt by its burn!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Shuckle is hurt by its burn!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, diff --git a/data/battle_tests/abilities/emergency_exit/stealth_rocks.c b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c new file mode 100644 index 0000000000..1147c99319 --- /dev/null +++ b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c @@ -0,0 +1,116 @@ +// Test: Emergency Exit - activates when taking damage from Stealth Rocks and going below 50% +#include "../../battle_tests.h" +BEGIN_TEST +{ + .battleType = BATTLE_TYPE_TRAINER, + .weather = FIELD_CONDITION_NONE, + .fieldCondition = 0, + .terrain = TERRAIN_NONE, + .playerParty = { + { + .species = SPECIES_SHUCKLE, + .level = 40, + .form = 0, + .ability = ABILITY_GLUTTONY, + .item = ITEM_NONE, + .moves = { MOVE_STEALTH_ROCK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } + }, + .enemyParty = { { + .species = SPECIES_LITTEN, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_GOLISOPOD, + .level = 50, + .form = 0, + .ability = ABILITY_EMERGENCY_EXIT, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = 76, // 150 + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { + .species = SPECIES_INCINEROAR, + .level = 50, + .form = 0, + .ability = ABILITY_INTIMIDATE, + .item = ITEM_NONE, + .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .hp = FULL_HP, + .status = 0, + .condition2 = 0, + .moveEffectFlags = 0, + }, + { .species = SPECIES_NONE }, + { .species = SPECIES_NONE } }, + .playerScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, + { ACTION_SWITCH_SLOT_1, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + }, + { + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + { ACTION_NONE, 0 }, + } }, + .expectations = { + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Litten used Sleep Talk!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten used Stealth Rock!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Golisopod" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, + }, + .knownFailing = TRUE, +} +END_TEST diff --git a/hooks b/hooks index 1e5cbf5246..12297ef4f5 100644 --- a/hooks +++ b/hooks @@ -163,6 +163,8 @@ arm9 CreateStarterMon_hook 020960E6 1 0012 BtlCmd_TryPerishSong 02241A04 3 0012 BtlCmd_Metronome 0224089C 3 0012 BtlCmd_TryCopycat 02243A0C 3 +0012 BtlCmd_CheckSpikes 02241980 3 +0012 BtlCmd_CheckStealthRock 022446AC 3 #trainer data expansion arm9 MakeTrainerPokemonParty 08073604 3 diff --git a/include/battle.h b/include/battle.h index 1eed39d6f9..24f52baed6 100644 --- a/include/battle.h +++ b/include/battle.h @@ -1978,6 +1978,12 @@ u8 LONG_CALL IsClientEnemy(void *bw, int client); */ int LONG_CALL TypeCalc(void *bw, struct BattleStruct *sp, int movenum, int movetype, int attacker, int defender, int damage, u32 *flag); +/** + * @brief used to calculate effectiveness of Stealth Rock during switch-in + * + */ +int LONG_CALL CalculateTypeEffectiveness(u8 typeMove, u8 typeMon1, u8 typeMon2); + /** * @brief sees if anticipation will automatially log a certain move (i.e. OHKO moves) * @@ -3803,4 +3809,6 @@ BOOL LONG_CALL sub_02017068(void *animManager, int battlerId); void LONG_CALL *ov12_0223B750(struct BattleSystem *battleSystem); void LONG_CALL ov12_022600F0(SysTask *task, void *data); // Task_PlayFaintingSequence +BOOL LONG_CALL IsBattlerOnField(struct BattleStruct *ctx, int battlerId); + #endif // BATTLE_H diff --git a/rom.ld b/rom.ld index ba5ebd2e1c..12496f1fc1 100644 --- a/rom.ld +++ b/rom.ld @@ -225,6 +225,7 @@ GetHeldItemFlingPower = 0x022558F8 | 1; GetHeldItemModifier = 0x02255844 | 1; GetNaturalGiftPower = 0x02255890 | 1; GetNaturalGiftType = 0x022558A4 | 1; +CalculateTypeEffectiveness = 0x0225264C | 1; GetArceusType = 0x02071C28 | 1; BattleWorkBattleStatusFlagGet = 0x0223B514 | 1; diff --git a/src/battle/battle_script_commands.c b/src/battle/battle_script_commands.c index dc08ec9697..6fcb97e57b 100644 --- a/src/battle/battle_script_commands.c +++ b/src/battle/battle_script_commands.c @@ -3147,6 +3147,72 @@ BOOL btl_scr_cmd_102_removeentryhazardfromqueue(void *bsys UNUSED, struct Battle return FALSE; } +BOOL BtlCmd_CheckSpikes(void *bw, struct BattleStruct *ctx) +{ + IncrementBattleScriptPtr(ctx, 1); + + int category = read_battle_script_param(ctx); + int adrs = read_battle_script_param(ctx); + + int battlerId = GrabClientFromBattleScriptParam(bw, ctx, category); + int fieldSide = (1 & battlerId); + + if (ctx->scw[fieldSide].spikesLayers && IsBattlerOnField(ctx, battlerId)) { + ctx->hp_calc_work = (5 - ctx->scw[fieldSide].spikesLayers) * 2; + ctx->hp_calc_work = BattleDamageDivide(ctx->battlemon[battlerId].maxhp * -1, ctx->hp_calc_work); + } else { + IncrementBattleScriptPtr(ctx, adrs); + } + + return FALSE; +} + +BOOL BtlCmd_CheckStealthRock(void *bw, struct BattleStruct *ctx) +{ + IncrementBattleScriptPtr(ctx, 1); + + int category = read_battle_script_param(ctx); + int adrs = read_battle_script_param(ctx); + + int battlerId = GrabClientFromBattleScriptParam(bw, ctx, category); + int fieldSide = (1 & battlerId); + + // TODO deal with terra + int type1 = GetBattlerVar(ctx, battlerId, BMON_DATA_TYPE_1, NULL); + int type2 = GetBattlerVar(ctx, battlerId, BMON_DATA_TYPE_2, NULL); + + if (ctx->side_condition[fieldSide] & SIDE_STATUS_STEALTH_ROCK && IsBattlerOnField(ctx, battlerId)) { + switch (CalculateTypeEffectiveness(TYPE_ROCK, type1, type2)) { + case 160: + ctx->hp_calc_work = 2; + break; + case 80: + ctx->hp_calc_work = 4; + break; + case 40: + ctx->hp_calc_work = 8; + break; + case 20: + ctx->hp_calc_work = 16; + break; + case 10: + ctx->hp_calc_work = 32; + break; + case 0: + IncrementBattleScriptPtr(ctx, adrs); + return FALSE; + default: + GF_ASSERT(FALSE); + break; + } + ctx->hp_calc_work = BattleDamageDivide(ctx->battlemon[battlerId].maxhp * -1, ctx->hp_calc_work); + } else { + IncrementBattleScriptPtr(ctx, adrs); + } + + return FALSE; +} + BOOL btl_scr_cmd_103_checkprotectcontactmoves(void *bsys UNUSED, struct BattleStruct *ctx) { IncrementBattleScriptPtr(ctx, 1); @@ -5895,13 +5961,13 @@ BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *c int noActivation = read_battle_script_param(ctx); - if (ctx->battlemon[client_no].hp + if (IsBattlerOnField(ctx, client_no) && (ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) && ((GetBattlerAbility(ctx, client_no) == ABILITY_WIMP_OUT) // not affected by Mold Breaker || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) - && ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) + //&& ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) // unsure if bug && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2)) - // EE can be triggered by any residual damage, such as Burn, Sandstorm, etc + // EE can be triggered by any residual damage, such as Burn, Sandstorm, Curse, etc && ((ctx->battlemon[client_no].hp - ctx->hp_calc_work) > (s32)ctx->battlemon[client_no].maxhp / 2)) { ctx->addeffect_type = SIDE_EFFECT_TYPE_ABILITY; ctx->state_client = client_no; diff --git a/src/battle/other_battle_calculators.c b/src/battle/other_battle_calculators.c index 133d2ee098..f01fb3a98a 100644 --- a/src/battle/other_battle_calculators.c +++ b/src/battle/other_battle_calculators.c @@ -4624,3 +4624,14 @@ BOOL LONG_CALL ShouldUseNormalTypeEffCalc(struct BattleStruct *ctx, int attack_c return ret; } + +BOOL LONG_CALL IsBattlerOnField(struct BattleStruct *ctx, int battlerId) +{ + if (ctx->battlemon[battlerId].hp == 0) { + return FALSE; + } + if (ctx->client_status[battlerId] & 1) { // switched out + return FALSE; + } + return TRUE; +} diff --git a/src/individual/ServerDoPostMoveEffects.c b/src/individual/ServerDoPostMoveEffects.c index a25485011d..747808c116 100644 --- a/src/individual/ServerDoPostMoveEffects.c +++ b/src/individual/ServerDoPostMoveEffects.c @@ -2662,7 +2662,8 @@ int LONG_CALL Activate_WimpOut_EmergencyExit(void *bsys, struct BattleStruct *ct if ((ctx->currentMoveSwitchStatus < CURRENT_MOVE_SWITCH_PENDING) && ((GetBattlerAbility(ctx, client_no) == ABILITY_WIMP_OUT) // not affected by Mold Breaker || (GetBattlerAbility(ctx, client_no) == ABILITY_EMERGENCY_EXIT)) - && ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) + && (ctx->pursuitContext.isActive == FALSE) + //&& ((ctx->battlemon[client_no].effect_of_moves & MOVE_EFFECT_FLAG_SEMI_INVULNERABLE) == 0) // unsure if bug && (ctx->battlemon[client_no].hp <= (s32)(ctx->battlemon[client_no].maxhp / 2))) { BOOL canActivate = FALSE; if (ctx->attack_client == client_no) { diff --git a/src/individual/ServerFieldConditionCheck.c b/src/individual/ServerFieldConditionCheck.c index 11896d2e5d..c2657999ae 100644 --- a/src/individual/ServerFieldConditionCheck.c +++ b/src/individual/ServerFieldConditionCheck.c @@ -724,7 +724,8 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) if ((sp->battlemon[battlerId].effect_of_moves & MOVE_EFFECT_FLAG_LEECH_SEED) && (sp->battlemon[sp->battlemon[battlerId].effect_of_moves & MOVE_EFFECT_FLAG_LEECH_SEED_RECIPIENT].hp != 0) - && (GetBattlerAbility(sp, battlerId) != ABILITY_MAGIC_GUARD && sp->battlemon[battlerId].hp != 0)) { + && (GetBattlerAbility(sp, battlerId) != ABILITY_MAGIC_GUARD) + && IsBattlerOnField(sp, battlerId)) { sp->attack_client_work = sp->battlemon[battlerId].effect_of_moves & MOVE_EFFECT_FLAG_LEECH_SEED_RECIPIENT; sp->defence_client_work = battlerId; LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_LEECH_SEED_EFFECT); @@ -751,14 +752,14 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) while (sp->scc_work < client_set_max) { battlerId = sp->turnOrder[sp->scc_work]; - if ((sp->battlemon[battlerId].condition & STATUS_POISON) && sp->battlemon[battlerId].hp != 0) { + if ((sp->battlemon[battlerId].condition & STATUS_POISON) && IsBattlerOnField(sp, battlerId)) { sp->battlerIdTemp = battlerId; sp->hp_calc_work = BattleDamageDivide(sp->battlemon[battlerId].maxhp * -1, 8); LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_POISON_DAMAGE); sp->next_server_seq_no = sp->server_seq_no; sp->server_seq_no = 22; ret = 1; - } else if ((sp->battlemon[battlerId].condition & STATUS_BAD_POISON) && sp->battlemon[battlerId].hp != 0) { + } else if ((sp->battlemon[battlerId].condition & STATUS_BAD_POISON) && IsBattlerOnField(sp, battlerId)) { sp->battlerIdTemp = battlerId; sp->hp_calc_work = BattleDamageDivide(sp->battlemon[battlerId].maxhp, 16); if ((sp->battlemon[battlerId].condition & STATUS_POISON_COUNT) != STATUS_POISON_COUNT) { @@ -791,7 +792,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) while (sp->scc_work < client_set_max) { battlerId = sp->turnOrder[sp->scc_work]; - if ((sp->battlemon[battlerId].condition & STATUS_BURN) && sp->battlemon[battlerId].hp != 0) { + if ((sp->battlemon[battlerId].condition & STATUS_BURN) && IsBattlerOnField(sp, battlerId)) { sp->battlerIdTemp = battlerId; LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_BURN_DAMAGE); sp->next_server_seq_no = sp->server_seq_no; @@ -818,7 +819,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) while (sp->scc_work < client_set_max) { battlerId = sp->turnOrder[sp->scc_work]; - if ((sp->battlemon[battlerId].condition2 & STATUS2_NIGHTMARE) && sp->battlemon[battlerId].hp != 0) { + if ((sp->battlemon[battlerId].condition2 & STATUS2_NIGHTMARE) && IsBattlerOnField(sp, battlerId)) { if (sp->battlemon[battlerId].condition & STATUS_SLEEP) { sp->battlerIdTemp = battlerId; LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_NIGHTMARE_EFFECT); @@ -849,7 +850,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) while (sp->scc_work < client_set_max) { battlerId = sp->turnOrder[sp->scc_work]; - if ((sp->battlemon[battlerId].condition2 & STATUS2_CURSE) && sp->battlemon[battlerId].hp != 0) { + if ((sp->battlemon[battlerId].condition2 & STATUS2_CURSE) && IsBattlerOnField(sp, battlerId)) { sp->battlerIdTemp = battlerId; LoadBattleSubSeqScript(sp, ARC_BATTLE_SUB_SEQ, BATTLE_SUBSCRIPT_CURSE_DAMAGE); sp->next_server_seq_no = sp->server_seq_no; @@ -876,7 +877,7 @@ void ServerFieldConditionCheck(void *bw, struct BattleStruct *sp) while (sp->scc_work < client_set_max) { battlerId = sp->turnOrder[sp->scc_work]; - if (sp->binding_turns[battlerId] && sp->battlemon[battlerId].hp != 0) { + if (sp->binding_turns[battlerId] && IsBattlerOnField(sp, battlerId)) { // sp->battlemon[battlerId].condition2 -= 1 << 13; sp->binding_turns[battlerId]--; if (sp->binding_turns[battlerId]) { From b4e32e0298bda5865e305291082b1e03f080ab34 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Mon, 12 Oct 2026 14:51:13 +0200 Subject: [PATCH 11/13] reset hazards queue when EE triggers --- .../subscripts/subscript_0009_SWITCH_POKEMON.s | 1 + .../subscripts/subscript_0099_HAZARDS_CHECK.s | 2 ++ .../emergency_exit/residual_damage_burn_leech_seed.c | 3 +-- data/battle_tests/abilities/emergency_exit/stealth_rocks.c | 7 +++++-- src/battle/battle_script_commands.c | 1 + src/test_battle.c | 2 +- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/data/battle_scripts/subscripts/subscript_0009_SWITCH_POKEMON.s b/data/battle_scripts/subscripts/subscript_0009_SWITCH_POKEMON.s index 8e9afe5369..fad7578165 100644 --- a/data/battle_scripts/subscripts/subscript_0009_SWITCH_POKEMON.s +++ b/data/battle_scripts/subscripts/subscript_0009_SWITCH_POKEMON.s @@ -49,6 +49,7 @@ _047: WaitTime 72 HealthbarSlideIn BATTLER_CATEGORY_SWITCHED_MON Wait + SetCurrentMoveSwitchingStatus CURRENT_MOVE_NO_SWITCH Call BATTLE_SUBSCRIPT_HEALING_WISH Call BATTLE_SUBSCRIPT_HAZARDS_CHECK CompareVarToValue OPCODE_GT, BSCRIPT_VAR_BATTLER_FAINTED, BATTLER_ENEMY2, _081 diff --git a/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s b/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s index 53f40f5187..b8edad4cd0 100644 --- a/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s +++ b/data/battle_scripts/subscripts/subscript_0099_HAZARDS_CHECK.s @@ -53,6 +53,7 @@ _checkSpikes: WaitButtonABTime 30 TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noWimpOutFromSpikes Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT + GoToSubscript BATTLE_SUBSCRIPT_SHOW_PARTY_LIST _noWimpOutFromSpikes: GoTo _landingPad @@ -130,6 +131,7 @@ _checkStealthRock: WaitButtonABTime 30 TryActivateEmergencyExit BATTLER_CATEGORY_MSG_BATTLER_TEMP, _noWimpOutFromRocks Call BATTLE_SUBSCRIPT_HANDLE_WIMP_OUT_EMERGENCY_EXIT + GoToSubscript BATTLE_SUBSCRIPT_SHOW_PARTY_LIST _noWimpOutFromRocks: GoTo _landingPad diff --git a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c index 7fe88c46a8..818ac5572b 100644 --- a/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c +++ b/data/battle_tests/abilities/emergency_exit/residual_damage_burn_leech_seed.c @@ -118,13 +118,12 @@ BEGIN_TEST .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod was seeded!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Golisopod's health is sapped by Leech Seed!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "TGolisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_DOES_NOT_CONTAIN, .expectationValue.message = "The opposing Golisopod is hurt by its burn!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Shuckle is hurt by its burn!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Blaziken's Speed rose!" }, }, - .knownFailing = TRUE, } END_TEST diff --git a/data/battle_tests/abilities/emergency_exit/stealth_rocks.c b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c index 1147c99319..a7e2bbd0b3 100644 --- a/data/battle_tests/abilities/emergency_exit/stealth_rocks.c +++ b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c @@ -105,9 +105,12 @@ BEGIN_TEST } }, .expectations = { { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Litten used Sleep Talk!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten used Stealth Rock!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Golisopod" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Shuckle used Stealth Rock!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones float in the air around the opposing side!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Golisopod!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones dug into the opposing Golisopod!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones dug into the opposing Litten!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, }, diff --git a/src/battle/battle_script_commands.c b/src/battle/battle_script_commands.c index 6fcb97e57b..baa2eb06ae 100644 --- a/src/battle/battle_script_commands.c +++ b/src/battle/battle_script_commands.c @@ -5972,6 +5972,7 @@ BOOL btl_scr_cmd_12B_TryActivateEmergencyExit(void *bsys, struct BattleStruct *c ctx->addeffect_type = SIDE_EFFECT_TYPE_ABILITY; ctx->state_client = client_no; ctx->battlerIdTemp = client_no; + ctx->hazardQueueTracker = 0; // TODO: script command for this would be better? } else { IncrementBattleScriptPtr(ctx, noActivation); } diff --git a/src/test_battle.c b/src/test_battle.c index 2f7ee51d2e..21942cdd5d 100644 --- a/src/test_battle.c +++ b/src/test_battle.c @@ -679,7 +679,7 @@ static int TestBattle_FindNextLivePartySlot(struct BattleSystem *bsys, struct Ba static BOOL TestBattle_ShouldAutoSelectKOSwitch(struct BattleSystem *bsys, struct BattleStruct *ctx, int battler) { return sCurrentScenario != NULL - && (battler == BATTLER_PLAYER_FIRST || battler == BATTLER_PLAYER_SECOND) + //&& (battler == BATTLER_PLAYER_FIRST || battler == BATTLER_PLAYER_SECOND) // do it for all && (ctx->client_status[battler] & 1) && TestBattle_FindNextLivePartySlot(bsys, ctx, battler) != 6; } From d9109cc6bbfa07c4d81a82d7fe779ad81ed8633f Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Mon, 12 Oct 2026 15:02:01 +0200 Subject: [PATCH 12/13] all EE test work now --- data/battle_tests/abilities/emergency_exit/stealth_rocks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/battle_tests/abilities/emergency_exit/stealth_rocks.c b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c index a7e2bbd0b3..4f2dc4270d 100644 --- a/data/battle_tests/abilities/emergency_exit/stealth_rocks.c +++ b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c @@ -110,10 +110,9 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Golisopod!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones dug into the opposing Golisopod!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones dug into the opposing Litten!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones dug into the opposing Litten!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, }, - .knownFailing = TRUE, } END_TEST From a3d17f165dd9401c586528657102dec298b95a72 Mon Sep 17 00:00:00 2001 From: kumakuma1 Date: Mon, 12 Oct 2026 17:32:08 +0200 Subject: [PATCH 13/13] tspikes check if battler is on field --- .../abilities/emergency_exit/stealth_rocks.c | 13 ++++++++----- src/battle/battle_script_commands.c | 8 +++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/data/battle_tests/abilities/emergency_exit/stealth_rocks.c b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c index 4f2dc4270d..43cea06c53 100644 --- a/data/battle_tests/abilities/emergency_exit/stealth_rocks.c +++ b/data/battle_tests/abilities/emergency_exit/stealth_rocks.c @@ -13,7 +13,7 @@ BEGIN_TEST .form = 0, .ability = ABILITY_GLUTTONY, .item = ITEM_NONE, - .moves = { MOVE_STEALTH_ROCK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, + .moves = { MOVE_STEALTH_ROCK, MOVE_TOXIC_SPIKES, MOVE_NONE, MOVE_NONE }, .hp = FULL_HP, .status = 0, .condition2 = 0, @@ -42,7 +42,7 @@ BEGIN_TEST .level = 50, .form = 0, .ability = ABILITY_EMERGENCY_EXIT, - .item = ITEM_NONE, + .item = ITEM_LUM_BERRY, .moves = { MOVE_SLEEP_TALK, MOVE_NONE, MOVE_NONE, MOVE_NONE }, .hp = 76, // 150 .status = 0, @@ -65,13 +65,13 @@ BEGIN_TEST { .species = SPECIES_NONE } }, .playerScript = { { { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, + { ACTION_MOVE_SLOT_2, BATTLER_ENEMY_FIRST }, { ACTION_MOVE_SLOT_1, BATTLER_ENEMY_FIRST }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, }, { { ACTION_NONE, 0 }, @@ -84,6 +84,7 @@ BEGIN_TEST { ACTION_NONE, 0 }, } }, .enemyScript = { { + { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, { ACTION_MOVE_SLOT_1, BATTLER_PLAYER_FIRST }, { ACTION_SWITCH_SLOT_1, 0 }, { ACTION_NONE, 0 }, @@ -91,7 +92,6 @@ BEGIN_TEST { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, { ACTION_NONE, 0 }, - { ACTION_NONE, 0 }, }, { { ACTION_NONE, 0 }, @@ -107,11 +107,14 @@ BEGIN_TEST { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Litten used Sleep Talk!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Shuckle used Stealth Rock!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones float in the air around the opposing side!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Shuckle used Toxic Spikes!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Poison spikes were scattered on the ground all around the opposing side!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Golisopod!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones dug into the opposing Golisopod!" }, - { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Golisopod's EmergencyExit" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE_DOES_NOT_CONTAIN, .expectationValue.message = "was poisoned!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE_CONTAINS, .expectationValue.message = "sent out Litten" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Pointed stones dug into the opposing Litten!" }, + { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "The opposing Litten was poisoned!" }, { .expectationType = EXPECTATION_TYPE_MESSAGE, .expectationValue.message = "Litten's Intimidate" }, }, } diff --git a/src/battle/battle_script_commands.c b/src/battle/battle_script_commands.c index baa2eb06ae..55f60dec47 100644 --- a/src/battle/battle_script_commands.c +++ b/src/battle/battle_script_commands.c @@ -4694,9 +4694,11 @@ BOOL BtlCmd_CheckToxicSpikes(struct BattleSystem *bsys, struct BattleStruct *ctx int fieldSide = IsClientEnemy(bsys, battlerID); if (ctx->scw[fieldSide].toxicSpikesLayers) { - ctx->calc_work = ctx->scw[fieldSide].toxicSpikesLayers; - ctx->addeffect_type = SIDE_EFFECT_TYPE_TOXIC_SPIKES; - ctx->state_client = battlerID; + if (IsBattlerOnField(ctx, battlerID)) { + ctx->calc_work = ctx->scw[fieldSide].toxicSpikesLayers; + ctx->addeffect_type = SIDE_EFFECT_TYPE_TOXIC_SPIKES; + ctx->state_client = battlerID; + } if (HasType(ctx, battlerID, TYPE_POISON)) { ctx->side_condition[fieldSide] &= ~SIDE_EFFECT_TYPE_TOXIC_SPIKES; ctx->scw[fieldSide].toxicSpikesLayers = 0;