Secondary effects overhaul minor follow-up (#4062)

* settwoturnstring command

* Unified two-turn attacks and Meteor Beam

To do: Solar Beam

* Solar Beam

Also fixed various function, removed EFFECT_GUST (who knows why that exists?)

* Updated Solar Beam + tests

* Redid two turn move + animations logic

Removed pointless various function; to do: remove Skull Bash, fix AI test

* Removed now-pointless flag

* Removed Skull Bash

And temporarily commented out failing AI tests

* Removed Sky Uppercut effect

Not sure when or why this was ever necessary

* Removed BattleScript_EffectSemiInvulnerable

Now uses BattleScript_EffectTwoTurnsAttack. Kept the effect; used the argument field to determine which STATUS3 such moves should apply but added a function to jump over weather checks in BattleScript_EffectTwoTurnsAttack if the current move is semi-invulnerable (since the instant-fire weather check and STATUS3 use the same field)

* Applied review changes

* Replaced VARIOUS with callnative

Tried to fix test but couldn't :/ wtf is going on

* Fixed one AI test

Cant fix the other...

* Added KNOWN_FAILING to failing AI tests

Separated them out into their own test

* Optimised script, overhauled charge turn string setting

Condensed multiple confusing macros into one, jumpifweathercheckchargeeffects. Script now tweaked and trimmed, string ids for charge turns now added to argument along with status3 (thanks to compression macro) and instant-fire-weather for semi-invulnerable and two-turn moves respectively. Also introduced a savedStringId in gBattleScripting to make string selection work.

* Unified two turn move tests + minor corrections

* Added semi-invulnerable move tests

Set the Razor Wind test to known failing - something to do with its animation?

---------

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
Nephrite 2024-02-05 07:02:59 +09:00 committed by GitHub
parent 7ae50ea507
commit 65c508d193
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1000 additions and 595 deletions

View File

@ -72,6 +72,10 @@
.2byte \id
.endm
.macro printsavedstring
printstring 0
.endm
.macro printselectionstring id:req
.byte 0x11
.2byte \id
@ -776,8 +780,21 @@
.byte 0x8b
.endm
.macro unused0x8C
.macro twoturnmoveschargestringandanimation
.byte 0x8c
.4byte 1f @animation then attack string
@default - attack string then animation
printsavedstring
waitmessage B_WAIT_TIME_LONG
attackanimation
waitanimation
goto 2f
1:
attackanimation
waitanimation
printsavedstring
waitmessage B_WAIT_TIME_LONG
2:
.endm
.macro setmultihitcounter value:req
@ -1035,12 +1052,20 @@
.4byte \failInstr
.endm
.macro setsemiinvulnerablebit
.macro setsemiinvulnerablebit clear=FALSE
.byte 0xc5
.byte \clear
.endm
.macro clearsemiinvulnerablebit
setsemiinvulnerablebit TRUE
.endm
.macro jumpifweathercheckchargeeffects battler:req, checkChargeTurnEffects:req, jumpInstr:req
.byte 0xc6
.byte \battler
.byte \checkChargeTurnEffects
.4byte \jumpInstr
.endm
.macro setminimize
@ -1337,13 +1362,6 @@
.4byte \jumpInstr
.endm
.macro jumpifholdeffect battler:req, holdEffect:req, jumpInstr:req
callnative BS_JumpIfHoldEffect
.byte \battler
.2byte \holdEffect
.4byte \jumpInstr
.endm
.macro dostockpilestatchangeswearoff, battler:req, statChangeInstr:req
callnative BS_DoStockpileStatChangesWearOff
.byte \battler
@ -2005,10 +2023,15 @@
.4byte \jumpInstr
.endm
.macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req
various \battler, VARIOUS_JUMP_IF_NO_HOLD_EFFECT
.macro jumpifholdeffect battler:req, holdEffect:req, jumpInstr:req, equal=TRUE
various \battler, VARIOUS_JUMP_IF_HOLD_EFFECT
.byte \holdEffect
.4byte \jumpInstr
.byte \equal
.endm
.macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req
jumpifholdeffect \battler, \holdEffect, \jumpInstr, FALSE
.endm
.macro infatuatewithbattler battler:req, infatuateWith:req

View File

@ -585,46 +585,9 @@ BattleScript_BeakBlastBurn::
call BattleScript_MoveEffectBurn
return
BattleScript_EffectMeteorBeam::
@ DecideTurn
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn
jumpifmove MOVE_METEOR_BEAM, BattleScript_SetStringMeteorBeam
jumpifmove MOVE_ELECTRO_SHOT, BattleScript_SetStringElectroShock
BattleScript_TryCharging:
call BattleScript_FirstChargingTurnMeteorBeam
jumpifmove MOVE_METEOR_BEAM, BattleScript_TryMeteorBeam
jumpifweatheraffected BS_ATTACKER, B_WEATHER_RAIN, BattleScript_TwoTurnMovesSecondTurn @ Check for move Electro Shot
BattleScript_TryMeteorBeam:
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd
call BattleScript_PowerHerbActivation
goto BattleScript_TwoTurnMovesSecondTurn
BattleScript_SetStringMeteorBeam:
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_METEOR_BEAM
goto BattleScript_TryCharging
BattleScript_SetStringElectroShock:
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_ELECTRO_SHOCK
goto BattleScript_TryCharging
BattleScript_FirstChargingTurnMeteorBeam::
attackcanceler
flushtextbox
ppreduce
attackanimation
waitanimation
orword gHitMarker, HITMARKER_CHARGING
seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER
copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID
printfromtable gFirstTurnOfTwoStringIds
waitmessage B_WAIT_TIME_LONG
setadditionaleffects @ only onChargeTurnOnly effects will work here
return
BattleScript_EffectSkyDrop::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2
attackcanceler
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2
ppreduce
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
@ -638,16 +601,10 @@ BattleScript_EffectSkyDrop::
BattleScript_SkyDropWork:
setskydrop
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKY_DROP
setsemiinvulnerablebit
call BattleScriptFirstChargingTurnAfterAttackString
call BattleScript_FirstChargingTurnAfterAttackString
goto BattleScript_MoveEnd
BattleScript_SkyDropTurn2:
attackcanceler
setbyte sB_ANIM_TURN, 0x1
clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING
orword gHitMarker, HITMARKER_NO_PPDEDUCT
clearsemiinvulnerablebit
call BattleScript_TwoTurnMovesSecondTurnRet
attackstring
clearskydrop BattleScript_SkyDropChangedTarget
jumpiftype BS_TARGET, TYPE_FLYING, BattleScript_SkyDropFlyingType
@ -3485,29 +3442,6 @@ BattleScript_KOFail::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_TwoTurnMovesSecondTurn::
attackcanceler
setbyte sB_ANIM_TURN, 1
clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING
orword gHitMarker, HITMARKER_NO_PPDEDUCT
goto BattleScript_HitFromAccCheck
BattleScriptFirstChargingTurn::
attackcanceler
flushtextbox
ppreduce
attackstring
BattleScriptFirstChargingTurnAfterAttackString:
pause B_WAIT_TIME_LONG
copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID
printfromtable gFirstTurnOfTwoStringIds
waitmessage B_WAIT_TIME_LONG
attackanimation
waitanimation
orword gHitMarker, HITMARKER_CHARGING
seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER
return
BattleScript_EffectSuperFang::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
@ -3784,34 +3718,16 @@ BattleScript_PowerHerbActivation:
BattleScript_EffectTwoTurnsAttack::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn
jumpifmove MOVE_SKY_ATTACK, BattleScript_EffectTwoTurnsAttackSkyAttack
jumpifmove MOVE_RAZOR_WIND, BattleScript_EffectTwoTurnsAttackRazorWind
jumpifmove MOVE_ICE_BURN, BattleScript_EffectTwoTurnsAttackIceBurn
jumpifmove MOVE_FREEZE_SHOCK, BattleScript_EffectTwoTurnsAttackFreezeShock
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND
BattleScript_EffectTwoTurnsAttackContinue:
call BattleScriptFirstChargingTurn
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd
call BattleScript_PowerHerbActivation
goto BattleScript_TwoTurnMovesSecondTurn
BattleScript_EffectTwoTurnsAttackSkyAttack:
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKY_ATTACK
goto BattleScript_EffectTwoTurnsAttackContinue
BattleScript_EffectTwoTurnsAttackRazorWind:
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND
goto BattleScript_EffectTwoTurnsAttackContinue
BattleScript_EffectTwoTurnsAttackIceBurn:
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND
goto BattleScript_EffectTwoTurnsAttackContinue
BattleScript_EffectTwoTurnsAttackFreezeShock:
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_FREEZE_SHOCK
goto BattleScript_EffectTwoTurnsAttackContinue
jumpifweathercheckchargeeffects BS_ATTACKER, TRUE, BattleScript_EffectHit
call BattleScript_FirstChargingTurn
jumpifweathercheckchargeeffects BS_ATTACKER, FALSE, BattleScript_TwoTurnMovesSecondTurn
jumpifholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_TwoTurnMovesSecondPowerHerbActivates
goto BattleScript_MoveEnd
BattleScript_EffectGeomancy::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_GeomancySecondTurn
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_GeomancySecondTurn
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_GEOMANCY
call BattleScriptFirstChargingTurn
call BattleScript_FirstChargingTurn
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd
call BattleScript_PowerHerbActivation
BattleScript_GeomancySecondTurn:
@ -3848,6 +3764,43 @@ BattleScript_GeomancyTrySpeed::
BattleScript_GeomancyEnd::
goto BattleScript_MoveEnd
BattleScript_FirstChargingTurn::
attackcanceler
.if B_UPDATED_MOVE_DATA >= GEN_5 @ before Gen 5, charge moves did not print an attack string on the charge turn
flushtextbox
attackstring
waitmessage B_WAIT_TIME_LONG
.endif
ppreduce
BattleScript_FirstChargingTurnAfterAttackString:
setsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP
orword gHitMarker, HITMARKER_CHARGING
seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER
twoturnmoveschargestringandanimation
setadditionaleffects @ only onChargeTurnOnly effects will work here
return
BattleScript_TwoTurnMovesSecondPowerHerbActivates:
call BattleScript_PowerHerbActivation
call BattleScript_TwoTurnMovesSecondTurnRet
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
.if B_UPDATED_MOVE_DATA < GEN_5 @ before Gen 5, charge moves did not print an attack string on the charge turn
attackstring
.endif
goto BattleScript_HitFromCritCalc
BattleScript_TwoTurnMovesSecondTurn::
attackcanceler
call BattleScript_TwoTurnMovesSecondTurnRet
orword gHitMarker, HITMARKER_NO_PPDEDUCT
goto BattleScript_HitFromAccCheck
BattleScript_TwoTurnMovesSecondTurnRet:
setbyte sB_ANIM_TURN, 1
clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING
clearsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP
return
BattleScript_EffectSubstitute::
attackcanceler
ppreduce
@ -4178,17 +4131,6 @@ BattleScript_PartyHealEnd::
waitstate
goto BattleScript_MoveEnd
BattleScript_EffectTripleKick::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
jumpifmove MOVE_TRIPLE_AXEL BS_TripleAxel
addbyte sTRIPLE_KICK_POWER, 10 @ triple kick gets +10 power
goto BattleScript_HitFromAtkString
BS_TripleAxel:
addbyte sTRIPLE_KICK_POWER, 20 @ triple axel gets +20 power
goto BattleScript_HitFromAtkString
BattleScript_EffectMeanLook::
attackcanceler
attackstring
@ -4654,23 +4596,6 @@ BattleScript_EffectMirrorCoat::
adjustdamage
goto BattleScript_HitFromAtkAnimation
BattleScript_EffectSkullBash::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKULL_BASH
call BattleScriptFirstChargingTurn
setstatchanger STAT_DEF, 1, FALSE
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_SkullBashEnd
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_SkullBashEnd
setgraphicalstatchangevalues
playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
printfromtable gStatUpStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_SkullBashEnd::
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd
call BattleScript_PowerHerbActivation
goto BattleScript_TwoTurnMovesSecondTurn
BattleScript_EffectFutureSight::
attackcanceler
attackstring
@ -4682,25 +4607,6 @@ BattleScript_EffectFutureSight::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectGust::
goto BattleScript_EffectHit
BattleScript_EffectSolarBeam::
jumpifweatheraffected BS_ATTACKER, B_WEATHER_SUN, BattleScript_SolarBeamOnFirstTurn
BattleScript_SolarBeamDecideTurn::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SOLAR_BEAM
call BattleScriptFirstChargingTurn
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd
call BattleScript_PowerHerbActivation
goto BattleScript_TwoTurnMovesSecondTurn
BattleScript_SolarBeamOnFirstTurn::
orword gHitMarker, HITMARKER_CHARGING
seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER
ppreduce
goto BattleScript_TwoTurnMovesSecondTurn
BattleScript_EffectTeleport::
.if B_TELEPORT_BEHAVIOR >= GEN_7
jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_EffectBatonPass
@ -4765,46 +4671,6 @@ BattleScript_BeatUpEnd::
end
.endif
BattleScript_EffectSemiInvulnerable::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SecondTurnSemiInvulnerable
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_SecondTurnSemiInvulnerable
jumpifmove MOVE_FLY, BattleScript_FirstTurnFly
jumpifmove MOVE_DIVE, BattleScript_FirstTurnDive
jumpifmove MOVE_BOUNCE, BattleScript_FirstTurnBounce
jumpifmove MOVE_PHANTOM_FORCE, BattleScript_FirstTurnPhantomForce
jumpifmove MOVE_SHADOW_FORCE, BattleScript_FirstTurnPhantomForce
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_DIG
goto BattleScript_FirstTurnSemiInvulnerable
BattleScript_FirstTurnBounce::
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_BOUNCE
goto BattleScript_FirstTurnSemiInvulnerable
BattleScript_FirstTurnDive::
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_DIVE
goto BattleScript_FirstTurnSemiInvulnerable
BattleScript_FirstTurnPhantomForce:
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_PHANTOM_FORCE
goto BattleScript_FirstTurnSemiInvulnerable
BattleScript_FirstTurnFly::
setbyte sTWOTURN_STRINGID, B_MSG_TURN1_FLY
BattleScript_FirstTurnSemiInvulnerable::
call BattleScriptFirstChargingTurn
setsemiinvulnerablebit
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd
call BattleScript_PowerHerbActivation
BattleScript_SecondTurnSemiInvulnerable::
attackcanceler
setbyte sB_ANIM_TURN, 1
clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING
orword gHitMarker, HITMARKER_NO_PPDEDUCT
BattleScript_SemiInvulnerableTryHit::
accuracycheck BattleScript_SemiInvulnerableMiss, ACC_CURR_MOVE
clearsemiinvulnerablebit
goto BattleScript_HitFromAtkString
BattleScript_SemiInvulnerableMiss::
clearsemiinvulnerablebit
goto BattleScript_PrintMoveMissed
BattleScript_EffectDefenseCurl::
attackcanceler
attackstring
@ -5544,9 +5410,6 @@ BattleScript_CosmicPowerTrySpDef::
BattleScript_CosmicPowerEnd::
goto BattleScript_MoveEnd
BattleScript_EffectSkyUppercut::
goto BattleScript_EffectHit
BattleScript_EffectBulkUp::
attackcanceler
attackstring

View File

@ -54,7 +54,8 @@ struct __attribute__((packed, aligned(2))) BattleMoveEffect
const u8 *battleScript;
u16 battleTvScore:3;
u16 encourageEncore:1;
u16 flags:12; // coming soon...
u16 semiInvulnerableEffect:1;
u16 flags:11; // coming soon...
};
#define GET_MOVE_BATTLESCRIPT(move) gBattleMoveEffects[gMovesInfo[move].effect].battleScript
@ -846,10 +847,10 @@ struct BattleScripting
s32 bideDmg;
u8 multihitString[6];
bool8 expOnCatch;
u8 twoTurnsMoveStringId;
u8 unused;
u8 animArg1;
u8 animArg2;
u16 tripleKickPower;
u16 savedStringId;
u8 moveendState;
u8 savedStatChanger; // For further use, if attempting to change stat two times(ex. Moody)
u8 shiftSwitched; // When the game tells you the next enemy's pokemon and you switch. Option for noobs but oh well.

