From ae482ed1f4648fb5f09e0c4c382ff401ce2cec96 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Mon, 12 Dec 2022 21:08:46 -0300 Subject: [PATCH 01/40] Implemented Ceaseless Edge's effect --- data/battle_scripts_1.s | 35 +++++++++++++++++++++++++ include/constants/battle_move_effects.h | 3 ++- src/data/battle_moves.h | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 94a220608..d1d1e670f 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -416,6 +416,18 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectDoubleShock @ EFFECT_DOUBLE_SHOCK .4byte BattleScript_EffectSpecialAttackUpHit @ EFFECT_SPECIAL_ATTACK_UP_HIT .4byte BattleScript_EffectVictoryDance @ EFFECT_VICTORY_DANCE + .4byte BattleScript_EffectCeaselessEdge @ EFFECT_CEASELESS_EDGE + +BattleScript_EffectCeaselessEdge:: + call BattleScript_EffectHit_Ret + trysetspikes BattleScript_ButItFailedAtkStringPpReduce + attackstring + ppreduce + attackanimation + waitanimation + printstring STRINGID_SPIKESSCATTERED + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd BattleScript_AffectionBasedEndurance:: playanimation BS_TARGET, B_ANIM_AFFECTION_HANGED_ON @@ -3029,6 +3041,29 @@ BattleScript_MoveEnd:: moveendall end +BattleScript_EffectHit_Ret:: + attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce + critcalc + damagecalc + adjustdamage + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage B_WAIT_TIME_LONG + resultmessage + waitmessage B_WAIT_TIME_LONG + seteffectwithchance + tryfaintmon BS_TARGET + return + BattleScript_EffectNaturalGift: attackcanceler attackstring diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 5af1017a9..1ac102c35 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -397,7 +397,8 @@ #define EFFECT_DOUBLE_SHOCK 391 #define EFFECT_SPECIAL_ATTACK_UP_HIT 392 #define EFFECT_VICTORY_DANCE 393 +#define EFFECT_CEASELESS_EDGE 394 -#define NUM_BATTLE_MOVE_EFFECTS 394 +#define NUM_BATTLE_MOVE_EFFECTS 395 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index ef4d15039..225da6ff7 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13598,7 +13598,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_CEASELESS_EDGE] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_CEASELESS_EDGE, + .effect = EFFECT_CEASELESS_EDGE, .power = 65, .type = TYPE_DARK, .accuracy = 90, From 3ce486976640d517f34188be08124bce9461d33a Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Mon, 12 Dec 2022 21:41:13 -0300 Subject: [PATCH 02/40] Implemented Shell Trap's effect --- data/battle_scripts_1.s | 18 ++++++++++++++++++ include/battle_scripts.h | 1 + include/constants/battle_move_effects.h | 3 ++- include/constants/battle_string_ids.h | 4 +++- src/battle_main.c | 13 ++++++++----- src/battle_message.c | 4 ++++ src/battle_script_commands.c | 7 ++++--- src/data/battle_moves.h | 2 +- 8 files changed, 41 insertions(+), 11 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d1d1e670f..f2a0223cf 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -417,6 +417,24 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectSpecialAttackUpHit @ EFFECT_SPECIAL_ATTACK_UP_HIT .4byte BattleScript_EffectVictoryDance @ EFFECT_VICTORY_DANCE .4byte BattleScript_EffectCeaselessEdge @ EFFECT_CEASELESS_EDGE + .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP + +BattleScript_EffectShellTrap: + jumpifnodamage BattleScript_EffectShellTrap_Failed + goto BattleScript_EffectHit + +BattleScript_EffectShellTrap_Failed: + printstring STRINGID_ATTACKERSHELLTRAPDIDNTWORK + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + +BattleScript_ShellTrapSetUp:: + printstring STRINGID_EMPTYSTRING3 + waitmessage 1 + playanimation BS_ATTACKER, B_ANIM_SHELL_TRAP_SETUP, NULL + printstring STRINGID_ATTACKERSETASHELLTRAP + waitmessage B_WAIT_TIME_LONG + end2 BattleScript_EffectCeaselessEdge:: call BattleScript_EffectHit_Ret diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 75afdbf63..89ad9302b 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -433,6 +433,7 @@ extern const u8 BattleScript_MultiHitPrintStrings[]; extern const u8 BattleScript_BurnUpRemoveType[]; extern const u8 BattleScript_TargetAbilityStatRaiseRet[]; extern const u8 BattleScript_DoubleShockRemoveType[]; +extern const u8 BattleScript_ShellTrapSetUp[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 1ac102c35..c16084c30 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -398,7 +398,8 @@ #define EFFECT_SPECIAL_ATTACK_UP_HIT 392 #define EFFECT_VICTORY_DANCE 393 #define EFFECT_CEASELESS_EDGE 394 +#define EFFECT_SHELL_TRAP 395 -#define NUM_BATTLE_MOVE_EFFECTS 395 +#define NUM_BATTLE_MOVE_EFFECTS 396 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 8dd94cca4..64ddcbebd 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -632,8 +632,10 @@ #define STRINGID_TARGETTOUGHEDITOUT 630 #define STRINGID_ATTACKERLOSTELECTRICTYPE 631 #define STRINGID_ATTACKERSWITCHEDSTATWITHTARGET 632 +#define STRINGID_ATTACKERSETASHELLTRAP 633 +#define STRINGID_ATTACKERSHELLTRAPDIDNTWORK 634 -#define BATTLESTRINGS_COUNT 633 +#define BATTLESTRINGS_COUNT 635 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/src/battle_main.c b/src/battle_main.c index d138cf231..8ba87133e 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -101,7 +101,7 @@ static void SetActionsAndBattlersTurnOrder(void); static void UpdateBattlerPartyOrdersOnSwitch(void); static bool8 AllAtActionConfirmed(void); static void TryChangeTurnOrder(void); -static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void); +static void CheckChosenMoveForEffectsBeforeTurnStarts(void); static void CheckMegaEvolutionBeforeTurn(void); static void CheckQuickClaw_CustapBerryActivation(void); static void FreeResetData_ReturnToOvOrDoEvolutions(void); @@ -4878,7 +4878,7 @@ static void CheckMegaEvolutionBeforeTurn(void) } #if B_MEGA_EVO_TURN_ORDER <= GEN_6 - gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; + gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; gBattleStruct->focusPunchBattlerId = 0; #else gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved @@ -4903,11 +4903,11 @@ static void TryChangeTurnOrder(void) } } } - gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; + gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; gBattleStruct->focusPunchBattlerId = 0; } -static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) +static void CheckChosenMoveForEffectsBeforeTurnStarts(void) { u32 i; @@ -4921,7 +4921,7 @@ static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) && !(gDisableStructs[gBattlerAttacker].truantCounter) && !(gProtectStructs[gActiveBattler].noValidMoves)) { - switch(gChosenMoveByBattler[gActiveBattler]) + switch (gChosenMoveByBattler[gActiveBattler]) { case MOVE_FOCUS_PUNCH: BattleScriptExecute(BattleScript_FocusPunchSetUp); @@ -4929,6 +4929,9 @@ static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) case MOVE_BEAK_BLAST: BattleScriptExecute(BattleScript_BeakBlastSetUp); return; + case MOVE_SHELL_TRAP: + BattleScriptExecute(BattleScript_ShellTrapSetUp); + return; } } } diff --git a/src/battle_message.c b/src/battle_message.c index 031261a28..60578897e 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -761,9 +761,13 @@ static const u8 sText_AttackerMeltedTheIce[] = _("{B_ATK_NAME_WITH_PREFIX} melte static const u8 sText_TargetToughedItOut[] = _("{B_DEF_NAME_WITH_PREFIX} toughed it out\nto show you its best side!"); static const u8 sText_AttackerLostElectricType[] = _("{B_ATK_NAME_WITH_PREFIX} used up all\nof its electricity!"); static const u8 sText_AttackerSwitchedStatWithTarget[] = _("{B_ATK_NAME_WITH_PREFIX} switched {B_BUFF1}\nwith its target!"); +static const u8 sText_AttackerSetAShellTrap[] = _("{B_ATK_NAME_WITH_PREFIX} set\na shell trap!"); +static const u8 sText_AttackerShellTrapDidntWork[] = _("{B_ATK_NAME_WITH_PREFIX}'s shell trap\ndidn't work!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_ATTACKERSHELLTRAPDIDNTWORK - BATTLESTRINGS_TABLE_START] = sText_AttackerShellTrapDidntWork, + [STRINGID_ATTACKERSETASHELLTRAP - BATTLESTRINGS_TABLE_START] = sText_AttackerSetAShellTrap, [STRINGID_ATTACKERSWITCHEDSTATWITHTARGET - BATTLESTRINGS_TABLE_START] = sText_AttackerSwitchedStatWithTarget, [STRINGID_TARGETTOUGHEDITOUT - BATTLESTRINGS_TABLE_START] = sText_TargetToughedItOut, [STRINGID_ATTACKERMELTEDTHEICE - BATTLESTRINGS_TABLE_START] = sText_AttackerMeltedTheIce, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 9c780c28e..3d85d1379 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -10031,7 +10031,7 @@ static void Cmd_various(void) gBattleMons[gActiveBattler].item = gLastUsedItem; break; case VARIOUS_SET_BEAK_BLAST: - gProtectStructs[gBattlerAttacker].beakBlastCharge = TRUE; + gProtectStructs[gActiveBattler].beakBlastCharge = TRUE; break; case VARIOUS_SWAP_SIDE_STATUSES: CourtChangeSwapSideStatuses(); @@ -11926,8 +11926,9 @@ static void Cmd_trysetencore(void) } if (gLastMoves[gBattlerTarget] == MOVE_STRUGGLE - || gLastMoves[gBattlerTarget] == MOVE_ENCORE - || gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE) + || gLastMoves[gBattlerTarget] == MOVE_ENCORE + || gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE + || gLastMoves[gBattlerTarget] == MOVE_SHELL_TRAP) { i = MAX_MON_MOVES; } diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 225da6ff7..6badf0ee5 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -11616,7 +11616,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_SHELL_TRAP] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_SHELL_TRAP, + .effect = EFFECT_SHELL_TRAP, .power = 150, .type = TYPE_FIRE, .accuracy = 100, From 2cb9400ef0b6b732da416d10a12065ffd38191e4 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Tue, 13 Dec 2022 02:19:41 -0300 Subject: [PATCH 03/40] Implemented Dire Claw's effect --- data/battle_scripts_1.s | 5 +++++ include/constants/battle.h | 3 ++- include/constants/battle_move_effects.h | 3 ++- src/battle_script_commands.c | 16 ++++++++++++++++ src/data/battle_moves.h | 4 ++-- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index f2a0223cf..b3313abbe 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -418,6 +418,11 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectVictoryDance @ EFFECT_VICTORY_DANCE .4byte BattleScript_EffectCeaselessEdge @ EFFECT_CEASELESS_EDGE .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP + .4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW + +BattleScript_EffectDireClaw:: + setmoveeffect MOVE_EFFECT_DIRE_CLAW + goto BattleScript_EffectHit BattleScript_EffectShellTrap: jumpifnodamage BattleScript_EffectShellTrap_Failed diff --git a/include/constants/battle.h b/include/constants/battle.h index be44c21cb..fa9d4bd18 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -371,8 +371,9 @@ #define MOVE_EFFECT_RELIC_SONG 69 #define MOVE_EFFECT_TRAP_BOTH 70 #define MOVE_EFFECT_DOUBLE_SHOCK 71 +#define MOVE_EFFECT_DIRE_CLAW 72 -#define NUM_MOVE_EFFECTS 72 +#define NUM_MOVE_EFFECTS 73 #define MOVE_EFFECT_AFFECTS_USER 0x4000 #define MOVE_EFFECT_CERTAIN 0x8000 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index c16084c30..fa3184855 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -399,7 +399,8 @@ #define EFFECT_VICTORY_DANCE 393 #define EFFECT_CEASELESS_EDGE 394 #define EFFECT_SHELL_TRAP 395 +#define EFFECT_DIRE_CLAW 396 -#define NUM_BATTLE_MOVE_EFFECTS 396 +#define NUM_BATTLE_MOVE_EFFECTS 397 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3d85d1379..e5d7935fd 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3647,6 +3647,22 @@ void SetMoveEffect(bool32 primary, u32 certain) BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_DoubleShockRemoveType; break; + case MOVE_EFFECT_DIRE_CLAW: + if (gBattleMons[gEffectBattler].status1) + { + gBattlescriptCurrInstr++; + } + else + { + DIRE_CLAW_STATUS_PICK: + gBattleScripting.moveEffect = Random() % 6; + if (gBattleScripting.moveEffect == MOVE_EFFECT_NONE + || gBattleScripting.moveEffect == MOVE_EFFECT_BURN + || gBattleScripting.moveEffect == MOVE_EFFECT_FREEZE) + goto DIRE_CLAW_STATUS_PICK; + SetMoveEffect(FALSE, 0); + } + break; } } } diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 6badf0ee5..6481b7d45 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13279,11 +13279,11 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = #else .power = 60, #endif - .effect = EFFECT_PLACEHOLDER, // EFFECT_DIRE_CLAW, + .effect = EFFECT_DIRE_CLAW, .type = TYPE_POISON, .accuracy = 100, .pp = 15, - .secondaryEffectChance = 0, + .secondaryEffectChance = 50, .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, From 1229fa752a3b711e802f401e4a5e09d3160b9bfd Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 14 Dec 2022 09:14:40 -0300 Subject: [PATCH 04/40] Fixed Triple Axe's target, added its effect and fixed Ceaseless Edge's --- data/battle_scripts_1.s | 12 ++++++++---- include/constants/battle_move_effects.h | 3 ++- src/data/battle_moves.h | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b3313abbe..622463830 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -419,6 +419,14 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectCeaselessEdge @ EFFECT_CEASELESS_EDGE .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP .4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW + .4byte BattleScript_EffectStoneAxe @ EFFECT_STONE_AXE + +BattleScript_EffectStoneAxe:: + call BattleScript_EffectHit_Ret + setstealthrock BattleScript_ButItFailed + printstring STRINGID_POINTEDSTONESFLOAT + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd BattleScript_EffectDireClaw:: setmoveeffect MOVE_EFFECT_DIRE_CLAW @@ -444,10 +452,6 @@ BattleScript_ShellTrapSetUp:: BattleScript_EffectCeaselessEdge:: call BattleScript_EffectHit_Ret trysetspikes BattleScript_ButItFailedAtkStringPpReduce - attackstring - ppreduce - attackanimation - waitanimation printstring STRINGID_SPIKESSCATTERED waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index fa3184855..16aac5284 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -400,7 +400,8 @@ #define EFFECT_CEASELESS_EDGE 394 #define EFFECT_SHELL_TRAP 395 #define EFFECT_DIRE_CLAW 396 +#define EFFECT_STONE_AXE 397 -#define NUM_BATTLE_MOVE_EFFECTS 397 +#define NUM_BATTLE_MOVE_EFFECTS 398 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 6481b7d45..22b695fc4 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13326,13 +13326,13 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_STONE_AXE] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_STONE_AXE, + .effect = EFFECT_STONE_AXE, .power = 65, .type = TYPE_ROCK, .accuracy = 90, .pp = 15, .secondaryEffectChance = 0, - .target = MOVE_TARGET_USER, + .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, .split = SPLIT_PHYSICAL, From 4bb86c5d961a68da83f5798a0dd06be4a8507c97 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 14 Dec 2022 09:33:09 -0300 Subject: [PATCH 05/40] Implemented Barb Barrage's effect --- data/battle_scripts_1.s | 2 ++ include/constants/battle_move_effects.h | 3 ++- src/battle_util.c | 4 ++++ src/data/battle_moves.h | 10 +++++++--- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 622463830..ec604ebc6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -420,6 +420,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP .4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW .4byte BattleScript_EffectStoneAxe @ EFFECT_STONE_AXE + .4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE BattleScript_EffectStoneAxe:: call BattleScript_EffectHit_Ret @@ -3265,6 +3266,7 @@ BattleScript_CantMakeAsleep:: orhalfword gMoveResultFlags, MOVE_RESULT_FAILED goto BattleScript_MoveEnd +BattleScript_EffectBarbBarrage: BattleScript_EffectPoisonHit: setmoveeffect MOVE_EFFECT_POISON goto BattleScript_EffectHit diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 16aac5284..f51ae08c1 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -401,7 +401,8 @@ #define EFFECT_SHELL_TRAP 395 #define EFFECT_DIRE_CLAW 396 #define EFFECT_STONE_AXE 397 +#define EFFECT_BARB_BARRAGE 398 -#define NUM_BATTLE_MOVE_EFFECTS 398 +#define NUM_BATTLE_MOVE_EFFECTS 399 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/src/battle_util.c b/src/battle_util.c index 00e3b869b..08eea7f41 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8483,6 +8483,10 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) basePower = CalcBeatUpPower(); #endif break; + case EFFECT_BARB_BARRAGE: + if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) + basePower *= 2; + break; } // Move-specific base power changes diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 22b695fc4..227f77daf 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13490,12 +13490,16 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_BARB_BARRAGE] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_BARB_BARRAGE, + .effect = EFFECT_BARB_BARRAGE, .power = 60, .type = TYPE_POISON, .accuracy = 100, - .pp = 15, - .secondaryEffectChance = 0, + #if B_UPDATED_MOVE_DATA >= GEN_9 + .pp = 10, + #else + .pp = 15, + #endif + .secondaryEffectChance = 50, .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, From 9972cce0ec3ad30c7acf2c853aec3fc996a93833 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 21 Dec 2022 20:05:10 -0300 Subject: [PATCH 06/40] Updated MOVE_EFFECT_DIRE_CLAW --- src/battle_script_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e5d7935fd..bb0d20199 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3656,7 +3656,7 @@ void SetMoveEffect(bool32 primary, u32 certain) { DIRE_CLAW_STATUS_PICK: gBattleScripting.moveEffect = Random() % 6; - if (gBattleScripting.moveEffect == MOVE_EFFECT_NONE + if (gBattleScripting.moveEffect == 0 || gBattleScripting.moveEffect == MOVE_EFFECT_BURN || gBattleScripting.moveEffect == MOVE_EFFECT_FREEZE) goto DIRE_CLAW_STATUS_PICK; From 0159065deaf90c3d8296c1ecca6b2912fdc6a8b9 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 28 Dec 2022 07:51:11 -0300 Subject: [PATCH 07/40] Removed Shell Trap It's time to let it go for my own mental sanity. Besides, it's a Gen. 7 move. I shouldn't have added it here. --- data/battle_scripts_1.s | 18 ------------------ include/battle_scripts.h | 1 - include/constants/battle_move_effects.h | 9 ++++----- include/constants/battle_string_ids.h | 4 +--- src/battle_main.c | 13 +++++-------- src/battle_message.c | 4 ---- src/battle_script_commands.c | 5 ++--- src/data/battle_moves.h | 2 +- 8 files changed, 13 insertions(+), 43 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index ec604ebc6..49d06a033 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -417,7 +417,6 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectSpecialAttackUpHit @ EFFECT_SPECIAL_ATTACK_UP_HIT .4byte BattleScript_EffectVictoryDance @ EFFECT_VICTORY_DANCE .4byte BattleScript_EffectCeaselessEdge @ EFFECT_CEASELESS_EDGE - .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP .4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW .4byte BattleScript_EffectStoneAxe @ EFFECT_STONE_AXE .4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE @@ -433,23 +432,6 @@ BattleScript_EffectDireClaw:: setmoveeffect MOVE_EFFECT_DIRE_CLAW goto BattleScript_EffectHit -BattleScript_EffectShellTrap: - jumpifnodamage BattleScript_EffectShellTrap_Failed - goto BattleScript_EffectHit - -BattleScript_EffectShellTrap_Failed: - printstring STRINGID_ATTACKERSHELLTRAPDIDNTWORK - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd - -BattleScript_ShellTrapSetUp:: - printstring STRINGID_EMPTYSTRING3 - waitmessage 1 - playanimation BS_ATTACKER, B_ANIM_SHELL_TRAP_SETUP, NULL - printstring STRINGID_ATTACKERSETASHELLTRAP - waitmessage B_WAIT_TIME_LONG - end2 - BattleScript_EffectCeaselessEdge:: call BattleScript_EffectHit_Ret trysetspikes BattleScript_ButItFailedAtkStringPpReduce diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 89ad9302b..75afdbf63 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -433,7 +433,6 @@ extern const u8 BattleScript_MultiHitPrintStrings[]; extern const u8 BattleScript_BurnUpRemoveType[]; extern const u8 BattleScript_TargetAbilityStatRaiseRet[]; extern const u8 BattleScript_DoubleShockRemoveType[]; -extern const u8 BattleScript_ShellTrapSetUp[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index f51ae08c1..423fa9a58 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -398,11 +398,10 @@ #define EFFECT_SPECIAL_ATTACK_UP_HIT 392 #define EFFECT_VICTORY_DANCE 393 #define EFFECT_CEASELESS_EDGE 394 -#define EFFECT_SHELL_TRAP 395 -#define EFFECT_DIRE_CLAW 396 -#define EFFECT_STONE_AXE 397 -#define EFFECT_BARB_BARRAGE 398 +#define EFFECT_DIRE_CLAW 395 +#define EFFECT_STONE_AXE 396 +#define EFFECT_BARB_BARRAGE 397 -#define NUM_BATTLE_MOVE_EFFECTS 399 +#define NUM_BATTLE_MOVE_EFFECTS 398 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 64ddcbebd..8dd94cca4 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -632,10 +632,8 @@ #define STRINGID_TARGETTOUGHEDITOUT 630 #define STRINGID_ATTACKERLOSTELECTRICTYPE 631 #define STRINGID_ATTACKERSWITCHEDSTATWITHTARGET 632 -#define STRINGID_ATTACKERSETASHELLTRAP 633 -#define STRINGID_ATTACKERSHELLTRAPDIDNTWORK 634 -#define BATTLESTRINGS_COUNT 635 +#define BATTLESTRINGS_COUNT 633 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/src/battle_main.c b/src/battle_main.c index 8ba87133e..d138cf231 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -101,7 +101,7 @@ static void SetActionsAndBattlersTurnOrder(void); static void UpdateBattlerPartyOrdersOnSwitch(void); static bool8 AllAtActionConfirmed(void); static void TryChangeTurnOrder(void); -static void CheckChosenMoveForEffectsBeforeTurnStarts(void); +static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void); static void CheckMegaEvolutionBeforeTurn(void); static void CheckQuickClaw_CustapBerryActivation(void); static void FreeResetData_ReturnToOvOrDoEvolutions(void); @@ -4878,7 +4878,7 @@ static void CheckMegaEvolutionBeforeTurn(void) } #if B_MEGA_EVO_TURN_ORDER <= GEN_6 - gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; + gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; gBattleStruct->focusPunchBattlerId = 0; #else gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved @@ -4903,11 +4903,11 @@ static void TryChangeTurnOrder(void) } } } - gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; + gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; gBattleStruct->focusPunchBattlerId = 0; } -static void CheckChosenMoveForEffectsBeforeTurnStarts(void) +static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) { u32 i; @@ -4921,7 +4921,7 @@ static void CheckChosenMoveForEffectsBeforeTurnStarts(void) && !(gDisableStructs[gBattlerAttacker].truantCounter) && !(gProtectStructs[gActiveBattler].noValidMoves)) { - switch (gChosenMoveByBattler[gActiveBattler]) + switch(gChosenMoveByBattler[gActiveBattler]) { case MOVE_FOCUS_PUNCH: BattleScriptExecute(BattleScript_FocusPunchSetUp); @@ -4929,9 +4929,6 @@ static void CheckChosenMoveForEffectsBeforeTurnStarts(void) case MOVE_BEAK_BLAST: BattleScriptExecute(BattleScript_BeakBlastSetUp); return; - case MOVE_SHELL_TRAP: - BattleScriptExecute(BattleScript_ShellTrapSetUp); - return; } } } diff --git a/src/battle_message.c b/src/battle_message.c index 60578897e..031261a28 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -761,13 +761,9 @@ static const u8 sText_AttackerMeltedTheIce[] = _("{B_ATK_NAME_WITH_PREFIX} melte static const u8 sText_TargetToughedItOut[] = _("{B_DEF_NAME_WITH_PREFIX} toughed it out\nto show you its best side!"); static const u8 sText_AttackerLostElectricType[] = _("{B_ATK_NAME_WITH_PREFIX} used up all\nof its electricity!"); static const u8 sText_AttackerSwitchedStatWithTarget[] = _("{B_ATK_NAME_WITH_PREFIX} switched {B_BUFF1}\nwith its target!"); -static const u8 sText_AttackerSetAShellTrap[] = _("{B_ATK_NAME_WITH_PREFIX} set\na shell trap!"); -static const u8 sText_AttackerShellTrapDidntWork[] = _("{B_ATK_NAME_WITH_PREFIX}'s shell trap\ndidn't work!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { - [STRINGID_ATTACKERSHELLTRAPDIDNTWORK - BATTLESTRINGS_TABLE_START] = sText_AttackerShellTrapDidntWork, - [STRINGID_ATTACKERSETASHELLTRAP - BATTLESTRINGS_TABLE_START] = sText_AttackerSetAShellTrap, [STRINGID_ATTACKERSWITCHEDSTATWITHTARGET - BATTLESTRINGS_TABLE_START] = sText_AttackerSwitchedStatWithTarget, [STRINGID_TARGETTOUGHEDITOUT - BATTLESTRINGS_TABLE_START] = sText_TargetToughedItOut, [STRINGID_ATTACKERMELTEDTHEICE - BATTLESTRINGS_TABLE_START] = sText_AttackerMeltedTheIce, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bb0d20199..6e0f4c05d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -10047,7 +10047,7 @@ static void Cmd_various(void) gBattleMons[gActiveBattler].item = gLastUsedItem; break; case VARIOUS_SET_BEAK_BLAST: - gProtectStructs[gActiveBattler].beakBlastCharge = TRUE; + gProtectStructs[gBattlerAttacker].beakBlastCharge = TRUE; break; case VARIOUS_SWAP_SIDE_STATUSES: CourtChangeSwapSideStatuses(); @@ -11943,8 +11943,7 @@ static void Cmd_trysetencore(void) if (gLastMoves[gBattlerTarget] == MOVE_STRUGGLE || gLastMoves[gBattlerTarget] == MOVE_ENCORE - || gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE - || gLastMoves[gBattlerTarget] == MOVE_SHELL_TRAP) + || gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE) { i = MAX_MON_MOVES; } diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 227f77daf..40663a421 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -11616,7 +11616,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_SHELL_TRAP] = { - .effect = EFFECT_SHELL_TRAP, + .effect = EFFECT_PLACEHOLDER, // EFFECT_SHELL_TRAP, .power = 150, .type = TYPE_FIRE, .accuracy = 100, From f5c165ff0ccc0801ff0a9b93619103cc81940f91 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 28 Dec 2022 08:18:15 -0300 Subject: [PATCH 08/40] Updated Dire Claw's effect again Thanks to MGriffin for providing a much nicer code for it. --- src/battle_script_commands.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6e0f4c05d..df995ef60 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3654,13 +3654,9 @@ void SetMoveEffect(bool32 primary, u32 certain) } else { - DIRE_CLAW_STATUS_PICK: - gBattleScripting.moveEffect = Random() % 6; - if (gBattleScripting.moveEffect == 0 - || gBattleScripting.moveEffect == MOVE_EFFECT_BURN - || gBattleScripting.moveEffect == MOVE_EFFECT_FREEZE) - goto DIRE_CLAW_STATUS_PICK; - SetMoveEffect(FALSE, 0); + static const u8 sDireClawEffects[] = { MOVE_EFFECT_POISON, MOVE_EFFECT_PARALYSIS, MOVE_EFFECT_SLEEP }; + gBattleScripting.moveEffect = sDireClawEffects[Random() % 3]; + SetMoveEffect(TRUE, 0); } break; } @@ -11942,8 +11938,8 @@ static void Cmd_trysetencore(void) } if (gLastMoves[gBattlerTarget] == MOVE_STRUGGLE - || gLastMoves[gBattlerTarget] == MOVE_ENCORE - || gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE) + || gLastMoves[gBattlerTarget] == MOVE_ENCORE + || gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE) { i = MAX_MON_MOVES; } From 9a6bdfb74f50d9133860944c8824382d907aba4c Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 4 Jan 2023 12:13:34 -0300 Subject: [PATCH 09/40] Added AI conditionals for the new move effects in this batch --- src/battle_ai_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 92866a7c2..5ec8e91aa 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1486,6 +1486,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SPIKES: + case EFFECT_CEASELESS_EDGE: if (gSideTimers[GetBattlerSide(battlerDef)].spikesAmount >= 3) score -= 10; else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) @@ -1493,6 +1494,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; // only one mon needs to set up the last layer of Spikes break; case EFFECT_STEALTH_ROCK: + case EFFECT_STONE_AXE: if (gSideTimers[GetBattlerSide(battlerDef)].stealthRockAmount > 0 || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) //Only one mon needs to set up Stealth Rocks score -= 10; @@ -3472,6 +3474,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TOXIC: case EFFECT_POISON: + case EFFECT_BARB_BARRAGE: IncreasePoisonScore(battlerAtk, battlerDef, move, &score); break; case EFFECT_LIGHT_SCREEN: @@ -3865,7 +3868,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_SPIKES: + case EFFECT_CEASELESS_EDGE: case EFFECT_STEALTH_ROCK: + case EFFECT_STONE_AXE: case EFFECT_STICKY_WEB: case EFFECT_TOXIC_SPIKES: if (AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) @@ -4596,6 +4601,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; }*/ break; + case EFFECT_DIRE_CLAW: + if (gBattleMons[battlerDef].status1 & STATUS1_NONE) + score += 3; + break; case EFFECT_FEINT: if (gBattleMoves[predictedMove].effect == EFFECT_PROTECT) score += 3; @@ -4900,6 +4909,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_HAIL: case EFFECT_GEOMANCY: case EFFECT_VICTORY_DANCE: + case EFFECT_STONE_AXE: score += 2; break; default: From 2414283bb76739b5f24e0926a08e9366744a196d Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 4 Jan 2023 12:32:17 -0300 Subject: [PATCH 10/40] Removed BattleScript_EffectHit_Ret --- data/battle_scripts_1.s | 71 +++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d606809ed..d64336638 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -423,22 +423,62 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE BattleScript_EffectStoneAxe:: - call BattleScript_EffectHit_Ret + attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce + critcalc + damagecalc + adjustdamage + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage B_WAIT_TIME_LONG + resultmessage + waitmessage B_WAIT_TIME_LONG + seteffectwithchance + tryfaintmon BS_TARGET setstealthrock BattleScript_ButItFailed printstring STRINGID_POINTEDSTONESFLOAT waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + moveendall + end BattleScript_EffectDireClaw:: setmoveeffect MOVE_EFFECT_DIRE_CLAW goto BattleScript_EffectHit BattleScript_EffectCeaselessEdge:: - call BattleScript_EffectHit_Ret + attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce + critcalc + damagecalc + adjustdamage + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage B_WAIT_TIME_LONG + resultmessage + waitmessage B_WAIT_TIME_LONG + seteffectwithchance + tryfaintmon BS_TARGET trysetspikes BattleScript_ButItFailedAtkStringPpReduce printstring STRINGID_SPIKESSCATTERED waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + moveendall + end BattleScript_EffectTeatime:: attackcanceler @@ -3151,29 +3191,6 @@ BattleScript_MoveEnd:: moveendall end -BattleScript_EffectHit_Ret:: - attackcanceler - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - ppreduce - critcalc - damagecalc - adjustdamage - attackanimation - waitanimation - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG - seteffectwithchance - tryfaintmon BS_TARGET - return - BattleScript_EffectNaturalGift: attackcanceler attackstring From 7e398ce80e1858833d33110e469becd5c7c334f9 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 4 Jan 2023 12:45:49 -0300 Subject: [PATCH 11/40] Corrected the AI conditionals --- src/battle_ai_main.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 5ec8e91aa..2f9690250 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1486,7 +1486,6 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SPIKES: - case EFFECT_CEASELESS_EDGE: if (gSideTimers[GetBattlerSide(battlerDef)].spikesAmount >= 3) score -= 10; else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) @@ -1494,7 +1493,6 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; // only one mon needs to set up the last layer of Spikes break; case EFFECT_STEALTH_ROCK: - case EFFECT_STONE_AXE: if (gSideTimers[GetBattlerSide(battlerDef)].stealthRockAmount > 0 || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) //Only one mon needs to set up Stealth Rocks score -= 10; @@ -4601,10 +4599,6 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; }*/ break; - case EFFECT_DIRE_CLAW: - if (gBattleMons[battlerDef].status1 & STATUS1_NONE) - score += 3; - break; case EFFECT_FEINT: if (gBattleMoves[predictedMove].effect == EFFECT_PROTECT) score += 3; From 72566e63230497ebf8533d8b254ea33ba7f9216a Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Thu, 5 Jan 2023 03:01:18 -0300 Subject: [PATCH 12/40] Turned Stone Axe's and Ceaseless Edge's effects into MOVE_EFFECTs --- data/battle_scripts_1.s | 24 ++++++++++++++++-------- include/battle_scripts.h | 2 ++ include/constants/battle.h | 4 +++- src/battle_script_commands.c | 20 ++++++++++++++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d64336638..238c0e21b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -441,14 +441,18 @@ BattleScript_EffectStoneAxe:: waitmessage B_WAIT_TIME_LONG resultmessage waitmessage B_WAIT_TIME_LONG - seteffectwithchance tryfaintmon BS_TARGET - setstealthrock BattleScript_ButItFailed - printstring STRINGID_POINTEDSTONESFLOAT - waitmessage B_WAIT_TIME_LONG + setmoveeffect MOVE_EFFECT_STEALTH_ROCK + seteffectprimary moveendall end +BattleScript_StealthRockActivates:: + setstealthrock BattleScript_MoveEnd + printstring STRINGID_POINTEDSTONESFLOAT + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + BattleScript_EffectDireClaw:: setmoveeffect MOVE_EFFECT_DIRE_CLAW goto BattleScript_EffectHit @@ -472,14 +476,18 @@ BattleScript_EffectCeaselessEdge:: waitmessage B_WAIT_TIME_LONG resultmessage waitmessage B_WAIT_TIME_LONG - seteffectwithchance tryfaintmon BS_TARGET - trysetspikes BattleScript_ButItFailedAtkStringPpReduce - printstring STRINGID_SPIKESSCATTERED - waitmessage B_WAIT_TIME_LONG + setmoveeffect MOVE_EFFECT_SPIKES + seteffectprimary moveendall end +BattleScript_SpikesActivate:: + trysetspikes BattleScript_MoveEnd + printstring STRINGID_SPIKESSCATTERED + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + BattleScript_EffectTeatime:: attackcanceler attackstring diff --git a/include/battle_scripts.h b/include/battle_scripts.h index d79566a44..55caf9022 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -448,6 +448,8 @@ extern const u8 BattleScript_SupremeOverlordActivates[]; extern const u8 BattleScript_CostarActivates[]; extern const u8 BattleScript_ToxicDebrisActivates[]; extern const u8 BattleScript_EarthEaterActivates[]; +extern const u8 BattleScript_StealthRockActivates[]; +extern const u8 BattleScript_SpikesActivate[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index fa9d4bd18..e7e65951b 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -372,8 +372,10 @@ #define MOVE_EFFECT_TRAP_BOTH 70 #define MOVE_EFFECT_DOUBLE_SHOCK 71 #define MOVE_EFFECT_DIRE_CLAW 72 +#define MOVE_EFFECT_STEALTH_ROCK 73 +#define MOVE_EFFECT_SPIKES 74 -#define NUM_MOVE_EFFECTS 73 +#define NUM_MOVE_EFFECTS 75 #define MOVE_EFFECT_AFFECTS_USER 0x4000 #define MOVE_EFFECT_CERTAIN 0x8000 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index fcf09641f..9a8a2cabf 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2789,6 +2789,7 @@ void SetMoveEffect(bool32 primary, u32 certain) bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR); u32 flags = 0; u16 battlerAbility; + bool8 activateAfterFaint = FALSE; if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT && gBattleMons[gBattlerTarget].hp != 0 @@ -2806,6 +2807,10 @@ void SetMoveEffect(bool32 primary, u32 certain) gBattleStruct->moveEffect2 = gBattleScripting.moveEffect; gBattlescriptCurrInstr++; return; + case MOVE_EFFECT_STEALTH_ROCK: + case MOVE_EFFECT_SPIKES: + activateAfterFaint = TRUE; + break; } if (gBattleScripting.moveEffect & MOVE_EFFECT_AFFECTS_USER) @@ -2838,6 +2843,7 @@ void SetMoveEffect(bool32 primary, u32 certain) INCREMENT_RESET_RETURN if (gBattleMons[gEffectBattler].hp == 0 + && !activateAfterFaint && gBattleScripting.moveEffect != MOVE_EFFECT_PAYDAY && gBattleScripting.moveEffect != MOVE_EFFECT_STEAL_ITEM && gBattleScripting.moveEffect != MOVE_EFFECT_BUG_BITE) @@ -3659,6 +3665,20 @@ void SetMoveEffect(bool32 primary, u32 certain) SetMoveEffect(TRUE, 0); } break; + case MOVE_EFFECT_STEALTH_ROCK: + if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_STEALTH_ROCK)) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_StealthRockActivates; + } + break; + case MOVE_EFFECT_SPIKES: + if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_SPIKES)) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_SpikesActivate; + } + break; } } } From f39f48fc5102c10eeed978e9832f501c607e64d5 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Thu, 5 Jan 2023 03:07:28 -0300 Subject: [PATCH 13/40] Made MOVE_EFFECT_DIRE_CLAW a tiny bit prettier --- src/battle_script_commands.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 9a8a2cabf..74e5f31ef 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3654,11 +3654,7 @@ void SetMoveEffect(bool32 primary, u32 certain) gBattlescriptCurrInstr = BattleScript_DoubleShockRemoveType; break; case MOVE_EFFECT_DIRE_CLAW: - if (gBattleMons[gEffectBattler].status1) - { - gBattlescriptCurrInstr++; - } - else + if (!gBattleMons[gEffectBattler].status1) { static const u8 sDireClawEffects[] = { MOVE_EFFECT_POISON, MOVE_EFFECT_PARALYSIS, MOVE_EFFECT_SLEEP }; gBattleScripting.moveEffect = sDireClawEffects[Random() % 3]; From fe74dd938b3a6a4547d9f3fbc1fdd6b7396b16c6 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 1 Mar 2023 04:00:19 -0300 Subject: [PATCH 14/40] Small corrections --- data/battle_scripts_1.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 70730d2a9..2556d60e1 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -417,7 +417,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectVictoryDance @ EFFECT_VICTORY_DANCE .4byte BattleScript_EffectTeatime @ EFFECT_TEATIME .4byte BattleScript_EffectAttackUpUserAlly @ EFFECT_ATTACK_UP_USER_ALLY - .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP + .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP .4byte BattleScript_EffectCeaselessEdge @ EFFECT_CEASELESS_EDGE .4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW .4byte BattleScript_EffectStoneAxe @ EFFECT_STONE_AXE @@ -524,7 +524,7 @@ BattleScript_EffectAttackUpUserAlly_AllyAnim: playanimation BS_TARGET, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 printfromtable gStatUpStringIds waitmessage B_WAIT_TIME_LONG - goto BattleScript_EffectAttackUpUserAlly_Ends + goto BattleScript_EffectAttackUpUserAlly_End BattleScript_EffectTeatime:: attackcanceler From fe735f9700262fe0baf103ba77af83c9cf6c59f7 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 1 Mar 2023 20:03:27 -0300 Subject: [PATCH 15/40] Applied review suggestions -Merged EFFECT_CEASELESS_EDGE and EFFECT_STONE_AXE into EFFECT_HIT_SET_ENTRY_HAZARD -Updated gDmgHazardsStringIds and relevant battle scripts. Can't say that I like it at all. String tables are stupid imo. -Coupled Barb Barrage's effect in CalcMoveBasePower with Venoshock's --- data/battle_scripts_1.s | 36 ++++--------------------- include/constants/battle_move_effects.h | 7 +++-- include/constants/battle_string_ids.h | 6 +++++ src/battle_ai_main.c | 5 ++-- src/battle_message.c | 5 +++- src/battle_script_commands.c | 6 +++-- src/battle_util.c | 5 +--- src/data/battle_moves.h | 6 +++-- 8 files changed, 29 insertions(+), 47 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 2556d60e1..425834b7e 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -418,39 +418,13 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectTeatime @ EFFECT_TEATIME .4byte BattleScript_EffectAttackUpUserAlly @ EFFECT_ATTACK_UP_USER_ALLY .4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP - .4byte BattleScript_EffectCeaselessEdge @ EFFECT_CEASELESS_EDGE + .4byte BattleScript_EffectHitSetEntryHazard @ EFFECT_HIT_SET_ENTRY_HAZARD .4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW - .4byte BattleScript_EffectStoneAxe @ EFFECT_STONE_AXE .4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE -BattleScript_EffectStoneAxe:: - attackcanceler - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - ppreduce - critcalc - damagecalc - adjustdamage - attackanimation - waitanimation - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_TARGET - setmoveeffect MOVE_EFFECT_STEALTH_ROCK - seteffectprimary - moveendall - end - BattleScript_StealthRockActivates:: setstealthrock BattleScript_MoveEnd - printstring STRINGID_POINTEDSTONESFLOAT + printfromtable gDmgHazardsStringIds waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd @@ -458,7 +432,7 @@ BattleScript_EffectDireClaw:: setmoveeffect MOVE_EFFECT_DIRE_CLAW goto BattleScript_EffectHit -BattleScript_EffectCeaselessEdge:: +BattleScript_EffectHitSetEntryHazard:: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE attackstring @@ -478,14 +452,14 @@ BattleScript_EffectCeaselessEdge:: resultmessage waitmessage B_WAIT_TIME_LONG tryfaintmon BS_TARGET - setmoveeffect MOVE_EFFECT_SPIKES + argumenttomoveeffect seteffectprimary moveendall end BattleScript_SpikesActivate:: trysetspikes BattleScript_MoveEnd - printstring STRINGID_SPIKESSCATTERED + printfromtable gDmgHazardsStringIds waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 0adee76c9..ad589211c 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -399,11 +399,10 @@ #define EFFECT_TEATIME 393 #define EFFECT_ATTACK_UP_USER_ALLY 394 // Howl 8th Gen #define EFFECT_SHELL_TRAP 395 -#define EFFECT_CEASELESS_EDGE 396 +#define EFFECT_HIT_SET_ENTRY_HAZARD 396 #define EFFECT_DIRE_CLAW 397 -#define EFFECT_STONE_AXE 398 -#define EFFECT_BARB_BARRAGE 399 +#define EFFECT_BARB_BARRAGE 398 -#define NUM_BATTLE_MOVE_EFFECTS 400 +#define NUM_BATTLE_MOVE_EFFECTS 399 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index f3e702ec4..01065e047 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -921,4 +921,10 @@ #define B_MSG_Z_STAT_UP 5 #define B_MSG_Z_HP_TRAP 6 +// gDmgHazardsStringIds +#define B_MSG_PKMNHURTBYSPIKES 0 +#define B_MSG_STEALTHROCKDMG 1 +#define B_MSG_POINTEDSTONESFLOAT 2 +#define B_MSG_SPIKESSCATTERED 3 + #endif // GUARD_CONSTANTS_BATTLE_STRING_IDS_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index bbc1660fa..f61dc49a8 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3872,9 +3872,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_SPIKES: - case EFFECT_CEASELESS_EDGE: + case EFFECT_HIT_SET_ENTRY_HAZARD: case EFFECT_STEALTH_ROCK: - case EFFECT_STONE_AXE: case EFFECT_STICKY_WEB: case EFFECT_TOXIC_SPIKES: if (AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) @@ -4910,7 +4909,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_HAIL: case EFFECT_GEOMANCY: case EFFECT_VICTORY_DANCE: - case EFFECT_STONE_AXE: + case EFFECT_HIT_SET_ENTRY_HAZARD: score += 2; break; default: diff --git a/src/battle_message.c b/src/battle_message.c index 29580ded0..99f88f53b 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -1462,7 +1462,10 @@ const u16 gHealingWishStringIds[] = const u16 gDmgHazardsStringIds[] = { - STRINGID_PKMNHURTBYSPIKES, STRINGID_STEALTHROCKDMG + [B_MSG_PKMNHURTBYSPIKES] = STRINGID_PKMNHURTBYSPIKES, + [B_MSG_STEALTHROCKDMG] = STRINGID_STEALTHROCKDMG, + [B_MSG_POINTEDSTONESFLOAT] = STRINGID_POINTEDSTONESFLOAT, + [B_MSG_SPIKESSCATTERED] = STRINGID_SPIKESSCATTERED }; const u16 gSwitchInAbilityStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index a445786ce..0799cf2f3 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3768,6 +3768,7 @@ void SetMoveEffect(bool32 primary, u32 certain) case MOVE_EFFECT_STEALTH_ROCK: if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_STEALTH_ROCK)) { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_StealthRockActivates; } @@ -3775,6 +3776,7 @@ void SetMoveEffect(bool32 primary, u32 certain) case MOVE_EFFECT_SPIKES: if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_SPIKES)) { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_SpikesActivate; } @@ -7045,7 +7047,7 @@ static void Cmd_switchineffects(void) gBattleMoveDamage = 1; gSideStatuses[GetBattlerSide(gActiveBattler)] |= SIDE_STATUS_SPIKES_DAMAGED; - SetDmgHazardsBattlescript(gActiveBattler, 0); + SetDmgHazardsBattlescript(gActiveBattler, B_MSG_PKMNHURTBYSPIKES); } else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK_DAMAGED) && (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK) @@ -7056,7 +7058,7 @@ static void Cmd_switchineffects(void) gBattleMoveDamage = GetStealthHazardDamage(gBattleMoves[MOVE_STEALTH_ROCK].type, gActiveBattler); if (gBattleMoveDamage != 0) - SetDmgHazardsBattlescript(gActiveBattler, 1); + SetDmgHazardsBattlescript(gActiveBattler, B_MSG_STEALTHROCKDMG); } else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_TOXIC_SPIKES_DAMAGED) && (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_TOXIC_SPIKES) diff --git a/src/battle_util.c b/src/battle_util.c index a94467085..9b3a39406 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8761,10 +8761,6 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) basePower = CalcBeatUpPower(); #endif break; - case EFFECT_BARB_BARRAGE: - if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) - basePower *= 2; - break; } // Move-specific base power changes @@ -9130,6 +9126,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe if (gBattleMons[battlerDef].hp <= (gBattleMons[battlerDef].maxHP / 2)) MulModifier(&modifier, UQ_4_12(2.0)); break; + case EFFECT_BARB_BARRAGE: case EFFECT_VENOSHOCK: if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) MulModifier(&modifier, UQ_4_12(2.0)); diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index cc4e322f2..530dacd13 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13323,7 +13323,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_STONE_AXE] = { - .effect = EFFECT_STONE_AXE, + .effect = EFFECT_HIT_SET_ENTRY_HAZARD, .power = 65, .type = TYPE_ROCK, .accuracy = 90, @@ -13335,6 +13335,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .split = SPLIT_PHYSICAL, .zMovePower = 120, .zMoveEffect = Z_EFFECT_NONE, + .argument = MOVE_EFFECT_STEALTH_ROCK, }, [MOVE_SPRINGTIDE_STORM] = @@ -13599,7 +13600,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_CEASELESS_EDGE] = { - .effect = EFFECT_CEASELESS_EDGE, + .effect = EFFECT_HIT_SET_ENTRY_HAZARD, .power = 65, .type = TYPE_DARK, .accuracy = 90, @@ -13611,6 +13612,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .split = SPLIT_PHYSICAL, .zMovePower = 120, .zMoveEffect = Z_EFFECT_NONE, + .argument = MOVE_EFFECT_SPIKES, }, [MOVE_BLEAKWIND_STORM] = From d695c76881d10423427b6b82856b43fd99036618 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 8 Mar 2023 22:11:28 +0100 Subject: [PATCH 16/40] test for assist and removed assist array from battle struct --- include/battle.h | 1 - src/battle_script_commands.c | 47 ++++++++++++++++++++---------------- test/move_effect_assist.c | 22 +++++++++++++++++ test/test_runner_battle.c | 1 + 4 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 test/move_effect_assist.c diff --git a/include/battle.h b/include/battle.h index a43fc65cd..491273b80 100644 --- a/include/battle.h +++ b/include/battle.h @@ -534,7 +534,6 @@ struct BattleStruct u8 wildVictorySong; u8 dynamicMoveType; u8 wrappedBy[MAX_BATTLERS_COUNT]; - u16 assistPossibleMoves[PARTY_SIZE * MAX_MON_MOVES]; // Each of mons can know max 4 moves. u8 focusPunchBattlerId; u8 battlerPreventingSwitchout; u8 moneyMultiplier:6; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 126ca06fd..bd1b248b2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -23,6 +23,7 @@ #include "main.h" #include "palette.h" #include "money.h" +#include "malloc.h" #include "bg.h" #include "string_util.h" #include "pokemon_icon.h" @@ -14766,38 +14767,40 @@ static void Cmd_assistattackselect(void) s32 chooseableMovesNo = 0; struct Pokemon *party; s32 monId, moveId; - u16 *validMoves = gBattleStruct->assistPossibleMoves; + u16 *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES); - if (GET_BATTLER_SIDE(gBattlerAttacker) != B_SIDE_PLAYER) - party = gEnemyParty; - else - party = gPlayerParty; - - for (monId = 0; monId < PARTY_SIZE; monId++) + if (validMoves != NULL) { - if (monId == gBattlerPartyIndexes[gBattlerAttacker]) - continue; - if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) - continue; - if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) - continue; + if (GET_BATTLER_SIDE(gBattlerAttacker) != B_SIDE_PLAYER) + party = gEnemyParty; + else + party = gPlayerParty; - for (moveId = 0; moveId < MAX_MON_MOVES; moveId++) + for (monId = 0; monId < PARTY_SIZE; monId++) { - s32 i = 0; - u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); - - if (sForbiddenMoves[move] & FORBIDDEN_ASSIST) + if (monId == gBattlerPartyIndexes[gBattlerAttacker]) + continue; + if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) + continue; + if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) continue; - validMoves[chooseableMovesNo] = move; - chooseableMovesNo++; + for (moveId = 0; moveId < MAX_MON_MOVES; moveId++) + { + u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); + + if (sForbiddenMoves[move] & FORBIDDEN_ASSIST) + continue; + + validMoves[chooseableMovesNo++] = move; + } } } + if (chooseableMovesNo) { gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; - gCalledMove = validMoves[((Random() & 0xFF) * chooseableMovesNo) >> 8]; + gCalledMove = validMoves[Random() % chooseableMovesNo]; gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -14805,6 +14808,8 @@ static void Cmd_assistattackselect(void) { gBattlescriptCurrInstr = cmd->failInstr; } + + TRY_FREE_AND_SET_NULL(validMoves); } static void Cmd_trysetmagiccoat(void) diff --git a/test/move_effect_assist.c b/test/move_effect_assist.c new file mode 100644 index 000000000..e95d84654 --- /dev/null +++ b/test/move_effect_assist.c @@ -0,0 +1,22 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_ASSIST].effect == EFFECT_ASSIST); +} + +SINGLE_BATTLE_TEST("Assist fails if there are no valid moves to choose from") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Moves(MOVE_ASSIST, MOVE_CELEBRATE, MOVE_METRONOME, MOVE_ME_FIRST); } + PLAYER(SPECIES_WOBBUFFET) {Moves(MOVE_ASSIST, MOVE_ENDURE, MOVE_DRAGON_TAIL, MOVE_SPOTLIGHT); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ASSIST); } + } SCENE { + MESSAGE("Wobbuffet used Assist!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSIST, player); + MESSAGE("But it failed!"); + } +} diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index ec7ba275c..f4b8aa4fd 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1227,6 +1227,7 @@ void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) const struct BattleMove *move = &gBattleMoves[moveId]; if (move->target == MOVE_TARGET_RANDOM || move->target == MOVE_TARGET_BOTH + || move->target == MOVE_TARGET_DEPENDS || move->target == MOVE_TARGET_FOES_AND_ALLY || move->target == MOVE_TARGET_OPPONENTS_FIELD || move->target == MOVE_TARGET_ALL_BATTLERS) From aefd3189456da25904b0e39a6ca2a5f63c1a3505 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Tue, 14 Mar 2023 11:43:52 -0300 Subject: [PATCH 17/40] Applied review suggestions and expanded usage of activateAfterFaint in Cmd_setmoveeffect --- data/battle_scripts_1.s | 25 ++----------------------- include/battle_scripts.h | 2 +- src/battle_script_commands.c | 13 ++++++------- src/data/battle_moves.h | 4 ++-- 4 files changed, 11 insertions(+), 33 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 425834b7e..4c61e5e69 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -433,31 +433,10 @@ BattleScript_EffectDireClaw:: goto BattleScript_EffectHit BattleScript_EffectHitSetEntryHazard:: - attackcanceler - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - ppreduce - critcalc - damagecalc - adjustdamage - attackanimation - waitanimation - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_TARGET argumenttomoveeffect - seteffectprimary - moveendall - end + goto BattleScript_EffectHit -BattleScript_SpikesActivate:: +BattleScript_SpikesActivates:: trysetspikes BattleScript_MoveEnd printfromtable gDmgHazardsStringIds waitmessage B_WAIT_TIME_LONG diff --git a/include/battle_scripts.h b/include/battle_scripts.h index c4b15d9f1..c6cbddcd3 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -457,7 +457,7 @@ extern const u8 BattleScript_LunarDanceActivates[]; extern const u8 BattleScript_ShellTrapSetUp[]; extern const u8 BattleScript_CouldntFullyProtect[]; extern const u8 BattleScript_StealthRockActivates[]; -extern const u8 BattleScript_SpikesActivate[]; +extern const u8 BattleScript_SpikesActivates[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0799cf2f3..15a72c7e8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2896,6 +2896,9 @@ void SetMoveEffect(bool32 primary, u32 certain) return; case MOVE_EFFECT_STEALTH_ROCK: case MOVE_EFFECT_SPIKES: + case MOVE_EFFECT_PAYDAY: + case MOVE_EFFECT_STEAL_ITEM: + case MOVE_EFFECT_BUG_BITE: activateAfterFaint = TRUE; break; } @@ -2938,11 +2941,7 @@ void SetMoveEffect(bool32 primary, u32 certain) if (TestSheerForceFlag(gBattlerAttacker, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER) INCREMENT_RESET_RETURN - if (gBattleMons[gEffectBattler].hp == 0 - && !activateAfterFaint - && gBattleScripting.moveEffect != MOVE_EFFECT_PAYDAY - && gBattleScripting.moveEffect != MOVE_EFFECT_STEAL_ITEM - && gBattleScripting.moveEffect != MOVE_EFFECT_BUG_BITE) + if (gBattleMons[gEffectBattler].hp == 0 && !activateAfterFaint) INCREMENT_RESET_RETURN if (DoesSubstituteBlockMove(gBattlerAttacker, gEffectBattler, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER) @@ -3761,7 +3760,7 @@ void SetMoveEffect(bool32 primary, u32 certain) if (!gBattleMons[gEffectBattler].status1) { static const u8 sDireClawEffects[] = { MOVE_EFFECT_POISON, MOVE_EFFECT_PARALYSIS, MOVE_EFFECT_SLEEP }; - gBattleScripting.moveEffect = sDireClawEffects[Random() % 3]; + gBattleScripting.moveEffect = sDireClawEffects[Random() % ARRAY_COUNT(sDireClawEffects)]; SetMoveEffect(TRUE, 0); } break; @@ -3778,7 +3777,7 @@ void SetMoveEffect(bool32 primary, u32 certain) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED; BattleScriptPush(gBattlescriptCurrInstr + 1); - gBattlescriptCurrInstr = BattleScript_SpikesActivate; + gBattlescriptCurrInstr = BattleScript_SpikesActivates; } break; } diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 530dacd13..22eb7ca1a 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13328,7 +13328,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .type = TYPE_ROCK, .accuracy = 90, .pp = 15, - .secondaryEffectChance = 0, + .secondaryEffectChance = 100, .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_SLICING_MOVE, @@ -13605,7 +13605,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .type = TYPE_DARK, .accuracy = 90, .pp = 15, - .secondaryEffectChance = 0, + .secondaryEffectChance = 100, .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_SLICING_MOVE, From 509ff4c7e0d31fb18f76dc7af3c9702095eb20fe Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Fri, 3 Feb 2023 11:21:08 +0000 Subject: [PATCH 18/40] Generic TEST that executes a function --- test/test.h | 23 +++++++++++++++++++++++ test/test_battle.h | 2 ++ test/test_runner.c | 41 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/test/test.h b/test/test.h index 27475bb48..764443302 100644 --- a/test/test.h +++ b/test/test.h @@ -53,6 +53,16 @@ extern const u8 gTestRunnerI; extern const char gTestRunnerArgv[256]; extern const struct TestRunner gAssumptionsRunner; + +struct FunctionTestRunnerState +{ + u8 parameters; + u8 runParameter; +}; + +extern const struct TestRunner gFunctionTestRunner; +extern struct FunctionTestRunnerState *gFunctionTestRunnerState; + extern struct TestRunnerState gTestRunnerState; void CB2_TestRunner(void); @@ -62,6 +72,17 @@ void Test_ExitWithResult(enum TestResult, const char *fmt, ...); s32 MgbaPrintf_(const char *fmt, ...); +#define TEST(_name) \ + static void CAT(Test, __LINE__)(void); \ + __attribute__((section(".tests"))) static const struct Test CAT(sTest, __LINE__) = \ + { \ + .name = _name, \ + .filename = __FILE__, \ + .runner = &gFunctionTestRunner, \ + .data = (void *)CAT(Test, __LINE__), \ + }; \ + static void CAT(Test, __LINE__)(void) + #define ASSUMPTIONS \ static void Assumptions(void); \ __attribute__((section(".tests"))) static const struct Test sAssumptions = \ @@ -138,4 +159,6 @@ s32 MgbaPrintf_(const char *fmt, ...); #define KNOWN_FAILING \ Test_ExpectedResult(TEST_RESULT_FAIL) +#define PARAMETRIZE if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter) + #endif diff --git a/test/test_battle.h b/test/test_battle.h index 16918251b..a447102f3 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -639,6 +639,8 @@ extern struct BattleTestRunnerState *gBattleTestRunnerState; /* Parametrize */ +#undef PARAMETRIZE // Override test/test.h's implementation. + #define PARAMETRIZE if (gBattleTestRunnerState->parametersCount++ == i) /* Randomly */ diff --git a/test/test_runner.c b/test/test_runner.c index f2d4d5de3..e3d286c55 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -4,6 +4,7 @@ #include "gpu_regs.h" #include "main.h" #include "malloc.h" +#include "random.h" #include "test.h" #include "test_runner.h" @@ -12,6 +13,7 @@ void CB2_TestRunner(void); EWRAM_DATA struct TestRunnerState gTestRunnerState; +EWRAM_DATA struct FunctionTestRunnerState *gFunctionTestRunnerState; void TestRunner_Battle(const struct Test *); @@ -230,6 +232,38 @@ void Test_ExpectedResult(enum TestResult result) gTestRunnerState.expectedResult = result; } +static void FunctionTest_SetUp(void *data) +{ + (void)data; + gFunctionTestRunnerState = AllocZeroed(sizeof(*gFunctionTestRunnerState)); + SeedRng(0); +} + +static void FunctionTest_Run(void *data) +{ + void (*function)(void) = data; + do + { + if (gFunctionTestRunnerState->parameters) + MgbaPrintf_(":N%s %d/%d", gTestRunnerState.test->name, gFunctionTestRunnerState->runParameter + 1, gFunctionTestRunnerState->parameters); + gFunctionTestRunnerState->parameters = 0; + function(); + } while (++gFunctionTestRunnerState->runParameter < gFunctionTestRunnerState->parameters); +} + +static void FunctionTest_TearDown(void *data) +{ + (void)data; + FREE_AND_SET_NULL(gFunctionTestRunnerState); +} + +const struct TestRunner gFunctionTestRunner = +{ + .setUp = FunctionTest_SetUp, + .run = FunctionTest_Run, + .tearDown = FunctionTest_TearDown, +}; + static void Assumptions_Run(void *data) { void (*function)(void) = data; @@ -288,11 +322,12 @@ static void Intr_Timer2(void) void Test_ExitWithResult(enum TestResult result, const char *fmt, ...) { + bool32 handled = FALSE; gTestRunnerState.result = result; ReinitCallbacks(); - if (gTestRunnerState.test->runner->handleExitWithResult - && !gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result) - && gTestRunnerState.result != gTestRunnerState.expectedResult) + if (gTestRunnerState.test->runner->handleExitWithResult) + handled = gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result); + if (!handled && gTestRunnerState.result != gTestRunnerState.expectedResult) { va_list va; va_start(va, fmt); From 89deda04164c90197f309bc2b2a4bf54a5b34270 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 20 Feb 2023 20:06:01 +0000 Subject: [PATCH 19/40] Allow tests to override specific RNG calls --- include/random.h | 63 +++++++ src/battle_script_commands.c | 53 +++--- src/battle_util.c | 24 +-- src/random.c | 24 +++ test/ability_compound_eyes.c | 4 +- test/ability_cute_charm.c | 3 - test/ability_sand_veil.c | 2 +- test/ability_stench.c | 5 +- test/hold_effect_red_card.c | 4 +- test/move.c | 26 ++- test/move_effect_accuracy_down.c | 2 +- test/move_effect_evasion_up.c | 2 +- test/move_effect_hit_switch_target.c | 5 +- test/move_effect_rampage.c | 6 +- test/move_effect_roar.c | 4 +- test/move_effect_sleep.c | 21 ++- test/random.c | 81 +++++++++ test/status1.c | 7 +- test/test_battle.h | 68 ++++++-- test/test_runner_battle.c | 243 ++++++++++++++++----------- 20 files changed, 472 insertions(+), 175 deletions(-) create mode 100644 test/random.c diff --git a/include/random.h b/include/random.h index 6bf61de6c..60c718348 100644 --- a/include/random.h +++ b/include/random.h @@ -20,4 +20,67 @@ u16 Random2(void); void SeedRng(u16 seed); void SeedRng2(u16 seed); +/* Structured random number generator. + * Instead of the caller converting bits from Random() to a meaningful + * value, the caller provides metadata that is used to return the + * meaningful value directly. This allows code to interpret the random + * call, for example, battle tests know what the domain of a random call + * is, and can exhaustively test it. + * + * RandomTag identifies the purpose of the value. + * + * RandomUniform(tag, lo, hi) returns a number from lo to hi inclusive. + * + * RandomPercentage(tag, t) returns FALSE with probability (1-t)/100, + * and TRUE with probability t/100. + * + * RandomWeighted(tag, w0, w1, ... wN) returns a number from 0 to N + * inclusive. The return value is proportional to the weights, e.g. + * RandomWeighted(..., 1, 1) returns 50% 0s and 50% 1s. + * RandomWeighted(..., 2, 1) returns 2/3 0s and 1/3 1s. */ + +enum RandomTag +{ + RNG_NONE, + RNG_ACCURACY, + RNG_CONFUSION, + RNG_CRITICAL_HIT, + RNG_CUTE_CHARM, + RNG_DAMAGE_MODIFIER, + RNG_FLAME_BODY, + RNG_FORCE_RANDOM_SWITCH, + RNG_FROZEN, + RNG_HOLD_EFFECT_FLINCH, + RNG_INFATUATION, + RNG_PARALYSIS, + RNG_POISON_POINT, + RNG_RAMPAGE_TURNS, + RNG_SECONDARY_EFFECT, + RNG_SLEEP_TURNS, + RNG_SPEED_TIE, + RNG_STATIC, + RNG_STENCH, +}; + +#define RandomWeighted(tag, ...) \ + ({ \ + const u8 weights[] = { __VA_ARGS__ }; \ + u32 sum, i; \ + for (i = 0, sum = 0; i < ARRAY_COUNT(weights); i++) \ + sum += weights[i]; \ + RandomWeightedArray(tag, sum, ARRAY_COUNT(weights), weights); \ + }) + +#define RandomPercentage(tag, t) \ + ({ \ + const u8 weights[] = { 100 - t, t }; \ + RandomWeightedArray(tag, 100, ARRAY_COUNT(weights), weights); \ + }) + +u32 RandomUniform(enum RandomTag, u32 lo, u32 hi); +u32 RandomWeightedArray(enum RandomTag, u32 sum, u32 n, const u8 *weights); + +u32 RandomUniformDefault(enum RandomTag, u32 lo, u32 hi); +u32 RandomWeightedArrayDefault(enum RandomTag, u32 sum, u32 n, const u8 *weights); + #endif // GUARD_RANDOM_H diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 126ca06fd..e0ef56184 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1917,15 +1917,25 @@ static void Cmd_accuracycheck(void) } else { + u32 accuracy; + GET_MOVE_TYPE(move, type); if (JumpIfMoveAffectedByProtect(move)) return; if (AccuracyCalcHelper(move)) return; - // final calculation - if ((Random() % 100 + 1) > GetTotalAccuracy(gBattlerAttacker, gBattlerTarget, move, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget), - GetBattlerHoldEffect(gBattlerAttacker, TRUE), GetBattlerHoldEffect(gBattlerTarget, TRUE))) + accuracy = GetTotalAccuracy( + gBattlerAttacker, + gBattlerTarget, + move, + GetBattlerAbility(gBattlerAttacker), + GetBattlerAbility(gBattlerTarget), + GetBattlerHoldEffect(gBattlerAttacker, TRUE), + GetBattlerHoldEffect(gBattlerTarget, TRUE) + ); + + if (!RandomPercentage(RNG_ACCURACY, accuracy)) { gMoveResultFlags |= MOVE_RESULT_MISSED; if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_BLUNDER_POLICY) @@ -2104,10 +2114,8 @@ static void Cmd_critcalc(void) gIsCriticalHit = FALSE; else if (critChance == -2) gIsCriticalHit = TRUE; - else if (Random() % sCriticalHitChance[critChance] == 0) - gIsCriticalHit = TRUE; else - gIsCriticalHit = FALSE; + gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitChance[critChance] - 1, 1); // Counter for EVO_CRITICAL_HITS. partySlot = gBattlerPartyIndexes[gBattlerAttacker]; @@ -3153,9 +3161,9 @@ void SetMoveEffect(bool32 primary, u32 certain) if (sStatusFlagsForMoveEffects[gBattleScripting.moveEffect] == STATUS1_SLEEP) #if B_SLEEP_TURNS >= GEN_5 - gBattleMons[gEffectBattler].status1 |= ((Random() % 3) + 2); + gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 1, 3)); #else - gBattleMons[gEffectBattler].status1 |= ((Random() % 4) + 3); + gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 2, 5)); #endif else gBattleMons[gEffectBattler].status1 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect]; @@ -3558,7 +3566,7 @@ void SetMoveEffect(bool32 primary, u32 certain) { gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS; gLockedMoves[gEffectBattler] = gCurrentMove; - gBattleMons[gEffectBattler].status2 |= STATUS2_LOCK_CONFUSE_TURN((Random() & 1) + 2); // thrash for 2-3 turns + gBattleMons[gEffectBattler].status2 |= STATUS2_LOCK_CONFUSE_TURN(RandomUniform(RNG_RAMPAGE_TURNS, 2, 3)); } break; case MOVE_EFFECT_SP_ATK_TWO_DOWN: // Overheat @@ -3779,20 +3787,23 @@ static void Cmd_seteffectwithchance(void) else percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance; - if (gBattleScripting.moveEffect & MOVE_EFFECT_CERTAIN - && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleScripting.moveEffect) { - gBattleScripting.moveEffect &= ~MOVE_EFFECT_CERTAIN; - SetMoveEffect(FALSE, MOVE_EFFECT_CERTAIN); - } - else if (Random() % 100 < percentChance - && gBattleScripting.moveEffect - && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) - { - if (percentChance >= 100) + if (gBattleScripting.moveEffect & MOVE_EFFECT_CERTAIN + || percentChance >= 100) + { + gBattleScripting.moveEffect &= ~MOVE_EFFECT_CERTAIN; SetMoveEffect(FALSE, MOVE_EFFECT_CERTAIN); - else + } + else if (RandomPercentage(RNG_SECONDARY_EFFECT, percentChance)) + { SetMoveEffect(FALSE, 0); + } + else + { + gBattlescriptCurrInstr = cmd->nextInstr; + } } else { @@ -12343,7 +12354,7 @@ static void Cmd_forcerandomswitch(void) *(gBattleStruct->battlerPartyIndexes + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget]; gBattlescriptCurrInstr = BattleScript_RoarSuccessSwitch; gBattleStruct->forcedSwitch |= gBitTable[gBattlerTarget]; - *(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = validMons[Random() % validMonsCount]; + *(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = validMons[RandomUniform(RNG_FORCE_RANDOM_SWITCH, 0, validMonsCount - 1)]; if (!IsMultiBattle()) SwitchPartyOrder(gBattlerTarget); diff --git a/src/battle_util.c b/src/battle_util.c index 4b50bc704..e5b037acf 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3479,7 +3479,7 @@ u8 AtkCanceller_UnableToUseMove(void) case CANCELLER_FROZEN: // check being frozen if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE && !(gBattleMoves[gCurrentMove].flags & FLAG_THAW_USER)) { - if (Random() % 5) + if (!RandomPercentage(RNG_FROZEN, 20)) { gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; gHitMarker |= HITMARKER_NO_ATTACKSTRING; @@ -3597,9 +3597,9 @@ u8 AtkCanceller_UnableToUseMove(void) { // confusion dmg #if B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 - if (Random() % 3 == 0) + if (RandomWeighted(RNG_CONFUSION, 2, 1)) #else - if (Random() % 2 == 0) + if (RandomWeighted(RNG_CONFUSION, 1, 1)) #endif { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; @@ -3625,7 +3625,7 @@ u8 AtkCanceller_UnableToUseMove(void) gBattleStruct->atkCancellerTracker++; break; case CANCELLER_PARALYSED: // paralysis - if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && (Random() % 4) == 0) + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && !RandomPercentage(RNG_PARALYSIS, 75)) { gProtectStructs[gBattlerAttacker].prlzImmobility = TRUE; // This is removed in FRLG and Emerald for some reason @@ -3640,7 +3640,7 @@ u8 AtkCanceller_UnableToUseMove(void) if (gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) { gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10); - if (Random() & 1) + if (!RandomPercentage(RNG_INFATUATION, 50)) { BattleScriptPushCursor(); } @@ -5645,7 +5645,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && TARGET_TURN_DAMAGED && CanBePoisoned(gBattlerTarget, gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) - && (Random() % 3) == 0) + && RandomWeighted(RNG_POISON_POINT, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_POISON; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); @@ -5663,7 +5663,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && TARGET_TURN_DAMAGED && CanBeParalyzed(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) - && (Random() % 3) == 0) + && RandomWeighted(RNG_STATIC, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS; BattleScriptPushCursor(); @@ -5679,7 +5679,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && (IsMoveMakingContact(move, gBattlerAttacker)) && TARGET_TURN_DAMAGED && CanBeBurned(gBattlerAttacker) - && (Random() % 3) == 0) + && RandomWeighted(RNG_FLAME_BODY, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; BattleScriptPushCursor(); @@ -5695,7 +5695,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && (IsMoveMakingContact(move, gBattlerAttacker)) && TARGET_TURN_DAMAGED && gBattleMons[gBattlerTarget].hp != 0 - && (Random() % 3) == 0 + && RandomWeighted(RNG_CUTE_CHARM, 2, 1) && GetBattlerAbility(gBattlerAttacker) != ABILITY_OBLIVIOUS && !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL) && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) @@ -5915,7 +5915,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg - && (Random() % 10) == 0 + && RandomWeighted(RNG_STENCH, 9, 1) && !IS_MOVE_STATUS(move) && !sMovesNotAffectedByStench[gCurrentMove]) { @@ -7635,7 +7635,7 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) if (gBattleMoveDamage != 0 // Need to have done damage && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED - && (Random() % 100) < atkHoldEffectParam + && RandomPercentage(RNG_HOLD_EFFECT_FLINCH, atkHoldEffectParam) && gBattleMoves[gCurrentMove].flags & FLAG_KINGS_ROCK_AFFECTED && gBattleMons[gBattlerTarget].hp) { @@ -9752,7 +9752,7 @@ static s32 DoMoveDamageCalc(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, // Add a random factor. if (randomFactor) { - dmg *= 100 - (Random() % 16); + dmg *= 100 - RandomUniform(RNG_DAMAGE_MODIFIER, 0, 15); dmg /= 100; } diff --git a/src/random.c b/src/random.c index de923fba6..e59b56387 100644 --- a/src/random.c +++ b/src/random.c @@ -31,3 +31,27 @@ u16 Random2(void) gRng2Value = ISO_RANDOMIZE1(gRng2Value); return gRng2Value >> 16; } + +__attribute__((weak, alias("RandomUniformDefault"))) +u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi); + +__attribute__((weak, alias("RandomWeightedArrayDefault"))) +u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights); + +u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi) +{ + return lo + (((hi - lo) * Random()) >> 16); +} + +u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights) +{ + s32 i, targetSum; + targetSum = (sum * Random()) >> 16; + for (i = 0; i < n - 1; i++) + { + targetSum -= weights[i]; + if (targetSum < 0) + return i; + } + return n - 1; +} diff --git a/test/ability_compound_eyes.c b/test/ability_compound_eyes.c index 97ab84dd2..bbe718176 100644 --- a/test/ability_compound_eyes.c +++ b/test/ability_compound_eyes.c @@ -3,7 +3,7 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") { - PASSES_RANDOMLY(91, 100); + PASSES_RANDOMLY(91, 100, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_THUNDER].accuracy == 70); PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }; @@ -22,7 +22,7 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") SINGLE_BATTLE_TEST("Compound Eyes does not affect OHKO moves") { KNOWN_FAILING; - PASSES_RANDOMLY(30, 100); + PASSES_RANDOMLY(30, 100, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_FISSURE].accuracy == 30); ASSUME(gBattleMoves[MOVE_FISSURE].effect == EFFECT_OHKO); diff --git a/test/ability_cute_charm.c b/test/ability_cute_charm.c index 5e089efdf..ccf5c490b 100644 --- a/test/ability_cute_charm.c +++ b/test/ability_cute_charm.c @@ -1,9 +1,6 @@ #include "global.h" #include "test_battle.h" -// TODO: Currently PASSES_RANDOMLY is incapable of testing Cute Charm -// because it only activates 33% of the time, but we only want to -// measure the 50% of the time that the infatuation prevents our move. SINGLE_BATTLE_TEST("Cute Charm inflicts infatuation on contact") { u32 move; diff --git a/test/ability_sand_veil.c b/test/ability_sand_veil.c index 5d2325140..5514f27c0 100644 --- a/test/ability_sand_veil.c +++ b/test/ability_sand_veil.c @@ -16,7 +16,7 @@ SINGLE_BATTLE_TEST("Sand Veil prevents damage from sandstorm") SINGLE_BATTLE_TEST("Sand Veil reduces accuracy during sandstorm") { - PASSES_RANDOMLY(4,5); + PASSES_RANDOMLY(4, 5, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_POUND].accuracy == 100); PLAYER(SPECIES_SANDSHREW) { Ability(ABILITY_SAND_VEIL); }; diff --git a/test/ability_stench.c b/test/ability_stench.c index 18d909768..fb76ebc07 100644 --- a/test/ability_stench.c +++ b/test/ability_stench.c @@ -3,7 +3,7 @@ SINGLE_BATTLE_TEST("Stench has a 10% chance to flinch") { - PASSES_RANDOMLY(1,10); + PASSES_RANDOMLY(1, 10, RNG_STENCH); GIVEN { ASSUME(gBattleMoves[MOVE_TACKLE].power > 0); PLAYER(SPECIES_GRIMER) { Ability(ABILITY_STENCH); }; @@ -17,7 +17,8 @@ SINGLE_BATTLE_TEST("Stench has a 10% chance to flinch") SINGLE_BATTLE_TEST("Stench does not stack with King's Rock") { - PASSES_RANDOMLY(1,10); + KNOWN_FAILING; + PASSES_RANDOMLY(1, 10, RNG_STENCH); GIVEN { ASSUME(gItems[ITEM_KINGS_ROCK].holdEffect == HOLD_EFFECT_FLINCH); ASSUME(gBattleMoves[MOVE_TACKLE].power > 0); diff --git a/test/hold_effect_red_card.c b/test/hold_effect_red_card.c index 882007cfd..0a8bb3460 100644 --- a/test/hold_effect_red_card.c +++ b/test/hold_effect_red_card.c @@ -8,7 +8,7 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Red Card switches the attacker with a random non-fainted replacement") { - PASSES_RANDOMLY(1, 2); + PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH); GIVEN { PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); } OPPONENT(SPECIES_WOBBUFFET); @@ -27,7 +27,7 @@ SINGLE_BATTLE_TEST("Red Card switches the attacker with a random non-fainted rep DOUBLE_BATTLE_TEST("Red Card switches the target with a random non-battler, non-fainted replacement") { - PASSES_RANDOMLY(1, 2); + PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH); GIVEN { PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); } PLAYER(SPECIES_WYNAUT); diff --git a/test/move.c b/test/move.c index d7e759f27..9ad336330 100644 --- a/test/move.c +++ b/test/move.c @@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("Accuracy controls the proportion of misses") PARAMETRIZE { move = MOVE_RAZOR_LEAF; } PARAMETRIZE { move = MOVE_SCRATCH; } ASSUME(0 < gBattleMoves[move].accuracy && gBattleMoves[move].accuracy <= 100); - PASSES_RANDOMLY(gBattleMoves[move].accuracy, 100); + PASSES_RANDOMLY(gBattleMoves[move].accuracy, 100, RNG_ACCURACY); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -27,10 +27,9 @@ SINGLE_BATTLE_TEST("Secondary Effect Chance controls the proportion of secondary PARAMETRIZE { move = MOVE_THUNDER_SHOCK; } PARAMETRIZE { move = MOVE_DISCHARGE; } PARAMETRIZE { move = MOVE_NUZZLE; } - ASSUME(gBattleMoves[move].accuracy == 100); ASSUME(gBattleMoves[move].effect == EFFECT_PARALYZE_HIT); ASSUME(0 < gBattleMoves[move].secondaryEffectChance && gBattleMoves[move].secondaryEffectChance <= 100); - PASSES_RANDOMLY(gBattleMoves[move].secondaryEffectChance, 100); + PASSES_RANDOMLY(gBattleMoves[move].secondaryEffectChance, 100, RNG_SECONDARY_EFFECT); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -70,6 +69,7 @@ SINGLE_BATTLE_TEST("Turn order is determined by speed if priority ties") SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and speed tie") { + KNOWN_FAILING; // The algorithm is significantly biased. PASSES_RANDOMLY(1, 2); GIVEN { PLAYER(SPECIES_WOBBUFFET) { Speed(1); } @@ -85,15 +85,29 @@ SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and speed tie" SINGLE_BATTLE_TEST("Critical hits occur at a 1/24 rate") { ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100); - PASSES_RANDOMLY(100 / 24, 100); + PASSES_RANDOMLY(1, 24, RNG_CRITICAL_HIT); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_SCRATCH); } } SCENE { - MESSAGE("It's a critical hit!"); + MESSAGE("A critical hit!"); + } +} + +SINGLE_BATTLE_TEST("Slash's critical hits occur at a 1/8 rate") +{ + ASSUME(B_CRIT_CHANCE >= GEN_7); + ASSUME(gBattleMoves[MOVE_SLASH].flags & FLAG_HIGH_CRIT); + PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SLASH); } + } SCENE { + MESSAGE("A critical hit!"); } } diff --git a/test/move_effect_accuracy_down.c b/test/move_effect_accuracy_down.c index 2a90d8ea2..a6a79d8db 100644 --- a/test/move_effect_accuracy_down.c +++ b/test/move_effect_accuracy_down.c @@ -9,7 +9,7 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Sand Attack lowers Accuracy") { ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100); - PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100); + PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100, RNG_ACCURACY); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/move_effect_evasion_up.c b/test/move_effect_evasion_up.c index d14d15334..4a4e99db7 100644 --- a/test/move_effect_evasion_up.c +++ b/test/move_effect_evasion_up.c @@ -9,7 +9,7 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Double Team raises Evasion") { ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100); - PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100); + PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100, RNG_ACCURACY); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/move_effect_hit_switch_target.c b/test/move_effect_hit_switch_target.c index 9c50a4e4c..5af3062a4 100644 --- a/test/move_effect_hit_switch_target.c +++ b/test/move_effect_hit_switch_target.c @@ -9,8 +9,7 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Dragon Tail switches the target with a random non-fainted replacement") { - KNOWN_FAILING; // Only 18/50. Waiting for an improved PASSES_RANDOMLY. - PASSES_RANDOMLY(90 * 1, 100 * 2); + PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -27,7 +26,7 @@ SINGLE_BATTLE_TEST("Dragon Tail switches the target with a random non-fainted re DOUBLE_BATTLE_TEST("Dragon Tail switches the target with a random non-battler, non-fainted replacement") { - PASSES_RANDOMLY(90 * 1, 100 * 2); + PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH); GIVEN { PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WYNAUT); diff --git a/test/move_effect_rampage.c b/test/move_effect_rampage.c index a3afebf6b..aa4a002fb 100644 --- a/test/move_effect_rampage.c +++ b/test/move_effect_rampage.c @@ -8,7 +8,7 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Thrash lasts for 2 or 3 turns") { - PASSES_RANDOMLY(1, 2); + PASSES_RANDOMLY(1, 2, RNG_RAMPAGE_TURNS); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -26,7 +26,6 @@ SINGLE_BATTLE_TEST("Thrash lasts for 2 or 3 turns") SINGLE_BATTLE_TEST("Thrash confuses the user after it finishes") { GIVEN { - RNGSeed(0x00000000); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -45,7 +44,6 @@ SINGLE_BATTLE_TEST("Thrash does not confuse the user if it is canceled on turn 1 { GIVEN { ASSUME(B_RAMPAGE_CANCELLING >= GEN_5); - RNGSeed(0x00000000); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -61,7 +59,6 @@ SINGLE_BATTLE_TEST("Thrash does not confuse the user if it is canceled on turn 2 { GIVEN { ASSUME(B_RAMPAGE_CANCELLING >= GEN_5); - RNGSeed(0x00000000); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -78,7 +75,6 @@ SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3") KNOWN_FAILING; GIVEN { ASSUME(B_RAMPAGE_CANCELLING >= GEN_5); - RNGSeed(0x00000000); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/move_effect_roar.c b/test/move_effect_roar.c index 2d4eadda8..99256b298 100644 --- a/test/move_effect_roar.c +++ b/test/move_effect_roar.c @@ -8,7 +8,7 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Roar switches the target with a random non-fainted replacement") { - PASSES_RANDOMLY(1, 2); + PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -25,7 +25,7 @@ SINGLE_BATTLE_TEST("Roar switches the target with a random non-fainted replaceme DOUBLE_BATTLE_TEST("Roar switches the target with a random non-battler, non-fainted replacement") { - PASSES_RANDOMLY(1, 2); + PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH); GIVEN { PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WYNAUT); diff --git a/test/move_effect_sleep.c b/test/move_effect_sleep.c index c80faf4bd..c34e1248e 100644 --- a/test/move_effect_sleep.c +++ b/test/move_effect_sleep.c @@ -6,16 +6,33 @@ ASSUMPTIONS ASSUME(gBattleMoves[MOVE_HYPNOSIS].effect == EFFECT_SLEEP); } -SINGLE_BATTLE_TEST("Hypnosis inflicts sleep") +SINGLE_BATTLE_TEST("Hypnosis inflicts 1-3 turns of sleep") { + u32 turns, count; + ASSUME(B_SLEEP_TURNS >= GEN_5); + PARAMETRIZE { turns = 1; } + PARAMETRIZE { turns = 2; } + PARAMETRIZE { turns = 3; } + PASSES_RANDOMLY(1, 3, RNG_SLEEP_TURNS); GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_HYPNOSIS); } + TURN { MOVE(player, MOVE_HYPNOSIS); MOVE(opponent, MOVE_CELEBRATE); } + for (count = 0; count < turns; ++count) + TURN {} } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPNOSIS, player); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + MESSAGE("Foe Wobbuffet fell asleep!"); STATUS_ICON(opponent, sleep: TRUE); + for (count = 0; count < turns; ++count) + { + if (count < turns - 1) + MESSAGE("Foe Wobbuffet is fast asleep."); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + } + MESSAGE("Foe Wobbuffet woke up!"); + STATUS_ICON(opponent, none: TRUE); } } diff --git a/test/random.c b/test/random.c new file mode 100644 index 000000000..abaf4a4f8 --- /dev/null +++ b/test/random.c @@ -0,0 +1,81 @@ +#include "global.h" +#include "test.h" +#include "random.h" + +TEST("RandomUniform generates lo..hi") +{ + u32 lo, hi, i; + PARAMETRIZE { lo = 0; hi = 1; } + PARAMETRIZE { lo = 0; hi = 2; } + PARAMETRIZE { lo = 0; hi = 3; } + PARAMETRIZE { lo = 2; hi = 4; } + for (i = 0; i < 1024; i++) + { + u32 r = RandomUniformDefault(RNG_NONE, lo, hi); + EXPECT(lo <= r && r <= hi); + } +} + +TEST("RandomWeighted generates 0..n-1") +{ + u32 n, sum, i; + static const u8 ws[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + PARAMETRIZE { n = 1; } + PARAMETRIZE { n = 2; } + PARAMETRIZE { n = 3; } + PARAMETRIZE { n = 4; } + ASSUME(n <= ARRAY_COUNT(ws)); + for (i = 0, sum = 0; i < n; i++) + sum += ws[i]; + for (i = 0; i < 1024; i++) + { + u32 r = RandomWeightedArrayDefault(RNG_NONE, sum, n, ws); + EXPECT(0 <= r && r < n); + } +} + +TEST("RandomUniform generates uniform distribution") +{ + u32 i, error; + u16 distribution[4]; + + memset(distribution, 0, sizeof(distribution)); + for (i = 0; i < 4096; i++) + { + u32 r = RandomUniformDefault(RNG_NONE, 0, ARRAY_COUNT(distribution)); + EXPECT(0 <= r && r < ARRAY_COUNT(distribution)); + distribution[r]++; + } + + error = 0; + for (i = 0; i < ARRAY_COUNT(distribution); i++) + error += abs(UQ_4_12(0.25) - distribution[i]); + + EXPECT_LT(error, UQ_4_12(0.025)); +} + +TEST("RandomWeighted generates distribution in proportion to the weights") +{ + u32 i, sum, error; + static const u8 ws[4] = { 1, 2, 2, 3 }; + u16 distribution[ARRAY_COUNT(ws)]; + + for (i = 0, sum = 0; i < ARRAY_COUNT(ws); i++) + sum += ws[i]; + + memset(distribution, 0, sizeof(distribution)); + for (i = 0; i < 4096; i++) + { + u32 r = RandomWeightedArrayDefault(RNG_NONE, sum, ARRAY_COUNT(ws), ws); + EXPECT(0 <= r && r < ARRAY_COUNT(ws)); + distribution[r]++; + } + + error = 0; + error += abs(UQ_4_12(0.125) - distribution[0]); + error += abs(UQ_4_12(0.250) - distribution[1]); + error += abs(UQ_4_12(0.250) - distribution[2]); + error += abs(UQ_4_12(0.375) - distribution[3]); + + EXPECT_LT(error, UQ_4_12(0.025)); +} diff --git a/test/status1.c b/test/status1.c index 63a9cd041..f4d3c7d94 100644 --- a/test/status1.c +++ b/test/status1.c @@ -72,7 +72,7 @@ SINGLE_BATTLE_TEST("Burn reduces attack by 50%", s16 damage) SINGLE_BATTLE_TEST("Freeze has a 20% chance of being thawed") { - PASSES_RANDOMLY(20, 100); + PASSES_RANDOMLY(20, 100, RNG_FROZEN); GIVEN { PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } OPPONENT(SPECIES_WOBBUFFET); @@ -90,9 +90,8 @@ SINGLE_BATTLE_TEST("Freeze is thawed by opponent's Fire-type attacks") PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_EMBER); } + TURN { MOVE(opponent, MOVE_EMBER); MOVE(player, MOVE_CELEBRATE); } } SCENE { - MESSAGE("Wobbuffet is frozen solid!"); MESSAGE("Foe Wobbuffet used Ember!"); MESSAGE("Wobbuffet was defrosted!"); STATUS_ICON(player, none: TRUE); @@ -145,7 +144,7 @@ SINGLE_BATTLE_TEST("Paralysis reduces speed by 50%") SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn") { - PASSES_RANDOMLY(25, 100); + PASSES_RANDOMLY(25, 100, RNG_PARALYSIS); GIVEN { PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); } OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/test_battle.h b/test/test_battle.h index a447102f3..8c554216e 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -65,8 +65,9 @@ * single turn. MOVE causes the player to use Stun Spore and adds the * move to the Pokémon's moveset if an explicit Moves was not specified. * Pokémon that are not mentioned in a TURN use Celebrate. - * The test runner attempts to rig the RNG so that the first move used - * in a turn does not miss and activates its secondary effects (if any). + * The test runner rigs the RNG so that unless otherwise specified, + * moves always hit, never critical hit, always activate their secondary + * effects, and always roll the same damage modifier. * * SCENE describes the player-visible output of the battle. In this case * ANIMATION checks that the Stun Spore animation played, MESSAGE checks @@ -228,12 +229,35 @@ * } * } * - * PASSES_RANDOMLY(successes, trials) - * Checks that the test passes approximately successes/trials. Used for - * testing RNG-based attacks, e.g.: + * PASSES_RANDOMLY(successes, trials, [tag]) + * Checks that the test passes successes/trials. If tag is provided, the + * test is run for each value that the tag can produce. For example, to + * check that Paralysis causes the turn to be skipped 25/100 times, we + * can write the following test that passes only if the Pokémon is fully + * paralyzed and specify that we expect it to pass 25/100 times when + * RNG_PARALYSIS varies: + * SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn") + * { + * PASSES_RANDOMLY(25, 100, RNG_PARALYSIS); + * GIVEN { + * PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); } + * OPPONENT(SPECIES_WOBBUFFET); + * } WHEN { + * TURN { MOVE(player, MOVE_CELEBRATE); } + * } SCENE { + * MESSAGE("Wobbuffet is paralyzed! It can't move!"); + * } + * } + * All BattleRandom calls involving tag will return the same number, so + * this cannot be used to have two moves independently hit or miss, for + * example. + * + * If the tag is not provided, runs the test 50 times and computes an + * approximate pass ratio. * PASSES_RANDOMLY(gBattleMoves[move].accuracy, 100); - * Note that PASSES_RANDOMLY makes the tests run very slowly and should - * be avoided where possible. + * Note that this mode of PASSES_RANDOMLY makes the tests run very + * slowly and should be avoided where possible. If the mechanic you are + * testing is missing its tag, you should add it. * * GIVEN * Contains the initial state of the parties before the battle. @@ -419,6 +443,7 @@ #include "battle_anim.h" #include "data.h" #include "item.h" +#include "random.h" #include "recorded_battle.h" #include "test.h" #include "util.h" @@ -433,6 +458,7 @@ // NOTE: If the stack is too small the test runner will probably crash // or loop. #define BATTLE_TEST_STACK_SIZE 1024 +#define MAX_TURNS 16 #define MAX_QUEUED_EVENTS 25 enum { BATTLE_TEST_SINGLES, BATTLE_TEST_DOUBLES }; @@ -512,6 +538,13 @@ struct QueuedEvent } as; }; +struct BattlerTurn +{ + u8 hit:2; + u8 criticalHit:2; + u8 secondaryEffect:2; +}; + struct BattleTestData { u8 stack[BATTLE_TEST_STACK_SIZE]; @@ -533,14 +566,13 @@ struct BattleTestData u8 turns; u8 actionBattlers; u8 moveBattlers; - bool8 hasRNGActions:1; struct RecordedBattleSave recordedBattle; u8 battleRecordTypes[MAX_BATTLERS_COUNT][BATTLER_RECORD_SIZE]; u8 battleRecordSourceLineOffsets[MAX_BATTLERS_COUNT][BATTLER_RECORD_SIZE]; u16 recordIndexes[MAX_BATTLERS_COUNT]; + struct BattlerTurn battleRecordTurns[MAX_TURNS][MAX_BATTLERS_COUNT]; u8 lastActionTurn; - u8 nextRNGTurn; u8 queuedEventsCount; u8 queueGroupType; @@ -555,11 +587,12 @@ struct BattleTestRunnerState u8 parametersCount; // Valid only in BattleTest_Setup. u8 parameters; u8 runParameter; + u16 rngTag; u8 trials; - u8 expectedPasses; - u8 observedPasses; - u8 skippedTrials; u8 runTrial; + u16 expectedRatio; + u16 observedRatio; + u16 trialRatio; bool8 runRandomly:1; bool8 runGiven:1; bool8 runWhen:1; @@ -645,9 +678,14 @@ extern struct BattleTestRunnerState *gBattleTestRunnerState; /* Randomly */ -#define PASSES_RANDOMLY(passes, trials) for (; gBattleTestRunnerState->runRandomly; gBattleTestRunnerState->runRandomly = FALSE) Randomly(__LINE__, passes, trials) +#define PASSES_RANDOMLY(passes, trials, ...) for (; gBattleTestRunnerState->runRandomly; gBattleTestRunnerState->runRandomly = FALSE) Randomly(__LINE__, passes, trials, (struct RandomlyContext) { __VA_ARGS__ }) -void Randomly(u32 sourceLine, u32 passes, u32 trials); +struct RandomlyContext +{ + u16 tag; +}; + +void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext); /* Given */ @@ -721,6 +759,8 @@ struct MoveContext u16 explicitHit:1; u16 criticalHit:1; u16 explicitCriticalHit:1; + u16 secondaryEffect:1; + u16 explicitSecondaryEffect:1; u16 megaEvolve:1; u16 explicitMegaEvolve:1; // TODO: u8 zMove:1; diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index ec7ba275c..0c3c23063 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -15,26 +15,10 @@ #define STATE gBattleTestRunnerState #define DATA gBattleTestRunnerState->data -/* RNG seeds for controlling the first move of the turn. - * Found via brute force. */ +#define RNG_SEED_DEFAULT 0x00000000 -/* Default seed, triggers most things. - * The 1st roll % 100 is <= 29, to make 30%+ accuracycheck pass. - * The 2nd roll is not a critical hit at the regular crit stage. - * The 3rd roll is consumed by damagecalc. - * The 4th roll is consumed by adjustdamage. - * The 5th roll % 100 is <= 9, to make 10%+ seteffectwithchance pass - * and % 3 is == 0, to make Poison Point and other 1/3s pass. */ -#define RNG_SEED_DEFAULT 0x000002BE - -/* Causes the first attack to critical hit if B_CRIT_CHANCE >= GEN_6. - * The 2nd roll % 24 == 0 to be a critical hit at any stage. - * The other rolls match RNG_SEED_DEFAULT. */ -#define RNG_SEED_CRITICAL_HIT 0x0000A9F4 - -/* Causes the first attack to miss if possible. - * The 1st roll % 100 is 99, to make 99%- accuracycheck fail. */ -#define RNG_SEED_MISS 0x00000074 +#undef Q_4_12 +#define Q_4_12(n) (s32)((n) * 4096) EWRAM_DATA struct BattleTestRunnerState *gBattleTestRunnerState = NULL; @@ -129,12 +113,13 @@ static u32 BattleTest_EstimateCost(void *data) if (!STATE) return 0; STATE->runRandomly = TRUE; - DATA.recordedBattle.rngSeed = RNG_SEED_DEFAULT; InvokeTestFunction(test); cost = 1; if (STATE->parametersCount != 0) cost *= STATE->parametersCount; - if (STATE->trials != 0) + if (STATE->trials == 1) + cost *= 3; + else if (STATE->trials > 1) cost *= STATE->trials; FREE_AND_SET_NULL(STATE); return cost; @@ -162,6 +147,28 @@ static void BattleTest_SetUp(void *data) } } +static void PrintTestName(void) +{ + if (STATE->trials && STATE->parameters) + { + if (STATE->trials == 1) + MgbaPrintf_(":N%s %d/%d (%d/?)", gTestRunnerState.test->name, STATE->runParameter + 1, STATE->parameters, STATE->runTrial + 1); + else + MgbaPrintf_(":N%s %d/%d (%d/%d)", gTestRunnerState.test->name, STATE->runParameter + 1, STATE->parameters, STATE->runTrial + 1, STATE->trials); + } + else if (STATE->trials) + { + if (STATE->trials == 1) + MgbaPrintf_(":N%s (%d/?)", gTestRunnerState.test->name, STATE->runTrial + 1); + else + MgbaPrintf_(":N%s (%d/%d)", gTestRunnerState.test->name, STATE->runTrial + 1, STATE->trials); + } + else if (STATE->parameters) + { + MgbaPrintf_(":N%s %d/%d", gTestRunnerState.test->name, STATE->runParameter + 1, STATE->parameters); + } +} + // This does not take into account priority, statuses, or any other // modifiers. static void SetImplicitSpeeds(void) @@ -280,12 +287,82 @@ static void BattleTest_Run(void *data) STATE->checkProgressTrial = 0; STATE->checkProgressTurn = 0; - if (STATE->trials && STATE->parameters) - MgbaPrintf_(":N%s %d/%d (%d/%d)", gTestRunnerState.test->name, STATE->runParameter + 1, STATE->parameters, STATE->runTrial + 1, STATE->trials); - else if (STATE->trials) - MgbaPrintf_(":N%s (%d/%d)", gTestRunnerState.test->name, STATE->runTrial + 1, STATE->trials); - else if (STATE->parameters) - MgbaPrintf_(":N%s %d/%d", gTestRunnerState.test->name, STATE->runParameter + 1, STATE->parameters); + PrintTestName(); +} + +u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi) +{ + if (tag == STATE->rngTag) + { + u32 n = hi - lo + 1; + if (STATE->trials == 1) + { + STATE->trials = n; + PrintTestName(); + } + else if (STATE->trials != n) + { + Test_ExitWithResult(TEST_RESULT_ERROR, "RandomUniform called with inconsistent trials %d and %d", STATE->trials, n); + } + STATE->trialRatio = Q_4_12(1) / n; + return STATE->runTrial + lo; + } + + return hi; +} + +u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights) +{ + const struct BattlerTurn *turn = NULL; + u32 default_ = n-1; + + if (gCurrentTurnActionNumber < gBattlersCount) + { + u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber]; + turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId]; + } + + switch (tag) + { + case RNG_ACCURACY: + ASSUME(n == 2); + if (turn && turn->hit) + return turn->hit - 1; + default_ = TRUE; + break; + + case RNG_CRITICAL_HIT: + ASSUME(n == 2); + if (turn && turn->criticalHit) + return turn->criticalHit - 1; + default_ = FALSE; + break; + + case RNG_SECONDARY_EFFECT: + ASSUME(n == 2); + if (turn && turn->secondaryEffect) + return turn->secondaryEffect - 1; + default_ = TRUE; + break; + } + + if (tag == STATE->rngTag) + { + if (STATE->trials == 1) + { + STATE->trials = n; + PrintTestName(); + } + else if (STATE->trials != n) + { + Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeighted called with inconsistent trials %d and %d", STATE->trials, n); + } + // TODO: Detect inconsistent sum. + STATE->trialRatio = Q_4_12(weights[STATE->runTrial]) / sum; + return STATE->runTrial; + } + + return default_; } static s32 TryAbilityPopUp(s32 i, s32 n, u32 battlerId, u32 ability) @@ -711,42 +788,36 @@ static void CB2_BattleTest_NextTrial(void) SetMainCallback2(CB2_BattleTest_NextParameter); + switch (gTestRunnerState.result) + { + case TEST_RESULT_FAIL: + break; + case TEST_RESULT_PASS: + STATE->observedRatio += STATE->trialRatio; + break; + default: + return; + } + if (STATE->rngTag) + STATE->trialRatio = 0; + if (++STATE->runTrial < STATE->trials) { - switch (gTestRunnerState.result) - { - case TEST_RESULT_FAIL: - break; - case TEST_RESULT_PASS: - STATE->observedPasses++; - break; - case TEST_RESULT_ASSUMPTION_FAIL: - STATE->skippedTrials++; - if (STATE->skippedTrials > STATE->trials / 4) - Test_ExitWithResult(TEST_RESULT_INVALID, "25%% of the trials were SKIPed"); - break; - default: - return; - } - if (STATE->parameters) - MgbaPrintf_(":N%s %d/%d (%d/%d)", gTestRunnerState.test->name, STATE->runParameter + 1, STATE->parameters, STATE->runTrial + 1, STATE->trials); - else - MgbaPrintf_(":N%s (%d/%d)", gTestRunnerState.test->name, STATE->runTrial + 1, STATE->trials); - gTestRunnerState.result = TEST_RESULT_PASS; + PrintTestName(); + gTestRunnerState.result = TEST_RESULT_PASS; DATA.recordedBattle.rngSeed = ISO_RANDOMIZE1(STATE->runTrial); DATA.queuedEvent = 0; DATA.lastActionTurn = 0; - DATA.nextRNGTurn = 0; SetVariablesForRecordedBattle(&DATA.recordedBattle); SetMainCallback2(CB2_InitBattle); } else { - // This is a tolerance of +/- 4%. - if (abs(STATE->observedPasses - STATE->expectedPasses) <= 2) + // This is a tolerance of +/- ~2%. + if (abs(STATE->observedRatio - STATE->expectedRatio) <= Q_4_12(0.02)) gTestRunnerState.result = TEST_RESULT_PASS; else - Test_ExitWithResult(TEST_RESULT_FAIL, "Expected %d/%d passes, observed %d/%d", STATE->expectedPasses, STATE->trials, STATE->observedPasses, STATE->trials); + Test_ExitWithResult(TEST_RESULT_FAIL, "Expected %q passes/successes, observed %q", STATE->expectedRatio, STATE->observedRatio); } } @@ -773,7 +844,8 @@ static bool32 BattleTest_CheckProgress(void *data) static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result) { - if (result != TEST_RESULT_INVALID + if (result != TEST_RESULT_ASSUMPTION_FAIL + && result != TEST_RESULT_INVALID && result != TEST_RESULT_ERROR && result != TEST_RESULT_TIMEOUT && STATE->runTrial < STATE->trials) @@ -787,16 +859,25 @@ static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result } } -void Randomly(u32 sourceLine, u32 passes, u32 trials) +void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx) { - INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set"); - // This is a precision of 2%. - STATE->trials = 50; - STATE->expectedPasses = STATE->trials * passes / trials; - STATE->observedPasses = 0; - STATE->skippedTrials = 0; + INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials); + STATE->rngTag = ctx.tag; STATE->runTrial = 0; - DATA.recordedBattle.rngSeed = 0; + STATE->expectedRatio = Q_4_12(passes) / trials; + STATE->observedRatio = 0; + if (STATE->rngTag) + { + STATE->trials = 1; + STATE->trialRatio = Q_4_12(1); + } + else + { + INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set"); + STATE->trials = 50; + STATE->trialRatio = Q_4_12(1) / STATE->trials; + DATA.recordedBattle.rngSeed = 0; + } } void RNGSeed_(u32 sourceLine, u32 seed) @@ -1037,16 +1118,6 @@ static void PushBattlerAction(u32 sourceLine, s32 battlerId, u32 actionType, u32 void BattleTest_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType) { - // TODO: Support explicit seeds for each turn? - if (DATA.nextRNGTurn == gBattleResults.battleTurnCounter - && (DATA.recordedBattle.rngSeed == RNG_SEED_DEFAULT - || DATA.recordedBattle.rngSeed == RNG_SEED_CRITICAL_HIT - || DATA.recordedBattle.rngSeed == RNG_SEED_MISS)) - { - gRngValue = DATA.recordedBattle.rngSeed; - DATA.nextRNGTurn++; - } - // An illegal move choice will cause the battle to request a new // move slot and target. This detects the move slot. if (actionType == RECORDED_MOVE_SLOT @@ -1122,10 +1193,11 @@ void BattleTest_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 void OpenTurn(u32 sourceLine) { INVALID_IF(DATA.turnState != TURN_CLOSED, "Nested TURN"); + if (DATA.turns == MAX_TURNS) + Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: TURN exceeds MAX_TURNS", gTestRunnerState.test->filename, sourceLine); DATA.turnState = TURN_OPEN; DATA.actionBattlers = 0x00; DATA.moveBattlers = 0x00; - DATA.hasRNGActions = FALSE; } static void SetSlowerThan(s32 battlerId) @@ -1253,21 +1325,12 @@ void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) } } - if (ctx.explicitHit && !ctx.hit) - { - if (DATA.hasRNGActions != 0) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: hit only supported on the first move", gTestRunnerState.test->filename, sourceLine); - INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set"); - DATA.recordedBattle.rngSeed = RNG_SEED_MISS; - } - - if (ctx.explicitCriticalHit && ctx.criticalHit) - { - if (DATA.hasRNGActions != 0) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: criticalHit only supported on the first move", gTestRunnerState.test->filename, sourceLine); - INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set"); - DATA.recordedBattle.rngSeed = RNG_SEED_CRITICAL_HIT; - } + if (ctx.explicitHit) + DATA.battleRecordTurns[DATA.turns][battlerId].hit = 1 + ctx.hit; + if (ctx.explicitCriticalHit) + DATA.battleRecordTurns[DATA.turns][battlerId].criticalHit = 1 + ctx.criticalHit; + if (ctx.explicitSecondaryEffect) + DATA.battleRecordTurns[DATA.turns][battlerId].secondaryEffect = 1 + ctx.secondaryEffect; if (!(DATA.actionBattlers & (1 << battlerId))) { @@ -1288,14 +1351,6 @@ void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) DATA.actionBattlers |= 1 << battlerId; DATA.moveBattlers |= 1 << battlerId; } - - // WARNING: Approximation. The move could still cause the RNG to - // advance. - if (gBattleMoves[moveId].accuracy != 0 - || gBattleMoves[moveId].split != SPLIT_STATUS) - { - DATA.hasRNGActions = TRUE; - } } void ForcedMove(u32 sourceLine, struct BattlePokemon *battler) From 372c6a1afa1836ffb0595a6767fccbc034e5e80c Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sun, 19 Mar 2023 20:55:39 +0100 Subject: [PATCH 20/40] Add new item variables and defines --- include/constants/hold_effects.h | 1 + include/constants/items.h | 33 ++- src/data/item_icon_table.h | 29 +++ src/data/items.h | 331 +++++++++++++++++++++++++++++- src/data/text/item_descriptions.h | 60 ++++++ 5 files changed, 452 insertions(+), 2 deletions(-) diff --git a/include/constants/hold_effects.h b/include/constants/hold_effects.h index cd4256632..5fc268cb0 100644 --- a/include/constants/hold_effects.h +++ b/include/constants/hold_effects.h @@ -159,6 +159,7 @@ #define HOLD_EFFECT_PUNCHING_GLOVE 178 #define HOLD_EFFECT_COVERT_CLOAK 179 #define HOLD_EFFECT_LOADED_DICE 180 +#define HOLD_EFFECT_BOOSTER_ENERGY 181 // Not implemented. #define HOLD_EFFECT_CHOICE(holdEffect)((holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF || holdEffect == HOLD_EFFECT_CHOICE_SPECS)) diff --git a/include/constants/items.h b/include/constants/items.h index 1cb2803f3..f64790b38 100644 --- a/include/constants/items.h +++ b/include/constants/items.h @@ -928,13 +928,44 @@ #define ITEM_RUBY 756 #define ITEM_SAPPHIRE 757 +// GEN IX ITEMS #define ITEM_ABILITY_SHIELD 758 #define ITEM_CLEAR_AMULET 759 #define ITEM_PUNCHING_GLOVE 760 #define ITEM_COVERT_CLOAK 761 #define ITEM_LOADED_DICE 762 +#define ITEM_AUSPICIOUS_ARMOR 763 +#define ITEM_BOOSTER_ENERGY 764 +#define ITEM_BIG_BAMBOO_SHOOT 765 +#define ITEM_GIMMIGHOUL_COIN 766 +#define ITEM_LEADERS_CREST 767 +#define ITEM_MALICIOUS_ARMOR 768 +#define ITEM_MIRROR_HERB 769 +#define ITEM_SCROLL_OF_DARKNESS 770 +#define ITEM_SCROLL_OF_WATERS 771 +#define ITEM_TERA_ORB 772 +#define ITEM_TINY_BAMBOO_SHOOT 773 -#define ITEMS_COUNT 763 +#define ITEM_BUG_TERA_SHARD 774 +#define ITEM_DARK_TERA_SHARD 775 +#define ITEM_DRAGON_TERA_SHARD 776 +#define ITEM_ELECTRIC_TERA_SHARD 777 +#define ITEM_FAIRY_TERA_SHARD 778 +#define ITEM_FIGHTING_TERA_SHARD 779 +#define ITEM_FIRE_TERA_SHARD 780 +#define ITEM_FLYING_TERA_SHARD 781 +#define ITEM_GHOST_TERA_SHARD 782 +#define ITEM_GRASS_TERA_SHARD 783 +#define ITEM_GROUND_TERA_SHARD 784 +#define ITEM_ICE_TERA_SHARD 785 +#define ITEM_NORMAL_TERA_SHARD 786 +#define ITEM_POISON_TERA_SHARD 787 +#define ITEM_PSYCHIC_TERA_SHARD 788 +#define ITEM_ROCK_TERA_SHARD 789 +#define ITEM_STEEL_TERA_SHARD 790 +#define ITEM_WATER_TERA_SHARD 791 + +#define ITEMS_COUNT 792 #define ITEM_FIELD_ARROW ITEMS_COUNT // A special item id associated with "Cancel"/"Exit" etc. in a list of items or decorations diff --git a/src/data/item_icon_table.h b/src/data/item_icon_table.h index 0107b71ab..0dbec3f85 100644 --- a/src/data/item_icon_table.h +++ b/src/data/item_icon_table.h @@ -808,6 +808,35 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_PUNCHING_GLOVE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PunchingGlove, gItemIconPalette_PunchingGlove}, [ITEM_COVERT_CLOAK] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_CovertCloak, gItemIconPalette_CovertCloak}, [ITEM_LOADED_DICE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LoadedDice, gItemIconPalette_LoadedDice}, + [ITEM_AUSPICIOUS_ARMOR] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_AuspiciousArmor, gItemIconPalette_AuspiciousArmor}, + [ITEM_BOOSTER_ENERGY] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BoosterEnergy, gItemIconPalette_BoosterEnergy}, + [ITEM_BIG_BAMBOO_SHOOT] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BigBambooShoot, gItemIconPalette_BigBambooShoot}, + [ITEM_GIMMIGHOUL_COIN] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GimmighoulCoin, gItemIconPalette_GimmighoulCoin}, + [ITEM_LEADERS_CREST] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LeadersCrest, gItemIconPalette_LeadersCrest}, + [ITEM_MALICIOUS_ARMOR] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_MaliciousArmor, gItemIconPalette_MaliciousArmor}, + [ITEM_MIRROR_HERB] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_MirrorHerb, gItemIconPalette_MirrorHerb}, + [ITEM_SCROLL_OF_DARKNESS] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ScrollOfDarkness, gItemIconPalette_ScrollOfDarkness}, + [ITEM_SCROLL_OF_WATERS] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ScrollOfWaters, gItemIconPalette_ScrollOfWaters}, + [ITEM_TERA_ORB] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_TeraOrb, gItemIconPalette_TeraOrb}, + [ITEM_TINY_BAMBOO_SHOOT] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_TinyBambooShoot, gItemIconPalette_TinyBambooShoot}, + [ITEM_BUG_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BugTeraShard, gItemIconPalette_BugTeraShard}, + [ITEM_DARK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DarkTeraShard, gItemIconPalette_DarkTeraShard}, + [ITEM_DRAGON_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DragonTeraShard, gItemIconPalette_DragonTeraShard}, + [ITEM_ELECTRIC_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ElectricTeraShard, gItemIconPalette_ElectricTeraShard}, + [ITEM_FAIRY_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FairyTeraShard, gItemIconPalette_FairyTeraShard}, + [ITEM_FIGHTING_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FightingTeraShard, gItemIconPalette_FightingTeraShard}, + [ITEM_FIRE_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FireTeraShard, gItemIconPalette_FireTeraShard}, + [ITEM_FLYING_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FlyingTeraShard, gItemIconPalette_FlyingTeraShard}, + [ITEM_GHOST_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GhostTeraShard, gItemIconPalette_GhostTeraShard}, + [ITEM_GRASS_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GrassTeraShard, gItemIconPalette_GrassTeraShard}, + [ITEM_GROUND_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GroundTeraShard, gItemIconPalette_GroundTeraShard}, + [ITEM_ICE_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_IceTeraShard, gItemIconPalette_IceTeraShard}, + [ITEM_NORMAL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_NormalTeraShard, gItemIconPalette_NormalTeraShard}, + [ITEM_POISON_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PoisonTeraShard, gItemIconPalette_PoisonTeraShard}, + [ITEM_PSYCHIC_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PsychicTeraShard, gItemIconPalette_PsychicTeraShard}, + [ITEM_ROCK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_RockTeraShard, gItemIconPalette_RockTeraShard}, + [ITEM_STEEL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_SteelTeraShard, gItemIconPalette_SteelTeraShard}, + [ITEM_WATER_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_WaterTeraShard, gItemIconPalette_WaterTeraShard}, // Return to field arrow [ITEMS_COUNT] = {gItemIcon_ReturnToFieldArrow, gItemIconPalette_ReturnToFieldArrow}, }; diff --git a/src/data/items.h b/src/data/items.h index 63446913e..a4e311338 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -9907,7 +9907,6 @@ const struct Item gItems[] = [ITEM_LOADED_DICE] = { - //YellwApricorn .name = _("Loaded Dice"), .itemId = ITEM_LOADED_DICE, .price = 20000, @@ -9918,4 +9917,334 @@ const struct Item gItems[] = .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .flingPower = 30, }, + + [ITEM_AUSPICIOUS_ARMOR] = + { + .name = _("AuspciousArmr"), + .itemId = ITEM_AUSPICIOUS_ARMOR, + .price = 3000, + .description = sAuspiciousArmorDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_EvolutionStone, + .flingPower = 30, + }, + + [ITEM_BOOSTER_ENERGY] = + { + .name = _("BoosterEnergy"), + .itemId = ITEM_BOOSTER_ENERGY, + .price = 0, + .holdEffect = HOLD_EFFECT_BOOSTER_ENERGY, + .description = sBoosterEnergyDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .flingPower = 30, + }, + + [ITEM_BIG_BAMBOO_SHOOT] = + { + .name = _("BigBmbooShoot"), + .itemId = ITEM_BIG_BAMBOO_SHOOT, + .price = 3000, + .description = sBigBambooShootDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .flingPower = 30, + }, + + [ITEM_GIMMIGHOUL_COIN] = + { + .name = _("GimighoulCoin"), + .itemId = ITEM_GIMMIGHOUL_COIN, + .price = 400, + .description = sGimmighoulCoinDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_LEADERS_CREST] = + { + .name = _("Leader'sCrest"), + .itemId = ITEM_LEADERS_CREST, + .price = 3000, + .description = sLeadersCrestDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_MALICIOUS_ARMOR] = + { + .name = _("MaliciousArmr"), + .itemId = ITEM_MALICIOUS_ARMOR, + .price = 3000, + .description = sMaliciousArmorDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_EvolutionStone, + .flingPower = 30, + }, + + [ITEM_MIRROR_HERB] = + { + .name = _("Mirror Herb"), + .itemId = ITEM_MIRROR_HERB, + .price = 30000, + .holdEffect = HOLD_EFFECT_MIRROR_HERB, + .description = sMirrorHerbDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .flingPower = 30, + }, + + [ITEM_SCROLL_OF_DARKNESS] = + { + .name = _("ScrllOfDrknss"), + .itemId = ITEM_SCROLL_OF_DARKNESS, + .price = 0, + .description = sScrollOfDarknessDesc, + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_EvolutionStone, + }, + + [ITEM_SCROLL_OF_WATERS] = + { + .name = _("ScrollOfWatrs"), + .itemId = ITEM_SCROLL_OF_WATERS, + .price = 0, + .description = sScrollOfWatersDesc, + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_EvolutionStone, + }, + + [ITEM_TERA_ORB] = + { + .name = _("Tera Orb"), + .itemId = ITEM_TERA_ORB, + .price = 0, + .description = sTeraOrbDesc, + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_TINY_BAMBOO_SHOOT] = + { + .name = _("TinyBmbooShot"), + .itemId = ITEM_TINY_BAMBOO_SHOOT, + .price = 750, + .description = sTinyBambooShootDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .flingPower = 30, + }, + + [ITEM_BUG_TERA_SHARD] = + { + .name = _("Bug TeraShard"), + .itemId = ITEM_BUG_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_DARK_TERA_SHARD] = + { + .name = _("DarkTeraShard"), + .itemId = ITEM_DARK_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_DRAGON_TERA_SHARD] = + { + .name = _("DragnTeraShrd"), + .itemId = ITEM_DRAGON_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_ELECTRIC_TERA_SHARD] = + { + .name = _("EltrcTeraShrd"), + .itemId = ITEM_ELECTRIC_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_FAIRY_TERA_SHARD] = + { + .name = _("FairyTeraShrd"), + .itemId = ITEM_FAIRY_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_FIGHTING_TERA_SHARD] = + { + .name = _("FghtngTerShrd"), + .itemId = ITEM_FIGHTING_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_FIRE_TERA_SHARD] = + { + .name = _("FireTeraShard"), + .itemId = ITEM_FIRE_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_FLYING_TERA_SHARD] = + { + .name = _("FlyngTeraShrd"), + .itemId = ITEM_FLYING_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_GHOST_TERA_SHARD] = + { + .name = _("GhostTeraShrd"), + .itemId = ITEM_GHOST_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_GRASS_TERA_SHARD] = + { + .name = _("GrassTeraShrd"), + .itemId = ITEM_GRASS_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_GROUND_TERA_SHARD] = + { + .name = _("GrondTeraShrd"), + .itemId = ITEM_GROUND_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_ICE_TERA_SHARD] = + { + .name = _("Ice TeraShard"), + .itemId = ITEM_ICE_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_NORMAL_TERA_SHARD] = + { + .name = _("NormlTeraShrd"), + .itemId = ITEM_NORMAL_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_POISON_TERA_SHARD] = + { + .name = _("PoisnTeraShrd"), + .itemId = ITEM_POISON_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_PSYCHIC_TERA_SHARD] = + { + .name = _("PschcTeraShrd"), + .itemId = ITEM_PSYCHIC_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_ROCK_TERA_SHARD] = + { + .name = _("RockTeraShard"), + .itemId = ITEM_ROCK_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_STEEL_TERA_SHARD] = + { + .name = _("SteelTeraShrd"), + .itemId = ITEM_STEEL_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, + + [ITEM_WATER_TERA_SHARD] = + { + .name = _("WaterTeraShrd"), + .itemId = ITEM_WATER_TERA_SHARD, + .price = 0, + .description = sTeraShardDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + }, }; diff --git a/src/data/text/item_descriptions.h b/src/data/text/item_descriptions.h index e815d8ae1..cee413aa5 100644 --- a/src/data/text/item_descriptions.h +++ b/src/data/text/item_descriptions.h @@ -3817,3 +3817,63 @@ static const u8 sLoadedDiceDesc[] = _( "Rolls high numbers.\n" "Multihit strikes\n" "hit more times."); + +static const u8 sAuspiciousArmorDesc[] = _( + "Armor inhabited by\n" + "auspicious wishes.\n" + "Causes evolution."); + +static const u8 sBoosterEnergyDesc[] = _( + "Encapsuled energy\n" + "ups Pokémon with\n" + "certain Abilities."); + +static const u8 sBigBambooShootDesc[] = _( + "A large and rare\n" + "bamboo shoot. Best\n" + "sold to gourmands."); + +static const u8 sGimmighoulCoinDesc[] = _( + "Gimmighoul hoard\n" + "and treasure these\n" + "curious coins."); + +static const u8 sLeadersCrestDesc[] = _( + "A shard of an old\n" + "blade of some sort.\n" + "Held by Bisharp."); + +static const u8 sMaliciousArmorDesc[] = _( + "Armor inhabited by\n" + "malicious will.\n" + "Causes evolution."); + +static const u8 sMirrorHerbDesc[] = _( + "Mirrors an enemy's\n" + "stat increases\n" + "but only once."); + +static const u8 sScrollOfDarknessDesc[] = _( + "A peculiar scroll\n" + "with secrets of\n" + "the dark path."); + +static const u8 sScrollOfWatersDesc[] = _( + "A peculiar scroll\n" + "with secrets of\n" + "the water path."); + +static const u8 sTeraOrbDesc[] = _( + "Energy charges can\n" + "be used to cause\n" + "Terastallization."); + +static const u8 sTinyBambooShootDesc[] = _( + "A small and rare\n" + "bamboo shoot. Best\n" + "sold to gourmands."); + +static const u8 sTeraShardDesc[] = _( + "These shards may\n" + "form when a Tera\n" + "Pokémon faints."); From d480fc90f2edd085da8e1ba7fd983fa8c57d2b0c Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sun, 19 Mar 2023 21:21:43 +0100 Subject: [PATCH 21/40] Fix Scrolls type --- src/data/items.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/items.h b/src/data/items.h index a4e311338..982375f3e 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -10010,7 +10010,7 @@ const struct Item gItems[] = .description = sScrollOfDarknessDesc, .importance = 1, .pocket = POCKET_KEY_ITEMS, - .type = ITEM_USE_BAG_MENU, + .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_EvolutionStone, }, @@ -10022,7 +10022,7 @@ const struct Item gItems[] = .description = sScrollOfWatersDesc, .importance = 1, .pocket = POCKET_KEY_ITEMS, - .type = ITEM_USE_BAG_MENU, + .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_EvolutionStone, }, From 9ec31c61bfdfe01803a83f152b962ae324b8df69 Mon Sep 17 00:00:00 2001 From: May8th1995 <69943962+May8th1995@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:12:46 +0000 Subject: [PATCH 22/40] Fixes weirdly set value for eject buttons etc (#2846) * Fixes weirdly set value for eject buttons etc * Remove known fialing from fixed test --------- Co-authored-by: tennis --- data/battle_scripts_1.s | 1 + test/ability_intimidate.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 52c135294..a32e9a4ec 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8654,6 +8654,7 @@ BattleScript_IntimidateLoopIncrement: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_IntimidateLoop BattleScript_IntimidateEnd: + copybyte sBATTLER, gBattlerAttacker destroyabilitypopup pause B_WAIT_TIME_MED end3 diff --git a/test/ability_intimidate.c b/test/ability_intimidate.c index 347c0bc71..0318435e0 100644 --- a/test/ability_intimidate.c +++ b/test/ability_intimidate.c @@ -98,7 +98,6 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack") { - KNOWN_FAILING; // Issue #2837 GIVEN { ASSUME(gItems[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON); PLAYER(SPECIES_WOBBUFFET); From 358180946d60e5acc6fd5a95bb95f39232b93382 Mon Sep 17 00:00:00 2001 From: tennis Date: Wed, 22 Mar 2023 11:42:32 +0000 Subject: [PATCH 23/40] Fix Intimidate targetting dead sides and false postpones --- data/battle_scripts_1.s | 1 + src/battle_script_commands.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a32e9a4ec..2754b5d7a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8627,6 +8627,7 @@ BattleScript_IntimidateActivates:: BattleScript_IntimidateLoop: jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_IntimidateLoopIncrement jumpiftargetally BattleScript_IntimidateLoopIncrement + jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement jumpifholdeffect BS_TARGET, HOLD_EFFECT_CLEAR_AMULET, BattleScript_IntimidatePrevented_Item jumpifability BS_TARGET, ABILITY_CLEAR_BODY, BattleScript_IntimidatePrevented diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 126ca06fd..7949dbae8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6973,9 +6973,9 @@ bool32 ShouldPostponeSwitchInAbilities(u32 battlerId) // Checks for double battle, so abilities like Intimidate wait until all battlers are switched-in before activating. if (IsDoubleBattle()) { - if (aliveOpposing1 && !aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_OPPOSITE(battlerId), PARTY_SIZE, PARTY_SIZE)) + if (aliveOpposing1 && !aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerId)), PARTY_SIZE, PARTY_SIZE)) return TRUE; - if (!aliveOpposing1 && aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerId)), PARTY_SIZE, PARTY_SIZE)) + if (!aliveOpposing1 && aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_OPPOSITE(battlerId), PARTY_SIZE, PARTY_SIZE)) return TRUE; } From 4541cc3319454410db2dc5f298546117357960c3 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Wed, 22 Mar 2023 22:30:24 +0100 Subject: [PATCH 24/40] add check for Gorilla Tactics knock off shouldnt reset choicedMove when the pokemon has the Gorilla Tactics ability --- src/battle_script_commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 126ca06fd..0038844ac 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5330,7 +5330,8 @@ static bool32 TryKnockOffBattleScript(u32 battlerDef) gLastUsedItem = gBattleMons[battlerDef].item; gBattleMons[battlerDef].item = 0; - gBattleStruct->choicedMove[battlerDef] = 0; + if (gBattleMons[battlerDef].ability != ABILITY_GORILLA_TACTICS) + gBattleStruct->choicedMove[battlerDef] = 0; gWishFutureKnock.knockedOffMons[side] |= gBitTable[gBattlerPartyIndexes[battlerDef]]; CheckSetUnburden(battlerDef); From 299911139a56e36460fb54d0784cf55ca9d947d8 Mon Sep 17 00:00:00 2001 From: tennis Date: Wed, 22 Mar 2023 22:25:20 +0000 Subject: [PATCH 25/40] Add test for intimidate on empty slot --- test/ability_intimidate.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/ability_intimidate.c b/test/ability_intimidate.c index 0318435e0..a891f9af8 100644 --- a/test/ability_intimidate.c +++ b/test/ability_intimidate.c @@ -122,3 +122,40 @@ SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack") } } } + +DOUBLE_BATTLE_TEST("Intimidate activates on an empty slot") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_CROAGUNK); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_HITMONTOP) { Ability(ABILITY_INTIMIDATE); }; + OPPONENT(SPECIES_RALTS); + OPPONENT(SPECIES_AZURILL); + } WHEN { + TURN { + SWITCH(playerLeft, 2); + MOVE(playerRight, MOVE_GUNK_SHOT, target: opponentLeft); + MOVE(opponentRight, MOVE_SPLASH); + } + TURN { + SWITCH(playerLeft, 3); + MOVE(playerRight, MOVE_SPLASH); + } + + + } SCENE { + MESSAGE("Wobbuffet, that's enough! Come back!"); + MESSAGE("Go! Wynaut!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GUNK_SHOT, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponentRight); + MESSAGE("Wynaut, that's enough! Come back!"); + MESSAGE("Go! Hitmontop!"); + ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + NONE_OF { + MESSAGE("Hitmontop's Intimidate cuts Foe Ralts's attack!"); + } + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Hitmontop's Intimidate cuts Foe Azurill's attack!"); + } +} From 03f6fe635f856f6f87798bbe5651daf23e919e40 Mon Sep 17 00:00:00 2001 From: AgustinGDLV <103095241+AgustinGDLV@users.noreply.github.com> Date: Thu, 23 Mar 2023 01:50:44 -0700 Subject: [PATCH 26/40] changed CanBeConfused to actually check its argument (#2855) --- src/battle_util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index fcfec90c2..bcdc13080 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6546,8 +6546,8 @@ bool32 CanBeFrozen(u8 battlerId) bool32 CanBeConfused(u8 battlerId) { - if (GetBattlerAbility(gEffectBattler) == ABILITY_OWN_TEMPO - || gBattleMons[gEffectBattler].status2 & STATUS2_CONFUSION + if (GetBattlerAbility(battlerId) == ABILITY_OWN_TEMPO + || gBattleMons[battlerId].status2 & STATUS2_CONFUSION || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; From 023cc4d38dc144f13cd76baeded914f3b01fe709 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Thu, 23 Mar 2023 05:58:26 -0300 Subject: [PATCH 27/40] Fixed erroneous uses of non e-reader Enigma Berries (#2839) --- src/battle_util.c | 6 +++--- src/battle_z_move.c | 2 +- src/pokemon.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index bcdc13080..3e8f94bf6 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8194,7 +8194,7 @@ u32 GetBattlerHoldEffect(u8 battlerId, bool32 checkNegating) return gBattleStruct->debugHoldEffects[battlerId]; else #endif - if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) + if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battlerId].holdEffect; else return ItemId_GetHoldEffect(gBattleMons[battlerId].item); @@ -8203,7 +8203,7 @@ u32 GetBattlerHoldEffect(u8 battlerId, bool32 checkNegating) // static u32 GetBattlerItemHoldEffectParam(u8 battlerId, u16 item) { - if (item == ITEM_ENIGMA_BERRY) + if (item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battlerId].holdEffectParam; else return ItemId_GetHoldEffectParam(item); @@ -8211,7 +8211,7 @@ static u32 GetBattlerItemHoldEffectParam(u8 battlerId, u16 item) u32 GetBattlerHoldEffectParam(u8 battlerId) { - if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) + if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battlerId].holdEffectParam; else return ItemId_GetHoldEffectParam(gBattleMons[battlerId].item); diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 9c9c5e0fa..104a44daa 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -199,7 +199,7 @@ bool32 IsViableZMove(u8 battlerId, u16 move) holdEffect = gBattleStruct->debugHoldEffects[battlerId]; else #endif - if (item == ITEM_ENIGMA_BERRY) + if (item == ITEM_ENIGMA_BERRY_E_READER) return FALSE; // HoldEffect = gEnigmaBerries[battlerId].holdEffect; else holdEffect = ItemId_GetHoldEffect(item); diff --git a/src/pokemon.c b/src/pokemon.c index 8087b9621..4250d3244 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6543,7 +6543,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s partnerSpecies = GetMonData(tradePartner, MON_DATA_SPECIES, 0); partnerHeldItem = GetMonData(tradePartner, MON_DATA_HELD_ITEM, 0); - if (partnerHeldItem == ITEM_ENIGMA_BERRY) + if (partnerHeldItem == ITEM_ENIGMA_BERRY_E_READER) partnerHoldEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect; else partnerHoldEffect = ItemId_GetHoldEffect(partnerHeldItem); @@ -7213,7 +7213,7 @@ void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies) u8 bonus; heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0); - if (heldItem == ITEM_ENIGMA_BERRY) + if (heldItem == ITEM_ENIGMA_BERRY_E_READER) { if (gMain.inBattle) holdEffect = gEnigmaBerries[0].holdEffect; From 2658fe32c20d338cf5073a407353d028a449fdd5 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Thu, 23 Mar 2023 06:02:32 -0300 Subject: [PATCH 28/40] Removed ITEM_HAS_EFFECT (#2840) --- include/constants/items.h | 3 - include/pokemon.h | 3 +- src/battle_ai_switch_items.c | 4 +- src/data/pokemon/item_effects.h | 208 ++++++++++++++++---------------- src/party_menu.c | 17 +-- src/pokemon.c | 12 +- 6 files changed, 123 insertions(+), 124 deletions(-) diff --git a/include/constants/items.h b/include/constants/items.h index 1cb2803f3..a5a3c95e2 100644 --- a/include/constants/items.h +++ b/include/constants/items.h @@ -1001,7 +1001,4 @@ #define ITEM_B_USE_MEDICINE 1 #define ITEM_B_USE_OTHER 2 -// Check if the item is one that can be used on a Pokemon. -#define ITEM_HAS_EFFECT(item) ((item) >= ITEM_POTION && (item) <= LAST_BERRY_INDEX) - #endif // GUARD_CONSTANTS_ITEMS_H diff --git a/include/pokemon.h b/include/pokemon.h index cbfd194c6..581677cae 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -2,6 +2,7 @@ #define GUARD_POKEMON_H #include "sprite.h" +#include "constants/items.h" #include "constants/region_map_sections.h" #include "constants/map_groups.h" @@ -401,7 +402,7 @@ extern const struct BattleMove gBattleMoves[]; extern const u8 gFacilityClassToPicIndex[]; extern const u8 gFacilityClassToTrainerClass[]; extern const struct SpeciesInfo gSpeciesInfo[]; -extern const u8 *const gItemEffectTable[]; +extern const u8 *const gItemEffectTable[ITEMS_COUNT]; extern const u32 gExperienceTables[][MAX_LEVEL + 1]; extern const struct LevelUpMove *const gLevelUpLearnsets[]; extern const u16 *const gTeachableLearnsets[]; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index e8512affa..d1272d67d 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1085,13 +1085,13 @@ static bool8 ShouldUseItem(void) item = gBattleResources->battleHistory->trainerItems[i]; if (item == ITEM_NONE) continue; - if (gItemEffectTable[item - ITEM_POTION] == NULL) + if (gItemEffectTable[item] == NULL) continue; if (item == ITEM_ENIGMA_BERRY_E_READER) itemEffects = gSaveBlock1Ptr->enigmaBerry.itemEffect; else - itemEffects = gItemEffectTable[item - ITEM_POTION]; + itemEffects = gItemEffectTable[item]; *(gBattleStruct->AI_itemType + gActiveBattler / 2) = GetAI_ItemType(item, itemEffects); diff --git a/src/data/pokemon/item_effects.h b/src/data/pokemon/item_effects.h index 2651b4daf..8323801d5 100644 --- a/src/data/pokemon/item_effects.h +++ b/src/data/pokemon/item_effects.h @@ -447,128 +447,128 @@ const u8 gItemEffect_TamatoBerry[10] = { EV_BERRY_FRIENDSHIP_CHANGE, }; -const u8 *const gItemEffectTable[] = +const u8 *const gItemEffectTable[ITEMS_COUNT] = { // Medicine - [ITEM_POTION - ITEM_POTION] = gItemEffect_Potion, - [ITEM_SUPER_POTION - ITEM_POTION] = gItemEffect_SuperPotion, - [ITEM_HYPER_POTION - ITEM_POTION] = gItemEffect_HyperPotion, - [ITEM_MAX_POTION - ITEM_POTION] = gItemEffect_MaxPotion, - [ITEM_FULL_RESTORE - ITEM_POTION] = gItemEffect_FullRestore, - [ITEM_REVIVE - ITEM_POTION] = gItemEffect_Revive, - [ITEM_MAX_REVIVE - ITEM_POTION] = gItemEffect_MaxRevive, - [ITEM_FRESH_WATER - ITEM_POTION] = gItemEffect_FreshWater, - [ITEM_SODA_POP - ITEM_POTION] = gItemEffect_SodaPop, - [ITEM_LEMONADE - ITEM_POTION] = gItemEffect_Lemonade, - [ITEM_MOOMOO_MILK - ITEM_POTION] = gItemEffect_MoomooMilk, - [ITEM_ENERGY_POWDER - ITEM_POTION] = gItemEffect_EnergyPowder, - [ITEM_ENERGY_ROOT - ITEM_POTION] = gItemEffect_EnergyRoot, - [ITEM_HEAL_POWDER - ITEM_POTION] = gItemEffect_HealPowder, - [ITEM_REVIVAL_HERB - ITEM_POTION] = gItemEffect_RevivalHerb, - [ITEM_ANTIDOTE - ITEM_POTION] = gItemEffect_Antidote, - [ITEM_PARALYZE_HEAL - ITEM_POTION] = gItemEffect_ParalyzeHeal, - [ITEM_BURN_HEAL - ITEM_POTION] = gItemEffect_BurnHeal, - [ITEM_ICE_HEAL - ITEM_POTION] = gItemEffect_IceHeal, - [ITEM_AWAKENING - ITEM_POTION] = gItemEffect_Awakening, - [ITEM_FULL_HEAL - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_ETHER - ITEM_POTION] = gItemEffect_Ether, - [ITEM_MAX_ETHER - ITEM_POTION] = gItemEffect_MaxEther, - [ITEM_ELIXIR - ITEM_POTION] = gItemEffect_Elixir, - [ITEM_MAX_ELIXIR - ITEM_POTION] = gItemEffect_MaxElixir, - [ITEM_BERRY_JUICE - ITEM_POTION] = gItemEffect_BerryJuice, - [ITEM_SACRED_ASH - ITEM_POTION] = gItemEffect_SacredAsh, - [ITEM_SWEET_HEART - ITEM_POTION] = gItemEffect_Potion, - [ITEM_MAX_HONEY - ITEM_POTION] = gItemEffect_MaxRevive, + [ITEM_POTION] = gItemEffect_Potion, + [ITEM_SUPER_POTION] = gItemEffect_SuperPotion, + [ITEM_HYPER_POTION] = gItemEffect_HyperPotion, + [ITEM_MAX_POTION] = gItemEffect_MaxPotion, + [ITEM_FULL_RESTORE] = gItemEffect_FullRestore, + [ITEM_REVIVE] = gItemEffect_Revive, + [ITEM_MAX_REVIVE] = gItemEffect_MaxRevive, + [ITEM_FRESH_WATER] = gItemEffect_FreshWater, + [ITEM_SODA_POP] = gItemEffect_SodaPop, + [ITEM_LEMONADE] = gItemEffect_Lemonade, + [ITEM_MOOMOO_MILK] = gItemEffect_MoomooMilk, + [ITEM_ENERGY_POWDER] = gItemEffect_EnergyPowder, + [ITEM_ENERGY_ROOT] = gItemEffect_EnergyRoot, + [ITEM_HEAL_POWDER] = gItemEffect_HealPowder, + [ITEM_REVIVAL_HERB] = gItemEffect_RevivalHerb, + [ITEM_ANTIDOTE] = gItemEffect_Antidote, + [ITEM_PARALYZE_HEAL] = gItemEffect_ParalyzeHeal, + [ITEM_BURN_HEAL] = gItemEffect_BurnHeal, + [ITEM_ICE_HEAL] = gItemEffect_IceHeal, + [ITEM_AWAKENING] = gItemEffect_Awakening, + [ITEM_FULL_HEAL] = gItemEffect_FullHeal, + [ITEM_ETHER] = gItemEffect_Ether, + [ITEM_MAX_ETHER] = gItemEffect_MaxEther, + [ITEM_ELIXIR] = gItemEffect_Elixir, + [ITEM_MAX_ELIXIR] = gItemEffect_MaxElixir, + [ITEM_BERRY_JUICE] = gItemEffect_BerryJuice, + [ITEM_SACRED_ASH] = gItemEffect_SacredAsh, + [ITEM_SWEET_HEART] = gItemEffect_Potion, + [ITEM_MAX_HONEY] = gItemEffect_MaxRevive, // Regional Specialties - [ITEM_PEWTER_CRUNCHIES - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_RAGE_CANDY_BAR - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_LAVA_COOKIE - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_OLD_GATEAU - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_CASTELIACONE - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_LUMIOSE_GALETTE - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_SHALOUR_SABLE - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_BIG_MALASADA - ITEM_POTION] = gItemEffect_FullHeal, + [ITEM_PEWTER_CRUNCHIES] = gItemEffect_FullHeal, + [ITEM_RAGE_CANDY_BAR] = gItemEffect_FullHeal, + [ITEM_LAVA_COOKIE] = gItemEffect_FullHeal, + [ITEM_OLD_GATEAU] = gItemEffect_FullHeal, + [ITEM_CASTELIACONE] = gItemEffect_FullHeal, + [ITEM_LUMIOSE_GALETTE] = gItemEffect_FullHeal, + [ITEM_SHALOUR_SABLE] = gItemEffect_FullHeal, + [ITEM_BIG_MALASADA] = gItemEffect_FullHeal, // Vitamins - [ITEM_HP_UP - ITEM_POTION] = gItemEffect_HPUp, - [ITEM_PROTEIN - ITEM_POTION] = gItemEffect_Protein, - [ITEM_IRON - ITEM_POTION] = gItemEffect_Iron, - [ITEM_CALCIUM - ITEM_POTION] = gItemEffect_Calcium, - [ITEM_ZINC - ITEM_POTION] = gItemEffect_Zinc, - [ITEM_CARBOS - ITEM_POTION] = gItemEffect_Carbos, - [ITEM_PP_UP - ITEM_POTION] = gItemEffect_PPUp, - [ITEM_PP_MAX - ITEM_POTION] = gItemEffect_PPMax, + [ITEM_HP_UP] = gItemEffect_HPUp, + [ITEM_PROTEIN] = gItemEffect_Protein, + [ITEM_IRON] = gItemEffect_Iron, + [ITEM_CALCIUM] = gItemEffect_Calcium, + [ITEM_ZINC] = gItemEffect_Zinc, + [ITEM_CARBOS] = gItemEffect_Carbos, + [ITEM_PP_UP] = gItemEffect_PPUp, + [ITEM_PP_MAX] = gItemEffect_PPMax, // EV Feathers - [ITEM_HEALTH_FEATHER - ITEM_POTION] = gItemEffect_HpFeather, - [ITEM_MUSCLE_FEATHER - ITEM_POTION] = gItemEffect_AtkFeather, - [ITEM_RESIST_FEATHER - ITEM_POTION] = gItemEffect_DefFeather, - [ITEM_GENIUS_FEATHER - ITEM_POTION] = gItemEffect_SpatkFeather, - [ITEM_CLEVER_FEATHER - ITEM_POTION] = gItemEffect_SpdefFeather, - [ITEM_SWIFT_FEATHER - ITEM_POTION] = gItemEffect_SpeedFeather, + [ITEM_HEALTH_FEATHER] = gItemEffect_HpFeather, + [ITEM_MUSCLE_FEATHER] = gItemEffect_AtkFeather, + [ITEM_RESIST_FEATHER] = gItemEffect_DefFeather, + [ITEM_GENIUS_FEATHER] = gItemEffect_SpatkFeather, + [ITEM_CLEVER_FEATHER] = gItemEffect_SpdefFeather, + [ITEM_SWIFT_FEATHER] = gItemEffect_SpeedFeather, // Candy - [ITEM_RARE_CANDY - ITEM_POTION] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_XS - ITEM_POTION] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_S - ITEM_POTION] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_M - ITEM_POTION] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_L - ITEM_POTION] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_XL - ITEM_POTION] = gItemEffect_RareCandy, - //[ITEM_DYNAMAX_CANDY - ITEM_POTION] = gItemEffect_DynamaxCandy, // Todo + [ITEM_RARE_CANDY] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_XS] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_S] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_M] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_L] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_XL] = gItemEffect_RareCandy, + //[ITEM_DYNAMAX_CANDY] = gItemEffect_DynamaxCandy, // Todo // Medicinal Flutes - [ITEM_BLUE_FLUTE - ITEM_POTION] = gItemEffect_BlueFlute, - [ITEM_YELLOW_FLUTE - ITEM_POTION] = gItemEffect_YellowFlute, - [ITEM_RED_FLUTE - ITEM_POTION] = gItemEffect_RedFlute, + [ITEM_BLUE_FLUTE] = gItemEffect_BlueFlute, + [ITEM_YELLOW_FLUTE] = gItemEffect_YellowFlute, + [ITEM_RED_FLUTE] = gItemEffect_RedFlute, // X Items - [ITEM_X_ATTACK - ITEM_POTION] = gItemEffect_XAttack, - [ITEM_X_DEFENSE - ITEM_POTION] = gItemEffect_XDefense, - [ITEM_X_SPEED - ITEM_POTION] = gItemEffect_XSpeed, - [ITEM_X_ACCURACY - ITEM_POTION] = gItemEffect_XAccuracy, - [ITEM_X_SP_ATK - ITEM_POTION] = gItemEffect_XSpecialAttack, - [ITEM_X_SP_DEF - ITEM_POTION] = gItemEffect_XSpecialDefense, + [ITEM_X_ATTACK] = gItemEffect_XAttack, + [ITEM_X_DEFENSE] = gItemEffect_XDefense, + [ITEM_X_SPEED] = gItemEffect_XSpeed, + [ITEM_X_ACCURACY] = gItemEffect_XAccuracy, + [ITEM_X_SP_ATK] = gItemEffect_XSpecialAttack, + [ITEM_X_SP_DEF] = gItemEffect_XSpecialDefense, - [ITEM_DIRE_HIT - ITEM_POTION] = gItemEffect_DireHit, - [ITEM_GUARD_SPEC - ITEM_POTION] = gItemEffect_GuardSpec, + [ITEM_DIRE_HIT] = gItemEffect_DireHit, + [ITEM_GUARD_SPEC] = gItemEffect_GuardSpec, - //[ITEM_MAX_MUSHROOMS - ITEM_POTION] = gItemEffect_MaxMushrooms, // Todo + //[ITEM_MAX_MUSHROOMS] = gItemEffect_MaxMushrooms, // Todo // Evolution Items - [ITEM_FIRE_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_WATER_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_THUNDER_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_LEAF_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_ICE_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_SUN_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_MOON_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_SHINY_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_DUSK_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_DAWN_STONE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_SWEET_APPLE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_TART_APPLE - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_CRACKED_POT - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_CHIPPED_POT - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_GALARICA_CUFF - ITEM_POTION] = gItemEffect_EvoItem, - [ITEM_GALARICA_WREATH - ITEM_POTION] = gItemEffect_EvoItem, + [ITEM_FIRE_STONE] = gItemEffect_EvoItem, + [ITEM_WATER_STONE] = gItemEffect_EvoItem, + [ITEM_THUNDER_STONE] = gItemEffect_EvoItem, + [ITEM_LEAF_STONE] = gItemEffect_EvoItem, + [ITEM_ICE_STONE] = gItemEffect_EvoItem, + [ITEM_SUN_STONE] = gItemEffect_EvoItem, + [ITEM_MOON_STONE] = gItemEffect_EvoItem, + [ITEM_SHINY_STONE] = gItemEffect_EvoItem, + [ITEM_DUSK_STONE] = gItemEffect_EvoItem, + [ITEM_DAWN_STONE] = gItemEffect_EvoItem, + [ITEM_SWEET_APPLE] = gItemEffect_EvoItem, + [ITEM_TART_APPLE] = gItemEffect_EvoItem, + [ITEM_CRACKED_POT] = gItemEffect_EvoItem, + [ITEM_CHIPPED_POT] = gItemEffect_EvoItem, + [ITEM_GALARICA_CUFF] = gItemEffect_EvoItem, + [ITEM_GALARICA_WREATH] = gItemEffect_EvoItem, // Berries - [ITEM_CHERI_BERRY - ITEM_POTION] = gItemEffect_CheriBerry, - [ITEM_CHESTO_BERRY - ITEM_POTION] = gItemEffect_ChestoBerry, - [ITEM_PECHA_BERRY - ITEM_POTION] = gItemEffect_PechaBerry, - [ITEM_RAWST_BERRY - ITEM_POTION] = gItemEffect_RawstBerry, - [ITEM_ASPEAR_BERRY - ITEM_POTION] = gItemEffect_AspearBerry, - [ITEM_LEPPA_BERRY - ITEM_POTION] = gItemEffect_LeppaBerry, - [ITEM_ORAN_BERRY - ITEM_POTION] = gItemEffect_OranBerry, - [ITEM_PERSIM_BERRY - ITEM_POTION] = gItemEffect_PersimBerry, - [ITEM_LUM_BERRY - ITEM_POTION] = gItemEffect_FullHeal, - [ITEM_SITRUS_BERRY - ITEM_POTION] = gItemEffect_SitrusBerry, - [ITEM_POMEG_BERRY - ITEM_POTION] = gItemEffect_PomegBerry, - [ITEM_KELPSY_BERRY - ITEM_POTION] = gItemEffect_KelpsyBerry, - [ITEM_QUALOT_BERRY - ITEM_POTION] = gItemEffect_QualotBerry, - [ITEM_HONDEW_BERRY - ITEM_POTION] = gItemEffect_HondewBerry, - [ITEM_GREPA_BERRY - ITEM_POTION] = gItemEffect_GrepaBerry, - [ITEM_TAMATO_BERRY - ITEM_POTION] = gItemEffect_TamatoBerry, - [LAST_BERRY_INDEX - ITEM_POTION] = NULL, + [ITEM_CHERI_BERRY] = gItemEffect_CheriBerry, + [ITEM_CHESTO_BERRY] = gItemEffect_ChestoBerry, + [ITEM_PECHA_BERRY] = gItemEffect_PechaBerry, + [ITEM_RAWST_BERRY] = gItemEffect_RawstBerry, + [ITEM_ASPEAR_BERRY] = gItemEffect_AspearBerry, + [ITEM_LEPPA_BERRY] = gItemEffect_LeppaBerry, + [ITEM_ORAN_BERRY] = gItemEffect_OranBerry, + [ITEM_PERSIM_BERRY] = gItemEffect_PersimBerry, + [ITEM_LUM_BERRY] = gItemEffect_FullHeal, + [ITEM_SITRUS_BERRY] = gItemEffect_SitrusBerry, + [ITEM_POMEG_BERRY] = gItemEffect_PomegBerry, + [ITEM_KELPSY_BERRY] = gItemEffect_KelpsyBerry, + [ITEM_QUALOT_BERRY] = gItemEffect_QualotBerry, + [ITEM_HONDEW_BERRY] = gItemEffect_HondewBerry, + [ITEM_GREPA_BERRY] = gItemEffect_GrepaBerry, + [ITEM_TAMATO_BERRY] = gItemEffect_TamatoBerry, + [LAST_BERRY_INDEX] = NULL, }; diff --git a/src/party_menu.c b/src/party_menu.c index 9a04f4c50..6964fa0c5 100755 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4287,7 +4287,10 @@ static bool8 IsHPRecoveryItem(u16 item) if (item == ITEM_ENIGMA_BERRY_E_READER) effect = gSaveBlock1Ptr->enigmaBerry.itemEffect; else - effect = gItemEffectTable[item - ITEM_POTION]; + effect = gItemEffectTable[item]; + + if (effect == NULL) + return FALSE; if (effect[4] & ITEM4_HEAL_HP) return TRUE; @@ -4798,9 +4801,9 @@ void ItemUseCB_PPRecovery(u8 taskId, TaskFunc task) if (item == ITEM_ENIGMA_BERRY_E_READER) effect = gSaveBlock1Ptr->enigmaBerry.itemEffect; else - effect = gItemEffectTable[item - ITEM_POTION]; + effect = gItemEffectTable[item]; - if (!(effect[4] & ITEM4_HEAL_PP_ONE)) + if (effect == NULL || !(effect[4] & ITEM4_HEAL_PP_ONE)) { gPartyMenu.data1 = 0; TryUsePPItem(taskId); @@ -5673,14 +5676,14 @@ u8 GetItemEffectType(u16 item) const u8 *itemEffect; u32 statusCure; - if (!ITEM_HAS_EFFECT(item)) - return ITEM_EFFECT_NONE; - // Read the item's effect properties. if (item == ITEM_ENIGMA_BERRY_E_READER) itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect; else - itemEffect = gItemEffectTable[item - ITEM_POTION]; + itemEffect = gItemEffectTable[item]; + + if (itemEffect == NULL) + return ITEM_EFFECT_NONE; if ((itemEffect[0] & ITEM0_DIRE_HIT) || itemEffect[1] || (itemEffect[3] & ITEM3_GUARD_SPEC)) return ITEM_EFFECT_X_ITEM; diff --git a/src/pokemon.c b/src/pokemon.c index 4250d3244..f9f7d811d 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -5770,9 +5770,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov } // Skip using the item if it won't do anything - if (!ITEM_HAS_EFFECT(item)) - return TRUE; - if (gItemEffectTable[item - ITEM_POTION] == NULL && item != ITEM_ENIGMA_BERRY_E_READER) + if (gItemEffectTable[item] == NULL && item != ITEM_ENIGMA_BERRY_E_READER) return TRUE; // Get item effect @@ -5785,7 +5783,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov } else { - itemEffect = gItemEffectTable[item - ITEM_POTION]; + itemEffect = gItemEffectTable[item]; } // Do item effect @@ -6339,9 +6337,9 @@ u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit) offset = ITEM_EFFECT_ARG_START; - temp = gItemEffectTable[itemId - ITEM_POTION]; + temp = gItemEffectTable[itemId]; - if (!temp && itemId != ITEM_ENIGMA_BERRY_E_READER) + if (temp != NULL && !temp && itemId != ITEM_ENIGMA_BERRY_E_READER) return 0; if (itemId == ITEM_ENIGMA_BERRY_E_READER) @@ -6469,7 +6467,7 @@ u8 *UseStatIncreaseItem(u16 itemId) } else { - itemEffect = gItemEffectTable[itemId - ITEM_POTION]; + itemEffect = gItemEffectTable[itemId]; } gPotentialItemEffectBattler = gBattlerInMenuId; From 8cf357a37ba4c57efd638a1bd22e24a3e31a649e Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 23 Mar 2023 12:29:04 +0100 Subject: [PATCH 29/40] tests for dire claw ceaseless edge barb barrage --- src/battle_message.c | 2 +- src/battle_script_commands.c | 2 +- test/move_effect_barb_barrage.c | 43 +++++++ test/move_effect_dire_claw.c | 138 +++++++++++++++++++++ test/move_effect_hit_set_entry_hazardss.c | 117 ++++++++++++++++++ test/move_effect_spikes.c | 8 +- test/move_effect_tri_attack.c | 144 ++++++++++++++++++++++ test/move_effect_venoshock.c | 27 ++++ 8 files changed, 475 insertions(+), 6 deletions(-) create mode 100644 test/move_effect_barb_barrage.c create mode 100644 test/move_effect_dire_claw.c create mode 100644 test/move_effect_hit_set_entry_hazardss.c create mode 100644 test/move_effect_tri_attack.c create mode 100644 test/move_effect_venoshock.c diff --git a/src/battle_message.c b/src/battle_message.c index 562680f89..2ecfac0c7 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -209,7 +209,7 @@ static const u8 sText_PkmnFellIntoNightmare[] = _("{B_DEF_NAME_WITH_PREFIX} fell static const u8 sText_PkmnLockedInNightmare[] = _("{B_ATK_NAME_WITH_PREFIX} is locked\nin a NIGHTMARE!"); static const u8 sText_PkmnLaidCurse[] = _("{B_ATK_NAME_WITH_PREFIX} cut its own HP and\nlaid a CURSE on {B_DEF_NAME_WITH_PREFIX}!"); static const u8 sText_PkmnAfflictedByCurse[] = _("{B_ATK_NAME_WITH_PREFIX} is afflicted\nby the CURSE!"); -static const u8 sText_SpikesScattered[] = _("Spikes were scattered all around\nthe opponent's side!"); +static const u8 sText_SpikesScattered[] = _("Spikes were scattered all around\n{B_DEF_TEAM2} team!"); static const u8 sText_PkmnHurtBySpikes[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is hurt\nby spikes!"); static const u8 sText_PkmnIdentified[] = _("{B_ATK_NAME_WITH_PREFIX} identified\n{B_DEF_NAME_WITH_PREFIX}!"); static const u8 sText_PkmnPerishCountFell[] = _("{B_ATK_NAME_WITH_PREFIX}'s PERISH count\nfell to {B_BUFF1}!"); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bd444e04e..34257d896 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3783,7 +3783,7 @@ void SetMoveEffect(bool32 primary, u32 certain) } break; case MOVE_EFFECT_SPIKES: - if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_SPIKES)) + if (gSideTimers[GetBattlerSide(gEffectBattler)].spikesAmount < 3) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED; BattleScriptPush(gBattlescriptCurrInstr + 1); diff --git a/test/move_effect_barb_barrage.c b/test/move_effect_barb_barrage.c new file mode 100644 index 000000000..9d9c2cc84 --- /dev/null +++ b/test/move_effect_barb_barrage.c @@ -0,0 +1,43 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + //ASSUME(gBattleMoves[MOVE_BARB_BARRAGE].effect == EFFECT_BARB_BARRAGE); +} + +SINGLE_BATTLE_TEST("Barb Barrage inflicts poison") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BARB_BARRAGE); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BARB_BARRAGE, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + } +} + +SINGLE_BATTLE_TEST("Barb Barrage's power doubles if the target is poisoned/badly poisoned", s16 damage) +{ + u32 status1; + PARAMETRIZE { status1 = 0; } + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) {Status1(status1);} + } WHEN { + TURN { MOVE(player, MOVE_BARB_BARRAGE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BARB_BARRAGE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage); + } +} diff --git a/test/move_effect_dire_claw.c b/test/move_effect_dire_claw.c new file mode 100644 index 000000000..a5270ce3f --- /dev/null +++ b/test/move_effect_dire_claw.c @@ -0,0 +1,138 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_DIRE_CLAW].effect == EFFECT_DIRE_CLAW); +} + +// found by brute-force +#define RNG_SLEEP 0xcb0 +#define RNG_POISON 0x2BE +#define RNG_PARALYSIS 5 + +SINGLE_BATTLE_TEST("Dire Claw can inflict poison, paralysis or sleep") +{ + u8 statusAnim; + u32 rng; + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; } + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DIRE_CLAW); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_PRZ) { + STATUS_ICON(opponent, paralysis: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_SLP) { + STATUS_ICON(opponent, sleep: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PSN) { + STATUS_ICON(opponent, poison: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze poison/electric types respectively") +{ + u8 statusAnim; + u16 species; + u32 rng; + #if B_PARALYZE_ELECTRIC >= GEN_6 + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; } + #endif // B_PARALYZE_ELECTRIC + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; species = SPECIES_ARBOK;} + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_DIRE_CLAW); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_PRZ) { + NOT STATUS_ICON(opponent, paralysis: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PSN) { + NOT STATUS_ICON(opponent, poison: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep pokemon with abilities preventing respective statuses") +{ + u8 statusAnim; + u16 species, ability; + u32 rng; + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; } + #if P_GEN_4_POKEMON == TRUE + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; } + #endif // P_GEN_4_POKEMON + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; species = SPECIES_ZANGOOSE; ability = ABILITY_IMMUNITY; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; species = SPECIES_VIGOROTH; ability = ABILITY_VITAL_SPIRIT; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; species = SPECIES_HYPNO; ability = ABILITY_INSOMNIA; } + + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species) {Ability(ability);} + } WHEN { + TURN { MOVE(player, MOVE_DIRE_CLAW); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_PRZ) { + NOT STATUS_ICON(opponent, paralysis: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_SLP) { + NOT STATUS_ICON(opponent, sleep: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PSN) { + NOT STATUS_ICON(opponent, poison: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep a mon which is already statused") +{ + u8 statusAnim; + u32 rng; + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; } + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_BURN);} + } WHEN { + TURN { MOVE(player, MOVE_DIRE_CLAW); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_PRZ) { + NOT STATUS_ICON(opponent, paralysis: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_SLP) { + NOT STATUS_ICON(opponent, sleep: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PSN) { + NOT STATUS_ICON(opponent, poison: TRUE); + } + } +} diff --git a/test/move_effect_hit_set_entry_hazardss.c b/test/move_effect_hit_set_entry_hazardss.c new file mode 100644 index 000000000..4e9761aac --- /dev/null +++ b/test/move_effect_hit_set_entry_hazardss.c @@ -0,0 +1,117 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_STONE_AXE].effect == EFFECT_HIT_SET_ENTRY_HAZARD); + ASSUME(gBattleMoves[MOVE_CEASELESS_EDGE].effect == EFFECT_HIT_SET_ENTRY_HAZARD); +} + +SINGLE_BATTLE_TEST("Stone Axe / Ceaseless Edge set up hazards after hitting the target") +{ + u16 move; + PARAMETRIZE {move = MOVE_STONE_AXE; } + PARAMETRIZE {move = MOVE_CEASELESS_EDGE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + TURN { SWITCH(opponent, 1); } + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + if (move == MOVE_CEASELESS_EDGE) { + MESSAGE("Spikes were scattered all around the opposing team!"); + } + else { + MESSAGE("Pointed stones float in the air around the opposing team!"); + } + MESSAGE("2 sent out Wobbuffet!"); + if (move == MOVE_CEASELESS_EDGE) { + HP_BAR(opponent, damage: maxHP / 8); + MESSAGE("Foe Wobbuffet is hurt by spikes!"); + } + else { + HP_BAR(opponent, damage: maxHP / 8); + MESSAGE("Pointed stones dug into Foe Wobbuffet!"); + } + } +} + +SINGLE_BATTLE_TEST("Ceaseless Edge can set up to 3 layers of Spikes") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { SWITCH(opponent, 1); } + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + MESSAGE("Spikes were scattered all around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + MESSAGE("Spikes were scattered all around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + MESSAGE("Spikes were scattered all around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + NOT MESSAGE("Spikes were scattered all around the opposing team!"); + + MESSAGE("2 sent out Wynaut!"); + HP_BAR(opponent, damage: maxHP / 4); + MESSAGE("Foe Wynaut is hurt by spikes!"); + } +} + +SINGLE_BATTLE_TEST("Stone Axe can set up pointed stones only once") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { SWITCH(opponent, 1); } + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + MESSAGE("Pointed stones float in the air around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + NOT MESSAGE("Pointed stones float in the air around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + NOT MESSAGE("Pointed stones float in the air around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + NOT MESSAGE("Pointed stones float in the air around the opposing team!"); + + MESSAGE("2 sent out Wynaut!"); + HP_BAR(opponent, damage: maxHP / 8); + MESSAGE("Pointed stones dug into Foe Wynaut!"); + } +} + diff --git a/test/move_effect_spikes.c b/test/move_effect_spikes.c index 33b0bad4b..0c84a5fb1 100644 --- a/test/move_effect_spikes.c +++ b/test/move_effect_spikes.c @@ -28,7 +28,7 @@ SINGLE_BATTLE_TEST("Spikes damage on switch in") s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); for (count = 0; count < layers; ++count) { ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player); - MESSAGE("Spikes were scattered all around the opponent's side!"); + MESSAGE("Spikes were scattered all around the opposing team!"); } MESSAGE("2 sent out Wynaut!"); HP_BAR(opponent, damage: maxHP / divisor); @@ -51,11 +51,11 @@ SINGLE_BATTLE_TEST("Spikes fails after 3 layers") } SCENE { s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player); - MESSAGE("Spikes were scattered all around the opponent's side!"); + MESSAGE("Spikes were scattered all around the opposing team!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player); - MESSAGE("Spikes were scattered all around the opponent's side!"); + MESSAGE("Spikes were scattered all around the opposing team!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player); - MESSAGE("Spikes were scattered all around the opponent's side!"); + MESSAGE("Spikes were scattered all around the opposing team!"); MESSAGE("Wobbuffet used Spikes!"); MESSAGE("But it failed!"); MESSAGE("2 sent out Wynaut!"); diff --git a/test/move_effect_tri_attack.c b/test/move_effect_tri_attack.c new file mode 100644 index 000000000..31627ef2d --- /dev/null +++ b/test/move_effect_tri_attack.c @@ -0,0 +1,144 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TRI_ATTACK].effect == EFFECT_TRI_ATTACK); +} + +// found by brute-force +#define RNG_PARALYSIS 0xcb0 +#define RNG_BURN 0x2BE +#define RNG_FREEZE 5 + +SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze") +{ + u8 statusAnim; + u32 rng; + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; } + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TRI_ATTACK); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_BRN) { + STATUS_ICON(opponent, burn: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_FRZ) { + STATUS_ICON(opponent, freeze: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PRZ) { + STATUS_ICON(opponent, paralysis: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice types respectively") +{ + u8 statusAnim; + u16 species; + u32 rng; + #if B_PARALYZE_ELECTRIC >= GEN_6 + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU;} + #endif // B_PARALYZE_ELECTRIC + PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_ARCANINE; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; species = SPECIES_GLALIE; } + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_TRI_ATTACK); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_BRN) { + NOT STATUS_ICON(opponent, burn: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_FRZ) { + NOT STATUS_ICON(opponent, freeze: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PRZ) { + NOT STATUS_ICON(opponent, paralysis: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilities preventing respective statuses") +{ + u8 statusAnim; + u16 species, ability; + u32 rng; + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; } + #if P_GEN_4_POKEMON == TRUE + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; } + #endif // P_GEN_4_POKEMON + #if P_GEN_7_POKEMON == TRUE + PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; } + #endif // P_GEN_7_POKEMON + PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_SEAKING; ability = ABILITY_WATER_VEIL; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; } + + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species) {Ability(ability);} + } WHEN { + TURN { MOVE(player, MOVE_TRI_ATTACK); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_BRN) { + NOT STATUS_ICON(opponent, burn: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_FRZ) { + NOT STATUS_ICON(opponent, freeze: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PRZ) { + NOT STATUS_ICON(opponent, paralysis: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is already statused") +{ + u8 statusAnim; + u32 rng; + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; } + GIVEN { + RNGSeed(rng); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);} + } WHEN { + TURN { MOVE(player, MOVE_TRI_ATTACK); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); + if (statusAnim == B_ANIM_STATUS_BRN) { + NOT STATUS_ICON(opponent, burn: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_FRZ) { + NOT STATUS_ICON(opponent, freeze: TRUE); + } + else if (statusAnim == B_ANIM_STATUS_PRZ) { + NOT STATUS_ICON(opponent, paralysis: TRUE); + } + } +} diff --git a/test/move_effect_venoshock.c b/test/move_effect_venoshock.c new file mode 100644 index 000000000..876f611c9 --- /dev/null +++ b/test/move_effect_venoshock.c @@ -0,0 +1,27 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_VENOSHOCK].effect == EFFECT_VENOSHOCK); +} + +SINGLE_BATTLE_TEST("Venoshock's power doubles if the target is poisoned/badly poisoned", s16 damage) +{ + u32 status1; + PARAMETRIZE { status1 = 0; } + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) {Status1(status1);} + } WHEN { + TURN { MOVE(player, MOVE_VENOSHOCK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOSHOCK, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage); + } +} From f15fb1540fe60d4f791cb537406cbd5f3ecdba0a Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Thu, 23 Mar 2023 23:16:08 +0100 Subject: [PATCH 30/40] Allow Kubfu evolution with Scroll items --- src/data/pokemon/evolution.h | 4 +- src/data/pokemon/item_effects.h | 210 ++++++++++++++++---------------- src/party_menu.c | 3 +- 3 files changed, 112 insertions(+), 105 deletions(-) diff --git a/src/data/pokemon/evolution.h b/src/data/pokemon/evolution.h index 4f09867a6..b76e83a73 100644 --- a/src/data/pokemon/evolution.h +++ b/src/data/pokemon/evolution.h @@ -542,7 +542,9 @@ const struct Evolution gEvolutionTable[NUM_SPECIES][EVOS_PER_MON] = [SPECIES_DREEPY] = {{EVO_LEVEL, 50, SPECIES_DRAKLOAK}}, [SPECIES_DRAKLOAK] = {{EVO_LEVEL, 60, SPECIES_DRAGAPULT}}, [SPECIES_KUBFU] = {{EVO_DARK_SCROLL, 0, SPECIES_URSHIFU}, - {EVO_WATER_SCROLL, 0, SPECIES_URSHIFU_RAPID_STRIKE_STYLE}}, + {EVO_ITEM, ITEM_SCROLL_OF_DARKNESS, SPECIES_URSHIFU}, + {EVO_WATER_SCROLL, 0, SPECIES_URSHIFU_RAPID_STRIKE_STYLE}, + {EVO_ITEM, ITEM_SCROLL_OF_WATERS, SPECIES_URSHIFU_RAPID_STRIKE_STYLE}}, #endif [SPECIES_RATTATA_ALOLAN] = {{EVO_LEVEL_NIGHT, 20, SPECIES_RATICATE_ALOLAN}}, [SPECIES_SANDSHREW_ALOLAN] = {{EVO_ITEM, ITEM_ICE_STONE, SPECIES_SANDSLASH_ALOLAN}}, diff --git a/src/data/pokemon/item_effects.h b/src/data/pokemon/item_effects.h index 8323801d5..607f57814 100644 --- a/src/data/pokemon/item_effects.h +++ b/src/data/pokemon/item_effects.h @@ -450,125 +450,129 @@ const u8 gItemEffect_TamatoBerry[10] = { const u8 *const gItemEffectTable[ITEMS_COUNT] = { // Medicine - [ITEM_POTION] = gItemEffect_Potion, - [ITEM_SUPER_POTION] = gItemEffect_SuperPotion, - [ITEM_HYPER_POTION] = gItemEffect_HyperPotion, - [ITEM_MAX_POTION] = gItemEffect_MaxPotion, - [ITEM_FULL_RESTORE] = gItemEffect_FullRestore, - [ITEM_REVIVE] = gItemEffect_Revive, - [ITEM_MAX_REVIVE] = gItemEffect_MaxRevive, - [ITEM_FRESH_WATER] = gItemEffect_FreshWater, - [ITEM_SODA_POP] = gItemEffect_SodaPop, - [ITEM_LEMONADE] = gItemEffect_Lemonade, - [ITEM_MOOMOO_MILK] = gItemEffect_MoomooMilk, - [ITEM_ENERGY_POWDER] = gItemEffect_EnergyPowder, - [ITEM_ENERGY_ROOT] = gItemEffect_EnergyRoot, - [ITEM_HEAL_POWDER] = gItemEffect_HealPowder, - [ITEM_REVIVAL_HERB] = gItemEffect_RevivalHerb, - [ITEM_ANTIDOTE] = gItemEffect_Antidote, - [ITEM_PARALYZE_HEAL] = gItemEffect_ParalyzeHeal, - [ITEM_BURN_HEAL] = gItemEffect_BurnHeal, - [ITEM_ICE_HEAL] = gItemEffect_IceHeal, - [ITEM_AWAKENING] = gItemEffect_Awakening, - [ITEM_FULL_HEAL] = gItemEffect_FullHeal, - [ITEM_ETHER] = gItemEffect_Ether, - [ITEM_MAX_ETHER] = gItemEffect_MaxEther, - [ITEM_ELIXIR] = gItemEffect_Elixir, - [ITEM_MAX_ELIXIR] = gItemEffect_MaxElixir, - [ITEM_BERRY_JUICE] = gItemEffect_BerryJuice, - [ITEM_SACRED_ASH] = gItemEffect_SacredAsh, - [ITEM_SWEET_HEART] = gItemEffect_Potion, - [ITEM_MAX_HONEY] = gItemEffect_MaxRevive, + [ITEM_POTION] = gItemEffect_Potion, + [ITEM_SUPER_POTION] = gItemEffect_SuperPotion, + [ITEM_HYPER_POTION] = gItemEffect_HyperPotion, + [ITEM_MAX_POTION] = gItemEffect_MaxPotion, + [ITEM_FULL_RESTORE] = gItemEffect_FullRestore, + [ITEM_REVIVE] = gItemEffect_Revive, + [ITEM_MAX_REVIVE] = gItemEffect_MaxRevive, + [ITEM_FRESH_WATER] = gItemEffect_FreshWater, + [ITEM_SODA_POP] = gItemEffect_SodaPop, + [ITEM_LEMONADE] = gItemEffect_Lemonade, + [ITEM_MOOMOO_MILK] = gItemEffect_MoomooMilk, + [ITEM_ENERGY_POWDER] = gItemEffect_EnergyPowder, + [ITEM_ENERGY_ROOT] = gItemEffect_EnergyRoot, + [ITEM_HEAL_POWDER] = gItemEffect_HealPowder, + [ITEM_REVIVAL_HERB] = gItemEffect_RevivalHerb, + [ITEM_ANTIDOTE] = gItemEffect_Antidote, + [ITEM_PARALYZE_HEAL] = gItemEffect_ParalyzeHeal, + [ITEM_BURN_HEAL] = gItemEffect_BurnHeal, + [ITEM_ICE_HEAL] = gItemEffect_IceHeal, + [ITEM_AWAKENING] = gItemEffect_Awakening, + [ITEM_FULL_HEAL] = gItemEffect_FullHeal, + [ITEM_ETHER] = gItemEffect_Ether, + [ITEM_MAX_ETHER] = gItemEffect_MaxEther, + [ITEM_ELIXIR] = gItemEffect_Elixir, + [ITEM_MAX_ELIXIR] = gItemEffect_MaxElixir, + [ITEM_BERRY_JUICE] = gItemEffect_BerryJuice, + [ITEM_SACRED_ASH] = gItemEffect_SacredAsh, + [ITEM_SWEET_HEART] = gItemEffect_Potion, + [ITEM_MAX_HONEY] = gItemEffect_MaxRevive, // Regional Specialties - [ITEM_PEWTER_CRUNCHIES] = gItemEffect_FullHeal, - [ITEM_RAGE_CANDY_BAR] = gItemEffect_FullHeal, - [ITEM_LAVA_COOKIE] = gItemEffect_FullHeal, - [ITEM_OLD_GATEAU] = gItemEffect_FullHeal, - [ITEM_CASTELIACONE] = gItemEffect_FullHeal, - [ITEM_LUMIOSE_GALETTE] = gItemEffect_FullHeal, - [ITEM_SHALOUR_SABLE] = gItemEffect_FullHeal, - [ITEM_BIG_MALASADA] = gItemEffect_FullHeal, + [ITEM_PEWTER_CRUNCHIES] = gItemEffect_FullHeal, + [ITEM_RAGE_CANDY_BAR] = gItemEffect_FullHeal, + [ITEM_LAVA_COOKIE] = gItemEffect_FullHeal, + [ITEM_OLD_GATEAU] = gItemEffect_FullHeal, + [ITEM_CASTELIACONE] = gItemEffect_FullHeal, + [ITEM_LUMIOSE_GALETTE] = gItemEffect_FullHeal, + [ITEM_SHALOUR_SABLE] = gItemEffect_FullHeal, + [ITEM_BIG_MALASADA] = gItemEffect_FullHeal, // Vitamins - [ITEM_HP_UP] = gItemEffect_HPUp, - [ITEM_PROTEIN] = gItemEffect_Protein, - [ITEM_IRON] = gItemEffect_Iron, - [ITEM_CALCIUM] = gItemEffect_Calcium, - [ITEM_ZINC] = gItemEffect_Zinc, - [ITEM_CARBOS] = gItemEffect_Carbos, - [ITEM_PP_UP] = gItemEffect_PPUp, - [ITEM_PP_MAX] = gItemEffect_PPMax, + [ITEM_HP_UP] = gItemEffect_HPUp, + [ITEM_PROTEIN] = gItemEffect_Protein, + [ITEM_IRON] = gItemEffect_Iron, + [ITEM_CALCIUM] = gItemEffect_Calcium, + [ITEM_ZINC] = gItemEffect_Zinc, + [ITEM_CARBOS] = gItemEffect_Carbos, + [ITEM_PP_UP] = gItemEffect_PPUp, + [ITEM_PP_MAX] = gItemEffect_PPMax, // EV Feathers - [ITEM_HEALTH_FEATHER] = gItemEffect_HpFeather, - [ITEM_MUSCLE_FEATHER] = gItemEffect_AtkFeather, - [ITEM_RESIST_FEATHER] = gItemEffect_DefFeather, - [ITEM_GENIUS_FEATHER] = gItemEffect_SpatkFeather, - [ITEM_CLEVER_FEATHER] = gItemEffect_SpdefFeather, - [ITEM_SWIFT_FEATHER] = gItemEffect_SpeedFeather, + [ITEM_HEALTH_FEATHER] = gItemEffect_HpFeather, + [ITEM_MUSCLE_FEATHER] = gItemEffect_AtkFeather, + [ITEM_RESIST_FEATHER] = gItemEffect_DefFeather, + [ITEM_GENIUS_FEATHER] = gItemEffect_SpatkFeather, + [ITEM_CLEVER_FEATHER] = gItemEffect_SpdefFeather, + [ITEM_SWIFT_FEATHER] = gItemEffect_SpeedFeather, // Candy - [ITEM_RARE_CANDY] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_XS] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_S] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_M] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_L] = gItemEffect_RareCandy, - [ITEM_EXP_CANDY_XL] = gItemEffect_RareCandy, - //[ITEM_DYNAMAX_CANDY] = gItemEffect_DynamaxCandy, // Todo + [ITEM_RARE_CANDY] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_XS] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_S] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_M] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_L] = gItemEffect_RareCandy, + [ITEM_EXP_CANDY_XL] = gItemEffect_RareCandy, + //[ITEM_DYNAMAX_CANDY] = gItemEffect_DynamaxCandy, // Todo // Medicinal Flutes - [ITEM_BLUE_FLUTE] = gItemEffect_BlueFlute, - [ITEM_YELLOW_FLUTE] = gItemEffect_YellowFlute, - [ITEM_RED_FLUTE] = gItemEffect_RedFlute, + [ITEM_BLUE_FLUTE] = gItemEffect_BlueFlute, + [ITEM_YELLOW_FLUTE] = gItemEffect_YellowFlute, + [ITEM_RED_FLUTE] = gItemEffect_RedFlute, // X Items - [ITEM_X_ATTACK] = gItemEffect_XAttack, - [ITEM_X_DEFENSE] = gItemEffect_XDefense, - [ITEM_X_SPEED] = gItemEffect_XSpeed, - [ITEM_X_ACCURACY] = gItemEffect_XAccuracy, - [ITEM_X_SP_ATK] = gItemEffect_XSpecialAttack, - [ITEM_X_SP_DEF] = gItemEffect_XSpecialDefense, + [ITEM_X_ATTACK] = gItemEffect_XAttack, + [ITEM_X_DEFENSE] = gItemEffect_XDefense, + [ITEM_X_SPEED] = gItemEffect_XSpeed, + [ITEM_X_ACCURACY] = gItemEffect_XAccuracy, + [ITEM_X_SP_ATK] = gItemEffect_XSpecialAttack, + [ITEM_X_SP_DEF] = gItemEffect_XSpecialDefense, - [ITEM_DIRE_HIT] = gItemEffect_DireHit, - [ITEM_GUARD_SPEC] = gItemEffect_GuardSpec, + [ITEM_DIRE_HIT] = gItemEffect_DireHit, + [ITEM_GUARD_SPEC] = gItemEffect_GuardSpec, - //[ITEM_MAX_MUSHROOMS] = gItemEffect_MaxMushrooms, // Todo + //[ITEM_MAX_MUSHROOMS] = gItemEffect_MaxMushrooms, // Todo // Evolution Items - [ITEM_FIRE_STONE] = gItemEffect_EvoItem, - [ITEM_WATER_STONE] = gItemEffect_EvoItem, - [ITEM_THUNDER_STONE] = gItemEffect_EvoItem, - [ITEM_LEAF_STONE] = gItemEffect_EvoItem, - [ITEM_ICE_STONE] = gItemEffect_EvoItem, - [ITEM_SUN_STONE] = gItemEffect_EvoItem, - [ITEM_MOON_STONE] = gItemEffect_EvoItem, - [ITEM_SHINY_STONE] = gItemEffect_EvoItem, - [ITEM_DUSK_STONE] = gItemEffect_EvoItem, - [ITEM_DAWN_STONE] = gItemEffect_EvoItem, - [ITEM_SWEET_APPLE] = gItemEffect_EvoItem, - [ITEM_TART_APPLE] = gItemEffect_EvoItem, - [ITEM_CRACKED_POT] = gItemEffect_EvoItem, - [ITEM_CHIPPED_POT] = gItemEffect_EvoItem, - [ITEM_GALARICA_CUFF] = gItemEffect_EvoItem, - [ITEM_GALARICA_WREATH] = gItemEffect_EvoItem, + [ITEM_FIRE_STONE] = gItemEffect_EvoItem, + [ITEM_WATER_STONE] = gItemEffect_EvoItem, + [ITEM_THUNDER_STONE] = gItemEffect_EvoItem, + [ITEM_LEAF_STONE] = gItemEffect_EvoItem, + [ITEM_ICE_STONE] = gItemEffect_EvoItem, + [ITEM_SUN_STONE] = gItemEffect_EvoItem, + [ITEM_MOON_STONE] = gItemEffect_EvoItem, + [ITEM_SHINY_STONE] = gItemEffect_EvoItem, + [ITEM_DUSK_STONE] = gItemEffect_EvoItem, + [ITEM_DAWN_STONE] = gItemEffect_EvoItem, + [ITEM_SWEET_APPLE] = gItemEffect_EvoItem, + [ITEM_TART_APPLE] = gItemEffect_EvoItem, + [ITEM_CRACKED_POT] = gItemEffect_EvoItem, + [ITEM_CHIPPED_POT] = gItemEffect_EvoItem, + [ITEM_GALARICA_CUFF] = gItemEffect_EvoItem, + [ITEM_GALARICA_WREATH] = gItemEffect_EvoItem, + [ITEM_AUSPICIOUS_ARMOR] = gItemEffect_EvoItem, + [ITEM_MALICIOUS_ARMOR] = gItemEffect_EvoItem, + [ITEM_SCROLL_OF_DARKNESS] = gItemEffect_EvoItem, + [ITEM_SCROLL_OF_WATERS] = gItemEffect_EvoItem, // Berries - [ITEM_CHERI_BERRY] = gItemEffect_CheriBerry, - [ITEM_CHESTO_BERRY] = gItemEffect_ChestoBerry, - [ITEM_PECHA_BERRY] = gItemEffect_PechaBerry, - [ITEM_RAWST_BERRY] = gItemEffect_RawstBerry, - [ITEM_ASPEAR_BERRY] = gItemEffect_AspearBerry, - [ITEM_LEPPA_BERRY] = gItemEffect_LeppaBerry, - [ITEM_ORAN_BERRY] = gItemEffect_OranBerry, - [ITEM_PERSIM_BERRY] = gItemEffect_PersimBerry, - [ITEM_LUM_BERRY] = gItemEffect_FullHeal, - [ITEM_SITRUS_BERRY] = gItemEffect_SitrusBerry, - [ITEM_POMEG_BERRY] = gItemEffect_PomegBerry, - [ITEM_KELPSY_BERRY] = gItemEffect_KelpsyBerry, - [ITEM_QUALOT_BERRY] = gItemEffect_QualotBerry, - [ITEM_HONDEW_BERRY] = gItemEffect_HondewBerry, - [ITEM_GREPA_BERRY] = gItemEffect_GrepaBerry, - [ITEM_TAMATO_BERRY] = gItemEffect_TamatoBerry, - [LAST_BERRY_INDEX] = NULL, + [ITEM_CHERI_BERRY] = gItemEffect_CheriBerry, + [ITEM_CHESTO_BERRY] = gItemEffect_ChestoBerry, + [ITEM_PECHA_BERRY] = gItemEffect_PechaBerry, + [ITEM_RAWST_BERRY] = gItemEffect_RawstBerry, + [ITEM_ASPEAR_BERRY] = gItemEffect_AspearBerry, + [ITEM_LEPPA_BERRY] = gItemEffect_LeppaBerry, + [ITEM_ORAN_BERRY] = gItemEffect_OranBerry, + [ITEM_PERSIM_BERRY] = gItemEffect_PersimBerry, + [ITEM_LUM_BERRY] = gItemEffect_FullHeal, + [ITEM_SITRUS_BERRY] = gItemEffect_SitrusBerry, + [ITEM_POMEG_BERRY] = gItemEffect_PomegBerry, + [ITEM_KELPSY_BERRY] = gItemEffect_KelpsyBerry, + [ITEM_QUALOT_BERRY] = gItemEffect_QualotBerry, + [ITEM_HONDEW_BERRY] = gItemEffect_HondewBerry, + [ITEM_GREPA_BERRY] = gItemEffect_GrepaBerry, + [ITEM_TAMATO_BERRY] = gItemEffect_TamatoBerry, + [LAST_BERRY_INDEX] = NULL, }; diff --git a/src/party_menu.c b/src/party_menu.c index 6964fa0c5..9185657a7 100755 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -5516,7 +5516,8 @@ void ItemUseCB_EvolutionStone(u8 taskId, TaskFunc task) } else { - RemoveBagItem(gSpecialVar_ItemId, 1); + if (ItemId_GetPocket(gSpecialVar_ItemId) != POCKET_KEY_ITEMS) + RemoveBagItem(gSpecialVar_ItemId, 1); FreePartyPointers(); } } From e601719c911b7c21421c36c69089a9f379e2704d Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Fri, 24 Mar 2023 00:04:08 +0100 Subject: [PATCH 31/40] Add new PLA form change items --- include/constants/items.h | 6 +++- src/data/item_icon_table.h | 3 ++ src/data/items.h | 36 +++++++++++++++++++ src/data/pokemon/form_change_table_pointers.h | 4 +++ src/data/pokemon/form_change_tables.h | 14 +++++++- src/data/text/item_descriptions.h | 15 ++++++++ 6 files changed, 76 insertions(+), 2 deletions(-) diff --git a/include/constants/items.h b/include/constants/items.h index d7d325376..2d186601b 100644 --- a/include/constants/items.h +++ b/include/constants/items.h @@ -965,7 +965,11 @@ #define ITEM_STEEL_TERA_SHARD 790 #define ITEM_WATER_TERA_SHARD 791 -#define ITEMS_COUNT 792 +#define ITEM_ADAMANT_CRYSTAL 792 +#define ITEM_GRISEOUS_CORE 793 +#define ITEM_LUSTROUS_GLOBE 794 + +#define ITEMS_COUNT 795 #define ITEM_FIELD_ARROW ITEMS_COUNT // A special item id associated with "Cancel"/"Exit" etc. in a list of items or decorations diff --git a/src/data/item_icon_table.h b/src/data/item_icon_table.h index 0dbec3f85..a0c27ea72 100644 --- a/src/data/item_icon_table.h +++ b/src/data/item_icon_table.h @@ -837,6 +837,9 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_ROCK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_RockTeraShard, gItemIconPalette_RockTeraShard}, [ITEM_STEEL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_SteelTeraShard, gItemIconPalette_SteelTeraShard}, [ITEM_WATER_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_WaterTeraShard, gItemIconPalette_WaterTeraShard}, + [ITEM_ADAMANT_CRYSTAL] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_AdamantCrystal, gItemIconPalette_AdamantCrystal}, + [ITEM_GRISEOUS_CORE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GriseousCore, gItemIconPalette_GriseousCore}, + [ITEM_LUSTROUS_GLOBE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LustrousGlobe, gItemIconPalette_LustrousGlobe}, // Return to field arrow [ITEMS_COUNT] = {gItemIcon_ReturnToFieldArrow, gItemIconPalette_ReturnToFieldArrow}, }; diff --git a/src/data/items.h b/src/data/items.h index 982375f3e..82e41901c 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -10247,4 +10247,40 @@ const struct Item gItems[] = .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, }, + + [ITEM_ADAMANT_CRYSTAL] = + { + .name = _("AdamantCrystl"), + .itemId = ITEM_ADAMANT_CRYSTAL, + .price = 0, + .description = sAdamantCrystalDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .flingPower = 60, + }, + + [ITEM_GRISEOUS_CORE] = + { + .name = _("Griseous Core"), + .itemId = ITEM_GRISEOUS_CORE, + .price = 0, + .description = sGriseousCoreDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .flingPower = 60, + }, + + [ITEM_LUSTROUS_GLOBE] = + { + .name = _("LustrousGlobe"), + .itemId = ITEM_LUSTROUS_GLOBE, + .price = 0, + .description = sLustrousGlobeDesc, + .pocket = POCKET_ITEMS, + .type = ITEM_USE_BAG_MENU, + .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .flingPower = 60, + }, }; diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h index 90be7112b..62afeb2e0 100644 --- a/src/data/pokemon/form_change_table_pointers.h +++ b/src/data/pokemon/form_change_table_pointers.h @@ -1,6 +1,10 @@ const struct FormChange *const gFormChangeTablePointers[NUM_SPECIES] = { #if P_GEN_4_POKEMON == TRUE + [SPECIES_DIALGA] = sDialgaFormChangeTable, + [SPECIES_DIALGA_ORIGIN] = sDialgaFormChangeTable, + [SPECIES_PALKIA] = sPalkiaFormChangeTable, + [SPECIES_PALKIA_ORIGIN] = sPalkiaFormChangeTable, [SPECIES_GIRATINA] = sGiratinaFormChangeTable, [SPECIES_GIRATINA_ORIGIN] = sGiratinaFormChangeTable, [SPECIES_SHAYMIN] = sShayminFormChangeTable, diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 94da0add3..723bda4b2 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -44,9 +44,21 @@ FORM_BATTLE_END: #define NIGHT 2 #if P_GEN_4_POKEMON == TRUE +static const struct FormChange sDialgaFormChangeTable[] = { + {FORM_ITEM_HOLD, SPECIES_DIALGA, ITEM_NONE}, + {FORM_ITEM_HOLD, SPECIES_DIALGA_ORIGIN, ITEM_ADAMANT_CRYSTAL}, + {FORM_CHANGE_END}, +}; + +static const struct FormChange sPalkiaFormChangeTable[] = { + {FORM_ITEM_HOLD, SPECIES_PALKIA, ITEM_NONE}, + {FORM_ITEM_HOLD, SPECIES_PALKIA_ORIGIN, ITEM_LUSTROUS_GLOBE}, + {FORM_CHANGE_END}, +}; + static const struct FormChange sGiratinaFormChangeTable[] = { {FORM_ITEM_HOLD, SPECIES_GIRATINA, ITEM_NONE}, - {FORM_ITEM_HOLD, SPECIES_GIRATINA_ORIGIN, ITEM_GRISEOUS_ORB}, + {FORM_ITEM_HOLD, SPECIES_GIRATINA_ORIGIN, ITEM_GRISEOUS_CORE}, {FORM_CHANGE_END}, }; diff --git a/src/data/text/item_descriptions.h b/src/data/text/item_descriptions.h index cee413aa5..da15f001c 100644 --- a/src/data/text/item_descriptions.h +++ b/src/data/text/item_descriptions.h @@ -3877,3 +3877,18 @@ static const u8 sTeraShardDesc[] = _( "These shards may\n" "form when a Tera\n" "Pokémon faints."); + +static const u8 sAdamantCrystalDesc[] = _( + "A large, glowing gem\n" + "that lets Dialga\n" + "change form."); + +static const u8 sGriseousCoreDesc[] = _( + "A large, glowing gem\n" + "that lets Giratina\n" + "change form."); + +static const u8 sLustrousGlobeDesc[] = _( + "A large, glowing gem\n" + "that lets Palkia\n" + "change form."); From cd6d71cd01118ecab1405ddf28ce8dc4b00a51fb Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Fri, 24 Mar 2023 00:50:06 +0100 Subject: [PATCH 32/40] Add most of the new item icons (Clear Amulet & Shards missing) --- .../items/icon_palettes/ability_shield.pal | 19 ++++++ .../items/icon_palettes/adamant_crystal.pal | 19 ++++++ .../items/icon_palettes/auspicious_armor.pal | 19 ++++++ .../items/icon_palettes/big_bamboo_shoot.pal | 19 ++++++ .../items/icon_palettes/booster_energy.pal | 19 ++++++ graphics/items/icon_palettes/covert_cloak.pal | 19 ++++++ .../items/icon_palettes/gimmighoul_coin.pal | 19 ++++++ .../items/icon_palettes/griseous_core.pal | 19 ++++++ .../items/icon_palettes/leaders_crest.pal | 19 ++++++ graphics/items/icon_palettes/loaded_dice.pal | 19 ++++++ .../items/icon_palettes/lustrous_globe.pal | 19 ++++++ .../items/icon_palettes/malicious_armor.pal | 19 ++++++ graphics/items/icon_palettes/mirror_herb.pal | 19 ++++++ .../items/icon_palettes/punching_glove.pal | 19 ++++++ .../icon_palettes/scroll_of_darkness.pal | 19 ++++++ .../items/icon_palettes/scroll_of_waters.pal | 19 ++++++ graphics/items/icon_palettes/tera_orb.pal | 19 ++++++ .../items/icon_palettes/tiny_bamboo_shoot.pal | 19 ++++++ graphics/items/icons/ability_shield.png | Bin 0 -> 692 bytes graphics/items/icons/adamant_crystal.png | Bin 0 -> 735 bytes graphics/items/icons/auspicious_armor.png | Bin 0 -> 710 bytes graphics/items/icons/big_bamboo_shoot.png | Bin 0 -> 300 bytes graphics/items/icons/booster_energy.png | Bin 0 -> 309 bytes graphics/items/icons/covert_cloak.png | Bin 0 -> 308 bytes graphics/items/icons/gimmighoul_coin.png | Bin 0 -> 270 bytes graphics/items/icons/griseous_core.png | Bin 0 -> 724 bytes graphics/items/icons/leaders_crest.png | Bin 0 -> 276 bytes graphics/items/icons/loaded_dice.png | Bin 0 -> 307 bytes graphics/items/icons/lustrous_globe.png | Bin 0 -> 750 bytes graphics/items/icons/malicious_armor.png | Bin 0 -> 690 bytes graphics/items/icons/mirror_herb.png | Bin 0 -> 331 bytes graphics/items/icons/punching_glove.png | Bin 0 -> 682 bytes graphics/items/icons/scroll_of_darkness.png | Bin 0 -> 264 bytes graphics/items/icons/scroll_of_waters.png | Bin 0 -> 264 bytes graphics/items/icons/tera_orb.png | Bin 0 -> 327 bytes graphics/items/icons/tiny_bamboo_shoot.png | Bin 0 -> 249 bytes include/graphics.h | 39 ++++++++++++ src/data/graphics/items.h | 60 +++++++++++++++--- src/data/item_icon_table.h | 36 +++++------ 39 files changed, 451 insertions(+), 26 deletions(-) create mode 100644 graphics/items/icon_palettes/ability_shield.pal create mode 100644 graphics/items/icon_palettes/adamant_crystal.pal create mode 100644 graphics/items/icon_palettes/auspicious_armor.pal create mode 100644 graphics/items/icon_palettes/big_bamboo_shoot.pal create mode 100644 graphics/items/icon_palettes/booster_energy.pal create mode 100644 graphics/items/icon_palettes/covert_cloak.pal create mode 100644 graphics/items/icon_palettes/gimmighoul_coin.pal create mode 100644 graphics/items/icon_palettes/griseous_core.pal create mode 100644 graphics/items/icon_palettes/leaders_crest.pal create mode 100644 graphics/items/icon_palettes/loaded_dice.pal create mode 100644 graphics/items/icon_palettes/lustrous_globe.pal create mode 100644 graphics/items/icon_palettes/malicious_armor.pal create mode 100644 graphics/items/icon_palettes/mirror_herb.pal create mode 100644 graphics/items/icon_palettes/punching_glove.pal create mode 100644 graphics/items/icon_palettes/scroll_of_darkness.pal create mode 100644 graphics/items/icon_palettes/scroll_of_waters.pal create mode 100644 graphics/items/icon_palettes/tera_orb.pal create mode 100644 graphics/items/icon_palettes/tiny_bamboo_shoot.pal create mode 100644 graphics/items/icons/ability_shield.png create mode 100644 graphics/items/icons/adamant_crystal.png create mode 100644 graphics/items/icons/auspicious_armor.png create mode 100644 graphics/items/icons/big_bamboo_shoot.png create mode 100644 graphics/items/icons/booster_energy.png create mode 100644 graphics/items/icons/covert_cloak.png create mode 100644 graphics/items/icons/gimmighoul_coin.png create mode 100644 graphics/items/icons/griseous_core.png create mode 100644 graphics/items/icons/leaders_crest.png create mode 100644 graphics/items/icons/loaded_dice.png create mode 100644 graphics/items/icons/lustrous_globe.png create mode 100644 graphics/items/icons/malicious_armor.png create mode 100644 graphics/items/icons/mirror_herb.png create mode 100644 graphics/items/icons/punching_glove.png create mode 100644 graphics/items/icons/scroll_of_darkness.png create mode 100644 graphics/items/icons/scroll_of_waters.png create mode 100644 graphics/items/icons/tera_orb.png create mode 100644 graphics/items/icons/tiny_bamboo_shoot.png diff --git a/graphics/items/icon_palettes/ability_shield.pal b/graphics/items/icon_palettes/ability_shield.pal new file mode 100644 index 000000000..d11016b32 --- /dev/null +++ b/graphics/items/icon_palettes/ability_shield.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +248 200 240 +224 176 232 +200 144 224 +240 224 248 +176 120 216 +77 146 186 +105 179 221 +238 246 246 +222 222 222 +197 197 197 +161 161 161 +48 48 48 +255 255 255 +0 0 0 diff --git a/graphics/items/icon_palettes/adamant_crystal.pal b/graphics/items/icon_palettes/adamant_crystal.pal new file mode 100644 index 000000000..738ff0498 --- /dev/null +++ b/graphics/items/icon_palettes/adamant_crystal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +184 208 248 +168 200 248 +81 123 173 +128 168 216 +200 232 248 +224 248 248 +248 248 248 +104 104 104 +48 48 48 +255 255 255 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/auspicious_armor.pal b/graphics/items/icon_palettes/auspicious_armor.pal new file mode 100644 index 000000000..9d0c8e444 --- /dev/null +++ b/graphics/items/icon_palettes/auspicious_armor.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +160 144 32 +192 176 56 +224 208 88 +128 112 32 +72 56 24 +224 80 80 +176 88 88 +120 72 72 +48 48 48 +255 255 255 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/big_bamboo_shoot.pal b/graphics/items/icon_palettes/big_bamboo_shoot.pal new file mode 100644 index 000000000..28ab4fe69 --- /dev/null +++ b/graphics/items/icon_palettes/big_bamboo_shoot.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +189 202 71 +121 168 43 +120 167 42 +163 126 74 +116 101 78 +79 66 46 +115 77 43 +180 133 94 +233 176 96 +241 241 193 +183 170 147 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/booster_energy.pal b/graphics/items/icon_palettes/booster_energy.pal new file mode 100644 index 000000000..21dc17ba2 --- /dev/null +++ b/graphics/items/icon_palettes/booster_energy.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +111 45 22 +0 0 0 +190 91 13 +255 139 1 +255 255 255 +255 231 10 +251 42 6 +154 157 151 +193 161 19 +234 238 234 +122 111 115 +74 67 68 +90 185 248 +78 86 255 +111 52 255 diff --git a/graphics/items/icon_palettes/covert_cloak.pal b/graphics/items/icon_palettes/covert_cloak.pal new file mode 100644 index 000000000..06491d2ad --- /dev/null +++ b/graphics/items/icon_palettes/covert_cloak.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +54 52 72 +101 120 143 +103 139 163 +84 105 130 +74 75 101 +90 108 136 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/gimmighoul_coin.pal b/graphics/items/icon_palettes/gimmighoul_coin.pal new file mode 100644 index 000000000..555a04dbe --- /dev/null +++ b/graphics/items/icon_palettes/gimmighoul_coin.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +180 124 35 +115 66 13 +255 249 234 +224 159 27 +255 226 121 +255 200 59 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/griseous_core.pal b/graphics/items/icon_palettes/griseous_core.pal new file mode 100644 index 000000000..05980782c --- /dev/null +++ b/graphics/items/icon_palettes/griseous_core.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +206 181 41 +222 198 57 +247 231 140 +156 132 33 +189 156 41 +123 99 33 +49 49 49 +255 255 255 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/leaders_crest.pal b/graphics/items/icon_palettes/leaders_crest.pal new file mode 100644 index 000000000..1f60ee220 --- /dev/null +++ b/graphics/items/icon_palettes/leaders_crest.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +188 143 70 +131 85 43 +186 181 176 +80 74 71 +239 185 78 +138 128 128 +220 217 215 +250 225 159 +246 205 93 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/loaded_dice.pal b/graphics/items/icon_palettes/loaded_dice.pal new file mode 100644 index 000000000..16624b9e9 --- /dev/null +++ b/graphics/items/icon_palettes/loaded_dice.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +48 52 78 +127 216 17 +0 0 0 +192 240 140 +128 217 18 +90 137 46 +129 218 19 +88 135 48 +107 182 17 +128 217 18 +93 144 44 +89 136 47 +255 255 255 +127 216 19 +48 38 64 diff --git a/graphics/items/icon_palettes/lustrous_globe.pal b/graphics/items/icon_palettes/lustrous_globe.pal new file mode 100644 index 000000000..b62730a16 --- /dev/null +++ b/graphics/items/icon_palettes/lustrous_globe.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +248 216 232 +198 159 217 +210 171 229 +224 192 240 +210 192 240 +160 192 232 +144 200 232 +160 216 240 +128 216 224 +208 240 240 +248 240 240 +248 248 248 +80 80 80 +48 48 48 diff --git a/graphics/items/icon_palettes/malicious_armor.pal b/graphics/items/icon_palettes/malicious_armor.pal new file mode 100644 index 000000000..1ef3efe89 --- /dev/null +++ b/graphics/items/icon_palettes/malicious_armor.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +66 66 99 +90 90 132 +115 115 156 +90 115 230 +132 140 181 +125 147 246 +48 48 48 +255 255 255 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/mirror_herb.pal b/graphics/items/icon_palettes/mirror_herb.pal new file mode 100644 index 000000000..b4ad3f972 --- /dev/null +++ b/graphics/items/icon_palettes/mirror_herb.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +39 194 168 +124 228 211 +19 101 84 +187 228 77 +238 250 169 +234 244 124 +24 152 108 +126 106 28 +173 171 15 +43 138 128 +127 175 55 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/punching_glove.pal b/graphics/items/icon_palettes/punching_glove.pal new file mode 100644 index 000000000..f21cb39ec --- /dev/null +++ b/graphics/items/icon_palettes/punching_glove.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +255 212 0 +200 56 32 +248 64 48 +232 232 232 +48 48 48 +255 255 255 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/scroll_of_darkness.pal b/graphics/items/icon_palettes/scroll_of_darkness.pal new file mode 100644 index 000000000..1b66d85e0 --- /dev/null +++ b/graphics/items/icon_palettes/scroll_of_darkness.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +130 116 116 +86 86 86 +54 41 49 +104 78 86 +56 56 56 +141 105 41 +223 191 65 +242 241 242 +184 172 179 +186 140 57 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/scroll_of_waters.pal b/graphics/items/icon_palettes/scroll_of_waters.pal new file mode 100644 index 000000000..41a94976f --- /dev/null +++ b/graphics/items/icon_palettes/scroll_of_waters.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +118 109 109 +143 130 127 +101 77 83 +73 68 68 +122 108 104 +141 105 41 +223 191 65 +242 241 242 +184 172 179 +186 140 57 +102 78 84 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/tera_orb.pal b/graphics/items/icon_palettes/tera_orb.pal new file mode 100644 index 000000000..78b3689f9 --- /dev/null +++ b/graphics/items/icon_palettes/tera_orb.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +81 41 75 +98 61 92 +66 72 65 +51 4 43 +201 185 199 +110 86 106 +84 73 92 +32 41 34 +255 255 255 +71 50 92 +64 71 94 +139 165 201 +183 191 209 +0 0 0 diff --git a/graphics/items/icon_palettes/tiny_bamboo_shoot.pal b/graphics/items/icon_palettes/tiny_bamboo_shoot.pal new file mode 100644 index 000000000..1172befa6 --- /dev/null +++ b/graphics/items/icon_palettes/tiny_bamboo_shoot.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +111 167 43 +179 192 68 +107 144 66 +84 116 48 +68 93 37 +124 137 52 +168 87 80 +168 116 111 +241 241 193 +168 87 80 +183 170 147 +121 52 57 +0 0 0 +0 0 0 diff --git a/graphics/items/icons/ability_shield.png b/graphics/items/icons/ability_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..de7354d71f8ab38c4042d94cfad8b58b856dc5f2 GIT binary patch literal 692 zcmV;l0!#ggP)EX>4Tx04R}tkv&MmKpe$i(@I4u4t5ZA$j~}j5EXHhDi*;)X)CnqU~=gfG-*gu zTpR`0f`cE6RR0!X5l%$?&0I>U5saWpZjz4D+QAQK9P8i>4rtTK|Hf* z>74h8!>lYR#OK8023?T&k?XR{Z=6dG3p_JyWYhD+VPdh=#c~(3vY`@B6Gs$PqkJLf zvch?bvs$gQ_C5IvLj`Rm!*!aYNMH#`q#!~@9TikzAx5i4iitGs$36VRjz2{%nOqex zax9<*6_Voz|AXJ%n#HL}Hz^bcdR}b%BMJm}fmXw|zmILZbprUGfh(=!uQh?$PtqG5 zEph}5Yy%h99ZlW?E_Z;TCtWfmNAlAYibdf4jJ_!k4Bi5LYi@7teVjf3S?X%}1~@nb z#!8gE?(y#4?%w`A)9&vFlVWnTjs1QK0000mP)t-s00030|Nr>N@ZhlM$dKUh;P|k3 z*iDkUX|vt#_V(W1-o?emp`oELFfjlB{{R30f`gsi0000GbW%=J0RR90|NsC0|NsC0 z|NsC0z)*`r0001!Nkl8f8U5zc zlZ8E!NQqx7-RP-z+M?uzA6lc2s23cMnUkR#m}&(K$w2i8$sh(tGeMH`(+%uUYZLaM z=*X~NcXYPi1UZ4-ng9m-D<*)S0+imz83rTxKqU1(!3az4Z4x5aKE!kDT|D=G{oIHC a_q+k9MhXH6sl^!p0000fQS8WW2&PqdmRCNqX=2;|!TkpJ`Unud?RrDvnWg}+IW^2rVL;u+U;U;n%pM-e+KMKTt7o)AiVUcQ4<(y+1wb?_Y*1CPn8WH}?qvW5^)DC&U#< z|NsAg$Aup&PW%Y0UfZzZ#)%g{9{d1-jEoEe0|THePz@ZUCdg+2IRYg?e!(D}$lyRg zw=+;~v!{z=h{nXN6L0e!R^V~g?oj;rzy5>Fi$i8_6ZN~Vn0+eLoAUZsw1LtI{a*#& z0~f^1kw37vIpxQk4;SY(6ss8)GG73yk)hf%1JM;eJ}Ybc+V+Rw`b-1mnIFD7dk5G*jj5|=hnD-^J$(E+wkN%E{_o;1+N_fq?Iu?Ay=-&c<7l%v^W~iH zorl-xXdZcf=+_n zW&LEcuwXgnHD&L+kMHiiyZ6ub^1Ju!laq?J^!}{_MU+8+Plzj!{{R2~f(Z%-Hds8k z5YbSe;9(*0ARu5vL_~#$hk=0sP!^~L4zd;t_5(QrB|(0{Af3qIKtQ)MP;RNGi(`n! z#HE23g$^rly!I5B_5c3^*0P zU#>Q+uaZTQD!GDu72M<7jVC@c;t_oKROLg*lHI#LhAr@2A9N)&Zr!Sf0av6`6`xG0 zDbn1>mO6LVRAasD2MXWje*d~b+-QGTSXY{0S#re7z?mD3a+>0J<5hFKk{>ng6EZ!% jvFq#c3Z1tF$nMpaRt&q zu=kXE92{GIfQeYLl=#=YE-@bTlp?W-n3lmW?8&QGrbDS?t8zhH*{ z|Ns93Vi<`2dRzx6*XQZt7@{$;b<%CVW&}?|IAO7FBX7?4=UHQz*=Ese_mu|%~ zYH!fx*cR@pu-7}`MYTSMj%Vn?jjO}fI5ux&$czp=Y3gC4^~tnfOSti~5{G;~+rlje zm0jAL)HLURu|7EUO8dhnEJnT`H!hs>%)_eb*bgte<8S}f%;zgQB3PGY)7NJlYFo4K d;j8)`ubFlCu)NHY>l6oh!PC{xWt~$(699KKc>w?b literal 0 HcmV?d00001 diff --git a/graphics/items/icons/booster_energy.png b/graphics/items/icons/booster_energy.png new file mode 100644 index 0000000000000000000000000000000000000000..df583a2508d4e908ff8c32664f65480c4da30528 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAF%}28J29*~C-V}>F$nMpaRt)( zx?(_bUo`LkZpQyW@SN+n7Tc`3(+@5be)aBERerITvrE*@AAVu~^G*IU*UvKmsum~- z@(X4FsYU_^0=k`na??Fs978lF_D=E?J8Zz=tlM^r(`4WO+~(aZVyT*Y`Q0jd{&Owx zOWWY_c*6>Y7@3-o=TC0+M7S<9P=49{;>?aB{av}b?4Mmd_lHY_wf)Y0x9yDA;`Of{ zpRP%HAaRS~+ncrb7@k$<>pR$-IQM}kV9kfmMh7~!@=sF$nMpaRt&q zU}oZxTG5~0y*MPZ$;&%6DyIV?j!C{d_dE_LDo_&S7tHV<6&wiYb_U8#^K@|x(U_P! zDUgpvk;8eZ^T&VY_ngXv*m8U1Yc;htg-!N+tz>hl^}t>EV~%HHyN^g3+)$ll=aQzv zEjmL`+uoCB!~X<+g-^dE7=FLfS3Z*>U)KNl~s zV^~wB!f?CRc!6*PlXQVft)oeTsC7e|0<+`awYT4_`#sImo^7ey^0VjpSBo>x>7OsG P3G$PttDnm{r-UW|j&*9# literal 0 HcmV?d00001 diff --git a/graphics/items/icons/gimmighoul_coin.png b/graphics/items/icons/gimmighoul_coin.png new file mode 100644 index 0000000000000000000000000000000000000000..b161cf12ac37b2e9eaa9beeb36382e8e3e97d9e4 GIT binary patch literal 270 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAF%}28J29*~C-V}>F$nMpaRt&_ zYLttec>n)=^A)n z2SpDka5ziz6#nm(UQ_#u!FHt6`zyLClZz{=(PSIF}1I-uJVTbg@^6O{5H=%z@p<_8|%G)m**9O`N2sqTYv4@ dE&pkIDRYD()2!=@=KTY?+0)g}Wt~$(695QOXPN*2 literal 0 HcmV?d00001 diff --git a/graphics/items/icons/griseous_core.png b/graphics/items/icons/griseous_core.png new file mode 100644 index 0000000000000000000000000000000000000000..08748fe7bba170faebb029c29aa9fc4b8046143e GIT binary patch literal 724 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbQ|Pfw4c+**PE}BfliSI3vG6!PD7U zp`a)~Ei)&Tfk9(p?L=FT!wxb>fQS8WW2&PqdmRCNqX=2;|!TkpJ`Unud?RrDvnWg}+IW^2rVL;u+U;U;n%pM-e+KMKTt7o)AiVUcQ4<(y+1wb?_Y*1CPn8WH}?qvW5^)DC&U#< z|NsC0+*Zwd$1J};@0ru0xOa|bb+V$Np&?KlD2oQl0xWg|g#=21{DMI`k->q0ZfBs} z3Qrfu5RHjTCpropHsD|mZf^Pa|B+I9)NbD@En~Zg*B%aikJqn?uX+`i{;99y;{)-P zlS0@Q#GJ0#Q0m7pWtz9!#EB*=c^6zwNP8{4LW;r7rTOz@@e>A$?#)d198OpSZV$Nd zX_m^J8DH)67tIiSDwG(z{9fn2Z|C*OA}u tXl*c?-tNWQ?yd{<+VQvDYTf$vUs;$V*>)OpRnGx=(bLt>Wt~$(6995%J!Sv^ literal 0 HcmV?d00001 diff --git a/graphics/items/icons/leaders_crest.png b/graphics/items/icons/leaders_crest.png new file mode 100644 index 0000000000000000000000000000000000000000..b105fcd29d95d1ff1f2c240fea023c8001e9db50 GIT binary patch literal 276 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAF%}28J29*~C-V}>F$nMpaRt&q zu&3XxIaGVs)(rt(?(cW{bu~2Hxq1E9!};IN#zGXp$se5`4+1#?B|(0{4F6HVfq-sj zpj?@!i(`n!#I1prOa~M=9tU0dZ@**V;=~+RSs{-AmwyqfqS(Lt&R9MBsPBrI;YW6K zTFOqW6mCsA9;9X9V&WCLA$j8@#`7sluP@$sFD^gf_1;82MLmn-O6*%p8q~{Mrm20B uJZ_#kU0->^y$42O2~y2_lwUnqe~sDVE7Pe4Cr literal 0 HcmV?d00001 diff --git a/graphics/items/icons/loaded_dice.png b/graphics/items/icons/loaded_dice.png new file mode 100644 index 0000000000000000000000000000000000000000..1295450928df350a450cc87ab127f5248959175e GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAF%}28J29*~C-V}>F$nMpaRt#P ze)Ts5fs6wmdKzvDMRn>m-V%;zH^|;52;|02(24BO|NsAg{S9FQH3!|-_8=PsN`m}? z8Gxey|A7bywKK{18BlJjr;B5V#>Bl74)PsQ;Bax32zKiE|9^Y;me4zo<-To=OZ#+S z+m!!~-Rt-0^|i4&a4&4!tT53$#>In6`yJZ?ZqJVi=Zazug~eRh&KcLr^O;jwJ11q| zrfvrn?KLh@o+ZWFZ(U6k1qO+6Kl3EAAL;D1FQMlpyWq9QZ2M0zm;*YR}^4`1&hJj?yupCc&e4F-4w z;yI@2Ch-Pwbko#0?-PeuNtB7ti6?ZrAn_yDC5PWQ7j5?P%#fZ*%@c=+g<=Ow9n4C) zLOeqpmKBxqy;+B4&Rd+7N{zMd$zK@At1D@)(;Pt@OGqFI0TOB`qXH8VT2)dEq-a0p z;vcsCNpeZ#Dua<@0aYjvZ9n)Q{O;B$OiwvUff&&JVp$(!fPWWg)-CJ%*s_`@fbSW& zQrrG&1DO3Jz24TsM?n8JaB8-Vo z(+40!T`k`L2Zz9Tk+Roa-rdvL+rMX8{rv#cG;+>avyKt~001yhOjJbx00960|M=MG z#-G{JtL5Op@Y2BWpup&m$mpQh@POFh(D3m1@bLKf_)t(#FfcGLRPaau000nlQchC< z|NsC0|NsC0|NsC0|NsBMP>VwV006>CL_t(2k%f{`4#OY}Lnj)uKq1`!wljpbw9_>I zRPw_KBz?X9$*fUkPrD&hDxlp^Lk%&?wn?aHi%&aI6SdXPlp~RY80hbH&q^hd0Gk1% z%*F#~Ch-UZ1V=*SCh&*_AS+vFPGnI43FejHhtr<)J;11Sfu4xyI>975{R?EoVNc&d zZ%)N{+CZ^+-J#?kHlHsXP3Wr*e`BB>BROk`obU4?`q?fPD(iR;a`XG5iX#bSU6il3 gP|wx={u#dY0duGe>o^(Lg8%>k07*qoM6N<$f=vBdUH||9 literal 0 HcmV?d00001 diff --git a/graphics/items/icons/malicious_armor.png b/graphics/items/icons/malicious_armor.png new file mode 100644 index 0000000000000000000000000000000000000000..931850013a6d012fc4855aba69ff7a0cffe928c0 GIT binary patch literal 690 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbQ|Pfw3ji**U<|*;%2WC_gPTCzXLg zV`A+@TaUvIGDqVtd%0@yl?jxdxL_42AX+#@OYERiruLRs2}Li(O)@=#Vq{paH#N(5 zhq8;O$I8aAI(}e2a`B|+MOUvs@j)4aj}I2w-@99VXD@S0+SHY42}}W|r#;jkCF$+g zVv83%#KF{&=hnD-^J$(E+wkN%E{_o;1+N_fq?Iu?Ay=-&c<7l%v^W~iH zorl-xXdZcf=+_n zW&LEcuwXgnHD&L+kMHiiyZ6ub^1Ju!laq?J^!}{_MU+8+Plzj!{{R2KlT&h3R7-L3 zoT%bwEj?RnCx0_AFaU}JWzm4S>z6k`A%T)0zhIC~WN;v$+ZiY~(bL5-L}TLG07s$2 z4ji`vw}1LCE`H`p&KuL1Ne&JrYo{|h_MdE5Wa2n}XrWlXOAu$d$$Pf_6_(MO&67_Q z-Q!C6;nJcS|3~0J{-?a?iF2++vOUfh(pvp7IXjb8p*? zJ)Mg>PRwaD5WTmJ*;(VpUZ;5OOFPaNpSoHoSNpf>e`qat{LiHmdK II;Vst0PmeHtpET3 literal 0 HcmV?d00001 diff --git a/graphics/items/icons/mirror_herb.png b/graphics/items/icons/mirror_herb.png new file mode 100644 index 0000000000000000000000000000000000000000..e792bad8294cd25b4411e62c7e9f5de1b7f1fc0f GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAF%}28J29*~C-V}>F$nMpaRt&q zpnhmY&6CT*sUf?c_`dtK^3|6bi5WR{Su$%^^J{lC)UP*(CH_Ji$Ht1!&MgubxT~`)h%xZ_GNrUbe{&XRG0n9Wl|ET^@PL?34o{K5-J*l?*|`y7 zXI^Bt%(<9n4!5C{g!;M6869Q zreJ;e64QQ-??#SW#3ojsaGhTqXm|BxCZpWFUk~~3T$L_QVl~d=hnD-^J$(E+wkN%E{_o;1+N_fq?Iu?Ay=-&c<7l%v^W~iH zorl-xXdZcf=+_n zW&LEcuwXgnHD&L+kMHiiyZ6ub^1Ju!laq?J^!}{_MU+8+Plzj!{tpCK7*1Fy{BSUM z@#2MnfdNneD2^4#Eh{(#ln^Kh@(Tv*1nGu?0|DL6K)Fs&7sn8diLDbG`Hm=XI8PV* z`QP78z)!tmXWGq4b}J0#Ki_|)bo0?8ZBx>c7d>JY`0-lh)zt$zRl?j&JR6rUS-Jd# z#OLHoyf!H*%;DYrTNd4Lkh|y5_xc;7irAd5zjM?$>&;J`HrvlsxApH0uP*u9iK=t! uwZt;aMO)r{PoFFxeqaAv*!&mlD}FI#|6?h?YGU&gF$nMpaRt&q z&{R?q78YivX_(;`W?^B`o2hw!zvHKmpLVR-ysO6&q6R{`f39i;vII(k{DK+&qk;ng z-OfO{EKe855RHjzCwlT7FyLT*><}^O|NrePtF+H8462oBK6>ev{2|80!R(79jx^4A zIh%R5^%kjpubErTTOzLg+O3?;x6P(z(w`F_Og;M)rW?;Q)BGMVD_P;QeR+b+f<1Sm k1Mjsr?N2DE`}5R2*6=dprNWffA0W4Ry85}Sb4q9e030c3F$nMpaRt&q zP?nq9-&CLK8|>-gQk9d@o2hw!zvHKmpLVR-ysO7D%`XI^3`ow5no$F!1WJPZf*Jm! zf&&5F&Oo^=PZ!4!jfra~dh#7G;9!325Hach|LrTQw9hRJs+DOzdg+$@A;!hQ?29Ch zG|qTAn|ZeN7O8!&nOn_UBCh?~t(?ub&8BA3pA#QUJ^K`<8_zS-{2nkXS>dyNd4kM> nJ$Iu6@3lAWPbjGS^VB`o@G|42!j#q@Am@3y`njxgN@xNAvm$E` literal 0 HcmV?d00001 diff --git a/graphics/items/icons/tera_orb.png b/graphics/items/icons/tera_orb.png new file mode 100644 index 0000000000000000000000000000000000000000..03ecb8e82c2d5267de84291cc4d4e79e2f12bf98 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAF%}28J29*~C-V}>F$nMpaRt&q z5UA;$WEeNIW(5J~yWRmhr~lXIZH`!LWw)&|VTQ0!-sLas zCoeFEE&h_n`{o>@xI5R=&q3}l_Uv1eJu}zS-Cr_7D(3I1cdIOzo<*AST#o8Ds>pj2 z?G*a@_=L2`1%B%i9FmKdsI!@}UD+pn@#N0RiBI;r-`>4wu>)`0)E^ICZaB6{uVeol zah4M^GkY(8zRG;Ebz#Tr(rdoe`+;3BNh5rlN)=R8>KIJaCKu0rpy85}S Ib4q9e05-0R6aWAK literal 0 HcmV?d00001 diff --git a/graphics/items/icons/tiny_bamboo_shoot.png b/graphics/items/icons/tiny_bamboo_shoot.png new file mode 100644 index 0000000000000000000000000000000000000000..6b24c7e383e46234a138381d7fa8b30383a2b5e3 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAF%}28J29*~C-V}>F$nMpaRt&q zkiT4e^8uIa2~Hs;1}?FxHJv6a!UI;61u3d46@@1yb}qQYP+aJ@X;% zBin-OOt;o#7slkf-O&xM-EmLgeAC}_$2*%9?55u=cvP&rUhl^CnK>P@&o?|}5xnwq b!ThI;%bqgsYv+=T2RX*m)z4*}Q$iB}thHwP literal 0 HcmV?d00001 diff --git a/include/graphics.h b/include/graphics.h index 8b1e0c3a2..7cf903034 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -8834,6 +8834,45 @@ extern const u32 gItemIcon_Gem[]; extern const u32 gItemIconPalette_Ruby[]; extern const u32 gItemIconPalette_Sapphire[]; +// GEN IX ITEMS +extern const u32 gItemIcon_AbilityShield[]; +extern const u32 gItemIconPalette_AbilityShield[]; +extern const u32 gItemIcon_AuspiciousArmor[]; +extern const u32 gItemIconPalette_AuspiciousArmor[]; +extern const u32 gItemIcon_BigBambooShoot[]; +extern const u32 gItemIconPalette_BigBambooShoot[]; +extern const u32 gItemIcon_BoosterEnergy[]; +extern const u32 gItemIconPalette_BoosterEnergy[]; +extern const u32 gItemIcon_CovertCloak[]; +extern const u32 gItemIconPalette_CovertCloak[]; +extern const u32 gItemIcon_GimmighoulCoin[]; +extern const u32 gItemIconPalette_GimmighoulCoin[]; +extern const u32 gItemIcon_LeadersCrest[]; +extern const u32 gItemIconPalette_LeadersCrest[]; +extern const u32 gItemIcon_LoadedDice[]; +extern const u32 gItemIconPalette_LoadedDice[]; +extern const u32 gItemIcon_MaliciousArmor[]; +extern const u32 gItemIconPalette_MaliciousArmor[]; +extern const u32 gItemIcon_MirrorHerb[]; +extern const u32 gItemIconPalette_MirrorHerb[]; +extern const u32 gItemIcon_PunchingGlove[]; +extern const u32 gItemIconPalette_PunchingGlove[]; +extern const u32 gItemIcon_ScrollOfDarkness[]; +extern const u32 gItemIconPalette_ScrollOfDarkness[]; +extern const u32 gItemIcon_ScrollOfWaters[]; +extern const u32 gItemIconPalette_ScrollOfWaters[]; +extern const u32 gItemIcon_TeraOrb[]; +extern const u32 gItemIconPalette_TeraOrb[]; +extern const u32 gItemIcon_TinyBambooShoot[]; +extern const u32 gItemIconPalette_TinyBambooShoot[]; + +extern const u32 gItemIcon_AdamantCrystal[]; +extern const u32 gItemIconPalette_AdamantCrystal[]; +extern const u32 gItemIcon_GriseousCore[]; +extern const u32 gItemIconPalette_GriseousCore[]; +extern const u32 gItemIcon_LustrousGlobe[]; +extern const u32 gItemIconPalette_LustrousGlobe[]; + extern const u32 gItemIcon_ReturnToFieldArrow[]; extern const u32 gItemIconPalette_ReturnToFieldArrow[]; diff --git a/src/data/graphics/items.h b/src/data/graphics/items.h index 35ce13db2..ce4ecf608 100644 --- a/src/data/graphics/items.h +++ b/src/data/graphics/items.h @@ -1931,17 +1931,61 @@ const u32 gItemIconPalette_Ruby[] = INCBIN_U32("graphics/items/icon_palettes/rub const u32 gItemIconPalette_Sapphire[] = INCBIN_U32("graphics/items/icon_palettes/sapphire.gbapal.lz"); -//const u32 gItemIcon_AbilityShield[] = INCBIN_U32("graphics/items/icons/ability_shield.4bpp.lz"); -//const u32 gItemIconPalette_AbilityShield[] = INCBIN_U32("graphics/items/icon_palettes/ability_shield.gbapal.lz"); +const u32 gItemIcon_AbilityShield[] = INCBIN_U32("graphics/items/icons/ability_shield.4bpp.lz"); +const u32 gItemIconPalette_AbilityShield[] = INCBIN_U32("graphics/items/icon_palettes/ability_shield.gbapal.lz"); //const u32 gItemIcon_ClearAmulet[] = INCBIN_U32("graphics/items/icons/clear_amulet.4bpp.lz"); //const u32 gItemIconPalette_ClearAmulet[] = INCBIN_U32("graphics/items/icon_palettes/clear_amulet.gbapal.lz"); -//const u32 gItemIcon_PunchingGlove[] = INCBIN_U32("graphics/items/icons/punching_glove.4bpp.lz"); -//const u32 gItemIconPalette_PunchingGlove[] = INCBIN_U32("graphics/items/icon_palettes/punching_glove.gbapal.lz"); +const u32 gItemIcon_PunchingGlove[] = INCBIN_U32("graphics/items/icons/punching_glove.4bpp.lz"); +const u32 gItemIconPalette_PunchingGlove[] = INCBIN_U32("graphics/items/icon_palettes/punching_glove.gbapal.lz"); -//const u32 gItemIcon_CovertCloak[] = INCBIN_U32("graphics/items/icons/covert_cloak.4bpp.lz"); -//const u32 gItemIconPalette_CovertCloak[] = INCBIN_U32("graphics/items/icon_palettes/covert_cloak.gbapal.lz"); +const u32 gItemIcon_CovertCloak[] = INCBIN_U32("graphics/items/icons/covert_cloak.4bpp.lz"); +const u32 gItemIconPalette_CovertCloak[] = INCBIN_U32("graphics/items/icon_palettes/covert_cloak.gbapal.lz"); -//const u32 gItemIcon_LoadedDice[] = INCBIN_U32("graphics/items/icons/loaded_dice.4bpp.lz"); -//const u32 gItemIconPalette_LoadedDice[] = INCBIN_U32("graphics/items/icon_palettes/loaded_dice.gbapal.lz"); +const u32 gItemIcon_LoadedDice[] = INCBIN_U32("graphics/items/icons/loaded_dice.4bpp.lz"); +const u32 gItemIconPalette_LoadedDice[] = INCBIN_U32("graphics/items/icon_palettes/loaded_dice.gbapal.lz"); + +const u32 gItemIcon_AuspiciousArmor[] = INCBIN_U32("graphics/items/icons/auspicious_armor.4bpp.lz"); +const u32 gItemIconPalette_AuspiciousArmor[] = INCBIN_U32("graphics/items/icon_palettes/auspicious_armor.gbapal.lz"); + +const u32 gItemIcon_BigBambooShoot[] = INCBIN_U32("graphics/items/icons/big_bamboo_shoot.4bpp.lz"); +const u32 gItemIconPalette_BigBambooShoot[] = INCBIN_U32("graphics/items/icon_palettes/big_bamboo_shoot.gbapal.lz"); + +const u32 gItemIcon_BoosterEnergy[] = INCBIN_U32("graphics/items/icons/booster_energy.4bpp.lz"); +const u32 gItemIconPalette_BoosterEnergy[] = INCBIN_U32("graphics/items/icon_palettes/booster_energy.gbapal.lz"); + +const u32 gItemIcon_GimmighoulCoin[] = INCBIN_U32("graphics/items/icons/gimmighoul_coin.4bpp.lz"); +const u32 gItemIconPalette_GimmighoulCoin[] = INCBIN_U32("graphics/items/icon_palettes/gimmighoul_coin.gbapal.lz"); + +const u32 gItemIcon_LeadersCrest[] = INCBIN_U32("graphics/items/icons/leaders_crest.4bpp.lz"); +const u32 gItemIconPalette_LeadersCrest[] = INCBIN_U32("graphics/items/icon_palettes/leaders_crest.gbapal.lz"); + +const u32 gItemIcon_MaliciousArmor[] = INCBIN_U32("graphics/items/icons/malicious_armor.4bpp.lz"); +const u32 gItemIconPalette_MaliciousArmor[] = INCBIN_U32("graphics/items/icon_palettes/malicious_armor.gbapal.lz"); + +const u32 gItemIcon_MirrorHerb[] = INCBIN_U32("graphics/items/icons/mirror_herb.4bpp.lz"); +const u32 gItemIconPalette_MirrorHerb[] = INCBIN_U32("graphics/items/icon_palettes/mirror_herb.gbapal.lz"); + +const u32 gItemIcon_ScrollOfDarkness[] = INCBIN_U32("graphics/items/icons/scroll_of_darkness.4bpp.lz"); +const u32 gItemIconPalette_ScrollOfDarkness[] = INCBIN_U32("graphics/items/icon_palettes/scroll_of_darkness.gbapal.lz"); + +const u32 gItemIcon_ScrollOfWaters[] = INCBIN_U32("graphics/items/icons/scroll_of_waters.4bpp.lz"); +const u32 gItemIconPalette_ScrollOfWaters[] = INCBIN_U32("graphics/items/icon_palettes/scroll_of_waters.gbapal.lz"); + +const u32 gItemIcon_TeraOrb[] = INCBIN_U32("graphics/items/icons/tera_orb.4bpp.lz"); +const u32 gItemIconPalette_TeraOrb[] = INCBIN_U32("graphics/items/icon_palettes/tera_orb.gbapal.lz"); + +const u32 gItemIcon_TinyBambooShoot[] = INCBIN_U32("graphics/items/icons/tiny_bamboo_shoot.4bpp.lz"); +const u32 gItemIconPalette_TinyBambooShoot[] = INCBIN_U32("graphics/items/icon_palettes/tiny_bamboo_shoot.gbapal.lz"); + +// Tera Shards here + +const u32 gItemIcon_AdamantCrystal[] = INCBIN_U32("graphics/items/icons/adamant_crystal.4bpp.lz"); +const u32 gItemIconPalette_AdamantCrystal[] = INCBIN_U32("graphics/items/icon_palettes/adamant_crystal.gbapal.lz"); + +const u32 gItemIcon_GriseousCore[] = INCBIN_U32("graphics/items/icons/griseous_core.4bpp.lz"); +const u32 gItemIconPalette_GriseousCore[] = INCBIN_U32("graphics/items/icon_palettes/griseous_core.gbapal.lz"); + +const u32 gItemIcon_LustrousGlobe[] = INCBIN_U32("graphics/items/icons/lustrous_globe.4bpp.lz"); +const u32 gItemIconPalette_LustrousGlobe[] = INCBIN_U32("graphics/items/icon_palettes/lustrous_globe.gbapal.lz"); diff --git a/src/data/item_icon_table.h b/src/data/item_icon_table.h index a0c27ea72..9e4b56777 100644 --- a/src/data/item_icon_table.h +++ b/src/data/item_icon_table.h @@ -803,22 +803,22 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_TEA] = {gItemIcon_Tea, gItemIconPalette_Tea}, [ITEM_RUBY] = {gItemIcon_Gem, gItemIconPalette_Ruby}, [ITEM_SAPPHIRE] = {gItemIcon_Gem, gItemIconPalette_Sapphire}, - [ITEM_ABILITY_SHIELD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_AbilityShield, gItemIconPalette_AbilityShield}, + [ITEM_ABILITY_SHIELD] = {gItemIcon_AbilityShield, gItemIconPalette_AbilityShield}, [ITEM_CLEAR_AMULET] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ClearAmulet, gItemIconPalette_ClearAmulet}, - [ITEM_PUNCHING_GLOVE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PunchingGlove, gItemIconPalette_PunchingGlove}, - [ITEM_COVERT_CLOAK] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_CovertCloak, gItemIconPalette_CovertCloak}, - [ITEM_LOADED_DICE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LoadedDice, gItemIconPalette_LoadedDice}, - [ITEM_AUSPICIOUS_ARMOR] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_AuspiciousArmor, gItemIconPalette_AuspiciousArmor}, - [ITEM_BOOSTER_ENERGY] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BoosterEnergy, gItemIconPalette_BoosterEnergy}, - [ITEM_BIG_BAMBOO_SHOOT] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BigBambooShoot, gItemIconPalette_BigBambooShoot}, - [ITEM_GIMMIGHOUL_COIN] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GimmighoulCoin, gItemIconPalette_GimmighoulCoin}, - [ITEM_LEADERS_CREST] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LeadersCrest, gItemIconPalette_LeadersCrest}, - [ITEM_MALICIOUS_ARMOR] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_MaliciousArmor, gItemIconPalette_MaliciousArmor}, - [ITEM_MIRROR_HERB] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_MirrorHerb, gItemIconPalette_MirrorHerb}, - [ITEM_SCROLL_OF_DARKNESS] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ScrollOfDarkness, gItemIconPalette_ScrollOfDarkness}, - [ITEM_SCROLL_OF_WATERS] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ScrollOfWaters, gItemIconPalette_ScrollOfWaters}, - [ITEM_TERA_ORB] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_TeraOrb, gItemIconPalette_TeraOrb}, - [ITEM_TINY_BAMBOO_SHOOT] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_TinyBambooShoot, gItemIconPalette_TinyBambooShoot}, + [ITEM_PUNCHING_GLOVE] = {gItemIcon_PunchingGlove, gItemIconPalette_PunchingGlove}, + [ITEM_COVERT_CLOAK] = {gItemIcon_CovertCloak, gItemIconPalette_CovertCloak}, + [ITEM_LOADED_DICE] = {gItemIcon_LoadedDice, gItemIconPalette_LoadedDice}, + [ITEM_AUSPICIOUS_ARMOR] = {gItemIcon_AuspiciousArmor, gItemIconPalette_AuspiciousArmor}, + [ITEM_BOOSTER_ENERGY] = {gItemIcon_BoosterEnergy, gItemIconPalette_BoosterEnergy}, + [ITEM_BIG_BAMBOO_SHOOT] = {gItemIcon_BigBambooShoot, gItemIconPalette_BigBambooShoot}, + [ITEM_GIMMIGHOUL_COIN] = {gItemIcon_GimmighoulCoin, gItemIconPalette_GimmighoulCoin}, + [ITEM_LEADERS_CREST] = {gItemIcon_LeadersCrest, gItemIconPalette_LeadersCrest}, + [ITEM_MALICIOUS_ARMOR] = {gItemIcon_MaliciousArmor, gItemIconPalette_MaliciousArmor}, + [ITEM_MIRROR_HERB] = {gItemIcon_MirrorHerb, gItemIconPalette_MirrorHerb}, + [ITEM_SCROLL_OF_DARKNESS] = {gItemIcon_ScrollOfDarkness, gItemIconPalette_ScrollOfDarkness}, + [ITEM_SCROLL_OF_WATERS] = {gItemIcon_ScrollOfWaters, gItemIconPalette_ScrollOfWaters}, + [ITEM_TERA_ORB] = {gItemIcon_TeraOrb, gItemIconPalette_TeraOrb}, + [ITEM_TINY_BAMBOO_SHOOT] = {gItemIcon_TinyBambooShoot, gItemIconPalette_TinyBambooShoot}, [ITEM_BUG_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BugTeraShard, gItemIconPalette_BugTeraShard}, [ITEM_DARK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DarkTeraShard, gItemIconPalette_DarkTeraShard}, [ITEM_DRAGON_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DragonTeraShard, gItemIconPalette_DragonTeraShard}, @@ -837,9 +837,9 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_ROCK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_RockTeraShard, gItemIconPalette_RockTeraShard}, [ITEM_STEEL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_SteelTeraShard, gItemIconPalette_SteelTeraShard}, [ITEM_WATER_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_WaterTeraShard, gItemIconPalette_WaterTeraShard}, - [ITEM_ADAMANT_CRYSTAL] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_AdamantCrystal, gItemIconPalette_AdamantCrystal}, - [ITEM_GRISEOUS_CORE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GriseousCore, gItemIconPalette_GriseousCore}, - [ITEM_LUSTROUS_GLOBE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LustrousGlobe, gItemIconPalette_LustrousGlobe}, + [ITEM_ADAMANT_CRYSTAL] = {gItemIcon_AdamantCrystal, gItemIconPalette_AdamantCrystal}, + [ITEM_GRISEOUS_CORE] = {gItemIcon_GriseousCore, gItemIconPalette_GriseousCore}, + [ITEM_LUSTROUS_GLOBE] = {gItemIcon_LustrousGlobe, gItemIconPalette_LustrousGlobe}, // Return to field arrow [ITEMS_COUNT] = {gItemIcon_ReturnToFieldArrow, gItemIconPalette_ReturnToFieldArrow}, }; From 1da65c40517a64a4ef0695b27d3aa3d26ecb2e9f Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Thu, 23 Mar 2023 22:33:47 -0300 Subject: [PATCH 33/40] Fixed Grassy Terrain not healing --- src/battle_util.c | 32 +++++++++++++++++++++----------- test/terrain_grassy.c | 16 ++++++++++++++++ 2 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 test/terrain_grassy.c diff --git a/src/battle_util.c b/src/battle_util.c index 3e8f94bf6..cfb1d577b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2127,15 +2127,25 @@ enum ENDTURN_FIELD_COUNT, }; -static bool32 TryEndTerrain(u32 terrainFlag, u32 stringTableId) +static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) { - if (gFieldStatuses & terrainFlag - && (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0)) + if (gFieldStatuses & terrainFlag) { - gFieldStatuses &= ~terrainFlag; - TryToRevertMimicry(); - gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; - BattleScriptExecute(BattleScript_TerrainEnds); + if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0) + { + gFieldStatuses &= ~terrainFlag; + TryToRevertMimicry(); + + if (terrainFlag != STATUS_FIELD_GRASSY_TERRAIN) + { + gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; + BattleScriptExecute(BattleScript_TerrainEnds); + } + } + if (terrainFlag == STATUS_FIELD_GRASSY_TERRAIN) + { + BattleScriptExecute(BattleScript_GrassyTerrainHeals); + } return TRUE; } return FALSE; @@ -2492,19 +2502,19 @@ u8 DoFieldEndTurnEffects(void) gBattleStruct->turnCountersTracker++; break; case ENDTURN_ELECTRIC_TERRAIN: - effect = TryEndTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC); + effect += EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_MISTY_TERRAIN: - effect = TryEndTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY); + effect += EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY); gBattleStruct->turnCountersTracker++; break; case ENDTURN_GRASSY_TERRAIN: - effect = TryEndTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS); + effect += EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS); gBattleStruct->turnCountersTracker++; break; case ENDTURN_PSYCHIC_TERRAIN: - effect = TryEndTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC); + effect += EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_WATER_SPORT: diff --git a/test/terrain_grassy.c b/test/terrain_grassy.c new file mode 100644 index 000000000..6b3a93234 --- /dev/null +++ b/test/terrain_grassy.c @@ -0,0 +1,16 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Grassy Terrain recovers 1/16th HP at end of turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GRASSY_TERRAIN); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + MESSAGE("Wobbuffet is healed by the grassy terrain!"); + HP_BAR(player, damage: -maxHP / 16); + } +} From c922ff69b91ace7dd00eb2303b18b01e4f59120a Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Thu, 23 Mar 2023 22:34:08 -0300 Subject: [PATCH 34/40] Added To-Do test results --- test/terrain_grassy.c | 3 +++ test/test.h | 7 +++++++ test/test_battle.h | 9 +++++++++ test/test_runner.c | 6 ++++++ tools/mgba-rom-test-hydra/main.c | 8 ++++++++ 5 files changed, 33 insertions(+) diff --git a/test/terrain_grassy.c b/test/terrain_grassy.c index 6b3a93234..77f8a9927 100644 --- a/test/terrain_grassy.c +++ b/test/terrain_grassy.c @@ -14,3 +14,6 @@ SINGLE_BATTLE_TEST("Grassy Terrain recovers 1/16th HP at end of turn") HP_BAR(player, damage: -maxHP / 16); } } + +TO_DO_BATTLE_TEST("Grassy Terrain increases power of Grass-type moves by 30/50 percent") +TO_DO_BATTLE_TEST("Grassy Terrain decreases power of Earthquake, Magnitude and Bulldoze by 50 percent") diff --git a/test/test.h b/test/test.h index 27475bb48..a05e42a04 100644 --- a/test/test.h +++ b/test/test.h @@ -13,6 +13,7 @@ enum TestResult TEST_RESULT_INVALID, TEST_RESULT_ERROR, TEST_RESULT_TIMEOUT, + TEST_RESULT_TODO, }; struct TestRunner @@ -138,4 +139,10 @@ s32 MgbaPrintf_(const char *fmt, ...); #define KNOWN_FAILING \ Test_ExpectedResult(TEST_RESULT_FAIL) +#define TO_DO \ + Test_ExpectedResult(TEST_RESULT_TODO) + +#define EXPECT_TO_DO \ + Test_ExitWithResult(TEST_RESULT_TODO, "%s:%d: EXPECT_TO_DO", gTestRunnerState.test->filename, __LINE__) + #endif diff --git a/test/test_battle.h b/test/test_battle.h index 16918251b..1e97229bd 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -601,6 +601,15 @@ extern struct BattleTestRunnerState *gBattleTestRunnerState; /* Test */ +#define TO_DO_BATTLE_TEST(_name) \ + SINGLE_BATTLE_TEST("TODO: " _name) \ + { \ + TO_DO; \ + GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } \ + WHEN { TURN { } } \ + THEN { EXPECT_TO_DO; } \ + } + #define SINGLE_BATTLE_TEST(_name, ...) \ struct CAT(Result, __LINE__) { MEMBERS(__VA_ARGS__) }; \ static void CAT(Test, __LINE__)(struct CAT(Result, __LINE__) *, u32, struct BattlePokemon *, struct BattlePokemon *); \ diff --git a/test/test_runner.c b/test/test_runner.c index f2d4d5de3..1d3f14aad 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -193,6 +193,10 @@ void CB2_TestRunner(void) result = "ASSUMPTION_FAIL"; color = "\e[33m"; break; + case TEST_RESULT_TODO: + result = "TO_DO"; + color = "\e[33m"; + break; case TEST_RESULT_INVALID: result = "INVALID"; break; @@ -211,6 +215,8 @@ void CB2_TestRunner(void) MgbaPrintf_(":P%s%s\e[0m", color, result); else if (gTestRunnerState.result == TEST_RESULT_ASSUMPTION_FAIL) MgbaPrintf_(":A%s%s\e[0m", color, result); + else if (gTestRunnerState.result == TEST_RESULT_TODO) + MgbaPrintf_(":T%s%s\e[0m", color, result); else if (gTestRunnerState.expectedResult == gTestRunnerState.result) MgbaPrintf_(":K%s%s\e[0m", color, result); else diff --git a/tools/mgba-rom-test-hydra/main.c b/tools/mgba-rom-test-hydra/main.c index f506d0428..8705225b8 100644 --- a/tools/mgba-rom-test-hydra/main.c +++ b/tools/mgba-rom-test-hydra/main.c @@ -44,6 +44,7 @@ struct Runner char *output_buffer; int passes; int knownFails; + int todos; int assumptionFails; int fails; int results; @@ -87,6 +88,9 @@ static void handle_read(struct Runner *runner) case 'K': runner->knownFails++; goto add_to_results; + case 'T': + runner->todos++; + goto add_to_results; case 'A': runner->assumptionFails++; goto add_to_results; @@ -427,6 +431,7 @@ int main(int argc, char *argv[]) int exit_code = 0; int passes = 0; int knownFails = 0; + int todos = 0; int assumptionFails = 0; int fails = 0; int results = 0; @@ -444,6 +449,7 @@ int main(int argc, char *argv[]) exit_code = WEXITSTATUS(wstatus); passes += runners[i].passes; knownFails += runners[i].knownFails; + todos += runners[i].todos; assumptionFails += runners[i].assumptionFails; fails += runners[i].fails; results += runners[i].results; @@ -459,6 +465,8 @@ int main(int argc, char *argv[]) fprintf(stdout, "- Tests \e[32mPASSED\e[0m: %d\n", passes); if (knownFails > 0) fprintf(stdout, "- Tests \e[33mKNOWN_FAILING\e[0m: %d\n", knownFails); + if (todos > 0) + fprintf(stdout, "- Tests \e[33mTO_DO\e[0m: %d\n", todos); if (fails > 0) fprintf(stdout, "- Tests \e[31mFAILED\e[0m : %d\n", fails); if (assumptionFails > 0) From c08df20166cb8103e599d306b47f1c73579f95c7 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Fri, 24 Mar 2023 10:17:57 +0000 Subject: [PATCH 35/40] Fix "Too many different moves" detection --- test/test_runner_battle.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 0c3c23063..b1a94b14e 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1106,6 +1106,31 @@ void Status1_(u32 sourceLine, u32 status1) SetMonData(DATA.currentMon, MON_DATA_STATUS, &status1); } +static const char *const sBattlerIdentifiersSingles[] = +{ + "player", + "opponent", +}; + +static const char *const sBattlerIdentifiersDoubles[] = +{ + "playerLeft", + "opponentLeft", + "playerRight", + "opponentRight", +}; + +static const char *BattlerIdentifier(s32 battlerId) +{ + const struct BattleTest *test = gTestRunnerState.test->data; + switch (test->type) + { + case BATTLE_TEST_SINGLES: return sBattlerIdentifiersSingles[battlerId]; + case BATTLE_TEST_DOUBLES: return sBattlerIdentifiersDoubles[battlerId]; + } + return ""; +} + static void PushBattlerAction(u32 sourceLine, s32 battlerId, u32 actionType, u32 byte) { u32 recordIndex = DATA.recordIndexes[battlerId]++; @@ -1267,7 +1292,6 @@ void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) else if (moveId == MOVE_NONE) { INVALID_IF(DATA.explicitMoves[battlerId & BIT_SIDE] & (1 << DATA.currentMonIndexes[battlerId]), "Missing explicit %S", gMoveNames[ctx.move]); - INVALID_IF(i == MAX_MON_MOVES, "Too many different moves"); SetMonData(mon, MON_DATA_MOVE1 + i, &ctx.move); SetMonData(DATA.currentMon, MON_DATA_PP1 + i, &gBattleMoves[ctx.move].pp); moveSlot = i; @@ -1275,6 +1299,7 @@ void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) break; } } + INVALID_IF(i == MAX_MON_MOVES, "Too many different moves for %s", BattlerIdentifier(battlerId)); } else if (ctx.explicitMoveSlot) { From efc9bad8427c91a4a577facd4d2b56614245d742 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Fri, 24 Mar 2023 10:21:38 +0000 Subject: [PATCH 36/40] Fix Defog "Too many moves" --- test/move_effect_defog.c | 47 ++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/test/move_effect_defog.c b/test/move_effect_defog.c index d877899bb..e9e55d57f 100644 --- a/test/move_effect_defog.c +++ b/test/move_effect_defog.c @@ -327,7 +327,6 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Aurora Veil from p DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes everything it can") { - bool32 defogTurn = FALSE; GIVEN { ASSUME(gBattleMoves[MOVE_HAIL].effect == EFFECT_HAIL); ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE); @@ -343,33 +342,29 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes everything it can" TURN { MOVE(playerLeft, MOVE_STICKY_WEB); MOVE(playerRight, MOVE_SPIKES); MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(opponentRight, MOVE_SPIKES); } TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); SWITCH(opponentLeft, 2); SWITCH(opponentRight, 3);} TURN { MOVE(playerLeft, MOVE_TOXIC_SPIKES); MOVE(playerRight, MOVE_STEALTH_ROCK); MOVE(opponentLeft, MOVE_TOXIC_SPIKES); MOVE(opponentRight, MOVE_STEALTH_ROCK); } - TURN { MOVE(playerLeft, MOVE_HAIL); MOVE(playerRight, MOVE_AURORA_VEIL); MOVE(opponentLeft, MOVE_AURORA_VEIL); MOVE(opponentRight, MOVE_STEALTH_ROCK); } - TURN { MOVE(playerLeft, MOVE_REFLECT); MOVE(playerRight, MOVE_LIGHT_SCREEN); MOVE(opponentLeft, MOVE_REFLECT); MOVE(opponentRight, MOVE_LIGHT_SCREEN); } - TURN { MOVE(playerLeft, MOVE_MIST); MOVE(playerRight, MOVE_SAFEGUARD); MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_SAFEGUARD); } - TURN { defogTurn = TRUE ; MOVE(opponentRight, MOVE_DEFOG, target:playerLeft);} + TURN { MOVE(playerLeft, MOVE_HAIL); MOVE(playerRight, MOVE_AURORA_VEIL); MOVE(opponentLeft, MOVE_AURORA_VEIL); MOVE(opponentRight, MOVE_LIGHT_SCREEN); } + TURN { MOVE(playerLeft, MOVE_REFLECT); MOVE(playerRight, MOVE_LIGHT_SCREEN); MOVE(opponentLeft, MOVE_REFLECT); MOVE(opponentRight, MOVE_SAFEGUARD); } + TURN { MOVE(playerLeft, MOVE_MIST); MOVE(playerRight, MOVE_SAFEGUARD); MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_DEFOG, target: playerLeft); } } SCENE { - if (defogTurn == TRUE) - { - MESSAGE("Foe Glalie used Defog!"); - MESSAGE("Glalie is protected by MIST!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, opponentRight); - // Player side - MESSAGE("Ally's Reflect wore off!"); - MESSAGE("Ally's Light Screen wore off!"); - MESSAGE("Ally's Mist wore off!"); - MESSAGE("Ally's Aurora Veil wore off!"); - MESSAGE("Ally's Safeguard wore off!"); + MESSAGE("Foe Glalie used Defog!"); + MESSAGE("Glalie is protected by MIST!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, opponentRight); + // Player side + MESSAGE("Ally's Reflect wore off!"); + MESSAGE("Ally's Light Screen wore off!"); + MESSAGE("Ally's Mist wore off!"); + MESSAGE("Ally's Aurora Veil wore off!"); + MESSAGE("Ally's Safeguard wore off!"); - MESSAGE("The spikes disappeared from the ground around your team!"); - MESSAGE("The pointed stones disappeared from around your team!"); - MESSAGE("The poison spikes disappeared from the ground around your team!"); - MESSAGE("The sticky web has disappeared from the ground around your team!"); + MESSAGE("The spikes disappeared from the ground around your team!"); + MESSAGE("The pointed stones disappeared from around your team!"); + MESSAGE("The poison spikes disappeared from the ground around your team!"); + MESSAGE("The sticky web has disappeared from the ground around your team!"); - // Opponent side - MESSAGE("The spikes disappeared from the ground around the opposing team!"); - MESSAGE("The pointed stones disappeared from around the opposing team!"); - MESSAGE("The poison spikes disappeared from the ground around the opposing team!"); - MESSAGE("The sticky web has disappeared from the ground around the opposing team!"); - } + // Opponent side + MESSAGE("The spikes disappeared from the ground around the opposing team!"); + MESSAGE("The pointed stones disappeared from around the opposing team!"); + MESSAGE("The poison spikes disappeared from the ground around the opposing team!"); + MESSAGE("The sticky web has disappeared from the ground around the opposing team!"); } } From c383c995f1413091846b9140155ca8426d759ce3 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Fri, 24 Mar 2023 10:12:09 -0300 Subject: [PATCH 37/40] Applied review changes --- src/battle_util.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index cfb1d577b..e8adc8a11 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2135,17 +2135,14 @@ static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) { gFieldStatuses &= ~terrainFlag; TryToRevertMimicry(); - - if (terrainFlag != STATUS_FIELD_GRASSY_TERRAIN) + if (!(terrainFlag & STATUS_FIELD_GRASSY_TERRAIN)) { gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; BattleScriptExecute(BattleScript_TerrainEnds); } } - if (terrainFlag == STATUS_FIELD_GRASSY_TERRAIN) - { + if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN) BattleScriptExecute(BattleScript_GrassyTerrainHeals); - } return TRUE; } return FALSE; @@ -2502,19 +2499,19 @@ u8 DoFieldEndTurnEffects(void) gBattleStruct->turnCountersTracker++; break; case ENDTURN_ELECTRIC_TERRAIN: - effect += EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC); + effect = EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_MISTY_TERRAIN: - effect += EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY); + effect = EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY); gBattleStruct->turnCountersTracker++; break; case ENDTURN_GRASSY_TERRAIN: - effect += EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS); + effect = EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS); gBattleStruct->turnCountersTracker++; break; case ENDTURN_PSYCHIC_TERRAIN: - effect += EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC); + effect = EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_WATER_SPORT: From b9447f88fc3a30b78206bca459b60bdc831f461a Mon Sep 17 00:00:00 2001 From: sbird Date: Fri, 24 Mar 2023 15:41:18 +0100 Subject: [PATCH 38/40] fix compound eyes ohko test --- src/battle_script_commands.c | 2 +- test/ability_compound_eyes.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 283adb54a..9ace33bf0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12562,7 +12562,7 @@ static void Cmd_tryKO(void) if (gCurrentMove == MOVE_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE)) odds -= 10; #endif - if (Random() % 100 + 1 < odds && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) + if (RandomPercentage(RNG_ACCURACY, odds) && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) lands = TRUE; } diff --git a/test/ability_compound_eyes.c b/test/ability_compound_eyes.c index bbe718176..d90604d5c 100644 --- a/test/ability_compound_eyes.c +++ b/test/ability_compound_eyes.c @@ -21,12 +21,11 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") // than we expect. SINGLE_BATTLE_TEST("Compound Eyes does not affect OHKO moves") { - KNOWN_FAILING; PASSES_RANDOMLY(30, 100, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_FISSURE].accuracy == 30); ASSUME(gBattleMoves[MOVE_FISSURE].effect == EFFECT_OHKO); - PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_TINTED_LENS); }; + PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }; OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_FISSURE); } From aea5d79aa2965e88fc69ef483f7f2c87b02dfc28 Mon Sep 17 00:00:00 2001 From: sbird Date: Thu, 23 Feb 2023 23:14:14 +0100 Subject: [PATCH 39/40] [trainer_parties] implement fully customizable npc trainer parties fix nature related bug, fix hash generation, add tests --- include/battle_main.h | 4 + include/constants/trainers.h | 9 +- include/data.h | 42 +++++-- include/global.h | 3 + src/battle_main.c | 217 +++++++++++++++++++++++++++-------- src/data.c | 1 + test/trainer_control.c | 140 ++++++++++++++++++++++ 7 files changed, 356 insertions(+), 60 deletions(-) create mode 100644 test/trainer_control.c diff --git a/include/battle_main.h b/include/battle_main.h index df59dcae3..8d1aad454 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -1,6 +1,9 @@ #ifndef GUARD_BATTLE_MAIN_H #define GUARD_BATTLE_MAIN_H +#include "pokemon.h" +#include "data.h" + struct TrainerMoney { u8 classId; @@ -66,6 +69,7 @@ bool8 TryRunFromBattle(u8 battlerId); void SpecialStatusesClear(void); void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk); bool32 IsWildMonSmart(void); +u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags); extern struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE]; diff --git a/include/constants/trainers.h b/include/constants/trainers.h index 39ca2a2b8..25660ac07 100644 --- a/include/constants/trainers.h +++ b/include/constants/trainers.h @@ -374,7 +374,12 @@ // All trainer parties specify the IV, level, and species for each Pokémon in the // party. Some trainer parties also specify held items and custom moves for each // Pokémon. -#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0) -#define F_TRAINER_PARTY_HELD_ITEM (1 << 1) +#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0) +#define F_TRAINER_PARTY_HELD_ITEM (1 << 1) +#define F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED (1 << 3) + +// Trainer party defines +#define TRAINER_MON_MALE 1 +#define TRAINER_MON_FEMALE 2 #endif // GUARD_TRAINERS_H diff --git a/include/data.h b/include/data.h index 79f6a3715..6e41ac703 100644 --- a/include/data.h +++ b/include/data.h @@ -31,6 +31,26 @@ struct MonCoords #define MON_COORDS_SIZE(width, height)(DIV_ROUND_UP(width, 8) << 4 | DIV_ROUND_UP(height, 8)) #define GET_MON_COORDS_WIDTH(size)((size >> 4) * 8) #define GET_MON_COORDS_HEIGHT(size)((size & 0xF) * 8) +#define TRAINER_PARTY_IVS(hp, atk, def, speed, spatk, spdef) (hp | (atk << 5) | (def << 10) | (speed << 15) | (spatk << 20) | (spdef << 25)) +#define TRAINER_PARTY_EVS(hp, atk, def, speed, spatk, spdef) ((const u8[6]){hp,atk,def,spatk,spdef,speed}) +#define TRAINER_PARTY_NATURE(nature) (nature+1) + +struct TrainerMonCustomized +{ + const u8 *nickname; + const u8 *ev; + u32 iv; + u16 moves[4]; + u16 species; + u16 heldItem; + u16 ability; + u8 lvl; + u8 ball; + u8 friendship; + u8 nature : 5; + bool8 gender : 2; + bool8 isShiny : 1; +}; struct TrainerMonNoItemDefaultMoves { @@ -68,6 +88,7 @@ struct TrainerMonItemCustomMoves #define NO_ITEM_CUSTOM_MOVES(party) { .NoItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET #define ITEM_DEFAULT_MOVES(party) { .ItemDefaultMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_HELD_ITEM #define ITEM_CUSTOM_MOVES(party) { .ItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM +#define EVERYTHING_CUSTOMIZED(party) { .EverythingCustomized = party}, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED union TrainerMonPtr { @@ -75,20 +96,21 @@ union TrainerMonPtr const struct TrainerMonNoItemCustomMoves *NoItemCustomMoves; const struct TrainerMonItemDefaultMoves *ItemDefaultMoves; const struct TrainerMonItemCustomMoves *ItemCustomMoves; + const struct TrainerMonCustomized *EverythingCustomized; }; struct Trainer { - /*0x00*/ u8 partyFlags; - /*0x01*/ u8 trainerClass; - /*0x02*/ u8 encounterMusic_gender; // last bit is gender - /*0x03*/ u8 trainerPic; - /*0x04*/ u8 trainerName[TRAINER_NAME_LENGTH + 1]; - /*0x10*/ u16 items[MAX_TRAINER_ITEMS]; - /*0x18*/ bool8 doubleBattle; - /*0x1C*/ u32 aiFlags; - /*0x20*/ u8 partySize; - /*0x24*/ union TrainerMonPtr party; + /*0x00*/ u32 aiFlags; + /*0x04*/ union TrainerMonPtr party; + /*0x08*/ u16 items[MAX_TRAINER_ITEMS]; + /*0x10*/ u8 trainerClass; + /*0x11*/ u8 encounterMusic_gender; // last bit is gender + /*0x12*/ u8 trainerPic; + /*0x13*/ u8 trainerName[TRAINER_NAME_LENGTH + 1]; + /*0x1E*/ bool8 doubleBattle:1; + u8 partyFlags:7; + /*0x1F*/ u8 partySize; }; #define TRAINER_ENCOUNTER_MUSIC(trainer)((gTrainers[trainer].encounterMusic_gender & 0x7F)) diff --git a/include/global.h b/include/global.h index 4b3e90bfa..f7d166bb5 100644 --- a/include/global.h +++ b/include/global.h @@ -147,6 +147,9 @@ #define CAT(a, b) CAT_(a, b) #define CAT_(a, b) a ## b +// Converts a string to a compound literal, essentially making it a pointer to const u8 +#define COMPOUND_STRING(str) (const u8[]) _(str) + // This produces an error at compile-time if expr is zero. // It looks like file.c:line: size of array `id' is negative #define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1]; diff --git a/src/battle_main.c b/src/battle_main.c index 9ea3b6644..efbf1b819 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -118,6 +118,10 @@ static void HandleEndTurn_FinishBattle(void); static void SpriteCB_UnusedBattleInit(struct Sprite *sprite); static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite); static void TrySpecialEvolution(void); +static u32 Crc32B (const u8 *data, u32 size); +static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i); +static void ModifyPersonalityForNature(u32 *personality, u32 newNature); +static u32 GeneratePersonalityForGender(u32 gender, u32 species); EWRAM_DATA u16 gBattle_BG0_X = 0; EWRAM_DATA u16 gBattle_BG0_Y = 0; @@ -1869,72 +1873,129 @@ static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite) } } -static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer) +static u32 Crc32B (const u8 *data, u32 size) +{ + s32 i, j; + u32 byte, crc, mask; + + i = 0; + crc = 0xFFFFFFFF; + for (i = 0; i < size; ++i) + { + byte = data[i]; + crc = crc ^ byte; + for (j = 7; j >= 0; --j) + { + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + } + return ~crc; +} + +static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i) +{ + const u8 *buffer; + u32 n; + if (trainer->partyFlags == 0) + { + buffer = (const u8 *) &trainer->party.NoItemDefaultMoves[i]; + n = sizeof(*trainer->party.NoItemDefaultMoves); + } + else if (trainer->partyFlags == F_TRAINER_PARTY_CUSTOM_MOVESET) + { + buffer = (const u8 *) &trainer->party.NoItemCustomMoves[i]; + n = sizeof(*trainer->party.NoItemCustomMoves); + } + else if (trainer->partyFlags == F_TRAINER_PARTY_HELD_ITEM) + { + buffer = (const u8 *) &trainer->party.ItemDefaultMoves[i]; + n = sizeof(*trainer->party.ItemDefaultMoves); + } + else if (trainer->partyFlags == (F_TRAINER_PARTY_HELD_ITEM | F_TRAINER_PARTY_CUSTOM_MOVESET)) + { + buffer = (const u8 *) &trainer->party.ItemCustomMoves[i]; + n = sizeof(*trainer->party.ItemCustomMoves); + } + else if (trainer->partyFlags == F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED) + { + buffer = (const u8 *) &trainer->party.EverythingCustomized[i]; + n = sizeof(*trainer->party.EverythingCustomized); + } + return Crc32B(buffer, n); +} + +static void ModifyPersonalityForNature(u32 *personality, u32 newNature) +{ + u32 nature = GetNatureFromPersonality(*personality); + s32 diff = abs(nature - newNature); + s32 sign = (nature > newNature) ? 1 : -1; + if (diff > NUM_NATURES / 2) + { + diff = NUM_NATURES - diff; + sign *= -1; + } + *personality -= (diff * sign); +} + +static u32 GeneratePersonalityForGender(u32 gender, u32 species) +{ + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species]; + if (gender == MON_MALE) + return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio; + else + return speciesInfo->genderRatio / 2; +} + +u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags) { - u32 nameHash = 0; u32 personalityValue; u8 fixedIV; s32 i, j; u8 monsCount; - u16 ball; - - if (trainerNum == TRAINER_SECRET_BASE) - return 0; - - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER + s32 ball = -1; + if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL))) { if (firstTrainer == TRUE) ZeroEnemyPartyMons(); - if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) + if (battleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) { - if (gTrainers[trainerNum].partySize > PARTY_SIZE / 2) + if (trainer->partySize > PARTY_SIZE / 2) monsCount = PARTY_SIZE / 2; else - monsCount = gTrainers[trainerNum].partySize; + monsCount = trainer->partySize; } else { - monsCount = gTrainers[trainerNum].partySize; + monsCount = trainer->partySize; } for (i = 0; i < monsCount; i++) { - - if (gTrainers[trainerNum].doubleBattle == TRUE) + u32 personalityHash = GeneratePartyHash(trainer, i); + if (trainer->doubleBattle == TRUE) personalityValue = 0x80; - else if (gTrainers[trainerNum].encounterMusic_gender & F_TRAINER_FEMALE) + else if (trainer->encounterMusic_gender & F_TRAINER_FEMALE) personalityValue = 0x78; // Use personality more likely to result in a female Pokémon else personalityValue = 0x88; // Use personality more likely to result in a male Pokémon - for (j = 0; gTrainers[trainerNum].trainerName[j] != EOS; j++) - nameHash += gTrainers[trainerNum].trainerName[j]; - - switch (gTrainers[trainerNum].partyFlags) + personalityValue += personalityHash << 8; + switch (trainer->partyFlags) { case 0: { - const struct TrainerMonNoItemDefaultMoves *partyData = gTrainers[trainerNum].party.NoItemDefaultMoves; - - for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++) - nameHash += gSpeciesNames[partyData[i].species][j]; - - personalityValue += nameHash << 8; + const struct TrainerMonNoItemDefaultMoves *partyData = trainer->party.NoItemDefaultMoves; fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255; CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0); break; } case F_TRAINER_PARTY_CUSTOM_MOVESET: { - const struct TrainerMonNoItemCustomMoves *partyData = gTrainers[trainerNum].party.NoItemCustomMoves; - - for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++) - nameHash += gSpeciesNames[partyData[i].species][j]; - - personalityValue += nameHash << 8; + const struct TrainerMonNoItemCustomMoves *partyData = trainer->party.NoItemCustomMoves; fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255; CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0); @@ -1947,12 +2008,7 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir } case F_TRAINER_PARTY_HELD_ITEM: { - const struct TrainerMonItemDefaultMoves *partyData = gTrainers[trainerNum].party.ItemDefaultMoves; - - for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++) - nameHash += gSpeciesNames[partyData[i].species][j]; - - personalityValue += nameHash << 8; + const struct TrainerMonItemDefaultMoves *partyData = trainer->party.ItemDefaultMoves; fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255; CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0); @@ -1961,12 +2017,7 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir } case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM: { - const struct TrainerMonItemCustomMoves *partyData = gTrainers[trainerNum].party.ItemCustomMoves; - - for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++) - nameHash += gSpeciesNames[partyData[i].species][j]; - - personalityValue += nameHash << 8; + const struct TrainerMonItemCustomMoves *partyData = trainer->party.ItemCustomMoves; fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255; CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0); @@ -1979,18 +2030,88 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir } break; } + case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED: + { + const struct TrainerMonCustomized *partyData = trainer->party.EverythingCustomized; + u32 otIdType = OT_ID_RANDOM_NO_SHINY; + u32 fixedOtId = 0; + if (partyData[i].gender == TRAINER_MON_MALE) + personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); + else if (partyData[i].gender == TRAINER_MON_FEMALE) + personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); + if (partyData[i].nature != 0) + ModifyPersonalityForNature(&personalityValue, partyData[i].nature - 1); + if (partyData[i].isShiny) + { + otIdType = OT_ID_PRESET; + fixedOtId = HIHALF(personalityValue) ^ LOHALF(personalityValue); + } + CreateMon(&party[i], partyData[i].species, partyData[i].lvl, 0, TRUE, personalityValue, otIdType, fixedOtId); + SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem); + + // TODO: Figure out a default strategy when moves are not set, to generate a good moveset + for (j = 0; j < MAX_MON_MOVES; ++j) + { + SetMonData(&party[i], MON_DATA_MOVE1 + j, &partyData[i].moves[j]); + SetMonData(&party[i], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp); + } + SetMonData(&party[i], MON_DATA_IVS, &(partyData[i].iv)); + if (partyData[i].ev != NULL) + { + SetMonData(&party[i], MON_DATA_HP_EV, &(partyData[i].ev[0])); + SetMonData(&party[i], MON_DATA_ATK_EV, &(partyData[i].ev[1])); + SetMonData(&party[i], MON_DATA_DEF_EV, &(partyData[i].ev[2])); + SetMonData(&party[i], MON_DATA_SPATK_EV, &(partyData[i].ev[3])); + SetMonData(&party[i], MON_DATA_SPDEF_EV, &(partyData[i].ev[4])); + SetMonData(&party[i], MON_DATA_SPEED_EV, &(partyData[i].ev[5])); + } + if (partyData[i].ability != ABILITY_NONE) + { + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; + u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); + for (j = 0; j < maxAbilities; ++j) + { + if (speciesInfo->abilities[j] == partyData[i].ability) + break; + } + if (j < maxAbilities) + SetMonData(&party[i], MON_DATA_ABILITY_NUM, &j); + } + SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship)); + if (partyData[i].ball != ITEM_NONE) + { + ball = partyData[i].ball; + SetMonData(&party[i], MON_DATA_POKEBALL, &ball); + } + if (partyData[i].nickname != NULL) + { + SetMonData(&party[i], MON_DATA_NICKNAME, partyData[i].nickname); + } + CalculateMonStats(&party[i]); + } } #if B_TRAINER_CLASS_POKE_BALLS >= GEN_7 - ball = (sTrainerBallTable[gTrainers[trainerNum].trainerClass]) ? sTrainerBallTable[gTrainers[trainerNum].trainerClass] : ITEM_POKE_BALL; - SetMonData(&party[i], MON_DATA_POKEBALL, &ball); + if (ball == -1) + { + ball = (sTrainerBallTable[trainer->trainerClass]) ? sTrainerBallTable[trainer->trainerClass] : ITEM_POKE_BALL; + SetMonData(&party[i], MON_DATA_POKEBALL, &ball); + } #endif } - - gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle; } - return gTrainers[trainerNum].partySize; + return trainer->partySize; +} + +static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer) +{ + u8 retVal; + if (trainerNum == TRAINER_SECRET_BASE) + return 0; + retVal = CreateNPCTrainerPartyFromTrainer(party, &gTrainers[trainerNum], firstTrainer, gBattleTypeFlags); + + gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle; } void VBlankCB_Battle(void) diff --git a/src/data.c b/src/data.c index 08b2c96e0..1ced9ce81 100644 --- a/src/data.c +++ b/src/data.c @@ -3,6 +3,7 @@ #include "battle.h" #include "data.h" #include "graphics.h" +#include "constants/abilities.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/trainers.h" diff --git a/test/trainer_control.c b/test/trainer_control.c new file mode 100644 index 000000000..307942207 --- /dev/null +++ b/test/trainer_control.c @@ -0,0 +1,140 @@ +#include "global.h" +#include "test.h" +#include "battle.h" +#include "battle_main.h" +#include "data.h" +#include "malloc.h" +#include "string_util.h" +#include "constants/item.h" +#include "constants/abilities.h" +#include "constants/trainers.h" +#include "constants/battle.h" + + +static const struct TrainerMonCustomized sTestParty1[] = +{ + { + .species = SPECIES_WOBBUFFET, + .ball = ITEM_MASTER_BALL, + .ability = ABILITY_TELEPATHY, + .friendship = 42, + .gender = TRAINER_MON_FEMALE, + .heldItem = ITEM_ASSAULT_VEST, + .isShiny = TRUE, + .iv = TRAINER_PARTY_IVS(25,26,27,28,29,30), + .ev = TRAINER_PARTY_EVS(252, 0, 0, 252, 4, 0), + .lvl = 67, + .moves = {MOVE_AIR_SLASH, MOVE_BARRIER, MOVE_SOLAR_BEAM, MOVE_EXPLOSION}, + .nature = TRAINER_PARTY_NATURE(NATURE_HASTY), + .nickname = COMPOUND_STRING("Bubbles") + }, + { + .species = SPECIES_WOBBUFFET, + .ability = ABILITY_SHADOW_TAG, + .lvl = 5, + }, +}; + +static const struct TrainerMonNoItemDefaultMoves sTestParty2[] = +{ + { + .species = SPECIES_WOBBUFFET, + .lvl = 5, + }, + { + .species = SPECIES_WOBBUFFET, + .lvl = 6, + } +}; + +static const struct Trainer sTestTrainer1 = +{ + .trainerName = _("Test1"), + .party = EVERYTHING_CUSTOMIZED(sTestParty1), +}; + +static const struct Trainer sTestTrainer2 = +{ + .trainerName = _("Test2"), + .party = NO_ITEM_DEFAULT_MOVES(sTestParty2), +}; + +TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon") +{ + struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); + u8 nickBuffer[20]; + CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainer1, TRUE, BATTLE_TYPE_TRAINER); + EXPECT(IsMonShiny(&testParty[0])); + EXPECT(!IsMonShiny(&testParty[1])); + + EXPECT(GetMonData(&testParty[0], MON_DATA_POKEBALL, 0) == ITEM_MASTER_BALL); + EXPECT(GetMonData(&testParty[1], MON_DATA_POKEBALL, 0) == ITEM_POKE_BALL); + + EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES, 0) == SPECIES_WOBBUFFET); + EXPECT(GetMonData(&testParty[1], MON_DATA_SPECIES, 0) == SPECIES_WOBBUFFET); + + EXPECT(GetMonAbility(&testParty[0]) == ABILITY_TELEPATHY); + EXPECT(GetMonAbility(&testParty[1]) == ABILITY_SHADOW_TAG); + + EXPECT(GetMonData(&testParty[0], MON_DATA_FRIENDSHIP, 0) == 42); + EXPECT(GetMonData(&testParty[1], MON_DATA_FRIENDSHIP, 0) == 0); + + EXPECT(GetMonData(&testParty[0], MON_DATA_HELD_ITEM, 0) == ITEM_ASSAULT_VEST); + EXPECT(GetMonData(&testParty[1], MON_DATA_HELD_ITEM, 0) == ITEM_NONE); + + EXPECT(GetMonData(&testParty[0], MON_DATA_HP_IV, 0) == 25); + EXPECT(GetMonData(&testParty[0], MON_DATA_ATK_IV, 0) == 26); + EXPECT(GetMonData(&testParty[0], MON_DATA_DEF_IV, 0) == 27); + EXPECT(GetMonData(&testParty[0], MON_DATA_SPEED_IV, 0) == 28); + EXPECT(GetMonData(&testParty[0], MON_DATA_SPATK_IV, 0) == 29); + EXPECT(GetMonData(&testParty[0], MON_DATA_SPDEF_IV, 0) == 30); + + EXPECT(GetMonData(&testParty[1], MON_DATA_HP_IV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_ATK_IV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_DEF_IV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_SPEED_IV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_SPATK_IV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_SPDEF_IV, 0) == 0); + + EXPECT(GetMonData(&testParty[0], MON_DATA_HP_EV, 0) == 252); + EXPECT(GetMonData(&testParty[0], MON_DATA_ATK_EV, 0) == 0); + EXPECT(GetMonData(&testParty[0], MON_DATA_DEF_EV, 0) == 0); + EXPECT(GetMonData(&testParty[0], MON_DATA_SPEED_EV, 0) == 252); + EXPECT(GetMonData(&testParty[0], MON_DATA_SPATK_EV, 0) == 4); + EXPECT(GetMonData(&testParty[0], MON_DATA_SPDEF_EV, 0) == 0); + + EXPECT(GetMonData(&testParty[1], MON_DATA_HP_EV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_ATK_EV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_DEF_EV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_SPEED_EV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_SPATK_EV, 0) == 0); + EXPECT(GetMonData(&testParty[1], MON_DATA_SPDEF_EV, 0) == 0); + + EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL, 0) == 67); + EXPECT(GetMonData(&testParty[1], MON_DATA_LEVEL, 0) == 5); + + EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE1, 0) == MOVE_AIR_SLASH); + EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE2, 0) == MOVE_BARRIER); + EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE3, 0) == MOVE_SOLAR_BEAM); + EXPECT(GetMonData(&testParty[0], MON_DATA_MOVE4, 0) == MOVE_EXPLOSION); + + GetMonData(&testParty[0], MON_DATA_NICKNAME, nickBuffer); + EXPECT(StringCompare(nickBuffer, COMPOUND_STRING("Bubbles")) == 0); + + GetMonData(&testParty[1], MON_DATA_NICKNAME, nickBuffer); + EXPECT(StringCompare(nickBuffer, COMPOUND_STRING("Wobbuffet")) == 0); + + EXPECT(GetGenderFromSpeciesAndPersonality(GetMonData(&testParty[0], MON_DATA_SPECIES, 0), testParty[0].box.personality) == MON_FEMALE); + + EXPECT(GetNature(&testParty[0]) == NATURE_HASTY); + + Free(testParty); +} + +TEST("CreateNPCTrainerPartyForTrainer generates different personalities for different mons") +{ + struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); + CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainer2, TRUE, BATTLE_TYPE_TRAINER); + EXPECT(testParty[0].box.personality != testParty[1].box.personality); + Free(testParty); +} From 4b43f3eb9df60e42758666411fcf8ed79c5890f6 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 26 Mar 2023 21:10:51 +0200 Subject: [PATCH 40/40] add cases for everything customized --- src/battle_script_commands.c | 6 +++++ src/battle_setup.c | 8 ++++++ src/battle_tower.c | 49 ++++++++++++++++++++++++++++++++++++ src/match_call.c | 3 +++ 4 files changed, 66 insertions(+) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index c50d801b1..b1ce7f964 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7504,6 +7504,12 @@ static u32 GetTrainerMoneyToGive(u16 trainerId) lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl; } break; + case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED: + { + const struct TrainerMonCustomized *party = gTrainers[trainerId].party.EverythingCustomized; + lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl; + } + break; } for (; gTrainerMoneyTable[i].classId != 0xFF; i++) diff --git a/src/battle_setup.c b/src/battle_setup.c index 220edd394..2b8e63e76 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -798,6 +798,14 @@ static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons) sum += party[i].lvl; } break; + case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED: + { + const struct TrainerMonCustomized *party; + party = gTrainers[opponentId].party.EverythingCustomized; + for (i = 0; i < count; i++) + sum += party[i].lvl; + } + break; } return sum; diff --git a/src/battle_tower.c b/src/battle_tower.c index 930075656..a4758b930 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -23,6 +23,7 @@ #include "field_message_box.h" #include "tv.h" #include "battle_factory.h" +#include "constants/abilities.h" #include "constants/apprentice.h" #include "constants/battle_dome.h" #include "constants/battle_frontier.h" @@ -3007,6 +3008,7 @@ static void FillPartnerParty(u16 trainerId) u16 monId; u32 otID; u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1]; + s32 ball = -1; SetFacilityPtrsGetLevel(); if (trainerId == TRAINER_STEVEN_PARTNER) @@ -3098,6 +3100,53 @@ static void FillPartnerParty(u16 trainerId) } break; } + case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED: + { + const struct TrainerMonCustomized *partyData = gTrainers[trainerId - TRAINER_CUSTOM_PARTNER].party.EverythingCustomized; + + CreateMon(&gPlayerParty[i], partyData[i].species, partyData[i].lvl, 0, TRUE, j, TRUE, otID); + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem); + + // TODO: Figure out a default strategy when moves are not set, to generate a good moveset + for (j = 0; j < MAX_MON_MOVES; ++j) + { + SetMonData(&gPlayerParty[i], MON_DATA_MOVE1 + j, &partyData[i].moves[j]); + SetMonData(&gPlayerParty[i], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp); + } + SetMonData(&gPlayerParty[i], MON_DATA_IVS, &(partyData[i].iv)); + if (partyData[i].ev != NULL) + { + SetMonData(&gPlayerParty[i], MON_DATA_HP_EV, &(partyData[i].ev[0])); + SetMonData(&gPlayerParty[i], MON_DATA_ATK_EV, &(partyData[i].ev[1])); + SetMonData(&gPlayerParty[i], MON_DATA_DEF_EV, &(partyData[i].ev[2])); + SetMonData(&gPlayerParty[i], MON_DATA_SPATK_EV, &(partyData[i].ev[3])); + SetMonData(&gPlayerParty[i], MON_DATA_SPDEF_EV, &(partyData[i].ev[4])); + SetMonData(&gPlayerParty[i], MON_DATA_SPEED_EV, &(partyData[i].ev[5])); + } + if (partyData[i].ability != ABILITY_NONE) + { + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; + u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); + for (j = 0; j < maxAbilities; ++j) + { + if (speciesInfo->abilities[j] == partyData[i].ability) + break; + } + if (j < maxAbilities) + SetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM, &j); + } + SetMonData(&gPlayerParty[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship)); + if (partyData[i].ball != ITEM_NONE) + { + ball = partyData[i].ball; + SetMonData(&gPlayerParty[i], MON_DATA_POKEBALL, &ball); + } + if (partyData[i].nickname != NULL) + { + SetMonData(&gPlayerParty[i], MON_DATA_NICKNAME, partyData[i].nickname); + } + CalculateMonStats(&gPlayerParty[i]); + } } StringCopy(trainerName, gTrainers[trainerId - TRAINER_CUSTOM_PARTNER].trainerName); diff --git a/src/match_call.c b/src/match_call.c index a9d18b347..c3378040e 100644 --- a/src/match_call.c +++ b/src/match_call.c @@ -1812,6 +1812,9 @@ static void PopulateSpeciesFromTrainerParty(int matchCallId, u8 *destStr) case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM: speciesName = gSpeciesNames[party.ItemCustomMoves[monId].species]; break; + case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED: + speciesName = gSpeciesNames[party.EverythingCustomized[monId].species]; + break; } StringCopy(destStr, speciesName);