Merge branch 'upcoming' of https://github.com/rh-hideout/pokeemerald-expansion into castform_fix

This commit is contained in:
AgustinGDLV 2023-05-16 16:29:38 -07:00
commit 785b100693
82 changed files with 2489 additions and 703 deletions

View File

@ -1350,6 +1350,37 @@
callnative BS_TryRevertWeatherForm
.endm
.macro setsnow
callnative BS_SetSnow
.endm
.macro setzeffect
callnative BS_SetZEffect
.endm
@ Used by effects that may proc Symbiosis but do not call removeitem.
.macro trysymbiosis
callnative BS_TrySymbiosis
.endm
@ returns TRUE or FALSE to gBattleCommunication[0]
.macro canteleport battler:req
callnative BS_CanTeleport
.byte \battler
.endm
@ returns B_SIDE_x to gBattleCommunication[0]
.macro getbattlerside battler:req
callnative BS_GetBattlerSide
.byte \battler
.endm
.macro checkparentalbondcounter counter:req, ptr:req
callnative BS_CheckParentalBondCounter
.byte \counter
.4byte \ptr
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
@ -1835,10 +1866,6 @@
various \battler, VARIOUS_TRY_ACTIVATE_GRIM_NEIGH
.endm
.macro setzeffect
various BS_ATTACKER, VARIOUS_SET_Z_EFFECT
.endm
.macro consumeberry battler:req, fromBattler:req
various \battler, VARIOUS_CONSUME_BERRY
.byte \fromBattler
@ -2050,19 +2077,6 @@
various BS_ATTACKER, VARIOUS_SWAP_SIDE_STATUSES
.endm
.macro canteleport battler:req
various \battler, VARIOUS_CAN_TELEPORT
.endm
.macro getbattlerside battler:req
various \battler, VARIOUS_GET_BATTLER_SIDE
.endm
.macro checkparentalbondcounter counter:req, jumpInstr:req
various BS_ATTACKER, VARIOUS_CHECK_PARENTAL_BOND_COUNTER
.byte \counter
.4byte \jumpInstr
.endm
.macro swapstats stat:req
various BS_ATTACKER, VARIOUS_SWAP_STATS
.byte \stat
@ -2242,11 +2256,6 @@
various 0, VARIOUS_SKY_DROP_YAWN
.endm
@ Used by effects that may proc Symbiosis but do not call removeitem.
.macro trysymbiosis
various BS_ATTACKER, VARIOUS_TRY_SYMBIOSIS
.endm
@ Tries to increase or decrease a battler's stat's stat stage by a specified amount. If impossible, jumps to \script.
.macro modifybattlerstatstage battler:req, stat:req, mode:req, amount:req, script:req, animation:req, customString

View File

@ -947,6 +947,7 @@ gBattleAnims_General::
.4byte General_ShellTrapSetUp @ B_ANIM_SHELL_TRAP_SETUP
.4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE
.4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON
.4byte General_Snow @ B_ANIM_SNOW_CONTINUES
.align 2
gBattleAnims_Special::
@ -14385,6 +14386,18 @@ Move_SILK_TRAP::
clearmonbg ANIM_ATK_PARTNER
end
@ Also used by Snow weather. Currently identical with Move_HAIL
Move_SNOWSCAPE::
loadspritegfx ANIM_TAG_HAIL
loadspritegfx ANIM_TAG_ICE_CRYSTALS
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 6, RGB_BLACK
waitforvisualfinish
createvisualtask AnimTask_Hail, 5
loopsewithpan SE_M_HAIL, 0, 8, 10
waitforvisualfinish
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 6, 0, RGB_BLACK
end
Move_WICKED_BLOW::
Move_SURGING_STRIKES::
Move_THUNDER_CAGE::
@ -14448,7 +14461,6 @@ Move_ELECTRO_DRIFT::
Move_SHED_TAIL::
Move_CHILLY_RECEPTION::
Move_TIDY_UP::
Move_SNOWSCAPE::
Move_POUNCE::
Move_TRAILBLAZE::
Move_CHILLING_WATER::
@ -23924,6 +23936,7 @@ Move_WEATHER_BALL:
jumpreteq ANIM_WEATHER_RAIN, WeatherBallWater
jumpreteq ANIM_WEATHER_SANDSTORM, WeatherBallSandstorm
jumpreteq ANIM_WEATHER_HAIL, WeatherBallIce
jumpreteq ANIM_WEATHER_SNOW, WeatherBallIce
WeatherBallNormal:
loadspritegfx ANIM_TAG_IMPACT
createsprite gWeatherBallNormalDownSpriteTemplate, ANIM_TARGET, 2, -30, -100, 25, 1, 0, 0
@ -24679,6 +24692,9 @@ General_Sandstorm:
General_Hail:
goto Move_HAIL
General_Snow:
goto Move_SNOWSCAPE
General_LeechSeedDrain:
createvisualtask AnimTask_GetBattlersFromArg, 5
delay 0

View File

@ -429,14 +429,15 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE
.4byte BattleScript_EffectRevivalBlessing @ EFFECT_REVIVAL_BLESSING
.4byte BattleScript_EffectFrostbiteHit @ EFFECT_FROSTBITE_HIT
.4byte BattleScript_EffectSnow @ EFFECT_SNOWSCAPE
BattleScript_EffectRevivalBlessing::
attackcanceler
attackstring
ppreduce
tryrevivalblessing BattleScript_ButItFailed
attackanimation
waitanimation
tryrevivalblessing BattleScript_ButItFailed
printstring STRINGID_PKMNREVIVEDREADYTOFIGHT
waitmessage B_WAIT_TIME_LONG
jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_EffectRevivalBlessingSendOut
@ -614,7 +615,7 @@ BattleScript_ShellTrapSetUp::
playanimation BS_ATTACKER, B_ANIM_SHELL_TRAP_SETUP, NULL
printstring STRINGID_PREPARESHELLTRAP
waitmessage B_WAIT_TIME_LONG
end2
end3
BattleScript_EffectShellTrap::
attackcanceler
@ -688,7 +689,7 @@ BattleScript_BeakBlastSetUp::
playanimation BS_ATTACKER, B_ANIM_BEAK_BLAST_SETUP, NULL
printstring STRINGID_HEATUPBEAK
waitmessage B_WAIT_TIME_LONG
end2
end3
BattleScript_BeakBlastBurn::
setbyte cMULTISTRING_CHOOSER, 0
@ -6855,7 +6856,7 @@ BattleScript_RainContinuesOrEndsEnd::
end2
BattleScript_DamagingWeatherContinues::
printfromtable gSandStormHailContinuesStringIds
printfromtable gSandStormHailSnowContinuesStringIds
waitmessage B_WAIT_TIME_LONG
playanimation_var BS_ATTACKER, sB_ANIM_ARG1
setbyte gBattleCommunication, 0
@ -6888,8 +6889,8 @@ BattleScript_DamagingWeatherContinuesEnd::
call BattleScript_ActivateWeatherAbilities
end2
BattleScript_SandStormHailEnds::
printfromtable gSandStormHailEndStringIds
BattleScript_SandStormHailSnowEnds::
printfromtable gSandStormHailSnowEndStringIds
waitmessage B_WAIT_TIME_LONG
call BattleScript_ActivateWeatherAbilities
end2
@ -7901,7 +7902,7 @@ BattleScript_FocusPunchSetUp::
playanimation BS_ATTACKER, B_ANIM_FOCUS_PUNCH_SETUP
printstring STRINGID_PKMNTIGHTENINGFOCUS
waitmessage B_WAIT_TIME_LONG
end2
end3
BattleScript_MegaEvolution::
printstring STRINGID_EMPTYSTRING3
@ -7917,7 +7918,7 @@ BattleScript_MegaEvolutionAfterString:
printstring STRINGID_MEGAEVOEVOLVED
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
end2
end3
BattleScript_WishMegaEvolution::
printstring STRINGID_EMPTYSTRING3
@ -8879,7 +8880,7 @@ BattleScript_MimicryActivates_End3::
waitmessage B_WAIT_TIME_SHORT
end3
BattleScript_SnowWarningActivates::
BattleScript_SnowWarningActivatesHail::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_SNOWWARNINGHAIL
@ -8888,6 +8889,15 @@ BattleScript_SnowWarningActivates::
call BattleScript_ActivateWeatherAbilities
end3
BattleScript_SnowWarningActivatesSnow::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_SNOWWARNINGSNOW
waitstate
playanimation BS_BATTLER_0, B_ANIM_SNOW_CONTINUES
call BattleScript_ActivateWeatherAbilities
end3
BattleScript_ActivateTerrainEffects:
savetarget
setbyte gBattlerTarget, 0
@ -9377,8 +9387,8 @@ BattleScript_FriskActivates::
end3
BattleScript_ImposterActivates::
transformdataexecution
call BattleScript_AbilityPopUp
transformdataexecution
playmoveanimation BS_ATTACKER, MOVE_TRANSFORM
waitanimation
printstring STRINGID_IMPOSTERTRANSFORM
@ -10038,23 +10048,42 @@ BattleScript_PrintPlayerForfeitedLinkBattle::
end2
BattleScript_TotemFlaredToLife::
playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE
playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE, NULL
printstring STRINGID_AURAFLAREDTOLIFE
waitmessage B_WAIT_TIME_LONG
goto BattleScript_ApplyTotemVarBoost
call BattleScript_ApplyTotemVarBoost
end2
@ remove the mirror herb, do totem loop
BattleScript_MirrorHerbCopyStatChangeEnd2::
call BattleScript_MirrorHerbCopyStatChange
end2
BattleScript_MirrorHerbCopyStatChange::
playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, NULL
printstring STRINGID_MIRRORHERBCOPIED
waitmessage B_WAIT_TIME_LONG
removeitem BS_SCRIPTING
call BattleScript_TotemVar_Ret
copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe
return
BattleScript_TotemVar::
call BattleScript_TotemVar_Ret
end2
BattleScript_TotemVar_Ret::
gettotemboost BattleScript_ApplyTotemVarBoost
BattleScript_TotemVarEnd:
end2
return
BattleScript_ApplyTotemVarBoost:
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_TotemVarEnd
setgraphicalstatchangevalues
playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
BattleScript_TotemVarPrintStatMsg:
printfromtable gStatUpStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_TotemVar @loop until stats bitfield is empty
goto BattleScript_TotemVar_Ret @loop until stats bitfield is empty
BattleScript_AnnounceAirLockCloudNine::
call BattleScript_AbilityPopUp
@ -10473,3 +10502,13 @@ BattleScript_BerserkGeneRet_OwnTempoPrevents:
BattleScript_BerserkGeneRet_End:
removeitem BS_SCRIPTING
end3
BattleScript_EffectSnow::
attackcanceler
attackstring
ppreduce
jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_SUN_PRIMAL, BattleScript_ExtremelyHarshSunlightWasNotLessened
jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_RAIN_PRIMAL, BattleScript_NoReliefFromHeavyRain
jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_MysteriousAirCurrentBlowsOn
setsnow
goto BattleScript_MoveWeatherChange

View File

@ -75,7 +75,9 @@ BattleScript_ItemCureStatus::
BattleScript_ItemHealAndCureStatus::
call BattleScript_UseItemMessage
itemrestorehp
curestatus BS_ATTACKER
itemcurestatus
printstring STRINGID_ITEMRESTOREDSPECIESHEALTH
waitmessage B_WAIT_TIME_LONG
bichalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE
healthbarupdate BS_ATTACKER

View File

