Overhaul modifiers and add temp stat boosts

pull/1/head
Flashfyre 2023-04-18 22:09:37 -04:00
parent 59155b7c18
commit d2d2f956ef
18 changed files with 1878 additions and 1526 deletions

View File

@ -17,17 +17,12 @@
- Critical capture - Critical capture
- Save data - Save data
- Pokedex - Pokedex
- Battle info
- Owned icon
- Status effect indicator
- Modifiers - Modifiers
- PP Up - PP Up
- Type enhancers
- Various mainline game items for various enhancements - Various mainline game items for various enhancements
- Items that cause effects on hit (?) - Items that cause effects on hit (?)
- Capture rate booster - Capture rate booster
- Balancing - Balancing
- Modifiers
- Biome pools - Biome pools
- Custom art - Custom art
- Battle bases and backgrounds - Battle bases and backgrounds

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

View File

@ -5,18 +5,18 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, ConditionalMoveA
import { Mode } from './ui/ui'; import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler"; import { Command } from "./ui/command-ui-handler";
import { Stat } from "./pokemon-stat"; import { Stat } from "./pokemon-stat";
import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, HitHealModifier } from "./modifier"; import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, HitHealModifier, TempBattleStatBoosterModifier } from "./modifier";
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler"; import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball";
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
import { StatusEffect, getStatusEffectActivationText, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect"; import { StatusEffect, getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect";
import { SummaryUiMode } from "./ui/summary-ui-handler"; import { SummaryUiMode } from "./ui/summary-ui-handler";
import EvolutionSceneHandler from "./ui/evolution-scene-handler"; import EvolutionSceneHandler from "./ui/evolution-scene-handler";
import { EvolutionPhase } from "./evolution-phase"; import { EvolutionPhase } from "./evolution-phase";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./battle-stat"; import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./battle-stat";
import { Biome, biomeLinks } from "./biome"; import { Biome, biomeLinks } from "./biome";
import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier-type"; import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TempBattleStat, getModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier-type";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./battle-tag"; import { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./battle-tag";
import { getPokemonMessage } from "./messages"; import { getPokemonMessage } from "./messages";
@ -103,6 +103,7 @@ export class EncounterPhase extends BattlePhase {
end() { end() {
if (this.scene.getEnemyPokemon().shiny) if (this.scene.getEnemyPokemon().shiny)
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false)); this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false));
// TODO: Remove // TODO: Remove
//this.scene.unshiftPhase(new SelectModifierPhase(this.scene)); //this.scene.unshiftPhase(new SelectModifierPhase(this.scene));
@ -531,6 +532,8 @@ export class TurnEndPhase extends BattlePhase {
} }
start() { start() {
super.start();
const playerPokemon = this.scene.getPlayerPokemon(); const playerPokemon = this.scene.getPlayerPokemon();
const enemyPokemon = this.scene.getEnemyPokemon(); const enemyPokemon = this.scene.getEnemyPokemon();
@ -548,6 +551,24 @@ export class TurnEndPhase extends BattlePhase {
} }
} }
export class BattleEndPhase extends BattlePhase {
constructor(scene: BattleScene) {
super(scene);
}
start() {
super.start();
const tempBattleStatBoosterModifiers = this.scene.getModifiers(TempBattleStatBoosterModifier) as TempBattleStatBoosterModifier[];
for (let m of tempBattleStatBoosterModifiers) {
if (!m.lapse())
this.scene.removeModifier(m);
}
this.scene.updateModifiers().then(() => this.end());
}
}
export abstract class PokemonPhase extends BattlePhase { export abstract class PokemonPhase extends BattlePhase {
protected player: boolean; protected player: boolean;
@ -805,14 +826,16 @@ abstract class MoveEffectPhase extends PokemonPhase {
} }
if (this.move.getMove().category !== MoveCategory.STATUS) { if (this.move.getMove().category !== MoveCategory.STATUS) {
const userAccuracyLevel = this.getUserPokemon().summonData.battleStats[BattleStat.ACC]; const userAccuracyLevel = new Utils.IntegerHolder(this.getUserPokemon().summonData.battleStats[BattleStat.ACC]);
const targetEvasionLevel = this.getTargetPokemon().summonData.battleStats[BattleStat.EVA]; const targetEvasionLevel = new Utils.IntegerHolder(this.getTargetPokemon().summonData.battleStats[BattleStat.EVA]);
if (this.getUserPokemon().isPlayer())
this.scene.applyModifiers(TempBattleStatBoosterModifier, TempBattleStat.ACC, userAccuracyLevel);
const rand = Utils.randInt(100, 1); const rand = Utils.randInt(100, 1);
let accuracyMultiplier = 1; let accuracyMultiplier = 1;
if (userAccuracyLevel !== targetEvasionLevel) { if (userAccuracyLevel.value !== targetEvasionLevel.value) {
accuracyMultiplier = userAccuracyLevel > targetEvasionLevel accuracyMultiplier = userAccuracyLevel.value > targetEvasionLevel.value
? (3 + Math.min(userAccuracyLevel - targetEvasionLevel, 6)) / 3 ? (3 + Math.min(userAccuracyLevel.value - targetEvasionLevel.value, 6)) / 3
: 3 / (3 + Math.min(targetEvasionLevel - userAccuracyLevel, 6)); : 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6));
} }
return rand <= this.move.getMove().accuracy * accuracyMultiplier; return rand <= this.move.getMove().accuracy * accuracyMultiplier;
} }
@ -1172,7 +1195,7 @@ export class VictoryPhase extends PokemonPhase {
const participantIds = this.scene.currentBattle.playerParticipantIds; const participantIds = this.scene.currentBattle.playerParticipantIds;
const party = this.scene.getParty(); const party = this.scene.getParty();
const expShareModifier = this.scene.getModifier(ExpShareModifier) as ExpShareModifier; const expShareModifier = this.scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
const expValue = this.scene.getEnemyPokemon().getExpValue(); const expValue = this.scene.getEnemyPokemon().getExpValue();
for (let pm = 0; pm < party.length; pm++) { for (let pm = 0; pm < party.length; pm++) {
const pokemon = party[pm]; const pokemon = party[pm];
@ -1192,6 +1215,8 @@ export class VictoryPhase extends PokemonPhase {
this.scene.unshiftPhase(new ExpPhase(this.scene, pm, expValue * expMultiplier)); this.scene.unshiftPhase(new ExpPhase(this.scene, pm, expValue * expMultiplier));
} }
} }
this.scene.pushPhase(new BattleEndPhase(this.scene));
this.scene.pushPhase(new SelectModifierPhase(this.scene)); this.scene.pushPhase(new SelectModifierPhase(this.scene));
this.scene.newBattle(); this.scene.newBattle();
@ -1434,7 +1459,7 @@ export class AttemptCapturePhase extends BattlePhase {
const _2h = 2 * pokemon.hp; const _2h = 2 * pokemon.hp;
const catchRate = pokemon.species.catchRate; const catchRate = pokemon.species.catchRate;
const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType); const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType);
const statusMultiplier = 1; const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1;
const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier); const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
const y = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x))); const y = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x)));

View File

@ -645,8 +645,18 @@ export default class BattleScene extends Phaser.Scene {
}); });
} }
getModifier(modifierType: { new(...args: any[]): Modifier }): Modifier { removeModifier(modifier: PersistentModifier): boolean {
return this.modifiers.find(m => m instanceof modifierType); const modifierIndex = this.modifiers.indexOf(modifier);
if (modifierIndex > -1) {
this.modifiers.splice(modifierIndex, 1);
return true;
}
return false;
}
getModifiers(modifierType: { new(...args: any[]): Modifier }): Modifier[] {
return this.modifiers.filter(m => m instanceof modifierType);
} }
findModifier(modifierFilter: ModifierPredicate): Modifier { findModifier(modifierFilter: ModifierPredicate): Modifier {

View File

@ -1,3 +1,4 @@
import { BattleStat, getBattleStatName } from './battle-stat';
import * as Modifiers from './modifier'; import * as Modifiers from './modifier';
import { AttackMove, Moves, allMoves } from './move'; import { AttackMove, Moves, allMoves } from './move';
import { PokeballType, getPokeballName } from './pokeball'; import { PokeballType, getPokeballName } from './pokeball';
@ -25,13 +26,15 @@ export class ModifierType {
public name: string; public name: string;
public description: string; public description: string;
public iconImage: string; public iconImage: string;
public group: string;
public tier: ModifierTier; public tier: ModifierTier;
private newModifierFunc: NewModifierFunc; private newModifierFunc: NewModifierFunc;
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, iconImage?: string) { constructor(name: string, description: string, newModifierFunc: NewModifierFunc, iconImage?: string, group?: string,) {
this.name = name; this.name = name;
this.description = description; this.description = description;
this.iconImage = iconImage || name?.replace(/[ \-]/g, '_')?.toLowerCase(); this.iconImage = iconImage || name?.replace(/[ \-]/g, '_')?.toLowerCase();
this.group = group || '';
this.newModifierFunc = newModifierFunc; this.newModifierFunc = newModifierFunc;
} }
@ -46,15 +49,15 @@ export class ModifierType {
class AddPokeballModifierType extends ModifierType { class AddPokeballModifierType extends ModifierType {
constructor(pokeballType: PokeballType, count: integer, iconImage?: string) { constructor(pokeballType: PokeballType, count: integer, iconImage?: string) {
super(`${count}x ${getPokeballName(pokeballType)}`, `Receive ${getPokeballName(pokeballType)} x${count}`, (_type, _args) => new Modifiers.AddPokeballModifier(this, pokeballType, count), iconImage); super(`${count}x ${getPokeballName(pokeballType)}`, `Receive ${getPokeballName(pokeballType)} x${count}`, (_type, _args) => new Modifiers.AddPokeballModifier(this, pokeballType, count), iconImage, 'pb');
} }
} }
export class PokemonModifierType extends ModifierType { export class PokemonModifierType extends ModifierType {
public selectFilter: PokemonSelectFilter; public selectFilter: PokemonSelectFilter;
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string) { constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string, group?: string) {
super(name, description, newModifierFunc, iconImage); super(name, description, newModifierFunc, iconImage, group);
this.selectFilter = selectFilter; this.selectFilter = selectFilter;
} }
@ -64,14 +67,14 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
protected restorePoints: integer; protected restorePoints: integer;
protected percent: boolean; protected percent: boolean;
constructor(name: string, restorePoints: integer, percent?: boolean, newModifierFunc?: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string) { constructor(name: string, restorePoints: integer, percent?: boolean, newModifierFunc?: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string, group?: string) {
super(name, `Restore ${restorePoints}${percent ? '%' : ''} HP for one POKéMON`, super(name, `Restore ${restorePoints}${percent ? '%' : ''} HP for one POKéMON`,
newModifierFunc || ((_type, args) => new Modifiers.PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, this.percent, false)), newModifierFunc || ((_type, args) => new Modifiers.PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, this.percent, false)),
selectFilter || ((pokemon: PlayerPokemon) => { selectFilter || ((pokemon: PlayerPokemon) => {
if (!pokemon.hp || pokemon.hp >= pokemon.getMaxHp()) if (!pokemon.hp || pokemon.hp >= pokemon.getMaxHp())
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
return null; return null;
}), iconImage); }), iconImage, group || 'potion');
this.restorePoints = restorePoints; this.restorePoints = restorePoints;
this.percent = !!percent; this.percent = !!percent;
@ -85,7 +88,7 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
if (pokemon.hp) if (pokemon.hp)
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
return null; return null;
}), iconImage); }), iconImage, 'revive');
this.description = `Revive one POKéMON and restore ${restorePercent}% HP`; this.description = `Revive one POKéMON and restore ${restorePercent}% HP`;
this.selectFilter = (pokemon: PlayerPokemon) => { this.selectFilter = (pokemon: PlayerPokemon) => {
@ -99,8 +102,9 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
export abstract class PokemonMoveModifierType extends PokemonModifierType { export abstract class PokemonMoveModifierType extends PokemonModifierType {
public moveSelectFilter: PokemonMoveSelectFilter; public moveSelectFilter: PokemonMoveSelectFilter;
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, moveSelectFilter?: PokemonMoveSelectFilter, iconImage?: string) { constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, moveSelectFilter?: PokemonMoveSelectFilter,
super(name, description, newModifierFunc, selectFilter, iconImage); iconImage?: string, group?: string) {
super(name, description, newModifierFunc, selectFilter, iconImage, group);
this.moveSelectFilter = moveSelectFilter; this.moveSelectFilter = moveSelectFilter;
} }
@ -117,7 +121,7 @@ export class PokemonPpRestoreModifierType extends PokemonMoveModifierType {
if (!pokemonMove.ppUsed) if (!pokemonMove.ppUsed)
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
return null; return null;
}, iconImage); }, iconImage, 'ether');
this.restorePoints = this.restorePoints; this.restorePoints = this.restorePoints;
} }
@ -132,12 +136,60 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
if (!pokemon.moveset.filter(m => m.ppUsed).length) if (!pokemon.moveset.filter(m => m.ppUsed).length)
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
return null; return null;
}, iconImage); }, iconImage, 'elixir');
this.restorePoints = this.restorePoints; this.restorePoints = this.restorePoints;
} }
} }
export enum TempBattleStat {
ATK,
DEF,
SPATK,
SPDEF,
SPD,
ACC,
CRIT
}
function getTempBattleStatName(tempBattleStat: TempBattleStat) {
if (tempBattleStat === TempBattleStat.CRIT)
return 'critical-hit ratio';
return getBattleStatName(tempBattleStat as integer as BattleStat);
}
function getTempBattleStatBoosterItemName(tempBattleStat: TempBattleStat) {
switch (tempBattleStat) {
case TempBattleStat.ATK:
return 'X Attack';
case TempBattleStat.DEF:
return 'X Defense';
case TempBattleStat.SPATK:
return 'X Sp. Atk';
case TempBattleStat.SPDEF:
return 'X Sp. Def';
case TempBattleStat.SPD:
return 'X Speed';
case TempBattleStat.ACC:
return 'X Accuracy';
case TempBattleStat.CRIT:
return 'Dire Hit';
}
}
export class TempBattleStatBoosterModifierType extends ModifierType {
public tempBattleStat: TempBattleStat;
constructor(tempBattleStat: TempBattleStat) {
super(Utils.toPokemonUpperCase(getTempBattleStatBoosterItemName(tempBattleStat)),
`Increases the ${getTempBattleStatName(tempBattleStat)} of all party members by 1 stage for 5 battles`,
(_type, _args) => new Modifiers.TempBattleStatBoosterModifier(this, this.tempBattleStat),
getTempBattleStatBoosterItemName(tempBattleStat).replace(/\./g, '').replace(/[ ]/g, '_').toLowerCase());
this.tempBattleStat = tempBattleStat;
}
}
function getAttackTypeBoosterItemName(type: Type) { function getAttackTypeBoosterItemName(type: Type) {
switch (type) { switch (type) {
case Type.NORMAL: case Type.NORMAL:
@ -184,7 +236,7 @@ export class AttackTypeBoosterModifierType extends PokemonModifierType {
public boostPercent: integer; public boostPercent: integer;
constructor(moveType: Type, boostPercent: integer) { constructor(moveType: Type, boostPercent: integer) {
super(getAttackTypeBoosterItemName(moveType), `Inceases the power of a POKéMON's ${Type[moveType]}-type moves by 20%`, super(Utils.toPokemonUpperCase(getAttackTypeBoosterItemName(moveType)), `Inceases the power of a POKéMON's ${Type[moveType]}-type moves by 20%`,
(_type, args) => new Modifiers.AttackTypeBoosterModifier(this, (args[0] as PlayerPokemon).id, moveType, boostPercent), (_type, args) => new Modifiers.AttackTypeBoosterModifier(this, (args[0] as PlayerPokemon).id, moveType, boostPercent),
null, `${getAttackTypeBoosterItemName(moveType).replace(/[ \-]/g, '_').toLowerCase()}`); null, `${getAttackTypeBoosterItemName(moveType).replace(/[ \-]/g, '_').toLowerCase()}`);
@ -200,6 +252,23 @@ export class PokemonLevelIncrementModifierType extends PokemonModifierType {
} }
} }
function getBaseStatBoosterItemName(stat: Stat) {
switch (stat) {
case Stat.HP:
return 'HP-UP';
case Stat.ATK:
return 'PROTEIN';
case Stat.DEF:
return 'IRON';
case Stat.SPATK:
return 'CALCIUM';
case Stat.SPDEF:
return 'ZINC';
case Stat.SPD:
return 'CARBOS';
}
}
export class PokemonBaseStatBoosterModifierType extends PokemonModifierType { export class PokemonBaseStatBoosterModifierType extends PokemonModifierType {
private stat: Stat; private stat: Stat;
@ -237,7 +306,7 @@ export class TmModifierType extends PokemonModifierType {
if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.moveset.filter(m => m?.moveId === moveId).length) if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.moveset.filter(m => m?.moveId === moveId).length)
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
return null; return null;
}, `tm_${Type[allMoves[moveId - 1].type].toLowerCase()}`); }, `tm_${Type[allMoves[moveId - 1].type].toLowerCase()}`, 'tm');
this.moveId = moveId; this.moveId = moveId;
} }
@ -297,10 +366,8 @@ class ModifierTypeGenerator extends ModifierType {
generateType(party: PlayerPokemon[]) { generateType(party: PlayerPokemon[]) {
const ret = this.genTypeFunc(party); const ret = this.genTypeFunc(party);
if (ret) { if (ret)
console.log(ret);
ret.setTier(this.tier); ret.setTier(this.tier);
}
return ret; return ret;
} }
} }
@ -374,25 +441,43 @@ class WeightedModifierType {
} }
} }
class WeightedModifierTypeGroup {
public modifierTypes: WeightedModifierType[];
constructor(...modifierTypes: WeightedModifierType[]) {
this.modifierTypes = modifierTypes;
}
setTier(tier: ModifierTier) {
for (let modifierType of this.modifierTypes)
modifierType.setTier(tier);
}
}
const modifierPool = { const modifierPool = {
[ModifierTier.COMMON]: [ [ModifierTier.COMMON]: [
new WeightedModifierType(new AddPokeballModifierType(PokeballType.POKEBALL, 5, 'pb'), 2), new WeightedModifierType(new AddPokeballModifierType(PokeballType.POKEBALL, 5, 'pb'), 6),
new WeightedModifierType(new PokemonLevelIncrementModifierType('RARE CANDY'), 2),
new WeightedModifierType(new PokemonHpRestoreModifierType('POTION', 20), (party: PlayerPokemon[]) => { new WeightedModifierType(new PokemonHpRestoreModifierType('POTION', 20), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 10 || p.getHpRatio() <= 0.875).length; const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 10 || p.getHpRatio() <= 0.875).length;
return thresholdPartyMemberCount; return thresholdPartyMemberCount * 3;
}), }),
new WeightedModifierType(new PokemonHpRestoreModifierType('SUPER POTION', 50), (party: PlayerPokemon[]) => { new WeightedModifierType(new PokemonHpRestoreModifierType('SUPER POTION', 50), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 25 || p.getHpRatio() <= 0.75).length; const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 25 || p.getHpRatio() <= 0.75).length;
return Math.ceil(thresholdPartyMemberCount / 3);
}),
new WeightedModifierType(new PokemonPpRestoreModifierType('ETHER', 10), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed >= 5).length).length;
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}), }),
new WeightedModifierType(new PokemonPpRestoreModifierType('ETHER', 10), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
return thresholdPartyMemberCount * 3;
}),
new WeightedModifierType(new PokemonPpRestoreModifierType('MAX ETHER', -1), (party: PlayerPokemon[]) => { new WeightedModifierType(new PokemonPpRestoreModifierType('MAX ETHER', -1), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed > 10).length).length; const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
return Math.ceil(thresholdPartyMemberCount / 3); return thresholdPartyMemberCount;
}) }),
new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
const randTempBattleStat = Utils.randInt(7) as TempBattleStat;
return new TempBattleStatBoosterModifierType(randTempBattleStat);
}), 4)
].map(m => { m.setTier(ModifierTier.COMMON); return m; }), ].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
[ModifierTier.GREAT]: [ [ModifierTier.GREAT]: [
new WeightedModifierType(new AddPokeballModifierType(PokeballType.GREAT_BALL, 5, 'gb'), 12), new WeightedModifierType(new AddPokeballModifierType(PokeballType.GREAT_BALL, 5, 'gb'), 12),
@ -416,11 +501,11 @@ const modifierPool = {
return Math.ceil(thresholdPartyMemberCount / 1.5); return Math.ceil(thresholdPartyMemberCount / 1.5);
}), }),
new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('ELIXIR', 10), (party: PlayerPokemon[]) => { new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('ELIXIR', 10), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed >= 5).length).length; const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
return thresholdPartyMemberCount * 2; return thresholdPartyMemberCount * 2;
}), }),
new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('MAX ELIXIR', -1), (party: PlayerPokemon[]) => { new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('MAX ELIXIR', -1), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed > 10).length).length; const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
return Math.ceil(thresholdPartyMemberCount / 1.5); return Math.ceil(thresholdPartyMemberCount / 1.5);
}), }),
new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => { new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
@ -429,13 +514,11 @@ const modifierPool = {
const randTmIndex = Utils.randInt(uniqueCompatibleTms.length); const randTmIndex = Utils.randInt(uniqueCompatibleTms.length);
return new TmModifierType(uniqueCompatibleTms[randTmIndex]); return new TmModifierType(uniqueCompatibleTms[randTmIndex]);
}), 4), }), 4),
new WeightedModifierType(new PokemonLevelIncrementModifierType('RARE CANDY'), 4), new WeightedModifierType(new ModifierType('EXP. SHARE', 'All POKéMON in your party gain an additional 10% of a battle\'s EXP. Points', (type, _args) => new Modifiers.ExpShareModifier(type), 'exp_share'), 2),
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('HP-UP', Stat.HP), 1), new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('PROTEIN', Stat.ATK), 1), const randStat = Utils.randInt(6) as Stat;
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('IRON', Stat.DEF), 1), return new PokemonBaseStatBoosterModifierType(getBaseStatBoosterItemName(randStat), randStat);
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('CALCIUM', Stat.SPATK), 1), }), 4)
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('ZINC', Stat.SPDEF), 1),
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('CARBOS', Stat.SPD), 1)
].map(m => { m.setTier(ModifierTier.GREAT); return m; }), ].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
[ModifierTier.ULTRA]: [ [ModifierTier.ULTRA]: [
new WeightedModifierType(new AddPokeballModifierType(PokeballType.ULTRA_BALL, 5, 'ub'), 8), new WeightedModifierType(new AddPokeballModifierType(PokeballType.ULTRA_BALL, 5, 'ub'), 8),
@ -444,9 +527,8 @@ const modifierPool = {
new ModifierType('OVAL CHARM', 'For every X (no. of party members) items in a POKéMON\'s held item stack, give one to each other party member', new ModifierType('OVAL CHARM', 'For every X (no. of party members) items in a POKéMON\'s held item stack, give one to each other party member',
(type, _args) => new Modifiers.PartyShareModifier(type), 'oval_charm'), (type, _args) => new Modifiers.PartyShareModifier(type), 'oval_charm'),
new ModifierType('HEALING CHARM', 'Doubles the effectiveness of HP restoring moves and items (excludes revives)', (type, _args) => new Modifiers.HealingBoosterModifier(type, 2), 'healing_charm'), new ModifierType('HEALING CHARM', 'Doubles the effectiveness of HP restoring moves and items (excludes revives)', (type, _args) => new Modifiers.HealingBoosterModifier(type, 2), 'healing_charm'),
new WeightedModifierType(new PokemonModifierType('SHELL BELL', 'Heals 1/8 of a POKéMON\'s dealt damage', (type, args) => new Modifiers.HitHealModifier(type, (args[0] as PlayerPokemon).id)), 8), new WeightedModifierType(new PokemonModifierType('SHELL BELL', 'Heals 1/8 of a POKéMON\'s dealt damage', (type, args) => new Modifiers.HitHealModifier(type, (args[0] as PlayerPokemon).id)), 2),
new WeightedModifierType(new ExpBoosterModifierType('LUCKY EGG', 25), 4), new WeightedModifierType(new ExpBoosterModifierType('LUCKY EGG', 25), 4)
new WeightedModifierType(new ModifierType('EXP. SHARE', 'All POKéMON in your party gain an additional 10% of a battle\'s EXP. Points', (type, _args) => new Modifiers.ExpShareModifier(type), 'exp_share'), 3)
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
[ModifierTier.MASTER]: [ [ModifierTier.MASTER]: [
new AddPokeballModifierType(PokeballType.MASTER_BALL, 1, 'mb'), new AddPokeballModifierType(PokeballType.MASTER_BALL, 1, 'mb'),
@ -486,13 +568,21 @@ export function regenerateModifierPoolThresholds(party: PlayerPokemon[]) {
}, 0); }, 0);
return [ t, Object.fromEntries(thresholds) ] return [ t, Object.fromEntries(thresholds) ]
}))); })));
console.log(modifierPoolThresholds)
} }
export function getModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] { export function getModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] {
if (waveIndex % 10 === 0) if (waveIndex % 10 === 0)
return modifierPool[ModifierTier.LUXURY].map(m => new ModifierTypeOption(m, false)); return modifierPool[ModifierTier.LUXURY].map(m => new ModifierTypeOption(m, false));
return new Array(count).fill(0).map(() => getNewModifierTypeOption(party)); const options: ModifierTypeOption[] = [];
const retryCount = Math.min(count * 5, 50);
new Array(count).fill(0).map(() => {
let candidate = getNewModifierTypeOption(party);
let r = 0;
while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length)
candidate = getNewModifierTypeOption(party, candidate.type.tier, candidate.upgraded);
options.push(candidate);
});
return options;
} }
function getNewModifierTypeOption(party: PlayerPokemon[], tier?: ModifierTier, upgrade?: boolean): ModifierTypeOption { function getNewModifierTypeOption(party: PlayerPokemon[], tier?: ModifierTier, upgrade?: boolean): ModifierTypeOption {

View File

@ -131,7 +131,7 @@ export abstract class PersistentModifier extends Modifier {
const maxStrokeColor = '#984038'; const maxStrokeColor = '#984038';
if (virtual) { if (virtual) {
const virtualText = addTextObject(scene, 1 * 11 + 16, 12, `+${this.virtualStackCount.toString()}`, TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#40c8f8' : maxColor }); const virtualText = addTextObject(scene, 27, 12, `+${this.virtualStackCount.toString()}`, TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#40c8f8' : maxColor });
virtualText.setShadow(0, 0, null); virtualText.setShadow(0, 0, null);
virtualText.setStroke(!isStackMax ? '#006090' : maxStrokeColor, 16) virtualText.setStroke(!isStackMax ? '#006090' : maxStrokeColor, 16)
virtualText.setOrigin(1, 0); virtualText.setOrigin(1, 0);
@ -181,6 +181,50 @@ export class AddPokeballModifier extends ConsumableModifier {
} }
} }
export class TempBattleStatBoosterModifier extends PersistentModifier {
private tempBattleStat: ModifierTypes.TempBattleStat;
private battlesLeft: integer;
constructor(type: ModifierTypes.TempBattleStatBoosterModifierType, tempBattleStat: ModifierTypes.TempBattleStat) {
super(type);
this.tempBattleStat = tempBattleStat;
this.battlesLeft = 5;
}
clone(): TempBattleStatBoosterModifier {
return new TempBattleStatBoosterModifier(this.type as ModifierTypes.TempBattleStatBoosterModifierType, this.tempBattleStat);
}
apply(args: any[]): boolean {
const tempBattleStat = args[0] as ModifierTypes.TempBattleStat;
if (tempBattleStat === this.tempBattleStat) {
const statLevel = args[1] as Utils.IntegerHolder;
statLevel.value = Math.min(statLevel.value + 1, 6);
return true;
}
return false;
}
lapse(): boolean {
return !!--this.battlesLeft;
}
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
const container = super.getIcon(scene);
const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: '66px', color: '#f89890' });
battleCountText.setShadow(0, 0, null);
battleCountText.setStroke('#984038', 16)
battleCountText.setOrigin(1, 0);
container.add(battleCountText);
return container;
}
}
export abstract class PokemonHeldItemModifier extends PersistentModifier { export abstract class PokemonHeldItemModifier extends PersistentModifier {
public pokemonId: integer; public pokemonId: integer;
@ -387,7 +431,6 @@ export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier {
apply(args: any[]): boolean { apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon; const pokemon = args[0] as Pokemon;
const move = pokemon.moveset[this.moveIndex]; const move = pokemon.moveset[this.moveIndex];
console.log(move.ppUsed, this.restorePoints, this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0);
move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
return true; return true;

View File

@ -1,7 +1,7 @@
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
import { DamagePhase, EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "./battle-phases"; import { DamagePhase, EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "./battle-phases";
import { BattleStat } from "./battle-stat"; import { BattleStat } from "./battle-stat";
import { BattleTagType, ProtectedTag } from "./battle-tag"; import { BattleTagType } from "./battle-tag";
import { getPokemonMessage } from "./messages"; import { getPokemonMessage } from "./messages";
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./pokemon"; import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./pokemon";
import { StatusEffect } from "./status-effect"; import { StatusEffect } from "./status-effect";
@ -684,8 +684,7 @@ export class MoveHitEffectAttr extends MoveAttr {
export class HighCritAttr extends MoveAttr { export class HighCritAttr extends MoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const critChance = args[0] as Utils.IntegerHolder; (args[0] as Utils.IntegerHolder).value++;
critChance.value /= 2;
return true; return true;
} }

View File

@ -8,7 +8,7 @@ import * as Utils from './utils';
import { Type, getTypeDamageMultiplier } from './type'; import { Type, getTypeDamageMultiplier } from './type';
import { getLevelTotalExp } from './exp'; import { getLevelTotalExp } from './exp';
import { Stat } from './pokemon-stat'; import { Stat } from './pokemon-stat';
import { AttackTypeBoosterModifier, PokemonBaseStatModifier as PokemonBaseStatBoosterModifier, ShinyRateBoosterModifier } from './modifier'; import { AttackTypeBoosterModifier, PokemonBaseStatModifier as PokemonBaseStatBoosterModifier, ShinyRateBoosterModifier, TempBattleStatBoosterModifier } from './modifier';
import { PokeballType } from './pokeball'; import { PokeballType } from './pokeball';
import { Gender } from './gender'; import { Gender } from './gender';
import { initMoveAnim, loadMoveAnimAssets } from './battle-anims'; import { initMoveAnim, loadMoveAnimAssets } from './battle-anims';
@ -20,6 +20,7 @@ import { BattleStat } from './battle-stat';
import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './battle-tag'; import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './battle-tag';
import { Species } from './species'; import { Species } from './species';
import { WeatherType } from './weather'; import { WeatherType } from './weather';
import { TempBattleStat } from './modifier-type';
export default abstract class Pokemon extends Phaser.GameObjects.Container { export default abstract class Pokemon extends Phaser.GameObjects.Container {
public id: integer; public id: integer;
@ -263,8 +264,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
getBattleStat(stat: Stat): integer { getBattleStat(stat: Stat): integer {
if (stat === Stat.HP) if (stat === Stat.HP)
return this.stats[Stat.HP]; return this.stats[Stat.HP];
const statLevel = this.summonData.battleStats[(stat - 1) as BattleStat]; const battleStat = (stat - 1) as BattleStat;
let ret = this.stats[stat] * (Math.max(2, 2 + statLevel) / Math.max(2, 2 - statLevel)); const statLevel = new Utils.IntegerHolder(this.summonData.battleStats[battleStat]);
if (this.isPlayer())
this.scene.applyModifiers(TempBattleStatBoosterModifier, battleStat as integer as TempBattleStat, statLevel);
let ret = this.stats[stat] * (Math.max(2, 2 + statLevel.value) / Math.max(2, 2 - statLevel.value));
if (stat === Stat.SPDEF && this.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) if (stat === Stat.SPDEF && this.scene.arena.weather?.weatherType === WeatherType.SANDSTORM)
ret *= 1.5; ret *= 1.5;
if (this.status && this.status.effect === StatusEffect.PARALYSIS) if (this.status && this.status.effect === StatusEffect.PARALYSIS)
@ -461,9 +465,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type); const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type);
applyMoveAttrs(VariablePowerAttr, source, this, move, power); applyMoveAttrs(VariablePowerAttr, source, this, move, power);
this.scene.applyModifiers(AttackTypeBoosterModifier, source, power); this.scene.applyModifiers(AttackTypeBoosterModifier, source, power);
const critChance = new Utils.IntegerHolder(16); const critLevel = new Utils.IntegerHolder(0);
applyMoveAttrs(HighCritAttr, source, this, move, critChance); applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
let isCritical = Utils.randInt(critChance.value) === 0; if (source.isPlayer())
this.scene.applyModifiers(TempBattleStatBoosterModifier, TempBattleStat.CRIT, critLevel);
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
let isCritical = critChance === 1 || !Utils.randInt(critChance);
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK); const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF); const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1; const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1;

View File

@ -103,4 +103,19 @@ export function getStatusEffectHealText(statusEffect: StatusEffect) {
} }
return ''; return '';
}
export function getStatusEffectCatchRateMultiplier(statusEffect: StatusEffect) {
switch (statusEffect) {
case StatusEffect.POISON:
case StatusEffect.TOXIC:
case StatusEffect.PARALYSIS:
case StatusEffect.BURN:
return 1.5;
case StatusEffect.SLEEP:
case StatusEffect.FREEZE:
return 2.5;
}
return 1;
} }