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:
Xavion3 2024-04-20 03:32:40 +10:00 committed by Samuel H
parent e94b17bbf5
commit 9dfd5a1e6e
4 changed files with 78 additions and 7 deletions

View File

@ -2863,6 +2863,7 @@ export class RemoveTypeAttr extends MoveEffectAttr {
const userTypes = user.getTypes(true)
const modifiedTypes = userTypes.filter(type => type !== this.removedType);
user.summonData.types = modifiedTypes;
user.updateInfo();
if (this.messageCallback) {
@ -2883,11 +2884,16 @@ export class CopyTypeAttr extends MoveEffectAttr {
return false;
user.summonData.types = target.getTypes(true);
user.updateInfo();
user.scene.queueMessage(getPokemonMessage(user, `'s type\nchanged to match ${target.name}'s!`));
return true;
}
getCondition(): MoveConditionFunc {
return (user, target, move) => target.getTypes()[0] !== Type.UNKNOWN;
}
}
export class CopyBiomeTypeAttr extends MoveEffectAttr {
@ -2902,6 +2908,7 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr {
const biomeType = user.scene.arena.getTypeForBiome();
user.summonData.types = [ biomeType ];
user.updateInfo();
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 {
constructor() {
super(true);
@ -4889,7 +4944,7 @@ export function initMoves() {
.attr(BattleStatRatioPowerAttr, Stat.SPD)
.ballBombMove(),
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)
.attr(StatChangeAttr, BattleStat.SPD, 1, true),
new SelfStatusMove(Moves.COIL, Type.POISON, -1, 20, -1, 0, 5)
@ -5095,7 +5150,8 @@ export function initMoves() {
.ignoresProtect()
.ignoresVirtual(),
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)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1)
.soundBased(),
@ -5107,7 +5163,8 @@ export function initMoves() {
.target(MoveTarget.ALL_NEAR_OTHERS)
.triageMove(),
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)
.windMove()
.makesContact(false)

View File

@ -21,9 +21,12 @@ export enum Type {
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 {
if (attackType === Type.UNKNOWN || defType === Type.UNKNOWN)
return 1;
switch (defType) {
case Type.NORMAL:
switch (attackType) {

View File

@ -745,7 +745,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
if (!types.length)
types.push(Type.NORMAL);
types.push(Type.UNKNOWN);
return types;
}
@ -859,7 +859,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (moveType === Type.STELLAR)
return this.isTerastallized() ? 2 : 1;
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
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;

View File

@ -40,6 +40,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
private hpNumbersContainer: Phaser.GameObjects.Container;
private type1Icon: Phaser.GameObjects.Sprite;
private type2Icon: Phaser.GameObjects.Sprite;
private type3Icon: Phaser.GameObjects.Sprite;
private expBar: Phaser.GameObjects.Image;
public expMaskRect: Phaser.GameObjects.Graphics;
@ -137,6 +138,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.type2Icon.setOrigin(0, 0);
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) {
this.hpNumbersContainer = this.scene.add.container(-15, 10);
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.setFrame(Type[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1)
this.type2Icon.setFrame(Type[types[1]].toLowerCase());
if (types.length > 2)
this.type3Icon.setFrame(Type[types[2]].toLowerCase());
if (this.player) {
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 ];
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.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.setFrame(Type[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1)
this.type2Icon.setFrame(Type[types[1]].toLowerCase());
if (types.length > 2)
this.type3Icon.setFrame(Type[types[2]].toLowerCase());
const updateHpFrame = () => {
const hpFrame = this.hpBar.scaleX > 0.5 ? 'high' : this.hpBar.scaleX > 0.25 ? 'medium' : 'low';