diff --git a/MODEL_COMPILATION_ON_BUILDTIME_CONTRACT.md b/MODEL_COMPILATION_ON_BUILDTIME_CONTRACT.md index 2db9a43..a720dd7 100644 --- a/MODEL_COMPILATION_ON_BUILDTIME_CONTRACT.md +++ b/MODEL_COMPILATION_ON_BUILDTIME_CONTRACT.md @@ -423,7 +423,7 @@ Minimum messages: - `Preparing model compiler...` - `Preparing model compilation workspace...` - `Compiling model 12/324: part/belt/pfa0_belt018.mdl` -- `Reusing compiled model: vfxs/headaccessory/hfx_bandana.mdl` +- `Reusing compiled model: vfxs/head_accessories/hfx_bandana.mdl` - `Skipping binary model source: creatures/foo.mdl` - `FAILED model compile: placeables/broken_model.mdl` diff --git a/internal/pipeline/pipeline_test.go b/internal/pipeline/pipeline_test.go index ba32dbe..d3ae836 100644 --- a/internal/pipeline/pipeline_test.go +++ b/internal/pipeline/pipeline_test.go @@ -2178,10 +2178,10 @@ func TestPlanHAKsWritesManifestWithoutArchives(t *testing.T) { func TestBuildHAKsWritesConfiguredHeadVisualeffectsAutogenManifest(t *testing.T) { root := t.TempDir() mustMkdir(t, filepath.Join(root, "src")) - mustMkdir(t, filepath.Join(root, "assets", "vfxs", "chestaccessory")) - mustMkdir(t, filepath.Join(root, "assets", "vfxs", "headaccessory")) - mustMkdir(t, filepath.Join(root, "assets", "vfxs", "headdecoration")) - mustMkdir(t, filepath.Join(root, "assets", "vfxs", "headfeature", "hair")) + mustMkdir(t, filepath.Join(root, "assets", "vfxs", "chest_accessories")) + mustMkdir(t, filepath.Join(root, "assets", "vfxs", "head_accessories")) + mustMkdir(t, filepath.Join(root, "assets", "vfxs", "head_decorations")) + mustMkdir(t, filepath.Join(root, "assets", "vfxs", "head_features", "hair")) mustMkdir(t, filepath.Join(root, "build")) mustWriteFile(t, filepath.Join(root, "nwn-tool.json"), `{ @@ -2200,7 +2200,7 @@ func TestBuildHAKsWritesConfiguredHeadVisualeffectsAutogenManifest(t *testing.T) { "id": "head_visualeffects", "root": "vfxs", - "include": ["chestaccessory/**/*.mdl", "headaccessory/**/*.mdl", "headdecoration/**/*.mdl", "headfeature/**/*.mdl"], + "include": ["chest_accessories/**/*.mdl", "head_accessories/**/*.mdl", "head_decorations/**/*.mdl", "head_features/**/*.mdl"], "derive": { "kind": "model_stem", "group_from": "first_path_segment" @@ -2212,8 +2212,8 @@ func TestBuildHAKsWritesConfiguredHeadVisualeffectsAutogenManifest(t *testing.T) }, "head_visualeffects": { "groups": { - "headaccessory": { - "prefix": "headaccessory", + "head_accessories": { + "prefix": "head_accessories", "model_columns": ["Imp_HeadCon_Node", "Imp_Root_H_Node"] } } @@ -2233,10 +2233,10 @@ func TestBuildHAKsWritesConfiguredHeadVisualeffectsAutogenManifest(t *testing.T) } `) - mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "chestaccessory", "tfx_sash.mdl"), "sash") - mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "headaccessory", "hfx_bandana.mdl"), "bandana") - mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "headdecoration", "hfx_laurel.mdl"), "laurel") - mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "headfeature", "hair", "hfx_hair_bangs.mdl"), "hair") + mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "chest_accessories", "tfx_sash.mdl"), "sash") + mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "head_accessories", "hfx_bandana.mdl"), "bandana") + mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "head_decorations", "hfx_laurel.mdl"), "laurel") + mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "head_features", "hair", "hfx_hair_bangs.mdl"), "hair") p, err := project.Load(root) if err != nil { @@ -2267,16 +2267,16 @@ func TestBuildHAKsWritesConfiguredHeadVisualeffectsAutogenManifest(t *testing.T) text := string(headRaw) for _, want := range []string{ `"id": "head_visualeffects"`, - `"group": "chestaccessory"`, + `"group": "chest_accessories"`, `"model_stem": "tfx_sash"`, - `"group": "headaccessory"`, + `"group": "head_accessories"`, `"model_stem": "hfx_bandana"`, `"head_visualeffects": {`, `"model_columns": [`, `"Imp_Root_H_Node"`, - `"group": "headdecoration"`, + `"group": "head_decorations"`, `"model_stem": "hfx_laurel"`, - `"group": "headfeature"`, + `"group": "head_features"`, `"model_stem": "hfx_hair_bangs"`, } { if !strings.Contains(text, want) { @@ -2288,7 +2288,7 @@ func TestBuildHAKsWritesConfiguredHeadVisualeffectsAutogenManifest(t *testing.T) func TestBuildHAKsWritesFolderCategoryInHeadVisualeffectsAutogenManifest(t *testing.T) { root := t.TempDir() mustMkdir(t, filepath.Join(root, "src")) - mustMkdir(t, filepath.Join(root, "assets", "vfxs", "headfeature", "sinfar", "ears_plt")) + mustMkdir(t, filepath.Join(root, "assets", "vfxs", "head_features", "sinfar", "ears_plt")) mustMkdir(t, filepath.Join(root, "build")) mustWriteFile(t, filepath.Join(root, "nwn-tool.json"), `{ @@ -2307,7 +2307,7 @@ func TestBuildHAKsWritesFolderCategoryInHeadVisualeffectsAutogenManifest(t *test { "id": "head_visualeffects", "root": "vfxs", - "include": ["headfeature/**/*.mdl"], + "include": ["head_features/**/*.mdl"], "derive": { "kind": "model_stem", "group_from": "first_path_segment" @@ -2319,7 +2319,7 @@ func TestBuildHAKsWritesFolderCategoryInHeadVisualeffectsAutogenManifest(t *test }, "head_visualeffects": { "groups": { - "headfeature": { + "head_features": { "model_columns": ["Imp_HeadCon_Node", "Imp_Root_H_Node"] } }, @@ -2346,7 +2346,7 @@ func TestBuildHAKsWritesFolderCategoryInHeadVisualeffectsAutogenManifest(t *test } `) - mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "headfeature", "sinfar", "ears_plt", "hfx_Ear_EL_L.mdl"), "ear") + mustWriteFile(t, filepath.Join(root, "assets", "vfxs", "head_features", "sinfar", "ears_plt", "hfx_Ear_EL_L.mdl"), "ear") p, err := project.Load(root) if err != nil { @@ -2368,11 +2368,11 @@ func TestBuildHAKsWritesFolderCategoryInHeadVisualeffectsAutogenManifest(t *test } text := string(headRaw) for _, want := range []string{ - `"group": "headfeature"`, + `"group": "head_features"`, `"subgroup": "sinfar/ears_plt"`, `"category": "ears_plt"`, `"model_stem": "hfx_Ear_EL_L"`, - `"source": "headfeature/sinfar/ears_plt/hfx_Ear_EL_L.mdl"`, + `"source": "head_features/sinfar/ears_plt/hfx_Ear_EL_L.mdl"`, `"group_token_source": "folder_name"`, `"category_from": "immediate_parent"`, } { diff --git a/internal/project/project_test.go b/internal/project/project_test.go index 288fec2..f0365fb 100644 --- a/internal/project/project_test.go +++ b/internal/project/project_test.go @@ -1332,7 +1332,7 @@ func TestValidateLayoutRejectsInvalidAutogenConsumerConfig(t *testing.T) { Dataset: "visualeffects", Mode: "unsupported", Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl"}, Derive: AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"}, Manifest: AutogenManifestConfig{ ReleaseTag: "head-vfx-manifest-current", @@ -1434,7 +1434,7 @@ func TestValidateLayoutRejectsInvalidHeadVisualeffectsNamingConfig(t *testing.T) Dataset: "visualeffects", Mode: "head_visualeffects", Root: "vfxs", - Include: []string{"headfeature/**/*.mdl"}, + Include: []string{"head_features/**/*.mdl"}, Derive: AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"}, Manifest: AutogenManifestConfig{ ReleaseTag: "head-vfx-manifest-current", @@ -1443,7 +1443,7 @@ func TestValidateLayoutRejectsInvalidHeadVisualeffectsNamingConfig(t *testing.T) }, HeadVisualeffects: HeadVisualeffectsConfig{ Groups: map[string]HeadVisualeffectGroupConfig{ - "headfeature": {}, + "head_features": {}, }, GroupTokenSource: "unsupported", CategoryFrom: "unsupported", @@ -1488,19 +1488,19 @@ autogen: mode: head_visualeffects root: vfxs include: - - headfeature/**/*.mdl + - head_features/**/*.mdl derive: kind: model_stem group_from: first_path_segment head_visualeffects: groups: - headfeature: + head_features: model_columns: - Imp_HeadCon_Node group_token_source: folder_name category_from: immediate_parent legacy_groups: - head_features: headfeature + head_features: head_features legacy_key_format: "{dataset}:{group}_{stem}" `) @@ -1557,7 +1557,7 @@ func TestValidateLayoutAcceptsGeneratedTopData2DAConfig(t *testing.T) { ID: "cachedmodels", Mode: "cachedmodels_rows", Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl", "head_features/**/*.mdl"}, Derive: AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"}, }, }, diff --git a/internal/topdata/autogen.go b/internal/topdata/autogen.go index fd7094e..1c3cb99 100644 --- a/internal/topdata/autogen.go +++ b/internal/topdata/autogen.go @@ -850,10 +850,10 @@ func applyHeadVisualeffectsConfig(policy *headVisualeffectsPolicy, cfg project.H func defaultHeadVisualeffectsPolicy() headVisualeffectsPolicy { return headVisualeffectsPolicy{ Groups: map[string]headVisualeffectsGroupPolicy{ - "chestaccessory": {}, - "headaccessory": {}, - "headdecoration": {}, - "headfeature": {}, + "chest_accessories": {}, + "head_accessories": {}, + "head_decorations": {}, + "head_features": {}, }, GroupTokenSource: "folder_name", CategoryFrom: "immediate_parent", diff --git a/internal/topdata/topdata_test.go b/internal/topdata/topdata_test.go index a71b9bb..8905fc9 100644 --- a/internal/topdata/topdata_test.go +++ b/internal/topdata/topdata_test.go @@ -6858,7 +6858,7 @@ func TestNormalizeProjectImportsLegacyVisualeffects(t *testing.T) { }`+"\n") writeFile(t, filepath.Join(root, "reference", "data", "visualeffects", "lock.json"), `{ "visualeffects:chatbubble": 10100, - "visualeffects:headaccessory_antlers_l": 10101, + "visualeffects:head_accessories_antlers_l": 10101, "visualeffects:vcm_hitmasscrit": 10125, "visualeffects:com_hitnegative": 288, "visualeffects:fnf_blinddeaf": 18 @@ -6919,8 +6919,8 @@ func TestNormalizeProjectImportsLegacyVisualeffects(t *testing.T) { }`+"\n") writeFile(t, filepath.Join(root, "reference", "data", "visualeffects", "modules", "accessories", "add_eos.json"), `{ "entries": { - "visualeffects:headaccessory_antlers_l": { - "Label": "headaccessory_ANTLERS_L", + "visualeffects:head_accessories_antlers_l": { + "Label": "head_accessories_ANTLERS_L", "Type_FD": "D", "OrientWithGround": "0", "Imp_HeadCon_Node": "hfx_antlers_l", @@ -6986,7 +6986,7 @@ func TestNormalizeProjectImportsLegacyVisualeffects(t *testing.T) { t.Fatalf("read visualeffects lock: %v", err) } lockText := string(lockRaw) - if !strings.Contains(lockText, `"visualeffects:chatbubble": 10100`) || !strings.Contains(lockText, `"visualeffects:headaccessory_antlers_l": 10101`) { + if !strings.Contains(lockText, `"visualeffects:chatbubble": 10100`) || !strings.Contains(lockText, `"visualeffects:head_accessories_antlers_l": 10101`) { t.Fatalf("expected imported visualeffects lock IDs, got:\n%s", lockText) } } @@ -7005,7 +7005,7 @@ func TestBuildSupportsCanonicalVisualeffects(t *testing.T) { }`+"\n") writeFile(t, filepath.Join(root, "topdata", "data", "visualeffects", "lock.json"), `{ "visualeffects:chatbubble": 10100, - "visualeffects:headaccessory_antlers_l": 10101, + "visualeffects:head_accessories_antlers_l": 10101, "visualeffects:vcm_hitmasscrit": 10125, "visualeffects:com_hitnegative": 288, "visualeffects:fnf_blinddeaf": 18 @@ -7066,8 +7066,8 @@ func TestBuildSupportsCanonicalVisualeffects(t *testing.T) { }`+"\n") writeFile(t, filepath.Join(root, "topdata", "data", "visualeffects", "modules", "accessories", "add_eos.json"), `{ "entries": { - "visualeffects:headaccessory_antlers_l": { - "Label": "headaccessory_ANTLERS_L", + "visualeffects:head_accessories_antlers_l": { + "Label": "head_accessories_ANTLERS_L", "Type_FD": "D", "OrientWithGround": "0", "Imp_HeadCon_Node": "hfx_antlers_l", @@ -7113,7 +7113,7 @@ func TestBuildSupportsCanonicalVisualeffects(t *testing.T) { "18\tVFX_FNF_BLINDDEAF\tF\t0", "288\tVFX_COM_HIT_NEGATIVE\tF\t0", "10100\tVFX_DUR_CHAT_BUBBLE\tD\t0", - "10101\theadaccessory_ANTLERS_L\tD\t0", + "10101\thead_accessories_ANTLERS_L\tD\t0", "10125\tVFX_COM_HIT_MASSIVE_CRITICALS\tF\t0", "scm_hitsmite", "vff_explblind_l", @@ -7147,7 +7147,7 @@ with open(os.path.join(out, "visualeffects.2da"), "w", encoding="utf-8") as f: f.write("18\tVFX_FNF_BLINDDEAF\tF\t0\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\tvff_explblind_l\t0\n") f.write("288\tVFX_COM_HIT_NEGATIVE\tF\t0\t****\t****\t****\t****\t****\t****\tscm_hitsmite\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t0\n") f.write("10100\tVFX_DUR_CHAT_BUBBLE\tD\t0\tvdr_chatbubble\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t0\n") - f.write("10101\theadaccessory_ANTLERS_L\tD\t0\thfx_antlers_l\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t0\n") + f.write("10101\thead_accessories_ANTLERS_L\tD\t0\thfx_antlers_l\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t0\n") f.write("10125\tVFX_COM_HIT_MASSIVE_CRITICALS\tF\t0\t****\tvcm_hitmasscrit\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t****\t0\n") with open(os.path.join(tlk, "sow_tlk.tlk"), "wb") as f: f.write(b"TLK") @@ -7163,7 +7163,7 @@ with open(os.path.join(tlk, "sow_tlk.tlk"), "wb") as f: }`+"\n") writeFile(t, filepath.Join(root, target, "data", "visualeffects", "lock.json"), `{ "visualeffects:chatbubble": 10100, - "visualeffects:headaccessory_antlers_l": 10101, + "visualeffects:head_accessories_antlers_l": 10101, "visualeffects:vcm_hitmasscrit": 10125, "visualeffects:com_hitnegative": 288, "visualeffects:fnf_blinddeaf": 18 @@ -7224,8 +7224,8 @@ with open(os.path.join(tlk, "sow_tlk.tlk"), "wb") as f: }`+"\n") writeFile(t, filepath.Join(root, target, "data", "visualeffects", "modules", "accessories", "add_eos.json"), `{ "entries": { - "visualeffects:headaccessory_antlers_l": { - "Label": "headaccessory_ANTLERS_L", + "visualeffects:head_accessories_antlers_l": { + "Label": "head_accessories_ANTLERS_L", "Type_FD": "D", "OrientWithGround": "0", "Imp_HeadCon_Node": "hfx_antlers_l", @@ -7328,12 +7328,12 @@ func TestApplyAutogenConsumersAugmentsPartsFromLocalOverride(t *testing.T) { func TestApplyAutogenConsumersAugmentsHeadVisualeffectsFromLocalOverride(t *testing.T) { root := testProjectRoot(t) overrideRoot := filepath.Join(root, "autogen-assets") - mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "headaccessory", "hat")) - mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "headdecoration", "laurel")) - mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "headfeature", "hair")) - writeFile(t, filepath.Join(overrideRoot, "vfxs", "headaccessory", "hat", "hfx_bandana.mdl"), "mdl\n") - writeFile(t, filepath.Join(overrideRoot, "vfxs", "headdecoration", "laurel", "hfx_laurel.mdl"), "mdl\n") - writeFile(t, filepath.Join(overrideRoot, "vfxs", "headfeature", "hair", "hfx_hair_bangs.mdl"), "mdl\n") + mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "head_accessories", "hat")) + mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "head_decorations", "laurel")) + mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "head_features", "hair")) + writeFile(t, filepath.Join(overrideRoot, "vfxs", "head_accessories", "hat", "hfx_bandana.mdl"), "mdl\n") + writeFile(t, filepath.Join(overrideRoot, "vfxs", "head_decorations", "laurel", "hfx_laurel.mdl"), "mdl\n") + writeFile(t, filepath.Join(overrideRoot, "vfxs", "head_features", "hair", "hfx_hair_bangs.mdl"), "mdl\n") p := testProject(root) p.Config.Autogen.Consumers = []project.AutogenConsumerConfig{ @@ -7343,7 +7343,7 @@ func TestApplyAutogenConsumersAugmentsHeadVisualeffectsFromLocalOverride(t *test Dataset: "visualeffects", Mode: "head_visualeffects", Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl", "headdecoration/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl", "head_decorations/**/*.mdl", "head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"}, Manifest: project.AutogenManifestConfig{ReleaseTag: "head-vfx-manifest-current", AssetName: "sow-head-vfx-manifest.json", CacheName: "sow-head-vfx-manifest.json"}, LocalOverrideRoot: "autogen-assets", @@ -7374,37 +7374,37 @@ func TestApplyAutogenConsumersAugmentsHeadVisualeffectsFromLocalOverride(t *test rowByKey[row["key"].(string)] = row } - bandana := rowByKey["visualeffects:headaccessory/hat/bandana"] + bandana := rowByKey["visualeffects:head_accessories/hat/bandana"] if bandana == nil { t.Fatalf("expected head accessory row, got %#v", got[0].Rows) } - if bandana["Label"] != "headaccessory/hat/bandana" || bandana["Imp_HeadCon_Node"] != "hfx_bandana" || bandana["OrientWithObject"] != "1" { + if bandana["Label"] != "head_accessories/hat/bandana" || bandana["Imp_HeadCon_Node"] != "hfx_bandana" || bandana["OrientWithObject"] != "1" { t.Fatalf("unexpected head accessory defaults: %#v", bandana) } - laurel := rowByKey["visualeffects:headdecoration/laurel/laurel"] + laurel := rowByKey["visualeffects:head_decorations/laurel/laurel"] if laurel == nil { t.Fatalf("expected head decoration row, got %#v", got[0].Rows) } - if laurel["Label"] != "headdecoration/laurel/laurel" || laurel["Imp_HeadCon_Node"] != "hfx_laurel" || laurel["Type_FD"] != "D" { + if laurel["Label"] != "head_decorations/laurel/laurel" || laurel["Imp_HeadCon_Node"] != "hfx_laurel" || laurel["Type_FD"] != "D" { t.Fatalf("unexpected head decoration defaults: %#v", laurel) } - hair := rowByKey["visualeffects:headfeature/hair/hair_bangs"] + hair := rowByKey["visualeffects:head_features/hair/hair_bangs"] if hair == nil { t.Fatalf("expected head feature row, got %#v", got[0].Rows) } - if hair["Label"] != "headfeature/hair/hair_bangs" || hair["Imp_HeadCon_Node"] != "hfx_hair_bangs" || hair["Type_FD"] != "D" { + if hair["Label"] != "head_features/hair/hair_bangs" || hair["Imp_HeadCon_Node"] != "hfx_hair_bangs" || hair["Type_FD"] != "D" { t.Fatalf("unexpected head feature defaults: %#v", hair) } - if _, ok := got[0].LockData["visualeffects:headaccessory/hat/bandana"]; !ok { + if _, ok := got[0].LockData["visualeffects:head_accessories/hat/bandana"]; !ok { t.Fatalf("expected head accessory lock id, got %#v", got[0].LockData) } - if _, ok := got[0].LockData["visualeffects:headdecoration/laurel/laurel"]; !ok { + if _, ok := got[0].LockData["visualeffects:head_decorations/laurel/laurel"]; !ok { t.Fatalf("expected head decoration lock id, got %#v", got[0].LockData) } - if _, ok := got[0].LockData["visualeffects:headfeature/hair/hair_bangs"]; !ok { + if _, ok := got[0].LockData["visualeffects:head_features/hair/hair_bangs"]; !ok { t.Fatalf("expected head feature lock id, got %#v", got[0].LockData) } } @@ -7412,8 +7412,8 @@ func TestApplyAutogenConsumersAugmentsHeadVisualeffectsFromLocalOverride(t *test func TestApplyAutogenConsumersUsesFolderDrivenSlashHeadVisualeffectsNames(t *testing.T) { root := testProjectRoot(t) overrideRoot := filepath.Join(root, "autogen-assets") - mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "headfeature", "sinfar", "ears_plt")) - writeFile(t, filepath.Join(overrideRoot, "vfxs", "headfeature", "sinfar", "ears_plt", "hfx_Ear_EL_L.mdl"), "mdl\n") + mkdirAll(t, filepath.Join(overrideRoot, "vfxs", "head_features", "sinfar", "ears_plt")) + writeFile(t, filepath.Join(overrideRoot, "vfxs", "head_features", "sinfar", "ears_plt", "hfx_Ear_EL_L.mdl"), "mdl\n") p := testProject(root) p.Config.Autogen.Consumers = []project.AutogenConsumerConfig{ @@ -7423,13 +7423,13 @@ func TestApplyAutogenConsumersUsesFolderDrivenSlashHeadVisualeffectsNames(t *tes Dataset: "visualeffects", Mode: "head_visualeffects", Root: "vfxs", - Include: []string{"headfeature/**/*.mdl"}, + Include: []string{"head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"}, Manifest: project.AutogenManifestConfig{ReleaseTag: "head-vfx-manifest-current", AssetName: "sow-head-vfx-manifest.json", CacheName: "sow-head-vfx-manifest.json"}, LocalOverrideRoot: "autogen-assets", HeadVisualeffects: project.HeadVisualeffectsConfig{ Groups: map[string]project.HeadVisualeffectGroupConfig{ - "headfeature": { + "head_features": { ModelColumns: []string{"Imp_HeadCon_Node", "Imp_Root_H_Node"}, }, }, @@ -7455,7 +7455,7 @@ func TestApplyAutogenConsumersUsesFolderDrivenSlashHeadVisualeffectsNames(t *tes Columns: []string{"Label", "Type_FD", "OrientWithGround", "Imp_HeadCon_Node", "Imp_Root_H_Node", "OrientWithObject"}, Rows: nil, LockData: map[string]int{ - "visualeffects:headfeature/ears_plt/Ear_EL_L": 11030, + "visualeffects:head_features/ears_plt/Ear_EL_L": 11030, }, }, } @@ -7468,13 +7468,13 @@ func TestApplyAutogenConsumersUsesFolderDrivenSlashHeadVisualeffectsNames(t *tes t.Fatalf("expected one autogenerated visualeffects row, got %#v", got) } row := got[0].Rows[0] - if row["key"] != "visualeffects:headfeature/ears_plt/Ear_EL_L" { + if row["key"] != "visualeffects:head_features/ears_plt/Ear_EL_L" { t.Fatalf("expected folder-driven key, got %#v", row) } if row["id"] != 11030 { t.Fatalf("expected existing lock id to be preserved, got %#v", row) } - if row["Label"] != "headfeature/ears_plt/Ear_EL_L" { + if row["Label"] != "head_features/ears_plt/Ear_EL_L" { t.Fatalf("expected preserve-case slash label, got %#v", row) } if row["Imp_HeadCon_Node"] != "hfx_Ear_EL_L" || row["Imp_Root_H_Node"] != "hfx_Ear_EL_L" { @@ -7487,7 +7487,7 @@ func TestBuildNativeUsesConfiguredHeadVisualeffectsGroups(t *testing.T) { mkdirAll(t, filepath.Join(root, "src")) mkdirAll(t, filepath.Join(root, "build")) mkdirAll(t, filepath.Join(root, "topdata", "data", "visualeffects")) - mkdirAll(t, filepath.Join(root, "autogen-assets", "vfxs", "chestaccessory")) + mkdirAll(t, filepath.Join(root, "autogen-assets", "vfxs", "chest_accessories")) mkdirAll(t, filepath.Join(root, "autogen-assets", "vfxs", "head_jewels")) writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n") writeFile(t, filepath.Join(root, "topdata", "data", "visualeffects", "base.json"), `{ @@ -7495,7 +7495,7 @@ func TestBuildNativeUsesConfiguredHeadVisualeffectsGroups(t *testing.T) { "columns": ["Label", "Type_FD", "OrientWithGround", "Imp_HeadCon_Node", "Imp_Root_S_Node", "Imp_Root_M_Node", "Imp_Root_L_Node", "Imp_Root_H_Node", "OrientWithObject"], "rows": [] }`+"\n") - writeFile(t, filepath.Join(root, "autogen-assets", "vfxs", "chestaccessory", "tfx_sash.mdl"), "mdl\n") + writeFile(t, filepath.Join(root, "autogen-assets", "vfxs", "chest_accessories", "tfx_sash.mdl"), "mdl\n") writeFile(t, filepath.Join(root, "autogen-assets", "vfxs", "head_jewels", "hfx_circlet.mdl"), "mdl\n") writeFile(t, filepath.Join(root, "nwn-tool.yaml"), ` module: @@ -7515,7 +7515,7 @@ autogen: mode: head_visualeffects root: vfxs include: - - chestaccessory/**/*.mdl + - chest_accessories/**/*.mdl - head_jewels/**/*.mdl derive: kind: model_stem @@ -7527,8 +7527,8 @@ autogen: local_override_root: autogen-assets head_visualeffects: groups: - chestaccessory: - prefix: chestaccessory + chest_accessories: + prefix: chest_accessories model_columns: - Imp_Root_S_Node - Imp_Root_M_Node @@ -7561,7 +7561,7 @@ autogen: } text := string(visualeffectsBytes) for _, want := range []string{ - "0\tchestaccessory_sash\tF\t0\t****\ttfx_sash\ttfx_sash\ttfx_sash\ttfx_sash\t0", + "0\tchest_accessories_sash\tF\t0\t****\ttfx_sash\ttfx_sash\ttfx_sash\ttfx_sash\t0", "1\theadjewel_circlet\tF\t0\thfx_circlet\t****\t****\t****\t****\t0", } { if !strings.Contains(text, want) { @@ -7572,16 +7572,16 @@ autogen: func TestBuildGenerated2DAAssetsAppendsCachedModelsFromHeadVisualeffects(t *testing.T) { root := testProjectRoot(t) - mkdirAll(t, filepath.Join(root, "assets", "vfxs", "chestaccessory")) - mkdirAll(t, filepath.Join(root, "assets", "vfxs", "headaccessory")) - mkdirAll(t, filepath.Join(root, "assets", "vfxs", "headdecoration")) - mkdirAll(t, filepath.Join(root, "assets", "vfxs", "headfeature", "hair")) + mkdirAll(t, filepath.Join(root, "assets", "vfxs", "chest_accessories")) + mkdirAll(t, filepath.Join(root, "assets", "vfxs", "head_accessories")) + mkdirAll(t, filepath.Join(root, "assets", "vfxs", "head_decorations")) + mkdirAll(t, filepath.Join(root, "assets", "vfxs", "head_features", "hair")) mkdirAll(t, filepath.Join(root, "topdata", "data", "cachedmodels")) - writeFile(t, filepath.Join(root, "assets", "vfxs", "chestaccessory", "tfx_sash.mdl"), "mdl\n") - writeFile(t, filepath.Join(root, "assets", "vfxs", "headaccessory", "hfx_bandana.mdl"), "mdl\n") - writeFile(t, filepath.Join(root, "assets", "vfxs", "headaccessory", "hfx_existing.mdl"), "mdl\n") - writeFile(t, filepath.Join(root, "assets", "vfxs", "headdecoration", "hfx_laurel.mdl"), "mdl\n") - writeFile(t, filepath.Join(root, "assets", "vfxs", "headfeature", "hair", "hfx_hair_bangs.mdl"), "mdl\n") + writeFile(t, filepath.Join(root, "assets", "vfxs", "chest_accessories", "tfx_sash.mdl"), "mdl\n") + writeFile(t, filepath.Join(root, "assets", "vfxs", "head_accessories", "hfx_bandana.mdl"), "mdl\n") + writeFile(t, filepath.Join(root, "assets", "vfxs", "head_accessories", "hfx_existing.mdl"), "mdl\n") + writeFile(t, filepath.Join(root, "assets", "vfxs", "head_decorations", "hfx_laurel.mdl"), "mdl\n") + writeFile(t, filepath.Join(root, "assets", "vfxs", "head_features", "hair", "hfx_hair_bangs.mdl"), "mdl\n") writeFile(t, filepath.Join(root, "topdata", "data", "cachedmodels", "base.json"), `{ "columns": ["Model"], "rows": [ @@ -7603,7 +7603,7 @@ func TestBuildGenerated2DAAssetsAppendsCachedModelsFromHeadVisualeffects(t *test ID: "cachedmodels", Mode: "cachedmodels_rows", Root: "vfxs", - Include: []string{"chestaccessory/**/*.mdl", "headaccessory/**/*.mdl", "headdecoration/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"chest_accessories/**/*.mdl", "head_accessories/**/*.mdl", "head_decorations/**/*.mdl", "head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"}, }, }, @@ -12808,10 +12808,10 @@ func TestBuildNativeWithReleasedHeadVisualeffectsManifest(t *testing.T) { "generated_at": "2026-04-25T00:00:00Z", "head_visualeffects": { "groups": { - "headaccessory": { + "head_accessories": { "model_columns": ["Imp_HeadCon_Node", "Imp_Root_H_Node"] }, - "headfeature": { + "head_features": { "model_columns": ["Imp_HeadCon_Node", "Imp_Root_H_Node"] } }, @@ -12830,14 +12830,14 @@ func TestBuildNativeWithReleasedHeadVisualeffectsManifest(t *testing.T) { }, "entries": [ { - "group": "headaccessory", + "group": "head_accessories", "model_stem": "hfx_bandana", - "source": "headaccessory/hfx_bandana.mdl" + "source": "head_accessories/hfx_bandana.mdl" }, { - "group": "headfeature", + "group": "head_features", "model_stem": "hfx_hair_bangs", - "source": "headfeature/hair/hfx_hair_bangs.mdl" + "source": "head_features/hair/hfx_hair_bangs.mdl" } ] }` + "\n" @@ -12877,7 +12877,7 @@ func TestBuildNativeWithReleasedHeadVisualeffectsManifest(t *testing.T) { Dataset: "visualeffects", Mode: "head_visualeffects", Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl", "head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{ Kind: "model_stem", GroupFrom: "first_path_segment", @@ -12904,8 +12904,8 @@ func TestBuildNativeWithReleasedHeadVisualeffectsManifest(t *testing.T) { } text := string(visualeffectsBytes) for _, want := range []string{ - "0\theadaccessory/bandana\tD\t0\thfx_bandana\thfx_bandana\t1", - "1\theadfeature/hair/hair_bangs\tD\t0\thfx_hair_bangs\thfx_hair_bangs\t1", + "0\thead_accessories/bandana\tD\t0\thfx_bandana\thfx_bandana\t1", + "1\thead_features/hair/hair_bangs\tD\t0\thfx_hair_bangs\thfx_hair_bangs\t1", "17\tEXISTING\tF\t0\t****\t****\t0", } { if !strings.Contains(text, want) { @@ -12932,7 +12932,7 @@ func TestBuildNativePrefersRemoteReleasedHeadVisualeffectsManifestOverFreshCache }`+"\n") writeFile(t, filepath.Join(projRoot, "topdata", "data", "visualeffects", "lock.json"), `{ "visualeffects:existing": 17, - "visualeffects:headaccessory/bandana": 42 + "visualeffects:head_accessories/bandana": 42 }`+"\n") mkdirAll(t, filepath.Join(projRoot, "reference")) writeFile(t, filepath.Join(projRoot, "reference", "build.py"), "print('ok')\n") @@ -12943,9 +12943,9 @@ func TestBuildNativePrefersRemoteReleasedHeadVisualeffectsManifestOverFreshCache "generated_at": "2026-04-25T00:00:00Z", "entries": [ { - "group": "headfeature", + "group": "head_features", "model_stem": "hfx_hair_bangs", - "source": "headfeature/hair/hfx_hair_bangs.mdl" + "source": "head_features/hair/hfx_hair_bangs.mdl" } ] }`+"\n") @@ -12959,9 +12959,9 @@ func TestBuildNativePrefersRemoteReleasedHeadVisualeffectsManifestOverFreshCache "generated_at": "2026-04-25T12:00:00Z", "entries": [ { - "group": "headaccessory", + "group": "head_accessories", "model_stem": "hfx_bandana", - "source": "headaccessory/hfx_bandana.mdl" + "source": "head_accessories/hfx_bandana.mdl" } ] }` + "\n" @@ -13001,7 +13001,7 @@ func TestBuildNativePrefersRemoteReleasedHeadVisualeffectsManifestOverFreshCache Dataset: "visualeffects", Mode: "head_visualeffects", Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl", "head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{ Kind: "model_stem", GroupFrom: "first_path_segment", @@ -13027,7 +13027,7 @@ func TestBuildNativePrefersRemoteReleasedHeadVisualeffectsManifestOverFreshCache t.Fatalf("read visualeffects.2da: %v", err) } text := string(visualeffectsBytes) - if !strings.Contains(text, "42\theadaccessory/bandana\tD\t0\thfx_bandana\t1") { + if !strings.Contains(text, "42\thead_accessories/bandana\tD\t0\thfx_bandana\t1") { t.Fatalf("expected remote manifest entry with preserved lock id, got:\n%s", text) } if strings.Contains(text, "HAIR_BANGS") { @@ -13052,7 +13052,7 @@ func TestBuildPackageInvalidatesFreshAutogenCacheWhenReleaseAssetIsNewer(t *test setCompiledOutputTimes(t, result, outputTime) cachePath := filepath.Join(root, ".cache", "sow-head-vfx-manifest.json") - writeFile(t, cachePath, `{"id":"head_visualeffects","generated_at":"2026-04-25T10:00:00Z","entries":[{"group":"headaccessory","model_stem":"hfx_bandana","source":"headaccessory/hfx_bandana.mdl"}]}`+"\n") + writeFile(t, cachePath, `{"id":"head_visualeffects","generated_at":"2026-04-25T10:00:00Z","entries":[{"group":"head_accessories","model_stem":"hfx_bandana","source":"head_accessories/hfx_bandana.mdl"}]}`+"\n") setFileTime(t, cachePath, time.Now()) var server *httptest.Server @@ -13063,7 +13063,7 @@ func TestBuildPackageInvalidatesFreshAutogenCacheWhenReleaseAssetIsNewer(t *test _, _ = w.Write([]byte(`{"assets":[{"name":"sow-head-vfx-manifest.json","updated_at":"3026-04-25T12:00:00Z","browser_download_url":"` + server.URL + `/downloads/sow-head-vfx-manifest.json"}]}`)) case "/downloads/sow-head-vfx-manifest.json": w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{"id":"head_visualeffects","entries":[{"group":"headaccessory","model_stem":"hfx_bandana","source":"headaccessory/hfx_bandana.mdl"}]}`)) + _, _ = w.Write([]byte(`{"id":"head_visualeffects","entries":[{"group":"head_accessories","model_stem":"hfx_bandana","source":"head_accessories/hfx_bandana.mdl"}]}`)) default: http.NotFound(w, r) } @@ -13081,7 +13081,7 @@ func TestBuildPackageInvalidatesFreshAutogenCacheWhenReleaseAssetIsNewer(t *test Mode: "head_visualeffects", Optional: true, Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl", "head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{ Kind: "model_stem", GroupFrom: "first_path_segment", @@ -13406,7 +13406,7 @@ func TestBuildPackageFailsWhenAutogenManifestCacheIsStale(t *testing.T) { Mode: "head_visualeffects", Optional: true, Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl", "head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{ Kind: "model_stem", GroupFrom: "first_path_segment", @@ -13419,7 +13419,7 @@ func TestBuildPackageFailsWhenAutogenManifestCacheIsStale(t *testing.T) { }, } cachePath := filepath.Join(root, ".cache", "sow-head-vfx-manifest.json") - writeFile(t, cachePath, `{"id":"head_visualeffects","entries":[{"group":"headaccessory","model_stem":"hfx_bandana","source":"headaccessory/hfx_bandana.mdl"}]}`+"\n") + writeFile(t, cachePath, `{"id":"head_visualeffects","entries":[{"group":"head_accessories","model_stem":"hfx_bandana","source":"head_accessories/hfx_bandana.mdl"}]}`+"\n") setFileTime(t, cachePath, time.Now().Add(-2*autogenManifestCacheMaxAge)) if _, err := BuildPackage(proj, nil); err == nil || !strings.Contains(err.Error(), "older than source file") { @@ -14354,7 +14354,7 @@ func testProject(root string) *project.Project { Mode: "head_visualeffects", Optional: true, Root: "vfxs", - Include: []string{"headaccessory/**/*.mdl", "headfeature/**/*.mdl"}, + Include: []string{"head_accessories/**/*.mdl", "head_features/**/*.mdl"}, Derive: project.AutogenDeriveConfig{ Kind: "model_stem", GroupFrom: "first_path_segment",