Merge pull request #87 from AsdarDevelops/one-time-encounters

implement max spawns per run for individual encounters, tweak spawn rates
This commit is contained in:
ImperialSympathizer 2024-07-15 14:03:20 -04:00 committed by GitHub
commit a40f27ff8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 12 deletions

View File

@ -1071,13 +1071,14 @@ export default class BattleScene extends SceneBase {
this.field.add(newTrainer);
}
// Check for mystery encounter
// Can only occur in place of a standard wild battle, waves 10-180
// TODO: remove this once spawn rates are finalized
// let testStartingWeight = 0;
// while (testStartingWeight < 20) {
// while (testStartingWeight < 3) {
// calculateMEAggregateStats(this, testStartingWeight);
// testStartingWeight += 1;
// }
// Check for mystery encounter
// Can only occur in place of a standard wild battle, waves 10-180
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < 180 && newWaveIndex > 10) {
const roll = Utils.randSeedInt(256);
@ -2651,7 +2652,7 @@ export default class BattleScene extends SceneBase {
}
// Common / Uncommon / Rare / Super Rare
const tierWeights = [61, 40, 21, 6];
const tierWeights = [64, 40, 21, 3];
// Adjust tier weights by previously encountered events to lower odds of only common/uncommons in run
this.mysteryEncounterData.encounteredEvents.forEach(val => {
@ -2681,10 +2682,23 @@ export default class BattleScene extends SceneBase {
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
while (availableEncounters.length === 0 && tier >= 0) {
availableEncounters = biomeMysteryEncounters
.filter((encounterType) =>
allMysteryEncounters[encounterType]?.meetsRequirements(this) &&
allMysteryEncounters[encounterType].encounterTier === tier &&
(isNullOrUndefined(previousEncounter) || encounterType !== previousEncounter))
.filter((encounterType) => {
if (allMysteryEncounters[encounterType].encounterTier !== tier) { // Encounter is in tier
return false;
}
if (!allMysteryEncounters[encounterType]?.meetsRequirements(this)) { // Meets encounter requirements
return false;
}
if (!isNullOrUndefined(previousEncounter) && encounterType === previousEncounter) { // Previous encounter was not this one
return false;
}
if (this.mysteryEncounterData.encounteredEvents?.length > 0 && // Encounter has not exceeded max allowed encounters
allMysteryEncounters[encounterType].maxAllowedEncounters > 0
&& this.mysteryEncounterData.encounteredEvents.filter(e => e[0] === encounterType).length >= allMysteryEncounters[encounterType].maxAllowedEncounters) {
return false;
}
return true;
})
.map((m) => (allMysteryEncounters[m]));
tier--;
}

View File

@ -50,6 +50,7 @@ export default interface IMysteryEncounter {
hideBattleIntroMessage?: boolean;
hideIntroVisuals?: boolean;
catchAllowed?: boolean;
maxAllowedEncounters?: number;
doEncounterExp?: (scene: BattleScene) => boolean;
doEncounterRewards?: (scene: BattleScene) => boolean;
onInit?: (scene: BattleScene) => boolean;
@ -144,6 +145,8 @@ export default class IMysteryEncounter implements IMysteryEncounter {
}
this.encounterTier = this.encounterTier ? this.encounterTier : MysteryEncounterTier.COMMON;
this.dialogue = this.dialogue ?? {};
// Default max is 1 for ROGUE encounters, 3 for others
this.maxAllowedEncounters = this.maxAllowedEncounters ?? this.encounterTier === MysteryEncounterTier.ROGUE ? 1 : 3;
this.encounterVariant = MysteryEncounterVariant.DEFAULT;
this.requirements = this.requirements ? this.requirements : [];
this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false;
@ -435,9 +438,9 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* If not specified, defaults to COMMON
* Tiers are:
* COMMON 32/64 odds
* UNCOMMON 16/64 odds
* RARE 10/64 odds
* SUPER_RARE 6/64 odds
* GREAT 16/64 odds
* ULTRA 10/64 odds
* ROGUE 6/64 odds
* ULTRA_RARE Not currently used
* @param encounterTier
* @returns
@ -446,6 +449,15 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
return Object.assign(this, { encounterTier: encounterTier });
}
/**
* Sets the maximum number of times that an encounter can spawn in a given Classic run
* @param maxAllowedEncounters
* @returns
*/
withMaxAllowedEncounters(maxAllowedEncounters: number): this & Required<Pick<IMysteryEncounter, "maxAllowedEncounters">> {
return Object.assign(this, { maxAllowedEncounters: maxAllowedEncounters });
}
/**
* Specifies a requirement for an encounter
* For example, passing requirement as "new WaveCountRequirement([2, 180])" would create a requirement that the encounter can only be spawned between waves 2 and 180

View File

@ -632,7 +632,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
// Calculate encounter rarity
// Common / Uncommon / Rare / Super Rare (base is out of 128)
const tierWeights = [61, 40, 21, 6];
const tierWeights = [64, 40, 21, 3];
// Adjust tier weights by currently encountered events (pity system that lowers odds of multiple common/uncommons)
tierWeights[0] = tierWeights[0] - 6 * numEncounters[0];