mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 11:44:17 +01:00
Merge branch 'upcoming' into RHH/content/gen9SnowWeather
This commit is contained in:
commit
fe5e59c910
2
Makefile
2
Makefile
@ -480,7 +480,7 @@ endif
|
||||
check: $(TESTELF)
|
||||
@cp $< $(HEADLESSELF)
|
||||
$(PATCHELF) $(HEADLESSELF) gTestRunnerHeadless '\x01' gTestRunnerSkipIsFail "$(TEST_SKIP_IS_FAIL)"
|
||||
$(ROMTESTHYDRA) $(ROMTEST) $(HEADLESSELF)
|
||||
$(ROMTESTHYDRA) $(ROMTEST) $(OBJCOPY) $(HEADLESSELF)
|
||||
|
||||
libagbsyscall:
|
||||
@$(MAKE) -C libagbsyscall TOOLCHAIN=$(TOOLCHAIN) MODERN=$(MODERN)
|
||||
|
@ -453,7 +453,7 @@ BattleScript_StealthRockActivates::
|
||||
setstealthrock BattleScript_MoveEnd
|
||||
printfromtable gDmgHazardsStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
goto BattleScript_MoveEnd
|
||||
return
|
||||
|
||||
BattleScript_EffectDireClaw::
|
||||
setmoveeffect MOVE_EFFECT_DIRE_CLAW
|
||||
@ -467,7 +467,7 @@ BattleScript_SpikesActivates::
|
||||
trysetspikes BattleScript_MoveEnd
|
||||
printfromtable gDmgHazardsStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
goto BattleScript_MoveEnd
|
||||
return
|
||||
|
||||
BattleScript_EffectAttackUpUserAlly:
|
||||
jumpifnoally BS_ATTACKER, BattleScript_EffectAttackUp
|
||||
@ -10034,23 +10034,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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -147,6 +147,7 @@ struct ProtectStruct
|
||||
u16 quash:1;
|
||||
u16 shellTrap:1;
|
||||
u16 silkTrapped:1;
|
||||
u16 eatMirrorHerb:1;
|
||||
u32 physicalDmg;
|
||||
u32 specialDmg;
|
||||
u8 physicalBattlerId;
|
||||
@ -657,6 +658,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;
|
||||
|
@ -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[];
|
||||
|
@ -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.
|
||||
|
@ -659,12 +659,13 @@
|
||||
#define STRINGID_PKMNFROSTBITEHEALED 657
|
||||
#define STRINGID_PKMNFROSTBITEHEALED2 658
|
||||
#define STRINGID_PKMNFROSTBITEHEALEDBY 659
|
||||
#define STRINGID_STARTEDSNOW 660
|
||||
#define STRINGID_SNOWCONTINUES 661
|
||||
#define STRINGID_SNOWSTOPPED 662
|
||||
#define STRINGID_SNOWWARNINGSNOW 663
|
||||
#define STRINGID_MIRRORHERBCOPIED 660
|
||||
#define STRINGID_STARTEDSNOW 661
|
||||
#define STRINGID_SNOWCONTINUES 662
|
||||
#define STRINGID_SNOWSTOPPED 663
|
||||
#define STRINGID_SNOWWARNINGSNOW 664
|
||||
|
||||
#define BATTLESTRINGS_COUNT 664
|
||||
#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,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
@ -45,6 +45,8 @@ enum
|
||||
RECORDED_PARTY_INDEX,
|
||||
RECORDED_BATTLE_PALACE_ACTION,
|
||||
RECORDED_ITEM_ID,
|
||||
RECORDED_ITEM_TARGET,
|
||||
RECORDED_ITEM_MOVE,
|
||||
};
|
||||
|
||||
extern u32 gRecordedBattleRngSeed;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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!");
|
||||
@ -798,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,
|
||||
|
@ -12132,6 +12132,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16531,16 +16545,20 @@ void BS_ItemIncreaseStat(void) {
|
||||
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.
|
||||
@ -16551,7 +16569,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);
|
||||
|
@ -6808,6 +6808,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;
|
||||
@ -7011,6 +7031,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;
|
||||
@ -7575,6 +7598,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)
|
||||
@ -9283,7 +9309,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b
|
||||
#if B_PLUS_MINUS_INTERACTION >= GEN_5
|
||||
case ABILITY_PLUS:
|
||||
case ABILITY_MINUS:
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)))
|
||||
if (IS_MOVE_SPECIAL(move) && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)))
|
||||
{
|
||||
u32 partnerAbility = GetBattlerAbility(BATTLE_PARTNER(battlerAtk));
|
||||
if (partnerAbility == ABILITY_PLUS || partnerAbility == ABILITY_MINUS)
|
||||
@ -9292,11 +9318,11 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b
|
||||
break;
|
||||
#else
|
||||
case ABILITY_PLUS:
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_MINUS)
|
||||
if (IS_MOVE_SPECIAL(move) && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_MINUS)
|
||||
MulModifier(&modifier, UQ_4_12(1.5));
|
||||
break;
|
||||
case ABILITY_MINUS:
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_PLUS)
|
||||
if (IS_MOVE_SPECIAL(move) && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_PLUS)
|
||||
MulModifier(&modifier, UQ_4_12(1.5));
|
||||
break;
|
||||
#endif
|
||||
|
@ -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,
|
||||
},
|
||||
|
||||
|
199
src/daycare.c
199
src/daycare.c
@ -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);
|
||||
|
||||
|
@ -4944,7 +4944,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);
|
||||
|
@ -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++)
|
||||
|
@ -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); }
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
49
test/hold_effect_mirror_herb.c
Normal file
49
test/hold_effect_mirror_herb.c
Normal 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]);
|
||||
}
|
||||
}
|
339
test/item_effect_cure_status.c
Normal file
339
test/item_effect_cure_status.c
Normal 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
|
||||
}
|
||||
}
|
43
test/item_effect_heal_and_cure_status.c
Normal file
43
test/item_effect_heal_and_cure_status.c
Normal 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);
|
||||
}
|
||||
}
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
78
test/item_effect_revive.c
Normal 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
|
21
test/item_effect_set_focus_energy.c
Normal file
21
test/item_effect_set_focus_energy.c
Normal 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!");
|
||||
}
|
||||
}
|
19
test/item_effect_set_mist.c
Normal file
19
test/item_effect_set_mist.c
Normal 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!");
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 \
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -23,7 +23,9 @@
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#ifndef __APPLE__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
@ -176,9 +178,9 @@ static void exit2(int _)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
if (argc < 4)
|
||||
{
|
||||
fprintf(stderr, "usage %s mgba-rom-test rom\n", argv[0]);
|
||||
fprintf(stderr, "usage %s mgba-rom-test objcopy rom\n", argv[0]);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
@ -205,7 +207,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
int elffd;
|
||||
if ((elffd = open(argv[2], O_RDONLY)) == -1)
|
||||
if ((elffd = open(argv[3], O_RDONLY)) == -1)
|
||||
{
|
||||
perror("open elffd failed");
|
||||
exit(2);
|
||||
@ -264,11 +266,13 @@ int main(int argc, char *argv[])
|
||||
perror("fork mgba-rom-test failed");
|
||||
exit(2);
|
||||
} else if (pid == 0) {
|
||||
#ifndef __APPLE__
|
||||
if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1)
|
||||
{
|
||||
perror("prctl failed");
|
||||
_exit(2);
|
||||
}
|
||||
#endif
|
||||
if (getppid() != parent_pid) // Parent died.
|
||||
{
|
||||
_exit(2);
|
||||
@ -332,6 +336,36 @@ int main(int argc, char *argv[])
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
pid_t objcopypid = fork();
|
||||
if (objcopypid == -1)
|
||||
{
|
||||
perror("fork objcopy failed");
|
||||
_exit(2);
|
||||
}
|
||||
else if (objcopypid == 0)
|
||||
{
|
||||
if (execlp(argv[2], argv[2], "-O", "binary", rom_path, rom_path, NULL) == -1)
|
||||
{
|
||||
perror("execlp objcopy failed");
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int wstatus;
|
||||
if (waitpid(objcopypid, &wstatus, 0) == -1)
|
||||
{
|
||||
perror("waitpid objcopy failed");
|
||||
_exit(2);
|
||||
}
|
||||
if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
|
||||
{
|
||||
fprintf(stderr, "objcopy exited with an error\n");
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// stdbuf is required because otherwise mgba never flushes
|
||||
// stdout.
|
||||
if (execlp("stdbuf", "stdbuf", "-oL", argv[1], "-l15", "-ClogLevel.gba.dma=16", "-Rr0", rom_path, NULL) == -1)
|
||||
|
3147
tools/patchelf/elf.h
Normal file
3147
tools/patchelf/elf.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
#include <ctype.h>
|
||||
#include <elf.h>
|
||||
#include "elf.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
|
Loading…
Reference in New Issue
Block a user