Merge branch '_RHH/master' into _RHH/upcoming
This commit is contained in:
commit
afa7ab9b2e
14 changed files with 3451 additions and 45 deletions
|
@ -9280,8 +9280,8 @@ gBattleAnimMove_ThousandArrows::
|
|||
playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER
|
||||
waitforvisualfinish
|
||||
delay 0x30
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
|
||||
call ThousandArrowsDown
|
||||
call ThousandArrowsDown
|
||||
call ThousandArrowsDown
|
||||
|
@ -9481,8 +9481,8 @@ gBattleAnimMove_LandsWrath::
|
|||
fadetobg BG_FISSURE
|
||||
waitbgfadeout
|
||||
playsewithpan SE_M_EARTHQUAKE, 0x0
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
|
||||
createvisualtask AnimTask_PositionFissureBgOnBattler, 5, ANIM_TARGET, 5, -1
|
||||
waitbgfadein
|
||||
waitforvisualfinish
|
||||
|
@ -9649,8 +9649,8 @@ gBattleAnimMove_PrecipiceBlades::
|
|||
fadetobg 0x15
|
||||
waitbgfadeout
|
||||
createvisualtask AnimTask_PositionFissureBgOnBattler, 5, ANIM_TARGET, 5, -1
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x5
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x5
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x5
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x5
|
||||
playsewithpan SE_M_EARTHQUAKE, 0x0
|
||||
createvisualtask AnimTask_IsTargetPlayerSide, 0x2
|
||||
jumpargeq 0x7, ANIM_TARGET, PrecipiceBladesOpponent
|
||||
|
@ -11761,8 +11761,8 @@ gBattleAnimMove_StompingTantrum::
|
|||
monbg ANIM_TARGET
|
||||
createvisualtask AnimTask_Splash, 2, ANIM_ATTACKER, 3
|
||||
delay 0x5
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x5
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x5
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x5
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x5
|
||||
createsprite gStompingTantrumRockTemplate, ANIM_ATTACKER, 2, 0x0, 0x0
|
||||
createsprite gStompingTantrumRockTemplate, ANIM_ATTACKER, 2, 0x0, 0x1
|
||||
createsprite gStompingTantrumRockTemplate, ANIM_ATTACKER, 2, 0x0, 0x2
|
||||
|
@ -11897,8 +11897,8 @@ gBattleAnimMove_PrismaticLaser::
|
|||
unloadspritegfx ANIM_TAG_ICE_CHUNK
|
||||
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
|
||||
delay 0x1E
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
|
||||
playsewithpan SE_M_SOLAR_BEAM, SOUND_PAN_ATTACKER
|
||||
call PrismaticLaserRain
|
||||
call PrismaticLaserRain
|
||||
|
@ -16579,8 +16579,8 @@ gBattleAnimMove_GigatonHammer::
|
|||
delay 18
|
||||
createvisualtask AnimTask_SquishTarget, 0x2
|
||||
delay 6
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 50
|
||||
call GigatonHammerImpact
|
||||
waitforvisualfinish
|
||||
end
|
||||
|
@ -20282,8 +20282,8 @@ gBattleAnimMove_LowKick::
|
|||
end
|
||||
|
||||
gBattleAnimMove_Earthquake::
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 50
|
||||
playsewithpan SE_M_EARTHQUAKE, 0
|
||||
delay 10
|
||||
createsprite gComplexPaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 3, 1, RGB_BLACK, 14, RGB_WHITE, 14
|
||||
|
@ -20293,8 +20293,8 @@ gBattleAnimMove_Earthquake::
|
|||
|
||||
gBattleAnimMove_Fissure::
|
||||
loadspritegfx ANIM_TAG_MUD_SAND
|
||||
createvisualtask AnimTask_HorizontalShake, 3, ANIM_PLAYER_RIGHT, 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 3, ANIM_TARGET, 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 50
|
||||
playsewithpan SE_M_EARTHQUAKE, SOUND_PAN_TARGET
|
||||
delay 8
|
||||
call FissureDirtPlumeFar
|
||||
|
@ -21020,13 +21020,13 @@ gBattleAnimMove_Magnitude::
|
|||
MagnitudeEnd:
|
||||
end
|
||||
MagnitudeRegular:
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 0, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 0, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 0, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 0, 50
|
||||
loopsewithpan SE_M_STRENGTH, SOUND_PAN_TARGET, 8, 10
|
||||
goto MagnitudeEnd
|
||||
MagnitudeIntense:
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 0, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 0, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 0, 50
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 0, 50
|
||||
loopsewithpan SE_M_STRENGTH, SOUND_PAN_TARGET, 8, 10
|
||||
delay 10
|
||||
createsprite gComplexPaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 3, 1, RGB_BLACK, 14, RGB_WHITE, 14
|
||||
|
@ -21499,8 +21499,8 @@ gBattleAnimMove_Eruption::
|
|||
createsprite gEruptionFallingRockSpriteTemplate, ANIM_ATTACKER, 40, 110, -32, 64, 50, 0
|
||||
createsprite gEruptionFallingRockSpriteTemplate, ANIM_ATTACKER, 40, 60, -32, 80, 70, 1
|
||||
delay 22
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 8, 60
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 8, 60
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 8, 60
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 8, 60
|
||||
loopsewithpan SE_M_ROCK_THROW, SOUND_PAN_TARGET, 16, 12
|
||||
delay 80
|
||||
createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 40, F_PAL_BG | F_PAL_BATTLERS, 4, 4, 0, RGB_RED
|
||||
|
@ -21530,7 +21530,7 @@ gBattleAnimMove_Imprison::
|
|||
waitforvisualfinish
|
||||
delay 4
|
||||
createsprite gRedXSpriteTemplate, ANIM_ATTACKER, 5, ANIM_ATTACKER, 40
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 1, 10
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 1, 10
|
||||
playsewithpan SE_M_HYPER_BEAM, SOUND_PAN_ATTACKER
|
||||
clearmonbg ANIM_DEF_PARTNER
|
||||
call UnsetPsychicBg
|
||||
|
@ -29004,8 +29004,8 @@ gBattleAnimMove_BreakneckBlitz::
|
|||
loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
|
||||
loadspritegfx ANIM_TAG_METEOR
|
||||
loadspritegfx ANIM_TAG_FLAT_ROCK
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_RIGHT, 5, 32 @ shake screen
|
||||
createvisualtask AnimTask_HorizontalShake, 5, ANIM_PLAYER_LEFT, 5, 32 @ shake banks
|
||||
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 5, 32 @ shake screen
|
||||
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 5, 32 @ shake banks
|
||||
playsewithpan SE_M_EARTHQUAKE, 0x0
|
||||
monbg ANIM_ATTACKER
|
||||
splitbgprio ANIM_ATTACKER
|
||||
|
|
|
@ -45,5 +45,6 @@ void SE12PanpotControl(s8 pan);
|
|||
bool8 IsSEPlaying(void);
|
||||
bool8 IsBGMPlaying(void);
|
||||
bool8 IsSpecialSEPlaying(void);
|
||||
void Task_DuckBGMForPokemonCry(u8 taskId);
|
||||
|
||||
#endif // GUARD_SOUND_H
|
||||
|
|
|
@ -356,13 +356,14 @@ const struct SpriteTemplate gSkyAttackBirdSpriteTemplate =
|
|||
.callback = AnimSkyAttackBird,
|
||||
};
|
||||
|
||||
// same as AnimEllipticalGust but centered on targets
|
||||
// same as AnimEllipticalGust but centered on targets in a double battle
|
||||
static void AnimEllipticalGustCentered(struct Sprite *sprite)
|
||||
{
|
||||
if (IsDoubleBattle())
|
||||
InitSpritePosToAnimTargetsCentre(sprite, FALSE);
|
||||
else
|
||||
InitSpritePosToAnimTarget(sprite, FALSE);
|
||||
|
||||
sprite->y += 20;
|
||||
sprite->data[1] = 191;
|
||||
sprite->callback = AnimEllipticalGust_Step;
|
||||
|
|
|
@ -1772,7 +1772,20 @@ void CB2_QuitRecordedBattle(void)
|
|||
m4aMPlayStop(&gMPlayInfo_SE1);
|
||||
m4aMPlayStop(&gMPlayInfo_SE2);
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
// Clean up potentially-leaking tasks.
|
||||
// I think these leak when the battle ends soon after a
|
||||
// battler is fainted.
|
||||
u8 taskId;
|
||||
taskId = FindTaskIdByFunc(Task_PlayerController_RestoreBgmAfterCry);
|
||||
if (taskId != TASK_NONE)
|
||||
DestroyTask(taskId);
|
||||
taskId = FindTaskIdByFunc(Task_DuckBGMForPokemonCry);
|
||||
if (taskId != TASK_NONE)
|
||||
DestroyTask(taskId);
|
||||
|
||||
TestRunner_Battle_AfterLastTurn();
|
||||
}
|
||||
FreeRestoreBattleData();
|
||||
FreeAllWindowBuffers();
|
||||
SetMainCallback2(gMain.savedCallback);
|
||||
|
|
|
@ -5132,7 +5132,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
break;
|
||||
case ABILITY_SHED_SKIN:
|
||||
if ((gBattleMons[battler].status1 & STATUS1_ANY)
|
||||
&& (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_SHED_SKIN, 30) : RandomChance(RNG_SHED_SKIN, 1, 3)))
|
||||
&& (B_ABILITY_TRIGGER_CHANCE == GEN_4 ? RandomPercentage(RNG_SHED_SKIN, 30) : RandomChance(RNG_SHED_SKIN, 1, 3)))
|
||||
{
|
||||
ABILITY_HEAL_MON_STATUS:
|
||||
if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON))
|
||||
|
|
|
@ -18792,7 +18792,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
|
|||
.type = TYPE_FAIRY,
|
||||
.accuracy = 80,
|
||||
.pp = 5,
|
||||
.target = MOVE_TARGET_SELECTED,
|
||||
.target = MOVE_TARGET_BOTH,
|
||||
.priority = 0,
|
||||
.category = DAMAGE_CATEGORY_SPECIAL,
|
||||
.windMove = TRUE,
|
||||
|
|
|
@ -460,7 +460,8 @@ struct PokedexView
|
|||
u16 maxScrollTimer;
|
||||
u16 scrollSpeed;
|
||||
u16 unkArr1[4]; // Cleared, never read
|
||||
u8 filler[8];
|
||||
u16 originalSearchSelectionNum;
|
||||
u8 filler[6];
|
||||
u8 currentPage;
|
||||
u8 currentPageBackup;
|
||||
bool8 isSearchResults:1;
|
||||
|
@ -2139,6 +2140,7 @@ static void ResetPokedexView(struct PokedexView *pokedexView)
|
|||
pokedexView->unkArr2[i] = 0;
|
||||
for (i = 0; i < ARRAY_COUNT(pokedexView->unkArr3); i++)
|
||||
pokedexView->unkArr3[i] = 0;
|
||||
pokedexView->originalSearchSelectionNum = 0;
|
||||
}
|
||||
|
||||
static void VBlankCB_Pokedex(void)
|
||||
|
@ -2438,6 +2440,13 @@ static bool8 LoadPokedexListPage(u8 page)
|
|||
case 3:
|
||||
if (page == PAGE_MAIN)
|
||||
CreatePokedexList(sPokedexView->dexMode, sPokedexView->dexOrder);
|
||||
if (sPokedexView->originalSearchSelectionNum != 0)
|
||||
{
|
||||
// when returning to search results after selecting an evo, we have to restore
|
||||
// the original dexNum because the search results page doesn't rebuild the list
|
||||
sPokedexListItem->dexNum = sPokedexView->originalSearchSelectionNum;
|
||||
sPokedexView->originalSearchSelectionNum = 0;
|
||||
}
|
||||
CreateMonSpritesAtPos(sPokedexView->selectedPokemon, 0xE);
|
||||
sPokedexView->statBarsSpriteId = 0xFF; //stat bars
|
||||
CreateStatBars(&sPokedexView->pokedexList[sPokedexView->selectedPokemon]); //stat bars
|
||||
|
@ -6146,6 +6155,9 @@ static void Task_HandleEvolutionScreenInput(u8 taskId)
|
|||
{
|
||||
u16 targetSpecies = sPokedexView->sEvoScreenData.targetSpecies[sPokedexView->sEvoScreenData.menuPos];
|
||||
u16 dexNum = SpeciesToNationalPokedexNum(targetSpecies);
|
||||
if (sPokedexView->isSearchResults && sPokedexView->originalSearchSelectionNum == 0)
|
||||
sPokedexView->originalSearchSelectionNum = sPokedexListItem->dexNum;
|
||||
|
||||
sPokedexListItem->dexNum = dexNum;
|
||||
sPokedexListItem->seen = GetSetPokedexFlag(dexNum, FLAG_GET_SEEN);
|
||||
sPokedexListItem->owned = GetSetPokedexFlag(dexNum, FLAG_GET_CAUGHT);
|
||||
|
@ -6881,6 +6893,9 @@ static void Task_HandleFormsScreenInput(u8 taskId)
|
|||
{
|
||||
u8 formId = sPokedexView->sFormScreenData.formIds[menuPos];
|
||||
u16 formSpecies = GetFormSpeciesId(NationalPokedexNumToSpecies(sPokedexListItem->dexNum), formId);
|
||||
if (sPokedexView->isSearchResults && sPokedexView->originalSearchSelectionNum == 0)
|
||||
sPokedexView->originalSearchSelectionNum = sPokedexListItem->dexNum;
|
||||
|
||||
if (formSpecies == GetFormSpeciesId(formSpecies, 0))
|
||||
sPokedexView->formSpecies = 0;
|
||||
else
|
||||
|
|
|
@ -31,7 +31,6 @@ extern struct ToneData gCryTable_Reverse[];
|
|||
|
||||
static void Task_Fanfare(u8 taskId);
|
||||
static void CreateFanfareTask(void);
|
||||
static void Task_DuckBGMForPokemonCry(u8 taskId);
|
||||
static void RestoreBGMVolumeAfterPokemonCry(void);
|
||||
|
||||
static const struct Fanfare sFanfares[] = {
|
||||
|
@ -513,7 +512,7 @@ bool8 IsCryPlaying(void)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void Task_DuckBGMForPokemonCry(u8 taskId)
|
||||
void Task_DuckBGMForPokemonCry(u8 taskId)
|
||||
{
|
||||
if (gPokemonCryBGMDuckingCounter)
|
||||
{
|
||||
|
|
28
test/battle/ability/guard_dog.c
Normal file
28
test/battle/ability/guard_dog.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Guard Dog raises Attack when intimidated", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
PARAMETRIZE { ability = ABILITY_INTIMIDATE; }
|
||||
PARAMETRIZE { ability = ABILITY_SHED_SKIN; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_OKIDOGI) { Ability(ABILITY_GUARD_DOG); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ARBOK) { Ability(ability); }
|
||||
} WHEN {
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_INTIMIDATE)
|
||||
{
|
||||
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
|
||||
ABILITY_POPUP(player, ABILITY_GUARD_DOG);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Okidogi's Attack rose!");
|
||||
}
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[0].damage);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Shed Skin triggers 30% of the time")
|
||||
SINGLE_BATTLE_TEST("Shed Skin triggers 33% of the time")
|
||||
{
|
||||
PASSES_RANDOMLY(3, 10, RNG_SHED_SKIN);
|
||||
if (B_ABILITY_TRIGGER_CHANCE == GEN_4)
|
||||
PASSES_RANDOMLY(30, 100, RNG_SHED_SKIN);
|
||||
else
|
||||
PASSES_RANDOMLY(33, 100, RNG_SHED_SKIN);
|
||||
GIVEN {
|
||||
ASSUME(B_ABILITY_TRIGGER_CHANCE >= GEN_4);
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].makesContact);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ARBOK) { Status1(STATUS1_POISON); Ability(ABILITY_SHED_SKIN); }
|
||||
|
|
23
test/battle/ability/steam_engine.c
Normal file
23
test/battle/ability/steam_engine.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Steam Engine raises speed when hit by a Fire or Water move")
|
||||
{
|
||||
u16 move;
|
||||
|
||||
PARAMETRIZE { move = MOVE_EMBER; }
|
||||
PARAMETRIZE { move = MOVE_WATER_GUN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_COALOSSAL) { Ability(ABILITY_STEAM_ENGINE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, move); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_STEAM_ENGINE);
|
||||
MESSAGE("Coalossal's Speed drastically rose!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 6);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include "main.h"
|
||||
#include "malloc.h"
|
||||
#include "random.h"
|
||||
#include "task.h"
|
||||
#include "constants/characters.h"
|
||||
#include "test_runner.h"
|
||||
#include "test/test.h"
|
||||
|
@ -190,6 +191,7 @@ top:
|
|||
else
|
||||
gTestRunnerState.timeoutSeconds = UINT_MAX;
|
||||
InitHeap(gHeap, HEAP_SIZE);
|
||||
ResetTasks();
|
||||
EnableInterrupts(INTR_FLAG_TIMER2);
|
||||
REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second.
|
||||
REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK;
|
||||
|
@ -243,6 +245,7 @@ top:
|
|||
if (gTestRunnerState.result == TEST_RESULT_PASS
|
||||
&& !gTestRunnerState.expectLeaks)
|
||||
{
|
||||
int i;
|
||||
const struct MemBlock *head = HeapHead();
|
||||
const struct MemBlock *block = head;
|
||||
do
|
||||
|
@ -251,7 +254,7 @@ top:
|
|||
|| !(EWRAM_START <= (uintptr_t)block->next && (uintptr_t)block->next < EWRAM_END)
|
||||
|| (block->next <= block && block->next != head))
|
||||
{
|
||||
Test_MgbaPrintf("gHeap corrupted block at 0x%p", block);
|
||||
Test_MgbaPrintf("gHeap corrupted block at %p", block);
|
||||
gTestRunnerState.result = TEST_RESULT_ERROR;
|
||||
break;
|
||||
}
|
||||
|
@ -268,6 +271,15 @@ top:
|
|||
block = block->next;
|
||||
}
|
||||
while (block != head);
|
||||
|
||||
for (i = 0; i < NUM_TASKS; i++)
|
||||
{
|
||||
if (gTasks[i].isActive)
|
||||
{
|
||||
Test_MgbaPrintf("%p: task not freed", gTasks[i].func);
|
||||
gTestRunnerState.result = TEST_RESULT_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gTestRunnerState.test->runner == &gAssumptionsRunner)
|
||||
|
@ -585,6 +597,9 @@ static s32 MgbaVPrintf_(const char *fmt, va_list va)
|
|||
p = va_arg(va, unsigned);
|
||||
{
|
||||
s32 n;
|
||||
i = MgbaPutchar_(i, '<');
|
||||
i = MgbaPutchar_(i, '0');
|
||||
i = MgbaPutchar_(i, 'x');
|
||||
for (n = 0; n < 7; n++)
|
||||
{
|
||||
unsigned nybble = (p >> (24 - (4*n))) & 0xF;
|
||||
|
@ -593,6 +608,7 @@ static s32 MgbaVPrintf_(const char *fmt, va_list va)
|
|||
else
|
||||
i = MgbaPutchar_(i, 'a' + nybble - 10);
|
||||
}
|
||||
i = MgbaPutchar_(i, '>');
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
|
|
3147
tools/mgba-rom-test-hydra/elf.h
Normal file
3147
tools/mgba-rom-test-hydra/elf.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -21,6 +21,7 @@
|
|||
#include <regex.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "elf.h"
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
|
@ -69,10 +71,103 @@ struct Runner
|
|||
char assumeFailed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
|
||||
};
|
||||
|
||||
struct Symbol {
|
||||
const char *name;
|
||||
uint32_t address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct SymbolTable {
|
||||
struct Symbol *symbols;
|
||||
size_t symbols_n;
|
||||
};
|
||||
|
||||
static unsigned nrunners = 0;
|
||||
static unsigned runners_digits = 0;
|
||||
static struct Runner *runners = NULL;
|
||||
|
||||
// TODO: Build the symbol table on demand.
|
||||
static struct SymbolTable symbol_table = { NULL, 0 };
|
||||
|
||||
static const struct Symbol *lookup_address(uint32_t address)
|
||||
{
|
||||
int lo = 0, hi = symbol_table.symbols_n;
|
||||
while (lo < hi)
|
||||
{
|
||||
int mi = lo + (hi - lo) / 2;
|
||||
const struct Symbol *symbol = &symbol_table.symbols[mi];
|
||||
if (address < symbol->address)
|
||||
hi = mi;
|
||||
else if (address >= symbol->address + symbol->size)
|
||||
lo = mi + 1;
|
||||
else
|
||||
return symbol;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Similar to 'fwrite(buffer, 1, size, f)' except that anything which
|
||||
// looks like the output of '%p' (i.e. '<0x\d{7}>') is translated into
|
||||
// the name of a symbol (if it represents one).
|
||||
static void fprint_buffer(FILE *f, const char *buffer, size_t size)
|
||||
{
|
||||
const char *buffer_end = buffer + size;
|
||||
while (buffer < buffer_end)
|
||||
{
|
||||
// Find the next '<0x'.
|
||||
char *buffer_ = memmem(buffer, buffer_end - buffer, "<0x", 3);
|
||||
|
||||
// No '<0x' or could not possibly match, print everything.
|
||||
if (buffer_ == NULL || buffer_end - buffer_ < 11 || buffer_[10] != '>')
|
||||
{
|
||||
fwrite(buffer, 1, buffer_end - buffer, f);
|
||||
break;
|
||||
}
|
||||
|
||||
// Print everything before the '<0x'.
|
||||
fwrite(buffer, 1, buffer_ - buffer, f);
|
||||
buffer = buffer_;
|
||||
|
||||
unsigned long address = strtoul(buffer + 3, &buffer_, 16);
|
||||
// Un-mirror EWRAM/IWRAM/ROM addresses.
|
||||
switch (address & 0xF000000)
|
||||
{
|
||||
case 0x2000000: address = address & 0x203FFFF; break;
|
||||
case 0x3000000: address = address & 0x3007FFF; break;
|
||||
case 0x7000000: address = address & 0x70003FF; break;
|
||||
case 0xA000000: address = address & 0x9FFFFFF; break;
|
||||
case 0xB000000: address = address & 0x9FFFFFF; break;
|
||||
case 0xC000000: address = address & 0x9FFFFFF; break;
|
||||
case 0xD000000: address = address & 0x9FFFFFF; break;
|
||||
}
|
||||
|
||||
// Not a 7-digit address, print the '<0x' part and loop.
|
||||
if (buffer_ != buffer + 10)
|
||||
{
|
||||
fwrite(buffer, 1, 3, f);
|
||||
buffer += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
const struct Symbol *symbol = lookup_address(address);
|
||||
|
||||
// Not a symbol, print the parsed part and loop.
|
||||
if (symbol == NULL)
|
||||
{
|
||||
fwrite(buffer, 1, 11, f);
|
||||
buffer += 11;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol->address == address)
|
||||
fprintf(f, "<%s>", symbol->name);
|
||||
else
|
||||
fprintf(f, "<%s+0x%lx>", symbol->name, address - symbol->address);
|
||||
|
||||
buffer += 11;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_read(int i, struct Runner *runner)
|
||||
{
|
||||
char *sol = runner->input_buffer;
|
||||
|
@ -156,7 +251,7 @@ add_to_results:
|
|||
soc += 2;
|
||||
fprintf(stdout, "[%0*d] %s: ", runners_digits, i, runner->test_name);
|
||||
fwrite(soc, 1, eol - soc, stdout);
|
||||
fwrite(runner->output_buffer, 1, runner->output_buffer_size, stdout);
|
||||
fprint_buffer(stdout, runner->output_buffer, runner->output_buffer_size);
|
||||
strcpy(runner->test_name, "WAITING...");
|
||||
runner->output_buffer_size = 0;
|
||||
break;
|
||||
|
@ -186,11 +281,7 @@ buffer_output:
|
|||
}
|
||||
else
|
||||
{
|
||||
if (write(STDOUT_FILENO, sol, eol - sol) == -1)
|
||||
{
|
||||
perror("write failed");
|
||||
exit(2);
|
||||
}
|
||||
fwrite(sol, 1, eol - sol, stdout);
|
||||
}
|
||||
sol += n;
|
||||
consumed += n;
|
||||
|
@ -233,12 +324,80 @@ static void exit2(int _)
|
|||
exit(2);
|
||||
}
|
||||
|
||||
int compare_strings(const void * a, const void * b)
|
||||
static int compare_addresses(const void *a, const void *b)
|
||||
{
|
||||
const char *arg1 = (const char *) a;
|
||||
const char *arg2 = (const char *) b;
|
||||
const struct Symbol *sa = a, *sb = b;
|
||||
if (sa->address < sb->address)
|
||||
return -1;
|
||||
else if (sa->address == sb->address)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return strcmp(arg1, arg2);
|
||||
static void build_symbol_table(void *elf)
|
||||
{
|
||||
if (memcmp(elf, ELFMAG, 4) != 0)
|
||||
goto error;
|
||||
|
||||
size_t symbol_table_symbols_c = 1024;
|
||||
symbol_table.symbols = malloc(symbol_table_symbols_c * sizeof(*symbol_table.symbols));
|
||||
if (symbol_table.symbols == NULL)
|
||||
goto error;
|
||||
|
||||
const Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elf;
|
||||
const Elf32_Shdr *shdrs = (Elf32_Shdr *)(elf + ehdr->e_shoff);
|
||||
|
||||
if (ehdr->e_shstrndx == SHN_UNDEF)
|
||||
goto error;
|
||||
const Elf32_Shdr *shdr_shstr = &shdrs[ehdr->e_shstrndx];
|
||||
const char *shstr = (const char *)(elf + shdr_shstr->sh_offset);
|
||||
const Elf32_Shdr *shdr_symtab = NULL;
|
||||
const Elf32_Shdr *shdr_strtab = NULL;
|
||||
for (int i = 0; i < ehdr->e_shnum; i++)
|
||||
{
|
||||
const char *sh_name = shstr + shdrs[i].sh_name;
|
||||
if (strcmp(sh_name, ".symtab") == 0)
|
||||
shdr_symtab = &shdrs[i];
|
||||
else if (strcmp(sh_name, ".strtab") == 0)
|
||||
shdr_strtab = &shdrs[i];
|
||||
}
|
||||
if (!shdr_symtab)
|
||||
goto error;
|
||||
if (!shdr_strtab)
|
||||
goto error;
|
||||
|
||||
const Elf32_Sym *symtab = (Elf32_Sym *)(elf + shdr_symtab->sh_offset);
|
||||
const char *strtab = (const char *)(elf + shdr_strtab->sh_offset);
|
||||
for (int i = 0; i < shdr_symtab->sh_size / shdr_symtab->sh_entsize; i++)
|
||||
{
|
||||
if (symtab[i].st_name == 0) continue;
|
||||
if (symtab[i].st_shndx > ehdr->e_shnum) continue;
|
||||
if (symtab[i].st_value < 0x2000000 || symtab[i].st_size == 0) continue;
|
||||
struct Symbol symbol =
|
||||
{
|
||||
.name = strtab + symtab[i].st_name,
|
||||
.address = symtab[i].st_value,
|
||||
.size = symtab[i].st_size,
|
||||
};
|
||||
if (symbol_table.symbols_n == symbol_table_symbols_c)
|
||||
{
|
||||
symbol_table_symbols_c *= 2;
|
||||
void *symbols = realloc(symbol_table.symbols, symbol_table_symbols_c * sizeof(*symbol_table.symbols));
|
||||
if (symbols == NULL)
|
||||
goto error;
|
||||
symbol_table.symbols = symbols;
|
||||
}
|
||||
symbol_table.symbols[symbol_table.symbols_n++] = symbol;
|
||||
}
|
||||
|
||||
qsort(symbol_table.symbols, symbol_table.symbols_n, sizeof(*symbol_table.symbols), compare_addresses);
|
||||
return;
|
||||
|
||||
error:
|
||||
free(symbol_table.symbols);
|
||||
symbol_table.symbols = NULL;
|
||||
symbol_table.symbols_n = 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -292,6 +451,8 @@ int main(int argc, char *argv[])
|
|||
exit(2);
|
||||
}
|
||||
|
||||
build_symbol_table(elf);
|
||||
|
||||
nrunners = 1;
|
||||
const char *makeflags = getenv("MAKEFLAGS");
|
||||
if (makeflags)
|
||||
|
|
Loading…
Reference in a new issue