merge with upcoming

This commit is contained in:
DizzyEggg 2023-02-22 19:27:42 +01:00
commit 877e3fd6af
45 changed files with 1967 additions and 373 deletions

View File

@ -464,7 +464,7 @@ LD_SCRIPT_TEST := ld_script_test.txt
$(OBJ_DIR)/ld_script_test.ld: $(LD_SCRIPT_TEST) $(LD_SCRIPT_DEPS)
cd $(OBJ_DIR) && sed "s#tools/#../../tools/#g" ../../$(LD_SCRIPT_TEST) > ld_script_test.ld
$(TESTELF): $(OBJ_DIR)/ld_script_test.ld $(OBJS) $(TEST_OBJS) libagbsyscall check-tools
$(TESTELF): $(OBJ_DIR)/ld_script_test.ld $(OBJS) $(TEST_OBJS) libagbsyscall tools check-tools
@echo "cd $(OBJ_DIR) && $(LD) -T ld_script_test.ld -o ../../$@ <objects> <test-objects> <lib>"
@cd $(OBJ_DIR) && $(LD) $(TESTLDFLAGS) -T ld_script_test.ld -o ../../$@ $(OBJS_REL) $(TEST_OBJS_REL) $(LIB)
$(FIX) $@ -t"$(TITLE)" -c$(GAME_CODE) -m$(MAKER_CODE) -r$(REVISION) --silent

View File

@ -270,6 +270,50 @@
.macro stopsound
.byte 0x2f
.endm
@ same as createvisualtask except takes in battlerargindex, which is the battle anim arg index of the battler to loop through
.macro createvisualtaskontargets addr:req, priority:req, battlerargindex:req, argv:vararg
.byte 0x30
.4byte \addr
.byte \priority
.byte \battlerargindex
.byte (.Lcreatetask_\@_2 - .Lcreatetask_\@_1) / 2 @ num_args
.Lcreatetask_\@_1:
.2byte \argv
.Lcreatetask_\@_2:
.endm
@ same as createsprite except takes in battlerargindex, which is the battle anim arg index of the battler to loop through
.macro createspriteontargets template:req, anim_battler:req, subpriority_offset:req, battlerargindex:req, argv:vararg
.byte 0x31
.4byte \template
.if \anim_battler == ANIM_TARGET
.byte 0x80 | (\subpriority_offset & 0x7F)
.else
.byte (\subpriority_offset & 0x7F)
.endif
.byte \battlerargindex
.byte (.Lsprite_\@_2 - .Lsprite_\@_1) / 2
.Lsprite_\@_1:
.2byte \argv
.Lsprite_\@_2:
.endm
@ does not overwrite gBattleAnimArgs[battlerargindex], some sprite templates are too dependent on the value (e.g. heal block)
.macro createspriteontargets_onpos template:req, anim_battler:req, subpriority_offset:req, battlerargindex:req, argv:vararg
.byte 0x32
.4byte \template
.if \anim_battler == ANIM_TARGET
.byte 0x80 | (\subpriority_offset & 0x7F)
.else
.byte (\subpriority_offset & 0x7F)
.endif
.byte \battlerargindex
.byte (.Lsprite_\@_2 - .Lsprite_\@_1) / 2
.Lsprite_\@_1:
.2byte \argv
.Lsprite_\@_2:
.endm
@ useful macros
.macro jumpreteq value:req, ptr:req

View File

@ -1317,11 +1317,22 @@
.2byte \holdEffect
.4byte \jumpInstr
.endm
.macro jumpifmorethanhalfHP battler:req, jumpInstr:req
callnative BS_JumpIfMoreThanHalfHP
.byte \battler
.4byte \jumpInstr
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
.endm
@ Stores Healing Wish effect.
.macro storehealingwish battler:req
various \battler, VARIOUS_STORE_HEALING_WISH
.endm
.macro setmagiccoattarget battler:req
various \battler, VARIOUS_SET_MAGIC_COAT_TARGET
@ -2044,11 +2055,6 @@
various \battler, VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES
.endm
.macro jumpifnovalidtargets jumpInstr:req
various BS_ATTACKER, VARIOUS_JUMP_IF_NO_VALID_TARGETS
.4byte \jumpInstr
.endm
@ helpful macros
.macro setstatchanger stat:req, stages:req, down:req
setbyte sSTATCHANGER, \stat | \stages << 3 | \down << 7
@ -2242,3 +2248,12 @@
various \battler, VARIOUS_JUMP_IF_EMERGENCY_EXITED
.4byte \jumpInstr
.endm
.macro jumpifshelltrap battler:req, ptr:req
various \battler, VARIOUS_JUMP_IF_SHELL_TRAP
.4byte \ptr
.endm
.macro hitswitchtargetfailed
various 0, VARIOUS_HIT_SWITCH_TARGET_FAILED
.endm

View File

@ -1492,22 +1492,22 @@ Move_HEAL_BLOCK:
loadspritegfx ANIM_TAG_BLUE_STAR
monbg ANIM_TARGET
createsoundtask SoundTask_PlaySeChangingVolume, SE_M_ABSORB_2, SOUND_PAN_TARGET, 256, -16, 0, 2
createsprite gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 0, -5, 1, 0
createspriteontargets_onpos gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 2, 0, -5, ANIM_TARGET, 0
delay 7
createsprite gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, -15, 10, 1, 0
createspriteontargets_onpos gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 2, -15, 10, ANIM_TARGET, 0
delay 7
createvisualtask AnimTask_BlendBattleAnimPal, 10, 1 | 4, 4, 2, 12, 0, RGB_BLACK
createsprite gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 0, -5, 1, 0
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_DEF_SIDE, 4, 2, 12, 0, RGB_BLACK
createspriteontargets_onpos gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 2, 0, -5, ANIM_TARGET, 0
delay 7
createsprite gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, -15, 10, 1, 0
createspriteontargets_onpos gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 2, -15, 10, ANIM_TARGET, 0
delay 7
createsprite gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, -15, -15, 1, 0
createspriteontargets_onpos gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 2, -15, -15, ANIM_TARGET, 0
delay 7
createsprite gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 10, -5, 1, 0
createspriteontargets_onpos gHealingBlueStarSpriteTemplate, ANIM_TARGET, 2, 2, 10, -5, ANIM_TARGET, 0
delay 7
waitforvisualfinish
delay 11
createvisualtask AnimTask_BlendBattleAnimPal, 10, 1 | 4, 4, 2, 0, 12, RGB_BLACK
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_DEF_SIDE, 4, 2, 0, 12, RGB_BLACK
waitforvisualfinish
clearmonbg ANIM_TARGET
end
@ -6405,11 +6405,7 @@ Move_ELECTROWEB:
clearmonbg ANIM_DEF_PARTNER
delay 1
createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 5, 1, 2, 9, 0, RGB_BLACK
@ ElectricityEffect looks ugly against both opponents, to do later
jumpifdoublebattle Move_ELECTROWEB_Wait
call ElectricityEffect
call ElectricityEffect_OnTargets
Move_ELECTROWEB_Wait:
waitforvisualfinish
end
@ -9278,16 +9274,16 @@ Move_EERIE_IMPULSE::
Move_VENOM_DRENCH::
loadspritegfx ANIM_TAG_POISON_BUBBLE
monbg ANIM_DEF_PARTNER
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xfffb 0x1 0xfffb 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xfffb 0x1 0xfffb 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x5 0x0 0x6 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x5 0x0 0x6 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x13 0x1 0xa 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x13 0x1 0xa 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffe9 0x2 0xfff6 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffe9 0x2 0xfff6 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
call AcidDrench
@ -9296,28 +9292,28 @@ Move_VENOM_DRENCH::
clearmonbg ANIM_DEF_PARTNER
end
AcidDrench:
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffec 0x0 0xfff6 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffec 0x0 0xfff6 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x1c 0x1 0xa 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x1c 0x1 0xa 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xfff6 0x1 0xfffb 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xfff6 0x1 0xfffb 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xa 0x0 0x6 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xa 0x0 0x6 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x18 0x1 0xa 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x18 0x1 0xa 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffe0 0x2 0xfff6 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffe0 0x2 0xfff6 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffec 0x0 0xfff6 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0xffec 0x0 0xfff6 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x1e 0x2 0xa 0x0
launchtemplate gVenomDrenchAcidTemplate 0x82, 0x4, 0x1e 0x2 0xa 1
playsewithpan SE_M_BUBBLE3, SOUND_PAN_TARGET
delay 0x2
return
@ -10175,6 +10171,7 @@ Move_ORIGIN_PULSE::
launchtemplate gOriginPulseOrbInwardTemplate 0x82 0x5 0x1 0x0 0xFF2A 0xFFAA 0x10 @between left and upper left
launchtemplate gOriginPulseOrbInwardTemplate 0x82 0x5 0x1 0x0 0xFFDA 0xFF94 0x10 @between up and upper left
waitforvisualfinish
createvisualtaskontargets AnimTask_ShakeMon2, 5, 0, ANIM_TARGET, 2, 0, 18, 1
monbg ANIM_DEF_PARTNER
launchtemplate gOriginPulseBasicSplatTemplate 0x83 0x4 0xffb0 0xfff0 0x1 0x1
stopsound
@ -12039,168 +12036,168 @@ ShellTrapUnleash:
monbg ANIM_TARGET
waitplaysewithpan SE_M_REFLECT, SOUND_PAN_ATTACKER, 0x10
delay 0x6
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapYellowImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapYellowImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0x0
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gFireSpiralOutwardSpriteTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x38, 0x0
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x2
launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0x4
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gFireSpiralOutwardSpriteTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x38, 0x4
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x2
launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0x8
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gFireSpiralOutwardSpriteTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x38, 0x8
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x2
launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0xc
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gFireSpiralOutwardSpriteTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x38, 0xc
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x2
launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0x10
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gFireSpiralOutwardSpriteTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x38, 0x10
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x2
launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0x14
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gFireSpiralOutwardSpriteTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x38, 0x14
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x2
launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0x18
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gFireSpiralOutwardSpriteTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x38, 0x18
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
launchtemplate gShellTrapRedImpactTemplate 0x2 0x4 0x0 0x0 0x0 0x2
createsprite gShellTrapRedImpactTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0x0, 0x2
delay 0x5
waitforvisualfinish
call ShellTrapFireLaunch1
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0x0 0x0 0x0 0x38 0x4 0x4 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0x0, 0x0, 0x0, 0x38, 0x4, 0x4, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xfff6 0x0 0xfff6 0x38 0x4 0x4 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xfff6, 0x0, 0xfff6, 0x38, 0x4, 0x4, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xa 0x0 0xa 0x38 0xfffc 0x3 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xa, 0x0, 0xa, 0x38, 0xfffc, 0x3, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xffec 0x0 0xffec 0x38 0xfffc 0x5 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xffec, 0x0, 0xffec, 0x38, 0xfffc, 0x5, 0x1
delay 0x3
call ShellTrapFireLaunch2
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
delay 0x3
waitforvisualfinish
clearmonbg ANIM_TARGET
end
ShellTrapFireLaunch1:
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0x0 0x0 0x0 0x38 0x4 0x4 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0x0, 0x0, 0x0, 0x38, 0x4, 0x4, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xfff6 0x0 0xfff6 0x38 0x4 0x4 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xfff6, 0x0, 0xfff6, 0x38, 0x4, 0x4, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xa 0x0 0xa 0x38 0xfffc 0x3 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xa, 0x0, 0xa, 0x38, 0xfffc, 0x3, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xffec 0x0 0xffec 0x38 0xfffc 0x5 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xffec, 0x0, 0xffec, 0x38, 0xfffc, 0x5, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xf 0x0 0xf 0x38 0x4 0x4 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xf, 0x0, 0xf, 0x38, 0x4, 0x4, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xffec 0x0 0xffec 0x38 0x4 0x4 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xffec, 0x0, 0xffec, 0x38, 0x4, 0x4, 0x1
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0x14 0x0 0x14 0x38 0x4 0x4 0x1
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0x14, 0x0, 0x14, 0x38, 0x4, 0x4, 0x1
delay 0x3
return
ShellTrapFireLaunch2:
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0x0 0x0 0x0 0x38 0x4 0x4 0x1
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0x0, 0x0, 0x0, 0x38, 0x4, 0x4, 0x1
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xfff6 0x0 0xfff6 0x38 0x4 0x4 0x1
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xfff6, 0x0, 0xfff6, 0x38, 0x4, 0x4, 0x1
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xa 0x0 0xa 0x38 0xfffc 0x3 0x1
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xa, 0x0, 0xa, 0x38, 0xfffc, 0x3, 0x1
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xffec 0x0 0xffec 0x38 0xfffc 0x5 0x1
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xffec, 0x0, 0xffec, 0x38, 0xfffc, 0x5, 0x1
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xf 0x0 0xf 0x38 0x4 0x4 0x1
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xf, 0x0, 0xf, 0x38, 0x4, 0x4, 0x1
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0xffec 0x0 0xffec 0x38 0x4 0x4 0x1
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0xffec, 0x0, 0xffec, 0x38, 0x4, 0x4, 0x1
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x3
launchtemplate gShellTrapFireHitsTemplate 0x28 0x8 0x0 0x14 0x0 0x14 0x38 0x4 0x4 0x1
launchtask AnimTask_ShakeMon 0x2 0x5 ANIM_TARGET 0x0003 0x0000 0x0006 0x0001
createsprite gShellTrapFireHitsTemplate, ANIM_ATTACKER, 40, 0x0, 0x14, 0x0, 0x14, 0x38, 0x4, 0x4, 0x1
createvisualtaskontargets AnimTask_ShakeMon, 0x2, 0, ANIM_TARGET, 0x0003, 0x0000, 0x0006, 0x0001
playsewithpan SE_M_FLAME_WHEEL, SOUND_PAN_ATTACKER
delay 0x3
return
@ -23375,15 +23372,15 @@ Move_OVERHEAT:
waitforvisualfinish
createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 3, -5, 3, ANIM_TARGET, 0
playsewithpan SE_M_FLAMETHROWER, SOUND_PAN_TARGET
createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 10, 0, 25, 1
createvisualtaskontargets AnimTask_ShakeMon, 2, 0, ANIM_TARGET, 10, 0, 25, 1
delay 6
createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 3, 8, -5, ANIM_TARGET, 0
createspriteontargets gBasicHitSplatSpriteTemplate, ANIM_TARGET, 3, 2, 8, -5, ANIM_TARGET, 0
playsewithpan SE_M_FLAMETHROWER, SOUND_PAN_TARGET
delay 8
createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 3, 10, 10, ANIM_TARGET, 0
createspriteontargets gBasicHitSplatSpriteTemplate, ANIM_TARGET, 3, 2, 10, 10, ANIM_TARGET, 0
playsewithpan SE_M_FLAMETHROWER, SOUND_PAN_TARGET
delay 8
createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 3, 0, 0, ANIM_TARGET, 0
createspriteontargets gBasicHitSplatSpriteTemplate, ANIM_TARGET, 3, 2, 0, 0, ANIM_TARGET, 0
playsewithpan SE_M_FLAMETHROWER, SOUND_PAN_TARGET
createvisualtask AnimTask_CopyPalFadedToUnfaded, 5, 1
delay 1
@ -24209,21 +24206,40 @@ WaterBubblesEffectLong:
ElectricityEffect:
playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_TARGET
ElectricityEffectNoSound:
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, 5, 0, 5, 0, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 5, 0, 5, 0, ANIM_TARGET
delay 2
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, -5, 10, 5, 1, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, -5, 10, 5, 1, ANIM_TARGET
delay 2
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, 15, 20, 5, 2, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 15, 20, 5, 2, ANIM_TARGET
delay 2
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, -15, -10, 5, 0, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, -15, -10, 5, 0, ANIM_TARGET
delay 2
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, 25, 0, 5, 1, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 25, 0, 5, 1, ANIM_TARGET
delay 2
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, -8, 8, 5, 2, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, -8, 8, 5, 2, ANIM_TARGET
delay 2
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, 2, -8, 5, 0, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 2, -8, 5, 0, ANIM_TARGET
delay 2
createsprite gElectricitySpriteTemplate, ANIM_TARGET, 2, -20, 15, 5, 1, ANIM_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, -20, 15, 5, 1, ANIM_TARGET
return
ElectricityEffect_OnTargets:
playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_TARGET
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, 5, 0, 5, 0, ANIM_TARGET
delay 2
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, -5, 10, 5, 1, ANIM_TARGET
delay 2
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, 15, 20, 5, 2, ANIM_TARGET
delay 2
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, -15, -10, 5, 0, ANIM_TARGET
delay 2
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, 25, 0, 5, 1, ANIM_TARGET
delay 2
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, -8, 8, 5, 2, ANIM_TARGET
delay 2
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, 2, -8, 5, 0, ANIM_TARGET
delay 2
createspriteontargets gElectricitySpriteTemplate, ANIM_TARGET, 2, 4, -20, 15, 5, 1, ANIM_TARGET
return
ConfusionEffect:

