merge lang/foundations into lang/native-jolt (seeds regenerated)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
b247603b7e
62 changed files with 41302 additions and 40864 deletions
4
changes/near-fade-spirv.md
Normal file
4
changes/near-fade-spirv.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
**The foliage's near-fade programs have SPIR-V.** The three `NEAR_FADE` variants were missing
|
||||||
|
from `variants.list`, so on Vulkan the trees' and cards' programs failed to build.
|
||||||
10
changes/vulkan-on-macos.md
Normal file
10
changes/vulkan-on-macos.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
bump: minor
|
||||||
|
type: feature
|
||||||
|
**render3d's Vulkan renderer runs in a macOS window, through MoltenVK.** `R3D_GFX=vk` (or
|
||||||
|
`gpu_request("vulkan")`) now presents to a `CAMetalLayer` on the window (`VK_EXT_metal_surface`,
|
||||||
|
the runtime's `win_metal_layer()`), where it used to fall back to OpenGL. The loader also opens
|
||||||
|
MoltenVK on its own - `Contents/Frameworks/libMoltenVK.dylib` in a bundle, beside the executable,
|
||||||
|
or `$VULKAN_SDK/lib` - so a game needs no Vulkan SDK to ship it. A covered window is not drawn to
|
||||||
|
(its layer would hold every frame for a second), and on macOS one frame stays in flight so the CPU
|
||||||
|
runs the next tick while the GPU draws. OpenGL stays the default: on an M4 Pro the Vulkan frame is
|
||||||
|
still about a third slower, most of it in the alpha-tested foliage.
|
||||||
7
changes/window-buffers.md
Normal file
7
changes/window-buffers.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
**A windowed program's input no longer stops it on the first frame.** Since slices carry their
|
||||||
|
length, the window's built-ins (`win_held`, `win_mouse`, `win_pad`, `win_touch`, `win_text`,
|
||||||
|
`win_present`) were handed the slice's header instead of its elements, so the platform wrote the
|
||||||
|
mouse position over the length and the next read failed its bounds check. They take the elements,
|
||||||
|
as an `extern` call already did.
|
||||||
|
|
@ -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 }
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,18 @@
|
||||||
# mute.ludic - the cards a player has said they know, kept BY KEY: a card added, removed or moved in
|
# mute.ludic - the cards a player has said they know, kept BY KEY: a card added, removed or moved in
|
||||||
# the registry leaves every other mute where it was, and a key this build does not know is carried
|
# the registry leaves every other mute where it was, and a key this build does not know is carried
|
||||||
|
|
||||||
function hn_mute_ensure(hints_st: mut HintsState) -> void {
|
|
||||||
if hints_st.hn_mute != null { return }
|
|
||||||
hints_st.hn_mute = new []bool
|
|
||||||
for i in 0 .. HINT_COUNT { push(hints_st.hn_mute, false) }
|
|
||||||
hints_st.hn_foreign = new []string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hints_muted(hints_st: mut HintsState, id: int) -> bool {
|
export function hints_muted(hints_st: HintsState, id: int) -> bool {
|
||||||
hn_mute_ensure(hints_st)
|
|
||||||
if id < 0 or id >= HINT_COUNT { return false }
|
if id < 0 or id >= HINT_COUNT { return false }
|
||||||
return hints_st.hn_mute[id]
|
return hints_st.hn_mute[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hints_mute(hints_st: mut HintsState, id: int, on: bool) -> void {
|
export function hints_mute(hints_st: mut HintsState, id: int, on: bool) -> void {
|
||||||
hn_mute_ensure(hints_st)
|
|
||||||
if id < 0 or id >= HINT_COUNT { return }
|
if id < 0 or id >= HINT_COUNT { return }
|
||||||
hints_st.hn_mute[id] = on
|
hints_st.hn_mute[id] = on
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hints_mute_count(hints_st: mut HintsState) -> int {
|
export function hints_mute_count(hints_st: HintsState) -> int {
|
||||||
hn_mute_ensure(hints_st)
|
|
||||||
var n = 0
|
var n = 0
|
||||||
for i in 0 .. HINT_COUNT { if hints_st.hn_mute[i] { n += 1 } }
|
for i in 0 .. HINT_COUNT { if hints_st.hn_mute[i] { n += 1 } }
|
||||||
return n
|
return n
|
||||||
|
|
@ -29,7 +20,6 @@ export function hints_mute_count(hints_st: mut HintsState) -> int {
|
||||||
|
|
||||||
# every muted card given back, from the next pass
|
# every muted card given back, from the next pass
|
||||||
export function hints_unmute_all(hints_st: mut HintsState) -> void {
|
export function hints_unmute_all(hints_st: mut HintsState) -> void {
|
||||||
hn_mute_ensure(hints_st)
|
|
||||||
for i in 0 .. HINT_COUNT { hints_st.hn_mute[i] = false }
|
for i in 0 .. HINT_COUNT { hints_st.hn_mute[i] = false }
|
||||||
hints_st.hn_foreign = new []string
|
hints_st.hn_foreign = new []string
|
||||||
hints_st.hn_t = 0.0
|
hints_st.hn_t = 0.0
|
||||||
|
|
@ -37,8 +27,7 @@ export function hints_unmute_all(hints_st: mut HintsState) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
# the mutes as a list of keys, for wherever the game keeps the player's own choices
|
# the mutes as a list of keys, for wherever the game keeps the player's own choices
|
||||||
export function hints_mutes_save(hints_st: mut HintsState) -> Val {
|
export function hints_mutes_save(hints_st: HintsState) -> Val {
|
||||||
hn_mute_ensure(hints_st)
|
|
||||||
let l = Value.list()
|
let l = Value.list()
|
||||||
for i in 0 .. HINT_COUNT { if hints_st.hn_mute[i] { Value.add(l, Value.str(Hints[i].key)) } }
|
for i in 0 .. HINT_COUNT { if hints_st.hn_mute[i] { Value.add(l, Value.str(Hints[i].key)) } }
|
||||||
for i in 0 .. len(hints_st.hn_foreign) { Value.add(l, Value.str(hints_st.hn_foreign[i])) }
|
for i in 0 .. len(hints_st.hn_foreign) { Value.add(l, Value.str(hints_st.hn_foreign[i])) }
|
||||||
|
|
@ -47,8 +36,7 @@ export function hints_mutes_save(hints_st: mut HintsState) -> Val {
|
||||||
|
|
||||||
# a list of keys read back; anything that is not a list is no mutes
|
# a list of keys read back; anything that is not a list is no mutes
|
||||||
export function hints_mutes_load(hints_st: mut HintsState, l: Val) -> void {
|
export function hints_mutes_load(hints_st: mut HintsState, l: Val) -> void {
|
||||||
hints_st.hn_mute = null
|
hints_st.hn_mute = hn_mutes()
|
||||||
hn_mute_ensure(hints_st)
|
|
||||||
if l == null or Value.kind(l) != 5 { return }
|
if l == null or Value.kind(l) != 5 { return }
|
||||||
for i in 0 .. Value.count(l) {
|
for i in 0 .. Value.count(l) {
|
||||||
let e = Value.at(l, i)
|
let e = Value.at(l, i)
|
||||||
|
|
@ -58,3 +46,8 @@ export function hints_mutes_load(hints_st: mut HintsState, l: Val) -> void {
|
||||||
if id >= 0 { hints_st.hn_mute[id] = true } else { push(hints_st.hn_foreign, k) }
|
if id >= 0 { hints_st.hn_mute[id] = true } else { push(hints_st.hn_foreign, k) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function hn_mutes() -> []bool {
|
||||||
|
let out = new []bool
|
||||||
|
for i in 0 .. HINT_COUNT { push(out, false) }
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
# rail.ludic - the rail: once a pass (a second by default) the most urgent true cards, up to a few,
|
# rail.ludic - the rail: once a pass (a second by default) the most urgent true cards, up to a few,
|
||||||
# muted ones left out; in a tie the registry's order decides
|
# muted ones left out; in a tie the registry's order decides
|
||||||
export state HintsState {
|
export state HintsState {
|
||||||
hn_mute: []bool = null
|
hn_mute: []bool = hn_mutes()
|
||||||
hn_foreign: []string = null # 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,22 +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)
|
||||||
}
|
}
|
||||||
|
# a rail of n empty places
|
||||||
function hn_ensure(hints_st: mut HintsState) -> void {
|
function hn_slots(n: int) -> []int {
|
||||||
if hints_st.hn_top == null or len(hints_st.hn_top) != hints_st.hn_shown {
|
let out = new []int
|
||||||
hints_st.hn_top = new []int
|
for i in 0 .. n { push(out, -1) }
|
||||||
for i in 0 .. hints_st.hn_shown { push(hints_st.hn_top, -1) }
|
return out
|
||||||
}
|
|
||||||
hn_mute_ensure(hints_st)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 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
|
||||||
|
|
@ -53,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 {
|
||||||
|
|
@ -76,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]) }
|
||||||
|
|
@ -96,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,14 +2,12 @@
|
||||||
# the jobs' own dice seeded by the day, so a board never moves anyone else's rolls
|
# the jobs' own dice seeded by the day, so a board never moves anyone else's rolls
|
||||||
export function jobs_board_ok(b: int) -> bool { return b >= 0 and b < BOARD_COUNT }
|
export function jobs_board_ok(b: int) -> bool { return b >= 0 and b < BOARD_COUNT }
|
||||||
|
|
||||||
function jobs__at(jobs_st: mut JobsState, b: int, s: int) -> int {
|
function jobs__at(jobs_st: JobsState, b: int, s: int) -> int {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
return jobs_st.jobs__off[b] + s
|
return jobs_st.jobs__off[b] + s
|
||||||
}
|
}
|
||||||
|
|
||||||
# a whole number in [lo, hi] from the jobs' dice: what JobsWorld.roll draws with
|
# a whole number in [lo, hi] from the jobs' dice: what JobsWorld.roll draws with
|
||||||
export function jobs_roll(jobs_st: mut JobsState, lo: int, hi: int) -> int {
|
export function jobs_roll(jobs_st: JobsState, lo: int, hi: int) -> int {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
return rng_between(jobs_st.jobs__dice, lo, hi)
|
return rng_between(jobs_st.jobs__dice, lo, hi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +49,6 @@ function jobs__repost(jobs_st: mut JobsState, b: int, s: int) -> void {
|
||||||
# a new day, on the boards this machine keeps: an empty or stale post is replaced, and one
|
# a new day, on the boards this machine keeps: an empty or stale post is replaced, and one
|
||||||
# finished on an earlier day
|
# finished on an earlier day
|
||||||
export function jobs_morning(jobs_st: mut JobsState) -> void {
|
export function jobs_morning(jobs_st: mut JobsState) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
let day = JobsWorld.day()
|
let day = JobsWorld.day()
|
||||||
for b in 0 .. BOARD_COUNT {
|
for b in 0 .. BOARD_COUNT {
|
||||||
let bd = JobBoards[b]
|
let bd = JobBoards[b]
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ function jobs__takes(only: int, scope: int) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
function jobs__count(jobs_st: mut JobsState, only: int, kind: int, param: int, n: int) -> void {
|
function jobs__count(jobs_st: mut JobsState, only: int, kind: int, param: int, n: int) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
for i in 0 .. JOB_COUNT {
|
for i in 0 .. JOB_COUNT {
|
||||||
let d = Jobs[i]
|
let d = Jobs[i]
|
||||||
if jobs_st.jobs__st[i] != JOBS_ACTIVE or not jobs__takes(only, d.scope) { continue }
|
if jobs_st.jobs__st[i] != JOBS_ACTIVE or not jobs__takes(only, d.scope) { continue }
|
||||||
|
|
@ -49,7 +48,6 @@ function jobs__recheck_one(jobs_st: mut JobsState, i: int) -> void {
|
||||||
# every job under way against what the state already proves: work done before it was taken counts
|
# every job under way against what the state already proves: work done before it was taken counts
|
||||||
# for a written job; a post counts only what came after it went up
|
# for a written job; a post counts only what came after it went up
|
||||||
export function jobs_recheck(jobs_st: mut JobsState) -> void {
|
export function jobs_recheck(jobs_st: mut JobsState) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
for i in 0 .. JOB_COUNT { jobs__recheck_one(jobs_st, i) }
|
for i in 0 .. JOB_COUNT { jobs__recheck_one(jobs_st, i) }
|
||||||
for b in 0 .. BOARD_COUNT {
|
for b in 0 .. BOARD_COUNT {
|
||||||
if not JobsWorld.owns(JobBoards[b].scope) { continue }
|
if not JobsWorld.owns(JobBoards[b].scope) { continue }
|
||||||
|
|
@ -64,7 +62,6 @@ export function jobs_recheck(jobs_st: mut JobsState) -> void {
|
||||||
|
|
||||||
# offers newly open are said once; the first look after a reset says none of them
|
# offers newly open are said once; the first look after a reset says none of them
|
||||||
function jobs__offers(jobs_st: mut JobsState) -> void {
|
function jobs__offers(jobs_st: mut JobsState) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
for i in 0 .. JOB_COUNT {
|
for i in 0 .. JOB_COUNT {
|
||||||
let now = jobs_offered(jobs_st, i)
|
let now = jobs_offered(jobs_st, i)
|
||||||
if now and not jobs_st.jobs__seen[i] and jobs_st.jobs__primed { jobs__fact(jobs_st, JOBS_F_OFFERED, i, -1, -1, 0, Jobs[i].need) }
|
if now and not jobs_st.jobs__seen[i] and jobs_st.jobs__primed { jobs__fact(jobs_st, JOBS_F_OFFERED, i, -1, -1, 0, Jobs[i].need) }
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,24 @@
|
||||||
# jobs.ludic - the written jobs: offered by standing and story, taken, readied, handed in and paid
|
# jobs.ludic - the written jobs: offered by standing and story, taken, readied, handed in and paid
|
||||||
export function jobs_ok(i: int) -> bool { return i >= 0 and i < JOB_COUNT }
|
export function jobs_ok(i: int) -> bool { return i >= 0 and i < JOB_COUNT }
|
||||||
|
|
||||||
export function jobs_state(jobs_st: mut JobsState, i: int) -> int {
|
export function jobs_state(jobs_st: JobsState, i: int) -> int {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
return jobs_st.jobs__st[i]
|
return jobs_st.jobs__st[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_have(jobs_st: mut JobsState, i: int) -> int {
|
export function jobs_have(jobs_st: JobsState, i: int) -> int {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
return jobs_st.jobs__hv[i]
|
return jobs_st.jobs__hv[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_ready_now(jobs_st: mut JobsState, i: int) -> bool { return jobs_state(jobs_st, i) == JOBS_READY }
|
export function jobs_ready_now(jobs_st: JobsState, i: int) -> bool { return jobs_state(jobs_st, i) == JOBS_READY }
|
||||||
|
|
||||||
# taken and not yet paid
|
# taken and not yet paid
|
||||||
export function jobs_under_way(jobs_st: mut JobsState, i: int) -> bool {
|
export function jobs_under_way(jobs_st: JobsState, i: int) -> bool {
|
||||||
let s = jobs_state(jobs_st, i)
|
let s = jobs_state(jobs_st, i)
|
||||||
return s == JOBS_ACTIVE or s == JOBS_READY
|
return s == JOBS_ACTIVE or s == JOBS_READY
|
||||||
}
|
}
|
||||||
|
|
||||||
# on offer: not taken, the scope's standing and the story far enough, and the game allowing it
|
# on offer: not taken, the scope's standing and the story far enough, and the game allowing it
|
||||||
export function jobs_offered(jobs_st: mut JobsState, i: int) -> bool {
|
export function jobs_offered(jobs_st: JobsState, i: int) -> bool {
|
||||||
let d = Jobs[i]
|
let d = Jobs[i]
|
||||||
if jobs_state(jobs_st, i) != JOBS_OFFERED { return false }
|
if jobs_state(jobs_st, i) != JOBS_OFFERED { return false }
|
||||||
return JobsWorld.rep(d.scope) >= d.minrep and JobsWorld.stage() >= d.minstage and JobsWorld.open(i)
|
return JobsWorld.rep(d.scope) >= d.minrep and JobsWorld.stage() >= d.minstage and JobsWorld.open(i)
|
||||||
|
|
@ -79,18 +77,15 @@ export function jobs_complete(jobs_st: mut JobsState, i: int) -> bool {
|
||||||
|
|
||||||
# the state outright: the host's word for a party job, a test
|
# the state outright: the host's word for a party job, a test
|
||||||
export function jobs_set(jobs_st: mut JobsState, i: int, state: int, have: int) -> void {
|
export function jobs_set(jobs_st: mut JobsState, i: int, state: int, have: int) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
jobs_st.jobs__st[i] = state
|
jobs_st.jobs__st[i] = state
|
||||||
jobs_st.jobs__hv[i] = have
|
jobs_st.jobs__hv[i] = have
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_done(jobs_st: mut JobsState, group: int) -> int {
|
export function jobs_done(jobs_st: JobsState, group: int) -> int {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
if group < 0 or group >= JOBS_GROUPS { return 0 }
|
if group < 0 or group >= JOBS_GROUPS { return 0 }
|
||||||
return jobs_st.jobs__done_n[group]
|
return jobs_st.jobs__done_n[group]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_set_done(jobs_st: mut JobsState, group: int, n: int) -> void {
|
export function jobs_set_done(jobs_st: mut JobsState, group: int, n: int) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
if group >= 0 and group < JOBS_GROUPS { jobs_st.jobs__done_n[group] = n }
|
if group >= 0 and group < JOBS_GROUPS { jobs_st.jobs__done_n[group] = n }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# lists.ludic - the written jobs as a screen lists them: a vendor's, the taken, the ones to show
|
# lists.ludic - the written jobs as a screen lists them: a vendor's, the taken, the ones to show
|
||||||
# a vendor's offers and the ones it can take back, in a group (-1 every vendor)
|
# a vendor's offers and the ones it can take back, in a group (-1 every vendor)
|
||||||
export function jobs_at(jobs_st: mut JobsState, group: int, vendor: int) -> []int {
|
export function jobs_at(jobs_st: JobsState, group: int, vendor: int) -> []int {
|
||||||
let out = new []int
|
let out = new []int
|
||||||
for i in 0 .. JOB_COUNT {
|
for i in 0 .. JOB_COUNT {
|
||||||
let d = Jobs[i]
|
let d = Jobs[i]
|
||||||
|
|
@ -13,14 +13,14 @@ export function jobs_at(jobs_st: mut JobsState, group: int, vendor: int) -> []in
|
||||||
}
|
}
|
||||||
|
|
||||||
# every job of a group taken at some point, done ones too
|
# every job of a group taken at some point, done ones too
|
||||||
export function jobs_taken(jobs_st: mut JobsState, group: int) -> []int {
|
export function jobs_taken(jobs_st: JobsState, group: int) -> []int {
|
||||||
let out = new []int
|
let out = new []int
|
||||||
for i in 0 .. JOB_COUNT { if Jobs[i].group == group and jobs_state(jobs_st, i) != JOBS_OFFERED { push(out, i) } }
|
for i in 0 .. JOB_COUNT { if Jobs[i].group == group and jobs_state(jobs_st, i) != JOBS_OFFERED { push(out, i) } }
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
# the first `most` of a group under way
|
# the first `most` of a group under way
|
||||||
export function jobs_shown(jobs_st: mut JobsState, group: int, most: int) -> []int {
|
export function jobs_shown(jobs_st: JobsState, group: int, most: int) -> []int {
|
||||||
let out = new []int
|
let out = new []int
|
||||||
for i in 0 .. JOB_COUNT {
|
for i in 0 .. JOB_COUNT {
|
||||||
if len(out) >= most { break }
|
if len(out) >= most { break }
|
||||||
|
|
@ -30,7 +30,7 @@ export function jobs_shown(jobs_st: mut JobsState, group: int, most: int) -> []i
|
||||||
}
|
}
|
||||||
|
|
||||||
# the first under way and not yet met, or -1
|
# the first under way and not yet met, or -1
|
||||||
export function jobs_first_open(jobs_st: mut JobsState, group: int) -> int {
|
export function jobs_first_open(jobs_st: JobsState, group: int) -> int {
|
||||||
for i in 0 .. JOB_COUNT { if Jobs[i].group == group and jobs_state(jobs_st, i) == JOBS_ACTIVE { return i } }
|
for i in 0 .. JOB_COUNT { if Jobs[i].group == group and jobs_state(jobs_st, i) == JOBS_ACTIVE { return i } }
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
# posts.ludic - what is on a board, slot by slot, and a board as the host says it
|
# posts.ludic - what is on a board, slot by slot, and a board as the host says it
|
||||||
export function jobs_post_kind(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__pk[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_kind(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__pk[jobs__at(jobs_st, b, s)] }
|
||||||
export function jobs_post_param(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__pp[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_param(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__pp[jobs__at(jobs_st, b, s)] }
|
||||||
export function jobs_post_need(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__pn[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_need(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__pn[jobs__at(jobs_st, b, s)] }
|
||||||
export function jobs_post_have(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__ph[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_have(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__ph[jobs__at(jobs_st, b, s)] }
|
||||||
export function jobs_post_money(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__pm[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_money(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__pm[jobs__at(jobs_st, b, s)] }
|
||||||
export function jobs_post_rep(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__pr[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_rep(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__pr[jobs__at(jobs_st, b, s)] }
|
||||||
export function jobs_post_state(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__ps[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_state(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__ps[jobs__at(jobs_st, b, s)] }
|
||||||
export function jobs_post_day(jobs_st: mut JobsState, b: int, s: int) -> int { return jobs_st.jobs__pd[jobs__at(jobs_st, b, s)] }
|
export function jobs_post_day(jobs_st: JobsState, b: int, s: int) -> int { return jobs_st.jobs__pd[jobs__at(jobs_st, b, s)] }
|
||||||
|
|
||||||
export function jobs_slots(b: int) -> int {
|
export function jobs_slots(b: int) -> int {
|
||||||
if not jobs_board_ok(b) { return 0 }
|
if not jobs_board_ok(b) { return 0 }
|
||||||
|
|
@ -14,21 +14,20 @@ export function jobs_slots(b: int) -> int {
|
||||||
}
|
}
|
||||||
|
|
||||||
# the slots with something posted, in order
|
# the slots with something posted, in order
|
||||||
export function jobs_posted(jobs_st: mut JobsState, b: int) -> []int {
|
export function jobs_posted(jobs_st: JobsState, b: int) -> []int {
|
||||||
let out = new []int
|
let out = new []int
|
||||||
for s in 0 .. jobs_slots(b) { if jobs_post_kind(jobs_st, b, s) >= 0 { push(out, s) } }
|
for s in 0 .. jobs_slots(b) { if jobs_post_kind(jobs_st, b, s) >= 0 { push(out, s) } }
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
# how many of a board's posts are paid
|
# how many of a board's posts are paid
|
||||||
export function jobs_posts_done(jobs_st: mut JobsState, b: int) -> int {
|
export function jobs_posts_done(jobs_st: JobsState, b: int) -> int {
|
||||||
var n = 0
|
var n = 0
|
||||||
for s in 0 .. jobs_slots(b) { if jobs_post_kind(jobs_st, b, s) >= 0 and jobs_post_state(jobs_st, b, s) == JOBS_DONE { n += 1 } }
|
for s in 0 .. jobs_slots(b) { if jobs_post_kind(jobs_st, b, s) >= 0 and jobs_post_state(jobs_st, b, s) == JOBS_DONE { n += 1 } }
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_total(jobs_st: mut JobsState, b: int) -> int {
|
export function jobs_total(jobs_st: JobsState, b: int) -> int {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
return jobs_st.jobs__total[b]
|
return jobs_st.jobs__total[b]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +45,5 @@ export function jobs_post_set(jobs_st: mut JobsState, b: int, s: int, kind: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_set_total(jobs_st: mut JobsState, b: int, n: int) -> void {
|
export function jobs_set_total(jobs_st: mut JobsState, b: int, n: int) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
jobs_st.jobs__total[b] = n
|
jobs_st.jobs__total[b] = n
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# save.ludic - a scope's jobs and boards in a section of their own, by key: a job the game has since
|
# save.ludic - a scope's jobs and boards in a section of their own, by key: a job the game has since
|
||||||
# removed is dropped, a new one starts offered
|
# removed is dropped, a new one starts offered
|
||||||
function jobs__reset_scope(jobs_st: mut JobsState, sc: int) -> void {
|
function jobs__reset_scope(jobs_st: mut JobsState, sc: int) -> void {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
for i in 0 .. JOB_COUNT {
|
for i in 0 .. JOB_COUNT {
|
||||||
if Jobs[i].scope != sc { continue }
|
if Jobs[i].scope != sc { continue }
|
||||||
jobs_st.jobs__st[i] = JOBS_OFFERED
|
jobs_st.jobs__st[i] = JOBS_OFFERED
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,25 @@
|
||||||
# state.ludic - each job's state and count, the posts on the boards, the done counts, the dice and
|
# state.ludic - each job's state and count, the posts on the boards, the done counts, the dice and
|
||||||
# the facts. Made on first use, so the registries are complete by then.
|
# the facts, each sized from the registries when the state is made.
|
||||||
export state JobsState {
|
export state JobsState {
|
||||||
jobs__st: []int = null
|
jobs__st: []int = jobs__ints(JOB_COUNT, JOBS_OFFERED)
|
||||||
jobs__hv: []int = null
|
jobs__hv: []int = jobs__ints(JOB_COUNT, 0)
|
||||||
jobs__seen: []bool = null # an offer already said
|
jobs__seen: []bool = jobs__bools(JOB_COUNT) # an offer already said
|
||||||
jobs__primed: bool = false # the first tick after a reset learns the offers quietly
|
jobs__primed: bool = false # the first tick after a reset learns the offers quietly
|
||||||
jobs__done_n: []int = null # handed in, per group
|
jobs__done_n: []int = jobs__ints(JOBS_GROUPS, 0) # handed in, per group
|
||||||
jobs__off: []int = null
|
jobs__off: []int = jobs__offsets()
|
||||||
jobs__pk: []int = null # kind, -1 empty
|
jobs__pk: []int = jobs__ints(jobs__slots(), -1) # kind, -1 empty
|
||||||
jobs__pp: []int = null
|
jobs__pp: []int = jobs__ints(jobs__slots(), -1)
|
||||||
jobs__pn: []int = null
|
jobs__pn: []int = jobs__ints(jobs__slots(), 0)
|
||||||
jobs__ph: []int = null
|
jobs__ph: []int = jobs__ints(jobs__slots(), 0)
|
||||||
jobs__pb: []int = null # the evidence when it went up
|
jobs__pb: []int = jobs__ints(jobs__slots(), 0) # the evidence when it went up
|
||||||
jobs__pm: []int = null
|
jobs__pm: []int = jobs__ints(jobs__slots(), 0)
|
||||||
jobs__pr: []int = null
|
jobs__pr: []int = jobs__ints(jobs__slots(), 0)
|
||||||
jobs__ps: []int = null
|
jobs__ps: []int = jobs__ints(jobs__slots(), JOBS_OFFERED)
|
||||||
jobs__pd: []int = null # the day it went up
|
jobs__pd: []int = jobs__ints(jobs__slots(), 0) # the day it went up
|
||||||
jobs__total: []int = null # handed in, per board
|
jobs__total: []int = jobs__ints(BOARD_COUNT, 0) # handed in, per board
|
||||||
jobs__serial: []int = null # posts made, per board: each roll its own seed
|
jobs__serial: []int = jobs__ints(BOARD_COUNT, 0) # posts made, per board: each roll its own seed
|
||||||
jobs__seed: int = 1000
|
jobs__seed: int = 1000
|
||||||
jobs__dice: Rng = null
|
jobs__dice: Rng = rng_new(1000)
|
||||||
jobs__q: Queue<JobsFact> = jobs__q__new()
|
jobs__q: Queue<JobsFact> = jobs__q__new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,31 +32,25 @@ function jobs__ints(n: int, v: int) -> []int {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
function jobs__ensure(jobs_st: mut JobsState) -> void {
|
function jobs__bools(n: int) -> []bool {
|
||||||
if jobs_st.jobs__st != null { return }
|
let out = new []bool
|
||||||
jobs_st.jobs__st = jobs__ints(JOB_COUNT, JOBS_OFFERED)
|
for i in 0 .. n { push(out, false) }
|
||||||
jobs_st.jobs__hv = jobs__ints(JOB_COUNT, 0)
|
return out
|
||||||
jobs_st.jobs__seen = new []bool
|
}
|
||||||
for i in 0 .. JOB_COUNT { push(jobs_st.jobs__seen, false) }
|
# every board's slots end to end: where each board's start, and how many in all
|
||||||
jobs_st.jobs__done_n = jobs__ints(JOBS_GROUPS, 0)
|
function jobs__offsets() -> []int {
|
||||||
jobs_st.jobs__off = new []int
|
let out = new []int
|
||||||
var n = 0
|
var n = 0
|
||||||
for b in 0 .. BOARD_COUNT {
|
for b in 0 .. BOARD_COUNT {
|
||||||
push(jobs_st.jobs__off, n)
|
push(out, n)
|
||||||
n += JobBoards[b].slots
|
n += JobBoards[b].slots
|
||||||
}
|
}
|
||||||
jobs_st.jobs__pk = jobs__ints(n, -1)
|
return out
|
||||||
jobs_st.jobs__pp = jobs__ints(n, -1)
|
}
|
||||||
jobs_st.jobs__pn = jobs__ints(n, 0)
|
function jobs__slots() -> int {
|
||||||
jobs_st.jobs__ph = jobs__ints(n, 0)
|
var n = 0
|
||||||
jobs_st.jobs__pb = jobs__ints(n, 0)
|
for b in 0 .. BOARD_COUNT { n += JobBoards[b].slots }
|
||||||
jobs_st.jobs__pm = jobs__ints(n, 0)
|
return n
|
||||||
jobs_st.jobs__pr = jobs__ints(n, 0)
|
|
||||||
jobs_st.jobs__ps = jobs__ints(n, JOBS_OFFERED)
|
|
||||||
jobs_st.jobs__pd = jobs__ints(n, 0)
|
|
||||||
jobs_st.jobs__total = jobs__ints(BOARD_COUNT, 0)
|
|
||||||
jobs_st.jobs__serial = jobs__ints(BOARD_COUNT, 0)
|
|
||||||
jobs_st.jobs__dice = rng_new(jobs_st.jobs__seed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_facts(jobs_st: JobsState) -> Queue<JobsFact> {
|
export function jobs_facts(jobs_st: JobsState) -> Queue<JobsFact> {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@ export function jobs_reset(jobs_st: mut JobsState) -> void {
|
||||||
|
|
||||||
export function jobs_party_reset(jobs_st: mut JobsState) -> void { jobs__reset_scope(jobs_st, JOBS_PARTY) }
|
export function jobs_party_reset(jobs_st: mut JobsState) -> void { jobs__reset_scope(jobs_st, JOBS_PARTY) }
|
||||||
|
|
||||||
export function jobs_save(jobs_st: mut JobsState) -> Val {
|
export function jobs_save(jobs_st: JobsState) -> Val {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
let v = jobs__save_scope(jobs_st, JOBS_SELF)
|
let v = jobs__save_scope(jobs_st, JOBS_SELF)
|
||||||
sv_put_ints(v, "done", jobs_st.jobs__done_n)
|
sv_put_ints(v, "done", jobs_st.jobs__done_n)
|
||||||
return v
|
return v
|
||||||
|
|
@ -24,8 +23,7 @@ export function jobs_load(jobs_st: mut JobsState, v: Val, version: int) -> void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jobs_party_save(jobs_st: mut JobsState) -> Val {
|
export function jobs_party_save(jobs_st: JobsState) -> Val {
|
||||||
jobs__ensure(jobs_st)
|
|
||||||
return jobs__save_scope(jobs_st, JOBS_PARTY)
|
return jobs__save_scope(jobs_st, JOBS_PARTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 = ""
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ export function nb_copy(dst: []byte, d: int, src: []byte, s: int, n: int) -> voi
|
||||||
|
|
||||||
# the writer: past the MTU it counts on and keeps nothing, and np_queue cuts the message there
|
# the writer: past the MTU it counts on and keeps nothing, and np_queue cuts the message there
|
||||||
export function nw_begin(net_st: mut NetState) -> void {
|
export function nw_begin(net_st: mut NetState) -> void {
|
||||||
net__init(net_st)
|
|
||||||
net_st.net__wl = 0
|
net_st.net__wl = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +81,6 @@ export function nr_bad(net_st: NetState) -> bool { return net_st.net__rbad }
|
||||||
|
|
||||||
# a message that came in, made the one nr_* reads
|
# a message that came in, made the one nr_* reads
|
||||||
export function net_read(net_st: mut NetState, m: NetMessage) -> void {
|
export function net_read(net_st: mut NetState, m: NetMessage) -> void {
|
||||||
net__init(net_st)
|
|
||||||
nb_copy(net_st.net__rb, 0, m.data, 0, m.len)
|
nb_copy(net_st.net__rb, 0, m.data, 0, m.len)
|
||||||
net_st.net__rl = m.len
|
net_st.net__rl = m.len
|
||||||
net_st.net__rp = 0
|
net_st.net__rp = 0
|
||||||
|
|
@ -90,8 +88,7 @@ export function net_read(net_st: mut NetState, m: NetMessage) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
# the message just written, as if it had come in (a loopback: a test, a host telling itself)
|
# the message just written, as if it had come in (a loopback: a test, a host telling itself)
|
||||||
export function net_written(net_st: mut NetState) -> NetMessage {
|
export function net_written(net_st: NetState) -> NetMessage {
|
||||||
net__init(net_st)
|
|
||||||
let m = new NetMessage
|
let m = new NetMessage
|
||||||
m.from = net_st.net__my_pid
|
m.from = net_st.net__my_pid
|
||||||
m.len = min(net_st.net__wl, NET_MTU)
|
m.len = min(net_st.net__wl, NET_MTU)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ export function net_room_join(net_st: mut NetState, code: string) -> bool {
|
||||||
net_st.net__why = NET_WHY_NO_MATCHMAKER
|
net_st.net__why = NET_WHY_NO_MATCHMAKER
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
net__init(net_st)
|
|
||||||
if not net__socket(net_st) { return false }
|
if not net__socket(net_st) { return false }
|
||||||
net_st.net__r_role = 2
|
net_st.net__r_role = 2
|
||||||
net_st.net__room = Text.upper(code)
|
net_st.net__room = Text.upper(code)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
# open a socket on `port` and take guests; false (net_why) when the port is taken
|
# open a socket on `port` and take guests; false (net_why) when the port is taken
|
||||||
export function net_host(net_st: mut NetState, port: int, token: int) -> bool {
|
export function net_host(net_st: mut NetState, port: int, token: int) -> bool {
|
||||||
net__init(net_st)
|
|
||||||
if net_live(net_st) { return true }
|
if net_live(net_st) { return true }
|
||||||
net_st.net__sock = NetSocket.open(port)
|
net_st.net__sock = NetSocket.open(port)
|
||||||
if net_st.net__sock <= 0 {
|
if net_st.net__sock <= 0 {
|
||||||
|
|
@ -20,7 +19,6 @@ export function net_host(net_st: mut NetState, port: int, token: int) -> bool {
|
||||||
|
|
||||||
# dial a host at an address; the hello (net_hello_set) goes until a welcome or a refusal
|
# dial a host at an address; the hello (net_hello_set) goes until a welcome or a refusal
|
||||||
export function net_join(net_st: mut NetState, ip: int, port: int) -> bool {
|
export function net_join(net_st: mut NetState, ip: int, port: int) -> bool {
|
||||||
net__init(net_st)
|
|
||||||
if net_live(net_st) { return false }
|
if net_live(net_st) { return false }
|
||||||
if not net__socket(net_st) { return false }
|
if not net__socket(net_st) { return false }
|
||||||
for i in 0 .. NET_PEERS { np_close(net_st, i) }
|
for i in 0 .. NET_PEERS { np_close(net_st, i) }
|
||||||
|
|
@ -42,7 +40,6 @@ function net__socket(net_st: mut NetState) -> bool {
|
||||||
|
|
||||||
# the message just written is the hello: what a host is asked to let this machine in with
|
# the message just written is the hello: what a host is asked to let this machine in with
|
||||||
export function net_hello_set(net_st: mut NetState) -> void {
|
export function net_hello_set(net_st: mut NetState) -> void {
|
||||||
net__init(net_st)
|
|
||||||
nb_copy(net_st.net__hello_b, 0, net_st.net__wb, 0, net_st.net__wl)
|
nb_copy(net_st.net__hello_b, 0, net_st.net__wb, 0, net_st.net__wl)
|
||||||
net_st.net__hello_l = net_st.net__wl
|
net_st.net__hello_l = net_st.net__wl
|
||||||
if net_st.net__hello_l > NET_MTU - 40 { net_st.net__hello_l = NET_MTU - 40 }
|
if net_st.net__hello_l > NET_MTU - 40 { net_st.net__hello_l = NET_MTU - 40 }
|
||||||
|
|
@ -50,7 +47,6 @@ export function net_hello_set(net_st: mut NetState) -> void {
|
||||||
|
|
||||||
# (again) to that address, with a fresh connection and the hello sent anew
|
# (again) to that address, with a fresh connection and the hello sent anew
|
||||||
export function net_dial(net_st: mut NetState, ip: int, port: int) -> void {
|
export function net_dial(net_st: mut NetState, ip: int, port: int) -> void {
|
||||||
net__init(net_st)
|
|
||||||
np_close(net_st, 0)
|
np_close(net_st, 0)
|
||||||
net_st.net__join_ip = ip
|
net_st.net__join_ip = ip
|
||||||
net_st.net__join_port = port
|
net_st.net__join_port = port
|
||||||
|
|
@ -70,7 +66,6 @@ export function net_follow(net_st: mut NetState, ip: int, port: int) -> void {
|
||||||
export function net_stand_alone(net_st: mut NetState) -> void { net_st.net__state = NET_IDLE }
|
export function net_stand_alone(net_st: mut NetState) -> void { net_st.net__state = NET_IDLE }
|
||||||
|
|
||||||
export function net_become_host(net_st: mut NetState, token: int) -> void {
|
export function net_become_host(net_st: mut NetState, token: int) -> void {
|
||||||
net__init(net_st)
|
|
||||||
net_st.net__state = NET_HOSTING
|
net_st.net__state = NET_HOSTING
|
||||||
net_st.net__my_pid = 1
|
net_st.net__my_pid = 1
|
||||||
net_st.net__next_pid = 2
|
net_st.net__next_pid = 2
|
||||||
|
|
|
||||||
|
|
@ -79,17 +79,17 @@ export state NetState {
|
||||||
net__token: int = 0
|
net__token: int = 0
|
||||||
net__my_pid: int = 0
|
net__my_pid: int = 0
|
||||||
net__next_pid: int = 2
|
net__next_pid: int = 2
|
||||||
net__peers: []NetPeer = null
|
net__peers: []NetPeer = net__peers_new()
|
||||||
net__in: []byte = null
|
net__in: []byte = buffer(NET_MTU + 64)
|
||||||
net__out: []byte = null
|
net__out: []byte = buffer(NET_MTU + 64)
|
||||||
net__wb: []byte = null # the message being written
|
net__wb: []byte = buffer(NET_MTU) # the message being written
|
||||||
net__wl: int = 0
|
net__wl: int = 0
|
||||||
net__rb: []byte = null # the message being read
|
net__rb: []byte = buffer(NET_MTU) # the message being read
|
||||||
net__rl: int = 0
|
net__rl: int = 0
|
||||||
net__rp: int = 0
|
net__rp: int = 0
|
||||||
net__rbad: bool = false
|
net__rbad: bool = false
|
||||||
net__tb: []byte = buffer(NET_MTU) # text read out of a message, through one scratch buffer
|
net__tb: []byte = buffer(NET_MTU) # text read out of a message, through one scratch buffer
|
||||||
net__hello_b: []byte = null # the game's hello, sent again on every dial
|
net__hello_b: []byte = buffer(NET_MTU) # the game's hello, sent again on every dial
|
||||||
net__hello_l: int = 0
|
net__hello_l: int = 0
|
||||||
net__hello_t: float = 0.0 # seconds since it went; -1 not yet
|
net__hello_t: float = 0.0 # seconds since it went; -1 not yet
|
||||||
net__wait_more: int = 0 # extra seconds a slow host is given (a map switch, a matchmaker)
|
net__wait_more: int = 0 # extra seconds a slow host is given (a map switch, a matchmaker)
|
||||||
|
|
@ -111,19 +111,15 @@ export function net_inbox(net_st: NetState) -> Queue<NetMessage> {
|
||||||
return net_st.net__inbox
|
return net_st.net__inbox
|
||||||
}
|
}
|
||||||
|
|
||||||
function net__init(net_st: mut NetState) -> void {
|
function net__facts__new() -> Queue<NetFact> { return queue_new("net.facts") }
|
||||||
if net_st.net__peers != null { return }
|
function net__inbox__new() -> Queue<NetMessage> { return queue_new("net.inbox") }
|
||||||
net_st.net__peers = new []NetPeer
|
# every peer slot, each with its own queue
|
||||||
|
function net__peers_new() -> []NetPeer {
|
||||||
|
let out = new []NetPeer
|
||||||
for i in 0 .. NET_PEERS {
|
for i in 0 .. NET_PEERS {
|
||||||
let p = new NetPeer
|
let p = new NetPeer
|
||||||
p.queue = new []NetMsg
|
p.queue = new []NetMsg
|
||||||
push(net_st.net__peers, p)
|
push(out, p)
|
||||||
}
|
}
|
||||||
net_st.net__in = buffer(NET_MTU + 64)
|
return out
|
||||||
net_st.net__out = buffer(NET_MTU + 64)
|
|
||||||
net_st.net__wb = buffer(NET_MTU)
|
|
||||||
net_st.net__rb = buffer(NET_MTU)
|
|
||||||
net_st.net__hello_b = buffer(NET_MTU)
|
|
||||||
}
|
}
|
||||||
function net__facts__new() -> Queue<NetFact> { return queue_new("net.facts") }
|
|
||||||
function net__inbox__new() -> Queue<NetMessage> { return queue_new("net.inbox") }
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -41,37 +41,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,19 +74,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 }
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,10 @@ export state Render3dState {
|
||||||
gvk_al_spare: []int = null # ids to reuse
|
gvk_al_spare: []int = null # ids to reuse
|
||||||
gvk_pool: long = 0
|
gvk_pool: long = 0
|
||||||
gvk_fence: bytes = null
|
gvk_fence: bytes = null
|
||||||
|
gvk_frame_fence: bytes = null # the presented frame still on the GPU (one frame in flight)
|
||||||
|
gvk_frame_pending: bool = false
|
||||||
|
gvk_frame_pending_cb: pointer = null
|
||||||
|
gvk_inflight: int = -1 # -1 until asked: 1 leaves the presented frame in flight
|
||||||
gvk_has_hqr: bool = false
|
gvk_has_hqr: bool = false
|
||||||
gvk_ts_period: float = 0.0 # float bits: nanoseconds per timestamp tick
|
gvk_ts_period: float = 0.0 # float bits: nanoseconds per timestamp tick
|
||||||
gvk_qpool: long = 0
|
gvk_qpool: long = 0
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,10 @@ function gpu_select(render3d_st: mut Render3dState) -> int {
|
||||||
if render3d_st.gpu_wanted == 0 { render3d_st.gpu_wanted = GPU_GL }
|
if render3d_st.gpu_wanted == 0 { render3d_st.gpu_wanted = GPU_GL }
|
||||||
render3d_st.gpu_kind = GPU_GL
|
render3d_st.gpu_kind = GPU_GL
|
||||||
if render3d_st.gpu_wanted == GPU_VK {
|
if render3d_st.gpu_wanted == GPU_VK {
|
||||||
# a window needs a surface, and only the Win32 one is built
|
# a window needs a surface: Win32 on Windows, a CAMetalLayer through MoltenVK on macOS
|
||||||
render3d_st.gvk_want_surface = is_windowed()
|
render3d_st.gvk_want_surface = is_windowed()
|
||||||
if is_windowed() and Os.platform() != "windows" { render3d_st.gpu_fallback_reason = "the Vulkan window is built for Windows only" }
|
let os = Os.platform()
|
||||||
|
if is_windowed() and os != "windows" and os != "macos" { render3d_st.gpu_fallback_reason = `no Vulkan window is built for {os}` }
|
||||||
else if not gvk_init(render3d_st) { render3d_st.gpu_fallback_reason = render3d_st.gvk_why }
|
else if not gvk_init(render3d_st) { render3d_st.gpu_fallback_reason = render3d_st.gvk_why }
|
||||||
else if not gvk_manifest(render3d_st) { render3d_st.gpu_fallback_reason = "the renderer's SPIR-V manifest is missing" }
|
else if not gvk_manifest(render3d_st) { render3d_st.gpu_fallback_reason = "the renderer's SPIR-V manifest is missing" }
|
||||||
else { render3d_st.gpu_kind = GPU_VK }
|
else { render3d_st.gpu_kind = GPU_VK }
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,11 @@ function gvk_ext_in(props: bytes, n: int, want: string) -> bool {
|
||||||
# device with the Tier 1 floor switched on. False, with gvk_why set, if any of it is missing:
|
# device with the Tier 1 floor switched on. False, with gvk_why set, if any of it is missing:
|
||||||
# the caller stays on OpenGL.
|
# the caller stays on OpenGL.
|
||||||
|
|
||||||
|
function gvk_surface_ext() -> string {
|
||||||
|
if Os.platform() == "macos" { return VK_EXT_METAL_SURFACE_EXTENSION_NAME }
|
||||||
|
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME
|
||||||
|
}
|
||||||
|
|
||||||
function gvk_init(render3d_st: mut Render3dState) -> bool {
|
function gvk_init(render3d_st: mut Render3dState) -> bool {
|
||||||
if render3d_st.gvk_ready { return true }
|
if render3d_st.gvk_ready { return true }
|
||||||
gsl_boot(render3d_st)
|
gsl_boot(render3d_st)
|
||||||
|
|
@ -72,12 +77,13 @@ function gvk_init(render3d_st: mut Render3dState) -> bool {
|
||||||
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); n_iext += 1
|
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); n_iext += 1
|
||||||
Vk.put_i32(ici, VkInstanceCreateInfo_flags, VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR)
|
Vk.put_i32(ici, VkInstanceCreateInfo_flags, VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR)
|
||||||
}
|
}
|
||||||
# a window's surface: only the Win32 one is built, so a window elsewhere stays on OpenGL
|
# a window's surface: Win32 on Windows, Metal (MoltenVK) on macOS
|
||||||
render3d_st.gvk_has_surface = render3d_st.gvk_want_surface and gvk_ext_in(iexts, nie, VK_KHR_SURFACE_EXTENSION_NAME) and gvk_ext_in(iexts, nie, VK_KHR_WIN32_SURFACE_EXTENSION_NAME)
|
let surf_ext = gvk_surface_ext()
|
||||||
if render3d_st.gvk_want_surface and not render3d_st.gvk_has_surface { render3d_st.gvk_why = "no Win32 surface in this Vulkan instance"; return false }
|
render3d_st.gvk_has_surface = render3d_st.gvk_want_surface and gvk_ext_in(iexts, nie, VK_KHR_SURFACE_EXTENSION_NAME) and gvk_ext_in(iexts, nie, surf_ext)
|
||||||
|
if render3d_st.gvk_want_surface and not render3d_st.gvk_has_surface { render3d_st.gvk_why = `no {surf_ext} in this Vulkan instance`; return false }
|
||||||
if render3d_st.gvk_has_surface {
|
if render3d_st.gvk_has_surface {
|
||||||
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_SURFACE_EXTENSION_NAME); n_iext += 1
|
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_SURFACE_EXTENSION_NAME); n_iext += 1
|
||||||
Vk.put_ptr(iext_names, n_iext * 8, VK_KHR_WIN32_SURFACE_EXTENSION_NAME); n_iext += 1
|
Vk.put_ptr(iext_names, n_iext * 8, surf_ext); n_iext += 1
|
||||||
}
|
}
|
||||||
# HDR output: an instance only lists the HDR colour spaces when it asks for them
|
# HDR output: an instance only lists the HDR colour spaces when it asks for them
|
||||||
render3d_st.gvk_has_colorspace = render3d_st.gvk_has_surface and gvk_ext_in(iexts, nie, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME)
|
render3d_st.gvk_has_colorspace = render3d_st.gvk_has_surface and gvk_ext_in(iexts, nie, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME)
|
||||||
|
|
@ -488,6 +494,9 @@ function gvk_cmd_init(render3d_st: mut Render3dState) -> bool {
|
||||||
render3d_st.gvk_fence = bytes(8)
|
render3d_st.gvk_fence = bytes(8)
|
||||||
r = Vk.create_fence(render3d_st.gvk_dev, fci, null, render3d_st.gvk_fence)
|
r = Vk.create_fence(render3d_st.gvk_dev, fci, null, render3d_st.gvk_fence)
|
||||||
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateFence", r) }
|
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateFence", r) }
|
||||||
|
render3d_st.gvk_frame_fence = bytes(8)
|
||||||
|
r = Vk.create_fence(render3d_st.gvk_dev, fci, null, render3d_st.gvk_frame_fence)
|
||||||
|
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateFence", r) }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
# a command buffer, begun
|
# a command buffer, begun
|
||||||
|
|
@ -510,6 +519,7 @@ function gvk_once_begin(render3d_st: Render3dState) -> pointer {
|
||||||
}
|
}
|
||||||
# end it, submit it, wait for it, free it
|
# end it, submit it, wait for it, free it
|
||||||
function gvk_once_end(render3d_st: mut Render3dState, cb: pointer) -> bool {
|
function gvk_once_end(render3d_st: mut Render3dState, cb: pointer) -> bool {
|
||||||
|
gvk_frame_wait(render3d_st)
|
||||||
var r = Vk.end_command_buffer(cb)
|
var r = Vk.end_command_buffer(cb)
|
||||||
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkEndCommandBuffer", r) }
|
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkEndCommandBuffer", r) }
|
||||||
let cbs = bytes(8)
|
let cbs = bytes(8)
|
||||||
|
|
@ -529,12 +539,61 @@ function gvk_once_end(render3d_st: mut Render3dState, cb: pointer) -> bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---- one frame in flight -------------------------------------------------------------------
|
||||||
|
# A window's frame is submitted and presented without waiting, so the game's next tick runs on the
|
||||||
|
# CPU while the GPU draws: waiting at the present serialised the two, which cost MoltenVK a third
|
||||||
|
# of its frame rate against OpenGL. Anything that would reuse what that frame reads - the next
|
||||||
|
# frame's command buffer and ring, a one-off submit, a descriptor pool reset, a swapchain rebuild -
|
||||||
|
# waits for it first (gvk_frame_wait), and a buffer it reads is busy (gvk_buf_busy). On by default
|
||||||
|
# on macOS; R3D_VK_INFLIGHT=0/1 says otherwise.
|
||||||
|
function gvk_inflight_on(render3d_st: mut Render3dState) -> bool {
|
||||||
|
if render3d_st.gvk_inflight < 0 {
|
||||||
|
render3d_st.gvk_inflight = 0
|
||||||
|
if Os.platform() == "macos" { render3d_st.gvk_inflight = 1 }
|
||||||
|
if r3d_env_has(render3d_st, "R3D_VK_INFLIGHT") { render3d_st.gvk_inflight = Text.to_int(r3d_env(render3d_st, "R3D_VK_INFLIGHT")) }
|
||||||
|
}
|
||||||
|
return render3d_st.gvk_inflight == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function gvk_frame_submit(render3d_st: mut Render3dState, cb: pointer) -> bool {
|
||||||
|
gvk_frame_wait(render3d_st)
|
||||||
|
var r = Vk.end_command_buffer(cb)
|
||||||
|
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkEndCommandBuffer", r) }
|
||||||
|
let cbs = bytes(8)
|
||||||
|
Vk.put_ptr(cbs, 0, cb)
|
||||||
|
Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_frame_fence)
|
||||||
|
let si = bytes(VkSubmitInfo_sizeof)
|
||||||
|
Vk.zero(si, VkSubmitInfo_sizeof)
|
||||||
|
Vk.put_i32(si, VkSubmitInfo_sType, VK_STRUCTURE_TYPE_SUBMIT_INFO)
|
||||||
|
Vk.put_i32(si, VkSubmitInfo_commandBufferCount, 1)
|
||||||
|
Vk.put_ptr(si, VkSubmitInfo_pCommandBuffers, cbs)
|
||||||
|
r = Vk.queue_submit(render3d_st.gvk_queue, 1, si, Vk.get_i64(render3d_st.gvk_frame_fence, 0))
|
||||||
|
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkQueueSubmit", r) }
|
||||||
|
render3d_st.gvk_frame_pending = true
|
||||||
|
render3d_st.gvk_frame_pending_cb = cb
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function gvk_frame_wait(render3d_st: mut Render3dState) -> void {
|
||||||
|
if not render3d_st.gvk_frame_pending { return }
|
||||||
|
let forever: long = -1
|
||||||
|
Vk.wait_for_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_frame_fence, 1, forever)
|
||||||
|
let cbs = bytes(8)
|
||||||
|
Vk.put_ptr(cbs, 0, render3d_st.gvk_frame_pending_cb)
|
||||||
|
Vk.free_command_buffers(render3d_st.gvk_dev, render3d_st.gvk_pool, 1, cbs)
|
||||||
|
render3d_st.gvk_frame_pending = false
|
||||||
|
render3d_st.gvk_frame_pending_cb = null
|
||||||
|
gvk_retire_flush(render3d_st)
|
||||||
|
}
|
||||||
|
|
||||||
# ---- teardown -----------------------------------------------------------------------------
|
# ---- teardown -----------------------------------------------------------------------------
|
||||||
function gvk_shutdown(render3d_st: mut Render3dState) -> void {
|
function gvk_shutdown(render3d_st: mut Render3dState) -> void {
|
||||||
if not render3d_st.gvk_ready { return }
|
if not render3d_st.gvk_ready { return }
|
||||||
|
gvk_frame_wait(render3d_st)
|
||||||
Vk.device_wait_idle(render3d_st.gvk_dev)
|
Vk.device_wait_idle(render3d_st.gvk_dev)
|
||||||
gsl_shutdown(render3d_st)
|
gsl_shutdown(render3d_st)
|
||||||
Vk.destroy_fence(render3d_st.gvk_dev, Vk.get_i64(render3d_st.gvk_fence, 0), null)
|
Vk.destroy_fence(render3d_st.gvk_dev, Vk.get_i64(render3d_st.gvk_fence, 0), null)
|
||||||
|
Vk.destroy_fence(render3d_st.gvk_dev, Vk.get_i64(render3d_st.gvk_frame_fence, 0), null)
|
||||||
Vk.destroy_command_pool(render3d_st.gvk_dev, render3d_st.gvk_pool, null)
|
Vk.destroy_command_pool(render3d_st.gvk_dev, render3d_st.gvk_pool, null)
|
||||||
Vk.destroy_device(render3d_st.gvk_dev, null)
|
Vk.destroy_device(render3d_st.gvk_dev, null)
|
||||||
Vk.destroy_instance(render3d_st.gvk_inst, null)
|
Vk.destroy_instance(render3d_st.gvk_inst, null)
|
||||||
|
|
|
||||||
|
|
@ -729,6 +729,7 @@ function gvk_sc_clear(render3d_st: mut Render3dState) -> void {
|
||||||
# the pool is full: finish the frame recorded so far (it may use sets from it), then start again
|
# the pool is full: finish the frame recorded so far (it may use sets from it), then start again
|
||||||
function gvk_kpool_reset(render3d_st: mut Render3dState) -> void {
|
function gvk_kpool_reset(render3d_st: mut Render3dState) -> void {
|
||||||
gvk_flush(render3d_st)
|
gvk_flush(render3d_st)
|
||||||
|
gvk_frame_wait(render3d_st)
|
||||||
Vk.reset_descriptor_pool(render3d_st.gvk_dev, render3d_st.gvk_kpool, 0)
|
Vk.reset_descriptor_pool(render3d_st.gvk_dev, render3d_st.gvk_kpool, 0)
|
||||||
gvk_sc_clear(render3d_st)
|
gvk_sc_clear(render3d_st)
|
||||||
}
|
}
|
||||||
|
|
@ -953,6 +954,7 @@ function gvk_screen_make(render3d_st: mut Render3dState, w: int, h: int) -> bool
|
||||||
|
|
||||||
function gvk_frame_cb(render3d_st: mut Render3dState) -> pointer {
|
function gvk_frame_cb(render3d_st: mut Render3dState) -> pointer {
|
||||||
if render3d_st.gvk_cb == null {
|
if render3d_st.gvk_cb == null {
|
||||||
|
gvk_frame_wait(render3d_st)
|
||||||
gvk_frame_reset(render3d_st)
|
gvk_frame_reset(render3d_st)
|
||||||
render3d_st.gvk_cb = gvk_once_begin(render3d_st)
|
render3d_st.gvk_cb = gvk_once_begin(render3d_st)
|
||||||
}
|
}
|
||||||
|
|
@ -1279,7 +1281,13 @@ function gvk_flush(render3d_st: mut Render3dState) -> void {
|
||||||
# - and that image is presented.
|
# - and that image is presented.
|
||||||
function gvk_present(render3d_st: mut Render3dState) -> void {
|
function gvk_present(render3d_st: mut Render3dState) -> void {
|
||||||
gvk_prof_frame(render3d_st)
|
gvk_prof_frame(render3d_st)
|
||||||
if render3d_st.gvk_swap != 0 { gvk_present_window(render3d_st); render3d_st.gvk_frame_no += 1; gvk_retire_flush(render3d_st); return }
|
if render3d_st.gvk_swap != 0 {
|
||||||
|
gvk_present_window(render3d_st)
|
||||||
|
render3d_st.gvk_frame_no += 1
|
||||||
|
# what the frame in flight retired goes when it is done (gvk_frame_wait)
|
||||||
|
if not render3d_st.gvk_frame_pending { gvk_retire_flush(render3d_st) }
|
||||||
|
return
|
||||||
|
}
|
||||||
if render3d_st.gvk_cb == null { return }
|
if render3d_st.gvk_cb == null { return }
|
||||||
gvk_pass_end(render3d_st)
|
gvk_pass_end(render3d_st)
|
||||||
gvk_once_end(render3d_st, render3d_st.gvk_cb)
|
gvk_once_end(render3d_st, render3d_st.gvk_cb)
|
||||||
|
|
@ -1332,6 +1340,8 @@ function gvk_open(render3d_st: mut Render3dState, w: int, h: int, title: string)
|
||||||
if is_windowed() {
|
if is_windowed() {
|
||||||
win_open(w, h, 1, title) # the window exists before main: this retitles it
|
win_open(w, h, 1, title) # the window exists before main: this retitles it
|
||||||
win_gl_resize(w, h)
|
win_gl_resize(w, h)
|
||||||
|
# macOS: the Metal layer sets the backing scale, so it exists before the drawable is measured
|
||||||
|
if Os.platform() == "macos" and win_metal_layer() == null { render3d_st.gvk_why = "no Metal layer on the window"; return false }
|
||||||
if render3d_st.gvk_size_buf == null { render3d_st.gvk_size_buf = words(4) }
|
if render3d_st.gvk_size_buf == null { render3d_st.gvk_size_buf = words(4) }
|
||||||
win_gl_drawable(render3d_st.gvk_size_buf)
|
win_gl_drawable(render3d_st.gvk_size_buf)
|
||||||
if render3d_st.gvk_size_buf[0] > 0 and render3d_st.gvk_size_buf[1] > 0 { gl_set_drawable(render3d_st.gvk_size_buf[0], render3d_st.gvk_size_buf[1]) }
|
if render3d_st.gvk_size_buf[0] > 0 and render3d_st.gvk_size_buf[1] > 0 { gl_set_drawable(render3d_st.gvk_size_buf[0], render3d_st.gvk_size_buf[1]) }
|
||||||
|
|
@ -1519,13 +1529,24 @@ function gvk_read_screen(render3d_st: mut Render3dState, w: int, h: int, out: po
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- the window: surface and swapchain ----------------------------------------------------------
|
# ---- the window: surface and swapchain ----------------------------------------------------------
|
||||||
# Windows only for now (the Win32 surface); gpu_select keeps a window elsewhere on OpenGL.
|
# The Win32 surface on Windows; on macOS a CAMetalLayer the runtime hangs off the view (MoltenVK).
|
||||||
extern function win_native_window() -> pointer = "win_native_window"
|
extern function win_native_window() -> pointer = "win_native_window"
|
||||||
extern function win_native_instance() -> pointer = "win_native_instance"
|
extern function win_native_instance() -> pointer = "win_native_instance"
|
||||||
|
extern function win_metal_layer() -> pointer = "win_metal_layer"
|
||||||
|
extern function win_visible() -> int = "win_visible"
|
||||||
|
|
||||||
|
function gvk_surface_os(render3d_st: mut Render3dState, out: pointer) -> bool {
|
||||||
function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
|
if Os.platform() == "macos" {
|
||||||
if render3d_st.gvk_surface != 0 { return true }
|
let layer = win_metal_layer()
|
||||||
|
if layer == null { render3d_st.gvk_why = "no Metal layer on the window"; return false }
|
||||||
|
let mci = bytes(VkMetalSurfaceCreateInfoEXT_sizeof)
|
||||||
|
Vk.zero(mci, VkMetalSurfaceCreateInfoEXT_sizeof)
|
||||||
|
Vk.put_i32(mci, VkMetalSurfaceCreateInfoEXT_sType, VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT)
|
||||||
|
Vk.put_ptr(mci, VkMetalSurfaceCreateInfoEXT_pLayer, layer)
|
||||||
|
let rm = Vk.create_metal_surface_ext(render3d_st.gvk_inst, mci, null, out)
|
||||||
|
if rm != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateMetalSurfaceEXT", rm) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
let hwnd = win_native_window()
|
let hwnd = win_native_window()
|
||||||
if hwnd == null { render3d_st.gvk_why = "no window to present to"; return false }
|
if hwnd == null { render3d_st.gvk_why = "no window to present to"; return false }
|
||||||
let sci = bytes(VkWin32SurfaceCreateInfoKHR_sizeof)
|
let sci = bytes(VkWin32SurfaceCreateInfoKHR_sizeof)
|
||||||
|
|
@ -1533,9 +1554,15 @@ function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
|
||||||
Vk.put_i32(sci, VkWin32SurfaceCreateInfoKHR_sType, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR)
|
Vk.put_i32(sci, VkWin32SurfaceCreateInfoKHR_sType, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR)
|
||||||
Vk.put_ptr(sci, VkWin32SurfaceCreateInfoKHR_hinstance, win_native_instance())
|
Vk.put_ptr(sci, VkWin32SurfaceCreateInfoKHR_hinstance, win_native_instance())
|
||||||
Vk.put_ptr(sci, VkWin32SurfaceCreateInfoKHR_hwnd, hwnd)
|
Vk.put_ptr(sci, VkWin32SurfaceCreateInfoKHR_hwnd, hwnd)
|
||||||
let out = bytes(8)
|
|
||||||
let r = Vk.create_win32_surface_khr(render3d_st.gvk_inst, sci, null, out)
|
let r = Vk.create_win32_surface_khr(render3d_st.gvk_inst, sci, null, out)
|
||||||
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateWin32SurfaceKHR", r) }
|
if r != VK_SUCCESS { return gvk_fail(render3d_st, "vkCreateWin32SurfaceKHR", r) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
|
||||||
|
if render3d_st.gvk_surface != 0 { return true }
|
||||||
|
let out = bytes(8)
|
||||||
|
if not gvk_surface_os(render3d_st, out) { return false }
|
||||||
render3d_st.gvk_surface = gvk_handle(out)
|
render3d_st.gvk_surface = gvk_handle(out)
|
||||||
let ok = bytes(4)
|
let ok = bytes(4)
|
||||||
Vk.put_i32(ok, 0, 0)
|
Vk.put_i32(ok, 0, 0)
|
||||||
|
|
@ -1551,6 +1578,7 @@ function gvk_surface_make(render3d_st: mut Render3dState) -> bool {
|
||||||
# (Re)make the swapchain for a w x h client area. The old one is handed over and then destroyed.
|
# (Re)make the swapchain for a w x h client area. The old one is handed over and then destroyed.
|
||||||
function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool {
|
function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool {
|
||||||
if not gvk_surface_make(render3d_st) { return false }
|
if not gvk_surface_make(render3d_st) { return false }
|
||||||
|
gvk_frame_wait(render3d_st)
|
||||||
Vk.device_wait_idle(render3d_st.gvk_dev)
|
Vk.device_wait_idle(render3d_st.gvk_dev)
|
||||||
let caps = bytes(VkSurfaceCapabilitiesKHR_sizeof)
|
let caps = bytes(VkSurfaceCapabilitiesKHR_sizeof)
|
||||||
Vk.get_physical_device_surface_capabilities_khr(render3d_st.gvk_pd, render3d_st.gvk_surface, caps)
|
Vk.get_physical_device_surface_capabilities_khr(render3d_st.gvk_pd, render3d_st.gvk_surface, caps)
|
||||||
|
|
@ -1641,6 +1669,14 @@ function gvk_swap_make(render3d_st: mut Render3dState, w: int, h: int) -> bool {
|
||||||
function gvk_present_window(render3d_st: mut Render3dState) -> void {
|
function gvk_present_window(render3d_st: mut Render3dState) -> void {
|
||||||
let cb = gvk_frame_cb(render3d_st)
|
let cb = gvk_frame_cb(render3d_st)
|
||||||
gvk_pass_end(render3d_st)
|
gvk_pass_end(render3d_st)
|
||||||
|
# a covered window is not drawn to: its layer would hold the frame for a drawable the compositor
|
||||||
|
# hands back once a second. The frame still runs, paced at about 60 Hz, so the game keeps time.
|
||||||
|
if win_visible() == 0 {
|
||||||
|
if gvk_inflight_on(render3d_st) { gvk_frame_submit(render3d_st, cb) } else { gvk_once_end(render3d_st, cb) }
|
||||||
|
render3d_st.gvk_cb = null
|
||||||
|
Time.sleep_us(16000)
|
||||||
|
return
|
||||||
|
}
|
||||||
if render3d_st.gvk_swap_stale { gvk_once_end(render3d_st, cb); render3d_st.gvk_cb = null; gvk_swap_make(render3d_st, render3d_st.gvk_swap_w, render3d_st.gvk_swap_h); return }
|
if render3d_st.gvk_swap_stale { gvk_once_end(render3d_st, cb); render3d_st.gvk_cb = null; gvk_swap_make(render3d_st, render3d_st.gvk_swap_w, render3d_st.gvk_swap_h); return }
|
||||||
let idx = bytes(4)
|
let idx = bytes(4)
|
||||||
Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_acq_fence)
|
Vk.reset_fences(render3d_st.gvk_dev, 1, render3d_st.gvk_acq_fence)
|
||||||
|
|
@ -1670,7 +1706,7 @@ function gvk_present_window(render3d_st: mut Render3dState) -> void {
|
||||||
Vk.cmd_blit_image(cb, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blit, VK_FILTER_LINEAR)
|
Vk.cmd_blit_image(cb, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blit, VK_FILTER_LINEAR)
|
||||||
gvk_barrier(cb, src, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
gvk_barrier(cb, src, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||||
gvk_barrier(cb, dst, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
|
gvk_barrier(cb, dst, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
|
||||||
gvk_once_end(render3d_st, cb)
|
if gvk_inflight_on(render3d_st) { gvk_frame_submit(render3d_st, cb) } else { gvk_once_end(render3d_st, cb) }
|
||||||
render3d_st.gvk_cb = null
|
render3d_st.gvk_cb = null
|
||||||
let pi = bytes(VkPresentInfoKHR_sizeof)
|
let pi = bytes(VkPresentInfoKHR_sizeof)
|
||||||
Vk.zero(pi, VkPresentInfoKHR_sizeof)
|
Vk.zero(pi, VkPresentInfoKHR_sizeof)
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ function gvk_staging(render3d_st: mut Render3dState, n: int, usage: int) -> poin
|
||||||
return Vk.get_ptr(pp, 0)
|
return Vk.get_ptr(pp, 0)
|
||||||
}
|
}
|
||||||
function gvk_staging_free(render3d_st: mut Render3dState) -> void {
|
function gvk_staging_free(render3d_st: mut Render3dState) -> void {
|
||||||
|
gvk_frame_wait(render3d_st) # the frame in flight may still read it
|
||||||
Vk.unmap_memory(render3d_st.gvk_dev, render3d_st.gvk_st_mem)
|
Vk.unmap_memory(render3d_st.gvk_dev, render3d_st.gvk_st_mem)
|
||||||
Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_st_buf, null)
|
Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_st_buf, null)
|
||||||
Vk.free_memory(render3d_st.gvk_dev, render3d_st.gvk_st_mem, null)
|
Vk.free_memory(render3d_st.gvk_dev, render3d_st.gvk_st_mem, null)
|
||||||
|
|
@ -142,6 +143,7 @@ function gvk_staging_free(render3d_st: mut Render3dState) -> void {
|
||||||
|
|
||||||
# (Re)make the image behind a handle: glTexImage2D on a texture that already has one replaces it.
|
# (Re)make the image behind a handle: glTexImage2D on a texture that already has one replaces it.
|
||||||
function gvk_tex_storage(render3d_st: mut Render3dState, tex: int, array: bool, ifmt: int, w: int, h: int, layers: int, with_mips: bool) -> bool {
|
function gvk_tex_storage(render3d_st: mut Render3dState, tex: int, array: bool, ifmt: int, w: int, h: int, layers: int, with_mips: bool) -> bool {
|
||||||
|
gvk_frame_wait(render3d_st) # the frame in flight may still read it
|
||||||
if tex <= 0 or tex >= len(render3d_st.gvk_tex_image) { return false }
|
if tex <= 0 or tex >= len(render3d_st.gvk_tex_image) { return false }
|
||||||
let vkfmt = gvk_format(ifmt)
|
let vkfmt = gvk_format(ifmt)
|
||||||
if vkfmt == VK_FORMAT_UNDEFINED { print(`r3d: vulkan: no image format for GL format {ifmt}`); return false }
|
if vkfmt == VK_FORMAT_UNDEFINED { print(`r3d: vulkan: no image format for GL format {ifmt}`); return false }
|
||||||
|
|
@ -304,6 +306,7 @@ function gvk_tex_upload(render3d_st: mut Render3dState, tex: int, ifmt: int, w:
|
||||||
# A target made without pixels has one level; the renderer asking it for mipmaps (the exposure
|
# A target made without pixels has one level; the renderer asking it for mipmaps (the exposure
|
||||||
# measure reads the HDR scene's smallest level every frame) grows it a full chain, level 0 kept.
|
# measure reads the HDR scene's smallest level every frame) grows it a full chain, level 0 kept.
|
||||||
function gvk_tex_grow_mips(render3d_st: mut Render3dState, tex: int, w: int, h: int) -> bool {
|
function gvk_tex_grow_mips(render3d_st: mut Render3dState, tex: int, w: int, h: int) -> bool {
|
||||||
|
gvk_frame_wait(render3d_st) # the frame in flight may still read it
|
||||||
let old_image = render3d_st.gvk_tex_image[tex]
|
let old_image = render3d_st.gvk_tex_image[tex]
|
||||||
let old_view = render3d_st.gvk_tex_view[tex]
|
let old_view = render3d_st.gvk_tex_view[tex]
|
||||||
let old_mem = render3d_st.gvk_tex_mem[tex]
|
let old_mem = render3d_st.gvk_tex_mem[tex]
|
||||||
|
|
@ -420,6 +423,7 @@ function gvk_tex_read(render3d_st: mut Render3dState, tex: int, ifmt: int, w: in
|
||||||
}
|
}
|
||||||
|
|
||||||
function gvk_tex_release(render3d_st: mut Render3dState, tex: int) -> void {
|
function gvk_tex_release(render3d_st: mut Render3dState, tex: int) -> void {
|
||||||
|
gvk_frame_wait(render3d_st) # the frame in flight may still read it
|
||||||
if render3d_st.gvk_tex_image[tex] == 0 { return }
|
if render3d_st.gvk_tex_image[tex] == 0 { return }
|
||||||
let zero: long = 0
|
let zero: long = 0
|
||||||
Vk.destroy_image_view(render3d_st.gvk_dev, render3d_st.gvk_tex_view[tex], null)
|
Vk.destroy_image_view(render3d_st.gvk_dev, render3d_st.gvk_tex_view[tex], null)
|
||||||
|
|
@ -553,8 +557,8 @@ function gvk_buf_new(render3d_st: mut Render3dState) -> int {
|
||||||
function gvk_buf_release(render3d_st: mut Render3dState, b: int) -> void {
|
function gvk_buf_release(render3d_st: mut Render3dState, b: int) -> void {
|
||||||
if render3d_st.gvk_buf[b] == 0 { return }
|
if render3d_st.gvk_buf[b] == 0 { return }
|
||||||
let zero: long = 0
|
let zero: long = 0
|
||||||
if render3d_st.gvk_buf_used[b] == render3d_st.gvk_frame_no {
|
if gvk_buf_busy(render3d_st, b) {
|
||||||
# a draw recorded this frame still reads it: destroy it once the frame has been submitted
|
# a draw recorded this frame (or the frame in flight) still reads it: destroy it once the frame has been submitted
|
||||||
push(render3d_st.gvk_retired_buf, render3d_st.gvk_buf[b]); push(render3d_st.gvk_retired_mem, render3d_st.gvk_buf_mem[b])
|
push(render3d_st.gvk_retired_buf, render3d_st.gvk_buf[b]); push(render3d_st.gvk_retired_mem, render3d_st.gvk_buf_mem[b])
|
||||||
} else {
|
} else {
|
||||||
Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_buf[b], null)
|
Vk.destroy_buffer(render3d_st.gvk_dev, render3d_st.gvk_buf[b], null)
|
||||||
|
|
@ -562,6 +566,12 @@ function gvk_buf_release(render3d_st: mut Render3dState, b: int) -> void {
|
||||||
}
|
}
|
||||||
render3d_st.gvk_buf[b] = zero; render3d_st.gvk_buf_mem[b] = zero; render3d_st.gvk_buf_size[b] = 0; render3d_st.gvk_buf_map[b] = null; render3d_st.gvk_buf_used[b] = 0
|
render3d_st.gvk_buf[b] = zero; render3d_st.gvk_buf_mem[b] = zero; render3d_st.gvk_buf_size[b] = 0; render3d_st.gvk_buf_map[b] = null; render3d_st.gvk_buf_used[b] = 0
|
||||||
}
|
}
|
||||||
|
# read by a draw recorded this frame, or by the presented frame still on the GPU
|
||||||
|
function gvk_buf_busy(render3d_st: Render3dState, b: int) -> bool {
|
||||||
|
let u = render3d_st.gvk_buf_used[b]
|
||||||
|
return u == render3d_st.gvk_frame_no or (render3d_st.gvk_frame_pending and u == render3d_st.gvk_frame_no - 1)
|
||||||
|
}
|
||||||
|
|
||||||
# the frame's submitted work is done: storage retired during it can go
|
# the frame's submitted work is done: storage retired during it can go
|
||||||
function gvk_retire_flush(render3d_st: mut Render3dState) -> void {
|
function gvk_retire_flush(render3d_st: mut Render3dState) -> void {
|
||||||
if render3d_st.gvk_retired_buf == null { return }
|
if render3d_st.gvk_retired_buf == null { return }
|
||||||
|
|
@ -587,7 +597,7 @@ function gvk_buf_is_gpu(render3d_st: Render3dState, b: int) -> bool { return ren
|
||||||
function gvk_buf_reserve(render3d_st: mut Render3dState, b: int, n: int) -> bool {
|
function gvk_buf_reserve(render3d_st: mut Render3dState, b: int, n: int) -> bool {
|
||||||
if b <= 0 or b >= len(render3d_st.gvk_buf) { return false }
|
if b <= 0 or b >= len(render3d_st.gvk_buf) { return false }
|
||||||
# already big enough, and no draw this frame reads what is there: fill it in place
|
# already big enough, and no draw this frame reads what is there: fill it in place
|
||||||
if render3d_st.gvk_buf[b] != 0 and render3d_st.gvk_buf_size[b] >= n and (render3d_st.gvk_buf_used[b] != render3d_st.gvk_frame_no or gvk_buf_is_gpu(render3d_st, b)) { return true }
|
if render3d_st.gvk_buf[b] != 0 and render3d_st.gvk_buf_size[b] >= n and (not gvk_buf_busy(render3d_st, b) or gvk_buf_is_gpu(render3d_st, b)) { return true }
|
||||||
gvk_buf_release(render3d_st, b)
|
gvk_buf_release(render3d_st, b)
|
||||||
var size = n
|
var size = n
|
||||||
if size < 64 { size = 64 }
|
if size < 64 { size = 64 }
|
||||||
|
|
|
||||||
BIN
packages/ludic.render3d/shaders/spv/266d2401.frag.spv
Normal file
BIN
packages/ludic.render3d/shaders/spv/266d2401.frag.spv
Normal file
Binary file not shown.
BIN
packages/ludic.render3d/shaders/spv/266d2401.vert.spv
Normal file
BIN
packages/ludic.render3d/shaders/spv/266d2401.vert.spv
Normal file
Binary file not shown.
BIN
packages/ludic.render3d/shaders/spv/5e70d731.frag.spv
Normal file
BIN
packages/ludic.render3d/shaders/spv/5e70d731.frag.spv
Normal file
Binary file not shown.
BIN
packages/ludic.render3d/shaders/spv/5e70d731.vert.spv
Normal file
BIN
packages/ludic.render3d/shaders/spv/5e70d731.vert.spv
Normal file
Binary file not shown.
BIN
packages/ludic.render3d/shaders/spv/b603a056.frag.spv
Normal file
BIN
packages/ludic.render3d/shaders/spv/b603a056.frag.spv
Normal file
Binary file not shown.
BIN
packages/ludic.render3d/shaders/spv/b603a056.vert.spv
Normal file
BIN
packages/ludic.render3d/shaders/spv/b603a056.vert.spv
Normal file
Binary file not shown.
|
|
@ -141,6 +141,56 @@ T c57b4f14 u_shadow 5
|
||||||
T c57b4f14 u_src 6
|
T c57b4f14 u_src 6
|
||||||
T c57b4f14 u_tershadow 7
|
T c57b4f14 u_tershadow 7
|
||||||
T c57b4f14 u_ts_height 8
|
T c57b4f14 u_ts_height 8
|
||||||
|
P 5161e6ba fullscreen.vert dof.frag
|
||||||
|
B 5161e6ba frag 1 916
|
||||||
|
U 5161e6ba frag u_cascade_vp 0 mat4 5 64
|
||||||
|
U 5161e6ba frag u_cascade_split 320 float 5 16
|
||||||
|
U 5161e6ba frag u_cascade_range 400 float 5 16
|
||||||
|
U 5161e6ba frag u_cascade_texel 480 float 5 16
|
||||||
|
U 5161e6ba frag u_sun_dir 560 vec3 1 0
|
||||||
|
U 5161e6ba frag u_sun_color 576 vec3 1 0
|
||||||
|
U 5161e6ba frag u_cam_pos 592 vec3 1 0
|
||||||
|
U 5161e6ba frag u_prefilter_levels 604 float 1 0
|
||||||
|
U 5161e6ba frag u_fog_density 608 float 1 0
|
||||||
|
U 5161e6ba frag u_fog_height_falloff 612 float 1 0
|
||||||
|
U 5161e6ba frag u_fog_base 616 float 1 0
|
||||||
|
U 5161e6ba frag u_clip_y 620 float 1 0
|
||||||
|
U 5161e6ba frag u_spec_scale 624 float 1 0
|
||||||
|
U 5161e6ba frag u_sky_rot 632 vec2 1 0
|
||||||
|
U 5161e6ba frag u_ground_alb 640 vec3 1 0
|
||||||
|
U 5161e6ba frag u_ground_alb_hi 656 vec3 1 0
|
||||||
|
U 5161e6ba frag u_ground_hi_y 668 float 1 0
|
||||||
|
U 5161e6ba frag u_ground_hi_w 672 float 1 0
|
||||||
|
U 5161e6ba frag u_ibl_scale 688 vec3 1 0
|
||||||
|
U 5161e6ba frag u_daylight 700 float 1 0
|
||||||
|
U 5161e6ba frag u_fire_pos 704 vec3 1 0
|
||||||
|
U 5161e6ba frag u_fire_color 720 vec3 1 0
|
||||||
|
U 5161e6ba frag u_hand_pos 736 vec3 1 0
|
||||||
|
U 5161e6ba frag u_hand_color 752 vec3 1 0
|
||||||
|
U 5161e6ba frag u_hand_dir 768 vec3 1 0
|
||||||
|
U 5161e6ba frag u_hand_cone 780 float 1 0
|
||||||
|
U 5161e6ba frag u_hand_reach 784 float 1 0
|
||||||
|
U 5161e6ba frag u_ts_origin 792 vec2 1 0
|
||||||
|
U 5161e6ba frag u_ts_half 800 float 1 0
|
||||||
|
U 5161e6ba frag u_ts_on 804 float 1 0
|
||||||
|
U 5161e6ba frag u_force_cascade 808 int 1 0
|
||||||
|
U 5161e6ba frag u_cloud_shadow 812 float 1 0
|
||||||
|
U 5161e6ba frag u_time 816 float 1 0
|
||||||
|
U 5161e6ba frag u_fog_inscatter 820 float 1 0
|
||||||
|
U 5161e6ba frag u_fog_desat 824 float 1 0
|
||||||
|
U 5161e6ba frag u_inv_proj 832 mat4 1 0
|
||||||
|
U 5161e6ba frag u_texel 896 vec2 1 0
|
||||||
|
U 5161e6ba frag u_focus 904 float 1 0
|
||||||
|
U 5161e6ba frag u_aperture 908 float 1 0
|
||||||
|
U 5161e6ba frag u_max_coc 912 float 1 0
|
||||||
|
T 5161e6ba u_brdf 2
|
||||||
|
T 5161e6ba u_depth 3
|
||||||
|
T 5161e6ba u_irradiance 4
|
||||||
|
T 5161e6ba u_prefilter 5
|
||||||
|
T 5161e6ba u_shadow 6
|
||||||
|
T 5161e6ba u_src 7
|
||||||
|
T 5161e6ba u_tershadow 8
|
||||||
|
T 5161e6ba u_ts_height 9
|
||||||
P 4be7acd9 fullscreen.vert heightgen.frag #define DEM;
|
P 4be7acd9 fullscreen.vert heightgen.frag #define DEM;
|
||||||
B 4be7acd9 frag 1 920
|
B 4be7acd9 frag 1 920
|
||||||
U 4be7acd9 frag u_cascade_vp 0 mat4 5 64
|
U 4be7acd9 frag u_cascade_vp 0 mat4 5 64
|
||||||
|
|
@ -740,6 +790,58 @@ T a4683767 u_prefilter 8
|
||||||
T a4683767 u_shadow 9
|
T a4683767 u_shadow 9
|
||||||
T a4683767 u_tershadow 10
|
T a4683767 u_tershadow 10
|
||||||
T a4683767 u_ts_height 11
|
T a4683767 u_ts_height 11
|
||||||
|
P 74258189 fullscreen.vert volumetric.frag
|
||||||
|
B 74258189 frag 1 924
|
||||||
|
U 74258189 frag u_cascade_vp 0 mat4 5 64
|
||||||
|
U 74258189 frag u_cascade_split 320 float 5 16
|
||||||
|
U 74258189 frag u_cascade_range 400 float 5 16
|
||||||
|
U 74258189 frag u_cascade_texel 480 float 5 16
|
||||||
|
U 74258189 frag u_sun_dir 560 vec3 1 0
|
||||||
|
U 74258189 frag u_sun_color 576 vec3 1 0
|
||||||
|
U 74258189 frag u_cam_pos 592 vec3 1 0
|
||||||
|
U 74258189 frag u_prefilter_levels 604 float 1 0
|
||||||
|
U 74258189 frag u_fog_density 608 float 1 0
|
||||||
|
U 74258189 frag u_fog_height_falloff 612 float 1 0
|
||||||
|
U 74258189 frag u_fog_base 616 float 1 0
|
||||||
|
U 74258189 frag u_clip_y 620 float 1 0
|
||||||
|
U 74258189 frag u_spec_scale 624 float 1 0
|
||||||
|
U 74258189 frag u_sky_rot 632 vec2 1 0
|
||||||
|
U 74258189 frag u_ground_alb 640 vec3 1 0
|
||||||
|
U 74258189 frag u_ground_alb_hi 656 vec3 1 0
|
||||||
|
U 74258189 frag u_ground_hi_y 668 float 1 0
|
||||||
|
U 74258189 frag u_ground_hi_w 672 float 1 0
|
||||||
|
U 74258189 frag u_ibl_scale 688 vec3 1 0
|
||||||
|
U 74258189 frag u_daylight 700 float 1 0
|
||||||
|
U 74258189 frag u_fire_pos 704 vec3 1 0
|
||||||
|
U 74258189 frag u_fire_color 720 vec3 1 0
|
||||||
|
U 74258189 frag u_hand_pos 736 vec3 1 0
|
||||||
|
U 74258189 frag u_hand_color 752 vec3 1 0
|
||||||
|
U 74258189 frag u_hand_dir 768 vec3 1 0
|
||||||
|
U 74258189 frag u_hand_cone 780 float 1 0
|
||||||
|
U 74258189 frag u_hand_reach 784 float 1 0
|
||||||
|
U 74258189 frag u_ts_origin 792 vec2 1 0
|
||||||
|
U 74258189 frag u_ts_half 800 float 1 0
|
||||||
|
U 74258189 frag u_ts_on 804 float 1 0
|
||||||
|
U 74258189 frag u_force_cascade 808 int 1 0
|
||||||
|
U 74258189 frag u_cloud_shadow 812 float 1 0
|
||||||
|
U 74258189 frag u_time 816 float 1 0
|
||||||
|
U 74258189 frag u_fog_inscatter 820 float 1 0
|
||||||
|
U 74258189 frag u_fog_desat 824 float 1 0
|
||||||
|
U 74258189 frag u_inv_vp 832 mat4 1 0
|
||||||
|
U 74258189 frag u_vol_steps 896 float 1 0
|
||||||
|
U 74258189 frag u_vol_density 900 float 1 0
|
||||||
|
U 74258189 frag u_vol_falloff 904 float 1 0
|
||||||
|
U 74258189 frag u_vol_far 908 float 1 0
|
||||||
|
U 74258189 frag u_vol_g 912 float 1 0
|
||||||
|
U 74258189 frag u_vol_mist 916 float 1 0
|
||||||
|
U 74258189 frag u_vol_mist_h 920 float 1 0
|
||||||
|
T 74258189 u_brdf 2
|
||||||
|
T 74258189 u_depth 3
|
||||||
|
T 74258189 u_irradiance 4
|
||||||
|
T 74258189 u_prefilter 5
|
||||||
|
T 74258189 u_shadow 6
|
||||||
|
T 74258189 u_tershadow 7
|
||||||
|
T 74258189 u_ts_height 8
|
||||||
P d48d6a48 grass.mesh model.frag #define FOLIAGE;#define BLADE;#define MESH;
|
P d48d6a48 grass.mesh model.frag #define FOLIAGE;#define BLADE;#define MESH;
|
||||||
B d48d6a48 vert 0 4384
|
B d48d6a48 vert 0 4384
|
||||||
U d48d6a48 vert u_view 0 mat4 1 0
|
U d48d6a48 vert u_view 0 mat4 1 0
|
||||||
|
|
@ -1355,6 +1457,68 @@ T bfa15301 u_prefilter 5
|
||||||
T bfa15301 u_shadow 6
|
T bfa15301 u_shadow 6
|
||||||
T bfa15301 u_tershadow 7
|
T bfa15301 u_tershadow 7
|
||||||
T bfa15301 u_ts_height 8
|
T bfa15301 u_ts_height 8
|
||||||
|
P 266d2401 model.vert depth.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
|
||||||
|
B 266d2401 vert 0 236
|
||||||
|
U 266d2401 vert u_view 0 mat4 1 0
|
||||||
|
U 266d2401 vert u_proj 64 mat4 1 0
|
||||||
|
U 266d2401 vert u_light_vp 128 mat4 1 0
|
||||||
|
U 266d2401 vert u_time 192 float 1 0
|
||||||
|
U 266d2401 vert u_wind 196 float 1 0
|
||||||
|
U 266d2401 vert u_card_w 200 float 1 0
|
||||||
|
U 266d2401 vert u_card_h 204 float 1 0
|
||||||
|
U 266d2401 vert u_ground 208 float 1 0
|
||||||
|
U 266d2401 vert u_ts_origin 216 vec2 1 0
|
||||||
|
U 266d2401 vert u_ts_half 224 float 1 0
|
||||||
|
U 266d2401 vert u_model_h 228 float 1 0
|
||||||
|
U 266d2401 vert u_flutter 232 float 1 0
|
||||||
|
I 266d2401 i_rot 4 vec4
|
||||||
|
I 266d2401 a_pos 0 vec3
|
||||||
|
I 266d2401 i_pos 3 vec4
|
||||||
|
I 266d2401 a_nrm 1 vec3
|
||||||
|
I 266d2401 a_uv 2 vec2
|
||||||
|
B 266d2401 frag 1 828
|
||||||
|
U 266d2401 frag u_cascade_vp 0 mat4 5 64
|
||||||
|
U 266d2401 frag u_cascade_split 320 float 5 16
|
||||||
|
U 266d2401 frag u_cascade_range 400 float 5 16
|
||||||
|
U 266d2401 frag u_cascade_texel 480 float 5 16
|
||||||
|
U 266d2401 frag u_sun_dir 560 vec3 1 0
|
||||||
|
U 266d2401 frag u_sun_color 576 vec3 1 0
|
||||||
|
U 266d2401 frag u_cam_pos 592 vec3 1 0
|
||||||
|
U 266d2401 frag u_prefilter_levels 604 float 1 0
|
||||||
|
U 266d2401 frag u_fog_density 608 float 1 0
|
||||||
|
U 266d2401 frag u_fog_height_falloff 612 float 1 0
|
||||||
|
U 266d2401 frag u_fog_base 616 float 1 0
|
||||||
|
U 266d2401 frag u_clip_y 620 float 1 0
|
||||||
|
U 266d2401 frag u_spec_scale 624 float 1 0
|
||||||
|
U 266d2401 frag u_sky_rot 632 vec2 1 0
|
||||||
|
U 266d2401 frag u_ground_alb 640 vec3 1 0
|
||||||
|
U 266d2401 frag u_ground_alb_hi 656 vec3 1 0
|
||||||
|
U 266d2401 frag u_ground_hi_y 668 float 1 0
|
||||||
|
U 266d2401 frag u_ground_hi_w 672 float 1 0
|
||||||
|
U 266d2401 frag u_ibl_scale 688 vec3 1 0
|
||||||
|
U 266d2401 frag u_daylight 700 float 1 0
|
||||||
|
U 266d2401 frag u_fire_pos 704 vec3 1 0
|
||||||
|
U 266d2401 frag u_fire_color 720 vec3 1 0
|
||||||
|
U 266d2401 frag u_hand_pos 736 vec3 1 0
|
||||||
|
U 266d2401 frag u_hand_color 752 vec3 1 0
|
||||||
|
U 266d2401 frag u_hand_dir 768 vec3 1 0
|
||||||
|
U 266d2401 frag u_hand_cone 780 float 1 0
|
||||||
|
U 266d2401 frag u_hand_reach 784 float 1 0
|
||||||
|
U 266d2401 frag u_ts_origin 792 vec2 1 0
|
||||||
|
U 266d2401 frag u_ts_half 800 float 1 0
|
||||||
|
U 266d2401 frag u_ts_on 804 float 1 0
|
||||||
|
U 266d2401 frag u_force_cascade 808 int 1 0
|
||||||
|
U 266d2401 frag u_cloud_shadow 812 float 1 0
|
||||||
|
U 266d2401 frag u_time 816 float 1 0
|
||||||
|
U 266d2401 frag u_fog_inscatter 820 float 1 0
|
||||||
|
U 266d2401 frag u_fog_desat 824 float 1 0
|
||||||
|
T 266d2401 u_brdf 2
|
||||||
|
T 266d2401 u_diff 3
|
||||||
|
T 266d2401 u_irradiance 4
|
||||||
|
T 266d2401 u_prefilter 5
|
||||||
|
T 266d2401 u_shadow 6
|
||||||
|
T 266d2401 u_tershadow 7
|
||||||
|
T 266d2401 u_ts_height 8
|
||||||
P 01089da8 model.vert model.frag
|
P 01089da8 model.vert model.frag
|
||||||
B 01089da8 vert 0 236
|
B 01089da8 vert 0 236
|
||||||
U 01089da8 vert u_view 0 mat4 1 0
|
U 01089da8 vert u_view 0 mat4 1 0
|
||||||
|
|
@ -1566,6 +1730,76 @@ T 1ce5c5d1 u_prefilter 8
|
||||||
T 1ce5c5d1 u_shadow 9
|
T 1ce5c5d1 u_shadow 9
|
||||||
T 1ce5c5d1 u_tershadow 10
|
T 1ce5c5d1 u_tershadow 10
|
||||||
T 1ce5c5d1 u_ts_height 11
|
T 1ce5c5d1 u_ts_height 11
|
||||||
|
P 5e70d731 model.vert model.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
|
||||||
|
B 5e70d731 vert 0 236
|
||||||
|
U 5e70d731 vert u_view 0 mat4 1 0
|
||||||
|
U 5e70d731 vert u_proj 64 mat4 1 0
|
||||||
|
U 5e70d731 vert u_light_vp 128 mat4 1 0
|
||||||
|
U 5e70d731 vert u_time 192 float 1 0
|
||||||
|
U 5e70d731 vert u_wind 196 float 1 0
|
||||||
|
U 5e70d731 vert u_card_w 200 float 1 0
|
||||||
|
U 5e70d731 vert u_card_h 204 float 1 0
|
||||||
|
U 5e70d731 vert u_ground 208 float 1 0
|
||||||
|
U 5e70d731 vert u_ts_origin 216 vec2 1 0
|
||||||
|
U 5e70d731 vert u_ts_half 224 float 1 0
|
||||||
|
U 5e70d731 vert u_model_h 228 float 1 0
|
||||||
|
U 5e70d731 vert u_flutter 232 float 1 0
|
||||||
|
I 5e70d731 i_rot 4 vec4
|
||||||
|
I 5e70d731 a_pos 0 vec3
|
||||||
|
I 5e70d731 i_pos 3 vec4
|
||||||
|
I 5e70d731 a_nrm 1 vec3
|
||||||
|
I 5e70d731 a_uv 2 vec2
|
||||||
|
B 5e70d731 frag 1 916
|
||||||
|
U 5e70d731 frag u_cascade_vp 0 mat4 5 64
|
||||||
|
U 5e70d731 frag u_cascade_split 320 float 5 16
|
||||||
|
U 5e70d731 frag u_cascade_range 400 float 5 16
|
||||||
|
U 5e70d731 frag u_cascade_texel 480 float 5 16
|
||||||
|
U 5e70d731 frag u_sun_dir 560 vec3 1 0
|
||||||
|
U 5e70d731 frag u_sun_color 576 vec3 1 0
|
||||||
|
U 5e70d731 frag u_cam_pos 592 vec3 1 0
|
||||||
|
U 5e70d731 frag u_prefilter_levels 604 float 1 0
|
||||||
|
U 5e70d731 frag u_fog_density 608 float 1 0
|
||||||
|
U 5e70d731 frag u_fog_height_falloff 612 float 1 0
|
||||||
|
U 5e70d731 frag u_fog_base 616 float 1 0
|
||||||
|
U 5e70d731 frag u_clip_y 620 float 1 0
|
||||||
|
U 5e70d731 frag u_spec_scale 624 float 1 0
|
||||||
|
U 5e70d731 frag u_sky_rot 632 vec2 1 0
|
||||||
|
U 5e70d731 frag u_ground_alb 640 vec3 1 0
|
||||||
|
U 5e70d731 frag u_ground_alb_hi 656 vec3 1 0
|
||||||
|
U 5e70d731 frag u_ground_hi_y 668 float 1 0
|
||||||
|
U 5e70d731 frag u_ground_hi_w 672 float 1 0
|
||||||
|
U 5e70d731 frag u_ibl_scale 688 vec3 1 0
|
||||||
|
U 5e70d731 frag u_daylight 700 float 1 0
|
||||||
|
U 5e70d731 frag u_fire_pos 704 vec3 1 0
|
||||||
|
U 5e70d731 frag u_fire_color 720 vec3 1 0
|
||||||
|
U 5e70d731 frag u_hand_pos 736 vec3 1 0
|
||||||
|
U 5e70d731 frag u_hand_color 752 vec3 1 0
|
||||||
|
U 5e70d731 frag u_hand_dir 768 vec3 1 0
|
||||||
|
U 5e70d731 frag u_hand_cone 780 float 1 0
|
||||||
|
U 5e70d731 frag u_hand_reach 784 float 1 0
|
||||||
|
U 5e70d731 frag u_ts_origin 792 vec2 1 0
|
||||||
|
U 5e70d731 frag u_ts_half 800 float 1 0
|
||||||
|
U 5e70d731 frag u_ts_on 804 float 1 0
|
||||||
|
U 5e70d731 frag u_force_cascade 808 int 1 0
|
||||||
|
U 5e70d731 frag u_cloud_shadow 812 float 1 0
|
||||||
|
U 5e70d731 frag u_time 816 float 1 0
|
||||||
|
U 5e70d731 frag u_fog_inscatter 820 float 1 0
|
||||||
|
U 5e70d731 frag u_fog_desat 824 float 1 0
|
||||||
|
U 5e70d731 frag u_model_h 828 float 1 0
|
||||||
|
U 5e70d731 frag u_view 832 mat4 1 0
|
||||||
|
U 5e70d731 frag u_tint 896 vec3 1 0
|
||||||
|
U 5e70d731 frag u_rough_scale 908 float 1 0
|
||||||
|
U 5e70d731 frag u_emissive 912 float 1 0
|
||||||
|
T 5e70d731 u_arm 2
|
||||||
|
T 5e70d731 u_blade_tex 3
|
||||||
|
T 5e70d731 u_brdf 4
|
||||||
|
T 5e70d731 u_diff 5
|
||||||
|
T 5e70d731 u_irradiance 6
|
||||||
|
T 5e70d731 u_nrm 7
|
||||||
|
T 5e70d731 u_prefilter 8
|
||||||
|
T 5e70d731 u_shadow 9
|
||||||
|
T 5e70d731 u_tershadow 10
|
||||||
|
T 5e70d731 u_ts_height 11
|
||||||
P f4a2b586 model.vert model.frag #define FOLIAGE;#define WIND;#define BLADE;
|
P f4a2b586 model.vert model.frag #define FOLIAGE;#define WIND;#define BLADE;
|
||||||
B f4a2b586 vert 0 252
|
B f4a2b586 vert 0 252
|
||||||
U f4a2b586 vert u_view 0 mat4 1 0
|
U f4a2b586 vert u_view 0 mat4 1 0
|
||||||
|
|
@ -1783,6 +2017,76 @@ T 4dda406a u_prefilter 8
|
||||||
T 4dda406a u_shadow 9
|
T 4dda406a u_shadow 9
|
||||||
T 4dda406a u_tershadow 10
|
T 4dda406a u_tershadow 10
|
||||||
T 4dda406a u_ts_height 11
|
T 4dda406a u_ts_height 11
|
||||||
|
P b603a056 model.vert model.frag #define FOLIAGE;#define WIND;#define EQ_PASS;#define NEAR_FADE;
|
||||||
|
B b603a056 vert 0 236
|
||||||
|
U b603a056 vert u_view 0 mat4 1 0
|
||||||
|
U b603a056 vert u_proj 64 mat4 1 0
|
||||||
|
U b603a056 vert u_light_vp 128 mat4 1 0
|
||||||
|
U b603a056 vert u_time 192 float 1 0
|
||||||
|
U b603a056 vert u_wind 196 float 1 0
|
||||||
|
U b603a056 vert u_card_w 200 float 1 0
|
||||||
|
U b603a056 vert u_card_h 204 float 1 0
|
||||||
|
U b603a056 vert u_ground 208 float 1 0
|
||||||
|
U b603a056 vert u_ts_origin 216 vec2 1 0
|
||||||
|
U b603a056 vert u_ts_half 224 float 1 0
|
||||||
|
U b603a056 vert u_model_h 228 float 1 0
|
||||||
|
U b603a056 vert u_flutter 232 float 1 0
|
||||||
|
I b603a056 i_rot 4 vec4
|
||||||
|
I b603a056 a_pos 0 vec3
|
||||||
|
I b603a056 i_pos 3 vec4
|
||||||
|
I b603a056 a_nrm 1 vec3
|
||||||
|
I b603a056 a_uv 2 vec2
|
||||||
|
B b603a056 frag 1 916
|
||||||
|
U b603a056 frag u_cascade_vp 0 mat4 5 64
|
||||||
|
U b603a056 frag u_cascade_split 320 float 5 16
|
||||||
|
U b603a056 frag u_cascade_range 400 float 5 16
|
||||||
|
U b603a056 frag u_cascade_texel 480 float 5 16
|
||||||
|
U b603a056 frag u_sun_dir 560 vec3 1 0
|
||||||
|
U b603a056 frag u_sun_color 576 vec3 1 0
|
||||||
|
U b603a056 frag u_cam_pos 592 vec3 1 0
|
||||||
|
U b603a056 frag u_prefilter_levels 604 float 1 0
|
||||||
|
U b603a056 frag u_fog_density 608 float 1 0
|
||||||
|
U b603a056 frag u_fog_height_falloff 612 float 1 0
|
||||||
|
U b603a056 frag u_fog_base 616 float 1 0
|
||||||
|
U b603a056 frag u_clip_y 620 float 1 0
|
||||||
|
U b603a056 frag u_spec_scale 624 float 1 0
|
||||||
|
U b603a056 frag u_sky_rot 632 vec2 1 0
|
||||||
|
U b603a056 frag u_ground_alb 640 vec3 1 0
|
||||||
|
U b603a056 frag u_ground_alb_hi 656 vec3 1 0
|
||||||
|
U b603a056 frag u_ground_hi_y 668 float 1 0
|
||||||
|
U b603a056 frag u_ground_hi_w 672 float 1 0
|
||||||
|
U b603a056 frag u_ibl_scale 688 vec3 1 0
|
||||||
|
U b603a056 frag u_daylight 700 float 1 0
|
||||||
|
U b603a056 frag u_fire_pos 704 vec3 1 0
|
||||||
|
U b603a056 frag u_fire_color 720 vec3 1 0
|
||||||
|
U b603a056 frag u_hand_pos 736 vec3 1 0
|
||||||
|
U b603a056 frag u_hand_color 752 vec3 1 0
|
||||||
|
U b603a056 frag u_hand_dir 768 vec3 1 0
|
||||||
|
U b603a056 frag u_hand_cone 780 float 1 0
|
||||||
|
U b603a056 frag u_hand_reach 784 float 1 0
|
||||||
|
U b603a056 frag u_ts_origin 792 vec2 1 0
|
||||||
|
U b603a056 frag u_ts_half 800 float 1 0
|
||||||
|
U b603a056 frag u_ts_on 804 float 1 0
|
||||||
|
U b603a056 frag u_force_cascade 808 int 1 0
|
||||||
|
U b603a056 frag u_cloud_shadow 812 float 1 0
|
||||||
|
U b603a056 frag u_time 816 float 1 0
|
||||||
|
U b603a056 frag u_fog_inscatter 820 float 1 0
|
||||||
|
U b603a056 frag u_fog_desat 824 float 1 0
|
||||||
|
U b603a056 frag u_model_h 828 float 1 0
|
||||||
|
U b603a056 frag u_view 832 mat4 1 0
|
||||||
|
U b603a056 frag u_tint 896 vec3 1 0
|
||||||
|
U b603a056 frag u_rough_scale 908 float 1 0
|
||||||
|
U b603a056 frag u_emissive 912 float 1 0
|
||||||
|
T b603a056 u_arm 2
|
||||||
|
T b603a056 u_blade_tex 3
|
||||||
|
T b603a056 u_brdf 4
|
||||||
|
T b603a056 u_diff 5
|
||||||
|
T b603a056 u_irradiance 6
|
||||||
|
T b603a056 u_nrm 7
|
||||||
|
T b603a056 u_prefilter 8
|
||||||
|
T b603a056 u_shadow 9
|
||||||
|
T b603a056 u_tershadow 10
|
||||||
|
T b603a056 u_ts_height 11
|
||||||
P 3b183d8f model.vert model.frag #define FOLIAGE;#define WIND;#define FLOWER;
|
P 3b183d8f model.vert model.frag #define FOLIAGE;#define WIND;#define FLOWER;
|
||||||
B 3b183d8f vert 0 236
|
B 3b183d8f vert 0 236
|
||||||
U 3b183d8f vert u_view 0 mat4 1 0
|
U 3b183d8f vert u_view 0 mat4 1 0
|
||||||
|
|
@ -3218,105 +3522,3 @@ T 46e2325c u_scene 7
|
||||||
T 46e2325c u_shadow 8
|
T 46e2325c u_shadow 8
|
||||||
T 46e2325c u_tershadow 9
|
T 46e2325c u_tershadow 9
|
||||||
T 46e2325c u_ts_height 10
|
T 46e2325c u_ts_height 10
|
||||||
P 74258189 fullscreen.vert volumetric.frag
|
|
||||||
B 74258189 frag 1 924
|
|
||||||
U 74258189 frag u_cascade_vp 0 mat4 5 64
|
|
||||||
U 74258189 frag u_cascade_split 320 float 5 16
|
|
||||||
U 74258189 frag u_cascade_range 400 float 5 16
|
|
||||||
U 74258189 frag u_cascade_texel 480 float 5 16
|
|
||||||
U 74258189 frag u_sun_dir 560 vec3 1 0
|
|
||||||
U 74258189 frag u_sun_color 576 vec3 1 0
|
|
||||||
U 74258189 frag u_cam_pos 592 vec3 1 0
|
|
||||||
U 74258189 frag u_prefilter_levels 604 float 1 0
|
|
||||||
U 74258189 frag u_fog_density 608 float 1 0
|
|
||||||
U 74258189 frag u_fog_height_falloff 612 float 1 0
|
|
||||||
U 74258189 frag u_fog_base 616 float 1 0
|
|
||||||
U 74258189 frag u_clip_y 620 float 1 0
|
|
||||||
U 74258189 frag u_spec_scale 624 float 1 0
|
|
||||||
U 74258189 frag u_sky_rot 632 vec2 1 0
|
|
||||||
U 74258189 frag u_ground_alb 640 vec3 1 0
|
|
||||||
U 74258189 frag u_ground_alb_hi 656 vec3 1 0
|
|
||||||
U 74258189 frag u_ground_hi_y 668 float 1 0
|
|
||||||
U 74258189 frag u_ground_hi_w 672 float 1 0
|
|
||||||
U 74258189 frag u_ibl_scale 688 vec3 1 0
|
|
||||||
U 74258189 frag u_daylight 700 float 1 0
|
|
||||||
U 74258189 frag u_fire_pos 704 vec3 1 0
|
|
||||||
U 74258189 frag u_fire_color 720 vec3 1 0
|
|
||||||
U 74258189 frag u_hand_pos 736 vec3 1 0
|
|
||||||
U 74258189 frag u_hand_color 752 vec3 1 0
|
|
||||||
U 74258189 frag u_hand_dir 768 vec3 1 0
|
|
||||||
U 74258189 frag u_hand_cone 780 float 1 0
|
|
||||||
U 74258189 frag u_hand_reach 784 float 1 0
|
|
||||||
U 74258189 frag u_ts_origin 792 vec2 1 0
|
|
||||||
U 74258189 frag u_ts_half 800 float 1 0
|
|
||||||
U 74258189 frag u_ts_on 804 float 1 0
|
|
||||||
U 74258189 frag u_force_cascade 808 int 1 0
|
|
||||||
U 74258189 frag u_cloud_shadow 812 float 1 0
|
|
||||||
U 74258189 frag u_time 816 float 1 0
|
|
||||||
U 74258189 frag u_fog_inscatter 820 float 1 0
|
|
||||||
U 74258189 frag u_fog_desat 824 float 1 0
|
|
||||||
U 74258189 frag u_inv_vp 832 mat4 1 0
|
|
||||||
U 74258189 frag u_vol_steps 896 float 1 0
|
|
||||||
U 74258189 frag u_vol_density 900 float 1 0
|
|
||||||
U 74258189 frag u_vol_falloff 904 float 1 0
|
|
||||||
U 74258189 frag u_vol_far 908 float 1 0
|
|
||||||
U 74258189 frag u_vol_g 912 float 1 0
|
|
||||||
U 74258189 frag u_vol_mist 916 float 1 0
|
|
||||||
U 74258189 frag u_vol_mist_h 920 float 1 0
|
|
||||||
T 74258189 u_brdf 2
|
|
||||||
T 74258189 u_depth 3
|
|
||||||
T 74258189 u_irradiance 4
|
|
||||||
T 74258189 u_prefilter 5
|
|
||||||
T 74258189 u_shadow 6
|
|
||||||
T 74258189 u_tershadow 7
|
|
||||||
T 74258189 u_ts_height 8
|
|
||||||
P 5161e6ba fullscreen.vert dof.frag
|
|
||||||
B 5161e6ba frag 1 916
|
|
||||||
U 5161e6ba frag u_cascade_vp 0 mat4 5 64
|
|
||||||
U 5161e6ba frag u_cascade_split 320 float 5 16
|
|
||||||
U 5161e6ba frag u_cascade_range 400 float 5 16
|
|
||||||
U 5161e6ba frag u_cascade_texel 480 float 5 16
|
|
||||||
U 5161e6ba frag u_sun_dir 560 vec3 1 0
|
|
||||||
U 5161e6ba frag u_sun_color 576 vec3 1 0
|
|
||||||
U 5161e6ba frag u_cam_pos 592 vec3 1 0
|
|
||||||
U 5161e6ba frag u_prefilter_levels 604 float 1 0
|
|
||||||
U 5161e6ba frag u_fog_density 608 float 1 0
|
|
||||||
U 5161e6ba frag u_fog_height_falloff 612 float 1 0
|
|
||||||
U 5161e6ba frag u_fog_base 616 float 1 0
|
|
||||||
U 5161e6ba frag u_clip_y 620 float 1 0
|
|
||||||
U 5161e6ba frag u_spec_scale 624 float 1 0
|
|
||||||
U 5161e6ba frag u_sky_rot 632 vec2 1 0
|
|
||||||
U 5161e6ba frag u_ground_alb 640 vec3 1 0
|
|
||||||
U 5161e6ba frag u_ground_alb_hi 656 vec3 1 0
|
|
||||||
U 5161e6ba frag u_ground_hi_y 668 float 1 0
|
|
||||||
U 5161e6ba frag u_ground_hi_w 672 float 1 0
|
|
||||||
U 5161e6ba frag u_ibl_scale 688 vec3 1 0
|
|
||||||
U 5161e6ba frag u_daylight 700 float 1 0
|
|
||||||
U 5161e6ba frag u_fire_pos 704 vec3 1 0
|
|
||||||
U 5161e6ba frag u_fire_color 720 vec3 1 0
|
|
||||||
U 5161e6ba frag u_hand_pos 736 vec3 1 0
|
|
||||||
U 5161e6ba frag u_hand_color 752 vec3 1 0
|
|
||||||
U 5161e6ba frag u_hand_dir 768 vec3 1 0
|
|
||||||
U 5161e6ba frag u_hand_cone 780 float 1 0
|
|
||||||
U 5161e6ba frag u_hand_reach 784 float 1 0
|
|
||||||
U 5161e6ba frag u_ts_origin 792 vec2 1 0
|
|
||||||
U 5161e6ba frag u_ts_half 800 float 1 0
|
|
||||||
U 5161e6ba frag u_ts_on 804 float 1 0
|
|
||||||
U 5161e6ba frag u_force_cascade 808 int 1 0
|
|
||||||
U 5161e6ba frag u_cloud_shadow 812 float 1 0
|
|
||||||
U 5161e6ba frag u_time 816 float 1 0
|
|
||||||
U 5161e6ba frag u_fog_inscatter 820 float 1 0
|
|
||||||
U 5161e6ba frag u_fog_desat 824 float 1 0
|
|
||||||
U 5161e6ba frag u_inv_proj 832 mat4 1 0
|
|
||||||
U 5161e6ba frag u_texel 896 vec2 1 0
|
|
||||||
U 5161e6ba frag u_focus 904 float 1 0
|
|
||||||
U 5161e6ba frag u_aperture 908 float 1 0
|
|
||||||
U 5161e6ba frag u_max_coc 912 float 1 0
|
|
||||||
T 5161e6ba u_brdf 2
|
|
||||||
T 5161e6ba u_depth 3
|
|
||||||
T 5161e6ba u_irradiance 4
|
|
||||||
T 5161e6ba u_prefilter 5
|
|
||||||
T 5161e6ba u_shadow 6
|
|
||||||
T 5161e6ba u_src 7
|
|
||||||
T 5161e6ba u_tershadow 8
|
|
||||||
T 5161e6ba u_ts_height 9
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
fullscreen.vert|adapt.frag|
|
fullscreen.vert|adapt.frag|
|
||||||
fullscreen.vert|bloom_down.frag|
|
fullscreen.vert|bloom_down.frag|
|
||||||
fullscreen.vert|bloom_up.frag|
|
fullscreen.vert|bloom_up.frag|
|
||||||
|
fullscreen.vert|dof.frag|
|
||||||
fullscreen.vert|heightgen.frag|#define DEM;
|
fullscreen.vert|heightgen.frag|#define DEM;
|
||||||
fullscreen.vert|ibl_brdf.frag|
|
fullscreen.vert|ibl_brdf.frag|
|
||||||
fullscreen.vert|ibl_irradiance.frag|
|
fullscreen.vert|ibl_irradiance.frag|
|
||||||
|
|
@ -17,6 +18,7 @@ fullscreen.vert|ternormal.frag|
|
||||||
fullscreen.vert|tershadow.frag|#define NOISE_ONLY;
|
fullscreen.vert|tershadow.frag|#define NOISE_ONLY;
|
||||||
fullscreen.vert|tonemap.frag|
|
fullscreen.vert|tonemap.frag|
|
||||||
fullscreen.vert|tonemap.frag|#define HDR10;
|
fullscreen.vert|tonemap.frag|#define HDR10;
|
||||||
|
fullscreen.vert|volumetric.frag|
|
||||||
grass.mesh|model.frag|#define FOLIAGE;#define BLADE;#define MESH;
|
grass.mesh|model.frag|#define FOLIAGE;#define BLADE;#define MESH;
|
||||||
grass.vert|model.frag|#define FOLIAGE;#define BLADE;
|
grass.vert|model.frag|#define FOLIAGE;#define BLADE;
|
||||||
grass.vert|model.frag|#define FOLIAGE;#define BLADE;#define TILES;
|
grass.vert|model.frag|#define FOLIAGE;#define BLADE;#define TILES;
|
||||||
|
|
@ -26,12 +28,15 @@ model.vert|bake.frag|
|
||||||
model.vert|bake.frag|#define CARD;
|
model.vert|bake.frag|#define CARD;
|
||||||
model.vert|bake.frag|#define FLOWER;
|
model.vert|bake.frag|#define FLOWER;
|
||||||
model.vert|depth.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;
|
model.vert|depth.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;
|
||||||
|
model.vert|depth.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
|
||||||
model.vert|model.frag|
|
model.vert|model.frag|
|
||||||
model.vert|model.frag|#define CARD;#define CHEAP;
|
model.vert|model.frag|#define CARD;#define CHEAP;
|
||||||
model.vert|model.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;
|
model.vert|model.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;
|
||||||
|
model.vert|model.frag|#define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE;
|
||||||
model.vert|model.frag|#define FOLIAGE;#define WIND;#define BLADE;
|
model.vert|model.frag|#define FOLIAGE;#define WIND;#define BLADE;
|
||||||
model.vert|model.frag|#define FOLIAGE;#define WIND;#define CARD;
|
model.vert|model.frag|#define FOLIAGE;#define WIND;#define CARD;
|
||||||
model.vert|model.frag|#define FOLIAGE;#define WIND;#define EQ_PASS;
|
model.vert|model.frag|#define FOLIAGE;#define WIND;#define EQ_PASS;
|
||||||
|
model.vert|model.frag|#define FOLIAGE;#define WIND;#define EQ_PASS;#define NEAR_FADE;
|
||||||
model.vert|model.frag|#define FOLIAGE;#define WIND;#define FLOWER;
|
model.vert|model.frag|#define FOLIAGE;#define WIND;#define FLOWER;
|
||||||
model.vert|model.frag|#define SHADOW_PASS;#define WIND;#define CARD;
|
model.vert|model.frag|#define SHADOW_PASS;#define WIND;#define CARD;
|
||||||
model.vert|model.frag|#define WIND;
|
model.vert|model.frag|#define WIND;
|
||||||
|
|
@ -54,5 +59,3 @@ terrain.vert|terrain.frag|#define SUN_INLINE;#define FAR_ONLY;
|
||||||
terrain.vert|terrain.frag|#define SUN_INLINE;#define NEAR_ONLY;
|
terrain.vert|terrain.frag|#define SUN_INLINE;#define NEAR_ONLY;
|
||||||
terrain.vert|tersun.frag|
|
terrain.vert|tersun.frag|
|
||||||
water.vert|water.frag|
|
water.vert|water.frag|
|
||||||
fullscreen.vert|volumetric.frag|
|
|
||||||
fullscreen.vert|dof.frag|
|
|
||||||
|
|
|
||||||
|
|
@ -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) }
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ down ends the night). The transport says what this machine became (`ses_set_role
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `ply_init()`, `ply_join(pid) -> slot`, `ply_leave(p)`, `ply_leave_all()` | the roster (-1: full, or pid 0) |
|
| `ply_join(pid) -> slot`, `ply_leave(p)`, `ply_leave_all()` | the roster (-1: full, or pid 0) |
|
||||||
| `ply_report(p, x, y, z, yaw, speed)`, `ply_report_rest(p, rest, sleeping)`, `ply_report_name(p, name)` | what a remote machine said |
|
| `ply_report(p, x, y, z, yaw, speed)`, `ply_report_rest(p, rest, sleeping)`, `ply_report_name(p, name)` | what a remote machine said |
|
||||||
| `ply_on`, `ply_is_local`, `ply_slot_of(pid)`, `ply_count`, `ply_pid`, `ply_name`, `ply_x/y/z/yaw/speed/rest(p)`, `ply_sleeping(p)` | who and where |
|
| `ply_on`, `ply_is_local`, `ply_slot_of(pid)`, `ply_count`, `ply_pid`, `ply_name`, `ply_x/y/z/yaw/speed/rest(p)`, `ply_sleeping(p)` | who and where |
|
||||||
| `ply_near(x, z)`, `ply_near_d2`, `ply_near_i()`, `ply_dist(p, x, z)` | the nearest player |
|
| `ply_near(x, z)`, `ply_near_d2`, `ply_near_i()`, `ply_dist(p, x, z)` | the nearest player |
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
# roster.ludic - who is here. Slot 0 is this machine's player and reads the SessionLocal port
|
# roster.ludic - who is here. Slot 0 is this machine's player and reads the SessionLocal port
|
||||||
# live; slots 1.. are remote players, filled by what their machines report.
|
# live; slots 1.. are remote players, filled by what their machines report.
|
||||||
export function ply_init(session_st: mut SessionState) -> void { session__init(session_st) }
|
|
||||||
|
|
||||||
export function ply_on(session_st: SessionState, p: int) -> bool {
|
export function ply_on(session_st: SessionState, p: int) -> bool {
|
||||||
if p == 0 { return true }
|
if p == 0 { return true }
|
||||||
|
|
@ -26,7 +25,6 @@ export function ply_count(session_st: SessionState) -> int {
|
||||||
# a remote player comes in, standing where this one stands until they say: the first free
|
# a remote player comes in, standing where this one stands until they say: the first free
|
||||||
# slot, or -1 when the party is full
|
# slot, or -1 when the party is full
|
||||||
export function ply_join(session_st: mut SessionState, pid: int) -> int {
|
export function ply_join(session_st: mut SessionState, pid: int) -> int {
|
||||||
session__init(session_st)
|
|
||||||
if pid <= 0 { return -1 } # 0 is this machine's player
|
if pid <= 0 { return -1 } # 0 is this machine's player
|
||||||
for p in 1 .. PLY_MAX {
|
for p in 1 .. PLY_MAX {
|
||||||
if session_st.session__slot[p].on { continue }
|
if session_st.session__slot[p].on { continue }
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ function session__nameless() -> string { return "" }
|
||||||
|
|
||||||
export state SessionState {
|
export state SessionState {
|
||||||
session__me_sleeping: bool = false
|
session__me_sleeping: bool = false
|
||||||
session__slot: []PartyMember = null
|
session__slot: []PartyMember = session__slots()
|
||||||
session__role: int = 0
|
session__role: int = 0
|
||||||
session__max: int = 4
|
session__max: int = 4
|
||||||
session__open: bool = true
|
session__open: bool = true
|
||||||
|
|
@ -92,10 +92,11 @@ function session__fact(session_st: SessionState, what: int, slot: int, pid: int)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
function session__init(session_st: mut SessionState) -> void {
|
|
||||||
if session_st.session__slot != null { return }
|
|
||||||
session_st.session__slot = new []PartyMember
|
|
||||||
for i in 0 .. PLY_MAX { push(session_st.session__slot, new PartyMember) }
|
|
||||||
session_st.session__slot[0].on = true
|
|
||||||
}
|
|
||||||
function session__facts__new() -> Queue<SessionFact> { return queue_new("session.facts") }
|
function session__facts__new() -> Queue<SessionFact> { return queue_new("session.facts") }
|
||||||
|
# the roster's slots, slot 0 this machine's own and on
|
||||||
|
function session__slots() -> []PartyMember {
|
||||||
|
let out = new []PartyMember
|
||||||
|
for i in 0 .. PLY_MAX { push(out, new PartyMember) }
|
||||||
|
out[0].on = true
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,12 @@ program SessionTest {
|
||||||
|
|
||||||
# a host with n guests, pids 2.., facts drained
|
# a host with n guests, pids 2.., facts drained
|
||||||
function party(session_st: mut SessionState, n: int) -> void {
|
function party(session_st: mut SessionState, n: int) -> void {
|
||||||
ply_init(session_st)
|
|
||||||
ses_set_role(session_st, SES_HOST)
|
ses_set_role(session_st, SES_HOST)
|
||||||
for i in 0 .. n { ply_join(session_st, 2 + i) }
|
for i in 0 .. n { ply_join(session_st, 2 + i) }
|
||||||
q_clear(ses_facts(session_st))
|
q_clear(ses_facts(session_st))
|
||||||
}
|
}
|
||||||
|
|
||||||
test "slot 0 is the local player, read live; others join at its feet and leave" (session_st: mut SessionState, session_test_st: mut SessionTestState) {
|
test "slot 0 is the local player, read live; others join at its feet and leave" (session_st: mut SessionState, session_test_st: mut SessionTestState) {
|
||||||
ply_init(session_st)
|
|
||||||
session_test_st.me_x = 5.0
|
session_test_st.me_x = 5.0
|
||||||
expect(ply_on(session_st, 0))
|
expect(ply_on(session_st, 0))
|
||||||
expect_near(ply_x(session_st, 0), 5.0, 0.001)
|
expect_near(ply_x(session_st, 0), 5.0, 0.001)
|
||||||
|
|
@ -58,14 +56,12 @@ program SessionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "the roster fills at twelve" (session_st: mut SessionState) {
|
test "the roster fills at twelve" (session_st: mut SessionState) {
|
||||||
ply_init(session_st)
|
|
||||||
for i in 0 .. PLY_MAX - 1 { expect(ply_join(session_st, 10 + i) > 0) }
|
for i in 0 .. PLY_MAX - 1 { expect(ply_join(session_st, 10 + i) > 0) }
|
||||||
expect_eq(ply_join(session_st, 99), -1)
|
expect_eq(ply_join(session_st, 99), -1)
|
||||||
expect_eq(ply_count(session_st), PLY_MAX)
|
expect_eq(ply_count(session_st), PLY_MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
test "the nearest player, whoever it is" (session_st: mut SessionState, session_test_st: mut SessionTestState) {
|
test "the nearest player, whoever it is" (session_st: mut SessionState, session_test_st: mut SessionTestState) {
|
||||||
ply_init(session_st)
|
|
||||||
session_test_st.me_x = 100.0
|
session_test_st.me_x = 100.0
|
||||||
let p = ply_join(session_st, 2)
|
let p = ply_join(session_st, 2)
|
||||||
ply_report(session_st, p, 3.0, 0.0, 4.0, 0.0, 1.0)
|
ply_report(session_st, p, 3.0, 0.0, 4.0, 0.0, 1.0)
|
||||||
|
|
@ -75,7 +71,6 @@ program SessionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "roles: alone owns the world, a guest does not, and a change is a fact" (session_st: mut SessionState) {
|
test "roles: alone owns the world, a guest does not, and a change is a fact" (session_st: mut SessionState) {
|
||||||
ply_init(session_st)
|
|
||||||
expect(ses_owns_world(session_st))
|
expect(ses_owns_world(session_st))
|
||||||
expect(not ses_live(session_st))
|
expect(not ses_live(session_st))
|
||||||
expect(ses_night_passes(session_st))
|
expect(ses_night_passes(session_st))
|
||||||
|
|
@ -115,7 +110,6 @@ program SessionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "a guest's turning in is a fact for the host to hear; alone a bed just sleeps" (session_st: mut SessionState) {
|
test "a guest's turning in is a fact for the host to hear; alone a bed just sleeps" (session_st: mut SessionState) {
|
||||||
ply_init(session_st)
|
|
||||||
expect(not ses_sleep(session_st, true))
|
expect(not ses_sleep(session_st, true))
|
||||||
ses_set_role(session_st, SES_GUEST)
|
ses_set_role(session_st, SES_GUEST)
|
||||||
q_clear(ses_facts(session_st))
|
q_clear(ses_facts(session_st))
|
||||||
|
|
@ -153,7 +147,6 @@ program SessionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "alone it may go; a guest may not, and its vote goes to the host" (session_st: mut SessionState) {
|
test "alone it may go; a guest may not, and its vote goes to the host" (session_st: mut SessionState) {
|
||||||
ply_init(session_st)
|
|
||||||
expect_eq(ses_may(session_st, 1, 0, "x"), SES_MAY_GO)
|
expect_eq(ses_may(session_st, 1, 0, "x"), SES_MAY_GO)
|
||||||
ses_set_role(session_st, SES_GUEST)
|
ses_set_role(session_st, SES_GUEST)
|
||||||
expect_eq(ses_may(session_st, 1, 0, "x"), SES_MAY_HOSTS)
|
expect_eq(ses_may(session_st, 1, 0, "x"), SES_MAY_HOSTS)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
# ludic.settings/file.ludic - the store as a JSON object, by key: a switch is 0 or 1, a whole number
|
# ludic.settings/file.ludic - the store as a JSON object, by key: a switch is 0 or 1, a whole number
|
||||||
# is itself, a float is thousandths (sv_put_float), text is a string. A key the file lacks keeps
|
# is itself, a float is thousandths (sv_put_float), text is a string. A key the file lacks keeps
|
||||||
# what the store holds, and a key the store does not know is ignored.
|
# what the store holds, and a key the store does not know is ignored.
|
||||||
export function settings_to_json(settings_st: mut SettingsState) -> Val {
|
export function settings_to_json(settings_st: SettingsState) -> Val {
|
||||||
sg_init(settings_st)
|
|
||||||
let v = Value.object()
|
let v = Value.object()
|
||||||
for i in 0 .. len(settings_st.sg_defs) { settings_put(settings_st, v, i) }
|
for i in 0 .. len(settings_st.sg_defs) { settings_put(settings_st, v, i) }
|
||||||
return v
|
return v
|
||||||
|
|
@ -23,7 +22,6 @@ export function settings_put(settings_st: SettingsState, v: Val, id: int) -> voi
|
||||||
|
|
||||||
# every key `v` names, held to its range; quiet, since a load is applied whole by its caller
|
# every key `v` names, held to its range; quiet, since a load is applied whole by its caller
|
||||||
export function settings_from_json(settings_st: mut SettingsState, v: Val) -> void {
|
export function settings_from_json(settings_st: mut SettingsState, v: Val) -> void {
|
||||||
sg_init(settings_st)
|
|
||||||
if v == null { return }
|
if v == null { return }
|
||||||
for i in 0 .. len(settings_st.sg_defs) { sg_read(settings_st, v, i) }
|
for i in 0 .. len(settings_st.sg_defs) { sg_read(settings_st, v, i) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# ludic.settings/rules.ludic - the whole store at once: every setting to its default, every one
|
# ludic.settings/rules.ludic - the whole store at once: every setting to its default, every one
|
||||||
# held to its range, and the rescue set. None of these is news: whoever calls them applies it all
|
# held to its range, and the rescue set. None of these is news: whoever calls them applies it all
|
||||||
export function settings_reset_all(settings_st: mut SettingsState) -> void {
|
export function settings_reset_all(settings_st: mut SettingsState) -> void {
|
||||||
sg_init(settings_st)
|
|
||||||
for i in 0 .. len(settings_st.sg_defs) {
|
for i in 0 .. len(settings_st.sg_defs) {
|
||||||
settings_st.sg_num[i] = settings_st.sg_defs[i].num
|
settings_st.sg_num[i] = settings_st.sg_defs[i].num
|
||||||
settings_st.sg_text[i] = settings_st.sg_defs[i].text
|
settings_st.sg_text[i] = settings_st.sg_defs[i].text
|
||||||
|
|
@ -11,14 +10,12 @@ export function settings_reset_all(settings_st: mut SettingsState) -> void {
|
||||||
# a hand-edited file, or one from a build with more choices, never hands the game a value it has
|
# a hand-edited file, or one from a build with more choices, never hands the game a value it has
|
||||||
# no case for
|
# no case for
|
||||||
export function settings_clamp_all(settings_st: mut SettingsState) -> void {
|
export function settings_clamp_all(settings_st: mut SettingsState) -> void {
|
||||||
sg_init(settings_st)
|
|
||||||
for i in 0 .. len(settings_st.sg_defs) { settings_st.sg_num[i] = settings_held(settings_st, i, settings_st.sg_num[i]) }
|
for i in 0 .. len(settings_st.sg_defs) { settings_st.sg_num[i] = settings_held(settings_st, i, settings_st.sg_num[i]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
# the values every machine can run: each setting marked `rescue` back to its default, and the
|
# the values every machine can run: each setting marked `rescue` back to its default, and the
|
||||||
# rest (keys, language, volume) left the player's
|
# rest (keys, language, volume) left the player's
|
||||||
export function settings_rescue(settings_st: mut SettingsState) -> void {
|
export function settings_rescue(settings_st: mut SettingsState) -> void {
|
||||||
sg_init(settings_st)
|
|
||||||
for i in 0 .. len(settings_st.sg_defs) {
|
for i in 0 .. len(settings_st.sg_defs) {
|
||||||
if settings_st.sg_defs[i].rescue {
|
if settings_st.sg_defs[i].rescue {
|
||||||
settings_st.sg_num[i] = settings_st.sg_defs[i].num
|
settings_st.sg_num[i] = settings_st.sg_defs[i].num
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,12 @@
|
||||||
# ludic.settings/store.ludic - the values, by id (a setting's place in the order it was added),
|
# ludic.settings/store.ludic - the values, by id (a setting's place in the order it was added),
|
||||||
# and the verbs: nothing else holds a setting, and every set that changes one is a fact
|
# and the verbs: nothing else holds a setting, and every set that changes one is a fact
|
||||||
export state SettingsState {
|
export state SettingsState {
|
||||||
sg_defs: []SettingDef = null
|
sg_defs: []SettingDef = new []SettingDef
|
||||||
sg_num: []float = null
|
sg_num: []float = new []float
|
||||||
sg_text: []string = null
|
sg_text: []string = new []string
|
||||||
sg_changes: Queue<SettingChanged> = sg_changes__new()
|
sg_changes: Queue<SettingChanged> = sg_changes__new()
|
||||||
}
|
}
|
||||||
|
|
||||||
function sg_init(settings_st: mut SettingsState) -> void {
|
|
||||||
if settings_st.sg_defs != null { return }
|
|
||||||
settings_st.sg_defs = new []SettingDef
|
|
||||||
settings_st.sg_num = new []float
|
|
||||||
settings_st.sg_text = new []string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function settings_changes(settings_st: SettingsState) -> Queue<SettingChanged> {
|
export function settings_changes(settings_st: SettingsState) -> Queue<SettingChanged> {
|
||||||
return settings_st.sg_changes
|
return settings_st.sg_changes
|
||||||
|
|
@ -28,23 +22,20 @@ export function settings_clear(settings_st: mut SettingsState) -> void {
|
||||||
|
|
||||||
# a setting, at its default; its id is its place in the order added
|
# a setting, at its default; its id is its place in the order added
|
||||||
export function settings_add(settings_st: mut SettingsState, d: SettingDef) -> int {
|
export function settings_add(settings_st: mut SettingsState, d: SettingDef) -> int {
|
||||||
sg_init(settings_st)
|
|
||||||
push(settings_st.sg_defs, d)
|
push(settings_st.sg_defs, d)
|
||||||
push(settings_st.sg_num, d.num)
|
push(settings_st.sg_num, d.num)
|
||||||
push(settings_st.sg_text, d.text)
|
push(settings_st.sg_text, d.text)
|
||||||
return len(settings_st.sg_defs) - 1
|
return len(settings_st.sg_defs) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export function settings_count(settings_st: mut SettingsState) -> int {
|
export function settings_count(settings_st: SettingsState) -> int {
|
||||||
sg_init(settings_st)
|
|
||||||
return len(settings_st.sg_defs)
|
return len(settings_st.sg_defs)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function settings_def(settings_st: SettingsState, id: int) -> SettingDef { return settings_st.sg_defs[id] }
|
export function settings_def(settings_st: SettingsState, id: int) -> SettingDef { return settings_st.sg_defs[id] }
|
||||||
|
|
||||||
# the id of `key`, or -1
|
# the id of `key`, or -1
|
||||||
export function settings_id_of(settings_st: mut SettingsState, key: string) -> int {
|
export function settings_id_of(settings_st: SettingsState, key: string) -> int {
|
||||||
sg_init(settings_st)
|
|
||||||
for i in 0 .. len(settings_st.sg_defs) { if settings_st.sg_defs[i].key == key { return i } }
|
for i in 0 .. len(settings_st.sg_defs) { if settings_st.sg_defs[i].key == key { return i } }
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -1607,6 +1607,14 @@ go:
|
||||||
%scale = select i1 %ison, i32 %bsv, i32 1
|
%scale = select i1 %ison, i32 %bsv, i32 1
|
||||||
store i32 %scale, ptr @W_glscale
|
store i32 %scale, ptr @W_glscale
|
||||||
call void @win_gl_update()
|
call void @win_gl_update()
|
||||||
|
; a Metal layer (Vulkan) draws at the same scale
|
||||||
|
%ml = load ptr, ptr @W_mtl
|
||||||
|
%noml = icmp eq ptr %ml, null
|
||||||
|
br i1 %noml, label %out, label %mtl
|
||||||
|
mtl:
|
||||||
|
%scd = sitofp i32 %scale to double
|
||||||
|
%sel_csc = call ptr @sel_registerName(ptr @.s_setcsc)
|
||||||
|
%rm = call ptr (ptr, ptr, double) @objc_msgSend(ptr %ml, ptr %sel_csc, double %scd)
|
||||||
br label %out
|
br label %out
|
||||||
out:
|
out:
|
||||||
ret void
|
ret void
|
||||||
|
|
@ -2035,3 +2043,81 @@ define ptr @win_native_instance() {
|
||||||
entry:
|
entry:
|
||||||
ret ptr null
|
ret ptr null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; ---- a Metal layer on the view, for Vulkan through MoltenVK -----------------------------
|
||||||
|
; VK_EXT_metal_surface presents to a CAMetalLayer, so the view is made layer-hosting with one
|
||||||
|
; (the layer set before wantsLayer, which is what makes the view host it rather than back it).
|
||||||
|
; Its contentsScale is the window's backing scale, or the swapchain's pixels are stretched over
|
||||||
|
; a Retina window's points; @W_glscale follows it, as win_gl_attach sets it for OpenGL. The class
|
||||||
|
; is looked up by name: the program links QuartzCore when it uses Vk.* (null without it).
|
||||||
|
; win_metal_layer() -> CAMetalLayer* | null (no window, or no QuartzCore)
|
||||||
|
@.c_mtllayer = private unnamed_addr constant [13 x i8] c"CAMetalLayer\00"
|
||||||
|
@.s_layer = private unnamed_addr constant [6 x i8] c"layer\00"
|
||||||
|
@.s_retain = private unnamed_addr constant [7 x i8] c"retain\00"
|
||||||
|
@.s_setlayer = private unnamed_addr constant [10 x i8] c"setLayer:\00"
|
||||||
|
@.s_wantsl = private unnamed_addr constant [15 x i8] c"setWantsLayer:\00"
|
||||||
|
@.s_setcsc = private unnamed_addr constant [18 x i8] c"setContentsScale:\00"
|
||||||
|
@W_mtl = internal global ptr null
|
||||||
|
|
||||||
|
define ptr @win_metal_layer() {
|
||||||
|
entry:
|
||||||
|
%have = load ptr, ptr @W_mtl
|
||||||
|
%got = icmp ne ptr %have, null
|
||||||
|
br i1 %got, label %done, label %make
|
||||||
|
make:
|
||||||
|
%view = load ptr, ptr @W_view
|
||||||
|
%win = load ptr, ptr @W_win
|
||||||
|
%noview = icmp eq ptr %view, null
|
||||||
|
%nowin = icmp eq ptr %win, null
|
||||||
|
%nothing = or i1 %noview, %nowin
|
||||||
|
br i1 %nothing, label %none, label %cls
|
||||||
|
cls:
|
||||||
|
%c = call ptr @objc_getClass(ptr @.c_mtllayer)
|
||||||
|
%noc = icmp eq ptr %c, null
|
||||||
|
br i1 %noc, label %none, label %go
|
||||||
|
go:
|
||||||
|
%sel_l = call ptr @sel_registerName(ptr @.s_layer)
|
||||||
|
%l0 = call ptr @objc_msgSend(ptr %c, ptr %sel_l)
|
||||||
|
%sel_r = call ptr @sel_registerName(ptr @.s_retain)
|
||||||
|
%l = call ptr @objc_msgSend(ptr %l0, ptr %sel_r)
|
||||||
|
%sel_bs = call ptr @sel_registerName(ptr @.s_bscale)
|
||||||
|
%bs = call double (ptr, ptr) @objc_msgSend(ptr %win, ptr %sel_bs)
|
||||||
|
%bsok = fcmp ogt double %bs, 0.0
|
||||||
|
%scale = select i1 %bsok, double %bs, double 1.0
|
||||||
|
%sel_cs = call ptr @sel_registerName(ptr @.s_setcsc)
|
||||||
|
%r0 = call ptr (ptr, ptr, double) @objc_msgSend(ptr %l, ptr %sel_cs, double %scale)
|
||||||
|
%sel_sl = call ptr @sel_registerName(ptr @.s_setlayer)
|
||||||
|
%r1 = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %view, ptr %sel_sl, ptr %l)
|
||||||
|
%sel_wl = call ptr @sel_registerName(ptr @.s_wantsl)
|
||||||
|
%r2 = call ptr (ptr, ptr, i8) @objc_msgSend(ptr %view, ptr %sel_wl, i8 1)
|
||||||
|
%si = fptosi double %scale to i32
|
||||||
|
store i32 %si, ptr @W_glscale
|
||||||
|
store ptr %l, ptr @W_mtl
|
||||||
|
ret ptr %l
|
||||||
|
done:
|
||||||
|
ret ptr %have
|
||||||
|
none:
|
||||||
|
ret ptr null
|
||||||
|
}
|
||||||
|
|
||||||
|
; ---- is the window on screen ----------------------------------------------------------------------
|
||||||
|
; A covered or minimised window's CAMetalLayer hands out drawables at the compositor's pace (a
|
||||||
|
; second's timeout each), so a Vulkan frame waiting for one stalled the whole game at 3 fps behind
|
||||||
|
; another window. NSWindowOcclusionStateVisible is bit 1 of -occlusionState.
|
||||||
|
; win_visible() -> 1 | 0 (1 with no window)
|
||||||
|
@.s_occl = private unnamed_addr constant [15 x i8] c"occlusionState\00"
|
||||||
|
define i32 @win_visible() {
|
||||||
|
entry:
|
||||||
|
%win = load ptr, ptr @W_win
|
||||||
|
%nowin = icmp eq ptr %win, null
|
||||||
|
br i1 %nowin, label %yes, label %ask
|
||||||
|
ask:
|
||||||
|
%sel = call ptr @sel_registerName(ptr @.s_occl)
|
||||||
|
%st = call i64 (ptr, ptr) @objc_msgSend(ptr %win, ptr %sel)
|
||||||
|
%bit = and i64 %st, 2
|
||||||
|
%vis = icmp ne i64 %bit, 0
|
||||||
|
%r = zext i1 %vis to i32
|
||||||
|
ret i32 %r
|
||||||
|
yes:
|
||||||
|
ret i32 1
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -398,6 +398,15 @@ define weak ptr @win_native_instance() {
|
||||||
entry:
|
entry:
|
||||||
ret ptr null
|
ret ptr null
|
||||||
}
|
}
|
||||||
|
define weak ptr @win_metal_layer() {
|
||||||
|
entry:
|
||||||
|
ret ptr null
|
||||||
|
}
|
||||||
|
; the window is always drawn to here (cocoa.ll asks macOS whether it is covered)
|
||||||
|
define weak i32 @win_visible() {
|
||||||
|
entry:
|
||||||
|
ret i32 1
|
||||||
|
}
|
||||||
; the drawable's size, for a graphics API that follows the window without a GL context (render3d's
|
; the drawable's size, for a graphics API that follows the window without a GL context (render3d's
|
||||||
; Vulkan resize check); headless there is no window, so 0 x 0. cocoa.ll's real one wins when linked.
|
; Vulkan resize check); headless there is no window, so 0 x 0. cocoa.ll's real one wins when linked.
|
||||||
; win_set_title: cocoa.ll's in a windowed build; nothing to retitle headless
|
; win_set_title: cocoa.ll's in a windowed build; nothing to retitle headless
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,16 @@ define ptr @win_native_instance() {
|
||||||
entry:
|
entry:
|
||||||
ret ptr null
|
ret ptr null
|
||||||
}
|
}
|
||||||
|
; macOS only (cocoa.ll): the CAMetalLayer MoltenVK presents to
|
||||||
|
define ptr @win_metal_layer() {
|
||||||
|
entry:
|
||||||
|
ret ptr null
|
||||||
|
}
|
||||||
|
; the window is always drawn to here (cocoa.ll asks macOS whether it is covered)
|
||||||
|
define i32 @win_visible() {
|
||||||
|
entry:
|
||||||
|
ret i32 1
|
||||||
|
}
|
||||||
|
|
||||||
define void @win_set_title(ptr %title) {
|
define void @win_set_title(ptr %title) {
|
||||||
entry:
|
entry:
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
; macOS has no Vulkan of its own: the LunarG loader and MoltenVK (Vulkan over
|
; macOS has no Vulkan of its own: the LunarG loader and MoltenVK (Vulkan over
|
||||||
; Metal) come with the Vulkan SDK or with a game that bundles them. So the loader
|
; Metal) come with the Vulkan SDK or with a game that bundles them. So the loader
|
||||||
; is looked for at run time - beside the executable, in the usual library
|
; is looked for at run time - beside the executable, in the usual library
|
||||||
; directories, then under $VULKAN_SDK - and a Mac without it gets 0 from vk_open
|
; directories, then MoltenVK on its own (a bundle's Contents/Frameworks), then under
|
||||||
; and stays on OpenGL. The contract is vk_win.ll's:
|
; $VULKAN_SDK - and a Mac without any gets 0 from vk_open and stays on OpenGL. The contract is vk_win.ll's:
|
||||||
;
|
;
|
||||||
; lvk_open() -> 1 | 0 lvk_sym(name) -> ptr lvk_has(name) -> 1 | 0
|
; lvk_open() -> 1 | 0 lvk_sym(name) -> ptr lvk_has(name) -> 1 | 0
|
||||||
;
|
;
|
||||||
|
|
@ -25,6 +25,10 @@ declare i32 @lvk_bind()
|
||||||
@.lvk_c3 = private unnamed_addr constant [36 x i8] c"/opt/homebrew/lib/libvulkan.1.dylib\00"
|
@.lvk_c3 = private unnamed_addr constant [36 x i8] c"/opt/homebrew/lib/libvulkan.1.dylib\00"
|
||||||
@.lvk_env = private unnamed_addr constant [11 x i8] c"VULKAN_SDK\00"
|
@.lvk_env = private unnamed_addr constant [11 x i8] c"VULKAN_SDK\00"
|
||||||
@.lvk_fmt = private unnamed_addr constant [25 x i8] c"%s/lib/libvulkan.1.dylib\00"
|
@.lvk_fmt = private unnamed_addr constant [25 x i8] c"%s/lib/libvulkan.1.dylib\00"
|
||||||
|
@.lvk_fmtm = private unnamed_addr constant [25 x i8] c"%s/lib/libMoltenVK.dylib\00"
|
||||||
|
@.lvk_m0 = private unnamed_addr constant [49 x i8] c"@executable_path/../Frameworks/libMoltenVK.dylib\00"
|
||||||
|
@.lvk_m1 = private unnamed_addr constant [35 x i8] c"@executable_path/libMoltenVK.dylib\00"
|
||||||
|
@.lvk_m2 = private unnamed_addr constant [18 x i8] c"libMoltenVK.dylib\00"
|
||||||
@lvk_lib = internal global ptr null
|
@lvk_lib = internal global ptr null
|
||||||
|
|
||||||
; RTLD_NOW (2) | RTLD_LOCAL (4)
|
; RTLD_NOW (2) | RTLD_LOCAL (4)
|
||||||
|
|
@ -34,52 +38,51 @@ entry:
|
||||||
ret ptr %h
|
ret ptr %h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; the candidates in order: a Vulkan loader first (the SDK's, so the validation layer can be
|
||||||
|
; stacked in development), then MoltenVK itself, which exports every vk* entry point and is what
|
||||||
|
; a bundle ships in Contents/Frameworks - no loader, no ICD json.
|
||||||
|
@lvk_paths = internal constant [7 x ptr] [ptr @.lvk_c0, ptr @.lvk_c1, ptr @.lvk_c2, ptr @.lvk_c3, ptr @.lvk_m0, ptr @.lvk_m1, ptr @.lvk_m2]
|
||||||
|
|
||||||
define i32 @lvk_open() {
|
define i32 @lvk_open() {
|
||||||
entry:
|
entry:
|
||||||
%have = load ptr, ptr @lvk_lib
|
%have = load ptr, ptr @lvk_lib
|
||||||
%open = icmp ne ptr %have, null
|
%open = icmp ne ptr %have, null
|
||||||
br i1 %open, label %ok, label %t0
|
br i1 %open, label %ok, label %loop
|
||||||
t0:
|
loop:
|
||||||
%h0 = call ptr @lvk_try(ptr @.lvk_c0)
|
%i = phi i32 [ 0, %entry ], [ %i1, %next ]
|
||||||
%n0 = icmp eq ptr %h0, null
|
%slot = getelementptr [7 x ptr], ptr @lvk_paths, i32 0, i32 %i
|
||||||
br i1 %n0, label %t1, label %got0
|
%path = load ptr, ptr %slot
|
||||||
got0:
|
%h = call ptr @lvk_try(ptr %path)
|
||||||
store ptr %h0, ptr @lvk_lib
|
%miss = icmp eq ptr %h, null
|
||||||
|
br i1 %miss, label %next, label %got
|
||||||
|
next:
|
||||||
|
%i1 = add i32 %i, 1
|
||||||
|
%more = icmp slt i32 %i1, 7
|
||||||
|
br i1 %more, label %loop, label %sdk
|
||||||
|
got:
|
||||||
|
store ptr %h, ptr @lvk_lib
|
||||||
br label %bind
|
br label %bind
|
||||||
t1:
|
sdk:
|
||||||
%h1 = call ptr @lvk_try(ptr @.lvk_c1)
|
%sdkp = call ptr @getenv(ptr @.lvk_env)
|
||||||
%n1 = icmp eq ptr %h1, null
|
%nosdk = icmp eq ptr %sdkp, null
|
||||||
br i1 %n1, label %t2, label %got1
|
br i1 %nosdk, label %fail, label %s1
|
||||||
got1:
|
s1:
|
||||||
store ptr %h1, ptr @lvk_lib
|
|
||||||
br label %bind
|
|
||||||
t2:
|
|
||||||
%h2 = call ptr @lvk_try(ptr @.lvk_c2)
|
|
||||||
%n2 = icmp eq ptr %h2, null
|
|
||||||
br i1 %n2, label %t3, label %got2
|
|
||||||
got2:
|
|
||||||
store ptr %h2, ptr @lvk_lib
|
|
||||||
br label %bind
|
|
||||||
t3:
|
|
||||||
%h3 = call ptr @lvk_try(ptr @.lvk_c3)
|
|
||||||
%n3 = icmp eq ptr %h3, null
|
|
||||||
br i1 %n3, label %t4, label %got3
|
|
||||||
got3:
|
|
||||||
store ptr %h3, ptr @lvk_lib
|
|
||||||
br label %bind
|
|
||||||
t4:
|
|
||||||
%sdk = call ptr @getenv(ptr @.lvk_env)
|
|
||||||
%nosdk = icmp eq ptr %sdk, null
|
|
||||||
br i1 %nosdk, label %fail, label %t4b
|
|
||||||
t4b:
|
|
||||||
%buf = alloca [1024 x i8]
|
%buf = alloca [1024 x i8]
|
||||||
%w = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %buf, i64 1024, ptr @.lvk_fmt, ptr %sdk)
|
%w = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %buf, i64 1024, ptr @.lvk_fmt, ptr %sdkp)
|
||||||
%h4 = call ptr @lvk_try(ptr %buf)
|
%h4 = call ptr @lvk_try(ptr %buf)
|
||||||
%n4 = icmp eq ptr %h4, null
|
%n4 = icmp eq ptr %h4, null
|
||||||
br i1 %n4, label %fail, label %got4
|
br i1 %n4, label %s2, label %got4
|
||||||
|
s2:
|
||||||
|
%w2 = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %buf, i64 1024, ptr @.lvk_fmtm, ptr %sdkp)
|
||||||
|
%h5 = call ptr @lvk_try(ptr %buf)
|
||||||
|
%n5 = icmp eq ptr %h5, null
|
||||||
|
br i1 %n5, label %fail, label %got5
|
||||||
got4:
|
got4:
|
||||||
store ptr %h4, ptr @lvk_lib
|
store ptr %h4, ptr @lvk_lib
|
||||||
br label %bind
|
br label %bind
|
||||||
|
got5:
|
||||||
|
store ptr %h5, ptr @lvk_lib
|
||||||
|
br label %bind
|
||||||
bind:
|
bind:
|
||||||
%n = call i32 @lvk_bind()
|
%n = call i32 @lvk_bind()
|
||||||
br label %ok
|
br label %ok
|
||||||
|
|
|
||||||
|
|
@ -547,6 +547,16 @@ entry:
|
||||||
%inst = call ptr @GetModuleHandleA(ptr null)
|
%inst = call ptr @GetModuleHandleA(ptr null)
|
||||||
ret ptr %inst
|
ret ptr %inst
|
||||||
}
|
}
|
||||||
|
; macOS only (cocoa.ll): the CAMetalLayer MoltenVK presents to
|
||||||
|
define ptr @win_metal_layer() {
|
||||||
|
entry:
|
||||||
|
ret ptr null
|
||||||
|
}
|
||||||
|
; the window is always drawn to here (cocoa.ll asks macOS whether it is covered)
|
||||||
|
define i32 @win_visible() {
|
||||||
|
entry:
|
||||||
|
ret i32 1
|
||||||
|
}
|
||||||
|
|
||||||
; set (%on != 0) or clear a key's bit in the 256-bit held set
|
; set (%on != 0) or clear a key's bit in the 256-bit held set
|
||||||
define void @win_held_bit(i32 %k, i32 %on) {
|
define void @win_held_bit(i32 %k, i32 %on) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,12 @@ function is_intrinsic(name: pointer) -> bool {
|
||||||
function emit_bind(rest: pointer) -> pointer { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
|
function emit_bind(rest: pointer) -> pointer { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
|
||||||
|
|
||||||
function arg_code(e: Node, i: int) -> pointer { let v = emit_expr(e.kids[i]); return v.code }
|
function arg_code(e: Node, i: int) -> pointer { let v = emit_expr(e.kids[i]); return v.code }
|
||||||
|
# a buffer handed to the platform layer: a slice's elements, never its header (as an extern's are)
|
||||||
|
function arg_buf(e: Node, i: int) -> pointer {
|
||||||
|
let v = emit_expr(e.kids[i])
|
||||||
|
if is_slice_ty(v.ty) { return emit_bind(`load ptr, ptr {slice_field(v.code, 0)}`) }
|
||||||
|
return v.code
|
||||||
|
}
|
||||||
|
|
||||||
function emit_intrinsic(name: pointer, e: Node) -> Val {
|
function emit_intrinsic(name: pointer, e: Node) -> Val {
|
||||||
g_intrin_ok = true
|
g_intrin_ok = true
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ function emit_intrinsic2(name: pointer, e: Node) -> Val {
|
||||||
return val("0", "void")
|
return val("0", "void")
|
||||||
}
|
}
|
||||||
if (name == "win_present") {
|
if (name == "win_present") {
|
||||||
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
|
let a = arg_buf(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
|
||||||
emit(" call void @win_present(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(")\n")
|
emit(" call void @win_present(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(")\n")
|
||||||
return val("0", "void")
|
return val("0", "void")
|
||||||
}
|
}
|
||||||
|
|
@ -78,19 +78,19 @@ function emit_intrinsic2(name: pointer, e: Node) -> Val {
|
||||||
# #50 — fill a caller buffer with the platform device state (windowed only,
|
# #50 — fill a caller buffer with the platform device state (windowed only,
|
||||||
# DCE'd headless). win_held: an 8-word held-key bitset; win_mouse: [x, y,
|
# DCE'd headless). win_held: an 8-word held-key bitset; win_mouse: [x, y,
|
||||||
# button-mask, wheel-delta].
|
# button-mask, wheel-delta].
|
||||||
if (name == "win_held") { let a = arg_code(e, 0); emit(" call void @win_held(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
if (name == "win_held") { let a = arg_buf(e, 0); emit(" call void @win_held(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
||||||
if (name == "win_mouse") { let a = arg_code(e, 0); emit(" call void @win_mouse(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
if (name == "win_mouse") { let a = arg_buf(e, 0); emit(" call void @win_mouse(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
||||||
# what the active keyboard layout types on a held-set key position (windowed only,
|
# what the active keyboard layout types on a held-set key position (windowed only,
|
||||||
# DCE'd headless): a code point, 0 for none.
|
# DCE'd headless): a code point, 0 for none.
|
||||||
if (name == "win_key_char") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @win_key_char(i32 {a})`), "int") }
|
if (name == "win_key_char") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @win_key_char(i32 {a})`), "int") }
|
||||||
# what the player TYPED since the last call - UTF-16 code units into the caller's buffer, one per
|
# what the player TYPED since the last call - UTF-16 code units into the caller's buffer, one per
|
||||||
# word, returning how many. The layout, the modifiers and any dead key are already applied, which
|
# word, returning how many. The layout, the modifiers and any dead key are already applied, which
|
||||||
# is why a name field must read this rather than build text out of key codes.
|
# is why a name field must read this rather than build text out of key codes.
|
||||||
if (name == "win_text") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call i32 @win_text(ptr {a}, i32 {b})`), "int") }
|
if (name == "win_text") { let a = arg_buf(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call i32 @win_text(ptr {a}, i32 {b})`), "int") }
|
||||||
# #51 — win_pad: 6 words/pad [connected, mask, lx, ly, rx, ry]; win_touch: 3
|
# #51 — win_pad: 6 words/pad [connected, mask, lx, ly, rx, ry]; win_touch: 3
|
||||||
# words/point [active, x, y]. Both windowed-only, DCE'd headless.
|
# words/point [active, x, y]. Both windowed-only, DCE'd headless.
|
||||||
if (name == "win_pad") { let a = arg_code(e, 0); emit(" call void @win_pad(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
if (name == "win_pad") { let a = arg_buf(e, 0); emit(" call void @win_pad(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
||||||
if (name == "win_touch") { let a = arg_code(e, 0); emit(" call void @win_touch(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
if (name == "win_touch") { let a = arg_buf(e, 0); emit(" call void @win_touch(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
||||||
# #89 — set the OS cursor capture mode (windowed only; DCE'd headless).
|
# #89 — set the OS cursor capture mode (windowed only; DCE'd headless).
|
||||||
if (name == "win_cursor_mode") { let a = arg_code(e, 0); emit(" call void @win_cursor_mode(i32 "); emit(a); emit(")\n"); return val("0", "void") }
|
if (name == "win_cursor_mode") { let a = arg_code(e, 0); emit(" call void @win_cursor_mode(i32 "); emit(a); emit(")\n"); return val("0", "void") }
|
||||||
# #22 audio backend — AVAudioPlayer via audio.ll; windowed-only, DCE'd headless.
|
# #22 audio backend — AVAudioPlayer via audio.ll; windowed-only, DCE'd headless.
|
||||||
|
|
|
||||||
40436
selfhost/ludicc.seed.ll
40436
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -360,6 +360,8 @@ entry {
|
||||||
var vkl = join_path(home, "runtime/native/vk_mac.ll")
|
var vkl = join_path(home, "runtime/native/vk_mac.ll")
|
||||||
if g_target_win { vkl = join_path(home, "runtime/native/vk_win.ll") }
|
if g_target_win { vkl = join_path(home, "runtime/native/vk_win.ll") }
|
||||||
cmd = `{cmd} {vkt} {vkl}`
|
cmd = `{cmd} {vkt} {vkl}`
|
||||||
|
# the CAMetalLayer a macOS window presents through, found by name in cocoa.ll
|
||||||
|
if not g_target_win { cmd = `{cmd} -Wl,-needed_framework,QuartzCore` }
|
||||||
}
|
}
|
||||||
# A shipped Windows game is a GUI-subsystem program: started from Explorer, a
|
# A shipped Windows game is a GUI-subsystem program: started from Explorer, a
|
||||||
# console-subsystem one opens a console window behind the game. mainCRTStartup keeps
|
# console-subsystem one opens a console window behind the game. mainCRTStartup keeps
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,8 @@ function gl_link_flags(ll: pointer) -> pointer {
|
||||||
function vk_link_flags(ll: pointer) -> pointer {
|
function vk_link_flags(ll: pointer) -> pointer {
|
||||||
if not shq(`grep -q "@lvk_" {ll}`) { return "" }
|
if not shq(`grep -q "@lvk_" {ll}`) { return "" }
|
||||||
let home = ludic_home()
|
let home = ludic_home()
|
||||||
return ` {home}runtime/native/vk_thunks.ll {home}runtime/native/vk_mac.ll`
|
# QuartzCore: the CAMetalLayer a window presents through (cocoa.ll finds the class by name)
|
||||||
|
return ` {home}runtime/native/vk_thunks.ll {home}runtime/native/vk_mac.ll -Wl,-needed_framework,QuartzCore`
|
||||||
}
|
}
|
||||||
|
|
||||||
# A package that carries a native library (phase 15) is written into the IR by the compiler as
|
# A package that carries a native library (phase 15) is written into the IR by the compiler as
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue