refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9e4f4edb92
commit
f516148e46
15 changed files with 73 additions and 169 deletions
|
|
@ -23,10 +23,14 @@ event Died { e: int = 0, src: int = 0 }
|
||||||
event Healed { e: int = 0, amount: int = 0 }
|
event Healed { e: int = 0, amount: int = 0 }
|
||||||
event Crit { target: int = 0, src: int = 0 } # the attacker's crit_pct fired
|
event Crit { target: int = 0, src: int = 0 } # the attacker's crit_pct fired
|
||||||
|
|
||||||
|
# the faction table: FAC_MAX ids square, every pair unset (faction.ludic)
|
||||||
|
const FAC_MAX: int = 32
|
||||||
|
function fac_table() -> words { return words(FAC_MAX * FAC_MAX) }
|
||||||
|
|
||||||
state GameplayState {
|
state GameplayState {
|
||||||
combat_reflecting: bool = false # thorns never reflect thorns
|
combat_reflecting: bool = false # thorns never reflect thorns
|
||||||
combat_pending: int = 0
|
combat_pending: int = 0
|
||||||
fac_tbl: words = words(FAC_MAX * FAC_MAX)
|
fac_tbl: words = fac_table()
|
||||||
}
|
}
|
||||||
|
|
||||||
# the mutable channel for lever 6: listeners rewrite the pending damage here.
|
# the mutable channel for lever 6: listeners rewrite the pending damage here.
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,14 @@
|
||||||
|
|
||||||
property Faction { id: int = 0 }
|
property Faction { id: int = 0 }
|
||||||
|
|
||||||
const FAC_MAX: int = 32
|
|
||||||
const REL_FRIEND: int = 0
|
const REL_FRIEND: int = 0
|
||||||
const REL_NEUTRAL: int = 1
|
const REL_NEUTRAL: int = 1
|
||||||
const REL_HOSTILE: int = 2
|
const REL_HOSTILE: int = 2
|
||||||
|
|
||||||
# relationships stored as (rel + 1); a zero slot means "unset -> use the default".
|
# relationships stored as (rel + 1); a zero slot means "unset -> use the default"
|
||||||
# A global var initialiser must be a literal, so it is allocated lazily.
|
|
||||||
|
|
||||||
function fac_ensure(gameplay_st: mut GameplayState) -> void {
|
|
||||||
}
|
|
||||||
|
|
||||||
# Faction.set(a, b, rel) — record a symmetric relationship between two factions.
|
# Faction.set(a, b, rel) — record a symmetric relationship between two factions.
|
||||||
@Namespace(Faction) function faction_set(gameplay_st: mut GameplayState, a: int, b: int, rel: int) -> void {
|
@Namespace(Faction) function faction_set(gameplay_st: mut GameplayState, a: int, b: int, rel: int) -> void {
|
||||||
fac_ensure(gameplay_st)
|
|
||||||
if (a < 0) or (a >= FAC_MAX) { return }
|
if (a < 0) or (a >= FAC_MAX) { return }
|
||||||
if (b < 0) or (b >= FAC_MAX) { return }
|
if (b < 0) or (b >= FAC_MAX) { return }
|
||||||
gameplay_st.fac_tbl[a * FAC_MAX + b] = rel + 1
|
gameplay_st.fac_tbl[a * FAC_MAX + b] = rel + 1
|
||||||
|
|
@ -31,7 +25,6 @@ function fac_ensure(gameplay_st: mut GameplayState) -> void {
|
||||||
|
|
||||||
# Faction.rel(a, b) — the relationship code (0 friend, 1 neutral, 2 hostile).
|
# Faction.rel(a, b) — the relationship code (0 friend, 1 neutral, 2 hostile).
|
||||||
@Namespace(Faction) function faction_rel(gameplay_st: mut GameplayState, a: int, b: int) -> int {
|
@Namespace(Faction) function faction_rel(gameplay_st: mut GameplayState, a: int, b: int) -> int {
|
||||||
fac_ensure(gameplay_st)
|
|
||||||
if (a >= 0) and (a < FAC_MAX) and (b >= 0) and (b < FAC_MAX) {
|
if (a >= 0) and (a < FAC_MAX) and (b >= 0) and (b < FAC_MAX) {
|
||||||
let v = gameplay_st.fac_tbl[a * FAC_MAX + b]
|
let v = gameplay_st.fac_tbl[a * FAC_MAX + b]
|
||||||
if v != 0 { return v - 1 }
|
if v != 0 { return v - 1 }
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export state HintsState {
|
||||||
hn_foreign: []string = new []string # muted keys with no card in this build, written back as read
|
hn_foreign: []string = new []string # muted keys with no card in this build, written back as read
|
||||||
hn_shown: int = 3 # how many the rail holds
|
hn_shown: int = 3 # how many the rail holds
|
||||||
hn_every: float = 1.0 # seconds between passes
|
hn_every: float = 1.0 # seconds between passes
|
||||||
hn_top: []int = null # the cards on the rail this pass
|
hn_top: []int = hn_slots(3) # the cards on the rail this pass
|
||||||
hn_n: int = 0
|
hn_n: int = 0
|
||||||
hn_t: float = 0.0 # seconds to the next pass
|
hn_t: float = 0.0 # seconds to the next pass
|
||||||
hn_pick: int = 0 # which chip the key would open
|
hn_pick: int = 0 # which chip the key would open
|
||||||
|
|
@ -28,21 +28,19 @@ function hn_fact(hints_st: HintsState, what: int, card: int) -> void {
|
||||||
export function hints_config(hints_st: mut HintsState, shown: int, every: float) -> void {
|
export function hints_config(hints_st: mut HintsState, shown: int, every: float) -> void {
|
||||||
hints_st.hn_shown = shown
|
hints_st.hn_shown = shown
|
||||||
hints_st.hn_every = every
|
hints_st.hn_every = every
|
||||||
hints_st.hn_top = null
|
hints_st.hn_top = hn_slots(shown)
|
||||||
}
|
|
||||||
|
|
||||||
function hn_ensure(hints_st: mut HintsState) -> void {
|
|
||||||
if hints_st.hn_top == null or len(hints_st.hn_top) != hints_st.hn_shown {
|
|
||||||
hints_st.hn_top = new []int
|
|
||||||
for i in 0 .. hints_st.hn_shown { push(hints_st.hn_top, -1) }
|
|
||||||
}
|
}
|
||||||
|
# a rail of n empty places
|
||||||
|
function hn_slots(n: int) -> []int {
|
||||||
|
let out = new []int
|
||||||
|
for i in 0 .. n { push(out, -1) }
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
# the next pass comes on the next tick rather than in a second
|
# the next pass comes on the next tick rather than in a second
|
||||||
export function hints_refresh(hints_st: mut HintsState) -> void { hints_st.hn_t = 0.0 }
|
export function hints_refresh(hints_st: mut HintsState) -> void { hints_st.hn_t = 0.0 }
|
||||||
|
|
||||||
export function hints_tick(hints_st: mut HintsState, dt: float) -> void {
|
export function hints_tick(hints_st: mut HintsState, dt: float) -> void {
|
||||||
hn_ensure(hints_st)
|
|
||||||
hints_st.hn_t = hints_st.hn_t - dt
|
hints_st.hn_t = hints_st.hn_t - dt
|
||||||
if hints_st.hn_t > 0.0 { return }
|
if hints_st.hn_t > 0.0 { return }
|
||||||
hints_st.hn_t = hints_st.hn_every
|
hints_st.hn_t = hints_st.hn_every
|
||||||
|
|
@ -52,7 +50,6 @@ export function hints_tick(hints_st: mut HintsState, dt: float) -> void {
|
||||||
# one pass now: the rail re-sorted, the pick kept in range, and the card following its subject
|
# one pass now: the rail re-sorted, the pick kept in range, and the card following its subject
|
||||||
# along the rail - or put down when what it was about stopped being true
|
# along the rail - or put down when what it was about stopped being true
|
||||||
export function hints_sort(hints_st: mut HintsState) -> void {
|
export function hints_sort(hints_st: mut HintsState) -> void {
|
||||||
hn_ensure(hints_st)
|
|
||||||
hints_st.hn_n = 0
|
hints_st.hn_n = 0
|
||||||
let on = HintsWorld.showing()
|
let on = HintsWorld.showing()
|
||||||
for u in 0 .. 4 {
|
for u in 0 .. 4 {
|
||||||
|
|
@ -75,8 +72,7 @@ export function hints_sort(hints_st: mut HintsState) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
# the cards on the rail, most urgent first (empty while the rail is not showing)
|
# the cards on the rail, most urgent first (empty while the rail is not showing)
|
||||||
export function hints_rail(hints_st: mut HintsState) -> []int {
|
export function hints_rail(hints_st: HintsState) -> []int {
|
||||||
hn_ensure(hints_st)
|
|
||||||
let out = new []int
|
let out = new []int
|
||||||
if not HintsWorld.showing() { return out }
|
if not HintsWorld.showing() { return out }
|
||||||
for k in 0 .. hints_st.hn_n { push(out, hints_st.hn_top[k]) }
|
for k in 0 .. hints_st.hn_n { push(out, hints_st.hn_top[k]) }
|
||||||
|
|
@ -95,7 +91,7 @@ export function hints_pick(hints_st: HintsState) -> int { return hints_st.hn_pic
|
||||||
export function hints_hot(hints_st: HintsState, k: int) -> bool { return k == hints_st.hn_pick }
|
export function hints_hot(hints_st: HintsState, k: int) -> bool { return k == hints_st.hn_pick }
|
||||||
|
|
||||||
export function hints_reset(hints_st: mut HintsState) -> void {
|
export function hints_reset(hints_st: mut HintsState) -> void {
|
||||||
hints_st.hn_top = null
|
hints_st.hn_top = hn_slots(hints_st.hn_shown)
|
||||||
hints_st.hn_n = 0
|
hints_st.hn_n = 0
|
||||||
hints_st.hn_t = 0.0
|
hints_st.hn_t = 0.0
|
||||||
hints_st.hn_pick = 0
|
hints_st.hn_pick = 0
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,21 @@
|
||||||
# compressed (a PNG anything opens, with no zlib here): the shot is the PPM's pixels, filter 0
|
# compressed (a PNG anything opens, with no zlib here): the shot is the PPM's pixels, filter 0
|
||||||
# on every row, in deflate's stored blocks, with its CRCs and its Adler-32.
|
# on every row, in deflate's stored blocks, with its CRCs and its Adler-32.
|
||||||
|
|
||||||
function lp_table_init(lab_st: mut LabState) -> void {
|
# the CRC-32 table, made once with the state
|
||||||
if lab_st.lp_table != null { return }
|
function lp_crc_table() -> []long {
|
||||||
lab_st.lp_table = new []long
|
let t = new []long
|
||||||
let poly: long = 3988292384 # 0xEDB88320: eight hex digits would be a 32-bit pattern
|
let poly: long = 3988292384 # 0xEDB88320: eight hex digits would be a 32-bit pattern
|
||||||
for n in 0 .. 256 {
|
for n in 0 .. 256 {
|
||||||
var c = long(n)
|
var c = long(n)
|
||||||
for k in 0 .. 8 {
|
for k in 0 .. 8 {
|
||||||
if (c & long(1)) != long(0) { c = (c >> long(1)) ^ poly } else { c = c >> long(1) }
|
if (c & long(1)) != long(0) { c = (c >> long(1)) ^ poly } else { c = c >> long(1) }
|
||||||
}
|
}
|
||||||
push(lab_st.lp_table, c)
|
push(t, c)
|
||||||
}
|
}
|
||||||
|
return t
|
||||||
}
|
}
|
||||||
# CRC-32 of out[from .. from + n)
|
# CRC-32 of out[from .. from + n)
|
||||||
function lp_crc(lab_st: mut LabState, out: []byte, from: int, n: int) -> long {
|
function lp_crc(lab_st: mut LabState, out: []byte, from: int, n: int) -> long {
|
||||||
lp_table_init(lab_st)
|
|
||||||
let mask: long = 4294967295
|
let mask: long = 4294967295
|
||||||
var c = mask
|
var c = mask
|
||||||
for i in from .. from + n {
|
for i in from .. from + n {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
export state LabState {
|
export state LabState {
|
||||||
lb_plate: Actor = null
|
lb_plate: Actor = null
|
||||||
lb_plate_dir: string = ""
|
lb_plate_dir: string = ""
|
||||||
lp_table: []long = null
|
lp_table: []long = lp_crc_table()
|
||||||
lp_w: int = 0
|
lp_w: int = 0
|
||||||
lp_h: int = 0
|
lp_h: int = 0
|
||||||
lb_scene: string = ""
|
lb_scene: string = ""
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# census.ludic - making one (a slot reused before a new one, a look, a name, a start on its own
|
# census.ludic - making one (a slot reused before a new one, a look, a name, a start on its own
|
||||||
# ground out of the players' sight where that can be had), a post's fixed person, and leaving
|
# ground out of the players' sight where that can be had), a post's fixed person, and leaving
|
||||||
export function npc_spawn(npc_st: mut NpcState, kind: int) -> NpcPerson {
|
export function npc_spawn(npc_st: mut NpcState, kind: int) -> NpcPerson {
|
||||||
np_ensure()
|
|
||||||
if npc_st.np_n >= npc_st.np_max or kind < 0 or kind >= NPCK_COUNT { return null }
|
if npc_st.np_n >= npc_st.np_max or kind < 0 or kind >= NPCK_COUNT { return null }
|
||||||
var p: NpcPerson = null
|
var p: NpcPerson = null
|
||||||
for i in 0 .. len(npc_st.np_walkers) {
|
for i in 0 .. len(npc_st.np_walkers) {
|
||||||
|
|
@ -94,7 +93,6 @@ export function npc_set_at(p: NpcPerson, x: float, z: float) -> void {
|
||||||
# somebody fixed at a post (a vendor behind the counter): never walks, never counted, never leaves,
|
# somebody fixed at a post (a vendor behind the counter): never walks, never counted, never leaves,
|
||||||
# turns to a player within its kind's notice and back to `yaw` when they go
|
# turns to a player within its kind's notice and back to `yaw` when they go
|
||||||
export function npc_place(npc_st: mut NpcState, kind: int, x: float, z: float, yaw: float, name: string, look: int) -> NpcPerson {
|
export function npc_place(npc_st: mut NpcState, kind: int, x: float, z: float, yaw: float, name: string, look: int) -> NpcPerson {
|
||||||
np_ensure()
|
|
||||||
let p = new NpcPerson
|
let p = new NpcPerson
|
||||||
p.slot = len(npc_st.np_posts)
|
p.slot = len(npc_st.np_posts)
|
||||||
np_fresh(p, kind, look)
|
np_fresh(p, kind, look)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
# on: this machine's own walkers leave, and only the host's are drawn until it is turned off
|
# on: this machine's own walkers leave, and only the host's are drawn until it is turned off
|
||||||
export function npc_mirror(npc_st: mut NpcState, on: bool) -> void {
|
export function npc_mirror(npc_st: mut NpcState, on: bool) -> void {
|
||||||
np_ensure()
|
|
||||||
if npc_st.np_mirror == on { return }
|
if npc_st.np_mirror == on { return }
|
||||||
npc_st.np_mirror = on
|
npc_st.np_mirror = on
|
||||||
for i in 0 .. len(npc_st.np_walkers) { npc_retire(npc_st, npc_st.np_walkers[i]) }
|
for i in 0 .. len(npc_st.np_walkers) { npc_retire(npc_st, npc_st.np_walkers[i]) }
|
||||||
|
|
@ -14,7 +13,6 @@ export function npc_mirroring(npc_st: NpcState) -> bool { return npc_st.np_mirro
|
||||||
|
|
||||||
# the host's slot: somebody new in it (born), or nobody (gone)
|
# the host's slot: somebody new in it (born), or nobody (gone)
|
||||||
export function npc_put(npc_st: mut NpcState, slot: int, id: int, kind: int, look: int, seed: float, name: string, hx: float, hz: float) -> NpcPerson {
|
export function npc_put(npc_st: mut NpcState, slot: int, id: int, kind: int, look: int, seed: float, name: string, hx: float, hz: float) -> NpcPerson {
|
||||||
np_ensure()
|
|
||||||
if slot < 0 or slot >= npc_st.np_max or kind < 0 or kind >= NPCK_COUNT { return null }
|
if slot < 0 or slot >= npc_st.np_max or kind < 0 or kind >= NPCK_COUNT { return null }
|
||||||
while len(npc_st.np_walkers) <= slot {
|
while len(npc_st.np_walkers) <= slot {
|
||||||
let q = new NpcPerson
|
let q = new NpcPerson
|
||||||
|
|
@ -60,7 +58,6 @@ export function npc_heard(npc_st: NpcState, slot: int, x: float, y: float, z: fl
|
||||||
export function npc_heard_lately(npc_st: NpcState, p: NpcPerson) -> bool { return p.heard and npc_st.np_clock - p.heard_t <= 6.0 }
|
export function npc_heard_lately(npc_st: NpcState, p: NpcPerson) -> bool { return p.heard and npc_st.np_clock - p.heard_t <= 6.0 }
|
||||||
|
|
||||||
export function npc_mirror_tick(npc_st: mut NpcState, dt: float) -> void {
|
export function npc_mirror_tick(npc_st: mut NpcState, dt: float) -> void {
|
||||||
np_ensure()
|
|
||||||
npc_st.np_clock = npc_st.np_clock + dt
|
npc_st.np_clock = npc_st.np_clock + dt
|
||||||
let k = Math.min(1.0, dt * 8.0)
|
let k = Math.min(1.0, dt * 8.0)
|
||||||
for i in 0 .. len(npc_st.np_walkers) {
|
for i in 0 .. len(npc_st.np_walkers) {
|
||||||
|
|
|
||||||
|
|
@ -39,37 +39,30 @@ function np_fact(npc_st: NpcState, what: int, p: NpcPerson, player: int) -> NpcF
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
function np_ensure() -> void {
|
|
||||||
}
|
|
||||||
|
|
||||||
# how many walkers the place can hold at once
|
# how many walkers the place can hold at once
|
||||||
export function npc_config(npc_st: mut NpcState, max: int) -> void { npc_st.np_max = max }
|
export function npc_config(npc_st: mut NpcState, max: int) -> void { npc_st.np_max = max }
|
||||||
|
|
||||||
export function npc_seed(npc_st: NpcState, n: int) -> void {
|
export function npc_seed(npc_st: NpcState, n: int) -> void {
|
||||||
np_ensure()
|
|
||||||
rng_seed(npc_st.np_dice, n)
|
rng_seed(npc_st.np_dice, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
# 0 .. 1, and whole seconds lo .. hi, off the package's dice
|
# 0 .. 1, and whole seconds lo .. hi, off the package's dice
|
||||||
export function npc_rnd(npc_st: NpcState) -> float {
|
export function npc_rnd(npc_st: NpcState) -> float {
|
||||||
np_ensure()
|
|
||||||
return rng_float(npc_st.np_dice)
|
return rng_float(npc_st.np_dice)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npc_between(npc_st: NpcState, lo: int, hi: int) -> int {
|
export function npc_between(npc_st: NpcState, lo: int, hi: int) -> int {
|
||||||
np_ensure()
|
|
||||||
return rng_between(npc_st.np_dice, lo, hi)
|
return rng_between(npc_st.np_dice, lo, hi)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npc_secs(npc_st: NpcState, lo: int, hi: int) -> float { return float(npc_between(npc_st, lo, np_hi(lo, hi))) }
|
export function npc_secs(npc_st: NpcState, lo: int, hi: int) -> float { return float(npc_between(npc_st, lo, np_hi(lo, hi))) }
|
||||||
|
|
||||||
export function npc_walkers(npc_st: NpcState) -> []NpcPerson {
|
export function npc_walkers(npc_st: NpcState) -> []NpcPerson {
|
||||||
np_ensure()
|
|
||||||
return npc_st.np_walkers
|
return npc_st.np_walkers
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npc_posts(npc_st: NpcState) -> []NpcPerson {
|
export function npc_posts(npc_st: NpcState) -> []NpcPerson {
|
||||||
np_ensure()
|
|
||||||
return npc_st.np_posts
|
return npc_st.np_posts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,19 +72,16 @@ export function npc_max(npc_st: NpcState) -> int { return npc_st.np_max }
|
||||||
|
|
||||||
# a walker's slot or a post, and anyone by id
|
# a walker's slot or a post, and anyone by id
|
||||||
export function npc_at(npc_st: NpcState, slot: int) -> NpcPerson {
|
export function npc_at(npc_st: NpcState, slot: int) -> NpcPerson {
|
||||||
np_ensure()
|
|
||||||
if slot < 0 or slot >= len(npc_st.np_walkers) { return null }
|
if slot < 0 or slot >= len(npc_st.np_walkers) { return null }
|
||||||
return npc_st.np_walkers[slot]
|
return npc_st.np_walkers[slot]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npc_post(npc_st: NpcState, i: int) -> NpcPerson {
|
export function npc_post(npc_st: NpcState, i: int) -> NpcPerson {
|
||||||
np_ensure()
|
|
||||||
if i < 0 or i >= len(npc_st.np_posts) { return null }
|
if i < 0 or i >= len(npc_st.np_posts) { return null }
|
||||||
return npc_st.np_posts[i]
|
return npc_st.np_posts[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npc_by_id(npc_st: NpcState, id: int) -> NpcPerson {
|
export function npc_by_id(npc_st: NpcState, id: int) -> NpcPerson {
|
||||||
np_ensure()
|
|
||||||
for i in 0 .. len(npc_st.np_walkers) { if npc_st.np_walkers[i].active and npc_st.np_walkers[i].id == id { return npc_st.np_walkers[i] } }
|
for i in 0 .. len(npc_st.np_walkers) { if npc_st.np_walkers[i].active and npc_st.np_walkers[i].id == id { return npc_st.np_walkers[i] } }
|
||||||
for i in 0 .. len(npc_st.np_posts) { if npc_st.np_posts[i].active and npc_st.np_posts[i].id == id { return npc_st.np_posts[i] } }
|
for i in 0 .. len(npc_st.np_posts) { if npc_st.np_posts[i].active and npc_st.np_posts[i].id == id { return npc_st.np_posts[i] } }
|
||||||
return null
|
return null
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# tick.ludic - a step of everyone, on the machine that runs the place: the census on the half hour,
|
# tick.ludic - a step of everyone, on the machine that runs the place: the census on the half hour,
|
||||||
# the day's end (only ever out of every player's sight), the walk or the act, and who noticed whom
|
# the day's end (only ever out of every player's sight), the walk or the act, and who noticed whom
|
||||||
export function npc_tick(npc_st: mut NpcState, dt: float) -> void {
|
export function npc_tick(npc_st: mut NpcState, dt: float) -> void {
|
||||||
np_ensure()
|
|
||||||
for i in 0 .. len(npc_st.np_posts) { if npc_st.np_posts[i].active { np_step_post(npc_st, npc_st.np_posts[i], dt) } }
|
for i in 0 .. len(npc_st.np_posts) { if npc_st.np_posts[i].active { np_step_post(npc_st, npc_st.np_posts[i], dt) } }
|
||||||
if npc_st.np_hold or npc_st.np_mirror { return }
|
if npc_st.np_hold or npc_st.np_mirror { return }
|
||||||
np_census(npc_st)
|
np_census(npc_st)
|
||||||
|
|
@ -15,7 +14,6 @@ export function npc_tick(npc_st: mut NpcState, dt: float) -> void {
|
||||||
|
|
||||||
# the posts alone: a machine that does not run the place still turns its vendors to whoever comes
|
# the posts alone: a machine that does not run the place still turns its vendors to whoever comes
|
||||||
export function npc_posts_tick(npc_st: mut NpcState, dt: float) -> void {
|
export function npc_posts_tick(npc_st: mut NpcState, dt: float) -> void {
|
||||||
np_ensure()
|
|
||||||
for i in 0 .. len(npc_st.np_posts) { if npc_st.np_posts[i].active { np_step_post(npc_st, npc_st.np_posts[i], dt) } }
|
for i in 0 .. len(npc_st.np_posts) { if npc_st.np_posts[i].active { np_step_post(npc_st, npc_st.np_posts[i], dt) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +80,6 @@ export function npc_hold(npc_st: mut NpcState, on: bool) -> void { npc_st.np_hol
|
||||||
|
|
||||||
# everyone gone, as a new trip starts; the posts are the place's and stay
|
# everyone gone, as a new trip starts; the posts are the place's and stay
|
||||||
export function npc_reset(npc_st: mut NpcState) -> void {
|
export function npc_reset(npc_st: mut NpcState) -> void {
|
||||||
np_ensure()
|
|
||||||
for i in 0 .. len(npc_st.np_walkers) { npc_retire(npc_st, npc_st.np_walkers[i]) }
|
for i in 0 .. len(npc_st.np_walkers) { npc_retire(npc_st, npc_st.np_walkers[i]) }
|
||||||
npc_st.np_n = 0
|
npc_st.np_n = 0
|
||||||
npc_st.np_next_h = 0.0
|
npc_st.np_next_h = 0.0
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,12 @@ const PREFS_MAX: int = 64
|
||||||
|
|
||||||
state PrefsState {
|
state PrefsState {
|
||||||
prefs_path: pointer = null
|
prefs_path: pointer = null
|
||||||
prefs_names: pointers = null
|
prefs_names: pointers = pointers(PREFS_MAX)
|
||||||
prefs_vals: words = null
|
prefs_vals: words = words(PREFS_MAX)
|
||||||
prefs_n: int = 0
|
prefs_n: int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Prefs {
|
namespace Prefs {
|
||||||
internal function init(prefs_st: mut PrefsState) -> void {
|
|
||||||
if prefs_st.prefs_names != null { return }
|
|
||||||
prefs_st.prefs_names = pointers(PREFS_MAX)
|
|
||||||
prefs_st.prefs_vals = words(PREFS_MAX)
|
|
||||||
prefs_st.prefs_n = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
internal function find(prefs_st: PrefsState, key: pointer) -> int {
|
internal function find(prefs_st: PrefsState, key: pointer) -> int {
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -31,7 +25,6 @@ namespace Prefs {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get(prefs_st: mut PrefsState, key: pointer, fallback: int) -> int {
|
export function get(prefs_st: mut PrefsState, key: pointer, fallback: int) -> int {
|
||||||
init(prefs_st)
|
|
||||||
let i = find(prefs_st, key)
|
let i = find(prefs_st, key)
|
||||||
if i < 0 { return fallback }
|
if i < 0 { return fallback }
|
||||||
return prefs_st.prefs_vals[i]
|
return prefs_st.prefs_vals[i]
|
||||||
|
|
@ -45,7 +38,6 @@ namespace Prefs {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function set(prefs_st: mut PrefsState, key: pointer, value: int) -> void {
|
export function set(prefs_st: mut PrefsState, key: pointer, value: int) -> void {
|
||||||
init(prefs_st)
|
|
||||||
var i = find(prefs_st, key)
|
var i = find(prefs_st, key)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
if prefs_st.prefs_n >= PREFS_MAX { return }
|
if prefs_st.prefs_n >= PREFS_MAX { return }
|
||||||
|
|
@ -89,7 +81,6 @@ namespace Prefs {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function load(prefs_st: mut PrefsState, path: pointer) -> void {
|
export function load(prefs_st: mut PrefsState, path: pointer) -> void {
|
||||||
init(prefs_st)
|
|
||||||
prefs_st.prefs_path = path
|
prefs_st.prefs_path = path
|
||||||
let buf = read_all(path)
|
let buf = read_all(path)
|
||||||
if buf == null { return }
|
if buf == null { return }
|
||||||
|
|
@ -109,7 +100,6 @@ namespace Prefs {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function save(prefs_st: mut PrefsState) -> void {
|
export function save(prefs_st: mut PrefsState) -> void {
|
||||||
init(prefs_st)
|
|
||||||
if prefs_st.prefs_path == null { return }
|
if prefs_st.prefs_path == null { return }
|
||||||
let f = file_open(prefs_st.prefs_path, "wb")
|
let f = file_open(prefs_st.prefs_path, "wb")
|
||||||
if f == null { return }
|
if f == null { return }
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,12 @@ const CRAFT_MAX: int = 128
|
||||||
const CRAFT_ING_MAX: int = 512
|
const CRAFT_ING_MAX: int = 512
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event cancellable CraftRequested { e: int = 0, recipe: int = 0 }
|
event cancellable CraftRequested { e: int = 0, recipe: int = 0 }
|
||||||
event CraftCompleted { 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.
|
# Craft.recipe(name, out_item, out_qty) — declare a recipe; returns its id.
|
||||||
@Namespace(Craft) function craft_recipe(rpg_st: mut RpgState, name: pointer, out_item: pointer, out_qty: int) -> int {
|
@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 }
|
if rpg_st.recipe_n >= CRAFT_MAX { return -1 }
|
||||||
let id = rpg_st.recipe_n
|
let id = rpg_st.recipe_n
|
||||||
rpg_st.recipe_out[id] = out_item; rpg_st.recipe_outqty[id] = out_qty
|
rpg_st.recipe_out[id] = out_item; rpg_st.recipe_outqty[id] = out_qty
|
||||||
|
|
@ -31,10 +23,9 @@ event CraftCompleted { e: int = 0, recipe: int = 0 }
|
||||||
Dict.set(rpg_st.recipe_reg, name, id)
|
Dict.set(rpg_st.recipe_reg, name, id)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
@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) }
|
@Namespace(Craft) function craft_id(rpg_st: mut RpgState, name: pointer) -> int { return Dict.get_or(rpg_st.recipe_reg, name, -1) }
|
||||||
# Craft.ingredient(recipe_name, item, qty) — add an ingredient requirement.
|
# Craft.ingredient(recipe_name, item, qty) — add an ingredient requirement.
|
||||||
@Namespace(Craft) function craft_ingredient(rpg_st: mut RpgState, recipe_name: pointer, item: pointer, qty: int) -> void {
|
@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)
|
let rid = craft_id(rpg_st, recipe_name)
|
||||||
if rid < 0 { return }
|
if rid < 0 { return }
|
||||||
if rpg_st.ing_n >= CRAFT_ING_MAX { return }
|
if rpg_st.ing_n >= CRAFT_ING_MAX { return }
|
||||||
|
|
@ -44,7 +35,6 @@ event CraftCompleted { e: int = 0, recipe: int = 0 }
|
||||||
|
|
||||||
# Craft.can(e, recipe_name) — does the owner hold every ingredient?
|
# Craft.can(e, recipe_name) — does the owner hold every ingredient?
|
||||||
@Namespace(Craft) function craft_can(rpg_st: mut RpgState, e: int, recipe_name: pointer) -> int {
|
@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)
|
let rid = craft_id(rpg_st, recipe_name)
|
||||||
if rid < 0 { return 0 }
|
if rid < 0 { return 0 }
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -59,7 +49,6 @@ event CraftCompleted { e: int = 0, recipe: int = 0 }
|
||||||
|
|
||||||
# Craft.make(e, recipe_name) — consume ingredients + produce output; 1 on success.
|
# Craft.make(e, recipe_name) — consume ingredients + produce output; 1 on success.
|
||||||
@Namespace(Craft) function craft_make(rpg_st: mut RpgState, e: int, recipe_name: pointer) -> int {
|
@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)
|
let rid = craft_id(rpg_st, recipe_name)
|
||||||
if rid < 0 { return 0 }
|
if rid < 0 { return 0 }
|
||||||
if craft_can(rpg_st, e, recipe_name) == 0 { return 0 }
|
if craft_can(rpg_st, e, recipe_name) == 0 { return 0 }
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,6 @@ const DLG_NODE_MAX: int = 512
|
||||||
const DLG_CHOICE_MAX: int = 1024
|
const DLG_CHOICE_MAX: int = 1024
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property Dialog { tree: int = 0, node: int = 0, active: int = 0 }
|
property Dialog { tree: int = 0, node: int = 0, active: int = 0 }
|
||||||
|
|
||||||
|
|
@ -27,7 +19,6 @@ event DialogEnded { e: int = 0 }
|
||||||
|
|
||||||
# Dialog.tree(name) — register (or fetch) a tree id.
|
# Dialog.tree(name) — register (or fetch) a tree id.
|
||||||
@Namespace(Dialog) function dialog_tree(rpg_st: mut RpgState, name: pointer) -> int {
|
@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)
|
let ex = Dict.get_or(rpg_st.dlg_reg, name, -1)
|
||||||
if ex >= 0 { return ex }
|
if ex >= 0 { return ex }
|
||||||
if rpg_st.dlg_tree_n >= DLG_TREE_MAX { return -1 }
|
if rpg_st.dlg_tree_n >= DLG_TREE_MAX { return -1 }
|
||||||
|
|
@ -40,7 +31,6 @@ event DialogEnded { e: int = 0 }
|
||||||
# Dialog.node(tree_name, text) — add a line node; the first node added to a tree is
|
# Dialog.node(tree_name, text) — add a line node; the first node added to a tree is
|
||||||
# its entry point. Returns the node id.
|
# its entry point. Returns the node id.
|
||||||
@Namespace(Dialog) function dialog_node(rpg_st: mut RpgState, tree_name: pointer, text: pointer) -> int {
|
@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)
|
let tid = dialog_tree(rpg_st, tree_name)
|
||||||
if tid < 0 { return -1 }
|
if tid < 0 { return -1 }
|
||||||
if rpg_st.dlg_node_n >= DLG_NODE_MAX { return -1 }
|
if rpg_st.dlg_node_n >= DLG_NODE_MAX { return -1 }
|
||||||
|
|
@ -52,7 +42,6 @@ event DialogEnded { e: int = 0 }
|
||||||
}
|
}
|
||||||
# Dialog.choice(node, label, next_node) — add a branch (next_node -1 ends).
|
# Dialog.choice(node, label, next_node) — add a branch (next_node -1 ends).
|
||||||
@Namespace(Dialog) function dialog_choice(rpg_st: mut RpgState, node: int, label: pointer, next_node: int) -> int {
|
@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 }
|
if rpg_st.dlg_choice_n >= DLG_CHOICE_MAX { return -1 }
|
||||||
let id = rpg_st.dlg_choice_n
|
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_node[id] = node; rpg_st.dlg_choice_label[id] = label; rpg_st.dlg_choice_next[id] = next_node
|
||||||
|
|
@ -71,7 +60,6 @@ function dlg_set(e: int, name: pointer, v: int) -> void {
|
||||||
|
|
||||||
# Dialog.start(e, tree_name) — open the tree on a speaker (DialogStarted + Line).
|
# Dialog.start(e, tree_name) — open the tree on a speaker (DialogStarted + Line).
|
||||||
@Namespace(Dialog) function dialog_start(rpg_st: mut RpgState, e: int, tree_name: pointer) -> void {
|
@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)
|
let tid = dialog_tree(rpg_st, tree_name)
|
||||||
if tid < 0 { return }
|
if tid < 0 { return }
|
||||||
dlg_set(e, "tree", tid)
|
dlg_set(e, "tree", tid)
|
||||||
|
|
@ -87,7 +75,6 @@ function dlg_set(e: int, name: pointer, v: int) -> void {
|
||||||
}
|
}
|
||||||
# Dialog.count(e) — number of choices at the current node.
|
# Dialog.count(e) — number of choices at the current node.
|
||||||
@Namespace(Dialog) function dialog_count(rpg_st: mut RpgState, e: int) -> int {
|
@Namespace(Dialog) function dialog_count(rpg_st: mut RpgState, e: int) -> int {
|
||||||
dlg_ensure(rpg_st)
|
|
||||||
if dlg_get(e, "active") == 0 { return 0 }
|
if dlg_get(e, "active") == 0 { return 0 }
|
||||||
let node = dlg_get(e, "node")
|
let node = dlg_get(e, "node")
|
||||||
var n = 0; var i = 0
|
var n = 0; var i = 0
|
||||||
|
|
@ -96,7 +83,6 @@ function dlg_set(e: int, name: pointer, v: int) -> void {
|
||||||
}
|
}
|
||||||
# Dialog.choose(e, i) — take the i-th choice (veto-able); advance or end.
|
# Dialog.choose(e, i) — take the i-th choice (veto-able); advance or end.
|
||||||
@Namespace(Dialog) function dialog_choose(rpg_st: mut RpgState, e: int, idx: int) -> int {
|
@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 }
|
if dlg_get(e, "active") == 0 { return 0 }
|
||||||
let node = dlg_get(e, "node")
|
let node = dlg_get(e, "node")
|
||||||
var seen = 0; var i = 0; var pick = -1
|
var seen = 0; var i = 0; var pick = -1
|
||||||
|
|
|
||||||
|
|
@ -11,52 +11,45 @@ const INV_MAX: int = 256
|
||||||
|
|
||||||
# ---- item registry --------------------------------------------------------
|
# ---- item registry --------------------------------------------------------
|
||||||
state RpgState {
|
state RpgState {
|
||||||
recipe_reg: Dict = null # recipe name -> id
|
recipe_reg: Dict = Dict.new() # recipe name -> id
|
||||||
recipe_n: int = 0
|
recipe_n: int = 0
|
||||||
recipe_out: pointers = null # output item name
|
recipe_out: pointers = pointers(CRAFT_MAX) # output item name
|
||||||
recipe_outqty: words = null
|
recipe_outqty: words = words(CRAFT_MAX)
|
||||||
ing_n: int = 0
|
ing_n: int = 0
|
||||||
ing_rid: words = null # owning recipe id
|
ing_rid: words = words(CRAFT_ING_MAX) # owning recipe id
|
||||||
ing_name: pointers = null # ingredient item name
|
ing_name: pointers = pointers(CRAFT_ING_MAX) # ingredient item name
|
||||||
ing_qty: words = null
|
ing_qty: words = words(CRAFT_ING_MAX)
|
||||||
dlg_reg: Dict = null # tree name -> tree id
|
dlg_reg: Dict = Dict.new() # tree name -> tree id
|
||||||
dlg_tree_n: int = 0
|
dlg_tree_n: int = 0
|
||||||
dlg_tree_start: words = null # first node id per tree
|
dlg_tree_start: words = words(DLG_TREE_MAX) # first node id per tree
|
||||||
dlg_node_n: int = 0
|
dlg_node_n: int = 0
|
||||||
dlg_node_tree: words = null
|
dlg_node_tree: words = words(DLG_NODE_MAX)
|
||||||
dlg_node_text: pointers = null
|
dlg_node_text: pointers = pointers(DLG_NODE_MAX)
|
||||||
dlg_choice_n: int = 0
|
dlg_choice_n: int = 0
|
||||||
dlg_choice_node: words = null # owning node
|
dlg_choice_node: words = words(DLG_CHOICE_MAX) # owning node
|
||||||
dlg_choice_label: pointers = null
|
dlg_choice_label: pointers = pointers(DLG_CHOICE_MAX)
|
||||||
dlg_choice_next: words = null # next node id, or -1 to end
|
dlg_choice_next: words = words(DLG_CHOICE_MAX) # next node id, or -1 to end
|
||||||
item_reg: Dict = null
|
item_reg: Dict = Dict.new()
|
||||||
item_n: int = 0
|
item_n: int = 0
|
||||||
item_stackmax: words = null
|
item_stackmax: words = words(INV_MAX)
|
||||||
item_weight: words = null
|
item_weight: words = words(INV_MAX)
|
||||||
item_value: words = null
|
item_value: words = words(INV_MAX)
|
||||||
inv_counts: Dict = null
|
inv_counts: Dict = Dict.new()
|
||||||
quest_reg: Dict = null # quest name -> id
|
quest_reg: Dict = Dict.new() # quest name -> id
|
||||||
quest_n: int = 0
|
quest_n: int = 0
|
||||||
quest_state: words = null
|
quest_state: words = words(QUEST_MAX)
|
||||||
obj_n: int = 0
|
obj_n: int = 0
|
||||||
obj_qid: words = null # owning quest id
|
obj_qid: words = words(OBJ_MAX) # owning quest id
|
||||||
obj_kind: words = null
|
obj_kind: words = words(OBJ_MAX)
|
||||||
obj_target: words = null # a target id/tag (kind-specific; 0 = any)
|
obj_target: words = words(OBJ_MAX) # a target id/tag (kind-specific; 0 = any)
|
||||||
obj_need: words = null
|
obj_need: words = words(OBJ_MAX)
|
||||||
obj_have: words = null
|
obj_have: words = words(OBJ_MAX)
|
||||||
quest_flags: Dict = null # global flag store
|
quest_flags: Dict = Dict.new() # global flag store
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
# 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 {
|
@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 }
|
if rpg_st.item_n >= INV_MAX { return -1 }
|
||||||
let id = rpg_st.item_n
|
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_stackmax[id] = stack_max; rpg_st.item_weight[id] = weight; rpg_st.item_value[id] = value
|
||||||
|
|
@ -64,7 +57,7 @@ function item_ensure(rpg_st: mut RpgState) -> void {
|
||||||
Dict.set(rpg_st.item_reg, name, id)
|
Dict.set(rpg_st.item_reg, name, id)
|
||||||
return id
|
return 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_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 {
|
@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 }
|
let id = item_id(rpg_st, name); if id < 0 { return 0 }
|
||||||
return rpg_st.item_value[id]
|
return rpg_st.item_value[id]
|
||||||
|
|
@ -73,7 +66,6 @@ function item_ensure(rpg_st: mut RpgState) -> void {
|
||||||
# ---- per-owner counts -----------------------------------------------------
|
# ---- per-owner counts -----------------------------------------------------
|
||||||
property Inventory { gold: int = 0, weight_max: int = 0, slots: int = 0 }
|
property Inventory { gold: int = 0, weight_max: int = 0, slots: int = 0 }
|
||||||
|
|
||||||
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}` }
|
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 ItemAdded { e: int = 0, item: int = 0, qty: int = 0 } # item = registry id
|
||||||
|
|
@ -82,14 +74,12 @@ event cancellable ItemUse { e: int = 0, item: int = 0 } # veto a use (
|
||||||
|
|
||||||
# Inventory.add(e, name, n) — add n of an item; emits ItemAdded.
|
# 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 {
|
@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)
|
let k = inv_key(e, name)
|
||||||
Dict.set(rpg_st.inv_counts, k, Dict.get_or(rpg_st.inv_counts, k, 0) + 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)
|
emit ItemAdded(e: e, item: item_id(rpg_st, name), qty: n)
|
||||||
}
|
}
|
||||||
# Inventory.count(e, name) — how many the owner holds.
|
# Inventory.count(e, name) — how many the owner holds.
|
||||||
@Namespace(Inventory) function inventory_count(rpg_st: mut RpgState, e: int, name: pointer) -> int {
|
@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)
|
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 {
|
@Namespace(Inventory) function inventory_has(rpg_st: mut RpgState, e: int, name: pointer) -> int {
|
||||||
|
|
@ -98,7 +88,6 @@ event cancellable ItemUse { e: int = 0, item: int = 0 } # veto a use (
|
||||||
}
|
}
|
||||||
# Inventory.remove(e, name, n) — remove up to n; returns 1 if it had enough.
|
# 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 {
|
@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 k = inv_key(e, name)
|
||||||
let have = Dict.get_or(rpg_st.inv_counts, k, 0)
|
let have = Dict.get_or(rpg_st.inv_counts, k, 0)
|
||||||
if have < n { return 0 }
|
if have < n { return 0 }
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,6 @@ const QUEST_MAX: int = 128
|
||||||
const OBJ_MAX: int = 512
|
const OBJ_MAX: int = 512
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event QuestStarted { quest: int = 0 }
|
event QuestStarted { quest: int = 0 }
|
||||||
event QuestCompleted { quest: int = 0 }
|
event QuestCompleted { quest: int = 0 }
|
||||||
|
|
@ -30,7 +22,6 @@ event ObjectiveProgressed { quest: int = 0, objective: int = 0, have: int = 0, n
|
||||||
|
|
||||||
# Quest.define(name) — register a quest (state hidden). Returns id.
|
# Quest.define(name) — register a quest (state hidden). Returns id.
|
||||||
@Namespace(Quest) function quest_define(rpg_st: mut RpgState, name: pointer) -> int {
|
@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 }
|
if rpg_st.quest_n >= QUEST_MAX { return -1 }
|
||||||
let id = rpg_st.quest_n
|
let id = rpg_st.quest_n
|
||||||
rpg_st.quest_state[id] = 0
|
rpg_st.quest_state[id] = 0
|
||||||
|
|
@ -38,10 +29,9 @@ event ObjectiveProgressed { quest: int = 0, objective: int = 0, have: int = 0, n
|
||||||
Dict.set(rpg_st.quest_reg, name, id)
|
Dict.set(rpg_st.quest_reg, name, id)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
@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) }
|
@Namespace(Quest) function quest_id(rpg_st: mut RpgState, name: pointer) -> int { return Dict.get_or(rpg_st.quest_reg, name, -1) }
|
||||||
# Quest.objective(quest_name, kind, target, need) — add an objective.
|
# Quest.objective(quest_name, kind, target, need) — add an objective.
|
||||||
@Namespace(Quest) function quest_objective(rpg_st: mut RpgState, quest_name: pointer, kind: int, target: int, need: int) -> int {
|
@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)
|
let qid = quest_id(rpg_st, quest_name)
|
||||||
if qid < 0 { return -1 }
|
if qid < 0 { return -1 }
|
||||||
if rpg_st.obj_n >= OBJ_MAX { return -1 }
|
if rpg_st.obj_n >= OBJ_MAX { return -1 }
|
||||||
|
|
@ -53,14 +43,12 @@ event ObjectiveProgressed { quest: int = 0, objective: int = 0, have: int = 0, n
|
||||||
}
|
}
|
||||||
# Quest.start(name) — activate a quest (emits QuestStarted).
|
# Quest.start(name) — activate a quest (emits QuestStarted).
|
||||||
@Namespace(Quest) function quest_start(rpg_st: mut RpgState, name: pointer) -> void {
|
@Namespace(Quest) function quest_start(rpg_st: mut RpgState, name: pointer) -> void {
|
||||||
quest_ensure(rpg_st)
|
|
||||||
let qid = quest_id(rpg_st, name)
|
let qid = quest_id(rpg_st, name)
|
||||||
if qid < 0 { return }
|
if qid < 0 { return }
|
||||||
rpg_st.quest_state[qid] = 2
|
rpg_st.quest_state[qid] = 2
|
||||||
emit QuestStarted(quest: qid)
|
emit QuestStarted(quest: qid)
|
||||||
}
|
}
|
||||||
@Namespace(Quest) function quest_state_of(rpg_st: mut RpgState, name: pointer) -> int {
|
@Namespace(Quest) function quest_state_of(rpg_st: mut RpgState, name: pointer) -> int {
|
||||||
quest_ensure(rpg_st)
|
|
||||||
let qid = quest_id(rpg_st, name)
|
let qid = quest_id(rpg_st, name)
|
||||||
if qid < 0 { return -1 }
|
if qid < 0 { return -1 }
|
||||||
return rpg_st.quest_state[qid]
|
return rpg_st.quest_state[qid]
|
||||||
|
|
@ -80,7 +68,6 @@ function quest_all_done(rpg_st: RpgState, qid: int) -> int {
|
||||||
# quest's matching objectives tick up (target 0 on the objective = "any"); a quest
|
# quest's matching objectives tick up (target 0 on the objective = "any"); a quest
|
||||||
# whose objectives are all met auto-completes.
|
# whose objectives are all met auto-completes.
|
||||||
@Namespace(Quest) function quest_notify(rpg_st: mut RpgState, kind: int, target: int, amount: int) -> void {
|
@Namespace(Quest) function quest_notify(rpg_st: mut RpgState, kind: int, target: int, amount: int) -> void {
|
||||||
quest_ensure(rpg_st)
|
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < rpg_st.obj_n {
|
while i < rpg_st.obj_n {
|
||||||
let qid = rpg_st.obj_qid[i]
|
let qid = rpg_st.obj_qid[i]
|
||||||
|
|
@ -104,5 +91,5 @@ function quest_all_done(rpg_st: RpgState, qid: int) -> int {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- global flag store (branching / world state) --------------------------
|
# ---- global flag store (branching / world state) --------------------------
|
||||||
@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(rpg_st: mut RpgState, name: pointer, value: int) -> void { 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) }
|
@Namespace(Quest) function quest_flag_get(rpg_st: mut RpgState, name: pointer) -> int { return Dict.get_or(rpg_st.quest_flags, name, 0) }
|
||||||
|
|
|
||||||
|
|
@ -70,36 +70,26 @@ event WeaponFired { owner: int = 0, def_id: int = 0 }
|
||||||
# Weapon registry (name-keyed, mod-friendly — add a gun with no code)
|
# Weapon registry (name-keyed, mod-friendly — add a gun with no code)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
state ShooterState {
|
state ShooterState {
|
||||||
wpn_reg: Dict = null
|
wpn_reg: Dict = Dict.new()
|
||||||
wpn_n: int = 0
|
wpn_n: int = 0
|
||||||
wpn_rate: words = null
|
wpn_rate: words = words(WPN_MAX)
|
||||||
wpn_dmg: words = null
|
wpn_dmg: words = words(WPN_MAX)
|
||||||
wpn_speed: words = null
|
wpn_speed: words = words(WPN_MAX)
|
||||||
wpn_spread: words = null
|
wpn_spread: words = words(WPN_MAX)
|
||||||
wpn_pellets: words = null
|
wpn_pellets: words = words(WPN_MAX)
|
||||||
wpn_pattern: words = null
|
wpn_pattern: words = words(WPN_MAX)
|
||||||
wpn_ttl: words = null
|
wpn_ttl: words = words(WPN_MAX)
|
||||||
wpn_pierce: words = null
|
wpn_pierce: words = words(WPN_MAX)
|
||||||
wpn_homing: words = null
|
wpn_homing: words = words(WPN_MAX)
|
||||||
wpn_color: words = null
|
wpn_color: words = words(WPN_MAX)
|
||||||
wpn_rate_base: words = null
|
wpn_rate_base: words = words(WPN_MAX)
|
||||||
wpn_fire_color: int = 0xffffff # the colour of the weapon being fired (sh_fire)
|
wpn_fire_color: int = 0xffffff # the colour of the weapon being fired (sh_fire)
|
||||||
}
|
}
|
||||||
|
|
||||||
function wpn_ensure(shooter_st: mut ShooterState) -> void {
|
|
||||||
if shooter_st.wpn_reg == null {
|
|
||||||
shooter_st.wpn_reg = Dict.new()
|
|
||||||
shooter_st.wpn_rate = words(WPN_MAX); shooter_st.wpn_dmg = words(WPN_MAX); shooter_st.wpn_speed = words(WPN_MAX)
|
|
||||||
shooter_st.wpn_spread = words(WPN_MAX); shooter_st.wpn_pellets = words(WPN_MAX); shooter_st.wpn_pattern = words(WPN_MAX)
|
|
||||||
shooter_st.wpn_ttl = words(WPN_MAX); shooter_st.wpn_pierce = words(WPN_MAX); shooter_st.wpn_homing = words(WPN_MAX)
|
|
||||||
shooter_st.wpn_color = words(WPN_MAX); shooter_st.wpn_rate_base = words(WPN_MAX)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Weapon.def(name, fire_rate, damage, speed, spread, pellets, pattern) — register a
|
# Weapon.def(name, fire_rate, damage, speed, spread, pellets, pattern) — register a
|
||||||
# gun. pattern: 0 single/aimed, 1 spread cone, 2 ring, 3 spiral. Returns the def id.
|
# gun. pattern: 0 single/aimed, 1 spread cone, 2 ring, 3 spiral. Returns the def id.
|
||||||
@Namespace(Weapon) function weapon_def(shooter_st: mut ShooterState, name: pointer, fire_rate: int, damage: int, speed: int, spread: int, pellets: int, pattern: int) -> int {
|
@Namespace(Weapon) function weapon_def(shooter_st: mut ShooterState, name: pointer, fire_rate: int, damage: int, speed: int, spread: int, pellets: int, pattern: int) -> int {
|
||||||
wpn_ensure(shooter_st)
|
|
||||||
if shooter_st.wpn_n >= WPN_MAX { return -1 }
|
if shooter_st.wpn_n >= WPN_MAX { return -1 }
|
||||||
let id = shooter_st.wpn_n
|
let id = shooter_st.wpn_n
|
||||||
shooter_st.wpn_rate[id] = fire_rate; shooter_st.wpn_dmg[id] = damage; shooter_st.wpn_speed[id] = speed
|
shooter_st.wpn_rate[id] = fire_rate; shooter_st.wpn_dmg[id] = damage; shooter_st.wpn_speed[id] = speed
|
||||||
|
|
@ -112,36 +102,35 @@ function wpn_ensure(shooter_st: mut ShooterState) -> void {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
@Namespace(Weapon) function weapon_id(shooter_st: mut ShooterState, name: pointer) -> int {
|
@Namespace(Weapon) function weapon_id(shooter_st: mut ShooterState, name: pointer) -> int {
|
||||||
wpn_ensure(shooter_st)
|
|
||||||
return Dict.get_or(shooter_st.wpn_reg, name, -1)
|
return Dict.get_or(shooter_st.wpn_reg, name, -1)
|
||||||
}
|
}
|
||||||
# Weapon.set_rate(id, frames) — frames between shots (a fire-rate upgrade changes it in place).
|
# Weapon.set_rate(id, frames) — frames between shots (a fire-rate upgrade changes it in place).
|
||||||
@Namespace(Weapon) function weapon_set_rate(shooter_st: mut ShooterState, id: int, frames: int) -> void {
|
@Namespace(Weapon) function weapon_set_rate(shooter_st: mut ShooterState, id: int, frames: int) -> void {
|
||||||
wpn_ensure(shooter_st); if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_rate[id] = frames }
|
if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_rate[id] = frames }
|
||||||
}
|
}
|
||||||
@Namespace(Weapon) function weapon_rate(shooter_st: mut ShooterState, id: int) -> int {
|
@Namespace(Weapon) function weapon_rate(shooter_st: mut ShooterState, id: int) -> int {
|
||||||
wpn_ensure(shooter_st); if (id >= 0) and (id < WPN_MAX) { return shooter_st.wpn_rate[id] }; return 0
|
if (id >= 0) and (id < WPN_MAX) { return shooter_st.wpn_rate[id] }; return 0
|
||||||
}
|
}
|
||||||
# Weapon.reset(id) — back to the definition: no pierce, no homing, the defined fire rate.
|
# Weapon.reset(id) — back to the definition: no pierce, no homing, the defined fire rate.
|
||||||
@Namespace(Weapon) function weapon_reset(shooter_st: mut ShooterState, id: int) -> void {
|
@Namespace(Weapon) function weapon_reset(shooter_st: mut ShooterState, id: int) -> void {
|
||||||
wpn_ensure(shooter_st); if (id < 0) or (id >= WPN_MAX) { return }
|
if (id < 0) or (id >= WPN_MAX) { return }
|
||||||
shooter_st.wpn_pierce[id] = 0; shooter_st.wpn_homing[id] = 0; shooter_st.wpn_rate[id] = shooter_st.wpn_rate_base[id]
|
shooter_st.wpn_pierce[id] = 0; shooter_st.wpn_homing[id] = 0; shooter_st.wpn_rate[id] = shooter_st.wpn_rate_base[id]
|
||||||
}
|
}
|
||||||
# Weapon.set_color(id, color) — the colour the engine draws this weapon's shots in.
|
# Weapon.set_color(id, color) — the colour the engine draws this weapon's shots in.
|
||||||
@Namespace(Weapon) function weapon_set_color(shooter_st: mut ShooterState, id: int, color: int) -> void {
|
@Namespace(Weapon) function weapon_set_color(shooter_st: mut ShooterState, id: int, color: int) -> void {
|
||||||
wpn_ensure(shooter_st); if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_color[id] = color }
|
if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_color[id] = color }
|
||||||
}
|
}
|
||||||
# Weapon.set_ttl(id, frames) — projectile lifetime for a weapon (range control).
|
# Weapon.set_ttl(id, frames) — projectile lifetime for a weapon (range control).
|
||||||
@Namespace(Weapon) function weapon_set_ttl(shooter_st: mut ShooterState, id: int, frames: int) -> void {
|
@Namespace(Weapon) function weapon_set_ttl(shooter_st: mut ShooterState, id: int, frames: int) -> void {
|
||||||
wpn_ensure(shooter_st); if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_ttl[id] = frames }
|
if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_ttl[id] = frames }
|
||||||
}
|
}
|
||||||
# Weapon.set_pierce(id, n) — a shot passes through n targets before it dies.
|
# Weapon.set_pierce(id, n) — a shot passes through n targets before it dies.
|
||||||
@Namespace(Weapon) function weapon_set_pierce(shooter_st: mut ShooterState, id: int, n: int) -> void {
|
@Namespace(Weapon) function weapon_set_pierce(shooter_st: mut ShooterState, id: int, n: int) -> void {
|
||||||
wpn_ensure(shooter_st); if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_pierce[id] = n }
|
if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_pierce[id] = n }
|
||||||
}
|
}
|
||||||
# Weapon.set_homing(id, deg) — max degrees/frame a shot turns toward an enemy.
|
# Weapon.set_homing(id, deg) — max degrees/frame a shot turns toward an enemy.
|
||||||
@Namespace(Weapon) function weapon_set_homing(shooter_st: mut ShooterState, id: int, deg: int) -> void {
|
@Namespace(Weapon) function weapon_set_homing(shooter_st: mut ShooterState, id: int, deg: int) -> void {
|
||||||
wpn_ensure(shooter_st); if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_homing[id] = deg }
|
if (id >= 0) and (id < WPN_MAX) { shooter_st.wpn_homing[id] = deg }
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -534,7 +523,6 @@ function sh_spawn_bullet(shooter_st: ShooterState, owner: int, fac: int, px: int
|
||||||
@EngineSystem(Weapon, Update) function esys_weapon(shooter_st: mut ShooterState) -> void {
|
@EngineSystem(Weapon, Update) function esys_weapon(shooter_st: mut ShooterState) -> void {
|
||||||
let P = World.prop_id("Weapon")
|
let P = World.prop_id("Weapon")
|
||||||
if P < 0 { return }
|
if P < 0 { return }
|
||||||
wpn_ensure(shooter_st)
|
|
||||||
var e = World.query_next(P, 0)
|
var e = World.query_next(P, 0)
|
||||||
while e >= 0 {
|
while e >= 0 {
|
||||||
let cd = World.get(e, P, World.field_id(P, "cooldown"))
|
let cd = World.get(e, P, World.field_id(P, "cooldown"))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue