feat: implement scoped stat system with player/global support
Introduces a scoping mechanism for statistics, allowing properties to be defined as either "player" (prefixed with player name) or "global". - Adds `scope` property to StatDef - Implements key resolution to handle bare keys vs full keys - Updates `canModifyStat` to enforce player-specific permissions - Enhances `makeStatLookup` to resolve scoped references in formulas - Adds documentation for the new stat system
This commit is contained in:
@@ -15,7 +15,7 @@ import { actionPrefill, setActionPrefill } from "../stores/reveal";
|
||||
import { useJournalCompletions, ensureCompletions } from "./completions";
|
||||
import { resolveRollPayload } from "./types/roll";
|
||||
import { resolveSparkPayload } from "./types/spark";
|
||||
import { resolveStatRoll, canModifyStat } from "./stat-helpers";
|
||||
import { resolveStatRoll, canModifyStat, fullKey } from "./stat-helpers";
|
||||
import { parseInput } from "./command-parser";
|
||||
import type { CompletionItem } from "./command-parser";
|
||||
import { buildCompletions } from "./command-completions";
|
||||
@@ -147,13 +147,18 @@ export const JournalInput: Component = () => {
|
||||
const p = payload as { action?: string; key?: string; value?: string };
|
||||
|
||||
if (p.action === "roll" && p.key) {
|
||||
const resolved = resolveStatRoll(p.key, comp.data.stats, stream.stats);
|
||||
const resolved = resolveStatRoll(
|
||||
p.key,
|
||||
comp.data.stats,
|
||||
stream.stats,
|
||||
stream.myName,
|
||||
);
|
||||
if (resolved.error) {
|
||||
setError(resolved.error);
|
||||
} else {
|
||||
const result = sendMessage("stat", {
|
||||
action: "set",
|
||||
key: p.key,
|
||||
key: resolved.fullKey,
|
||||
value: resolved.value,
|
||||
});
|
||||
const r = unwrap(result);
|
||||
@@ -167,16 +172,38 @@ export const JournalInput: Component = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canModifyStat(stream.myRole, stream.myName, p.key)) {
|
||||
// Resolve bare key to full key for set/del
|
||||
const fk = resolveKey(p.key, comp.data.stats, stream.myName);
|
||||
|
||||
if (!canModifyStat(stream.myRole, stream.myName, fk, comp.data.stats)) {
|
||||
setError(`无权修改属性: ${p.key}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = sendMessage("stat", payload);
|
||||
const result = sendMessage("stat", {
|
||||
action: p.action,
|
||||
key: fk,
|
||||
value: p.value,
|
||||
});
|
||||
const r = unwrap(result);
|
||||
finish(r.ok, r.err);
|
||||
}
|
||||
|
||||
/** Resolve a bare or full key to the actual runtime key. */
|
||||
function resolveKey(
|
||||
inputKey: string,
|
||||
statDefs: typeof comp.data.stats,
|
||||
playerName: string,
|
||||
): string {
|
||||
// Exact match
|
||||
if (statDefs.some((d) => fullKey(d, playerName) === inputKey))
|
||||
return inputKey;
|
||||
// Bare key → full key
|
||||
const def = statDefs.find((d) => d.key === inputKey);
|
||||
if (def) return fullKey(def, playerName);
|
||||
return inputKey;
|
||||
}
|
||||
|
||||
/** Clear text + error on success, or set error on failure. */
|
||||
function finish(success: boolean, err?: string) {
|
||||
if (success) {
|
||||
|
||||
Reference in New Issue
Block a user