[Bug] Take weight into account when getting the tier of a modifier (#4775)
* disable timed events in tests * Take weight into account when getting the tier of modifiers * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: PigeonBar <56974298+PigeonBar@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: PigeonBar <56974298+PigeonBar@users.noreply.github.com>
This commit is contained in:
parent
8169760e1e
commit
c2d24d6e93
|
@ -323,6 +323,7 @@ export default class BattleScene extends SceneBase {
|
||||||
this.conditionalQueue = [];
|
this.conditionalQueue = [];
|
||||||
this.phaseQueuePrependSpliceIndex = -1;
|
this.phaseQueuePrependSpliceIndex = -1;
|
||||||
this.nextCommandPhaseQueue = [];
|
this.nextCommandPhaseQueue = [];
|
||||||
|
this.eventManager = new TimedEventManager();
|
||||||
this.updateGameInfo();
|
this.updateGameInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,7 +379,6 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
this.fieldSpritePipeline = new FieldSpritePipeline(this.game);
|
this.fieldSpritePipeline = new FieldSpritePipeline(this.game);
|
||||||
(this.renderer as Phaser.Renderer.WebGL.WebGLRenderer).pipelines.add("FieldSprite", this.fieldSpritePipeline);
|
(this.renderer as Phaser.Renderer.WebGL.WebGLRenderer).pipelines.add("FieldSprite", this.fieldSpritePipeline);
|
||||||
this.eventManager = new TimedEventManager();
|
|
||||||
|
|
||||||
this.launchBattle();
|
this.launchBattle();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config";
|
import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import { ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
@ -280,7 +280,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||||
let numRogue = 0;
|
let numRogue = 0;
|
||||||
items.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
items.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
||||||
.forEach(m => {
|
.forEach(m => {
|
||||||
const type = m.type.withTierFromPool();
|
const type = m.type.withTierFromPool(ModifierPoolType.PLAYER, party);
|
||||||
const tier = type.tier ?? ModifierTier.ULTRA;
|
const tier = type.tier ?? ModifierTier.ULTRA;
|
||||||
if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
|
if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
|
||||||
numRogue += m.stackCount;
|
numRogue += m.stackCount;
|
||||||
|
|
|
@ -418,7 +418,7 @@ export function generateModifierType(scene: BattleScene, modifier: () => Modifie
|
||||||
// Populates item id and tier (order matters)
|
// Populates item id and tier (order matters)
|
||||||
result = result
|
result = result
|
||||||
.withIdFromFunc(modifierTypes[modifierId])
|
.withIdFromFunc(modifierTypes[modifierId])
|
||||||
.withTierFromPool();
|
.withTierFromPool(ModifierPoolType.PLAYER, scene.getParty());
|
||||||
|
|
||||||
return result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result;
|
return result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { Unlockables } from "#app/system/unlockables";
|
||||||
import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system/voucher";
|
import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system/voucher";
|
||||||
import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
||||||
import { getModifierTierTextTint } from "#app/ui/text";
|
import { getModifierTierTextTint } from "#app/ui/text";
|
||||||
import { formatMoney, getEnumKeys, getEnumValues, IntegerHolder, NumberHolder, padInt, randSeedInt, randSeedItem } from "#app/utils";
|
import { formatMoney, getEnumKeys, getEnumValues, IntegerHolder, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#app/utils";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
@ -121,18 +121,41 @@ export class ModifierType {
|
||||||
* Populates item tier for ModifierType instance
|
* Populates item tier for ModifierType instance
|
||||||
* Tier is a necessary field for items that appear in player shop (determines the Pokeball visual they use)
|
* Tier is a necessary field for items that appear in player shop (determines the Pokeball visual they use)
|
||||||
* To find the tier, this function performs a reverse lookup of the item type in modifier pools
|
* To find the tier, this function performs a reverse lookup of the item type in modifier pools
|
||||||
|
* It checks the weight of the item and will use the first tier for which the weight is greater than 0
|
||||||
|
* This is to allow items to be in multiple item pools depending on the conditions, for example for events
|
||||||
|
* If all tiers have a weight of 0 for the item, the first tier where the item was found is used
|
||||||
* @param poolType Default 'ModifierPoolType.PLAYER'. Which pool to lookup item tier from
|
* @param poolType Default 'ModifierPoolType.PLAYER'. Which pool to lookup item tier from
|
||||||
|
* @param party optional. Needed to check the weight of modifiers with conditional weight (see {@linkcode WeightedModifierTypeWeightFunc})
|
||||||
|
* if not provided or empty, the weight check will be ignored
|
||||||
|
* @param rerollCount Default `0`. Used to check the weight of modifiers with conditional weight (see {@linkcode WeightedModifierTypeWeightFunc})
|
||||||
*/
|
*/
|
||||||
withTierFromPool(poolType: ModifierPoolType = ModifierPoolType.PLAYER): ModifierType {
|
withTierFromPool(poolType: ModifierPoolType = ModifierPoolType.PLAYER, party?: PlayerPokemon[], rerollCount: number = 0): ModifierType {
|
||||||
|
let defaultTier: undefined | ModifierTier;
|
||||||
for (const tier of Object.values(getModifierPoolForType(poolType))) {
|
for (const tier of Object.values(getModifierPoolForType(poolType))) {
|
||||||
for (const modifier of tier) {
|
for (const modifier of tier) {
|
||||||
if (this.id === modifier.modifierType.id) {
|
if (this.id === modifier.modifierType.id) {
|
||||||
this.tier = modifier.modifierType.tier;
|
let weight: number;
|
||||||
return this;
|
if (modifier.weight instanceof Function) {
|
||||||
|
weight = party ? modifier.weight(party, rerollCount) : 0;
|
||||||
|
} else {
|
||||||
|
weight = modifier.weight;
|
||||||
|
}
|
||||||
|
if (weight > 0) {
|
||||||
|
this.tier = modifier.modifierType.tier;
|
||||||
|
return this;
|
||||||
|
} else if (isNullOrUndefined(defaultTier)) {
|
||||||
|
// If weight is 0, keep track of the first tier where the item was found
|
||||||
|
defaultTier = modifier.modifierType.tier;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Didn't find a pool with weight > 0, fallback to first tier where the item was found, if any
|
||||||
|
if (defaultTier) {
|
||||||
|
this.tier = defaultTier;
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2117,7 +2140,7 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo
|
||||||
// Populates item id and tier
|
// Populates item id and tier
|
||||||
guaranteedMod = guaranteedMod
|
guaranteedMod = guaranteedMod
|
||||||
.withIdFromFunc(modifierTypes[modifierId])
|
.withIdFromFunc(modifierTypes[modifierId])
|
||||||
.withTierFromPool();
|
.withTierFromPool(ModifierPoolType.PLAYER, party);
|
||||||
|
|
||||||
const modType = guaranteedMod instanceof ModifierTypeGenerator ? guaranteedMod.generateType(party) : guaranteedMod;
|
const modType = guaranteedMod instanceof ModifierTypeGenerator ? guaranteedMod.generateType(party) : guaranteedMod;
|
||||||
if (modType) {
|
if (modType) {
|
||||||
|
@ -2186,7 +2209,7 @@ export function overridePlayerModifierTypeOptions(options: ModifierTypeOption[],
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modifierType) {
|
if (modifierType) {
|
||||||
options[i].type = modifierType.withIdFromFunc(modifierFunc).withTierFromPool();
|
options[i].type = modifierType.withIdFromFunc(modifierFunc).withTierFromPool(ModifierPoolType.PLAYER, party);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin;
|
||||||
import EventEmitter = Phaser.Events.EventEmitter;
|
import EventEmitter = Phaser.Events.EventEmitter;
|
||||||
import UpdateList = Phaser.GameObjects.UpdateList;
|
import UpdateList = Phaser.GameObjects.UpdateList;
|
||||||
import { version } from "../../../package.json";
|
import { version } from "../../../package.json";
|
||||||
|
import { MockTimedEventManager } from "./mocks/mockTimedEventManager";
|
||||||
|
|
||||||
Object.defineProperty(window, "localStorage", {
|
Object.defineProperty(window, "localStorage", {
|
||||||
value: mockLocalStorage(),
|
value: mockLocalStorage(),
|
||||||
|
@ -232,6 +233,7 @@ export default class GameWrapper {
|
||||||
this.scene.make = new MockGameObjectCreator(mockTextureManager);
|
this.scene.make = new MockGameObjectCreator(mockTextureManager);
|
||||||
this.scene.time = new MockClock(this.scene);
|
this.scene.time = new MockClock(this.scene);
|
||||||
this.scene.remove = vi.fn(); // TODO: this should be stubbed differently
|
this.scene.remove = vi.fn(); // TODO: this should be stubbed differently
|
||||||
|
this.scene.eventManager = new MockTimedEventManager(); // Disable Timed Events
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { TimedEventManager } from "#app/timed-event-manager";
|
||||||
|
|
||||||
|
/** Mock TimedEventManager so that ongoing events don't impact tests */
|
||||||
|
export class MockTimedEventManager extends TimedEventManager {
|
||||||
|
override activeEvent() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
override isEventActive(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
override getFriendshipMultiplier(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
override getShinyMultiplier(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue