Merge branch 'triple-axel-triple-kick-powerup-fix' of github.com:XGSleepWalker/pokerogue into triple-axel-triple-kick-powerup-fix

This commit is contained in:
Frederico Santos 2024-05-11 16:58:52 +01:00
commit bcb5c53f84
2 changed files with 128 additions and 33 deletions

View File

@ -4,7 +4,7 @@ import { Variant, VariantSet, variantColorCache } from '#app/data/variant';
import { variantData } from '#app/data/variant';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from '../ui/battle-info';
import { Moves } from "../data/enums/moves";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveTypeAttr, VariableMoveCategoryAttr, CounterDamageAttr, IgnoreWeatherTypeDebuffAttr, MultiHitType } from "../data/move";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveTypeAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, MultiHitType } from "../data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from '../data/pokemon-species';
import * as Utils from '../utils';
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from '../data/type';
@ -16,7 +16,7 @@ import { Gender } from '../data/gender';
import { initMoveAnim, loadMoveAnimAssets } from '../data/battle-anims';
import { Status, StatusEffect, getRandomStatus } from '../data/status-effect';
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from '../data/pokemon-evolutions';
import { reverseCompatibleTms, tmSpecies } from '../data/tms';
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from '../data/tms';
import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase } from '../phases';
import { BattleStat } from '../data/battle-stat';
import { BattlerTag, BattlerTagLapseType, EncoreTag, HelpingHandTag, HighestStatBoostTag, TypeBoostTag, getBattlerTag } from '../data/battler-tags';
@ -46,6 +46,8 @@ import { TrainerSlot } from '../data/trainer-config';
import * as Overrides from '../overrides';
import { BerryType } from '../data/berry';
import i18next from '../plugins/i18n';
import { speciesEggMoves } from '../data/egg-moves';
import { ModifierTier } from '../modifier/modifier-tier';
export enum FieldPosition {
CENTER,
@ -1137,7 +1139,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
generateAndPopulateMoveset(): void {
this.moveset = [];
let movePool: Moves[] = [];
let movePool: [Moves, number][] = [];
const allLevelMoves = this.getLevelMoves(1, true, true);
if (!allLevelMoves) {
console.log(this.species.speciesId, 'ERROR');
@ -1148,38 +1150,134 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const levelMove = allLevelMoves[m];
if (this.level < levelMove[0])
break;
if (movePool.indexOf(levelMove[1]) === -1) {
if (!allMoves[levelMove[1]].name.endsWith(' (N)'))
movePool.push(levelMove[1]);
else
movePool.unshift(levelMove[1]);
let weight = levelMove[0];
if (weight === 0) // Evo Moves
weight = 50;
if (weight === 1 && allMoves[levelMove[1]].power >= 80) // Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight
weight = 40;
if (allMoves[levelMove[1]].name.endsWith(' (N)'))
weight /= 100; // Unimplemented level up moves are possible to generate, but 1% of their normal chance.
if (!movePool.some(m => m[0] === levelMove[1]))
movePool.push([levelMove[1], weight]);
}
if (this.hasTrainer()) {
const tms = Object.keys(tmSpecies);
for (let tm of tms) {
const moveId = parseInt(tm) as Moves;
let compatible = false;
for (let p of tmSpecies[tm]) {
if (Array.isArray(p)) {
if (p[0] === this.species.speciesId || (this.fusionSpecies && p[0] === this.fusionSpecies.speciesId) && p.slice(1).indexOf(this.species.forms[this.formIndex]) > -1) {
compatible = true;
break;
}
} else if (p === this.species.speciesId || (this.fusionSpecies && p === this.fusionSpecies.speciesId)) {
compatible = true;
break;
}
}
if (compatible && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(' (N)')) {
if (tmPoolTiers[moveId] === ModifierTier.COMMON && this.level >= 15)
movePool.push([moveId, 4]);
else if (tmPoolTiers[moveId] === ModifierTier.GREAT && this.level >= 30)
movePool.push([moveId, 8]);
else if (tmPoolTiers[moveId] === ModifierTier.ULTRA && this.level >= 50)
movePool.push([moveId, 14]);
}
}
if (this.level >= 25) { // No egg moves below level 25
for (let i = 0; i < 3; i++) {
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i];
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(' (N)'))
movePool.push([moveId, Math.min(this.level * 0.5, 40)]);
}
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][3];
if (this.level >= 60 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(' (N)')) // No rare egg moves before level 60
movePool.push([moveId, Math.min(this.level * 0.2, 20)]);
if (this.fusionSpecies) {
for (let i = 0; i < 3; i++) {
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][i];
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(' (N)'))
movePool.push([moveId, Math.min(this.level * 0.5, 30)]);
}
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3];
if (this.level >= 60 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(' (N)')) // No rare egg moves before level 60
movePool.push([moveId, Math.min(this.level * 0.2, 20)]);
}
}
}
if (this.isBoss())
movePool = movePool.filter(m => !allMoves[m].getAttrs(SacrificialAttr).length);
movePool.reverse();
const attackMovePool = movePool.filter(m => {
const move = allMoves[m];
return move.category !== MoveCategory.STATUS;
});
const easeType = this.hasTrainer() || this.isBoss() ? this.hasTrainer() && this.isBoss() ? 'Quart.easeIn' : 'Cubic.easeIn' : 'Sine.easeIn';
if (attackMovePool.length) {
const randomAttackMove = Utils.randSeedEasedWeightedItem(attackMovePool, easeType);
this.moveset.push(new PokemonMove(randomAttackMove, 0, 0));
console.log(allMoves[randomAttackMove]);
movePool.splice(movePool.findIndex(m => m === randomAttackMove), 1);
if (this.isBoss()) // Bosses never get self ko moves
movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(SacrificialAttr).length);
if (this.hasTrainer()) {
// Trainers never get OHKO moves
movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(OneHitKOAttr).length);
// Half the weight of self KO moves
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(SacrificialAttr).length ? 0.5 : 1)]);
// Trainers get a weight bump to stat buffing moves
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => (a as StatChangeAttr).levels > 1 && (a as StatChangeAttr).selfTarget) ? 1.25 : 1)]);
// Trainers get a weight decrease to multiturn moves
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(ChargeAttr).length || !!allMoves[m[0]].getAttrs(RechargeAttr).length ? 0.7 : 1)]);
}
while (movePool.length && this.moveset.length < 4) {
const randomMove = Utils.randSeedEasedWeightedItem(movePool, easeType);
this.moveset.push(new PokemonMove(randomMove, 0, 0));
console.log(allMoves[randomMove]);
movePool.splice(movePool.indexOf(randomMove), 1);
// Weight towards higher power moves, by reducing the power of moves below the highest power.
// Caps max power at 90 to avoid something like hyper beam ruining the stats.
// This is a pretty soft weighting factor, although it is scaled with the weight multiplier.
const maxPower = Math.min(movePool.reduce((v, m) => Math.max(allMoves[m[0]].power, v), 40), 90);
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].category === MoveCategory.STATUS ? 1 : Math.max(Math.min(allMoves[m[0]].power/maxPower, 1), 0.5))]);
// Weight damaging moves against the lower stat
const worseCategory: MoveCategory = this.stats[Stat.ATK] > this.stats[Stat.SPATK] ? MoveCategory.SPECIAL : MoveCategory.PHYSICAL;
const statRatio = worseCategory === MoveCategory.PHYSICAL ? this.stats[Stat.ATK]/this.stats[Stat.SPATK] : this.stats[Stat.SPATK]/this.stats[Stat.ATK];
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1)]);
let weightMultiplier = 0.9; // The higher this is the more the game weights towards higher level moves. At 0 all moves are equal weight.
if (this.hasTrainer())
weightMultiplier += 0.7;
if (this.isBoss())
weightMultiplier += 0.4;
const baseWeights: [Moves, number][] = movePool.map(m => [m[0], Math.ceil(Math.pow(m[1], weightMultiplier)*100)]);
if (this.hasTrainer() || this.isBoss()) { // Trainers and bosses always force a stab move
const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type));
if (stabMovePool.length) {
const totalWeight = stabMovePool.reduce((v, m) => v + m[1], 0);
let rand = Utils.randSeedInt(totalWeight);
let index = 0;
while (rand > stabMovePool[index][1])
rand -= stabMovePool[index++][1];
this.moveset.push(new PokemonMove(stabMovePool[index][0], 0, 0));
}
} else { // Normal wild pokemon just force a random damaging move
const attackMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS);
if (attackMovePool.length) {
const totalWeight = attackMovePool.reduce((v, m) => v + m[1], 0);
let rand = Utils.randSeedInt(totalWeight);
let index = 0;
while (rand > attackMovePool[index][1])
rand -= attackMovePool[index++][1];
this.moveset.push(new PokemonMove(attackMovePool[index][0], 0, 0));
}
}
while (movePool.length > 1 && this.moveset.length < 4) {
if (this.hasTrainer()) {
// Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier.
// Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB.
// Status moves remain unchanged on weight, this encourages 1-2
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId)).map(m => [m[0], this.moveset.some(mo => mo.getMove().category !== MoveCategory.STATUS && mo.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1]/Math.max(Math.pow(4, this.moveset.filter(mo => mo.getMove().power > 1).length)/8,0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1]]);
} else { // Non-trainer pokemon just use normal weights
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId));
}
const totalWeight = movePool.reduce((v, m) => v + m[1], 0);
let rand = Utils.randSeedInt(totalWeight);
let index = 0;
while (rand > movePool[index][1])
rand -= movePool[index++][1];
this.moveset.push(new PokemonMove(movePool[index][0], 0, 0));
}
this.scene.triggerPokemonFormChange(this, SpeciesFormChangeMoveLearnedTrigger);

