Overhaul modifiers and add temp stat boosts
@ -17,17 +17,12 @@
|
||||
- Critical capture
|
||||
- Save data
|
||||
- Pokedex
|
||||
- Battle info
|
||||
- Owned icon
|
||||
- Status effect indicator
|
||||
- Modifiers
|
||||
- PP Up
|
||||
- Type enhancers
|
||||
- Various mainline game items for various enhancements
|
||||
- Items that cause effects on hit (?)
|
||||
- Capture rate booster
|
||||
- Balancing
|
||||
- Modifiers
|
||||
- Biome pools
|
||||
- Custom art
|
||||
- Battle bases and backgrounds
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 38 KiB |
BIN
public/images/items/dire_hit.png
Normal file
After Width: | Height: | Size: 335 B |
BIN
public/images/items/guard_spec.png
Normal file
After Width: | Height: | Size: 329 B |
BIN
public/images/items/x_accuracy.png
Normal file
After Width: | Height: | Size: 331 B |
BIN
public/images/items/x_attack.png
Normal file
After Width: | Height: | Size: 333 B |
BIN
public/images/items/x_defense.png
Normal file
After Width: | Height: | Size: 331 B |
BIN
public/images/items/x_sp_atk.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
public/images/items/x_sp_def.png
Normal file
After Width: | Height: | Size: 330 B |
BIN
public/images/items/x_speed.png
Normal file
After Width: | Height: | Size: 325 B |
@ -5,18 +5,18 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, ConditionalMoveA
|
||||
import { Mode } from './ui/ui';
|
||||
import { Command } from "./ui/command-ui-handler";
|
||||
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 { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball";
|
||||
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 EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
||||
import { EvolutionPhase } from "./evolution-phase";
|
||||
import { BattlePhase } from "./battle-phase";
|
||||
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./battle-stat";
|
||||
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 { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./battle-tag";
|
||||
import { getPokemonMessage } from "./messages";
|
||||
@ -103,6 +103,7 @@ export class EncounterPhase extends BattlePhase {
|
||||
end() {
|
||||
if (this.scene.getEnemyPokemon().shiny)
|
||||
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false));
|
||||
|
||||
// TODO: Remove
|
||||
//this.scene.unshiftPhase(new SelectModifierPhase(this.scene));
|
||||
|
||||
@ -531,6 +532,8 @@ export class TurnEndPhase extends BattlePhase {
|
||||
}
|
||||
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
const playerPokemon = this.scene.getPlayerPokemon();
|
||||
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 {
|
||||
protected player: boolean;
|
||||
|
||||
@ -805,14 +826,16 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
if (this.move.getMove().category !== MoveCategory.STATUS) {
|
||||
const userAccuracyLevel = this.getUserPokemon().summonData.battleStats[BattleStat.ACC];
|
||||
const targetEvasionLevel = this.getTargetPokemon().summonData.battleStats[BattleStat.EVA];
|
||||
const userAccuracyLevel = new Utils.IntegerHolder(this.getUserPokemon().summonData.battleStats[BattleStat.ACC]);
|
||||
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);
|
||||
let accuracyMultiplier = 1;
|
||||
if (userAccuracyLevel !== targetEvasionLevel) {
|
||||
accuracyMultiplier = userAccuracyLevel > targetEvasionLevel
|
||||
? (3 + Math.min(userAccuracyLevel - targetEvasionLevel, 6)) / 3
|
||||
: 3 / (3 + Math.min(targetEvasionLevel - userAccuracyLevel, 6));
|
||||
if (userAccuracyLevel.value !== targetEvasionLevel.value) {
|
||||
accuracyMultiplier = userAccuracyLevel.value > targetEvasionLevel.value
|
||||
? (3 + Math.min(userAccuracyLevel.value - targetEvasionLevel.value, 6)) / 3
|
||||
: 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6));
|
||||
}
|
||||
return rand <= this.move.getMove().accuracy * accuracyMultiplier;
|
||||
}
|
||||
@ -1172,7 +1195,7 @@ export class VictoryPhase extends PokemonPhase {
|
||||
|
||||
const participantIds = this.scene.currentBattle.playerParticipantIds;
|
||||
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();
|
||||
for (let pm = 0; pm < party.length; 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.pushPhase(new BattleEndPhase(this.scene));
|
||||
this.scene.pushPhase(new SelectModifierPhase(this.scene));
|
||||
this.scene.newBattle();
|
||||
|
||||
@ -1434,7 +1459,7 @@ export class AttemptCapturePhase extends BattlePhase {
|
||||
const _2h = 2 * pokemon.hp;
|
||||
const catchRate = pokemon.species.catchRate;
|
||||
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 y = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x)));
|
||||
|
||||
|
@ -645,8 +645,18 @@ export default class BattleScene extends Phaser.Scene {
|
||||
});
|
||||
}
|
||||
|
||||
getModifier(modifierType: { new(...args: any[]): Modifier }): Modifier {
|
||||
return this.modifiers.find(m => m instanceof modifierType);
|
||||
removeModifier(modifier: PersistentModifier): boolean {
|
||||
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 {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { BattleStat, getBattleStatName } from './battle-stat';
|
||||
import * as Modifiers from './modifier';
|
||||
import { AttackMove, Moves, allMoves } from './move';
|
||||
import { PokeballType, getPokeballName } from './pokeball';
|
||||
@ -25,13 +26,15 @@ export class ModifierType {
|
||||
public name: string;
|
||||
public description: string;
|
||||
public iconImage: string;
|
||||
public group: string;
|
||||
public tier: ModifierTier;
|
||||
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.description = description;
|
||||
this.iconImage = iconImage || name?.replace(/[ \-]/g, '_')?.toLowerCase();
|
||||
this.group = group || '';
|
||||
this.newModifierFunc = newModifierFunc;
|
||||
}
|
||||
|
||||
@ -46,15 +49,15 @@ export class ModifierType {
|
||||
|
||||
class AddPokeballModifierType extends ModifierType {
|
||||
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 {
|
||||
public selectFilter: PokemonSelectFilter;
|
||||
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string) {
|
||||
super(name, description, newModifierFunc, iconImage);
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string, group?: string) {
|
||||
super(name, description, newModifierFunc, iconImage, group);
|
||||
|
||||
this.selectFilter = selectFilter;
|
||||
}
|
||||
@ -64,14 +67,14 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
|
||||
protected restorePoints: integer;
|
||||
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`,
|
||||
newModifierFunc || ((_type, args) => new Modifiers.PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, this.percent, false)),
|
||||
selectFilter || ((pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.hp || pokemon.hp >= pokemon.getMaxHp())
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}), iconImage);
|
||||
}), iconImage, group || 'potion');
|
||||
|
||||
this.restorePoints = restorePoints;
|
||||
this.percent = !!percent;
|
||||
@ -85,7 +88,7 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
|
||||
if (pokemon.hp)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}), iconImage);
|
||||
}), iconImage, 'revive');
|
||||
|
||||
this.description = `Revive one POKéMON and restore ${restorePercent}% HP`;
|
||||
this.selectFilter = (pokemon: PlayerPokemon) => {
|
||||
@ -99,8 +102,9 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
|
||||
export abstract class PokemonMoveModifierType extends PokemonModifierType {
|
||||
public moveSelectFilter: PokemonMoveSelectFilter;
|
||||
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, moveSelectFilter?: PokemonMoveSelectFilter, iconImage?: string) {
|
||||
super(name, description, newModifierFunc, selectFilter, iconImage);
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, moveSelectFilter?: PokemonMoveSelectFilter,
|
||||
iconImage?: string, group?: string) {
|
||||
super(name, description, newModifierFunc, selectFilter, iconImage, group);
|
||||
|
||||
this.moveSelectFilter = moveSelectFilter;
|
||||
}
|
||||
@ -117,7 +121,7 @@ export class PokemonPpRestoreModifierType extends PokemonMoveModifierType {
|
||||
if (!pokemonMove.ppUsed)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}, iconImage);
|
||||
}, iconImage, 'ether');
|
||||
|
||||
this.restorePoints = this.restorePoints;
|
||||
}
|
||||
@ -132,12 +136,60 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
|
||||
if (!pokemon.moveset.filter(m => m.ppUsed).length)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}, iconImage);
|
||||
}, iconImage, 'elixir');
|
||||
|
||||
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) {
|
||||
switch (type) {
|
||||
case Type.NORMAL:
|
||||
@ -184,7 +236,7 @@ export class AttackTypeBoosterModifierType extends PokemonModifierType {
|
||||
public 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),
|
||||
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 {
|
||||
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)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}, `tm_${Type[allMoves[moveId - 1].type].toLowerCase()}`);
|
||||
}, `tm_${Type[allMoves[moveId - 1].type].toLowerCase()}`, 'tm');
|
||||
|
||||
this.moveId = moveId;
|
||||
}
|
||||
@ -297,10 +366,8 @@ class ModifierTypeGenerator extends ModifierType {
|
||||
|
||||
generateType(party: PlayerPokemon[]) {
|
||||
const ret = this.genTypeFunc(party);
|
||||
if (ret) {
|
||||
console.log(ret);
|
||||
if (ret)
|
||||
ret.setTier(this.tier);
|
||||
}
|
||||
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 = {
|
||||
[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[]) => {
|
||||
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[]) => {
|
||||
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;
|
||||
}),
|
||||
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[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed > 10).length).length;
|
||||
return Math.ceil(thresholdPartyMemberCount / 3);
|
||||
})
|
||||
const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
|
||||
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; }),
|
||||
[ModifierTier.GREAT]: [
|
||||
new WeightedModifierType(new AddPokeballModifierType(PokeballType.GREAT_BALL, 5, 'gb'), 12),
|
||||
@ -416,11 +501,11 @@ const modifierPool = {
|
||||
return Math.ceil(thresholdPartyMemberCount / 1.5);
|
||||
}),
|
||||
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;
|
||||
}),
|
||||
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);
|
||||
}),
|
||||
new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
|
||||
@ -429,13 +514,11 @@ const modifierPool = {
|
||||
const randTmIndex = Utils.randInt(uniqueCompatibleTms.length);
|
||||
return new TmModifierType(uniqueCompatibleTms[randTmIndex]);
|
||||
}), 4),
|
||||
new WeightedModifierType(new PokemonLevelIncrementModifierType('RARE CANDY'), 4),
|
||||
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('HP-UP', Stat.HP), 1),
|
||||
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('PROTEIN', Stat.ATK), 1),
|
||||
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('IRON', Stat.DEF), 1),
|
||||
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('CALCIUM', Stat.SPATK), 1),
|
||||
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('ZINC', Stat.SPDEF), 1),
|
||||
new WeightedModifierType(new PokemonBaseStatBoosterModifierType('CARBOS', Stat.SPD), 1)
|
||||
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 ModifierTypeGenerator((party: PlayerPokemon[]) => {
|
||||
const randStat = Utils.randInt(6) as Stat;
|
||||
return new PokemonBaseStatBoosterModifierType(getBaseStatBoosterItemName(randStat), randStat);
|
||||
}), 4)
|
||||
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
|
||||
[ModifierTier.ULTRA]: [
|
||||
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',
|
||||
(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 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 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)
|
||||
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)
|
||||
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
|
||||
[ModifierTier.MASTER]: [
|
||||
new AddPokeballModifierType(PokeballType.MASTER_BALL, 1, 'mb'),
|
||||
@ -486,13 +568,21 @@ export function regenerateModifierPoolThresholds(party: PlayerPokemon[]) {
|
||||
}, 0);
|
||||
return [ t, Object.fromEntries(thresholds) ]
|
||||
})));
|
||||
console.log(modifierPoolThresholds)
|
||||
}
|
||||
|
||||
export function getModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] {
|
||||
if (waveIndex % 10 === 0)
|
||||
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 {
|
||||
|
@ -131,7 +131,7 @@ export abstract class PersistentModifier extends Modifier {
|
||||
const maxStrokeColor = '#984038';
|
||||
|
||||
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.setStroke(!isStackMax ? '#006090' : maxStrokeColor, 16)
|
||||
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 {
|
||||
public pokemonId: integer;
|
||||
|
||||
@ -387,7 +431,6 @@ export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier {
|
||||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as Pokemon;
|
||||
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;
|
||||
|
||||
return true;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||
import { DamagePhase, EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "./battle-phases";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import { BattleTagType, ProtectedTag } from "./battle-tag";
|
||||
import { BattleTagType } from "./battle-tag";
|
||||
import { getPokemonMessage } from "./messages";
|
||||
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./pokemon";
|
||||
import { StatusEffect } from "./status-effect";
|
||||
@ -684,8 +684,7 @@ export class MoveHitEffectAttr extends MoveAttr {
|
||||
|
||||
export class HighCritAttr extends MoveAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const critChance = args[0] as Utils.IntegerHolder;
|
||||
critChance.value /= 2;
|
||||
(args[0] as Utils.IntegerHolder).value++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import * as Utils from './utils';
|
||||
import { Type, getTypeDamageMultiplier } from './type';
|
||||
import { getLevelTotalExp } from './exp';
|
||||
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 { Gender } from './gender';
|
||||
import { initMoveAnim, loadMoveAnimAssets } from './battle-anims';
|
||||
@ -20,6 +20,7 @@ import { BattleStat } from './battle-stat';
|
||||
import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './battle-tag';
|
||||
import { Species } from './species';
|
||||
import { WeatherType } from './weather';
|
||||
import { TempBattleStat } from './modifier-type';
|
||||
|
||||
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
public id: integer;
|
||||
@ -263,8 +264,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
getBattleStat(stat: Stat): integer {
|
||||
if (stat === Stat.HP)
|
||||
return this.stats[Stat.HP];
|
||||
const statLevel = this.summonData.battleStats[(stat - 1) as BattleStat];
|
||||
let ret = this.stats[stat] * (Math.max(2, 2 + statLevel) / Math.max(2, 2 - statLevel));
|
||||
const battleStat = (stat - 1) as BattleStat;
|
||||
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)
|
||||
ret *= 1.5;
|
||||
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);
|
||||
applyMoveAttrs(VariablePowerAttr, source, this, move, power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source, power);
|
||||
const critChance = new Utils.IntegerHolder(16);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critChance);
|
||||
let isCritical = Utils.randInt(critChance.value) === 0;
|
||||
const critLevel = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
|
||||
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 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;
|
||||
|
@ -104,3 +104,18 @@ export function getStatusEffectHealText(statusEffect: StatusEffect) {
|
||||
|
||||
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;
|
||||
}
|