View File

@ -108,7 +108,7 @@ bool32 IsStatLoweringMoveEffect(u32 moveEffect);
bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility);
bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 IsHazardMoveEffect(u32 moveEffect);
bool32 IsChargingMove(u32 battlerAtk, u32 effect);
bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move);
void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score);
bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect);
bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect);

View File

@ -611,7 +611,6 @@ extern const u8 BattleScript_EffectSleepTalk[];
extern const u8 BattleScript_EffectDestinyBond[];
extern const u8 BattleScript_EffectSpite[];
extern const u8 BattleScript_EffectHealBell[];
extern const u8 BattleScript_EffectTripleKick[];
extern const u8 BattleScript_EffectMeanLook[];
extern const u8 BattleScript_EffectNightmare[];
extern const u8 BattleScript_EffectMinimize[];

View File

@ -225,6 +225,7 @@ bool32 MoveHasMoveEffect(u32 move, u32 moveEffect);
bool32 MoveHasMoveEffectWithChance(u32 move, u32 moveEffect, u32 chance);
bool32 MoveHasMoveEffectSelf(u32 move, u32 moveEffect);
bool32 MoveHasMoveEffectSelfArg(u32 move, u32 moveEffect, u32 argument);
bool32 MoveHasChargeTurnMoveEffect(u32 move);
bool32 CanSleep(u32 battler);
bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget);

View File

@ -117,10 +117,8 @@ enum {
EFFECT_BELLY_DRUM,
EFFECT_PSYCH_UP,
EFFECT_MIRROR_COAT,
EFFECT_SKULL_BASH,
EFFECT_EARTHQUAKE,
EFFECT_FUTURE_SIGHT,
EFFECT_GUST,
EFFECT_SOLAR_BEAM,
EFFECT_THUNDER,
EFFECT_TELEPORT,
@ -172,7 +170,6 @@ enum {
EFFECT_WEATHER_BALL,
EFFECT_TICKLE,
EFFECT_COSMIC_POWER,
EFFECT_SKY_UPPERCUT,
EFFECT_BULK_UP,
EFFECT_WATER_SPORT,
EFFECT_CALM_MIND,
@ -318,7 +315,6 @@ enum {
EFFECT_BOLT_BEAK,
EFFECT_SKY_DROP,
EFFECT_EXPANDING_FORCE,
EFFECT_METEOR_BEAM,
EFFECT_RISING_VOLTAGE,
EFFECT_BEAK_BLAST,
EFFECT_COURT_CHANGE,

View File

@ -6,10 +6,10 @@
#define sBIDE_DMG (gBattleScripting + 0x04) // bideDmg
#define sMULTIHIT_STRING (gBattleScripting + 0x08) // multihitString
#define sEXP_CATCH (gBattleScripting + 0x0E) // expOnCatch
#define sTWOTURN_STRINGID (gBattleScripting + 0x0F) // twoTurnsMoveStringId
#define sUNUSED (gBattleScripting + 0x0F) // unused
#define sB_ANIM_ARG1 (gBattleScripting + 0x10) // animArg1
#define sB_ANIM_ARG2 (gBattleScripting + 0x11) // animArg2
#define sTRIPLE_KICK_POWER (gBattleScripting + 0x12) // tripleKickPower
#define sSAVED_STRINID (gBattleScripting + 0x12) // savedStringId
#define sMOVEEND_STATE (gBattleScripting + 0x14) // moveendState
#define sSAVED_STAT_CHANGER (gBattleScripting + 0x15) // savedStatChanger
#define sSHIFT_SWITCHED (gBattleScripting + 0x16) // shiftSwitched
@ -181,7 +181,7 @@
#define VARIOUS_TRY_FAIRY_LOCK 89
#define VARIOUS_JUMP_IF_NO_ALLY 90
#define VARIOUS_POISON_TYPE_IMMUNITY 91
#define VARIOUS_JUMP_IF_NO_HOLD_EFFECT 92
#define VARIOUS_JUMP_IF_HOLD_EFFECT 92
#define VARIOUS_INFATUATE_WITH_BATTLER 93
#define VARIOUS_SET_LAST_USED_ITEM 94
#define VARIOUS_PARALYZE_TYPE_IMMUNITY 95
@ -220,25 +220,24 @@
#define VARIOUS_SET_SKY_DROP 128
#define VARIOUS_CLEAR_SKY_DROP 129
#define VARIOUS_SKY_DROP_YAWN 130
#define VARIOUS_JUMP_IF_HOLD_EFFECT 131
#define VARIOUS_CURE_CERTAIN_STATUSES 132
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 133
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 134
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 135
#define VARIOUS_SAVE_BATTLER_ITEM 136
#define VARIOUS_RESTORE_BATTLER_ITEM 137
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 138
#define VARIOUS_SET_BEAK_BLAST 139
#define VARIOUS_SWAP_SIDE_STATUSES 140
#define VARIOUS_SWAP_STATS 141
#define VARIOUS_TEATIME_INVUL 142
#define VARIOUS_TEATIME_TARGETS 143
#define VARIOUS_TRY_WIND_RIDER_POWER 144
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 145
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 146
#define VARIOUS_STORE_HEALING_WISH 147
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 148
#define VARIOUS_TRY_REVIVAL_BLESSING 149
#define VARIOUS_CURE_CERTAIN_STATUSES 131
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 132
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 133
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 134
#define VARIOUS_SAVE_BATTLER_ITEM 135
#define VARIOUS_RESTORE_BATTLER_ITEM 136
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 137
#define VARIOUS_SET_BEAK_BLAST 138
#define VARIOUS_SWAP_SIDE_STATUSES 139
#define VARIOUS_SWAP_STATS 140
#define VARIOUS_TEATIME_INVUL 141
#define VARIOUS_TEATIME_TARGETS 142
#define VARIOUS_TRY_WIND_RIDER_POWER 143
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 144
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 145
#define VARIOUS_STORE_HEALING_WISH 146
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 147
#define VARIOUS_TRY_REVIVAL_BLESSING 148
// Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0

View File

@ -697,13 +697,14 @@
#define STRINGID_THESWAMPDISAPPEARED 695
#define STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 696
#define STRINGID_HOSPITALITYRESTORATION 697
#define STRINGID_ELECTROSHOCKCHARGING 698
#define STRINGID_ELECTROSHOTCHARGING 698
#define STRINGID_ITEMWASUSEDUP 699
#define STRINGID_ATTACKERLOSTITSTYPE 700
#define STRINGID_SHEDITSTAIL 701
#define STRINGID_SUPERSWEETAROMAWAFTS 702
#define STRINGID_CLOAKEDINAHARSHLIGHT 702
#define STRINGID_SUPERSWEETAROMAWAFTS 703
#define BATTLESTRINGS_COUNT 703
#define BATTLESTRINGS_COUNT 704
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
@ -747,22 +748,6 @@
#define B_MSG_LEECH_SEED_DRAIN 3
#define B_MSG_LEECH_SEED_OOZE 4
// gFirstTurnOfTwoStringIds
#define B_MSG_TURN1_RAZOR_WIND 0
#define B_MSG_TURN1_SOLAR_BEAM 1
#define B_MSG_TURN1_SKULL_BASH 2
#define B_MSG_TURN1_SKY_ATTACK 3
#define B_MSG_TURN1_FLY 4
#define B_MSG_TURN1_DIG 5
#define B_MSG_TURN1_DIVE 6
#define B_MSG_TURN1_BOUNCE 7
#define B_MSG_TURN1_PHANTOM_FORCE 8
#define B_MSG_TURN1_GEOMANCY 9
#define B_MSG_TURN1_FREEZE_SHOCK 10
#define B_MSG_TURN1_SKY_DROP 11
#define B_MSG_TURN1_METEOR_BEAM 12
#define B_MSG_TURN1_ELECTRO_SHOCK 13
// gMoveWeatherChangeStringIds
#define B_MSG_STARTED_RAIN 0
#define B_MSG_STARTED_DOWNPOUR 1

View File

@ -83,6 +83,62 @@
// Extracts the lower 16 bits of a 32-bit number
#define LOHALF(n) ((n) & 0xFFFF)
/* (Credit to MGriffin) A rather monstrous way of finding the set bit in a word.
Invalid input causes a compiler error. Sample: https://cexplore.karathan.at/z/x1hm7B */
#define BIT_INDEX(n) \
(n) == (1 << 0) ? 0 : \
(n) == (1 << 1) ? 1 : \
(n) == (1 << 2) ? 2 : \
(n) == (1 << 3) ? 3 : \
(n) == (1 << 4) ? 4 : \
(n) == (1 << 5) ? 5 : \
(n) == (1 << 6) ? 6 : \
(n) == (1 << 7) ? 7 : \
(n) == (1 << 8) ? 8 : \
(n) == (1 << 9) ? 9 : \
(n) == (1 << 10) ? 10 : \
(n) == (1 << 11) ? 11 : \
(n) == (1 << 12) ? 12 : \
(n) == (1 << 13) ? 13 : \
(n) == (1 << 14) ? 14 : \
(n) == (1 << 15) ? 15 : \
(n) == (1 << 16) ? 16 : \
(n) == (1 << 17) ? 17 : \
(n) == (1 << 18) ? 18 : \
(n) == (1 << 19) ? 19 : \
(n) == (1 << 20) ? 20 : \
(n) == (1 << 21) ? 21 : \
(n) == (1 << 22) ? 22 : \
(n) == (1 << 23) ? 23 : \
(n) == (1 << 24) ? 24 : \
(n) == (1 << 25) ? 25 : \
(n) == (1 << 26) ? 26 : \
(n) == (1 << 27) ? 27 : \
(n) == (1 << 28) ? 28 : \
(n) == (1 << 29) ? 29 : \
(n) == (1 << 30) ? 30 : \
(n) == (1 << 31) ? 31 : \
*(u32 *)NULL
#define COMPRESS_BITS_0 0, 1
#define COMPRESS_BITS_1 1, 1
#define COMPRESS_BITS_2 2, 1
#define COMPRESS_BITS_3 3, 1
#define COMPRESS_BITS_4 4, 1
#define COMPRESS_BITS_5 5, 1
#define COMPRESS_BITS_6 6, 1
#define COMPRESS_BITS_7 7, 1
/* Will try and compress a set bit (or up to three sequential bits) into a single byte
Input must be of the form (upper << lower) where upper can be up to 3, lower up to 31 */
#define COMPRESS_BITS(_val) COMPRESS_BITS_STEP_2 _val
#define COMPRESS_BITS_STEP_2(_unpacked) COMPRESS_BITS_STEP_3(COMPRESS_BITS_## _unpacked)
#define COMPRESS_BITS_STEP_3(...) COMPRESS_BITS_STEP_4(__VA_ARGS__)
#define COMPRESS_BITS_STEP_4(upper, lower) (((upper % 8) << 5) + (BIT_INDEX(lower)))
/* Will read a compressed bit stored by COMPRESS_BIT into a single byte */
#define UNCOMPRESS_BITS(compressed) ((compressed >> 5) << (compressed & 0x1F))
// There are many quirks in the source code which have overarching behavioral differences from
// a number of other files. For example, diploma.c seems to declare rodata before each use while
// other files declare out of order and must be at the beginning. There are also a number of

View File

@ -813,7 +813,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move
if (IsChargingMove(battlerAtk, moveEffect) && CanTargetFaintAi(battlerDef, battlerAtk))
if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk))
RETURN_SCORE_MINUS(10);
// check if negates type
@ -3133,7 +3133,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
s32 score = 0;
s32 leastHits = 1000;
u16 *moves = GetMovesArray(battlerAtk);
bool8 isChargingMoveEffect[MAX_MON_MOVES];
bool8 isTwoTurnNotSemiInvulnerableMove[MAX_MON_MOVES];
for (i = 0; i < MAX_MON_MOVES; i++)
{
@ -3145,13 +3145,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
leastHits = noOfHits[i];
}
viableMoveScores[i] = AI_SCORE_DEFAULT;
isChargingMoveEffect[i] = IsChargingMove(battlerAtk, gMovesInfo[moves[i]].effect);
isTwoTurnNotSemiInvulnerableMove[i] = IsTwoTurnNotSemiInvulnerableMove(battlerAtk, moves[i]);
}
else
{
noOfHits[i] = -1;
viableMoveScores[i] = 0;
isChargingMoveEffect[i] = FALSE;
isTwoTurnNotSemiInvulnerableMove[i] = FALSE;
}
/*
MgbaPrintf_("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]);
@ -3175,9 +3175,9 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
{
multipleBestMoves = TRUE;
// We need to make sure it's the current move which is objectively better.
if (isChargingMoveEffect[i] && !isChargingMoveEffect[currId])
if (isTwoTurnNotSemiInvulnerableMove[i] && !isTwoTurnNotSemiInvulnerableMove[currId])
viableMoveScores[i] -= 3;
else if (!isChargingMoveEffect[i] && isChargingMoveEffect[currId])
else if (!isTwoTurnNotSemiInvulnerableMove[i] && isTwoTurnNotSemiInvulnerableMove[currId])
viableMoveScores[currId] -= 3;
switch (CompareMoveAccuracies(battlerAtk, battlerDef, currId, i))

View File

@ -2016,17 +2016,14 @@ bool32 HasSnatchAffectedMove(u32 battler)
CHECK_MOVE_FLAG(snatchAffected);
}
bool32 IsChargingMove(u32 battlerAtk, u32 effect)
bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move)
{
switch (effect)
switch (gMovesInfo[move].effect)
{
case EFFECT_SOLAR_BEAM:
if (AI_GetWeather(AI_DATA) & B_WEATHER_SUN)
return FALSE;
case EFFECT_SKULL_BASH:
case EFFECT_METEOR_BEAM:
case EFFECT_TWO_TURNS_ATTACK:
return !(AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB);
return !(AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB
|| (AI_GetWeather(AI_DATA) & gMovesInfo[move].argument));
default:
return FALSE;
}

View File

@ -151,6 +151,7 @@ static const u8 sText_PkmnWhippedWhirlwind[] = _("{B_ATK_NAME_WITH_PREFIX} whipp
static const u8 sText_PkmnTookSunlight[] = _("{B_ATK_NAME_WITH_PREFIX} took\nin sunlight!");
static const u8 sText_PkmnLoweredHead[] = _("{B_ATK_NAME_WITH_PREFIX} lowered\nits head!");
static const u8 sText_PkmnIsGlowing[] = _("{B_ATK_NAME_WITH_PREFIX} is glowing!");
static const u8 sText_PkmnIsCloakedInAHarshLight[] = _("{B_ATK_NAME_WITH_PREFIX} became\ncloaked in a harsh light!");
static const u8 sText_PkmnFlewHigh[] = _("{B_ATK_NAME_WITH_PREFIX} flew\nup high!");
static const u8 sText_PkmnDugHole[] = _("{B_ATK_NAME_WITH_PREFIX} dug a hole!");
static const u8 sText_PkmnHidUnderwater[] = _("{B_ATK_NAME_WITH_PREFIX} hid\nunderwater!");
@ -834,7 +835,7 @@ static const u8 sText_TheSeaOfFireDisappeared[] = _("The sea of fire around {B_A
static const u8 sText_SwampEnvelopedSide[] = _("A swamp enveloped\n{B_DEF_TEAM2} team!");
static const u8 sText_TheSwampDisappeared[] = _("The swamp around {B_ATK_TEAM2}\nteam disappeared!");
static const u8 sText_HospitalityRestoration[] = _("The {B_ATK_PARTNER_NAME} drank down all\nthe matcha that Sinistcha made!");
static const u8 sText_ElectroShockCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorbed\nelectricity!");
static const u8 sText_ElectroShotCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorbed\nelectricity!");
static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up...");
static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!");
static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!");
@ -844,7 +845,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
[STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts,
[STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail,
[STRINGID_ELECTROSHOCKCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShockCharging,
[STRINGID_ELECTROSHOTCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShotCharging,
[STRINGID_HOSPITALITYRESTORATION - BATTLESTRINGS_TABLE_START] = sText_HospitalityRestoration,
[STRINGID_THESWAMPDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSwampDisappeared,
[STRINGID_SWAMPENVELOPEDSIDE - BATTLESTRINGS_TABLE_START] = sText_SwampEnvelopedSide,
@ -1533,6 +1534,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP - BATTLESTRINGS_TABLE_START] = sText_TargetCoveredInStickyCandySyrup,
[STRINGID_ITEMWASUSEDUP - BATTLESTRINGS_TABLE_START] = sText_ItemWasUsedUp,
[STRINGID_ATTACKERLOSTITSTYPE - BATTLESTRINGS_TABLE_START] = sText_AttackerLostItsType,
[STRINGID_CLOAKEDINAHARSHLIGHT - BATTLESTRINGS_TABLE_START] = sText_PkmnIsCloakedInAHarshLight,
};
const u16 gTrainerUsedItemStringIds[] =
@ -1758,25 +1760,6 @@ const u16 gStatDownStringIds[] =
[B_MSG_STAT_FELL_EMPTY] = STRINGID_EMPTYSTRING3,
};
// Index read from sTWOTURN_STRINGID
const u16 gFirstTurnOfTwoStringIds[] =
{
[B_MSG_TURN1_RAZOR_WIND] = STRINGID_PKMNWHIPPEDWHIRLWIND,
[B_MSG_TURN1_SOLAR_BEAM] = STRINGID_PKMNTOOKSUNLIGHT,
[B_MSG_TURN1_SKULL_BASH] = STRINGID_PKMNLOWEREDHEAD,
[B_MSG_TURN1_SKY_ATTACK] = STRINGID_PKMNISGLOWING,
[B_MSG_TURN1_FLY] = STRINGID_PKMNFLEWHIGH,
[B_MSG_TURN1_DIG] = STRINGID_PKMNDUGHOLE,
[B_MSG_TURN1_DIVE] = STRINGID_PKMNHIDUNDERWATER,
[B_MSG_TURN1_BOUNCE] = STRINGID_PKMNSPRANGUP,
[B_MSG_TURN1_PHANTOM_FORCE] = STRINGID_VANISHEDINSTANTLY,
[B_MSG_TURN1_GEOMANCY] = STRINGID_PKNMABSORBINGPOWER,
[B_MSG_TURN1_FREEZE_SHOCK] = STRINGID_CLOAKEDINAFREEZINGLIGHT,
[B_MSG_TURN1_SKY_DROP] = STRINGID_PKMNTOOKTARGETHIGH,
[B_MSG_TURN1_METEOR_BEAM] = STRINGID_METEORBEAMCHARGING,
[B_MSG_TURN1_ELECTRO_SHOCK] = STRINGID_ELECTROSHOCKCHARGING,
};
// Index copied from move's index in sTrappingMoves
const u16 gWrappedStringIds[NUM_TRAPPING_MOVES] =
{

View File

@ -498,7 +498,7 @@ static void Cmd_setdrainedhp(void);
static void Cmd_statbuffchange(void);
static void Cmd_normalisebuffs(void);
static void Cmd_setbide(void);
static void Cmd_unused0x8C(void);
static void Cmd_twoturnmoveschargestringandanimation(void);
static void Cmd_setmultihitcounter(void);
static void Cmd_initmultihitstring(void);
static void Cmd_forcerandomswitch(void);
@ -556,7 +556,7 @@ static void Cmd_selectfirstvalidtarget(void);
static void Cmd_trysetfutureattack(void);
static void Cmd_trydobeatup(void);
static void Cmd_setsemiinvulnerablebit(void);
static void Cmd_clearsemiinvulnerablebit(void);
static void Cmd_jumpifweathercheckchargeeffects(void);
static void Cmd_setminimize(void);
static void Cmd_sethail(void);
static void Cmd_trymemento(void);
@ -757,7 +757,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_statbuffchange, //0x89
Cmd_normalisebuffs, //0x8A
Cmd_setbide, //0x8B
Cmd_unused0x8C, //0x8C
Cmd_twoturnmoveschargestringandanimation, //0x8C
Cmd_setmultihitcounter, //0x8D
Cmd_initmultihitstring, //0x8E
Cmd_forcerandomswitch, //0x8F
@ -815,7 +815,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_trysetfutureattack, //0xC3
Cmd_trydobeatup, //0xC4
Cmd_setsemiinvulnerablebit, //0xC5
Cmd_clearsemiinvulnerablebit, //0xC6
Cmd_jumpifweathercheckchargeeffects, //0xC6
Cmd_setminimize, //0xC7
Cmd_sethail, //0xC8
Cmd_trymemento, //0xC9
@ -845,7 +845,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_unused2, //0xE1
Cmd_switchoutabilities, //0xE2
Cmd_jumpifhasnohp, //0xE3
Cmd_jumpifnotcurrentmoveargtype, //0xE4
Cmd_jumpifnotcurrentmoveargtype, //0xE4
Cmd_pickup, //0xE5
Cmd_unused3, //0xE6
Cmd_unused4, //0xE7
@ -2622,7 +2622,7 @@ static void Cmd_printstring(void)
if (gBattleControllerExecFlags == 0)
{
u16 id = cmd->id;
u16 id = (cmd->id == 0 ? gBattleScripting.savedStringId : cmd->id);
gBattlescriptCurrInstr = cmd->nextInstr;
PrepareStringBattle(id, gBattlerAttacker);
@ -8694,16 +8694,19 @@ static void Cmd_various(void)
}
return;
}
case VARIOUS_JUMP_IF_NO_HOLD_EFFECT:
case VARIOUS_JUMP_IF_HOLD_EFFECT:
{
VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr);
if (GetBattlerHoldEffect(battler, TRUE) != cmd->holdEffect)
VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr, u8 equal);
if ((GetBattlerHoldEffect(battler, TRUE) == cmd->holdEffect) == cmd->equal)
{
if (cmd->equal)
gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else
{
gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM
if (!cmd->equal)
gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM
gBattlescriptCurrInstr = cmd->nextInstr;
}
return;
@ -11690,8 +11693,15 @@ static void Cmd_setbide(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_unused0x8C(void)
static void Cmd_twoturnmoveschargestringandanimation(void)
{
CMD_ARGS(const u8 *animationThenStringPtr);
gBattleScripting.savedStringId = LOHALF(gMovesInfo[gCurrentMove].argument);
if (B_UPDATED_MOVE_DATA < GEN_5 || MoveHasChargeTurnMoveEffect(gCurrentMove))
gBattlescriptCurrInstr = cmd->animationThenStringPtr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_setmultihitcounter(void)
@ -13646,36 +13656,36 @@ static void Cmd_trydobeatup(void)
static void Cmd_setsemiinvulnerablebit(void)
{
CMD_ARGS();
CMD_ARGS(bool8 clear);
switch (gCurrentMove)
if (gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].semiInvulnerableEffect == TRUE)
{
case MOVE_FLY:
case MOVE_BOUNCE:
case MOVE_SKY_DROP:
gStatuses3[gBattlerAttacker] |= STATUS3_ON_AIR;
break;
case MOVE_DIG:
gStatuses3[gBattlerAttacker] |= STATUS3_UNDERGROUND;
break;
case MOVE_DIVE:
gStatuses3[gBattlerAttacker] |= STATUS3_UNDERWATER;
break;
case MOVE_PHANTOM_FORCE:
case MOVE_SHADOW_FORCE:
gStatuses3[gBattlerAttacker] |= STATUS3_PHANTOM_FORCE;
break;
u32 semiInvulnerableEffect = UNCOMPRESS_BITS(HIHALF(gMovesInfo[gCurrentMove].argument));
if (cmd->clear)
gStatuses3[gBattlerAttacker] &= ~semiInvulnerableEffect;
else
gStatuses3[gBattlerAttacker] |= semiInvulnerableEffect;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_clearsemiinvulnerablebit(void)
static void Cmd_jumpifweathercheckchargeeffects(void)
{
CMD_ARGS();
CMD_ARGS(u8 battler, bool8 checkChargeTurnEffects, const u8 *jumpInstr);
gStatuses3[gBattlerAttacker] &= ~STATUS3_SEMI_INVULNERABLE;
gBattlescriptCurrInstr = cmd->nextInstr;
/* If this is NOT semi-invulnerable move and we don't have charge turn effects
yet to fire, we can fire the move right away so long as the weather matches
the argument and the battler is affected by it (not blocked by Cloud Nine etc) */
if (gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].semiInvulnerableEffect == FALSE
&& !(cmd->checkChargeTurnEffects && MoveHasChargeTurnMoveEffect(gCurrentMove))
&& IsBattlerWeatherAffected(cmd->battler, HIHALF(gMovesInfo[gCurrentMove].argument)))
{
gBattleScripting.animTurn = 1;
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_setminimize(void)
@ -15552,23 +15562,6 @@ void BS_JumpIfMoreThanHalfHP(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfHoldEffect(void)
{
u8 battler = gBattlescriptCurrInstr[5];
u16 holdEffect = T1_READ_16(gBattlescriptCurrInstr + 6);
if (GetBattlerHoldEffect(battler, TRUE) == holdEffect)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 8);
}
else
{
RecordItemEffectBattle(battler, holdEffect);
gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM
gBattlescriptCurrInstr += 12;
}
}
void BS_DoStockpileStatChangesWearOff(void)
{
NATIVE_ARGS(u8 battler, const u8 *statChangeInstr);

View File

@ -3192,7 +3192,6 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_DESTINY_BOND;
gStatuses3[gBattlerAttacker] &= ~STATUS3_GRUDGE;
gStatuses4[gBattlerAttacker] &= ~ STATUS4_GLAIVE_RUSH;
gBattleScripting.tripleKickPower = 0;
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_SKY_DROP:
@ -11050,6 +11049,17 @@ bool32 MoveHasMoveEffectSelfArg(u32 move, u32 moveEffect, u32 argument)
return (gMovesInfo[move].argument == argument) && MoveHasMoveEffectSelf(move, moveEffect);
}
bool32 MoveHasChargeTurnMoveEffect(u32 move)
{
u8 i = 0;
for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++)
{
if (gMovesInfo[move].additionalEffects[i].onChargeTurnOnly)
return TRUE;
}
return FALSE;
}
bool8 CanMonParticipateInSkyBattle(struct Pokemon *mon)
{
u16 species = GetMonData(mon, MON_DATA_SPECIES);

View File

@ -383,7 +383,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_TWO_TURNS_ATTACK] =
{
.battleScript = BattleScript_EffectTwoTurnsAttack,
.battleTvScore = 0, // TODO: Assign points
.battleTvScore = 3,
},
[EFFECT_SUBSTITUTE] =
@ -527,7 +527,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_TRIPLE_KICK] =
{
.battleScript = BattleScript_EffectTripleKick,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 1,
},
@ -748,12 +748,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.encourageEncore = TRUE,
},
[EFFECT_SKULL_BASH] =
{
.battleScript = BattleScript_EffectSkullBash,
.battleTvScore = 3,
},
[EFFECT_EARTHQUAKE] =
{
.battleScript = BattleScript_EffectHit,
@ -767,15 +761,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.encourageEncore = TRUE,
},
[EFFECT_GUST] =
{
.battleScript = BattleScript_EffectGust,
.battleTvScore = 1,
},
[EFFECT_SOLAR_BEAM] =
{
.battleScript = BattleScript_EffectSolarBeam,
.battleScript = BattleScript_EffectTwoTurnsAttack,
.battleTvScore = 1,
},
@ -799,8 +787,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_SEMI_INVULNERABLE] =
{
.battleScript = BattleScript_EffectSemiInvulnerable,
.battleScript = BattleScript_EffectTwoTurnsAttack,
.battleTvScore = 3,
.semiInvulnerableEffect = TRUE,
},
[EFFECT_DEFENSE_CURL] =
@ -1101,12 +1090,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.encourageEncore = TRUE,
},
[EFFECT_SKY_UPPERCUT] =
{
.battleScript = BattleScript_EffectSkyUppercut,
.battleTvScore = 1,
},
[EFFECT_BULK_UP] =
{
.battleScript = BattleScript_EffectBulkUp,
@ -2016,6 +1999,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
{
.battleScript = BattleScript_EffectSkyDrop,
.battleTvScore = 0, // TODO: Assign points
.semiInvulnerableEffect = TRUE,
},
[EFFECT_EXPANDING_FORCE] =
@ -2024,12 +2008,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_METEOR_BEAM] =
{
.battleScript = BattleScript_EffectMeteorBeam,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_RISING_VOLTAGE] =
{
.battleScript = BattleScript_EffectHit,

View File

@ -2,6 +2,7 @@
#include "constants/battle.h"
#include "constants/battle_move_effects.h"
#include "constants/battle_script_commands.h"
#include "constants/battle_string_ids.h"
#include "constants/battle_z_move_effects.h"
#include "constants/hold_effects.h"
#include "constants/moves.h"
@ -21,6 +22,11 @@
#define BINDING_TURNS "2 to 5"
#endif
/* First arg is the charge turn string id, second arg depends on effect
EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP: semi-invulnerable STATUS3 to apply to battler
EFFECT_TWO_TURNS_ATTACK/EFFECT_SOLAR_BEAM: weather in which to skip charge turn */
#define TWO_TURN_ARG(stringid, ...) (stringid) __VA_OPT__(| ((__VA_ARGS__) << 16))
// Shared Move Description entries
const u8 gNotDoneYetDescription[] = _(
@ -427,6 +433,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.windMove = B_EXTRAPOLATED_MOVE_FLAGS,
.argument = TWO_TURN_ARG(STRINGID_PKMNWHIPPEDWHIRLWIND),
.contestEffect = CONTEST_EFFECT_AFFECTED_BY_PREV_APPEAL,
.contestCategory = CONTEST_CATEGORY_COOL,
.contestComboStarterId = 0,
@ -484,7 +491,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"Strikes the foe with a gust\n"
"of wind whipped up by wings."),
.effect = EFFECT_GUST,
.effect = EFFECT_HIT,
.power = 40,
.type = TYPE_FLYING,
.accuracy = 100,
@ -569,6 +576,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.assistBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNFLEWHIGH, COMPRESS_BITS(STATUS3_ON_AIR)),
.contestEffect = CONTEST_EFFECT_AVOID_STARTLE,
.contestCategory = CONTEST_CATEGORY_SMART,
.contestComboStarterId = 0,
@ -1932,6 +1940,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.twoTurnMove = TRUE,
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNTOOKSUNLIGHT, B_WEATHER_SUN),
.contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING,
.contestCategory = CONTEST_CATEGORY_COOL,
.contestComboStarterId = 0,
@ -2291,6 +2300,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.instructBanned = TRUE,
.assistBanned = TRUE,
.skyBattleBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNDUGHOLE, COMPRESS_BITS(STATUS3_UNDERGROUND)),
.contestEffect = CONTEST_EFFECT_AVOID_STARTLE,
.contestCategory = CONTEST_CATEGORY_SMART,
.contestComboStarterId = 0,
@ -3221,7 +3231,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"Tucks in the head, then\n"
"attacks on the next turn."),
.effect = EFFECT_SKULL_BASH,
.effect = EFFECT_TWO_TURNS_ATTACK,
.power = B_UPDATED_MOVE_DATA >= GEN_6 ? 130 : 100,
.type = TYPE_NORMAL,
.accuracy = 100,
@ -3233,6 +3243,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.makesContact = TRUE,
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNLOWEREDHEAD),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_DEF_PLUS_1,
.self = TRUE,
.onChargeTurnOnly = TRUE,
}),
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_FRONT_MON,
.contestCategory = CONTEST_CATEGORY_TOUGH,
.contestComboStarterId = 0,
@ -3541,11 +3557,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_PHYSICAL,
.criticalHitStage = B_UPDATED_MOVE_DATA >= GEN_3 ? 1 : 0,
.criticalHitStage = B_UPDATED_MOVE_DATA >= GEN_3,
.twoTurnMove = TRUE,
.sheerForceBoost = TRUE,
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.argument = TWO_TURN_ARG(B_UPDATED_MOVE_DATA >= GEN_4 ? STRINGID_CLOAKEDINAHARSHLIGHT : STRINGID_PKMNISGLOWING),
#if B_UPDATED_MOVE_DATA >= GEN_3
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_FLINCH,
@ -7140,6 +7157,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.instructBanned = TRUE,
.assistBanned = TRUE,
.skyBattleBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNHIDUNDERWATER, COMPRESS_BITS(STATUS3_UNDERWATER)),
.contestEffect = CONTEST_EFFECT_AVOID_STARTLE_ONCE,
.contestCategory = CONTEST_CATEGORY_BEAUTY,
.contestComboStarterId = COMBO_STARTER_DIVE,
@ -8000,7 +8018,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"An uppercut thrown as if\n"
"leaping into the sky."),
.effect = EFFECT_SKY_UPPERCUT,
.effect = EFFECT_HIT,
.power = 85,
.type = TYPE_FIGHTING,
.accuracy = 90,
@ -8312,6 +8330,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.assistBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNSPRANGUP, COMPRESS_BITS(STATUS3_ON_AIR)),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_PARALYSIS,
.chance = 30,
@ -11319,6 +11338,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.assistBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_VANISHEDINSTANTLY, COMPRESS_BITS(STATUS3_PHANTOM_FORCE)),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_FEINT,
}),
@ -12252,6 +12272,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.assistBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNTOOKTARGETHIGH, COMPRESS_BITS(STATUS3_ON_AIR)),
.contestEffect = CONTEST_EFFECT_AVOID_STARTLE,
.contestCategory = CONTEST_CATEGORY_SMART,
.contestComboStarterId = 0,
@ -13318,6 +13339,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.metronomeBanned = TRUE,
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_CLOAKEDINAFREEZINGLIGHT),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_PARALYSIS,
.chance = 30,
@ -13347,6 +13369,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.metronomeBanned = TRUE,
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_CLOAKEDINAFREEZINGLIGHT),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_BURN,
.chance = 30,
@ -13648,6 +13671,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.assistBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_VANISHEDINSTANTLY, COMPRESS_BITS(STATUS3_PHANTOM_FORCE)),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_FEINT,
}),
@ -14475,6 +14499,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.skyBattleBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKNMABSORBINGPOWER),
.contestEffect = CONTEST_EFFECT_IMPROVE_CONDITION_PREVENT_NERVOUSNESS,
.contestCategory = CONTEST_CATEGORY_CUTE,
.contestComboStarterId = 0,
@ -15218,6 +15243,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.slicingMove = TRUE,
.sleepTalkBanned = TRUE,
.instructBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_PKMNTOOKSUNLIGHT, B_WEATHER_SUN),
.contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING,
.contestCategory = CONTEST_CATEGORY_TOUGH,
.contestComboStarterId = 0,
@ -17458,7 +17484,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"A 2-turn move that raises\n"
"Sp. Attack before attacking."),
.effect = EFFECT_METEOR_BEAM,
.effect = EFFECT_TWO_TURNS_ATTACK,
.power = 120,
.type = TYPE_ROCK,
.accuracy = 90,
@ -17468,6 +17494,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.category = DAMAGE_CATEGORY_SPECIAL,
.twoTurnMove = TRUE,
.instructBanned = TRUE,
.argument = TWO_TURN_ARG(STRINGID_METEORBEAMCHARGING),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1,
.self = TRUE,
@ -19714,7 +19741,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"Absorbs electricity in one turn,\n"
"then attacks next turn."),
.effect = EFFECT_METEOR_BEAM,
.effect = EFFECT_TWO_TURNS_ATTACK,
.power = 130,
.type = TYPE_ELECTRIC,
.accuracy = 100,
@ -19723,6 +19750,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.priority = 0,
.category = DAMAGE_CATEGORY_SPECIAL,
.sheerForceBoost = TRUE,
.argument = TWO_TURN_ARG(STRINGID_ELECTROSHOTCHARGING, B_WEATHER_RAIN),
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1,
.self = TRUE,

