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 demo_map = maps.load("maps/demo.map.json") maps.set_current(demo_map) local m_size = maps.size() local t_size = maps.tile_size() local map_center_x = (m_size.w * t_size) / 2 local map_center_y = (m_size.h * t_size) / 2 camera.set_target(map_center_x, map_center_y) camera.set_zoom(1.0) -- Bind input actions (string-key-names per lib-core.input v0.2.0) input.bind("quit", { "escape" }) input.bind("move_left", { "a", "left" }) input.bind("move_right", { "d", "right" }) input.bind("move_up", { "w", "up" }) input.bind("move_down", { "s", "down" }) input.bind("interact", { "e" }) -- P.3.5: Free-Cam demo (Tab toggle) input.bind("toggle_free_cam", { "tab" }) input.bind("pan_drag", { "mouse_middle" }) -- pan_*-Actions teilen sich die Keys mit move_*-Actions (additive bindings). input.bind("pan_left", { "a", "left" }) input.bind("pan_right", { "d", "right" }) input.bind("pan_up", { "w", "up" }) input.bind("pan_down", { "s", "down" }) camera.bind_pan_keys("pan_left", "pan_right", "pan_up", "pan_down") camera.bind_pan_drag("pan_drag") camera.enable_pan_edge() -- Start in follow-mode: pan_keys aus (drag/edge bleiben enabled, sind aber -- 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") local PLAYER_R, PLAYER_G, PLAYER_B = 200, 60, 80 -- 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 -- P.3.5: Free-Cam-Mode state local free_cam_mode = false local frames = 0 local TARGET_FRAMES = 60 function init(ctx) engine.print("spine-prototype init: maps loaded") engine.print(string.format("map size: %dx%d", m_size.w, m_size.h)) local corner = maps.tile_at(0, 0) local interior = maps.tile_at(5, 5) engine.print("tile at (0,0): " .. corner.id) engine.print("tile at (5,5): " .. interior.id) engine.print("walkable at (0,0): " .. tostring(maps.is_walkable(0, 0))) engine.print("walkable at (5,5): " .. tostring(maps.is_walkable(5, 5))) engine.print(string.format("render: %d tiles per frame", m_size.w * m_size.h)) 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() engine.print(string.format("player: pos=(%d,%d) size=%dx%d speed=%d", p.x, p.y, s.w, s.h, player.speed())) interaction.register(sign.x, sign.y, 40, "interact", function(info) engine.print(sign.text) end) engine.print(string.format("interaction: %d trigger(s) registered", interaction.trigger_count())) end function render(ctx) engine.render.clear_color(engine.render.rgb(20, 20, 30)) camera.begin() r_lib.draw_map() -- 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)) -- draw player on top of sign local p = player.position() local s = player.size() engine.render.draw_rect(p.x, p.y, s.w, s.h, engine.render.rgb(PLAYER_R, PLAYER_G, PLAYER_B)) camera.finish() 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") return end -- P.3.5: Tab toggles between follow-cam and free-cam. if input.was_action_pressed("toggle_free_cam") then free_cam_mode = not free_cam_mode camera.set_pan_enabled("keys", free_cam_mode) end -- 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) end -- Pan-Summation jeden Frame (keys nur wenn enabled; drag + edge always-on). camera.update(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) end -- CI-conditional self-exit (unverändert aus P.0): frames = frames + 1 if os.getenv("SPOREL_CI") == "1" and frames >= TARGET_FRAMES then engine.exit(0) end end function cleanup(ctx) engine.print("spine-prototype cleanup: total_frames=" .. frames) end