diff --git a/README.md b/README.md index 6b99c16..9701a0f 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,18 @@ callback, foot-body-orientation ("tactical twist"), scale-deformation walk cycle, and inheritance-decoupled foot sprites. End-to-end inventory pickup/drop demo: pick up a rock into a backpack container, drop it back into the world. -**Version:** 0.13.0 +**Version:** 0.14.0 **Module-ID:** vagrant-skeleton -**Requires:** lib-core.input >=0.4.0, lib-core.camera >=0.3.0, lib-core.render >=0.1.0, lib-core.maps >=0.1.2, lib-core.puppet >=0.4.4, lib-core.interaction >=0.1.0, lib-core.composition >=0.3.0, lib-core.actor >=0.1.0, lib-core.inventory-list >=0.1.0, lib-core.panel >=0.2.0, lib-core.inventory-list-display >=0.1.0, lib-core.notify >=0.1.0, lib-core.notify-display >=0.1.0, lib-core.world-overlay >=0.1.0, lib-core.crafting >=0.2.0, lib-core.crafting-display >=0.2.0, lib-asset.prototype-subterrain >=0.1.0 +**Requires:** lib-core.input >=0.4.0, lib-core.camera >=0.3.0, lib-core.render >=0.2.0, lib-core.maps >=0.1.2, lib-core.puppet >=0.4.4, lib-core.interaction >=0.1.0, lib-core.composition >=0.3.0, lib-core.actor >=0.1.0, lib-core.inventory-list >=0.1.0, lib-core.panel >=0.2.0, lib-core.inventory-list-display >=0.1.0, lib-core.notify >=0.1.0, lib-core.notify-display >=0.1.0, lib-core.world-overlay >=0.1.0, lib-core.crafting >=0.2.0, lib-core.crafting-display >=0.2.0, lib-asset.prototype-subterrain >=0.2.0, lib-asset.prototype-tc-basics >=0.1.0 **Tags:** test-chamber, puppet, walking-sim, interaction +> **v0.14.0:** Bed + Bench + Backpack visuals migrated from the proprietary +> Subterrain world-atlas to TC_Basics sprites (`single_bed1`, `bench1`, +> `sack1`). Bed + Bench promoted to composition-entities (renderable tag) — +> the hardcoded `draw_sprite_transform` calls for furniture are gone. First +> concrete consumer of ADR-0054 Item-Visual-Identity. The Subterrain dep +> remains for the puppet sprite parts (`subterrain_player` alias). + ## Topology diff --git a/init.lua b/init.lua index ba83d13..cd43e69 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,6 @@ --- sporel-module-vagrant-skeleton v0.13.0 +-- sporel-module-vagrant-skeleton v0.14.0 -- Sprite-mode test-chamber: 13-bone humanoid puppet + sprite-tilemap + --- hardcoded furniture. Subterrain-style puppet control model: +-- TC_Basics furniture (bed/bench/backpack). Subterrain-style puppet control model: -- multi-look-target (aim/soft), slerp body rotation, hip-static legs, -- foot-body-orientation. Sprint (shift+wasd) + interaction (E near -- pickup-item) added v0.4.4. @@ -63,6 +63,17 @@ -- Inventory with layout="right-half"; right-click opens both panels -- simultaneously side-by-side. Replaces the v0.12.0 single-active -- caveat where workbench_inv covered workbench_crafting. +-- +-- v0.14.0: Vagrant migrates Bed + Bench + Backpack visuals off the +-- proprietary Subterrain world-atlas onto TC_Basics sprites +-- (single_bed1, bench1, sack1) — first concrete consumer of the +-- ADR-0054 Item-Visual-Identity weiche. Bed + Bench promoted to +-- composition-templates with `renderable` tag, rendered via the +-- existing draw_entities path; no more hardcoded draw_sprite_transform +-- calls for furniture. lib-asset.prototype-subterrain dep retained +-- for the puppet sprite atlas (subterrain_player); the world-atlas +-- (subterrain_world) drop closes the last furniture dependency on +-- the Subterrain world-tiles. local input = require("lib-core.input") local camera = require("lib-core.camera") @@ -89,19 +100,30 @@ local WALK_SPEED_SPRINT = 240 -- 2x local ANIM_SPEED_NORMAL = 0.45 local ANIM_SPEED_SPRINT = 0.9 -- 2x --- Furniture positions (Bed/Bench remain hardcoded for v0.1; Backpack --- promoted to Inactive-Entity via composition. Bed/Bench have no --- interaction-trigger and no demo-pflicht to be entities — they stay --- atlas-furniture until a container-on-bed feature or similar triggers). +-- World-furniture positions (all three now composition-entities with +-- renderable tag; rendered via draw_entities). Positions are visual +-- CENTER per lib-core.render v0.2.0 anchor convention. local BACKPACK_X, BACKPACK_Y = 350, 400 +local BED_X, BED_Y = 200, 150 +local BENCH_X, BENCH_Y = 400, 300 local INTERACTION_RANGE = 40 -- pixels -- World-overlay float colors (pickup confirmation) local COLOR_PICKUP_FLOAT = 0xFFFF80FF +-- Project canonical pixels-per-meter, anchored to the puppet shoulder +-- width (64 px = 0.5 m per ~/sporel_tile_scale.md). All sprite atlases +-- declare their own `atlas_meta.pixels_per_meter` in their sprite-mode +-- JSON output (atlas-baker v0.4.0+); the per-entity scale-factor is +-- derived as PROJECT_PIXELS_PER_METER / atlas_ppm at template-load time +-- and applied via lib-core.render v0.3.0's `sprite_scale` property. +-- Atlases authored at canonical ppm get scale 1.0 → no-op. +local PROJECT_PIXELS_PER_METER = 128 + -- Backpack-Template (Phase A.4 — Atlas-UV render-path). -- v0.8.0: container={kind="list"} declared so the backpack receives -- items via inventory.add (capability-by-declaration). +-- v0.14.0: sprite source migrated to TC_Basics `sack1`. -- Real sprite_atlas + sprite_uv values are filled in M.init after the -- atlas-JSON is parsed; defaults here are placeholders so the inert- -- declaration types are correct (string + numbers). @@ -110,12 +132,41 @@ composition.define_template{ properties = { sprite_atlas = "", sprite_uv = {x = 0, y = 0, w = 1, h = 1}, + sprite_scale = 1.0, position = {x = 0, y = 0}, }, tags = {"renderable"}, container = { kind = "list" }, -- receives items via inventory.add } +-- Bed + Bench-Templates (v0.14.0 — ADR-0054 first-consumer slice). +-- Static world-furniture, no interaction-trigger, no container-block: +-- just sprite + position + `renderable` tag so draw_entities renders +-- them via the Atlas-UV path. Migrates off the proprietary Subterrain +-- world-atlas onto TC_Basics `single_bed1` + `bench1`. Real +-- sprite_atlas + sprite_uv values are filled in M.init after the +-- TC_Basics sprite-mode atlas JSON is parsed. +composition.define_template{ + id = "bed", + properties = { + sprite_atlas = "", + sprite_uv = {x = 0, y = 0, w = 1, h = 1}, + sprite_scale = 1.0, + position = {x = 0, y = 0}, + }, + tags = {"renderable"}, +} +composition.define_template{ + id = "bench", + properties = { + sprite_atlas = "", + sprite_uv = {x = 0, y = 0, w = 1, h = 1}, + sprite_scale = 1.0, + position = {x = 0, y = 0}, + }, + tags = {"renderable"}, +} + -- Rock-Template: an item sitting in the world that the player can pick up. -- sprite_atlas + sprite_uv are filled in M.init after the blob_rect_stone atlas -- is loaded; placeholder defaults here keep the inert-declaration types correct. @@ -205,9 +256,9 @@ local state = { player = nil, -- puppet handle (state-bearing for animation) actor = nil, -- composition-entity owning position map = nil, - world_tex = nil, -- subterrain_world atlas diffuse handle (used for bed/bench) - furniture_uvs = nil, -- name -> { x, y, w, h } from atlas JSON backpack = nil, -- composition-entity, created in M.init after atlas load + bed = nil, -- composition-entity (v0.14.0 — TC_Basics single_bed1) + bench = nil, -- composition-entity (v0.14.0 — TC_Basics bench1) rock = nil, -- composition-entity (item), created in M.init after blob_rect_stone load stick = nil, -- composition-entity (item), placeholder for crafting demo workbench = nil, -- composition-entity (container), Phase D Workbench @@ -491,34 +542,61 @@ function M.init(ctx) puppet.write_bone(handle, "foot_r", { rot = foot_local_deg }) end) - -- Furniture atlas (subterrain_world): load JSON + diffuse texture once. - local world_lib = aliases.subterrain_world - local world_base = world_lib .. "/assets/atlases/subterrain_world" - local world_atlas_png = world_base .. "/tiles.diffuse.atlas.png" - local world_meta = engine.asset.load_json(world_base .. "/tiles.atlas.json") - state.world_tex = engine.asset.load_texture(world_atlas_png) - state.furniture_uvs = {} - for _, t in ipairs(world_meta.tiles) do - state.furniture_uvs[t.name] = { x = t.uv[1], y = t.uv[2], w = t.uv[3], h = t.uv[4] } - end + -- Furniture atlas (TC_Basics sprite-mode): load JSON + diffuse atlas. + -- atlas-baker v0.4.0 sprite-mode output layout — sprites table is + -- a map of alias → {x, y, w, h, source_file}; rendering reads + -- x/y/w/h via render.draw_entities' Atlas-UV path. atlas_meta. + -- pixels_per_meter declares the atlas's native resolution; scale + -- against the project canonical to get the per-entity sprite_scale. + local tc_lib = aliases.tc_basics + local tc_base = tc_lib .. "/assets/atlases/tcbasics" + local tc_atlas_png = tc_base .. "/sprites.diffuse.atlas.png" + local tc_meta = engine.asset.load_json(tc_base .. "/sprites.uv.json") + local tc_sprites = tc_meta.sprites + local tc_atlas_ppm = (tc_meta.atlas_meta + and tc_meta.atlas_meta.pixels_per_meter) + or PROJECT_PIXELS_PER_METER + local tc_scale = PROJECT_PIXELS_PER_METER / tc_atlas_ppm - -- Spawn Backpack-Entity with real Atlas-UV values now that the - -- atlas-json is parsed. lib-core.render's atlas-cache will load - -- the texture lazy-on-first-draw (separate from state.world_tex - -- which serves bed/bench's direct draw_sprite_transform call; - -- ineffizient aber kein-Schaden, single-texture-handle-merge ist - -- ein späterer Slice). - local backpack_uv = state.furniture_uvs["backpack_001"] + -- Spawn Backpack-Entity with TC_Basics `sack1` sprite. lib-core.render's + -- atlas-cache lazy-loads the texture on first draw_entities call. + local sack_uv = tc_sprites.sack1 state.backpack = composition.create{ template = "backpack", properties = { - sprite_atlas = world_atlas_png, - sprite_uv = {x = backpack_uv.x, y = backpack_uv.y, - w = backpack_uv.w, h = backpack_uv.h}, + sprite_atlas = tc_atlas_png, + sprite_uv = {x = sack_uv.x, y = sack_uv.y, + w = sack_uv.w, h = sack_uv.h}, + sprite_scale = tc_scale, position = {x = BACKPACK_X, y = BACKPACK_Y}, }, } + -- Spawn Bed + Bench entities (v0.14.0). Static world-furniture + -- rendered via the same Atlas-UV path as the backpack. + local bed_uv = tc_sprites.single_bed1 + state.bed = composition.create{ + template = "bed", + properties = { + sprite_atlas = tc_atlas_png, + sprite_uv = {x = bed_uv.x, y = bed_uv.y, + w = bed_uv.w, h = bed_uv.h}, + sprite_scale = tc_scale, + position = {x = BED_X, y = BED_Y}, + }, + } + local bench_uv = tc_sprites.bench1 + state.bench = composition.create{ + template = "bench", + properties = { + sprite_atlas = tc_atlas_png, + sprite_uv = {x = bench_uv.x, y = bench_uv.y, + w = bench_uv.w, h = bench_uv.h}, + sprite_scale = tc_scale, + position = {x = BENCH_X, y = BENCH_Y}, + }, + } + -- Blob-rect-stone atlas: load JSON + diffuse texture for the rock sprite. -- blob_rect_stone alias resolves to lib-asset.prototype-blob-geom. The -- "slot_00_isolated" tile is the standalone isolated stone shape — reads as @@ -534,15 +612,15 @@ function M.init(ctx) end -- Crafted-output templates: defined here (not top-level) so their - -- sprite_atlas defaults can carry the resolved blob_rect_stone PNG path. + -- sprite_atlas defaults can carry the resolved atlas PNG path. -- crafting.craft calls composition.create with no overrides, so the -- template defaults are the only carrier for sprite_atlas / sprite_uv -- on the crafted instance. Without this, crafted items render as a -- "?" placeholder in the inventory and a magenta-fallback square on -- drop (lib-core.render path-3). - -- slot_05_tee_open reads as a tool-shaped tile (rock_pick). - -- slot_13_solid reads as a dense cluster tile (rock_pile). - local rock_pick_uv = stone_uvs["slot_05_tee_open"] + -- v0.14.0: rock_pick visual switched to TC_Basics `spade`; rock_pile + -- stays on blob_rect_stone (no TC_Basics rock-stack equivalent). + local spade_uv = tc_sprites.spade composition.define_template{ id = "rock_pick", properties = { @@ -553,9 +631,10 @@ function M.init(ctx) volume = 0.0015, ["composition.stone"] = 0.9, ["composition.wood"] = 0.1, - sprite_atlas = stone_atlas_png, - sprite_uv = {x = rock_pick_uv.x, y = rock_pick_uv.y, - w = rock_pick_uv.w, h = rock_pick_uv.h}, + sprite_atlas = tc_atlas_png, + sprite_uv = {x = spade_uv.x, y = spade_uv.y, + w = spade_uv.w, h = spade_uv.h}, + sprite_scale = tc_scale, position = {x = 0, y = 0}, }, tags = {"renderable", "item"}, @@ -627,14 +706,18 @@ function M.init(ctx) -- on the same Y as rock + backpack). Placeholder visual: slot_04_straight -- from blob_rect_stone reads as a long horizontal bar (Phase C-Q10 atlas -- re-use; echtes Stick-Sprite folgt mit atlas-baker-decals). + -- v0.14.0: stick visual switched to TC_Basics `log_pile1`. Sprite_scale + -- via property-override since the stick template default (1.0) was set + -- before tc_scale was known. local STICK_X, STICK_Y = BACKPACK_X - 160, BACKPACK_Y - local stick_uv = stone_uvs["slot_04_straight"] + local log_pile_uv = tc_sprites.log_pile1 state.stick = composition.create{ template = "stick", properties = { - sprite_atlas = stone_atlas_png, - sprite_uv = {x = stick_uv.x, y = stick_uv.y, - w = stick_uv.w, h = stick_uv.h}, + sprite_atlas = tc_atlas_png, + sprite_uv = {x = log_pile_uv.x, y = log_pile_uv.y, + w = log_pile_uv.w, h = log_pile_uv.h}, + sprite_scale = tc_scale, position = {x = STICK_X, y = STICK_Y}, }, } @@ -674,6 +757,23 @@ function M.init(ctx) description = "Stack rocks for storage.", } + -- v0.14.0: per-recipe icon resolver for the Crafting + Workbench-Crafting + -- widgets. Maps recipe.output.template → {atlas, uv} for the icon + -- column added in crafting-display v0.3.0. composition has no + -- template-property-getter today (ADR-0001 keeps the read-side + -- domain-free), so the module-side that DEFINES the templates is the + -- canonical place to know each output's sprite. + local craft_icons = { + rock_pick = { atlas = tc_atlas_png, uv = tc_sprites.spade }, + rock_pile = { atlas = stone_atlas_png, uv = stone_uvs.slot_13_solid }, + } + local function craft_icon_for(recipe) + if recipe and recipe.output and recipe.output.template then + return craft_icons[recipe.output.template] + end + return nil + end + -- Inventory-UI Setup panel.set_theme{ panel_width_frac = 0.4, font_size_title = 20 } @@ -737,6 +837,7 @@ function M.init(ctx) ctx_factory = function() return { actor = state.actor } end, + icon_resolver = craft_icon_for, }) -- Craft action: invokes crafting.craft on the backpack, posts a @@ -818,6 +919,7 @@ function M.init(ctx) ctx_factory = function() return { actor = state.actor, at_workbench = true } end, + icon_resolver = craft_icon_for, }) -- Craft action: invokes crafting.craft on the resolved locale (multi- @@ -1148,19 +1250,9 @@ end function M.draw(ctx) camera.begin() maps.draw_map_pre_entities() - -- Bed + Bench remain hardcoded furniture (no interaction-trigger → - -- no entity-Pflicht in Phase A; see Backpack-Template comment). - local bed_uv = state.furniture_uvs["bed_001"] - local bench_uv = state.furniture_uvs["bench_001"] - engine.render.draw_sprite_transform(state.world_tex, 200, 150, 0, 1, 1, - bed_uv.w / 2, bed_uv.h / 2, 0xFFFFFFFF, - bed_uv.x, bed_uv.y, bed_uv.w, bed_uv.h) - engine.render.draw_sprite_transform(state.world_tex, 400, 300, 0, 1, 1, - bench_uv.w / 2, bench_uv.h / 2, 0xFFFFFFFF, - bench_uv.x, bench_uv.y, bench_uv.w, bench_uv.h) - -- Backpack (and any other renderable entity) via Phase-A.2 tag-based - -- render. The atlas-cache loads tiles.diffuse.atlas.png lazy-on-first - -- call (separate handle from state.world_tex — see M.init note). + -- All renderable furniture + items via Phase-A.2 tag-based render + -- (v0.14.0: Bed + Bench joined Backpack as composition-entities). + -- lib-core.render's atlas-cache lazy-loads each sprite_atlas path. r_lib.draw_entities{ tag = "renderable" } -- Puppet (sprite-mode via texture_handles populated by load_rig+load_textures). puppet.render(state.player) diff --git a/manifest.module b/manifest.module index 426792f..1ca6b71 100644 --- a/manifest.module +++ b/manifest.module @@ -1,24 +1,24 @@ { "id": "vagrant-skeleton", - "version": "0.13.0", + "version": "0.14.0", "kind": "module", "api": "^0.1", "ci_frames": 60, "entry_point": "init.lua", "asset_aliases": { "subterrain_player": "lib-asset.prototype-subterrain", - "subterrain_world": "lib-asset.prototype-subterrain", "blob_testbench": "lib-asset.prototype-blob-geom", - "blob_rect_stone": "lib-asset.prototype-blob-geom" + "blob_rect_stone": "lib-asset.prototype-blob-geom", + "tc_basics": "lib-asset.prototype-tc-basics" }, "deps": [ {"id": "lib-core.input", "version": "0.4.0"}, {"id": "lib-core.camera", "version": "0.3.0"}, - {"id": "lib-core.render", "version": "0.2.0"}, + {"id": "lib-core.render", "version": "0.3.0"}, {"id": "lib-core.composition", "version": "0.3.0"}, {"id": "lib-core.actor", "version": "0.1.0"}, {"id": "lib-core.panel", "version": "0.4.0"}, - {"id": "lib-core.inventory-list-display", "version": "0.1.1"}, + {"id": "lib-core.inventory-list-display", "version": "0.2.0"}, {"id": "lib-core.notify", "version": "0.1.0"}, {"id": "lib-core.notify-display", "version": "0.1.0"}, {"id": "lib-core.world-overlay", "version": "0.1.0"}, @@ -27,8 +27,9 @@ {"id": "lib-core.interaction", "version": "0.1.0"}, {"id": "lib-core.inventory-list", "version": "0.1.0"}, {"id": "lib-core.crafting", "version": "0.2.0"}, - {"id": "lib-core.crafting-display", "version": "0.2.0"}, + {"id": "lib-core.crafting-display", "version": "0.3.0"}, {"id": "lib-asset.prototype-subterrain", "version": "0.2.0"}, - {"id": "lib-asset.prototype-blob-geom", "version": "0.1.0"} + {"id": "lib-asset.prototype-blob-geom", "version": "0.1.0"}, + {"id": "lib-asset.prototype-tc-basics", "version": "0.3.0"} ] }