Add base logic for enemy buff modifiers

This commit is contained in:
Flashfyre 2023-10-28 13:24:57 -04:00
parent ae6d4d0ea0
commit d1c58224bc
30 changed files with 2134 additions and 1588 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

View File

@ -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 = () => {

View File

@ -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());
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
@ -1224,4 +1225,88 @@ export class ExtraModifierModifier extends PersistentModifier {
getMaxStackCount(): integer {
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;
}
}

View File

@ -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) {

View File

@ -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);
}