Release Healer Man 0.1.7

This commit is contained in:
phenom committed 2026-08-22 15:53:43 -04:00
1 parent b12fb84859
commit eca6b5db9b
99 files changed
+1931 -150

No files matched your search

+18 -16
View File
@@ -1,11 +1,13 @@
import { useEffect, useMemo, useState, type CSSProperties } from "react";
import type { ClassId } from "../app/characterCatalog";
import { runtimeTalentAllocationFailure } from "../game/talentRuntimeCatalog";
import {
talentDescriptionForRank,
talentAllocationFailure,
type TalentAllocationFailure,
type TalentNodeDefinition,
type TalentRanks,
talentUiDescriptionForRank,
type TalentUiNode,
} from "../game/talentUiCatalog";
import type {
TalentAllocationFailure,
TalentRanks,
} from "../game/talentCatalog";
import { useGameStore } from "../game/store";
import { useMenuController, type MenuAction, type MenuDirection } from "../input/useMenuController";
@@ -20,7 +22,7 @@ export interface TalentTreeView {
sigil: string;
iconUrl?: string;
backgroundUrl: string;
nodes: readonly TalentNodeDefinition[];
nodes: readonly TalentUiNode[];
}
export interface TalentsPanelProps {
@@ -53,10 +55,10 @@ function TalentImage({ src, fallback }: { src?: string; fallback: string }) {
}
function nearestNode(
nodes: readonly TalentNodeDefinition[],
source: TalentNodeDefinition,
nodes: readonly TalentUiNode[],
source: TalentUiNode,
direction: MenuDirection,
): TalentNodeDefinition | null {
): TalentUiNode | null {
const vertical = direction === "up" || direction === "down";
const candidates = nodes.filter((candidate) => {
if (direction === "up") return candidate.row < source.row;
@@ -77,9 +79,9 @@ function nearestNode(
function failureText(
failure: TalentAllocationFailure | null,
node: TalentNodeDefinition,
node: TalentUiNode,
tree: TalentTreeView,
prerequisite: TalentNodeDefinition | null,
prerequisite: TalentUiNode | null,
): string {
if (!failure) return "Ready to learn";
if (failure === "max-rank") return "Maximum rank learned";
@@ -125,7 +127,7 @@ function TalentsDialog({
const nodeStateById = useMemo(() => new Map(allNodes.map((node) => {
const rank = Math.max(0, Math.min(node.maxRank, Math.trunc(ranks[node.id] ?? 0)));
const tree = treeByNodeId.get(node.id);
const rawFailure = talentAllocationFailure(ranks, classId, node.id, playerLevel);
const rawFailure = runtimeTalentAllocationFailure(ranks, classId, node.id, playerLevel);
const failure: TalentAllocationFailure | null = rank >= node.maxRank
? "max-rank"
: node.system !== "coa" && tree && tree.points < (node.row - 1) * 5
@@ -215,7 +217,7 @@ function TalentsDialog({
setDetailNodeId(nodeId);
};
const activateNode = (treeId: string, node: TalentNodeDefinition) => {
const activateNode = (treeId: string, node: TalentUiNode) => {
inspectNode(treeId, node.id);
if (!nodeStateById.get(node.id)?.failure) onInvest(node.id);
};
@@ -333,7 +335,7 @@ function TalentsDialog({
aria-label={label}
aria-disabled={state.failure !== null}
aria-describedby={detailNode?.id === node.id ? "talent-node-details" : undefined}
title={`${label} — ${talentDescriptionForRank(node, state.rank || 1)}`}
title={`${label} — ${talentUiDescriptionForRank(node, state.rank || 1)}`}
onFocus={() => inspectNode(tree.id, node.id)}
onPointerEnter={() => inspectNode(tree.id, node.id)}
onClick={() => activateNode(tree.id, node)}
@@ -368,11 +370,11 @@ function TalentsDialog({
<small className="talent-details__effect-rank">
{detailState.rank > 0 ? `Rank ${detailState.rank} effect` : "Rank 1 effect"}
</small>
<p>{talentDescriptionForRank(detailNode, detailState.rank || 1)}</p>
<p>{talentUiDescriptionForRank(detailNode, detailState.rank || 1)}</p>
{detailState.rank > 0 && detailState.rank < detailNode.maxRank && (
<>
<small className="talent-details__effect-rank">Next rank</small>
<p>{talentDescriptionForRank(detailNode, detailState.rank + 1)}</p>
<p>{talentUiDescriptionForRank(detailNode, detailState.rank + 1)}</p>
</>
)}
{(detailNode.row > 1 || prerequisite || detailNode.system === "coa") && (