View File

@ -417,6 +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
BattleScript_EffectAttackUpUserAlly:
jumpifnoally BS_ATTACKER, BattleScript_EffectAttackUp
@ -557,6 +558,23 @@ BattleScript_AffectionBasedStatusHeal_Continue:
waitstate
end2
BattleScript_ShellTrapSetUp::
printstring STRINGID_EMPTYSTRING3
waitmessage 0x1
playanimation BS_ATTACKER, B_ANIM_SHELL_TRAP_SETUP, NULL
printstring STRINGID_PREPARESHELLTRAP
waitmessage B_WAIT_TIME_LONG
end2
BattleScript_EffectShellTrap::
attackcanceler
jumpifshelltrap BS_ATTACKER, BattleScript_HitFromAccCheck
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING | HITMARKER_NO_PPDEDUCT, BattleScript_MoveEnd
ppreduce
printstring STRINGID_SHELLTRAPDIDNTWORK
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectSteelBeam::
attackcanceler
attackstring
@ -2128,9 +2146,14 @@ BattleScript_EffectHitSwitchTarget:
moveendall
jumpifability BS_TARGET, ABILITY_SUCTION_CUPS, BattleScript_AbilityPreventsPhasingOut
jumpifstatus3 BS_TARGET, STATUS3_ROOTED, BattleScript_PrintMonIsRooted
tryhitswitchtarget BattleScript_EffectHitSwitchTargetMoveEnd
BattleScript_EffectHitSwitchTargetMoveEnd:
end
tryhitswitchtarget BattleScript_MoveEnd
forcerandomswitch BattleScript_HitSwitchTargetForceRandomSwitchFailed
goto BattleScript_MoveEnd
BattleScript_HitSwitchTargetForceRandomSwitchFailed:
hitswitchtargetfailed
setbyte sSWITCH_CASE, B_SWITCH_NORMAL
goto BattleScript_MoveEnd
BattleScript_EffectClearSmog:
setmoveeffect MOVE_EFFECT_CLEAR_SMOG
@ -2697,6 +2720,8 @@ BattleScript_EffectHealingWish:
instanthpdrop BS_ATTACKER
setatkhptozero
tryfaintmon BS_ATTACKER
storehealingwish BS_ATTACKER
.if B_HEALING_WISH_SWITCH <= GEN_4
openpartyscreen BS_ATTACKER, BattleScript_EffectHealingWishEnd
switchoutabilities BS_ATTACKER
waitstate
@ -2711,11 +2736,19 @@ BattleScript_EffectHealingWish:
printstring STRINGID_SWITCHINMON
switchinanim BS_ATTACKER, TRUE
waitstate
switchineffects BS_ATTACKER
.endif
BattleScript_EffectHealingWishEnd:
moveendall
end
BattleScript_HealingWishActivates::
setbyte cMULTISTRING_CHOOSER, 0
jumpifnotchosenmove MOVE_LUNAR_DANCE, BattleScript_EffectHealingWishNewMon
goto BattleScript_EffectHealingWishRestore
BattleScript_LunarDanceActivates::
setbyte cMULTISTRING_CHOOSER, 1
restorepp BS_ATTACKER
BattleScript_EffectHealingWishNewMon:
BattleScript_EffectHealingWishRestore:
printfromtable gHealingWishStringIds
waitmessage B_WAIT_TIME_LONG
playanimation BS_ATTACKER, B_ANIM_WISH_HEAL
@ -2730,10 +2763,7 @@ BattleScript_EffectHealingWishNewMon:
waitstate
printstring STRINGID_HEALINGWISHHEALED
waitmessage B_WAIT_TIME_LONG
switchineffects BS_ATTACKER
BattleScript_EffectHealingWishEnd:
moveendall
end
return
BattleScript_EffectWorrySeed:
attackcanceler
@ -3405,27 +3435,20 @@ BattleScript_EffectFreezeHit::
BattleScript_EffectParalyzeHit::
setmoveeffect MOVE_EFFECT_PARALYSIS
goto BattleScript_EffectHit
BattleScript_EffectExplosion::
attackcanceler
attackstring
ppreduce
@ Below jumps to BattleScript_DampStopsExplosion if it fails (only way it can)
tryexplosion
setatkhptozero
waitstate
jumpifbyte CMP_NO_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_MISSED, BattleScript_ExplosionDoAnimStartLoop
BattleScript_EffectExplosion_AnimDmgRet:
jumpifbyte CMP_NO_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_MISSED, BattleScript_ExplosionAnimRet
call BattleScript_PreserveMissedBitDoMoveAnim
goto BattleScript_ExplosionLoop
BattleScript_ExplosionDoAnimStartLoop:
goto BattleScript_ExplosionDmgRet
BattleScript_ExplosionAnimRet:
attackanimation
waitanimation
BattleScript_ExplosionLoop:
BattleScript_ExplosionDmgRet:
movevaluescleanup
critcalc
damagecalc
adjustdamage
accuracycheck BattleScript_ExplosionMissed, ACC_CURR_MOVE
accuracycheck BattleScript_ExplosionMissedRet, ACC_CURR_MOVE
effectivenesssound
hitanimation BS_TARGET
waitstate
@ -3436,17 +3459,25 @@ BattleScript_ExplosionLoop:
resultmessage
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_TARGET
moveendto MOVEEND_NEXT_TARGET
jumpifnexttargetvalid BattleScript_ExplosionLoop
tryfaintmon BS_ATTACKER
moveendcase MOVEEND_CLEAR_BITS
end
BattleScript_ExplosionMissed:
BattleScript_ExplosionAnimEndRet_Return:
return
BattleScript_ExplosionMissedRet:
effectivenesssound
resultmessage
waitmessage B_WAIT_TIME_LONG
moveendto MOVEEND_NEXT_TARGET
jumpifnexttargetvalid BattleScript_ExplosionLoop
goto BattleScript_ExplosionAnimEndRet_Return
BattleScript_EffectExplosion::
attackcanceler
attackstring
ppreduce
@ Below jumps to BattleScript_DampStopsExplosion if it fails (only way it can)
tryexplosion
waitstate
BattleScript_EffectExplosion_AnimDmgFaintAttacker:
call BattleScript_EffectExplosion_AnimDmgRet
moveendall
setatkhptozero
tryfaintmon BS_ATTACKER
end
@ -3454,14 +3485,28 @@ BattleScript_EffectMindBlown::
attackcanceler
attackstring
ppreduce
tryexplosion
jumpifbyte CMP_GREATER_THAN, sB_ANIM_TARGETS_HIT, 0, BattleScript_EffectMindBlown_NoHpLoss
jumpifabilitypresent ABILITY_DAMP, BattleScript_MindBlownDamp
jumpifmorethanhalfHP BS_ATTACKER, BattleScript_EffectMindBlown_HpDown
setbyte sMULTIHIT_EFFECT, 0 @ Note to faint the attacker
instanthpdrop BS_ATTACKER
waitstate
goto BattleScript_EffectExplosion_AnimDmgFaintAttacker
BattleScript_EffectMindBlown_NoHpLoss:
jumpifbyte CMP_EQUAL, sMULTIHIT_EFFECT, 0, BattleScript_EffectExplosion_AnimDmgFaintAttacker
goto BattleScript_EffectMindBlown_AnimDmgNoFaint
BattleScript_MindBlownDamp:
copybyte gBattlerTarget, gBattlerAbility
goto BattleScript_DampStopsExplosion
BattleScript_EffectMindBlown_HpDown:
setbyte sMULTIHIT_EFFECT, 1 @ Note to not faint the attacker
dmg_1_2_attackerhp
healthbarupdate BS_ATTACKER
datahpupdate BS_ATTACKER
waitstate
jumpifbyte CMP_NO_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_MISSED, BattleScript_ExplosionDoAnimStartLoop
call BattleScript_PreserveMissedBitDoMoveAnim
goto BattleScript_ExplosionLoop
BattleScript_EffectMindBlown_AnimDmgNoFaint:
call BattleScript_EffectExplosion_AnimDmgRet
goto BattleScript_MoveEnd
BattleScript_PreserveMissedBitDoMoveAnim:
bichalfword gMoveResultFlags, MOVE_RESULT_MISSED
@ -3701,7 +3746,6 @@ BattleScript_EffectRoar::
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed
BattleScript_ForceRandomSwitch::
forcerandomswitch BattleScript_ButItFailed
BattleScript_EffectMultiHit::
@ -8349,9 +8393,9 @@ BattleScript_AbilityRaisesDefenderStat::
BattleScript_AbilityPopUp:
.if B_ABILITY_POP_UP == TRUE
showabilitypopup BS_ABILITY_BATTLER
recordability BS_ABILITY_BATTLER
pause 40
.endif
recordability BS_ABILITY_BATTLER
sethword sABILITY_OVERWRITE, 0
return
@ -8562,7 +8606,6 @@ BattleScript_TryAdrenalineOrbRet:
return
BattleScript_IntimidateActivates::
jumpifnovalidtargets BattleScript_IntimidateEnd
showabilitypopup BS_ATTACKER
pause B_WAIT_TIME_LONG
destroyabilitypopup

