ability updates

This commit is contained in:
Prof-Holon
2026-04-08 11:55:32 +01:00
parent 8a3c796c48
commit 601247df5f
9 changed files with 200 additions and 5 deletions

View File

@@ -4389,3 +4389,16 @@ BattleScript_ActionSelectionItemsCantBeUsed::
BattleScript_FlushMessageBox::
printstring STRINGID_EMPTYSTRING3
return
BattleScript_LowHpAbilityBoost::
pause B_WAIT_TIME_SHORT
printfromtable gLowHpAbilityBoostStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_DampCuresStatus::
printstring STRINGID_PKMNSXCUREDYPROBLEM
waitmessage B_WAIT_TIME_LONG
updatestatusicon BS_ATTACKER
end3

View File

@@ -234,6 +234,7 @@ extern const u8 *const gStatNamesTable2[];
extern const u16 gMissStringIds[];
extern const u16 gTrappingMoves[];
extern const u16 gLowHpAbilityBoostStringIds[];
extern const u8 gText_Sleep[];
extern const u8 gText_Poison[];

View File

@@ -152,6 +152,7 @@
#define STATUS3_INTIMIDATE_POKES (1 << 19)
#define STATUS3_TRACE (1 << 20)
#define STATUS3_SEMI_INVULNERABLE (STATUS3_UNDERGROUND | STATUS3_ON_AIR | STATUS3_UNDERWATER)
#define STATUS3_LOW_HP_ABILITY_MSG (1 << 21) // added for blaze/torrent/overgrow message trigger
// Not really sure what a "hitmarker" is.
#define HITMARKER_WAKE_UP_CLEAR (1 << 4) // Cleared when waking up. Never set or checked.

View File

@@ -389,7 +389,15 @@
#define STRINGID_PKMNCOPIEDSECONDARYTYPE 387 //add to redefine conversion2
#define STRINGID_PKMNSXSUMMONEDHAIL 388 //ADD snow warning
#define BATTLESTRINGS_COUNT 389
//add boosting abilities tooltip
#define STRINGID_OVERGROWBOOSTED 389
#define STRINGID_BLAZINGBOOSTED 390
#define STRINGID_TORRENTBOOSTED 391
#define STRINGID_SWARMBOOSTED 392
#define STRINGID_EARLYBIRDBOOSTED 393
#define BATTLESTRINGS_COUNT 394
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View File

