Implement Charge and more abilities (#163)

* Implement Rivalry

* Implement Quick Feet, Liquid Voice, and Normalize

* Forgot paralysis is half speed instead of a quarter

* Remove log statements

* Fix minor edge case in rivalry for gendered vs genderless

* Add wind abilities and charge

* Implement Charge and more abilities

* Add i18n support and Beedrill fury cutter

* Fix merge conflict in taunt

* More English strings removed
pull/168/head
Tempoanon 2024-04-17 01:09:15 -04:00 committed by GitHub
parent db92663daa
commit cf2bd4d3da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 74 additions and 15 deletions

View File

@ -429,6 +429,26 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr {
}
}
export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr {
private stat: BattleStat;
private levels: integer;
constructor(immuneCondition: PreDefendAbAttrCondition, stat: BattleStat, levels: integer) {
super(immuneCondition);
this.stat = stat;
this.levels = levels;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args)
if (ret) {
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels));
}
return ret;
}
}
export class PostDefendStatChangeAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition;
private stat: BattleStat;
@ -454,6 +474,25 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr {
}
}
export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition;
private tagType: BattlerTagType;
constructor(condition: PokemonDefendCondition, tagType: BattlerTagType) {
super(true);
this.condition = condition;
this.tagType = tagType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
pokemon.addTag(this.tagType, undefined, undefined, pokemon.id);
return true;
}
return false;
}
}
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) {
@ -2535,7 +2574,9 @@ export function initAbilities() {
.attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1),
new Ability(Abilities.JUSTIFIED, "Justified", "Being hit by a Dark-type move boosts the Attack stat of the Pokémon, for justice.", 5)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.DARK && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1),
new Ability(Abilities.RATTLED, "Rattled (N)", "Dark-, Ghost-, and Bug-type moves scare the Pokémon and boost its Speed stat.", 5),
new Ability(Abilities.RATTLED, "Rattled (P)", "Intimidate or being hit by a Dark-, Ghost-, or Bug-type move will scare the Pokémon and boost its Speed stat.", 5)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS && (move.type === Type.DARK || move.type === Type.BUG ||
move.type === Type.GHOST), BattleStat.SPD, 1),
new Ability(Abilities.MAGIC_BOUNCE, "Magic Bounce (N)", "Reflects status moves instead of getting hit by them.", 5)
.ignorable(),
new Ability(Abilities.SAP_SIPPER, "Sap Sipper", "Boosts the Attack stat if hit by a Grass-type move instead of taking damage.", 5)
@ -2559,7 +2600,8 @@ export function initAbilities() {
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(UnsuppressableAbilityAbAttr),
new Ability(Abilities.VICTORY_STAR, "Victory Star (N)", "Boosts the accuracy of its allies and itself.", 5),
new Ability(Abilities.VICTORY_STAR, "Victory Star (P)", "Boosts the accuracy of its allies and itself.", 5)
.attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.1),
new Ability(Abilities.TURBOBLAZE, "Turboblaze", "Moves can be used on the target regardless of its Abilities.", 5)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' is radiating a blazing aura!'))
.attr(MoveAbilityBypassAbAttr),
@ -2586,7 +2628,9 @@ export function initAbilities() {
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5),
new Ability(Abilities.REFRIGERATE, "Refrigerate", "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", 6)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.ICE, 1.2),
new Ability(Abilities.SWEET_VEIL, "Sweet Veil (N)", "Prevents itself and ally Pokémon from falling asleep.", 6)
new Ability(Abilities.SWEET_VEIL, "Sweet Veil (P)", "Prevents itself and ally Pokémon from falling asleep.", 6)
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
.ignorable(),
new Ability(Abilities.STANCE_CHANGE, "Stance Change", "The Pokémon changes its form to Blade Forme when it uses an attack move and changes to Shield Forme when it uses King's Shield.", 6)
.attr(UncopiableAbilityAbAttr)
@ -2833,7 +2877,7 @@ export function initAbilities() {
new Ability(Abilities.SEED_SOWER, "Seed Sower", "Turns the ground into Grassy Terrain when the Pokémon is hit by an attack.", 9)
.attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY),
new Ability(Abilities.THERMAL_EXCHANGE, "Thermal Exchange", "Boosts the Attack stat when the Pokémon is hit by a Fire-type move. The Pokémon also cannot be burned.", 9)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.FIRE, BattleStat.ATK, 1)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.FIRE && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1)
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN)
.ignorable(),
new Ability(Abilities.ANGER_SHELL, "Anger Shell (N)", "When an attack causes its HP to drop to half or less, the Pokémon gets angry. This lowers its Defense and Sp. Def stats but boosts its Attack, Sp. Atk, and Speed stats.", 9),
@ -2844,13 +2888,15 @@ export function initAbilities() {
new Ability(Abilities.WELL_BAKED_BODY, "Well-Baked Body", "The Pokémon takes no damage when hit by Fire-type moves. Instead, its Defense stat is sharply boosted.", 9)
.attr(TypeImmunityStatChangeAbAttr, Type.FIRE, BattleStat.DEF, 2)
.ignorable(),
new Ability(Abilities.WIND_RIDER, "Wind Rider (N)", "Boosts the Pokémon's Attack stat if Tailwind takes effect or if the Pokémon is hit by a wind move. The Pokémon also takes no damage from wind moves.", 9)
new Ability(Abilities.WIND_RIDER, "Wind Rider (P)", "Boosts the Pokémon's Attack stat if Tailwind takes effect or if the Pokémon is hit by a wind move. The Pokémon also takes no damage from wind moves.", 9)
.attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.WIND_MOVE), BattleStat.ATK, 1)
.ignorable(),
new Ability(Abilities.GUARD_DOG, "Guard Dog (N)", "Boosts the Pokémon's Attack stat if intimidated. Moves and items that would force the Pokémon to switch out also fail to work.", 9)
.ignorable(),
new Ability(Abilities.ROCKY_PAYLOAD, "Rocky Payload", "Powers up Rock-type moves.", 9)
.attr(MoveTypePowerBoostAbAttr, Type.ROCK),
new Ability(Abilities.WIND_POWER, "Wind Power (N)", "The Pokémon becomes charged when it is hit by a wind move, boosting the power of the next Electric-type move the Pokémon uses.", 9),
new Ability(Abilities.WIND_POWER, "Wind Power (P)", "The Pokémon becomes charged when it is hit by a wind move, boosting the power of the next Electric-type move the Pokémon uses.", 9)
.attr(PostDefendApplyBattlerTagAbAttr, (target, user, move) => move.hasFlag(MoveFlags.WIND_MOVE), BattlerTagType.CHARGED),
new Ability(Abilities.ZERO_TO_HERO, "Zero to Hero (N)", "The Pokémon transforms into its Hero Form when it switches out.", 9)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
@ -2859,7 +2905,8 @@ export function initAbilities() {
new Ability(Abilities.COMMANDER, "Commander (N)", "When the Pokémon enters a battle, it goes inside the mouth of an ally Dondozo if one is on the field. The Pokémon then issues commands from there.", 9)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr),
new Ability(Abilities.ELECTROMORPHOSIS, "Electromorphosis (N)", "The Pokémon becomes charged when it takes damage, boosting the power of the next Electric-type move the Pokémon uses.", 9),
new Ability(Abilities.ELECTROMORPHOSIS, "Electromorphosis", "The Pokémon becomes charged when it takes damage, boosting the power of the next Electric-type move the Pokémon uses.", 9)
.attr(PostDefendApplyBattlerTagAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattlerTagType.CHARGED),
new Ability(Abilities.PROTOSYNTHESIS, "Protosynthesis", "Boosts the Pokémon's most proficient stat in harsh sunlight or if the Pokémon is holding Booster Energy.", 9)
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), PostSummonAddBattlerTagAbAttr, BattlerTagType.PROTOSYNTHESIS, 0, true)
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.PROTOSYNTHESIS, 0, WeatherType.SUNNY, WeatherType.HARSH_SUN)

View File

@ -922,11 +922,15 @@ export class HideSpriteTag extends BattlerTag {
export class TypeBoostTag extends BattlerTag {
public boostedType: Type;
public boostValue: number;
public oneUse: boolean;
constructor(tagType: BattlerTagType, sourceMove: Moves, boostedType: Type) {
constructor(tagType: BattlerTagType, sourceMove: Moves, boostedType: Type, boostValue: number, oneUse: boolean) {
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove);
this.boostedType = boostedType;
this.boostValue = boostValue;
this.oneUse = oneUse;
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -1081,7 +1085,7 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
case BattlerTagType.HIDDEN:
return new HideSpriteTag(tagType, turnCount, sourceMove);
case BattlerTagType.FIRE_BOOST:
return new TypeBoostTag(tagType, sourceMove, Type.FIRE);
return new TypeBoostTag(tagType, sourceMove, Type.FIRE, 1.5, false);
case BattlerTagType.CRIT_BOOST:
return new CritBoostTag(tagType, sourceMove);
case BattlerTagType.ALWAYS_CRIT:
@ -1098,6 +1102,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount - 1, sourceMove);
case BattlerTagType.SALT_CURED:
return new SaltCuredTag(sourceId);
case BattlerTagType.CHARGED:
return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true);
case BattlerTagType.NONE:
default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -47,6 +47,7 @@ export enum BattlerTagType {
IGNORE_ACCURACY = "IGNORE_ACCURACY",
BYPASS_SLEEP = "BYPASS_SLEEP",
IGNORE_FLYING = "IGNORE_FLYING",
GROUNDED = "GROUNDED",
SALT_CURED = "SALT_CURED"
SALT_CURED = "SALT_CURED",
CHARGED = "CHARGED",
GROUNDED = "GROUNDED"
}

View File

@ -4038,7 +4038,6 @@ export function initMoves() {
.target(MoveTarget.RANDOM_NEAR_ENEMY),
new StatusMove(Moves.SANDSTORM, Type.ROCK, -1, 10, -1, 0, 2)
.attr(WeatherChangeAttr, WeatherType.SANDSTORM)
.windMove()
.target(MoveTarget.BOTH_SIDES),
new AttackMove(Moves.GIGA_DRAIN, Type.GRASS, MoveCategory.SPECIAL, 75, 100, 10, -1, 0, 2)
.attr(HitHealAttr)
@ -4224,7 +4223,7 @@ export function initMoves() {
.ignoresVirtual(),
new SelfStatusMove(Moves.CHARGE, Type.ELECTRIC, -1, 20, -1, 0, 3)
.attr(StatChangeAttr, BattleStat.SPDEF, 1, true)
.partial(),
.attr(AddBattlerTagAttr, BattlerTagType.CHARGED, true, true),
new StatusMove(Moves.TAUNT, Type.DARK, 100, 20, -1, 0, 3)
.unimplemented(),
new StatusMove(Moves.HELPING_HAND, Type.NORMAL, -1, 20, -1, 5, 3)

View File

@ -203,6 +203,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[Species.BEEDRILL]: [
[ 0, Moves.TWINEEDLE ],
[ 1, Moves.FURY_ATTACK ],
[ 11, Moves.FURY_CUTTER ],
[ 14, Moves.RAGE ],
[ 17, Moves.PURSUIT ],
[ 20, Moves.FOCUS_ENERGY ],

View File

@ -1183,8 +1183,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (cancelled.value)
result = HitResult.NO_EFFECT;
else {
if (source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === type))
power.value *= 1.5;
let typeBoost = source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === type) as TypeBoostTag;
if (typeBoost) {
power.value *= typeBoost.boostValue;
if (typeBoost.oneUse) {
this.removeTag(typeBoost.tagType);
}
}
const arenaAttackTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(type, source.isGrounded());
if (this.scene.arena.getTerrainType() === TerrainType.GRASSY && this.isGrounded() && type === Type.GROUND && move.moveTarget === MoveTarget.ALL_NEAR_OTHERS)
power.value /= 2;