Fix alignment/ids output

This commit is contained in:
2026-05-31 08:17:41 +02:00
parent 7eeb32927d
commit 67c03fa6bd
3 changed files with 88 additions and 0 deletions
+5
View File
@@ -153,6 +153,11 @@ and column. Set `dataset: "*"` to apply an encoding to the same column name in
any native topdata dataset; an exact dataset rule takes precedence over the
wildcard rule for that column. Supported compact encodings include packed hex
lists, external hex-list sidecar tables, alignment set masks, and ID hex lists.
Explicit `{"alignments": [...]}` and `{"ids": [...]}` objects are portable in
any native topdata dataset even without a column-specific rule; configured
rules still override when a column needs custom bounds, widths, or alignment
encoding mode. Plain `{"list": [...]}` objects remain config-driven because
numeric lists need table-specific output semantics.
Class spell availability can be authored as JSON spellbooks under
`topdata/data/classes/spellbooks/`. Each file names a `classes:*` row, and the
+32
View File
@@ -4252,6 +4252,20 @@ func (r *valueResolver) resolveValue(row map[string]any, field string, value any
return tlkCompiledValue{Value: value}, nil
}
}
if _, hasAlignments := typed["alignments"]; hasAlignments {
encoded, err := encodeConfiguredAlignmentSetMask(typed, defaultExplicitAlignmentSetMaskEncoding())
if err != nil {
return tlkCompiledValue{}, fmt.Errorf("field %s: %w", field, err)
}
return tlkCompiledValue{Value: encoded}, nil
}
if _, hasIDs := typed["ids"]; hasIDs {
value, err := r.encodeConfiguredIDHexList(typed, defaultExplicitIDHexListEncoding())
if err != nil {
return tlkCompiledValue{}, fmt.Errorf("field %s: %w", field, err)
}
return tlkCompiledValue{Value: value}, nil
}
if refID, ok := typed["id"]; ok {
if refKey, ok := refID.(string); ok && strings.Contains(refKey, ":") {
id, ok := r.keyToID[refKey]
@@ -4413,6 +4427,24 @@ func encodeConfiguredAlignmentSetMask(raw any, encoding project.TopDataValueEnco
return "0x" + fmt.Sprintf("%0*X", encoding.HexWidth, mask), nil
}
func defaultExplicitAlignmentSetMaskEncoding() project.TopDataValueEncodingConfig {
return project.TopDataValueEncodingConfig{
Mode: "alignment_set_mask",
Min: 0,
Max: 511,
HexWidth: 3,
}
}
func defaultExplicitIDHexListEncoding() project.TopDataValueEncodingConfig {
return project.TopDataValueEncodingConfig{
Mode: "id_hex_list",
Min: 0,
Max: 65535,
HexWidth: 4,
}
}
func (r *valueResolver) encodeConfiguredIDHexList(obj map[string]any, encoding project.TopDataValueEncodingConfig) (string, error) {
mode := strings.TrimSpace(encoding.Mode)
if mode != "id_hex_list" {
+51
View File
@@ -586,6 +586,57 @@ func TestBuildNativeAppliesWildcardConfiguredValueEncodings(t *testing.T) {
}
}
func TestBuildNativeEncodesExplicitAlignmentAndIDListsInAnyDataset(t *testing.T) {
root := testProjectRoot(t)
mkdirAll(t, filepath.Join(root, "topdata", "data", "feat"))
mkdirAll(t, filepath.Join(root, "topdata", "data", "futuretable", "modules"))
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
writeFile(t, filepath.Join(root, "topdata", "data", "feat", "base.json"), `{
"output": "feat.2da",
"columns": ["LABEL"],
"rows": [
{"id": 2, "key": "feat:first_future", "LABEL": "FirstFuture"},
{"id": 17, "key": "feat:second_future", "LABEL": "SecondFuture"}
]
}`+"\n")
writeFile(t, filepath.Join(root, "topdata", "data", "feat", "lock.json"), `{
"feat:first_future": 2,
"feat:second_future": 17
}`+"\n")
writeFile(t, filepath.Join(root, "topdata", "data", "futuretable", "base.json"), `{
"output": "futuretable.2da",
"columns": ["Label", "AnyAlignmentMask", "AnyIDList"],
"rows": [
{"id": 0, "key": "futuretable:first", "Label": "First"}
]
}`+"\n")
writeFile(t, filepath.Join(root, "topdata", "data", "futuretable", "lock.json"), `{"futuretable:first":0}`+"\n")
writeFile(t, filepath.Join(root, "topdata", "data", "futuretable", "modules", "10_first.json"), `{
"overrides": [
{
"key": "futuretable:first",
"AnyAlignmentMask": {"alignments": ["lg", "tn", "ce"]},
"AnyIDList": {"ids": ["feat:first_future", {"id": "feat:second_future"}]}
}
]
}`+"\n")
proj := testProject(root)
result, err := BuildNativeWithOptions(proj, NativeBuildOptions{BuildWiki: false}, nil)
if err != nil {
t.Fatalf("BuildNativeWithOptions failed: %v", err)
}
raw, err := os.ReadFile(filepath.Join(result.Output2DADir, "futuretable.2da"))
if err != nil {
t.Fatalf("read futuretable.2da: %v", err)
}
got := string(raw)
if !strings.Contains(got, "0\tFirst\t0x111\t0x00020011\n") {
t.Fatalf("expected generic explicit list encodings in futuretable.2da, got:\n%s", got)
}
}
func TestBuildNativeAppliesConfiguredValueDefaults(t *testing.T) {
root := testProjectRoot(t)
mkdirAll(t, filepath.Join(root, "topdata", "data", "racialtypes", "core", "modules"))