@@ -3421,6 +3421,12 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
else
speedMultiplierBattler2 = 1;
}
else if (gBattleWeather != B_WEATHER_NONE) // weather exists but is being suppressed
{
// ADD: Air Lock doubles speed when actively negating weather
speedMultiplierBattler1 = (gBattleMons[battler1].ability == ABILITY_AIR_LOCK) ? 2 : 1;
speedMultiplierBattler2 = (gBattleMons[battler2].ability == ABILITY_AIR_LOCK) ? 2 : 1;
}
else
{
speedMultiplierBattler1 = 1;

View File

@@ -428,6 +428,20 @@ static const u8 sText_ExclamationMark3[] = _("!");
static const u8 sText_ExclamationMark4[] = _("!");
static const u8 sText_ExclamationMark5[] = _("!");
static const u8 sText_Overgrow_Boosted[] = _("{B_ATK_NAME_WITH_PREFIX}'s Overgrow\nboosted its Grass moves!");
static const u8 sText_Blaze_Boosted[] = _("{B_ATK_NAME_WITH_PREFIX}'s Blaze\nboosted its Fire moves!");
static const u8 sText_Torrent_Boosted[] = _("{B_ATK_NAME_WITH_PREFIX}'s Torrent\nboosted its Water moves!");
static const u8 sText_Swarm_Boosted[] = _("{B_ATK_NAME_WITH_PREFIX}'s Swarm\nboosted its Bug moves!");
static const u8 sText_Early_Bird_Boosted[] = _("{B_ATK_NAME_WITH_PREFIX}'s Early Bird\nboosted its Flying moves!");
const u16 gLowHpAbilityBoostStringIds[] = {
STRINGID_OVERGROWBOOSTED, // 0
STRINGID_BLAZEBOOSTED, // 1
STRINGID_TORRENTBOOSTED, // 2
STRINGID_SWARMBOOSTED, // 3
STRINGID_EARLYBIRDBOOSTED, // 4
};
static const u8 sText_HP2[] = _("HP");
static const u8 sText_Attack2[] = _("ATTACK");
static const u8 sText_Defense2[] = _("DEFENSE");

View File

@@ -1084,8 +1084,10 @@ static void Cmd_accuracycheck(void)
calc = (calc * 130) / 100; // 1.3 compound eyes boost
if (WEATHER_HAS_EFFECT && gBattleMons[gBattlerTarget].ability == ABILITY_SAND_VEIL && gBattleWeather & B_WEATHER_SANDSTORM)
calc = (calc * 80) / 100; // 1.2 sand veil loss
if (gBattleMons[gBattlerAttacker].ability == ABILITY_HUSTLE && IS_TYPE_PHYSICAL(type))
calc = (calc * 80) / 100; // 1.2 hustle loss
if (WEATHER_HAS_EFFECT && gBattleMons[gBattlerTarget].ability == ABILITY_WATER_VEIL && gBattleWeather & B_WEATHER_RAIN)
calc = (calc * 80) / 100; // 1.2 water veil loss
if (gBattleMons[gBattlerAttacker].ability == ABILITY_HUSTLE && (gBattleMoves[gCurrentMove].flags & FLAG_MAKES_CONTACT))
calc = (calc * 80) / 100; // 1.2 hustle loss for contact moves
if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY)
{
@@ -1770,6 +1772,8 @@ static void Cmd_healthbarupdate(void)
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER && gBattleMoveDamage > 0)
gBattleResults.playerMonWasDamaged = TRUE;
}
if (gBattleMons[gActiveBattler].hp < oldHp && gBattleMons[gActiveBattler].hp != 0) // trigger ability if HP dropped
AbilityBattleEffects(ABILITYEFFECT_HP_CHANGE, gActiveBattler, 0, 0, 0);
}
gBattlescriptCurrInstr += 2;

View File

