Implement Transform move and Imposter ability

This commit is contained in:
Flashfyre committed 2023-10-29 01:28:56 -04:00
1 parent e3368b79e5
commit 813b4894f3
261 files changed
+115 -42

No files matched your search

+3 -3
View File
@@ -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');
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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[];
}
+5 -5
View File
@@ -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
+4 -4
View File
@@ -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: