Compare commits

..

12 Commits

Author SHA1 Message Date
Axel Meyer
05e50a85ff feat: ship manifest.launcher.md for master-detail launcher 2026-06-11 07:57:35 +02:00
Axel Meyer
0227aad47d feat(0.3.0): Player als Actor-Entity (Phase A.6)
Refactor spine-prototype für player_control v0.2.0 + actor:
- composition.define_template{id="player", properties={position,
  movement_speed, sprite_color, sprite_w, sprite_h}, tags={"renderable"}}
- actor.create instanziiert Player bei map-center
- player_control.update(player, dt) ersetzt player.update(dt)
- camera.set_target / interaction.update lesen via actor.position(player)
  (Lua-multi-return)
- Manueller draw_rect für Player raus — Player wird via
  render.draw_entities{tag="renderable"} zusammen mit Sign gezeichnet
- Draw-order: stones zuerst, dann Entities (Sign + Player). Stones-vs-
  Player-Overlay-Issue in der Praxis nicht relevant (Positionen kollidieren
  nicht)

PLAYER_R/G/B Konstanten entfernt (Color jetzt im Template).

Bumps: 0.2.0 → 0.3.0; +actor v0.1.0; player_control 0.1.0 → 0.2.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-09 14:13:17 +00:00
Axel Meyer
fcf8f80b1e feat(0.2.0): Sign as composition-Entity (Phase A.3)
Refactor spine-prototype to use lib-core.composition for the Sign:
template defines sprite_color + sprite_w + sprite_h + text + position;
create instantiates with per-instance position + text override.
Render switches from direct draw_rect to render.draw_entities{tag=
"renderable"}; interaction.register reads position from entity-properties.

Direct hardcoded `local sign = {x, y, text}` Lua-local + SIGN_R/G/B
constants entfernt. Sign demo-behavior identisch (12×12 yellow rect at
tile-center (4,4) = (144,144); E within 40px → print text).

Bumps: render dep 0.1.0 → 0.2.0; +composition v0.1.0; module 0.1.0 →
0.2.0.

Stones + Player bleiben Lua-locals (A.5/A.6 territory).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-09 13:45:27 +00:00
Axel Meyer
d55b9fe4d9 chore(deps): bump lib-core.maps 0.5.6 -> 0.5.7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 22:44:25 +02:00
Axel Meyer
7781f8a089 chore(deps): bump lib-core.maps 0.5.5 -> 0.5.6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 17:19:15 +02:00
Axel Meyer
c49f34efac ESC -> lib-management.launcher (renamed per ADR-0045)
The launcher migrated from lib-sporel.launcher to lib-management.launcher
per ADR-0045 (three-tier library naming). Update the ESC handler to
target the new module id so quit-to-launcher continues to work.
2026-05-31 00:22:25 +02:00
calic
f00b8ba76a chore(deps): bump lib-core.maps 0.5.4 -> 0.5.5 2026-05-29 09:56:51 +02:00
calic
5f39b43554 chore(deps): bump lib-core.maps 0.5.3 -> 0.5.4 2026-05-29 01:37:43 +02:00
calic
025ddf3687 chore(deps): bump lib-core.maps 0.5.2 -> 0.5.3 2026-05-29 01:29:23 +02:00
calic
7e0acd9256 chore(deps): bump lib-core.maps 0.4.0 -> 0.5.2 2026-05-28 23:53:21 +02:00
Axel Meyer
037b3ad2c9 Bump lib-core.maps dep to 0.4.0
Picks up the new write APIs (set_cell_gid, set_roof, save_to_disk).
No source changes — lib is purely additive.
2026-05-24 00:53:42 +02:00
Axel Meyer
782387473e Switch spine-prototype demo to fa_terrain_v1 atlas
Replaces the inline color-only demo_tilemap with a dep on the
prototype-fa-starter asset lib and its baked fa_terrain_v1 atlas.
The map atlases[] alias-string is updated to fa_terrain_v1; the
packed-u32 layer-tiles keep their numeric values because the baker
assigned the expected IDs (1 for grass_field surface tile, 2 for
stone_wall_brick wall tile). Adds maps.load_textures() call in init.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 23:16:39 +02:00
5 changed files with 121 additions and 54 deletions

View File

@@ -1,8 +0,0 @@
{
"id": "demo_tilemap",
"tile_size": 32,
"tiles": [
{"id": "grass", "walkable": true, "color": [110, 170, 80]},
{"id": "stone", "walkable": false, "color": [120, 120, 120]}
]
}

120
init.lua
View File