View File

@ -143,6 +143,7 @@ struct ProtectStruct
u16 quickDraw:1;
u16 beakBlastCharge:1;
u16 quash:1;
u16 shellTrap:1;
u16 silkTrapped:1;
u32 physicalDmg;
u32 specialDmg;
@ -645,6 +646,7 @@ struct BattleStruct
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 forcedSwitch:4; // For each battler
u8 switchInAbilityPostponed:4; // To not activate against an empty field, each bit for battler
u8 ballSpriteIds[2]; // item gfx, window gfx
u8 stickyWebUser;
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
@ -652,10 +654,13 @@ struct BattleStruct
// When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without.
u8 attackerBeforeBounce:2;
u8 beatUpSlot:3;
bool8 hitSwitchTargetFailed:1;
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
bool8 allowedToChangeFormInWeather[PARTY_SIZE][2]; // For each party member and side, used by Ice Face.
u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
u8 storedHealingWish:4; // Each battler as a bit.
u8 storedLunarDance:4; // Each battler as a bit.
};
#define F_DYNAMIC_TYPE_1 (1 << 6)

View File

@ -44,6 +44,7 @@ u16 GetSecretPowerMoveEffect(void);
void StealTargetItem(u8 battlerStealer, u8 battlerItem);
u8 GetCatchingBattler(void);
u32 GetHighestStatId(u32 battlerId);
bool32 DoSwitchInAbilitiesItems(u32 battlerId);
extern void (* const gBattleScriptingCommandsTable[])(void);
extern const u8 gBattlePalaceNatureToMoveGroupLikelihood[NUM_NATURES][4];

View File

@ -293,7 +293,6 @@ extern const u8 BattleScript_WishMegaEvolution[];
extern const u8 BattleScript_MoveEffectRecoilWithStatus[];
extern const u8 BattleScript_EffectWithChance[];
extern const u8 BattleScript_MoveEffectClearSmog[];
extern const u8 BattleScript_ForceRandomSwitch[];
extern const u8 BattleScript_SideStatusWoreOffReturn[];
extern const u8 BattleScript_MoveEffectSmackDown[];
extern const u8 BattleScript_MoveEffectFlameBurst[];
@ -452,6 +451,9 @@ extern const u8 BattleScript_MimicryActivates_End3[];
extern const u8 BattleScript_IceFaceNullsDamage[];
extern const u8 BattleScript_BattlerFormChangeWithStringEnd3[];
extern const u8 BattleScript_DampPreventsAftermath[];
extern const u8 BattleScript_HealingWishActivates[];
extern const u8 BattleScript_LunarDanceActivates[];
extern const u8 BattleScript_ShellTrapSetUp[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View File

@ -181,6 +181,7 @@ void UndoMegaEvolution(u32 monId);
void UndoFormChange(u32 monId, u32 side, bool32 isSwitchingOut);
bool32 DoBattlersShareType(u32 battler1, u32 battler2);
bool32 CanBattlerGetOrLoseItem(u8 battlerId, u16 itemId);
u32 GetIllusionMonSpecies(u32 battlerId);
struct Pokemon *GetIllusionMonPtr(u32 battlerId);
void ClearIllusionMon(u32 battlerId);
bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId);

View File

@ -95,6 +95,8 @@
#define B_BEAT_UP GEN_LATEST // In Gen5+, Beat Up uses a different formula to calculate its damage, and deals Dark-type damage. Prior to Gen 5, each hit also announces the party member's name.
#define B_DARK_VOID_FAIL GEN_LATEST // In Gen7+, only Darkrai can use Dark Void.
#define B_BURN_HIT_THAW GEN_LATEST // In Gen6+, damaging moves with a chance of burn will thaw the target, regardless if they're fire-type moves or not.
#define B_HEALING_WISH_SWITCH GEN_LATEST // In Gen5+, the mon receiving Healing Wish is sent out at the end of the turn.
// Additionally, in gen8+ the Healing Wish's effect will be stored until the user switches into a statused or hurt mon.
// Ability settings
#define B_EXPANDED_ABILITY_NAMES TRUE // If TRUE, ability names are increased from 12 characters to 16 characters.

View File

@ -13,11 +13,14 @@
#define P_EGG_HATCH_LEVEL GEN_LATEST // Since Gen 4, Pokémon will hatch from eggs at level 1 instead of 5.
#define P_BALL_INHERITING GEN_LATEST // Since Gen 6, Eggs from the Daycare will inherit the Poké Ball from their mother. From Gen7 onwards, the father can pass it down as well, as long as it's of the same species as the mother.
// Other settings
// Species-specific settings
#define P_SHEDINJA_BALL GEN_LATEST // Since Gen 4, Shedinja requires a Poké Ball for its evolution. In Gen 3, Shedinja inherits Nincada's Ball.
#define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs.
#define P_KADABRA_EVERSTONE GEN_LATEST // Since Gen 4, Kadabra can evolve even when holding an Everstone.
#define P_HIPPO_GENDER_DIFF_ICONS TRUE // If TRUE, will give Hippopotas and Hippowdon custom icons for their female forms.
#define P_SHUCKLE_BERRY_JUICE TRUE // In Gen 2, Shuckle had a 1/16 chance of converting Berry that it's holding into Berry Juice. Setting this to TRUE will allow to do this with an Oran Berry, which is the spiritual succesor of the Berry item.
// Other settings
#define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs.
// Flag settings
// To use the following features in scripting, replace the 0s with the flag ID you're assigning it to.

View File

@ -398,7 +398,8 @@
#define EFFECT_VICTORY_DANCE 392
#define EFFECT_TEATIME 393
#define EFFECT_ATTACK_UP_USER_ALLY 394 // Howl 8th Gen
#define EFFECT_SHELL_TRAP 395
#define NUM_BATTLE_MOVE_EFFECTS 395
#define NUM_BATTLE_MOVE_EFFECTS 396
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H

View File

@ -255,8 +255,10 @@
#define VARIOUS_TRY_WIND_RIDER_POWER 163
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 164
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 165
#define VARIOUS_JUMP_IF_NO_VALID_TARGETS 166
#define VARIOUS_JUMP_IF_EMERGENCY_EXITED 167
#define VARIOUS_JUMP_IF_EMERGENCY_EXITED 166
#define VARIOUS_STORE_HEALING_WISH 167
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 168
#define VARIOUS_JUMP_IF_SHELL_TRAP 169
// Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0

View File

@ -639,8 +639,10 @@
#define STRINGID_ABILITYWEAKENEDFSURROUNDINGMONSSTAT 637
#define STRINGID_ATTACKERGAINEDSTRENGTHFROMTHEFALLEN 638
#define STRINGID_PKMNSABILITYPREVENTSABILITY 639
#define STRINGID_PREPARESHELLTRAP 640
#define STRINGID_SHELLTRAPDIDNTWORK 641
#define BATTLESTRINGS_COUNT 640
#define BATTLESTRINGS_COUNT 642
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View File

@ -982,6 +982,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
// stat raising effects
case EFFECT_ATTACK_UP:
case EFFECT_ATTACK_UP_2:
case EFFECT_ATTACK_UP_USER_ALLY:
if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL))
score -= 10;
break;
@ -3191,6 +3192,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
// stat raising effects
case EFFECT_ATTACK_UP:
case EFFECT_ATTACK_UP_2:
case EFFECT_ATTACK_UP_USER_ALLY:
if (MovesWithSplitUnusable(battlerAtk, battlerDef, SPLIT_PHYSICAL))
{
score -= 8;
@ -4824,6 +4826,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
switch (gBattleMoves[move].effect)
{
case EFFECT_ATTACK_UP:
case EFFECT_ATTACK_UP_USER_ALLY:
case EFFECT_DEFENSE_UP:
case EFFECT_SPEED_UP:
case EFFECT_SPECIAL_ATTACK_UP:
@ -5154,6 +5157,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
switch (effect)
{
case EFFECT_ATTACK_UP:
case EFFECT_ATTACK_UP_USER_ALLY:
case EFFECT_DEFENSE_UP:
case EFFECT_SPEED_UP:
case EFFECT_SPECIAL_ATTACK_UP:

View File

@ -3,6 +3,7 @@
#include "battle_anim.h"
#include "battle_controllers.h"
#include "battle_interface.h"
#include "battle_util.h"
#include "bg.h"
#include "contest.h"
#include "decompress.h"
@ -81,7 +82,9 @@ static void Cmd_visible(void);
static void Cmd_teamattack_moveback(void);
static void Cmd_teamattack_movefwd(void);
static void Cmd_stopsound(void);
static void Cmd_createvisualtaskontargets(void);
static void Cmd_createspriteontargets(void);
static void Cmd_createspriteontargets_onpos(void);
static void RunAnimScriptCommand(void);
static void Task_UpdateMonBg(u8 taskId);
static void FlipBattlerBgTiles(void);
@ -169,6 +172,9 @@ static void (* const sScriptCmdTable[])(void) =
Cmd_teamattack_moveback, // 0x2D
Cmd_teamattack_movefwd, // 0x2E
Cmd_stopsound, // 0x2F
Cmd_createvisualtaskontargets, // 0x30
Cmd_createspriteontargets, // 0x31
Cmd_createspriteontargets_onpos, // 0x32
};
void ClearBattleAnimationVars(void)
@ -425,29 +431,46 @@ static void Cmd_unloadspritegfx(void)
ClearSpriteIndex(GET_TRUE_SPRITE_INDEX(index));
}
static void Cmd_createsprite(void)
static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets)
{
s32 i;
const struct SpriteTemplate *template;
u8 argVar;
u8 argsCount;
s16 subpriority;
sBattleAnimScriptPtr++;
template = (const struct SpriteTemplate *)(T2_READ_32(sBattleAnimScriptPtr));
sBattleAnimScriptPtr += 4;
argVar = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
argsCount = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
for (i = 0; i < argsCount; i++)
u8 numTargets = 1;
switch (GetBattlerMoveTargetType(gBattleAnimAttacker, gAnimMoveIndex))
{
gBattleAnimArgs[i] = T1_READ_16(sBattleAnimScriptPtr);
sBattleAnimScriptPtr += 2;
case MOVE_TARGET_BOTH:
targets[0] = gBattleAnimArgs[battlerArgIndex];
numTargets = 1;
if (IsBattlerAlive(targets[0] ^ BIT_FLANK)) {
targets[1] = targets[0] ^ BIT_FLANK;
numTargets++;
}
break;
case MOVE_TARGET_FOES_AND_ALLY:
targets[0] = gBattleAnimArgs[battlerArgIndex];
numTargets = 1;
if (IsBattlerAlive(targets[0] ^ BIT_FLANK)) {
targets[1] = targets[0] ^ BIT_FLANK;
numTargets++;
}
if (IsBattlerAlive(gBattleAnimAttacker ^ BIT_FLANK)) {
targets[2] = gBattleAnimAttacker ^ BIT_FLANK;
numTargets++;
}
break;
default:
targets[0] = gBattleAnimArgs[battlerArgIndex]; // original
numTargets = 1;
break;
}
return numTargets;
}
static s16 GetSubpriorityForMoveAnim(u8 argVar)
{
s16 subpriority;
if (argVar & ANIMSPRITE_IS_TARGET)
{
argVar ^= ANIMSPRITE_IS_TARGET;
@ -470,6 +493,34 @@ static void Cmd_createsprite(void)
if (subpriority < 3)
subpriority = 3;
return subpriority;
}
static void Cmd_createsprite(void)
{
s32 i;
const struct SpriteTemplate *template;
u8 argVar;
u8 argsCount;
s16 subpriority;
sBattleAnimScriptPtr++;
template = (const struct SpriteTemplate *)(T2_READ_32(sBattleAnimScriptPtr));
sBattleAnimScriptPtr += 4;
argVar = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
argsCount = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
for (i = 0; i < argsCount; i++)
{
gBattleAnimArgs[i] = T1_READ_16(sBattleAnimScriptPtr);
sBattleAnimScriptPtr += 2;
}
subpriority = GetSubpriorityForMoveAnim(argVar);
CreateSpriteAndAnimate(
template,
@ -479,6 +530,85 @@ static void Cmd_createsprite(void)
gAnimVisualTaskCount++;
}
static void CreateSpriteOnTargets(const struct SpriteTemplate *template, u8 argVar, u8 battlerArgIndex, u8 argsCount, bool32 overwriteAnimTgt)
{
u32 i;
u8 targets[MAX_BATTLERS_COUNT];
int ntargets;
s16 subpriority;
for (i = 0; i < argsCount; i++)
{
gBattleAnimArgs[i] = T1_READ_16(sBattleAnimScriptPtr);
sBattleAnimScriptPtr += 2;
}
subpriority = GetSubpriorityForMoveAnim(argVar);
ntargets = GetBattleAnimMoveTargets(battlerArgIndex, targets);
for (i = 0; i < ntargets; i++) {
if (overwriteAnimTgt)
gBattleAnimArgs[battlerArgIndex] = targets[i];
CreateSpriteAndAnimate(
template,
GetBattlerSpriteCoord(targets[i], BATTLER_COORD_X_2),
GetBattlerSpriteCoord(targets[i], BATTLER_COORD_Y_PIC_OFFSET),
subpriority);
gAnimVisualTaskCount++;
}
}
// will NOT overwrite gBattleAnimArgs
static void Cmd_createspriteontargets_onpos(void)
{
const struct SpriteTemplate *template;
u8 argVar;
u8 argsCount;
u8 battlerArgIndex;
sBattleAnimScriptPtr++;
template = (const struct SpriteTemplate *)(T2_READ_32(sBattleAnimScriptPtr));
sBattleAnimScriptPtr += 4;
argVar = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
battlerArgIndex = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
argsCount = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
CreateSpriteOnTargets(template, argVar, battlerArgIndex, argsCount, FALSE);
}
// DOES overwrite gBattleAnimArgs
static void Cmd_createspriteontargets(void)
{
const struct SpriteTemplate *template;
u8 argVar;
u8 argsCount;
u8 battlerArgIndex;
sBattleAnimScriptPtr++;
template = (const struct SpriteTemplate *)(T2_READ_32(sBattleAnimScriptPtr));
sBattleAnimScriptPtr += 4;
argVar = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
battlerArgIndex = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
argsCount = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
CreateSpriteOnTargets(template, argVar, battlerArgIndex, argsCount, TRUE);
}
static void Cmd_createvisualtask(void)
{
TaskFunc taskFunc;
@ -509,6 +639,48 @@ static void Cmd_createvisualtask(void)
gAnimVisualTaskCount++;
}
static void Cmd_createvisualtaskontargets(void)
{
TaskFunc taskFunc;
u8 taskPriority;
u8 taskId;
u8 numArgs;
u8 battlerArgIndex; // index in gBattleAnimArgs that has the battlerId
s32 i;
u8 targets[MAX_BATTLERS_COUNT] = {0};
sBattleAnimScriptPtr++;
taskFunc = (TaskFunc)T2_READ_32(sBattleAnimScriptPtr);
sBattleAnimScriptPtr += 4;
taskPriority = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
battlerArgIndex = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
numArgs = sBattleAnimScriptPtr[0];
sBattleAnimScriptPtr++;
// copy task arguments
for (i = 0; i < numArgs; i++) {
gBattleAnimArgs[i] = T1_READ_16(sBattleAnimScriptPtr);
sBattleAnimScriptPtr += 2;
}
numArgs = GetBattleAnimMoveTargets(battlerArgIndex, targets);
for (i = 0; i < numArgs; i++)
{
gBattleAnimArgs[battlerArgIndex] = targets[i];
taskId = CreateTask(taskFunc, taskPriority);
taskFunc(taskId);
gAnimVisualTaskCount++;
}
}
static void Cmd_delay(void)
{
sBattleAnimScriptPtr++;

View File

@ -4806,8 +4806,8 @@ void AnimTask_CycleMagicalLeafPal(u8 taskId)
void AnimNeedleArmSpike(struct Sprite *sprite)
{
u8 a;
u8 b;
s16 a;
s16 b;
u16 c;
u16 x;
u16 y;
@ -4820,13 +4820,27 @@ void AnimNeedleArmSpike(struct Sprite *sprite)
{
if (gBattleAnimArgs[0] == 0)
{
a = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X_2);
b = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_Y_PIC_OFFSET);
if (IsDoubleBattle())
{
SetAverageBattlerPositions(gBattleAnimAttacker, TRUE, &a, &b);
}
else
{
a = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_X_2);
b = GetBattlerSpriteCoord(gBattleAnimAttacker, BATTLER_COORD_Y_PIC_OFFSET);
}
}
else
{
a = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2);
b = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET);
if (IsDoubleBattle())
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &a, &b);
}
else
{
a = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2);
b = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET);
}
}
sprite->data[0] = gBattleAnimArgs[4];