View File

@ -28,8 +28,7 @@ SINGLE_BATTLE_TEST("Sheer Force boosts power, but removes secondary effects of m
TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, move); }
else
TURN { MOVE(player, move); }
if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE
|| gMovesInfo[move].effect == EFFECT_METEOR_BEAM) {
if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE) {
TURN { SKIP_TURN(player); }
TURN { ; }
}

View File

@ -292,16 +292,39 @@ AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking
// Psychic and Solar Beam are chosen because user is holding Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; }
// Psychic and Skull Bash are chosen because user is holding Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SKULL_BASH; }
// Skull Bash is chosen because it's the most accurate and is holding Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_SKULL_BASH; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(5); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); Item(holdItemAtk); }
} WHEN {
TURN { if (expectedMove2 == MOVE_NONE) { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
else {EXPECT_MOVES(opponent, expectedMove, expectedMove2); SCORE_EQ(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1);}
}
}
SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect failing")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
u16 expectedMove, expectedMove2 = MOVE_NONE;
u16 abilityAtk = ABILITY_NONE, holdItemAtk = ITEM_NONE;
// Fiery Dance and Skull Bash are chosen because user is holding Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_FIERY_DANCE; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_FIERY_DANCE; expectedMove2 = MOVE_SKULL_BASH; }
// Crabhammer is chosen even if Skull Bash is more accurate, the user has no Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_CRABHAMMER;
expectedMove = MOVE_CRABHAMMER; }
KNOWN_FAILING;
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(5); }

