# crafting.ludic — RPG Module C: a data-driven crafting registry (#59). # # Recipes are content, not code: register with Craft.recipe + Craft.ingredient, so a # game or mod adds crafting with zero code. Craft.make consumes ingredients from the # ludic.rpg Inventory and yields the output; every step is a veto-able event. import "ludic.rpg/inventory.ludic" const CRAFT_MAX: int = 128 const CRAFT_ING_MAX: int = 512 var recipe_reg: Dict = null # recipe name -> id var recipe_n: int = 0 var recipe_out: pointers = null # output item name var recipe_outqty: words = null var ing_n: int = 0 var ing_rid: words = null # owning recipe id var ing_name: pointers = null # ingredient item name var ing_qty: words = null function craft_ensure() -> void { if recipe_reg == null { recipe_reg = Dict.new() recipe_out = bytes(CRAFT_MAX * 8); recipe_outqty = words(CRAFT_MAX) ing_rid = words(CRAFT_ING_MAX); ing_name = bytes(CRAFT_ING_MAX * 8); ing_qty = words(CRAFT_ING_MAX) } } event cancellable CraftRequested { e: int = 0, recipe: int = 0 } event CraftCompleted { e: int = 0, recipe: int = 0 } # Craft.recipe(name, out_item, out_qty) — declare a recipe; returns its id. @Namespace(Craft) function craft_recipe(name: pointer, out_item: pointer, out_qty: int) -> int { craft_ensure() if recipe_n >= CRAFT_MAX { return 0 - 1 } let id = recipe_n recipe_out[id] = out_item; recipe_outqty[id] = out_qty recipe_n = recipe_n + 1 Dict.set(recipe_reg, name, id) return id } @Namespace(Craft) function craft_id(name: pointer) -> int { craft_ensure(); return Dict.get_or(recipe_reg, name, 0 - 1) } # Craft.ingredient(recipe_name, item, qty) — add an ingredient requirement. @Namespace(Craft) function craft_ingredient(recipe_name: pointer, item: pointer, qty: int) -> void { craft_ensure() let rid = craft_id(recipe_name) if rid < 0 { return } if ing_n >= CRAFT_ING_MAX { return } ing_rid[ing_n] = rid; ing_name[ing_n] = item; ing_qty[ing_n] = qty ing_n = ing_n + 1 } # Craft.can(e, recipe_name) — does the owner hold every ingredient? @Namespace(Craft) function craft_can(e: int, recipe_name: pointer) -> int { craft_ensure() let rid = craft_id(recipe_name) if rid < 0 { return 0 } var i = 0 while i < ing_n { if ing_rid[i] == rid { if Inventory.count(e, ing_name[i]) < ing_qty[i] { return 0 } } i = i + 1 } return 1 } # Craft.make(e, recipe_name) — consume ingredients + produce output; 1 on success. @Namespace(Craft) function craft_make(e: int, recipe_name: pointer) -> int { craft_ensure() let rid = craft_id(recipe_name) if rid < 0 { return 0 } if craft_can(e, recipe_name) == 0 { return 0 } if emit CraftRequested(e: e, recipe: rid) != 0 { return 0 } var i = 0 while i < ing_n { if ing_rid[i] == rid { Inventory.remove(e, ing_name[i], ing_qty[i]) } i = i + 1 } Inventory.add(e, recipe_out[rid], recipe_outqty[rid]) emit CraftCompleted(e: e, recipe: rid) return 1 }