Add base logic for enemy buff modifiers
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 257 B |
After Width: | Height: | Size: 273 B |
After Width: | Height: | Size: 280 B |
After Width: | Height: | Size: 272 B |
After Width: | Height: | Size: 281 B |
After Width: | Height: | Size: 275 B |
After Width: | Height: | Size: 278 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 277 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 267 B |
After Width: | Height: | Size: 278 B |
After Width: | Height: | Size: 277 B |
After Width: | Height: | Size: 272 B |
After Width: | Height: | Size: 279 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 264 B |
After Width: | Height: | Size: 284 B |
|
@ -16,7 +16,7 @@ import { EvolutionPhase } from "./evolution-phase";
|
|||
import { BattlePhase } from "./battle-phase";
|
||||
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat";
|
||||
import { Biome, biomeLinks } from "./data/biome";
|
||||
import { ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
|
||||
import { ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
|
||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||
import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag";
|
||||
import { getPokemonMessage } from "./messages";
|
||||
|
@ -1293,7 +1293,7 @@ export class BattleEndPhase extends BattlePhase {
|
|||
pokemon.resetBattleSummonData();
|
||||
}
|
||||
|
||||
this.scene.clearEnemyModifiers();
|
||||
this.scene.clearEnemyHeldItemModifiers();
|
||||
|
||||
const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier) as LapsingPersistentModifier[];
|
||||
for (let m of lapsingModifiers) {
|
||||
|
@ -2171,9 +2171,7 @@ export class ModifierRewardPhase extends BattlePhase {
|
|||
constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc) {
|
||||
super(scene);
|
||||
|
||||
this.modifierType = modifierTypeFunc();
|
||||
if (!this.modifierType.id)
|
||||
this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc);
|
||||
this.modifierType = getModifierType(modifierTypeFunc);
|
||||
}
|
||||
|
||||
start() {
|
||||
|
@ -2694,7 +2692,7 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||
this.scene.getPlayerField().filter(p => p.isActive()).forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id));
|
||||
pokemon.hp = 0;
|
||||
pokemon.trySetStatus(StatusEffect.FAINT);
|
||||
this.scene.clearEnemyModifiers();
|
||||
this.scene.clearEnemyHeldItemModifiers();
|
||||
this.scene.field.remove(pokemon, true);
|
||||
};
|
||||
const addToParty = () => {
|
||||
|
|
|
@ -16,7 +16,7 @@ import { GameData } from './system/game-data';
|
|||
import StarterSelectUiHandler from './ui/starter-select-ui-handler';
|
||||
import { TextStyle, addTextObject } from './ui/text';
|
||||
import { Moves, initMoves } from './data/move';
|
||||
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type';
|
||||
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getModifierType, modifierTypes } from './modifier/modifier-type';
|
||||
import AbilityBar from './ui/ability-bar';
|
||||
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, applyAbAttrs, initAbilities } from './data/ability';
|
||||
import Battle, { BattleType, FixedBattleConfig, fixedBattles } from './battle';
|
||||
|
@ -104,7 +104,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
private modifierBar: ModifierBar;
|
||||
private enemyModifierBar: ModifierBar;
|
||||
private modifiers: PersistentModifier[];
|
||||
private enemyModifiers: PokemonHeldItemModifier[];
|
||||
private enemyModifiers: PersistentModifier[];
|
||||
public uiContainer: Phaser.GameObjects.Container;
|
||||
public ui: UI;
|
||||
|
||||
|
@ -1148,7 +1148,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
});
|
||||
}
|
||||
|
||||
addEnemyModifier(itemModifier: PokemonHeldItemModifier): Promise<void> {
|
||||
addEnemyModifier(itemModifier: PersistentModifier): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
itemModifier.add(this.enemyModifiers, false);
|
||||
this.updateModifiers(false).then(() => resolve());
|
||||
|
@ -1206,7 +1206,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
removePartyMemberModifiers(partyMemberIndex: integer): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
const pokemonId = this.getParty()[partyMemberIndex].id;
|
||||
const modifiersToRemove = this.modifiers.filter(m => (m instanceof PokemonHeldItemModifier) && (m as PokemonHeldItemModifier).pokemonId === pokemonId);
|
||||
const modifiersToRemove = this.modifiers.filter(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId);
|
||||
for (let m of modifiersToRemove)
|
||||
this.modifiers.splice(this.modifiers.indexOf(m), 1);
|
||||
this.updateModifiers().then(() => resolve());
|
||||
|
@ -1244,8 +1244,17 @@ export default class BattleScene extends Phaser.Scene {
|
|||
});
|
||||
}
|
||||
|
||||
clearEnemyModifiers(): void {
|
||||
this.enemyModifiers.splice(0, this.enemyModifiers.length);
|
||||
generateEnemyBuffModifier(): void{
|
||||
const enemyBuffModifierTypes = [ modifierTypes.ENEMY_DAMAGE_BOOSTER, modifierTypes.ENEMY_DAMAGE_REDUCTION ];
|
||||
this.executeWithSeedOffset(() => {
|
||||
(getModifierType(Phaser.Math.RND.pick(enemyBuffModifierTypes)).newModifier() as PersistentModifier).add(this.enemyModifiers, false);
|
||||
}, Math.floor(this.currentBattle.waveIndex / 50));
|
||||
}
|
||||
|
||||
clearEnemyHeldItemModifiers(): void {
|
||||
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier);
|
||||
for (let m of modifiersToRemove)
|
||||
this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1);
|
||||
this.updateModifiers(false).then(() => this.updateUIPositions());
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import Pokemon, { HitResult, PokemonMove } from "../pokemon";
|
|||
import { Type } from "./type";
|
||||
import * as Utils from "../utils";
|
||||
import { BattleStat, getBattleStatName } from "./battle-stat";
|
||||
import { DamagePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../battle-phases";
|
||||
import { DamagePhase, ObtainStatusEffectPhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../battle-phases";
|
||||
import { getPokemonMessage } from "../messages";
|
||||
import { Weather, WeatherType } from "./weather";
|
||||
import { BattlerTag, BattlerTagType } from "./battler-tag";
|
||||
|
@ -290,9 +290,9 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
|||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance) {
|
||||
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance && !pokemon.status) {
|
||||
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[Utils.randInt(this.effects.length)];
|
||||
return attacker.trySetStatus(effect);
|
||||
pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, attacker.getBattlerIndex(), effect));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -660,6 +660,9 @@ export const modifierTypes = {
|
|||
|
||||
GOLDEN_POKEBALL: () => new ModifierType(`Golden ${getPokeballName(PokeballType.POKEBALL)}`, 'Adds 1 extra item option at the end of every battle',
|
||||
(type, _args) => new Modifiers.ExtraModifierModifier(type), 'pb_gold', null, 'pb_bounce_1'),
|
||||
|
||||
ENEMY_DAMAGE_BOOSTER: () => new ModifierType('Damage Booster', 'Increases damage by 20%', (type, _args) => new Modifiers.EnemyDamageBoosterModifier(type), 'wl_item_drop'),
|
||||
ENEMY_DAMAGE_REDUCTION: () => new ModifierType('Damage Reducer', 'Reduces incoming damage by 10%', (type, _args) => new Modifiers.EnemyDamageReducerModifier(type), 'wl_guard_spec')
|
||||
};
|
||||
|
||||
const modifierPool = {
|
||||
|
@ -797,6 +800,13 @@ const trainerModifierPool = {
|
|||
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
|
||||
};
|
||||
|
||||
export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType {
|
||||
const modifierType = modifierTypeFunc();
|
||||
if (!modifierType.id)
|
||||
modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc);
|
||||
return modifierType;
|
||||
}
|
||||
|
||||
let modifierPoolThresholds = {};
|
||||
let ignoredPoolIndexes = {};
|
||||
|
||||
|
@ -860,11 +870,8 @@ export function getPlayerModifierTypeOptionsForWave(waveIndex: integer, count: i
|
|||
|
||||
export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, gameMode: GameMode): PokemonHeldItemModifierType[] {
|
||||
const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType).type as PokemonHeldItemModifierType);
|
||||
if ((gameMode === GameMode.CLASSIC && waveIndex === 200) || !(waveIndex % 1000)) {
|
||||
const miniBlackHole = modifierTypes.MINI_BLACK_HOLE();
|
||||
miniBlackHole.id = 'MINI_BLACK_HOLE';
|
||||
ret.push(miniBlackHole);
|
||||
}
|
||||
if ((gameMode === GameMode.CLASSIC && waveIndex === 200) || !(waveIndex % 1000))
|
||||
ret.push(getModifierType(modifierTypes.MINI_BLACK_HOLE) as PokemonHeldItemModifierType);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as ModifierTypes from './modifier-type';
|
||||
import { LearnMovePhase, LevelUpPhase, PokemonHealPhase } from "../battle-phases";
|
||||
import { LearnMovePhase, LevelUpPhase, ObtainStatusEffectPhase, PokemonHealPhase } from "../battle-phases";
|
||||
import BattleScene from "../battle-scene";
|
||||
import { getLevelTotalExp } from "../data/exp";
|
||||
import { PokeballType } from "../data/pokeball";
|
||||
|
@ -15,6 +15,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
|
|||
import { BerryType, getBerryEffectFunc, getBerryPredicate } from '../data/berry';
|
||||
import { Species } from '../data/species';
|
||||
import { BattleType } from '../battle';
|
||||
import { StatusEffect } from '../data/status-effect';
|
||||
|
||||
type ModifierType = ModifierTypes.ModifierType;
|
||||
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
||||
|
@ -1225,3 +1226,87 @@ export class ExtraModifierModifier extends PersistentModifier {
|
|||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class EnemyPersistentModifer extends PersistentModifier {
|
||||
constructor(type: ModifierType, stackCount?: integer) {
|
||||
super(type, stackCount);
|
||||
}
|
||||
}
|
||||
|
||||
export class EnemyDamageBoosterModifier extends EnemyPersistentModifer {
|
||||
constructor(type: ModifierType, stackCount?: integer) {
|
||||
super(type, stackCount);
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof EnemyDamageBoosterModifier;
|
||||
}
|
||||
|
||||
clone(): EnemyDamageBoosterModifier {
|
||||
return new EnemyDamageBoosterModifier(this.type, this.stackCount);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + 0.2 * this.getStackCount()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): number {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
export class EnemyDamageReducerModifier extends EnemyPersistentModifer {
|
||||
constructor(type: ModifierType, stackCount?: integer) {
|
||||
super(type, stackCount);
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof EnemyDamageReducerModifier;
|
||||
}
|
||||
|
||||
clone(): EnemyDamageReducerModifier {
|
||||
return new EnemyDamageReducerModifier(this.type, this.stackCount);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 - 0.2 * this.getStackCount()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): number {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifer {
|
||||
public effect: StatusEffect;
|
||||
|
||||
constructor(type: ModifierType, effect: StatusEffect, stackCount?: integer) {
|
||||
super(type, stackCount);
|
||||
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof EnemyAttackStatusEffectChanceModifier && modifier.effect === this.effect;
|
||||
}
|
||||
|
||||
clone(): EnemyDamageReducerModifier {
|
||||
return new EnemyAttackStatusEffectChanceModifier(this.type, this.effect, this.stackCount);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const target = (args[0] as Pokemon);
|
||||
if (Utils.randInt(10) < this.getStackCount())
|
||||
target.scene.unshiftPhase(new ObtainStatusEffectPhase(target.scene, target.getBattlerIndex(), this.effect));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): number {
|
||||
return 5;
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import * as Utils from './utils';
|
|||
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier } from './data/type';
|
||||
import { getLevelTotalExp } from './data/exp';
|
||||
import { Stat } from './data/pokemon-stat';
|
||||
import { AttackTypeBoosterModifier, PokemonBaseStatModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier } from './modifier/modifier';
|
||||
import { AttackTypeBoosterModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, PokemonBaseStatModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier } from './modifier/modifier';
|
||||
import { PokeballType } from './data/pokeball';
|
||||
import { Gender } from './data/gender';
|
||||
import { initMoveAnim, loadMoveAnimAssets } from './data/battle-anims';
|
||||
|
@ -636,7 +636,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
let result: HitResult;
|
||||
const move = battlerMove.getMove();
|
||||
const moveCategory = move.category;
|
||||
let damage = 0;
|
||||
let damage = new Utils.NumberHolder(0);
|
||||
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
const typeless = !!move.getAttrs(TypelessAttr).length
|
||||
|
@ -690,19 +690,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
|
||||
|
||||
if (!isTypeImmune) {
|
||||
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
|
||||
damage = Math.floor(damage / 2);
|
||||
damage.value = Math.floor(damage.value / 2);
|
||||
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||
if (this.getTag(hta.tagType))
|
||||
damage *= 2;
|
||||
damage.value *= 2;
|
||||
});
|
||||
}
|
||||
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
|
||||
if (!isTypeImmune && fixedDamage.value) {
|
||||
damage = fixedDamage.value;
|
||||
damage.value = fixedDamage.value;
|
||||
isCritical = false;
|
||||
result = HitResult.EFFECTIVE;
|
||||
}
|
||||
|
@ -720,15 +720,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
result = HitResult.NO_EFFECT;
|
||||
}
|
||||
|
||||
if (!source.isPlayer())
|
||||
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage);
|
||||
|
||||
if (!this.isPlayer())
|
||||
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
|
||||
|
||||
if (damage) {
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, this.getBattlerIndex(), result as DamageResult));
|
||||
if (isCritical)
|
||||
this.scene.queueMessage('A critical hit!');
|
||||
this.scene.setPhaseQueueSplice();
|
||||
damage = Math.min(damage, this.hp);
|
||||
this.damage(damage);
|
||||
source.turnData.damageDealt += damage;
|
||||
this.turnData.attacksReceived.unshift({ move: move.id, result: result as DamageResult, damage: damage, critical: isCritical, sourceId: source.id });
|
||||
damage.value = Math.min(damage.value, this.hp);
|
||||
this.damage(damage.value);
|
||||
source.turnData.damageDealt += damage.value;
|
||||
this.turnData.attacksReceived.unshift({ move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id });
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
|
|
|
@ -274,7 +274,7 @@ export class GameData {
|
|||
}
|
||||
|
||||
for (let enemyModifierData of sessionData.enemyModifiers) {
|
||||
const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]) as PokemonHeldItemModifier;
|
||||
const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]);
|
||||
if (modifier)
|
||||
scene.addEnemyModifier(modifier);
|
||||
}
|
||||
|
|