@@ -34,6 +34,21 @@ static const u16 sSoundMovesTable[] =
MOVE_UPROAR, MOVE_METAL_SOUND, MOVE_GRASS_WHISTLE, MOVE_HYPER_VOICE, SOUND_MOVES_END
};
#define PRIO_MOVES_END 0xFFFF
static const u16 sPrioMoves[] =
{
MOVE_QUICK_ATTACK, MOVE_EXTREMESPEED, MOVE_FAKE_OUT, MOVE_FEINT, MOVE_MACH_PUNCH, PRIO_MOVES_END
};
#define TAUNT_MOVES_END 0xFFFF
static const u16 sTauntMoves[] =
{
MOVE_TAUNT, MOVE_FOLLOW_ME, TAUNT_MOVES_END
};
u8 GetBattlerForBattleScript(u8 caseId)
{
u8 ret = 0;
@@ -1836,6 +1851,36 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA
}
}
break;
case ABILITYEFFECT_HP_CHANGE: // <-- ADD YOUR CODE HERE
{
u8 ability = gBattleMons[battler].ability;
if ((ability == ABILITY_BLAZE
|| ability == ABILITY_TORRENT
|| ability == ABILITY_OVERGROW
|| ability == ABILITY_SWARM)
|| ability == ABILITY_EARLY_BIRD
&& gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 3
&& !(gStatuses3[battler] & STATUS3_LOW_HP_ABILITY_MSG))
{
gStatuses3[battler] |= STATUS3_LOW_HP_ABILITY_MSG;
switch (ability)
{
case ABILITY_OVERGROW: gBattleCommunication[MULTISTRING_CHOOSER] = 0; break;
case ABILITY_BLAZE: gBattleCommunication[MULTISTRING_CHOOSER] = 1; break;
case ABILITY_TORRENT: gBattleCommunication[MULTISTRING_CHOOSER] = 2; break;
case ABILITY_SWARM: gBattleCommunication[MULTISTRING_CHOOSER] = 3; break;
case ABILITY_EARLY_BIRD: gBattleCommunication[MULTISTRING_CHOOSER] = 4; break;
}
gBattlerAbility = battler;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_LowHpAbilityBoost;
effect++;
}
}
break;
}
break;
case ABILITYEFFECT_ENDTURN: // 1
@@ -1879,6 +1924,31 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA
effect++;
}
break;
case ABILITY_DAMP:
if (WEATHER_HAS_EFFECT
&& (gBattleWeather & B_WEATHER_RAIN)
&& (gBattleMons[battler].status1 & STATUS1_ANY))
{
// Copy the same status string pattern from Shed Skin
if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON))
StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
if (gBattleMons[battler].status1 & STATUS1_PARALYSIS)
StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
if (gBattleMons[battler].status1 & STATUS1_BURN)
StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
if (gBattleMons[battler].status1 & STATUS1_FREEZE)
StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
gBattleMons[battler].status1 = 0;
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleScripting.battler = gActiveBattler = battler;
BattleScriptPushCursorAndCallback(BattleScript_DampCuresStatus);
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
MarkBattlerForControllerExec(gActiveBattler);
effect++;
}
break;
case ABILITY_SPEED_BOOST:
if (gBattleMons[battler].statStages[STAT_SPEED] < MAX_STAT_STAGE && gDisableStructs[battler].isFirstTurn != 2)
{
@@ -1912,6 +1982,38 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA
effect = 1;
}
}
// ADD: Inner Focus blocks priority moves
else if (gLastUsedAbility == ABILITY_INNER_FOCUS)
{
for (i = 0; sPrioMoves[i] != PRIO_MOVES_END; i++)
{
if (sPrioMoves[i] == move)
break;
}
if (sPrioMoves[i] != PRIO_MOVES_END)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattlescriptCurrInstr = BattleScript_SoundproofProtected;
effect = 1;
}
}
// ADD: Oblivious blocks taunt/redirect moves
else if (gLastUsedAbility == ABILITY_OBLIVIOUS)
{
for (i = 0; sTauntMoves[i] != TAUNT_MOVES_END; i++)
{
if (sTauntMoves[i] == move)
break;
}
if (sTauntMoves[i] != TAUNT_MOVES_END)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattlescriptCurrInstr = BattleScript_SoundproofProtected;
effect = 1;
}
}
break;
case ABILITYEFFECT_ABSORBING: // 3
if (move)
@@ -1966,6 +2068,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA
}
}
break;
}
if (effect == 1)
{

View File

@@ -1637,6 +1637,26 @@ static const u16 sHMMoves[] =
MOVE_ROCK_SMASH, MOVE_WATERFALL, MOVE_DIVE, HM_MOVES_END
};
#define ABILITY_MOVE_LIST_END 0xFFFF
static const u16 sSlicingMoves[] =
{
MOVE_CUT, MOVE_RAZOR_LEAF, MOVE_SLASH, MOVE_FURY_CUTTER,
MOVE_AIR_CUTTER, MOVE_AERIAL_ACE, MOVE_LEAF_BLADE, ABILITY_MOVE_LIST_END
};
static const u16 sClawMoves[] =
{
MOVE_VISE_GRIP, MOVE_CLAMP, MOVE_CRABHAMMER, MOVE_METAL_CLAW,
MOVE_CRUSH_CLAW, MOVE_DRAGON_CLAW, MOVE_GUILLOTINE, ABILITY_MOVE_LIST_END
};
static const u16 sHeadMoves[] =
{
MOVE_HEADBUTT, MOVE_MEGAHORN, MOVE_HORN_ATTACK,
MOVE_SKULL_BASH, MOVE_HORN_DRILL, ABILITY_MOVE_LIST_END
};
#if defined(FIRERED)
// Attack forme
static const u16 sDeoxysBaseStats[] =
@@ -2381,6 +2401,16 @@ static void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 mo
#define ShouldGetStatBadgeBoost(flag, battler)\
(!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER)) && FlagGet(flag) && GetBattlerSide(battler) == B_SIDE_PLAYER)
static bool8 IsMoveInList(u16 move, const u16 *list)
{
u32 i;
for (i = 0; list[i] != ABILITY_MOVE_LIST_END; i++)
{
if (list[i] == move)
return TRUE;
}
return FALSE;
}
s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *defender, u32 move, u16 sideStatus, u16 powerOverride, u8 typeOverride, u8 battlerIdAtk, u8 battlerIdDef)
{
@@ -2434,8 +2464,10 @@ s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *de
defenderHoldEffectParam = ItemId_GetHoldEffectParam(defender->item);
}
if (attacker->ability == ABILITY_HUGE_POWER || attacker->ability == ABILITY_PURE_POWER)
if (attacker->ability == ABILITY_HUGE_POWER)
attack *= 2;
if (attacker->ability == attacker->ability == ABILITY_PURE_POWER)
spAttack *= 2;
if (ShouldGetStatBadgeBoost(FLAG_BADGE01_GET, battlerIdAtk))
attack = (110 * attack) / 100;
@@ -2480,7 +2512,8 @@ s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *de
if (defender->ability == ABILITY_THICK_FAT && (type == TYPE_FIRE || type == TYPE_ICE))
spAttack /= 2;
if (attacker->ability == ABILITY_HUSTLE)
attack = (150 * attack) / 100;
&& (gBattleMoves[move].flags & FLAG_MAKES_CONTACT)) // boost only for contact moves
gBattleMovePower = (150 * gBattleMovePower) / 100; // alternatively boost both attack and SpAttack but this is neater
if (attacker->ability == ABILITY_PLUS && ABILITY_ON_FIELD2(ABILITY_MINUS))
spAttack = (150 * spAttack) / 100;
if (attacker->ability == ABILITY_MINUS && ABILITY_ON_FIELD2(ABILITY_PLUS))
@@ -2501,6 +2534,18 @@ s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *de
gBattleMovePower = (150 * gBattleMovePower) / 100;
if (type == TYPE_BUG && attacker->ability == ABILITY_SWARM && attacker->hp <= (attacker->maxHP / 3))
gBattleMovePower = (150 * gBattleMovePower) / 100;
if (type == TYPE_FLYING && attacker->ability == ABILITY_EARLY_BIRD && attacker->hp <= (attacker->maxHP / 3))
gBattleMovePower = (150 * gBattleMovePower) / 100;
if (type == TYPE_ELECTRIC && attacker->ability == ABILITY_ILLUMINATE && attacker->hp <= (attacker->maxHP / 3)) // ADD illuminate electric boost
gBattleMovePower = (150 * gBattleMovePower) / 100;
// Hyper Cutter: 1.5x boost for slicing or claw moves
if (attacker->ability == ABILITY_HYPER_CUTTER
&& (IsMoveInList(move, sSlicingMoves) || IsMoveInList(move, sClawMoves)))
gBattleMovePower = (150 * gBattleMovePower) / 100;
// Rock Head: 1.5x boost for head moves
if (attacker->ability == ABILITY_ROCK_HEAD
&& IsMoveInList(move, sHeadMoves))
gBattleMovePower = (150 * gBattleMovePower) / 100;
// Self-destruct / Explosion cut defense in half
if (gBattleMoves[gCurrentMove].effect == EFFECT_EXPLOSION)