@Asset(kind, map): a path under each map's directory - ludicc --check looks for it in every map (a @PerMap row's in its own, a game-wide row's in all), unless @Asset(kind, map, optional); the schema marks it scope map; reseeded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 22:13:14 +03:00
parent 9cc67fee74
commit af90a1a334
10 changed files with 79959 additions and 77965 deletions

View file

@ -191,6 +191,7 @@ function sc_attrs(b: Buf, g: int) -> void {
buf_puts(b, ", \"args\": ")
buf_puts(b, g_at_args[j])
if g_at_unres[j] { buf_puts(b, ", \"unresolved\": true") }
if (g_at_name[j] == "Asset") and len(at_arg_names(g_at_args[j])) >= 2 { buf_puts(b, ", \"scope\": \"map\"") }
if (g_at_name[j] == "Ref") and g_at_first[j] != null and pm_named(g_at_first[j]) { buf_puts(b, ", \"scope\": \"map\"") }
buf_puts(b, "}")
j += 1

View file

@ -135,11 +135,21 @@ function at_field(g: int, a: pointer) -> void {
g_at_strs_next = (a == "OneOf") and g_at_allstr
if (a == "Range") and n != 2 { perr("@Range takes the least and the most: @Range(0, 1.5)") }
if (a == "Range") and not g_at_allnum { perr("@Range takes two numbers: @Range(0, 1.5)") }
if ((a == "Unit") or (a == "Asset")) and (n != 1 or toks[pi - 2].kind != TK_STR) { perr(`@{a} takes one string: @{a}("m/s")`) }
if (a == "Unit") and (n != 1 or toks[pi - 2].kind != TK_STR) { perr(`@{a} takes one string: @{a}("m/s")`) }
if (a == "Asset") and not at_asset_ok(args, n) { perr("@Asset takes a kind, and `map` for a path under each map's directory (then `optional`): @Asset(\"png\"), @Asset(\"png\", map), @Asset(\"png\", map, optional)") }
if a == "Unit" { at_unit_check(g_at_arg0, toks[pi - 2]) }
if at_flag(a) and n != 0 { perr(`@{a} takes nothing`) }
at_push(g, a, args, g_at_arg0, n)
}
# @Asset("png"), @Asset("png", map) or @Asset("png", map, optional)
function at_asset_ok(args: pointer, n: int) -> bool {
if n < 1 or n > 3 { return false }
let w = at_arg_names(args)
if len(w) != n { return false }
if n >= 2 and not (w[1] == "map") { return false }
if n == 3 and not (w[2] == "optional") { return false }
return true
}
# a field's group, made the first time it needs one
function at_field_group(f: Node) -> int {
if f.at < 0 { f.at = at_group() }

View file

@ -167,3 +167,18 @@ function at_decl_named(name: pointer) -> bool {
}
return false
}
# a field's @Asset: 0 none or a game path, 1 a path under each map's directory, 2 the same and optional
function at_asset_map(f: Node) -> int {
if f.at < 0 { return 0 }
var j = g_at_start[f.at]
while j < g_at_start[f.at] + g_at_count[f.at] {
if g_at_name[j] == "Asset" {
let w = at_arg_names(g_at_args[j])
if len(w) >= 3 { return 2 }
if len(w) == 2 { return 1 }
return 0
}
j += 1
}
return 0
}

View file

@ -141,6 +141,7 @@ var g_pmk_ref_reg: []pointer = new []pointer # per cross-row @Ref met: the tabl
var g_pmk_ref_key: []pointer = new []pointer
var g_pmk_ref_at: []Node = new []Node
var g_pmk_mod: pointer = "" # the module of the registry being checked
var g_pmk_md: pointer = "" # the map directory being checked
function pmk_read(r: int, path: pointer) -> void {
let src = read_file(path)
@ -186,6 +187,7 @@ function pm_check_maps() -> void {
}
}
function pmk_map(md: pointer) -> void {
g_pmk_md = md
g_pmk_reg = new []int
g_pmk_key = new []pointer
g_pmk_rec = new []Node
@ -217,6 +219,7 @@ function pmk_map(md: pointer) -> void {
e += 1
}
pmk_refs()
pmk_game_assets()
}
# a key written twice in one file
function pmk_dups() -> void {
@ -414,6 +417,57 @@ function pmk_has_dot(t: pointer) -> bool {
return false
}
# ---- map-relative assets: @Asset("png", map) -----------------------------------------------------
function pmk_exists(p: pointer) -> bool {
let fh = file_open(p, "rb")
if fh == null { return false }
file_close(fh)
return true
}
# a value of an @Asset(kind, map) field, at node at: the file is in this map's directory, unless optional
function pmk_asset(at: Node, f: Node, v: pointer) -> void {
let m = at_asset_map(f)
if m != 1 { return }
let p = `{g_pmk_md}/{v}`
if not pmk_exists(p) { pmk_err_at(at, `{f.s} is "{v}", and this map has no {p} - @Asset(..., map) is a path under each map's directory`) }
}
# a game-wide registry's rows whose @Asset(kind, map) field names a file: that file in every map
function pmk_game_assets() -> void {
var r = 0
while r < len(g_rg_name) {
if not pm_is(r) { pmk_game_asset_reg(r) }
r += 1
}
}
function pmk_game_asset_reg(r: int) -> void {
let comp = find_comp(g_rg_type[r])
if comp == null { return }
var d = 0
while d < len(g_df_rec) {
if (g_df_reg[d] == g_rg_name[r]) { pmk_game_asset_row(comp, g_df_rec[d]) }
d += 1
}
}
function pmk_game_asset_row(comp: Node, rec: Node) -> void {
var k = 0
while k < len(rec.kids) {
let fi = rec.kids[k]
if fi.kind == E_FINIT and fi.a != null and fi.a.kind == E_STR and len(fi.a.s) > 0 {
let f = pmk_field_of(comp, fi.s)
if f != null { pmk_asset(fi, f, fi.a.s) }
}
k += 1
}
}
function pmk_field_of(comp: Node, name: pointer) -> Node {
var i = 0
while i < len(comp.kids) {
if comp.kids[i].s != null and (comp.kids[i].s == name) { return comp.kids[i] }
i += 1
}
return null
}
# ---- attributes ---------------------------------------------------------------------------------
function pmk_attrs_str(e: Node, f: Node) -> void {
if f.at < 0 { return }
@ -424,6 +478,7 @@ function pmk_attrs_str(e: Node, f: Node) -> void {
let words = at_arg_names(g_at_args[j])
if not at_word_in(e.s, words) { pmk_err_at(e, `{f.s} is "{e.s}", and it is @OneOf({at_words_text(words)})`) }
}
if (a == "Asset") and len(e.s) > 0 { pmk_asset(e, f, e.s) }
if (a == "Ref") and pm_named(g_at_first[j]) and len(e.s) > 0 {
push(g_pmk_ref_reg, g_at_first[j])
push(g_pmk_ref_key, e.s)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff