DEMI-PROJECT-PACKAGE í{"files":[{"hash":"sha256:e735a66356f32246d2ce9d6712e52afc007ab97df2026ec5ce7bba75b6dc8b32","path":"README.md","size":222},{"hash":"sha256:032544b502f419d6f5e927f0867957b2a5339d103fc0658caa6b65af5802d72d","path":"scripts/demi/gameplay/interactions.lua","size":1362},{"hash":"sha256:9ff07e9d6969d84480f771090bc96e082d9f1d34fdd15ebf9078be12ab809bf7","path":"scripts/demi/gameplay/pickups.lua","size":857},{"hash":"sha256:88e05d010b5e739f9227e052c17f7c28e02ad9043fc5a5637a40dbe1c3e9be2c","path":"tests/interactions_test.lua","size":1230}],"format_version":1,"manifest":{"dependencies":{"demi.gameplay.core":"^1.0.0"},"engine":"^0.1.0","exported_events":["interaction_selected","pickup_collected"],"files":["README.md","scripts/demi/gameplay/interactions.lua","scripts/demi/gameplay/pickups.lua","tests/interactions_test.lua"],"format_version":1,"name":"demi.gameplay.interactions","public_modules":["demi.gameplay.interactions","demi.gameplay.pickups"],"version":"1.0.0"},"package_type":"DemiProjectPackage"}# demi.gameplay.interactions Deterministic interaction candidate selection and single-use pickup collection. The game supplies distance and enabled state, then decides what a confirmed interaction or collected item does. local Interactions = {} Interactions.__index = Interactions function Interactions.new(events) return setmetatable({ events = assert(events), candidates = {} }, Interactions) end function Interactions:set(id, values) assert(type(id) == "string") values = values or {} self.candidates[id] = { id = id, priority = values.priority or 0, distance = values.distance or math.huge, enabled = values.enabled ~= false, prompt = values.prompt, payload = values.payload } end function Interactions:remove(id) self.candidates[id] = nil end function Interactions:best(max_distance) local result for _, value in pairs(self.candidates) do if value.enabled and value.distance <= (max_distance or math.huge) and (not result or value.priority > result.priority or value.priority == result.priority and value.distance < result.distance or value.priority == result.priority and value.distance == result.distance and value.id < result.id) then result = value end end return result end function Interactions:confirm(max_distance) local selected = self:best(max_distance) if not selected or not self.candidates[selected.id] or not selected.enabled then return false end self.events:emit("interaction_selected", { entity = selected.id, payload = selected.payload }) return true, selected.id end return Interactions local Pickups = {} Pickups.__index = Pickups function Pickups.new(events) return setmetatable({ events = assert(events), entries = {} }, Pickups) end function Pickups:add(id, values) assert(type(id) == "string") values = values or {} self.entries[id] = { id = id, item = values.item, count = values.count or 1, enabled = values.enabled ~= false, payload = values.payload, } end function Pickups:remove(id) self.entries[id] = nil end function Pickups:collect(id, collector) local pickup = self.entries[id] if not pickup or not pickup.enabled then return false end pickup.enabled = false self.entries[id] = nil self.events:emit("pickup_collected", { pickup = id, collector = collector, item = pickup.item, count = pickup.count, payload = pickup.payload, }) return true end return Pickups local Events = require("demi.gameplay.events") local Interactions = require("demi.gameplay.interactions") Test.case("ties use priority distance then stable id", function() local interactions = Interactions.new(Events.new()) interactions:set("b", { priority = 2, distance = 4 }) interactions:set("a", { priority = 2, distance = 4 }) interactions:set("near-low", { priority = 1, distance = 1 }) Test.equal(interactions:best(5).id, "a") end) Test.case("disabled candidate cannot be confirmed", function() local interactions = Interactions.new(Events.new()) interactions:set("door", { enabled = false, distance = 0 }) Test.equal(interactions:confirm(1), false) end) Test.case("pickup collection is single use", function() local Events = require("demi.gameplay.events") local Pickups = require("demi.gameplay.pickups") local events, collected = Events.new(), 0 events:on("pickup_collected", function(value) collected = collected + value.count end) local pickups = Pickups.new(events) pickups:add("coin-1", { item = "coin", count = 3 }) Test.equal(pickups:collect("coin-1", "player"), true) Test.equal(pickups:collect("coin-1", "player"), false) events:flush() Test.equal(collected, 3) end)