View File

@ -1,61 +0,0 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_ELECTRO_SHOT].effect == EFFECT_METEOR_BEAM);
}
SINGLE_BATTLE_TEST("Electro Shot needs a charging Turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ELECTRO_SHOT); }
TURN { SKIP_TURN(player); }
} SCENE {
// Charging turn
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player);
MESSAGE("Wobbuffet absorbed electricity!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Sp. Atk rose!");
// Attack turn
MESSAGE("Wobbuffet used Electro Shot!");
}
}
SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge when it's raining")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_RAIN_DANCE); MOVE(player, MOVE_ELECTRO_SHOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player);
MESSAGE("Wobbuffet absorbed electricity!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Sp. Atk rose!");
MESSAGE("Wobbuffet used Electro Shot!");
}
}
SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge with Power Herb")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ELECTRO_SHOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player);
MESSAGE("Wobbuffet absorbed electricity!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Sp. Atk rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet became fully charged due to its Power Herb!");
MESSAGE("Wobbuffet used Electro Shot!");
}
}

View File

@ -0,0 +1,250 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_FLY].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_FLY].argument)) == STATUS3_ON_AIR);
ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_DIG].argument)) == STATUS3_UNDERGROUND);
ASSUME(gMovesInfo[MOVE_BOUNCE].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_BOUNCE].argument)) == STATUS3_ON_AIR);
ASSUME(gMovesInfo[MOVE_DIVE].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_DIVE].argument)) == STATUS3_UNDERWATER);
ASSUME(gMovesInfo[MOVE_PHANTOM_FORCE].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_PHANTOM_FORCE].argument)) == STATUS3_PHANTOM_FORCE);
ASSUME(gMovesInfo[MOVE_SHADOW_FORCE].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_SHADOW_FORCE].argument)) == STATUS3_PHANTOM_FORCE);
}
SINGLE_BATTLE_TEST("Semi-invulnerable moves make the user semi-invulnerable turn 1, then strike turn 2")
{
u16 move;
PARAMETRIZE { move = MOVE_FLY; }
PARAMETRIZE { move = MOVE_DIG; }
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIVE; }
PARAMETRIZE { move = MOVE_PHANTOM_FORCE; }
PARAMETRIZE { move = MOVE_SHADOW_FORCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); MOVE(opponent, MOVE_AERIAL_ACE); }
TURN { SKIP_TURN(player); }
} SCENE {
// Charging turn
if (B_UPDATED_MOVE_DATA >= GEN_5)
{
switch (move)
{
case MOVE_FLY:
NOT MESSAGE("Wobbuffet flew up high!");
MESSAGE("Wobbuffet used Fly!");
break;
case MOVE_DIG:
NOT MESSAGE("Wobbuffet dug a hole!");
MESSAGE("Wobbuffet used Dig!");
break;
case MOVE_BOUNCE:
NOT MESSAGE("Wobbuffet sprang up!");
MESSAGE("Wobbuffet used Bounce!");
break;
case MOVE_DIVE:
NOT MESSAGE("Wobbuffet hid underwater!");
MESSAGE("Wobbuffet used Dive!");
break;
case MOVE_PHANTOM_FORCE:
NOT MESSAGE("Wobbuffet vanished instantly!");
MESSAGE("Wobbuffet used PhantomForce!");
break;
case MOVE_SHADOW_FORCE:
NOT MESSAGE("Wobbuffet vanished instantly!");
MESSAGE("Wobbuffet used Shadow Force!");
break;
}
} else {
ANIMATION(ANIM_TYPE_MOVE, move, player);
}
if (B_UPDATED_MOVE_DATA < GEN_5)
{
switch (move)
{
case MOVE_FLY:
MESSAGE("Wobbuffet flew up high!");
break;
case MOVE_DIG:
MESSAGE("Wobbuffet dug a hole!");
break;
case MOVE_BOUNCE:
MESSAGE("Wobbuffet sprang up!");
break;
case MOVE_DIVE:
MESSAGE("Wobbuffet hid underwater!");
break;
case MOVE_PHANTOM_FORCE:
case MOVE_SHADOW_FORCE:
MESSAGE("Wobbuffet vanished instantly!");
break;
}
}
else
ANIMATION(ANIM_TYPE_MOVE, move, player);
// Aerial Ace cannot miss unless the target is semi-invulnerable
MESSAGE("Foe Wobbuffet used Aerial Ace!");
MESSAGE("Foe Wobbuffet's attack missed!");
// Attack turn
switch (move)
{
case MOVE_FLY:
MESSAGE("Wobbuffet used Fly!");
break;
case MOVE_DIG:
MESSAGE("Wobbuffet used Dig!");
break;
case MOVE_BOUNCE:
MESSAGE("Wobbuffet used Bounce!");
break;
case MOVE_DIVE:
MESSAGE("Wobbuffet used Dive!");
break;
case MOVE_PHANTOM_FORCE:
MESSAGE("Wobbuffet used PhantomForce!");
break;
case MOVE_SHADOW_FORCE:
MESSAGE("Wobbuffet used Shadow Force!");
break;
}
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Semi-invulnerable moves don't need to charge with Power Herb")
{
u16 move;
PARAMETRIZE { move = MOVE_FLY; }
PARAMETRIZE { move = MOVE_DIG; }
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIVE; }
PARAMETRIZE { move = MOVE_PHANTOM_FORCE; }
PARAMETRIZE { move = MOVE_SHADOW_FORCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
// Charging turn
if (B_UPDATED_MOVE_DATA >= GEN_5)
{
switch (move)
{
case MOVE_FLY:
NOT MESSAGE("Wobbuffet flew up high!");
MESSAGE("Wobbuffet used Fly!");
break;
case MOVE_DIG:
NOT MESSAGE("Wobbuffet dug a hole!");
MESSAGE("Wobbuffet used Dig!");
break;
case MOVE_BOUNCE:
NOT MESSAGE("Wobbuffet sprang up!");
MESSAGE("Wobbuffet used Bounce!");
break;
case MOVE_DIVE:
NOT MESSAGE("Wobbuffet hid underwater!");
MESSAGE("Wobbuffet used Dive!");
break;
case MOVE_PHANTOM_FORCE:
NOT MESSAGE("Wobbuffet vanished instantly!");
MESSAGE("Wobbuffet used PhantomForce!");
break;
case MOVE_SHADOW_FORCE:
NOT MESSAGE("Wobbuffet vanished instantly!");
MESSAGE("Wobbuffet used Shadow Force!");
break;
}
} else {
ANIMATION(ANIM_TYPE_MOVE, move, player);
}
if (B_UPDATED_MOVE_DATA < GEN_5)
{
switch (move)
{
case MOVE_FLY:
MESSAGE("Wobbuffet flew up high!");
break;
case MOVE_DIG:
MESSAGE("Wobbuffet dug a hole!");
break;
case MOVE_BOUNCE:
MESSAGE("Wobbuffet sprang up!");
break;
case MOVE_DIVE:
MESSAGE("Wobbuffet hid underwater!");
break;
case MOVE_PHANTOM_FORCE:
case MOVE_SHADOW_FORCE:
MESSAGE("Wobbuffet vanished instantly!");
break;
}
}
else
ANIMATION(ANIM_TYPE_MOVE, move, player);
MESSAGE("Wobbuffet became fully charged due to its Power Herb!");
if (B_UPDATED_MOVE_DATA < GEN_5)
{
switch (move)
{
case MOVE_FLY:
MESSAGE("Wobbuffet used Fly!");
break;
case MOVE_DIG:
MESSAGE("Wobbuffet used Dig!");
break;
case MOVE_BOUNCE:
MESSAGE("Wobbuffet used Bounce!");
break;
case MOVE_DIVE:
MESSAGE("Wobbuffet used Dive!");
break;
case MOVE_PHANTOM_FORCE:
MESSAGE("Wobbuffet used PhantomForce!");
break;
case MOVE_SHADOW_FORCE:
MESSAGE("Wobbuffet used Shadow Force!");
break;
}
}
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent);
}
}
// No way to apply this test with Shadow Force/Phantom Force
SINGLE_BATTLE_TEST("Semi-invulnerable moves apply a status that won't block certain moves")
{
u16 move, opMove;
PARAMETRIZE { move = MOVE_FLY; opMove = MOVE_SKY_UPPERCUT; }
PARAMETRIZE { move = MOVE_DIG; opMove = MOVE_EARTHQUAKE; }
PARAMETRIZE { move = MOVE_BOUNCE; opMove = MOVE_THUNDER; }
PARAMETRIZE { move = MOVE_DIVE; opMove = MOVE_SURF; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); MOVE(opponent, opMove); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, opMove, opponent);
HP_BAR(player);
}
}

