# inventory.ludic — RPG Module B: inventory + a name-keyed item registry (#59). # # Items are data (a registry entry, like Anim.clip), so a game/mod adds items with # zero code. Per-owner counts live in one string-keyed Dict ("/"), so # the whole inventory is captured by world_save. Equipment routes its stat effects # through the ludic.gameplay Stats modifier stack. import "ludic.gameplay/stats.ludic" const INV_MAX: int = 256 # ---- item registry -------------------------------------------------------- state RpgState { recipe_reg: Dict = Dict.new() # recipe name -> id recipe_n: int = 0 recipe_out: pointers = pointers(CRAFT_MAX) # output item name recipe_outqty: words = words(CRAFT_MAX) ing_n: int = 0 ing_rid: words = words(CRAFT_ING_MAX) # owning recipe id ing_name: pointers = pointers(CRAFT_ING_MAX) # ingredient item name ing_qty: words = words(CRAFT_ING_MAX) dlg_reg: Dict = Dict.new() # tree name -> tree id dlg_tree_n: int = 0 dlg_tree_start: words = words(DLG_TREE_MAX) # first node id per tree dlg_node_n: int = 0 dlg_node_tree: words = words(DLG_NODE_MAX) dlg_node_text: pointers = pointers(DLG_NODE_MAX) dlg_choice_n: int = 0 dlg_choice_node: words = words(DLG_CHOICE_MAX) # owning node dlg_choice_label: pointers = pointers(DLG_CHOICE_MAX) dlg_choice_next: words = words(DLG_CHOICE_MAX) # next node id, or -1 to end item_reg: Dict = Dict.new() item_n: int = 0 item_stackmax: words = words(INV_MAX) item_weight: words = words(INV_MAX) item_value: words = words(INV_MAX) inv_counts: Dict = Dict.new() quest_reg: Dict = Dict.new() # quest name -> id quest_n: int = 0 quest_state: words = words(QUEST_MAX) obj_n: int = 0 obj_qid: words = words(OBJ_MAX) # owning quest id obj_kind: words = words(OBJ_MAX) obj_target: words = words(OBJ_MAX) # a target id/tag (kind-specific; 0 = any) obj_need: words = words(OBJ_MAX) obj_have: words = words(OBJ_MAX) quest_flags: Dict = Dict.new() # global flag store } # Item.def(name, stack_max, weight, value) — register an item template. Returns id. @Namespace(Item) function item_def(rpg_st: mut RpgState, name: pointer, stack_max: int, weight: int, value: int) -> int { if rpg_st.item_n >= INV_MAX { return -1 } let id = rpg_st.item_n rpg_st.item_stackmax[id] = stack_max; rpg_st.item_weight[id] = weight; rpg_st.item_value[id] = value rpg_st.item_n += 1 Dict.set(rpg_st.item_reg, name, id) return id } @Namespace(Item) function item_id(rpg_st: mut RpgState, name: pointer) -> int { return Dict.get_or(rpg_st.item_reg, name, -1) } @Namespace(Item) function item_value_of(rpg_st: mut RpgState, name: pointer) -> int { let id = item_id(rpg_st, name); if id < 0 { return 0 } return rpg_st.item_value[id] } # ---- per-owner counts ----------------------------------------------------- property Inventory { gold: int = 0, weight_max: int = 0, slots: int = 0 } function inv_key(e: int, name: pointer) -> pointer { return `{e}/{name}` } event ItemAdded { e: int = 0, item: int = 0, qty: int = 0 } # item = registry id event ItemRemoved { e: int = 0, item: int = 0, qty: int = 0 } event cancellable ItemUse { e: int = 0, item: int = 0 } # veto a use (e.g. cursed / requirement) # Inventory.add(e, name, n) — add n of an item; emits ItemAdded. @Namespace(Inventory) function inventory_add(rpg_st: mut RpgState, e: int, name: pointer, n: int) -> void { let k = inv_key(e, name) Dict.set(rpg_st.inv_counts, k, Dict.get_or(rpg_st.inv_counts, k, 0) + n) emit ItemAdded(e: e, item: item_id(rpg_st, name), qty: n) } # Inventory.count(e, name) — how many the owner holds. @Namespace(Inventory) function inventory_count(rpg_st: mut RpgState, e: int, name: pointer) -> int { return Dict.get_or(rpg_st.inv_counts, inv_key(e, name), 0) } @Namespace(Inventory) function inventory_has(rpg_st: mut RpgState, e: int, name: pointer) -> int { if inventory_count(rpg_st, e, name) > 0 { return 1 } return 0 } # Inventory.remove(e, name, n) — remove up to n; returns 1 if it had enough. @Namespace(Inventory) function inventory_remove(rpg_st: mut RpgState, e: int, name: pointer, n: int) -> int { let k = inv_key(e, name) let have = Dict.get_or(rpg_st.inv_counts, k, 0) if have < n { return 0 } Dict.set(rpg_st.inv_counts, k, have - n) emit ItemRemoved(e: e, item: item_id(rpg_st, name), qty: n) return 1 } # Inventory.use(e, name) — consume one item unless a listener vetoes ItemUse. @Namespace(Inventory) function inventory_use(rpg_st: mut RpgState, e: int, name: pointer) -> int { if inventory_count(rpg_st, e, name) <= 0 { return 0 } if emit ItemUse(e: e, item: item_id(rpg_st, name)) != 0 { return 0 } return inventory_remove(rpg_st, e, name, 1) } # Inventory.gold / add_gold / spend_gold @Namespace(Inventory) function inventory_gold(e: int) -> int { let P = World.prop_id("Inventory") if P < 0 { return 0 } return World.get(e, P, World.field_id(P, "gold")) } @Namespace(Inventory) function inventory_add_gold(e: int, n: int) -> void { let P = World.prop_id("Inventory") if P < 0 { return } let f = World.field_id(P, "gold") World.set(e, P, f, World.get(e, P, f) + n) } @Namespace(Inventory) function inventory_spend_gold(e: int, n: int) -> int { let g = inventory_gold(e) if g < n { return 0 } inventory_add_gold(e, -n) return 1 } # ---- equipment: stat effects flow through the gameplay modifier stack ------ # Equipment holds one modifier-entity id per slot (0 = empty); equipping pushes a # Stats modifier, unequipping drops it. slot ids: 0 weapon, 1 armor, 2 trinket. property Equipment { weapon: int = 0, armor: int = 0, trinket: int = 0 } # Inventory.equip(e, slot, stat, op, amount) — apply an equipment bonus and record # the modifier so it can be removed on unequip. Returns the modifier entity. @Namespace(Inventory) function inventory_equip(e: int, slot: int, stat: int, op: int, amount: int) -> int { inventory_unequip(e, slot) # replace whatever was there let m = Stats.modify(e, stat, op, amount, 0) # permanent until unequipped let P = World.prop_id("Equipment") if P >= 0 { if slot == 0 { World.set(e, P, World.field_id(P, "weapon"), m + 1) } if slot == 1 { World.set(e, P, World.field_id(P, "armor"), m + 1) } if slot == 2 { World.set(e, P, World.field_id(P, "trinket"), m + 1) } } return m } @Namespace(Inventory) function inventory_unequip(e: int, slot: int) -> void { let P = World.prop_id("Equipment") if P < 0 { return } var f = World.field_id(P, "weapon") if slot == 1 { f = World.field_id(P, "armor") } if slot == 2 { f = World.field_id(P, "trinket") } let stored = World.get(e, P, f) if stored > 0 { despawn (stored - 1); World.set(e, P, f, 0) } }