@@ -1,14 +1,17 @@
local maps = require("lib-core.maps")
local r_lib = require("lib-core.render")
local camera = require("lib-core.camera")
local input = require("lib-core.input")
local player = require("lib-core.player_control")
local interaction = require("lib-core.interaction")
local selection = require("lib-core.selection")
local command = require("lib-core.command")
local maps = require("lib-core.maps")
local r_lib = require("lib-core.render")
local camera = require("lib-core.camera")
local input = require("lib-core.input")
local player_control = require("lib-core.player_control")
local interaction = require("lib-core.interaction")
local selection = require("lib-core.selection")
local command = require("lib-core.command")
local composition = require("lib-core.composition")
local actor = require("lib-core.actor")
local demo_map = maps.load("maps/demo.map.json")
maps.set_current(demo_map)
maps.load_textures(engine.module.asset_aliases())
local m_size = maps.size()
local t_size = maps.tile_size()
@@ -55,17 +58,52 @@ camera.enable_pan_edge()
-- unsichtbar in follow-mode wegen set_target-override jeden Frame).
camera.set_pan_enabled("keys", false)
-- Configure player: defaults are 24x24 + 200 px/sec; place at map-center
local p_size = player.size()
player.set_position(map_center_x - p_size.w / 2, map_center_y - p_size.h / 2)
player.bind_movement("move_left", "move_right", "move_up", "move_down")
-- Player-Template (Phase A.5/A.6 — Actor + composition).
-- position is the visual CENTER per render.draw_entities convention.
composition.define_template{
id = "player",
properties = {
position = {x = 0, y = 0},
movement_speed = 200,
sprite_color = engine.render.rgb(200, 60, 80),
sprite_w = 24,
sprite_h = 24,
},
tags = {"renderable"},
}
local PLAYER_R, PLAYER_G, PLAYER_B = 200, 60, 80
-- Spawn the player at map-center; bind WASD movement.
local player = actor.create{
template = "player",
properties = {
position = {x = map_center_x, y = map_center_y},
movement_speed = 200,
},
}
player_control.bind_movement("move_left", "move_right", "move_up", "move_down")
-- Sign as module-local (actor deferred — see actor-deferral spec)
-- Tile (4,4) center: 4*32+16 = 144 px in walkable grass-area
local sign = { x = 144, y = 144, text = "Hier steht: Willkommen im Spine." }
local SIGN_R, SIGN_G, SIGN_B = 220, 200, 60
-- Sign-Template (Phase A.1 — composition + Inactive-Entity-Pattern).
-- Position-Property is the visual CENTER per render.draw_entities convention.
composition.define_template{
id = "sign",
properties = {
sprite_color = engine.render.rgb(220, 200, 60),
sprite_w = 12,
sprite_h = 12,
text = "default sign text",
position = {x = 0, y = 0},
},
tags = {"renderable"},
}
-- Instantiate the demo sign at tile (4,4)-center = 144 px in grass-area.
local sign = composition.create{
template = "sign",
properties = {
position = {x = 144, y = 144},
text = "Hier steht: Willkommen im Spine.",
},
}
-- P.3.6/P.3.7: 6 stones as selectables + movable units (3 speed classes).
local stones = {
@@ -96,14 +134,22 @@ function init(ctx)
local t = camera.target()
engine.print(string.format("camera: target=(%d,%d) zoom=%.2f", t.x, t.y, camera.zoom()))
engine.print(string.format("input: %d actions bound", input.action_count()))
local p = player.position()
local s = player.size()
local px, py = actor.position(player)
local sw = player:get_property("sprite_w")
local sh = player:get_property("sprite_h")
engine.print(string.format("player: pos=(%d,%d) size=%dx%d speed=%d",
p.x, p.y, s.w, s.h, player.speed()))
px, py, sw, sh, actor.movement_speed(player)))
interaction.register(sign.x, sign.y, 40, "interact", function(info)
engine.print(sign.text)
end)
-- Position + text are read once at registration; the sign is stationary
-- in v0.1 so a snapshot is correct. If it ever moves, the interaction-
-- API would need closure-form anchor (currently raw-number-only).
interaction.register(
sign:get_property("position.x"),
sign:get_property("position.y"),
40, "interact",
function(info)
engine.print(sign:get_property("text"))
end)
engine.print(string.format("interaction: %d trigger(s) registered",
interaction.trigger_count()))
-- P.3.6/P.3.7: Register each stone as selectable AND movable.
@@ -127,19 +173,17 @@ function render(ctx)
engine.render.clear_color(engine.render.rgb(20, 20, 30))
camera.begin()
maps.draw_map_pre_entities()
-- draw sign on top of map
engine.render.draw_rect(sign.x - 6, sign.y - 6, 12, 12,
engine.render.rgb(SIGN_R, SIGN_G, SIGN_B))
-- P.3.6: draw stones
-- P.3.6: draw stones (still Lua-tables; future-slice makes them
-- entities too)
for _, s in ipairs(stones) do
engine.render.draw_rect(s.x, s.y, s.w, s.h,
engine.render.rgb(s.color[1], s.color[2], s.color[3]))
end
-- draw player on top of map/stones
local p = player.position()
local sz = player.size()
engine.render.draw_rect(p.x, p.y, sz.w, sz.h,
engine.render.rgb(PLAYER_R, PLAYER_G, PLAYER_B))
-- Draw all renderable entities (Sign + Player). Drawn AFTER
-- stones so the Player visually overlays stone-occlusions —
-- in practice positions don't collide, but the layering
-- intent is preserved.
r_lib.draw_entities{ tag = "renderable" }
-- P.3.6: highlights over selected entities (world-space)
selection.render_highlights()
maps.draw_map_post_entities()
@@ -151,7 +195,7 @@ end
function update(ctx, dt)
if input.was_action_pressed("quit") then
-- ADR-0028: ESC returns to launcher per P.3.4.
engine.switch_module("lib-sporel.launcher")
engine.switch_module("lib-management.launcher")
return
end
@@ -163,10 +207,8 @@ function update(ctx, dt)
-- Player + interaction nur in follow-mode (in free-cam ist player pausiert).
if not free_cam_mode then
player.update(dt)
local p = player.position()
local s = player.size()
interaction.update(p.x + s.w / 2, p.y + s.h / 2)
player_control.update(player, dt)
interaction.update(actor.position(player))
end
-- Pan-Summation jeden Frame (keys nur wenn enabled; drag + edge always-on).
@@ -176,9 +218,7 @@ function update(ctx, dt)
if not free_cam_mode then
-- Follow-cam: set_target jeden Frame -> drag/edge "drift" wird unsichtbar.
local p = player.position()
local s = player.size()
camera.set_target(p.x + s.w / 2, p.y + s.h / 2)
camera.set_target(actor.position(player))
end
-- CI-conditional self-exit (unverändert aus P.0):

