wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run

ludic migrate state packages <every example program> packages/ludic.lab/example/plate.ludic
  1804 vars into 126 states, 64 into lets; 23498 edits in 460 files

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 15:31:34 +03:00
parent 5ffe50ed02
commit 19fcf60599
459 changed files with 17862 additions and 17603 deletions

View file

@ -9,20 +9,12 @@ 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 = pointers(CRAFT_MAX); recipe_outqty = words(CRAFT_MAX)
ing_rid = words(CRAFT_ING_MAX); ing_name = pointers(CRAFT_ING_MAX); ing_qty = words(CRAFT_ING_MAX)
function craft_ensure(rpg_st: mut RpgState) -> void {
if rpg_st.recipe_reg == null {
rpg_st.recipe_reg = Dict.new()
rpg_st.recipe_out = pointers(CRAFT_MAX); rpg_st.recipe_outqty = words(CRAFT_MAX)
rpg_st.ing_rid = words(CRAFT_ING_MAX); rpg_st.ing_name = pointers(CRAFT_ING_MAX); rpg_st.ing_qty = words(CRAFT_ING_MAX)
}
}
@ -30,35 +22,35 @@ 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 -1 }
let id = recipe_n
recipe_out[id] = out_item; recipe_outqty[id] = out_qty
recipe_n += 1
Dict.set(recipe_reg, name, id)
@Namespace(Craft) function craft_recipe(rpg_st: mut RpgState, name: pointer, out_item: pointer, out_qty: int) -> int {
craft_ensure(rpg_st)
if rpg_st.recipe_n >= CRAFT_MAX { return -1 }
let id = rpg_st.recipe_n
rpg_st.recipe_out[id] = out_item; rpg_st.recipe_outqty[id] = out_qty
rpg_st.recipe_n += 1
Dict.set(rpg_st.recipe_reg, name, id)
return id
}
@Namespace(Craft) function craft_id(name: pointer) -> int { craft_ensure(); return Dict.get_or(recipe_reg, name, -1) }
@Namespace(Craft) function craft_id(rpg_st: mut RpgState, name: pointer) -> int { craft_ensure(rpg_st); return Dict.get_or(rpg_st.recipe_reg, name, -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)
@Namespace(Craft) function craft_ingredient(rpg_st: mut RpgState, recipe_name: pointer, item: pointer, qty: int) -> void {
craft_ensure(rpg_st)
let rid = craft_id(rpg_st, 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 += 1
if rpg_st.ing_n >= CRAFT_ING_MAX { return }
rpg_st.ing_rid[rpg_st.ing_n] = rid; rpg_st.ing_name[rpg_st.ing_n] = item; rpg_st.ing_qty[rpg_st.ing_n] = qty
rpg_st.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)
@Namespace(Craft) function craft_can(rpg_st: mut RpgState, e: int, recipe_name: pointer) -> int {
craft_ensure(rpg_st)
let rid = craft_id(rpg_st, 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 }
while i < rpg_st.ing_n {
if rpg_st.ing_rid[i] == rid {
if Inventory.count(e, rpg_st.ing_name[i]) < rpg_st.ing_qty[i] { return 0 }
}
i += 1
}
@ -66,18 +58,18 @@ event CraftCompleted { e: int = 0, recipe: int = 0 }
}
# 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)
@Namespace(Craft) function craft_make(rpg_st: mut RpgState, e: int, recipe_name: pointer) -> int {
craft_ensure(rpg_st)
let rid = craft_id(rpg_st, recipe_name)
if rid < 0 { return 0 }
if craft_can(e, recipe_name) == 0 { return 0 }
if craft_can(rpg_st, 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]) }
while i < rpg_st.ing_n {
if rpg_st.ing_rid[i] == rid { Inventory.remove(e, rpg_st.ing_name[i], rpg_st.ing_qty[i]) }
i += 1
}
Inventory.add(e, recipe_out[rid], recipe_outqty[rid])
Inventory.add(e, rpg_st.recipe_out[rid], rpg_st.recipe_outqty[rid])
emit CraftCompleted(e: e, recipe: rid)
return 1
}

View file

