mirror of
https://github.com/TemujinCalidius/SurvivorCore.git
synced 2026-08-14 00:58:01 +00:00
A new server-authoritative, dupe-proof trade system. Walk up to another player, trigger the "Trade" prompt; they Accept/Decline; both stage loose backpack stacks (drag from the inventory grid, with −/+ qty steppers) and must Confirm before anything moves. Anti-dupe by construction: staging is by-reference (items never leave the owner until commit), and the commit is one synchronous, no-yield critical section — re-validate holds → pre-flight both receivers have room (new Inventory.canAccept) → remove both → grant with addUpTo → refund any residue. Item count is conserved on every branch (verified: 200k-iteration conservation + fit-oracle fuzz). Auto-cancels on death / leave / out-of-range (MaxDistance) / request timeout; a staging change resets both confirms. New Trade server system + TradeUi client window, the "Trading" Config section (tunable in SurvivorCore Studio), hooks trade:started / trade:completed, and a trades_total progression counter. v1 is backpack stacks only (worn gear reserved behind AllowEquippedItems). - src/systems/Trade.luau, src/client/TradeUi.luau, src/shared/TradingConfig.luau (new) - src/systems/Inventory.luau: exported canAccept (weight+slot fit oracle) - src/shared/EngineConfig.luau: Trading section; src/init.luau boot wiring - docs/trading.md, README, CHANGELOG (Unreleased), Hooks catalogue, extending.md - demo/server/TradeTestStation.server.luau: /tradetest 2-player conservation harness Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
2.8 KiB
Luau
65 lines
2.8 KiB
Luau
--[[
|
|
Hooks — lifecycle extension points. Where EventBridge is "something happened",
|
|
Hooks is "the engine is about to / just did X — creators, do your thing here".
|
|
|
|
Hooks.on("craft:start", function(ctx) ctx.station:lightFire() end)
|
|
Hooks.on("craft:end", function(ctx) ringBell(ctx.station) end)
|
|
-- inside the engine:
|
|
Hooks.run("craft:start", { station = station, recipe = recipe, player = player })
|
|
|
|
This is how game-specific flourish (tree-felling physics, station VFX, custom drops)
|
|
stays OUT of the engine while remaining first-class.
|
|
|
|
Engine-fired hooks (ctx is a single table unless noted):
|
|
gather:hit / gather:depleted / gather:blocked { instance, player, resource, item?, … }
|
|
craft:start / craft:end / craft:blocked { station?, recipe, player, reason? }
|
|
item:use { player, itemId, … }
|
|
mob:spawned / mob:hit / mob:died { instance, mobType, player?, killer?, position? }
|
|
mob:attack { instance, mobType, player, damage, position }
|
|
combat:hit { attacker, victim, weapon, source, damage, victimHpLeft }
|
|
combat:kill { attacker, victim, weapon, source } -- source "melee"|"bow"
|
|
quest:started / quest:progress / quest:completed / quest:blocked
|
|
{ player, questId, def?, index?, count?, reason? }
|
|
achievement:unlocked { player, key, def }
|
|
player:died { player, position } -- after any death-drop
|
|
lootbag:dropped / lootbag:collected { player, bag, position?, items? / emptied }
|
|
trade:started / trade:completed { player, partner, gave?, got? } -- fired per player
|
|
|
|
Per-resource / per-mob-type variants of these dispatch through Reactions (see Reactions.luau):
|
|
SurvivorCore.Gather.onReaction(resourceId, …) and SurvivorCore.Mobs.onReaction(mobType, …).
|
|
|
|
Gameplay lifecycle events (gather:*, craft:*, item:use, mob:*, combat:*, quest:*,
|
|
achievement:unlocked) ALSO cross the EventBridge bus with the same names — that is what
|
|
quests/achievements/analytics subscribe to (see EventBridge.luau and systems/Progression.luau).
|
|
]]
|
|
|
|
local Hooks = {}
|
|
|
|
type Hook = (...any) -> ()
|
|
|
|
local hooks: { [string]: { Hook } } = {}
|
|
|
|
function Hooks.on(name: string, callback: Hook): () -> ()
|
|
hooks[name] = hooks[name] or {}
|
|
table.insert(hooks[name], callback)
|
|
return function()
|
|
local list = hooks[name]
|
|
local i = list and table.find(list, callback)
|
|
if i then
|
|
table.remove(list, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
function Hooks.run(name: string, ...)
|
|
local list = hooks[name]
|
|
if not list then
|
|
return
|
|
end
|
|
for _, cb in list do
|
|
task.spawn(cb, ...)
|
|
end
|
|
end
|
|
|
|
return Hooks
|