diff --git a/internal/topdata/native.go b/internal/topdata/native.go index fd7f29a..d0139a4 100644 --- a/internal/topdata/native.go +++ b/internal/topdata/native.go @@ -4339,6 +4339,15 @@ func updateOverrideRowKey(datasetName string, row map[string]any, expanded map[s } return changed, retiredKey, nil } + if oldKey == newKey { + row["key"] = newKey + rowByKey[newKey] = row + if _, ok := lockData[newKey]; !ok { + lockData[newKey] = rowID + changed = true + } + return changed, "", nil + } conflictingID := -1 if conflicting, ok := rowByKey[newKey]; ok && conflicting != nil { var conflictingHasID bool diff --git a/internal/topdata/topdata_test.go b/internal/topdata/topdata_test.go index 7c22b86..2a745f7 100644 --- a/internal/topdata/topdata_test.go +++ b/internal/topdata/topdata_test.go @@ -8776,6 +8776,80 @@ func TestBuildBaseitemsSplitReaddsRetiredKeyToLockfile(t *testing.T) { } } +func TestBuildBaseitemsAllowsKeyOnlyOverrideBeforeSameKeyIsRelocatedAndReadded(t *testing.T) { + root := testProjectRoot(t) + mkdirAll(t, filepath.Join(root, "topdata", "data", "baseitems", "modules", "10_vanillaoverrides")) + writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n") + writeFile(t, filepath.Join(root, "topdata", "data", "baseitems", "base.json"), `{ + "output": "baseitems.2da", + "columns": ["label", "MaxRange"], + "rows": [ + {"id": 58, "key": "baseitems:shortspear", "label": "shortspear", "MaxRange": "100"} + ] +}`+"\n") + writeFile(t, filepath.Join(root, "topdata", "data", "baseitems", "lock.json"), `{"baseitems:shortspear":59}`+"\n") + writeFile(t, filepath.Join(root, "topdata", "data", "baseitems", "modules", "10_vanillaoverrides", "ovr_baseitems_maxranges.json"), `{ + "overrides": [ + { + "key": "baseitems:shortspear", + "MaxRange": "255" + } + ] +}`+"\n") + writeFile(t, filepath.Join(root, "topdata", "data", "baseitems", "modules", "10_vanillaoverrides", "ovr_baseitems_spear.json"), `{ + "overrides": [ + { + "id": 58, + "key": "baseitems:spear", + "label": "spear" + } + ] +}`+"\n") + writeFile(t, filepath.Join(root, "topdata", "data", "baseitems", "modules", "add_baseitems_shortspear.json"), `{ + "entries": { + "baseitems:shortspear": { + "label": "shortspear", + "MaxRange": "255" + } + } +}`+"\n") + mkdirAll(t, filepath.Join(root, "reference")) + writeFile(t, filepath.Join(root, "reference", "build.py"), "print('ok')\n") + + result, err := BuildNative(testProject(root), nil) + if err != nil { + t.Fatalf("BuildNative failed: %v", err) + } + lockRaw, err := os.ReadFile(filepath.Join(root, "topdata", "data", "baseitems", "lock.json")) + if err != nil { + t.Fatalf("read baseitems lock: %v", err) + } + lockText := string(lockRaw) + if !strings.Contains(lockText, `"baseitems:spear": 58`) { + t.Fatalf("expected spear lock entry, got:\n%s", lockText) + } + lockData := map[string]int{} + if err := json.Unmarshal(lockRaw, &lockData); err != nil { + t.Fatalf("parse baseitems lock: %v", err) + } + shortspearID, ok := lockData["baseitems:shortspear"] + if !ok { + t.Fatalf("expected relocated shortspear lock entry to survive, got:\n%s", lockText) + } + if shortspearID == 58 { + t.Fatalf("expected relocated shortspear to use a fresh row id, got lock:\n%s", lockText) + } + + got, err := os.ReadFile(filepath.Join(result.Output2DADir, "baseitems.2da")) + if err != nil { + t.Fatalf("read baseitems.2da: %v", err) + } + text := string(got) + if !strings.Contains(text, "58\tspear") || !strings.Contains(text, fmt.Sprintf("%d\tshortspear", shortspearID)) { + t.Fatalf("expected split spear rows in output, got:\n%s", text) + } +} + func TestBuildSkillsIgnoresStaleBaseSpanLockDuringInitialLoad(t *testing.T) { root := testProjectRoot(t) mkdirAll(t, filepath.Join(root, "topdata", "data", "skills", "modules"))