Implement Transform move and Imposter ability
This commit is contained in:
1 parent
e3368b79e5
commit
813b4894f3
261 files changed
+115
-42
No files matched your search
@@ -979,7 +979,7 @@ export class CommandPhase extends FieldPhase {
|
||||
case Command.FIGHT:
|
||||
let useStruggle = false;
|
||||
if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean) || (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) {
|
||||
const moveId = !useStruggle ? playerPokemon.moveset[cursor].moveId : Moves.STRUGGLE;
|
||||
const moveId = !useStruggle ? playerPokemon.getMoveset()[cursor].moveId : Moves.STRUGGLE;
|
||||
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor,
|
||||
move: cursor > -1 ? { move: moveId, targets: [] } : null, args: args };
|
||||
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, cursor > -1 ? moveId : Moves.NONE) : args[2];
|
||||
@@ -2757,9 +2757,9 @@ export class AttemptRunPhase extends PokemonPhase {
|
||||
const playerPokemon = this.getPokemon();
|
||||
const enemyField = this.scene.getEnemyField();
|
||||
|
||||
const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.stats[Stat.SPD], 0) / enemyField.length;
|
||||
const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length;
|
||||
|
||||
const escapeChance = (((playerPokemon.stats[Stat.SPD] * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256;
|
||||
const escapeChance = (((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256;
|
||||
|
||||
if (Utils.randInt(256) < escapeChance) {
|
||||
this.scene.playSound('flee');
|
||||
|
||||
@@ -320,6 +320,8 @@ export default class BattleScene extends Phaser.Scene {
|
||||
this.loadSe('pb_tray_ball');
|
||||
this.loadSe('pb_tray_empty');
|
||||
|
||||
this.loadSe('PRSFX- Transform', 'battle_anims');
|
||||
|
||||
this.loadBgm('menu');
|
||||
|
||||
this.loadBgm('level_up_fanfare', 'bw/level_up_fanfare.mp3');
|
||||
|
||||
+32
-1
@@ -9,6 +9,7 @@ import { BattlerTag, BattlerTagType } from "./battler-tag";
|
||||
import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
|
||||
import { MoveFlags, Moves, RecoilAttr } from "./move";
|
||||
import { ArenaTagType } from "./arena-tag";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
|
||||
export class Ability {
|
||||
public id: Abilities;
|
||||
@@ -487,6 +488,35 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class PostSummonTransformAbAttr extends PostSummonAbAttr {
|
||||
constructor() {
|
||||
super(true);
|
||||
}
|
||||
|
||||
applyPostSummon(pokemon: Pokemon, args: any[]): boolean {
|
||||
const targets = pokemon.getOpponents();
|
||||
let target: Pokemon;
|
||||
if (targets.length > 1)
|
||||
pokemon.scene.executeWithSeedOffset(() => target = Phaser.Math.RND.pick(targets), pokemon.scene.currentBattle.waveIndex);
|
||||
else
|
||||
target = targets[0];
|
||||
|
||||
pokemon.summonData.speciesForm = target.getSpeciesForm();
|
||||
pokemon.summonData.gender = target.getGender();
|
||||
pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1));
|
||||
pokemon.summonData.battleStats = target.summonData.battleStats.slice(0);
|
||||
pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m.moveId, m.ppUsed, m.ppUp));
|
||||
pokemon.summonData.types = target.getTypes();
|
||||
|
||||
pokemon.scene.playSound('PRSFX- Transform');
|
||||
pokemon.loadAssets().then(() => pokemon.playAnim());
|
||||
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` transformed\ninto ${target.name}!`));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PreStatChangeAbAttr extends AbAttr {
|
||||
applyPreStatChange(pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
return false;
|
||||
@@ -1437,7 +1467,8 @@ export function initAbilities() {
|
||||
new Ability(Abilities.HEALER, "Healer (N)", "May heal an ally's status conditions.", 5),
|
||||
new Ability(Abilities.HEAVY_METAL, "Heavy Metal (N)", "Doubles the Pokémon's weight.", 5),
|
||||
new Ability(Abilities.ILLUSION, "Illusion (N)", "Enters battle disguised as the last Pokémon in the party.", 5),
|
||||
new Ability(Abilities.IMPOSTER, "Imposter (N)", "It transforms itself into the Pokémon it is facing.", 5),
|
||||
new Ability(Abilities.IMPOSTER, "Imposter", "It transforms itself into the Pokémon it is facing.", 5)
|
||||
.attr(PostSummonTransformAbAttr),
|
||||
new Ability(Abilities.INFILTRATOR, "Infiltrator (N)", "Passes through the foe's barrier and strikes.", 5),
|
||||
new Ability(Abilities.IRON_BARBS, "Iron Barbs (N)", "Inflicts damage to the Pokémon on contact.", 5),
|
||||
new Ability(Abilities.JUSTIFIED, "Justified (N)", "Raises Attack when hit by a Dark-type move.", 5),
|
||||
|
||||
+25
-1
@@ -2199,6 +2199,29 @@ export class SketchAttr extends MoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class TransformAttr extends MoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
if (!super.apply(user, target, move, args))
|
||||
return resolve(false);
|
||||
|
||||
user.summonData.speciesForm = target.getSpeciesForm();
|
||||
user.summonData.gender = target.getGender();
|
||||
user.summonData.stats = [ user.stats[Stat.HP] ].concat(target.stats.slice(1));
|
||||
user.summonData.battleStats = target.summonData.battleStats.slice(0);
|
||||
user.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m.moveId, m.ppUsed, m.ppUp));
|
||||
user.summonData.types = target.getTypes();
|
||||
|
||||
user.scene.queueMessage(getPokemonMessage(user, ` transformed\ninto ${target.name}!`));
|
||||
|
||||
user.loadAssets().then(() => {
|
||||
user.playAnim();
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const failOnGravityCondition = (user: Pokemon, target: Pokemon, move: Move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY);
|
||||
|
||||
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
|
||||
@@ -2611,7 +2634,8 @@ export function initMoves() {
|
||||
.makesContact(false)
|
||||
.ignoresVirtual()
|
||||
.target(MoveTarget.OTHER),
|
||||
new SelfStatusMove(Moves.TRANSFORM, "Transform (N)", Type.NORMAL, -1, 10, -1, "User takes on the form and attacks of the opponent.", -1, 0, 1),
|
||||
new StatusMove(Moves.TRANSFORM, "Transform", Type.NORMAL, -1, 10, -1, "User takes on the form and attacks of the opponent.", -1, 0, 1)
|
||||
.attr(TransformAttr),
|
||||
new AttackMove(Moves.BUBBLE, "Bubble", Type.WATER, MoveCategory.SPECIAL, 40, 100, 30, -1, "May lower opponent's Speed.", 10, 0, 1)
|
||||
.attr(StatChangeAttr, BattleStat.SPD, -1)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
|
||||
+44
-28
@@ -158,7 +158,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.add(sprite);
|
||||
this.add(tintSprite);
|
||||
|
||||
this.getSpeciesForm().generateIconAnim(scene, this.gender === Gender.FEMALE, formIndex);
|
||||
this.getSpeciesForm().generateIconAnim(scene, this.getGender() === Gender.FEMALE, formIndex);
|
||||
|
||||
if (this.shiny) {
|
||||
const shinySparkle = this.scene.add.sprite(0, 0, 'shiny');
|
||||
@@ -204,7 +204,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
Promise.allSettled(moveIds.map(m => initMoveAnim(m)))
|
||||
.then(() => {
|
||||
loadMoveAnimAssets(this.scene, moveIds);
|
||||
this.getSpeciesForm().loadAssets(this.scene, this.gender === Gender.FEMALE, this.formIndex, this.shiny);
|
||||
this.getSpeciesForm().loadAssets(this.scene, this.getGender() === Gender.FEMALE, this.formIndex, this.shiny);
|
||||
if (this.isPlayer())
|
||||
this.scene.loadAtlas(this.getBattleSpriteKey(), 'pokemon', this.getBattleSpriteAtlasPath());
|
||||
this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => {
|
||||
@@ -232,43 +232,45 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
});
|
||||
}
|
||||
|
||||
getSpriteAtlasPath(): string {
|
||||
return this.getSpriteId().replace(/\_{2}/g, '/');
|
||||
getSpriteAtlasPath(ignoreOverride?: boolean): string {
|
||||
return this.getSpriteId(ignoreOverride).replace(/\_{2}/g, '/');
|
||||
}
|
||||
|
||||
getBattleSpriteAtlasPath(): string {
|
||||
return this.getBattleSpriteId().replace(/\_{2}/g, '/');
|
||||
getBattleSpriteAtlasPath(ignoreOverride?: boolean): string {
|
||||
return this.getBattleSpriteId(ignoreOverride).replace(/\_{2}/g, '/');
|
||||
}
|
||||
|
||||
getSpriteId(): string {
|
||||
return this.getSpeciesForm().getSpriteId(this.gender === Gender.FEMALE, this.formIndex, this.shiny);
|
||||
getSpriteId(ignoreOverride?: boolean): string {
|
||||
return this.getSpeciesForm(ignoreOverride).getSpriteId(this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, this.shiny);
|
||||
}
|
||||
|
||||
getBattleSpriteId(): string {
|
||||
return `${this.isPlayer() ? 'back__' : ''}${this.getSpriteId()}`;
|
||||
getBattleSpriteId(ignoreOverride?: boolean): string {
|
||||
return `${this.isPlayer() ? 'back__' : ''}${this.getSpriteId(ignoreOverride)}`;
|
||||
}
|
||||
|
||||
getSpriteKey(): string {
|
||||
return this.getSpeciesForm().getSpriteKey(this.gender === Gender.FEMALE, this.formIndex, this.shiny);
|
||||
getSpriteKey(ignoreOverride?: boolean): string {
|
||||
return this.getSpeciesForm(ignoreOverride).getSpriteKey(this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, this.shiny);
|
||||
}
|
||||
|
||||
getBattleSpriteKey(): string {
|
||||
return `pkmn__${this.getBattleSpriteId()}`;
|
||||
getBattleSpriteKey(ignoreOverride?: boolean): string {
|
||||
return `pkmn__${this.getBattleSpriteId(ignoreOverride)}`;
|
||||
}
|
||||
|
||||
getIconAtlasKey(): string {
|
||||
return this.getSpeciesForm().getIconAtlasKey(this.formIndex);
|
||||
getIconAtlasKey(ignoreOverride?: boolean): string {
|
||||
return this.getSpeciesForm(ignoreOverride).getIconAtlasKey(this.formIndex);
|
||||
}
|
||||
|
||||
getIconId(): string {
|
||||
return this.getSpeciesForm().getIconId(this.gender === Gender.FEMALE, this.formIndex);
|
||||
getIconId(ignoreOverride?: boolean): string {
|
||||
return this.getSpeciesForm(ignoreOverride).getIconId(this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex);
|
||||
}
|
||||
|
||||
getIconKey(): string {
|
||||
return `pkmn_icon__${this.getIconId()}`;
|
||||
getIconKey(ignoreOverride?: boolean): string {
|
||||
return `pkmn_icon__${this.getIconId(ignoreOverride)}`;
|
||||
}
|
||||
|
||||
getSpeciesForm(): PokemonSpeciesForm {
|
||||
getSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm {
|
||||
if (!ignoreOverride && this.summonData?.speciesForm)
|
||||
return this.summonData.speciesForm;
|
||||
if (!this.species.forms?.length)
|
||||
return this.species;
|
||||
return this.species.forms[this.formIndex];
|
||||
@@ -335,14 +337,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
});
|
||||
}
|
||||
|
||||
getStat(stat: Stat): integer {
|
||||
return this.stats[stat];
|
||||
}
|
||||
|
||||
getBattleStat(stat: Stat): integer {
|
||||
if (stat === Stat.HP)
|
||||
return this.stats[Stat.HP];
|
||||
return this.getStat(Stat.HP);
|
||||
const battleStat = (stat - 1) as BattleStat;
|
||||
const statLevel = new Utils.IntegerHolder(this.summonData.battleStats[battleStat]);
|
||||
if (this.isPlayer())
|
||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), battleStat as integer as TempBattleStat, statLevel);
|
||||
const statValue = new Utils.NumberHolder(this.stats[stat]);
|
||||
const statValue = new Utils.NumberHolder(this.getStat(stat));
|
||||
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, battleStat, statValue);
|
||||
let ret = statValue.value * (Math.max(2, 2 + statLevel.value) / Math.max(2, 2 - statLevel.value));
|
||||
if (stat === Stat.SPDEF && this.scene.arena.weather?.weatherType === WeatherType.SANDSTORM)
|
||||
@@ -380,7 +386,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
getMaxHp(): integer {
|
||||
return this.stats[Stat.HP];
|
||||
return this.getStat(Stat.HP);
|
||||
}
|
||||
|
||||
getInverseHp(): integer {
|
||||
@@ -391,16 +397,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return Math.floor((this.hp / this.getMaxHp()) * 100) / 100;
|
||||
}
|
||||
|
||||
getMoveset(): PokemonMove[] {
|
||||
if (this.summonData?.moveset)
|
||||
getGender(ignoreOverride?: boolean): Gender {
|
||||
if (!ignoreOverride && this.summonData?.gender !== undefined)
|
||||
return this.summonData.gender;
|
||||
return this.gender;
|
||||
}
|
||||
|
||||
getMoveset(ignoreOverride?: boolean): PokemonMove[] {
|
||||
if (!ignoreOverride && this.summonData?.moveset)
|
||||
return this.summonData.moveset;
|
||||
return this.moveset;
|
||||
}
|
||||
|
||||
getTypes(): Type[] {
|
||||
getTypes(ignoreOverride?: boolean): Type[] {
|
||||
const types = [];
|
||||
|
||||
if (this.summonData?.types)
|
||||
if (!ignoreOverride && this.summonData?.types)
|
||||
this.summonData.types.forEach(t => types.push(t));
|
||||
else {
|
||||
const speciesForm = this.getSpeciesForm();
|
||||
@@ -1397,6 +1409,10 @@ export class PokemonSummonData {
|
||||
public disabledMove: Moves = Moves.NONE;
|
||||
public disabledTurns: integer = 0;
|
||||
public tags: BattlerTag[] = [];
|
||||
|
||||
public speciesForm: PokemonSpeciesForm;
|
||||
public gender: Gender;
|
||||
public stats: integer[];
|
||||
public moveset: PokemonMove[];
|
||||
public types: Type[];
|
||||
}
|
||||
|
||||
@@ -618,8 +618,8 @@ class PartySlot extends Phaser.GameObjects.Container {
|
||||
|
||||
this.add(slotPb);
|
||||
|
||||
const pokemonIcon = this.scene.add.sprite(slotPb.x, slotPb.y, this.pokemon.getIconAtlasKey());
|
||||
pokemonIcon.play(this.pokemon.getIconKey());
|
||||
const pokemonIcon = this.scene.add.sprite(slotPb.x, slotPb.y, this.pokemon.getIconAtlasKey(true));
|
||||
pokemonIcon.play(this.pokemon.getIconKey(true));
|
||||
|
||||
this.add(pokemonIcon);
|
||||
|
||||
@@ -640,12 +640,12 @@ class PartySlot extends Phaser.GameObjects.Container {
|
||||
|
||||
slotInfoContainer.add([ slotName, slotLevelLabel, slotLevelText ]);
|
||||
|
||||
const genderSymbol = getGenderSymbol(this.pokemon.gender);
|
||||
const genderSymbol = getGenderSymbol(this.pokemon.getGender(true));
|
||||
|
||||
if (genderSymbol) {
|
||||
const slotGenderText = addTextObject(this.scene, 0, 0, genderSymbol, TextStyle.PARTY);
|
||||
slotGenderText.setColor(getGenderColor(this.pokemon.gender));
|
||||
slotGenderText.setShadowColor(getGenderColor(this.pokemon.gender, true));
|
||||
slotGenderText.setColor(getGenderColor(this.pokemon.getGender(true)));
|
||||
slotGenderText.setShadowColor(getGenderColor(this.pokemon.getGender(true), true));
|
||||
if (this.slotIndex >= battlerCount)
|
||||
slotGenderText.setPositionRelative(slotLevelLabel, 36, 0);
|
||||
else
|
||||
|
||||
@@ -196,15 +196,15 @@ export default class SummaryUiHandler extends UiHandler {
|
||||
this.numberText.setColor(getTextColor(!this.pokemon.shiny ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
|
||||
this.numberText.setShadowColor(getTextColor(!this.pokemon.shiny ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
|
||||
|
||||
this.pokemonSprite.play(this.pokemon.getSpriteKey());
|
||||
this.pokemonSprite.play(this.pokemon.getSpriteKey(true));
|
||||
this.pokemon.cry();
|
||||
|
||||
this.nameText.setText(this.pokemon.name);
|
||||
this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball));
|
||||
this.levelText.setText(this.pokemon.level.toString());
|
||||
this.genderText.setText(getGenderSymbol(this.pokemon.gender));
|
||||
this.genderText.setColor(getGenderColor(this.pokemon.gender));
|
||||
this.genderText.setShadowColor(getGenderColor(this.pokemon.gender, true));
|
||||
this.genderText.setText(getGenderSymbol(this.pokemon.getGender(true)));
|
||||
this.genderText.setColor(getGenderColor(this.pokemon.getGender(true)));
|
||||
this.genderText.setShadowColor(getGenderColor(this.pokemon.getGender(true), true));
|
||||
|
||||
switch (this.summaryUiMode) {
|
||||
case SummaryUiMode.DEFAULT:
|
||||
|
||||
Reference in new issue
Block a user