feat: add threshold support and tagmap support to variable system

Introduce a `threshold` column to tag modifiers in CSV declarations,
allowing modifiers to activate only when a tag's count meets a minimum
requirement.

Implement support for "tagmap" variables (e.g., `#warrior:1;#druid:2`)
which allow for complex, multi-tag state tracking. These variables
can receive specialized tagmap modifications rather than simple
numeric additions.
This commit is contained in:
2026-07-13 15:09:50 +08:00
parent d74ba69fdf
commit 7cb28e631e
4 changed files with 436 additions and 215 deletions
+18 -9
View File
@@ -3,15 +3,16 @@
*
* Shared between CLI completions scanner and browser-side completions.
*
* Format (columns can be in any order; `tag` is optional):
* tag,key,expr
* ,$hp,$con*5+$mod_hp ← variable declaration
* ,$ac,10+$dex ← variable declaration
* #warrior,$mod_hp,20 ← tag modifier
* #warrior,$mod_str,1 ← tag modifier
* Format (columns can be in any order; `tag` and `threshold` are optional):
* tag,threshold,key,expr
* ,,$hp,$con*5+$mod_hp ← variable declaration
* ,,$ac,10+$dex ← variable declaration
* #warrior,2,$mod_hp,20 ← tag modifier (threshold 2)
* #warrior,,$mod_str,1 ← tag modifier (threshold defaults to 1)
*
* When `tag` is empty: $key is a reactively computed variable.
* When `tag` is present: when #tag is active, $key gets expr added to its base.
* When `tag` is present: when #tag met (source's tagmap value >= threshold),
* $key gets expr added to its base.
*/
import { parse } from "csv-parse/browser/esm/sync";
@@ -57,6 +58,7 @@ export interface TagModifier {
tag: string; // "#warrior"
target: string; // "$mod_hp"
expression: string; // "20"
threshold: number; // minimum tagmap count to activate (default 1)
}
export interface DeclareResult {
@@ -84,7 +86,7 @@ export function parseDeclareCsv(csv: string, source: string): DeclareResult {
columns: true,
trim: true,
skipEmptyLines: true,
}) as Array<{ tag?: string; key: string; expr: string }>;
}) as Array<{ tag?: string; threshold?: string; key: string; expr: string }>;
const variables: VarDeclaration[] = [];
const tagModifiers: TagModifier[] = [];
@@ -111,7 +113,14 @@ export function parseDeclareCsv(csv: string, source: string): DeclareResult {
`${source}: key must start with $, got "${key}"`,
);
}
tagModifiers.push({ tag, target: key, expression: expr });
const thresholdRaw = row.threshold?.trim() ?? "";
const threshold = thresholdRaw ? parseInt(thresholdRaw, 10) : 1;
if (isNaN(threshold) || threshold < 1) {
throw new Error(
`${source}: threshold must be a positive integer, got "${row.threshold}"`,
);
}
tagModifiers.push({ tag, target: key, expression: expr, threshold });
} else {
// Variable declaration
if (!key.startsWith("$")) {