From 7c291ce0cf9110d84b308f9dab4beca7fefc94b7 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 23 Apr 2023 10:24:22 -0400 Subject: [PATCH] Re-implement mini black hole item --- src/battle-phases.ts | 27 +++++++------- src/battle-scene.ts | 30 ++++++++++++---- src/data/pokeball.ts | 2 +- src/modifier/modifier-type.ts | 67 +++++++++++++++++------------------ src/modifier/modifier.ts | 49 +++++++++++++------------ 5 files changed, 100 insertions(+), 75 deletions(-) diff --git a/src/battle-phases.ts b/src/battle-phases.ts index a05e89f13..71b2afe60 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -18,7 +18,7 @@ import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } fr import { Biome, biomeLinks } from "./data/biome"; import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getPlayerModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, DamagingTrapTag, TrappedTag as TrapTag } from "./data/battler-tag"; +import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag as TrapTag } from "./data/battler-tag"; import { getPokemonMessage } from "./messages"; import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; @@ -93,7 +93,7 @@ export class EncounterPhase extends BattlePhase { } doEncounter() { - if (startingWave > 10) { + if (startingWave > 10 && startingLevel < 100) { for (let m = 0; m < Math.floor(startingWave / 10); m++) this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getParty())[0].type.newModifier()); } @@ -293,8 +293,6 @@ export class SummonPhase extends BattlePhase { const playerPokemon = this.scene.getPlayerPokemon(); - this.scene.applyModifiers(HeldItemTransferModifier, true, this.scene); - pokeball.setVisible(true); this.scene.tweens.add({ targets: pokeball, @@ -627,6 +625,8 @@ export class TurnEndPhase extends BattlePhase { this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); + this.scene.applyModifiers(HeldItemTransferModifier, pokemon.isPlayer(), pokemon); + pokemon.battleSummonData.turnCount++; }; @@ -902,11 +902,13 @@ abstract class MoveEffectPhase extends PokemonPhase { const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT; ++user.turnData.hitCount; user.getMoveHistory().push({ move: this.move.moveId, result: result, virtual: this.move.virtual }); - applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove()); - // Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present - if (!isProtected && target.hp && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length) - applyMoveAttrs(MoveHitEffectAttr, user, target, this.move.getMove()); - this.end(); + if (result !== MoveResult.NO_EFFECT && result !== MoveResult.FAILED) { + applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove()); + // Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present + if (!isProtected && target.hp && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length) + applyMoveAttrs(MoveHitEffectAttr, user, target, this.move.getMove()); + this.end(); + } }); }); } @@ -1732,7 +1734,7 @@ export class AttemptCapturePhase extends BattlePhase { this.scene.sound.play('pb_catch'); this.scene.time.delayedCall(17, () => this.pokeball.setTexture('pb', `${pokeballAtlasKey}`)); - const doShake = this.pokeballType !== PokeballType.MASTER_BALL ? () => { + const doShake = pokeballMultiplier > -1 ? () => { let shakeCount = 0; const pbX = this.pokeball.x; const shakeCounter = this.scene.tweens.addCounter({ @@ -1885,10 +1887,11 @@ export class SelectModifierPhase extends BattlePhase { const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; const itemModifier = itemModifiers[itemIndex]; - this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true).then(success => { + this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, true).then(success => { if (success) { this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE); + super.end(); } else this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); }); @@ -1910,9 +1913,9 @@ export class SelectModifierPhase extends BattlePhase { const modifier = !isMoveModifier ? modifierType.newModifier(party[slotIndex]) : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); - this.scene.addModifier(modifier, true).then(() => super.end()); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE); + this.scene.addModifier(modifier, true).then(() => super.end()); }); } else this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); diff --git a/src/battle-scene.ts b/src/battle-scene.ts index c7f25a365..dccfdd2f9 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -702,12 +702,20 @@ export default class BattleScene extends Phaser.Scene { }); } - tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, playSound: boolean): Promise { + addEnemyModifier(itemModifier: PokemonHeldItemModifier): Promise { + return new Promise(resolve => { + itemModifier.add(this.enemyModifiers, false); + this.updateModifiers(false).then(() => resolve()); + }); + } + + tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, transferStack: boolean, playSound: boolean): Promise { return new Promise(resolve => { const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier; + const source = itemModifier.getPokemon(target.scene); newItemModifier.pokemonId = target.id; const matchingModifier = target.scene.findModifier(m => m instanceof PokemonHeldItemModifier - && (m as PokemonHeldItemModifier).matchType(itemModifier)) as PokemonHeldItemModifier; + && (m as PokemonHeldItemModifier).matchType(itemModifier), target.isPlayer()) as PokemonHeldItemModifier; let removeOld = true; if (matchingModifier) { const maxStackCount = matchingModifier.getMaxStackCount(); @@ -715,15 +723,25 @@ export default class BattleScene extends Phaser.Scene { resolve(false); return; } - const newStackCount = matchingModifier.stackCount + itemModifier.stackCount; + const newStackCount = matchingModifier.stackCount + (transferStack ? itemModifier.stackCount : 1); if (newStackCount > maxStackCount) { itemModifier.stackCount = newStackCount - maxStackCount; newItemModifier.stackCount = maxStackCount; removeOld = !itemModifier.stackCount; } - } - if (!removeOld || this.removeModifier(itemModifier)) { - this.addModifier(newItemModifier, playSound).then(() => resolve(true)); + } else if (!transferStack) + removeOld = !(--itemModifier.stackCount); + if (!removeOld || this.removeModifier(itemModifier, !source.isPlayer())) { + const addModifier = () => { + if (target.isPlayer()) + this.addModifier(newItemModifier, playSound).then(() => resolve(true)); + else + this.addEnemyModifier(newItemModifier).then(() => resolve(true)); + }; + if (source.isPlayer() !== target.isPlayer()) + this.updateModifiers(source.isPlayer()).then(() => addModifier()); + else + addModifier(); return; } resolve(false); diff --git a/src/data/pokeball.ts b/src/data/pokeball.ts index e4551f6dc..35bef2cac 100644 --- a/src/data/pokeball.ts +++ b/src/data/pokeball.ts @@ -55,7 +55,7 @@ export function getPokeballCatchMultiplier(type: PokeballType): number { case PokeballType.ULTRA_BALL: return 2; case PokeballType.MASTER_BALL: - return -1; + return 10; case PokeballType.LUXURY_BALL: return 1; } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index ac37053d1..4d8e8ce3f 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -425,17 +425,19 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { } } -class HeldItemTransferModifierType extends ModifierType { +class HeldItemTransferModifierType extends PokemonHeldItemModifierType { constructor(name: string, iconImage?: string, group?: string, soundName?: string) { - super(name, 'All held items in your party are transferred to a POKéMON on summon', (type, _args) => new Modifiers.HeldItemTransferModifier(type), iconImage, group, soundName); + super(name, 'Every turn, the holder acquires one held item from the foe POKéMON', (type, args) => new Modifiers.HeldItemTransferModifier(type, (args[0] as Pokemon).id), iconImage, group, soundName); } } +type WeightedModifierTypeWeightFunc = (party: Pokemon[]) => integer; + class WeightedModifierType { public modifierType: ModifierType; - public weight: integer | Function; + public weight: integer | WeightedModifierTypeWeightFunc; - constructor(modifierType: ModifierType, weight: integer | Function) { + constructor(modifierType: ModifierType, weight: integer | WeightedModifierTypeWeightFunc) { this.modifierType = modifierType; this.weight = weight; } @@ -591,46 +593,48 @@ const modifierPool = { return thresholdPartyMemberCount; }), new WeightedModifierType(modifierTypes.TM, 2), - modifierTypes.EXP_SHARE, + new WeightedModifierType(modifierTypes.EXP_SHARE, (party: Pokemon[]) => party.filter(p => p.level < 100).length ? 1 : 0), new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3) ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), [ModifierTier.ULTRA]: [ new WeightedModifierType(modifierTypes.ULTRA_BALL, 8), new WeightedModifierType(modifierTypes.EVOLUTION_ITEM, 12), new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 5), - modifierTypes.OVAL_CHARM, - modifierTypes.HEALING_CHARM, + new WeightedModifierType(modifierTypes.OVAL_CHARM, 1), + new WeightedModifierType(modifierTypes.HEALING_CHARM, 1), new WeightedModifierType(modifierTypes.LEFTOVERS, 2), new WeightedModifierType(modifierTypes.SHELL_BELL, 2), - new WeightedModifierType(modifierTypes.EXP_CHARM, 4), - new WeightedModifierType(modifierTypes.LUCKY_EGG, 3), new WeightedModifierType(modifierTypes.BERRY_POUCH, 3), - modifierTypes.EXP_BALANCE + new WeightedModifierType(modifierTypes.EXP_CHARM, (party: Pokemon[]) => party.filter(p => p.level < 100).length ? 4 : 0), + new WeightedModifierType(modifierTypes.LUCKY_EGG, (party: Pokemon[]) => party.filter(p => p.level < 100).length ? 3 : 0), + new WeightedModifierType(modifierTypes.EXP_BALANCE, (party: Pokemon[]) => party.filter(p => p.level < 100).length ? 1 : 0) ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), [ModifierTier.MASTER]: [ new WeightedModifierType(modifierTypes.MASTER_BALL, 3), new WeightedModifierType(modifierTypes.SHINY_CHARM, 2), - modifierTypes.MINI_BLACK_HOLE + new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, 1) ].map(m => { m.setTier(ModifierTier.MASTER); return m; }), [ModifierTier.LUXURY]: [ - modifierTypes.GOLDEN_EXP_CHARM, - modifierTypes.GOLDEN_POKEBALL + new WeightedModifierType(modifierTypes.GOLDEN_EXP_CHARM, (party: Pokemon[]) => party.filter(p => p.level < 100).length ? 1 : 0), + new WeightedModifierType(modifierTypes.GOLDEN_POKEBALL, 1) ].map(m => { m.setTier(ModifierTier.LUXURY); return m; }), }; const enemyModifierPool = { [ModifierTier.COMMON]: [ - modifierTypes.BERRY + new WeightedModifierType(modifierTypes.BERRY, 1) ].map(m => { m.setTier(ModifierTier.COMMON); return m; }), [ModifierTier.GREAT]: [ - modifierTypes.BASE_STAT_BOOSTER + new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1) ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), [ModifierTier.ULTRA]: [ new WeightedModifierType(new AttackTypeBoosterModifierTypeGenerator(), 5), new WeightedModifierType(modifierTypes.LUCKY_EGG, 2), ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), [ModifierTier.MASTER]: [ - new WeightedModifierType(modifierTypes.SHELL_BELL, 1) + new WeightedModifierType(modifierTypes.LEFTOVERS, 4), + new WeightedModifierType(modifierTypes.SHELL_BELL, 4), + new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, 1) ].map(m => { m.setTier(ModifierTier.MASTER); return m; }) }; @@ -649,20 +653,17 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], player?: bool ignoredIndexes[t] = []; const thresholds = new Map(); let i = 0; - pool[t].reduce((total: integer, modifierType: ModifierType | WeightedModifierType) => { - if (modifierType instanceof WeightedModifierType) { - const weightedModifierType = modifierType as WeightedModifierType; - const weight = weightedModifierType.weight instanceof Function - ? (weightedModifierType.weight as Function)(party) - : weightedModifierType.weight as integer; - if (weight) - total += weight; - else { - ignoredIndexes[t].push(i++); - return total; - } - } else - total++; + pool[t].reduce((total: integer, modifierType: WeightedModifierType) => { + const weightedModifierType = modifierType as WeightedModifierType; + const weight = weightedModifierType.weight instanceof Function + ? (weightedModifierType.weight as Function)(party) + : weightedModifierType.weight as integer; + if (weight) + total += weight; + else { + ignoredIndexes[t].push(i++); + return total; + } thresholds.set(total, i++); return total; }, 0); @@ -679,7 +680,7 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], player?: bool export function getPlayerModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] { if (waveIndex % 10 === 0) - return modifierPool[ModifierTier.LUXURY].map(m => new ModifierTypeOption(m, false)); + return modifierPool[ModifierTier.LUXURY].filter(m => !(m.weight instanceof Function) || m.weight(party)).map(m => new ModifierTypeOption(m.modifierType, false)); const options: ModifierTypeOption[] = []; const retryCount = Math.min(count * 5, 50); new Array(count).fill(0).map(() => { @@ -724,9 +725,7 @@ function getNewModifierTypeOption(party: Pokemon[], player?: boolean, tier?: Mod if (player) console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]) - let modifierType: ModifierType | WeightedModifierType = (player ? modifierPool : enemyModifierPool)[tier][index]; - if (modifierType instanceof WeightedModifierType) - modifierType = (modifierType as WeightedModifierType).modifierType; + let modifierType: ModifierType = ((player ? modifierPool : enemyModifierPool)[tier][index]).modifierType; if (modifierType instanceof ModifierTypeGenerator) { modifierType = (modifierType as ModifierTypeGenerator).generateType(party); if (modifierType === null) { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 32b09b21f..b123879cf 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -364,7 +364,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier { if (pokemon.getHpRatio() < 1) { const scene = pokemon.scene; - scene.unshiftPhase(new PokemonHealPhase(scene, true, Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true)); + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true)); } return true; @@ -393,7 +393,7 @@ export class HitHealModifier extends PokemonHeldItemModifier { if (pokemon.turnData.damageDealt && pokemon.getHpRatio() < 1) { const scene = pokemon.scene; - scene.unshiftPhase(new PokemonHealPhase(scene, true, Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true)); + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true)); } return true; @@ -828,41 +828,46 @@ export class ShinyRateBoosterModifier extends PersistentModifier { } getMaxStackCount(): integer { - return 5; + return 4; } } -export class HeldItemTransferModifier extends PersistentModifier { - constructor(type: ModifierType, stackCount?: integer) { - super(type, stackCount); +export class HeldItemTransferModifier extends PokemonHeldItemModifier { + constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { + super(type, pokemonId, stackCount); } - match(modifier: Modifier): boolean { + matchType(modifier: Modifier): boolean { return modifier instanceof HeldItemTransferModifier; } clone(): HeldItemTransferModifier { - return new HeldItemTransferModifier(this.type, this.stackCount); - } - - shouldApply(args: any[]): boolean { - return super.shouldApply(args) && args.length && args[0] instanceof BattleScene; + return new HeldItemTransferModifier(this.type, this.pokemonId, this.stackCount); } apply(args: any[]): boolean { - const scene = args[0] as BattleScene; - const targetPokemon = scene.getPlayerPokemon(); + const pokemon = args[0] as Pokemon; + const targetPokemon = pokemon.isPlayer() ? pokemon.scene.getEnemyPokemon() : pokemon.scene.getPlayerPokemon(); - const itemModifiers = scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && (m as PokemonHeldItemModifier).pokemonId !== targetPokemon.id) as PokemonHeldItemModifier[]; - for (let modifier of itemModifiers) - scene.tryTransferHeldItemModifier(modifier, targetPokemon, false); + const transferredModifierTypes: ModifierTypes.ModifierType[] = []; + const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier + && (m as PokemonHeldItemModifier).pokemonId === targetPokemon.id, targetPokemon.isPlayer()) as PokemonHeldItemModifier[]; + + for (let i = 0; i < this.getStackCount(); i++) { + if (!itemModifiers.length) + break; + const randItemIndex = Utils.randInt(itemModifiers.length); + const randItem = itemModifiers[randItemIndex]; + if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false, false)) { + transferredModifierTypes.push(randItem.type); + itemModifiers.splice(randItemIndex, 1); + } + } - return true; - } + for (let mt of transferredModifierTypes) + pokemon.scene.queueMessage(getPokemonMessage(targetPokemon, `'s ${mt.name} was absorbed\nby ${pokemon.name}'s MINI BLACK HOLE!`)); - getMaxStackCount(): integer { - return 1; + return !!transferredModifierTypes.length; } }