@ -1,37 +1,18 @@
#include "global.h"
#include "malloc.h"
static void *sHeapStart;
static u32 sHeapSize;
#define MALLOC_SYSTEM_ID 0xA3A3
struct MemBlock {
// Whether this block is currently allocated.
bool16 flag;
// Magic number used for error checking. Should equal MALLOC_SYSTEM_ID.
u16 magic;
// Size of the block (not including this header struct).
u32 size;
// Previous block pointer. Equals sHeapStart if this is the first block.
struct MemBlock *prev;
// Next block pointer. Equals sHeapStart if this is the last block.
struct MemBlock *next;
// Data in the memory block. (Arrays of length 0 are a GNU extension.)
u8 data[0];
};
void PutMemBlockHeader(void *block, struct MemBlock *prev, struct MemBlock *next, u32 size)
{
struct MemBlock *header = (struct MemBlock *)block;
header->flag = FALSE;
header->allocated = FALSE;
header->locationHi = 0;
header->magic = MALLOC_SYSTEM_ID;
header->size = size;
header->locationLo = 0;
header->prev = prev;
header->next = next;
}
@ -41,7 +22,7 @@ void PutFirstMemBlockHeader(void *block, u32 size)
PutMemBlockHeader(block, (struct MemBlock *)block, (struct MemBlock *)block, size - sizeof(struct MemBlock));
}
void *AllocInternal(void *heapStart, u32 size)
void *AllocInternal(void *heapStart, u32 size, const char *location)
{
struct MemBlock *pos = (struct MemBlock *)heapStart;
struct MemBlock *head = pos;
@ -55,14 +36,14 @@ void *AllocInternal(void *heapStart, u32 size)
for (;;) {
// Loop through the blocks looking for unused block that's big enough.
if (!pos->flag) {
if (!pos->allocated) {
foundBlockSize = pos->size;
if (foundBlockSize >= size) {
if (foundBlockSize - size < 2 * sizeof(struct MemBlock)) {
// The block isn't much bigger than the requested size,
// so just use it.
pos->flag = TRUE;
pos->allocated = TRUE;
} else {
// The block is significantly bigger than the requested
// size, so split the rest into a separate block.
@ -71,7 +52,7 @@ void *AllocInternal(void *heapStart, u32 size)
splitBlock = (struct MemBlock *)(pos->data + size);
pos->flag = TRUE;
pos->allocated = TRUE;
pos->size = size;
PutMemBlockHeader(splitBlock, pos, pos->next, foundBlockSize);
@ -82,6 +63,9 @@ void *AllocInternal(void *heapStart, u32 size)
splitBlock->next->prev = splitBlock;
}
pos->locationHi = ((uintptr_t)location) >> 14;
pos->locationLo = (uintptr_t)location;
return pos->data;
}
}
@ -98,12 +82,12 @@ void FreeInternal(void *heapStart, void *pointer)
if (pointer) {
struct MemBlock *head = (struct MemBlock *)heapStart;
struct MemBlock *block = (struct MemBlock *)((u8 *)pointer - sizeof(struct MemBlock));
block->flag = FALSE;
block->allocated = FALSE;
// If the freed block isn't the last one, merge with the next block
// if it's not in use.
if (block->next != head) {
if (!block->next->flag) {
if (!block->next->allocated) {
block->size += sizeof(struct MemBlock) + block->next->size;
block->next->magic = 0;
block->next = block->next->next;
@ -115,7 +99,7 @@ void FreeInternal(void *heapStart, void *pointer)
// If the freed block isn't the first one, merge with the previous block
// if it's not in use.
if (block != head) {
if (!block->prev->flag) {
if (!block->prev->allocated) {
block->prev->next = block->next;
if (block->next != head)
@ -128,9 +112,9 @@ void FreeInternal(void *heapStart, void *pointer)
}
}
void *AllocZeroedInternal(void *heapStart, u32 size)
void *AllocZeroedInternal(void *heapStart, u32 size, const char *location)
{
void *mem = AllocInternal(heapStart, size);
void *mem = AllocInternal(heapStart, size, location);
if (mem != NULL) {
if (size & 3)
@ -175,14 +159,14 @@ void InitHeap(void *heapStart, u32 heapSize)
PutFirstMemBlockHeader(heapStart, heapSize);
}
void *Alloc(u32 size)
void *Alloc_(u32 size, const char *location)
{
return AllocInternal(sHeapStart, size);
return AllocInternal(sHeapStart, size, location);
}
void *AllocZeroed(u32 size)
void *AllocZeroed_(u32 size, const char *location)
{
return AllocZeroedInternal(sHeapStart, size);
return AllocZeroedInternal(sHeapStart, size, location);
}
void Free(void *pointer)
@ -207,3 +191,16 @@ bool32 CheckHeap()
return TRUE;
}
const struct MemBlock *HeapHead(void)
{
return (const struct MemBlock *)sHeapStart;
}
const char *MemBlockLocation(const struct MemBlock *block)
{
if (!block->allocated)
return NULL;
return (const char *)(ROM_START | (block->locationHi << 14) | block->locationLo);
}

View File

@ -11,11 +11,48 @@
#define TRY_FREE_AND_SET_NULL(ptr) if (ptr != NULL) FREE_AND_SET_NULL(ptr)
#define MALLOC_SYSTEM_ID 0xA3A3
struct MemBlock
{
// Whether this block is currently allocated.
u16 allocated:1;
u16 unused_00:4;
// High 11 bits of location pointer.
u16 locationHi:11;
// Magic number used for error checking. Should equal MALLOC_SYSTEM_ID.
u16 magic;
// Size of the block (not including this header struct).
u32 size:18;
// Low 14 bits of location pointer.
u32 locationLo:14;
// Previous block pointer. Equals sHeapStart if this is the first block.
struct MemBlock *prev;
// Next block pointer. Equals sHeapStart if this is the last block.
struct MemBlock *next;
// Data in the memory block. (Arrays of length 0 are a GNU extension.)
u8 data[0];
};
extern u8 gHeap[];
void *Alloc(u32 size);
void *AllocZeroed(u32 size);
#define Alloc(size) Alloc_(size, __FILE__ ":" STR(__LINE__))
#define AllocZeroed(size) AllocZeroed_(size, __FILE__ ":" STR(__LINE__))
void *Alloc_(u32 size, const char *location);
void *AllocZeroed_(u32 size, const char *location);
void Free(void *pointer);
void InitHeap(void *pointer, u32 size);
const struct MemBlock *HeapHead(void);
const char *MemBlockLocation(const struct MemBlock *block);
#endif // GUARD_ALLOC_H

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 195 B

View File

@ -1,6 +1,6 @@
JASC-PAL
0100
256
16
152 208 160
152 128 80
224 176 72
@ -17,243 +17,3 @@ JASC-PAL
0 88 208
56 56 56
120 120 120
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -147,6 +147,7 @@ struct ProtectStruct
u16 quash:1;
u16 shellTrap:1;
u16 silkTrapped:1;
u16 eatMirrorHerb:1;
u32 physicalDmg;
u32 specialDmg;
u8 physicalBattlerId;
@ -530,7 +531,7 @@ struct BattleStruct
u8 wildVictorySong;
u8 dynamicMoveType;
u8 wrappedBy[MAX_BATTLERS_COUNT];
u8 focusPunchBattlerId;
u8 focusPunchBattlers; // as bits
u8 battlerPreventingSwitchout;
u8 moneyMultiplier:6;
u8 moneyMultiplierItem:1;
@ -649,6 +650,7 @@ struct BattleStruct
u8 attackerBeforeBounce:2;
u8 beatUpSlot:3;
bool8 hitSwitchTargetFailed:1;
bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects.
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
bool8 allowedToChangeFormInWeather[PARTY_SIZE][2]; // For each party member and side, used by Ice Face.
@ -657,6 +659,7 @@ struct BattleStruct
u8 storedLunarDance:4; // Each battler as a bit.
u16 supremeOverlordModifier[MAX_BATTLERS_COUNT];
u8 itemPartyIndex[MAX_BATTLERS_COUNT];
u8 itemMoveIndex[MAX_BATTLERS_COUNT];
bool8 trainerSlideHalfHpMsgDone;
u8 trainerSlideFirstCriticalHitMsgState:2;
u8 trainerSlideFirstSuperEffectiveHitMsgState:2;

View File

@ -112,6 +112,7 @@ bool32 IsEncoreEncouragedEffect(u16 moveEffect);
void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score);
bool32 ShouldSetSandstorm(u8 battler, u16 ability, u16 holdEffect);
bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect);
bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect);
bool32 ShouldSetRain(u8 battlerAtk, u16 ability, u16 holdEffect);
bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect);
bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef);

View File

@ -1,6 +1,8 @@
#ifndef GUARD_BATTLE_SCRIPTS_H
#define GUARD_BATTLE_SCRIPTS_H
extern const u8 BattleScript_MirrorHerbCopyStatChange[];
extern const u8 BattleScript_MirrorHerbCopyStatChangeEnd2[];
extern const u8 BattleScript_NotAffected[];
extern const u8 BattleScript_HitFromCritCalc[];
extern const u8 BattleScript_MoveEnd[];
@ -43,8 +45,9 @@ extern const u8 BattleScript_ActionSwitch[];
extern const u8 BattleScript_Pausex20[];
extern const u8 BattleScript_LevelUp[];
extern const u8 BattleScript_RainContinuesOrEnds[];
extern const u8 BattleScript_SnowContinuesOrEnds[];
extern const u8 BattleScript_DamagingWeatherContinues[];
extern const u8 BattleScript_SandStormHailEnds[];
extern const u8 BattleScript_SandStormHailSnowEnds[];
extern const u8 BattleScript_SunlightContinues[];
extern const u8 BattleScript_SunlightFaded[];
extern const u8 BattleScript_OverworldWeatherStarts[];
@ -272,7 +275,8 @@ extern const u8 BattleScript_CursedBodyActivates[];
extern const u8 BattleScript_MummyActivates[];
extern const u8 BattleScript_WeakArmorActivates[];
extern const u8 BattleScript_FellStingerRaisesStat[];
extern const u8 BattleScript_SnowWarningActivates[];
extern const u8 BattleScript_SnowWarningActivatesHail[];
extern const u8 BattleScript_SnowWarningActivatesSnow[];
extern const u8 BattleScript_HarvestActivates[];
extern const u8 BattleScript_ImposterActivates[];
extern const u8 BattleScript_SelectingNotAllowedMoveAssaultVest[];

View File

@ -192,7 +192,6 @@ bool32 IsHealBlockPreventingMove(u32 battler, u32 move);
bool32 HasEnoughHpToEatBerry(u32 battlerId, u32 hpFraction, u32 itemId);
bool32 IsPartnerMonFromSameTrainer(u8 battlerId);
u8 GetSplitBasedOnStats(u8 battlerId);
void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast);
bool32 TestSheerForceFlag(u8 battler, u16 move);
void TryRestoreHeldItems(void);
bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item);

View File

@ -122,6 +122,7 @@
#define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied.
#define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases.
#define B_LEAF_GUARD_PREVENTS_REST GEN_LATEST // In Gen5+, Leaf Guard prevents the use of Rest in harsh sunlight.
#define B_SNOW_WARNING GEN_LATEST // In Gen9+, Snow Warning will summon snow instead of hail.
// Item settings
#define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn.
@ -191,7 +192,7 @@
// Other settings
#define B_DOUBLE_WILD_CHANCE 0 // % chance of encountering two Pokémon in a Wild Encounter.
#define B_DOUBLE_WILD_REQUIRE_2_MONS FALSE // If set to TRUE, Wild Double Battles will default to Single Battles when the player only has 1 usuable Pokémon, ignoring B_DOUBLE_WILD_CHANCE and B_FLAG_FORCE_DOUBLE_WILD.
#define B_DOUBLE_WILD_REQUIRE_2_MONS FALSE // If set to TRUE, Wild Double Battles will default to Single Battles when the player only has 1 usable Pokémon, ignoring B_DOUBLE_WILD_CHANCE and B_FLAG_FORCE_DOUBLE_WILD.
#define B_MULTI_BATTLE_WHITEOUT GEN_LATEST // In Gen4+, multi battles end when the Player and also their Partner don't have any more Pokémon to fight.
#define B_EVOLUTION_AFTER_WHITEOUT GEN_LATEST // In Gen6+, Pokemon that qualify for evolution after battle will evolve even if the player loses.
#define B_WILD_NATURAL_ENEMIES TRUE // If set to TRUE, certain wild mon species will attack other species when partnered in double wild battles (eg. Zangoose vs Seviper)

View File

@ -8,10 +8,15 @@
#define P_UPDATED_EGG_GROUPS GEN_LATEST // Since Gen 8, certain Pokémon have gained new egg groups.
// Breeding settings
#define P_NIDORAN_M_DITTO_BREED GEN_LATEST // Since Gen 5, when Nidoran♂ breeds with Ditto it can produce Nidoran♀ offspring. Before, it would only yield male offspring. This change also applies to Volbeat.
#define P_INCENSE_BREEDING GEN_LATEST // Since Gen 9, cross-generation Baby Pokémon don't require Incense being held by the parents to be obtained via breeding.
#define P_EGG_HATCH_LEVEL GEN_LATEST // Since Gen 4, Pokémon will hatch from eggs at level 1 instead of 5.
#define P_BALL_INHERITING GEN_LATEST // Since Gen 6, Eggs from the Daycare will inherit the Poké Ball from their mother. From Gen7 onwards, the father can pass it down as well, as long as it's of the same species as the mother.
#define P_NIDORAN_M_DITTO_BREED GEN_LATEST // Since Gen 5, when Nidoran♂ breeds with Ditto it can produce Nidoran♀ offspring. Before, it would only yield male offspring. This change also applies to Volbeat.
#define P_INCENSE_BREEDING GEN_LATEST // Since Gen 9, cross-generation Baby Pokémon don't require Incense being held by the parents to be obtained via breeding.
#define P_EGG_HATCH_LEVEL GEN_LATEST // Since Gen 4, Pokémon will hatch from eggs at level 1 instead of 5.
#define P_BALL_INHERITING GEN_LATEST // Since Gen 6, Eggs from the Daycare will inherit the Poké Ball from their mother. From Gen 7 onwards, the father can pass it down as well, as long as it's of the same species as the mother.
#define P_TM_INHERITANCE GEN_LATEST // Since Gen 6, the father no longer passes down TMs to the baby.
#define P_MOTHER_EGG_MOVE_INHERITANCE GEN_LATEST // Since Gen 6, the mother can also pass down Egg Moves.
#define P_NATURE_INHERITANCE GEN_LATEST // In Gen 3, Everstone grants Ditto and mothers a 50% chance to pass on Nature. Since Gen 4, anyone can pass on nature. Since Gen 5, the chance is 100%.
#define P_ABILITY_INHERITANCE GEN_LATEST // In B2W2, a female Pokémon has an 80% chance of passing down their ability if bred with a male. Since Gen 6, the chance is 80% for normal ability and 60% for Hidden Ability, and anyone can pass down their abilities if bred with Ditto. NOTE: BW's effect: 60% chance to pass down HA and random for normal ability has been omitted.
#define P_EGG_MOVE_TRANSFER GEN_LATEST // Starting in Gen 8, if two Pokémon of the same species are together in the Daycare, one knows an Egg Move, and the other has an empty slot, the other Pokémon will receive the Egg Move in the empty slot. In Gen 9, if a Pokémon holds a Mirror Herb, it will receive Egg Moves from the other regardless of species.
// Species-specific settings
#define P_SHEDINJA_BALL GEN_LATEST // Since Gen 4, Shedinja requires a Poké Ball for its evolution. In Gen 3, Shedinja inherits Nincada's Ball.

View File

@ -288,8 +288,11 @@
#define B_WEATHER_HAIL_PERMANENT (1 << 10)
#define B_WEATHER_HAIL (B_WEATHER_HAIL_TEMPORARY | B_WEATHER_HAIL_PERMANENT)
#define B_WEATHER_STRONG_WINDS (1 << 11)
#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS)
#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS | B_WEATHER_SNOW)
#define B_WEATHER_PRIMAL_ANY (B_WEATHER_RAIN_PRIMAL | B_WEATHER_SUN_PRIMAL | B_WEATHER_STRONG_WINDS)
#define B_WEATHER_SNOW_TEMPORARY (1 << 12)
#define B_WEATHER_SNOW_PERMANENT (1 << 13)
#define B_WEATHER_SNOW (B_WEATHER_SNOW_TEMPORARY | B_WEATHER_SNOW_PERMANENT)
// Battle Weather as enum
#define ENUM_WEATHER_NONE 0
@ -300,6 +303,7 @@
#define ENUM_WEATHER_SUN_PRIMAL 5
#define ENUM_WEATHER_RAIN_PRIMAL 6
#define ENUM_WEATHER_STRONG_WINDS 7
#define ENUM_WEATHER_SNOW 8
// Move Effects
#define MOVE_EFFECT_SLEEP 1

View File

@ -30,6 +30,7 @@
#define AI_WEATHER_RAIN 2
#define AI_WEATHER_SANDSTORM 3
#define AI_WEATHER_HAIL 4
#define AI_WEATHER_SNOW 5
// get_how_powerful_move_is
#define MOVE_POWER_OTHER 0

View File

@ -544,6 +544,7 @@
#define B_ANIM_SHELL_TRAP_SETUP 34
#define B_ANIM_ZMOVE_ACTIVATE 35 // Using Z Moves
#define B_ANIM_AFFECTION_HANGED_ON 36
#define B_ANIM_SNOW_CONTINUES 37
// special animations table (gBattleAnims_Special)
#define B_ANIM_LVL_UP 0
@ -591,6 +592,7 @@
#define ANIM_WEATHER_RAIN 2
#define ANIM_WEATHER_SANDSTORM 3
#define ANIM_WEATHER_HAIL 4
#define ANIM_WEATHER_SNOW 5
// mon pal blend
#define ANIM_PAL_BG 0x1

View File

@ -406,7 +406,8 @@
#define EFFECT_BARB_BARRAGE 400
#define EFFECT_REVIVAL_BLESSING 401
#define EFFECT_FROSTBITE_HIT 402
#define EFFECT_SNOWSCAPE 403
#define NUM_BATTLE_MOVE_EFFECTS 403
#define NUM_BATTLE_MOVE_EFFECTS 404
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H

View File

@ -241,27 +241,22 @@
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 149
#define VARIOUS_SET_BEAK_BLAST 150
#define VARIOUS_SWAP_SIDE_STATUSES 151
#define VARIOUS_SET_Z_EFFECT 152
#define VARIOUS_TRY_SYMBIOSIS 153
#define VARIOUS_CAN_TELEPORT 154
#define VARIOUS_GET_BATTLER_SIDE 155
#define VARIOUS_CHECK_PARENTAL_BOND_COUNTER 156
#define VARIOUS_SWAP_STATS 157
#define VARIOUS_JUMP_IF_ROD 158
#define VARIOUS_JUMP_IF_ABSORB 159
#define VARIOUS_JUMP_IF_MOTOR 160
#define VARIOUS_TEATIME_INVUL 161
#define VARIOUS_TEATIME_TARGETS 162
#define VARIOUS_TRY_WIND_RIDER_POWER 163
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 164
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 165
#define VARIOUS_JUMP_IF_EMERGENCY_EXITED 166
#define VARIOUS_STORE_HEALING_WISH 167
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 168
#define VARIOUS_JUMP_IF_SHELL_TRAP 169
#define VARIOUS_TRY_REVIVAL_BLESSING 170
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 171
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 172
#define VARIOUS_SWAP_STATS 152
#define VARIOUS_JUMP_IF_ROD 153
#define VARIOUS_JUMP_IF_ABSORB 154
#define VARIOUS_JUMP_IF_MOTOR 155
#define VARIOUS_TEATIME_INVUL 156
#define VARIOUS_TEATIME_TARGETS 157
#define VARIOUS_TRY_WIND_RIDER_POWER 158
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 159
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 160
#define VARIOUS_JUMP_IF_EMERGENCY_EXITED 161
#define VARIOUS_STORE_HEALING_WISH 162
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 163
#define VARIOUS_JUMP_IF_SHELL_TRAP 164
#define VARIOUS_TRY_REVIVAL_BLESSING 165
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 166
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 167
// Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0

View File

@ -659,8 +659,13 @@
#define STRINGID_PKMNFROSTBITEHEALED 657
#define STRINGID_PKMNFROSTBITEHEALED2 658
#define STRINGID_PKMNFROSTBITEHEALEDBY 659
#define STRINGID_MIRRORHERBCOPIED 660
#define STRINGID_STARTEDSNOW 661
#define STRINGID_SNOWCONTINUES 662
#define STRINGID_SNOWSTOPPED 663
#define STRINGID_SNOWWARNINGSNOW 664
#define BATTLESTRINGS_COUNT 660
#define BATTLESTRINGS_COUNT 665
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
@ -726,15 +731,17 @@
#define B_MSG_STARTED_SANDSTORM 3
#define B_MSG_STARTED_SUNLIGHT 4
#define B_MSG_STARTED_HAIL 5
#define B_MSG_STARTED_SNOW 6
// gRainContinuesStringIds
#define B_MSG_RAIN_CONTINUES 0
#define B_MSG_DOWNPOUR_CONTINUES 1
#define B_MSG_RAIN_STOPPED 2
// gSandStormHailContinuesStringIds / gSandStormHailDmgStringIds/ gSandStormHailEndStringIds
// gSandStormHailSnowContinuesStringIds / gSandStormHailDmgStringIds/ gSandStormHailSnowEndStringIds
#define B_MSG_SANDSTORM 0
#define B_MSG_HAIL 1
#define B_MSG_SNOW 2
// gReflectLightScreenSafeguardStringIds
#define B_MSG_SIDE_STATUS_FAILED 0

View File

@ -53,7 +53,6 @@
#define OBJECT_EVENTS_COUNT 16
#define MAIL_COUNT (10 + PARTY_SIZE)
#define SECRET_BASES_COUNT 20
#define TV_SHOWS_COUNT 25
#define POKE_NEWS_COUNT 16
#define PC_ITEMS_COUNT 50
#define BAG_ITEMS_COUNT 30

View File

@ -74,6 +74,7 @@
// for TV shows from TVGROUP_NORMAL. The remainder are for TV
// shows from TVGROUP_RECORD_MIX.
#define NUM_NORMAL_TVSHOW_SLOTS 5
#define TV_SHOWS_COUNT (NUM_NORMAL_TVSHOW_SLOTS + 20)
#define PLAYERS_HOUSE_TV_NONE 0
#define PLAYERS_HOUSE_TV_LATI 1

View File

@ -22,6 +22,9 @@
#define INTR_CHECK (*(u16 *)0x3007FF8)
#define INTR_VECTOR (*(void **)0x3007FFC)
#define ROM_START 0x8000000
#define ROM_END 0xA000000
#define EWRAM_START 0x02000000
#define EWRAM_END (EWRAM_START + 0x40000)
#define IWRAM_START 0x03000000

View File

@ -147,6 +147,9 @@
#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a ## b
#define STR(a) STR_(a)
#define STR_(a) #a
// Converts a string to a compound literal, essentially making it a pointer to const u8
#define COMPOUND_STRING(str) (const u8[]) _(str)

View File

@ -443,6 +443,7 @@ void BoxMonToMon(const struct BoxPokemon *src, struct Pokemon *dest);
u8 GetLevelFromMonExp(struct Pokemon *mon);
u8 GetLevelFromBoxMonExp(struct BoxPokemon *boxMon);
u16 GiveMoveToMon(struct Pokemon *mon, u16 move);
u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move);
u16 GiveMoveToBattleMon(struct BattlePokemon *mon, u16 move);
void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot);
void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot);

View File

@ -45,6 +45,8 @@ enum
RECORDED_PARTY_INDEX,
RECORDED_BATTLE_PALACE_ACTION,
RECORDED_ITEM_ID,
RECORDED_ITEM_TARGET,
RECORDED_ITEM_MOVE,
};
extern u32 gRecordedBattleRngSeed;

View File

@ -1372,7 +1372,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
case EFFECT_AURORA_VEIL:
if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_AURORA_VEIL
|| PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)
|| !(gBattleWeather & B_WEATHER_HAIL))
|| !(gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
score -= 10;
break;
case EFFECT_OHKO:
@ -1581,6 +1581,15 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY)
|| PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove))
score -= 8;
else if (gBattleWeather & B_WEATHER_SNOW)
score -= 2; // mainly to prevent looping between hail and snow
break;
case EFFECT_SNOWSCAPE:
if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY)
|| PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove))
score -= 8;
else if (gBattleWeather & B_WEATHER_HAIL)
score -= 2; // mainly to prevent looping between hail and snow
break;
case EFFECT_ATTRACT:
if (!AI_CanBeInfatuated(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef],
@ -2803,6 +2812,13 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
RETURN_SCORE_PLUS(2); // our partner benefits from hail
}
break;
case EFFECT_SNOWSCAPE:
if (IsBattlerAlive(battlerAtkPartner)
&& ShouldSetSnow(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
{
RETURN_SCORE_PLUS(2); // our partner benefits from snow
}
break;
} // global move effect check
@ -3965,6 +3981,22 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
score += 2;
}
break;
case EFFECT_SNOWSCAPE:
if (ShouldSetSnow(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk]))
{
if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_AURORA_VEIL))
&& ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
score += 3;
score++;
if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK)
score++;
if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN)
|| HasMoveEffect(battlerDef, EFFECT_SYNTHESIS)
|| HasMoveEffect(battlerDef, EFFECT_MOONLIGHT))
score += 2;
}
break;
case EFFECT_RAIN_DANCE:
if (ShouldSetRain(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk]))
{
@ -4948,6 +4980,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
case EFFECT_SUNNY_DAY:
case EFFECT_SANDSTORM:
case EFFECT_HAIL:
case EFFECT_SNOWSCAPE:
case EFFECT_GEOMANCY:
case EFFECT_VICTORY_DANCE:
case EFFECT_HIT_SET_ENTRY_HAZARD:
@ -5174,6 +5207,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
case EFFECT_SUNNY_DAY:
case EFFECT_SANDSTORM:
case EFFECT_HAIL:
case EFFECT_SNOWSCAPE:
case EFFECT_RAIN_DANCE:
score -= 2;
break;

View File

@ -331,6 +331,7 @@ static const u16 sEncouragedEncoreEffects[] =
EFFECT_SPIT_UP,
EFFECT_SWALLOW,
EFFECT_HAIL,
EFFECT_SNOWSCAPE,
EFFECT_TORMENT,
EFFECT_WILL_O_WISP,
EFFECT_FOLLOW_ME,
@ -1504,7 +1505,7 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move)
// increased accuracy but don't always hit
if ((AI_WeatherHasEffect() &&
(((gBattleWeather & B_WEATHER_RAIN) && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE))
|| (((gBattleWeather & B_WEATHER_HAIL) && move == MOVE_BLIZZARD))))
|| (((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD))))
|| (gBattleMoves[move].effect == EFFECT_VITAL_THROW)
#if B_MINIMIZE_DMG_ACC >= GEN_6
|| ((gStatuses3[battlerDef] & STATUS3_MINIMIZED) && (gBattleMoves[move].flags & FLAG_DMG_MINIMIZE))
@ -1579,7 +1580,7 @@ bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect)
{
if (!AI_WeatherHasEffect())
return FALSE;
else if (gBattleWeather & B_WEATHER_HAIL)
else if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))
return FALSE;
if (ability == ABILITY_SNOW_CLOAK
@ -1649,6 +1650,26 @@ bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect)
return FALSE;
}
bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect)
{
if (!AI_WeatherHasEffect())
return FALSE;
else if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_HAIL))
return FALSE;
if (ability == ABILITY_SNOW_CLOAK
|| ability == ABILITY_ICE_BODY
|| ability == ABILITY_FORECAST
|| ability == ABILITY_SLUSH_RUSH
|| IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|| HasMove(battler, MOVE_BLIZZARD)
|| HasMoveEffect(battler, EFFECT_AURORA_VEIL)
|| HasMoveEffect(battler, EFFECT_WEATHER_BALL))
{
return TRUE;
}
return FALSE;
}
void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score)
{
@ -3137,7 +3158,7 @@ bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect)
{
case EFFECT_AURORA_VEIL:
// Use only in Hail and only if AI doesn't already have Reflect, Light Screen or Aurora Veil itself active.
if (gBattleWeather & B_WEATHER_HAIL
if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))
&& !(gSideStatuses[atkSide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL)))
return TRUE;
break;
@ -3242,7 +3263,8 @@ bool32 PartnerMoveEffectIsWeather(u8 battlerAtkPartner, u16 partnerMove)
&& (gBattleMoves[partnerMove].effect == EFFECT_SUNNY_DAY
|| gBattleMoves[partnerMove].effect == EFFECT_RAIN_DANCE
|| gBattleMoves[partnerMove].effect == EFFECT_SANDSTORM
|| gBattleMoves[partnerMove].effect == EFFECT_HAIL))
|| gBattleMoves[partnerMove].effect == EFFECT_HAIL
|| gBattleMoves[partnerMove].effect == EFFECT_SNOWSCAPE))
return TRUE;
return FALSE;

View File

@ -119,6 +119,7 @@ EWRAM_DATA u8 gBattleAnimAttacker = 0;
EWRAM_DATA u8 gBattleAnimTarget = 0;
EWRAM_DATA u16 gAnimBattlerSpecies[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gAnimCustomPanning = 0;
EWRAM_DATA static bool8 sAnimHideHpBoxes = FALSE;
#include "data/battle_anim.h"
@ -232,7 +233,6 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
{
s32 i;
const u8 *const *animsTable;
bool32 hideHpBoxes;
if (gTestRunnerEnabled)
{
@ -261,7 +261,7 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
break;
}
hideHpBoxes = !(animType == ANIM_TYPE_MOVE && animId == MOVE_TRANSFORM);
sAnimHideHpBoxes = !(animType == ANIM_TYPE_MOVE && animId == MOVE_TRANSFORM);
if (animType != ANIM_TYPE_MOVE)
{
switch (animId)
@ -276,10 +276,10 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
case B_ANIM_MEGA_EVOLUTION:
case B_ANIM_PRIMAL_REVERSION:
case B_ANIM_GULP_MISSILE:
hideHpBoxes = TRUE;
sAnimHideHpBoxes = TRUE;
break;
default:
hideHpBoxes = FALSE;
sAnimHideHpBoxes = FALSE;
break;
}
}
@ -287,7 +287,7 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
if (!IsContest())
{
InitPrioritiesForVisibleBattlers();
UpdateOamPriorityInAllHealthboxes(0, hideHpBoxes);
UpdateOamPriorityInAllHealthboxes(0, sAnimHideHpBoxes);
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (GetBattlerSide(i) != B_SIDE_PLAYER)
@ -764,7 +764,8 @@ static void Cmd_end(void)
if (!IsContest())
{
InitPrioritiesForVisibleBattlers();
UpdateOamPriorityInAllHealthboxes(1, TRUE);
UpdateOamPriorityInAllHealthboxes(1, sAnimHideHpBoxes);
sAnimHideHpBoxes = FALSE;
}
gAnimScriptActive = FALSE;
}

View File

@ -5603,6 +5603,8 @@ void AnimTask_GetWeather(u8 taskId)
gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SANDSTORM;
else if (gWeatherMoveAnim & B_WEATHER_HAIL)
gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_HAIL;
else if (gWeatherMoveAnim & B_WEATHER_SNOW)
gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SNOW;
DestroyAnimVisualTask(taskId);
}

View File

@ -2484,9 +2484,14 @@ void TryShinyAnimation(u8 battler, struct Pokemon *mon)
u32 otId, personality;
u32 shinyValue;
u8 taskCirc, taskDgnl;
struct Pokemon* illusionMon;
isShiny = FALSE;
gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = TRUE;
illusionMon = GetIllusionMonPtr(battler);
if (illusionMon != NULL)
mon = illusionMon;
otId = GetMonData(mon, MON_DATA_OT_ID);
personality = GetMonData(mon, MON_DATA_PERSONALITY);

View File

@ -1442,6 +1442,8 @@ static void RecordedOpponentHandleChooseItem(void)
u8 byte1 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler);
u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler);
gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2;
gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler);
gBattleStruct->itemMoveIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler);
BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]);
RecordedOpponentBufferExecCompleted();
}

View File

@ -1466,6 +1466,8 @@ static void RecordedPlayerHandleChooseItem(void)
u8 byte1 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler);
u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler);
gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2;
gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler);
gBattleStruct->itemMoveIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler);
BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]);
RecordedPlayerBufferExecCompleted();
}

View File

@ -492,6 +492,7 @@ static bool8 ShouldAnimBeDoneRegardlessOfSubstitute(u8 animId)
case B_ANIM_SUN_CONTINUES:
case B_ANIM_SANDSTORM_CONTINUES:
case B_ANIM_HAIL_CONTINUES:
case B_ANIM_SNOW_CONTINUES:
case B_ANIM_SNATCH_MOVE:
return TRUE;
default:

View File

@ -4642,7 +4642,7 @@ u32 GetBattlerTotalSpeedStat(u8 battlerId)
speed *= 2;
else if (ability == ABILITY_SAND_RUSH && gBattleWeather & B_WEATHER_SANDSTORM)
speed *= 2;
else if (ability == ABILITY_SLUSH_RUSH && gBattleWeather & B_WEATHER_HAIL)
else if (ability == ABILITY_SLUSH_RUSH && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
speed *= 2;
}
@ -4917,9 +4917,6 @@ static void SetActionsAndBattlersTurnOrder(void)
turnOrderId++;
}
}
gBattleMainFunc = CheckMegaEvolutionBeforeTurn;
gBattleStruct->mega.battlerId = 0;
return;
}
else
{
@ -4965,8 +4962,8 @@ static void SetActionsAndBattlersTurnOrder(void)
}
}
}
gBattleMainFunc = CheckMegaEvolutionBeforeTurn;
gBattleStruct->mega.battlerId = 0;
gBattleMainFunc = CheckQuickClaw_CustapBerryActivation;
gBattleStruct->quickClawBattlerId = 0;
}
static void TurnValuesCleanUp(bool8 var0)
@ -5015,49 +5012,98 @@ void SpecialStatusesClear(void)
memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses));
}
static void CheckMegaEvolutionBeforeTurn(void)
static void PopulateArrayWithBattlers(u8 *battlers)
{
if (!(gHitMarker & HITMARKER_RUN))
{
while (gBattleStruct->mega.battlerId < gBattlersCount)
{
gActiveBattler = gBattlerAttacker = gBattleStruct->mega.battlerId;
gBattleStruct->mega.battlerId++;
if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler]
&& !(gProtectStructs[gActiveBattler].noValidMoves))
{
struct Pokemon *party = GetBattlerParty(gActiveBattler);
struct Pokemon *mon = &party[gBattlerPartyIndexes[gActiveBattler]];
u32 i;
for (i = 0; i < gBattlersCount; i++)
battlers[i] = i;
}
static bool32 TryDoMegaEvosBeforeMoves(void)
{
if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->mega.toEvolve)
{
u32 i;
struct Pokemon *party;
struct Pokemon *mon;
u8 megaOrder[MAX_BATTLERS_COUNT];
PopulateArrayWithBattlers(megaOrder);
SortBattlersBySpeed(megaOrder, FALSE);
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleStruct->mega.toEvolve & gBitTable[megaOrder[i]]
&& !(gProtectStructs[megaOrder[i]].noValidMoves))
{
gActiveBattler = gBattlerAttacker = megaOrder[i];
gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]);
gLastUsedItem = gBattleMons[gActiveBattler].item;
party = GetBattlerParty(gActiveBattler);
mon = &party[gBattlerPartyIndexes[gActiveBattler]];
if (GetBattleFormChangeTargetSpecies(gActiveBattler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
BattleScriptExecute(BattleScript_WishMegaEvolution);
else
BattleScriptExecute(BattleScript_MegaEvolution);
return;
return TRUE;
}
}
}
#if B_MEGA_EVO_TURN_ORDER <= GEN_6
gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts;
gBattleStruct->focusPunchBattlerId = 0;
#else
gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved
#if B_MEGA_EVO_TURN_ORDER >= GEN_7
TryChangeTurnOrder(); // This will just do nothing if no mon has mega evolved.
#endif
return FALSE;
}
static bool32 TryDoMoveEffectsBeforeMoves(void)
{
if (!(gHitMarker & HITMARKER_RUN))
{
u32 i;
struct Pokemon *mon;
u8 battlers[MAX_BATTLERS_COUNT];
PopulateArrayWithBattlers(battlers);
SortBattlersBySpeed(battlers, FALSE);
for (i = 0; i < gBattlersCount; i++)
{
if (!(gBattleStruct->focusPunchBattlers & gBitTable[battlers[i]])
&& !(gBattleMons[battlers[i]].status1 & STATUS1_SLEEP)
&& !(gDisableStructs[battlers[i]].truantCounter)
&& !(gProtectStructs[battlers[i]].noValidMoves))
{
gBattleStruct->focusPunchBattlers |= gBitTable[battlers[i]];
gActiveBattler = gBattlerAttacker = battlers[i];
switch (gChosenMoveByBattler[gActiveBattler])
{
case MOVE_FOCUS_PUNCH:
BattleScriptExecute(BattleScript_FocusPunchSetUp);
return TRUE;
case MOVE_BEAK_BLAST:
BattleScriptExecute(BattleScript_BeakBlastSetUp);
return TRUE;
case MOVE_SHELL_TRAP:
BattleScriptExecute(BattleScript_ShellTrapSetUp);
return TRUE;
}
}
}
}
return FALSE;
}
// In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves
static void TryChangeTurnOrder(void)
{
s32 i, j;
u32 i, j;
for (i = 0; i < gBattlersCount - 1; i++)
{
for (j = i + 1; j < gBattlersCount; j++)
{
u8 battler1 = gBattlerByTurnOrder[i];
u8 battler2 = gBattlerByTurnOrder[j];
u32 battler1 = gBattlerByTurnOrder[i];
u32 battler2 = gBattlerByTurnOrder[j];
if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE
&& gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)
{
@ -5066,42 +5112,6 @@ static void TryChangeTurnOrder(void)
}
}
}
gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts;
gBattleStruct->focusPunchBattlerId = 0;
}
static void CheckChosenMoveForEffectsBeforeTurnStarts(void)
{
u32 i;
if (!(gHitMarker & HITMARKER_RUN))
{
while (gBattleStruct->focusPunchBattlerId < gBattlersCount)
{
gActiveBattler = gBattlerAttacker = gBattleStruct->focusPunchBattlerId;
gBattleStruct->focusPunchBattlerId++;
if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP)
&& !(gDisableStructs[gBattlerAttacker].truantCounter)
&& !(gProtectStructs[gActiveBattler].noValidMoves))
{
switch (gChosenMoveByBattler[gActiveBattler])
{
case MOVE_FOCUS_PUNCH:
BattleScriptExecute(BattleScript_FocusPunchSetUp);
return;
case MOVE_BEAK_BLAST:
BattleScriptExecute(BattleScript_BeakBlastSetUp);
return;
case MOVE_SHELL_TRAP:
BattleScriptExecute(BattleScript_ShellTrapSetUp);
return;
}
}
}
}
gBattleMainFunc = CheckQuickClaw_CustapBerryActivation;
gBattleStruct->quickClawBattlerId = 0;
}
static void CheckQuickClaw_CustapBerryActivation(void)
@ -5156,6 +5166,8 @@ static void CheckQuickClaw_CustapBerryActivation(void)
gCurrentTurnActionNumber = 0;
gCurrentActionFuncId = gActionsByTurnOrder[0];
gBattleStruct->dynamicMoveType = 0;
gBattleStruct->effectsBeforeUsingMoveDone = FALSE;
gBattleStruct->focusPunchBattlers = 0;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
gBattleStruct->ateBoost[i] = FALSE;
@ -5174,6 +5186,16 @@ static void RunTurnActionsFunctions(void)
if (gBattleOutcome != 0)
gCurrentActionFuncId = B_ACTION_FINISHED;
// Mega Evolve / Focus Punch-like moves after switching, items, running, but before using a move.
if (gCurrentActionFuncId == B_ACTION_USE_MOVE && !gBattleStruct->effectsBeforeUsingMoveDone)
{
if (TryDoMegaEvosBeforeMoves())
return;
else if (TryDoMoveEffectsBeforeMoves())
return;
gBattleStruct->effectsBeforeUsingMoveDone = TRUE;
}
*(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber;
sTurnActionsFuncsTable[gCurrentActionFuncId]();
@ -5590,7 +5612,7 @@ void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk)
gBattleStruct->dynamicMoveType = TYPE_ROCK | F_DYNAMIC_TYPE_2;
else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA)
gBattleStruct->dynamicMoveType = TYPE_FIRE | F_DYNAMIC_TYPE_2;
else if (gBattleWeather & B_WEATHER_HAIL)
else if (gBattleWeather & (B_WEATHER_HAIL |B_WEATHER_SNOW))
gBattleStruct->dynamicMoveType = TYPE_ICE | F_DYNAMIC_TYPE_2;
else
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_2;

View File

@ -265,7 +265,7 @@ static const u8 sText_XFoundOneY[] = _("{B_ATK_NAME_WITH_PREFIX} found\none {B_L
static const u8 sText_SoothingAroma[] = _("A soothing aroma wafted\nthrough the area!");
static const u8 sText_ItemsCantBeUsedNow[] = _("Items can't be used now.{PAUSE 64}");
static const u8 sText_ForXCommaYZ[] = _("For {B_SCR_ACTIVE_NAME_WITH_PREFIX},\n{B_LAST_ITEM} {B_BUFF1}");
static const u8 sText_PkmnUsedXToGetPumped[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} used\n{B_LAST_ITEM} to get pumped!");
static const u8 sText_PkmnUsedXToGetPumped[] = _("{B_ACTIVE_NAME_WITH_PREFIX} used\n{B_LAST_ITEM} to get pumped!");
static const u8 sText_PkmnLostFocus[] = _("{B_ATK_NAME_WITH_PREFIX} lost its\nfocus and couldn't move!");
static const u8 sText_PkmnWasDraggedOut[] = _("{B_DEF_NAME_WITH_PREFIX} was\ndragged out!\p");
static const u8 sText_TheWallShattered[] = _("The wall shattered!");
@ -365,6 +365,9 @@ static const u8 sText_SunlightFaded[] = _("The sunlight faded.");
static const u8 sText_StartedHail[] = _("It started to hail!");
static const u8 sText_HailContinues[] = _("Hail continues to fall.");
static const u8 sText_HailStopped[] = _("The hail stopped.");
static const u8 sText_StartedSnow[] = _("It started to snow!");
static const u8 sText_SnowContinues[] = _("Snow continues to fall.");
static const u8 sText_SnowStopped[] = _("The snow stopped.");
static const u8 sText_FailedToSpitUp[] = _("But it failed to spit up\na thing!");
static const u8 sText_FailedToSwallow[] = _("But it failed to swallow\na thing!");
static const u8 sText_WindBecameHeatWave[] = _("The wind turned into a\nHEAT WAVE!");
@ -614,6 +617,7 @@ static const u8 sText_AnticipationActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFI
static const u8 sText_ForewarnActivates[] = _("{B_SCR_ACTIVE_ABILITY} alerted {B_SCR_ACTIVE_NAME_WITH_PREFIX}\nto the {B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1}!");
static const u8 sText_IceBodyHpGain[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY}\nhealed it a little bit!");
static const u8 sText_SnowWarningHail[] = _("It started to hail!");
static const u8 sText_SnowWarningSnow[] = _("It started to snow!");
static const u8 sText_FriskActivates[] = _("{B_ATK_NAME_WITH_PREFIX} frisked {B_DEF_NAME_WITH_PREFIX} and\nfound its {B_LAST_ITEM}!");
static const u8 sText_UnnerveEnters[] = _("The opposing team is too nervous\nto eat Berries!");
static const u8 sText_HarvestBerry[] = _("{B_ATK_NAME_WITH_PREFIX} harvested\nits {B_LAST_ITEM}!");
@ -794,9 +798,11 @@ static const u8 sText_ItemRestoredSpeciesHealth[] = _("{B_BUFF1} had its\nHP res
static const u8 sText_ItemCuredSpeciesStatus[] = _("{B_BUFF1} had\nits status healed!");
static const u8 sText_ItemRestoredSpeciesPP[] = _("{B_BUFF1} had its\nPP restored!");
static const u8 sText_AtkTrappedDef[] = _("{B_ATK_NAME_WITH_PREFIX} trapped\nthe {B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_MirrorHerbCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} used its {B_LAST_ITEM}\nto mirror its opponent's stat changes!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
[STRINGID_MIRRORHERBCOPIED - BATTLESTRINGS_TABLE_START] = sText_MirrorHerbCopied,
[STRINGID_THUNDERCAGETRAPPED - BATTLESTRINGS_TABLE_START] = sText_AtkTrappedDef,
[STRINGID_ITEMRESTOREDSPECIESHEALTH - BATTLESTRINGS_TABLE_START] = sText_ItemRestoredSpeciesHealth,
[STRINGID_ITEMCUREDSPECIESSTATUS - BATTLESTRINGS_TABLE_START] = sText_ItemCuredSpeciesStatus,
@ -1141,6 +1147,9 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_STARTEDHAIL - BATTLESTRINGS_TABLE_START] = sText_StartedHail,
[STRINGID_HAILCONTINUES - BATTLESTRINGS_TABLE_START] = sText_HailContinues,
[STRINGID_HAILSTOPPED - BATTLESTRINGS_TABLE_START] = sText_HailStopped,
[STRINGID_STARTEDSNOW - BATTLESTRINGS_TABLE_START] = sText_StartedSnow,
[STRINGID_SNOWCONTINUES -BATTLESTRINGS_TABLE_START] = sText_SnowContinues,
[STRINGID_SNOWSTOPPED - BATTLESTRINGS_TABLE_START] = sText_SnowStopped,
[STRINGID_FAILEDTOSPITUP - BATTLESTRINGS_TABLE_START] = sText_FailedToSpitUp,
[STRINGID_FAILEDTOSWALLOW - BATTLESTRINGS_TABLE_START] = sText_FailedToSwallow,
[STRINGID_WINDBECAMEHEATWAVE - BATTLESTRINGS_TABLE_START] = sText_WindBecameHeatWave,
@ -1361,6 +1370,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_FOREWARNACTIVATES - BATTLESTRINGS_TABLE_START] = sText_ForewarnActivates,
[STRINGID_ICEBODYHPGAIN - BATTLESTRINGS_TABLE_START] = sText_IceBodyHpGain,
[STRINGID_SNOWWARNINGHAIL - BATTLESTRINGS_TABLE_START] = sText_SnowWarningHail,
[STRINGID_SNOWWARNINGSNOW - BATTLESTRINGS_TABLE_START] = sText_SnowWarningSnow,
[STRINGID_FRISKACTIVATES - BATTLESTRINGS_TABLE_START] = sText_FriskActivates,
[STRINGID_UNNERVEENTERS - BATTLESTRINGS_TABLE_START] = sText_UnnerveEnters,
[STRINGID_HARVESTBERRY - BATTLESTRINGS_TABLE_START] = sText_HarvestBerry,
@ -1557,12 +1567,14 @@ const u16 gMoveWeatherChangeStringIds[] =
[B_MSG_STARTED_SANDSTORM] = STRINGID_SANDSTORMBREWED,
[B_MSG_STARTED_SUNLIGHT] = STRINGID_SUNLIGHTGOTBRIGHT,
[B_MSG_STARTED_HAIL] = STRINGID_STARTEDHAIL,
[B_MSG_STARTED_SNOW] = STRINGID_STARTEDSNOW,
};
const u16 gSandStormHailContinuesStringIds[] =
const u16 gSandStormHailSnowContinuesStringIds[] =
{
[B_MSG_SANDSTORM] = STRINGID_SANDSTORMRAGES,
[B_MSG_HAIL] = STRINGID_HAILCONTINUES
[B_MSG_HAIL] = STRINGID_HAILCONTINUES,
[B_MSG_SNOW] = STRINGID_SNOWCONTINUES,
};
const u16 gSandStormHailDmgStringIds[] =
@ -1571,10 +1583,11 @@ const u16 gSandStormHailDmgStringIds[] =
[B_MSG_HAIL] = STRINGID_PKMNPELTEDBYHAIL
};
const u16 gSandStormHailEndStringIds[] =
const u16 gSandStormHailSnowEndStringIds[] =
{
[B_MSG_SANDSTORM] = STRINGID_SANDSTORMSUBSIDED,
[B_MSG_HAIL] = STRINGID_HAILSTOPPED
[B_MSG_HAIL] = STRINGID_HAILSTOPPED,
[B_MSG_SNOW] = STRINGID_SNOWSTOPPED,
};
const u16 gRainContinuesStringIds[] =
@ -3061,7 +3074,11 @@ static const u8 *BattleStringGetPlayerName(u8 *text, u8 battlerId)
toCpy = gSaveBlock2Ptr->playerName;
break;
case B_POSITION_PLAYER_RIGHT:
if (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & (BATTLE_TYPE_RECORDED | BATTLE_TYPE_MULTI))
if ((gBattleTypeFlags & BATTLE_TYPE_RECORDED) && !(gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER)))
{
toCpy = gLinkPlayers[0].name;
}
else if ((gBattleTypeFlags & BATTLE_TYPE_LINK) && gBattleTypeFlags & (BATTLE_TYPE_RECORDED | BATTLE_TYPE_MULTI))
{
toCpy = gLinkPlayers[2].name;
}

View File

@ -1812,7 +1812,7 @@ static bool32 AccuracyCalcHelper(u16 move)
return TRUE;
}
#if B_BLIZZARD_HAIL >= GEN_4
else if ((gBattleWeather & B_WEATHER_HAIL) && move == MOVE_BLIZZARD)
else if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD)
{
// thunder/hurricane ignore acc checks in rain unless target is holding utility umbrella
JumpIfMoveFailed(7, move);
@ -1892,7 +1892,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
calc = (calc * 80) / 100; // 1.2 sand veil loss
else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL)
else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
calc = (calc * 80) / 100; // 1.2 snow cloak loss
else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
calc = (calc * 50) / 100; // 1.5 tangled feet loss
@ -5190,7 +5190,8 @@ static void Cmd_playanimation(void)
else if (animId == B_ANIM_RAIN_CONTINUES
|| animId == B_ANIM_SUN_CONTINUES
|| animId == B_ANIM_SANDSTORM_CONTINUES
|| animId == B_ANIM_HAIL_CONTINUES)
|| animId == B_ANIM_HAIL_CONTINUES
|| animId == B_ANIM_SNOW_CONTINUES)
{
BtlController_EmitBattleAnimation(BUFFER_A, animId, *argPtr);
MarkBattlerForControllerExec(gActiveBattler);
@ -5239,7 +5240,8 @@ static void Cmd_playanimation_var(void)
else if (*animIdPtr == B_ANIM_RAIN_CONTINUES
|| *animIdPtr == B_ANIM_SUN_CONTINUES
|| *animIdPtr == B_ANIM_SANDSTORM_CONTINUES
|| *animIdPtr == B_ANIM_HAIL_CONTINUES)
|| *animIdPtr == B_ANIM_HAIL_CONTINUES
|| *animIdPtr == B_ANIM_SNOW_CONTINUES)
{
BtlController_EmitBattleAnimation(BUFFER_A, *animIdPtr, *argPtr);
MarkBattlerForControllerExec(gActiveBattler);
@ -5560,6 +5562,9 @@ static void Cmd_moveend(void)
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN;
gActiveBattler = gBattlerAttacker;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].status1), &gBattleMons[gActiveBattler].status1);
MarkBattlerForControllerExec(gActiveBattler);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_BeakBlastBurn;
effect = 1;
@ -10250,7 +10255,7 @@ static void Cmd_various(void)
{
VARIOUS_ARGS();
if (gSideStatuses[GET_BATTLER_SIDE(gActiveBattler)] & SIDE_STATUS_AURORA_VEIL
|| !(WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL))
|| !(WEATHER_HAS_EFFECT && gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
@ -10330,12 +10335,6 @@ static void Cmd_various(void)
}
return;
}
case VARIOUS_SET_Z_EFFECT:
{
VARIOUS_ARGS();
SetZEffect(); //handles battle script jumping internally
return;
}
case VARIOUS_MOVEEND_ITEM_EFFECTS:
{
VARIOUS_ARGS();
@ -11014,48 +11013,6 @@ static void Cmd_various(void)
CourtChangeSwapSideStatuses();
break;
}
case VARIOUS_TRY_SYMBIOSIS: //called by Bestow, Fling, and Bug Bite, which don't work with Cmd_removeitem.
{
VARIOUS_ARGS();
if (SYMBIOSIS_CHECK(gActiveBattler, BATTLE_PARTNER(gActiveBattler)))
{
BestowItem(BATTLE_PARTNER(gActiveBattler), gActiveBattler);
gLastUsedAbility = gBattleMons[BATTLE_PARTNER(gActiveBattler)].ability;
gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(gActiveBattler);
gBattlerAttacker = gActiveBattler;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SymbiosisActivates;
return;
}
break;
}
case VARIOUS_CAN_TELEPORT:
{
VARIOUS_ARGS();
gBattleCommunication[0] = CanTeleport(gActiveBattler);
break;
}
case VARIOUS_GET_BATTLER_SIDE:
{
VARIOUS_ARGS();
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
gBattleCommunication[0] = B_SIDE_PLAYER;
else
gBattleCommunication[0] = B_SIDE_OPPONENT;
break;
}
case VARIOUS_CHECK_PARENTAL_BOND_COUNTER:
{
VARIOUS_ARGS(u8 counter, const u8 *jumpInstr);
// Some effects should only happen on the first or second strike of Parental Bond,
// so a way to check this in battle scripts is useful
u8 counter = cmd->counter;
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == counter && gBattleMons[gBattlerTarget].hp != 0)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_SWAP_STATS:
{
VARIOUS_ARGS(u8 stat);
@ -12130,6 +12087,20 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr
{
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler);
gProtectStructs[gActiveBattler].statRaised = TRUE;
// check mirror herb
for (index = 0; index < gBattlersCount; index++)
{
if (GetBattlerSide(index) == GetBattlerSide(gActiveBattler))
continue; // Only triggers on opposing side
if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB
&& gBattleMons[index].statStages[statId] < MAX_STAT_STAGE)
{
gProtectStructs[index].eatMirrorHerb = 1;
gTotemBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk
gTotemBoosts[index].statChanges[statId - 1] = statValue;
}
}
}
}
@ -12767,6 +12738,20 @@ static void Cmd_weatherdamage(void)
gBattleMoveDamage = 1;
}
}
if (gBattleWeather & B_WEATHER_SNOW)
{
if (ability == ABILITY_ICE_BODY
&& !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
&& !BATTLER_MAX_HP(gBattlerAttacker)
&& !(gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK))
{
gBattlerAbility = gBattlerAttacker;
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;
}
}
}
gBattlescriptCurrInstr = cmd->nextInstr;
@ -16329,6 +16314,55 @@ static bool8 IsFinalStrikeEffect(u16 move)
return FALSE;
}
void BS_CheckParentalBondCounter(void)
{
NATIVE_ARGS(u8 counter, const u8 *jumpInstr);
// Some effects should only happen on the first or second strike of Parental Bond,
// so a way to check this in battle scripts is useful
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == cmd->counter && gBattleMons[gBattlerTarget].hp != 0)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_GetBattlerSide(void)
{
NATIVE_ARGS(u8 battler);
gBattleCommunication[0] = GetBattlerSide(cmd->battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_CanTeleport(void)
{
NATIVE_ARGS(u8 battler);
gBattleCommunication[0] = CanTeleport(cmd->battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TrySymbiosis(void)
{
NATIVE_ARGS();
//called by Bestow, Fling, and Bug Bite, which don't work with Cmd_removeitem.
gActiveBattler = gBattlerAttacker;
if (SYMBIOSIS_CHECK(gBattlerAttacker, BATTLE_PARTNER(gActiveBattler)))
{
BestowItem(BATTLE_PARTNER(gActiveBattler), gActiveBattler);
gLastUsedAbility = gBattleMons[BATTLE_PARTNER(gActiveBattler)].ability;
gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(gActiveBattler);
gBattlerAttacker = gActiveBattler;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SymbiosisActivates;
return;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_SetZEffect(void)
{
SetZEffect(); // Handles battle script jumping internally
}
static void TryUpdateRoundTurnOrder(void)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
@ -16519,16 +16553,20 @@ void BS_ItemRestorePP(void)
{
NATIVE_ARGS();
const u8 *effect = GetItemEffect(gLastUsedItem);
u32 i, pp, maxPP, moveId;
u32 loopEnd = MAX_MON_MOVES;
u32 i, pp, maxPP, moveId, loopEnd;
u32 battlerId = MAX_BATTLERS_COUNT;
struct Pokemon *mon = (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) ? &gPlayerParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]] : &gEnemyParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]];
// Check whether to apply to all moves.
if (effect[4] & ITEM4_HEAL_PP_ONE)
{
i = gChosenMovePos;
loopEnd = gChosenMovePos + 1;
i = gBattleStruct->itemMoveIndex[gBattlerAttacker];
loopEnd = i + 1;
}
else
{
i = 0;
loopEnd = MAX_MON_MOVES;
}
// Check if the recipient is an active battler.
@ -16539,7 +16577,7 @@ void BS_ItemRestorePP(void)
battlerId = BATTLE_PARTNER(gBattlerAttacker);
// Heal PP!
for (i = 0; i < loopEnd; i++)
for (; i < loopEnd; i++)
{
pp = GetMonData(mon, MON_DATA_PP1 + i, NULL);
moveId = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
@ -16577,3 +16615,20 @@ void BS_TryRevertWeatherForm(void)
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_SetSnow(void)
{
NATIVE_ARGS();
if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SNOW, FALSE))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SNOW;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -251,6 +251,7 @@ static const u16 sPoints_MoveEffect[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_SWALLOW] = 3,
// [EFFECT_UNUSED_A3] = 1,
[EFFECT_HAIL] = 4,
[EFFECT_SNOWSCAPE] = 4,
[EFFECT_TORMENT] = 7,
[EFFECT_FLATTER] = 7,
[EFFECT_WILL_O_WISP] = 5,
@ -1770,6 +1771,6 @@ static void AddPointsBasedOnWeather(u16 weatherFlags, u16 moveId, u8 moveSlot)
AddMovePoints(PTS_SUN, moveId, moveSlot, 0);
else if (weatherFlags & B_WEATHER_SANDSTORM)
AddMovePoints(PTS_SANDSTORM, moveId, moveSlot, 0);
else if (weatherFlags & B_WEATHER_HAIL)
else if (weatherFlags & (B_WEATHER_HAIL | B_WEATHER_SNOW))
AddMovePoints(PTS_HAIL, moveId, moveSlot, 0);
}

View File

@ -1598,7 +1598,8 @@ void BattleScriptPushCursor(void)
void BattleScriptPop(void)
{
gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size];
if (gBattleResources->battleScriptsStack->size != 0)
gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size];
}
static bool32 IsGravityPreventingMove(u32 move)
@ -2032,6 +2033,7 @@ enum
ENDTURN_SANDSTORM,
ENDTURN_SUN,
ENDTURN_HAIL,
ENDTURN_SNOW,
ENDTURN_GRAVITY,
ENDTURN_WATER_SPORT,
ENDTURN_MUD_SPORT,
@ -2340,7 +2342,7 @@ u8 DoFieldEndTurnEffects(void)
if (!(gBattleWeather & B_WEATHER_SANDSTORM_PERMANENT) && --gWishFutureKnock.weatherDuration == 0)
{
gBattleWeather &= ~B_WEATHER_SANDSTORM_TEMPORARY;
gBattlescriptCurrInstr = BattleScript_SandStormHailEnds;
gBattlescriptCurrInstr = BattleScript_SandStormHailSnowEnds;
}
else
{
@ -2380,7 +2382,7 @@ u8 DoFieldEndTurnEffects(void)
if (!(gBattleWeather & B_WEATHER_HAIL_PERMANENT) && --gWishFutureKnock.weatherDuration == 0)
{
gBattleWeather &= ~B_WEATHER_HAIL_TEMPORARY;
gBattlescriptCurrInstr = BattleScript_SandStormHailEnds;
gBattlescriptCurrInstr = BattleScript_SandStormHailSnowEnds;
}
else
{
@ -2394,6 +2396,26 @@ u8 DoFieldEndTurnEffects(void)
}
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_SNOW:
if (gBattleWeather & B_WEATHER_SNOW)
{
if (!(gBattleWeather & B_WEATHER_SNOW_PERMANENT) && --gWishFutureKnock.weatherDuration == 0)
{
gBattleWeather &= ~B_WEATHER_SNOW_TEMPORARY;
gBattlescriptCurrInstr = BattleScript_SandStormHailSnowEnds;
}
else
{
gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues;
}
gBattleScripting.animArg1 = B_ANIM_SNOW_CONTINUES;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW;
BattleScriptExecute(gBattlescriptCurrInstr);
effect++;
}
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_TRICK_ROOM:
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && --gFieldTimers.trickRoomTimer == 0)
{
@ -3993,6 +4015,7 @@ static const u16 sWeatherFlagsInfo[][3] =
[ENUM_WEATHER_SANDSTORM] = {B_WEATHER_SANDSTORM_TEMPORARY, B_WEATHER_SANDSTORM_PERMANENT, HOLD_EFFECT_SMOOTH_ROCK},
[ENUM_WEATHER_HAIL] = {B_WEATHER_HAIL_TEMPORARY, B_WEATHER_HAIL_PERMANENT, HOLD_EFFECT_ICY_ROCK},
[ENUM_WEATHER_STRONG_WINDS] = {B_WEATHER_STRONG_WINDS, B_WEATHER_STRONG_WINDS, HOLD_EFFECT_NONE},
[ENUM_WEATHER_SNOW] = {B_WEATHER_SNOW_TEMPORARY, B_WEATHER_SNOW_PERMANENT, HOLD_EFFECT_ICY_ROCK},
};
static void ShouldChangeFormInWeather(u8 battler)
@ -4013,7 +4036,6 @@ static void ShouldChangeFormInWeather(u8 battler)
bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility)
{
u16 battlerAbility = GetBattlerAbility(battler);
if (gBattleWeather & B_WEATHER_PRIMAL_ANY
&& battlerAbility != ABILITY_DESOLATE_LAND
&& battlerAbility != ABILITY_PRIMORDIAL_SEA
@ -4039,7 +4061,6 @@ bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility)
ShouldChangeFormInWeather(battler);
return TRUE;
}
return FALSE;
}
@ -4544,11 +4565,19 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
}
break;
case ABILITY_SNOW_WARNING:
if (TryChangeBattleWeather(battler, ENUM_WEATHER_HAIL, TRUE))
#if B_SNOW_WARNING >= GEN_9
if (TryChangeBattleWeather(battler, ENUM_WEATHER_SNOW, TRUE))
{
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivates);
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesSnow);
effect++;
}
#else
if (TryChangeBattleWeather(battler, ENUM_WEATHER_HAIL, TRUE))
{
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesHail);
effect++;
}
#endif
else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone)
{
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
@ -6023,7 +6052,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
}
break;
case ABILITY_ICE_FACE:
if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL)
if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL | B_WEATHER_SNOW)
&& gBattleMons[battler].species == SPECIES_EISCUE_NOICE_FACE
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
&& gBattleStruct->allowedToChangeFormInWeather[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)])
@ -6297,7 +6326,6 @@ bool32 CanBePoisoned(u8 battlerAttacker, u8 battlerTarget)
|| ability == ABILITY_IMMUNITY
|| ability == ABILITY_COMATOSE
|| IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL)
|| gBattleMons[battlerTarget].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battlerTarget)
|| IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
@ -6692,6 +6720,26 @@ static bool32 GetMentalHerbEffect(u8 battlerId)
return ret;
}
static u8 TryConsumeMirrorHerb(u8 battlerId, bool32 execute)
{
u8 effect = 0;
if (gProtectStructs[battlerId].eatMirrorHerb) {
gLastUsedItem = gBattleMons[battlerId].item;
gBattleScripting.savedBattler = gBattlerAttacker;
gBattleScripting.battler = gBattlerAttacker = battlerId;
gProtectStructs[battlerId].eatMirrorHerb = 0;
if (execute) {
BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2);
} else {
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MirrorHerbCopyStatChange;
}
effect = ITEM_STATS_CHANGE;
}
return effect;
}
static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect)
{
u8 effect = 0;
@ -6895,6 +6943,9 @@ static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect)
BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet);
effect = ITEM_STATS_CHANGE;
break;
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battlerId, FALSE);
break;
}
return effect;
@ -7459,6 +7510,9 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn)
BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet);
effect = ITEM_STATS_CHANGE;
break;
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battlerId, TRUE);
break;
}
if (effect != 0)
@ -7973,8 +8027,6 @@ u8 IsMonDisobedient(void)
if (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName))
levelReferenced = gBattleMons[gBattlerAttacker].metLevel;
else
#else
if (gBattleMons[gBattlerAttacker].level <= obedienceLevel)
#endif
levelReferenced = gBattleMons[gBattlerAttacker].level;
@ -9011,7 +9063,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe
MulModifier(&modifier, UQ_4_12(2.0));
break;
case EFFECT_SOLAR_BEAM:
if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN)))
if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN | B_WEATHER_SNOW)))
MulModifier(&modifier, UQ_4_12(0.5));
break;
case EFFECT_STOMPING_TANTRUM:
@ -9416,6 +9468,9 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType,
// sandstorm sp.def boost for rock types
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT && !usesDefStat)
MulModifier(&modifier, UQ_4_12(1.5));
// snow def boost for ice types
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat)
MulModifier(&modifier, UQ_4_12(1.5));
// The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges.
// Having the 5th badge boosts physical defense while having the 7th badge boosts special defense.
@ -10270,12 +10325,19 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId)
{
struct Pokemon *party, *partnerMon;
s32 i, id;
u8 side, partyCount;
gBattleStruct->illusion[battlerId].set = 1;
if (GetMonAbility(mon) != ABILITY_ILLUSION)
return FALSE;
party = GetBattlerParty(battlerId);
side = GetBattlerSide(battlerId);
partyCount = side == B_SIDE_PLAYER ? gPlayerPartyCount : gEnemyPartyCount;
// If this pokemon is last in the party, ignore Illusion.
if (&party[partyCount - 1] == mon)
return FALSE;
if (IsBattlerAlive(BATTLE_PARTNER(battlerId)))
partnerMon = &party[gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)]];
@ -10515,7 +10577,7 @@ bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability)
void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast)
{
int i, j, currSpeed, currBattler;
u16 speeds[4] = {0};
u16 speeds[MAX_BATTLERS_COUNT] = {0};
for (i = 0; i < gBattlersCount; i++)
speeds[i] = GetBattlerTotalSpeedStat(battlers[i]);

View File

@ -585,7 +585,7 @@ const u8 *GetZMoveName(u16 move)
return gZMoveNames[0]; // Failsafe
}
#define Z_EFFECT_BS_LENGTH 3
#define Z_EFFECT_BS_LENGTH 5
// This function kinda cheats by setting a return battle script to after the setzeffect various command
// and then jumping to a z effect script
void SetZEffect(void)
@ -676,7 +676,7 @@ void SetZEffect(void)
gBattlescriptCurrInstr = BattleScript_StatUpZMove;
break;
default:
gBattlescriptCurrInstr += 3;
gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH;
break;
}

