mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 11:44:17 +01:00
Merge branch 'RHH/upcoming' into RHH/pr/feature/formBattleChange
# Conflicts: # src/battle_util.c
This commit is contained in:
commit
101ea6522d
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
repository: pret/agbcc
|
||||
|
||||
- name: Install binutils
|
||||
run: sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi
|
||||
run: sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi libelf-dev
|
||||
# build-essential, git, and libpng-dev are already installed
|
||||
# gcc-arm-none-eabi is only needed for the modern build
|
||||
# as an alternative to dkP
|
||||
@ -41,10 +41,15 @@ jobs:
|
||||
working-directory: agbcc
|
||||
|
||||
- name: Agbcc
|
||||
run: make -j${nproc} all
|
||||
run: make -j${nproc} -O all
|
||||
|
||||
- name: Modern
|
||||
env:
|
||||
MODERN: 1
|
||||
COMPARE: 0
|
||||
run: make -j${nproc} all
|
||||
run: make -j${nproc} -O all
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
make -j${nproc} -O pokeemerald-test.elf
|
||||
make -j${nproc} check
|
||||
|
51
Makefile
51
Makefile
@ -79,6 +79,9 @@ ELF = $(ROM:.gba=.elf)
|
||||
MAP = $(ROM:.gba=.map)
|
||||
SYM = $(ROM:.gba=.sym)
|
||||
|
||||
TESTELF = $(ROM:.gba=-test.elf)
|
||||
HEADLESSELF = $(ROM:.gba=-test-headless.elf)
|
||||
|
||||
C_SUBDIR = src
|
||||
GFLIB_SUBDIR = gflib
|
||||
ASM_SUBDIR = asm
|
||||
@ -88,6 +91,7 @@ SONG_SUBDIR = sound/songs
|
||||
MID_SUBDIR = sound/songs/midi
|
||||
SAMPLE_SUBDIR = sound/direct_sound_samples
|
||||
CRY_SUBDIR = sound/direct_sound_samples/cries
|
||||
TEST_SUBDIR = test
|
||||
|
||||
C_BUILDDIR = $(OBJ_DIR)/$(C_SUBDIR)
|
||||
GFLIB_BUILDDIR = $(OBJ_DIR)/$(GFLIB_SUBDIR)
|
||||
@ -95,6 +99,7 @@ ASM_BUILDDIR = $(OBJ_DIR)/$(ASM_SUBDIR)
|
||||
DATA_ASM_BUILDDIR = $(OBJ_DIR)/$(DATA_ASM_SUBDIR)
|
||||
SONG_BUILDDIR = $(OBJ_DIR)/$(SONG_SUBDIR)
|
||||
MID_BUILDDIR = $(OBJ_DIR)/$(MID_SUBDIR)
|
||||
TEST_BUILDDIR = $(OBJ_DIR)/$(TEST_SUBDIR)
|
||||
|
||||
ASFLAGS := -mcpu=arm7tdmi --defsym MODERN=$(MODERN)
|
||||
|
||||
@ -131,10 +136,13 @@ RAMSCRGEN := tools/ramscrgen/ramscrgen$(EXE)
|
||||
FIX := tools/gbafix/gbafix$(EXE)
|
||||
MAPJSON := tools/mapjson/mapjson$(EXE)
|
||||
JSONPROC := tools/jsonproc/jsonproc$(EXE)
|
||||
PATCHELF := tools/patchelf/patchelf$(EXE)
|
||||
ROMTEST ?= $(shell { command -v mgba-rom-test || command -v tools/mgba/mgba-rom-test$(EXE); } 2>/dev/null)
|
||||
ROMTESTHYDRA := tools/mgba-rom-test-hydra/mgba-rom-test-hydra$(EXE)
|
||||
|
||||
PERL := perl
|
||||
|
||||
TOOLDIRS := $(filter-out tools/agbcc tools/binutils,$(wildcard tools/*))
|
||||
TOOLDIRS := $(filter-out tools/mgba tools/agbcc tools/binutils,$(wildcard tools/*))
|
||||
TOOLBASE = $(TOOLDIRS:tools/%=%)
|
||||
TOOLS = $(foreach tool,$(TOOLBASE),tools/$(tool)/$(tool)$(EXE))
|
||||
|
||||
@ -150,7 +158,7 @@ MAKEFLAGS += --no-print-directory
|
||||
# Secondary expansion is required for dependency variables in object rules.
|
||||
.SECONDEXPANSION:
|
||||
|
||||
.PHONY: all rom clean compare tidy tools mostlyclean clean-tools $(TOOLDIRS) libagbsyscall modern tidymodern tidynonmodern
|
||||
.PHONY: all rom clean compare tidy tools mostlyclean clean-tools $(TOOLDIRS) libagbsyscall modern tidymodern tidynonmodern check
|
||||
|
||||
infoshell = $(foreach line, $(shell $1 | sed "s/ /__SPACE__/g"), $(info $(subst __SPACE__, ,$(line))))
|
||||
|
||||
@ -158,7 +166,7 @@ infoshell = $(foreach line, $(shell $1 | sed "s/ /__SPACE__/g"), $(info $(subst
|
||||
# Disable dependency scanning for clean/tidy/tools
|
||||
# Use a separate minimal makefile for speed
|
||||
# Since we don't need to reload most of this makefile
|
||||
ifeq (,$(filter-out all rom compare modern libagbsyscall syms,$(MAKECMDGOALS)))
|
||||
ifeq (,$(filter-out all rom compare modern check libagbsyscall syms,$(MAKECMDGOALS)))
|
||||
$(call infoshell, $(MAKE) -f make_tools.mk)
|
||||
else
|
||||
NODEP ?= 1
|
||||
@ -182,6 +190,11 @@ C_SRCS_IN := $(wildcard $(C_SUBDIR)/*.c $(C_SUBDIR)/*/*.c $(C_SUBDIR)/*/*/*.c)
|
||||
C_SRCS := $(foreach src,$(C_SRCS_IN),$(if $(findstring .inc.c,$(src)),,$(src)))
|
||||
C_OBJS := $(patsubst $(C_SUBDIR)/%.c,$(C_BUILDDIR)/%.o,$(C_SRCS))
|
||||
|
||||
TEST_SRCS_IN := $(wildcard $(TEST_SUBDIR)/*.c $(TEST_SUBDIR)/*/*.c $(TEST_SUBDIR)/*/*/*.c)
|
||||
TEST_SRCS := $(foreach src,$(TEST_SRCS_IN),$(if $(findstring .inc.c,$(src)),,$(src)))
|
||||
TEST_OBJS := $(patsubst $(TEST_SUBDIR)/%.c,$(TEST_BUILDDIR)/%.o,$(TEST_SRCS))
|
||||
TEST_OBJS_REL := $(patsubst $(OBJ_DIR)/%,%,$(TEST_OBJS))
|
||||
|
||||
GFLIB_SRCS := $(wildcard $(GFLIB_SUBDIR)/*.c)
|
||||
GFLIB_OBJS := $(patsubst $(GFLIB_SUBDIR)/%.c,$(GFLIB_BUILDDIR)/%.o,$(GFLIB_SRCS))
|
||||
|
||||
@ -206,7 +219,7 @@ MID_OBJS := $(patsubst $(MID_SUBDIR)/%.mid,$(MID_BUILDDIR)/%.o,$(MID_SRCS))
|
||||
OBJS := $(C_OBJS) $(GFLIB_OBJS) $(C_ASM_OBJS) $(ASM_OBJS) $(DATA_ASM_OBJS) $(SONG_OBJS) $(MID_OBJS)
|
||||
OBJS_REL := $(patsubst $(OBJ_DIR)/%,%,$(OBJS))
|
||||
|
||||
SUBDIRS := $(sort $(dir $(OBJS)))
|
||||
SUBDIRS := $(sort $(dir $(OBJS) $(dir $(TEST_OBJS))))
|
||||
$(shell mkdir -p $(SUBDIRS))
|
||||
endif
|
||||
|
||||
@ -407,6 +420,14 @@ $(OBJ_DIR)/sym_common.ld: sym_common.txt $(C_OBJS) $(wildcard common_syms/*.txt)
|
||||
$(OBJ_DIR)/sym_ewram.ld: sym_ewram.txt
|
||||
$(RAMSCRGEN) ewram_data $< ENGLISH > $@
|
||||
|
||||
# NOTE: Based on C_DEP above, but without NODEP and KEEP_TEMPS handling.
|
||||
define TEST_DEP
|
||||
$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib -I test $2)
|
||||
@echo "$$(CC1) <flags> -o $$@ $$<"
|
||||
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
|
||||
endef
|
||||
$(foreach src, $(TEST_SRCS), $(eval $(call TEST_DEP,$(patsubst $(TEST_SUBDIR)/%.c,$(TEST_BUILDDIR)/%.o,$(src)),$(src),$(patsubst $(TEST_SUBDIR)/%.c,%,$(src)))))
|
||||
|
||||
ifeq ($(MODERN),0)
|
||||
LD_SCRIPT := ld_script.txt
|
||||
LD_SCRIPT_DEPS := $(OBJ_DIR)/sym_bss.ld $(OBJ_DIR)/sym_common.ld $(OBJ_DIR)/sym_ewram.ld
|
||||
@ -429,6 +450,28 @@ $(ROM): $(ELF)
|
||||
|
||||
modern: all
|
||||
|
||||
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
|
||||
@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
|
||||
$(PATCHELF) pokeemerald-test.elf gTestRunnerArgv "$(TESTS)\0"
|
||||
|
||||
ifeq ($(GITHUB_REPOSITORY_OWNER),rh-hideout)
|
||||
TEST_SKIP_IS_FAIL := \x01
|
||||
else
|
||||
TEST_SKIP_IS_FAIL := \x00
|
||||
endif
|
||||
|
||||
check: $(TESTELF)
|
||||
@cp $< $(HEADLESSELF)
|
||||
$(PATCHELF) $(HEADLESSELF) gTestRunnerHeadless '\x01' gTestRunnerSkipIsFail "$(TEST_SKIP_IS_FAIL)"
|
||||
$(ROMTESTHYDRA) $(ROMTEST) $(HEADLESSELF)
|
||||
|
||||
libagbsyscall:
|
||||
@$(MAKE) -C libagbsyscall TOOLCHAIN=$(TOOLCHAIN) MODERN=$(MODERN)
|
||||
|
||||
|
@ -2237,3 +2237,8 @@
|
||||
.endif
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
.endm
|
||||
|
||||
.macro jumpifemergencyexited battler:req, ptr:req
|
||||
various \battler, VARIOUS_JUMP_IF_EMERGENCY_EXITED
|
||||
.4byte \ptr
|
||||
.endm
|
||||
|
@ -7,3 +7,4 @@ gIntrTable
|
||||
gLinkVSyncDisabled
|
||||
IntrMain_Buffer
|
||||
gPcmDmaCounter
|
||||
gAgbMainLoop_sp
|
||||
|
@ -3055,6 +3055,7 @@ BattleScript_EffectHitEscape:
|
||||
jumpifbyte CMP_NOT_EQUAL gBattleOutcome 0, BattleScript_HitEscapeEnd
|
||||
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_HitEscapeEnd
|
||||
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_HitEscapeEnd
|
||||
jumpifemergencyexited BS_TARGET, BattleScript_HitEscapeEnd
|
||||
openpartyscreen BS_ATTACKER, BattleScript_HitEscapeEnd
|
||||
switchoutabilities BS_ATTACKER
|
||||
waitstate
|
||||
|
@ -258,7 +258,7 @@ gSpecials::
|
||||
def_special CallSlateportTentFunction
|
||||
def_special ChoosePartyForBattleFrontier
|
||||
def_special ValidateEReaderTrainer
|
||||
def_special GetBestBattleTowerStreak
|
||||
def_special GetBattleTowerSinglesStreak
|
||||
def_special ReducePlayerPartyToSelectedMons
|
||||
def_special BedroomPC
|
||||
def_special PlayerPC
|
||||
|
@ -196,6 +196,7 @@ struct SpecialStatus
|
||||
// End of byte
|
||||
u8 weatherAbilityDone:1;
|
||||
u8 terrainAbilityDone:1;
|
||||
u8 emergencyExited:1;
|
||||
};
|
||||
|
||||
struct SideTimer
|
||||
@ -483,7 +484,6 @@ struct MegaEvolutionData
|
||||
u8 battlerId;
|
||||
bool8 playerSelect;
|
||||
u8 triggerSpriteId;
|
||||
bool8 isWishMegaEvo;
|
||||
};
|
||||
|
||||
struct Illusion
|
||||
|
@ -5,6 +5,14 @@
|
||||
#include "constants/battle_anim.h"
|
||||
#include "task.h"
|
||||
|
||||
enum
|
||||
{
|
||||
ANIM_TYPE_GENERAL,
|
||||
ANIM_TYPE_MOVE,
|
||||
ANIM_TYPE_STATUS,
|
||||
ANIM_TYPE_SPECIAL,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BG_ANIM_SCREEN_SIZE,
|
||||
@ -54,7 +62,7 @@ extern u16 gAnimMoveIndex;
|
||||
|
||||
void ClearBattleAnimationVars(void);
|
||||
void DoMoveAnim(u16 move);
|
||||
void LaunchBattleAnimation(const u8 *const animsTable[], u16 tableId, bool8 isMoveAnim);
|
||||
void LaunchBattleAnimation(u32 animType, u32 animId);
|
||||
void DestroyAnimSprite(struct Sprite *sprite);
|
||||
void DestroyAnimVisualTask(u8 taskId);
|
||||
void DestroyAnimSoundTask(u8 taskId);
|
||||
|
@ -23,7 +23,7 @@ bool8 BattleInitAllSprites(u8 *state1, u8 *battlerId);
|
||||
void ClearSpritesHealthboxAnimData(void);
|
||||
void CopyAllBattleSpritesInvisibilities(void);
|
||||
void CopyBattleSpriteInvisibility(u8 battlerId);
|
||||
void HandleSpeciesGfxDataChange(u8 attacker, u8 target, bool8 notTransform, bool32 megaEvo);
|
||||
void HandleSpeciesGfxDataChange(u8 attacker, u8 target, bool8 notTransform, bool32 megaEvo, bool8 trackEnemyPersonality);
|
||||
void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite);
|
||||
void LoadBattleMonGfxAndAnimate(u8 battlerId, bool8 loadMonSprite, u8 spriteId);
|
||||
void TrySetBehindSubstituteSpriteBit(u8 battlerId, u16 move);
|
||||
|
@ -255,6 +255,7 @@
|
||||
#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
|
||||
|
||||
// Cmd_manipulatedamage
|
||||
#define DMG_CHANGE_SIGN 0
|
||||
|
@ -13,7 +13,6 @@
|
||||
#define DAYCARE_ONE_MON 2
|
||||
#define DAYCARE_TWO_MONS 3
|
||||
|
||||
#define INHERITED_IV_COUNT 3
|
||||
#if P_EGG_HATCH_LEVEL >= GEN_4
|
||||
#define EGG_HATCH_LEVEL 1
|
||||
#else
|
||||
|
@ -1,60 +1,60 @@
|
||||
#ifndef GUARD_CONSTANTS_GAME_STAT_H
|
||||
#define GUARD_CONSTANTS_GAME_STAT_H
|
||||
|
||||
#define GAME_STAT_SAVED_GAME 0
|
||||
#define GAME_STAT_FIRST_HOF_PLAY_TIME 1
|
||||
#define GAME_STAT_STARTED_TRENDS 2
|
||||
#define GAME_STAT_PLANTED_BERRIES 3
|
||||
#define GAME_STAT_TRADED_BIKES 4
|
||||
#define GAME_STAT_STEPS 5
|
||||
#define GAME_STAT_GOT_INTERVIEWED 6
|
||||
#define GAME_STAT_TOTAL_BATTLES 7
|
||||
#define GAME_STAT_WILD_BATTLES 8
|
||||
#define GAME_STAT_TRAINER_BATTLES 9
|
||||
#define GAME_STAT_ENTERED_HOF 10
|
||||
#define GAME_STAT_POKEMON_CAPTURES 11
|
||||
#define GAME_STAT_FISHING_CAPTURES 12
|
||||
#define GAME_STAT_HATCHED_EGGS 13
|
||||
#define GAME_STAT_EVOLVED_POKEMON 14
|
||||
#define GAME_STAT_USED_POKECENTER 15
|
||||
#define GAME_STAT_RESTED_AT_HOME 16
|
||||
#define GAME_STAT_ENTERED_SAFARI_ZONE 17
|
||||
#define GAME_STAT_USED_CUT 18
|
||||
#define GAME_STAT_USED_ROCK_SMASH 19
|
||||
#define GAME_STAT_MOVED_SECRET_BASE 20
|
||||
#define GAME_STAT_POKEMON_TRADES 21
|
||||
#define GAME_STAT_UNKNOWN_22 22
|
||||
#define GAME_STAT_LINK_BATTLE_WINS 23
|
||||
#define GAME_STAT_LINK_BATTLE_LOSSES 24
|
||||
#define GAME_STAT_LINK_BATTLE_DRAWS 25
|
||||
#define GAME_STAT_USED_SPLASH 26
|
||||
#define GAME_STAT_USED_STRUGGLE 27
|
||||
#define GAME_STAT_SLOT_JACKPOTS 28
|
||||
#define GAME_STAT_CONSECUTIVE_ROULETTE_WINS 29
|
||||
#define GAME_STAT_ENTERED_BATTLE_TOWER 30
|
||||
#define GAME_STAT_UNKNOWN_31 31
|
||||
#define GAME_STAT_BATTLE_TOWER_BEST_STREAK 32
|
||||
#define GAME_STAT_POKEBLOCKS 33
|
||||
#define GAME_STAT_POKEBLOCKS_WITH_FRIENDS 34
|
||||
#define GAME_STAT_WON_LINK_CONTEST 35
|
||||
#define GAME_STAT_ENTERED_CONTEST 36
|
||||
#define GAME_STAT_WON_CONTEST 37
|
||||
#define GAME_STAT_SHOPPED 38
|
||||
#define GAME_STAT_USED_ITEMFINDER 39
|
||||
#define GAME_STAT_GOT_RAINED_ON 40
|
||||
#define GAME_STAT_CHECKED_POKEDEX 41
|
||||
#define GAME_STAT_RECEIVED_RIBBONS 42
|
||||
#define GAME_STAT_JUMPED_DOWN_LEDGES 43
|
||||
#define GAME_STAT_WATCHED_TV 44
|
||||
#define GAME_STAT_CHECKED_CLOCK 45
|
||||
#define GAME_STAT_WON_POKEMON_LOTTERY 46
|
||||
#define GAME_STAT_USED_DAYCARE 47
|
||||
#define GAME_STAT_RODE_CABLE_CAR 48
|
||||
#define GAME_STAT_ENTERED_HOT_SPRINGS 49
|
||||
#define GAME_STAT_NUM_UNION_ROOM_BATTLES 50
|
||||
#define GAME_STAT_PLAYED_BERRY_CRUSH 51
|
||||
#define GAME_STAT_SAVED_GAME 0
|
||||
#define GAME_STAT_FIRST_HOF_PLAY_TIME 1
|
||||
#define GAME_STAT_STARTED_TRENDS 2
|
||||
#define GAME_STAT_PLANTED_BERRIES 3
|
||||
#define GAME_STAT_TRADED_BIKES 4
|
||||
#define GAME_STAT_STEPS 5
|
||||
#define GAME_STAT_GOT_INTERVIEWED 6
|
||||
#define GAME_STAT_TOTAL_BATTLES 7
|
||||
#define GAME_STAT_WILD_BATTLES 8
|
||||
#define GAME_STAT_TRAINER_BATTLES 9
|
||||
#define GAME_STAT_ENTERED_HOF 10
|
||||
#define GAME_STAT_POKEMON_CAPTURES 11
|
||||
#define GAME_STAT_FISHING_CAPTURES 12
|
||||
#define GAME_STAT_HATCHED_EGGS 13
|
||||
#define GAME_STAT_EVOLVED_POKEMON 14
|
||||
#define GAME_STAT_USED_POKECENTER 15
|
||||
#define GAME_STAT_RESTED_AT_HOME 16
|
||||
#define GAME_STAT_ENTERED_SAFARI_ZONE 17
|
||||
#define GAME_STAT_USED_CUT 18
|
||||
#define GAME_STAT_USED_ROCK_SMASH 19
|
||||
#define GAME_STAT_MOVED_SECRET_BASE 20
|
||||
#define GAME_STAT_POKEMON_TRADES 21
|
||||
#define GAME_STAT_UNKNOWN_22 22
|
||||
#define GAME_STAT_LINK_BATTLE_WINS 23
|
||||
#define GAME_STAT_LINK_BATTLE_LOSSES 24
|
||||
#define GAME_STAT_LINK_BATTLE_DRAWS 25
|
||||
#define GAME_STAT_USED_SPLASH 26
|
||||
#define GAME_STAT_USED_STRUGGLE 27
|
||||
#define GAME_STAT_SLOT_JACKPOTS 28
|
||||
#define GAME_STAT_CONSECUTIVE_ROULETTE_WINS 29
|
||||
#define GAME_STAT_ENTERED_BATTLE_TOWER 30
|
||||
#define GAME_STAT_UNKNOWN_31 31
|
||||
#define GAME_STAT_BATTLE_TOWER_SINGLES_STREAK 32
|
||||
#define GAME_STAT_POKEBLOCKS 33
|
||||
#define GAME_STAT_POKEBLOCKS_WITH_FRIENDS 34
|
||||
#define GAME_STAT_WON_LINK_CONTEST 35
|
||||
#define GAME_STAT_ENTERED_CONTEST 36
|
||||
#define GAME_STAT_WON_CONTEST 37
|
||||
#define GAME_STAT_SHOPPED 38
|
||||
#define GAME_STAT_USED_ITEMFINDER 39
|
||||
#define GAME_STAT_GOT_RAINED_ON 40
|
||||
#define GAME_STAT_CHECKED_POKEDEX 41
|
||||
#define GAME_STAT_RECEIVED_RIBBONS 42
|
||||
#define GAME_STAT_JUMPED_DOWN_LEDGES 43
|
||||
#define GAME_STAT_WATCHED_TV 44
|
||||
#define GAME_STAT_CHECKED_CLOCK 45
|
||||
#define GAME_STAT_WON_POKEMON_LOTTERY 46
|
||||
#define GAME_STAT_USED_DAYCARE 47
|
||||
#define GAME_STAT_RODE_CABLE_CAR 48
|
||||
#define GAME_STAT_ENTERED_HOT_SPRINGS 49
|
||||
#define GAME_STAT_NUM_UNION_ROOM_BATTLES 50
|
||||
#define GAME_STAT_PLAYED_BERRY_CRUSH 51
|
||||
|
||||
#define NUM_USED_GAME_STATS 52
|
||||
#define NUM_GAME_STATS 64
|
||||
#define NUM_USED_GAME_STATS 52
|
||||
#define NUM_GAME_STATS 64
|
||||
|
||||
#endif // GUARD_CONSTANTS_GAME_STAT_H
|
||||
|
@ -928,7 +928,13 @@
|
||||
#define ITEM_RUBY 756
|
||||
#define ITEM_SAPPHIRE 757
|
||||
|
||||
#define ITEMS_COUNT 758
|
||||
#define ITEM_ABILITY_SHIELD 758
|
||||
#define ITEM_CLEAR_AMULET 759
|
||||
#define ITEM_PUNCHING_GLOVE 760
|
||||
#define ITEM_COVERT_CLOAK 761
|
||||
#define ITEM_LOADED_DICE 762
|
||||
|
||||
#define ITEMS_COUNT 763
|
||||
#define ITEM_FIELD_ARROW ITEMS_COUNT
|
||||
|
||||
// A special item id associated with "Cancel"/"Exit" etc. in a list of items or decorations
|
||||
|
@ -139,6 +139,14 @@
|
||||
#define NUM_FLAG_BYTES ROUND_BITS_TO_BYTES(FLAGS_COUNT)
|
||||
#define NUM_ADDITIONAL_PHRASE_BYTES ROUND_BITS_TO_BYTES(NUM_ADDITIONAL_PHRASES)
|
||||
|
||||
// Calls m0/m1/.../m8 depending on how many arguments are passed.
|
||||
#define VARARG_8(m, ...) CAT(m, NARG_8(__VA_ARGS__))(__VA_ARGS__)
|
||||
#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N
|
||||
|
||||
#define CAT(a, b) CAT_(a, b)
|
||||
#define CAT_(a, b) a ## b
|
||||
|
||||
// This produces an error at compile-time if expr is zero.
|
||||
// It looks like file.c:line: size of array `id' is negative
|
||||
#define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1];
|
||||
|
@ -57,6 +57,7 @@ extern u32 IntrMain_Buffer[];
|
||||
extern s8 gPcmDmaCounter;
|
||||
|
||||
void AgbMain(void);
|
||||
void AgbMainLoop(void);
|
||||
void SetMainCallback2(MainCallback callback);
|
||||
void InitKeys(void);
|
||||
void SetVBlankCallback(IntrCallback callback);
|
||||
|
@ -566,5 +566,6 @@ bool32 ShouldShowFemaleDifferences(u16 species, u32 personality);
|
||||
bool32 TryFormChange(u32 monId, u32 side, u16 method);
|
||||
void TryToSetBattleFormChangeMoves(struct Pokemon *mon, u16 method);
|
||||
u32 GetMonFriendshipScore(struct Pokemon *pokemon);
|
||||
void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality);
|
||||
|
||||
#endif // GUARD_POKEMON_H
|
||||
|
@ -1,6 +1,51 @@
|
||||
#ifndef GUARD_RECORDED_BATTLE_H
|
||||
#define GUARD_RECORDED_BATTLE_H
|
||||
|
||||
#include "constants/battle.h"
|
||||
|
||||
#define BATTLER_RECORD_SIZE 664
|
||||
|
||||
struct RecordedBattleSave
|
||||
{
|
||||
struct Pokemon playerParty[PARTY_SIZE];
|
||||
struct Pokemon opponentParty[PARTY_SIZE];
|
||||
u8 playersName[MAX_BATTLERS_COUNT][PLAYER_NAME_LENGTH + 1];
|
||||
u8 playersGender[MAX_BATTLERS_COUNT];
|
||||
u32 playersTrainerId[MAX_BATTLERS_COUNT];
|
||||
u8 playersLanguage[MAX_BATTLERS_COUNT];
|
||||
u32 rngSeed;
|
||||
u32 battleFlags;
|
||||
u8 playersBattlers[MAX_BATTLERS_COUNT];
|
||||
u16 opponentA;
|
||||
u16 opponentB;
|
||||
u16 partnerId;
|
||||
u16 multiplayerId;
|
||||
u8 lvlMode;
|
||||
u8 frontierFacility;
|
||||
u8 frontierBrainSymbol;
|
||||
u8 battleScene:1;
|
||||
u8 textSpeed:3;
|
||||
u32 AI_scripts;
|
||||
u8 recordMixFriendName[PLAYER_NAME_LENGTH + 1];
|
||||
u8 recordMixFriendClass;
|
||||
u8 apprenticeId;
|
||||
u16 easyChatSpeech[EASY_CHAT_BATTLE_WORDS_COUNT];
|
||||
u8 recordMixFriendLanguage;
|
||||
u8 apprenticeLanguage;
|
||||
u8 battleRecord[MAX_BATTLERS_COUNT][BATTLER_RECORD_SIZE];
|
||||
u32 checksum;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
RECORDED_BYTE, // Generic.
|
||||
RECORDED_ACTION_TYPE,
|
||||
RECORDED_MOVE_SLOT,
|
||||
RECORDED_MOVE_TARGET,
|
||||
RECORDED_PARTY_INDEX,
|
||||
RECORDED_BATTLE_PALACE_ACTION,
|
||||
};
|
||||
|
||||
extern u32 gRecordedBattleRngSeed;
|
||||
extern u32 gBattlePalaceMoveSelectionRngValue;
|
||||
extern u8 gRecordedBattleMultiplayerId;
|
||||
@ -12,11 +57,12 @@ void RecordedBattle_Init(u8 mode);
|
||||
void RecordedBattle_SetTrainerInfo(void);
|
||||
void RecordedBattle_SetBattlerAction(u8 battlerId, u8 action);
|
||||
void RecordedBattle_ClearBattlerAction(u8 battlerId, u8 bytesToClear);
|
||||
u8 RecordedBattle_GetBattlerAction(u8 battlerId);
|
||||
u8 RecordedBattle_GetBattlerAction(u32 actionType, u8 battlerId);
|
||||
u8 RecordedBattle_BufferNewBattlerData(u8 *dst);
|
||||
void RecordedBattle_RecordAllBattlerData(u8 *data);
|
||||
bool32 CanCopyRecordedBattleSaveData(void);
|
||||
bool32 MoveRecordedBattleToSaveData(void);
|
||||
void SetVariablesForRecordedBattle(struct RecordedBattleSave *);
|
||||
void PlayRecordedBattle(void (*CB2_After)(void));
|
||||
u8 GetRecordedBattleFrontierFacility(void);
|
||||
u8 GetRecordedBattleFronterBrainSymbol(void);
|
||||
|
17
include/test_runner.h
Normal file
17
include/test_runner.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef GUARD_TEST_RUNNER_H
|
||||
#define GUARD_TEST_RUNNER_H
|
||||
|
||||
extern const bool8 gTestRunnerEnabled;
|
||||
extern const bool8 gTestRunnerHeadless;
|
||||
extern const bool8 gTestRunnerSkipIsFail;
|
||||
|
||||
void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, u32 ability);
|
||||
void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId);
|
||||
void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP);
|
||||
void TestRunner_Battle_RecordMessage(const u8 *message);
|
||||
void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1);
|
||||
void TestRunner_Battle_AfterLastTurn(void);
|
||||
|
||||
void BattleTest_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType);
|
||||
|
||||
#endif
|
@ -2,6 +2,7 @@ ENTRY(Start)
|
||||
|
||||
gNumMusicPlayers = 4;
|
||||
gMaxLines = 0;
|
||||
gInitialMainCB2 = CB2_InitCopyrightScreenAfterBootup;
|
||||
|
||||
SECTIONS {
|
||||
. = 0x2000000;
|
||||
|
@ -2,6 +2,7 @@ ENTRY(Start)
|
||||
|
||||
gNumMusicPlayers = 4;
|
||||
gMaxLines = 0;
|
||||
gInitialMainCB2 = CB2_InitCopyrightScreenAfterBootup;
|
||||
|
||||
SECTIONS {
|
||||
. = 0x2000000;
|
||||
|
140
ld_script_test.txt
Normal file
140
ld_script_test.txt
Normal file
@ -0,0 +1,140 @@
|
||||
ENTRY(Start)
|
||||
|
||||
gNumMusicPlayers = 4;
|
||||
gMaxLines = 0;
|
||||
gInitialMainCB2 = CB2_TestRunner;
|
||||
|
||||
SECTIONS {
|
||||
. = 0x2000000;
|
||||
|
||||
ewram (NOLOAD) :
|
||||
ALIGN(4)
|
||||
{
|
||||
gHeap = .;
|
||||
|
||||
. = 0x1C000;
|
||||
|
||||
src/*.o(ewram_data);
|
||||
gflib/*.o(ewram_data);
|
||||
test/*.o(ewram_data);
|
||||
|
||||
. = 0x40000;
|
||||
}
|
||||
|
||||
. = 0x3000000;
|
||||
|
||||
iwram (NOLOAD) :
|
||||
ALIGN(4)
|
||||
{
|
||||
/* .bss starts at 0x3000000 */
|
||||
src/*.o(.bss);
|
||||
gflib/*.o(.bss);
|
||||
data/*.o(.bss);
|
||||
test/*.o(.bss);
|
||||
*libc.a:*.o(.bss*);
|
||||
*libgcc.a:*.o(.bss*);
|
||||
*libnosys.a:*.o(.bss*);
|
||||
|
||||
/* .bss.code starts at 0x3001AA8 */
|
||||
src/m4a.o(.bss.code);
|
||||
|
||||
/* COMMON starts at 0x30022A8 */
|
||||
src/*.o(COMMON);
|
||||
gflib/*.o(COMMON);
|
||||
data/*.o(COMMON);
|
||||
test/*.o(COMMON);
|
||||
*libc.a:sbrkr.o(COMMON);
|
||||
end = .;
|
||||
. = 0x8000;
|
||||
}
|
||||
|
||||
. = 0x8000000;
|
||||
|
||||
.text :
|
||||
ALIGN(4)
|
||||
{
|
||||
src/rom_header.o(.text);
|
||||
src/rom_header_gf.o(.text.*);
|
||||
src/*.o(.text);
|
||||
gflib/*.o(.text);
|
||||
} =0
|
||||
|
||||
script_data :
|
||||
ALIGN(4)
|
||||
{
|
||||
data/*.o(script_data);
|
||||
} =0
|
||||
|
||||
lib_text :
|
||||
ALIGN(4)
|
||||
{
|
||||
*libagbsyscall.a:*.o(.text*);
|
||||
*libgcc.a:*.o(.text*);
|
||||
*libc.a:*.o(.text*);
|
||||
*libnosys.a:*.o(.text*);
|
||||
} =0
|
||||
|
||||
.rodata :
|
||||
ALIGN(4)
|
||||
{
|
||||
src/*.o(.rodata);
|
||||
gflib/*.o(.rodata);
|
||||
data/*.o(.rodata);
|
||||
} =0
|
||||
|
||||
song_data :
|
||||
ALIGN(4)
|
||||
{
|
||||
sound/songs/*.o(.rodata);
|
||||
} =0
|
||||
|
||||
lib_rodata :
|
||||
SUBALIGN(4)
|
||||
{
|
||||
*libgcc.a:*.o(.rodata*);
|
||||
*libc.a:*.o(.rodata*);
|
||||
*libc.a:*.o(.data*);
|
||||
src/libisagbprn.o(.rodata);
|
||||
} =0
|
||||
|
||||
tests :
|
||||
ALIGN(4)
|
||||
{
|
||||
__start_tests = .;
|
||||
test/*.o(.tests);
|
||||
__stop_tests = .;
|
||||
test/*.o(.text);
|
||||
test/*.o(.rodata);
|
||||
} =0
|
||||
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
|
||||
/* Discard everything not specifically mentioned above. */
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(*);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
TOOLDIRS := $(filter-out tools/agbcc tools/binutils,$(wildcard tools/*))
|
||||
TOOLDIRS := $(filter-out tools/mgba tools/agbcc tools/binutils,$(wildcard tools/*))
|
||||
|
||||
.PHONY: all $(TOOLDIRS)
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "sound.h"
|
||||
#include "sprite.h"
|
||||
#include "task.h"
|
||||
#include "test_runner.h"
|
||||
#include "constants/battle_anim.h"
|
||||
#include "constants/moves.h"
|
||||
|
||||
@ -27,7 +28,10 @@
|
||||
#define ANIM_SPRITE_INDEX_COUNT 8
|
||||
|
||||
extern const u16 gMovesWithQuietBGM[];
|
||||
extern const u8 *const gBattleAnims_General[];
|
||||
extern const u8 *const gBattleAnims_Moves[];
|
||||
extern const u8 *const gBattleAnims_Special[];
|
||||
extern const u8 *const gBattleAnims_StatusConditions[];
|
||||
|
||||
static void Cmd_loadspritegfx(void);
|
||||
static void Cmd_unloadspritegfx(void);
|
||||
@ -211,17 +215,50 @@ void DoMoveAnim(u16 move)
|
||||
gBattleAnimTarget = 0;
|
||||
}
|
||||
}
|
||||
LaunchBattleAnimation(gBattleAnims_Moves, move, TRUE);
|
||||
LaunchBattleAnimation(ANIM_TYPE_MOVE, move);
|
||||
}
|
||||
|
||||
void LaunchBattleAnimation(const u8 *const animsTable[], u16 tableId, bool8 isMoveAnim)
|
||||
static void Nop(void)
|
||||
{
|
||||
}
|
||||
|
||||
void LaunchBattleAnimation(u32 animType, u32 animId)
|
||||
{
|
||||
s32 i;
|
||||
bool32 hideHpBoxes = (tableId == MOVE_TRANSFORM) ? FALSE : TRUE;
|
||||
const u8 *const *animsTable;
|
||||
bool32 hideHpBoxes;
|
||||
|
||||
if (!isMoveAnim)
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
switch (tableId)
|
||||
TestRunner_Battle_RecordAnimation(animType, animId);
|
||||
if (gTestRunnerHeadless)
|
||||
{
|
||||
gAnimScriptCallback = Nop;
|
||||
gAnimScriptActive = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (animType)
|
||||
{
|
||||
case ANIM_TYPE_GENERAL:
|
||||
animsTable = gBattleAnims_General;
|
||||
break;
|
||||
case ANIM_TYPE_MOVE:
|
||||
animsTable = gBattleAnims_Moves;
|
||||
break;
|
||||
case ANIM_TYPE_STATUS:
|
||||
animsTable = gBattleAnims_StatusConditions;
|
||||
break;
|
||||
case ANIM_TYPE_SPECIAL:
|
||||
animsTable = gBattleAnims_Special;
|
||||
break;
|
||||
}
|
||||
|
||||
hideHpBoxes = !(animType == ANIM_TYPE_MOVE && animId == MOVE_TRANSFORM);
|
||||
if (animType != ANIM_TYPE_MOVE)
|
||||
{
|
||||
switch (animId)
|
||||
{
|
||||
case B_ANIM_TURN_TRAP:
|
||||
case B_ANIM_LEECH_SEED_DRAIN:
|
||||
@ -258,17 +295,17 @@ void LaunchBattleAnimation(const u8 *const animsTable[], u16 tableId, bool8 isMo
|
||||
gAnimBattlerSpecies[i] = gContestResources->moveAnim->species;
|
||||
}
|
||||
|
||||
if (!isMoveAnim)
|
||||
if (animType != ANIM_TYPE_MOVE)
|
||||
gAnimMoveIndex = 0;
|
||||
else
|
||||
gAnimMoveIndex = tableId;
|
||||
gAnimMoveIndex = animId;
|
||||
|
||||
for (i = 0; i < ANIM_ARGS_COUNT; i++)
|
||||
gBattleAnimArgs[i] = 0;
|
||||
|
||||
sMonAnimTaskIdArray[0] = TASK_NONE;
|
||||
sMonAnimTaskIdArray[1] = TASK_NONE;
|
||||
sBattleAnimScriptPtr = animsTable[tableId];
|
||||
sBattleAnimScriptPtr = animsTable[animId];
|
||||
gAnimScriptActive = TRUE;
|
||||
sAnimFramesToWait = 0;
|
||||
gAnimScriptCallback = RunAnimScriptCommand;
|
||||
@ -276,11 +313,11 @@ void LaunchBattleAnimation(const u8 *const animsTable[], u16 tableId, bool8 isMo
|
||||
for (i = 0; i < ANIM_SPRITE_INDEX_COUNT; i++)
|
||||
sAnimSpriteIndexArray[i] = 0xFFFF;
|
||||
|
||||
if (isMoveAnim)
|
||||
if (animType == ANIM_TYPE_MOVE)
|
||||
{
|
||||
for (i = 0; gMovesWithQuietBGM[i] != 0xFFFF; i++)
|
||||
{
|
||||
if (tableId == gMovesWithQuietBGM[i])
|
||||
if (animId == gMovesWithQuietBGM[i])
|
||||
{
|
||||
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 128);
|
||||
break;
|
||||
|
@ -2364,7 +2364,7 @@ void AnimTask_TransformMon(u8 taskId)
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10], gBattleAnimArgs[1]);
|
||||
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10], gBattleAnimArgs[1], TRUE);
|
||||
GetBgDataForTransform(&animBg, gBattleAnimAttacker);
|
||||
|
||||
if (IsContest())
|
||||
@ -2451,7 +2451,7 @@ void AnimTask_IsMonInvisible(u8 taskId)
|
||||
|
||||
void AnimTask_CastformGfxDataChange(u8 taskId)
|
||||
{
|
||||
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, TRUE, FALSE);
|
||||
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, TRUE, FALSE, FALSE);
|
||||
DestroyAnimVisualTask(taskId);
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
extern const struct CompressedSpriteSheet gBattleAnimPicTable[];
|
||||
extern const struct CompressedSpritePalette gBattleAnimPaletteTable[];
|
||||
extern const u8 *const gBattleAnims_StatusConditions[];
|
||||
extern const struct OamData gOamData_AffineOff_ObjNormal_8x8;
|
||||
extern const struct OamData gOamData_AffineOff_ObjBlend_64x64;
|
||||
|
||||
@ -568,7 +567,7 @@ void LaunchStatusAnimation(u8 battlerId, u8 statusAnimId)
|
||||
|
||||
gBattleAnimAttacker = battlerId;
|
||||
gBattleAnimTarget = battlerId;
|
||||
LaunchBattleAnimation(gBattleAnims_StatusConditions, statusAnimId, FALSE);
|
||||
LaunchBattleAnimation(ANIM_TYPE_STATUS, statusAnimId);
|
||||
taskId = CreateTask(Task_DoStatusAnimation, 10);
|
||||
gTasks[taskId].data[0] = battlerId;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "sound.h"
|
||||
#include "string_util.h"
|
||||
#include "task.h"
|
||||
#include "test_runner.h"
|
||||
#include "text.h"
|
||||
#include "util.h"
|
||||
#include "window.h"
|
||||
@ -1386,6 +1387,17 @@ static void RecordedOpponentHandlePrintString(void)
|
||||
gBattle_BG0_Y = 0;
|
||||
stringId = (u16 *)(&gBattleResources->bufferA[gActiveBattler][2]);
|
||||
BufferStringBattle(*stringId);
|
||||
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
TestRunner_Battle_RecordMessage(gDisplayedStringBattle);
|
||||
if (gTestRunnerHeadless)
|
||||
{
|
||||
RecordedOpponentBufferExecCompleted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG);
|
||||
gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter;
|
||||
}
|
||||
@ -1397,7 +1409,7 @@ static void RecordedOpponentHandlePrintSelectionString(void)
|
||||
|
||||
static void RecordedOpponentHandleChooseAction(void)
|
||||
{
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, RecordedBattle_GetBattlerAction(gActiveBattler), 0);
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, RecordedBattle_GetBattlerAction(RECORDED_ACTION_TYPE, gActiveBattler), 0);
|
||||
RecordedOpponentBufferExecCompleted();
|
||||
}
|
||||
|
||||
@ -1414,8 +1426,8 @@ static void RecordedOpponentHandleChooseMove(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 moveId = RecordedBattle_GetBattlerAction(gActiveBattler);
|
||||
u8 target = RecordedBattle_GetBattlerAction(gActiveBattler);
|
||||
u8 moveId = RecordedBattle_GetBattlerAction(RECORDED_MOVE_SLOT, gActiveBattler);
|
||||
u8 target = RecordedBattle_GetBattlerAction(RECORDED_MOVE_TARGET, gActiveBattler);
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, moveId | (target << 8));
|
||||
}
|
||||
|
||||
@ -1429,7 +1441,7 @@ static void RecordedOpponentHandleChooseItem(void)
|
||||
|
||||
static void RecordedOpponentHandleChoosePokemon(void)
|
||||
{
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = RecordedBattle_GetBattlerAction(gActiveBattler);
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = RecordedBattle_GetBattlerAction(RECORDED_PARTY_INDEX, gActiveBattler);
|
||||
BtlController_EmitChosenMonReturnValue(BUFFER_B, *(gBattleStruct->monToSwitchIntoId + gActiveBattler), NULL);
|
||||
RecordedOpponentBufferExecCompleted();
|
||||
}
|
||||
@ -1442,22 +1454,23 @@ static void RecordedOpponentHandleCmd23(void)
|
||||
static void RecordedOpponentHandleHealthBarUpdate(void)
|
||||
{
|
||||
s16 hpVal;
|
||||
s32 maxHP, curHP;
|
||||
|
||||
LoadBattleBarGfx(0);
|
||||
hpVal = gBattleResources->bufferA[gActiveBattler][2] | (gBattleResources->bufferA[gActiveBattler][3] << 8);
|
||||
|
||||
maxHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
|
||||
curHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP);
|
||||
|
||||
if (hpVal != INSTANT_HP_BAR_DROP)
|
||||
{
|
||||
u32 maxHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
|
||||
u32 curHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP);
|
||||
|
||||
SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, curHP, hpVal);
|
||||
TestRunner_Battle_RecordHP(gActiveBattler, curHP, min(maxHP, max(0, curHP - hpVal)));
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 maxHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
|
||||
|
||||
SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, 0, hpVal);
|
||||
TestRunner_Battle_RecordHP(gActiveBattler, curHP, 0);
|
||||
}
|
||||
|
||||
gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthbarDone;
|
||||
@ -1478,6 +1491,9 @@ static void RecordedOpponentHandleStatusIconUpdate(void)
|
||||
battlerId = gActiveBattler;
|
||||
gBattleSpritesDataPtr->healthBoxesData[battlerId].statusAnimActive = 0;
|
||||
gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation;
|
||||
|
||||
if (gTestRunnerEnabled)
|
||||
TestRunner_Battle_RecordStatus1(battlerId, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_STATUS));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "sound.h"
|
||||
#include "string_util.h"
|
||||
#include "task.h"
|
||||
#include "test_runner.h"
|
||||
#include "text.h"
|
||||
#include "util.h"
|
||||
#include "window.h"
|
||||
@ -1394,6 +1395,17 @@ static void RecordedPlayerHandlePrintString(void)
|
||||
gBattle_BG0_Y = 0;
|
||||
stringId = (u16 *)(&gBattleResources->bufferA[gActiveBattler][2]);
|
||||
BufferStringBattle(*stringId);
|
||||
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
TestRunner_Battle_RecordMessage(gDisplayedStringBattle);
|
||||
if (gTestRunnerHeadless)
|
||||
{
|
||||
RecordedPlayerBufferExecCompleted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG);
|
||||
gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter;
|
||||
}
|
||||
@ -1407,7 +1419,7 @@ static void ChooseActionInBattlePalace(void)
|
||||
{
|
||||
if (gBattleCommunication[4] >= gBattlersCount / 2)
|
||||
{
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, RecordedBattle_GetBattlerAction(gActiveBattler), 0);
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, RecordedBattle_GetBattlerAction(RECORDED_BATTLE_PALACE_ACTION, gActiveBattler), 0);
|
||||
RecordedPlayerBufferExecCompleted();
|
||||
}
|
||||
}
|
||||
@ -1420,7 +1432,7 @@ static void RecordedPlayerHandleChooseAction(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, RecordedBattle_GetBattlerAction(gActiveBattler), 0);
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, RecordedBattle_GetBattlerAction(RECORDED_ACTION_TYPE, gActiveBattler), 0);
|
||||
RecordedPlayerBufferExecCompleted();
|
||||
}
|
||||
}
|
||||
@ -1438,8 +1450,8 @@ static void RecordedPlayerHandleChooseMove(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 moveId = RecordedBattle_GetBattlerAction(gActiveBattler);
|
||||
u8 target = RecordedBattle_GetBattlerAction(gActiveBattler);
|
||||
u8 moveId = RecordedBattle_GetBattlerAction(RECORDED_MOVE_SLOT, gActiveBattler);
|
||||
u8 target = RecordedBattle_GetBattlerAction(RECORDED_MOVE_TARGET, gActiveBattler);
|
||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, moveId | (target << 8));
|
||||
}
|
||||
|
||||
@ -1453,7 +1465,7 @@ static void RecordedPlayerHandleChooseItem(void)
|
||||
|
||||
static void RecordedPlayerHandleChoosePokemon(void)
|
||||
{
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = RecordedBattle_GetBattlerAction(gActiveBattler);
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = RecordedBattle_GetBattlerAction(RECORDED_PARTY_INDEX, gActiveBattler);
|
||||
BtlController_EmitChosenMonReturnValue(BUFFER_B, *(gBattleStruct->monToSwitchIntoId + gActiveBattler), NULL);
|
||||
RecordedPlayerBufferExecCompleted();
|
||||
}
|
||||
@ -1466,23 +1478,24 @@ static void RecordedPlayerHandleCmd23(void)
|
||||
static void RecordedPlayerHandleHealthBarUpdate(void)
|
||||
{
|
||||
s16 hpVal;
|
||||
s32 maxHP, curHP;
|
||||
|
||||
LoadBattleBarGfx(0);
|
||||
hpVal = gBattleResources->bufferA[gActiveBattler][2] | (gBattleResources->bufferA[gActiveBattler][3] << 8);
|
||||
|
||||
maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
|
||||
curHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP);
|
||||
|
||||
if (hpVal != INSTANT_HP_BAR_DROP)
|
||||
{
|
||||
u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
|
||||
u32 curHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP);
|
||||
|
||||
SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, curHP, hpVal);
|
||||
TestRunner_Battle_RecordHP(gActiveBattler, curHP, min(maxHP, max(0, curHP - hpVal)));
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP);
|
||||
|
||||
SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, 0, hpVal);
|
||||
UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], HP_CURRENT, 0, maxHP);
|
||||
TestRunner_Battle_RecordHP(gActiveBattler, curHP, 0);
|
||||
}
|
||||
|
||||
gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthbarDone;
|
||||
@ -1503,6 +1516,9 @@ static void RecordedPlayerHandleStatusIconUpdate(void)
|
||||
battlerId = gActiveBattler;
|
||||
gBattleSpritesDataPtr->healthBoxesData[battlerId].statusAnimActive = 0;
|
||||
gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation;
|
||||
|
||||
if (gTestRunnerEnabled)
|
||||
TestRunner_Battle_RecordStatus1(battlerId, GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_STATUS));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1107,7 +1107,7 @@ static void Task_DebugMenuProcessInput(u8 taskId)
|
||||
struct BattleDebugMenu *data = GetStructPtr(taskId);
|
||||
|
||||
// Exit the menu.
|
||||
if (JOY_NEW(SELECT_BUTTON))
|
||||
if (JOY_NEW(SELECT_BUTTON) || ((JOY_NEW(B_BUTTON)) && data->activeWindow == ACTIVE_WIN_MAIN))
|
||||
{
|
||||
BeginNormalPaletteFade(-1, 0, 0, 0x10, 0);
|
||||
gTasks[taskId].func = Task_DebugMenuFadeOut;
|
||||
|
@ -4237,7 +4237,7 @@ static u8 Task_GetInfoCardInput(u8 taskId)
|
||||
#undef tUsingAlternateSlot
|
||||
|
||||
// allocatedArray below needs to be large enough to hold stat totals for each mon, or totals of each type of move points
|
||||
#define ALLOC_ARRAY_SIZE (NUM_STATS * FRONTIER_PARTY_SIZE >= NUM_MOVE_POINT_TYPES ? (NUM_STATS * FRONTIER_PARTY_SIZE) : NUM_MOVE_POINT_TYPES)
|
||||
#define ALLOC_ARRAY_SIZE max(NUM_STATS * FRONTIER_PARTY_SIZE, NUM_MOVE_POINT_TYPES)
|
||||
|
||||
static void DisplayTrainerInfoOnCard(u8 flags, u8 trainerTourneyId)
|
||||
{
|
||||
|
@ -27,8 +27,6 @@
|
||||
#include "constants/battle_palace.h"
|
||||
|
||||
extern const u8 gBattlePalaceNatureToMoveTarget[];
|
||||
extern const u8 *const gBattleAnims_General[];
|
||||
extern const u8 *const gBattleAnims_Special[];
|
||||
extern const struct CompressedSpriteSheet gSpriteSheet_EnemyShadow;
|
||||
extern const struct SpriteTemplate gSpriteTemplate_EnemyShadow;
|
||||
|
||||
@ -465,7 +463,7 @@ bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattler, u8 atkBattler, u8 de
|
||||
gBattleAnimAttacker = atkBattler;
|
||||
gBattleAnimTarget = defBattler;
|
||||
gBattleSpritesDataPtr->animationData->animArg = argument;
|
||||
LaunchBattleAnimation(gBattleAnims_General, tableId, FALSE);
|
||||
LaunchBattleAnimation(ANIM_TYPE_GENERAL, tableId);
|
||||
taskId = CreateTask(Task_ClearBitWhenBattleTableAnimDone, 10);
|
||||
gTasks[taskId].tBattlerId = activeBattler;
|
||||
gBattleSpritesDataPtr->healthBoxesData[gTasks[taskId].tBattlerId].animFromTableActive = 1;
|
||||
@ -509,7 +507,7 @@ void InitAndLaunchSpecialAnimation(u8 activeBattler, u8 atkBattler, u8 defBattle
|
||||
|
||||
gBattleAnimAttacker = atkBattler;
|
||||
gBattleAnimTarget = defBattler;
|
||||
LaunchBattleAnimation(gBattleAnims_Special, tableId, FALSE);
|
||||
LaunchBattleAnimation(ANIM_TYPE_SPECIAL, tableId);
|
||||
taskId = CreateTask(Task_ClearBitWhenSpecialAnimDone, 10);
|
||||
gTasks[taskId].tBattlerId = activeBattler;
|
||||
gBattleSpritesDataPtr->healthBoxesData[gTasks[taskId].tBattlerId].specialAnimActive = 1;
|
||||
@ -849,7 +847,7 @@ void CopyBattleSpriteInvisibility(u8 battlerId)
|
||||
gBattleSpritesDataPtr->battlerData[battlerId].invisible = gSprites[gBattlerSpriteIds[battlerId]].invisible;
|
||||
}
|
||||
|
||||
void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool8 castform, bool32 megaEvo)
|
||||
void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool8 castform, bool32 megaEvo, bool8 trackEnemyPersonality)
|
||||
{
|
||||
u32 personalityValue, otId, position, paletteOffset, targetSpecies;
|
||||
const void *lzPaletteData, *src;
|
||||
@ -878,7 +876,10 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool8 castform, bo
|
||||
|
||||
if (GetBattlerSide(battlerAtk) == B_SIDE_PLAYER)
|
||||
{
|
||||
personalityValue = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY);
|
||||
if (trackEnemyPersonality)
|
||||
personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY);
|
||||
else
|
||||
personalityValue = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY);
|
||||
otId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID);
|
||||
|
||||
HandleLoadSpecialPokePic(FALSE,
|
||||
@ -888,7 +889,10 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool8 castform, bo
|
||||
}
|
||||
else
|
||||
{
|
||||
personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY);
|
||||
if (trackEnemyPersonality)
|
||||
personalityValue = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY);
|
||||
else
|
||||
personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY);
|
||||
otId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID);
|
||||
|
||||
HandleLoadSpecialPokePic(TRUE,
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "item.h"
|
||||
#include "item_icon.h"
|
||||
#include "item_use.h"
|
||||
#include "test_runner.h"
|
||||
#include "constants/battle_anim.h"
|
||||
#include "constants/rgb.h"
|
||||
#include "constants/songs.h"
|
||||
@ -3069,6 +3070,13 @@ void CreateAbilityPopUp(u8 battlerId, u32 ability, bool32 isDoubleBattle)
|
||||
const s16 (*coords)[2];
|
||||
u8 spriteId1, spriteId2, battlerPosition, taskId;
|
||||
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
TestRunner_Battle_RecordAbilityPopUp(battlerId, ability);
|
||||
if (gTestRunnerHeadless)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gBattleScripting.abilityPopupOverwrite != 0)
|
||||
ability = gBattleScripting.abilityPopupOverwrite;
|
||||
|
||||
@ -3184,9 +3192,12 @@ static void SpriteCb_AbilityPopUp(struct Sprite *sprite)
|
||||
|
||||
void DestroyAbilityPopUp(u8 battlerId)
|
||||
{
|
||||
gSprites[gBattleStruct->abilityPopUpSpriteIds[battlerId][0]].tFrames = 0;
|
||||
gSprites[gBattleStruct->abilityPopUpSpriteIds[battlerId][1]].tFrames = 0;
|
||||
gBattleScripting.fixedPopup = FALSE;
|
||||
if (gBattleStruct->activeAbilityPopUps & gBitTable[battlerId])
|
||||
{
|
||||
gSprites[gBattleStruct->abilityPopUpSpriteIds[battlerId][0]].tFrames = 0;
|
||||
gSprites[gBattleStruct->abilityPopUpSpriteIds[battlerId][1]].tFrames = 0;
|
||||
gBattleScripting.fixedPopup = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void Task_FreeAbilityPopUpGfx(u8 taskId)
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "string_util.h"
|
||||
#include "strings.h"
|
||||
#include "task.h"
|
||||
#include "test_runner.h"
|
||||
#include "text.h"
|
||||
#include "trig.h"
|
||||
#include "tv.h"
|
||||
@ -474,7 +475,8 @@ const u8 *const gStatusConditionStringsTable[][2] =
|
||||
|
||||
void CB2_InitBattle(void)
|
||||
{
|
||||
MoveSaveBlocks_ResetHeap();
|
||||
if (!gTestRunnerEnabled)
|
||||
MoveSaveBlocks_ResetHeap();
|
||||
AllocateBattleResources();
|
||||
AllocateBattleSpritesData();
|
||||
AllocateMonSpritesGfx();
|
||||
@ -1793,6 +1795,8 @@ void CB2_QuitRecordedBattle(void)
|
||||
{
|
||||
m4aMPlayStop(&gMPlayInfo_SE1);
|
||||
m4aMPlayStop(&gMPlayInfo_SE2);
|
||||
if (gTestRunnerEnabled)
|
||||
TestRunner_Battle_AfterLastTurn();
|
||||
FreeRestoreBattleData();
|
||||
FreeAllWindowBuffers();
|
||||
SetMainCallback2(gMain.savedCallback);
|
||||
@ -4859,9 +4863,14 @@ static void CheckMegaEvolutionBeforeTurn(void)
|
||||
if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler]
|
||||
&& !(gProtectStructs[gActiveBattler].noValidMoves))
|
||||
{
|
||||
struct Pokemon *mon;
|
||||
if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT)
|
||||
mon = &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]];
|
||||
else
|
||||
mon = &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]];
|
||||
gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]);
|
||||
gLastUsedItem = gBattleMons[gActiveBattler].item;
|
||||
if (gBattleStruct->mega.isWishMegaEvo == TRUE)
|
||||
if (GetBattleFormChangeTargetSpecies(gActiveBattler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
|
||||
BattleScriptExecute(BattleScript_WishMegaEvolution);
|
||||
else
|
||||
BattleScriptExecute(BattleScript_MegaEvolution);
|
||||
@ -5204,6 +5213,8 @@ static void HandleEndTurn_FinishBattle(void)
|
||||
}
|
||||
|
||||
RecordedBattle_SetPlaybackFinished();
|
||||
if (gTestRunnerEnabled)
|
||||
TestRunner_Battle_AfterLastTurn();
|
||||
BeginFastPaletteFade(3);
|
||||
FadeOutMapMusic(5);
|
||||
#if B_TRAINERS_KNOCK_OFF_ITEMS == TRUE
|
||||
|
@ -209,8 +209,8 @@ static const u8 sText_PkmnFellIntoNightmare[] = _("{B_DEF_NAME_WITH_PREFIX} fell
|
||||
static const u8 sText_PkmnLockedInNightmare[] = _("{B_ATK_NAME_WITH_PREFIX} is locked\nin a NIGHTMARE!");
|
||||
static const u8 sText_PkmnLaidCurse[] = _("{B_ATK_NAME_WITH_PREFIX} cut its own HP and\nlaid a CURSE on {B_DEF_NAME_WITH_PREFIX}!");
|
||||
static const u8 sText_PkmnAfflictedByCurse[] = _("{B_ATK_NAME_WITH_PREFIX} is afflicted\nby the CURSE!");
|
||||
static const u8 sText_SpikesScattered[] = _("SPIKES were scattered all around\nthe opponent's side!");
|
||||
static const u8 sText_PkmnHurtBySpikes[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is hurt\nby SPIKES!");
|
||||
static const u8 sText_SpikesScattered[] = _("Spikes were scattered all around\nthe opponent's side!");
|
||||
static const u8 sText_PkmnHurtBySpikes[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is hurt\nby spikes!");
|
||||
static const u8 sText_PkmnIdentified[] = _("{B_ATK_NAME_WITH_PREFIX} identified\n{B_DEF_NAME_WITH_PREFIX}!");
|
||||
static const u8 sText_PkmnPerishCountFell[] = _("{B_ATK_NAME_WITH_PREFIX}'s PERISH count\nfell to {B_BUFF1}!");
|
||||
static const u8 sText_PkmnBracedItself[] = _("{B_ATK_NAME_WITH_PREFIX} braced\nitself!");
|
||||
@ -220,7 +220,7 @@ static const u8 sText_PkmnCutHPMaxedAttack[] = _("{B_ATK_NAME_WITH_PREFIX} cut i
|
||||
static const u8 sText_PkmnCopiedStatChanges[] = _("{B_ATK_NAME_WITH_PREFIX} copied\n{B_DEF_NAME_WITH_PREFIX}'s stat changes!");
|
||||
static const u8 sText_PkmnGotFree[] = _("{B_ATK_NAME_WITH_PREFIX} got free of\n{B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1}!");
|
||||
static const u8 sText_PkmnShedLeechSeed[] = _("{B_ATK_NAME_WITH_PREFIX} shed\nLEECH SEED!");
|
||||
static const u8 sText_PkmnBlewAwaySpikes[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nSPIKES!");
|
||||
static const u8 sText_PkmnBlewAwaySpikes[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nspikes!");
|
||||
static const u8 sText_PkmnFledFromBattle[] = _("{B_ATK_NAME_WITH_PREFIX} fled from\nbattle!");
|
||||
static const u8 sText_PkmnForesawAttack[] = _("{B_ATK_NAME_WITH_PREFIX} foresaw\nan attack!");
|
||||
static const u8 sText_PkmnTookAttack[] = _("{B_DEF_NAME_WITH_PREFIX} took the\n{B_BUFF1} attack!");
|
||||
@ -230,7 +230,7 @@ static const u8 sText_PkmnCenterAttention[] = _("{B_DEF_NAME_WITH_PREFIX} became
|
||||
static const u8 sText_PkmnChargingPower[] = _("{B_ATK_NAME_WITH_PREFIX} began\ncharging power!");
|
||||
static const u8 sText_NaturePowerTurnedInto[] = _("NATURE POWER turned into\n{B_CURRENT_MOVE}!");
|
||||
static const u8 sText_PkmnStatusNormal[] = _("{B_ATK_NAME_WITH_PREFIX}'s status\nreturned to normal!");
|
||||
static const u8 sText_PkmnSubjectedToTorment[] = _("{B_DEF_NAME_WITH_PREFIX} was subjected\nto TORMENT!");
|
||||
static const u8 sText_PkmnSubjectedToTorment[] = _("{B_DEF_NAME_WITH_PREFIX} was subjected\nto torment!");
|
||||
static const u8 sText_PkmnTighteningFocus[] = _("{B_ATK_NAME_WITH_PREFIX} is tightening\nits focus!");
|
||||
static const u8 sText_PkmnFellForTaunt[] = _("{B_DEF_NAME_WITH_PREFIX} fell for\nthe Taunt!");
|
||||
static const u8 sText_PkmnReadyToHelp[] = _("{B_ATK_NAME_WITH_PREFIX} is ready to\nhelp {B_DEF_NAME_WITH_PREFIX}!");
|
||||
@ -433,13 +433,19 @@ static const u8 sText_ExclamationMark2[] = _("!");
|
||||
static const u8 sText_ExclamationMark3[] = _("!");
|
||||
static const u8 sText_ExclamationMark4[] = _("!");
|
||||
static const u8 sText_ExclamationMark5[] = _("!");
|
||||
static const u8 sText_HP[] = _("HP");
|
||||
static const u8 sText_Attack[] = _("attack");
|
||||
static const u8 sText_Defense[] = _("defense");
|
||||
static const u8 sText_Speed[] = _("speed");
|
||||
static const u8 sText_SpAttack[] = _("sp. attack");
|
||||
static const u8 sText_SpDefense[] = _("sp. defense");
|
||||
static const u8 sText_Accuracy[] = _("accuracy");
|
||||
static const u8 sText_Evasiveness[] = _("evasiveness");
|
||||
|
||||
const u8 *const gStatNamesTable[NUM_BATTLE_STATS] =
|
||||
{
|
||||
gText_HP3, gText_Attack, gText_Defense,
|
||||
gText_Speed, gText_SpAtk, gText_SpDef,
|
||||
sText_HP, sText_Attack, sText_Defense,
|
||||
sText_Speed, sText_SpAttack, sText_SpDefense,
|
||||
sText_Accuracy, sText_Evasiveness
|
||||
};
|
||||
|
||||
@ -515,14 +521,14 @@ static const u8 sText_TwoInGameTrainersDefeated[];
|
||||
static const u8 sText_Trainer2LoseText[];
|
||||
|
||||
// New battle strings.
|
||||
static const s8 sText_EnduredViaSturdy[] = _("{B_DEF_NAME_WITH_PREFIX} Endured\nthe hit using {B_DEF_ABILITY}!");
|
||||
static const s8 sText_EnduredViaSturdy[] = _("{B_DEF_NAME_WITH_PREFIX} endured\nthe hit using {B_DEF_ABILITY}!");
|
||||
static const s8 sText_PowerHerbActivation[] = _("{B_ATK_NAME_WITH_PREFIX} became fully charged\ndue to its {B_LAST_ITEM}!");
|
||||
static const s8 sText_HurtByItem[] = _("{B_ATK_NAME_WITH_PREFIX} was hurt\nby its {B_LAST_ITEM}!");
|
||||
static const s8 sText_BadlyPoisonedByItem[] = _("{B_EFF_NAME_WITH_PREFIX} was badly \npoisoned by the {B_LAST_ITEM}!");
|
||||
static const s8 sText_BadlyPoisonedByItem[] = _("{B_EFF_NAME_WITH_PREFIX} was badly\npoisoned by the {B_LAST_ITEM}!");
|
||||
static const s8 sText_BurnedByItem[] = _("{B_EFF_NAME_WITH_PREFIX} was burned\nby the {B_LAST_ITEM}!");
|
||||
static const s8 sText_TargetAbilityActivates[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} activates!");
|
||||
static const u8 sText_GravityIntensified[] = _("Gravity intensified!");
|
||||
static const u8 sText_TargetIdentified[] = _("{B_DEF_NAME_WITH_PREFIX} was \nidentified!");
|
||||
static const u8 sText_TargetIdentified[] = _("{B_DEF_NAME_WITH_PREFIX} was\nidentified!");
|
||||
static const u8 sText_TargetWokeUp[] = _("{B_DEF_NAME_WITH_PREFIX} woke up!");
|
||||
static const u8 sText_PkmnStoleAndAteItem[] = _("{B_ATK_NAME_WITH_PREFIX} stole and\nate {B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}!");
|
||||
static const u8 sText_TailWindBlew[] = _("The tailwind blew from\nbehind {B_ATK_TEAM2} team!");
|
||||
@ -591,7 +597,7 @@ static const u8 sText_TargetsStatWasMaxedOut[] = _("{B_DEF_NAME_WITH_PREFIX}'s {
|
||||
static const u8 sText_PoisonHealHpUp[] = _("The poisoning healed {B_ATK_NAME_WITH_PREFIX}\na little bit!");
|
||||
static const u8 sText_BadDreamsDmg[] = _("{B_DEF_NAME_WITH_PREFIX} is tormented!");
|
||||
static const u8 sText_MoldBreakerEnters[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} breaks the mold!");
|
||||
static const u8 sText_TeravoltEnters[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is radiating \na bursting aura!");
|
||||
static const u8 sText_TeravoltEnters[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is radiating\na bursting aura!");
|
||||
static const u8 sText_TurboblazeEnters[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is radiating\na blazing aura!");
|
||||
static const u8 sText_SlowStartEnters[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} can't get it going!");
|
||||
static const u8 sText_SlowStartEnd[] = _("{B_ATK_NAME_WITH_PREFIX} finally got\nits act together!");
|
||||
@ -636,7 +642,7 @@ static const u8 sText_TargetElectrified[] = _("The {B_DEF_NAME_WITH_PREFIX}'s mo
|
||||
static const u8 sText_AssaultVestDoesntAllow[] = _("{B_LAST_ITEM}'s effects prevent\nstatus moves from being used!\p");
|
||||
static const u8 sText_GravityPreventsUsage[] = _("{B_ATK_NAME_WITH_PREFIX} can't use {B_CURRENT_MOVE}\nbecause of gravity!\p");
|
||||
static const u8 sText_HealBlockPreventsUsage[] = _("{B_ATK_NAME_WITH_PREFIX} was\nprevented from healing!\p");
|
||||
static const u8 sText_MegaEvoReacting[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_LAST_ITEM} is \nreacting to {B_ATK_TRAINER_NAME}'s Mega Ring!");
|
||||
static const u8 sText_MegaEvoReacting[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_LAST_ITEM} is\nreacting to {B_ATK_TRAINER_NAME}'s Mega Ring!");
|
||||
static const u8 sText_FerventWishReached[] = _("{B_ATK_TRAINER_NAME}'s fervent wish\nhas reached {B_ATK_NAME_WITH_PREFIX}!");
|
||||
static const u8 sText_MegaEvoEvolved[] = _("{B_ATK_NAME_WITH_PREFIX} has Mega Evolved into\nMega {B_BUFF1}!");
|
||||
static const u8 sText_drastically[] = _("drastically ");
|
||||
|
@ -834,7 +834,7 @@ static bool8 DoesAbilityPreventStatus(struct Pokemon *mon, u32 status)
|
||||
ret = TRUE;
|
||||
break;
|
||||
case STATUS1_TOXIC_POISON:
|
||||
if (ability == ABILITY_IMMUNITY)
|
||||
if (ability == ABILITY_IMMUNITY || ability == ABILITY_PASTEL_VEIL)
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
|
@ -2880,11 +2880,11 @@ void SetMoveEffect(bool32 primary, u32 certain)
|
||||
statusChanged = TRUE;
|
||||
break;
|
||||
case STATUS1_POISON:
|
||||
if (battlerAbility == ABILITY_IMMUNITY
|
||||
if ((battlerAbility == ABILITY_IMMUNITY || battlerAbility == ABILITY_PASTEL_VEIL)
|
||||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||||
{
|
||||
gLastUsedAbility = ABILITY_IMMUNITY;
|
||||
RecordAbilityBattle(gEffectBattler, ABILITY_IMMUNITY);
|
||||
gLastUsedAbility = battlerAbility;
|
||||
RecordAbilityBattle(gEffectBattler, battlerAbility);
|
||||
|
||||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||||
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
|
||||
@ -3004,10 +3004,11 @@ void SetMoveEffect(bool32 primary, u32 certain)
|
||||
statusChanged = TRUE;
|
||||
break;
|
||||
case STATUS1_TOXIC_POISON:
|
||||
if (battlerAbility == ABILITY_IMMUNITY && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||||
if ((battlerAbility == ABILITY_IMMUNITY || battlerAbility == ABILITY_PASTEL_VEIL)
|
||||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||||
{
|
||||
gLastUsedAbility = ABILITY_IMMUNITY;
|
||||
RecordAbilityBattle(gEffectBattler, ABILITY_IMMUNITY);
|
||||
gLastUsedAbility = battlerAbility;
|
||||
RecordAbilityBattle(gEffectBattler, battlerAbility);
|
||||
|
||||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||||
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
|
||||
@ -3485,7 +3486,9 @@ void SetMoveEffect(bool32 primary, u32 certain)
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_FLAME_BURST:
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget)) && GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) != ABILITY_MAGIC_GUARD)
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget))
|
||||
&& !(gStatuses3[BATTLE_PARTNER(gBattlerTarget)] & STATUS3_SEMI_INVULNERABLE)
|
||||
&& GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) != ABILITY_MAGIC_GUARD)
|
||||
{
|
||||
gBattleScripting.savedBattler = BATTLE_PARTNER(gBattlerTarget);
|
||||
gBattleMoveDamage = gBattleMons[BATTLE_PARTNER(gBattlerTarget)].hp / 16;
|
||||
@ -5402,7 +5405,7 @@ static void Cmd_moveend(void)
|
||||
gStatuses3[gBattlerTarget] |= STATUS3_SMACKED_DOWN;
|
||||
gStatuses3[gBattlerTarget] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR);
|
||||
effect = TRUE;
|
||||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||||
BattleScriptPush(gBattlescriptCurrInstr);
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEffectSmackDown;
|
||||
}
|
||||
break;
|
||||
@ -5415,7 +5418,7 @@ static void Cmd_moveend(void)
|
||||
BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
effect = TRUE;
|
||||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||||
BattleScriptPush(gBattlescriptCurrInstr);
|
||||
switch (gBattleMoves[gCurrentMove].argument)
|
||||
{
|
||||
case STATUS1_PARALYSIS:
|
||||
@ -5912,6 +5915,7 @@ static void Cmd_moveend(void)
|
||||
if (gBattleResources->flags->flags[i] & RESOURCE_FLAG_EMERGENCY_EXIT)
|
||||
{
|
||||
gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_EMERGENCY_EXIT;
|
||||
gSpecialStatuses[i].emergencyExited = TRUE;
|
||||
gBattlerTarget = gBattlerAbility = i;
|
||||
BattleScriptPushCursor();
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || GetBattlerSide(i) == B_SIDE_PLAYER)
|
||||
@ -6754,6 +6758,7 @@ static void Cmd_switchineffects(void)
|
||||
if (!(gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
|
||||
&& !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_STEEL)
|
||||
&& GetBattlerAbility(gActiveBattler) != ABILITY_IMMUNITY
|
||||
&& !IsAbilityOnSide(gActiveBattler, ABILITY_PASTEL_VEIL)
|
||||
&& !(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SAFEGUARD)
|
||||
&& !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN))
|
||||
{
|
||||
@ -10048,9 +10053,9 @@ static void Cmd_various(void)
|
||||
return;
|
||||
case VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY:
|
||||
if (ItemId_GetPocket(gLastUsedItem) == POCKET_BERRIES)
|
||||
gBattlescriptCurrInstr += 7;
|
||||
else
|
||||
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3);
|
||||
else
|
||||
gBattlescriptCurrInstr += 7;
|
||||
return;
|
||||
case VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT:
|
||||
if (ItemId_GetHoldEffect(gLastUsedItem) == gBattlescriptCurrInstr[3])
|
||||
@ -10133,6 +10138,8 @@ static void Cmd_various(void)
|
||||
break;
|
||||
}
|
||||
PREPARE_STAT_BUFFER(gBattleTextBuff1, statId);
|
||||
gBattlescriptCurrInstr += 4;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case VARIOUS_TEATIME_TARGETS:
|
||||
@ -10214,6 +10221,12 @@ static void Cmd_various(void)
|
||||
gBattlescriptCurrInstr += 7;
|
||||
}
|
||||
return;
|
||||
case VARIOUS_JUMP_IF_EMERGENCY_EXITED:
|
||||
if (gSpecialStatuses[gActiveBattler].emergencyExited)
|
||||
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3);
|
||||
else
|
||||
gBattlescriptCurrInstr += 7;
|
||||
return;
|
||||
} // End of switch (gBattlescriptCurrInstr[2])
|
||||
|
||||
gBattlescriptCurrInstr += 3;
|
||||
@ -12058,7 +12071,9 @@ static void Cmd_trysetencore(void)
|
||||
break;
|
||||
}
|
||||
|
||||
if (gLastMoves[gBattlerTarget] == MOVE_STRUGGLE
|
||||
if (gLastMoves[gBattlerTarget] == MOVE_NONE
|
||||
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE
|
||||
|| gLastMoves[gBattlerTarget] == MOVE_STRUGGLE
|
||||
|| gLastMoves[gBattlerTarget] == MOVE_ENCORE
|
||||
|| gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE
|
||||
|| gLastMoves[gBattlerTarget] == MOVE_SHELL_TRAP)
|
||||
|
@ -515,9 +515,11 @@ void HandleAction_UseMove(void)
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
BattleArena_AddMindPoints(gBattlerAttacker);
|
||||
|
||||
// Record HP of each battler
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
{
|
||||
gBattleStruct->hpBefore[i] = gBattleMons[i].hp;
|
||||
gSpecialStatuses[i].emergencyExited = FALSE;
|
||||
}
|
||||
|
||||
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
|
||||
}
|
||||
@ -5566,7 +5568,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
&& gBattleMons[gBattlerAttacker].hp != 0
|
||||
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
|
||||
&& TARGET_TURN_DAMAGED
|
||||
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget)
|
||||
&& CanBePoisoned(gBattlerTarget, gBattlerAttacker)
|
||||
&& IsMoveMakingContact(move, gBattlerAttacker)
|
||||
&& (Random() % 3) == 0)
|
||||
{
|
||||
@ -5803,7 +5805,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
|
||||
&& IS_MOVE_PHYSICAL(gCurrentMove)
|
||||
&& TARGET_TURN_DAMAGED
|
||||
&& !(gSideStatuses[gBattlerAttacker] & SIDE_STATUS_TOXIC_SPIKES)
|
||||
&& !(gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_TOXIC_SPIKES)
|
||||
&& IsBattlerAlive(gBattlerTarget))
|
||||
{
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
@ -5895,6 +5897,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
switch (GetBattlerAbility(battler))
|
||||
{
|
||||
case ABILITY_IMMUNITY:
|
||||
case ABILITY_PASTEL_VEIL:
|
||||
if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER))
|
||||
{
|
||||
StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
|
||||
@ -8150,11 +8153,9 @@ bool32 IsMoveMakingContact(u16 move, u8 battlerAtk)
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
else if (GetBattlerAbility(battlerAtk) == ABILITY_LONG_REACH || atkHoldEffect == HOLD_EFFECT_PUNCHING_GLOVE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
else if (atkHoldEffect == HOLD_EFFECT_PROTECTIVE_PADS)
|
||||
else if ((atkHoldEffect == HOLD_EFFECT_PUNCHING_GLOVE && (gBattleMoves[move].flags & FLAG_IRON_FIST_BOOST))
|
||||
|| atkHoldEffect == HOLD_EFFECT_PROTECTIVE_PADS
|
||||
|| GetBattlerAbility(battlerAtk) == ABILITY_LONG_REACH)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@ -10027,18 +10028,12 @@ bool32 CanMegaEvolve(u8 battlerId)
|
||||
|
||||
// Can Mega Evolve via Mega Stone.
|
||||
if (holdEffect == HOLD_EFFECT_MEGA_STONE)
|
||||
{
|
||||
gBattleStruct->mega.isWishMegaEvo = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is an entry in the evolution table for Wish Mega Evolution.
|
||||
if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
|
||||
{
|
||||
gBattleStruct->mega.isWishMegaEvo = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// No checks passed, the mon CAN'T mega evolve.
|
||||
return FALSE;
|
||||
|
@ -8150,7 +8150,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
|
||||
|
||||
[MOVE_WOOD_HAMMER] =
|
||||
{
|
||||
.effect = EFFECT_RECOIL_25,
|
||||
.effect = EFFECT_RECOIL_33,
|
||||
.power = 120,
|
||||
.type = TYPE_GRASS,
|
||||
.accuracy = 100,
|
||||
|
@ -1930,3 +1930,18 @@ const u32 gItemIcon_Gem[] = INCBIN_U32("graphics/items/icons/gem.4bpp.lz");
|
||||
const u32 gItemIconPalette_Ruby[] = INCBIN_U32("graphics/items/icon_palettes/ruby.gbapal.lz");
|
||||
|
||||
const u32 gItemIconPalette_Sapphire[] = INCBIN_U32("graphics/items/icon_palettes/sapphire.gbapal.lz");
|
||||
|
||||
//const u32 gItemIcon_AbilityShield[] = INCBIN_U32("graphics/items/icons/ability_shield.4bpp.lz");
|
||||
//const u32 gItemIconPalette_AbilityShield[] = INCBIN_U32("graphics/items/icon_palettes/ability_shield.gbapal.lz");
|
||||
|
||||
//const u32 gItemIcon_ClearAmulet[] = INCBIN_U32("graphics/items/icons/clear_amulet.4bpp.lz");
|
||||
//const u32 gItemIconPalette_ClearAmulet[] = INCBIN_U32("graphics/items/icon_palettes/clear_amulet.gbapal.lz");
|
||||
|
||||
//const u32 gItemIcon_PunchingGlove[] = INCBIN_U32("graphics/items/icons/punching_glove.4bpp.lz");
|
||||
//const u32 gItemIconPalette_PunchingGlove[] = INCBIN_U32("graphics/items/icon_palettes/punching_glove.gbapal.lz");
|
||||
|
||||
//const u32 gItemIcon_CovertCloak[] = INCBIN_U32("graphics/items/icons/covert_cloak.4bpp.lz");
|
||||
//const u32 gItemIconPalette_CovertCloak[] = INCBIN_U32("graphics/items/icon_palettes/covert_cloak.gbapal.lz");
|
||||
|
||||
//const u32 gItemIcon_LoadedDice[] = INCBIN_U32("graphics/items/icons/loaded_dice.4bpp.lz");
|
||||
//const u32 gItemIconPalette_LoadedDice[] = INCBIN_U32("graphics/items/icon_palettes/loaded_dice.gbapal.lz");
|
||||
|
@ -803,6 +803,11 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] =
|
||||
[ITEM_TEA] = {gItemIcon_Tea, gItemIconPalette_Tea},
|
||||
[ITEM_RUBY] = {gItemIcon_Gem, gItemIconPalette_Ruby},
|
||||
[ITEM_SAPPHIRE] = {gItemIcon_Gem, gItemIconPalette_Sapphire},
|
||||
[ITEM_ABILITY_SHIELD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_AbilityShield, gItemIconPalette_AbilityShield},
|
||||
[ITEM_CLEAR_AMULET] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ClearAmulet, gItemIconPalette_ClearAmulet},
|
||||
[ITEM_PUNCHING_GLOVE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PunchingGlove, gItemIconPalette_PunchingGlove},
|
||||
[ITEM_COVERT_CLOAK] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_CovertCloak, gItemIconPalette_CovertCloak},
|
||||
[ITEM_LOADED_DICE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LoadedDice, gItemIconPalette_LoadedDice},
|
||||
// Return to field arrow
|
||||
[ITEMS_COUNT] = {gItemIcon_ReturnToFieldArrow, gItemIconPalette_ReturnToFieldArrow},
|
||||
};
|
||||
|
@ -9841,4 +9841,70 @@ const struct Item gItems[] =
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
|
||||
},
|
||||
|
||||
[ITEM_ABILITY_SHIELD] =
|
||||
{
|
||||
.name = _("AbilityShield"),
|
||||
.itemId = ITEM_ABILITY_SHIELD,
|
||||
.price = 20000,
|
||||
.holdEffect = HOLD_EFFECT_ABILITY_SHIELD,
|
||||
.description = sAbilityShieldDesc,
|
||||
.pocket = POCKET_ITEMS,
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
|
||||
.flingPower = 30,
|
||||
},
|
||||
|
||||
[ITEM_CLEAR_AMULET] =
|
||||
{
|
||||
.name = _("Clear Amulet"),
|
||||
.itemId = ITEM_CLEAR_AMULET,
|
||||
.price = 30000,
|
||||
.holdEffect = HOLD_EFFECT_CLEAR_AMULET,
|
||||
.description = sClearAmuletDesc,
|
||||
.pocket = POCKET_ITEMS,
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
|
||||
.flingPower = 30,
|
||||
},
|
||||
|
||||
[ITEM_PUNCHING_GLOVE] =
|
||||
{
|
||||
.name = _("PunchingGlove"),
|
||||
.itemId = ITEM_PUNCHING_GLOVE,
|
||||
.price = 15000,
|
||||
.holdEffect = HOLD_EFFECT_PUNCHING_GLOVE,
|
||||
.description = sPunchingGloveDesc,
|
||||
.pocket = POCKET_ITEMS,
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
|
||||
.flingPower = 30,
|
||||
},
|
||||
|
||||
[ITEM_COVERT_CLOAK] =
|
||||
{
|
||||
.name = _("Covert Cloak"),
|
||||
.itemId = ITEM_COVERT_CLOAK,
|
||||
.price = 20000,
|
||||
.holdEffect = HOLD_EFFECT_COVERT_CLOAK,
|
||||
.description = sCovertCloakDesc,
|
||||
.pocket = POCKET_ITEMS,
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
|
||||
.flingPower = 30,
|
||||
},
|
||||
|
||||
[ITEM_LOADED_DICE] =
|
||||
{
|
||||
//YellwApricorn
|
||||
.name = _("Loaded Dice"),
|
||||
.itemId = ITEM_LOADED_DICE,
|
||||
.price = 20000,
|
||||
.holdEffect = HOLD_EFFECT_LOADED_DICE,
|
||||
.description = sLoadedDiceDesc,
|
||||
.pocket = POCKET_ITEMS,
|
||||
.type = ITEM_USE_BAG_MENU,
|
||||
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
|
||||
.flingPower = 30,
|
||||
},
|
||||
};
|
||||
|
@ -3792,3 +3792,28 @@ static const u8 sSapphireDesc[] = _(
|
||||
"A brilliant blue gem\n"
|
||||
"that symbolizes\n"
|
||||
"honesty.");
|
||||
|
||||
static const u8 sAbilityShieldDesc[] = _(
|
||||
"Ability changes are\n"
|
||||
"prevented for this\n"
|
||||
"items's holder.");
|
||||
|
||||
static const u8 sClearAmuletDesc[] = _(
|
||||
"Stat lowering is\n"
|
||||
"prevented for this\n"
|
||||
"items's holder.");
|
||||
|
||||
static const u8 sPunchingGloveDesc[] = _(
|
||||
"Powers up punching\n"
|
||||
"moves and removes\n"
|
||||
"their contact.");
|
||||
|
||||
static const u8 sCovertCloakDesc[] = _(
|
||||
"Protects the holder\n"
|
||||
"from secondary\n"
|
||||
"move effects.");
|
||||
|
||||
static const u8 sLoadedDiceDesc[] = _(
|
||||
"Rolls high numbers.\n"
|
||||
"Multihit strikes\n"
|
||||
"hit more times.");
|
||||
|
@ -541,11 +541,17 @@ static void RemoveIVIndexFromList(u8 *ivs, u8 selectedIv)
|
||||
|
||||
static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare)
|
||||
{
|
||||
u16 motherItem = GetBoxMonData(&daycare->mons[0].mon, MON_DATA_HELD_ITEM);
|
||||
u16 fatherItem = GetBoxMonData(&daycare->mons[1].mon, MON_DATA_HELD_ITEM);
|
||||
u8 i;
|
||||
u8 selectedIvs[INHERITED_IV_COUNT];
|
||||
u8 selectedIvs[5];
|
||||
u8 availableIVs[NUM_STATS];
|
||||
u8 whichParents[INHERITED_IV_COUNT];
|
||||
u8 whichParents[5];
|
||||
u8 iv;
|
||||
u8 howManyIVs = 3;
|
||||
|
||||
if (motherItem == ITEM_DESTINY_KNOT || fatherItem == ITEM_DESTINY_KNOT)
|
||||
howManyIVs = 5;
|
||||
|
||||
// Initialize a list of IV indices.
|
||||
for (i = 0; i < NUM_STATS; i++)
|
||||
@ -553,8 +559,8 @@ static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare)
|
||||
availableIVs[i] = i;
|
||||
}
|
||||
|
||||
// Select the 3 IVs that will be inherited.
|
||||
for (i = 0; i < INHERITED_IV_COUNT; i++)
|
||||
// Select which IVs that will be inherited.
|
||||
for (i = 0; i < howManyIVs; i++)
|
||||
{
|
||||
// Randomly pick an IV from the available list and stop from being chosen again.
|
||||
// BUG: Instead of removing the IV that was just picked, this
|
||||
@ -573,13 +579,13 @@ static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare)
|
||||
}
|
||||
|
||||
// Determine which parent each of the selected IVs should inherit from.
|
||||
for (i = 0; i < INHERITED_IV_COUNT; i++)
|
||||
for (i = 0; i < howManyIVs; i++)
|
||||
{
|
||||
whichParents[i] = Random() % DAYCARE_MON_COUNT;
|
||||
}
|
||||
|
||||
// Set each of inherited IVs on the egg mon.
|
||||
for (i = 0; i < INHERITED_IV_COUNT; i++)
|
||||
for (i = 0; i < howManyIVs; i++)
|
||||
{
|
||||
switch (selectedIvs[i])
|
||||
{
|
||||
|
@ -867,17 +867,17 @@ static const u8 *const sEasyChatKeyboardAlphabet[NUM_ALPHABET_ROWS] =
|
||||
static const struct SpriteSheet sSpriteSheets[] = {
|
||||
{
|
||||
.data = sTriangleCursor_Gfx,
|
||||
.size = 0x20,
|
||||
.size = sizeof(sTriangleCursor_Gfx),
|
||||
.tag = GFXTAG_TRIANGLE_CURSOR
|
||||
},
|
||||
{
|
||||
.data = sScrollIndicator_Gfx,
|
||||
.size = 0x100,
|
||||
.size = sizeof(sScrollIndicator_Gfx),
|
||||
.tag = GFXTAG_SCROLL_INDICATOR
|
||||
},
|
||||
{
|
||||
.data = sStartSelectButtons_Gfx,
|
||||
.size = 0x100,
|
||||
.size = sizeof(sStartSelectButtons_Gfx),
|
||||
.tag = GFXTAG_START_SELECT_BUTTONS
|
||||
},
|
||||
{0}
|
||||
|
@ -145,14 +145,14 @@ static const union AnimCmd *const sSpriteAnimTable_Egg[] =
|
||||
static const struct SpriteSheet sEggHatch_Sheet =
|
||||
{
|
||||
.data = sEggHatchTiles,
|
||||
.size = 0x800,
|
||||
.size = sizeof(sEggHatchTiles),
|
||||
.tag = GFXTAG_EGG,
|
||||
};
|
||||
|
||||
static const struct SpriteSheet sEggShards_Sheet =
|
||||
{
|
||||
.data = sEggShardTiles,
|
||||
.size = 0x80,
|
||||
.size = sizeof(sEggShardTiles),
|
||||
.tag = GFXTAG_EGG_SHARD,
|
||||
};
|
||||
|
||||
@ -378,9 +378,6 @@ static void AddHatchedMonToParty(u8 id)
|
||||
|
||||
GetMonNickname2(mon, gStringVar1);
|
||||
|
||||
ball = ITEM_POKE_BALL;
|
||||
SetMonData(mon, MON_DATA_POKEBALL, &ball);
|
||||
|
||||
// A met level of 0 is interpreted on the summary screen as "hatched at"
|
||||
metLevel = 0;
|
||||
SetMonData(mon, MON_DATA_MET_LEVEL, &metLevel);
|
||||
|
@ -1240,9 +1240,9 @@ void GetSecretBaseNearbyMapName(void)
|
||||
GetMapName(gStringVar1, VarGet(VAR_SECRET_BASE_MAP), 0);
|
||||
}
|
||||
|
||||
u16 GetBestBattleTowerStreak(void)
|
||||
u16 GetBattleTowerSinglesStreak(void)
|
||||
{
|
||||
return GetGameStat(GAME_STAT_BATTLE_TOWER_BEST_STREAK);
|
||||
return GetGameStat(GAME_STAT_BATTLE_TOWER_SINGLES_STREAK);
|
||||
}
|
||||
|
||||
void BufferEReaderTrainerName(void)
|
||||
|
@ -2068,7 +2068,7 @@ static void IncrementWinStreak(void)
|
||||
gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode]++;
|
||||
if (battleMode == FRONTIER_MODE_SINGLES)
|
||||
{
|
||||
SetGameStat(GAME_STAT_BATTLE_TOWER_BEST_STREAK, gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode]);
|
||||
SetGameStat(GAME_STAT_BATTLE_TOWER_SINGLES_STREAK, gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode]);
|
||||
gSaveBlock2Ptr->frontier.towerSinglesStreak = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode];
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ static const union AffineAnimCmd *const sRotatingBallAnimCmds_FullRotation[] =
|
||||
|
||||
static const struct SpriteSheet sRotatingBallTable =
|
||||
{
|
||||
sRotatingBall_Gfx, 0x80, TAG_ROTATING_BALL_GFX
|
||||
sRotatingBall_Gfx, sizeof(sRotatingBall_Gfx), TAG_ROTATING_BALL_GFX
|
||||
};
|
||||
|
||||
static const struct SpritePalette sRotatingBallPaletteTable =
|
||||
|
@ -134,7 +134,7 @@ static const u8 sWireless_ASCIItoRSETable[256] = {
|
||||
0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94
|
||||
};
|
||||
|
||||
static const u8 sWireless_RSEtoASCIITable[256] = {
|
||||
const u8 gWireless_RSEtoASCIITable[256] = {
|
||||
[CHAR_SPACE] = ' ',
|
||||
0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
|
||||
0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
|
||||
@ -612,7 +612,7 @@ static void PkmnStrToASCII(u8 *asciiStr, const u8 *pkmnStr)
|
||||
s32 i;
|
||||
|
||||
for (i = 0; pkmnStr[i] != EOS; i++)
|
||||
asciiStr[i] = sWireless_RSEtoASCIITable[pkmnStr[i]];
|
||||
asciiStr[i] = gWireless_RSEtoASCIITable[pkmnStr[i]];
|
||||
asciiStr[i] = 0;
|
||||
}
|
||||
|
||||
|
12
src/main.c
12
src/main.c
@ -31,6 +31,9 @@ static void VCountIntr(void);
|
||||
static void SerialIntr(void);
|
||||
static void IntrDummy(void);
|
||||
|
||||
// Defined in the linker script so that the test build can override it.
|
||||
extern void gInitialMainCB2(void);
|
||||
|
||||
const u8 gGameVersion = GAME_VERSION;
|
||||
|
||||
const u8 gGameLanguage = GAME_LANGUAGE; // English
|
||||
@ -68,6 +71,7 @@ IntrFunc gIntrTable[INTR_COUNT];
|
||||
u8 gLinkVSyncDisabled;
|
||||
u32 IntrMain_Buffer[0x200];
|
||||
s8 gPcmDmaCounter;
|
||||
void *gAgbMainLoop_sp;
|
||||
|
||||
static EWRAM_DATA u16 sTrainerId = 0;
|
||||
|
||||
@ -126,6 +130,12 @@ void AgbMain()
|
||||
AGBPrintfInit();
|
||||
#endif
|
||||
#endif
|
||||
gAgbMainLoop_sp = __builtin_frame_address(0);
|
||||
AgbMainLoop();
|
||||
}
|
||||
|
||||
void AgbMainLoop(void)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
ReadKeys();
|
||||
@ -178,7 +188,7 @@ static void InitMainCallbacks(void)
|
||||
gTrainerHillVBlankCounter = NULL;
|
||||
gMain.vblankCounter2 = 0;
|
||||
gMain.callback1 = NULL;
|
||||
SetMainCallback2(CB2_InitCopyrightScreenAfterBootup);
|
||||
SetMainCallback2(gInitialMainCB2);
|
||||
gSaveBlock2Ptr = &gSaveblock2.block;
|
||||
gPokemonStoragePtr = &gPokemonStorage.block;
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ static const s16 sCeilingCrumblePositions[][3] =
|
||||
|
||||
static const struct SpriteSheet sCeilingCrumbleSpriteSheets[] =
|
||||
{
|
||||
{sMirageTowerCrumbles_Gfx, 0x80, TAG_CEILING_CRUMBLE},
|
||||
{sMirageTowerCrumbles_Gfx, sizeof(sMirageTowerCrumbles_Gfx), TAG_CEILING_CRUMBLE},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -145,12 +145,19 @@
|
||||
|
||||
// The different versions of hearts are selected using animation
|
||||
// commands.
|
||||
#define APPEAL_HEART_EMPTY 0
|
||||
#define APPEAL_HEART_FULL 1
|
||||
#define JAM_HEART_EMPTY 2
|
||||
#define JAM_HEART_FULL 3
|
||||
enum {
|
||||
APPEAL_HEART_EMPTY,
|
||||
APPEAL_HEART_FULL,
|
||||
JAM_HEART_EMPTY,
|
||||
JAM_HEART_FULL,
|
||||
};
|
||||
|
||||
#define MAX_RELEARNER_MOVES (MAX_LEVEL_UP_MOVES > 25 ? MAX_LEVEL_UP_MOVES : 25)
|
||||
#define TAG_MODE_ARROWS 5325
|
||||
#define TAG_LIST_ARROWS 5425
|
||||
#define GFXTAG_UI 5525
|
||||
#define PALTAG_UI 5526
|
||||
|
||||
#define MAX_RELEARNER_MOVES max(MAX_LEVEL_UP_MOVES, 25)
|
||||
|
||||
static EWRAM_DATA struct
|
||||
{
|
||||
@ -174,11 +181,11 @@ static EWRAM_DATA struct {
|
||||
bool8 showContestInfo;
|
||||
} sMoveRelearnerMenuSate = {0};
|
||||
|
||||
static const u16 sMoveRelearnerPaletteData[] = INCBIN_U16("graphics/interface/ui_learn_move.gbapal");
|
||||
static const u16 sUI_Pal[] = INCBIN_U16("graphics/interface/ui_learn_move.gbapal");
|
||||
|
||||
// The arrow sprites in this spritesheet aren't used. The scroll-arrow system provides its own
|
||||
// arrow sprites.
|
||||
static const u8 sMoveRelearnerSpriteSheetData[] = INCBIN_U8("graphics/interface/ui_learn_move.4bpp");
|
||||
static const u8 sUI_Tiles[] = INCBIN_U8("graphics/interface/ui_learn_move.4bpp");
|
||||
|
||||
static const struct OamData sHeartSpriteOamData =
|
||||
{
|
||||
@ -233,15 +240,15 @@ static const struct OamData sUnusedOam2 =
|
||||
|
||||
static const struct SpriteSheet sMoveRelearnerSpriteSheet =
|
||||
{
|
||||
.data = sMoveRelearnerSpriteSheetData,
|
||||
.size = 0x180,
|
||||
.tag = 5525
|
||||
.data = sUI_Tiles,
|
||||
.size = sizeof(sUI_Tiles),
|
||||
.tag = GFXTAG_UI
|
||||
};
|
||||
|
||||
static const struct SpritePalette sMoveRelearnerPalette =
|
||||
{
|
||||
.data = sMoveRelearnerPaletteData,
|
||||
.tag = 5526
|
||||
.data = sUI_Pal,
|
||||
.tag = PALTAG_UI
|
||||
};
|
||||
|
||||
static const struct ScrollArrowsTemplate sDisplayModeArrowsTemplate =
|
||||
@ -254,8 +261,8 @@ static const struct ScrollArrowsTemplate sDisplayModeArrowsTemplate =
|
||||
.secondY = 16,
|
||||
.fullyUpThreshold = -1,
|
||||
.fullyDownThreshold = -1,
|
||||
.tileTag = 5325,
|
||||
.palTag = 5325,
|
||||
.tileTag = TAG_MODE_ARROWS,
|
||||
.palTag = TAG_MODE_ARROWS,
|
||||
.palNum = 0,
|
||||
};
|
||||
|
||||
@ -269,8 +276,8 @@ static const struct ScrollArrowsTemplate sMoveListScrollArrowsTemplate =
|
||||
.secondY = 104,
|
||||
.fullyUpThreshold = 0,
|
||||
.fullyDownThreshold = 0,
|
||||
.tileTag = 5425,
|
||||
.palTag = 5425,
|
||||
.tileTag = TAG_LIST_ARROWS,
|
||||
.palTag = TAG_LIST_ARROWS,
|
||||
.palNum = 0,
|
||||
};
|
||||
|
||||
@ -308,8 +315,8 @@ static const union AnimCmd *const sHeartSpriteAnimationCommands[] =
|
||||
|
||||
static const struct SpriteTemplate sConstestMoveHeartSprite =
|
||||
{
|
||||
.tileTag = 5525,
|
||||
.paletteTag = 5526,
|
||||
.tileTag = GFXTAG_UI,
|
||||
.paletteTag = PALTAG_UI,
|
||||
.oam = &sHeartSpriteOamData,
|
||||
.anims = sHeartSpriteAnimationCommands,
|
||||
.images = NULL,
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#define WAVEFORM_WINDOW_HEIGHT 56
|
||||
|
||||
#define TAG_NEEDLE 0x2000
|
||||
|
||||
struct PokedexCryMeterNeedle {
|
||||
s8 rotation;
|
||||
s8 targetRotation;
|
||||
@ -202,8 +204,8 @@ static const struct OamData sOamData_CryMeterNeedle =
|
||||
|
||||
static const struct SpriteTemplate sCryMeterNeedleSpriteTemplate =
|
||||
{
|
||||
.tileTag = 0x2000,
|
||||
.paletteTag = 0x2000,
|
||||
.tileTag = TAG_NEEDLE,
|
||||
.paletteTag = TAG_NEEDLE,
|
||||
.oam = &sOamData_CryMeterNeedle,
|
||||
.anims = sSpriteAnimTable_CryMeterNeedle,
|
||||
.images = NULL,
|
||||
@ -213,13 +215,13 @@ static const struct SpriteTemplate sCryMeterNeedleSpriteTemplate =
|
||||
|
||||
static const struct SpriteSheet sCryMeterNeedleSpriteSheets[] =
|
||||
{
|
||||
{sCryMeterNeedle_Gfx, 0x800, 0x2000},
|
||||
{sCryMeterNeedle_Gfx, sizeof(sCryMeterNeedle_Gfx), TAG_NEEDLE},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct SpritePalette sCryMeterNeedleSpritePalettes[] =
|
||||
{
|
||||
{sCryMeterNeedle_Pal, 0x2000},
|
||||
{sCryMeterNeedle_Pal, TAG_NEEDLE},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -8737,3 +8737,32 @@ u32 GetMonFriendshipScore(struct Pokemon *pokemon)
|
||||
|
||||
return FRIENDSHIP_NONE;
|
||||
}
|
||||
|
||||
void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality)
|
||||
{
|
||||
struct PokemonSubstruct0 *old0, *new0;
|
||||
struct PokemonSubstruct1 *old1, *new1;
|
||||
struct PokemonSubstruct2 *old2, *new2;
|
||||
struct PokemonSubstruct3 *old3, *new3;
|
||||
struct BoxPokemon old;
|
||||
|
||||
old = *boxMon;
|
||||
old0 = &(GetSubstruct(&old, old.personality, 0)->type0);
|
||||
old1 = &(GetSubstruct(&old, old.personality, 1)->type1);
|
||||
old2 = &(GetSubstruct(&old, old.personality, 2)->type2);
|
||||
old3 = &(GetSubstruct(&old, old.personality, 3)->type3);
|
||||
|
||||
new0 = &(GetSubstruct(boxMon, personality, 0)->type0);
|
||||
new1 = &(GetSubstruct(boxMon, personality, 1)->type1);
|
||||
new2 = &(GetSubstruct(boxMon, personality, 2)->type2);
|
||||
new3 = &(GetSubstruct(boxMon, personality, 3)->type3);
|
||||
|
||||
DecryptBoxMon(&old);
|
||||
boxMon->personality = personality;
|
||||
*new0 = *old0;
|
||||
*new1 = *old1;
|
||||
*new2 = *old2;
|
||||
*new3 = *old3;
|
||||
boxMon->checksum = CalculateBoxMonChecksum(boxMon);
|
||||
EncryptBoxMon(boxMon);
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ enum {
|
||||
|
||||
// The maximum number of Pokémon icons that can appear on-screen.
|
||||
// By default the limit is 40 (though in practice only 37 can be).
|
||||
#define MAX_MON_ICONS (IN_BOX_COUNT + PARTY_SIZE + 1 >= 40 ? IN_BOX_COUNT + PARTY_SIZE + 1 : 40)
|
||||
#define MAX_MON_ICONS max(IN_BOX_COUNT + PARTY_SIZE + 1, 40)
|
||||
|
||||
// The maximum number of item icons that can appear on-screen while
|
||||
// moving held items. 1 in the cursor, and 2 more while switching
|
||||
@ -1246,7 +1246,7 @@ static const union AffineAnimCmd *const sAffineAnims_ReleaseMon[] =
|
||||
|
||||
static const u16 sUnusedColor = RGB(26, 29, 8);
|
||||
|
||||
static const struct SpriteSheet sSpriteSheet_Arrow = {sArrow_Gfx, 0x80, GFXTAG_ARROW};
|
||||
static const struct SpriteSheet sSpriteSheet_Arrow = {sArrow_Gfx, sizeof(sArrow_Gfx), GFXTAG_ARROW};
|
||||
|
||||
static const struct OamData sOamData_BoxTitle =
|
||||
{
|
||||
|
@ -14,14 +14,13 @@
|
||||
#include "malloc.h"
|
||||
#include "util.h"
|
||||
#include "task.h"
|
||||
#include "test_runner.h"
|
||||
#include "text.h"
|
||||
#include "battle_setup.h"
|
||||
#include "frontier_util.h"
|
||||
#include "constants/trainers.h"
|
||||
#include "constants/rgb.h"
|
||||
|
||||
#define BATTLER_RECORD_SIZE 664
|
||||
|
||||
struct PlayerInfo
|
||||
{
|
||||
u32 trainerId;
|
||||
@ -31,37 +30,6 @@ struct PlayerInfo
|
||||
u16 language;
|
||||
};
|
||||
|
||||
struct RecordedBattleSave
|
||||
{
|
||||
struct Pokemon playerParty[PARTY_SIZE];
|
||||
struct Pokemon opponentParty[PARTY_SIZE];
|
||||
u8 playersName[MAX_BATTLERS_COUNT][PLAYER_NAME_LENGTH + 1];
|
||||
u8 playersGender[MAX_BATTLERS_COUNT];
|
||||
u32 playersTrainerId[MAX_BATTLERS_COUNT];
|
||||
u8 playersLanguage[MAX_BATTLERS_COUNT];
|
||||
u32 rngSeed;
|
||||
u32 battleFlags;
|
||||
u8 playersBattlers[MAX_BATTLERS_COUNT];
|
||||
u16 opponentA;
|
||||
u16 opponentB;
|
||||
u16 partnerId;
|
||||
u16 multiplayerId;
|
||||
u8 lvlMode;
|
||||
u8 frontierFacility;
|
||||
u8 frontierBrainSymbol;
|
||||
u8 battleScene:1;
|
||||
u8 textSpeed:3;
|
||||
u32 AI_scripts;
|
||||
u8 recordMixFriendName[PLAYER_NAME_LENGTH + 1];
|
||||
u8 recordMixFriendClass;
|
||||
u8 apprenticeId;
|
||||
u16 easyChatSpeech[EASY_CHAT_BATTLE_WORDS_COUNT];
|
||||
u8 recordMixFriendLanguage;
|
||||
u8 apprenticeLanguage;
|
||||
u8 battleRecord[MAX_BATTLERS_COUNT][BATTLER_RECORD_SIZE];
|
||||
u32 checksum;
|
||||
};
|
||||
|
||||
// Save data using TryWriteSpecialSaveSector is allowed to exceed SECTOR_DATA_SIZE (up to the counter field)
|
||||
STATIC_ASSERT(sizeof(struct RecordedBattleSave) <= SECTOR_COUNTER_OFFSET, RecordedBattleSaveFreeSpace);
|
||||
|
||||
@ -205,8 +173,11 @@ void RecordedBattle_ClearBattlerAction(u8 battlerId, u8 bytesToClear)
|
||||
}
|
||||
}
|
||||
|
||||
u8 RecordedBattle_GetBattlerAction(u8 battlerId)
|
||||
u8 RecordedBattle_GetBattlerAction(u32 actionType, u8 battlerId)
|
||||
{
|
||||
if (gTestRunnerEnabled)
|
||||
BattleTest_CheckBattleRecordActionType(battlerId, sBattlerRecordSizes[battlerId], actionType);
|
||||
|
||||
// Trying to read past array or invalid action byte, battle is over.
|
||||
if (sBattlerRecordSizes[battlerId] >= BATTLER_RECORD_SIZE || sBattleRecords[battlerId][sBattlerRecordSizes[battlerId]] == 0xFF)
|
||||
{
|
||||
@ -522,7 +493,7 @@ static void Task_StartAfterCountdown(u8 taskId)
|
||||
}
|
||||
}
|
||||
|
||||
static void SetVariablesForRecordedBattle(struct RecordedBattleSave *src)
|
||||
void SetVariablesForRecordedBattle(struct RecordedBattleSave *src)
|
||||
{
|
||||
bool8 var;
|
||||
s32 i, j;
|
||||
@ -755,14 +726,14 @@ void RecordedBattle_CheckMovesetChanges(u8 mode)
|
||||
|
||||
// We know the current action is ACTION_MOVE_CHANGE, retrieve
|
||||
// it without saving it to move on to the next action.
|
||||
RecordedBattle_GetBattlerAction(battlerId);
|
||||
RecordedBattle_GetBattlerAction(RECORDED_BYTE, battlerId);
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
ppBonuses[j] = ((gBattleMons[battlerId].ppBonuses & (3 << (j << 1))) >> (j << 1));
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
moveSlots[j] = RecordedBattle_GetBattlerAction(battlerId);
|
||||
moveSlots[j] = RecordedBattle_GetBattlerAction(RECORDED_BYTE, battlerId);
|
||||
movePp.moves[j] = gBattleMons[battlerId].moves[moveSlots[j]];
|
||||
movePp.currentPp[j] = gBattleMons[battlerId].pp[moveSlots[j]];
|
||||
movePp.maxPp[j] = ppBonuses[moveSlots[j]];
|
||||
|
@ -264,14 +264,14 @@ static const struct OamData sOamData_RotatingGateRegular =
|
||||
|
||||
static const struct SpriteSheet sRotatingGatesGraphicsTable[] =
|
||||
{
|
||||
{sRotatingGateTiles_1, 0x200, ROTATING_GATE_TILE_TAG + GATE_SHAPE_L1},
|
||||
{sRotatingGateTiles_2, 0x800, ROTATING_GATE_TILE_TAG + GATE_SHAPE_L2},
|
||||
{sRotatingGateTiles_3, 0x800, ROTATING_GATE_TILE_TAG + GATE_SHAPE_L3},
|
||||
{sRotatingGateTiles_4, 0x800, ROTATING_GATE_TILE_TAG + GATE_SHAPE_L4},
|
||||
{sRotatingGateTiles_5, 0x200, ROTATING_GATE_TILE_TAG + GATE_SHAPE_T1},
|
||||
{sRotatingGateTiles_6, 0x800, ROTATING_GATE_TILE_TAG + GATE_SHAPE_T2},
|
||||
{sRotatingGateTiles_7, 0x800, ROTATING_GATE_TILE_TAG + GATE_SHAPE_T3},
|
||||
{sRotatingGateTiles_8, 0x800, ROTATING_GATE_TILE_TAG + GATE_SHAPE_T4},
|
||||
{sRotatingGateTiles_1, sizeof(sRotatingGateTiles_1), ROTATING_GATE_TILE_TAG + GATE_SHAPE_L1},
|
||||
{sRotatingGateTiles_2, sizeof(sRotatingGateTiles_2), ROTATING_GATE_TILE_TAG + GATE_SHAPE_L2},
|
||||
{sRotatingGateTiles_3, sizeof(sRotatingGateTiles_3), ROTATING_GATE_TILE_TAG + GATE_SHAPE_L3},
|
||||
{sRotatingGateTiles_4, sizeof(sRotatingGateTiles_4), ROTATING_GATE_TILE_TAG + GATE_SHAPE_L4},
|
||||
{sRotatingGateTiles_5, sizeof(sRotatingGateTiles_5), ROTATING_GATE_TILE_TAG + GATE_SHAPE_T1},
|
||||
{sRotatingGateTiles_6, sizeof(sRotatingGateTiles_6), ROTATING_GATE_TILE_TAG + GATE_SHAPE_T2},
|
||||
{sRotatingGateTiles_7, sizeof(sRotatingGateTiles_7), ROTATING_GATE_TILE_TAG + GATE_SHAPE_T3},
|
||||
{sRotatingGateTiles_8, sizeof(sRotatingGateTiles_8), ROTATING_GATE_TILE_TAG + GATE_SHAPE_T4},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
|
46
src/test_runner_stub.c
Normal file
46
src/test_runner_stub.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include "global.h"
|
||||
#include "test_runner.h"
|
||||
|
||||
__attribute__((weak))
|
||||
const bool8 gTestRunnerEnabled = FALSE;
|
||||
|
||||
// The Makefile patches gTestRunnerHeadless as part of make test.
|
||||
// This allows us to open the ROM in an mgba with a UI and see the
|
||||
// animations and messages play, which helps when debugging a test.
|
||||
const bool8 gTestRunnerHeadless = FALSE;
|
||||
const bool8 gTestRunnerSkipIsFail = FALSE;
|
||||
|
||||
__attribute__((weak))
|
||||
void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, u32 ability)
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId)
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP)
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void TestRunner_Battle_RecordMessage(const u8 *string)
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1)
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void TestRunner_Battle_AfterLastTurn(void)
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void BattleTest_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType)
|
||||
{
|
||||
}
|
20
test/ability_blaze.c
Normal file
20
test/ability_blaze.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Blaze boosts Fire-type moves in a pinch", s16 damage)
|
||||
{
|
||||
u16 hp;
|
||||
PARAMETRIZE { hp = 99; }
|
||||
PARAMETRIZE { hp = 33; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
|
||||
PLAYER(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); MaxHP(99); HP(hp); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_GT(results[1].damage, results[0].damage);
|
||||
}
|
||||
}
|
48
test/ability_cute_charm.c
Normal file
48
test/ability_cute_charm.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
// TODO: Currently PASSES_RANDOMLY is incapable of testing Cute Charm
|
||||
// because it only activates 33% of the time, but we only want to
|
||||
// measure the 50% of the time that the infatuation prevents our move.
|
||||
SINGLE_BATTLE_TEST("Cute Charm inflicts infatuation on contact")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_TACKLE; }
|
||||
PARAMETRIZE { move = MOVE_SWIFT; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT);
|
||||
ASSUME(!(gBattleMoves[MOVE_SWIFT].flags & FLAG_MAKES_CONTACT));
|
||||
PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); }
|
||||
OPPONENT(SPECIES_CLEFAIRY) { Gender(MON_FEMALE); Ability(ABILITY_CUTE_CHARM); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
if (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) {
|
||||
ABILITY_POPUP(opponent, ABILITY_CUTE_CHARM);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_INFATUATION, player);
|
||||
MESSAGE("Foe Clefairy's Cute Charm infatuated Wobbuffet!");
|
||||
MESSAGE("Wobbuffet is in love with Foe Clefairy!");
|
||||
} else {
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_CUTE_CHARM);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_INFATUATION, player);
|
||||
NOT MESSAGE("Foe Clefairy's Cute Charm infatuated Wobbuffet!");
|
||||
NOT MESSAGE("Wobbuffet is in love with Foe Clefairy!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Cute Charm cannot infatuate same gender")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); }
|
||||
OPPONENT(SPECIES_CLEFAIRY) { Gender(MON_MALE); Ability(ABILITY_CUTE_CHARM); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_CUTE_CHARM);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
}
|
||||
}
|
29
test/ability_flame_body.c
Normal file
29
test/ability_flame_body.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Flame Body inflicts burn on contact")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_TACKLE; }
|
||||
PARAMETRIZE { move = MOVE_SWIFT; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT);
|
||||
ASSUME(!(gBattleMoves[MOVE_SWIFT].flags & FLAG_MAKES_CONTACT));
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_MAGMAR) { Ability(ABILITY_FLAME_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
if (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) {
|
||||
ABILITY_POPUP(opponent, ABILITY_FLAME_BODY);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, player);
|
||||
MESSAGE("Foe Magmar's Flame Body burned Wobbuffet!");
|
||||
STATUS_ICON(player, burn: TRUE);
|
||||
} else {
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_FLAME_BODY);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, player);
|
||||
NOT MESSAGE("Foe Magmar's Flame Body burned Wobbuffet!");
|
||||
NOT STATUS_ICON(player, burn: TRUE);
|
||||
}
|
||||
}
|
||||
}
|
47
test/ability_immunity.c
Normal file
47
test/ability_immunity.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Immunity prevents Poison Sting poison")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_POISON_STING].effect == EFFECT_POISON_HIT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_POISON_STING); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_STING, player);
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Immunity prevents Toxic bad poison")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Toxic!");
|
||||
ABILITY_POPUP(opponent, ABILITY_IMMUNITY);
|
||||
MESSAGE("Foe Snorlax's Immunity prevents poisoning!");
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Immunity prevents Toxic Spikes poison")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC_SPIKES].effect == EFFECT_TOXIC_SPIKES);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
169
test/ability_pastel_veil.c
Normal file
169
test/ability_pastel_veil.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_POISON_STING].effect == EFFECT_POISON_HIT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_POISON_STING); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_STING, player);
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison on partner")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_POISON_STING].effect == EFFECT_POISON_HIT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_POISON_STING, target: opponentRight); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_STING, playerLeft);
|
||||
NOT STATUS_ICON(opponentRight, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pastel Veil immediately cures Mold Breaker poison")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC);
|
||||
PLAYER(SPECIES_DRILBUR) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
|
||||
STATUS_ICON(opponent, badPoison: TRUE);
|
||||
ABILITY_POPUP(opponent, ABILITY_PASTEL_VEIL);
|
||||
MESSAGE("Foe Ponyta's Pastel Veil cured its poison problem!");
|
||||
STATUS_ICON(opponent, none: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pastel Veil does not cure Mold Breaker poison on partner")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC);
|
||||
PLAYER(SPECIES_DRILBUR) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TOXIC, target: opponentRight); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, playerLeft, target: opponentRight);
|
||||
STATUS_ICON(opponentRight, badPoison: TRUE);
|
||||
NOT STATUS_ICON(opponentRight, none: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Toxic!");
|
||||
ABILITY_POPUP(opponent, ABILITY_PASTEL_VEIL);
|
||||
MESSAGE("Foe Ponyta is protected by a pastel veil!");
|
||||
NOT STATUS_ICON(opponent, badPoison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TOXIC, target: opponentRight); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Toxic!");
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_PASTEL_VEIL);
|
||||
MESSAGE("Foe Wynaut is protected by a pastel veil!");
|
||||
NOT STATUS_ICON(opponentRight, badPoison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pastel Veil prevents Toxic Spikes poison")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC_SPIKES].effect == EFFECT_TOXIC_SPIKES);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
MESSAGE("2 sent out Ponyta!");
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic Spikes poison on partner")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC_SPIKES].effect == EFFECT_TOXIC_SPIKES);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponentRight, 2); }
|
||||
} SCENE {
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
NOT STATUS_ICON(opponentRight, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pastel Veil cures partner's poison on initial switch in")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
} WHEN {
|
||||
TURN {}
|
||||
} SCENE {
|
||||
MESSAGE("2 sent out Wobbuffet and Ponyta!");
|
||||
ABILITY_POPUP(opponentRight, ABILITY_PASTEL_VEIL);
|
||||
MESSAGE("Foe Wobbuffet was cured of its poisoning!");
|
||||
STATUS_ICON(opponentLeft, none: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pastel Veil cures partner's poison on switch in")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_PONYTA_GALARIAN) { Ability(ABILITY_PASTEL_VEIL); }
|
||||
} WHEN {
|
||||
TURN { SWITCH(opponentRight, 2); }
|
||||
} SCENE {
|
||||
MESSAGE("2 sent out Ponyta!");
|
||||
ABILITY_POPUP(opponentRight, ABILITY_PASTEL_VEIL);
|
||||
MESSAGE("Foe Wobbuffet was cured of its poisoning!");
|
||||
STATUS_ICON(opponentLeft, none: TRUE);
|
||||
}
|
||||
}
|
30
test/ability_poison_point.c
Normal file
30
test/ability_poison_point.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Poison Point inflicts poison on contact")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_TACKLE; }
|
||||
PARAMETRIZE { move = MOVE_SWIFT; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT);
|
||||
ASSUME(!(gBattleMoves[MOVE_SWIFT].flags & FLAG_MAKES_CONTACT));
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_NIDORAN_M) { Ability(ABILITY_POISON_POINT); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
if (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) {
|
||||
ABILITY_POPUP(opponent, ABILITY_POISON_POINT);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player);
|
||||
MESSAGE("Wobbuffet was poisoned by Foe Nidoran♂'s Poison Point!");
|
||||
STATUS_ICON(player, poison: TRUE);
|
||||
} else {
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_POISON_POINT);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player);
|
||||
NOT MESSAGE("Wobbuffet was poisoned by Foe Nidoran♂'s Poison Point!");
|
||||
NOT STATUS_ICON(player, poison: TRUE);
|
||||
}
|
||||
}
|
||||
}
|
29
test/ability_static.c
Normal file
29
test/ability_static.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Static inflicts paralysis on contact")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_TACKLE; }
|
||||
PARAMETRIZE { move = MOVE_SWIFT; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT);
|
||||
ASSUME(!(gBattleMoves[MOVE_SWIFT].flags & FLAG_MAKES_CONTACT));
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PIKACHU) { Ability(ABILITY_STATIC); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
if (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) {
|
||||
ABILITY_POPUP(opponent, ABILITY_STATIC);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, player);
|
||||
MESSAGE("Foe Pikachu's Static paralyzed Wobbuffet! It may be unable to move!");
|
||||
STATUS_ICON(player, paralysis: TRUE);
|
||||
} else {
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_STATIC);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, player);
|
||||
NOT MESSAGE("Foe Pikachu's Static paralyzed Wobbuffet! It may be unable to move!");
|
||||
NOT STATUS_ICON(player, paralysis: TRUE);
|
||||
}
|
||||
}
|
||||
}
|
47
test/ability_sturdy.c
Normal file
47
test/ability_sturdy.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Sturdy prevents OHKO moves")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_FISSURE].effect == EFFECT_OHKO);
|
||||
PLAYER(SPECIES_GEODUDE) { Ability(ABILITY_STURDY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FISSURE); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Fissure!");
|
||||
ABILITY_POPUP(player, ABILITY_STURDY);
|
||||
MESSAGE("Geodude was protected by Sturdy!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->hp, player->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sturdy prevents OHKOs")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GEODUDE) { Ability(ABILITY_STURDY); MaxHP(100); HP(100); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SEISMIC_TOSS); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEISMIC_TOSS, opponent);
|
||||
HP_BAR(player, hp: 1);
|
||||
ABILITY_POPUP(player, ABILITY_STURDY);
|
||||
MESSAGE("Geodude endured the hit using Sturdy!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sturdy does not prevent non-OHKOs")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GEODUDE) { Ability(ABILITY_STURDY); MaxHP(100); HP(99); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SEISMIC_TOSS); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEISMIC_TOSS, opponent);
|
||||
HP_BAR(player, hp: 0);
|
||||
}
|
||||
}
|
54
test/hold_effect_leftovers.c
Normal file
54
test/hold_effect_leftovers.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
gItems[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS;
|
||||
};
|
||||
|
||||
SINGLE_BATTLE_TEST("Leftovers recovers 1/16th HP at end of turn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); Item(ITEM_LEFTOVERS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {}
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
MESSAGE("Wobbuffet's Leftovers restored its HP a little!");
|
||||
HP_BAR(player, damage: -maxHP / 16);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Leftovers does nothing if max HP")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LEFTOVERS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {}
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
MESSAGE("Wobbuffet's Leftovers restored its HP a little!");
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Leftovers does nothing if Heal Block applies")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); Item(ITEM_LEFTOVERS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_HEAL_BLOCK); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
MESSAGE("Wobbuffet's Leftovers restored its HP a little!");
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
}
|
68
test/mega_evolution.c
Normal file
68
test/mega_evolution.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
|
||||
MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->species, SPECIES_VENUSAUR_MEGA);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rayquaza can Mega Evolve knowing Dragon Ascent")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_RAYQUAZA) { Moves(MOVE_DRAGON_ASCENT, MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("1's fervent wish has reached Rayquaza!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
|
||||
MESSAGE("Rayquaza has Mega Evolved into Mega Rayquaza!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->species, SPECIES_RAYQUAZA_MEGA);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Mega Evolution affects turn order")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_MEGA_EVO_TURN_ORDER);
|
||||
PLAYER(SPECIES_DIANCIE) { Item(ITEM_DIANCITE); Speed(105); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Diancie used Celebrate!");
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
} THEN {
|
||||
ASSUME(player->speed == 225);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Abilities replaced by Mega Evolution do not affect turn order")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_MEGA_EVO_TURN_ORDER);
|
||||
ASSUME(gSpeciesInfo[SPECIES_SABLEYE_MEGA].abilities[0] != ABILITY_STALL
|
||||
&& gSpeciesInfo[SPECIES_SABLEYE_MEGA].abilities[1] != ABILITY_STALL);
|
||||
PLAYER(SPECIES_SABLEYE) { Item(ITEM_SABLENITE); Ability(ABILITY_STALL); Speed(105); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(44); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Sableye used Celebrate!");
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
} THEN {
|
||||
ASSUME(player->speed == 45);
|
||||
}
|
||||
}
|
158
test/move.c
Normal file
158
test/move.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Accuracy controls the proportion of misses")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_DYNAMIC_PUNCH; }
|
||||
PARAMETRIZE { move = MOVE_THUNDER; }
|
||||
PARAMETRIZE { move = MOVE_HYDRO_PUMP; }
|
||||
PARAMETRIZE { move = MOVE_RAZOR_LEAF; }
|
||||
PARAMETRIZE { move = MOVE_SCRATCH; }
|
||||
ASSUME(0 < gBattleMoves[move].accuracy && gBattleMoves[move].accuracy <= 100);
|
||||
PASSES_RANDOMLY(gBattleMoves[move].accuracy, 100);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Secondary Effect Chance controls the proportion of secondary effects")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_THUNDER_SHOCK; }
|
||||
PARAMETRIZE { move = MOVE_DISCHARGE; }
|
||||
PARAMETRIZE { move = MOVE_NUZZLE; }
|
||||
ASSUME(gBattleMoves[move].accuracy == 100);
|
||||
ASSUME(gBattleMoves[move].effect == EFFECT_PARALYZE_HIT);
|
||||
ASSUME(0 < gBattleMoves[move].secondaryEffectChance && gBattleMoves[move].secondaryEffectChance <= 100);
|
||||
PASSES_RANDOMLY(gBattleMoves[move].secondaryEffectChance, 100);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
STATUS_ICON(opponent, paralysis: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Turn order is determined by priority")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_QUICK_ATTACK].priority > gBattleMoves[MOVE_TACKLE].priority);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_QUICK_ATTACK); MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Turn order is determined by speed if priority ties")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_QUICK_ATTACK); MOVE(opponent, MOVE_QUICK_ATTACK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and speed tie")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 2);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_QUICK_ATTACK); MOVE(opponent, MOVE_QUICK_ATTACK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Critical hits occur at a 1/24 rate")
|
||||
{
|
||||
ASSUME(B_CRIT_CHANCE >= GEN_7);
|
||||
ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100);
|
||||
PASSES_RANDOMLY(100 / 24, 100);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SCRATCH); }
|
||||
} SCENE {
|
||||
MESSAGE("It's a critical hit!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Critical hits deal 50% more damage", s16 damage)
|
||||
{
|
||||
bool32 criticalHit;
|
||||
PARAMETRIZE { criticalHit = FALSE; }
|
||||
PARAMETRIZE { criticalHit = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(B_CRIT_MULTIPLIER >= GEN_6);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SCRATCH, criticalHit: criticalHit); }
|
||||
} SCENE {
|
||||
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("Critical hits do not ignore positive stat stages", s16 damage)
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_CELEBRATE; }
|
||||
PARAMETRIZE { move = MOVE_HOWL; }
|
||||
PARAMETRIZE { move = MOVE_TAIL_WHIP; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_SCRATCH].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
TURN { MOVE(player, MOVE_SCRATCH, criticalHit: TRUE); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (i > 0)
|
||||
EXPECT_LT(results[0].damage, results[i].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Critical hits ignore negative stat stages", s16 damage)
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_CELEBRATE; }
|
||||
PARAMETRIZE { move = MOVE_HARDEN; }
|
||||
PARAMETRIZE { move = MOVE_GROWL; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_SCRATCH].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, move); }
|
||||
TURN { MOVE(player, MOVE_SCRATCH, criticalHit: TRUE); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (i > 0)
|
||||
EXPECT_EQ(results[0].damage, results[i].damage);
|
||||
}
|
||||
}
|
41
test/move_effect_absorb.c
Normal file
41
test/move_effect_absorb.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_ABSORB].effect == EFFECT_ABSORB);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Absorb recovers 50% of the damage dealt")
|
||||
{
|
||||
s16 damage;
|
||||
s16 healed;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ABSORB); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
|
||||
HP_BAR(opponent, captureDamage: &damage);
|
||||
HP_BAR(player, captureDamage: &healed);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage, Q_4_12(-0.5), healed);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Absorb fails if Heal Block applies")
|
||||
{
|
||||
ASSUME(B_HEAL_BLOCKING >= GEN_6);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_HEAL_BLOCK); MOVE(player, MOVE_ABSORB); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet was prevented from healing!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
|
||||
NOT HP_BAR(opponent);
|
||||
NOT HP_BAR(player);
|
||||
}
|
||||
}
|
24
test/move_effect_accuracy_down.c
Normal file
24
test/move_effect_accuracy_down.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sand Attack lowers Accuracy")
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100);
|
||||
PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SAND_ATTACK); MOVE(opponent, MOVE_SCRATCH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SAND_ATTACK, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Wobbuffet's accuracy fell!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
|
||||
}
|
||||
}
|
54
test/move_effect_after_you.c
Normal file
54
test/move_effect_after_you.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_AFTER_YOU].effect == EFFECT_AFTER_YOU);
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("After You makes the target move after user")
|
||||
{
|
||||
if (B_RECALC_TURN_AFTER_ACTIONS >= GEN_8) KNOWN_FAILING; // #2615.
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
PLAYER(SPECIES_WYNAUT) { Speed(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
|
||||
OPPONENT(SPECIES_WYNAUT) { Speed(2); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_AFTER_YOU, target: playerRight);
|
||||
MOVE(playerRight, MOVE_CELEBRATE);
|
||||
MOVE(opponentLeft, MOVE_CELEBRATE);
|
||||
MOVE(opponentRight, MOVE_CELEBRATE);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AFTER_YOU, playerLeft);
|
||||
MESSAGE("Wynaut took the kind offer!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("After You does nothing if the target has already moved")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
PLAYER(SPECIES_WYNAUT) { Speed(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
|
||||
OPPONENT(SPECIES_WYNAUT) { Speed(2); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_CELEBRATE);
|
||||
MOVE(playerRight, MOVE_CELEBRATE);
|
||||
MOVE(opponentLeft, MOVE_CELEBRATE);
|
||||
MOVE(opponentRight, MOVE_AFTER_YOU, target: opponentLeft);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
|
||||
MESSAGE("Foe Wynaut used After You!");
|
||||
MESSAGE("But it failed!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
|
||||
}
|
||||
}
|
32
test/move_effect_attack_down.c
Normal file
32
test/move_effect_attack_down.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Growl lowers Attack", s16 damage)
|
||||
{
|
||||
bool32 lowerAttack;
|
||||
PARAMETRIZE { lowerAttack = FALSE; }
|
||||
PARAMETRIZE { lowerAttack = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (lowerAttack) TURN { MOVE(player, MOVE_GROWL); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (lowerAttack) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Wobbuffet's attack fell!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[0].damage);
|
||||
}
|
||||
}
|
32
test/move_effect_attack_up.c
Normal file
32
test/move_effect_attack_up.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_MEDITATE].effect == EFFECT_ATTACK_UP);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Meditate raises Attack", s16 damage)
|
||||
{
|
||||
bool32 raiseAttack;
|
||||
PARAMETRIZE { raiseAttack = FALSE; }
|
||||
PARAMETRIZE { raiseAttack = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (raiseAttack) TURN { MOVE(player, MOVE_MEDITATE); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (raiseAttack) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEDITATE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's attack rose!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
}
|
||||
}
|
34
test/move_effect_bide.c
Normal file
34
test/move_effect_bide.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_BIDE].effect == EFFECT_BIDE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns")
|
||||
{
|
||||
s16 damage1;
|
||||
s16 damage2;
|
||||
s16 bideDamage;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BIDE); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { SKIP_TURN(player); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage1);
|
||||
MESSAGE("Wobbuffet is storing energy!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage2);
|
||||
MESSAGE("Wobbuffet unleashed energy!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
|
||||
HP_BAR(opponent, captureDamage: &bideDamage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(bideDamage, damage1 + damage2);
|
||||
}
|
||||
}
|
38
test/move_effect_burn_hit.c
Normal file
38
test/move_effect_burn_hit.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_EMBER].effect == EFFECT_BURN_HIT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ember inflicts burn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
|
||||
STATUS_ICON(opponent, burn: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ember cannot burn a Fire-type")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gSpeciesInfo[SPECIES_CHARMANDER].types[0] == TYPE_FIRE);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_CHARMANDER);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player);
|
||||
HP_BAR(opponent);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
|
||||
NOT STATUS_ICON(opponent, burn: TRUE);
|
||||
}
|
||||
}
|
32
test/move_effect_defense_down.c
Normal file
32
test/move_effect_defense_down.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TAIL_WHIP].effect == EFFECT_DEFENSE_DOWN);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tail Whip lowers Defense", s16 damage)
|
||||
{
|
||||
bool32 lowerDefense;
|
||||
PARAMETRIZE { lowerDefense = FALSE; }
|
||||
PARAMETRIZE { lowerDefense = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (lowerDefense) TURN { MOVE(player, MOVE_TAIL_WHIP); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (lowerDefense) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAIL_WHIP, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Wobbuffet's defense fell!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
}
|
||||
}
|
32
test/move_effect_defense_up.c
Normal file
32
test/move_effect_defense_up.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_HARDEN].effect == EFFECT_DEFENSE_UP);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harden raises Defense", s16 damage)
|
||||
{
|
||||
bool32 raiseDefense;
|
||||
PARAMETRIZE { raiseDefense = FALSE; }
|
||||
PARAMETRIZE { raiseDefense = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (raiseDefense) TURN { MOVE(player, MOVE_HARDEN); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (raiseDefense) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's defense rose!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[0].damage);
|
||||
}
|
||||
}
|
54
test/move_effect_dream_eater.c
Normal file
54
test/move_effect_dream_eater.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_DREAM_EATER].effect == EFFECT_DREAM_EATER);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dream Eater recovers 50% of the damage dealt")
|
||||
{
|
||||
s16 damage;
|
||||
s16 healed;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_DREAM_EATER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DREAM_EATER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage);
|
||||
HP_BAR(player, captureDamage: &healed);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage, Q_4_12(-1.0/2.0), healed);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dream Eater fails on awake targets")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_DREAM_EATER); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Dream Eater!");
|
||||
MESSAGE("Foe Wobbuffet wasn't affected!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dream Eater fails if Heal Block applies")
|
||||
{
|
||||
ASSUME(B_HEAL_BLOCKING >= GEN_6);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_HEAL_BLOCK); MOVE(player, MOVE_DREAM_EATER); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet was prevented from healing!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DREAM_EATER, player);
|
||||
NOT HP_BAR(opponent);
|
||||
NOT HP_BAR(player);
|
||||
}
|
||||
}
|
54
test/move_effect_encore.c
Normal file
54
test/move_effect_encore.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_ENCORE].effect == EFFECT_ENCORE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 2 turns")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_ENCORE); }
|
||||
TURN { FORCED_MOVE(player); }
|
||||
TURN { FORCED_MOVE(player); }
|
||||
TURN { MOVE(player, MOVE_SPLASH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Encore has no effect if no previous move")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_ENCORE); MOVE(player, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Encore!");
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Encore overrides the chosen move if it occurs first")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(opponent, MOVE_ENCORE); MOVE(player, MOVE_SPLASH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
}
|
||||
}
|
24
test/move_effect_evasion_up.c
Normal file
24
test/move_effect_evasion_up.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_DOUBLE_TEAM].effect == EFFECT_EVASION_UP);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Double Team raises Evasion")
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100);
|
||||
PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_DOUBLE_TEAM); MOVE(opponent, MOVE_SCRATCH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's evasiveness rose!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
|
||||
}
|
||||
}
|
53
test/move_effect_explosion.c
Normal file
53
test/move_effect_explosion.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Explosion causes the user to faint")
|
||||
{
|
||||
u16 remainingHP;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EXPLOSION); }
|
||||
} SCENE {
|
||||
HP_BAR(player, hp: 0);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Explosion causes the user to faint even if it misses")
|
||||
{
|
||||
u16 remainingHP;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EXPLOSION, hit: FALSE); }
|
||||
} SCENE {
|
||||
HP_BAR(player, hp: 0);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_GASTLY);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EXPLOSION); }
|
||||
} SCENE {
|
||||
HP_BAR(player, hp: 0);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player);
|
||||
MESSAGE("It doesn't affect Foe Gastly…");
|
||||
NOT HP_BAR(opponent);
|
||||
}
|
||||
}
|
38
test/move_effect_freeze_hit.c
Normal file
38
test/move_effect_freeze_hit.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_POWDER_SNOW].effect == EFFECT_FREEZE_HIT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Powder Snow inflicts freeze")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_POWDER_SNOW); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWDER_SNOW, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
|
||||
STATUS_ICON(opponent, freeze: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Powder Snow cannot freeze an Ice-type")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gSpeciesInfo[SPECIES_SNORUNT].types[0] == TYPE_ICE);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_SNORUNT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_POWDER_SNOW); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWDER_SNOW, player);
|
||||
HP_BAR(opponent);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
|
||||
NOT STATUS_ICON(opponent, freeze: TRUE);
|
||||
}
|
||||
}
|
32
test/move_effect_haze.c
Normal file
32
test/move_effect_haze.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_HAZE].effect == EFFECT_HAZE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Haze resets stat changes", s16 damage)
|
||||
{
|
||||
bool32 haze;
|
||||
PARAMETRIZE { haze = FALSE; }
|
||||
PARAMETRIZE { haze = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_MEDITATE].effect == EFFECT_ATTACK_UP);
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (haze) TURN { MOVE(player, MOVE_MEDITATE); MOVE(opponent, MOVE_HAZE); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (haze) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HAZE, opponent);
|
||||
MESSAGE("All stat changes were eliminated!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage);
|
||||
}
|
||||
}
|
33
test/move_effect_hex.c
Normal file
33
test/move_effect_hex.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_HEX].effect == EFFECT_HEX);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Hex deals double damage to foes with a status", s16 damage)
|
||||
{
|
||||
u32 status1;
|
||||
PARAMETRIZE { status1 = STATUS1_NONE; }
|
||||
PARAMETRIZE { status1 = STATUS1_SLEEP; }
|
||||
PARAMETRIZE { status1 = STATUS1_POISON; }
|
||||
PARAMETRIZE { status1 = STATUS1_BURN; }
|
||||
PARAMETRIZE { status1 = STATUS1_FREEZE; }
|
||||
PARAMETRIZE { status1 = STATUS1_PARALYSIS; }
|
||||
PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEX); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEX, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (i > 0)
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[i].damage);
|
||||
if (i > 1)
|
||||
EXPECT_EQ(results[i-1].damage, results[i].damage);
|
||||
}
|
||||
}
|
96
test/move_effect_hit_escape.c
Normal file
96
test/move_effect_hit_escape.c
Normal file
@ -0,0 +1,96 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn switches the user out")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("Go! Wynaut!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if the battle ends")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_U_TURN); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if no replacements")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_U_TURN); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if replacements fainted")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_U_TURN); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if Wimp Out activates")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WIMPOD) { MaxHP(100); HP(51); Ability(ABILITY_WIMP_OUT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
HP_BAR(opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_WIMP_OUT);
|
||||
MESSAGE("2 sent out Wobbuffet!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn switches the user out if Wimp Out fails to activate")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WIMPOD) { MaxHP(100); HP(51); Ability(ABILITY_WIMP_OUT); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
HP_BAR(opponent);
|
||||
NOT ABILITY_POPUP(opponent);
|
||||
MESSAGE("Your foe's weak! Get 'em, Wynaut!");
|
||||
}
|
||||
}
|
39
test/move_effect_paralyze_hit.c
Normal file
39
test/move_effect_paralyze_hit.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_THUNDER_SHOCK].effect == EFFECT_PARALYZE_HIT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Thunder Shock inflicts paralysis")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THUNDER_SHOCK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
|
||||
STATUS_ICON(opponent, paralysis: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_PARALYZE_ELECTRIC >= GEN_6);
|
||||
ASSUME(gSpeciesInfo[SPECIES_PIKACHU].types[0] == TYPE_ELECTRIC);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PIKACHU);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THUNDER_SHOCK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, player);
|
||||
HP_BAR(opponent);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
|
||||
NOT STATUS_ICON(opponent, paralysis: TRUE);
|
||||
}
|
||||
}
|
39
test/move_effect_poison_hit.c
Normal file
39
test/move_effect_poison_hit.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_POISON_STING].effect == EFFECT_POISON_HIT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Poison Sting inflicts poison")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_POISON_STING); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_STING, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Poison Sting cannot poison Poison-type")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gSpeciesInfo[SPECIES_NIDORAN_M].types[0] == TYPE_POISON);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_NIDORAN_M);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_POISON_STING); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_STING, player);
|
||||
HP_BAR(opponent);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
91
test/move_effect_rampage.c
Normal file
91
test/move_effect_rampage.c
Normal file
@ -0,0 +1,91 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_THRASH].effect == EFFECT_RAMPAGE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Thrash lasts for 2 or 3 turns")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 2);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THRASH); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Thrash confuses the user after it finishes")
|
||||
{
|
||||
GIVEN {
|
||||
RNGSeed(0x00000000);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THRASH); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THRASH, player);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Thrash does not confuse the user if it is canceled on turn 1 of 3")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
|
||||
RNGSeed(0x00000000);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THRASH); }
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); SKIP_TURN(player); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
} SCENE {
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Thrash does not confuse the user if it is canceled on turn 2 of 3")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
|
||||
RNGSeed(0x00000000);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THRASH); }
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); SKIP_TURN(player); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
} SCENE {
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
|
||||
RNGSeed(0x00000000);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THRASH); }
|
||||
TURN { SKIP_TURN(player); }
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); SKIP_TURN(player); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
|
||||
}
|
||||
}
|
57
test/move_effect_recoil_if_miss.c
Normal file
57
test/move_effect_recoil_if_miss.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_JUMP_KICK].effect == EFFECT_RECOIL_IF_MISS);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on miss")
|
||||
{
|
||||
s16 recoil;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_JUMP_KICK, hit: FALSE); }
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
MESSAGE("Wobbuffet used Jump Kick!");
|
||||
MESSAGE("Wobbuffet's attack missed!");
|
||||
MESSAGE("Wobbuffet kept going and crashed!");
|
||||
HP_BAR(player, damage: maxHP / 2);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on protect")
|
||||
{
|
||||
s16 recoil;
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_JUMP_KICK].flags & FLAG_PROTECT_AFFECTED);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); }
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_JUMP_KICK, player);
|
||||
HP_BAR(player, damage: maxHP / 2);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target")
|
||||
{
|
||||
KNOWN_FAILING; // #2596.
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_HEALING_WISH); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponent);
|
||||
NOT HP_BAR(player, damage: maxHP / 2);
|
||||
}
|
||||
}
|
77
test/move_effect_reflect.c
Normal file
77
test/move_effect_reflect.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_REFLECT].effect == EFFECT_REFLECT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Reflect reduces physical damage", s16 damage)
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_CELEBRATE; }
|
||||
PARAMETRIZE { move = MOVE_REFLECT; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_LT(results[1].damage, results[0].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Reflect applies for 5 turns")
|
||||
{
|
||||
u16 damage[6];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REFLECT); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage[1]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage[2]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage[3]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage[4]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &damage[5]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(1.0), damage[1]);
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(1.0), damage[2]);
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(1.0), damage[3]);
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(1.0), damage[4]);
|
||||
EXPECT_LT(damage[0], damage[5]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Reflect fails if already active")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REFLECT); }
|
||||
TURN { MOVE(player, MOVE_REFLECT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT, player);
|
||||
MESSAGE("Wobbuffet used Reflect!");
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
21
test/move_effect_sleep.c
Normal file
21
test/move_effect_sleep.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_HYPNOSIS].effect == EFFECT_SLEEP);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Hypnosis inflicts sleep")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HYPNOSIS); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPNOSIS, player);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent);
|
||||
STATUS_ICON(opponent, sleep: TRUE);
|
||||
}
|
||||
}
|
32
test/move_effect_special_attack_down.c
Normal file
32
test/move_effect_special_attack_down.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Confide lowers Special Attack", s16 damage)
|
||||
{
|
||||
bool32 lowerSpecialAttack;
|
||||
PARAMETRIZE { lowerSpecialAttack = FALSE; }
|
||||
PARAMETRIZE { lowerSpecialAttack = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (lowerSpecialAttack) TURN { MOVE(player, MOVE_CONFIDE); }
|
||||
TURN { MOVE(opponent, MOVE_GUST); }
|
||||
} SCENE {
|
||||
if (lowerSpecialAttack) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFIDE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Wobbuffet's sp. attack fell!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUST, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[0].damage);
|
||||
}
|
||||
}
|
32
test/move_effect_special_attack_up_3.c
Normal file
32
test/move_effect_special_attack_up_3.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TAIL_GLOW].effect == EFFECT_SPECIAL_ATTACK_UP_3);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tail Glow drastically raises Special Attack", s16 damage)
|
||||
{
|
||||
bool32 raiseSpecialAttack;
|
||||
PARAMETRIZE { raiseSpecialAttack = FALSE; }
|
||||
PARAMETRIZE { raiseSpecialAttack = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (raiseSpecialAttack) TURN { MOVE(player, MOVE_TAIL_GLOW); }
|
||||
TURN { MOVE(player, MOVE_GUST); }
|
||||
} SCENE {
|
||||
if (raiseSpecialAttack) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAIL_GLOW, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's sp. attack drastically rose!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUST, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.5), results[1].damage);
|
||||
}
|
||||
}
|
135
test/move_effect_spikes.c
Normal file
135
test/move_effect_spikes.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_SPIKES].effect == EFFECT_SPIKES);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Spikes damage on switch in")
|
||||
{
|
||||
u32 layers;
|
||||
u32 divisor;
|
||||
PARAMETRIZE { layers = 1; divisor = 8; }
|
||||
PARAMETRIZE { layers = 2; divisor = 6; }
|
||||
PARAMETRIZE { layers = 3; divisor = 4; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
u32 count;
|
||||
for (count = 0; count < layers; ++count) {
|
||||
TURN { MOVE(player, MOVE_SPIKES); }
|
||||
}
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
u32 count;
|
||||
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
|
||||
for (count = 0; count < layers; ++count) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
|
||||
MESSAGE("Spikes were scattered all around the opponent's side!");
|
||||
}
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
HP_BAR(opponent, damage: maxHP / divisor);
|
||||
MESSAGE("Foe Wynaut is hurt by spikes!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Spikes fails after 3 layers")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SPIKES); }
|
||||
TURN { MOVE(player, MOVE_SPIKES); }
|
||||
TURN { MOVE(player, MOVE_SPIKES); }
|
||||
TURN { MOVE(player, MOVE_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
|
||||
MESSAGE("Spikes were scattered all around the opponent's side!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
|
||||
MESSAGE("Spikes were scattered all around the opponent's side!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
|
||||
MESSAGE("Spikes were scattered all around the opponent's side!");
|
||||
MESSAGE("Wobbuffet used Spikes!");
|
||||
MESSAGE("But it failed!");
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
HP_BAR(opponent, damage: maxHP / 4);
|
||||
MESSAGE("Foe Wynaut is hurt by spikes!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Spikes damage on subsequent switch ins")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
} SCENE {
|
||||
s32 maxHP0 = GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP);
|
||||
s32 maxHP1 = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
HP_BAR(opponent, damage: maxHP1 / 8);
|
||||
MESSAGE("Foe Wynaut is hurt by spikes!");
|
||||
MESSAGE("2 sent out Wobbuffet!");
|
||||
HP_BAR(opponent, damage: maxHP0 / 8);
|
||||
MESSAGE("Foe Wobbuffet is hurt by spikes!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Spikes do not damage airborne Pokemon")
|
||||
{
|
||||
u32 species = SPECIES_WOBBUFFET;
|
||||
u32 item = ITEM_NONE;
|
||||
u32 move1 = MOVE_CELEBRATE;
|
||||
u32 move2 = MOVE_CELEBRATE;
|
||||
bool32 airborne;
|
||||
|
||||
ASSUME(gSpeciesInfo[SPECIES_PIDGEY].types[1] == TYPE_FLYING);
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; airborne = TRUE; }
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; item = ITEM_IRON_BALL; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; move1 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; move1 = MOVE_INGRAIN; airborne = FALSE; }
|
||||
|
||||
ASSUME(gSpeciesInfo[SPECIES_UNOWN].abilities[0] == ABILITY_LEVITATE);
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; airborne = TRUE; }
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; item = ITEM_IRON_BALL; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; move1 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; move1 = MOVE_INGRAIN; airborne = FALSE; }
|
||||
|
||||
PARAMETRIZE { move1 = MOVE_MAGNET_RISE; airborne = TRUE; }
|
||||
PARAMETRIZE { move1 = MOVE_MAGNET_RISE; item = ITEM_IRON_BALL; airborne = FALSE; }
|
||||
PARAMETRIZE { move1 = MOVE_MAGNET_RISE; move2 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
// Magnet Rise fails under Gravity.
|
||||
// Magnet Rise fails under Ingrain and vice-versa.
|
||||
|
||||
PARAMETRIZE { item = ITEM_AIR_BALLOON; airborne = TRUE; }
|
||||
PARAMETRIZE { item = ITEM_AIR_BALLOON; move1 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
PARAMETRIZE { item = ITEM_AIR_BALLOON; move1 = MOVE_INGRAIN; airborne = FALSE; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(species) { Item(item); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SPIKES); MOVE(opponent, move1); }
|
||||
TURN { MOVE(opponent, move2); }
|
||||
TURN { MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
|
||||
if (airborne) {
|
||||
NOT HP_BAR(opponent, damage: maxHP / 8);
|
||||
} else {
|
||||
HP_BAR(opponent, damage: maxHP / 8);
|
||||
}
|
||||
}
|
||||
}
|
55
test/move_effect_tailwind.c
Normal file
55
test/move_effect_tailwind.c
Normal file
@ -0,0 +1,55 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TAILWIND].effect == EFFECT_TAILWIND);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tailwind applies for 4 turns")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_TAILWIND_TURNS >= GEN_5);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(15); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_TAILWIND); }
|
||||
TURN {}
|
||||
TURN {}
|
||||
TURN {}
|
||||
TURN {}
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
MESSAGE("Wobbuffet used Tailwind!");
|
||||
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Tailwind affects partner on first turn")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_RECALC_TURN_AFTER_ACTIONS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(20); }
|
||||
PLAYER(SPECIES_WYNAUT) { Speed(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(15); }
|
||||
OPPONENT(SPECIES_WYNAUT) { Speed(14); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TAILWIND); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Tailwind!");
|
||||
MESSAGE("Wynaut used Celebrate!");
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
MESSAGE("Foe Wynaut used Celebrate!");
|
||||
}
|
||||
}
|
53
test/move_effect_torment.c
Normal file
53
test/move_effect_torment.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TORMENT].effect == EFFECT_TORMENT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Torment prevents consecutive move uses")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH, MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TORMENT); MOVE(opponent, MOVE_SPLASH); }
|
||||
TURN { MOVE(opponent, MOVE_SPLASH, allowed: FALSE); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TORMENT, player);
|
||||
MESSAGE("Foe Wobbuffet was subjected to torment!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Torment forces Struggle if the only move is prevented")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TORMENT); MOVE(opponent, MOVE_SPLASH); }
|
||||
TURN { MOVE(opponent, MOVE_SPLASH, allowed: FALSE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Torment allows non-consecutive move uses")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TORMENT); MOVE(opponent, MOVE_SPLASH); }
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(opponent, MOVE_SPLASH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent);
|
||||
}
|
||||
}
|
48
test/move_effect_toxic.c
Normal file
48
test/move_effect_toxic.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic inflicts bad poison")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
STATUS_ICON(opponent, badPoison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type")
|
||||
{
|
||||
u32 species;
|
||||
bool32 hit;
|
||||
PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(B_TOXIC_NEVER_MISS >= GEN_6);
|
||||
ASSUME(gSpeciesInfo[SPECIES_NIDORAN_M].types[0] == TYPE_POISON);
|
||||
PLAYER(species);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC, hit: FALSE); }
|
||||
} SCENE {
|
||||
if (hit) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
STATUS_ICON(opponent, badPoison: TRUE);
|
||||
} else {
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
|
||||
NOT ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
NOT STATUS_ICON(opponent, badPoison: TRUE);
|
||||
}
|
||||
}
|
||||
}
|
210
test/move_effect_toxic_spikes.c
Normal file
210
test/move_effect_toxic_spikes.c
Normal file
@ -0,0 +1,210 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TOXIC_SPIKES].effect == EFFECT_TOXIC_SPIKES);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes inflicts poison on switch in")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player);
|
||||
MESSAGE("Poison Spikes were scattered all around the opposing team's feet!");
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes inflicts bad poison on switch in")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player);
|
||||
MESSAGE("Poison Spikes were scattered all around the opposing team's feet!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player);
|
||||
MESSAGE("Poison Spikes were scattered all around the opposing team's feet!");
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
STATUS_ICON(opponent, badPoison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes fails after 2 layers")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player);
|
||||
MESSAGE("Poison Spikes were scattered all around the opposing team's feet!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player);
|
||||
MESSAGE("Poison Spikes were scattered all around the opposing team's feet!");
|
||||
MESSAGE("Wobbuffet used Toxic Spikes!");
|
||||
MESSAGE("But it failed!");
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
STATUS_ICON(opponent, badPoison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes inflicts poison on subsequent switch ins")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
MESSAGE("2 sent out Wynaut!");
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes do not poison airborne Pokemon")
|
||||
{
|
||||
u32 species = SPECIES_WOBBUFFET;
|
||||
u32 item = ITEM_NONE;
|
||||
u32 move1 = MOVE_CELEBRATE;
|
||||
u32 move2 = MOVE_CELEBRATE;
|
||||
bool32 airborne;
|
||||
|
||||
ASSUME(gSpeciesInfo[SPECIES_PIDGEY].types[1] == TYPE_FLYING);
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; airborne = TRUE; }
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; item = ITEM_IRON_BALL; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; move1 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_PIDGEY; move1 = MOVE_INGRAIN; airborne = FALSE; }
|
||||
|
||||
ASSUME(gSpeciesInfo[SPECIES_UNOWN].abilities[0] == ABILITY_LEVITATE);
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; airborne = TRUE; }
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; item = ITEM_IRON_BALL; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; move1 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_UNOWN; move1 = MOVE_INGRAIN; airborne = FALSE; }
|
||||
|
||||
PARAMETRIZE { move1 = MOVE_MAGNET_RISE; airborne = TRUE; }
|
||||
PARAMETRIZE { move1 = MOVE_MAGNET_RISE; item = ITEM_IRON_BALL; airborne = FALSE; }
|
||||
PARAMETRIZE { move1 = MOVE_MAGNET_RISE; move2 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
// Magnet Rise fails under Gravity.
|
||||
// Magnet Rise fails under Ingrain and vice-versa.
|
||||
|
||||
PARAMETRIZE { item = ITEM_AIR_BALLOON; airborne = TRUE; }
|
||||
PARAMETRIZE { item = ITEM_AIR_BALLOON; move1 = MOVE_GRAVITY; airborne = FALSE; }
|
||||
PARAMETRIZE { item = ITEM_AIR_BALLOON; move1 = MOVE_INGRAIN; airborne = FALSE; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(species) { Item(item); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); MOVE(opponent, move1); }
|
||||
TURN { MOVE(opponent, move2); }
|
||||
TURN { MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
if (airborne) {
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
} else {
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes do not affect Steel-types")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gSpeciesInfo[SPECIES_STEELIX].types[0] == TYPE_STEEL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_STEELIX);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes are removed by grounded Poison-types")
|
||||
{
|
||||
u32 species;
|
||||
u32 item = ITEM_NONE;
|
||||
u32 move = MOVE_CELEBRATE;
|
||||
bool32 grounded;
|
||||
PARAMETRIZE { species = SPECIES_EKANS; grounded = TRUE; }
|
||||
PARAMETRIZE { species = SPECIES_ZUBAT; grounded = FALSE; }
|
||||
PARAMETRIZE { species = SPECIES_ZUBAT; item = ITEM_IRON_BALL; grounded = TRUE; }
|
||||
PARAMETRIZE { species = SPECIES_ZUBAT; move = MOVE_GRAVITY; grounded = TRUE; }
|
||||
PARAMETRIZE { species = SPECIES_ZUBAT; move = MOVE_INGRAIN; grounded = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gSpeciesInfo[SPECIES_EKANS].types[0] == TYPE_POISON);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ZUBAT].types[0] == TYPE_POISON);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ZUBAT].types[1] == TYPE_FLYING);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(species) { Item(item); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); MOVE(opponent, move); }
|
||||
TURN { MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
} SCENE {
|
||||
if (grounded) {
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
MESSAGE("The poison spikes disappeared from around the opposing team's feet!");
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
} else {
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, opponent);
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This would test for what I believe to be a bug in the mainline games.
|
||||
// A Pokémon that gets passed magnet rise should still remove the Toxic
|
||||
// Spikes even though it is airborne.
|
||||
// The test currently fails, because we don't incorporate this bug.
|
||||
SINGLE_BATTLE_TEST("Toxic Spikes are removed by Poison-types affected by Magnet Rise")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
ASSUME(gSpeciesInfo[SPECIES_EKANS].types[0] == TYPE_POISON);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EKANS);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_MAGNET_RISE); }
|
||||
TURN { MOVE(player, MOVE_TOXIC_SPIKES); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
} SCENE {
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
MESSAGE("The poison spikes disappeared from around the opposing team's feet!");
|
||||
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||
}
|
||||
}
|
194
test/status1.c
Normal file
194
test/status1.c
Normal file
@ -0,0 +1,194 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
|
||||
{
|
||||
u32 turns;
|
||||
PARAMETRIZE { turns = 1; }
|
||||
PARAMETRIZE { turns = 2; }
|
||||
PARAMETRIZE { turns = 3; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP_TURN(turns)); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
for (i = 0; i < turns; i++)
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
for (i = 0; i < turns - 1; i++)
|
||||
MESSAGE("Wobbuffet is fast asleep.");
|
||||
MESSAGE("Wobbuffet woke up!");
|
||||
STATUS_ICON(player, none: TRUE);
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Poison deals 1/8th damage per turn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
for (i = 0; i < 4; i++)
|
||||
TURN {}
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
for (i = 0; i < 4; i++)
|
||||
HP_BAR(player, damage: maxHP / 8);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Burn deals 1/16th damage per turn")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_BURN_DAMAGE >= GEN_LATEST);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
for (i = 0; i < 4; i++)
|
||||
TURN {}
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
for (i = 0; i < 4; i++)
|
||||
HP_BAR(player, damage: maxHP / 16);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Burn reduces attack by 50%", s16 damage)
|
||||
{
|
||||
bool32 burned;
|
||||
PARAMETRIZE { burned = FALSE; }
|
||||
PARAMETRIZE { burned = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { if (burned) Status1(STATUS1_BURN); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Freeze has a 20% chance of being thawed")
|
||||
{
|
||||
PASSES_RANDOMLY(20, 100);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
STATUS_ICON(player, none: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Freeze is thawed by opponent's Fire-type attacks")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet is frozen solid!");
|
||||
MESSAGE("Foe Wobbuffet used Ember!");
|
||||
MESSAGE("Wobbuffet was defrosted!");
|
||||
STATUS_ICON(player, none: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Freeze is thawed by user's Flame Wheel")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_FLAME_WHEEL].flags & FLAG_THAW_USER);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FLAME_WHEEL); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet was defrosted by Flame Wheel!");
|
||||
STATUS_ICON(player, none: TRUE);
|
||||
MESSAGE("Wobbuffet used Flame Wheel!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Paralysis reduces speed by 50%")
|
||||
{
|
||||
u16 playerSpeed;
|
||||
bool32 playerFirst;
|
||||
PARAMETRIZE { playerSpeed = 98; playerFirst = FALSE; }
|
||||
PARAMETRIZE { playerSpeed = 102; playerFirst = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(B_PARALYSIS_SPEED >= GEN_7);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); Speed(playerSpeed); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
if (playerFirst) {
|
||||
ONE_OF {
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Wobbuffet is paralyzed! It can't move!");
|
||||
}
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
} else {
|
||||
MESSAGE("Foe Wobbuffet used Celebrate!");
|
||||
ONE_OF {
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Wobbuffet is paralyzed! It can't move!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn")
|
||||
{
|
||||
PASSES_RANDOMLY(25, 100);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet is paralyzed! It can't move!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Bad poison deals 1/16th cumulative damage per turn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
for (i = 0; i < 4; i++)
|
||||
TURN {}
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
for (i = 0; i < 4; i++)
|
||||
HP_BAR(player, damage: maxHP / 16 * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Bad poison cumulative damage resets on switch")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); }
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {}
|
||||
TURN {}
|
||||
TURN { SWITCH(player, 1); }
|
||||
TURN { SWITCH(player, 0); }
|
||||
TURN {}
|
||||
TURN {}
|
||||
} SCENE {
|
||||
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
|
||||
for (i = 0; i < 2; i++)
|
||||
HP_BAR(player, damage: maxHP / 16 * (i + 1));
|
||||
for (i = 0; i < 2; i++)
|
||||
HP_BAR(player, damage: maxHP / 16 * (i + 1));
|
||||
}
|
||||
}
|
142
test/test.h
Normal file
142
test/test.h
Normal file
@ -0,0 +1,142 @@
|
||||
#ifndef GUARD_TEST_H
|
||||
#define GUARD_TEST_H
|
||||
|
||||
#include "test_runner.h"
|
||||
|
||||
#define MAX_PROCESSES 32 // See also tools/mgba-rom-test-hydra/main.c
|
||||
|
||||
enum TestResult
|
||||
{
|
||||
TEST_RESULT_FAIL,
|
||||
TEST_RESULT_PASS,
|
||||
TEST_RESULT_SKIP,
|
||||
TEST_RESULT_INVALID,
|
||||
TEST_RESULT_ERROR,
|
||||
TEST_RESULT_TIMEOUT,
|
||||
};
|
||||
|
||||
struct TestRunner
|
||||
{
|
||||
u32 (*estimateCost)(void *);
|
||||
void (*setUp)(void *);
|
||||
void (*run)(void *);
|
||||
void (*tearDown)(void *);
|
||||
bool32 (*checkProgress)(void *);
|
||||
bool32 (*handleExitWithResult)(void *, enum TestResult);
|
||||
};
|
||||
|
||||
struct Test
|
||||
{
|
||||
const char *name;
|
||||
const char *filename;
|
||||
const struct TestRunner *runner;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct TestRunnerState
|
||||
{
|
||||
u8 state;
|
||||
u8 exitCode;
|
||||
s32 tests;
|
||||
s32 passes;
|
||||
s32 skips;
|
||||
const char *skipFilename;
|
||||
const struct Test *test;
|
||||
u32 processCosts[MAX_PROCESSES];
|
||||
|
||||
u8 result;
|
||||
u8 expectedResult;
|
||||
u32 timeoutSeconds;
|
||||
};
|
||||
|
||||
extern const u8 gTestRunnerN;
|
||||
extern const u8 gTestRunnerI;
|
||||
extern const char gTestRunnerArgv[256];
|
||||
|
||||
extern const struct TestRunner gAssumptionsRunner;
|
||||
extern struct TestRunnerState gTestRunnerState;
|
||||
|
||||
void CB2_TestRunner(void);
|
||||
|
||||
void Test_ExpectedResult(enum TestResult);
|
||||
void Test_ExitWithResult(enum TestResult, const char *fmt, ...);
|
||||
|
||||
s32 MgbaPrintf_(const char *fmt, ...);
|
||||
|
||||
#define ASSUMPTIONS \
|
||||
static void Assumptions(void); \
|
||||
__attribute__((section(".tests"))) static const struct Test sAssumptions = \
|
||||
{ \
|
||||
.name = "ASSUMPTIONS: " __FILE__, \
|
||||
.filename = __FILE__, \
|
||||
.runner = &gAssumptionsRunner, \
|
||||
.data = Assumptions, \
|
||||
}; \
|
||||
static void Assumptions(void)
|
||||
|
||||
#define ASSUME(c) \
|
||||
do \
|
||||
{ \
|
||||
if (!(c)) \
|
||||
Test_ExitWithResult(TEST_RESULT_SKIP, "%s:%d: ASSUME failed", gTestRunnerState.test->filename, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT(c) \
|
||||
do \
|
||||
{ \
|
||||
if (!(c)) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT failed", gTestRunnerState.test->filename, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_EQ(a, b) \
|
||||
do \
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a != _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_NE(a, b) \
|
||||
do \
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a == _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_LT(a, b) \
|
||||
do \
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a >= _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_LE(a, b) \
|
||||
do \
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a > _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_GT(a, b) \
|
||||
do \
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a <= _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_GE(a, b) \
|
||||
do \
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a < _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
} while (0)
|
||||
|
||||
#define KNOWN_FAILING \
|
||||
Test_ExpectedResult(TEST_RESULT_FAIL)
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user