forked from phenom/healer-man
Release Healer Man 0.1.49
This commit is contained in:
1 parent
39425f2494
commit
612170afd0
91 files changed
+2278
-263
No files matched your search
@@ -10,7 +10,13 @@ import {
|
||||
type ActionBindingLayer,
|
||||
} from "../game/actionBindings";
|
||||
import { useCombatStore } from "../game/combatStore";
|
||||
import { abilityAtLevel, defaultActionBarForCharacter, type AbilityDefinition } from "../game/abilityCatalog";
|
||||
import {
|
||||
abilityAtLevel,
|
||||
abilityTalentNodeId,
|
||||
defaultActionBarForCharacter,
|
||||
learnedAbilityRank,
|
||||
type AbilityDefinition,
|
||||
} from "../game/abilityCatalog";
|
||||
import { useGameStore } from "../game/store";
|
||||
import { actionKeyLabel, assignedActionBarAbilityIds, assignDesktopAbility, resetDesktopAbilities, type UiLayout } from "../game/uiLayout";
|
||||
import { currentUiDevice, useUiLayoutStore } from "../game/uiLayoutStore";
|
||||
@@ -110,14 +116,14 @@ function AbilityBindingsDialog({ onClose }: { onClose?: () => void }) {
|
||||
const talentRanks = useCombatStore((state) => state.talentRanks);
|
||||
const tooltipData = (definition: AbilityDefinition | null | undefined): AbilityTooltipData | null => {
|
||||
if (!definition) return null;
|
||||
const talentRank = definition.talentEntryId === undefined ? undefined : talentRanks[`coa-entry-${definition.talentEntryId}`] ?? 0;
|
||||
const talentRank = learnedAbilityRank(definition, talentRanks);
|
||||
const ability = abilityAtLevel(definition, level, talentRank);
|
||||
return {
|
||||
name: ability.name, description: ability.description, target: ability.target,
|
||||
resourceCost: ability.cost.amount, resourceLabel: ability.cost.resource.replace(/-/g, " "),
|
||||
cooldownSeconds: ability.cooldownMs / 1_000, castSeconds: ability.castTimeMs / 1_000,
|
||||
range: ability.range.max, lockedUntilLevel: ability.unlockLevel > level ? ability.unlockLevel : undefined,
|
||||
talentLocked: ability.talentEntryId !== undefined && !talentRank,
|
||||
talentLocked: Boolean(abilityTalentNodeId(ability)) && !talentRank,
|
||||
};
|
||||
};
|
||||
const bindings = useCombatStore((state) => state.actionBindings);
|
||||
@@ -132,7 +138,7 @@ function AbilityBindingsDialog({ onClose }: { onClose?: () => void }) {
|
||||
const isWow = layout === "wow";
|
||||
const assignedIds = assignedActionBarAbilityIds(pcLayout, bindings, isWow);
|
||||
const isMissing = (ability: AbilityDefinition) => ability.unlockLevel <= level
|
||||
&& (ability.talentEntryId === undefined || Boolean(talentRanks[`coa-entry-${ability.talentEntryId}`]))
|
||||
&& (!abilityTalentNodeId(ability) || Boolean(learnedAbilityRank(ability, talentRanks)))
|
||||
&& !assignedIds.has(ability.id);
|
||||
const assignment = (control: BindingControl) => typeof control === "number"
|
||||
? pcLayout.bars[bar].slots[control].abilityId : bindings[actionBindingId(layer, control)];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useShellStore } from "../app/shellStore";
|
||||
import { useGameStore } from "../game/store";
|
||||
import { useCombatStore } from "../game/combatStore";
|
||||
import { useArenaStore } from "../game/arenaStore";
|
||||
import { abilitiesForCharacter, abilityAtLevel } from "../game/abilityCatalog";
|
||||
import { abilitiesForCharacter, abilityAtLevel, learnedAbilityRank } from "../game/abilityCatalog";
|
||||
import type { ArenaActorSnapshot, ArenaAuthoritativeSnapshot, ArenaStageManifest } from "../game/arenaTypes";
|
||||
import { SpellbookPanel } from "./SpellbookPanel";
|
||||
import { EquipmentPanel } from "./EquipmentPanel";
|
||||
@@ -26,7 +26,7 @@ export function ArenaPanels({ character, actor, snapshot, stage, cast }: {
|
||||
const close = () => useGameStore.getState().closeOverlay();
|
||||
const color = classById(character.classId).color;
|
||||
const abilities = useMemo(() => abilitiesForCharacter(character.classId, character.secondaryClassId).map((definition) => {
|
||||
const rank = definition.talentEntryId === undefined ? undefined : actor?.combat?.talentRanks[`coa-entry-${definition.talentEntryId}`];
|
||||
const rank = learnedAbilityRank(definition, actor?.combat?.talentRanks ?? character.talentRanks);
|
||||
const ability = abilityAtLevel(definition, actor?.level ?? character.level, rank);
|
||||
return { id: ability.id, name: ability.name, description: ability.description, level: ability.unlockLevel,
|
||||
color, sigil: ability.sigil, iconUrl: ability.icon, known: ability.passive || Boolean(actor?.abilityIds?.includes(ability.id)),
|
||||
|
||||
@@ -117,8 +117,8 @@ export function DungeonSimulationHud() {
|
||||
})}
|
||||
</div>
|
||||
<p>{simulation.cameraMode === "free-fly"
|
||||
? "Right-drag to look · WASD fly · E/Space up · Q/Ctrl down · Shift boost"
|
||||
: "Choose a party member to follow · Right-drag to orbit"}</p>
|
||||
? "Right-drag/Arrows look · WASD fly · E/Space up · Q/Ctrl down · Shift boost"
|
||||
: "Choose a party member to follow · Right-drag/Arrows orbit"}</p>
|
||||
</section>
|
||||
|
||||
{simulation.sessionMode === "spectate-replay" && (
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useCombatStore } from "../game/combatStore";
|
||||
import {
|
||||
abilityAtLevel,
|
||||
abilityRankForLevel,
|
||||
abilityTalentNodeId,
|
||||
learnedAbilityRank,
|
||||
} from "../game/abilityCatalog";
|
||||
import { useGameStore } from "../game/store";
|
||||
import { OptionsPanel } from "./OptionsPanel";
|
||||
@@ -61,9 +63,7 @@ export function GameplayPanels() {
|
||||
const spellbookAbilities = useMemo(() => [...abilities]
|
||||
.sort((left, right) => left.unlockLevel - right.unlockLevel)
|
||||
.map((catalogAbility) => {
|
||||
const learnedTalentRank = catalogAbility.talentEntryId === undefined
|
||||
? undefined
|
||||
: talentRanks[`coa-entry-${catalogAbility.talentEntryId}`] ?? 0;
|
||||
const learnedTalentRank = learnedAbilityRank(catalogAbility, talentRanks);
|
||||
const ability = abilityAtLevel(catalogAbility, level, learnedTalentRank);
|
||||
const activeRank = abilityRankForLevel(catalogAbility, level, learnedTalentRank);
|
||||
return {
|
||||
@@ -80,8 +80,8 @@ export function GameplayPanels() {
|
||||
cooldownSeconds: ability.cooldownMs / 1_000,
|
||||
castSeconds: ability.castTimeMs / 1_000,
|
||||
range: ability.range.max,
|
||||
known: catalogAbility.talentEntryId === undefined || Boolean(learnedTalentRank),
|
||||
talentLocked: catalogAbility.talentEntryId !== undefined,
|
||||
known: !abilityTalentNodeId(catalogAbility) || Boolean(learnedTalentRank),
|
||||
talentLocked: Boolean(abilityTalentNodeId(catalogAbility)),
|
||||
passive: ability.passive,
|
||||
rank: activeRank?.rank,
|
||||
rankCount: catalogAbility.ranks?.length,
|
||||
|
||||
+3
-5
@@ -13,7 +13,7 @@ import {
|
||||
import { useProgress } from "@react-three/drei";
|
||||
import { classById, raceById } from "../app/characterCatalog";
|
||||
import { useShellStore } from "../app/shellStore";
|
||||
import { abilityAtLevel, resourceProfileForClass } from "../game/abilityCatalog";
|
||||
import { abilityAtLevel, abilityTalentNodeId, learnedAbilityRank, resourceProfileForClass } from "../game/abilityCatalog";
|
||||
import { ACTION_CONTROLS, actionBindingId } from "../game/actionBindings";
|
||||
import { resolvedAbilityCosts, useCombatStore } from "../game/combatStore";
|
||||
import { partyFightStillActive, respawnDefeatedPlayer } from "../game/deathRespawn";
|
||||
@@ -260,11 +260,9 @@ export function Hud({ surface = "top", onLayoutChange, completion, onFinish, onR
|
||||
return abilities.filter((ability) => used.has(ability.id));
|
||||
}, [abilities, actionBindings, desktopBars, ui.draft, ui.preferences]);
|
||||
const abilityItems = useMemo<Record<string, ActionBarItem>>(() => Object.fromEntries(assignedAbilities.map((catalogAbility) => {
|
||||
const learnedTalentRank = catalogAbility.talentEntryId === undefined
|
||||
? undefined
|
||||
: talentRanks[`coa-entry-${catalogAbility.talentEntryId}`] ?? 0;
|
||||
const learnedTalentRank = learnedAbilityRank(catalogAbility, talentRanks);
|
||||
const ability = abilityAtLevel(catalogAbility, level, learnedTalentRank);
|
||||
const talentLocked = ability.talentEntryId !== undefined && !learnedTalentRank;
|
||||
const talentLocked = Boolean(abilityTalentNodeId(ability)) && !learnedTalentRank;
|
||||
const personalRemaining = Math.max(0, (cooldowns[ability.id] ?? 0) - clock);
|
||||
const globalRemaining = Math.max(0, globalCooldownEndsAt - clock);
|
||||
const remaining = Math.max(personalRemaining, globalRemaining);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
|
||||
import { useShellStore } from "../app/shellStore";
|
||||
import { retryCloudSave, useCloudSaveStatus } from "../app/onlineAccountClient";
|
||||
import { useCombatStore } from "../game/combatStore";
|
||||
import { abilityAtLevel, defaultActionBarForCharacter } from "../game/abilityCatalog";
|
||||
import { abilityAtLevel, abilityTalentNodeId, defaultActionBarForCharacter, learnedAbilityRank } from "../game/abilityCatalog";
|
||||
import { useGameStore } from "../game/store";
|
||||
import {
|
||||
BAR_COLUMNS, HUD_ELEMENTS, actionKeyForEvent, bindDesktopSlot, defaultUiLayout, resetDesktopAbilities,
|
||||
@@ -190,14 +190,14 @@ export function UiLayoutEditor() {
|
||||
<button type="button" onClick={() => setAbility(null)}>Clear ability</button>
|
||||
<label>Find ability<input value={search} onChange={(event) => setSearch(event.target.value)} /></label>
|
||||
<div className="ui-layout-editor__abilities">{abilities.filter((ability) => !ability.passive && ability.name.toLowerCase().includes(search.toLowerCase())).map((definition) => {
|
||||
const talentRank = definition.talentEntryId === undefined ? undefined : talentRanks[`coa-entry-${definition.talentEntryId}`] ?? 0;
|
||||
const talentRank = learnedAbilityRank(definition, talentRanks);
|
||||
const ability = abilityAtLevel(definition, level, talentRank);
|
||||
return <AbilityTooltipTarget key={ability.id} ability={{
|
||||
name: ability.name, description: ability.description, target: ability.target,
|
||||
resourceCost: ability.cost.amount, resourceLabel: ability.cost.resource.replace(/-/g, " "),
|
||||
cooldownSeconds: ability.cooldownMs / 1_000, castSeconds: ability.castTimeMs / 1_000, range: ability.range.max,
|
||||
lockedUntilLevel: ability.unlockLevel > level ? ability.unlockLevel : undefined,
|
||||
talentLocked: ability.talentEntryId !== undefined && !talentRank,
|
||||
talentLocked: Boolean(abilityTalentNodeId(ability)) && !talentRank,
|
||||
}}><button type="button" onClick={() => setAbility(ability.id)} aria-pressed={draft.bars[selected.bar].slots[selected.slot].abilityId === ability.id}>{ability.name}</button></AbilityTooltipTarget>;
|
||||
})}</div>
|
||||
</div>}
|
||||
|
||||
Reference in new issue
Block a user