View File

@ -12699,12 +12699,13 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
{
#if B_UPDATED_MOVE_DATA >= GEN_9
.power = 80,
.accuracy = 100,
#else
.power = 75,
.accuracy = 90,
#endif
.effect = EFFECT_SPEED_UP_HIT,
.type = TYPE_PSYCHIC,
.accuracy = 90,
.pp = 10,
.secondaryEffectChance = 100,
.target = MOVE_TARGET_SELECTED,
@ -13356,7 +13357,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_SNOWSCAPE] =
{
.effect = EFFECT_HAIL,
.effect = EFFECT_SNOWSCAPE,
.power = 0,
.type = TYPE_ICE,
.accuracy = 0,

View File

@ -526,7 +526,7 @@ const struct Item gItems[] =
.pocket = POCKET_ITEMS,
.type = ITEM_USE_PARTY_MENU,
.fieldUseFunc = ItemUseOutOfBattle_Medicine,
.battleUsage = EFFECT_ITEM_RESTORE_HP,
.battleUsage = EFFECT_ITEM_CURE_STATUS,
.flingPower = 30,
},

View File

@ -158,8 +158,8 @@ static const struct FormChange sAbsolFormChangeTable[] = {
static const struct FormChange sCastformFormChangeTable[] = {
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SUNNY, B_WEATHER_SUN, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_RAINY, B_WEATHER_RAIN, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SNOWY, B_WEATHER_HAIL, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM, ~(B_WEATHER_SUN | B_WEATHER_RAIN | B_WEATHER_HAIL), ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_SNOWY, B_WEATHER_HAIL | B_WEATHER_SNOW, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM, ~(B_WEATHER_SUN | B_WEATHER_RAIN | B_WEATHER_HAIL | B_WEATHER_SNOW), ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM, B_WEATHER_NONE, ABILITY_FORECAST},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_CASTFORM},
{FORM_CHANGE_FAINT, SPECIES_CASTFORM},

View File

@ -79,7 +79,7 @@ static const u8 sTangledFeetDescription[] = _("Ups evasion if confused.");
static const u8 sMotorDriveDescription[] = _("Electricity raises Speed.");
static const u8 sRivalryDescription[] = _("Powers up against rivals.");
static const u8 sSteadfastDescription[] = _("Flinching raises Speed.");
static const u8 sSnowCloakDescription[] = _("Ups evasion in Hail.");
static const u8 sSnowCloakDescription[] = _("Ups evasion in Hail or Snow.");
static const u8 sGluttonyDescription[] = _("Eats Berries early.");
static const u8 sAngerPointDescription[] = _("Critical hits raise Attack.");
static const u8 sUnburdenDescription[] = _("Using a hold item ups Speed.");
@ -113,8 +113,12 @@ static const u8 sFilterDescription[] = _("Weakens “supereffective”.");
static const u8 sSlowStartDescription[] = _("Takes a while to get going.");
static const u8 sScrappyDescription[] = _("Hits Ghost-type Pokémon.");
static const u8 sStormDrainDescription[] = _("Draws in Water moves.");
static const u8 sIceBodyDescription[] = _("Slight HP recovery in Hail.");
static const u8 sSnowWarningDescription[] = _("Summons a hailstorm.");
static const u8 sIceBodyDescription[] = _("HP recovery in Hail or Snow.");
#if B_SNOW_WARNING < GEN_9
static const u8 sSnowWarningDescription[] = _("Summons a Hailstorm.");
#elif B_SNOW_WARNING >= GEN_9
static const u8 sSnowWarningDescription[] = _("Summons a Snowstorm.");
#endif
static const u8 sHoneyGatherDescription[] = _("May gather Honey.");
static const u8 sFriskDescription[] = _("Checks a foe's item.");
static const u8 sRecklessDescription[] = _("Boosts moves with recoil.");
@ -192,7 +196,7 @@ static const u8 sStakeoutDescription[] = _("Stronger as foes switch in.");
static const u8 sWaterBubbleDescription[] = _("Guards from fire and burns.");
static const u8 sSteelworkerDescription[] = _("Powers up Steel moves.");
static const u8 sBerserkDescription[] = _("Boosts Sp. Atk at low HP.");
static const u8 sSlushRushDescription[] = _("Raises Speed in hail.");
static const u8 sSlushRushDescription[] = _("Raises Speed in Hail or Snow.");
static const u8 sLongReachDescription[] = _("Never makes contact.");
static const u8 sLiquidVoiceDescription[] = _("Makes sound moves Water.");
static const u8 sTriageDescription[] = _("Healing moves go first.");
@ -234,7 +238,7 @@ static const u8 sPunkRockDescription[] = _("Ups and resists sound.");
static const u8 sSandSpitDescription[] = _("Creates a sandstorm if hit.");
static const u8 sIceScalesDescription[] = _("Halves special damage.");
static const u8 sRipenDescription[] = _("Doubles effect of Berries.");
static const u8 sIceFaceDescription[] = _("Take a free hit. Hail renews.");
static const u8 sIceFaceDescription[] = _("Hail or Snow renew free hit.");
static const u8 sPowerSpotDescription[] = _("Powers up ally moves.");
static const u8 sMimicryDescription[] = _("Changes type on terrain.");
static const u8 sScreenCleanerDescription[] = _("Removes walls of light.");

View File

@ -22,16 +22,20 @@
#include "item.h"
#include "constants/form_change_types.h"
#include "constants/items.h"
#include "constants/hold_effects.h"
#include "constants/moves.h"
#include "constants/region_map_sections.h"
extern const struct Evolution gEvolutionTable[][EVOS_PER_MON];
#define IS_DITTO(species) (gBaseStats[species].eggGroup1 == EGG_GROUP_DITTO || gBaseStats[species].eggGroup2 == EGG_GROUP_DITTO)
static void ClearDaycareMonMail(struct DaycareMail *mail);
static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare *daycare);
static u8 GetDaycareCompatibilityScore(struct DayCare *daycare);
static void DaycarePrintMonInfo(u8 windowId, u32 daycareSlotId, u8 y);
static u8 ModifyBreedingScoreForOvalCharm(u8 score);
static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves);
// RAM buffers used to assist with BuildEggMoveset()
EWRAM_DATA static u16 sHatchedEggLevelUpMoves[EGG_LVL_UP_MOVES_ARRAY_COUNT] = {0};
@ -162,6 +166,57 @@ static s8 Daycare_FindEmptySpot(struct DayCare *daycare)
return -1;
}
static void ClearHatchedEggMoves(void)
{
u16 i;
for (i = 0; i < EGG_MOVES_ARRAY_COUNT; i++)
sHatchedEggEggMoves[i] = MOVE_NONE;
}
static void TransferEggMoves(void)
{
u32 i, j, k, l;
u16 numEggMoves;
struct Pokemon mon;
for (i = 0; i < DAYCARE_MON_COUNT; i++)
{
if (!GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SANITY_HAS_SPECIES))
continue;
BoxMonToMon(&gSaveBlock1Ptr->daycare.mons[i].mon, &mon);
ClearHatchedEggMoves();
numEggMoves = GetEggMoves(&mon, sHatchedEggEggMoves);
for (j = 0; j < numEggMoves; j++)
{
// Go through other Daycare mons
for (k = 0; k < DAYCARE_MON_COUNT; k++)
{
if (k == i || !GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SANITY_HAS_SPECIES))
continue;
// Check if you can inherit from them
if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SPECIES) != GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SPECIES)
#if P_EGG_MOVE_TRANSFER >= GEN_9
&& GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_HELD_ITEM) != ITEM_MIRROR_HERB
#endif
)
continue;
for (l = 0; l < MAX_MON_MOVES; l++)
{
if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_MOVE1 + l) != sHatchedEggEggMoves[j])
continue;
if (GiveMoveToBoxMon(&gSaveBlock1Ptr->daycare.mons[i].mon, sHatchedEggEggMoves[j]) == MON_HAS_MAX_MOVES)
break;
}
}
}
}
}
static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycareMon)
{
if (MonHasMail(mon))
@ -184,6 +239,10 @@ static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycar
ZeroMonData(mon);
CompactPartySlots();
CalculatePlayerPartyCount();
#if P_EGG_MOVE_TRANSFER >= GEN_8
TransferEggMoves();
#endif
}
static void StorePokemonInEmptyDaycareSlot(struct Pokemon *mon, struct DayCare *daycare)
@ -427,43 +486,29 @@ static u16 GetEggSpecies(u16 species)
static s32 GetParentToInheritNature(struct DayCare *daycare)
{
u32 species[DAYCARE_MON_COUNT];
s32 i;
s32 dittoCount;
s32 parent = -1;
u32 i;
u8 numWithEverstone = 0;
s32 slot = -1;
// search for female gender
for (i = 0; i < DAYCARE_MON_COUNT; i++)
{
if (GetBoxMonGender(&daycare->mons[i].mon) == MON_FEMALE)
parent = i;
if (ItemId_GetHoldEffect(GetBoxMonData(&daycare->mons[i].mon, MON_DATA_HELD_ITEM)) == HOLD_EFFECT_PREVENT_EVOLVE
#if P_NATURE_INHERITANCE == GEN_3
&& (GetBoxMonGender(&daycare->mons[i].mon) == MON_FEMALE || IS_DITTO(GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES)))
#endif
) {
slot = i;
numWithEverstone++;
}
}
// search for ditto
for (dittoCount = 0, i = 0; i < DAYCARE_MON_COUNT; i++)
{
species[i] = GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES);
if (species[i] == SPECIES_DITTO)
dittoCount++, parent = i;
}
// coin flip on ...two Dittos
if (dittoCount == DAYCARE_MON_COUNT)
{
if (Random() >= USHRT_MAX / 2)
parent = 0;
else
parent = 1;
}
// Don't inherit nature if not holding Everstone
if (GetBoxMonData(&daycare->mons[parent].mon, MON_DATA_HELD_ITEM) != ITEM_EVERSTONE
|| Random() >= USHRT_MAX / 2)
{
return -1;
}
return parent;
if (numWithEverstone >= DAYCARE_MON_COUNT)
return Random() & 1;
#if P_NATURE_INHERITANCE > GEN_4
return slot;
#else
return Random() & 1 ? slot : -1;
#endif
}
static void _TriggerPendingDaycareEgg(struct DayCare *daycare)
@ -543,7 +588,7 @@ 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 i, start;
u8 selectedIvs[5];
u8 availableIVs[NUM_STATS];
u8 whichParents[5];
@ -559,8 +604,33 @@ static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare)
availableIVs[i] = i;
}
start = 0;
if (ItemId_GetHoldEffect(motherItem) == HOLD_EFFECT_POWER_ITEM &&
ItemId_GetHoldEffect(fatherItem) == HOLD_EFFECT_POWER_ITEM)
{
whichParents[0] = Random() % DAYCARE_MON_COUNT;
selectedIvs[0] = ItemId_GetSecondaryId(
GetBoxMonData(&daycare->mons[whichParents[0]].mon, MON_DATA_HELD_ITEM));
RemoveIVIndexFromList(availableIVs, selectedIvs[0]);
start++;
}
else if (ItemId_GetHoldEffect(motherItem) == HOLD_EFFECT_POWER_ITEM)
{
whichParents[0] = 0;
selectedIvs[0] = ItemId_GetSecondaryId(motherItem);
RemoveIVIndexFromList(availableIVs, selectedIvs[0]);
start++;
}
else if (ItemId_GetHoldEffect(fatherItem) == HOLD_EFFECT_POWER_ITEM)
{
whichParents[0] = 1;
selectedIvs[0] = ItemId_GetSecondaryId(fatherItem);
RemoveIVIndexFromList(availableIVs, selectedIvs[0]);
start++;
}
// Select which IVs that will be inherited.
for (i = 0; i < howManyIVs; i++)
for (i = start; 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
@ -579,7 +649,7 @@ static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare)
}
// Determine which parent each of the selected IVs should inherit from.
for (i = 0; i < howManyIVs; i++)
for (i = start; i < howManyIVs; i++)
{
whichParents[i] = Random() % DAYCARE_MON_COUNT;
}
@ -644,6 +714,35 @@ static void InheritPokeball(struct Pokemon *egg, struct BoxPokemon *father, stru
SetMonData(egg, MON_DATA_POKEBALL, &inheritBall);
}
static void InheritAbility(struct Pokemon *egg, struct BoxPokemon *father, struct BoxPokemon *mother)
{
u8 fatherAbility = GetBoxMonData(father, MON_DATA_ABILITY_NUM);
u8 motherAbility = GetBoxMonData(mother, MON_DATA_ABILITY_NUM);
u8 motherSpecies = GetBoxMonData(mother, MON_DATA_SPECIES);
u8 inheritAbility = motherAbility;
if (motherSpecies == SPECIES_DITTO)
#if P_ABILITY_INHERITANCE < GEN_6
return;
#else
inheritAbility = fatherAbility;
#endif
if (inheritAbility < 2 && (Random() % 10 < 8))
{
SetMonData(egg, MON_DATA_ABILITY_NUM, &inheritAbility);
}
#if P_ABILITY_INHERITANCE < GEN_6
else if (Random() % 10 < 8)
#else
else if (Random() % 10 < 6)
#endif
{
// Hidden Abilities have a different chance of being passed down
SetMonData(egg, MON_DATA_ABILITY_NUM, &inheritAbility);
}
}
// Counts the number of egg moves a pokemon learns and stores the moves in
// the given array.
static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves)
@ -691,8 +790,7 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru
sHatchedEggFatherMoves[i] = MOVE_NONE;
sHatchedEggFinalMoves[i] = MOVE_NONE;
}
for (i = 0; i < EGG_MOVES_ARRAY_COUNT; i++)
sHatchedEggEggMoves[i] = MOVE_NONE;
ClearHatchedEggMoves();
for (i = 0; i < EGG_LVL_UP_MOVES_ARRAY_COUNT; i++)
sHatchedEggLevelUpMoves[i] = MOVE_NONE;
@ -705,6 +803,28 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru
numEggMoves = GetEggMoves(egg, sHatchedEggEggMoves);
#if P_MOTHER_EGG_MOVE_INHERITANCE >= GEN_6
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (sHatchedEggMotherMoves[i] != MOVE_NONE)
{
for (j = 0; j < numEggMoves; j++)
{
if (sHatchedEggMotherMoves[i] == sHatchedEggEggMoves[j])
{
if (GiveMoveToMon(egg, sHatchedEggMotherMoves[i]) == MON_HAS_MAX_MOVES)
DeleteFirstMoveAndGiveMoveToMon(egg, sHatchedEggMotherMoves[i]);
break;
}
}
}
else
{
break;
}
}
#endif
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (sHatchedEggFatherMoves[i] != MOVE_NONE)
@ -724,6 +844,7 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru
break;
}
}
#if P_TM_INHERITANCE < GEN_6
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (sHatchedEggFatherMoves[i] != MOVE_NONE)
@ -739,6 +860,7 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru
}
}
}
#endif
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (sHatchedEggFatherMoves[i] == MOVE_NONE)
@ -909,6 +1031,9 @@ static void _GiveEggFromDaycare(struct DayCare *daycare)
InheritIVs(&egg, daycare);
InheritPokeball(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon);
BuildEggMoveset(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon);
#if P_ABILITY_INHERITANCE >= GEN_6
InheritAbility(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon);
#endif
GiveMoveIfItem(&egg, daycare);

View File

