feat: add GFF type schema definitions for visual editors

This commit is contained in:
plenarius
2026-04-20 20:07:48 -04:00
parent 3b91c0312d
commit c7f5646e62
8 changed files with 720 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
import { GffFieldType, type GffTypeSchema } from "./schema.js";
export const schema: GffTypeSchema = {
fileType: "are",
displayName: "Area",
categories: ["Identity", "Dimensions", "Lighting"],
fields: [
{
label: "Tag",
displayName: "Tag",
type: GffFieldType.CExoString,
category: "Identity",
},
{
label: "ResRef",
displayName: "ResRef",
type: GffFieldType.ResRef,
category: "Identity",
},
{
label: "Name",
displayName: "Name",
type: GffFieldType.CExoLocString,
category: "Identity",
},
{
label: "Flags",
displayName: "Area Flags",
type: GffFieldType.Dword,
category: "Identity",
description: "Bitmask: bit 0 = Interior, bit 1 = Underground, bit 2 = Natural",
},
{
label: "Width",
displayName: "Width",
type: GffFieldType.Int,
category: "Dimensions",
description: "Area width in tiles",
},
{
label: "Height",
displayName: "Height",
type: GffFieldType.Int,
category: "Dimensions",
description: "Area height in tiles",
},
{
label: "Tileset",
displayName: "Tileset",
type: GffFieldType.ResRef,
category: "Dimensions",
description: "Tileset resref from tilesets.2da",
},
{
label: "SunAmbientColor",
displayName: "Sun Ambient Color",
type: GffFieldType.Dword,
category: "Lighting",
description: "Ambient light color during daytime (BGR integer)",
},
{
label: "SunDiffuseColor",
displayName: "Sun Diffuse Color",
type: GffFieldType.Dword,
category: "Lighting",
description: "Directional light color during daytime (BGR integer)",
},
{
label: "SunFogColor",
displayName: "Sun Fog Color",
type: GffFieldType.Dword,
category: "Lighting",
},
{
label: "MoonAmbientColor",
displayName: "Moon Ambient Color",
type: GffFieldType.Dword,
category: "Lighting",
},
{
label: "MoonDiffuseColor",
displayName: "Moon Diffuse Color",
type: GffFieldType.Dword,
category: "Lighting",
},
{
label: "MoonFogColor",
displayName: "Moon Fog Color",
type: GffFieldType.Dword,
category: "Lighting",
},
{
label: "IsNight",
displayName: "Is Night",
type: GffFieldType.Byte,
category: "Lighting",
},
{
label: "SkyBox",
displayName: "Sky Box",
type: GffFieldType.Byte,
category: "Lighting",
description: "Skybox index from skyboxes.2da",
},
],
};
+138
View File
@@ -0,0 +1,138 @@
import { GffFieldType, type GffTypeSchema } from "./schema.js";
export const schema: GffTypeSchema = {
fileType: "dlg",
displayName: "Dialog",
categories: ["Properties", "Entries", "Replies"],
fields: [
{
label: "DelayEntry",
displayName: "Entry Delay",
type: GffFieldType.Dword,
category: "Properties",
description: "Delay in milliseconds before NPC lines appear",
},
{
label: "DelayReply",
displayName: "Reply Delay",
type: GffFieldType.Dword,
category: "Properties",
description: "Delay in milliseconds before PC reply options appear",
},
{
label: "PreventZoomIn",
displayName: "Prevent Zoom In",
type: GffFieldType.Byte,
category: "Properties",
description: "Disables camera zoom during conversation",
},
{
label: "NumWords",
displayName: "Word Count",
type: GffFieldType.Dword,
category: "Properties",
editable: false,
},
{
label: "EndConverAbort",
displayName: "On Abort Script",
type: GffFieldType.ResRef,
category: "Properties",
description: "Script fired when the player aborts the conversation",
},
{
label: "EndConversation",
displayName: "On End Script",
type: GffFieldType.ResRef,
category: "Properties",
description: "Script fired when the conversation ends normally",
},
{
label: "EntryList",
displayName: "NPC Entries",
type: GffFieldType.List,
category: "Entries",
description: "Lines spoken by NPCs",
fields: [
{
label: "Text",
displayName: "Text",
type: GffFieldType.CExoLocString,
description: "The dialog text displayed to the player",
},
{
label: "Speaker",
displayName: "Speaker",
type: GffFieldType.CExoString,
description: "Tag of the speaking creature (empty = conversation owner)",
},
{
label: "Animation",
displayName: "Animation",
type: GffFieldType.Dword,
description: "Animation to play while speaking",
},
{
label: "Sound",
displayName: "Sound",
type: GffFieldType.ResRef,
description: "Sound file to play with this line",
},
{
label: "Script",
displayName: "Action Script",
type: GffFieldType.ResRef,
description: "Script fired when this entry is displayed",
},
{
label: "Active",
displayName: "Condition Script",
type: GffFieldType.ResRef,
description: "Script that must return TRUE for this entry to appear",
},
],
},
{
label: "ReplyList",
displayName: "PC Replies",
type: GffFieldType.List,
category: "Replies",
description: "Response options available to the player",
fields: [
{
label: "Text",
displayName: "Text",
type: GffFieldType.CExoLocString,
description: "The reply text shown as a dialog option",
},
{
label: "Speaker",
displayName: "Speaker",
type: GffFieldType.CExoString,
},
{
label: "Animation",
displayName: "Animation",
type: GffFieldType.Dword,
},
{
label: "Sound",
displayName: "Sound",
type: GffFieldType.ResRef,
},
{
label: "Script",
displayName: "Action Script",
type: GffFieldType.ResRef,
description: "Script fired when this reply is selected",
},
{
label: "Active",
displayName: "Condition Script",
type: GffFieldType.ResRef,
description: "Script that must return TRUE for this reply to appear",
},
],
},
],
};
+53
View File
@@ -0,0 +1,53 @@
export enum GffFieldType {
Byte = 0,
Char = 1,
Word = 2,
Short = 3,
Dword = 4,
Int = 5,
Dword64 = 6,
Int64 = 7,
Float = 8,
Double = 9,
CExoString = 10,
ResRef = 11,
CExoLocString = 12,
Void = 13,
Struct = 14,
List = 15,
}
export interface GffFieldSchema {
label: string;
displayName: string;
type: GffFieldType;
description?: string;
category?: string;
editable?: boolean;
hidden?: boolean;
fields?: GffFieldSchema[];
}
export interface GffTypeSchema {
fileType: string;
displayName: string;
categories: string[];
fields: GffFieldSchema[];
}
const schemaRegistry = new Map<string, () => Promise<GffTypeSchema>>();
schemaRegistry.set("uti", () => import("./uti.schema.js").then((m) => m.schema));
schemaRegistry.set("utc", () => import("./utc.schema.js").then((m) => m.schema));
schemaRegistry.set("are", () => import("./are.schema.js").then((m) => m.schema));
schemaRegistry.set("dlg", () => import("./dlg.schema.js").then((m) => m.schema));
schemaRegistry.set("utp", () => import("./utp.schema.js").then((m) => m.schema));
schemaRegistry.set("utm", () => import("./utm.schema.js").then((m) => m.schema));
export async function getSchema(
type: string,
): Promise<GffTypeSchema | undefined> {
const loader = schemaRegistry.get(type);
if (!loader) return undefined;
return loader();
}
+144
View File
@@ -0,0 +1,144 @@
import { GffFieldType, type GffTypeSchema } from "./schema.js";
export const schema: GffTypeSchema = {
fileType: "utc",
displayName: "Creature",
categories: ["Identity", "Attributes", "Scripts", "Equipment", "AI"],
fields: [
{
label: "Tag",
displayName: "Tag",
type: GffFieldType.CExoString,
category: "Identity",
},
{
label: "TemplateResRef",
displayName: "Template ResRef",
type: GffFieldType.ResRef,
category: "Identity",
},
{
label: "Appearance_Type",
displayName: "Appearance Type",
type: GffFieldType.Word,
category: "Identity",
description: "Row index into appearance.2da",
},
{
label: "Race",
displayName: "Race",
type: GffFieldType.Byte,
category: "Attributes",
description: "Row index into racialtypes.2da",
},
{
label: "Gender",
displayName: "Gender",
type: GffFieldType.Byte,
category: "Attributes",
description: "0 = Male, 1 = Female",
},
{
label: "Str",
displayName: "Strength",
type: GffFieldType.Byte,
category: "Attributes",
},
{
label: "Dex",
displayName: "Dexterity",
type: GffFieldType.Byte,
category: "Attributes",
},
{
label: "Con",
displayName: "Constitution",
type: GffFieldType.Byte,
category: "Attributes",
},
{
label: "Int",
displayName: "Intelligence",
type: GffFieldType.Byte,
category: "Attributes",
},
{
label: "Wis",
displayName: "Wisdom",
type: GffFieldType.Byte,
category: "Attributes",
},
{
label: "Cha",
displayName: "Charisma",
type: GffFieldType.Byte,
category: "Attributes",
},
{
label: "FactionID",
displayName: "Faction ID",
type: GffFieldType.Word,
category: "AI",
description: "Determines NPC hostility and alliance behavior",
},
{
label: "WalkRate",
displayName: "Walk Rate",
type: GffFieldType.Int,
category: "AI",
description: "Movement speed index (7 = default from appearance.2da)",
},
{
label: "ChallengeRating",
displayName: "Challenge Rating",
type: GffFieldType.Float,
category: "AI",
},
{
label: "ScriptHeartbeat",
displayName: "Heartbeat Script",
type: GffFieldType.ResRef,
category: "Scripts",
description: "Runs every 6 seconds",
},
{
label: "ScriptOnDamaged",
displayName: "On Damaged Script",
type: GffFieldType.ResRef,
category: "Scripts",
},
{
label: "ScriptDeath",
displayName: "On Death Script",
type: GffFieldType.ResRef,
category: "Scripts",
},
{
label: "ScriptSpawn",
displayName: "On Spawn Script",
type: GffFieldType.ResRef,
category: "Scripts",
},
{
label: "ClassList",
displayName: "Classes",
type: GffFieldType.List,
category: "Attributes",
description: "Character class levels",
},
{
label: "Equip_ItemList",
displayName: "Equipped Items",
type: GffFieldType.List,
category: "Equipment",
description: "Items in equipment slots",
},
{
label: "ItemList",
displayName: "Inventory",
type: GffFieldType.List,
category: "Equipment",
description: "Items carried in inventory",
},
],
};
+89
View File
@@ -0,0 +1,89 @@
import { GffFieldType, type GffTypeSchema } from "./schema.js";
export const schema: GffTypeSchema = {
fileType: "uti",
displayName: "Item",
categories: ["Identity", "Properties", "Appearance"],
fields: [
{
label: "LocalizedName",
displayName: "Name",
type: GffFieldType.CExoLocString,
category: "Identity",
description: "Localized display name of the item",
},
{
label: "Tag",
displayName: "Tag",
type: GffFieldType.CExoString,
category: "Identity",
},
{
label: "TemplateResRef",
displayName: "Template ResRef",
type: GffFieldType.ResRef,
category: "Identity",
description: "Blueprint resource reference (max 16 chars)",
},
{
label: "BaseItem",
displayName: "Base Item Type",
type: GffFieldType.Int,
category: "Identity",
description: "Row index into baseitems.2da",
},
{
label: "StackSize",
displayName: "Stack Size",
type: GffFieldType.Word,
category: "Properties",
},
{
label: "Cost",
displayName: "Cost",
type: GffFieldType.Dword,
category: "Properties",
description: "Gold piece value override (0 = use 2da calculation)",
},
{
label: "Charges",
displayName: "Charges",
type: GffFieldType.Byte,
category: "Properties",
},
{
label: "Identified",
displayName: "Identified",
type: GffFieldType.Byte,
category: "Properties",
description: "Whether the item starts identified",
},
{
label: "Plot",
displayName: "Plot Item",
type: GffFieldType.Byte,
category: "Properties",
description: "Cannot be dropped or sold if set",
},
{
label: "Stolen",
displayName: "Stolen",
type: GffFieldType.Byte,
category: "Properties",
},
{
label: "Cursed",
displayName: "Cursed",
type: GffFieldType.Byte,
category: "Properties",
description: "Cannot be unequipped if set",
},
{
label: "PropertiesList",
displayName: "Item Properties",
type: GffFieldType.List,
category: "Properties",
description: "Magical and special properties on this item",
},
],
};
+62
View File
@@ -0,0 +1,62 @@
import { GffFieldType, type GffTypeSchema } from "./schema.js";
export const schema: GffTypeSchema = {
fileType: "utm",
displayName: "Store",
categories: ["Identity", "Economy", "Inventory"],
fields: [
{
label: "Tag",
displayName: "Tag",
type: GffFieldType.CExoString,
category: "Identity",
},
{
label: "TemplateResRef",
displayName: "Template ResRef",
type: GffFieldType.ResRef,
category: "Identity",
},
{
label: "MarkDown",
displayName: "Buy Price Adjustment",
type: GffFieldType.Int,
category: "Economy",
description: "Percentage adjustment when store buys from player (negative = pays less)",
},
{
label: "MarkUp",
displayName: "Sell Price Adjustment",
type: GffFieldType.Int,
category: "Economy",
description: "Percentage adjustment when store sells to player (positive = charges more)",
},
{
label: "BM_MarkDown",
displayName: "Black Market Buy Adjustment",
type: GffFieldType.Int,
category: "Economy",
},
{
label: "StoreGold",
displayName: "Store Gold",
type: GffFieldType.Int,
category: "Economy",
description: "Gold available for buying from players (-1 = unlimited)",
},
{
label: "BlackMarket",
displayName: "Black Market",
type: GffFieldType.Byte,
category: "Economy",
description: "Whether the store buys stolen goods",
},
{
label: "StoreList",
displayName: "Store Inventory",
type: GffFieldType.List,
category: "Inventory",
description: "Categories and items available for sale",
},
],
};
+116
View File
@@ -0,0 +1,116 @@
import { GffFieldType, type GffTypeSchema } from "./schema.js";
export const schema: GffTypeSchema = {
fileType: "utp",
displayName: "Placeable",
categories: ["Identity", "Lock", "Trap", "Scripts"],
fields: [
{
label: "Appearance",
displayName: "Appearance",
type: GffFieldType.Dword,
category: "Identity",
description: "Row index into placeables.2da",
},
{
label: "Tag",
displayName: "Tag",
type: GffFieldType.CExoString,
category: "Identity",
},
{
label: "TemplateResRef",
displayName: "Template ResRef",
type: GffFieldType.ResRef,
category: "Identity",
},
{
label: "Faction",
displayName: "Faction",
type: GffFieldType.Dword,
category: "Identity",
},
{
label: "Plot",
displayName: "Plot",
type: GffFieldType.Byte,
category: "Identity",
description: "Indestructible if set",
},
{
label: "Useable",
displayName: "Useable",
type: GffFieldType.Byte,
category: "Identity",
description: "Whether the player can interact with this placeable",
},
{
label: "HasInventory",
displayName: "Has Inventory",
type: GffFieldType.Byte,
category: "Identity",
},
{
label: "Locked",
displayName: "Locked",
type: GffFieldType.Byte,
category: "Lock",
},
{
label: "LockDC",
displayName: "Lock DC",
type: GffFieldType.Byte,
category: "Lock",
description: "Difficulty class to pick the lock",
},
{
label: "OpenLockDC",
displayName: "Open Lock DC",
type: GffFieldType.Byte,
category: "Lock",
},
{
label: "KeyName",
displayName: "Key Tag",
type: GffFieldType.CExoString,
category: "Lock",
description: "Tag of the key item that unlocks this",
},
{
label: "KeyRequired",
displayName: "Key Required",
type: GffFieldType.Byte,
category: "Lock",
},
{
label: "TrapDetectable",
displayName: "Trap Detectable",
type: GffFieldType.Byte,
category: "Trap",
},
{
label: "TrapDisarmable",
displayName: "Trap Disarmable",
type: GffFieldType.Byte,
category: "Trap",
},
{
label: "ScriptOnOpen",
displayName: "On Open Script",
type: GffFieldType.ResRef,
category: "Scripts",
},
{
label: "ScriptOnClosed",
displayName: "On Closed Script",
type: GffFieldType.ResRef,
category: "Scripts",
},
{
label: "ScriptOnUsed",
displayName: "On Used Script",
type: GffFieldType.ResRef,
category: "Scripts",
},
],
};