Implement Soak, Forest's Curse, and Trick-or-Treat
Updates the UI and type effectiveness to support three types, and makes old moves that change types update the UI.
This commit is contained in:
parent
e94b17bbf5
commit
9dfd5a1e6e
|
@ -2863,6 +2863,7 @@ export class RemoveTypeAttr extends MoveEffectAttr {
|
||||||
const userTypes = user.getTypes(true)
|
const userTypes = user.getTypes(true)
|
||||||
const modifiedTypes = userTypes.filter(type => type !== this.removedType);
|
const modifiedTypes = userTypes.filter(type => type !== this.removedType);
|
||||||
user.summonData.types = modifiedTypes;
|
user.summonData.types = modifiedTypes;
|
||||||
|
user.updateInfo();
|
||||||
|
|
||||||
|
|
||||||
if (this.messageCallback) {
|
if (this.messageCallback) {
|
||||||
|
@ -2883,11 +2884,16 @@ export class CopyTypeAttr extends MoveEffectAttr {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
user.summonData.types = target.getTypes(true);
|
user.summonData.types = target.getTypes(true);
|
||||||
|
user.updateInfo();
|
||||||
|
|
||||||
user.scene.queueMessage(getPokemonMessage(user, `'s type\nchanged to match ${target.name}'s!`));
|
user.scene.queueMessage(getPokemonMessage(user, `'s type\nchanged to match ${target.name}'s!`));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCondition(): MoveConditionFunc {
|
||||||
|
return (user, target, move) => target.getTypes()[0] !== Type.UNKNOWN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CopyBiomeTypeAttr extends MoveEffectAttr {
|
export class CopyBiomeTypeAttr extends MoveEffectAttr {
|
||||||
|
@ -2902,6 +2908,7 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr {
|
||||||
const biomeType = user.scene.arena.getTypeForBiome();
|
const biomeType = user.scene.arena.getTypeForBiome();
|
||||||
|
|
||||||
user.summonData.types = [ biomeType ];
|
user.summonData.types = [ biomeType ];
|
||||||
|
user.updateInfo();
|
||||||
|
|
||||||
user.scene.queueMessage(getPokemonMessage(user, ` transformed\ninto the ${Utils.toReadableString(Type[biomeType])} type!`));
|
user.scene.queueMessage(getPokemonMessage(user, ` transformed\ninto the ${Utils.toReadableString(Type[biomeType])} type!`));
|
||||||
|
|
||||||
|
@ -2909,6 +2916,54 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ChangeTypeAttr extends MoveEffectAttr {
|
||||||
|
private type: Type;
|
||||||
|
|
||||||
|
constructor(type: Type) {
|
||||||
|
super(false, MoveEffectTrigger.HIT);
|
||||||
|
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
target.summonData.types = [this.type];
|
||||||
|
target.updateInfo();
|
||||||
|
|
||||||
|
user.scene.queueMessage(getPokemonMessage(target, ` transformed\ninto the ${Utils.toReadableString(Type[this.type])} type!`));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCondition(): MoveConditionFunc {
|
||||||
|
return (user, target, move) => !target.isTerastallized() && !target.hasAbility(Abilities.MULTITYPE) && !target.hasAbility(Abilities.RKS_SYSTEM) && !(target.getTypes().length === 1 && target.getTypes()[0] === this.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddTypeAttr extends MoveEffectAttr {
|
||||||
|
private type: Type;
|
||||||
|
|
||||||
|
constructor(type: Type) {
|
||||||
|
super(false, MoveEffectTrigger.HIT);
|
||||||
|
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
const types = target.getTypes().slice(0, 2).filter(t => t !== Type.UNKNOWN); // TODO: Figure out some way to actually check if another version of this effect is already applied
|
||||||
|
types.push(this.type);
|
||||||
|
target.summonData.types = types;
|
||||||
|
target.updateInfo();
|
||||||
|
|
||||||
|
user.scene.queueMessage(`${Utils.toReadableString(Type[this.type])} was added to\n` + getPokemonMessage(target, '!'));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCondition(): MoveConditionFunc {
|
||||||
|
return (user, target, move) => !target.isTerastallized()&& !target.getTypes().includes(this.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class FirstMoveTypeAttr extends MoveEffectAttr {
|
export class FirstMoveTypeAttr extends MoveEffectAttr {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(true);
|
super(true);
|
||||||
|
@ -4889,7 +4944,7 @@ export function initMoves() {
|
||||||
.attr(BattleStatRatioPowerAttr, Stat.SPD)
|
.attr(BattleStatRatioPowerAttr, Stat.SPD)
|
||||||
.ballBombMove(),
|
.ballBombMove(),
|
||||||
new StatusMove(Moves.SOAK, Type.WATER, 100, 20, -1, 0, 5)
|
new StatusMove(Moves.SOAK, Type.WATER, 100, 20, -1, 0, 5)
|
||||||
.unimplemented(),
|
.attr(ChangeTypeAttr, Type.WATER),
|
||||||
new AttackMove(Moves.FLAME_CHARGE, Type.FIRE, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 5)
|
new AttackMove(Moves.FLAME_CHARGE, Type.FIRE, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 5)
|
||||||
.attr(StatChangeAttr, BattleStat.SPD, 1, true),
|
.attr(StatChangeAttr, BattleStat.SPD, 1, true),
|
||||||
new SelfStatusMove(Moves.COIL, Type.POISON, -1, 20, -1, 0, 5)
|
new SelfStatusMove(Moves.COIL, Type.POISON, -1, 20, -1, 0, 5)
|
||||||
|
@ -5095,7 +5150,8 @@ export function initMoves() {
|
||||||
.ignoresProtect()
|
.ignoresProtect()
|
||||||
.ignoresVirtual(),
|
.ignoresVirtual(),
|
||||||
new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6)
|
new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6)
|
||||||
.unimplemented(),
|
.attr(AddTypeAttr, Type.GHOST)
|
||||||
|
.partial(),
|
||||||
new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, 100, 0, 6)
|
new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, 100, 0, 6)
|
||||||
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1)
|
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1)
|
||||||
.soundBased(),
|
.soundBased(),
|
||||||
|
@ -5107,7 +5163,8 @@ export function initMoves() {
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS)
|
.target(MoveTarget.ALL_NEAR_OTHERS)
|
||||||
.triageMove(),
|
.triageMove(),
|
||||||
new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6)
|
new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6)
|
||||||
.unimplemented(),
|
.attr(AddTypeAttr, Type.GRASS)
|
||||||
|
.partial(),
|
||||||
new AttackMove(Moves.PETAL_BLIZZARD, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 6)
|
new AttackMove(Moves.PETAL_BLIZZARD, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 6)
|
||||||
.windMove()
|
.windMove()
|
||||||
.makesContact(false)
|
.makesContact(false)
|
||||||
|
|
|
@ -21,9 +21,12 @@ export enum Type {
|
||||||
STELLAR
|
STELLAR
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TypeDamageMultiplier = 0 | 0.25 | 0.5 | 1 | 2 | 4;
|
export type TypeDamageMultiplier = 0 | 0.125 | 0.25 | 0.5 | 1 | 2 | 4 | 8;
|
||||||
|
|
||||||
export function getTypeDamageMultiplier(attackType: integer, defType: integer): TypeDamageMultiplier {
|
export function getTypeDamageMultiplier(attackType: integer, defType: integer): TypeDamageMultiplier {
|
||||||
|
if (attackType === Type.UNKNOWN || defType === Type.UNKNOWN)
|
||||||
|
return 1;
|
||||||
|
|
||||||
switch (defType) {
|
switch (defType) {
|
||||||
case Type.NORMAL:
|
case Type.NORMAL:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
|
|
|
@ -745,7 +745,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!types.length)
|
if (!types.length)
|
||||||
types.push(Type.NORMAL);
|
types.push(Type.UNKNOWN);
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
@ -859,7 +859,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
if (moveType === Type.STELLAR)
|
if (moveType === Type.STELLAR)
|
||||||
return this.isTerastallized() ? 2 : 1;
|
return this.isTerastallized() ? 2 : 1;
|
||||||
const types = this.getTypes(true, true);
|
const types = this.getTypes(true, true);
|
||||||
let multiplier = getTypeDamageMultiplier(moveType, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;
|
let multiplier = getTypeDamageMultiplier(moveType, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(moveType, types[1]) : 1) * (types.length > 2 ? getTypeDamageMultiplier(moveType, types[2]) : 1) as TypeDamageMultiplier;
|
||||||
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
||||||
if (this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && multiplier >= 2 && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2)
|
if (this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && multiplier >= 2 && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2)
|
||||||
multiplier /= 2;
|
multiplier /= 2;
|
||||||
|
|
|
@ -40,6 +40,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||||
private hpNumbersContainer: Phaser.GameObjects.Container;
|
private hpNumbersContainer: Phaser.GameObjects.Container;
|
||||||
private type1Icon: Phaser.GameObjects.Sprite;
|
private type1Icon: Phaser.GameObjects.Sprite;
|
||||||
private type2Icon: Phaser.GameObjects.Sprite;
|
private type2Icon: Phaser.GameObjects.Sprite;
|
||||||
|
private type3Icon: Phaser.GameObjects.Sprite;
|
||||||
private expBar: Phaser.GameObjects.Image;
|
private expBar: Phaser.GameObjects.Image;
|
||||||
|
|
||||||
public expMaskRect: Phaser.GameObjects.Graphics;
|
public expMaskRect: Phaser.GameObjects.Graphics;
|
||||||
|
@ -137,6 +138,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||||
this.type2Icon.setOrigin(0, 0);
|
this.type2Icon.setOrigin(0, 0);
|
||||||
this.add(this.type2Icon);
|
this.add(this.type2Icon);
|
||||||
|
|
||||||
|
this.type3Icon = this.scene.add.sprite(player ? -154 : 0, player ? -17 : -15.5, `pbinfo_${player ? 'player' : 'enemy'}_type`);
|
||||||
|
this.type3Icon.setOrigin(0, 0);
|
||||||
|
this.add(this.type3Icon);
|
||||||
|
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
this.hpNumbersContainer = this.scene.add.container(-15, 10);
|
this.hpNumbersContainer = this.scene.add.container(-15, 10);
|
||||||
this.add(this.hpNumbersContainer);
|
this.add(this.hpNumbersContainer);
|
||||||
|
@ -221,8 +226,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||||
this.type1Icon.setTexture(`pbinfo_${this.player ? 'player' : 'enemy'}_type${types.length > 1 ? '1' : ''}`);
|
this.type1Icon.setTexture(`pbinfo_${this.player ? 'player' : 'enemy'}_type${types.length > 1 ? '1' : ''}`);
|
||||||
this.type1Icon.setFrame(Type[types[0]].toLowerCase());
|
this.type1Icon.setFrame(Type[types[0]].toLowerCase());
|
||||||
this.type2Icon.setVisible(types.length > 1);
|
this.type2Icon.setVisible(types.length > 1);
|
||||||
|
this.type3Icon.setVisible(types.length > 2);
|
||||||
if (types.length > 1)
|
if (types.length > 1)
|
||||||
this.type2Icon.setFrame(Type[types[1]].toLowerCase());
|
this.type2Icon.setFrame(Type[types[1]].toLowerCase());
|
||||||
|
if (types.length > 2)
|
||||||
|
this.type3Icon.setFrame(Type[types[2]].toLowerCase());
|
||||||
|
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
|
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
|
||||||
|
@ -249,7 +257,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||||
const offsetElements = [ this.nameText, this.genderText, this.teraIcon, this.splicedIcon, this.shinyIcon, this.statusIndicator, this.levelContainer ];
|
const offsetElements = [ this.nameText, this.genderText, this.teraIcon, this.splicedIcon, this.shinyIcon, this.statusIndicator, this.levelContainer ];
|
||||||
offsetElements.forEach(el => el.y += 1.5 * (mini ? -1 : 1));
|
offsetElements.forEach(el => el.y += 1.5 * (mini ? -1 : 1));
|
||||||
|
|
||||||
[ this.type1Icon, this.type2Icon ].forEach(el => {
|
[ this.type1Icon, this.type2Icon, this.type3Icon ].forEach(el => {
|
||||||
el.x += 4 * (mini ? 1 : -1);
|
el.x += 4 * (mini ? 1 : -1);
|
||||||
el.y += -8 * (mini ? 1 : -1);
|
el.y += -8 * (mini ? 1 : -1);
|
||||||
});
|
});
|
||||||
|
@ -346,8 +354,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||||
this.type1Icon.setTexture(`pbinfo_${this.player ? 'player' : 'enemy'}_type${types.length > 1 ? '1' : ''}`);
|
this.type1Icon.setTexture(`pbinfo_${this.player ? 'player' : 'enemy'}_type${types.length > 1 ? '1' : ''}`);
|
||||||
this.type1Icon.setFrame(Type[types[0]].toLowerCase());
|
this.type1Icon.setFrame(Type[types[0]].toLowerCase());
|
||||||
this.type2Icon.setVisible(types.length > 1);
|
this.type2Icon.setVisible(types.length > 1);
|
||||||
|
this.type3Icon.setVisible(types.length > 2);
|
||||||
if (types.length > 1)
|
if (types.length > 1)
|
||||||
this.type2Icon.setFrame(Type[types[1]].toLowerCase());
|
this.type2Icon.setFrame(Type[types[1]].toLowerCase());
|
||||||
|
if (types.length > 2)
|
||||||
|
this.type3Icon.setFrame(Type[types[2]].toLowerCase());
|
||||||
|
|
||||||
const updateHpFrame = () => {
|
const updateHpFrame = () => {
|
||||||
const hpFrame = this.hpBar.scaleX > 0.5 ? 'high' : this.hpBar.scaleX > 0.25 ? 'medium' : 'low';
|
const hpFrame = this.hpBar.scaleX > 0.5 ? 'high' : this.hpBar.scaleX > 0.25 ? 'medium' : 'low';
|
||||||
|
|
Loading…
Reference in New Issue