@ -2327,7 +2327,7 @@ static void DebugAction_Give_Pokemon_SelectNature(u8 taskId)
}
static void DebugAction_Give_Pokemon_SelectAbility(u8 taskId)
{
u8 abilityId;
u16 abilityId;
u8 abilityCount = NUM_ABILITY_SLOTS - 1; //-1 for proper iteration
u8 i = 0;

View File

@ -2576,7 +2576,7 @@ bool8 MovementType_WanderAround_Step2(struct ObjectEvent *objectEvent, struct Sp
{
if (!ObjectEventExecSingleMovementAction(objectEvent, sprite))
return FALSE;
SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]);
sprite->sTypeFuncId = 3;
return TRUE;
}
@ -2856,7 +2856,7 @@ bool8 MovementType_LookAround_Step2(struct ObjectEvent *objectEvent, struct Spri
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -2908,7 +2908,7 @@ bool8 MovementType_WanderUpAndDown_Step2(struct ObjectEvent *objectEvent, struct
if (!ObjectEventExecSingleMovementAction(objectEvent, sprite))
return FALSE;
SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]);
sprite->sTypeFuncId = 3;
return TRUE;
}
@ -2976,7 +2976,7 @@ bool8 MovementType_WanderLeftAndRight_Step2(struct ObjectEvent *objectEvent, str
if (!ObjectEventExecSingleMovementAction(objectEvent, sprite))
return FALSE;
SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]);
sprite->sTypeFuncId = 3;
return TRUE;
}
@ -3196,7 +3196,7 @@ bool8 MovementType_FaceDownAndUp_Step2(struct ObjectEvent *objectEvent, struct S
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3246,7 +3246,7 @@ bool8 MovementType_FaceLeftAndRight_Step2(struct ObjectEvent *objectEvent, struc
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3296,7 +3296,7 @@ bool8 MovementType_FaceUpAndLeft_Step2(struct ObjectEvent *objectEvent, struct S
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3346,7 +3346,7 @@ bool8 MovementType_FaceUpAndRight_Step2(struct ObjectEvent *objectEvent, struct
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3396,7 +3396,7 @@ bool8 MovementType_FaceDownAndLeft_Step2(struct ObjectEvent *objectEvent, struct
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3446,7 +3446,7 @@ bool8 MovementType_FaceDownAndRight_Step2(struct ObjectEvent *objectEvent, struc
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3496,7 +3496,7 @@ bool8 MovementType_FaceDownUpAndLeft_Step2(struct ObjectEvent *objectEvent, stru
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3546,7 +3546,7 @@ bool8 MovementType_FaceDownUpAndRight_Step2(struct ObjectEvent *objectEvent, str
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3596,7 +3596,7 @@ bool8 MovementType_FaceUpLeftAndRight_Step2(struct ObjectEvent *objectEvent, str
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}
@ -3646,7 +3646,7 @@ bool8 MovementType_FaceDownLeftAndRight_Step2(struct ObjectEvent *objectEvent, s
{
if (ObjectEventExecSingleMovementAction(objectEvent, sprite))
{
SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]);
SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]);
objectEvent->singleMovementActive = FALSE;
sprite->sTypeFuncId = 3;
}

View File

@ -249,7 +249,7 @@ static const struct ListMenuTemplate sItemListMenu =
.itemPrintFunc = BagMenu_ItemPrintCallback,
.totalItems = 0,
.maxShowed = 0,
.windowId = 0,
.windowId = WIN_ITEM_LIST,
.header_X = 0,
.item_X = 8,
.cursor_X = 0,

View File

@ -1284,23 +1284,34 @@ static void PlayAmbientCry(void)
PlayCry_NormalNoDucking(sAmbientCrySpecies, pan, volume, CRY_PRIORITY_AMBIENT);
}
// States for UpdateAmbientCry
enum {
AMB_CRY_INIT,
AMB_CRY_FIRST,
AMB_CRY_RESET,
AMB_CRY_WAIT,
AMB_CRY_IDLE,
};
void UpdateAmbientCry(s16 *state, u16 *delayCounter)
{
u8 i, monsCount, divBy;
switch (*state)
{
case 0:
case AMB_CRY_INIT:
// This state will be revisited whenever ResetFieldTasksArgs is called (which happens on map transition)
if (sAmbientCrySpecies == SPECIES_NONE)
*state = 4;
*state = AMB_CRY_IDLE;
else
*state = 1;
*state = AMB_CRY_FIRST;
break;
case 1:
case AMB_CRY_FIRST:
// It takes between 1200-3599 frames (~20-60 seconds) to play the first ambient cry after entering a map
*delayCounter = (Random() % 2400) + 1200;
*state = 3;
*state = AMB_CRY_WAIT;
break;
case 2:
case AMB_CRY_RESET:
divBy = 1;
monsCount = CalculatePlayerPartyCount();
for (i = 0; i < monsCount; i++)
@ -1312,18 +1323,20 @@ void UpdateAmbientCry(s16 *state, u16 *delayCounter)
break;
}
}
// Ambient cries after the first one take between 1200-2399 frames (~20-40 seconds)
// If the player has a pokemon with the ability Swarm in their party, the time is halved to 600-1199 frames (~10-20 seconds)
*delayCounter = ((Random() % 1200) + 1200) / divBy;
*state = 3;
*state = AMB_CRY_WAIT;
break;
case 3:
(*delayCounter)--;
if (*delayCounter == 0)
case AMB_CRY_WAIT:
if (--(*delayCounter) == 0)
{
PlayAmbientCry();
*state = 2;
*state = AMB_CRY_RESET;
}
break;
case 4:
case AMB_CRY_IDLE:
// No land/water pokemon on this map
break;
}
}

View File

@ -4663,7 +4663,6 @@ void Task_AbilityPatch(u8 taskId)
// Can't use.
if (gSpeciesInfo[tSpecies].abilities[tAbilityNum] == 0
|| !tSpecies
|| GetMonData(&gPlayerParty[tMonId], MON_DATA_ABILITY_NUM, NULL) > 1
)
{
gPartyMenuUseExitCallback = FALSE;
@ -4734,7 +4733,10 @@ void ItemUseCB_AbilityPatch(u8 taskId, TaskFunc task)
tState = 0;
tMonId = gPartyMenu.slotId;
tSpecies = GetMonData(&gPlayerParty[tMonId], MON_DATA_SPECIES, NULL);
tAbilityNum = 2;
if (GetMonData(&gPlayerParty[tMonId], MON_DATA_ABILITY_NUM, NULL) == 2)
tAbilityNum = 0;
else
tAbilityNum = 2;
SetWordTaskArg(taskId, tOldFunc, (uintptr_t)(gTasks[taskId].func));
gTasks[taskId].func = Task_AbilityPatch;
}
@ -4944,7 +4946,7 @@ static void TryUseItemOnMove(u8 taskId)
else
{
gBattleStruct->itemPartyIndex[gBattlerInMenuId] = GetPartyIdFromBattleSlot(gPartyMenu.slotId);
gChosenMovePos = ptr->data1;
gBattleStruct->itemMoveIndex[gBattlerInMenuId] = ptr->data1;
gPartyMenuUseExitCallback = TRUE;
RemoveBagItem(gSpecialVar_ItemId, 1);
ScheduleBgCopyTilemapToVram(2);

View File

@ -64,7 +64,6 @@ static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 perso
static void EncryptBoxMon(struct BoxPokemon *boxMon);
static void DecryptBoxMon(struct BoxPokemon *boxMon);
static void Task_PlayMapChosenOrBattleBGM(u8 taskId);
static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move);
static bool8 ShouldSkipFriendshipChange(void);
static void RemoveIVIndexFromList(u8 *ivs, u8 selectedIv);
void TrySpecialOverworldEvo();
@ -4196,7 +4195,7 @@ u16 GiveMoveToMon(struct Pokemon *mon, u16 move)
return GiveMoveToBoxMon(&mon->box, move);
}
static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
{
s32 i;
for (i = 0; i < MAX_MON_MOVES; i++)

View File

@ -172,12 +172,12 @@ void CreateScriptedDoubleWildMon(u16 species1, u8 level1, u16 item1, u16 species
SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, heldItem1);
}
CreateMon(&gEnemyParty[3], species2, level2, 32, 0, 0, OT_ID_PLAYER_ID, 0);
CreateMon(&gEnemyParty[1], species2, level2, 32, 0, 0, OT_ID_PLAYER_ID, 0);
if (item2)
{
heldItem2[0] = item2;
heldItem2[1] = item2 >> 8;
SetMonData(&gEnemyParty[3], MON_DATA_HELD_ITEM, heldItem2);
SetMonData(&gEnemyParty[1], MON_DATA_HELD_ITEM, heldItem2);
}
}

View File

@ -6,7 +6,7 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy")
PASSES_RANDOMLY(91, 100, RNG_ACCURACY);
GIVEN {
ASSUME(gBattleMoves[MOVE_THUNDER].accuracy == 70);
PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); };
PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_THUNDER); }
@ -16,16 +16,13 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy")
}
}
// This fails even though the ability works correctly. The failure is due to
// a statistical anomaly in the test system where FISSURE hits 3 times more often
// than we expect.
SINGLE_BATTLE_TEST("Compound Eyes does not affect OHKO moves")
{
PASSES_RANDOMLY(30, 100, RNG_ACCURACY);
GIVEN {
ASSUME(gBattleMoves[MOVE_FISSURE].accuracy == 30);
ASSUME(gBattleMoves[MOVE_FISSURE].effect == EFFECT_OHKO);
PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); };
PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FISSURE); }

View File

@ -33,10 +33,13 @@ SINGLE_BATTLE_TEST("Dry Skin heals 1/8th Max HP in Rain")
SINGLE_BATTLE_TEST("Dry Skin increases damage taken from Fire-type moves by 25%", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_EFFECT_SPORE; }
PARAMETRIZE { ability = ABILITY_DRY_SKIN; }
GIVEN {
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PARASECT) { Ability(ABILITY_DRY_SKIN); };
OPPONENT(SPECIES_PARASECT) { Ability(ability); };
} WHEN {
TURN {MOVE(player, MOVE_EMBER); }
} SCENE {

View File

@ -0,0 +1,24 @@
#include "global.h"
#include "test_battle.h"
#if B_SNOW_WARNING < GEN_9
SINGLE_BATTLE_TEST("Snow Warning summons hail")
#elif B_SNOW_WARNING >= GEN_9
SINGLE_BATTLE_TEST("Snow Warning summons snow")
#endif
{
GIVEN {
PLAYER(SPECIES_ABOMASNOW) { Ability(ABILITY_SNOW_WARNING); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {}
} SCENE {
#if B_SNOW_WARNING < GEN_9
MESSAGE("It started to hail!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HAIL_CONTINUES);
#elif B_SNOW_WARNING >= GEN_9
MESSAGE("It started to snow!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SNOW_CONTINUES);
#endif
}
}

View File

@ -63,8 +63,9 @@ SINGLE_BATTLE_TEST("Volt Absorb is only triggered once on multi strike moves")
}
}
DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from damaging other pokemon", s16 damage1, s16 damage2) // Fixed issue #1961
DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from damaging other pokemon") // Fixed issue #1961
{
s16 damage1, damage2;
GIVEN {
ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION);
ASSUME(gBattleMoves[MOVE_EXPLOSION].type == TYPE_NORMAL);
@ -78,12 +79,11 @@ DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from dama
ABILITY_POPUP(playerLeft, ABILITY_VOLT_ABSORB);
HP_BAR(playerLeft, hp: TEST_MAX_HP / 4 + 1);
MESSAGE("Jolteon restored HP using its Volt Absorb!");
HP_BAR(playerRight, captureDamage: &results->damage1);
HP_BAR(opponentRight, captureDamage: &results->damage2);
}
FINALLY {
EXPECT_NE(results[0].damage1, 0);
EXPECT_NE(results[0].damage2, 0);
HP_BAR(playerRight, captureDamage: &damage1);
HP_BAR(opponentRight, captureDamage: &damage2);
} THEN {
EXPECT_NE(damage1, 0);
EXPECT_NE(damage2, 0);
}
}

View File

@ -11,14 +11,11 @@ SINGLE_BATTLE_TEST("Berserk Gene sharply raises attack at the start of battle",
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
if (useItem) PASSES_RANDOMLY(66, 100, RNG_CONFUSION);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); };
PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_TACKLE);
}
TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_CONFUSION, FALSE)); }
} SCENE {
if (useItem)
{
@ -38,17 +35,13 @@ SINGLE_BATTLE_TEST("Berserk Gene activates on switch in", s16 damage)
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
if (useItem) PASSES_RANDOMLY(66, 100, RNG_CONFUSION);
GIVEN {
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); };
PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
SWITCH(player, 1);
} TURN {
MOVE(player, MOVE_TACKLE);
}
TURN { SWITCH(player, 1); }
TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_CONFUSION, FALSE)); }
} SCENE {
if (useItem)
{

View File

@ -0,0 +1,49 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB);
}
SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 damage)
{
u32 item;
PARAMETRIZE { item = ITEM_NONE; }
PARAMETRIZE { item = ITEM_MIRROR_HERB; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Item(item); }
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_DANCE); }
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
if (item == ITEM_NONE) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
}
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]);
EXPECT_EQ(player->statStages[STAT_SPEED], opponent->statStages[STAT_SPEED]);
}
}
SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks")
{
GIVEN {
ASSUME(gItems[ITEM_LIECHI_BERRY].holdEffect == HOLD_EFFECT_ATTACK_UP);
PLAYER(SPECIES_SKWOVET) { Item(ITEM_LIECHI_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); }
} WHEN {
TURN { MOVE(player, MOVE_STUFF_CHEEKS); }
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]);
EXPECT_EQ(player->statStages[STAT_DEF], opponent->statStages[STAT_DEF]);
}
}

View File

@ -0,0 +1,339 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Paralyze Heal heals a battler from being paralyzed")
{
GIVEN {
ASSUME(gItems[ITEM_PARALYZE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_PARALYZE_HEAL, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Antidote heals a battler from being poisoned")
{
GIVEN {
ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Antidote heals a battler from being badly poisoned")
{
GIVEN {
ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Awakening heals a battler from being asleep")
{
GIVEN {
ASSUME(gItems[ITEM_AWAKENING].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_AWAKENING, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Burn Heal heals a battler from being burned")
{
GIVEN {
ASSUME(gItems[ITEM_BURN_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_BURN_HEAL, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Ice Heal heals a battler from being paralyzed")
{
GIVEN {
ASSUME(gItems[ITEM_ICE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_ICE_HEAL, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_FULL_HEAL, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_HEAL_POWDER].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_HEAL_POWDER, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_PEWTER_CRUNCHIES].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_PEWTER_CRUNCHIES, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_LAVA_COOKIE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_LAVA_COOKIE, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_RAGE_CANDY_BAR].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_RAGE_CANDY_BAR, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_OLD_GATEAU].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_OLD_GATEAU, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_CASTELIACONE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_CASTELIACONE, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_LUMIOSE_GALETTE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_LUMIOSE_GALETTE, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");;
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_SHALOUR_SABLE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_SHALOUR_SABLE, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_BIG_MALASADA].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { USE_ITEM(player, ITEM_BIG_MALASADA, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Full Heal, Heal Powder and Local Specialties heal a battler from being confused")
{
u16 item;
PARAMETRIZE { item = ITEM_FULL_HEAL; }
PARAMETRIZE { item = ITEM_HEAL_POWDER; }
PARAMETRIZE { item = ITEM_PEWTER_CRUNCHIES; }
PARAMETRIZE { item = ITEM_LAVA_COOKIE; }
PARAMETRIZE { item = ITEM_RAGE_CANDY_BAR; }
PARAMETRIZE { item = ITEM_OLD_GATEAU; }
PARAMETRIZE { item = ITEM_CASTELIACONE; }
PARAMETRIZE { item = ITEM_LUMIOSE_GALETTE; }
PARAMETRIZE { item = ITEM_SHALOUR_SABLE; }
PARAMETRIZE { item = ITEM_BIG_MALASADA; }
GIVEN {
ASSUME(gItems[item].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GENGAR);
} WHEN {
TURN { MOVE(opponent, MOVE_CONFUSE_RAY); }
TURN { USE_ITEM(player, item, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status2, STATUS1_NONE); // because we dont have STATUS2_NONE
}
}

View File

@ -0,0 +1,43 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN{ USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); }
} SCENE {
MESSAGE("Wobbuffet had its HP restored!");
} THEN {
EXPECT_EQ(player->hp, player->maxHP);
EXPECT_EQ(player->status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion")
{
GIVEN {
ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300);};
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN{ MOVE(opponent, MOVE_CONFUSE_RAY); }
TURN{ USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); }
TURN{ MOVE(player, MOVE_TACKLE); }
} SCENE {
MESSAGE("Wobbuffet had its HP restored!");
NONE_OF { MESSAGE("Wobbuffet is confused!"); }
} THEN {
EXPECT_EQ(player->hp, player->maxHP);
}
}

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("X-Attack sharply raises battler's Attack stat", s16 damage)
SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
@ -23,3 +23,231 @@ SINGLE_BATTLE_TEST("X-Attack sharply raises battler's Attack stat", s16 damage)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("X Defense sharply raises battler's Defense stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_X_DEFENSE].battleUsage == EFFECT_ITEM_INCREASE_STAT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_X_DEFENSE); }
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
MESSAGE("Foe Wobbuffet used Tackle!");
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
if (B_X_ITEMS_BUFF >= GEN_7)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
else
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage);
}
}
SINGLE_BATTLE_TEST("X Sp. Atk sharply raises battler's Sp. Attack stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_X_SP_ATK].battleUsage == EFFECT_ITEM_INCREASE_STAT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_X_SP_ATK); }
TURN { MOVE(player, MOVE_DISARMING_VOICE); }
} SCENE {
MESSAGE("Wobbuffet used DisrmngVoice!");
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
if (B_X_ITEMS_BUFF >= GEN_7)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
else
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("X Sp. Def sharply raises battler's Sp. Defense stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_X_SP_DEF].battleUsage == EFFECT_ITEM_INCREASE_STAT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_X_SP_DEF); }
TURN { MOVE(opponent, MOVE_DISARMING_VOICE); }
} SCENE {
MESSAGE("Foe Wobbuffet used DisrmngVoice!");
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
if (B_X_ITEMS_BUFF >= GEN_7)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
else
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage);
}
}
SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_X_SPEED].battleUsage == EFFECT_ITEM_INCREASE_STAT);
if (B_X_ITEMS_BUFF >= GEN_7)
{
PLAYER(SPECIES_WOBBUFFET) { Speed(3); };
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); };
}
else
{
PLAYER(SPECIES_WOBBUFFET) { Speed(4); };
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); };
}
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_X_SPEED); }
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
if (useItem)
{
MESSAGE("Wobbuffet used Tackle!");
MESSAGE("Foe Wobbuffet used Tackle!");
} else
{
MESSAGE("Foe Wobbuffet used Tackle!");
MESSAGE("Wobbuffet used Tackle!");
}
}
}
SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat")
{
ASSUME(gBattleMoves[MOVE_SING].accuracy == 55);
if (B_X_ITEMS_BUFF >= GEN_7)
PASSES_RANDOMLY(gBattleMoves[MOVE_SING].accuracy * 5 / 3, 100, RNG_ACCURACY);
else
PASSES_RANDOMLY(gBattleMoves[MOVE_SING].accuracy * 4 / 3, 100, RNG_ACCURACY);
GIVEN {
ASSUME(gItems[ITEM_X_ACCURACY].battleUsage == EFFECT_ITEM_INCREASE_STAT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_X_ACCURACY); }
TURN { MOVE(player, MOVE_SING); }
} SCENE {
MESSAGE("Wobbuffet used Sing!");
MESSAGE("Foe Wobbuffet fell asleep!");
}
}
SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Attack stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
MESSAGE("Wobbuffet used Tackle!");
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("Max Mushrooms raises battler's Defense stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); }
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
MESSAGE("Foe Wobbuffet used Tackle!");
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Sp. Attack stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); }
TURN { MOVE(player, MOVE_DISARMING_VOICE); }
} SCENE {
MESSAGE("Wobbuffet used DisrmngVoice!");
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("Max Mushrooms battler's Sp. Defense stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); }
TURN { MOVE(opponent, MOVE_DISARMING_VOICE); }
} SCENE {
MESSAGE("Foe Wobbuffet used DisrmngVoice!");
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Speed stat", s16 damage)
{
u16 useItem;
PARAMETRIZE { useItem = FALSE; }
PARAMETRIZE { useItem = TRUE; }
GIVEN {
ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS);
PLAYER(SPECIES_WOBBUFFET) { Speed(4); };
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); };
} WHEN {
if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); }
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
if (useItem)
{
MESSAGE("Wobbuffet used Tackle!");
MESSAGE("Foe Wobbuffet used Tackle!");
} else
{
MESSAGE("Foe Wobbuffet used Tackle!");
MESSAGE("Wobbuffet used Tackle!");
}
}
}

View File

@ -1,37 +1,186 @@
#include "global.h"
#include "test_battle.h"
#define TEST_HP 1
#define MAX_HP 400
SINGLE_BATTLE_TEST("Potion restores a battler's HP by 20")
{
s16 damage;
GIVEN {
ASSUME(gItems[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); }
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_POTION, partyIndex: 0); }
} SCENE {
HP_BAR(player, captureDamage: &damage);
} FINALLY {
EXPECT_EQ(damage, -20);
HP_BAR(player, hp: TEST_HP + 20);
}
}
SINGLE_BATTLE_TEST("Sitrus Berry restores a battler's HP")
SINGLE_BATTLE_TEST("Super Potion restores a battler's HP by 60")
{
GIVEN {
ASSUME(gItems[ITEM_SUPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_SUPER_POTION, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 60);
}
}
SINGLE_BATTLE_TEST("Hyper Potion restores a battler's HP by 120")
{
GIVEN {
ASSUME(gItems[ITEM_HYPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_HYPER_POTION, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 120);
}
}
SINGLE_BATTLE_TEST("Max Potion restores a battler's HP fully")
{
GIVEN {
ASSUME(gItems[ITEM_MAX_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_MAX_POTION, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: MAX_HP);
}
}
SINGLE_BATTLE_TEST("Fresh Water restores a battler's HP by 30")
{
GIVEN {
ASSUME(gItems[ITEM_FRESH_WATER].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_FRESH_WATER, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 30);
}
}
SINGLE_BATTLE_TEST("Soda Pop restores a battler's HP by 50")
{
GIVEN {
ASSUME(gItems[ITEM_SODA_POP].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_SODA_POP, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 50);
}
}
SINGLE_BATTLE_TEST("Lemonade restores a battler's HP by 70")
{
GIVEN {
ASSUME(gItems[ITEM_LEMONADE].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_LEMONADE, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 70);
}
}
SINGLE_BATTLE_TEST("Moomoo Milk restores a battler's HP by 100")
{
GIVEN {
ASSUME(gItems[ITEM_MOOMOO_MILK].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_MOOMOO_MILK, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 100);
}
}
SINGLE_BATTLE_TEST("Energy Powder restores a battler's HP by 60(50)")
{
GIVEN {
ASSUME(gItems[ITEM_ENERGY_POWDER].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_ENERGY_POWDER, partyIndex: 0); }
} SCENE {
if (I_HEALTH_RECOVERY >= GEN_7)
HP_BAR(player, hp: TEST_HP + 60);
else
HP_BAR(player, hp: TEST_HP + 50);
}
}
SINGLE_BATTLE_TEST("Energy Root restores a battler's HP by 120(200)")
{
GIVEN {
ASSUME(gItems[ITEM_ENERGY_ROOT].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_ENERGY_ROOT, partyIndex: 0); }
} SCENE {
if (I_HEALTH_RECOVERY >= GEN_7)
HP_BAR(player, hp: TEST_HP + 120);
else
HP_BAR(player, hp: TEST_HP + 200);
}
}
SINGLE_BATTLE_TEST("Sweet Heart restores a battler's HP by 20")
{
GIVEN {
ASSUME(gItems[ITEM_SWEET_HEART].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_SWEET_HEART, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 20);
}
}
SINGLE_BATTLE_TEST("Oran Berry restores a battler's HP by 10")
{
GIVEN {
ASSUME(gItems[ITEM_ORAN_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_ORAN_BERRY, partyIndex: 0); }
} SCENE {
HP_BAR(player, hp: TEST_HP + 10);
}
}
SINGLE_BATTLE_TEST("Sitrus Berry restores a battler's HP by 25% of its max HP(30HP flat)")
{
s16 damage;
GIVEN {
ASSUME(gItems[ITEM_SITRUS_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); }
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_SITRUS_BERRY, partyIndex: 0); }
} SCENE {
HP_BAR(player, captureDamage: &damage);
} FINALLY {
if (I_SITRUS_BERRY_HEAL >= GEN_4)
EXPECT_EQ(damage, -25);
HP_BAR(player, hp: TEST_HP + MAX_HP * 0.25);
else
EXPECT_EQ(damage, -30);
HP_BAR(player, hp: TEST_HP + 30);
}
}
#undef TEST_HP
#undef MAX_HP

View File

@ -1,19 +1,66 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Ether restores the PP of one of a battler's moves")
SINGLE_BATTLE_TEST("Ether restores the PP of one of a battler's moves by 10 ")
{
GIVEN {
ASSUME(gItems[ITEM_ETHER].battleUsage == EFFECT_ITEM_RESTORE_PP);
ASSUME(gItems[ITEM_ETHER].type == ITEM_USE_PARTY_MENU_MOVES);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE, MOVE_CONFUSION); }
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 20}); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
TURN { MOVE(player, MOVE_CONFUSION); }
TURN { USE_ITEM(player, ITEM_ETHER, partyIndex: 0, move: MOVE_TACKLE); }
} FINALLY {
EXPECT_EQ(player->pp[0], 35);
EXPECT_EQ(player->pp[1], 24);
} THEN {
EXPECT_EQ(player->pp[0], 10);
EXPECT_EQ(player->pp[1], 20);
}
}
SINGLE_BATTLE_TEST("Max Ether restores the PP of one of a battler's moves fully")
{
GIVEN {
ASSUME(gItems[ITEM_MAX_ETHER].battleUsage == EFFECT_ITEM_RESTORE_PP);
ASSUME(gItems[ITEM_MAX_ETHER].type == ITEM_USE_PARTY_MENU_MOVES);
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 20}); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_MAX_ETHER, partyIndex: 0, move: MOVE_TACKLE); }
} THEN {
EXPECT_EQ(player->pp[0], 35);
EXPECT_EQ(player->pp[1], 20);
}
}
SINGLE_BATTLE_TEST("Elixir restores the PP of all of a battler's moves by 10")
{
GIVEN {
ASSUME(gItems[ITEM_ELIXIR].battleUsage == EFFECT_ITEM_RESTORE_PP);
ASSUME(gItems[ITEM_ELIXIR].type == ITEM_USE_PARTY_MENU);
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 0}, {MOVE_SCRATCH, 0}, {MOVE_GROWL, 0}); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_ELIXIR, partyIndex: 0); }
} THEN {
EXPECT_EQ(player->pp[0], 10);
EXPECT_EQ(player->pp[1], 10);
EXPECT_EQ(player->pp[2], 10);
EXPECT_EQ(player->pp[3], 10);
}
}
SINGLE_BATTLE_TEST("Max Elixir restores the PP of all of a battler's moves fully")
{
GIVEN {
ASSUME(gItems[ITEM_MAX_ELIXIR].battleUsage == EFFECT_ITEM_RESTORE_PP);
ASSUME(gItems[ITEM_MAX_ELIXIR].type == ITEM_USE_PARTY_MENU);
PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 0}, {MOVE_SCRATCH, 0}, {MOVE_GROWL, 0}); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_MAX_ELIXIR, partyIndex: 0); }
} THEN {
EXPECT_EQ(player->pp[0], 35);
EXPECT_EQ(player->pp[1], 25);
EXPECT_EQ(player->pp[2], 35);
EXPECT_EQ(player->pp[3], 40);
}
}

78
test/item_effect_revive.c Normal file
View File

@ -0,0 +1,78 @@
#include "global.h"
#include "test_battle.h"
#define MAX_HP 200
SINGLE_BATTLE_TEST("Revive restores a fainted battler's HP to half")
{
GIVEN {
ASSUME(gItems[ITEM_REVIVE].battleUsage == EFFECT_ITEM_REVIVE);
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
TURN { USE_ITEM(player, ITEM_REVIVE, partyIndex: 0); }
TURN { SWITCH(player, 0); }
} SCENE {
MESSAGE("Wynaut had its HP restored!");
} THEN {
EXPECT_EQ(player->hp, MAX_HP/2);
}
}
SINGLE_BATTLE_TEST("Max Revive restores a fainted battler's HP fully")
{
GIVEN {
ASSUME(gItems[ITEM_MAX_REVIVE].battleUsage == EFFECT_ITEM_REVIVE);
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
TURN { USE_ITEM(player, ITEM_MAX_REVIVE, partyIndex: 0); }
TURN { SWITCH(player, 0); }
} SCENE {
MESSAGE("Wynaut had its HP restored!");
} THEN {
EXPECT_EQ(player->hp, MAX_HP);
}
}
SINGLE_BATTLE_TEST("Revival Herb restores a fainted battler's HP fully")
{
GIVEN {
ASSUME(gItems[ITEM_REVIVAL_HERB].battleUsage == EFFECT_ITEM_REVIVE);
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
TURN { USE_ITEM(player, ITEM_REVIVAL_HERB, partyIndex: 0); }
TURN { SWITCH(player, 0); }
} SCENE {
MESSAGE("Wynaut had its HP restored!");
} THEN {
EXPECT_EQ(player->hp, MAX_HP);
}
}
SINGLE_BATTLE_TEST("Max Honey restores a fainted battler's HP fully")
{
GIVEN {
ASSUME(gItems[ITEM_MAX_HONEY].battleUsage == EFFECT_ITEM_REVIVE);
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
TURN { USE_ITEM(player, ITEM_MAX_HONEY, partyIndex: 0); }
TURN { SWITCH(player, 0); }
} SCENE {
MESSAGE("Wynaut had its HP restored!");
} THEN {
EXPECT_EQ(player->hp, MAX_HP);
}
}
#undef MAX_HP

View File

@ -0,0 +1,21 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stages")
{
ASSUME(B_CRIT_CHANCE >= GEN_7);
PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT);
GIVEN {
ASSUME(gItems[ITEM_DIRE_HIT].battleUsage == EFFECT_ITEM_SET_FOCUS_ENERGY);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_DIRE_HIT, partyIndex: 0); }
TURN { MOVE(player, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player);
MESSAGE("Wobbuffet used Dire Hit to get pumped!");
MESSAGE("Wobbuffet used Scratch!");
MESSAGE("A critical hit!");
}
}

View File

@ -0,0 +1,19 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Guard Spec. sets Mist effect on the battlers side")
{
GIVEN {
ASSUME(gItems[ITEM_GUARD_SPEC].battleUsage == EFFECT_ITEM_SET_MIST);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { USE_ITEM(player, ITEM_GUARD_SPEC, partyIndex: 0); }
TURN { MOVE(opponent, MOVE_GROWL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIST, player);
MESSAGE("Ally became shrouded in MIST!");
MESSAGE("Foe Wobbuffet used Growl!");
MESSAGE("Wobbuffet is protected by MIST!");
}
}

View File

@ -17,6 +17,44 @@ SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite")
}
}
DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - opponent faster")
{
GIVEN {
PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3);}
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(3);}
OPPONENT(SPECIES_WOBBUFFET) { Speed(4);}
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); }
} SCENE {
MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentLeft);
MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!");
MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft);
MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!");
}
}
DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - player faster")
{
GIVEN {
PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); Speed(5); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3);}
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2);}
OPPONENT(SPECIES_WOBBUFFET) { Speed(4);}
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); }
} SCENE {
MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft);
MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!");
MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentLeft);
MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!");
}
}
SINGLE_BATTLE_TEST("Rayquaza can Mega Evolve knowing Dragon Ascent")
{
GIVEN {
@ -66,3 +104,52 @@ SINGLE_BATTLE_TEST("Abilities replaced by Mega Evolution do not affect turn orde
ASSUME(player->speed == 45);
}
}
DOUBLE_BATTLE_TEST("Mega Evolution happens after switching, but before Focus Punch-like Moves")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_FOCUS_PUNCH].effect == EFFECT_FOCUS_PUNCH);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE);}
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_FOCUS_PUNCH, megaEvolve: TRUE, target:opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target:opponentLeft); }
TURN {};
} SCENE {
MESSAGE("2 withdrew Wobbuffet!");
MESSAGE("2 sent out Wobbuffet!");
MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerRight);
MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerRight);
MESSAGE("Venusaur is tightening its focus!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerLeft);
MESSAGE("Wobbuffet is tightening its focus!");
}
}
SINGLE_BATTLE_TEST("Regular Mega Evolution and Fervent Wish Mega Evolution can happen on the same turn")
{
GIVEN {
PLAYER(SPECIES_RAYQUAZA) { Moves(MOVE_DRAGON_ASCENT, MOVE_CELEBRATE); Speed(3);}
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2);}
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(opponent, 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!");
MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!");
} THEN {
EXPECT_EQ(player->species, SPECIES_RAYQUAZA_MEGA);
EXPECT_EQ(opponent->species, SPECIES_GARDEVOIR_MEGA);
}
}

View File

@ -0,0 +1,113 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_BEAK_BLAST].effect == EFFECT_BEAK_BLAST);
}
DOUBLE_BATTLE_TEST("Beak Blast's charging message is shown before other moves are used")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_BEAK_BLAST].priority < 0);
PLAYER(SPECIES_WYNAUT) {Speed(10); }
PLAYER(SPECIES_WOBBUFFET) {Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) {Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) {Speed(3); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BEAK_BLAST, target:opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, playerLeft);
MESSAGE("Wynaut started heating up its beak!");
MESSAGE("Wobbuffet used Celebrate!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
MESSAGE("Foe Wobbuffet used Celebrate!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight);
MESSAGE("Foe Wobbuffet used Celebrate!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
MESSAGE("Wynaut used Beak Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAK_BLAST, playerLeft);
HP_BAR(opponentLeft);
}
}
DOUBLE_BATTLE_TEST("Beak Blast burns all who make contact with the pokemon")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_BEAK_BLAST].priority < 0);
ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT);
PLAYER(SPECIES_WYNAUT) {Speed(10); }
PLAYER(SPECIES_WOBBUFFET) {Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) {Speed(3); }
OPPONENT(SPECIES_WOBBUFFET) {Speed(2); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_TACKLE, target:playerLeft); MOVE(opponentRight, MOVE_TACKLE, target:playerLeft); MOVE(playerLeft, MOVE_BEAK_BLAST, target:opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, playerLeft);
MESSAGE("Wynaut started heating up its beak!");
MESSAGE("Wobbuffet used Celebrate!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
MESSAGE("Foe Wobbuffet used Tackle!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
HP_BAR(playerLeft);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentLeft);
MESSAGE("Foe Wobbuffet was burned!");
STATUS_ICON(opponentLeft, burn: TRUE);
MESSAGE("Foe Wobbuffet used Tackle!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight);
HP_BAR(playerLeft);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentRight);
MESSAGE("Foe Wobbuffet was burned!");
STATUS_ICON(opponentRight, burn: TRUE);
MESSAGE("Wynaut used Beak Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAK_BLAST, playerLeft);
HP_BAR(opponentLeft);
}
}
SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
{
u32 move;
bool32 burn;
PARAMETRIZE { move = MOVE_TACKLE; burn = TRUE; }
PARAMETRIZE { move = MOVE_WATER_GUN; burn = FALSE; }
PARAMETRIZE { move = MOVE_LEER; burn = FALSE; }
GIVEN {
ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT);
ASSUME(!(gBattleMoves[MOVE_WATER_GUN].flags & FLAG_MAKES_CONTACT));
ASSUME(!(gBattleMoves[MOVE_LEER].flags & FLAG_MAKES_CONTACT));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BEAK_BLAST); MOVE(opponent, move); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player);
MESSAGE("Wobbuffet started heating up its beak!");
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (burn) {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
MESSAGE("Foe Wobbuffet was burned!");
STATUS_ICON(opponent, burn: TRUE);
}
else {
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
MESSAGE("Foe Wobbuffet was burned!");
STATUS_ICON(opponent, burn: TRUE);
}
}
MESSAGE("Wobbuffet used Beak Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAK_BLAST, player);
HP_BAR(opponent);
}
}

View File

@ -28,7 +28,7 @@ SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns")
MESSAGE("Wobbuffet unleashed energy!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
HP_BAR(opponent, captureDamage: &bideDamage);
} FINALLY {
EXPECT_EQ(bideDamage, damage1 + damage2);
} THEN {
EXPECT_EQ(bideDamage, 2 * (damage1 + damage2));
}
}

View File

@ -0,0 +1,76 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_FOCUS_PUNCH].effect == EFFECT_FOCUS_PUNCH);
}
SINGLE_BATTLE_TEST("Focus Punch activates only if not damaged")
{
u32 move;
bool32 activate;
PARAMETRIZE { move = MOVE_TACKLE; activate = FALSE; }
PARAMETRIZE { move = MOVE_WATER_GUN; activate = FALSE; }
PARAMETRIZE { move = MOVE_LEER; activate = TRUE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FOCUS_PUNCH); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, player);
MESSAGE("Wobbuffet is tightening its focus!");
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (activate) {
MESSAGE("Wobbuffet used Focus Punch!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, player);
HP_BAR(opponent);
} else {
MESSAGE("Wobbuffet lost its focus and couldn't move!");
NONE_OF {
MESSAGE("Wobbuffet used Focus Punch!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, player);
HP_BAR(opponent);
}
}
}
}
DOUBLE_BATTLE_TEST("Focus Punch activation is based on Speed")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) {Speed(2) ;}
PLAYER(SPECIES_WYNAUT) {Speed(3) ;}
OPPONENT(SPECIES_WOBBUFFET) {Speed(1) ;}
OPPONENT(SPECIES_WYNAUT) {Speed(5) ;}
} WHEN {
TURN { MOVE(opponentRight, MOVE_FOCUS_PUNCH, target:playerLeft); MOVE(playerRight, MOVE_FOCUS_PUNCH, target:opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target:opponentLeft); MOVE(opponentLeft, MOVE_FOCUS_PUNCH, target:playerLeft); }
}
SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, opponentRight);
MESSAGE("Foe Wynaut is tightening its focus!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerRight);
MESSAGE("Wynaut is tightening its focus!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerLeft);
MESSAGE("Wobbuffet is tightening its focus!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, opponentLeft);
MESSAGE("Foe Wobbuffet is tightening its focus!");
MESSAGE("Foe Wynaut used Focus Punch!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, opponentRight);
HP_BAR(playerLeft);
MESSAGE("Wynaut used Focus Punch!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, playerRight);
HP_BAR(opponentLeft);
MESSAGE("Wobbuffet lost its focus and couldn't move!");
MESSAGE("Foe Wobbuffet lost its focus and couldn't move!");
}
}

View File

@ -6,7 +6,7 @@ ASSUMPTIONS
ASSUME(gBattleMoves[MOVE_TRIPLE_KICK].effect & EFFECT_TRIPLE_KICK);
}
SINGLE_BATTLE_TEST("Triple Kick damage is increaased by its base damage for each hit")
SINGLE_BATTLE_TEST("Triple Kick damage is increased by its base damage for each hit")
{
s16 firstHit;
s16 secondHit;
@ -24,8 +24,8 @@ SINGLE_BATTLE_TEST("Triple Kick damage is increaased by its base damage for each
HP_BAR(opponent, captureDamage: &secondHit);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_KICK, player);
HP_BAR(opponent, captureDamage: &thirdHit);
} FINALLY {
EXPECT_EQ(secondHit, firstHit * 2);
EXPECT_EQ(thirdHit, firstHit * 3);
} THEN {
EXPECT_MUL_EQ(firstHit, Q_4_12(2.0), secondHit);
EXPECT_MUL_EQ(firstHit, Q_4_12(3.0), thirdHit);
}
}

View File

@ -20,7 +20,7 @@ SINGLE_BATTLE_TEST("Three-strike flag turns a move into a 3-hit move")
HP_BAR(opponent, captureDamage: &secondHit);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_DIVE, player);
HP_BAR(opponent, captureDamage: &thirdHit);
} FINALLY {
} THEN {
EXPECT_EQ(firstHit, secondHit);
EXPECT_EQ(secondHit, thirdHit);
EXPECT_EQ(firstHit, thirdHit);
@ -49,7 +49,7 @@ SINGLE_BATTLE_TEST("Surging Strikes hits 3 times with each hit being a critical
ANIMATION(ANIM_TYPE_MOVE, MOVE_SURGING_STRIKES, player);
HP_BAR(opponent, captureDamage: &thirdHit);
MESSAGE("A critical hit!");
} FINALLY {
} THEN {
EXPECT_EQ(firstHit, secondHit);
EXPECT_EQ(secondHit, thirdHit);
EXPECT_EQ(firstHit, thirdHit);

View File

@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Electric Terrain activates Electric Seed and Mimicry")
MESSAGE("Using Electric Seed, the Defense of Wobbuffet rose!");
ABILITY_POPUP(opponent);
MESSAGE("Foe Stunfisk's type changed to Electr!");
} FINALLY {
} THEN {
EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_ELECTRIC);
}
}