View File

@ -1149,7 +1149,9 @@ static void StartSendOutAnim(u8 battlerId, bool8 dontClearSubstituteBit)
ClearTemporarySpeciesSpriteData(battlerId, dontClearSubstituteBit);
gBattlerPartyIndexes[battlerId] = gBattleResources->bufferA[battlerId][1];
species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES);
species = GetIllusionMonSpecies(battlerId);
if (species == SPECIES_NONE)
species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES);
gBattleControllerData[battlerId] = CreateInvisibleSpriteWithCallback(SpriteCB_WaitForBattlerBallReleaseAnim);
BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId);
SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battlerId));

View File

@ -1177,7 +1177,9 @@ static void StartSendOutAnim(u8 battlerId, bool8 dontClearSubstituteBit)
ClearTemporarySpeciesSpriteData(battlerId, dontClearSubstituteBit);
gBattlerPartyIndexes[battlerId] = gBattleResources->bufferA[battlerId][1];
species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES);
species = GetIllusionMonSpecies(battlerId);
if (species == SPECIES_NONE)
species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES);
gBattleControllerData[battlerId] = CreateInvisibleSpriteWithCallback(SpriteCB_WaitForBattlerBallReleaseAnim);
BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId);
SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battlerId));

View File

@ -4954,6 +4954,9 @@ static void CheckChosenMoveForEffectsBeforeTurnStarts(void)
case MOVE_BEAK_BLAST:
BattleScriptExecute(BattleScript_BeakBlastSetUp);
return;
case MOVE_SHELL_TRAP:
BattleScriptExecute(BattleScript_ShellTrapSetUp);
return;
}
}
}

View File

@ -774,6 +774,8 @@ static const u8 sText_StatWasHeightened[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s
static const u8 sText_ElectricTerrainActivatedAbility[] = _("The Electric Terrain activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!");
static const u8 sText_AbilityWeakenedSurroundingMonsStat[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY}\nweakened the {B_BUFF1} of\lall surrounding Pokémon!\p");
static const u8 sText_AttackerGainedStrengthFromTheFallen[] = _("{B_ATK_NAME_WITH_PREFIX} gained strength\nfrom the fallen!");
static const u8 sText_PrepareShellTrap[] = _("{B_ATK_NAME_WITH_PREFIX} set a shell trap!");
static const u8 sText_ShellTrapDidntWork[] = _("{B_ATK_NAME_WITH_PREFIX}'s shell trap didn't work!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
@ -800,6 +802,8 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_ZMOVEHPTRAP - BATTLESTRINGS_TABLE_START] = sText_ZMoveHpSwitchInTrap,
[STRINGID_PLAYERLOSTTOENEMYTRAINER - BATTLESTRINGS_TABLE_START] = sText_PlayerLostToEnemyTrainer,
[STRINGID_PLAYERPAIDPRIZEMONEY - BATTLESTRINGS_TABLE_START] = sText_PlayerPaidPrizeMoney,
[STRINGID_SHELLTRAPDIDNTWORK - BATTLESTRINGS_TABLE_START] = sText_ShellTrapDidntWork,
[STRINGID_PREPARESHELLTRAP - BATTLESTRINGS_TABLE_START] = sText_PrepareShellTrap,
[STRINGID_COURTCHANGE - BATTLESTRINGS_TABLE_START] = sText_CourtChange,
[STRINGID_HEATUPBEAK - BATTLESTRINGS_TABLE_START] = sText_HeatingUpBeak,
[STRINGID_METEORBEAMCHARGING - BATTLESTRINGS_TABLE_START] = sText_MeteorBeamCharging,

View File

@ -350,6 +350,7 @@ static bool32 CriticalCapture(u32 odds);
static void BestowItem(u32 battlerAtk, u32 battlerDef);
static bool8 IsFinalStrikeEffect(u16 move);
static void TryUpdateRoundTurnOrder(void);
static bool32 ChangeOrderTargetAfterAttacker(void);
static void Cmd_attackcanceler(void);
static void Cmd_accuracycheck(void);
@ -5017,10 +5018,17 @@ static void Cmd_jumpifabilitypresent(void)
{
CMD_ARGS(u16 ability, const u8 *jumpInstr);
if (IsAbilityOnField(cmd->ability))
u16 ability = cmd->ability;
u32 abilityBattler = IsAbilityOnField(ability);
if (abilityBattler)
{
gBattlerAbility = abilityBattler - 1;
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void Cmd_endselectionscript(void)
@ -5725,6 +5733,22 @@ static void Cmd_moveend(void)
else
gBattleStruct->lastMoveFailed &= ~(gBitTable[gBattlerAttacker]);
// Set ShellTrap to activate after the attacker's turn if target was hit by a physical move.
if (gBattleMoves[gChosenMoveByBattler[gBattlerTarget]].effect == EFFECT_SHELL_TRAP
&& gBattlerTarget != gBattlerAttacker
&& GetBattlerSide(gBattlerTarget) != GetBattlerSide(gBattlerAttacker)
&& gProtectStructs[gBattlerTarget].physicalDmg
&& gProtectStructs[gBattlerTarget].physicalBattlerId == gBattlerAttacker
&& !TestSheerForceFlag(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerTarget].shellTrap = TRUE;
// Change move order in double battles, so the hit mon with shell trap moves immediately after being hit.
if (IsDoubleBattle())
{
ChangeOrderTargetAfterAttacker();
}
}
if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET)
{
gActiveBattler = gBattlerAttacker;
@ -5968,7 +5992,7 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_RED_CARD:
if (gBattleMoves[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET
if ((gBattleMoves[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET || gBattleStruct->hitSwitchTargetFailed)
&& IsBattlerAlive(gBattlerAttacker)
&& !TestSheerForceFlag(gBattlerAttacker, gCurrentMove)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_GUARD_DOG)
@ -6189,6 +6213,7 @@ static void Cmd_moveend(void)
gBattleStruct->targetsDone[gBattlerAttacker] = 0;
gProtectStructs[gBattlerAttacker].usesBouncedMove = FALSE;
gProtectStructs[gBattlerAttacker].targetAffected = FALSE;
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
gBattleStruct->ateBoost[gBattlerAttacker] = 0;
gStatuses3[gBattlerAttacker] &= ~STATUS3_ME_FIRST;
gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE;
@ -6199,6 +6224,7 @@ static void Cmd_moveend(void)
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE;
gBattleStruct->zmove.effect = EFFECT_HIT;
gBattleStruct->hitSwitchTargetFailed = FALSE;
gBattleScripting.moveendState++;
break;
case MOVEEND_COUNT:
@ -6900,6 +6926,35 @@ static void SetDmgHazardsBattlescript(u8 battlerId, u8 multistringId)
gBattlescriptCurrInstr = BattleScript_DmgHazardsOnFaintedBattler;
}
bool32 DoSwitchInAbilitiesItems(u32 battlerId)
{
return (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battlerId, 0, 0, 0)
|| (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battlerId, 0, 0, 0))
|| (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battlerId, 0, 0, 0))
|| ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battlerId, FALSE)
|| AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0));
}
bool32 ShouldPostponeSwitchInAbilities(u32 battlerId)
{
bool32 aliveOpposing1 = IsBattlerAlive(BATTLE_OPPOSITE(battlerId));
bool32 aliveOpposing2 = IsBattlerAlive(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerId)));
// No pokemon on opposing side - postpone.
if (!aliveOpposing1 && !aliveOpposing2)
return TRUE;
// 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))
return TRUE;
if (!aliveOpposing1 && aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerId)), PARTY_SIZE, PARTY_SIZE))
return TRUE;
}
return FALSE;
}
static void Cmd_switchineffects(void)
{
CMD_ARGS(u8 battler);
@ -6924,6 +6979,24 @@ static void Cmd_switchineffects(void)
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SwitchInAbilityMsgRet;
}
// Healing Wish activates before hazards.
// Starting from Gen8 - it heals only pokemon which can be healed. In gens 5,6,7 the effect activates anyways.
else if (((gBattleStruct->storedHealingWish & gBitTable[gActiveBattler]) || (gBattleStruct->storedLunarDance & gBitTable[gActiveBattler]))
&& (gBattleMons[gActiveBattler].hp != gBattleMons[gActiveBattler].maxHP || gBattleMons[gActiveBattler].status1 != 0 || B_HEALING_WISH_SWITCH < GEN_8))
{
if (gBattleStruct->storedHealingWish & gBitTable[gActiveBattler])
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_HealingWishActivates;
gBattleStruct->storedHealingWish &= ~(gBitTable[gActiveBattler]);
}
else // Lunar Dance
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_LunarDanceActivates;
gBattleStruct->storedLunarDance &= ~(gBitTable[gActiveBattler]);
}
}
else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES_DAMAGED)
&& (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES)
&& GetBattlerAbility(gActiveBattler) != ABILITY_MAGIC_GUARD
@ -7016,12 +7089,17 @@ static void Cmd_switchineffects(void)
gDisableStructs[gActiveBattler].truantSwitchInHack = 0;
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gActiveBattler, 0, 0, 0)
|| (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, gActiveBattler, 0, 0, 0))
|| (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, gActiveBattler, 0, 0, 0))
|| ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gActiveBattler, FALSE)
|| AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0))
return;
// Don't activate switch-in abilities if the opposing field is empty.
// This could happen when a mon uses explosion and causes everyone to faint.
if (ShouldPostponeSwitchInAbilities(gActiveBattler) || gBattleStruct->switchInAbilityPostponed)
{
gBattleStruct->switchInAbilityPostponed |= gBitTable[gActiveBattler];
}
else
{
if (DoSwitchInAbilitiesItems(gActiveBattler))
return;
}
gSideStatuses[GetBattlerSide(gActiveBattler)] &= ~(SIDE_STATUS_SPIKES_DAMAGED | SIDE_STATUS_TOXIC_SPIKES_DAMAGED | SIDE_STATUS_STEALTH_ROCK_DAMAGED | SIDE_STATUS_STICKY_WEB_DAMAGED);
@ -8584,6 +8662,38 @@ static bool32 CanTeleport(u8 battlerId)
return TRUE;
}
// Return True if the order was changed, and false if the order was not changed(for example because the target would move after the attacker anyway).
static bool32 ChangeOrderTargetAfterAttacker(void)
{
u32 i;
u8 data[MAX_BATTLERS_COUNT];
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
|| GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget))
return FALSE;
for (i = 0; i < gBattlersCount; i++)
data[i] = gBattlerByTurnOrder[i];
if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 2)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gBattlerByTurnOrder[2] = data[1];
gBattlerByTurnOrder[3] = data[3];
}
else if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 3)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gBattlerByTurnOrder[2] = data[1];
gBattlerByTurnOrder[3] = data[2];
}
else // Attacker == 1, Target == 3
{
gBattlerByTurnOrder[2] = gBattlerTarget;
gBattlerByTurnOrder[3] = data[2];
}
return TRUE;
}
static void Cmd_various(void)
{
CMD_ARGS(u8 battler, u8 id);
@ -9775,7 +9885,7 @@ static void Cmd_various(void)
&& GetBattlerAbility(gBattlerTarget) != ABILITY_GUARD_DOG)
{
gBattleScripting.switchCase = B_SWITCH_HIT;
gBattlescriptCurrInstr = BattleScript_ForceRandomSwitch;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
@ -9951,34 +10061,14 @@ static void Cmd_various(void)
case VARIOUS_AFTER_YOU:
{
VARIOUS_ARGS(const u8 *failInstr);
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
|| GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget))
if (ChangeOrderTargetAfterAttacker())
{
gBattlescriptCurrInstr = cmd->failInstr;
gSpecialStatuses[gBattlerTarget].afterYou = 1;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
for (i = 0; i < gBattlersCount; i++)
data[i] = gBattlerByTurnOrder[i];
if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 2)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gBattlerByTurnOrder[2] = data[1];
gBattlerByTurnOrder[3] = data[3];
}
else if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 3)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gBattlerByTurnOrder[2] = data[1];
gBattlerByTurnOrder[3] = data[2];
}
else
{
gBattlerByTurnOrder[2] = gBattlerTarget;
gBattlerByTurnOrder[3] = data[2];
}
gSpecialStatuses[gBattlerTarget].afterYou = 1;
gBattlescriptCurrInstr = cmd->nextInstr;
gBattlescriptCurrInstr = cmd->failInstr;
}
return;
}
@ -11009,6 +11099,15 @@ static void Cmd_various(void)
AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, gActiveBattler, 0, 0, 0);
return;
}
case VARIOUS_JUMP_IF_SHELL_TRAP:
{
VARIOUS_ARGS(const u8 *jumpInstr);
if (gProtectStructs[gActiveBattler].shellTrap)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES:
{
VARIOUS_ARGS();
@ -11016,22 +11115,6 @@ static void Cmd_various(void)
AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, gActiveBattler, 0, 0, 0);
return;
}
case VARIOUS_JUMP_IF_NO_VALID_TARGETS:
{
VARIOUS_ARGS(const u8 *jumpInstr);
u32 count = 0;
for (i = 0; i < gBattlersCount; i++)
{
if (GetBattlerSide(i) != GetBattlerSide(gBattlerAttacker) && IsBattlerAlive(i))
count++;
}
if (count == 0)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_JUMP_IF_EMERGENCY_EXITED:
{
VARIOUS_ARGS(const u8 *jumpInstr);
@ -11041,6 +11124,22 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_STORE_HEALING_WISH:
{
VARIOUS_ARGS();
if (gCurrentMove == MOVE_LUNAR_DANCE)
gBattleStruct->storedLunarDance |= gBitTable[gActiveBattler];
else
gBattleStruct->storedHealingWish |= gBitTable[gActiveBattler];
break;
}
case VARIOUS_HIT_SWITCH_TARGET_FAILED:
{
VARIOUS_ARGS();
gBattleStruct->hitSwitchTargetFailed = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
} // End of switch (cmd->id)
gBattlescriptCurrInstr = cmd->nextInstr;
@ -11149,14 +11248,15 @@ static void Cmd_tryexplosion(void)
{
CMD_ARGS();
u32 dampBattler;
if (gBattleControllerExecFlags)
return;
if ((gBattlerTarget = IsAbilityOnField(ABILITY_DAMP)))
if ((dampBattler = IsAbilityOnField(ABILITY_DAMP)))
{
// Failed, a battler has Damp
gLastUsedAbility = ABILITY_DAMP;
RecordAbilityBattle(--gBattlerTarget, ABILITY_DAMP);
gBattlerTarget = --dampBattler;
gBattlescriptCurrInstr = BattleScript_DampStopsExplosion;
return;
}
@ -11166,14 +11266,6 @@ static void Cmd_tryexplosion(void)
BtlController_EmitHealthBarUpdate(BUFFER_A, INSTANT_HP_BAR_DROP);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = cmd->nextInstr;
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
if (gBattlerTarget == gBattlerAttacker)
continue;
if (IsBattlerAlive(gBattlerTarget))
break;
}
}
static void Cmd_setatkhptozero(void)
@ -12063,8 +12155,8 @@ static void Cmd_forcerandomswitch(void)
s32 lastMonId = 0; // + 1
s32 monsCount;
struct Pokemon *party = NULL;
s32 validMons = 0;
s32 minNeeded;
u8 validMons[PARTY_SIZE];
s32 validMonsCount = 0;
bool32 redCardForcedSwitch = FALSE;
@ -12121,7 +12213,6 @@ static void Cmd_forcerandomswitch(void)
firstMonId = 0;
lastMonId = 6;
monsCount = 6;
minNeeded = 2;
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
}
@ -12140,7 +12231,6 @@ static void Cmd_forcerandomswitch(void)
lastMonId = PARTY_SIZE / 2;
}
monsCount = PARTY_SIZE / 2;
minNeeded = 1;
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
}
@ -12158,7 +12248,6 @@ static void Cmd_forcerandomswitch(void)
lastMonId = PARTY_SIZE / 2;
}
monsCount = PARTY_SIZE / 2;
minNeeded = 1;
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
}
@ -12169,7 +12258,6 @@ static void Cmd_forcerandomswitch(void)
firstMonId = 0;
lastMonId = PARTY_SIZE;
monsCount = PARTY_SIZE;
minNeeded = 2; // since there are two opponents, it has to be a double battle
}
else
{
@ -12184,7 +12272,6 @@ static void Cmd_forcerandomswitch(void)
lastMonId = PARTY_SIZE / 2;
}
monsCount = PARTY_SIZE / 2;
minNeeded = 1;
}
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
@ -12194,7 +12281,6 @@ static void Cmd_forcerandomswitch(void)
firstMonId = 0;
lastMonId = PARTY_SIZE;
monsCount = PARTY_SIZE;
minNeeded = 2;
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
}
@ -12203,7 +12289,6 @@ static void Cmd_forcerandomswitch(void)
firstMonId = 0;
lastMonId = PARTY_SIZE;
monsCount = PARTY_SIZE;
minNeeded = 1;
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget]; // there is only one pokemon out in single battles
battler1PartyId = gBattlerPartyIndexes[gBattlerTarget];
}
@ -12212,13 +12297,15 @@ static void Cmd_forcerandomswitch(void)
{
if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
&& GetMonData(&party[i], MON_DATA_HP) != 0)
&& GetMonData(&party[i], MON_DATA_HP) != 0
&& i != battler1PartyId
&& i != battler2PartyId)
{
validMons++;
validMons[validMonsCount++] = i;
}
}
if (!redCardForcedSwitch && validMons <= minNeeded)
if (validMonsCount == 0)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -12226,21 +12313,8 @@ static void Cmd_forcerandomswitch(void)
{
*(gBattleStruct->battlerPartyIndexes + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget];
gBattlescriptCurrInstr = BattleScript_RoarSuccessSwitch;
gBattleStruct->forcedSwitch |= gBitTable[gBattlerTarget];
do
{
i = Random() % monsCount;
i += firstMonId;
}
while (i == battler2PartyId
|| i == battler1PartyId
|| GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_NONE
|| GetMonData(&party[i], MON_DATA_IS_EGG) == TRUE
|| GetMonData(&party[i], MON_DATA_HP) == 0);
*(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = i;
*(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = validMons[Random() % validMonsCount];
if (!IsMultiBattle())
SwitchPartyOrder(gBattlerTarget);
@ -14898,55 +14972,18 @@ static void Cmd_pickup(void)
{
CMD_ARGS();
s32 i;
u16 species, heldItem;
u16 ability;
u32 i, j;
u16 species, heldItem, ability;
u8 lvlDivBy10;
if (InBattlePike())
{
}
else if (InBattlePyramid())
if (!InBattlePike()) // No items in Battle Pike.
{
bool32 isInPyramid = InBattlePyramid_();
for (i = 0; i < PARTY_SIZE; i++)
{
species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2);
heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
ability = gSpeciesInfo[species].abilities[GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM)];
if (ability == ABILITY_PICKUP
&& species != SPECIES_NONE
&& species != SPECIES_EGG
&& heldItem == ITEM_NONE
&& (Random() % 10) == 0)
{
heldItem = GetBattlePyramidPickupItemId();
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
}
#if (defined ITEM_HONEY)
else if (ability == ABILITY_HONEY_GATHER
&& species != 0
&& species != SPECIES_EGG
&& heldItem == ITEM_NONE)
{
if ((lvlDivBy10 + 1 ) * 5 > Random() % 100)
{
heldItem = ITEM_HONEY;
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
}
}
#endif
}
}
else
{
for (i = 0; i < PARTY_SIZE; i++)
{
species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2);
heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
lvlDivBy10 = (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL)-1) / 10; //Moving this here makes it easier to add in abilities like Honey Gather
lvlDivBy10 = (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL)-1) / 10; //Moving this here makes it easier to add in abilities like Honey Gather.
if (lvlDivBy10 > 9)
lvlDivBy10 = 9;
@ -14958,24 +14995,30 @@ static void Cmd_pickup(void)
&& heldItem == ITEM_NONE
&& (Random() % 10) == 0)
{
s32 j;
s32 rand = Random() % 100;
for (j = 0; j < (int)ARRAY_COUNT(sPickupProbabilities); j++)
if (isInPyramid)
{
if (sPickupProbabilities[j] > rand)
heldItem = GetBattlePyramidPickupItemId();
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
}
else
{
u32 rand = Random() % 100;
for (j = 0; j < ARRAY_COUNT(sPickupProbabilities); j++)
{
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupItems[lvlDivBy10 + j]);
break;
}
else if (rand == 99 || rand == 98)
{
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sRarePickupItems[lvlDivBy10 + (99 - rand)]);
break;
if (sPickupProbabilities[j] > rand)
{
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupItems[lvlDivBy10 + j]);
break;
}
else if (rand == 99 || rand == 98)
{
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sRarePickupItems[lvlDivBy10 + (99 - rand)]);
break;
}
}
}
}
#if (defined ITEM_HONEY)
else if (ability == ABILITY_HONEY_GATHER
&& species != 0
&& species != SPECIES_EGG
@ -14987,6 +15030,14 @@ static void Cmd_pickup(void)
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
}
}
#if P_SHUCKLE_BERRY_JUICE == TRUE
else if (species == SPECIES_SHUCKLE
&& heldItem == ITEM_ORAN_BERRY
&& (Random() % 16) == 0)
{
heldItem = ITEM_BERRY_JUICE;
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
}
#endif
}
}
@ -16041,6 +16092,17 @@ void BS_CalcMetalBurstDmg(void)
}
}
void BS_JumpIfMoreThanHalfHP(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u8 battler = GetBattlerForBattleScript(cmd->battler);
if (gBattleMons[battler].hp > (gBattleMons[battler].maxHP + 1) / 2)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfHoldEffect(void)
{
u8 battler = gBattlescriptCurrInstr[5];
@ -16076,10 +16138,8 @@ static bool32 CriticalCapture(u32 odds)
else
odds = (odds * 250) / 100;
#ifdef ITEM_CATCHING_CHARM
if (CheckBagHasItem(ITEM_CATCHING_CHARM, 1))
odds = (odds * (100 + B_CATCHING_CHARM_BOOST)) / 100;
#endif
odds /= 6;
if ((Random() % 255) < odds)

View File

@ -948,7 +948,8 @@ void HandleAction_ActionFinished(void)
u8 battler1 = gBattlerByTurnOrder[i];
u8 battler2 = gBattlerByTurnOrder[j];
if (gProtectStructs[battler1].quash || gProtectStructs[battler2].quash)
if (gProtectStructs[battler1].quash || gProtectStructs[battler2].quash
|| gProtectStructs[battler1].shellTrap || gProtectStructs[battler2].shellTrap)
continue;
// We recalculate order only for action of the same priority. If any action other than switch/move has been taken, they should
@ -3301,7 +3302,7 @@ bool8 HandleWishPerishSongOnTurnEnd(void)
return FALSE;
}
#define FAINTED_ACTIONS_MAX_CASE 7
#define FAINTED_ACTIONS_MAX_CASE 8
bool8 HandleFaintedMonActions(void)
{
@ -3386,7 +3387,19 @@ bool8 HandleFaintedMonActions(void)
else
gBattleStruct->faintedActionsState = 4;
break;
case 6:
case 6: // All battlers switch-in abilities happen here to prevent them happening against an empty field.
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleStruct->switchInAbilityPostponed & gBitTable[i])
{
if (DoSwitchInAbilitiesItems(i))
return TRUE;
gBattleStruct->switchInAbilityPostponed &= ~(gBitTable[i]);
}
}
gBattleStruct->faintedActionsState++;
break;
case 7:
if (ItemBattleEffects(ITEMEFFECT_NORMAL, 0, TRUE))
return TRUE;
gBattleStruct->faintedActionsState++;
@ -4749,6 +4762,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
case ABILITY_INTIMIDATE:
if (!gSpecialStatuses[battler].switchInAbilityDone)
{
gBattlerAttacker = battler;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
SET_STATCHANGER(STAT_ATK, 1, TRUE);
BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivates);
@ -10266,6 +10280,14 @@ void ClearIllusionMon(u32 battlerId)
memset(&gBattleStruct->illusion[battlerId], 0, sizeof(gBattleStruct->illusion[battlerId]));
}
u32 GetIllusionMonSpecies(u32 battlerId)
{
struct Pokemon *illusionMon = GetIllusionMonPtr(battlerId);
if (illusionMon != NULL)
return GetMonData(illusionMon, MON_DATA_SPECIES);
return SPECIES_NONE;
}
bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId)
{
struct Pokemon *party, *partnerMon;

View File

@ -204,11 +204,7 @@ bool32 IsViableZMove(u8 battlerId, u16 move)
else
holdEffect = ItemId_GetHoldEffect(item);
#ifdef ITEM_ULTRANECROZIUM_Z
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || item == ITEM_ULTRANECROZIUM_Z)
#else
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
#endif
{
u16 zMove = GetSignatureZMove(move, gBattleMons[battlerId].species, item);
if (zMove != MOVE_NONE)

View File

@ -6833,7 +6833,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
.accuracy = 100,
.pp = 15,
.secondaryEffectChance = 0,
.target = MOVE_TARGET_SELECTED,
.target = MOVE_TARGET_BOTH,
.priority = 0,
.split = SPLIT_STATUS,
.zMovePower = 0,
@ -11613,7 +11613,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,
@ -13294,7 +13294,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
.effect = EFFECT_DEFENSE_UP_HIT,
.power = 70,
.type = TYPE_PSYCHIC,
.accuracy = 100,
.accuracy = 90,
.pp = 10,
.secondaryEffectChance = 100,
.target = MOVE_TARGET_SELECTED,

View File

@ -5165,7 +5165,6 @@ void ItemUseCB_RareCandy(u8 taskId, TaskFunc task)
u16 *itemPtr = &gSpecialVar_ItemId;
bool8 cannotUseEffect;
u8 holdEffectParam = ItemId_GetHoldEffectParam(*itemPtr);
u16 targetSpecies = GetEvolutionTargetSpecies(mon, EVO_MODE_NORMAL, ITEM_NONE, NULL);
sInitialLevel = GetMonData(mon, MON_DATA_LEVEL);
if (sInitialLevel != MAX_LEVEL)
@ -5181,10 +5180,22 @@ void ItemUseCB_RareCandy(u8 taskId, TaskFunc task)
PlaySE(SE_SELECT);
if (cannotUseEffect)
{
if (targetSpecies != SPECIES_NONE && holdEffectParam == 0)
u16 targetSpecies = SPECIES_NONE;
// Resets values to 0 so other means of teaching moves doesn't overwrite levels
sInitialLevel = 0;
sFinalLevel = 0;
if (holdEffectParam == 0)
targetSpecies = GetEvolutionTargetSpecies(mon, EVO_MODE_NORMAL, ITEM_NONE, NULL);
if (targetSpecies != SPECIES_NONE)
{
PartyMenuTryEvolution(taskId);
RemoveBagItem(gSpecialVar_ItemId, 1);
FreePartyPointers();
gCB2_AfterEvolution = gPartyMenu.exitCallback;
BeginEvolutionScene(mon, targetSpecies, TRUE, gPartyMenu.slotId);
DestroyTask(taskId);
}
else
{

View File

@ -15,6 +15,6 @@ SINGLE_BATTLE_TEST("Blaze boosts Fire-type moves in a pinch", s16 damage)
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_GT(results[1].damage, results[0].damage);
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}

View File

@ -19,6 +19,26 @@ SINGLE_BATTLE_TEST("Damp prevents explosion-like moves from enemies")
}
}
DOUBLE_BATTLE_TEST("Damp prevents explosion-like moves from enemies in a double battle")
{
u32 move;
PARAMETRIZE { move = MOVE_EXPLOSION; }
PARAMETRIZE { move = MOVE_SELF_DESTRUCT; }
PARAMETRIZE { move = MOVE_MIND_BLOWN; }
PARAMETRIZE { move = MOVE_MISTY_EXPLOSION; }
GIVEN {
PLAYER(SPECIES_PARAS) { Ability(ABILITY_DAMP); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, move); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_DAMP);
NONE_OF { HP_BAR(playerLeft); HP_BAR(opponentLeft); HP_BAR(playerRight); HP_BAR(opponentRight); }
}
}
SINGLE_BATTLE_TEST("Damp prevents explosion-like moves from self")
{
u32 move;

95
test/ability_download.c Normal file
View File

@ -0,0 +1,95 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
ASSUME(gBattleMoves[MOVE_TRI_ATTACK].split == SPLIT_SPECIAL);
}
SINGLE_BATTLE_TEST("Download raises Attack if player has lower Def than Sp.Def", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_TRACE; }
PARAMETRIZE { ability = ABILITY_DOWNLOAD; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) {Defense(100); SpDefense(200); };
OPPONENT(SPECIES_PORYGON) { Ability(ability); Attack(100); };
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
if (ability == ABILITY_DOWNLOAD)
{
ABILITY_POPUP(opponent, ABILITY_DOWNLOAD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Porygon's Download raised its attack!");
}
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Download raises Sp.Attack if enemy has lower Sp.Def than Def", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_TRACE; }
PARAMETRIZE { ability = ABILITY_DOWNLOAD; }
GIVEN {
PLAYER(SPECIES_PORYGON) { Ability(ability); SpAttack(100); };
OPPONENT(SPECIES_WOBBUFFET) {Defense(200); SpDefense(100); };
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
} SCENE {
if (ability == ABILITY_DOWNLOAD)
{
ABILITY_POPUP(player, ABILITY_DOWNLOAD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Porygon's Download raised its sp. attack!");
}
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet", s16 damagePhysical, s16 damageSpecial)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_TRACE; }
PARAMETRIZE { ability = ABILITY_DOWNLOAD; }
GIVEN {
ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION);
PLAYER(SPECIES_WOBBUFFET) { Speed(100); };
PLAYER(SPECIES_PORYGON) { Ability(ability); Defense(400); SpDefense(300); Speed(300); Attack(100); };
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(100); };
OPPONENT(SPECIES_PORYGON2) { Ability(ability); Defense(100); SpDefense(200); Speed(200); };
} WHEN {
TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); }
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TRI_ATTACK);}
} SCENE {
HP_BAR(player, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
// Everyone faints.
MESSAGE("Go! Porygon!");
MESSAGE("2 sent out Porygon2!");
if (ability == ABILITY_DOWNLOAD)
{
ABILITY_POPUP(player, ABILITY_DOWNLOAD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Porygon's Download raised its attack!");
ABILITY_POPUP(opponent, ABILITY_DOWNLOAD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Porygon2's Download raised its sp. attack!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
HP_BAR(opponent, captureDamage: &results[i].damagePhysical);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, opponent);
HP_BAR(player, captureDamage: &results[i].damageSpecial);
} FINALLY {
EXPECT_MUL_EQ(results[0].damagePhysical, Q_4_12(1.5), results[1].damagePhysical);
EXPECT_MUL_EQ(results[0].damageSpecial, Q_4_12(1.5), results[1].damageSpecial);
}
}

View File

@ -20,7 +20,11 @@ SINGLE_BATTLE_TEST("Intimidate (opponent) lowers player's attack after switch ou
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
if (ability == ABILITY_INTIMIDATE)
{
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Foe Staraptor's Intimidate cuts Wobbuffet's ATTACK!");
}
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
@ -30,21 +34,64 @@ SINGLE_BATTLE_TEST("Intimidate (opponent) lowers player's attack after switch ou
SINGLE_BATTLE_TEST("Intimidate (opponent) lowers player's attack after KO", s16 damage)
{
u32 ability;
KNOWN_FAILING;
PARAMETRIZE { ability = ABILITY_INTIMIDATE; }
PARAMETRIZE { ability = ABILITY_RECKLESS; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(2); };
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); };
OPPONENT(SPECIES_STARAPTOR) { Ability(ABILITY_INTIMIDATE); Speed(1); };
OPPONENT(SPECIES_STARAPTOR) { Ability(ability); Speed(1); };
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); SEND_OUT(opponent, 1); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
HP_BAR(opponent);
if (ability == ABILITY_INTIMIDATE)
{
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Foe Staraptor's Intimidate cuts Wobbuffet's ATTACK!");
}
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}
DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double battle")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION);
PLAYER(SPECIES_WOBBUFFET) { };
PLAYER(SPECIES_WOBBUFFET) { HP(1); };
PLAYER(SPECIES_STARAVIA) { Ability(ABILITY_INTIMIDATE); };
PLAYER(SPECIES_ABRA);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); };
OPPONENT(SPECIES_WOBBUFFET) { HP(1); };
OPPONENT(SPECIES_STARAPTOR) { Ability(ABILITY_INTIMIDATE); };
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(playerRight, 3); SEND_OUT(opponentRight, 3); }
TURN { MOVE(playerLeft, MOVE_CELEBRATE);}
} SCENE {
HP_BAR(playerLeft, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, playerLeft);
// Everyone faints.
MESSAGE("Go! Staravia!");
MESSAGE("2 sent out Staraptor!");
MESSAGE("Go! Abra!");
MESSAGE("2 sent out Wynaut!");
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Staravia's Intimidate cuts Foe Staraptor's ATTACK!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("Staravia's Intimidate cuts Foe Wynaut's ATTACK!");
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Foe Staraptor's Intimidate cuts Staravia's ATTACK!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Foe Staraptor's Intimidate cuts Abra's ATTACK!");
}
}