View File

@ -1,167 +0,0 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM);
ASSUME(gMovesInfo[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM);
}
SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight")
{
u32 move;
PARAMETRIZE { move = MOVE_SOLAR_BEAM; }
PARAMETRIZE { move = MOVE_SOLAR_BLADE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, move); }
} SCENE {
NOT MESSAGE("Wobbuffet took in sunlight!");
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_RAIN_DANCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_RAIN_DANCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SANDSTORM; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SANDSTORM; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_HAIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_HAIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}

View File

@ -0,0 +1,449 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_RAZOR_WIND].effect == EFFECT_TWO_TURNS_ATTACK);
ASSUME(gMovesInfo[MOVE_SKULL_BASH].effect == EFFECT_TWO_TURNS_ATTACK);
ASSUME(MoveHasMoveEffectSelf(MOVE_SKULL_BASH, MOVE_EFFECT_DEF_PLUS_1) == TRUE);
ASSUME(gMovesInfo[MOVE_SKY_ATTACK].effect == EFFECT_TWO_TURNS_ATTACK);
// Solar Beam - check for sun
ASSUME(gMovesInfo[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM);
ASSUME(HIHALF(gMovesInfo[MOVE_SOLAR_BLADE].argument) == B_WEATHER_SUN);
ASSUME(gMovesInfo[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM);
ASSUME(HIHALF(gMovesInfo[MOVE_SOLAR_BLADE].argument) == B_WEATHER_SUN);
// Electro shot - check for rain
ASSUME(HIHALF(gMovesInfo[MOVE_ELECTRO_SHOT].argument) == B_WEATHER_RAIN);
ASSUME(gMovesInfo[MOVE_ELECTRO_SHOT].effect == EFFECT_TWO_TURNS_ATTACK);
ASSUME(MoveHasMoveEffectSelf(MOVE_ELECTRO_SHOT, MOVE_EFFECT_SP_ATK_PLUS_1) == TRUE);
}
SINGLE_BATTLE_TEST("Razor Wind needs a charging turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_RAZOR_WIND); }
TURN { SKIP_TURN(player); }
} SCENE {
// Charging turn
if (B_UPDATED_MOVE_DATA >= GEN_5) {
NOT MESSAGE("Wobbuffet whipped up a whirlwind!");
MESSAGE("Wobbuffet used Razor Wind!");
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player);
}
if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet whipped up a whirlwind!");
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player);
// Attack turn
MESSAGE("Wobbuffet used Razor Wind!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Razor Wind doesn't need to charge with Power Herb")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_RAZOR_WIND); }
} SCENE {
if (B_UPDATED_MOVE_DATA >= GEN_5) {
NOT MESSAGE("Wobbuffet whipped up a whirlwind!");
MESSAGE("Wobbuffet used Razor Wind!");
} else
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player);
if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet whipped up a whirlwind!");
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet became fully charged due to its Power Herb!");
if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet used Razor Wind!");
// For some reason, this breaks with and only with Razor Wind...
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Skull Bash needs a charging turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SKULL_BASH); }
TURN { SKIP_TURN(player); }
} SCENE {
// Charging turn
if (B_UPDATED_MOVE_DATA >= GEN_5) {
NOT MESSAGE("Wobbuffet lowered its head!");
MESSAGE("Wobbuffet used Skull Bash!");
} else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player);
if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet lowered its head!");
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Defense rose!");
// Attack turn
MESSAGE("Wobbuffet used Skull Bash!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Skull Bash doesn't need to charge with Power Herb")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SKULL_BASH); }
} SCENE {
if (B_UPDATED_MOVE_DATA >= GEN_5) {
NOT MESSAGE("Wobbuffet lowered its head!");
MESSAGE("Wobbuffet used Skull Bash!");
} else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player);
if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet lowered its head!");
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Defense rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet became fully charged due to its Power Herb!");
if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet used Skull Bash!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Sky Attack needs a charging turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SKY_ATTACK); }
TURN { SKIP_TURN(player); }
} SCENE {
// Charging turn
if (B_UPDATED_MOVE_DATA >= GEN_5) {
NONE_OF {
MESSAGE("Wobbuffet became cloaked in a harsh light!");
MESSAGE("Wobbuffet is glowing!");
}
MESSAGE("Wobbuffet used Sky Attack!");
} else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player);
if (B_UPDATED_MOVE_DATA < GEN_4)
MESSAGE("Wobbuffet is glowing!");
else if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet became cloaked in a harsh light!");
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player);
// Attack turn
MESSAGE("Wobbuffet used Sky Attack!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Sky Attack doesn't need to charge with Power Herb")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SKY_ATTACK); }
} SCENE {
if (B_UPDATED_MOVE_DATA >= GEN_5) {
NONE_OF {
MESSAGE("Wobbuffet became cloaked in a harsh light!");
MESSAGE("Wobbuffet is glowing!");
}
MESSAGE("Wobbuffet used Sky Attack!");
} else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player);
if (B_UPDATED_MOVE_DATA < GEN_4)
MESSAGE("Wobbuffet is glowing!");
else if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet became cloaked in a harsh light!");
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet became fully charged due to its Power Herb!");
if (B_UPDATED_MOVE_DATA < GEN_5)
MESSAGE("Wobbuffet used Sky Attack!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight")
{
u32 move1, move2;
PARAMETRIZE { move1 = MOVE_SPLASH; move2 = MOVE_SOLAR_BEAM; }
PARAMETRIZE { move1 = MOVE_SUNNY_DAY; move2 = MOVE_SOLAR_BEAM; }
PARAMETRIZE { move1 = MOVE_SPLASH; move2 = MOVE_SOLAR_BLADE; }
PARAMETRIZE { move1 = MOVE_SUNNY_DAY; move2 = MOVE_SOLAR_BLADE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move1); MOVE(player, move2); }
TURN { SKIP_TURN(player); }
} SCENE {
if (move1 == MOVE_SUNNY_DAY) {
NOT MESSAGE("Wobbuffet took in sunlight!");
} else {
if (move2 == MOVE_SOLAR_BEAM) {
if (B_UPDATED_MOVE_DATA >= GEN_5)
{
MESSAGE("Wobbuffet used Solar Beam!");
MESSAGE("Wobbuffet took in sunlight!");
ANIMATION(ANIM_TYPE_MOVE, move2, player);
} else {
NOT MESSAGE("Wobbuffet used Solar Beam!");
ANIMATION(ANIM_TYPE_MOVE, move2, player);
MESSAGE("Wobbuffet took in sunlight!");
}
MESSAGE("Wobbuffet used Solar Beam!");
} else {
if (B_UPDATED_MOVE_DATA >= GEN_5) {
MESSAGE("Wobbuffet used Solar Blade!");
MESSAGE("Wobbuffet took in sunlight!");
ANIMATION(ANIM_TYPE_MOVE, move2, player);
} else {
NOT MESSAGE("Wobbuffet used Solar Blade!");
ANIMATION(ANIM_TYPE_MOVE, move2, player);
MESSAGE("Wobbuffet took in sunlight!");
}
MESSAGE("Wobbuffet used Solar Blade!");
}
ANIMATION(ANIM_TYPE_MOVE, move2, player);
HP_BAR(opponent);
}
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_RAIN_DANCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_RAIN_DANCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SANDSTORM; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SANDSTORM; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_HAIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_HAIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Electro Shot needs a charging Turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ELECTRO_SHOT); }
TURN { SKIP_TURN(player); }
} SCENE {
// Charging turn
MESSAGE("Wobbuffet used Electro Shot!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player);
MESSAGE("Wobbuffet absorbed electricity!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Sp. Atk rose!");
// Attack turn
MESSAGE("Wobbuffet used Electro Shot!");
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge when it's raining")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_RAIN_DANCE); MOVE(player, MOVE_ELECTRO_SHOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent);
MESSAGE("Wobbuffet used Electro Shot!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player);
MESSAGE("Wobbuffet absorbed electricity!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Sp. Atk rose!");
NONE_OF {
MESSAGE("Wobbuffet used Electro Shot!");
}
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge with Power Herb")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ELECTRO_SHOT); }
} SCENE {
MESSAGE("Wobbuffet used Electro Shot!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player);
MESSAGE("Wobbuffet absorbed electricity!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Sp. Atk rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet became fully charged due to its Power Herb!");
NONE_OF {
MESSAGE("Wobbuffet used Electro Shot!");
}
HP_BAR(opponent);
}
}