Begin implementing abilities
parent
f790a5ff2a
commit
c614295b5e
|
@ -25,6 +25,7 @@ import { Gender } from "./data/gender";
|
|||
import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
|
||||
import { TempBattleStat } from "./data/temp-battle-stat";
|
||||
import { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
|
||||
import { ProtectStatAttr, applyPreStatChangeAbilityAttrs } from "./data/ability";
|
||||
|
||||
export class SelectStarterPhase extends BattlePhase {
|
||||
constructor(scene: BattleScene) {
|
||||
|
@ -1080,9 +1081,10 @@ export class MoveAnimTestPhase extends BattlePhase {
|
|||
|
||||
export class StatChangePhase extends PokemonPhase {
|
||||
private stats: BattleStat[];
|
||||
private selfTarget: boolean;
|
||||
private levels: integer;
|
||||
|
||||
constructor(scene: BattleScene, player: boolean, stats: BattleStat[], levels: integer) {
|
||||
constructor(scene: BattleScene, player: boolean, selfTarget: boolean, stats: BattleStat[], levels: integer) {
|
||||
super(scene, player);
|
||||
|
||||
const allStats = Utils.getEnumValues(BattleStat);
|
||||
|
@ -1093,19 +1095,26 @@ export class StatChangePhase extends PokemonPhase {
|
|||
start() {
|
||||
const pokemon = this.getPokemon();
|
||||
|
||||
const filteredStats = this.stats.filter(stat => {
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
|
||||
if (!this.selfTarget && this.levels < 0)
|
||||
applyPreStatChangeAbilityAttrs(ProtectStatAttr, this.getPokemon(), stat, cancelled);
|
||||
|
||||
return !cancelled.value;
|
||||
});
|
||||
|
||||
const battleStats = this.getPokemon().summonData.battleStats;
|
||||
const relLevels = this.stats.map(stat => (this.levels >= 1 ? Math.min(battleStats[stat] + this.levels, 6) : Math.max(battleStats[stat] + this.levels, -6)) - battleStats[stat]);
|
||||
const relLevels = filteredStats.map(stat => (this.levels >= 1 ? Math.min(battleStats[stat] + this.levels, 6) : Math.max(battleStats[stat] + this.levels, -6)) - battleStats[stat]);
|
||||
|
||||
const end = () => {
|
||||
const messages = this.getStatChangeMessages(relLevels);
|
||||
const messages = this.getStatChangeMessages(filteredStats, relLevels);
|
||||
for (let message of messages)
|
||||
this.scene.queueMessage(message);
|
||||
|
||||
for (let stat of this.stats)
|
||||
for (let stat of filteredStats)
|
||||
pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + this.levels, 6), -6);
|
||||
|
||||
console.log(pokemon.summonData.battleStats);
|
||||
|
||||
this.end();
|
||||
};
|
||||
|
||||
|
@ -1113,7 +1122,7 @@ export class StatChangePhase extends PokemonPhase {
|
|||
pokemon.enableMask();
|
||||
const pokemonMaskSprite = pokemon.maskSprite;
|
||||
|
||||
const statSprite = this.scene.add.tileSprite((this.player ? 106 : 236) * 6, ((this.player ? 148 : 84) + (this.levels >= 1 ? 160 : 0)) * 6, 156, 316, 'battle_stats', this.stats.length > 1 ? 'mix' : BattleStat[this.stats[0]].toLowerCase());
|
||||
const statSprite = this.scene.add.tileSprite((this.player ? 106 : 236) * 6, ((this.player ? 148 : 84) + (this.levels >= 1 ? 160 : 0)) * 6, 156, 316, 'battle_stats', filteredStats.length > 1 ? 'mix' : BattleStat[filteredStats[0]].toLowerCase());
|
||||
statSprite.setAlpha(0);
|
||||
statSprite.setScale(6);
|
||||
statSprite.setOrigin(0.5, 1);
|
||||
|
@ -1150,11 +1159,11 @@ export class StatChangePhase extends PokemonPhase {
|
|||
end();
|
||||
}
|
||||
|
||||
getStatChangeMessages(relLevels: integer[]): string[] {
|
||||
getStatChangeMessages(stats: BattleStat[], relLevels: integer[]): string[] {
|
||||
const messages: string[] = [];
|
||||
|
||||
for (let s = 0; s < this.stats.length; s++)
|
||||
messages.push(getPokemonMessage(this.getPokemon(), `'s ${getBattleStatName(this.stats[s])} ${getBattleStatLevelChangeDescription(Math.abs(relLevels[s]), this.levels >= 1)}!`));
|
||||
for (let s = 0; s < stats.length; s++)
|
||||
messages.push(getPokemonMessage(this.getPokemon(), `'s ${getBattleStatName(stats[s])} ${getBattleStatLevelChangeDescription(Math.abs(relLevels[s]), this.levels >= 1)}!`));
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,129 @@
|
|||
import Pokemon, { PokemonMove } from "../pokemon";
|
||||
import { Type } from "./type";
|
||||
import * as Utils from "../utils";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import { StatChangePhase } from "../battle-phases";
|
||||
|
||||
export class Ability {
|
||||
public id: Abilities;
|
||||
public name: string;
|
||||
public description: string;
|
||||
public generation: integer;
|
||||
public attrs: AbilityAttr[];
|
||||
|
||||
constructor(id: Abilities, name: string, description: string, generation: integer) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.generation = generation;
|
||||
this.attrs = [];
|
||||
}
|
||||
|
||||
getAttrs(attrType: { new(...args: any[]): AbilityAttr }): AbilityAttr[] {
|
||||
return this.attrs.filter(a => a instanceof attrType);
|
||||
}
|
||||
|
||||
attr<T extends new (...args: any[]) => AbilityAttr>(AttrType: T, ...args: ConstructorParameters<T>): Ability {
|
||||
const attr = new AttrType(...args);
|
||||
this.attrs.push(attr);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AbilityAttr { }
|
||||
|
||||
export class PreDefendAbilityAttr extends AbilityAttr {
|
||||
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class TypeImmunityAttr extends PreDefendAbilityAttr {
|
||||
private immuneType: Type;
|
||||
|
||||
constructor(immuneType: Type) {
|
||||
super();
|
||||
|
||||
this.immuneType = immuneType;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (move.getMove().type === this.immuneType) {
|
||||
(args[0] as Utils.NumberHolder).value = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class TypeImmunityStatChangeAttr extends TypeImmunityAttr {
|
||||
private stat: BattleStat;
|
||||
private levels: integer;
|
||||
|
||||
constructor(immuneType: Type, stat: BattleStat, levels: integer) {
|
||||
super(immuneType);
|
||||
|
||||
this.stat = stat;
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const ret = super.applyPreDefend(pokemon, attacker, move, cancelled, args);
|
||||
|
||||
if (ret) {
|
||||
cancelled.value = true;
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ this.stat ], this.levels));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
export class PreStatChangeAbilityAttr extends AbilityAttr {
|
||||
applyPreStatChange(pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtectStatAttr extends PreStatChangeAbilityAttr {
|
||||
private protectedStats: BattleStat[];
|
||||
|
||||
constructor(...stats: BattleStat[]) {
|
||||
super();
|
||||
|
||||
this.protectedStats = stats;
|
||||
}
|
||||
|
||||
applyPreStatChange(pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (!this.protectedStats.length || this.protectedStats.indexOf(stat) > -1) {
|
||||
cancelled.value = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function applyPreDefendAbilityAttrs(attrType: { new(...args: any[]): PreDefendAbilityAttr },
|
||||
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): void {
|
||||
const ability = pokemon.getAbility();
|
||||
const attrs = ability.getAttrs(attrType) as PreDefendAbilityAttr[];
|
||||
for (let attr of attrs) {
|
||||
if (attr.applyPreDefend(pokemon, attacker, move, cancelled, args))
|
||||
console.log('Applied', ability.name, attr);
|
||||
}
|
||||
}
|
||||
|
||||
export function applyPreStatChangeAbilityAttrs(attrType: { new(...args: any[]): PreStatChangeAbilityAttr },
|
||||
pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]) {
|
||||
const ability = pokemon.getAbility();
|
||||
const attrs = ability.getAttrs(attrType) as PreStatChangeAbilityAttr[];
|
||||
for (let attr of attrs) {
|
||||
if (attr.applyPreStatChange(pokemon, stat, cancelled, args))
|
||||
console.log('Applied', ability.name, attr);
|
||||
}
|
||||
}
|
||||
|
||||
export enum Abilities {
|
||||
|
@ -193,7 +307,8 @@ export const abilities = [
|
|||
new Ability(Abilities.BATTLE_ARMOR, "Battle Armor (N)", "The POKéMON is protected against critical hits.", 3),
|
||||
new Ability(Abilities.BLAZE, "Blaze (N)", "Powers up FIRE-type moves in a pinch.", 3),
|
||||
new Ability(Abilities.CHLOROPHYLL, "Chlorophyll (N)", "Boosts the POKéMON's SPEED in sunshine.", 3),
|
||||
new Ability(Abilities.CLEAR_BODY, "Clear Body (N)", "Prevents other POKéMON from lowering its stats.", 3),
|
||||
new Ability(Abilities.CLEAR_BODY, "Clear Body", "Prevents other POKéMON from lowering its stats.", 3)
|
||||
.attr(ProtectStatAttr),
|
||||
new Ability(Abilities.CLOUD_NINE, "Cloud Nine (N)", "Eliminates the effects of weather.", 3),
|
||||
new Ability(Abilities.COLOR_CHANGE, "Color Change (N)", "Changes the POKéMON's type to the foe's move.", 3),
|
||||
new Ability(Abilities.COMPOUND_EYES, "Compound Eyes (N)", "The POKéMON's accuracy is boosted.", 3),
|
||||
|
@ -209,15 +324,19 @@ export const abilities = [
|
|||
new Ability(Abilities.GUTS, "Guts (N)", "Boosts ATTACK if there is a status problem.", 3),
|
||||
new Ability(Abilities.HUGE_POWER, "Huge Power (N)", "Raises the POKéMON's ATTACK stat.", 3),
|
||||
new Ability(Abilities.HUSTLE, "Hustle (N)", "Boosts the ATTACK stat, but lowers accuracy.", 3),
|
||||
new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter (N)", "Prevents other POKéMON from lowering ATTACK stat.", 3),
|
||||
new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter", "Prevents other POKéMON from lowering ATTACK stat.", 3)
|
||||
.attr(ProtectStatAttr, BattleStat.ATK),
|
||||
new Ability(Abilities.ILLUMINATE, "Illuminate (N)", "Raises the likelihood of meeting wild POKéMON.", 3),
|
||||
new Ability(Abilities.IMMUNITY, "Immunity (N)", "Prevents the POKéMON from getting poisoned.", 3),
|
||||
new Ability(Abilities.INNER_FOCUS, "Inner Focus (N)", "The POKéMON is protected from flinching.", 3),
|
||||
new Ability(Abilities.INSOMNIA, "Insomnia (N)", "Prevents the POKéMON from falling asleep.", 3),
|
||||
new Ability(Abilities.INTIMIDATE, "Intimidate (N)", "Lowers the foe's ATTACK stat.", 3),
|
||||
new Ability(Abilities.KEEN_EYE, "Keen Eye (N)", "Prevents other POKéMON from lowering accuracy.", 3),
|
||||
new Ability(Abilities.LEVITATE, "Levitate (N)", "Gives immunity to GROUND-type moves.", 3),
|
||||
new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod (N)", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3),
|
||||
new Ability(Abilities.KEEN_EYE, "Keen Eye", "Prevents other POKéMON from lowering accuracy.", 3)
|
||||
.attr(ProtectStatAttr, BattleStat.ACC),
|
||||
new Ability(Abilities.LEVITATE, "Levitate", "Gives immunity to GROUND-type moves.", 3)
|
||||
.attr(TypeImmunityAttr, Type.FLYING),
|
||||
new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3)
|
||||
.attr(TypeImmunityStatChangeAttr, Type.ELECTRIC, BattleStat.SPATK, 1),
|
||||
new Ability(Abilities.LIMBER, "Limber (N)", "The POKéMON is protected from paralysis.", 3),
|
||||
new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "Damages attackers using any draining move.", 3),
|
||||
new Ability(Abilities.MAGMA_ARMOR, "Magma Armor (N)", "Prevents the POKéMON from becoming frozen.", 3),
|
||||
|
@ -262,7 +381,8 @@ export const abilities = [
|
|||
new Ability(Abilities.VOLT_ABSORB, "Volt Absorb (N)", "Restores HP if hit by an ELECTRIC-type move.", 3),
|
||||
new Ability(Abilities.WATER_ABSORB, "Water Absorb (N)", "Restores HP if hit by a WATER-type move.", 3),
|
||||
new Ability(Abilities.WATER_VEIL, "Water Veil (N)", "Prevents the POKéMON from getting a burn.", 3),
|
||||
new Ability(Abilities.WHITE_SMOKE, "White Smoke (N)", "Prevents other POKéMON from lowering its stats.", 3),
|
||||
new Ability(Abilities.WHITE_SMOKE, "White Smoke", "Prevents other POKéMON from lowering its stats.", 3)
|
||||
.attr(ProtectStatAttr),
|
||||
new Ability(Abilities.WONDER_GUARD, "Wonder Guard (N)", "Only supereffective moves will hit.", 3),
|
||||
new Ability(Abilities.ADAPTABILITY, "Adaptability (N)", "Powers up moves of the same type.", 4),
|
||||
new Ability(Abilities.AFTERMATH, "Aftermath (N)", "Damages the attacker landing the finishing hit.", 4),
|
||||
|
@ -304,7 +424,8 @@ export const abilities = [
|
|||
new Ability(Abilities.SOLID_ROCK, "Solid Rock (N)", "Reduces damage from super-effective attacks.", 4),
|
||||
new Ability(Abilities.STALL, "Stall (N)", "The POKéMON moves after all other POKéMON do.", 4),
|
||||
new Ability(Abilities.STEADFAST, "Steadfast (N)", "Raises SPEED each time the POKéMON flinches.", 4),
|
||||
new Ability(Abilities.STORM_DRAIN, "Storm Drain (N)", "Draws in all WATER-type moves to up SP. ATK.", 4),
|
||||
new Ability(Abilities.STORM_DRAIN, "Storm Drain", "Draws in all WATER-type moves to up SP. ATK.", 4)
|
||||
.attr(TypeImmunityStatChangeAttr, Type.WATER, BattleStat.SPATK, 1),
|
||||
new Ability(Abilities.SUPER_LUCK, "Super Luck (N)", "Heightens the critical-hit ratios of moves.", 4),
|
||||
new Ability(Abilities.TANGLED_FEET, "Tangled Feet (N)", "Raises evasion if the POKéMON is confused.", 4),
|
||||
new Ability(Abilities.TECHNICIAN, "Technician (N)", "Powers up the POKéMON's weaker moves.", 4),
|
||||
|
@ -312,7 +433,8 @@ export const abilities = [
|
|||
new Ability(Abilities.UNAWARE, "Unaware (N)", "Ignores any stat changes in the POKéMON.", 4),
|
||||
new Ability(Abilities.UNBURDEN, "Unburden (N)", "Raises SPEED if a held item is used.", 4),
|
||||
new Ability(Abilities.ANALYTIC, "Analytic (N)", "Boosts move power when the POKéMON moves last.", 5),
|
||||
new Ability(Abilities.BIG_PECKS, "Big Pecks (N)", "Protects the POKéMON from DEFENSE-lowering attacks.", 5),
|
||||
new Ability(Abilities.BIG_PECKS, "Big Pecks", "Protects the POKéMON from DEFENSE-lowering attacks.", 5)
|
||||
.attr(ProtectStatAttr, BattleStat.DEF),
|
||||
new Ability(Abilities.CONTRARY, "Contrary (N)", "Makes stat changes have an opposite effect.", 5),
|
||||
new Ability(Abilities.CURSED_BODY, "Cursed Body (N)", "May disable a move used on the POKéMON.", 5),
|
||||
new Ability(Abilities.DEFEATIST, "Defeatist (N)", "Lowers stats when HP drops below half.", 5),
|
||||
|
|
|
@ -101,13 +101,13 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||
case BerryType.APICOT:
|
||||
return (pokemon: Pokemon) => {
|
||||
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), [ battleStat ], 1));
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ battleStat ], 1));
|
||||
};
|
||||
case BerryType.LANSAT:
|
||||
return (pokemon: Pokemon) => {
|
||||
pokemon.addTag(BattlerTagType.CRIT_BOOST);
|
||||
};
|
||||
case BerryType.STARF:
|
||||
return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), [ BattleStat.RAND ], 2));
|
||||
return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ BattleStat.RAND ], 2));
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ import { Type } from "./type";
|
|||
import * as Utils from "../utils";
|
||||
import { WeatherType } from "./weather";
|
||||
import { ArenaTagType, ArenaTrapTag } from "./arena-tag";
|
||||
import { FlinchChanceModifier } from "../modifier/modifier";
|
||||
|
||||
export enum MoveCategory {
|
||||
PHYSICAL,
|
||||
|
@ -1095,7 +1094,7 @@ export class StatChangeAttr extends MoveEffectAttr {
|
|||
|
||||
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) {
|
||||
const levels = this.getLevels(user);
|
||||
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, this.stats, levels));
|
||||
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, this.selfTarget, this.stats, levels));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
126
src/pokemon.ts
126
src/pokemon.ts
|
@ -23,6 +23,7 @@ import { WeatherType } from './data/weather';
|
|||
import { TempBattleStat } from './data/temp-battle-stat';
|
||||
import { WeakenMoveTypeTag } from './data/arena-tag';
|
||||
import { Biome } from './data/biome';
|
||||
import { Abilities, Ability, TypeImmunityAttr, abilities, applyPreDefendAbilityAttrs } from './data/ability';
|
||||
|
||||
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
public id: integer;
|
||||
|
@ -345,6 +346,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return this.getTypes().indexOf(type) > -1;
|
||||
}
|
||||
|
||||
getAbility(): Ability {
|
||||
return abilities[this.species.getAbility(this.abilityIndex)];
|
||||
}
|
||||
|
||||
getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier {
|
||||
const types = this.getTypes();
|
||||
return getTypeDamageMultiplier(moveType, types[0]) * (types.length ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;
|
||||
|
@ -479,70 +484,77 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
case MoveCategory.PHYSICAL:
|
||||
case MoveCategory.SPECIAL:
|
||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
const power = new Utils.NumberHolder(move.power);
|
||||
const typeMultiplier = getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1);
|
||||
const typeMultiplier = new Utils.NumberHolder(getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1));
|
||||
const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type);
|
||||
applyMoveAttrs(VariablePowerAttr, source, this, move, power);
|
||||
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power);
|
||||
const critLevel = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
|
||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
||||
if (source.getTag(BattlerTagType.CRIT_BOOST))
|
||||
critLevel.value += 2;
|
||||
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
|
||||
let isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance));
|
||||
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
|
||||
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
|
||||
const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1;
|
||||
const criticalMultiplier = isCritical ? 2 : 1;
|
||||
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
|
||||
damage = Math.floor(damage / 2);
|
||||
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||
if (this.getTag(hta.tagType))
|
||||
damage *= 2;
|
||||
});
|
||||
applyPreDefendAbilityAttrs(TypeImmunityAttr, this, source, battlerMove, cancelled, typeMultiplier);
|
||||
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
|
||||
if (damage && fixedDamage.value) {
|
||||
damage = fixedDamage.value;
|
||||
isCritical = false;
|
||||
result = MoveResult.EFFECTIVE;
|
||||
}
|
||||
if (cancelled.value)
|
||||
result = MoveResult.NO_EFFECT;
|
||||
else {
|
||||
applyMoveAttrs(VariablePowerAttr, source, this, move, power);
|
||||
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power);
|
||||
const critLevel = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
|
||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
||||
if (source.getTag(BattlerTagType.CRIT_BOOST))
|
||||
critLevel.value += 2;
|
||||
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
|
||||
let isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance));
|
||||
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
|
||||
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
|
||||
const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1;
|
||||
const criticalMultiplier = isCritical ? 2 : 1;
|
||||
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier.value * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
|
||||
damage = Math.floor(damage / 2);
|
||||
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||
if (this.getTag(hta.tagType))
|
||||
damage *= 2;
|
||||
});
|
||||
|
||||
console.log('damage', damage, move.name, move.power, sourceAtk, targetDef);
|
||||
|
||||
if (!result) {
|
||||
if (typeMultiplier >= 2)
|
||||
result = MoveResult.SUPER_EFFECTIVE;
|
||||
else if (typeMultiplier >= 1)
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
|
||||
if (damage && fixedDamage.value) {
|
||||
damage = fixedDamage.value;
|
||||
isCritical = false;
|
||||
result = MoveResult.EFFECTIVE;
|
||||
else if (typeMultiplier > 0)
|
||||
result = MoveResult.NOT_VERY_EFFECTIVE;
|
||||
else
|
||||
result = MoveResult.NO_EFFECT;
|
||||
}
|
||||
}
|
||||
|
||||
if (damage) {
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult));
|
||||
if (isCritical)
|
||||
this.scene.queueMessage('A critical hit!');
|
||||
this.damage(damage);
|
||||
source.turnData.damageDealt += damage;
|
||||
}
|
||||
console.log('damage', damage, move.name, move.power, sourceAtk, targetDef);
|
||||
|
||||
switch (result) {
|
||||
case MoveResult.SUPER_EFFECTIVE:
|
||||
this.scene.queueMessage('It\'s super effective!');
|
||||
break;
|
||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
||||
this.scene.queueMessage('It\'s not very effective!');
|
||||
break;
|
||||
case MoveResult.NO_EFFECT:
|
||||
this.scene.queueMessage(`It doesn\'t affect ${this.name}!`);
|
||||
break;
|
||||
if (!result) {
|
||||
if (typeMultiplier.value >= 2)
|
||||
result = MoveResult.SUPER_EFFECTIVE;
|
||||
else if (typeMultiplier.value >= 1)
|
||||
result = MoveResult.EFFECTIVE;
|
||||
else if (typeMultiplier.value > 0)
|
||||
result = MoveResult.NOT_VERY_EFFECTIVE;
|
||||
else
|
||||
result = MoveResult.NO_EFFECT;
|
||||
}
|
||||
|
||||
if (damage) {
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult));
|
||||
if (isCritical)
|
||||
this.scene.queueMessage('A critical hit!');
|
||||
this.damage(damage);
|
||||
source.turnData.damageDealt += damage;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case MoveResult.SUPER_EFFECTIVE:
|
||||
this.scene.queueMessage('It\'s super effective!');
|
||||
break;
|
||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
||||
this.scene.queueMessage('It\'s not very effective!');
|
||||
break;
|
||||
case MoveResult.NO_EFFECT:
|
||||
this.scene.queueMessage(`It doesn\'t affect ${this.name}!`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MoveCategory.STATUS:
|
||||
|
|
|
@ -94,6 +94,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
|
||||
this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 126, 'ABILITY:', TextStyle.SUMMARY, { fontSize: '64px' });
|
||||
this.pokemonAbilityLabelText.setOrigin(0, 0);
|
||||
this.pokemonAbilityLabelText.setVisible(false);
|
||||
this.starterSelectContainer.add(this.pokemonAbilityLabelText);
|
||||
|
||||
this.pokemonAbilityText = addTextObject(this.scene, 38, 126, '', TextStyle.SUMMARY, { fontSize: '64px' });
|
||||
|
@ -454,11 +455,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
if (species && this.speciesStarterDexEntry) {
|
||||
this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 3));
|
||||
this.pokemonNameText.setText(species.name.toUpperCase());
|
||||
this.pokemonAbilityLabelText.setVisible(true);
|
||||
|
||||
this.setSpeciesDetails(species, !!this.speciesStarterDexEntry?.shiny, this.speciesStarterDexEntry?.formIndex, !!this.speciesStarterDexEntry?.female, this.speciesStarterDexEntry?.abilityIndex);
|
||||
} else {
|
||||
this.pokemonNumberText.setText(Utils.padInt(0, 3));
|
||||
this.pokemonNameText.setText(species ? '???' : '');
|
||||
this.pokemonAbilityLabelText.setVisible(false);
|
||||
|
||||
this.setSpeciesDetails(species, false, 0, false, 0);
|
||||
}
|
||||
|
@ -563,7 +566,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.canCycleAbility = false;
|
||||
}
|
||||
|
||||
if (species.malePercent !== null) {
|
||||
if (defaultDexEntry && species.malePercent !== null) {
|
||||
const gender = !female ? Gender.MALE : Gender.FEMALE;
|
||||
this.pokemonGenderText.setText(getGenderSymbol(gender));
|
||||
this.pokemonGenderText.setColor(getGenderColor(gender));
|
||||
|
@ -571,12 +574,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
} else
|
||||
this.pokemonGenderText.setText('');
|
||||
|
||||
const ability = this.lastSpecies.getAbility(abilityIndex);
|
||||
this.pokemonAbilityText.setText(abilities[ability].name.toUpperCase());
|
||||
if (defaultDexEntry) {
|
||||
const ability = this.lastSpecies.getAbility(abilityIndex);
|
||||
this.pokemonAbilityText.setText(abilities[ability].name.toUpperCase());
|
||||
|
||||
const isHidden = ability === this.lastSpecies.abilityHidden;
|
||||
this.pokemonAbilityText.setColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
|
||||
this.pokemonAbilityText.setShadowColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
|
||||
const isHidden = ability === this.lastSpecies.abilityHidden;
|
||||
this.pokemonAbilityText.setColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
|
||||
this.pokemonAbilityText.setShadowColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
|
||||
} else
|
||||
this.pokemonAbilityText.setText('');
|
||||
} else {
|
||||
this.pokemonGenderText.setText('');
|
||||
this.pokemonAbilityText.setText('');
|
||||
|
|
Loading…
Reference in New Issue