20
test/ability_overgrow.c Normal file
View File

@ -0,0 +1,20 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Overgrow boosts Grass-type moves in a pinch", s16 damage)
{
u16 hp;
PARAMETRIZE { hp = 99; }
PARAMETRIZE { hp = 33; }
GIVEN {
ASSUME(gBattleMoves[MOVE_VINE_WHIP].type == TYPE_GRASS);
PLAYER(SPECIES_BULBASAUR) { Ability(ABILITY_OVERGROW); MaxHP(99); HP(hp); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_VINE_WHIP); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}

20
test/ability_swarm.c Normal file
View File

@ -0,0 +1,20 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Swarm boosts Bug-type moves in a pinch", s16 damage)
{
u16 hp;
PARAMETRIZE { hp = 99; }
PARAMETRIZE { hp = 33; }
GIVEN {
ASSUME(gBattleMoves[MOVE_BUG_BITE].type == TYPE_BUG);
PLAYER(SPECIES_LEDYBA) { Ability(ABILITY_SWARM); MaxHP(99); HP(hp); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BUG_BITE); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}

20
test/ability_torrent.c Normal file
View File

@ -0,0 +1,20 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Torrent boosts Water-type moves in a pinch", s16 damage)
{
u16 hp;
PARAMETRIZE { hp = 99; }
PARAMETRIZE { hp = 33; }
GIVEN {
ASSUME(gBattleMoves[MOVE_BUBBLE].type == TYPE_WATER);
PLAYER(SPECIES_SQUIRTLE) { Ability(ABILITY_TORRENT); MaxHP(99); HP(hp); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BUBBLE); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}

View File

@ -63,6 +63,30 @@ SINGLE_BATTLE_TEST("Volt Absorb is only triggered once on multi strike moves")
}
}
DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from damaging other pokemon", s16 damage1, s16 damage2) // Fixed issue #1961
{
GIVEN {
ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION);
ASSUME(gBattleMoves[MOVE_EXPLOSION].type == TYPE_NORMAL);
PLAYER(SPECIES_JOLTEON) { Ability(ABILITY_VOLT_ABSORB); HP(1); MaxHP(TEST_MAX_HP); }
PLAYER(SPECIES_ABRA);
OPPONENT(SPECIES_GRAVELER_ALOLAN) { Ability(ABILITY_GALVANIZE); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_EXPLOSION); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_VOLT_ABSORB);
HP_BAR(playerLeft, hp: TEST_MAX_HP / 4 + 1);
MESSAGE("Jolteon restored HP using its Volt Absorb!");
HP_BAR(playerRight, captureDamage: &results->damage1);
HP_BAR(opponentRight, captureDamage: &results->damage2);
}
FINALLY {
EXPECT_NE(results[0].damage1, 0);
EXPECT_NE(results[0].damage2, 0);
}
}
SINGLE_BATTLE_TEST("Volt Absorb prevents Cell Battery from activating")
{
GIVEN {
@ -75,11 +99,11 @@ SINGLE_BATTLE_TEST("Volt Absorb prevents Cell Battery from activating")
ABILITY_POPUP(player, ABILITY_VOLT_ABSORB);
HP_BAR(player, hp: TEST_MAX_HP / 4 + 1);
MESSAGE("Jolteon restored HP using its Volt Absorb!");
NONE_OF {
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Using Cell Battery, the attack of Jolteon rose!");
}
}
}

378
test/hold_effect_red_card.c Normal file
View File

@ -0,0 +1,378 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gItems[ITEM_RED_CARD].holdEffect == HOLD_EFFECT_RED_CARD);
}
SINGLE_BATTLE_TEST("Red Card switches the attacker with a random non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_CHARMANDER);
OPPONENT(SPECIES_SQUIRTLE) { HP(0); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
MESSAGE("Foe Bulbasaur was dragged out!");
}
}
DOUBLE_BATTLE_TEST("Red Card switches the target with a random non-battler, non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_CHARMANDER);
OPPONENT(SPECIES_SQUIRTLE) { HP(0); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
MESSAGE("Foe Bulbasaur was dragged out!");
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if holder faints")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); Item(ITEM_RED_CARD); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if target is behind a Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
}
SINGLE_BATTLE_TEST("Red Card activates after the last hit of a multi-hit move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_DOUBLE_KICK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_KICK, opponent);
HP_BAR(player);
HP_BAR(player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if no replacements")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if replacements fainted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT) { HP(0); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if knocked off")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_KNOCK_OFF); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if stolen by a move")
{
u32 item;
bool32 activate;
PARAMETRIZE { item = ITEM_NONE; activate = FALSE; }
PARAMETRIZE { item = ITEM_POTION; activate = TRUE; }
ASSUME(gBattleMoves[MOVE_THIEF].effect == EFFECT_THIEF);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET) { Item(item); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_THIEF); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THIEF, opponent);
if (activate) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
} else {
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if stolen by Magician")
{
u32 item;
bool32 activate;
PARAMETRIZE { item = ITEM_NONE; activate = FALSE; }
PARAMETRIZE { item = ITEM_POTION; activate = TRUE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_FENNEKIN) { Ability(ABILITY_MAGICIAN); Item(item); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
if (activate) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Fennekin!");
} else {
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Fennekin!");
}
}
}
}
DOUBLE_BATTLE_TEST("Red Card activates for only the fastest target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(3); Item(ITEM_RED_CARD); }
PLAYER(SPECIES_WYNAUT) { Speed(2); Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WYNAUT) { Speed(4); }
OPPONENT(SPECIES_UNOWN) { Speed(1); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_ROCK_SLIDE);
MOVE(opponentRight, MOVE_TACKLE, target: playerRight);
}
} SCENE {
// Fastest target's Red Card activates.
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_SLIDE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
MESSAGE("Foe Unown was dragged out!");
// Slower target's Red Card still able to activate on other battler.
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
MESSAGE("Wynaut held up its Red Card against Foe Wynaut!");
MESSAGE("Foe Wobbuffet was dragged out!");
}
}
DOUBLE_BATTLE_TEST("Red Card activates but fails if the attacker is rooted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_UNOWN);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_INGRAIN); }
TURN {
MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft);
MOVE(opponentRight, MOVE_TACKLE, target: playerLeft);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
MESSAGE("Foe Wobbuffet anchored itself with its roots!");
// Red Card already consumed so cannot activate.
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
MESSAGE("Wynaut held up its Red Card against Foe Wynaut!");
}
}
}
DOUBLE_BATTLE_TEST("Red Card activates but fails if the attacker has Suction Cups")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_OCTILLERY) { Ability(ABILITY_SUCTION_CUPS); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_UNOWN);
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft);
MOVE(opponentRight, MOVE_TACKLE, target: playerLeft);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
MESSAGE("Wobbuffet held up its Red Card against Foe Octillery!");
MESSAGE("Foe Octillery anchors itself with Suction Cups!");
// Red Card already consumed so cannot activate.
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
MESSAGE("Wynaut held up its Red Card against Foe Wynaut!");
}
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if switched by Dragon Tail")
{
bool32 hasWynaut, activate;
PARAMETRIZE { hasWynaut = TRUE; activate = FALSE; }
PARAMETRIZE { hasWynaut = FALSE; activate = TRUE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
if (hasWynaut) PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponent);
if (activate) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
} else {
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
}
}
SINGLE_BATTLE_TEST("Red Card activates and overrides U-turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_U_TURN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Red Card does not activate if attacker's Sheer Force applied")
{
u32 move;
bool32 activate;
PARAMETRIZE { move = MOVE_TACKLE; activate = TRUE; }
PARAMETRIZE { move = MOVE_STOMP; activate = FALSE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_TAUROS) { Ability(ABILITY_SHEER_FORCE); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (activate) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Tauros!");
} else {
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Wobbuffet held up its Red Card against Foe Tauros!");
}
}
}
}
SINGLE_BATTLE_TEST("Red Card activates before Emergency Exit")
{
GIVEN {
PLAYER(SPECIES_GOLISOPOD) { MaxHP(100); HP(51); Item(ITEM_RED_CARD); }
PLAYER(SPECIES_WIMPOD);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
MESSAGE("Golisopod held up its Red Card against Foe Wobbuffet!");
ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT);
MESSAGE("Go! Wimpod!");
}
}
// SINGLE_BATTLE_TEST("Red Card activates but fails if the attacker has Dynamaxed")

View File

@ -8,7 +8,6 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Explosion causes the user to faint")
{
u16 remainingHP;
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -17,12 +16,29 @@ SINGLE_BATTLE_TEST("Explosion causes the user to faint")
} SCENE {
HP_BAR(player, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
MESSAGE("Wobbuffet fainted!");
}
}
SINGLE_BATTLE_TEST("Explosion causes the user & the target to faint")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_EXPLOSION); }
} SCENE {
HP_BAR(player, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
HP_BAR(opponent, hp: 0);
MESSAGE("Foe Wobbuffet fainted!");
MESSAGE("Wobbuffet fainted!");
}
}
SINGLE_BATTLE_TEST("Explosion causes the user to faint even if it misses")
{
u16 remainingHP;
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -31,12 +47,12 @@ SINGLE_BATTLE_TEST("Explosion causes the user to faint even if it misses")
} SCENE {
HP_BAR(player, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
MESSAGE("Wobbuffet fainted!");
}
}
SINGLE_BATTLE_TEST("Explosion causes the user to faint even if it has no effect")
{
u16 remainingHP;
GIVEN {
ASSUME(gBattleMoves[MOVE_EXPLOSION].type == TYPE_NORMAL);
ASSUME(gSpeciesInfo[SPECIES_GASTLY].types[0] == TYPE_GHOST);
@ -49,5 +65,29 @@ SINGLE_BATTLE_TEST("Explosion causes the user to faint even if it has no effect"
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
MESSAGE("It doesn't affect Foe Gastly…");
NOT HP_BAR(opponent);
MESSAGE("Wobbuffet fainted!");
}
}
DOUBLE_BATTLE_TEST("Explosion causes everyone to faint in a double battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT) { HP(1); }
OPPONENT(SPECIES_ABRA) { HP(1); }
OPPONENT(SPECIES_KADABRA) { HP(1); }
OPPONENT(SPECIES_KADABRA);
} WHEN {
TURN { MOVE(playerLeft, MOVE_EXPLOSION); }
} SCENE {
HP_BAR(playerLeft, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, playerLeft);
HP_BAR(opponentLeft, hp: 0);
MESSAGE("Foe Abra fainted!");
HP_BAR(playerRight, hp: 0);
MESSAGE("Wynaut fainted!");
HP_BAR(opponentRight, hp: 0);
MESSAGE("Foe Kadabra fainted!");
MESSAGE("Wobbuffet fainted!");
}
}

View File

@ -0,0 +1,80 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_HEALING_WISH].effect == EFFECT_HEALING_WISH);
ASSUME(gBattleMoves[MOVE_LUNAR_DANCE].effect == EFFECT_HEALING_WISH);
}
#define TEST_MAX_HP (100)
SINGLE_BATTLE_TEST("Healing Wish causes the user to faint and fully heals the replacement")
{
GIVEN {
ASSUME(B_HEALING_WISH_SWITCH >= GEN_5);
PLAYER(SPECIES_GARDEVOIR);
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(TEST_MAX_HP); Status1(STATUS1_POISON); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HEALING_WISH); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, player);
HP_BAR(player, hp: 0);
MESSAGE("Gardevoir fainted!");
MESSAGE("The healing wish came true for Wynaut!");
HP_BAR(player, hp: TEST_MAX_HP);
STATUS_ICON(player, none: TRUE);
MESSAGE("Wynaut regained health!");
}
}
DOUBLE_BATTLE_TEST("Lunar Dance causes the user to faint and fully heals the replacement in a double battle")
{
GIVEN {
ASSUME(B_HEALING_WISH_SWITCH >= GEN_5);
PLAYER(SPECIES_GARDEVOIR) { Speed(300); }
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }
PLAYER(SPECIES_WYNAUT) { HP(TEST_MAX_HP - 1); MaxHP(TEST_MAX_HP); Status1(STATUS1_BURN); Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_LUNAR_DANCE); SEND_OUT(playerLeft, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_LUNAR_DANCE, playerLeft);
HP_BAR(playerLeft, hp: 0);
MESSAGE("Gardevoir fainted!");
MESSAGE("Wynaut became cloaked in mystical moonlight!");
HP_BAR(playerLeft, hp: TEST_MAX_HP);
STATUS_ICON(playerLeft, none: TRUE);
MESSAGE("Wynaut regained health!");
}
}
SINGLE_BATTLE_TEST("Healing Wish effect activates only if the switched pokemon can be healed")
{
GIVEN {
ASSUME(B_HEALING_WISH_SWITCH >= GEN_8);
PLAYER(SPECIES_GARDEVOIR) { Speed(300); }
PLAYER(SPECIES_NINJASK) { Speed(400); }
PLAYER(SPECIES_WYNAUT) { HP(TEST_MAX_HP / 2); MaxHP(TEST_MAX_HP); Status1(STATUS1_PARALYSIS); Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) {Speed(50); }
} WHEN {
TURN { MOVE(player, MOVE_HEALING_WISH); SEND_OUT(player, 1); }
TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, player);
HP_BAR(player, hp: 0);
MESSAGE("Gardevoir fainted!");
NONE_OF {
MESSAGE("The healing wish came true for Wynaut!");
MESSAGE("Wynaut regained health!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
MESSAGE("Do it! Wynaut!");
MESSAGE("The healing wish came true for Wynaut!");
HP_BAR(player, hp: TEST_MAX_HP);
STATUS_ICON(player, none: TRUE);
MESSAGE("Wynaut regained health!");
}
}

View File

@ -0,0 +1,72 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_DRAGON_TAIL].effect == EFFECT_HIT_SWITCH_TARGET);
ASSUME(gBattleMoves[MOVE_LOCK_ON].effect == EFFECT_LOCK_ON);
}
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);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_CHARMANDER);
OPPONENT(SPECIES_SQUIRTLE) { HP(0); }
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, player);
MESSAGE("Foe Bulbasaur was dragged out!");
}
}
DOUBLE_BATTLE_TEST("Dragon Tail switches the target with a random non-battler, non-fainted replacement")
{
PASSES_RANDOMLY(90 * 1, 100 * 2);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_CHARMANDER);
OPPONENT(SPECIES_SQUIRTLE) { HP(0); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_DRAGON_TAIL, target: opponentRight); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, playerLeft);
MESSAGE("Foe Bulbasaur was dragged out!");
}
}
SINGLE_BATTLE_TEST("Dragon Tail does not fail if no replacements")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, player);
NOT MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Dragon Tail does not fail if replacements fainted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT) { HP(0); }
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, player);
NOT MESSAGE("But it failed!");
}
}

View File

