mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 11:44:17 +01:00
solve conflics
This commit is contained in:
commit
8ee3a3c5bb
42
INSTALL.md
42
INSTALL.md
@ -125,19 +125,53 @@ Otherwise, ask for help on Discord or IRC (see [README.md](README.md)), or conti
|
||||
|
||||
Note that in msys2, Copy is Ctrl+Insert and Paste is Shift+Insert.
|
||||
|
||||
1. Open msys2 at C:\devkitPro\msys2\mingw64.exe or run `C:\devkitPro\msys2\msys2_shell.bat -mingw64`.
|
||||
1. Open msys2 at C:\devkitPro\msys2\msys2_shell.bat.
|
||||
|
||||
2. Certain packages are required to build pokeemerald. Install these by running the following command:
|
||||
2. Certain packages are required to build pokeemerald. Install these by running the following two commands:
|
||||
|
||||
```bash
|
||||
pacman -S make zlib-devel git mingw-w64-x86_64-gcc mingw-w64-x86_64-libpng
|
||||
pacman -Sy msys2-keyring
|
||||
pacman -S make gcc zlib-devel git
|
||||
```
|
||||
<details>
|
||||
<summary><i>Note...</i></summary>
|
||||
|
||||
> This command will ask for confirmation, just enter the yes action when prompted.
|
||||
> The commands will ask for confirmation, just enter the yes action when prompted.
|
||||
</details>
|
||||
|
||||
3. Download [libpng](https://sourceforge.net/projects/libpng/files/libpng16/1.6.37/libpng-1.6.37.tar.xz/download).
|
||||
|
||||
4. Change directory to where libpng was downloaded. By default, msys2 will start in the current user's profile folder, located at **C:\Users\\⁠_\<user>_**, where *\<user>* is your Windows username. In most cases, libpng should be saved within a subfolder of the profile folder. For example, if libpng was saved to **C:\Users\\_\<user>_\Downloads** (the Downloads location for most users), enter this command:
|
||||
|
||||
```bash
|
||||
cd Downloads
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><i>Notes...</i></summary>
|
||||
|
||||
> Note 1: While not shown, msys uses forward slashes `/` instead of backwards slashes `\` as the directory separator.
|
||||
> Note 2: If the path has spaces, then the path must be wrapped with quotations, e.g. `cd "Downloads/My Downloads"`.
|
||||
> Note 3: Windows path names are case-insensitive so adhering to capitalization isn’t needed.
|
||||
> Note 4: If libpng was saved elsewhere, you will need to specify the full path to where libpng was downloaded, e.g. `cd c:/devkitpro/msys2` if it was saved there.
|
||||
</details>
|
||||
|
||||
5. Run the following commands to uncompress and install libpng.
|
||||
|
||||
```bash
|
||||
tar xf libpng-1.6.37.tar.xz
|
||||
cd libpng-1.6.37
|
||||
./configure --prefix=/usr
|
||||
make check
|
||||
make install
|
||||
```
|
||||
|
||||
6. Then finally, run the following command to change back to the user profile folder.
|
||||
|
||||
```bash
|
||||
cd
|
||||
```
|
||||
|
||||
### Choosing where to store pokeemerald (msys2)
|
||||
At this point, you can choose a folder to store pokeemerald into. If you're okay with storing pokeemerald in the user profile folder, then proceed to [Installation](#installation). Otherwise, you'll need to account for where pokeemerald is stored when changing directory to the pokeemerald folder.
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -118,7 +118,7 @@ LIBPATH := -L ../../tools/agbcc/lib
|
||||
LIB := $(LIBPATH) -lgcc -lc -L../../libagbsyscall -lagbsyscall
|
||||
else
|
||||
CC1 = $(shell $(PATH_MODERNCC) --print-prog-name=cc1) -quiet
|
||||
override CFLAGS += -mthumb -mthumb-interwork -O2 -mabi=apcs-gnu -mtune=arm7tdmi -march=armv4t -fno-toplevel-reorder -Wno-pointer-to-int-cast
|
||||
override CFLAGS += -mthumb -mthumb-interwork -O2 -mabi=apcs-gnu -mtune=arm7tdmi -march=armv4t -fno-toplevel-reorder -Wno-pointer-to-int-cast -std=gnu17 -fanalyzer
|
||||
ROM := $(MODERN_ROM_NAME)
|
||||
OBJ_DIR := $(MODERN_OBJ_DIR_NAME)
|
||||
LIBPATH := -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libgcc.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libnosys.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libc.a))"
|
||||
|
@ -1053,8 +1053,9 @@
|
||||
.byte 0xca
|
||||
.endm
|
||||
|
||||
.macro setcharge
|
||||
.macro setcharge battler:req
|
||||
.byte 0xcb
|
||||
.byte \battler
|
||||
.endm
|
||||
|
||||
.macro callterrainattack
|
||||
@ -1381,6 +1382,12 @@
|
||||
.4byte \ptr
|
||||
.endm
|
||||
|
||||
.macro jumpifcantloseitem battler:req, ptr:req
|
||||
callnative BS_JumpIfCantLoseItem
|
||||
.byte \battler
|
||||
.4byte \ptr
|
||||
.endm
|
||||
|
||||
@ various command changed to more readable macros
|
||||
.macro cancelmultiturnmoves battler:req
|
||||
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -435,6 +435,7 @@ gBattleScriptsForMoveEffects::
|
||||
.4byte BattleScript_EffectHit @ EFFECT_COLLISION_COURSE
|
||||
.4byte BattleScript_EffectSpinOut @ EFFECT_SPIN_OUT
|
||||
.4byte BattleScript_EffectMakeItRain @ EFFECT_MAKE_IT_RAIN
|
||||
.4byte BattleScript_EffectCorrosiveGas @ EFFECT_CORROSIVE_GAS
|
||||
.4byte BattleScript_EffectHit @ EFFECT_POPULATION_BOMB
|
||||
.4byte BattleScript_EffectMortalSpin @ EFFECT_MORTAL_SPIN
|
||||
|
||||
@ -447,6 +448,29 @@ BattleScript_EffectMortalSpin:
|
||||
moveendall
|
||||
end
|
||||
|
||||
BattleScript_EffectCorrosiveGas:
|
||||
attackcanceler
|
||||
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
|
||||
attackstring
|
||||
ppreduce
|
||||
jumpifsubstituteblocks BattleScript_CorrosiveGasFail
|
||||
jumpifcantloseitem BS_TARGET, BattleScript_CorrosiveGasFail
|
||||
attackanimation
|
||||
waitanimation
|
||||
jumpifability BS_TARGET, ABILITY_STICKY_HOLD, BattleScript_StickyHoldActivates
|
||||
setlastuseditem BS_TARGET
|
||||
removeitem BS_TARGET
|
||||
printstring STRINGID_PKMNITEMMELTED
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_CorrosiveGasFail:
|
||||
pause B_WAIT_TIME_SHORT
|
||||
orhalfword gMoveResultFlags, MOVE_RESULT_FAILED
|
||||
printstring STRINGID_NOEFFECTONTARGET
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_EffectMakeItRain:
|
||||
setmoveeffect MOVE_EFFECT_PAYDAY
|
||||
call BattleScript_EffectHit_Ret
|
||||
@ -3006,12 +3030,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindRider:
|
||||
|
||||
BattleScript_TryTailwindAbilitiesLoop_WindPower:
|
||||
call BattleScript_AbilityPopUp
|
||||
copybyte sSAVED_BATTLER, gBattlerAttacker
|
||||
copybyte gBattlerAttacker, gBattlerTarget
|
||||
setcharge
|
||||
setcharge BS_TARGET
|
||||
printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
copybyte gBattlerAttacker, sSAVED_BATTLER
|
||||
goto BattleScript_TryTailwindAbilitiesLoop_Increment
|
||||
|
||||
BattleScript_EffectMircleEye:
|
||||
@ -5747,7 +5768,7 @@ BattleScript_EffectCharge::
|
||||
attackcanceler
|
||||
attackstring
|
||||
ppreduce
|
||||
setcharge
|
||||
setcharge BS_ATTACKER
|
||||
attackanimation
|
||||
waitanimation
|
||||
.if B_CHARGE_SPDEF_RAISE >= GEN_5
|
||||
@ -6783,14 +6804,14 @@ BattleScript_SunlightFaded::
|
||||
BattleScript_OverworldWeatherStarts::
|
||||
printfromtable gWeatherStartsStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
playanimation_var BS_ATTACKER, sB_ANIM_ARG1
|
||||
playanimation_var BS_BATTLER_0, sB_ANIM_ARG1
|
||||
call BattleScript_ActivateWeatherAbilities
|
||||
end3
|
||||
|
||||
BattleScript_OverworldTerrain::
|
||||
printfromtable gTerrainStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG
|
||||
playanimation BS_BATTLER_0, B_ANIM_RESTORE_BG
|
||||
call BattleScript_ActivateTerrainEffects
|
||||
end3
|
||||
|
||||
@ -6814,27 +6835,6 @@ BattleScript_TailwindEnds::
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end2
|
||||
|
||||
BattleScript_WindPowerActivatesEnd2::
|
||||
setbyte gBattlerAttacker, 0
|
||||
BattleScript_WindPowerLoop:
|
||||
printstring STRINGID_EMPTYSTRING3
|
||||
jumpifability BS_ATTACKER, ABILITY_WIND_POWER, BattleScript_WindPowerLoop_Cont
|
||||
goto BattleScript_WindPowerIncrement
|
||||
BattleScript_WindPowerLoop_Cont:
|
||||
jumpifstatus3 BS_ATTACKER, STATUS3_CHARGED_UP, BattleScript_WindPowerIncrement
|
||||
goto BattleScript_WindPower_Activate
|
||||
BattleScript_WindPower_Activate:
|
||||
call BattleScript_AbilityPopUp
|
||||
setcharge
|
||||
printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_WindPowerIncrement:
|
||||
addbyte gBattlerAttacker, 1
|
||||
jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_WindPowerLoop
|
||||
BattleScript_WindPowerEnd:
|
||||
destroyabilitypopup
|
||||
end2
|
||||
|
||||
BattleScript_TrickRoomEnds::
|
||||
printstring STRINGID_TRICKROOMENDS
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
@ -7296,11 +7296,8 @@ BattleScript_AngerShellRet:
|
||||
return
|
||||
|
||||
BattleScript_WindPowerActivates::
|
||||
.if B_CHECK_IF_CHARGED_UP == TRUE
|
||||
jumpifstatus3 BS_ATTACKER, STATUS3_CHARGED_UP, BattleScript_WindPowerActivates_Ret
|
||||
.endif
|
||||
call BattleScript_AbilityPopUp
|
||||
setcharge
|
||||
setcharge BS_TARGET
|
||||
printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_WindPowerActivates_Ret:
|
||||
@ -8659,15 +8656,14 @@ BattleScript_DesolateLandActivates::
|
||||
call BattleScript_ActivateWeatherAbilities
|
||||
end3
|
||||
|
||||
BattleScript_DesolateLandEvaporatesWaterTypeMoves::
|
||||
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
|
||||
BattleScript_PrimalWeatherBlocksMove::
|
||||
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_ATTACKSTRING_PRINTED, BattleScript_MoveEnd @in case of multi-target moves, if move fails once, no point in printing the message twice
|
||||
accuracycheck BattleScript_PrintMoveMissed, NO_ACC_CALC_CHECK_LOCK_ON
|
||||
attackstring
|
||||
pause B_WAIT_TIME_SHORT
|
||||
ppreduce
|
||||
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd
|
||||
printstring STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT
|
||||
printfromtable gPrimalWeatherBlocksStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
orword gHitMarker, HITMARKER_STRING_PRINTED
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_PrimordialSeaActivates::
|
||||
@ -8679,17 +8675,6 @@ BattleScript_PrimordialSeaActivates::
|
||||
call BattleScript_ActivateWeatherAbilities
|
||||
end3
|
||||
|
||||
BattleScript_PrimordialSeaFizzlesOutFireTypeMoves::
|
||||
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
|
||||
attackstring
|
||||
pause B_WAIT_TIME_SHORT
|
||||
ppreduce
|
||||
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd
|
||||
printstring STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
orword gHitMarker, HITMARKER_STRING_PRINTED
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_DeltaStreamActivates::
|
||||
pause B_WAIT_TIME_SHORT
|
||||
call BattleScript_AbilityPopUp
|
||||
@ -10321,6 +10306,7 @@ BattleScript_SymbiosisActivates::
|
||||
return
|
||||
|
||||
BattleScript_TargetAbilityStatRaiseRet::
|
||||
copybyte sSAVED_BATTLER, gBattlerAttacker
|
||||
copybyte gBattlerAbility, gEffectBattler
|
||||
copybyte gBattlerAttacker, gBattlerTarget
|
||||
call BattleScript_AbilityPopUp
|
||||
@ -10328,6 +10314,7 @@ BattleScript_TargetAbilityStatRaiseRet::
|
||||
setgraphicalstatchangevalues
|
||||
call BattleScript_StatUp
|
||||
BattleScript_TargetAbilityStatRaiseRet_End:
|
||||
copybyte gBattlerAttacker, sSAVED_BATTLER
|
||||
return
|
||||
|
||||
BattleScript_PokemonCantUseTheMove::
|
||||
|
218
gflib/sprite.c
218
gflib/sprite.c
@ -48,11 +48,7 @@ struct OamDimensions
|
||||
s8 height;
|
||||
};
|
||||
|
||||
static void UpdateOamCoords(void);
|
||||
static void BuildSpritePriorities(void);
|
||||
static void SortSprites(void);
|
||||
static void CopyMatricesToOamBuffer(void);
|
||||
static void AddSpritesToOamBuffer(void);
|
||||
static void SortSprites(u32 *spritePriorities, s32 n);
|
||||
static u8 CreateSpriteAt(u8 index, const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority);
|
||||
static void ResetOamMatrices(void);
|
||||
static void ResetSprite(struct Sprite *sprite);
|
||||
@ -280,12 +276,12 @@ u32 gOamMatrixAllocBitmap;
|
||||
u8 gReservedSpritePaletteCount;
|
||||
|
||||
EWRAM_DATA struct Sprite gSprites[MAX_SPRITES + 1] = {0};
|
||||
EWRAM_DATA static u16 sSpritePriorities[MAX_SPRITES] = {0};
|
||||
EWRAM_DATA static u8 sSpriteOrder[MAX_SPRITES] = {0};
|
||||
EWRAM_DATA static bool8 sShouldProcessSpriteCopyRequests = 0;
|
||||
EWRAM_DATA static u8 sSpriteCopyRequestCount = 0;
|
||||
EWRAM_DATA static struct SpriteCopyRequest sSpriteCopyRequests[MAX_SPRITES] = {0};
|
||||
EWRAM_DATA u8 gOamLimit = 0;
|
||||
static EWRAM_DATA u8 gOamDummyIndex = 0;
|
||||
EWRAM_DATA u16 gReservedSpriteTileCount = 0;
|
||||
EWRAM_DATA static u8 sSpriteTileAllocBitmap[128] = {0};
|
||||
EWRAM_DATA s16 gSpriteCoordOffsetX = 0;
|
||||
@ -296,6 +292,7 @@ EWRAM_DATA bool8 gAffineAnimsDisabled = FALSE;
|
||||
void ResetSpriteData(void)
|
||||
{
|
||||
ResetOamRange(0, 128);
|
||||
gOamDummyIndex = 0;
|
||||
ResetAllSprites();
|
||||
ClearSpriteCopyRequests();
|
||||
ResetAffineAnimData();
|
||||
@ -326,26 +323,37 @@ void AnimateSprites(void)
|
||||
|
||||
void BuildOamBuffer(void)
|
||||
{
|
||||
u8 temp;
|
||||
UpdateOamCoords();
|
||||
BuildSpritePriorities();
|
||||
SortSprites();
|
||||
temp = gMain.oamLoadDisabled;
|
||||
gMain.oamLoadDisabled = TRUE;
|
||||
AddSpritesToOamBuffer();
|
||||
CopyMatricesToOamBuffer();
|
||||
gMain.oamLoadDisabled = temp;
|
||||
sShouldProcessSpriteCopyRequests = TRUE;
|
||||
}
|
||||
bool32 oamLoadDisabled;
|
||||
u32 i, stride;
|
||||
u8 oamIndex;
|
||||
|
||||
// All attributes which affect sorting packed into a single u32:
|
||||
// { priority:2, subpriority:8, y:9, :5, index:8 }.
|
||||
// Index has its own byte even though it only needs 6 bits so that
|
||||
// we can load it with a ldrb instead of having to mask out the
|
||||
// bottom 6 bits.
|
||||
u32 spritePriorities[MAX_SPRITES];
|
||||
s32 toSort = 0;
|
||||
u8 skippedSprites[MAX_SPRITES];
|
||||
u32 skippedSpritesN = 0;
|
||||
u32 matrices = 0;
|
||||
|
||||
void UpdateOamCoords(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
if (sprite->inUse && !sprite->invisible)
|
||||
// Reuse existing sSpriteOrder because we expect the order to be
|
||||
// relatively stable between frames.
|
||||
u32 index = sSpriteOrder[i];
|
||||
struct Sprite *sprite = &gSprites[index];
|
||||
s32 y;
|
||||
if (!sprite->inUse || sprite->invisible)
|
||||
{
|
||||
skippedSprites[skippedSpritesN++] = index;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
|
||||
matrices |= 1 << sprite->oam.matrixNum;
|
||||
|
||||
if (sprite->coordOffsetEnabled)
|
||||
{
|
||||
sprite->oam.x = sprite->x + sprite->x2 + sprite->centerToCornerVecX + gSpriteCoordOffsetX;
|
||||
@ -356,122 +364,54 @@ void UpdateOamCoords(void)
|
||||
sprite->oam.x = sprite->x + sprite->x2 + sprite->centerToCornerVecX;
|
||||
sprite->oam.y = sprite->y + sprite->y2 + sprite->centerToCornerVecY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildSpritePriorities(void)
|
||||
{
|
||||
u16 i;
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
y = sprite->oam.y;
|
||||
if (y >= DISPLAY_HEIGHT)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
u16 priority = sprite->subpriority | (sprite->oam.priority << 8);
|
||||
sSpritePriorities[i] = priority;
|
||||
y -= 256;
|
||||
}
|
||||
}
|
||||
|
||||
void SortSprites(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 1; i < MAX_SPRITES; i++)
|
||||
else if (sprite->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u8 j = i;
|
||||
struct Sprite *sprite1 = &gSprites[sSpriteOrder[i - 1]];
|
||||
struct Sprite *sprite2 = &gSprites[sSpriteOrder[i]];
|
||||
u16 sprite1Priority = sSpritePriorities[sSpriteOrder[i - 1]];
|
||||
u16 sprite2Priority = sSpritePriorities[sSpriteOrder[i]];
|
||||
s16 sprite1Y = sprite1->oam.y;
|
||||
s16 sprite2Y = sprite2->oam.y;
|
||||
|
||||
if (sprite1Y >= DISPLAY_HEIGHT)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
|
||||
if (sprite2Y >= DISPLAY_HEIGHT)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
|
||||
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite1->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite1->oam.shape;
|
||||
u32 shape = sprite->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite1Y > 128)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
if (y > 128)
|
||||
y -= 256;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite2->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite2->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite2Y > 128)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
}
|
||||
// y in [-128...159], so (159 - y) in [0..287].
|
||||
spritePriorities[toSort++]
|
||||
= (sprite->oam.priority << 30)
|
||||
| (sprite->subpriority << 22)
|
||||
| (((159 - y) & 0x1FF) << 13)
|
||||
| (index << 0);
|
||||
}
|
||||
|
||||
while (j > 0
|
||||
&& ((sprite1Priority > sprite2Priority)
|
||||
|| (sprite1Priority == sprite2Priority && sprite1Y < sprite2Y)))
|
||||
{
|
||||
u8 temp = sSpriteOrder[j];
|
||||
sSpriteOrder[j] = sSpriteOrder[j - 1];
|
||||
sSpriteOrder[j - 1] = temp;
|
||||
SortSprites(spritePriorities, toSort);
|
||||
|
||||
// UB: If j equals 1, then j-- makes j equal 0.
|
||||
// Then, sSpriteOrder[-1] gets accessed below.
|
||||
// Although this doesn't result in a bug in the ROM,
|
||||
// the behavior is undefined.
|
||||
j--;
|
||||
#ifdef UBFIX
|
||||
if (j == 0)
|
||||
for (i = 0; i < toSort; i++)
|
||||
sSpriteOrder[i] = spritePriorities[i] & 0xFF;
|
||||
for (i = 0; i < skippedSpritesN; i++)
|
||||
sSpriteOrder[toSort + i] = skippedSprites[i];
|
||||
|
||||
oamLoadDisabled = gMain.oamLoadDisabled;
|
||||
gMain.oamLoadDisabled = TRUE;
|
||||
|
||||
for (i = 0, oamIndex = 0; i < toSort; i++)
|
||||
{
|
||||
if (AddSpriteToOamBuffer(&gSprites[spritePriorities[i] & 0xFF], &oamIndex))
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
sprite1 = &gSprites[sSpriteOrder[j - 1]];
|
||||
sprite2 = &gSprites[sSpriteOrder[j]];
|
||||
sprite1Priority = sSpritePriorities[sSpriteOrder[j - 1]];
|
||||
sprite2Priority = sSpritePriorities[sSpriteOrder[j]];
|
||||
sprite1Y = sprite1->oam.y;
|
||||
sprite2Y = sprite2->oam.y;
|
||||
for (i = oamIndex; i < gOamDummyIndex; i++)
|
||||
gMain.oamBuffer[i] = gDummyOamData;
|
||||
gOamDummyIndex = oamIndex;
|
||||
|
||||
if (sprite1Y >= DISPLAY_HEIGHT)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
|
||||
if (sprite2Y >= DISPLAY_HEIGHT)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
|
||||
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite1->oam.size == ST_OAM_SIZE_3)
|
||||
for (i = 0; matrices != 0; i++, matrices >>= 1)
|
||||
{
|
||||
u32 shape = sprite1->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite1Y > 128)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite2->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite2->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite2Y > 128)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyMatricesToOamBuffer(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < OAM_MATRIX_COUNT; i++)
|
||||
if (matrices & 1)
|
||||
{
|
||||
u32 base = 4 * i;
|
||||
gMain.oamBuffer[base + 0].affineParam = gOamMatrices[i].a;
|
||||
@ -479,26 +419,32 @@ void CopyMatricesToOamBuffer(void)
|
||||
gMain.oamBuffer[base + 2].affineParam = gOamMatrices[i].c;
|
||||
gMain.oamBuffer[base + 3].affineParam = gOamMatrices[i].d;
|
||||
}
|
||||
}
|
||||
|
||||
gMain.oamLoadDisabled = oamLoadDisabled;
|
||||
sShouldProcessSpriteCopyRequests = TRUE;
|
||||
}
|
||||
|
||||
void AddSpritesToOamBuffer(void)
|
||||
static inline void InsertionSort(u32 *spritePriorities, s32 n)
|
||||
{
|
||||
u8 i = 0;
|
||||
u8 oamIndex = 0;
|
||||
|
||||
while (i < MAX_SPRITES)
|
||||
s32 i = 1;
|
||||
while (i < n)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[sSpriteOrder[i]];
|
||||
if (sprite->inUse && !sprite->invisible && AddSpriteToOamBuffer(sprite, &oamIndex))
|
||||
return;
|
||||
u32 x = spritePriorities[i];
|
||||
s32 j = i - 1;
|
||||
while (j >= 0 && spritePriorities[j] > x)
|
||||
{
|
||||
spritePriorities[j + 1] = spritePriorities[j];
|
||||
j--;
|
||||
}
|
||||
spritePriorities[j + 1] = x;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
while (oamIndex < gOamLimit)
|
||||
{
|
||||
gMain.oamBuffer[oamIndex] = gDummyOamData;
|
||||
oamIndex++;
|
||||
}
|
||||
static void SortSprites(u32 *spritePriorities, s32 n)
|
||||
{
|
||||
InsertionSort(spritePriorities, n);
|
||||
}
|
||||
|
||||
u8 CreateSprite(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
|
||||
@ -849,7 +795,7 @@ void CopyToSprites(u8 *src)
|
||||
|
||||
void ResetAllSprites(void)
|
||||
{
|
||||
u8 i;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
|
BIN
graphics/battle_anims/sprites/wood_hammer_hammer.png
Normal file
BIN
graphics/battle_anims/sprites/wood_hammer_hammer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 400 B |
Binary file not shown.
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 363 B |
@ -168,7 +168,9 @@ bool32 PartnerMoveIsSameNoTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove
|
||||
bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move);
|
||||
|
||||
// party logic
|
||||
s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon *mon);
|
||||
struct BattlePokemon *AllocSaveBattleMons(void);
|
||||
void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons);
|
||||
s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon);
|
||||
s32 CountUsablePartyMons(u8 battlerId);
|
||||
bool32 IsPartyFullyHealedExceptBattler(u8 battler);
|
||||
bool32 PartyHasMoveSplit(u8 battlerId, u8 split);
|
||||
|
@ -409,9 +409,8 @@ extern const u8 BattleScript_GulpMissileGorging[];
|
||||
extern const u8 BattleScript_GulpMissileGulping[];
|
||||
extern const u8 BattleScript_BattleBondActivatesOnMoveEndAttacker[];
|
||||
extern const u8 BattleScript_DesolateLandActivates[];
|
||||
extern const u8 BattleScript_DesolateLandEvaporatesWaterTypeMoves[];
|
||||
extern const u8 BattleScript_PrimordialSeaActivates[];
|
||||
extern const u8 BattleScript_PrimordialSeaFizzlesOutFireTypeMoves[];
|
||||
extern const u8 BattleScript_PrimalWeatherBlocksMove[];
|
||||
extern const u8 BattleScript_DeltaStreamActivates[];
|
||||
extern const u8 BattleScript_MysteriousAirCurrentBlowsOn[];
|
||||
extern const u8 BattleScript_AttackWeakenedByStrongWinds[];
|
||||
|
@ -136,8 +136,8 @@ u8 DoBattlerEndTurnEffects(void);
|
||||
bool8 HandleWishPerishSongOnTurnEnd(void);
|
||||
bool8 HandleFaintedMonActions(void);
|
||||
void TryClearRageAndFuryCutter(void);
|
||||
u8 AtkCanceller_UnableToUseMove(u32 moveType);
|
||||
void SetAtkCancellerForCalledMove(void);
|
||||
u8 AtkCanceller_UnableToUseMove(void);
|
||||
u8 AtkCanceller_UnableToUseMove2(void);
|
||||
bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2);
|
||||
bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility);
|
||||
|
@ -71,7 +71,7 @@
|
||||
#define GEN_7 4
|
||||
#define GEN_8 5
|
||||
#define GEN_9 6
|
||||
#define GEN_LATEST GEN_8
|
||||
#define GEN_LATEST GEN_9
|
||||
|
||||
// General settings
|
||||
#define EXPANSION_INTRO TRUE // If TRUE, a custom RHH intro will play after the vanilla copyright screen.
|
||||
|
@ -122,7 +122,6 @@
|
||||
#define B_PLUS_MINUS_INTERACTION GEN_LATEST // In Gen5+, Plus and Minus can be activated with themselves and the opposite ability. Before, only the opposing ability could activate it.
|
||||
#define B_WEATHER_FORMS GEN_LATEST // In Gen5+, Castform and Cherrim revert to their base form upon losing their respective ability. Cherrim needs Flower Gift to swap forms.
|
||||
#define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply.
|
||||
#define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied.
|
||||
#define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases.
|
||||
#define B_LEAF_GUARD_PREVENTS_REST GEN_LATEST // In Gen5+, Leaf Guard prevents the use of Rest in harsh sunlight.
|
||||
#define B_SNOW_WARNING GEN_LATEST // In Gen9+, Snow Warning will summon snow instead of hail.
|
||||
|
@ -397,6 +397,7 @@
|
||||
#define ANIM_TAG_STEEL_BEAM (ANIM_SPRITES_START + 383)
|
||||
#define ANIM_TAG_POLTERGEIST (ANIM_SPRITES_START + 384)
|
||||
#define ANIM_TAG_TEAPOT (ANIM_SPRITES_START + 385)
|
||||
#define ANIM_TAG_WOOD_HAMMER_HAMMER (ANIM_SPRITES_START + 386)
|
||||
|
||||
|
||||
// battlers
|
||||
|
@ -412,9 +412,10 @@
|
||||
#define EFFECT_COLLISION_COURSE 406
|
||||
#define EFFECT_SPIN_OUT 407
|
||||
#define EFFECT_MAKE_IT_RAIN 408
|
||||
#define EFFECT_POPULATION_BOMB 409
|
||||
#define EFFECT_MORTAL_SPIN 410
|
||||
#define EFFECT_CORROSIVE_GAS 409
|
||||
#define EFFECT_POPULATION_BOMB 410
|
||||
#define EFFECT_MORTAL_SPIN 411
|
||||
|
||||
#define NUM_BATTLE_MOVE_EFFECTS 411
|
||||
#define NUM_BATTLE_MOVE_EFFECTS 412
|
||||
|
||||
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H
|
||||
|
@ -664,8 +664,9 @@
|
||||
#define STRINGID_SNOWCONTINUES 662
|
||||
#define STRINGID_SNOWSTOPPED 663
|
||||
#define STRINGID_SNOWWARNINGSNOW 664
|
||||
#define STRINGID_PKMNITEMMELTED 665
|
||||
|
||||
#define BATTLESTRINGS_COUNT 665
|
||||
#define BATTLESTRINGS_COUNT 666
|
||||
|
||||
// This is the string id that gBattleStringsTable starts with.
|
||||
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
|
||||
@ -827,6 +828,10 @@
|
||||
#define B_MSG_SOMEONES_BOX_FULL 2
|
||||
#define B_MSG_LANETTES_BOX_FULL 3
|
||||
|
||||
// gPrimalWeatherBlocksStringIds
|
||||
#define B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN 0
|
||||
#define B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN 1
|
||||
|
||||
// gInobedientStringIds
|
||||
#define B_MSG_LOAFING 0
|
||||
#define B_MSG_WONT_OBEY 1
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "sprite.h"
|
||||
|
||||
extern u8 gDecompressionBuffer[0x4000];
|
||||
extern u8 ALIGNED(4) gDecompressionBuffer[0x4000];
|
||||
|
||||
void LZDecompressWram(const u32 *src, void *dest);
|
||||
void LZDecompressVram(const u32 *src, void *dest);
|
||||
|
@ -664,6 +664,7 @@
|
||||
#define TIMER_64CLK 0x01
|
||||
#define TIMER_256CLK 0x02
|
||||
#define TIMER_1024CLK 0x03
|
||||
#define TIMER_COUNTUP 0x04
|
||||
#define TIMER_INTR_ENABLE 0x40
|
||||
#define TIMER_ENABLE 0x80
|
||||
|
||||
|
@ -213,7 +213,7 @@ struct SoundInfo
|
||||
ExtVolPitFunc ExtVolPit;
|
||||
u8 gap2[16];
|
||||
struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS];
|
||||
s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2];
|
||||
s8 ALIGNED(4) pcmBuffer[PCM_DMA_BUF_SIZE * 2];
|
||||
};
|
||||
|
||||
struct SongHeader
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef GUARD_GBA_MACRO_H
|
||||
#define GUARD_GBA_MACRO_H
|
||||
|
||||
#define CPU_FILL(value, dest, size, bit) \
|
||||
#define CPU_FILL_UNCHECKED(value, dest, size, bit) \
|
||||
{ \
|
||||
vu##bit tmp = (vu##bit)(value); \
|
||||
CpuSet((void *)&tmp, \
|
||||
@ -9,10 +9,33 @@
|
||||
CPU_SET_##bit##BIT | CPU_SET_SRC_FIXED | ((size)/(bit/8) & 0x1FFFFF)); \
|
||||
}
|
||||
|
||||
#if MODERN
|
||||
#define CPU_FILL(value, dest, size, bit) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
|
||||
CPU_FILL_UNCHECKED(value, dest, size, bit); \
|
||||
} while (0)
|
||||
#else
|
||||
#define CPU_FILL(value, dest, size, bit) CPU_FILL_UNCHECKED(value, dest, size, bit)
|
||||
#endif
|
||||
|
||||
#define CpuFill16(value, dest, size) CPU_FILL(value, dest, size, 16)
|
||||
#define CpuFill32(value, dest, size) CPU_FILL(value, dest, size, 32)
|
||||
|
||||
#define CPU_COPY(src, dest, size, bit) CpuSet(src, dest, CPU_SET_##bit##BIT | ((size)/(bit/8) & 0x1FFFFF))
|
||||
#define CPU_COPY_UNCHECKED(src, dest, size, bit) CpuSet(src, dest, CPU_SET_##bit##BIT | ((size)/(bit/8) & 0x1FFFFF))
|
||||
|
||||
#if MODERN
|
||||
#define CPU_COPY(src, dest, size, bit) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(src) >= (bit / 8), "source potentially unaligned"); \
|
||||
_Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
|
||||
CPU_COPY_UNCHECKED(src, dest, size, bit); \
|
||||
} while (0)
|
||||
#else
|
||||
#define CPU_COPY(src, dest, size, bit) CPU_COPY_UNCHECKED(src, dest, size, bit)
|
||||
#endif
|
||||
|
||||
#define CpuCopy16(src, dest, size) CPU_COPY(src, dest, size, 16)
|
||||
#define CpuCopy32(src, dest, size) CPU_COPY(src, dest, size, 32)
|
||||
@ -31,7 +54,7 @@
|
||||
|
||||
#define CpuFastCopy(src, dest, size) CpuFastSet(src, dest, ((size)/(32/8) & 0x1FFFFF))
|
||||
|
||||
#define DmaSet(dmaNum, src, dest, control) \
|
||||
#define DmaSetUnchecked(dmaNum, src, dest, control) \
|
||||
{ \
|
||||
vu32 *dmaRegs = (vu32 *)REG_ADDR_DMA##dmaNum; \
|
||||
dmaRegs[0] = (vu32)(src); \
|
||||
@ -40,7 +63,21 @@
|
||||
dmaRegs[2]; \
|
||||
}
|
||||
|
||||
#define DMA_FILL(dmaNum, value, dest, size, bit) \
|
||||
#if MODERN
|
||||
// NOTE: Assumes 16-bit DMAs.
|
||||
#define DmaSet(dmaNum, src, dest, control) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(src) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & (DMA_32BIT << 16)) ? 4 : 2, 2), "source potentially unaligned"); \
|
||||
_Static_assert(_Alignof(dest) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & (DMA_32BIT << 16)) ? 4 : 2, 2), "destination potentially unaligned"); \
|
||||
DmaSetUnchecked(dmaNum, src, dest, control); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DmaSet(dmaNum, src, dest, control) \
|
||||
DmaSetUnchecked(dmaNum, src, dest, control)
|
||||
#endif
|
||||
|
||||
#define DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit) \
|
||||
{ \
|
||||
vu##bit tmp = (vu##bit)(value); \
|
||||
DmaSet(dmaNum, \
|
||||
@ -50,6 +87,17 @@
|
||||
| ((size)/(bit/8))); \
|
||||
}
|
||||
|
||||
#if MODERN
|
||||
#define DMA_FILL(dmaNum, value, dest, size, bit) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
|
||||
DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DMA_FILL(dmaNum, value, dest, size, bit) DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit)
|
||||
#endif
|
||||
|
||||
#define DmaFill16(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 16)
|
||||
#define DmaFill32(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 32)
|
||||
|
||||
@ -58,23 +106,46 @@
|
||||
// unit size (2 or 4 bytes) and then combined with the DMA control flags using a
|
||||
// bitwise OR operation.
|
||||
|
||||
#define DMA_CLEAR(dmaNum, dest, size, bit) \
|
||||
#define DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit) \
|
||||
{ \
|
||||
vu##bit *_dest = (vu##bit *)(dest); \
|
||||
u32 _size = size; \
|
||||
DmaFill##bit(dmaNum, 0, _dest, _size); \
|
||||
}
|
||||
|
||||
#if MODERN
|
||||
#define DMA_CLEAR(dmaNum, dest, size, bit) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
|
||||
DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DMA_CLEAR(dmaNum, dest, size, bit) DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit)
|
||||
#endif
|
||||
|
||||
#define DmaClear16(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 16)
|
||||
#define DmaClear32(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 32)
|
||||
|
||||
#define DMA_COPY(dmaNum, src, dest, size, bit) \
|
||||
#define DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit) \
|
||||
DmaSet(dmaNum, \
|
||||
src, \
|
||||
dest, \
|
||||
(DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_INC | DMA_DEST_INC) << 16 \
|
||||
| ((size)/(bit/8)))
|
||||
|
||||
#if MODERN
|
||||
#define DMA_COPY(dmaNum, src, dest, size, bit) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(src) >= (bit / 8), "source potentially unaligned"); \
|
||||
_Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
|
||||
DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DMA_COPY(dmaNum, src, dest, size, bit) DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit)
|
||||
#endif
|
||||
|
||||
#define DmaCopy16(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 16)
|
||||
#define DmaCopy32(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 32)
|
||||
|
||||
|
@ -27,10 +27,32 @@ u16 ArcTan2(s16 x, s16 y);
|
||||
|
||||
void CpuSet(const void *src, void *dest, u32 control);
|
||||
|
||||
#if MODERN
|
||||
// NOTE: Assumes 16-bit CpuSets unless control is a constant and has
|
||||
// CPU_SET_32BIT set.
|
||||
#define CpuSet(src, dest, control) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(src) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & CPU_SET_32BIT) ? 4 : 2, 2), "source potentially unaligned"); \
|
||||
_Static_assert(_Alignof(dest) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & CPU_SET_32BIT) ? 4 : 2, 2), "destination potentially unaligned"); \
|
||||
CpuSet(src, dest, control); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define CPU_FAST_SET_SRC_FIXED 0x01000000
|
||||
|
||||
void CpuFastSet(const void *src, void *dest, u32 control);
|
||||
|
||||
#if MODERN
|
||||
#define CpuFastSet(src, dest, control) \
|
||||
do \
|
||||
{ \
|
||||
_Static_assert(_Alignof(src) >= 4, "source potentially unaligned"); \
|
||||
_Static_assert(_Alignof(dest) >= 4, "destination potentially unaligned"); \
|
||||
CpuFastSet(src, dest, control); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
void BgAffineSet(struct BgAffineSrcData *src, struct BgAffineDstData *dest, s32 count);
|
||||
|
||||
void ObjAffineSet(struct ObjAffineSrcData *src, void *dest, s32 count, s32 offset);
|
||||
|
@ -21,7 +21,6 @@
|
||||
#define BLOCK_CROSS_JUMP asm("");
|
||||
|
||||
// to help in decompiling
|
||||
#define asm_comment(x) asm volatile("@ -- " x " -- ")
|
||||
#define asm_unified(x) asm(".syntax unified\n" x "\n.syntax divided")
|
||||
#define NAKED __attribute__((naked))
|
||||
|
||||
@ -116,6 +115,8 @@
|
||||
|
||||
// Calls m0/m1/.../m8 depending on how many arguments are passed.
|
||||
#define VARARG_8(m, ...) CAT(m, NARG_8(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
// This returns the number of arguments passed to it (up to 8).
|
||||
#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N
|
||||
|
||||
@ -831,7 +832,7 @@ struct WaldaPhrase
|
||||
struct TrainerNameRecord
|
||||
{
|
||||
u32 trainerId;
|
||||
u8 trainerName[PLAYER_NAME_LENGTH + 1];
|
||||
u8 ALIGNED(2) trainerName[PLAYER_NAME_LENGTH + 1];
|
||||
};
|
||||
|
||||
struct TrainerHillSave
|
||||
|
@ -9079,7 +9079,7 @@ extern const u32 gIntroGroudon_Gfx[];
|
||||
extern const u32 gIntroGroudon_Tilemap[];
|
||||
extern const u32 gIntroLegendBg_Gfx[];
|
||||
extern const u32 gIntroGroudonBg_Tilemap[];
|
||||
extern const u8 gIntro3Bg_Pal[0x200];
|
||||
extern const u8 ALIGNED(2) gIntro3Bg_Pal[0x200];
|
||||
extern const u32 gIntroKyogre_Gfx[];
|
||||
extern const u32 gIntroKyogre_Tilemap[];
|
||||
extern const u32 gIntroKyogreBg_Tilemap[];
|
||||
@ -10161,6 +10161,8 @@ extern const u32 gBattleAnimSpriteGfx_ZMoveSymbol[];
|
||||
extern const u32 gBattleAnimSpritePal_ZMoveSymbol[];
|
||||
extern const u32 gBattleAnimSpriteGfx_Teapot[];
|
||||
extern const u32 gBattleAnimSpritePal_Teapot[];
|
||||
extern const u32 gBattleAnimSpriteGfx_WoodHammerHammer[];
|
||||
extern const u32 gBattleAnimSpritePal_WoodHammerHammer[];
|
||||
|
||||
extern const u32 gBattleAnimBgImage_Dark[];
|
||||
extern const u32 gBattleAnimBgImage_Ghost[];
|
||||
@ -10589,8 +10591,8 @@ extern const u32 gPokenavOptions_Gfx[];
|
||||
extern const u16 gPokenavOptions_Pal[];
|
||||
|
||||
// Battle Factory Screen
|
||||
extern const u8 gFrontierFactorySelectMenu_Gfx[];
|
||||
extern const u8 gFrontierFactorySelectMenu_Tilemap[];
|
||||
extern const u16 gFrontierFactorySelectMenu_Gfx[];
|
||||
extern const u16 gFrontierFactorySelectMenu_Tilemap[];
|
||||
extern const u16 gFrontierFactorySelectMenu_Pal[];
|
||||
|
||||
// Object event pals
|
||||
|
@ -78,7 +78,7 @@ struct BagMenu
|
||||
u8 numShownItems[POCKETS_COUNT];
|
||||
s16 graphicsLoadState;
|
||||
u8 unused2[14];
|
||||
u8 pocketNameBuffer[32][32];
|
||||
u8 ALIGNED(4) pocketNameBuffer[32][32];
|
||||
u8 unused3[4];
|
||||
};
|
||||
|
||||
|
@ -329,7 +329,7 @@ struct RfuIntrStruct
|
||||
{
|
||||
union RfuPacket rxPacketAlloc;
|
||||
union RfuPacket txPacketAlloc;
|
||||
u8 block1[0x960]; // size of librfu_intr.s binary
|
||||
u8 ALIGNED(2) block1[0x960]; // size of librfu_intr.s binary
|
||||
struct STWIStatus block2;
|
||||
};
|
||||
|
||||
|
@ -238,7 +238,7 @@ struct BlockRequest
|
||||
};
|
||||
|
||||
extern struct Link gLink;
|
||||
extern u16 gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
|
||||
extern u16 ALIGNED(4) gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
|
||||
extern u8 gBlockSendBuffer[BLOCK_BUFFER_SIZE];
|
||||
extern u16 gLinkType;
|
||||
extern u32 gLinkStatus;
|
||||
|
@ -18,7 +18,7 @@ struct MonMarkingsMenu
|
||||
struct Sprite *textSprite;
|
||||
const u8 *frameTiles;
|
||||
const u16 *framePalette;
|
||||
u8 windowSpriteTiles[0x1000];
|
||||
u8 ALIGNED(2) windowSpriteTiles[0x1000];
|
||||
u8 unused[0x80];
|
||||
u8 tileLoadState;
|
||||
};
|
||||
|
@ -67,7 +67,7 @@ void LoadObjEventTemplatesFromHeader(void);
|
||||
void LoadSaveblockObjEventScripts(void);
|
||||
void SetObjEventTemplateCoords(u8 localId, s16 x, s16 y);
|
||||
void SetObjEventTemplateMovementType(u8 localId, u8 movementType);
|
||||
const struct MapLayout *GetMapLayout(void);
|
||||
const struct MapLayout *GetMapLayout(u16 mapLayoutId);
|
||||
void ApplyCurrentWarp(void);
|
||||
struct MapHeader const *const Overworld_GetMapHeaderByGroupAndId(u16 mapGroup, u16 mapNum);
|
||||
struct MapHeader const *const GetDestinationWarpMapHeader(void);
|
||||
|
@ -54,9 +54,9 @@ struct PaletteFadeControl
|
||||
|
||||
extern struct PaletteFadeControl gPaletteFade;
|
||||
extern u32 gPlttBufferTransferPending;
|
||||
extern u8 gPaletteDecompressionBuffer[];
|
||||
extern u16 gPlttBufferUnfaded[PLTT_BUFFER_SIZE];
|
||||
extern u16 gPlttBufferFaded[PLTT_BUFFER_SIZE];
|
||||
extern u8 ALIGNED(4) gPaletteDecompressionBuffer[];
|
||||
extern u16 ALIGNED(4) gPlttBufferUnfaded[PLTT_BUFFER_SIZE];
|
||||
extern u16 ALIGNED(4) gPlttBufferFaded[PLTT_BUFFER_SIZE];
|
||||
|
||||
void LoadCompressedPalette(const u32 *src, u16 offset, u16 size);
|
||||
void LoadPalette(const void *src, u16 offset, u16 size);
|
||||
|
@ -49,7 +49,7 @@ extern const struct SpriteTemplate gBallSpriteTemplates[];
|
||||
#define POKEBALL_OPPONENT_SENDOUT 0xFE
|
||||
|
||||
u8 DoPokeballSendOutAnimation(s16 pan, u8 kindOfThrow);
|
||||
void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes, u16 species);
|
||||
void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriority, u8 delay, u32 fadePalettes, u16 species);
|
||||
u8 CreateTradePokeballSprite(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subPriority, u8 delay, u32 fadePalettes);
|
||||
void StartHealthboxSlideIn(u8 battler);
|
||||
void DoHitAnimHealthboxEffect(u8 battler);
|
||||
|
@ -337,7 +337,7 @@ struct SpeciesInfo /*0x24*/
|
||||
struct BattleMove
|
||||
{
|
||||
u16 effect;
|
||||
u16 power; //higher than 255 for z moves
|
||||
u8 power;
|
||||
u8 type;
|
||||
u8 accuracy;
|
||||
u8 pp;
|
||||
@ -501,12 +501,17 @@ void SetMultiuseSpriteTemplateToPokemon(u16 speciesTag, u8 battlerPosition);
|
||||
void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosition);
|
||||
void SetMultiuseSpriteTemplateToTrainerFront(u16 trainerPicId, u8 battlerPosition);
|
||||
|
||||
// These are full type signatures for GetMonData() and GetBoxMonData(),
|
||||
// but they are not used since some code erroneously omits the third arg.
|
||||
// u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data);
|
||||
// u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data);
|
||||
u32 GetMonData();
|
||||
u32 GetBoxMonData();
|
||||
/* GameFreak called Get(Box)MonData with either 2 or 3 arguments, for
|
||||
* type safety we have a Get(Box)MonData macro which dispatches to
|
||||
* either Get(Box)MonData2 or Get(Box)MonData3 based on the number of
|
||||
* arguments. The two functions are aliases of each other, but they
|
||||
* differ for matching purposes in the caller's codegen. */
|
||||
#define GetMonData(...) CAT(GetMonData, NARG_8(__VA_ARGS__))(__VA_ARGS__)
|
||||
#define GetBoxMonData(...) CAT(GetBoxMonData, NARG_8(__VA_ARGS__))(__VA_ARGS__)
|
||||
u32 GetMonData3(struct Pokemon *mon, s32 field, u8 *data);
|
||||
u32 GetMonData2(struct Pokemon *mon, s32 field);
|
||||
u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data);
|
||||
u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field);
|
||||
|
||||
void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg);
|
||||
void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg);
|
||||
|
@ -37,7 +37,7 @@ struct ScanlineEffect
|
||||
|
||||
extern struct ScanlineEffect gScanlineEffect;
|
||||
|
||||
extern u16 gScanlineEffectRegBuffers[2][0x3C0];
|
||||
extern u16 ALIGNED(4) gScanlineEffectRegBuffers[2][0x3C0];
|
||||
|
||||
void ScanlineEffect_Stop(void);
|
||||
void ScanlineEffect_Clear(void);
|
||||
|
@ -28,6 +28,7 @@ static bool8 ShouldUseItem(void);
|
||||
static bool32 AiExpectsToFaintPlayer(void);
|
||||
static bool32 AI_ShouldHeal(u32 healAmount);
|
||||
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount);
|
||||
static bool32 IsAiPartyMonOHKOBy(u32 battlerAtk, struct Pokemon *aiMon);
|
||||
|
||||
static bool32 IsAceMon(u32 battlerId, u32 monPartyId)
|
||||
{
|
||||
@ -112,11 +113,7 @@ static bool8 ShouldSwitchIfWonderGuard(void)
|
||||
// Find a Pokemon in the party that has a super effective move.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_HP) == 0)
|
||||
continue;
|
||||
if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE)
|
||||
continue;
|
||||
if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
|
||||
if (!IsValidForBattle(&party[i]))
|
||||
continue;
|
||||
if (i == gBattlerPartyIndexes[gActiveBattler])
|
||||
continue;
|
||||
@ -195,13 +192,9 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
|
||||
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
u16 species;
|
||||
u16 monAbility;
|
||||
|
||||
if (GetMonData(&party[i], MON_DATA_HP) == 0)
|
||||
continue;
|
||||
species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
|
||||
if (species == SPECIES_NONE || species == SPECIES_EGG)
|
||||
if (!IsValidForBattle(&party[i]))
|
||||
continue;
|
||||
if (i == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
@ -215,7 +208,6 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
|
||||
continue;
|
||||
|
||||
monAbility = GetMonAbility(&party[i]);
|
||||
|
||||
if (absorbingTypeAbility == monAbility && Random() & 1)
|
||||
{
|
||||
// we found a mon.
|
||||
@ -290,9 +282,7 @@ static bool8 ShouldSwitchIfGameStatePrompt(void)
|
||||
continue;
|
||||
|
||||
//Look for mon in party that is able to be switched into and has ability that sets terrain
|
||||
if (GetMonData(&party[i], MON_DATA_HP) != 0
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
|
||||
if (IsValidForBattle(&party[i])
|
||||
&& i != gBattlerPartyIndexes[gActiveBattler]
|
||||
&& i != gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]
|
||||
&& IsBattlerGrounded(gActiveBattler)
|
||||
@ -561,13 +551,9 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent)
|
||||
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
u16 species;
|
||||
u16 monAbility;
|
||||
u16 species, monAbility;
|
||||
|
||||
if (GetMonData(&party[i], MON_DATA_HP) == 0)
|
||||
continue;
|
||||
species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
|
||||
if (species == SPECIES_NONE || species == SPECIES_EGG)
|
||||
if (!IsValidForBattle(&party[i]))
|
||||
continue;
|
||||
if (i == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
@ -580,8 +566,8 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent)
|
||||
if (IsAceMon(gActiveBattler, i))
|
||||
continue;
|
||||
|
||||
species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
|
||||
monAbility = GetMonAbility(&party[i]);
|
||||
|
||||
CalcPartyMonTypeEffectivenessMultiplier(gLastLandedMoves[gActiveBattler], species, monAbility);
|
||||
if (gMoveResultFlags & flags)
|
||||
{
|
||||
@ -650,11 +636,7 @@ bool32 ShouldSwitch(void)
|
||||
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_HP) == 0)
|
||||
continue;
|
||||
if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE)
|
||||
continue;
|
||||
if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
|
||||
if (!IsValidForBattle(&party[i]))
|
||||
continue;
|
||||
if (i == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
@ -751,7 +733,7 @@ void AI_TrySwitchOrUseItem(void)
|
||||
|
||||
for (monToSwitchId = (lastId-1); monToSwitchId >= firstId; monToSwitchId--)
|
||||
{
|
||||
if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0)
|
||||
if (!IsValidForBattle(&party[monToSwitchId]))
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
@ -785,7 +767,7 @@ void AI_TrySwitchOrUseItem(void)
|
||||
|
||||
// If there are two(or more) mons to choose from, always choose one that has baton pass
|
||||
// as most often it can't do much on its own.
|
||||
static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount)
|
||||
static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount, u32 opposingBattler)
|
||||
{
|
||||
int i, j, bits = 0;
|
||||
|
||||
@ -793,6 +775,8 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u
|
||||
{
|
||||
if (invalidMons & gBitTable[i])
|
||||
continue;
|
||||
if (IsAiPartyMonOHKOBy(opposingBattler, &party[i]))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
@ -837,6 +821,9 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId,
|
||||
u8 defType1 = gSpeciesInfo[species].types[0];
|
||||
u8 defType2 = gSpeciesInfo[species].types[1];
|
||||
|
||||
if (IsAiPartyMonOHKOBy(opposingBattler, &party[i]))
|
||||
continue;
|
||||
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType1)));
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType1)));
|
||||
@ -881,7 +868,7 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId,
|
||||
static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler)
|
||||
{
|
||||
int i, j;
|
||||
int bestDmg = 0;
|
||||
int dmg, bestDmg = 0;
|
||||
int bestMonId = PARTY_SIZE;
|
||||
|
||||
gMoveResultFlags = 0;
|
||||
@ -890,21 +877,16 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
||||
{
|
||||
if (gBitTable[i] & invalidMons)
|
||||
continue;
|
||||
if (IsAiPartyMonOHKOBy(opposingBattler, &party[i]))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
u32 move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
|
||||
if (move != MOVE_NONE && gBattleMoves[move].power != 0)
|
||||
{
|
||||
s32 dmg = AI_CalcPartyMonDamage(move, gActiveBattler, opposingBattler, &party[i]);
|
||||
dmg = AI_CalcPartyMonBestMoveDamage(gActiveBattler, opposingBattler, &party[i], NULL);
|
||||
if (bestDmg < dmg)
|
||||
{
|
||||
bestDmg = dmg;
|
||||
bestMonId = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMonId;
|
||||
}
|
||||
@ -912,7 +894,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
||||
u8 GetMostSuitableMonToSwitchInto(void)
|
||||
{
|
||||
u32 opposingBattler = 0;
|
||||
u32 bestMonId = 0;
|
||||
u32 bestMonId = PARTY_SIZE;
|
||||
u8 battlerIn1 = 0, battlerIn2 = 0;
|
||||
s32 firstId = 0;
|
||||
s32 lastId = 0; // + 1
|
||||
@ -954,10 +936,7 @@ u8 GetMostSuitableMonToSwitchInto(void)
|
||||
// Get invalid slots ids.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
u16 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
|
||||
if (species == SPECIES_NONE
|
||||
|| species == SPECIES_EGG
|
||||
|| GetMonData(&party[i], MON_DATA_HP) == 0
|
||||
if (!IsValidForBattle(&party[i])
|
||||
|| gBattlerPartyIndexes[battlerIn1] == i
|
||||
|| gBattlerPartyIndexes[battlerIn2] == i
|
||||
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)
|
||||
@ -977,7 +956,7 @@ u8 GetMostSuitableMonToSwitchInto(void)
|
||||
}
|
||||
}
|
||||
|
||||
bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount);
|
||||
bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount, opposingBattler);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
|
||||
@ -1040,9 +1019,7 @@ static bool8 ShouldUseItem(void)
|
||||
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_HP) != 0
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||||
&& GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG)
|
||||
if (IsValidForBattle(&party[i]))
|
||||
{
|
||||
validMons++;
|
||||
}
|
||||
@ -1155,3 +1132,29 @@ static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount)
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 IsAiPartyMonOHKOBy(u32 battlerAtk, struct Pokemon *aiMon)
|
||||
{
|
||||
bool32 ret = FALSE;
|
||||
struct BattlePokemon *savedBattleMons;
|
||||
s32 hp = GetMonData(aiMon, MON_DATA_HP);
|
||||
s32 bestDmg = AI_CalcPartyMonBestMoveDamage(battlerAtk, gActiveBattler, NULL, aiMon);
|
||||
|
||||
switch (GetNoOfHitsToKO(bestDmg, hp))
|
||||
{
|
||||
case 1:
|
||||
ret = TRUE;
|
||||
break;
|
||||
case 2: // if AI mon is faster allow 2 turns
|
||||
savedBattleMons = AllocSaveBattleMons();
|
||||
PokemonToBattleMon(aiMon, &gBattleMons[gActiveBattler]);
|
||||
if (AI_WhoStrikesFirst(gActiveBattler, battlerAtk, 0) == AI_IS_SLOWER)
|
||||
ret = TRUE;
|
||||
else
|
||||
ret = FALSE;
|
||||
FreeRestoreBattleMons(savedBattleMons);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3369,25 +3369,49 @@ bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// party logic
|
||||
s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon *mon)
|
||||
#define SIZE_G_BATTLE_MONS (sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT)
|
||||
|
||||
struct BattlePokemon *AllocSaveBattleMons(void)
|
||||
{
|
||||
s32 dmg;
|
||||
u32 i;
|
||||
struct BattlePokemon *savedBattleMons = Alloc(SIZE_G_BATTLE_MONS);
|
||||
memcpy(savedBattleMons, gBattleMons, SIZE_G_BATTLE_MONS);
|
||||
return savedBattleMons;
|
||||
}
|
||||
|
||||
void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons)
|
||||
{
|
||||
memcpy(gBattleMons, savedBattleMons, SIZE_G_BATTLE_MONS);
|
||||
Free(savedBattleMons);
|
||||
}
|
||||
|
||||
// party logic
|
||||
s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon)
|
||||
{
|
||||
s32 i, move, bestDmg, dmg;
|
||||
u8 effectiveness;
|
||||
struct BattlePokemon *battleMons = Alloc(sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT);
|
||||
struct BattlePokemon *savedBattleMons = AllocSaveBattleMons();
|
||||
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
battleMons[i] = gBattleMons[i];
|
||||
if (attackerMon != NULL)
|
||||
PokemonToBattleMon(attackerMon, &gBattleMons[battlerAtk]);
|
||||
if (targetMon != NULL)
|
||||
PokemonToBattleMon(targetMon, &gBattleMons[battlerDef]);
|
||||
|
||||
PokemonToBattleMon(mon, &gBattleMons[battlerAtk]);
|
||||
for (bestDmg = 0, i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (BattlerHasAi(battlerAtk))
|
||||
move = GetMonData(attackerMon, MON_DATA_MOVE1 + i);
|
||||
else
|
||||
move = AI_PARTY->mons[GET_BATTLER_SIDE2(battlerAtk)][gBattlerPartyIndexes[battlerAtk]].moves[i];
|
||||
|
||||
if (move != MOVE_NONE && gBattleMoves[move].power != 0)
|
||||
{
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, FALSE);
|
||||
if (dmg > bestDmg)
|
||||
bestDmg = dmg;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
gBattleMons[i] = battleMons[i];
|
||||
|
||||
Free(battleMons);
|
||||
|
||||
FreeRestoreBattleMons(savedBattleMons);
|
||||
return dmg;
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,9 @@ static void AnimGrassKnotStep(struct Sprite *);
|
||||
static void AnimGrassKnot(struct Sprite *);
|
||||
static void AnimWoodHammerSmall(struct Sprite *);
|
||||
static void AnimWoodHammerBig(struct Sprite *);
|
||||
static void AnimWoodHammerHammer(struct Sprite *);
|
||||
static void AnimWoodHammerHammer_WaitForPunch(struct Sprite *);
|
||||
static void AnimWoodHammerHammer_WaitForDestruction(struct Sprite *);
|
||||
static void AnimTask_DoubleTeam_Step(u8);
|
||||
static void AnimDoubleTeam(struct Sprite *);
|
||||
static void AnimNightSlash(struct Sprite *);
|
||||
@ -2847,24 +2850,67 @@ const union AffineAnimCmd *const gWoodHammerBigAffineAnims[] =
|
||||
gWoodHammerBigAffineAnimCmd_2,
|
||||
};
|
||||
|
||||
const union AnimCmd gWoodHammerSmallAnimCmd_1[] =
|
||||
#define WOOD_HAMMER_SCALE_STEP 5
|
||||
#define WOOD_HAMMER_CC_ROTATION_STEP 2
|
||||
#define WOOD_HAMMER_BACKWARDS_DURATION 40
|
||||
#define WOOD_HAMMER_ROTATED_AMOUNT (WOOD_HAMMER_CC_ROTATION_STEP * WOOD_HAMMER_BACKWARDS_DURATION)
|
||||
#define WOOD_HAMMER_SCALED_AMOUNT (WOOD_HAMMER_SCALE_STEP * WOOD_HAMMER_BACKWARDS_DURATION)
|
||||
|
||||
const union AffineAnimCmd gWoodHammerHammerAffineAnimCmd_BackwardsRotateAndScale[] =
|
||||
{
|
||||
ANIMCMD_FRAME(32, 1),
|
||||
ANIMCMD_END,
|
||||
AFFINEANIMCMD_FRAME(WOOD_HAMMER_SCALE_STEP, WOOD_HAMMER_SCALE_STEP, WOOD_HAMMER_CC_ROTATION_STEP, WOOD_HAMMER_BACKWARDS_DURATION),
|
||||
AFFINEANIMCMD_END
|
||||
};
|
||||
|
||||
const union AnimCmd gWoodHammerSmallAnimCmd_2[] =
|
||||
const union AffineAnimCmd gWoodHammerHammerAffineAnimCmd_BackwardsRotateAndScaleFlipped[] =
|
||||
{
|
||||
AFFINEANIMCMD_FRAME(-0x100, 0x100, 0, 0),
|
||||
AFFINEANIMCMD_FRAME(-WOOD_HAMMER_SCALE_STEP, WOOD_HAMMER_SCALE_STEP, -WOOD_HAMMER_CC_ROTATION_STEP, WOOD_HAMMER_BACKWARDS_DURATION),
|
||||
AFFINEANIMCMD_END
|
||||
};
|
||||
|
||||
const union AffineAnimCmd gWoodHammerHammerAffineAnimCmd_PunchClockwise[] =
|
||||
{
|
||||
AFFINEANIMCMD_FRAME(0x100 + WOOD_HAMMER_SCALED_AMOUNT, 0x100 + WOOD_HAMMER_SCALED_AMOUNT, WOOD_HAMMER_ROTATED_AMOUNT, 0),
|
||||
AFFINEANIMCMD_FRAME(0, 0, -16, 7),
|
||||
AFFINEANIMCMD_END
|
||||
};
|
||||
|
||||
const union AffineAnimCmd gWoodHammerHammerAffineAnimCmd_PunchCounterClockwise[] =
|
||||
{
|
||||
AFFINEANIMCMD_FRAME(-0x100 - WOOD_HAMMER_SCALED_AMOUNT, 0x100 + WOOD_HAMMER_SCALED_AMOUNT, -WOOD_HAMMER_ROTATED_AMOUNT, 0),
|
||||
AFFINEANIMCMD_FRAME(0, 0, 16, 7),
|
||||
AFFINEANIMCMD_END
|
||||
};
|
||||
|
||||
// Animations 0, 2 are for the player side attacking
|
||||
// Animations 1, 3 are for the opponent side attacking (flipped)
|
||||
const union AffineAnimCmd *const gWoodHammerHammerAffineAnims[] =
|
||||
{
|
||||
gWoodHammerHammerAffineAnimCmd_BackwardsRotateAndScale,
|
||||
gWoodHammerHammerAffineAnimCmd_BackwardsRotateAndScaleFlipped,
|
||||
gWoodHammerHammerAffineAnimCmd_PunchClockwise,
|
||||
gWoodHammerHammerAffineAnimCmd_PunchCounterClockwise,
|
||||
};
|
||||
|
||||
const union AnimCmd gWoodHammerSmallAnimCmd_1[] =
|
||||
{
|
||||
ANIMCMD_FRAME(48, 1),
|
||||
ANIMCMD_END,
|
||||
};
|
||||
|
||||
const union AnimCmd gWoodHammerSmallAnimCmd_3[] =
|
||||
const union AnimCmd gWoodHammerSmallAnimCmd_2[] =
|
||||
{
|
||||
ANIMCMD_FRAME(64, 1),
|
||||
ANIMCMD_END,
|
||||
};
|
||||
|
||||
const union AnimCmd gWoodHammerSmallAnimCmd_3[] =
|
||||
{
|
||||
ANIMCMD_FRAME(80, 1),
|
||||
ANIMCMD_END,
|
||||
};
|
||||
|
||||
const union AnimCmd *const gWoodHammerSmallAnims[] =
|
||||
{
|
||||
gWoodHammerSmallAnimCmd_1,
|
||||
@ -2905,6 +2951,17 @@ const struct SpriteTemplate gWoodHammerSmallSpriteTemplate =
|
||||
.callback = AnimWoodHammerSmall,
|
||||
};
|
||||
|
||||
const struct SpriteTemplate gWoodHammerHammerSpriteTemplate =
|
||||
{
|
||||
.tileTag = ANIM_TAG_WOOD_HAMMER_HAMMER,
|
||||
.paletteTag = ANIM_TAG_WOOD_HAMMER_HAMMER,
|
||||
.oam = &gOamData_AffineDouble_ObjNormal_64x64,
|
||||
.anims = gDummySpriteAnimTable,
|
||||
.images = NULL,
|
||||
.affineAnims = gWoodHammerHammerAffineAnims,
|
||||
.callback = AnimWoodHammerHammer,
|
||||
};
|
||||
|
||||
const struct SpriteTemplate gJudgmentGrayOutwardSpikesTemplate =
|
||||
{
|
||||
.tileTag = ANIM_TAG_GREEN_SPIKE,
|
||||
@ -3022,6 +3079,65 @@ static void AnimWoodHammerSmall(struct Sprite *sprite)
|
||||
StoreSpriteCallbackInData6(sprite, DestroySpriteAndMatrix);
|
||||
}
|
||||
|
||||
#define HAMMER_X_OFFSET 40
|
||||
#define HAMMER_PUNCH_WAIT_FRAMES 37
|
||||
|
||||
static void AnimWoodHammerHammer(struct Sprite *sprite)
|
||||
{
|
||||
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
|
||||
{
|
||||
sprite->x += HAMMER_X_OFFSET;
|
||||
StartSpriteAffineAnim(sprite, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite->x -= HAMMER_X_OFFSET;
|
||||
StartSpriteAffineAnim(sprite, 0);
|
||||
}
|
||||
sprite->data[6] = HAMMER_PUNCH_WAIT_FRAMES;
|
||||
sprite->callback = AnimWoodHammerHammer_WaitForPunch;
|
||||
}
|
||||
|
||||
static void AnimWoodHammerHammer_WaitForPunch(struct Sprite *sprite)
|
||||
{
|
||||
if (!sprite->affineAnimEnded)
|
||||
return;
|
||||
|
||||
if (sprite->data[6] != 0)
|
||||
{
|
||||
sprite->data[6]--;
|
||||
if (sprite->data[6] & 1)
|
||||
{
|
||||
if ((sprite->data[6] / 2) & 1)
|
||||
sprite->x2++;
|
||||
else
|
||||
sprite->x2--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
|
||||
{
|
||||
StartSpriteAffineAnim(sprite, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartSpriteAffineAnim(sprite, 2);
|
||||
}
|
||||
sprite->callback = AnimWoodHammerHammer_WaitForDestruction;
|
||||
}
|
||||
|
||||
static void AnimWoodHammerHammer_WaitForDestruction(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->affineAnimEnded)
|
||||
{
|
||||
DestroySpriteAndMatrix(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
#undef HAMMER_X_OFFSET
|
||||
#undef HAMMER_PUNCH_WAIT_FRAMES
|
||||
|
||||
// Animates the falling particles that horizontally wave back and forth.
|
||||
// Used by Sleep Powder, Stun Spore, and Poison Powder.
|
||||
// arg 0: initial x pixel offset
|
||||
|
@ -1688,7 +1688,6 @@ static void OpponentHandleChoosePokemon(void)
|
||||
else if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
|
||||
{
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto();
|
||||
|
||||
if (chosenMonId == PARTY_SIZE)
|
||||
{
|
||||
s32 battler1, battler2, firstId, lastId;
|
||||
@ -1702,14 +1701,13 @@ static void OpponentHandleChoosePokemon(void)
|
||||
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
pokemonInBattle = 2;
|
||||
|
||||
}
|
||||
|
||||
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
|
||||
|
||||
for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
|
||||
{
|
||||
if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0
|
||||
if (IsValidForBattle(&gEnemyParty[chosenMonId])
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler1]
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler2]
|
||||
&& (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON)
|
||||
|
@ -268,7 +268,7 @@ static const u8 sActionHighlightMiddle_Gfx[] = INCBIN_U8( "graphics/battle_front
|
||||
static const u8 sActionHighlightRight_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/action_highlight_right.4bpp");
|
||||
static const u8 sMonPicBgAnim_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg_anim.4bpp");
|
||||
static const u8 sMonPicBg_Tilemap[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg.bin");
|
||||
static const u8 sMonPicBg_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg.4bpp");
|
||||
static const u16 sMonPicBg_Gfx[] = INCBIN_U16("graphics/battle_frontier/factory_screen/mon_pic_bg.4bpp");
|
||||
static const u16 sMonPicBg_Pal[] = INCBIN_U16("graphics/battle_frontier/factory_screen/mon_pic_bg.gbapal");
|
||||
|
||||
static const struct SpriteSheet sSelect_SpriteSheets[] =
|
||||
|
@ -1209,10 +1209,13 @@ void AllocateMonSpritesGfx(void)
|
||||
*(gMonSpritesGfxPtr->templates + i) = gBattlerSpriteTemplates[i];
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
if (gMonSpritesGfxPtr->sprites.ptr[i])
|
||||
{
|
||||
gMonSpritesGfxPtr->frameImages[i][j].data = gMonSpritesGfxPtr->sprites.ptr[i] + (j * MON_PIC_SIZE);
|
||||
gMonSpritesGfxPtr->frameImages[i][j].size = MON_PIC_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
gMonSpritesGfxPtr->templates[i].images = gMonSpritesGfxPtr->frameImages[i];
|
||||
}
|
||||
|
@ -1983,7 +1983,6 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
|
||||
u8 fixedIV;
|
||||
s32 i, j;
|
||||
u8 monsCount;
|
||||
s32 ball = -1;
|
||||
if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER
|
||||
| BATTLE_TYPE_EREADER_TRAINER
|
||||
| BATTLE_TYPE_TRAINER_HILL)))
|
||||
@ -2005,6 +2004,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
|
||||
|
||||
for (i = 0; i < monsCount; i++)
|
||||
{
|
||||
s32 ball = -1;
|
||||
u32 personalityHash = GeneratePartyHash(trainer, i);
|
||||
if (trainer->doubleBattle == TRUE)
|
||||
personalityValue = 0x80;
|
||||
@ -3129,6 +3129,9 @@ static void BattleStartClearSetData(void)
|
||||
|
||||
gBattlerAttacker = 0;
|
||||
gBattlerTarget = 0;
|
||||
gEffectBattler = 0;
|
||||
gBattleScripting.battler = 0;
|
||||
gBattlerAbility = 0;
|
||||
gBattleWeather = 0;
|
||||
gHitMarker = 0;
|
||||
|
||||
|
@ -783,7 +783,7 @@ static const u8 sText_AttackerMeltedTheIce[] = _("{B_ATK_NAME_WITH_PREFIX} melte
|
||||
static const u8 sText_TargetToughedItOut[] = _("{B_DEF_NAME_WITH_PREFIX} toughed it out\nto show you its best side!");
|
||||
static const u8 sText_AttackerLostElectricType[] = _("{B_ATK_NAME_WITH_PREFIX} used up all\nof its electricity!");
|
||||
static const u8 sText_AttackerSwitchedStatWithTarget[] = _("{B_ATK_NAME_WITH_PREFIX} switched {B_BUFF1}\nwith its target!");
|
||||
static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURRENT_MOVE}\ncharged {B_ATK_NAME_WITH_PREFIX} with power!");
|
||||
static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURRENT_MOVE}\ncharged {B_DEF_NAME_WITH_PREFIX} with power!");
|
||||
static const u8 sText_SunlightActivatedAbility[] = _("The harsh sunlight activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!");
|
||||
static const u8 sText_StatWasHeightened[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1} was heightened!");
|
||||
static const u8 sText_ElectricTerrainActivatedAbility[] = _("The Electric Terrain activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!");
|
||||
@ -799,9 +799,11 @@ static const u8 sText_ItemCuredSpeciesStatus[] = _("{B_BUFF1} had\nits status he
|
||||
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!");
|
||||
static const u8 sText_PkmnItemMelted[] = _("{B_ATK_NAME_WITH_PREFIX} corroded\n{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}!");
|
||||
|
||||
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
|
||||
{
|
||||
[STRINGID_PKMNITEMMELTED - BATTLESTRINGS_TABLE_START] = sText_PkmnItemMelted,
|
||||
[STRINGID_MIRRORHERBCOPIED - BATTLESTRINGS_TABLE_START] = sText_MirrorHerbCopied,
|
||||
[STRINGID_THUNDERCAGETRAPPED - BATTLESTRINGS_TABLE_START] = sText_AtkTrappedDef,
|
||||
[STRINGID_ITEMRESTOREDSPECIESHEALTH - BATTLESTRINGS_TABLE_START] = sText_ItemRestoredSpeciesHealth,
|
||||
@ -1850,6 +1852,12 @@ const u16 gWeatherStartsStringIds[] =
|
||||
[WEATHER_ABNORMAL] = STRINGID_ITISRAINING
|
||||
};
|
||||
|
||||
const u16 gPrimalWeatherBlocksStringIds[] =
|
||||
{
|
||||
[B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN] = STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN,
|
||||
[B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN] = STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT,
|
||||
};
|
||||
|
||||
const u16 gInobedientStringIds[] =
|
||||
{
|
||||
[B_MSG_LOAFING] = STRINGID_PKMNLOAFING,
|
||||
|
@ -1273,25 +1273,8 @@ static void Cmd_attackcanceler(void)
|
||||
|
||||
s32 i, moveType;
|
||||
u16 attackerAbility = GetBattlerAbility(gBattlerAttacker);
|
||||
|
||||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||||
|
||||
if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power)
|
||||
{
|
||||
if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL))
|
||||
{
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_PrimordialSeaFizzlesOutFireTypeMoves;
|
||||
return;
|
||||
}
|
||||
else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL))
|
||||
{
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_DesolateLandEvaporatesWaterTypeMoves;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (gBattleOutcome != 0)
|
||||
{
|
||||
gCurrentActionFuncId = B_ACTION_FINISHED;
|
||||
@ -1307,9 +1290,27 @@ static void Cmd_attackcanceler(void)
|
||||
if (TryAegiFormChange())
|
||||
return;
|
||||
#endif
|
||||
if (AtkCanceller_UnableToUseMove())
|
||||
if (AtkCanceller_UnableToUseMove(moveType))
|
||||
return;
|
||||
|
||||
if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power)
|
||||
{
|
||||
if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL))
|
||||
{
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
|
||||
return;
|
||||
}
|
||||
else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL))
|
||||
{
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF
|
||||
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
|
||||
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
|
||||
@ -1622,6 +1623,8 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
|
||||
s8 buff, accStage, evasionStage;
|
||||
u8 atkParam = GetBattlerHoldEffectParam(battlerAtk);
|
||||
u8 defParam = GetBattlerHoldEffectParam(battlerDef);
|
||||
u8 atkAlly = BATTLE_PARTNER(battlerAtk);
|
||||
u16 atkAllyAbility = GetBattlerAbility(atkAlly);
|
||||
|
||||
gPotentialItemEffectBattler = battlerDef;
|
||||
accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
|
||||
@ -1655,30 +1658,66 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
|
||||
calc = gAccuracyStageRatios[buff].dividend * moveAcc;
|
||||
calc /= gAccuracyStageRatios[buff].divisor;
|
||||
|
||||
if (atkAbility == ABILITY_COMPOUND_EYES)
|
||||
// Attacker's ability
|
||||
switch (atkAbility)
|
||||
{
|
||||
case ABILITY_COMPOUND_EYES:
|
||||
calc = (calc * 130) / 100; // 1.3 compound eyes boost
|
||||
else if (atkAbility == ABILITY_VICTORY_STAR)
|
||||
break;
|
||||
case ABILITY_VICTORY_STAR:
|
||||
calc = (calc * 110) / 100; // 1.1 victory star boost
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_VICTORY_STAR)
|
||||
calc = (calc * 110) / 100; // 1.1 ally's victory star boost
|
||||
|
||||
if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
|
||||
calc = (calc * 80) / 100; // 1.2 sand veil loss
|
||||
else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
|
||||
calc = (calc * 80) / 100; // 1.2 snow cloak loss
|
||||
else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|
||||
calc = (calc * 50) / 100; // 1.5 tangled feet loss
|
||||
|
||||
if (atkAbility == ABILITY_HUSTLE && IS_MOVE_PHYSICAL(move))
|
||||
break;
|
||||
case ABILITY_HUSTLE:
|
||||
if (IS_MOVE_PHYSICAL(move))
|
||||
calc = (calc * 80) / 100; // 1.2 hustle loss
|
||||
break;
|
||||
}
|
||||
|
||||
if (defHoldEffect == HOLD_EFFECT_EVASION_UP)
|
||||
// Target's ability
|
||||
switch (defAbility)
|
||||
{
|
||||
case ABILITY_SAND_VEIL:
|
||||
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
|
||||
calc = (calc * 80) / 100; // 1.2 sand veil loss
|
||||
break;
|
||||
case ABILITY_SNOW_CLOAK:
|
||||
if (WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
|
||||
calc = (calc * 80) / 100; // 1.2 snow cloak loss
|
||||
break;
|
||||
case ABILITY_TANGLED_FEET:
|
||||
if (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|
||||
calc = (calc * 50) / 100; // 1.5 tangled feet loss
|
||||
break;
|
||||
}
|
||||
|
||||
// Attacker's ally's ability
|
||||
switch (atkAllyAbility)
|
||||
{
|
||||
case ABILITY_VICTORY_STAR:
|
||||
if (IsBattlerAlive(atkAlly))
|
||||
calc = (calc * 110) / 100; // 1.1 ally's victory star boost
|
||||
break;
|
||||
}
|
||||
|
||||
// Attacker's hold effect
|
||||
switch (atkHoldEffect)
|
||||
{
|
||||
case HOLD_EFFECT_WIDE_LENS:
|
||||
calc = (calc * (100 + atkParam)) / 100;
|
||||
break;
|
||||
case HOLD_EFFECT_ZOOM_LENS:
|
||||
if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
|
||||
calc = (calc * (100 + atkParam)) / 100;
|
||||
break;
|
||||
}
|
||||
|
||||
// Target's hold effect
|
||||
switch (defHoldEffect)
|
||||
{
|
||||
case HOLD_EFFECT_EVASION_UP:
|
||||
calc = (calc * (100 - defParam)) / 100;
|
||||
|
||||
if (atkHoldEffect == HOLD_EFFECT_WIDE_LENS)
|
||||
calc = (calc * (100 + atkParam)) / 100;
|
||||
else if (atkHoldEffect == HOLD_EFFECT_ZOOM_LENS && GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
|
||||
calc = (calc * (100 + atkParam)) / 100;
|
||||
break;
|
||||
}
|
||||
|
||||
if (gProtectStructs[battlerAtk].usedMicleBerry)
|
||||
{
|
||||
@ -3931,11 +3970,7 @@ static void Cmd_jumpifsideaffecting(void)
|
||||
u32 flags;
|
||||
const u8 *jumpInstr;
|
||||
|
||||
if (cmd->battler == BS_ATTACKER)
|
||||
side = GET_BATTLER_SIDE(gBattlerAttacker);
|
||||
else
|
||||
side = GET_BATTLER_SIDE(gBattlerTarget);
|
||||
|
||||
side = GET_BATTLER_SIDE(GetBattlerForBattleScript(cmd->battler));
|
||||
flags = cmd->flags;
|
||||
jumpInstr = cmd->jumpInstr;
|
||||
|
||||
@ -7759,7 +7794,9 @@ static void Cmd_removeitem(void)
|
||||
itemId = gBattleMons[gActiveBattler].item;
|
||||
|
||||
// Popped Air Balloon cannot be restored by any means.
|
||||
if (GetBattlerHoldEffect(gActiveBattler, TRUE) != HOLD_EFFECT_AIR_BALLOON)
|
||||
// Corroded items cannot be restored either.
|
||||
if (GetBattlerHoldEffect(gActiveBattler, TRUE) != HOLD_EFFECT_AIR_BALLOON
|
||||
&& gBattleMoves[gCurrentMove].effect != EFFECT_CORROSIVE_GAS)
|
||||
gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gActiveBattler]][GetBattlerSide(gActiveBattler)] = itemId; // Remember if switched out
|
||||
|
||||
gBattleMons[gActiveBattler].item = ITEM_NONE;
|
||||
@ -9935,7 +9972,8 @@ static void Cmd_various(void)
|
||||
if (gBattleMons[gBattlerAttacker].item == ITEM_NONE
|
||||
|| gBattleMons[gBattlerTarget].item != ITEM_NONE
|
||||
|| !CanBattlerGetOrLoseItem(gBattlerAttacker, gBattleMons[gBattlerAttacker].item)
|
||||
|| !CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerAttacker].item))
|
||||
|| !CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerAttacker].item)
|
||||
|| gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerTarget)] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]])
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
@ -14024,10 +14062,11 @@ static void Cmd_setforcedtarget(void)
|
||||
|
||||
static void Cmd_setcharge(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
CMD_ARGS(u8 battler);
|
||||
|
||||
gStatuses3[gBattlerAttacker] |= STATUS3_CHARGED_UP;
|
||||
gDisableStructs[gBattlerAttacker].chargeTimer = 2;
|
||||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||||
gStatuses3[battler] |= STATUS3_CHARGED_UP;
|
||||
gDisableStructs[battler].chargeTimer = 2;
|
||||
gBattlescriptCurrInstr++;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
@ -16074,6 +16113,18 @@ void BS_CheckParentalBondCounter(void)
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
void BS_JumpIfCantLoseItem(void)
|
||||
{
|
||||
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
|
||||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||||
u16 item = gBattleMons[battler].item;
|
||||
|
||||
if (item == ITEM_NONE || !CanBattlerGetOrLoseItem(battler, item))
|
||||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||||
else
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
void BS_GetBattlerSide(void)
|
||||
{
|
||||
NATIVE_ARGS(u8 battler);
|
||||
|
@ -3337,10 +3337,9 @@ void SetAtkCancellerForCalledMove(void)
|
||||
gBattleStruct->isAtkCancelerForCalledMove = TRUE;
|
||||
}
|
||||
|
||||
u8 AtkCanceller_UnableToUseMove(void)
|
||||
u8 AtkCanceller_UnableToUseMove(u32 moveType)
|
||||
{
|
||||
u8 effect = 0;
|
||||
s32 *bideDmg = &gBattleScripting.bideDmg;
|
||||
do
|
||||
{
|
||||
switch (gBattleStruct->atkCancellerTracker)
|
||||
@ -3601,7 +3600,7 @@ u8 AtkCanceller_UnableToUseMove(void)
|
||||
if (gTakenDmg[gBattlerAttacker])
|
||||
{
|
||||
gCurrentMove = MOVE_BIDE;
|
||||
*bideDmg = gTakenDmg[gBattlerAttacker] * 2;
|
||||
gBattleScripting.bideDmg = gTakenDmg[gBattlerAttacker] * 2;
|
||||
gBattlerTarget = gTakenDmgByBattler[gBattlerAttacker];
|
||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||
gBattlerTarget = GetMoveTarget(MOVE_BIDE, MOVE_TARGET_SELECTED + 1);
|
||||
@ -3671,8 +3670,6 @@ u8 AtkCanceller_UnableToUseMove(void)
|
||||
case CANCELLER_POWDER_STATUS:
|
||||
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER)
|
||||
{
|
||||
u32 moveType;
|
||||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||||
if (moveType == TYPE_FIRE)
|
||||
{
|
||||
gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE;
|
||||
@ -5639,7 +5636,6 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
||||
&& TARGET_TURN_DAMAGED
|
||||
&& IsBattlerAlive(gBattlerTarget))
|
||||
{
|
||||
gBattlerAttacker = gBattlerTarget;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_WindPowerActivates;
|
||||
effect++;
|
||||
|
@ -117,7 +117,7 @@ static const struct WindowTemplate sBerryFixWindowTemplates[] = {
|
||||
DUMMY_WIN_TEMPLATE
|
||||
};
|
||||
|
||||
static const u16 sBerryFixPalColors[] = {
|
||||
static const u16 ALIGNED(4) sBerryFixPalColors[] = {
|
||||
RGB_WHITE, RGB_WHITE, RGB(12, 12, 12), RGB(26, 26, 25),
|
||||
RGB(28, 1, 1), RGB(31, 23, 14), RGB(4, 19, 1), RGB(18, 30, 18),
|
||||
RGB(6, 10, 25), RGB(20, 24, 30), RGB_WHITE, RGB(12, 12, 12),
|
||||
|
@ -1300,8 +1300,8 @@ static void Task_ReadyStartLinkContest(u8 taskId)
|
||||
|
||||
static bool8 SetupContestGraphics(u8 *stateVar)
|
||||
{
|
||||
u16 tempPalette1[16];
|
||||
u16 tempPalette2[16];
|
||||
u16 ALIGNED(4) tempPalette1[16];
|
||||
u16 ALIGNED(4) tempPalette2[16];
|
||||
|
||||
switch (*stateVar)
|
||||
{
|
||||
|
@ -1451,6 +1451,7 @@ const struct CompressedSpriteSheet gBattleAnimPicTable[] =
|
||||
{gBattleAnimSpriteGfx_Orbs, 0x0180, ANIM_TAG_STEEL_BEAM},
|
||||
{gBattleAnimSpriteGfx_AuraSphere, 0x200, ANIM_TAG_POLTERGEIST},
|
||||
{gBattleAnimSpriteGfx_Teapot, 0x1800, ANIM_TAG_TEAPOT},
|
||||
{gBattleAnimSpriteGfx_WoodHammerHammer, 0x800, ANIM_TAG_WOOD_HAMMER_HAMMER},
|
||||
};
|
||||
|
||||
const struct CompressedSpritePalette gBattleAnimPaletteTable[] =
|
||||
@ -1902,6 +1903,7 @@ const struct CompressedSpritePalette gBattleAnimPaletteTable[] =
|
||||
{gBattleAnimSpritePal_SteelBeam, ANIM_TAG_STEEL_BEAM},
|
||||
{gBattleAnimSpritePal_Poltergeist, ANIM_TAG_POLTERGEIST},
|
||||
{gBattleAnimSpritePal_Teapot, ANIM_TAG_TEAPOT},
|
||||
{gBattleAnimSpritePal_WoodHammerHammer, ANIM_TAG_WOOD_HAMMER_HAMMER},
|
||||
};
|
||||
|
||||
const struct BattleAnimBackground gBattleAnimBackgroundTable[] =
|
||||
|
@ -12303,7 +12303,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
|
||||
|
||||
[MOVE_CORROSIVE_GAS] =
|
||||
{
|
||||
.effect = EFFECT_PLACEHOLDER, // EFFECT_CORROSIVE_GAS, TODO
|
||||
.effect = EFFECT_CORROSIVE_GAS,
|
||||
.power = 0,
|
||||
.type = TYPE_POISON,
|
||||
.accuracy = 100,
|
||||
|
@ -412,7 +412,7 @@ static void Debug_AddDaycareSteps(u16 numSteps)
|
||||
|
||||
u8 GetNumLevelsGainedFromDaycare(void)
|
||||
{
|
||||
if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004], MON_DATA_SPECIES) != 0)
|
||||
if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004].mon, MON_DATA_SPECIES) != 0)
|
||||
return GetNumLevelsGainedForDaycareMon(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004]);
|
||||
|
||||
return 0;
|
||||
|
@ -1916,7 +1916,7 @@ static void CopyPalette(u16 *dest, u16 pal)
|
||||
|
||||
static void CopyTile(u8 *dest, u16 tile)
|
||||
{
|
||||
u8 buffer[TILE_SIZE_4BPP];
|
||||
u8 ALIGNED(4) buffer[TILE_SIZE_4BPP];
|
||||
u16 mode;
|
||||
u16 i;
|
||||
|
||||
|
@ -61,7 +61,7 @@ static void None_Main(void);
|
||||
static u8 None_Finish(void);
|
||||
|
||||
EWRAM_DATA struct Weather gWeather = {0};
|
||||
EWRAM_DATA static u8 sFieldEffectPaletteColorMapTypes[32] = {0};
|
||||
EWRAM_DATA static u8 ALIGNED(2) sFieldEffectPaletteColorMapTypes[32] = {0};
|
||||
|
||||
static const u8 *sPaletteColorMapTypes;
|
||||
|
||||
@ -111,7 +111,7 @@ void (*const gWeatherPalStateFuncs[])(void) =
|
||||
|
||||
// This table specifies which of the color maps should be
|
||||
// applied to each of the background and sprite palettes.
|
||||
static const u8 sBasePaletteColorMapTypes[32] =
|
||||
static const u8 ALIGNED(2) sBasePaletteColorMapTypes[32] =
|
||||
{
|
||||
// background palettes
|
||||
COLOR_MAP_DARK_CONTRAST,
|
||||
@ -149,7 +149,7 @@ static const u8 sBasePaletteColorMapTypes[32] =
|
||||
COLOR_MAP_DARK_CONTRAST,
|
||||
};
|
||||
|
||||
const u16 gFogPalette[] = INCBIN_U16("graphics/weather/fog.gbapal");
|
||||
const u16 ALIGNED(4) gFogPalette[] = INCBIN_U16("graphics/weather/fog.gbapal");
|
||||
|
||||
void StartWeather(void)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ struct ConnectionFlags
|
||||
u8 east:1;
|
||||
};
|
||||
|
||||
EWRAM_DATA static u16 sBackupMapData[MAX_MAP_DATA_SIZE] = {0};
|
||||
EWRAM_DATA static u16 ALIGNED(4) sBackupMapData[MAX_MAP_DATA_SIZE] = {0};
|
||||
EWRAM_DATA struct MapHeader gMapHeader = {0};
|
||||
EWRAM_DATA struct Camera gCamera = {0};
|
||||
EWRAM_DATA static struct ConnectionFlags sMapConnectionFlags = {0};
|
||||
@ -621,8 +621,8 @@ bool32 CanCameraMoveInDirection(int direction)
|
||||
|
||||
static void SetPositionFromConnection(const struct MapConnection *connection, int direction, int x, int y)
|
||||
{
|
||||
struct MapHeader const *mapHeader;
|
||||
mapHeader = GetMapHeaderFromConnection(connection);
|
||||
struct MapHeader const *mapHeader = GetMapHeaderFromConnection(connection);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case CONNECTION_EAST:
|
||||
@ -641,6 +641,9 @@ static void SetPositionFromConnection(const struct MapConnection *connection, in
|
||||
gSaveBlock1Ptr->pos.x -= connection->offset;
|
||||
gSaveBlock1Ptr->pos.y = mapHeader->mapLayout->height;
|
||||
break;
|
||||
default:
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "SetPositionFromConnection was passed an invalid direction (%d)!", direction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,6 +666,8 @@ bool8 CameraMove(int x, int y)
|
||||
old_x = gSaveBlock1Ptr->pos.x;
|
||||
old_y = gSaveBlock1Ptr->pos.y;
|
||||
connection = GetIncomingConnection(direction, gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y);
|
||||
if (connection)
|
||||
{
|
||||
SetPositionFromConnection(connection, direction, x, y);
|
||||
LoadMapFromCameraTransition(connection->mapGroup, connection->mapNum);
|
||||
gCamera.active = TRUE;
|
||||
@ -672,6 +677,12 @@ bool8 CameraMove(int x, int y)
|
||||
gSaveBlock1Ptr->pos.y += y;
|
||||
MoveMapViewToBackup(direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugPrintfLevel(MGBA_LOG_WARN, "GetIncomingConnection returned an invalid connection inside CameraMove!");
|
||||
}
|
||||
|
||||
}
|
||||
return gCamera.active;
|
||||
}
|
||||
|
||||
|
@ -449,6 +449,9 @@ const u32 gBattleAnimSpriteGfx_SpinningBall[] = INCBIN_U32("graphics/battle_anim
|
||||
const u32 gBattleAnimSpritePal_SpinningBall[] = INCBIN_U32("graphics/battle_anims/unused/spinning_ball.gbapal.lz");
|
||||
const u32 gBattleAnimSpritePal_SpinningBall2[] = INCBIN_U32("graphics/battle_anims/unused/spinning_ball_2.gbapal.lz");
|
||||
|
||||
const u32 gBattleAnimSpriteGfx_WoodHammerHammer[] = INCBIN_U32("graphics/battle_anims/sprites/wood_hammer_hammer.4bpp.lz");
|
||||
const u32 gBattleAnimSpritePal_WoodHammerHammer[] = INCBIN_U32("graphics/battle_anims/sprites/wood_hammer_hammer.gbapal.lz");
|
||||
|
||||
// old battle interface data, unused
|
||||
|
||||
const u32 gOldBattleInterfaceGfx[] = INCBIN_U32("graphics/unused/obi1.4bpp.lz");
|
||||
@ -1710,8 +1713,8 @@ const u32 gRouletteMultiplier_Gfx[] = INCBIN_U32("graphics/roulette/multiplier.4
|
||||
const u16 gFrontierFactorySelectMenu_Pal[] = INCBIN_U16("graphics/battle_frontier/factory_menu1.gbapal");
|
||||
const u16 gFrontierFactorySelectMenu_Pal2[] = INCBIN_U16("graphics/battle_frontier/factory_menu2.gbapal");
|
||||
|
||||
const u8 gFrontierFactorySelectMenu_Gfx[] = INCBIN_U8("graphics/battle_frontier/factory_menu1.4bpp");
|
||||
const u8 gFrontierFactorySelectMenu_Gfx2[] = INCBIN_U8("graphics/battle_frontier/factory_menu2.4bpp");
|
||||
const u16 gFrontierFactorySelectMenu_Gfx[] = INCBIN_U16("graphics/battle_frontier/factory_menu1.4bpp");
|
||||
const u16 gFrontierFactorySelectMenu_Gfx2[] = INCBIN_U16("graphics/battle_frontier/factory_menu2.4bpp");
|
||||
|
||||
const u16 gFrontierFactorySelectMenu_Tilemap[] = INCBIN_U16("graphics/battle_frontier/factory_menu.bin");
|
||||
|
||||
|
@ -1096,6 +1096,10 @@ static u8 SetUpCopyrightScreen(void)
|
||||
REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON;
|
||||
SetSerialCallback(SerialCB_CopyrightScreen);
|
||||
GameCubeMultiBoot_Init(&gMultibootProgramStruct);
|
||||
// REG_DISPCNT needs to be overwritten the second time, because otherwise the intro won't show up on VBA 1.7.2 and John GBA Lite emulators.
|
||||
// The REG_DISPCNT overwrite is NOT needed in m-GBA, No$GBA, VBA 1.8.0, My Boy and Pizza Boy GBA emulators.
|
||||
case 1:
|
||||
REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON;
|
||||
default:
|
||||
UpdatePaletteFade();
|
||||
gMain.state++;
|
||||
|
@ -2436,16 +2436,16 @@ static void PrintPocketNames(const u8 *pocketName1, const u8 *pocketName2)
|
||||
|
||||
static void CopyPocketNameToWindow(u32 a)
|
||||
{
|
||||
u8 (* tileDataBuffer)[32][32];
|
||||
u8 (*tileDataBuffer)[32][32];
|
||||
u8 *windowTileData;
|
||||
int b;
|
||||
if (a > 8)
|
||||
a = 8;
|
||||
tileDataBuffer = &gBagMenu->pocketNameBuffer;
|
||||
windowTileData = (u8 *)GetWindowAttribute(2, WINDOW_TILE_DATA);
|
||||
CpuCopy32(tileDataBuffer[0][a], windowTileData, 0x100); // Top half of pocket name
|
||||
CpuCopy32(&tileDataBuffer[0][a], windowTileData, 0x100); // Top half of pocket name
|
||||
b = a + 16;
|
||||
CpuCopy32(tileDataBuffer[0][b], windowTileData + 0x100, 0x100); // Bottom half of pocket name
|
||||
CpuCopy32(&tileDataBuffer[0][b], windowTileData + 0x100, 0x100); // Bottom half of pocket name
|
||||
CopyWindowToVram(WIN_POCKET_NAME, COPYWIN_GFX);
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ bool8 gRemoteLinkPlayersNotReceived[MAX_LINK_PLAYERS];
|
||||
u8 gBlockReceivedStatus[MAX_LINK_PLAYERS];
|
||||
u32 gLinkFiller2;
|
||||
u16 gLinkHeldKeys;
|
||||
u16 gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
|
||||
u16 ALIGNED(4) gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
|
||||
u32 gLinkStatus;
|
||||
bool8 gLinkDummy1; // Never read
|
||||
bool8 gLinkDummy2; // Never read
|
||||
|
@ -75,7 +75,7 @@ static void Task_FossilFallAndSink(u8);
|
||||
static void SpriteCB_FallingFossil(struct Sprite *);
|
||||
static void UpdateDisintegrationEffect(u8 *, u16, u8, u8, u8);
|
||||
|
||||
static const u8 sBlankTile_Gfx[32] = {0};
|
||||
static const u8 ALIGNED(2) sBlankTile_Gfx[32] = {0};
|
||||
static const u8 sMirageTower_Gfx[] = INCBIN_U8("graphics/misc/mirage_tower.4bpp");
|
||||
static const u16 sMirageTowerTilemap[] = INCBIN_U16("graphics/misc/mirage_tower.bin");
|
||||
static const u16 sFossil_Pal[] = INCBIN_U16("graphics/object_events/pics/misc/fossil.gbapal"); // Unused
|
||||
|
@ -556,12 +556,9 @@ static void InitMapView(void)
|
||||
InitTilesetAnimations();
|
||||
}
|
||||
|
||||
const struct MapLayout *GetMapLayout(void)
|
||||
const struct MapLayout *GetMapLayout(u16 mapLayoutId)
|
||||
{
|
||||
u16 mapLayoutId = gSaveBlock1Ptr->mapLayoutId;
|
||||
if (mapLayoutId)
|
||||
return gMapLayouts[mapLayoutId - 1];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ApplyCurrentWarp(void)
|
||||
@ -618,13 +615,13 @@ static void LoadCurrentMapData(void)
|
||||
sLastMapSectionId = gMapHeader.regionMapSectionId;
|
||||
gMapHeader = *Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
||||
gSaveBlock1Ptr->mapLayoutId = gMapHeader.mapLayoutId;
|
||||
gMapHeader.mapLayout = GetMapLayout();
|
||||
gMapHeader.mapLayout = GetMapLayout(gMapHeader.mapLayoutId);
|
||||
}
|
||||
|
||||
static void LoadSaveblockMapHeader(void)
|
||||
{
|
||||
gMapHeader = *Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
||||
gMapHeader.mapLayout = GetMapLayout();
|
||||
gMapHeader.mapLayout = GetMapLayout(gMapHeader.mapLayoutId);
|
||||
}
|
||||
|
||||
static void SetPlayerCoordsFromWarp(void)
|
||||
@ -1020,7 +1017,7 @@ u8 GetFlashLevel(void)
|
||||
void SetCurrentMapLayout(u16 mapLayoutId)
|
||||
{
|
||||
gSaveBlock1Ptr->mapLayoutId = mapLayoutId;
|
||||
gMapHeader.mapLayout = GetMapLayout();
|
||||
gMapHeader.mapLayout = GetMapLayout(mapLayoutId);
|
||||
}
|
||||
|
||||
void SetObjectEventLoadFlag(u8 flag)
|
||||
|
@ -64,7 +64,7 @@ static EWRAM_DATA struct PaletteStruct sPaletteStructs[NUM_PALETTE_STRUCTS] = {0
|
||||
EWRAM_DATA struct PaletteFadeControl gPaletteFade = {0};
|
||||
static EWRAM_DATA u32 sFiller = 0;
|
||||
static EWRAM_DATA u32 sPlttBufferTransferPending = 0;
|
||||
EWRAM_DATA u8 gPaletteDecompressionBuffer[PLTT_SIZE] = {0};
|
||||
EWRAM_DATA u8 ALIGNED(2) gPaletteDecompressionBuffer[PLTT_SIZE] = {0};
|
||||
|
||||
static const struct PaletteStructTemplate sDummyPaletteStructTemplate = {
|
||||
.id = 0xFFFF,
|
||||
|
@ -1218,13 +1218,13 @@ static u8 LaunchBallFadeMonTaskForPokeball(bool8 unFadeLater, u8 spritePalNum, u
|
||||
#define sTrigIdx data[7]
|
||||
|
||||
// Pokeball in Birch intro, and when receiving via trade
|
||||
void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes, u16 species)
|
||||
void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriority, u8 delay, u32 fadePalettes, u16 species)
|
||||
{
|
||||
u8 spriteId;
|
||||
|
||||
LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[BALL_POKE]);
|
||||
LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[BALL_POKE]);
|
||||
spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subpriortiy);
|
||||
spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subpriority);
|
||||
|
||||
gSprites[spriteId].sMonSpriteId = monSpriteId;
|
||||
gSprites[spriteId].sFinalMonX = gSprites[monSpriteId].x;
|
||||
|
@ -4537,7 +4537,7 @@ static void PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top)
|
||||
|
||||
static void DrawFootprint(u8 windowId, u16 dexNum)
|
||||
{
|
||||
u8 footprint4bpp[TILE_SIZE_4BPP * NUM_FOOTPRINT_TILES];
|
||||
u8 ALIGNED(4) footprint4bpp[TILE_SIZE_4BPP * NUM_FOOTPRINT_TILES];
|
||||
const u8 *footprintGfx = gMonFootprintTable[NationalPokedexNumToSpecies(dexNum)];
|
||||
u32 i, j, tileIdx = 0;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
static EWRAM_DATA u8 *sPokedexAreaMapBgNum = NULL;
|
||||
|
||||
static const u16 sPokedexAreaMap_Pal[] = INCBIN_U16("graphics/pokedex/region_map.gbapal");
|
||||
static const u16 ALIGNED(4) sPokedexAreaMap_Pal[] = INCBIN_U16("graphics/pokedex/region_map.gbapal");
|
||||
static const u32 sPokedexAreaMap_Gfx[] = INCBIN_U32("graphics/pokedex/region_map.8bpp.lz");
|
||||
static const u32 sPokedexAreaMap_Tilemap[] = INCBIN_U32("graphics/pokedex/region_map.bin.lz");
|
||||
static const u32 sPokedexAreaMapAffine_Gfx[] = INCBIN_U32("graphics/pokedex/region_map_affine.8bpp.lz");
|
||||
|
272
src/pokemon.c
272
src/pokemon.c
@ -4688,7 +4688,11 @@ static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 perso
|
||||
return substruct;
|
||||
}
|
||||
|
||||
u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data)
|
||||
/* GameFreak called GetMonData with either 2 or 3 arguments, for type
|
||||
* safety we have a GetMonData macro (in include/pokemon.h) which
|
||||
* dispatches to either GetMonData2 or GetMonData3 based on the number
|
||||
* of arguments. */
|
||||
u32 GetMonData3(struct Pokemon *mon, s32 field, u8 *data)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
@ -4746,7 +4750,13 @@ u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
|
||||
u32 GetMonData2(struct Pokemon *mon, s32 field) __attribute__((alias("GetMonData3")));
|
||||
|
||||
/* GameFreak called GetBoxMonData with either 2 or 3 arguments, for type
|
||||
* safety we have a GetBoxMonData macro (in include/pokemon.h) which
|
||||
* dispatches to either GetBoxMonData2 or GetBoxMonData3 based on the
|
||||
* number of arguments. */
|
||||
u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
|
||||
{
|
||||
s32 i;
|
||||
u32 retVal = 0;
|
||||
@ -4771,88 +4781,9 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
|
||||
boxMon->isEgg = TRUE;
|
||||
substruct3->isEgg = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (field)
|
||||
{
|
||||
case MON_DATA_PERSONALITY:
|
||||
retVal = boxMon->personality;
|
||||
break;
|
||||
case MON_DATA_OT_ID:
|
||||
retVal = boxMon->otId;
|
||||
break;
|
||||
case MON_DATA_NICKNAME:
|
||||
{
|
||||
if (boxMon->isBadEgg)
|
||||
{
|
||||
for (retVal = 0;
|
||||
retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS;
|
||||
data[retVal] = gText_BadEgg[retVal], retVal++) {}
|
||||
|
||||
data[retVal] = EOS;
|
||||
}
|
||||
else if (boxMon->isEgg)
|
||||
{
|
||||
StringCopy(data, gText_EggNickname);
|
||||
retVal = StringLength(data);
|
||||
}
|
||||
else if (boxMon->language == LANGUAGE_JAPANESE)
|
||||
{
|
||||
data[0] = EXT_CTRL_CODE_BEGIN;
|
||||
data[1] = EXT_CTRL_CODE_JPN;
|
||||
|
||||
for (retVal = 2, i = 0;
|
||||
i < 5 && boxMon->nickname[i] != EOS;
|
||||
data[retVal] = boxMon->nickname[i], retVal++, i++) {}
|
||||
|
||||
data[retVal++] = EXT_CTRL_CODE_BEGIN;
|
||||
data[retVal++] = EXT_CTRL_CODE_ENG;
|
||||
data[retVal] = EOS;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (retVal = 0;
|
||||
retVal < POKEMON_NAME_LENGTH;
|
||||
data[retVal] = boxMon->nickname[retVal], retVal++){}
|
||||
|
||||
data[retVal] = EOS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MON_DATA_LANGUAGE:
|
||||
retVal = boxMon->language;
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_BAD_EGG:
|
||||
retVal = boxMon->isBadEgg;
|
||||
break;
|
||||
case MON_DATA_SANITY_HAS_SPECIES:
|
||||
retVal = boxMon->hasSpecies;
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_EGG:
|
||||
retVal = boxMon->isEgg;
|
||||
break;
|
||||
case MON_DATA_OT_NAME:
|
||||
{
|
||||
retVal = 0;
|
||||
|
||||
while (retVal < PLAYER_NAME_LENGTH)
|
||||
{
|
||||
data[retVal] = boxMon->otName[retVal];
|
||||
retVal++;
|
||||
}
|
||||
|
||||
data[retVal] = EOS;
|
||||
break;
|
||||
}
|
||||
case MON_DATA_MARKINGS:
|
||||
retVal = boxMon->markings;
|
||||
break;
|
||||
case MON_DATA_CHECKSUM:
|
||||
retVal = boxMon->checksum;
|
||||
break;
|
||||
case MON_DATA_ENCRYPT_SEPARATOR:
|
||||
retVal = boxMon->unknown;
|
||||
break;
|
||||
case MON_DATA_SPECIES:
|
||||
retVal = boxMon->isBadEgg ? SPECIES_EGG : substruct0->species;
|
||||
break;
|
||||
@ -5095,6 +5026,93 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (field)
|
||||
{
|
||||
case MON_DATA_PERSONALITY:
|
||||
retVal = boxMon->personality;
|
||||
break;
|
||||
case MON_DATA_OT_ID:
|
||||
retVal = boxMon->otId;
|
||||
break;
|
||||
case MON_DATA_NICKNAME:
|
||||
{
|
||||
if (boxMon->isBadEgg)
|
||||
{
|
||||
for (retVal = 0;
|
||||
retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS;
|
||||
data[retVal] = gText_BadEgg[retVal], retVal++) {}
|
||||
|
||||
data[retVal] = EOS;
|
||||
}
|
||||
else if (boxMon->isEgg)
|
||||
{
|
||||
StringCopy(data, gText_EggNickname);
|
||||
retVal = StringLength(data);
|
||||
}
|
||||
else if (boxMon->language == LANGUAGE_JAPANESE)
|
||||
{
|
||||
data[0] = EXT_CTRL_CODE_BEGIN;
|
||||
data[1] = EXT_CTRL_CODE_JPN;
|
||||
|
||||
for (retVal = 2, i = 0;
|
||||
i < 5 && boxMon->nickname[i] != EOS;
|
||||
data[retVal] = boxMon->nickname[i], retVal++, i++) {}
|
||||
|
||||
data[retVal++] = EXT_CTRL_CODE_BEGIN;
|
||||
data[retVal++] = EXT_CTRL_CODE_ENG;
|
||||
data[retVal] = EOS;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (retVal = 0;
|
||||
retVal < POKEMON_NAME_LENGTH;
|
||||
data[retVal] = boxMon->nickname[retVal], retVal++){}
|
||||
|
||||
data[retVal] = EOS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MON_DATA_LANGUAGE:
|
||||
retVal = boxMon->language;
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_BAD_EGG:
|
||||
retVal = boxMon->isBadEgg;
|
||||
break;
|
||||
case MON_DATA_SANITY_HAS_SPECIES:
|
||||
retVal = boxMon->hasSpecies;
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_EGG:
|
||||
retVal = boxMon->isEgg;
|
||||
break;
|
||||
case MON_DATA_OT_NAME:
|
||||
{
|
||||
retVal = 0;
|
||||
|
||||
while (retVal < PLAYER_NAME_LENGTH)
|
||||
{
|
||||
data[retVal] = boxMon->otName[retVal];
|
||||
retVal++;
|
||||
}
|
||||
|
||||
data[retVal] = EOS;
|
||||
break;
|
||||
}
|
||||
case MON_DATA_MARKINGS:
|
||||
retVal = boxMon->markings;
|
||||
break;
|
||||
case MON_DATA_CHECKSUM:
|
||||
retVal = boxMon->checksum;
|
||||
break;
|
||||
case MON_DATA_ENCRYPT_SEPARATOR:
|
||||
retVal = boxMon->unknown;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (field > MON_DATA_ENCRYPT_SEPARATOR)
|
||||
EncryptBoxMon(boxMon);
|
||||
@ -5102,6 +5120,8 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
|
||||
return retVal;
|
||||
}
|
||||
|
||||
u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field) __attribute__((alias("GetBoxMonData3")));
|
||||
|
||||
#define SET8(lhs) (lhs) = *data
|
||||
#define SET16(lhs) (lhs) = data[0] + (data[1] << 8)
|
||||
#define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
|
||||
@ -5176,51 +5196,9 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
|
||||
EncryptBoxMon(boxMon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (field)
|
||||
{
|
||||
case MON_DATA_PERSONALITY:
|
||||
SET32(boxMon->personality);
|
||||
break;
|
||||
case MON_DATA_OT_ID:
|
||||
SET32(boxMon->otId);
|
||||
break;
|
||||
case MON_DATA_NICKNAME:
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; i < POKEMON_NAME_LENGTH; i++)
|
||||
boxMon->nickname[i] = data[i];
|
||||
break;
|
||||
}
|
||||
case MON_DATA_LANGUAGE:
|
||||
SET8(boxMon->language);
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_BAD_EGG:
|
||||
SET8(boxMon->isBadEgg);
|
||||
break;
|
||||
case MON_DATA_SANITY_HAS_SPECIES:
|
||||
SET8(boxMon->hasSpecies);
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_EGG:
|
||||
SET8(boxMon->isEgg);
|
||||
break;
|
||||
case MON_DATA_OT_NAME:
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
|
||||
boxMon->otName[i] = data[i];
|
||||
break;
|
||||
}
|
||||
case MON_DATA_MARKINGS:
|
||||
SET8(boxMon->markings);
|
||||
break;
|
||||
case MON_DATA_CHECKSUM:
|
||||
SET16(boxMon->checksum);
|
||||
break;
|
||||
case MON_DATA_ENCRYPT_SEPARATOR:
|
||||
SET16(boxMon->unknown);
|
||||
break;
|
||||
case MON_DATA_SPECIES:
|
||||
{
|
||||
SET16(substruct0->species);
|
||||
@ -5413,6 +5391,54 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (field)
|
||||
{
|
||||
case MON_DATA_PERSONALITY:
|
||||
SET32(boxMon->personality);
|
||||
break;
|
||||
case MON_DATA_OT_ID:
|
||||
SET32(boxMon->otId);
|
||||
break;
|
||||
case MON_DATA_NICKNAME:
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; i < POKEMON_NAME_LENGTH; i++)
|
||||
boxMon->nickname[i] = data[i];
|
||||
break;
|
||||
}
|
||||
case MON_DATA_LANGUAGE:
|
||||
SET8(boxMon->language);
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_BAD_EGG:
|
||||
SET8(boxMon->isBadEgg);
|
||||
break;
|
||||
case MON_DATA_SANITY_HAS_SPECIES:
|
||||
SET8(boxMon->hasSpecies);
|
||||
break;
|
||||
case MON_DATA_SANITY_IS_EGG:
|
||||
SET8(boxMon->isEgg);
|
||||
break;
|
||||
case MON_DATA_OT_NAME:
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
|
||||
boxMon->otName[i] = data[i];
|
||||
break;
|
||||
}
|
||||
case MON_DATA_MARKINGS:
|
||||
SET8(boxMon->markings);
|
||||
break;
|
||||
case MON_DATA_CHECKSUM:
|
||||
SET16(boxMon->checksum);
|
||||
break;
|
||||
case MON_DATA_ENCRYPT_SEPARATOR:
|
||||
SET16(boxMon->unknown);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (field > MON_DATA_ENCRYPT_SEPARATOR)
|
||||
{
|
||||
|
@ -552,8 +552,8 @@ struct PokemonStorageSystemData
|
||||
u16 *displayMonTilePtr;
|
||||
struct Sprite *displayMonSprite;
|
||||
u16 displayMonPalBuffer[0x40];
|
||||
u8 tileBuffer[MON_PIC_SIZE * MAX_MON_PIC_FRAMES];
|
||||
u8 itemIconBuffer[0x800];
|
||||
u8 ALIGNED(4) tileBuffer[MON_PIC_SIZE * MAX_MON_PIC_FRAMES];
|
||||
u8 ALIGNED(4) itemIconBuffer[0x800];
|
||||
u8 wallpaperBgTilemapBuffer[0x1000];
|
||||
u8 displayMenuTilemapBuffer[0x800];
|
||||
};
|
||||
@ -10150,7 +10150,7 @@ void UpdateSpeciesSpritePSS(struct BoxPokemon *boxMon)
|
||||
{
|
||||
DestroyBoxMonIcon(sStorage->boxMonsSprites[sCursorPosition]);
|
||||
CreateBoxMonIconAtPos(sCursorPosition);
|
||||
SetBoxMonIconObjMode(sCursorPosition, GetMonData(boxMon, MON_DATA_HELD_ITEM) == ITEM_NONE);
|
||||
SetBoxMonIconObjMode(sCursorPosition, GetBoxMonData(boxMon, MON_DATA_HELD_ITEM) == ITEM_NONE);
|
||||
}
|
||||
}
|
||||
sJustOpenedBag = FALSE;
|
||||
|
@ -116,10 +116,12 @@ static const LoopedTask sLoopedTaskFuncs[] =
|
||||
[CONDITION_FUNC_CLOSE_MARKINGS] = LoopedTask_CloseMonMarkingsWindow
|
||||
};
|
||||
|
||||
typedef u8 ALIGNED(4) TilemapBuffer[BG_SCREEN_SIZE];
|
||||
|
||||
struct Pokenav_ConditionMenuGfx
|
||||
{
|
||||
u32 loopedTaskId;
|
||||
u8 tilemapBuffers[3][BG_SCREEN_SIZE];
|
||||
TilemapBuffer tilemapBuffers[3];
|
||||
u8 filler[2];
|
||||
u8 partyPokeballSpriteIds[PARTY_SIZE + 1];
|
||||
u32 (*callback)(void);
|
||||
|
@ -35,7 +35,7 @@ struct Pokenav_RegionMapGfx
|
||||
u32 loopTaskId;
|
||||
u16 infoWindowId;
|
||||
struct Sprite *cityZoomTextSprites[3];
|
||||
u8 tilemapBuffer[BG_SCREEN_SIZE];
|
||||
u8 ALIGNED(2) tilemapBuffer[BG_SCREEN_SIZE];
|
||||
u8 cityZoomPics[NUM_CITY_MAPS][200];
|
||||
};
|
||||
|
||||
|
@ -60,10 +60,12 @@ enum
|
||||
|
||||
#define MAX_SMOKE 10
|
||||
|
||||
typedef u8 ALIGNED(4) TilemapBuffer[BG_SCREEN_SIZE];
|
||||
|
||||
struct RayquazaScene
|
||||
{
|
||||
MainCallback exitCallback;
|
||||
u8 tilemapBuffers[4][BG_SCREEN_SIZE];
|
||||
TilemapBuffer tilemapBuffers[4];
|
||||
u16 unk; // never read
|
||||
u8 animId;
|
||||
bool8 endEarly;
|
||||
|
@ -13,7 +13,7 @@ static void CopyValue32Bit(void);
|
||||
// Per-scanline register values.
|
||||
// This is double buffered so that it can be safely written to at any time
|
||||
// without overwriting the buffer that the DMA is currently reading
|
||||
EWRAM_DATA u16 gScanlineEffectRegBuffers[2][0x3C0] = {0};
|
||||
EWRAM_DATA u16 ALIGNED(4) gScanlineEffectRegBuffers[2][0x3C0] = {0};
|
||||
|
||||
EWRAM_DATA struct ScanlineEffect gScanlineEffect = {0};
|
||||
EWRAM_DATA static bool8 sShouldStopWaveTask = FALSE;
|
||||
|
@ -158,7 +158,7 @@ void CopySpriteTiles(u8 shape, u8 size, u8 *tiles, u16 *tilemap, u8 *output)
|
||||
{
|
||||
u8 x, y;
|
||||
s8 i, j;
|
||||
u8 xflip[32];
|
||||
u8 ALIGNED(4) xflip[32];
|
||||
u8 h = sSpriteDimensions[shape][size][1];
|
||||
u8 w = sSpriteDimensions[shape][size][0];
|
||||
|
||||
|
56
test/ability_electromorphosis.c
Normal file
56
test/ability_electromorphosis.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Electromorphosis sets up Charge when hit by any move")
|
||||
{
|
||||
s16 dmgBefore, dmgAfter;
|
||||
u16 move;
|
||||
|
||||
PARAMETRIZE {move = MOVE_TACKLE; }
|
||||
PARAMETRIZE {move = MOVE_GUST; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_GUST].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL);
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
ASSUME(gBattleMoves[MOVE_THUNDERBOLT].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
|
||||
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ELECTROMORPHOSIS); Speed(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed.
|
||||
}
|
||||
WHEN {
|
||||
TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
|
||||
TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
|
||||
HP_BAR(opponent, captureDamage: &dmgBefore);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
|
||||
HP_BAR(player);
|
||||
ABILITY_POPUP(player, ABILITY_ELECTROMORPHOSIS);
|
||||
if (move == MOVE_TACKLE) {
|
||||
MESSAGE("Being hit by Tackle charged Wobbuffet with power!");
|
||||
}
|
||||
else {
|
||||
MESSAGE("Being hit by Gust charged Wobbuffet with power!");
|
||||
}
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
|
||||
HP_BAR(opponent, captureDamage: &dmgAfter);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
|
||||
HP_BAR(player);
|
||||
ABILITY_POPUP(player, ABILITY_ELECTROMORPHOSIS);
|
||||
if (move == MOVE_TACKLE) {
|
||||
MESSAGE("Being hit by Tackle charged Wobbuffet with power!");
|
||||
}
|
||||
else {
|
||||
MESSAGE("Being hit by Gust charged Wobbuffet with power!");
|
||||
}
|
||||
}
|
||||
THEN {
|
||||
EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter);
|
||||
}
|
||||
}
|
93
test/ability_rattled.c
Normal file
93
test/ability_rattled.c
Normal file
@ -0,0 +1,93 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_FURY_CUTTER].type == TYPE_BUG);
|
||||
ASSUME(gBattleMoves[MOVE_FURY_CUTTER].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_FEINT_ATTACK].type == TYPE_DARK);
|
||||
ASSUME(gBattleMoves[MOVE_FEINT_ATTACK].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_SHADOW_PUNCH].type == TYPE_GHOST);
|
||||
ASSUME(gBattleMoves[MOVE_SHADOW_PUNCH].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].type == TYPE_NORMAL);
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rattled boosts speed by 1 when hit by Bug, Dark or Ghost type move")
|
||||
{
|
||||
u16 move;
|
||||
PARAMETRIZE { move = MOVE_FURY_CUTTER; }
|
||||
PARAMETRIZE { move = MOVE_FEINT_ATTACK; }
|
||||
PARAMETRIZE { move = MOVE_SHADOW_PUNCH; }
|
||||
PARAMETRIZE { move = MOVE_TACKLE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(42) ;}
|
||||
OPPONENT(SPECIES_SUDOWOODO) {Speed(40); Ability(ABILITY_RATTLED);}
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
HP_BAR(opponent);
|
||||
if (move != MOVE_TACKLE) {
|
||||
ABILITY_POPUP(opponent, ABILITY_RATTLED);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Sudowoodo's Speed rose!");
|
||||
}
|
||||
MESSAGE("Foe Sudowoodo used Celebrate!");
|
||||
// Sudowoodo is now faster
|
||||
if (move != MOVE_TACKLE){
|
||||
MESSAGE("Foe Sudowoodo used Celebrate!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
HP_BAR(opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_RATTLED);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Sudowoodo's Speed rose!");
|
||||
}
|
||||
else {
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("Foe Sudowoodo used Celebrate!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rattled boosts speed by 1 when affected by Intimidate")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_UPDATED_INTIMIDATE >= GEN_8);
|
||||
PLAYER(SPECIES_GYARADOS) {Ability(ABILITY_INTIMIDATE); }
|
||||
OPPONENT(SPECIES_SUDOWOODO) {Ability(ABILITY_RATTLED); }
|
||||
} WHEN {
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Gyarados's Intimidate cuts Foe Sudowoodo's attack!");
|
||||
ABILITY_POPUP(opponent, ABILITY_RATTLED);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Sudowoodo's Speed rose!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rattled triggers correctly when hit by U-Turn") // Specific test here, because of #3124
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
|
||||
ASSUME(gBattleMoves[MOVE_U_TURN].type == TYPE_BUG);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_SUDOWOODO) {Ability(ABILITY_RATTLED); }
|
||||
OPPONENT(SPECIES_SUDOWOODO);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used U-turn!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
HP_BAR(opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_RATTLED);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Sudowoodo's Speed rose!");
|
||||
MESSAGE("Go! Wynaut!");
|
||||
}
|
||||
}
|
89
test/ability_stamina.c
Normal file
89
test/ability_stamina.c
Normal file
@ -0,0 +1,89 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
#define STAMINA_STAT_RAISE(target, msg) \
|
||||
{ \
|
||||
ABILITY_POPUP(target, ABILITY_STAMINA); \
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, target); \
|
||||
MESSAGE(msg); \
|
||||
}
|
||||
|
||||
#define STAMINA_HIT(attacker, target, move, msg, dmgVar) \
|
||||
{ \
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, attacker); \
|
||||
HP_BAR(target, captureDamage: &dmgVar); \
|
||||
STAMINA_STAT_RAISE(target, msg); \
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stamina raises Defense by 1 when hit by a move")
|
||||
{
|
||||
s16 turnOneHit, turnTwoHit;
|
||||
u16 move;
|
||||
|
||||
PARAMETRIZE {move = MOVE_TACKLE; }
|
||||
PARAMETRIZE {move = MOVE_GUST; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_GUST].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL);
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_STAMINA); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, move); }
|
||||
TURN { MOVE(opponent, move); }
|
||||
} SCENE {
|
||||
STAMINA_HIT(opponent, player, move, "Wobbuffet's Defense rose!", turnOneHit);
|
||||
STAMINA_HIT(opponent, player, move, "Wobbuffet's Defense rose!", turnTwoHit);
|
||||
}
|
||||
THEN {
|
||||
if (move == MOVE_TACKLE) {
|
||||
EXPECT_MUL_EQ(turnTwoHit, Q_4_12(1.5), turnOneHit);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(turnTwoHit, turnOneHit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Stamina activates correctly for every battler with the ability when hit by a multi target move")
|
||||
{
|
||||
u16 abilityLeft, abilityRight;
|
||||
|
||||
PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_STAMINA; }
|
||||
PARAMETRIZE {abilityLeft = ABILITY_STAMINA, abilityRight = ABILITY_NONE; }
|
||||
PARAMETRIZE {abilityLeft = ABILITY_STAMINA, abilityRight = ABILITY_STAMINA; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(20); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(15); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_EARTHQUAKE);}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, opponentLeft);
|
||||
|
||||
HP_BAR(playerLeft);
|
||||
if (abilityLeft == ABILITY_STAMINA) {
|
||||
STAMINA_STAT_RAISE(playerLeft, "Wobbuffet's Defense rose!");
|
||||
}
|
||||
NOT HP_BAR(opponentLeft); // We need to check the attacker itself does NOT get damaged. There was an issue when the targets would get overwritten by the Stamina's stat raise.
|
||||
|
||||
HP_BAR(playerRight);
|
||||
if (abilityRight == ABILITY_STAMINA) {
|
||||
STAMINA_STAT_RAISE(playerRight, "Wobbuffet's Defense rose!");
|
||||
}
|
||||
NOT HP_BAR(opponentLeft); // We need to check the attacker itself does NOT get damaged. There was an issue when the targets would get overwritten by the Stamina's stat raise.
|
||||
|
||||
HP_BAR(opponentRight);
|
||||
}
|
||||
THEN {
|
||||
EXPECT_NE(playerLeft->hp, playerLeft->maxHP);
|
||||
EXPECT_NE(playerRight->hp, playerRight->maxHP);
|
||||
EXPECT_NE(opponentRight->hp, opponentRight->maxHP);
|
||||
EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
|
||||
}
|
||||
}
|
221
test/ability_wind_power.c
Normal file
221
test/ability_wind_power.c
Normal file
@ -0,0 +1,221 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_THUNDERBOLT].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_AIR_CUTTER].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_AIR_CUTTER].target == MOVE_TARGET_BOTH);
|
||||
ASSUME(gBattleMoves[MOVE_AIR_CUTTER].windMove == TRUE);
|
||||
ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].windMove == TRUE);
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].windMove == FALSE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Wind Power sets up Charge for player when hit by a wind move")
|
||||
{
|
||||
s16 dmgBefore, dmgAfter;
|
||||
u16 move;
|
||||
|
||||
PARAMETRIZE {move = MOVE_TACKLE; }
|
||||
PARAMETRIZE {move = MOVE_AIR_CUTTER; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed.
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
|
||||
TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
|
||||
HP_BAR(opponent, captureDamage: &dmgBefore);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
|
||||
HP_BAR(player);
|
||||
if (move == MOVE_AIR_CUTTER) {
|
||||
ABILITY_POPUP(player, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
|
||||
}
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
|
||||
HP_BAR(opponent, captureDamage: &dmgAfter);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
|
||||
HP_BAR(player);
|
||||
if (move == MOVE_AIR_CUTTER) {
|
||||
ABILITY_POPUP(player, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
|
||||
}
|
||||
}
|
||||
THEN {
|
||||
if (move == MOVE_AIR_CUTTER) {
|
||||
EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(dmgAfter, dmgBefore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Wind Power sets up Charge for opponent when hit by a wind move")
|
||||
{
|
||||
s16 dmgBefore, dmgAfter;
|
||||
u16 move;
|
||||
|
||||
PARAMETRIZE {move = MOVE_TACKLE; }
|
||||
PARAMETRIZE {move = MOVE_AIR_CUTTER; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed.
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_THUNDERBOLT), MOVE(player, move); }
|
||||
TURN { MOVE(opponent, MOVE_THUNDERBOLT), MOVE(player, move); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, opponent);
|
||||
HP_BAR(player, captureDamage: &dmgBefore);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
HP_BAR(opponent);
|
||||
if (move == MOVE_AIR_CUTTER) {
|
||||
ABILITY_POPUP(opponent, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Air Cutter charged Foe Wobbuffet with power!");
|
||||
}
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, opponent);
|
||||
HP_BAR(player, captureDamage: &dmgAfter);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
HP_BAR(opponent);
|
||||
if (move == MOVE_AIR_CUTTER) {
|
||||
ABILITY_POPUP(opponent, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Air Cutter charged Foe Wobbuffet with power!");
|
||||
}
|
||||
}
|
||||
THEN {
|
||||
if (move == MOVE_AIR_CUTTER) {
|
||||
EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(dmgAfter, dmgBefore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a 2/3 target move")
|
||||
{
|
||||
u16 move, abilityLeft, abilityRight;
|
||||
|
||||
PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER;}
|
||||
PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_NONE; }
|
||||
PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_WIND_POWER; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(20); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(15); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_AIR_CUTTER); MOVE(opponentRight, MOVE_AIR_CUTTER);}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AIR_CUTTER, opponentLeft);
|
||||
|
||||
HP_BAR(playerLeft);
|
||||
if (abilityLeft == ABILITY_WIND_POWER) {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
|
||||
}
|
||||
HP_BAR(playerRight);
|
||||
if (abilityRight == ABILITY_WIND_POWER) {
|
||||
ABILITY_POPUP(playerRight, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
|
||||
}
|
||||
NOT HP_BAR(opponentLeft);
|
||||
NOT HP_BAR(opponentRight);
|
||||
}
|
||||
THEN {
|
||||
EXPECT_NE(playerLeft->hp, playerLeft->maxHP);
|
||||
EXPECT_NE(playerRight->hp, playerRight->maxHP);
|
||||
EXPECT_EQ(opponentRight->hp, opponentRight->maxHP);
|
||||
EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a 3 target move")
|
||||
{
|
||||
u16 abilityLeft, abilityRight;
|
||||
|
||||
PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER; }
|
||||
PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_NONE; }
|
||||
PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_WIND_POWER; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(20); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(15); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_PETAL_BLIZZARD);}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_BLIZZARD, opponentLeft);
|
||||
|
||||
HP_BAR(playerLeft);
|
||||
if (abilityLeft == ABILITY_WIND_POWER) {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by PetalBlizzrd charged Wobbuffet with power!");
|
||||
}
|
||||
HP_BAR(playerRight);
|
||||
if (abilityRight == ABILITY_WIND_POWER) {
|
||||
ABILITY_POPUP(playerRight, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by PetalBlizzrd charged Wobbuffet with power!");
|
||||
}
|
||||
HP_BAR(opponentRight);
|
||||
NOT HP_BAR(opponentLeft);
|
||||
}
|
||||
THEN {
|
||||
EXPECT_NE(playerLeft->hp, playerLeft->maxHP);
|
||||
EXPECT_NE(playerRight->hp, playerRight->maxHP);
|
||||
EXPECT_NE(opponentRight->hp, opponentRight->maxHP);
|
||||
EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Wind Power activates correctly when Tailwind is used")
|
||||
{
|
||||
bool8 opponentSide;
|
||||
|
||||
PARAMETRIZE {opponentSide = TRUE;}
|
||||
PARAMETRIZE {opponentSide = FALSE;}
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_TAILWIND].effect == EFFECT_TAILWIND);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(5); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(20); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(15); }
|
||||
} WHEN {
|
||||
TURN { MOVE((opponentSide == TRUE) ? opponentLeft : playerLeft, MOVE_TAILWIND);}
|
||||
} SCENE {
|
||||
if (opponentSide) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentLeft);
|
||||
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Tailwind charged Foe Wobbuffet with power!");
|
||||
|
||||
ABILITY_POPUP(opponentRight, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Tailwind charged Foe Wobbuffet with power!");
|
||||
}
|
||||
else {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, playerLeft);
|
||||
|
||||
ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Tailwind charged Wobbuffet with power!");
|
||||
|
||||
ABILITY_POPUP(playerRight, ABILITY_WIND_POWER);
|
||||
MESSAGE("Being hit by Tailwind charged Wobbuffet with power!");
|
||||
}
|
||||
}
|
||||
}
|
119
test/move_effect_corrosive_gas.c
Normal file
119
test/move_effect_corrosive_gas.c
Normal file
@ -0,0 +1,119 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_CORROSIVE_GAS].effect == EFFECT_CORROSIVE_GAS);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Corrosive Gas destroys the target's item or fails if the target has no item")
|
||||
{
|
||||
u16 item;
|
||||
|
||||
PARAMETRIZE {item = ITEM_NONE; }
|
||||
PARAMETRIZE {item = ITEM_POTION; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Item(item); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CORROSIVE_GAS); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used CorrosiveGas!");
|
||||
if (item == ITEM_POTION) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CORROSIVE_GAS, player);
|
||||
MESSAGE("Wobbuffet corroded Foe Wobbuffet's Potion!");
|
||||
}
|
||||
else {
|
||||
MESSAGE("It had no effect on Foe Wobbuffet!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Corrosive Gas doesn't destroy the item of a Pokemon with the Sticky Hold ability")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_MUK) {Item(ITEM_POISON_BARB); Ability(ABILITY_STICKY_HOLD); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CORROSIVE_GAS); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used CorrosiveGas!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CORROSIVE_GAS, player);
|
||||
NOT MESSAGE("Wobbuffet corroded Foe Wobbuffet's Potion!");
|
||||
ABILITY_POPUP(opponent, ABILITY_STICKY_HOLD);
|
||||
MESSAGE("Foe Muk's Sticky Hold made CorrosiveGas ineffective!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_POISON_BARB);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Items lost to Corrosive Gas cannot be restored by Recycle")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_RECYCLE].effect == EFFECT_RECYCLE);
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(15); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Item(ITEM_ORAN_BERRY); Speed(10); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CORROSIVE_GAS); MOVE(opponent, MOVE_RECYCLE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used CorrosiveGas!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CORROSIVE_GAS, player);
|
||||
MESSAGE("Wobbuffet corroded Foe Wobbuffet's Oran Berry!");
|
||||
MESSAGE("Foe Wobbuffet used Recycle!");
|
||||
MESSAGE("But it failed!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Corrosive Gas destroys foes and ally's items if they have one")
|
||||
{
|
||||
// Check it affects all targets in all possible configurations.
|
||||
u32 j, k, l;
|
||||
u16 itemOpponentLeft, itemOpponentRight, itemPlayerLeft;
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
for (k = 0; k < 2; k++) {
|
||||
for (l = 0; l < 2; l++) {
|
||||
PARAMETRIZE {itemOpponentLeft = (j & 1) ? ITEM_ORAN_BERRY : ITEM_NONE;
|
||||
itemOpponentRight = (k & 1) ? ITEM_CHESTO_BERRY : ITEM_NONE;
|
||||
itemPlayerLeft = (l & 1) ? ITEM_CHERI_BERRY : ITEM_NONE; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) {Item(itemPlayerLeft);}
|
||||
PLAYER(SPECIES_WYNAUT) {Item(ITEM_SITRUS_BERRY);}
|
||||
OPPONENT(SPECIES_ABRA) {Item(itemOpponentLeft);}
|
||||
OPPONENT(SPECIES_KADABRA) {Item(itemOpponentRight);}
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_CORROSIVE_GAS); }
|
||||
} SCENE {
|
||||
MESSAGE("Wynaut used CorrosiveGas!");
|
||||
if (itemPlayerLeft == ITEM_CHERI_BERRY) {
|
||||
MESSAGE("Wynaut corroded Wobbuffet's Cheri Berry!");
|
||||
} else {
|
||||
MESSAGE("It had no effect on Wobbuffet!");
|
||||
}
|
||||
if (itemOpponentLeft == ITEM_ORAN_BERRY) {
|
||||
MESSAGE("Wynaut corroded Foe Abra's Oran Berry!");
|
||||
} else {
|
||||
MESSAGE("It had no effect on Foe Abra!");
|
||||
}
|
||||
if (itemOpponentRight == ITEM_CHESTO_BERRY) {
|
||||
MESSAGE("Wynaut corroded Foe Kadabra's Chesto Berry!");
|
||||
} else {
|
||||
MESSAGE("It had no effect on Foe Kadabra!");
|
||||
}
|
||||
|
||||
} THEN {
|
||||
EXPECT_EQ(playerRight->item, ITEM_SITRUS_BERRY); // Attacker doesn't lose its item.
|
||||
EXPECT_EQ(playerLeft->item, ITEM_NONE);
|
||||
EXPECT_EQ(opponentLeft->item, ITEM_NONE);
|
||||
EXPECT_EQ(opponentRight->item, ITEM_NONE);
|
||||
}
|
||||
}
|
127
test/primal_weather.c
Normal file
127
test/primal_weather.c
Normal file
@ -0,0 +1,127 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_EMBER].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
|
||||
ASSUME(gBattleMoves[MOVE_WATER_GUN].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_EMBER); }
|
||||
TURN { MOVE(opponent, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Ember!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
|
||||
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
|
||||
NOT HP_BAR(player);
|
||||
MESSAGE("Foe Wobbuffet used Ember!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
|
||||
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
|
||||
NOT HP_BAR(player);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->hp, player->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE);
|
||||
ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH);
|
||||
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}}
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(10);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(8);}
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_ERUPTION); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Eruption!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft);
|
||||
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
|
||||
NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->hp, playerLeft->maxHP);
|
||||
EXPECT_EQ(playerRight->hp, playerRight->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
|
||||
MESSAGE("Foe Wobbuffet is fast asleep.");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);}
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_WATER_GUN); }
|
||||
TURN { MOVE(opponent, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Water Gun!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
|
||||
MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
|
||||
NOT HP_BAR(player);
|
||||
MESSAGE("Foe Wobbuffet used Water Gun!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
|
||||
MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
|
||||
NOT HP_BAR(player);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->hp, player->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves and prints the message only once with moves hitting multiple targets")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_SURF].power != 0);
|
||||
ASSUME(gBattleMoves[MOVE_SURF].type == TYPE_WATER);
|
||||
ASSUME(gBattleMoves[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB); {Speed(5);}}
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(10);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(8);}
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_SURF); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Surf!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentLeft);
|
||||
MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
|
||||
NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->hp, playerLeft->maxHP);
|
||||
EXPECT_EQ(playerRight->hp, playerRight->maxHP);
|
||||
EXPECT_EQ(opponentRight->hp, opponentRight->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Desolate Land does not block a move if pokemon is asleep and uses a Water-type move") // Sleep/confusion/paralysis all happen before the check for primal weather
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
|
||||
MESSAGE("Foe Wobbuffet is fast asleep.");
|
||||
}
|
||||
}
|
@ -193,3 +193,60 @@ TEST("RandomElement generates a uniform distribution")
|
||||
|
||||
EXPECT_LT(error, UQ_4_12(0.025));
|
||||
}
|
||||
|
||||
TEST("RandomUniform mul-based faster than mod-based (compile-time)")
|
||||
{
|
||||
u32 i;
|
||||
struct Benchmark mulBenchmark, modBenchmark;
|
||||
u32 mulSum = 0, modSum = 0;
|
||||
|
||||
BENCHMARK(&mulBenchmark)
|
||||
{
|
||||
mulSum += RandomUniformDefault(RNG_NONE, 0, 1);
|
||||
mulSum += RandomUniformDefault(RNG_NONE, 0, 2);
|
||||
mulSum += RandomUniformDefault(RNG_NONE, 0, 3);
|
||||
mulSum += RandomUniformDefault(RNG_NONE, 0, 4);
|
||||
}
|
||||
|
||||
BENCHMARK(&modBenchmark)
|
||||
{
|
||||
modSum += Random() % 2;
|
||||
modSum += Random() % 3;
|
||||
modSum += Random() % 4;
|
||||
modSum += Random() % 5;
|
||||
}
|
||||
|
||||
EXPECT_FASTER(mulBenchmark, modBenchmark);
|
||||
|
||||
// Reference mulSum/modSum to prevent optimization.
|
||||
// These numbers are different because multiplication and modulus
|
||||
// have subtly different biases (so subtle that it's irrelevant for
|
||||
// our purposes).
|
||||
EXPECT_EQ(mulSum, 3);
|
||||
EXPECT_EQ(modSum, 4);
|
||||
}
|
||||
|
||||
TEST("RandomUniform mul-based faster than mod-based (run-time)")
|
||||
{
|
||||
u32 i;
|
||||
struct Benchmark mulBenchmark, modBenchmark;
|
||||
u32 mulSum = 0, modSum = 0;
|
||||
|
||||
BENCHMARK(&mulBenchmark)
|
||||
{
|
||||
for (i = 0; i < 32; i++)
|
||||
mulSum += RandomUniformDefault(RNG_NONE, 0, i);
|
||||
}
|
||||
|
||||
BENCHMARK(&modBenchmark)
|
||||
{
|
||||
for (i = 0; i < 32; i++)
|
||||
modSum += Random() % (i + 1);
|
||||
}
|
||||
|
||||
EXPECT_FASTER(mulBenchmark, modBenchmark);
|
||||
|
||||
// Reference mulSum/modSum to prevent optimization.
|
||||
EXPECT_EQ(mulSum, 232);
|
||||
EXPECT_EQ(modSum, 249);
|
||||
}
|
||||
|
303
test/sprite.c
Normal file
303
test/sprite.c
Normal file
@ -0,0 +1,303 @@
|
||||
#include "global.h"
|
||||
#include "test.h"
|
||||
#include "main.h"
|
||||
#include "malloc.h"
|
||||
#include "random.h"
|
||||
#include "sprite.h"
|
||||
|
||||
#define OAM_MATRIX_COUNT 32
|
||||
|
||||
EWRAM_DATA static u16 sSpritePriorities[MAX_SPRITES] = {0};
|
||||
EWRAM_DATA static u8 sSpriteOrder[MAX_SPRITES] = {0};
|
||||
|
||||
static void Old_BuildOamBuffer(void);
|
||||
|
||||
static void ExpectEqOamBuffers(const struct OamData *oldOamBuffer, const struct OamData *newOamBuffer)
|
||||
{
|
||||
u32 i;
|
||||
u32 matrices = 0;
|
||||
|
||||
// Compare the non-matrix data.
|
||||
for (i = 0; i < gOamLimit; i++)
|
||||
{
|
||||
EXPECT(memcmp(&oldOamBuffer[i], &newOamBuffer[i], 6) == 0);
|
||||
if (newOamBuffer[i].affineMode & ST_OAM_AFFINE_ON_MASK)
|
||||
matrices |= 1 << newOamBuffer[i].matrixNum;
|
||||
}
|
||||
|
||||
// Compare the matrix data.
|
||||
for (i = 0; i < OAM_MATRIX_COUNT; i++)
|
||||
{
|
||||
if (matrices & (1 << i))
|
||||
{
|
||||
u32 base = 4 * i;
|
||||
EXPECT_EQ(oldOamBuffer[base + 0].affineParam, newOamBuffer[base + 0].affineParam);
|
||||
EXPECT_EQ(oldOamBuffer[base + 1].affineParam, newOamBuffer[base + 1].affineParam);
|
||||
EXPECT_EQ(oldOamBuffer[base + 2].affineParam, newOamBuffer[base + 2].affineParam);
|
||||
EXPECT_EQ(oldOamBuffer[base + 3].affineParam, newOamBuffer[base + 3].affineParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetSpriteData_(void)
|
||||
{
|
||||
u32 i;
|
||||
ResetSpriteData();
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
sSpriteOrder[i] = i;
|
||||
}
|
||||
|
||||
static void BenchmarkBuildOamBuffer(bool32 preSort)
|
||||
{
|
||||
struct Benchmark oldBuildOamBuffer, newBuildOamBuffer;
|
||||
struct OamData *oldOamBuffer = Alloc(sizeof(gMain.oamBuffer));
|
||||
|
||||
if (preSort)
|
||||
Old_BuildOamBuffer();
|
||||
BENCHMARK(&oldBuildOamBuffer)
|
||||
{
|
||||
Old_BuildOamBuffer();
|
||||
}
|
||||
memcpy(oldOamBuffer, gMain.oamBuffer, sizeof(gMain.oamBuffer));
|
||||
|
||||
if (preSort)
|
||||
BuildOamBuffer();
|
||||
BENCHMARK(&newBuildOamBuffer)
|
||||
{
|
||||
BuildOamBuffer();
|
||||
}
|
||||
|
||||
ExpectEqOamBuffers(oldOamBuffer, gMain.oamBuffer);
|
||||
EXPECT_FASTER(newBuildOamBuffer, oldBuildOamBuffer);
|
||||
Free(oldOamBuffer);
|
||||
}
|
||||
|
||||
TEST("BuildOamBuffer faster with no sprites")
|
||||
{
|
||||
ResetSpriteData_();
|
||||
BenchmarkBuildOamBuffer(FALSE);
|
||||
}
|
||||
|
||||
TEST("BuildOamBuffer faster with max sprites (equal y/subpriority)")
|
||||
{
|
||||
u32 i;
|
||||
|
||||
ResetSpriteData_();
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
CreateSprite(&gDummySpriteTemplate, 0, 0, 0);
|
||||
BenchmarkBuildOamBuffer(FALSE);
|
||||
}
|
||||
|
||||
TEST("BuildOamBuffer faster with max sprites (random y/subpriority)")
|
||||
{
|
||||
u32 i;
|
||||
ResetSpriteData_();
|
||||
SeedRng(0);
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
CreateSprite(&gDummySpriteTemplate, 0, Random() % 256, Random() % 256);
|
||||
BenchmarkBuildOamBuffer(FALSE);
|
||||
}
|
||||
|
||||
TEST("BuildOamBuffer faster on already-sorted max sprites")
|
||||
{
|
||||
u32 i;
|
||||
ResetSpriteData_();
|
||||
SeedRng(0);
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
CreateSprite(&gDummySpriteTemplate, 0, Random() % 256, Random() % 256);
|
||||
BenchmarkBuildOamBuffer(TRUE);
|
||||
}
|
||||
|
||||
TEST("BuildOamBuffer faster with mix of sprites")
|
||||
{
|
||||
u32 i;
|
||||
ResetSpriteData_();
|
||||
SeedRng(0);
|
||||
for (i = 0; i < MAX_SPRITES / 2; i++)
|
||||
{
|
||||
u32 spriteId = CreateSprite(&gDummySpriteTemplate, 0, Random() % 256, Random() % 256);
|
||||
gSprites[spriteId].invisible = Random() % 4 == 0;
|
||||
}
|
||||
BenchmarkBuildOamBuffer(FALSE);
|
||||
}
|
||||
|
||||
// Old implementation.
|
||||
|
||||
#define UBFIX
|
||||
|
||||
static void UpdateOamCoords(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
if (sprite->inUse && !sprite->invisible)
|
||||
{
|
||||
if (sprite->coordOffsetEnabled)
|
||||
{
|
||||
sprite->oam.x = sprite->x + sprite->x2 + sprite->centerToCornerVecX + gSpriteCoordOffsetX;
|
||||
sprite->oam.y = sprite->y + sprite->y2 + sprite->centerToCornerVecY + gSpriteCoordOffsetY;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite->oam.x = sprite->x + sprite->x2 + sprite->centerToCornerVecX;
|
||||
sprite->oam.y = sprite->y + sprite->y2 + sprite->centerToCornerVecY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void BuildSpritePriorities(void)
|
||||
{
|
||||
u16 i;
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
u16 priority = sprite->subpriority | (sprite->oam.priority << 8);
|
||||
sSpritePriorities[i] = priority;
|
||||
}
|
||||
}
|
||||
|
||||
static void SortSprites(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 1; i < MAX_SPRITES; i++)
|
||||
{
|
||||
u8 j = i;
|
||||
struct Sprite *sprite1 = &gSprites[sSpriteOrder[i - 1]];
|
||||
struct Sprite *sprite2 = &gSprites[sSpriteOrder[i]];
|
||||
u16 sprite1Priority = sSpritePriorities[sSpriteOrder[i - 1]];
|
||||
u16 sprite2Priority = sSpritePriorities[sSpriteOrder[i]];
|
||||
s16 sprite1Y = sprite1->oam.y;
|
||||
s16 sprite2Y = sprite2->oam.y;
|
||||
|
||||
if (sprite1Y >= DISPLAY_HEIGHT)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
|
||||
if (sprite2Y >= DISPLAY_HEIGHT)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
|
||||
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite1->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite1->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite1Y > 128)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite2->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite2->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite2Y > 128)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
}
|
||||
}
|
||||
|
||||
while (j > 0
|
||||
&& ((sprite1Priority > sprite2Priority)
|
||||
|| (sprite1Priority == sprite2Priority && sprite1Y < sprite2Y)))
|
||||
{
|
||||
u8 temp = sSpriteOrder[j];
|
||||
sSpriteOrder[j] = sSpriteOrder[j - 1];
|
||||
sSpriteOrder[j - 1] = temp;
|
||||
|
||||
// UB: If j equals 1, then j-- makes j equal 0.
|
||||
// Then, sSpriteOrder[-1] gets accessed below.
|
||||
// Although this doesn't result in a bug in the ROM,
|
||||
// the behavior is undefined.
|
||||
j--;
|
||||
#ifdef UBFIX
|
||||
if (j == 0)
|
||||
break;
|
||||
#endif
|
||||
|
||||
sprite1 = &gSprites[sSpriteOrder[j - 1]];
|
||||
sprite2 = &gSprites[sSpriteOrder[j]];
|
||||
sprite1Priority = sSpritePriorities[sSpriteOrder[j - 1]];
|
||||
sprite2Priority = sSpritePriorities[sSpriteOrder[j]];
|
||||
sprite1Y = sprite1->oam.y;
|
||||
sprite2Y = sprite2->oam.y;
|
||||
|
||||
if (sprite1Y >= DISPLAY_HEIGHT)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
|
||||
if (sprite2Y >= DISPLAY_HEIGHT)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
|
||||
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite1->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite1->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite1Y > 128)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite2->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite2->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite2Y > 128)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyMatricesToOamBuffer(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < OAM_MATRIX_COUNT; i++)
|
||||
{
|
||||
u32 base = 4 * i;
|
||||
gMain.oamBuffer[base + 0].affineParam = gOamMatrices[i].a;
|
||||
gMain.oamBuffer[base + 1].affineParam = gOamMatrices[i].b;
|
||||
gMain.oamBuffer[base + 2].affineParam = gOamMatrices[i].c;
|
||||
gMain.oamBuffer[base + 3].affineParam = gOamMatrices[i].d;
|
||||
}
|
||||
}
|
||||
|
||||
static void AddSpritesToOamBuffer(void)
|
||||
{
|
||||
u8 i = 0;
|
||||
u8 oamIndex = 0;
|
||||
|
||||
while (i < MAX_SPRITES)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[sSpriteOrder[i]];
|
||||
if (sprite->inUse && !sprite->invisible && AddSpriteToOamBuffer(sprite, &oamIndex))
|
||||
return;
|
||||
i++;
|
||||
}
|
||||
|
||||
while (oamIndex < gOamLimit)
|
||||
{
|
||||
gMain.oamBuffer[oamIndex] = gDummyOamData;
|
||||
oamIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
static void Old_BuildOamBuffer(void)
|
||||
{
|
||||
u8 temp;
|
||||
UpdateOamCoords();
|
||||
BuildSpritePriorities();
|
||||
SortSprites();
|
||||
temp = gMain.oamLoadDisabled;
|
||||
gMain.oamLoadDisabled = TRUE;
|
||||
AddSpritesToOamBuffer();
|
||||
CopyMatricesToOamBuffer();
|
||||
gMain.oamLoadDisabled = temp;
|
||||
//sShouldProcessSpriteCopyRequests = TRUE;
|
||||
}
|
44
test/test.h
44
test/test.h
@ -46,6 +46,7 @@ struct TestRunnerState
|
||||
u8 result;
|
||||
u8 expectedResult;
|
||||
bool8 expectLeaks:1;
|
||||
bool8 inBenchmark:1;
|
||||
u32 timeoutSeconds;
|
||||
};
|
||||
|
||||
@ -158,6 +159,49 @@ s32 MgbaPrintf_(const char *fmt, ...);
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
} while (0)
|
||||
|
||||
struct Benchmark { s32 ticks; };
|
||||
|
||||
static inline void BenchmarkStart(void)
|
||||
{
|
||||
gTestRunnerState.inBenchmark = TRUE;
|
||||
REG_TM3CNT = (TIMER_ENABLE | TIMER_64CLK) << 16;
|
||||
}
|
||||
|
||||
static inline struct Benchmark BenchmarkStop(void)
|
||||
{
|
||||
REG_TM3CNT_H = 0;
|
||||
gTestRunnerState.inBenchmark = FALSE;
|
||||
return (struct Benchmark) { REG_TM3CNT_L };
|
||||
}
|
||||
|
||||
#define BENCHMARK(id) \
|
||||
for (BenchmarkStart(); gTestRunnerState.inBenchmark; *(id) = BenchmarkStop())
|
||||
|
||||
// An approximation of how much overhead benchmarks introduce.
|
||||
#define BENCHMARK_ABS 2
|
||||
|
||||
// An approximation for what percentage faster a benchmark has to be for
|
||||
// us to be confident that it's faster than another.
|
||||
#define BENCHMARK_REL 95
|
||||
|
||||
#define EXPECT_FASTER(a, b) \
|
||||
do \
|
||||
{ \
|
||||
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
|
||||
MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
|
||||
if (((a_ - BENCHMARK_ABS) * BENCHMARK_REL) >= (b_ * 100)) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_FASTER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_SLOWER(a, b) \
|
||||
do \
|
||||
{ \
|
||||
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
|
||||
MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
|
||||
if ((a_ * 100) <= ((b_ - BENCHMARK_ABS) * BENCHMARK_REL)) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_SLOWER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define KNOWN_FAILING \
|
||||
Test_ExpectedResult(TEST_RESULT_FAIL)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user