@ -8,23 +8,13 @@ const DLG_TREE_MAX: int = 64
const DLG_NODE_MAX: int = 512
const DLG_CHOICE_MAX: int = 1024
var dlg_reg: Dict = null # tree name -> tree id
var dlg_tree_n: int = 0
var dlg_tree_start: words = null # first node id per tree
var dlg_node_n: int = 0
var dlg_node_tree: words = null
var dlg_node_text: pointers = null
var dlg_choice_n: int = 0
var dlg_choice_node: words = null # owning node
var dlg_choice_label: pointers = null
var dlg_choice_next: words = null # next node id, or -1 to end
function dlg_ensure() -> void {
if dlg_reg == null {
dlg_reg = Dict.new()
dlg_tree_start = words(DLG_TREE_MAX)
dlg_node_tree = words(DLG_NODE_MAX); dlg_node_text = pointers(DLG_NODE_MAX)
dlg_choice_node = words(DLG_CHOICE_MAX); dlg_choice_label = pointers(DLG_CHOICE_MAX); dlg_choice_next = words(DLG_CHOICE_MAX)
function dlg_ensure(rpg_st: mut RpgState) -> void {
if rpg_st.dlg_reg == null {
rpg_st.dlg_reg = Dict.new()
rpg_st.dlg_tree_start = words(DLG_TREE_MAX)
rpg_st.dlg_node_tree = words(DLG_NODE_MAX); rpg_st.dlg_node_text = pointers(DLG_NODE_MAX)
rpg_st.dlg_choice_node = words(DLG_CHOICE_MAX); rpg_st.dlg_choice_label = pointers(DLG_CHOICE_MAX); rpg_st.dlg_choice_next = words(DLG_CHOICE_MAX)
}
}
@ -36,37 +26,37 @@ event cancellable DialogChoice { e: int = 0, node: int = 0, choice: int = 0 }
event DialogEnded { e: int = 0 }
# Dialog.tree(name) — register (or fetch) a tree id.
@Namespace(Dialog) function dialog_tree(name: pointer) -> int {
dlg_ensure()
let ex = Dict.get_or(dlg_reg, name, -1)
@Namespace(Dialog) function dialog_tree(rpg_st: mut RpgState, name: pointer) -> int {
dlg_ensure(rpg_st)
let ex = Dict.get_or(rpg_st.dlg_reg, name, -1)
if ex >= 0 { return ex }
if dlg_tree_n >= DLG_TREE_MAX { return -1 }
let id = dlg_tree_n
dlg_tree_start[id] = -1
dlg_tree_n += 1
Dict.set(dlg_reg, name, id)
if rpg_st.dlg_tree_n >= DLG_TREE_MAX { return -1 }
let id = rpg_st.dlg_tree_n
rpg_st.dlg_tree_start[id] = -1
rpg_st.dlg_tree_n += 1
Dict.set(rpg_st.dlg_reg, name, id)
return id
}
# Dialog.node(tree_name, text) — add a line node; the first node added to a tree is
# its entry point. Returns the node id.
@Namespace(Dialog) function dialog_node(tree_name: pointer, text: pointer) -> int {
dlg_ensure()
let tid = dialog_tree(tree_name)
@Namespace(Dialog) function dialog_node(rpg_st: mut RpgState, tree_name: pointer, text: pointer) -> int {
dlg_ensure(rpg_st)
let tid = dialog_tree(rpg_st, tree_name)
if tid < 0 { return -1 }
if dlg_node_n >= DLG_NODE_MAX { return -1 }
let id = dlg_node_n
dlg_node_tree[id] = tid; dlg_node_text[id] = text
dlg_node_n += 1
if dlg_tree_start[tid] < 0 { dlg_tree_start[tid] = id }
if rpg_st.dlg_node_n >= DLG_NODE_MAX { return -1 }
let id = rpg_st.dlg_node_n
rpg_st.dlg_node_tree[id] = tid; rpg_st.dlg_node_text[id] = text
rpg_st.dlg_node_n += 1
if rpg_st.dlg_tree_start[tid] < 0 { rpg_st.dlg_tree_start[tid] = id }
return id
}
# Dialog.choice(node, label, next_node) — add a branch (next_node -1 ends).
@Namespace(Dialog) function dialog_choice(node: int, label: pointer, next_node: int) -> int {
dlg_ensure()
if dlg_choice_n >= DLG_CHOICE_MAX { return -1 }
let id = dlg_choice_n
dlg_choice_node[id] = node; dlg_choice_label[id] = label; dlg_choice_next[id] = next_node
dlg_choice_n += 1
@Namespace(Dialog) function dialog_choice(rpg_st: mut RpgState, node: int, label: pointer, next_node: int) -> int {
dlg_ensure(rpg_st)
if rpg_st.dlg_choice_n >= DLG_CHOICE_MAX { return -1 }
let id = rpg_st.dlg_choice_n
rpg_st.dlg_choice_node[id] = node; rpg_st.dlg_choice_label[id] = label; rpg_st.dlg_choice_next[id] = next_node
rpg_st.dlg_choice_n += 1
return id
}
@ -80,45 +70,45 @@ function dlg_set(e: int, name: pointer, v: int) -> void {
}
# Dialog.start(e, tree_name) — open the tree on a speaker (DialogStarted + Line).
@Namespace(Dialog) function dialog_start(e: int, tree_name: pointer) -> void {
dlg_ensure()
let tid = dialog_tree(tree_name)
@Namespace(Dialog) function dialog_start(rpg_st: mut RpgState, e: int, tree_name: pointer) -> void {
dlg_ensure(rpg_st)
let tid = dialog_tree(rpg_st, tree_name)
if tid < 0 { return }
dlg_set(e, "tree", tid)
dlg_set(e, "node", dlg_tree_start[tid])
dlg_set(e, "node", rpg_st.dlg_tree_start[tid])
dlg_set(e, "active", 1)
emit DialogStarted(e: e, tree: tid)
emit DialogLine(e: e, node: dlg_tree_start[tid])
emit DialogLine(e: e, node: rpg_st.dlg_tree_start[tid])
}
# Dialog.line(e) — text of the current node (empty when inactive).
@Namespace(Dialog) function dialog_line(e: int) -> pointer {
@Namespace(Dialog) function dialog_line(rpg_st: RpgState, e: int) -> pointer {
if dlg_get(e, "active") == 0 { return "" }
return dlg_node_text[dlg_get(e, "node")]
return rpg_st.dlg_node_text[dlg_get(e, "node")]
}
# Dialog.count(e) — number of choices at the current node.
@Namespace(Dialog) function dialog_count(e: int) -> int {
dlg_ensure()
@Namespace(Dialog) function dialog_count(rpg_st: mut RpgState, e: int) -> int {
dlg_ensure(rpg_st)
if dlg_get(e, "active") == 0 { return 0 }
let node = dlg_get(e, "node")
var n = 0; var i = 0
while i < dlg_choice_n { if dlg_choice_node[i] == node { n += 1 }; i += 1 }
while i < rpg_st.dlg_choice_n { if rpg_st.dlg_choice_node[i] == node { n += 1 }; i += 1 }
return n
}
# Dialog.choose(e, i) — take the i-th choice (veto-able); advance or end.
@Namespace(Dialog) function dialog_choose(e: int, idx: int) -> int {
dlg_ensure()
@Namespace(Dialog) function dialog_choose(rpg_st: mut RpgState, e: int, idx: int) -> int {
dlg_ensure(rpg_st)
if dlg_get(e, "active") == 0 { return 0 }
let node = dlg_get(e, "node")
var seen = 0; var i = 0; var pick = -1
while i < dlg_choice_n {
if dlg_choice_node[i] == node {
if seen == idx { pick = i; i = dlg_choice_n } else { seen += 1 }
while i < rpg_st.dlg_choice_n {
if rpg_st.dlg_choice_node[i] == node {
if seen == idx { pick = i; i = rpg_st.dlg_choice_n } else { seen += 1 }
}
i += 1
}
if pick < 0 { return 0 }
if emit DialogChoice(e: e, node: node, choice: idx) != 0 { return 0 }
let nxt = dlg_choice_next[pick]
let nxt = rpg_st.dlg_choice_next[pick]
if nxt < 0 {
dlg_set(e, "active", 0)
emit DialogEnded(e: e)

View file

@ -10,40 +10,70 @@ import "ludic.gameplay/stats.ludic"
const INV_MAX: int = 256
# ---- item registry --------------------------------------------------------
var item_reg: Dict = null
var item_n: int = 0
var item_stackmax: words = null
var item_weight: words = null
var item_value: words = null
state RpgState {
recipe_reg: Dict = null # recipe name -> id
recipe_n: int = 0
recipe_out: pointers = null # output item name
recipe_outqty: words = null
ing_n: int = 0
ing_rid: words = null # owning recipe id
ing_name: pointers = null # ingredient item name
ing_qty: words = null
dlg_reg: Dict = null # tree name -> tree id
dlg_tree_n: int = 0
dlg_tree_start: words = null # first node id per tree
dlg_node_n: int = 0
dlg_node_tree: words = null
dlg_node_text: pointers = null
dlg_choice_n: int = 0
dlg_choice_node: words = null # owning node
dlg_choice_label: pointers = null
dlg_choice_next: words = null # next node id, or -1 to end
item_reg: Dict = null
item_n: int = 0
item_stackmax: words = null
item_weight: words = null
item_value: words = null
inv_counts: Dict = null
quest_reg: Dict = null # quest name -> id
quest_n: int = 0
quest_state: words = null
obj_n: int = 0
obj_qid: words = null # owning quest id
obj_kind: words = null
obj_target: words = null # a target id/tag (kind-specific; 0 = any)
obj_need: words = null
obj_have: words = null
quest_flags: Dict = null # global flag store
}
function item_ensure() -> void {
if item_reg == null {
item_reg = Dict.new()
item_stackmax = words(INV_MAX); item_weight = words(INV_MAX); item_value = words(INV_MAX)
function item_ensure(rpg_st: mut RpgState) -> void {
if rpg_st.item_reg == null {
rpg_st.item_reg = Dict.new()
rpg_st.item_stackmax = words(INV_MAX); rpg_st.item_weight = words(INV_MAX); rpg_st.item_value = words(INV_MAX)
}
}
# Item.def(name, stack_max, weight, value) — register an item template. Returns id.
@Namespace(Item) function item_def(name: pointer, stack_max: int, weight: int, value: int) -> int {
item_ensure()
if item_n >= INV_MAX { return -1 }
let id = item_n
item_stackmax[id] = stack_max; item_weight[id] = weight; item_value[id] = value
item_n += 1
Dict.set(item_reg, name, id)
@Namespace(Item) function item_def(rpg_st: mut RpgState, name: pointer, stack_max: int, weight: int, value: int) -> int {
item_ensure(rpg_st)
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(name: pointer) -> int { item_ensure(); return Dict.get_or(item_reg, name, -1) }
@Namespace(Item) function item_value_of(name: pointer) -> int {
let id = item_id(name); if id < 0 { return 0 }
return item_value[id]
@Namespace(Item) function item_id(rpg_st: mut RpgState, name: pointer) -> int { item_ensure(rpg_st); 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 }
var inv_counts: Dict = null
function inv_ensure() -> void { if inv_counts == null { inv_counts = Dict.new() } }
function inv_ensure(rpg_st: mut RpgState) -> void { if rpg_st.inv_counts == null { rpg_st.inv_counts = Dict.new() } }
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
@ -51,36 +81,36 @@ 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(e: int, name: pointer, n: int) -> void {
inv_ensure()
@Namespace(Inventory) function inventory_add(rpg_st: mut RpgState, e: int, name: pointer, n: int) -> void {
inv_ensure(rpg_st)
let k = inv_key(e, name)
Dict.set(inv_counts, k, Dict.get_or(inv_counts, k, 0) + n)
emit ItemAdded(e: e, item: item_id(name), qty: n)
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(e: int, name: pointer) -> int {
inv_ensure()
return Dict.get_or(inv_counts, inv_key(e, name), 0)
@Namespace(Inventory) function inventory_count(rpg_st: mut RpgState, e: int, name: pointer) -> int {
inv_ensure(rpg_st)
return Dict.get_or(rpg_st.inv_counts, inv_key(e, name), 0)
}
@Namespace(Inventory) function inventory_has(e: int, name: pointer) -> int {
if inventory_count(e, name) > 0 { return 1 }
@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(e: int, name: pointer, n: int) -> int {
inv_ensure()
@Namespace(Inventory) function inventory_remove(rpg_st: mut RpgState, e: int, name: pointer, n: int) -> int {
inv_ensure(rpg_st)
let k = inv_key(e, name)
let have = Dict.get_or(inv_counts, k, 0)
let have = Dict.get_or(rpg_st.inv_counts, k, 0)
if have < n { return 0 }
Dict.set(inv_counts, k, have - n)
emit ItemRemoved(e: e, item: item_id(name), qty: n)
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(e: int, name: pointer) -> int {
if inventory_count(e, name) <= 0 { return 0 }
if emit ItemUse(e: e, item: item_id(name)) != 0 { return 0 }
return inventory_remove(e, name, 1)
@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 {

View file

@ -14,23 +14,13 @@
const QUEST_MAX: int = 128
const OBJ_MAX: int = 512
var quest_reg: Dict = null # quest name -> id
var quest_n: int = 0
var quest_state: words = null
var obj_n: int = 0
var obj_qid: words = null # owning quest id
var obj_kind: words = null
var obj_target: words = null # a target id/tag (kind-specific; 0 = any)
var obj_need: words = null
var obj_have: words = null
var quest_flags: Dict = null # global flag store
function quest_ensure() -> void {
if quest_reg == null {
quest_reg = Dict.new(); quest_flags = Dict.new()
quest_state = words(QUEST_MAX)
obj_qid = words(OBJ_MAX); obj_kind = words(OBJ_MAX); obj_target = words(OBJ_MAX)
obj_need = words(OBJ_MAX); obj_have = words(OBJ_MAX)
function quest_ensure(rpg_st: mut RpgState) -> void {
if rpg_st.quest_reg == null {
rpg_st.quest_reg = Dict.new(); rpg_st.quest_flags = Dict.new()
rpg_st.quest_state = words(QUEST_MAX)
rpg_st.obj_qid = words(OBJ_MAX); rpg_st.obj_kind = words(OBJ_MAX); rpg_st.obj_target = words(OBJ_MAX)
rpg_st.obj_need = words(OBJ_MAX); rpg_st.obj_have = words(OBJ_MAX)
}
}
@ -39,48 +29,48 @@ event QuestCompleted { quest: int = 0 }
event ObjectiveProgressed { quest: int = 0, objective: int = 0, have: int = 0, need: int = 0 }
# Quest.define(name) — register a quest (state hidden). Returns id.
@Namespace(Quest) function quest_define(name: pointer) -> int {
quest_ensure()
if quest_n >= QUEST_MAX { return -1 }
let id = quest_n
quest_state[id] = 0
quest_n += 1
Dict.set(quest_reg, name, id)
@Namespace(Quest) function quest_define(rpg_st: mut RpgState, name: pointer) -> int {
quest_ensure(rpg_st)
if rpg_st.quest_n >= QUEST_MAX { return -1 }
let id = rpg_st.quest_n
rpg_st.quest_state[id] = 0
rpg_st.quest_n += 1
Dict.set(rpg_st.quest_reg, name, id)
return id
}
@Namespace(Quest) function quest_id(name: pointer) -> int { quest_ensure(); return Dict.get_or(quest_reg, name, -1) }
@Namespace(Quest) function quest_id(rpg_st: mut RpgState, name: pointer) -> int { quest_ensure(rpg_st); return Dict.get_or(rpg_st.quest_reg, name, -1) }
# Quest.objective(quest_name, kind, target, need) — add an objective.
@Namespace(Quest) function quest_objective(quest_name: pointer, kind: int, target: int, need: int) -> int {
quest_ensure()
let qid = quest_id(quest_name)
@Namespace(Quest) function quest_objective(rpg_st: mut RpgState, quest_name: pointer, kind: int, target: int, need: int) -> int {
quest_ensure(rpg_st)
let qid = quest_id(rpg_st, quest_name)
if qid < 0 { return -1 }
if obj_n >= OBJ_MAX { return -1 }
let id = obj_n
obj_qid[id] = qid; obj_kind[id] = kind; obj_target[id] = target
obj_need[id] = need; obj_have[id] = 0
obj_n += 1
if rpg_st.obj_n >= OBJ_MAX { return -1 }
let id = rpg_st.obj_n
rpg_st.obj_qid[id] = qid; rpg_st.obj_kind[id] = kind; rpg_st.obj_target[id] = target
rpg_st.obj_need[id] = need; rpg_st.obj_have[id] = 0
rpg_st.obj_n += 1
return id
}
# Quest.start(name) — activate a quest (emits QuestStarted).
@Namespace(Quest) function quest_start(name: pointer) -> void {
quest_ensure()
let qid = quest_id(name)
@Namespace(Quest) function quest_start(rpg_st: mut RpgState, name: pointer) -> void {
quest_ensure(rpg_st)
let qid = quest_id(rpg_st, name)
if qid < 0 { return }
quest_state[qid] = 2
rpg_st.quest_state[qid] = 2
emit QuestStarted(quest: qid)
}
@Namespace(Quest) function quest_state_of(name: pointer) -> int {
quest_ensure()
let qid = quest_id(name)
@Namespace(Quest) function quest_state_of(rpg_st: mut RpgState, name: pointer) -> int {
quest_ensure(rpg_st)
let qid = quest_id(rpg_st, name)
if qid < 0 { return -1 }
return quest_state[qid]
return rpg_st.quest_state[qid]
}
# is every objective of a quest satisfied?
function quest_all_done(qid: int) -> int {
function quest_all_done(rpg_st: RpgState, qid: int) -> int {
var i = 0
while i < obj_n {
if obj_qid[i] == qid { if obj_have[i] < obj_need[i] { return 0 } }
while i < rpg_st.obj_n {
if rpg_st.obj_qid[i] == qid { if rpg_st.obj_have[i] < rpg_st.obj_need[i] { return 0 } }
i += 1
}
return 1
@ -89,20 +79,20 @@ function quest_all_done(qid: int) -> int {
# Quest.notify(kind, target, amount) — report a gameplay signal. Every ACTIVE
# quest's matching objectives tick up (target 0 on the objective = "any"); a quest
# whose objectives are all met auto-completes.
@Namespace(Quest) function quest_notify(kind: int, target: int, amount: int) -> void {
quest_ensure()
@Namespace(Quest) function quest_notify(rpg_st: mut RpgState, kind: int, target: int, amount: int) -> void {
quest_ensure(rpg_st)
var i = 0
while i < obj_n {
let qid = obj_qid[i]
if quest_state[qid] == 2 {
if obj_kind[i] == kind {
if (obj_target[i] == 0) or (obj_target[i] == target) {
if obj_have[i] < obj_need[i] {
obj_have[i] += amount
if obj_have[i] > obj_need[i] { obj_have[i] = obj_need[i] }
emit ObjectiveProgressed(quest: qid, objective: i, have: obj_have[i], need: obj_need[i])
if quest_all_done(qid) == 1 {
quest_state[qid] = 3
while i < rpg_st.obj_n {
let qid = rpg_st.obj_qid[i]
if rpg_st.quest_state[qid] == 2 {
if rpg_st.obj_kind[i] == kind {
if (rpg_st.obj_target[i] == 0) or (rpg_st.obj_target[i] == target) {
if rpg_st.obj_have[i] < rpg_st.obj_need[i] {
rpg_st.obj_have[i] += amount
if rpg_st.obj_have[i] > rpg_st.obj_need[i] { rpg_st.obj_have[i] = rpg_st.obj_need[i] }
emit ObjectiveProgressed(quest: qid, objective: i, have: rpg_st.obj_have[i], need: rpg_st.obj_need[i])
if quest_all_done(rpg_st, qid) == 1 {
rpg_st.quest_state[qid] = 3
emit QuestCompleted(quest: qid)
}
}
@ -114,5 +104,5 @@ function quest_all_done(qid: int) -> int {
}
# ---- global flag store (branching / world state) --------------------------
@Namespace(Quest) function quest_flag(name: pointer, value: int) -> void { quest_ensure(); Dict.set(quest_flags, name, value) }
@Namespace(Quest) function quest_flag_get(name: pointer) -> int { quest_ensure(); return Dict.get_or(quest_flags, name, 0) }
@Namespace(Quest) function quest_flag(rpg_st: mut RpgState, name: pointer, value: int) -> void { quest_ensure(rpg_st); Dict.set(rpg_st.quest_flags, name, value) }
@Namespace(Quest) function quest_flag_get(rpg_st: mut RpgState, name: pointer) -> int { quest_ensure(rpg_st); return Dict.get_or(rpg_st.quest_flags, name, 0) }