commit
49e2d8fba5
@ -67,3 +67,46 @@ A: Shadows can be disabled for certain locations by modifying the `CurrentMapHas
|
||||
|
||||
### Q: How do I change the default light-blend color?
|
||||
A: The default color is handled by the `#define DEFAULT_LIGHT_COLOR` in `src/palette.c`.
|
||||
|
||||
### Q. How do I use alternate nighttime palettes?
|
||||
|
||||
In addition to palette tinting, the DNS allows tilesets to define alternate nighttime palettes.
|
||||
These palettes are automatically blended with their corresponding daytime palettes with the passage of time.
|
||||
|
||||
Each tileset has `16` total palette slots. `3` of the BG palettes (`13 - 15`) are reserved for the UI leaving `13` (`0-12`) usable for tilesets. Because primary tilesets load `6` (`NUM_PALS_IN_PRIMARY`) palettes (`0-5`) and secondary Tilesets load `7` palettes (6-12), some slots are unused for each. DNS repurposes these unused slots to store alternate nighttime palettes.
|
||||
|
||||
To avoid overlap with active palettes, each nighttime palette is stored in a different slot determined by the formula: `night_pal = (day_pal + 9) % 16`
|
||||
|
||||
**Day palette index vs. Night palette index**
|
||||
|
||||
| Day | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
||||
|-----|---|---|---|---|---|---|---|---|---|----|----|----|----|----|----|----|
|
||||
| Night | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
||||
|
||||
|
||||
For instance, in a secondary Tileset (slots `6-12`), the nighttime palettes for index `8` would be stored at index `1` (which is unused in a secondary tileset). Slots `0` and `13-15` are not used for blending so the corresponding slots `9` and `6-8` are never used for nighttime palettes.
|
||||
|
||||
_Note that palette `0` is not used for blending._
|
||||
|
||||
Once the appropriate nighttime `.pal` file has been added, add a `swapPalettes` field to the tileset struct definition in `src/data/tilesets/headers.h`. The macro `SWAP_PAL(x)` is provided for this purpose.
|
||||
|
||||
`swapPalettes` is a bitmask so to use nighttime palettes for indices 7 and 9 in the Petalburg Tileset we add a `swapPalettes` with `SWAP_PAL(7) | SWAP_PAL(9)`.
|
||||
|
||||
Note that the palette index to specify here is the palette index that you want to swap at night (**NOT** the corresponding nighttime palette).
|
||||
|
||||
```diff
|
||||
const struct Tileset gTileset_Petalburg =
|
||||
{
|
||||
.isCompressed = TRUE,
|
||||
.isSecondary = TRUE,
|
||||
.tiles = gTilesetTiles_Petalburg,
|
||||
+ .swapPalettes = SWAP_PAL(7) | SWAP_PAL(9), // Enable nighttime variants for slots 7 and 9
|
||||
.palettes = gTilesetPalettes_Petalburg,
|
||||
.metatiles = gMetatiles_Petalburg,
|
||||
.metatileAttributes = gMetatileAttributes_Petalburg,
|
||||
.callback = InitTilesetAnim_Petalburg,
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@ -3162,23 +3162,31 @@ static u32 GetPoisonDamage(u32 battlerId)
|
||||
return damage;
|
||||
}
|
||||
|
||||
static bool32 BattlerAffectedBySandstorm(u32 battlerId, enum Ability ability)
|
||||
static bool32 DoesBattlerTakeSandstormDamage(u32 battlerId, enum Ability ability)
|
||||
{
|
||||
if (!(AI_GetWeather() & B_WEATHER_SANDSTORM))
|
||||
return FALSE;
|
||||
|
||||
if (!IS_BATTLER_ANY_TYPE(battlerId, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL)
|
||||
&& ability != ABILITY_SAND_VEIL
|
||||
&& ability != ABILITY_SAND_FORCE
|
||||
&& ability != ABILITY_SAND_RUSH
|
||||
&& ability != ABILITY_MAGIC_GUARD
|
||||
&& ability != ABILITY_OVERCOAT)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 BattlerAffectedByHail(u32 battlerId, enum Ability ability)
|
||||
static bool32 DoesBattlerTakeHailDamage(u32 battlerId, enum Ability ability)
|
||||
{
|
||||
if (!(AI_GetWeather() & B_WEATHER_HAIL))
|
||||
return FALSE;
|
||||
|
||||
if (!IS_BATTLER_OF_TYPE(battlerId, TYPE_ICE)
|
||||
&& ability != ABILITY_SNOW_CLOAK
|
||||
&& ability != ABILITY_OVERCOAT
|
||||
&& ability != ABILITY_ICE_BODY)
|
||||
&& ability != ABILITY_ICE_BODY
|
||||
&& ability != ABILITY_MAGIC_GUARD
|
||||
&& ability != ABILITY_OVERCOAT)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@ -3194,7 +3202,7 @@ static u32 GetWeatherDamage(u32 battlerId)
|
||||
|
||||
if (weather & B_WEATHER_SANDSTORM)
|
||||
{
|
||||
if (BattlerAffectedBySandstorm(battlerId, ability)
|
||||
if (DoesBattlerTakeSandstormDamage(battlerId, ability)
|
||||
&& gBattleMons[battlerId].volatiles.semiInvulnerable != STATE_UNDERGROUND
|
||||
&& gBattleMons[battlerId].volatiles.semiInvulnerable != STATE_UNDERWATER
|
||||
&& holdEffect != HOLD_EFFECT_SAFETY_GOGGLES)
|
||||
@ -3206,7 +3214,7 @@ static u32 GetWeatherDamage(u32 battlerId)
|
||||
}
|
||||
if ((weather & B_WEATHER_HAIL) && ability != ABILITY_ICE_BODY)
|
||||
{
|
||||
if (BattlerAffectedByHail(battlerId, ability)
|
||||
if (DoesBattlerTakeHailDamage(battlerId, ability)
|
||||
&& gBattleMons[battlerId].volatiles.semiInvulnerable != STATE_UNDERGROUND
|
||||
&& gBattleMons[battlerId].volatiles.semiInvulnerable != STATE_UNDERWATER
|
||||
&& holdEffect != HOLD_EFFECT_SAFETY_GOGGLES)
|
||||
@ -3238,8 +3246,11 @@ u32 GetBattlerSecondaryDamage(u32 battlerId)
|
||||
|
||||
bool32 BattlerWillFaintFromWeather(u32 battler, enum Ability ability)
|
||||
{
|
||||
if ((BattlerAffectedBySandstorm(battler, ability) || BattlerAffectedByHail(battler, ability))
|
||||
&& gBattleMons[battler].hp <= max(1, gBattleMons[battler].maxHP / 16))
|
||||
if (gAiLogicData->holdEffects[battler] == HOLD_EFFECT_SAFETY_GOGGLES)
|
||||
return FALSE;
|
||||
|
||||
if ((DoesBattlerTakeSandstormDamage(battler, ability) || DoesBattlerTakeHailDamage(battler, ability))
|
||||
&& gBattleMons[battler].hp <= max(1, GetNonDynamaxMaxHP(battler) / 16))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
|
||||
@ -4442,7 +4442,7 @@ static void Cmd_jumpifability(void)
|
||||
}
|
||||
break;
|
||||
case BS_TARGET_SIDE:
|
||||
battler = IsAbilityOnOpposingSide(gBattlerAttacker, ability);
|
||||
battler = IsAbilityOnSide(gBattlerTarget, ability);
|
||||
if (battler)
|
||||
{
|
||||
battler--;
|
||||
|
||||
@ -9703,8 +9703,11 @@ u32 TryImmunityAbilityHealStatus(u32 battler, enum AbilityEffect caseID)
|
||||
}
|
||||
|
||||
gBattleScripting.battler = gBattlerAbility = battler;
|
||||
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
|
||||
MarkBattlerForControllerExec(battler);
|
||||
if (effect == 1) // Only primary status changes should sync party status.
|
||||
{
|
||||
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
|
||||
MarkBattlerForControllerExec(battler);
|
||||
}
|
||||
return effect;
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,24 @@ DOUBLE_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Taunt")
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Aroma Veil protects the Pokémon's side from ally Taunt")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT);
|
||||
ASSUME(GetMoveCategory(MOVE_HARDEN) == DAMAGE_CATEGORY_STATUS);
|
||||
PLAYER(SPECIES_AROMATISSE) { Ability(ABILITY_AROMA_VEIL); Speed(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
|
||||
OPPONENT(SPECIES_WYNAUT) { Speed(4); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_TAUNT, target: playerLeft); MOVE(playerLeft, MOVE_HARDEN); }
|
||||
} SCENE {
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, playerRight);
|
||||
ABILITY_POPUP(playerLeft, ABILITY_AROMA_VEIL);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Torment")
|
||||
{
|
||||
struct BattlePokemon *moveTarget = NULL;
|
||||
|
||||
@ -99,3 +99,29 @@ SINGLE_BATTLE_TEST("Oblivious prevents Intimidate (Gen8+)")
|
||||
MESSAGE("Slowpoke's Oblivious prevents stat loss!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Oblivious cured infatuation should not persist toxic counter after switching")
|
||||
{
|
||||
s16 firstTick, secondTick, postSwitchTick;
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_ATTRACT) == EFFECT_ATTRACT);
|
||||
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); Status1(STATUS1_TOXIC_POISON); MaxHP(160); HP(160); Speed(100); }
|
||||
PLAYER(SPECIES_WYNAUT) { Speed(90); }
|
||||
OPPONENT(SPECIES_SLOWPOKE) { Gender(MON_FEMALE); Ability(ABILITY_OBLIVIOUS); Speed(80); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_ATTRACT); }
|
||||
TURN { MOVE(opponent, MOVE_SKILL_SWAP); }
|
||||
TURN { SWITCH(player, 1); }
|
||||
TURN { SWITCH(player, 0); }
|
||||
} SCENE {
|
||||
HP_BAR(player, captureDamage: &firstTick);
|
||||
HP_BAR(player, captureDamage: &secondTick);
|
||||
HP_BAR(player, captureDamage: &postSwitchTick);
|
||||
} THEN {
|
||||
EXPECT_EQ(firstTick, 10);
|
||||
EXPECT_EQ(secondTick, 20);
|
||||
EXPECT_EQ(postSwitchTick, 10);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,3 +143,29 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from items")
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Own Tempo cured confusion should not persist toxic counter after switching")
|
||||
{
|
||||
s16 firstTick, secondTick, postSwitchTick;
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_CONFUSE_RAY) == EFFECT_CONFUSE);
|
||||
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); MaxHP(160); HP(160); Speed(100); }
|
||||
PLAYER(SPECIES_WYNAUT) { Speed(90); }
|
||||
OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); Speed(80); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_CONFUSE_RAY); }
|
||||
TURN { MOVE(opponent, MOVE_SKILL_SWAP); MOVE(player, MOVE_CELEBRATE, WITH_RNG(RNG_CONFUSION, FALSE)); }
|
||||
TURN { SWITCH(player, 1); }
|
||||
TURN { SWITCH(player, 0); }
|
||||
} SCENE {
|
||||
HP_BAR(player, captureDamage: &firstTick);
|
||||
HP_BAR(player, captureDamage: &secondTick);
|
||||
HP_BAR(player, captureDamage: &postSwitchTick);
|
||||
} THEN {
|
||||
EXPECT_EQ(firstTick, 10);
|
||||
EXPECT_EQ(secondTick, 20);
|
||||
EXPECT_EQ(postSwitchTick, 10);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user