@ -0,0 +1,109 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_MIND_BLOWN].effect == EFFECT_MIND_BLOWN);
}
#define HP_TEST (400)
SINGLE_BATTLE_TEST("Mind Blown makes the user lose 1/2 of its HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(HP_TEST); MaxHP(HP_TEST); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MIND_BLOWN); }
} SCENE {
HP_BAR(player, hp: HP_TEST / 2);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIND_BLOWN, player);
NOT MESSAGE("Wobbuffet fainted!"); // Wobb had more than 1/2 of its HP, so it can't faint.
}
}
DOUBLE_BATTLE_TEST("Mind Blown makes the user lose 1/2 of its HP in a double battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(HP_TEST); MaxHP(HP_TEST); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_MIND_BLOWN); }
} SCENE {
HP_BAR(playerLeft, hp: HP_TEST / 2);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIND_BLOWN, playerLeft);
NOT MESSAGE("Wobbuffet fainted!"); // Wobb had more than 1/2 of its HP, so it can't faint.
}
}
SINGLE_BATTLE_TEST("Mind Blown causes the user to faint when below 1/2 of its HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(HP_TEST / 2); MaxHP(HP_TEST); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MIND_BLOWN); }
} SCENE {
HP_BAR(player, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIND_BLOWN, player);
MESSAGE("Wobbuffet fainted!");
}
}
DOUBLE_BATTLE_TEST("Mind Blown causes the user to faint when below 1/2 of its HP in a double battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(HP_TEST / 2); MaxHP(HP_TEST); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_MIND_BLOWN);}
} SCENE {
HP_BAR(playerLeft, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIND_BLOWN, playerLeft);
MESSAGE("Wobbuffet fainted!");
}
}
SINGLE_BATTLE_TEST("Mind Blown causes the user & the target to faint when below 1/2 of its HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(HP_TEST / 2) ; MaxHP(HP_TEST); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MIND_BLOWN);}
} SCENE {
HP_BAR(player, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIND_BLOWN, player);
HP_BAR(opponent, hp: 0);
MESSAGE("Foe Wobbuffet fainted!");
MESSAGE("Wobbuffet fainted!");
}
}
DOUBLE_BATTLE_TEST("Mind Blown causes everyone to faint in a double battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(HP_TEST / 2); MaxHP(HP_TEST); }
PLAYER(SPECIES_WYNAUT) { HP(1); }
OPPONENT(SPECIES_ABRA) { HP(1); }
OPPONENT(SPECIES_KADABRA) { HP(1); }
OPPONENT(SPECIES_KADABRA);
} WHEN {
TURN { MOVE(playerLeft, MOVE_MIND_BLOWN, criticalHit: FALSE); }
} SCENE {
HP_BAR(playerLeft, hp: 0);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIND_BLOWN, playerLeft);
HP_BAR(opponentLeft, hp: 0);
MESSAGE("Foe Abra fainted!");
HP_BAR(playerRight, hp: 0);
MESSAGE("Wynaut fainted!");
HP_BAR(opponentRight, hp: 0);
MESSAGE("Foe Kadabra fainted!");
MESSAGE("Wobbuffet fainted!");
}
}

View File

@ -42,8 +42,8 @@ SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on protect")
SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target")
{
KNOWN_FAILING; // #2596.
GIVEN {
ASSUME(B_HEALING_WISH_SWITCH >= GEN_5);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);

70
test/move_effect_roar.c Normal file
View File

@ -0,0 +1,70 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_ROAR].effect == EFFECT_ROAR);
}
SINGLE_BATTLE_TEST("Roar switches the target with a random non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_CHARMANDER);
OPPONENT(SPECIES_SQUIRTLE) { HP(0); }
} WHEN {
TURN { MOVE(player, MOVE_ROAR); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROAR, player);
MESSAGE("Foe Bulbasaur was dragged out!");
}
}
DOUBLE_BATTLE_TEST("Roar switches the target with a random non-battler, non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_CHARMANDER);
OPPONENT(SPECIES_SQUIRTLE) { HP(0); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_ROAR, target: opponentRight); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROAR, playerLeft);
MESSAGE("Foe Bulbasaur was dragged out!");
}
}
SINGLE_BATTLE_TEST("Roar fails if no replacements")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ROAR); }
} SCENE {
MESSAGE("Wobbuffet used Roar!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Roar fails if replacements fainted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT) { HP(0); }
} WHEN {
TURN { MOVE(player, MOVE_ROAR); }
} SCENE {
MESSAGE("Wobbuffet used Roar!");
MESSAGE("But it failed!");
}
}

View File

@ -0,0 +1,168 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_SHELL_TRAP].effect == EFFECT_SHELL_TRAP);
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
ASSUME(gBattleMoves[MOVE_WATER_GUN].split == SPLIT_SPECIAL);
ASSUME(gBattleMoves[MOVE_LEER].split == SPLIT_STATUS);
}
SINGLE_BATTLE_TEST("Shell Trap activates only if hit by a physical move")
{
u32 move;
bool32 activate;
PARAMETRIZE { move = MOVE_TACKLE; activate = TRUE; }
PARAMETRIZE { move = MOVE_WATER_GUN; activate = FALSE; }
PARAMETRIZE { move = MOVE_LEER; activate = FALSE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SHELL_TRAP); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, player);
MESSAGE("Wobbuffet set a shell trap!");
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (activate) {
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
HP_BAR(opponent);
} else {
MESSAGE("Wobbuffet's shell trap didn't work!");
NONE_OF {
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
HP_BAR(opponent);
}
}
}
}
SINGLE_BATTLE_TEST("Shell Trap does not activate if attacker's Sheer Force applied")
{
u32 move;
bool32 activate;
PARAMETRIZE { move = MOVE_TACKLE; activate = TRUE; }
PARAMETRIZE { move = MOVE_STOMP; activate = FALSE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TAUROS) { Ability(ABILITY_SHEER_FORCE); }
} WHEN {
TURN { MOVE(player, MOVE_SHELL_TRAP); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, player);
MESSAGE("Wobbuffet set a shell trap!");
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (activate) {
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
HP_BAR(opponent);
} else {
MESSAGE("Wobbuffet's shell trap didn't work!");
NONE_OF {
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
HP_BAR(opponent);
}
}
}
}
SINGLE_BATTLE_TEST("Shell Trap does not activate if battler faints before being able to activate it")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SHELL_TRAP); MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, player);
MESSAGE("Wobbuffet set a shell trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
MESSAGE("Wobbuffet fainted!");
MESSAGE("Go! Wobbuffet!");
NONE_OF {
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
HP_BAR(opponent);
}
}
}
DOUBLE_BATTLE_TEST("Shell Trap activates immediately after being hit on turn 1 and attacks both opponents")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_SHELL_TRAP].target == MOVE_TARGET_BOTH);
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WYNAUT) { Speed(1); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_SHELL_TRAP); MOVE(opponentLeft, MOVE_TACKLE, target:playerLeft); MOVE(playerRight, MOVE_CELEBRATE); MOVE(opponentRight, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, playerLeft);
MESSAGE("Wobbuffet set a shell trap!");
MESSAGE("Foe Wobbuffet used Tackle!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerLeft);
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Wynaut used Celebrate!");
}
}
DOUBLE_BATTLE_TEST("Shell Trap activates immediately after being hit on turn 2 and attacks both opponents")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_SHELL_TRAP].target == MOVE_TARGET_BOTH);
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WYNAUT) { Speed(6); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_SHELL_TRAP); MOVE(opponentLeft, MOVE_TACKLE, target:playerLeft); MOVE(playerRight, MOVE_CELEBRATE); MOVE(opponentRight, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, playerLeft);
MESSAGE("Wobbuffet set a shell trap!");
MESSAGE("Foe Wynaut used Celebrate!");
MESSAGE("Foe Wobbuffet used Tackle!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerLeft);
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
MESSAGE("Wobbuffet used Celebrate!");
}
}
DOUBLE_BATTLE_TEST("Shell Trap activates immediately after being hit on turn 3 and attacks both opponents")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_SHELL_TRAP].target == MOVE_TARGET_BOTH);
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(7); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WYNAUT) { Speed(6); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_SHELL_TRAP); MOVE(opponentLeft, MOVE_TACKLE, target:playerLeft); MOVE(playerRight, MOVE_CELEBRATE); MOVE(opponentRight, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, playerLeft);
MESSAGE("Wobbuffet set a shell trap!");
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Wynaut used Celebrate!");
MESSAGE("Foe Wobbuffet used Tackle!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
MESSAGE("Wobbuffet used Shell Trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerLeft);
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
}
}

View File

@ -39,7 +39,6 @@ struct TestRunnerState
u8 exitCode;
s32 tests;
s32 passes;
s32 skips;
const char *skipFilename;
const struct Test *test;
u32 processCosts[MAX_PROCESSES];

View File

@ -7,7 +7,7 @@
#include "test.h"
#include "test_runner.h"
#define TIMEOUT_SECONDS 30
#define TIMEOUT_SECONDS 55
void CB2_TestRunner(void);
@ -60,7 +60,6 @@ void CB2_TestRunner(void)
gTestRunnerState.exitCode = 0;
gTestRunnerState.tests = 0;
gTestRunnerState.passes = 0;
gTestRunnerState.skips = 0;
gTestRunnerState.skipFilename = NULL;
gTestRunnerState.test = __start_tests - 1;
break;
@ -70,20 +69,15 @@ void CB2_TestRunner(void)
if (gTestRunnerState.test == __stop_tests)
{
MgbaPrintf_("%s%d/%d PASSED\e[0m", gTestRunnerState.exitCode == 0 ? "\e[32m" : "\e[31m", gTestRunnerState.passes, gTestRunnerState.tests);
if (gTestRunnerState.skips)
{
if (gTestRunnerSkipIsFail)
MgbaPrintf_("\e[31m%d SKIPPED\e[0m", gTestRunnerState.skips);
else
MgbaPrintf_("%d SKIPPED", gTestRunnerState.skips);
}
gTestRunnerState.state = STATE_EXIT;
return;
}
if (!PrefixMatch(gTestRunnerArgv, gTestRunnerState.test->name))
if (gTestRunnerState.test->runner != &gAssumptionsRunner
&& !PrefixMatch(gTestRunnerArgv, gTestRunnerState.test->name))
{
return;
}
// Greedily assign tests to processes based on estimated cost.
// TODO: Make processCosts a min heap.
@ -111,6 +105,7 @@ void CB2_TestRunner(void)
return;
}
MgbaPrintf_(":N%s", gTestRunnerState.test->name);
gTestRunnerState.state = STATE_REPORT_RESULT;
gTestRunnerState.result = TEST_RESULT_PASS;
gTestRunnerState.expectedResult = TEST_RESULT_PASS;
@ -130,7 +125,6 @@ void CB2_TestRunner(void)
}
else
{
MgbaPrintf_(":N%s", gTestRunnerState.test->name);
if (gTestRunnerState.test->runner->setUp)
gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data);
gTestRunnerState.test->runner->run(gTestRunnerState.test->data);
@ -150,12 +144,6 @@ void CB2_TestRunner(void)
if (gTestRunnerState.result != TEST_RESULT_PASS)
gTestRunnerState.skipFilename = gTestRunnerState.test->filename;
}
else if (gTestRunnerState.result == TEST_RESULT_SKIP)
{
gTestRunnerState.skips++;
if (gTestRunnerSkipIsFail)
gTestRunnerState.exitCode = 1;
}
else
{
const char *color;
@ -206,7 +194,10 @@ void CB2_TestRunner(void)
default: result = "UNKNOWN"; break;
}
MgbaPrintf_(":R%s%s\e[0m", color, result);
if (gTestRunnerState.expectedResult == gTestRunnerState.result)
MgbaPrintf_(":P%s%s\e[0m", color, result);
else
MgbaPrintf_(":F%s%s\e[0m", color, result);
}
break;

View File

@ -38,6 +38,8 @@ struct Runner
size_t output_buffer_size;
size_t output_buffer_capacity;
char *output_buffer;
int passes;
int results;
};
static unsigned nrunners = 0;
@ -72,7 +74,11 @@ static void handle_read(struct Runner *runner)
runner->test_name[eol - soc - 1] = '\0';
break;
case 'R':
case 'P':
case 'F':
if (soc[1] == 'P')
runner->passes++;
runner->results++;
soc += 2;
fprintf(stdout, "%s: ", runner->test_name);
fwrite(soc, 1, eol - soc, stdout);
@ -404,6 +410,8 @@ int main(int argc, char *argv[])
// Reap test runners and collate exit codes.
int exit_code = 0;
int passes = 0;
int results = 0;
for (int i = 0; i < nrunners; i++)
{
int wstatus;
@ -412,8 +420,14 @@ int main(int argc, char *argv[])
perror("waitpid runners[i] failed");
exit(2);
}
if (runners[i].output_buffer_size > 0)
fwrite(runners[i].output_buffer, 1, runners[i].output_buffer_size, stdout);
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) > exit_code)
exit_code = WEXITSTATUS(wstatus);
passes += runners[i].passes;
results += runners[i].results;
}
fprintf(stdout, "%d/%d \e[32mPASS\e[0med\n", passes, results);
fflush(stdout);
return exit_code;
}