View File

@ -3486,7 +3486,6 @@ export class GameOverModifierRewardPhase extends ModifierRewardPhase {
this.scene.addModifier(newModifier).then(() => {
this.scene.playSound('level_up_fanfare');
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.arenaBg.setVisible(false);
this.scene.ui.fadeIn(250).then(() => {
this.scene.ui.showText(`You received\n${newModifier.type.name}!`, null, () => {
this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true));
@ -3513,7 +3512,6 @@ export class RibbonModifierRewardPhase extends ModifierRewardPhase {
this.scene.addModifier(newModifier).then(() => {
this.scene.playSound('level_up_fanfare');
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.arenaBg.setVisible(false);
this.scene.ui.fadeIn(250).then(() => {
this.scene.ui.showText(`${this.species.name} beat ${this.scene.gameMode.getName()} Mode for the first time!\nYou received ${newModifier.type.name}!`, null, () => {
resolve();
@ -3651,7 +3649,6 @@ export class UnlockPhase extends Phase {
this.scene.gameData.unlocks[this.unlockable] = true;
this.scene.playSound('level_up_fanfare');
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.arenaBg.setVisible(false);
this.scene.ui.fadeIn(250).then(() => {
this.scene.ui.showText(`${getUnlockableName(this.unlockable)}\nhas been unlocked.`, null, () => {
this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true));