Release Healer Man 0.1.46
This commit is contained in:
1 parent
f82daf1c13
commit
9ff574ac0b
73 files changed
+33486
-174
No files matched your search
+82
-4
@@ -89,6 +89,17 @@ interface PartyTemplate {
|
||||
readonly talentTreeId: string;
|
||||
}
|
||||
|
||||
export interface ConfiguredPartyMemberSeed {
|
||||
readonly name: string;
|
||||
readonly classId: ClassId;
|
||||
readonly secondaryClassId?: ClassId | null;
|
||||
readonly specialization: string;
|
||||
readonly specializationId?: string | null;
|
||||
readonly talentTreeId?: string;
|
||||
readonly talentRanks?: TalentRanks;
|
||||
readonly role: PartyRole;
|
||||
}
|
||||
|
||||
const TANK_TEMPLATES: readonly PartyTemplate[] = Object.freeze([
|
||||
{ name: "Garrick", classId: "warrior", specialization: "Protection", talentTreeId: "warrior-protection" },
|
||||
{ name: "Morvane", classId: "death-knight", specialization: "Blood", talentTreeId: "death-knight-blood" },
|
||||
@@ -241,6 +252,7 @@ function createMember(
|
||||
level: number,
|
||||
index: number,
|
||||
seed: string,
|
||||
configuredTalentRanks?: TalentRanks,
|
||||
): PartyMember {
|
||||
// Appearance has its own keyed stream so adding customization fields later
|
||||
// cannot reshuffle which class/spec templates make up this character's party.
|
||||
@@ -282,9 +294,9 @@ function createMember(
|
||||
specialization: template.specialization,
|
||||
specializationId,
|
||||
talentTreeId: template.talentTreeId,
|
||||
talentRanks: isRomClassId(template.classId)
|
||||
talentRanks: configuredTalentRanks ?? (isRomClassId(template.classId)
|
||||
? Object.freeze({})
|
||||
: runtimePartyTalentRanksForLevel(template.classId, template.specialization, level),
|
||||
: runtimePartyTalentRanksForLevel(template.classId, template.specialization, level)),
|
||||
role,
|
||||
level: Math.max(1, Math.trunc(level)),
|
||||
health: maxHealth,
|
||||
@@ -310,6 +322,27 @@ function createMember(
|
||||
};
|
||||
}
|
||||
|
||||
/** Creates one exact companion while retaining the live party stat/equipment path. */
|
||||
export function createConfiguredPartyMember(
|
||||
configuration: ConfiguredPartyMemberSeed,
|
||||
level: number,
|
||||
index: number,
|
||||
seed: string,
|
||||
): PartyMember {
|
||||
const member = createMember({
|
||||
name: configuration.name,
|
||||
classId: configuration.classId,
|
||||
specialization: configuration.specialization,
|
||||
talentTreeId: configuration.talentTreeId ?? configuration.specializationId ?? "",
|
||||
}, configuration.role, level, index, seed, configuration.talentRanks);
|
||||
return Object.freeze({
|
||||
...member,
|
||||
secondaryClassId: configuration.secondaryClassId ?? null,
|
||||
specializationId: configuration.specializationId ?? member.specializationId,
|
||||
talentRanks: configuration.talentRanks ?? member.talentRanks,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the four AI slots around the player's selected role. The resulting
|
||||
* group is always a classic five-player composition including the player.
|
||||
@@ -352,6 +385,29 @@ export function createDungeonParty(
|
||||
].slice(0, Math.max(0, partySize - 1));
|
||||
}
|
||||
|
||||
/** Builds a complete deterministic NPC group for GM spectator simulations. */
|
||||
export function createSpectatorDungeonParty(
|
||||
seed: string,
|
||||
level: number,
|
||||
categoryId: ContentCategoryId = "wow",
|
||||
): readonly PartyMember[] {
|
||||
const random = { value: hashSeed(seed || "healer-man-spectator") };
|
||||
const tankTemplates = categoryId === "rom" ? ROM_TANK_TEMPLATES : TANK_TEMPLATES;
|
||||
const healerTemplates = categoryId === "rom" ? ROM_HEALER_TEMPLATES : HEALER_TEMPLATES;
|
||||
const damagePool = [...(categoryId === "rom" ? ROM_DAMAGE_TEMPLATES : DAMAGE_TEMPLATES)];
|
||||
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 tank = tankTemplates[Math.floor(nextRandom(random) * tankTemplates.length)] ?? tankTemplates[0];
|
||||
const healer = healerTemplates[Math.floor(nextRandom(random) * healerTemplates.length)] ?? healerTemplates[0];
|
||||
return [
|
||||
createMember(tank, "tank", level, 0, seed),
|
||||
createMember(healer, "healer", level, 1, seed),
|
||||
...damagePool.slice(0, 3).map((template, index) => createMember(template, "damage", level, index + 2, seed)),
|
||||
];
|
||||
}
|
||||
|
||||
export interface PartyRuntimeCommit {
|
||||
readonly members: readonly PartyMember[];
|
||||
readonly activeMobId: string | null;
|
||||
@@ -361,6 +417,7 @@ export interface PartyRuntimeCommit {
|
||||
|
||||
export interface PartyState {
|
||||
seed: string | null;
|
||||
spectatorParty: boolean;
|
||||
playerRole: PartyRole;
|
||||
partySize: PartySize;
|
||||
categoryId: ContentCategoryId;
|
||||
@@ -381,6 +438,7 @@ export interface PartyState {
|
||||
partySize?: PartySize,
|
||||
categoryId?: ContentCategoryId,
|
||||
) => void;
|
||||
initializeSpectatorParty: (seed: string, level: number, categoryId?: ContentCategoryId) => void;
|
||||
resetCombat: (level: number, playerHealth: number) => void;
|
||||
syncLevel: (level: number) => void;
|
||||
syncEquipment: (equipment: EquipmentAssignments, inventory: readonly InventoryItem[]) => void;
|
||||
@@ -424,6 +482,7 @@ export interface PartyState {
|
||||
|
||||
export const usePartyStore = create<PartyState>((set, get) => ({
|
||||
seed: null,
|
||||
spectatorParty: false,
|
||||
playerRole: "healer",
|
||||
partySize: 5,
|
||||
categoryId: "wow",
|
||||
@@ -439,6 +498,7 @@ export const usePartyStore = create<PartyState>((set, get) => ({
|
||||
|
||||
initializeParty: (seed, level, playerHealth, playerRole = "healer", partySize = 5, categoryId = "wow") => set({
|
||||
seed,
|
||||
spectatorParty: false,
|
||||
playerRole,
|
||||
partySize,
|
||||
categoryId,
|
||||
@@ -452,10 +512,28 @@ export const usePartyStore = create<PartyState>((set, get) => ({
|
||||
memberTargetIds: {},
|
||||
}),
|
||||
|
||||
initializeSpectatorParty: (seed, level, categoryId = "wow") => set({
|
||||
seed,
|
||||
spectatorParty: true,
|
||||
playerRole: "healer",
|
||||
partySize: 5,
|
||||
categoryId,
|
||||
members: createSpectatorDungeonParty(seed, level, categoryId),
|
||||
command: "attack",
|
||||
selectedMemberId: null,
|
||||
selfSelected: false,
|
||||
routeBlocked: false,
|
||||
activeMobId: null,
|
||||
objectiveBossId: null,
|
||||
memberTargetIds: {},
|
||||
}),
|
||||
|
||||
resetCombat: (level, playerHealth) => set((state) => {
|
||||
return {
|
||||
members: createDungeonParty(state.seed ?? "healer-man", level, state.playerRole, state.partySize, state.categoryId),
|
||||
command: "defend",
|
||||
members: state.spectatorParty
|
||||
? createSpectatorDungeonParty(state.seed ?? "healer-man-spectator", level, state.categoryId)
|
||||
: createDungeonParty(state.seed ?? "healer-man", level, state.playerRole, state.partySize, state.categoryId),
|
||||
command: state.spectatorParty ? "attack" : "defend",
|
||||
selectedMemberId: null,
|
||||
selfSelected: false,
|
||||
routeBlocked: false,
|
||||
|
||||
Reference in new issue
Block a user