From c976353637bad669b9ac92a600e1cdb2ab954d53 Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Tue, 9 Jun 2026 13:51:02 +0000 Subject: [PATCH] feat(0.6.0): Backpack as composition-Entity (Phase A.4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor: Backpack ist composition-Entity mit Atlas-UV-Render-Pfad. Template at top-level declares sprite_atlas + sprite_uv.{x,y,w,h} + position. create() in M.init nach atlas-load setzt reale UV-Werte aus state.furniture_uvs["backpack_001"]. render.draw_entities{tag="renderable"} ersetzt den direkten draw_sprite_transform-Call für Backpack. interaction.register liest position aus entity-properties. Bed + Bench bleiben hardcoded Atlas-Furniture — kein Interaction-Trigger, kein Demo-Pflicht zur Entity-Promotion in Phase A. Phase B (container-on-bed) oder Phase D wäre der Trigger. Atlas-Texture wird doppelt geladen (state.world_tex für bed/bench, render-internal-cache für backpack) — ineffizient aber nicht kaputt; single-handle-merge ist späterer Slice. Bumps: render 0.1.0 → 0.2.0; +composition v0.1.0; module 0.4.4 → 0.6.0. Co-Authored-By: Claude Opus 4.7 (1M context) --- init.lua | 85 ++++++++++++++++++++++++++++++++++++++----------- manifest.module | 5 +-- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/init.lua b/init.lua index a126442..8592e06 100644 --- a/init.lua +++ b/init.lua @@ -1,9 +1,14 @@ --- sporel-module-vagrant-skeleton v0.5.0 +-- sporel-module-vagrant-skeleton v0.6.0 -- Sprite-mode test-chamber: 13-bone humanoid puppet + sprite-tilemap + -- hardcoded furniture. 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 -- backpack) added v0.4.4. +-- +-- v0.6.0 (Phase A.4): Backpack promoted to composition-Entity (Atlas-UV +-- render-path via render.draw_entities{tag="renderable"}). Bed + Bench +-- remain hardcoded atlas-furniture in v0.6.0 (no interaction-trigger, +-- no demo-pflicht to be entities yet). local input = require("lib-core.input") local camera = require("lib-core.camera") @@ -11,6 +16,7 @@ local r_lib = require("lib-core.render") local maps = require("lib-core.maps") local puppet = require("lib-core.puppet") local interaction = require("lib-core.interaction") +local composition = require("lib-core.composition") local M = {} @@ -20,15 +26,33 @@ local WALK_SPEED_SPRINT = 240 -- 2x local ANIM_SPEED_NORMAL = 0.45 local ANIM_SPEED_SPRINT = 0.9 -- 2x --- Furniture positions (matches M.draw hardcoded sprites). +-- 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 Phase B (container-on-bed) or similar triggers). local BACKPACK_X, BACKPACK_Y = 350, 400 local INTERACTION_RANGE = 40 -- pixels +-- Backpack-Template (Phase A.4 — Atlas-UV render-path). +-- 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). +composition.define_template{ + id = "backpack", + properties = { + sprite_atlas = "", + sprite_uv = {x = 0, y = 0, w = 1, h = 1}, + position = {x = 0, y = 0}, + }, + tags = {"renderable"}, +} + local state = { player = nil, map = nil, - world_tex = nil, -- subterrain_world atlas diffuse handle + 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 -- Subterrain-style controller state: walk_anim_current = nil, -- "walk_fwd_lower" / "walk_back_lower" / etc. (nil = idle_lower) lower_control_rot_deg = 0, @@ -191,13 +215,31 @@ function M.init(ctx) -- 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_base .. "/tiles.diffuse.atlas.png") + 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 + -- 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"] + 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}, + position = {x = BACKPACK_X, y = BACKPACK_Y}, + }, + } + -- Input bindings. input.bind("walk_left", { "a" }) input.bind("walk_right", { "d" }) @@ -207,13 +249,18 @@ function M.init(ctx) input.bind("interact", { "e" }) -- fires nearest proximity-trigger input.bind("quit_to_launcher", { "escape" }) - -- Proximity trigger on the backpack sprite (drawn at BACKPACK_X/Y in M.draw). - -- Range 40px; press E while within that radius to fire the callback. - interaction.register(BACKPACK_X, BACKPACK_Y, INTERACTION_RANGE, "interact", function(ctx) - engine.print(string.format( - "vagrant: interact with backpack (distance=%.1f)", - ctx.distance)) - end) + -- Proximity trigger on the backpack-Entity. Anchor read from the + -- entity's position-Properties; sign is stationary so a snapshot is + -- correct (interaction-API takes raw numbers, not closures, in v0.1). + interaction.register( + state.backpack:get_property("position.x"), + state.backpack:get_property("position.y"), + INTERACTION_RANGE, "interact", + function(ctx) + engine.print(string.format( + "vagrant: interact with backpack (distance=%.1f)", + ctx.distance)) + end) -- CI trace: assets loaded count (3 atlas textures: player + tiles + world). if CI_FRAME_PERF and not ci_assets_traced then @@ -321,20 +368,20 @@ end function M.draw(ctx) camera.begin() maps.draw_map_pre_entities() - -- Hardcoded furniture placements (test-chamber spirit). - -- Render via source-rect primitive using subterrain_world atlas UVs. - local bed_uv = state.furniture_uvs["bed_001"] - local bench_uv = state.furniture_uvs["bench_001"] - local backpack_uv = state.furniture_uvs["backpack_001"] + -- 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) - engine.render.draw_sprite_transform(state.world_tex, 350, 400, 0, 1, 1, - backpack_uv.w / 2, backpack_uv.h / 2, 0xFFFFFFFF, - backpack_uv.x, backpack_uv.y, backpack_uv.w, backpack_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). + r_lib.draw_entities{ tag = "renderable" } -- Puppet (sprite-mode via texture_handles populated by load_rig+load_textures). puppet.render(state.player) maps.draw_map_post_entities() diff --git a/manifest.module b/manifest.module index bb7acf0..308cade 100644 --- a/manifest.module +++ b/manifest.module @@ -1,6 +1,6 @@ { "id": "vagrant-skeleton", - "version": "0.4.4", + "version": "0.6.0", "kind": "module", "api": "^0.1", "ci_frames": 60, @@ -14,7 +14,8 @@ "deps": [ {"id": "lib-core.input", "version": "0.4.0"}, {"id": "lib-core.camera", "version": "0.3.0"}, - {"id": "lib-core.render", "version": "0.1.0"}, + {"id": "lib-core.render", "version": "0.2.0"}, + {"id": "lib-core.composition", "version": "0.1.0"}, {"id": "lib-core.maps","version":"0.5.7"}, {"id": "lib-core.puppet", "version": "0.5.0"}, {"id": "lib-core.interaction", "version": "0.1.0"},