Release Healer Man 0.1.58
This commit is contained in:
1 parent
14e105642c
commit
246b4832b0
38 files changed
+952
-93
No files matched your search
+49
-31
@@ -124,6 +124,10 @@ export interface CompanionPartyGenerationOptions {
|
||||
readonly readinessPolicyRevision?: number;
|
||||
readonly includeBetaSpecs?: boolean;
|
||||
readonly excludedPlayerSpecializationId?: string | null;
|
||||
readonly excludedPlayerSpecializationIds?: readonly string[];
|
||||
readonly allowedCompanionSpecializationIds?: readonly string[];
|
||||
/** All real-player roles in a shared party, including the local player. */
|
||||
readonly occupiedHumanRoles?: readonly PartyRole[];
|
||||
}
|
||||
|
||||
function companionTemplateFor(specializationId: string): PartyTemplate {
|
||||
@@ -146,9 +150,18 @@ function wowTemplates(
|
||||
seats: number,
|
||||
): readonly PartyTemplate[] {
|
||||
const policy = options.readinessPolicy ?? DEFAULT_COMPANION_SPEC_READINESS_POLICY;
|
||||
const definitions = companionSpecializationsForPolicy(policy, options.includeBetaSpecs === true, role);
|
||||
const withoutPlayer = definitions.filter((definition) => definition.id !== options.excludedPlayerSpecializationId);
|
||||
const candidates = withoutPlayer.length >= seats ? withoutPlayer : definitions;
|
||||
const allowed = options.allowedCompanionSpecializationIds
|
||||
? new Set(options.allowedCompanionSpecializationIds)
|
||||
: null;
|
||||
const definitions = allowed
|
||||
? WOW_COMPANION_SPECIALIZATIONS.filter((definition) => definition.role === role && allowed.has(definition.id))
|
||||
: companionSpecializationsForPolicy(policy, options.includeBetaSpecs === true, role);
|
||||
const excluded = new Set([
|
||||
...(options.excludedPlayerSpecializationIds ?? []),
|
||||
...(options.excludedPlayerSpecializationId ? [options.excludedPlayerSpecializationId] : []),
|
||||
]);
|
||||
const withoutPlayers = definitions.filter((definition) => !excluded.has(definition.id));
|
||||
const candidates = withoutPlayers.length >= seats ? withoutPlayers : definitions;
|
||||
return candidates.map((definition) => companionTemplateFor(definition.id));
|
||||
}
|
||||
|
||||
@@ -410,35 +423,40 @@ export function createDungeonParty(
|
||||
options: CompanionPartyGenerationOptions = {},
|
||||
): readonly PartyMember[] {
|
||||
const random = { value: hashSeed(seed || "healer-man") };
|
||||
const tankTemplates = categoryId === "rom" ? ROM_TANK_TEMPLATES : wowTemplates("tank", options, 1);
|
||||
const healerTemplates = categoryId === "rom" ? ROM_HEALER_TEMPLATES : wowTemplates("healer", options, 1);
|
||||
const damageSeats = playerRole === "damage" ? 2 : 3;
|
||||
const damageTemplates = categoryId === "rom" ? ROM_DAMAGE_TEMPLATES : wowTemplates("damage", options, damageSeats);
|
||||
const tank = tankTemplates[Math.floor(nextRandom(random) * tankTemplates.length)] ?? tankTemplates[0];
|
||||
const damagePool = [...damageTemplates];
|
||||
for (let index = damagePool.length - 1; index > 0; index -= 1) {
|
||||
const swapIndex = Math.floor(nextRandom(random) * (index + 1));
|
||||
[damagePool[index], damagePool[swapIndex]] = [damagePool[swapIndex], damagePool[index]];
|
||||
const npcSeats = Math.max(0, partySize - 1);
|
||||
const humanRoleCounts: Record<PartyRole, number> = { tank: 0, healer: 0, damage: 0 };
|
||||
for (const role of options.occupiedHumanRoles?.length ? options.occupiedHumanRoles : [playerRole]) {
|
||||
humanRoleCounts[role] += 1;
|
||||
}
|
||||
const healer = healerTemplates[Math.floor(nextRandom(random) * healerTemplates.length)]
|
||||
?? healerTemplates[0];
|
||||
if (playerRole === "tank") {
|
||||
return [
|
||||
createMember(healer, "healer", level, 0, seed),
|
||||
...damagePool.slice(0, 3).map((template, index) => createMember(template, "damage", level, index + 1, seed)),
|
||||
].slice(0, Math.max(0, partySize - 1));
|
||||
}
|
||||
if (playerRole === "damage") {
|
||||
return [
|
||||
createMember(tank, "tank", level, 0, seed),
|
||||
createMember(healer, "healer", level, 1, seed),
|
||||
...damagePool.slice(0, 2).map((template, index) => createMember(template, "damage", level, index + 2, seed)),
|
||||
].slice(0, Math.max(0, partySize - 1));
|
||||
}
|
||||
return [
|
||||
createMember(tank, "tank", level, 0, seed),
|
||||
...damagePool.slice(0, 3).map((template, index) => createMember(template, "damage", level, index + 1, seed)),
|
||||
].slice(0, Math.max(0, partySize - 1));
|
||||
const npcRoles = (["tank", "healer", "damage", "damage", "damage"] as const).filter((role) => {
|
||||
if (humanRoleCounts[role] <= 0) return true;
|
||||
humanRoleCounts[role] -= 1;
|
||||
return false;
|
||||
}).slice(0, npcSeats);
|
||||
const seatsByRole: Record<PartyRole, number> = { tank: 0, healer: 0, damage: 0 };
|
||||
for (const role of npcRoles) seatsByRole[role] += 1;
|
||||
const sourceForRole = (role: PartyRole): readonly PartyTemplate[] => {
|
||||
if (categoryId === "rom") {
|
||||
return role === "tank" ? ROM_TANK_TEMPLATES : role === "healer" ? ROM_HEALER_TEMPLATES : ROM_DAMAGE_TEMPLATES;
|
||||
}
|
||||
return wowTemplates(role, options, seatsByRole[role]);
|
||||
};
|
||||
const pools = Object.fromEntries((["tank", "healer", "damage"] as const).map((role) => {
|
||||
const pool = [...sourceForRole(role)];
|
||||
for (let index = pool.length - 1; index > 0; index -= 1) {
|
||||
const swapIndex = Math.floor(nextRandom(random) * (index + 1));
|
||||
[pool[index], pool[swapIndex]] = [pool[swapIndex], pool[index]];
|
||||
}
|
||||
return [role, pool] as const;
|
||||
})) as Record<PartyRole, PartyTemplate[]>;
|
||||
const usedByRole: Record<PartyRole, number> = { tank: 0, healer: 0, damage: 0 };
|
||||
return npcRoles.map((role, index) => {
|
||||
const pool = pools[role];
|
||||
if (!pool.length) throw new Error(`No ${role} companion is available for this party.`);
|
||||
const template = pool[usedByRole[role] % pool.length];
|
||||
usedByRole[role] += 1;
|
||||
return createMember(template, role, level, index, seed);
|
||||
});
|
||||
}
|
||||
|
||||
/** Builds a complete deterministic NPC group for GM spectator simulations. */
|
||||
|
||||
Reference in new issue
Block a user