feat: add specialized visual editors for items, creatures, areas, and dialogs
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
GffEditor,
|
||||
getFieldValue,
|
||||
type FieldOverrideProps,
|
||||
} from "./GffEditor";
|
||||
|
||||
interface CreatureEditorProps {
|
||||
repo: string;
|
||||
filePath: string;
|
||||
content: string;
|
||||
onSave?: (content: string) => void;
|
||||
onSwitchToRaw?: () => void;
|
||||
}
|
||||
|
||||
function AbilityScoresOverride({ data, onChange }: FieldOverrideProps) {
|
||||
const abilities = [
|
||||
["Str", "Dex", "Con"],
|
||||
["Int", "Wis", "Cha"],
|
||||
];
|
||||
|
||||
const displayNames: Record<string, string> = {
|
||||
Str: "STR", Dex: "DEX", Con: "CON",
|
||||
Int: "INT", Wis: "WIS", Cha: "CHA",
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm" style={{ color: "var(--forge-text-secondary)" }}>
|
||||
Ability Scores
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{abilities.flat().map((ab) => {
|
||||
const val = getFieldValue(data, ab);
|
||||
const num = typeof val === "number" ? val : 0;
|
||||
return (
|
||||
<div
|
||||
key={ab}
|
||||
className="flex flex-col items-center rounded border px-3 py-2"
|
||||
style={{ borderColor: "var(--forge-border)", backgroundColor: "var(--forge-bg)" }}
|
||||
>
|
||||
<span className="text-xs font-bold" style={{ color: "var(--forge-text-secondary)" }}>
|
||||
{displayNames[ab]}
|
||||
</span>
|
||||
<input
|
||||
type="number"
|
||||
value={num}
|
||||
min={1}
|
||||
max={99}
|
||||
onChange={(e) => onChange(ab, parseInt(e.target.value, 10))}
|
||||
className="mt-1 w-16 rounded border px-1 py-1 text-center text-lg font-semibold"
|
||||
style={{
|
||||
backgroundColor: "var(--forge-surface)",
|
||||
borderColor: "var(--forge-border)",
|
||||
color: "var(--forge-text)",
|
||||
}}
|
||||
/>
|
||||
<span className="mt-0.5 text-xs" style={{ color: "var(--forge-text-secondary)" }}>
|
||||
mod {num >= 10 ? "+" : ""}{Math.floor((num - 10) / 2)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RaceGenderOverride({ data, onChange }: FieldOverrideProps) {
|
||||
const race = getFieldValue(data, "Race");
|
||||
const gender = getFieldValue(data, "Gender");
|
||||
const raceNum = typeof race === "number" ? race : 0;
|
||||
const genderNum = typeof gender === "number" ? gender : 0;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm" style={{ color: "var(--forge-text-secondary)" }}>Race</label>
|
||||
<input
|
||||
type="number"
|
||||
value={raceNum}
|
||||
min={0}
|
||||
onChange={(e) => onChange("Race", parseInt(e.target.value, 10))}
|
||||
className="w-20 rounded border px-2 py-1.5 text-sm"
|
||||
style={{
|
||||
backgroundColor: "var(--forge-bg)",
|
||||
borderColor: "var(--forge-border)",
|
||||
color: "var(--forge-text)",
|
||||
}}
|
||||
/>
|
||||
<span className="text-xs" style={{ color: "var(--forge-text-secondary)" }}>
|
||||
(racialtypes.2da)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm" style={{ color: "var(--forge-text-secondary)" }}>Gender</label>
|
||||
<select
|
||||
value={genderNum}
|
||||
onChange={(e) => onChange("Gender", parseInt(e.target.value, 10))}
|
||||
className="rounded border px-2 py-1.5 text-sm"
|
||||
style={{
|
||||
backgroundColor: "var(--forge-bg)",
|
||||
borderColor: "var(--forge-border)",
|
||||
color: "var(--forge-text)",
|
||||
}}
|
||||
>
|
||||
<option value={0}>Male</option>
|
||||
<option value={1}>Female</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ScriptsOverride({ data, onChange }: FieldOverrideProps) {
|
||||
const scripts = [
|
||||
{ label: "ScriptHeartbeat", display: "Heartbeat" },
|
||||
{ label: "ScriptOnDamaged", display: "On Damaged" },
|
||||
{ label: "ScriptDeath", display: "On Death" },
|
||||
{ label: "ScriptSpawn", display: "On Spawn" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{scripts.map((s) => {
|
||||
const val = getFieldValue(data, s.label);
|
||||
const str = typeof val === "string" ? val : "";
|
||||
return (
|
||||
<div key={s.label} className="flex items-center gap-3">
|
||||
<label className="w-28 shrink-0 text-sm" style={{ color: "var(--forge-text-secondary)" }}>
|
||||
{s.display}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={str}
|
||||
maxLength={16}
|
||||
onChange={(e) => onChange(s.label, e.target.value)}
|
||||
className="flex-1 rounded border px-2 py-1.5 font-mono text-sm"
|
||||
style={{
|
||||
backgroundColor: "var(--forge-bg)",
|
||||
borderColor: "var(--forge-border)",
|
||||
color: "var(--forge-text)",
|
||||
}}
|
||||
placeholder="(none)"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function CreatureEditor({ repo, filePath, content, onSave, onSwitchToRaw }: CreatureEditorProps) {
|
||||
const fieldOverrides = useMemo(() => {
|
||||
const overrides = new Map<string, (props: FieldOverrideProps) => React.ReactNode>();
|
||||
|
||||
overrides.set("Str", (props) => <AbilityScoresOverride {...props} />);
|
||||
overrides.set("Dex", () => null);
|
||||
overrides.set("Con", () => null);
|
||||
overrides.set("Int", () => null);
|
||||
overrides.set("Wis", () => null);
|
||||
overrides.set("Cha", () => null);
|
||||
|
||||
overrides.set("Race", (props) => <RaceGenderOverride {...props} />);
|
||||
overrides.set("Gender", () => null);
|
||||
|
||||
overrides.set("ScriptHeartbeat", (props) => <ScriptsOverride {...props} />);
|
||||
overrides.set("ScriptOnDamaged", () => null);
|
||||
overrides.set("ScriptDeath", () => null);
|
||||
overrides.set("ScriptSpawn", () => null);
|
||||
|
||||
return overrides;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<GffEditor
|
||||
repo={repo}
|
||||
filePath={filePath}
|
||||
content={content}
|
||||
onSave={onSave}
|
||||
onSwitchToRaw={onSwitchToRaw}
|
||||
fieldOverrides={fieldOverrides}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user