29
manifest.launcher.md Normal file
View File

@@ -0,0 +1,29 @@
---
{
"summary":"Spine prototype demo — RMB-move + proximity sign",
"author":"calic",
"tags":["demo","foundational","tile-map"],
"teaser_images":[]
}
---
# Spine Prototype
Foundational end-to-end demo from the bootstrap phase.
## What it shows
- Tile-map loading via lib-core.maps
- Click-select + drag-box-select units
- Right-mouse-button move command
- Proximity-triggered sign interaction (press E near it)
- Free-pan camera toggle (Tab)
## Controls
- WASD / arrow-keys: move
- E: interact
- Tab: toggle free-pan camera
- Mouse-left: select units
- Mouse-right: move-command
- ESC: quit to launcher

View File

@@ -1,18 +1,24 @@
{
"id": "spine-prototype",
"version": "0.1.0",
"version": "0.3.1",
"kind": "module",
"api": "^0.1",
"ci_frames": 30,
"entry_point": "init.lua",
"asset_aliases": {
"fa_terrain_v1": "lib-asset.prototype-fa-starter"
},
"deps": [
{"id":"lib-core.maps","version":"0.2.0"},
{"id":"lib-core.render","version":"0.1.0"},
{"id":"lib-core.maps","version":"0.5.7"},
{"id":"lib-core.render","version":"0.2.0"},
{"id":"lib-core.camera","version":"0.3.0"},
{"id":"lib-core.selection","version":"0.1.0"},
{"id":"lib-core.input","version":"0.4.0"},
{"id":"lib-core.player_control","version":"0.1.0"},
{"id":"lib-core.player_control","version":"0.2.0"},
{"id":"lib-core.actor","version":"0.1.0"},
{"id":"lib-core.interaction","version":"0.1.0"},
{"id":"lib-core.command","version":"0.1.0"}
{"id":"lib-core.command","version":"0.1.0"},
{"id":"lib-core.composition","version":"0.1.0"},
{"id":"lib-asset.prototype-fa-starter","version":"0.2.0"}
]
}

View File

@@ -2,7 +2,7 @@
"schema_version": 2,
"id": "demo",
"size": { "w": 16, "h": 16 },
"atlases": ["demo_tilemap"],
"atlases": ["fa_terrain_v1"],
"layers": {
"surface": {
"tiles": [