View File

@ -30,7 +30,7 @@ SINGLE_BATTLE_TEST("Grassy Terrain activates Grassy Seed and Mimicry")
MESSAGE("Using Grassy Seed, the Defense of Wobbuffet rose!");
ABILITY_POPUP(opponent);
MESSAGE("Foe Stunfisk's type changed to Grass!");
} FINALLY {
} THEN {
EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_GRASS);
}
}

View File

@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Misty Terrain activates Misty Seed and Mimicry")
MESSAGE("Using Misty Seed, the Sp. Def of Wobbuffet rose!");
ABILITY_POPUP(opponent);
MESSAGE("Foe Stunfisk's type changed to Fairy!");
} FINALLY {
} THEN {
EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_FAIRY);
}
}

View File

@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain activates Psychic Seed and Mimicry")
MESSAGE("Using Psychic Seed, the Sp. Def of Wobbuffet rose!");
ABILITY_POPUP(opponent);
MESSAGE("Foe Stunfisk's type changed to Psychc!");
} FINALLY {
} THEN {
EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_PSYCHIC);
}
}
@ -61,7 +61,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain increases power of Psychic-type moves by 30/
}
}
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user", s16 damage)
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user")
{
GIVEN {
PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); HP(1); }
@ -76,7 +76,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the
}
}
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers", s16 damage)
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers")
{
KNOWN_FAILING;
GIVEN {
@ -91,7 +91,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all
}
}
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents", s16 damage)
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents")
{
KNOWN_FAILING;
GIVEN {
@ -106,7 +106,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all
}
}
DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target allies", s16 damage)
DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target allies")
{
GIVEN {
PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); }
@ -122,7 +122,7 @@ DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all
}
}
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves", s16 damage)
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves")
{
KNOWN_FAILING;
GIVEN {

View File

@ -46,6 +46,7 @@ struct TestRunnerState
u8 result;
u8 expectedResult;
bool8 expectLeaks:1;
u32 timeoutSeconds;
};
@ -69,6 +70,7 @@ extern struct TestRunnerState gTestRunnerState;
void CB2_TestRunner(void);
void Test_ExpectedResult(enum TestResult);
void Test_ExpectLeaks(bool32);
void Test_ExitWithResult(enum TestResult, const char *fmt, ...);
s32 MgbaPrintf_(const char *fmt, ...);
@ -160,6 +162,9 @@ s32 MgbaPrintf_(const char *fmt, ...);
#define KNOWN_FAILING \
Test_ExpectedResult(TEST_RESULT_FAIL)
#define KNOWN_LEAKING \
Test_ExpectLeaks(TRUE)
#define PARAMETRIZE if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter)
#define TO_DO \

View File

@ -369,7 +369,7 @@
* s16 damage;
* HP_BAR(player, captureDamage: &damage);
* If none of the above are used, causes the test to fail if the HP
* changes at all.
* does not change at all.
*
* MESSAGE(pattern)
* Causes the test to fail if the message in pattern is not displayed.
@ -614,6 +614,7 @@ struct BattleTestRunnerState
bool8 runThen:1;
bool8 runFinally:1;
bool8 runningFinally:1;
bool8 tearDownBattle:1;
struct BattleTestData data;
u8 *results;
u8 checkProgressParameter;
@ -712,6 +713,11 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext);
/* Given */
struct moveWithPP {
u16 moveId;
u8 pp;
};
#define GIVEN for (; gBattleTestRunnerState->runGiven; gBattleTestRunnerState->runGiven = FALSE)
#define RNGSeed(seed) RNGSeed_(__LINE__, seed)
@ -732,6 +738,7 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext);
#define Speed(speed) Speed_(__LINE__, speed)
#define Item(item) Item_(__LINE__, item)
#define Moves(move1, ...) Moves_(__LINE__, (const u16 [MAX_MON_MOVES]) { move1, __VA_ARGS__ })
#define MovesWithPP(movewithpp1, ...) MovesWithPP_(__LINE__, (struct moveWithPP[MAX_MON_MOVES]) {movewithpp1, __VA_ARGS__})
#define Friendship(friendship) Friendship_(__LINE__, friendship)
#define Status1(status1) Status1_(__LINE__, status1)
@ -752,6 +759,7 @@ void SpDefense_(u32 sourceLine, u32 spDefense);
void Speed_(u32 sourceLine, u32 speed);
void Item_(u32 sourceLine, u32 item);
void Moves_(u32 sourceLine, const u16 moves[MAX_MON_MOVES]);
void MovesWithPP_(u32 sourceLine, struct moveWithPP moveWithPP[MAX_MON_MOVES]);
void Friendship_(u32 sourceLine, u32 friendship);
void Status1_(u32 sourceLine, u32 status1);
@ -889,7 +897,9 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve
/* Finally */
#define FINALLY for (; gBattleTestRunnerState->runFinally; gBattleTestRunnerState->runFinally = FALSE) if ((gBattleTestRunnerState->runningFinally = TRUE))
#define FINALLY for (ValidateFinally(__LINE__); gBattleTestRunnerState->runFinally; gBattleTestRunnerState->runFinally = FALSE) if ((gBattleTestRunnerState->runningFinally = TRUE))
void ValidateFinally(u32 sourceLine);
/* Expect */

View File

@ -42,7 +42,14 @@ static bool32 PrefixMatch(const char *pattern, const char *string)
}
}
enum { STATE_INIT, STATE_NEXT_TEST, STATE_REPORT_RESULT, STATE_EXIT };
enum
{
STATE_INIT,
STATE_NEXT_TEST,
STATE_RUN_TEST,
STATE_REPORT_RESULT,
STATE_EXIT,
};
void CB2_TestRunner(void)
{
@ -81,6 +88,26 @@ void CB2_TestRunner(void)
return;
}
MgbaPrintf_(":N%s", gTestRunnerState.test->name);
gTestRunnerState.result = TEST_RESULT_PASS;
gTestRunnerState.expectedResult = TEST_RESULT_PASS;
gTestRunnerState.expectLeaks = FALSE;
if (gTestRunnerHeadless)
gTestRunnerState.timeoutSeconds = TIMEOUT_SECONDS;
else
gTestRunnerState.timeoutSeconds = UINT_MAX;
InitHeap(gHeap, HEAP_SIZE);
EnableInterrupts(INTR_FLAG_TIMER2);
REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second.
REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK;
// NOTE: Assumes that the compiler interns __FILE__.
if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename)
{
gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL;
return;
}
// Greedily assign tests to processes based on estimated cost.
// TODO: Make processCosts a min heap.
if (gTestRunnerState.test->runner != &gAssumptionsRunner)
@ -98,39 +125,26 @@ void CB2_TestRunner(void)
}
}
if (minCostProcess == gTestRunnerI)
gTestRunnerState.state = STATE_RUN_TEST;
else
gTestRunnerState.state = STATE_NEXT_TEST;
// XXX: If estimateCost exits only on some processes then
// processCosts will be inconsistent.
if (gTestRunnerState.test->runner->estimateCost)
gTestRunnerState.processCosts[minCostProcess] += gTestRunnerState.test->runner->estimateCost(gTestRunnerState.test->data);
else
gTestRunnerState.processCosts[minCostProcess] += 1;
if (minCostProcess != gTestRunnerI)
return;
}
MgbaPrintf_(":N%s", gTestRunnerState.test->name);
break;
case STATE_RUN_TEST:
gTestRunnerState.state = STATE_REPORT_RESULT;
gTestRunnerState.result = TEST_RESULT_PASS;
gTestRunnerState.expectedResult = TEST_RESULT_PASS;
if (gTestRunnerHeadless)
gTestRunnerState.timeoutSeconds = TIMEOUT_SECONDS;
else
gTestRunnerState.timeoutSeconds = UINT_MAX;
InitHeap(gHeap, HEAP_SIZE);
EnableInterrupts(INTR_FLAG_TIMER2);
REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second.
REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK;
// NOTE: Assumes that the compiler interns __FILE__.
if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename)
{
gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL;
}
else
{
if (gTestRunnerState.test->runner->setUp)
gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data);
gTestRunnerState.test->runner->run(gTestRunnerState.test->data);
}
if (gTestRunnerState.test->runner->setUp)
gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data);
gTestRunnerState.test->runner->run(gTestRunnerState.test->data);
break;
case STATE_REPORT_RESULT:
@ -141,6 +155,26 @@ void CB2_TestRunner(void)
if (gTestRunnerState.test->runner->tearDown)
gTestRunnerState.test->runner->tearDown(gTestRunnerState.test->data);
if (!gTestRunnerState.expectLeaks)
{
const struct MemBlock *head = HeapHead();
const struct MemBlock *block = head;
do
{
if (block->allocated)
{
const char *location = MemBlockLocation(block);
if (location)
MgbaPrintf_("%s: %d bytes not freed", location, block->size);
else
MgbaPrintf_("<unknown>: %d bytes not freed", block->size);
gTestRunnerState.result = TEST_RESULT_FAIL;
}
block = block->next;
}
while (block != head);
}
if (gTestRunnerState.test->runner == &gAssumptionsRunner)
{
if (gTestRunnerState.result != TEST_RESULT_PASS)
@ -238,6 +272,11 @@ void Test_ExpectedResult(enum TestResult result)
gTestRunnerState.expectedResult = result;
}
void Test_ExpectLeaks(bool32 expectLeaks)
{
gTestRunnerState.expectLeaks = expectLeaks;
}
static void FunctionTest_SetUp(void *data)
{
(void)data;
@ -319,6 +358,8 @@ static void Intr_Timer2(void)
}
else
{
if (gTestRunnerState.state == STATE_RUN_TEST)
gTestRunnerState.state = STATE_REPORT_RESULT;
gTestRunnerState.result = TEST_RESULT_TIMEOUT;
ReinitCallbacks();
IRQ_LR = ((uintptr_t)JumpToAgbMainLoop & ~1) + 4;
@ -328,16 +369,19 @@ static void Intr_Timer2(void)
void Test_ExitWithResult(enum TestResult result, const char *fmt, ...)
{
bool32 handled = FALSE;
gTestRunnerState.result = result;
ReinitCallbacks();
if (gTestRunnerState.test->runner->handleExitWithResult)
handled = gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result);
if (!handled && gTestRunnerState.result != gTestRunnerState.expectedResult)
if (gTestRunnerState.state == STATE_REPORT_RESULT
&& gTestRunnerState.test->runner->handleExitWithResult)
{
va_list va;
va_start(va, fmt);
MgbaVPrintf_(fmt, va);
if (!gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result)
&& gTestRunnerState.result != gTestRunnerState.expectedResult)
{
va_list va;
va_start(va, fmt);
MgbaVPrintf_(fmt, va);
va_end(va);
}
}
JumpToAgbMainLoop();
}

View File

@ -134,6 +134,8 @@ static void BattleTest_SetUp(void *data)
Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE = AllocZerod(%d)", sizeof(*STATE));
InvokeTestFunction(test);
STATE->parameters = STATE->parametersCount;
if (STATE->parametersCount == 0 && test->resultsSize > 0)
Test_ExitWithResult(TEST_RESULT_INVALID, "results without PARAMETRIZE");
STATE->results = AllocZeroed(test->resultsSize * STATE->parameters);
if (!STATE->results)
Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE->results = AllocZerod(%d)", sizeof(test->resultsSize * STATE->parameters));
@ -893,6 +895,15 @@ static void BattleTest_TearDown(void *data)
{
if (STATE)
{
// Free resources that aren't cleaned up when the battle was
// aborted unexpectedly.
if (STATE->tearDownBattle)
{
FreeMonSpritesGfx();
FreeBattleSpritesData();
FreeBattleResources();
FreeAllWindowBuffers();
}
FREE_AND_SET_NULL(STATE->results);
FREE_AND_SET_NULL(STATE);
}
@ -923,12 +934,15 @@ static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result
}
else
{
STATE->tearDownBattle = TRUE;
return FALSE;
}
}
void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx)
{
const struct BattleTest *test = gTestRunnerState.test->data;
INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results");
INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials);
STATE->rngTag = ctx.tag;
STATE->runTrial = 0;
@ -1164,6 +1178,21 @@ void Moves_(u32 sourceLine, const u16 moves[MAX_MON_MOVES])
DATA.explicitMoves[DATA.currentSide] |= 1 << DATA.currentPartyIndex;
}
void MovesWithPP_(u32 sourceLine, struct moveWithPP moveWithPP[MAX_MON_MOVES])
{
s32 i;
INVALID_IF(!DATA.currentMon, "Moves outside of PLAYER/OPPONENT");
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moveWithPP[i].moveId == MOVE_NONE)
break;
INVALID_IF(moveWithPP[i].moveId >= MOVES_COUNT, "Illegal move: %d", &moveWithPP[i].moveId);
SetMonData(DATA.currentMon, MON_DATA_MOVE1 + i, &moveWithPP[i].moveId);
SetMonData(DATA.currentMon, MON_DATA_PP1 + i, &moveWithPP[i].pp);
}
DATA.explicitMoves[DATA.currentSide] |= 1 << DATA.currentPartyIndex;
}
void Friendship_(u32 sourceLine, u32 friendship)
{
INVALID_IF(!DATA.currentMon, "Friendship outside of PLAYER/OPPONENT");
@ -1539,13 +1568,15 @@ void UseItem(u32 sourceLine, struct BattlePokemon *battler, struct ItemContext c
}
INVALID_IF(i == MAX_MON_MOVES, "USE_ITEM on invalid move: %d", ctx.move);
}
else
{
i = 0;
}
PushBattlerAction(sourceLine, battlerId, RECORDED_ACTION_TYPE, B_ACTION_USE_ITEM);
PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, (ctx.itemId >> 8) & 0xFF);
PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, ctx.itemId & 0xFF);
if (ctx.explicitPartyIndex)
gBattleStruct->itemPartyIndex[battlerId] = ctx.partyIndex;
if (ctx.explicitMove)
gBattleStruct->itemPartyIndex[battlerId] = i;
PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, ctx.partyIndex);
PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_MOVE, i);
DATA.actionBattlers |= 1 << battlerId;
}
@ -1696,7 +1727,6 @@ void QueueMessage(u32 sourceLine, const u8 *pattern)
};
}
void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEventContext ctx)
{
s32 battlerId = battler - gBattleMons;
@ -1736,3 +1766,11 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve
}},
};
}
void ValidateFinally(u32 sourceLine)
{
// Defer this error until after estimating the cost.
if (STATE->results == NULL)
return;
INVALID_IF(STATE->parametersCount == 0, "FINALLY without PARAMETRIZE");
}

157
test/weather_snow.c Normal file
View File

@ -0,0 +1,157 @@
#include "global.h"
#include "test_battle.h"
#define TEST_HP 1
#define MAX_HP 400
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_ICE && gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_ICE);
ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE || gSpeciesInfo[SPECIES_GLALIE].types[1] == TYPE_ICE);
}
SINGLE_BATTLE_TEST("Snow increases the defense of Ice types by 50 %", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
PARAMETRIZE{ move = MOVE_CELEBRATE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GLALIE);
} WHEN {
TURN { MOVE(opponent, move); }
TURN { MOVE(player, MOVE_TACKLE); }
} 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("Snow turns Weather Ball to an Ice-type move and doubles its power", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
GIVEN {
ASSUME(gBattleMoves[MOVE_WEATHER_BALL].effect == EFFECT_WEATHER_BALL);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_DRAGONAIR);
} WHEN {
TURN { MOVE(player, move); }
TURN { MOVE(player, MOVE_WEATHER_BALL); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.0), results[1].damage); // double base power + type effectiveness.
}
}
SINGLE_BATTLE_TEST("Snow allows Blizzard to bypass accuracy checks")
{
PASSES_RANDOMLY(100, 100);
GIVEN {
ASSUME(gBattleMoves[MOVE_BLIZZARD].accuracy == 70);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_BLIZZARD); }
} SCENE {
NONE_OF { MESSAGE("Wobbuffet's attack missed!"); }
}
}
SINGLE_BATTLE_TEST("Snow halves the power of Solar Beam", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
GIVEN {
ASSUME(gBattleMoves[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Snow halves the power of Solar Blade", s16 damage)
{
u16 move;
KNOWN_FAILING; // fails bc the bp of solar blade gets rounded up which leads to slightly incorrect calcs down the line
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
GIVEN {
ASSUME(gBattleMoves[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); }
TURN { SKIP_TURN(player); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Snow causes Moonlight to recover 1/4 of the user's max HP")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_MOONLIGHT].effect == EFFECT_MOONLIGHT);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MOONLIGHT); }
} SCENE {
HP_BAR(player, hp: TEST_HP + (MAX_HP/4));
}
}
SINGLE_BATTLE_TEST("Snow causes Moonlight to recover 1/4 of the user's max HP")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_MOONLIGHT].effect == EFFECT_MOONLIGHT);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MOONLIGHT); }
} SCENE {
HP_BAR(player, hp: TEST_HP + (MAX_HP/4));
}
}
SINGLE_BATTLE_TEST("Snow causes Synthesis to recover 1/4 of the user's max HP")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_SYNTHESIS].effect == EFFECT_SYNTHESIS);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_SYNTHESIS); }
} SCENE {
HP_BAR(player, hp: TEST_HP + (MAX_HP/4));
}
}
SINGLE_BATTLE_TEST("Snow causes Morning Sun to recover 1/4 of the user's max HP")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_MORNING_SUN].effect == EFFECT_MORNING_SUN);
PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MORNING_SUN); }
} SCENE {
HP_BAR(player, hp: TEST_HP + (MAX_HP/4));
}
}
#undef MAX_HP
#undef TEST_HP

View File

@ -75,6 +75,9 @@ string json_to_string(const Json &data, const string &field = "", bool silent =
case Json::Type::BOOL:
output = value.bool_value() ? "TRUE" : "FALSE";
break;
case Json::Type::NUL:
output = "";
break;
default:{
if (!silent) {
string s = !field.empty() ? ("Value for '" + field + "'") : "JSON field";
@ -461,26 +464,27 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) {
for (auto &group : groups_data["group_order"].array_items()) {
string groupName = json_to_string(group);
text << "// " << groupName << "\n";
vector<Json> map_ids;
vector<string> map_ids;
size_t max_length = 0;
int map_count = 0; //DEBUG
for (auto &map_name : groups_data[groupName].array_items()) {
string header_filepath = file_dir + json_to_string(map_name) + dir_separator + "map.json";
string map_filepath = file_dir + json_to_string(map_name) + dir_separator + "map.json";
string err_str;
Json map_data = Json::parse(read_text_file(header_filepath), err_str);
map_ids.push_back(map_data["id"]);
string id = json_to_string(map_data, "id");
Json map_data = Json::parse(read_text_file(map_filepath), err_str);
if (map_data == Json())
FATAL_ERROR("%s: %s\n", map_filepath.c_str(), err_str.c_str());
string id = json_to_string(map_data, "id", true);
map_ids.push_back(id);
if (id.length() > max_length)
max_length = id.length();
map_count++; //DEBUG
}
int map_id_num = 0;
for (Json map_id : map_ids) {
string id = json_to_string(map_id);
text << "#define " << id << string((max_length - id.length() + 1), ' ')
for (string map_id : map_ids) {
text << "#define " << map_id << string((max_length - map_id.length() + 1), ' ')
<< "(" << map_id_num++ << " | (" << group_num << " << 8))\n";
}
text << "\n";