nx_game_runtime_compose_test.nx source
↩ module page · 141 lines · 5521 B
1// nx_game_runtime_compose_test.nx -- game-runtime end-to-end.
2//
3// Full game runtime: collision → bounce → animation → audio cue →
4// save. All 4 new primitives composed with prior game-engine and
5// crypto-facade work. 22 assertions.
6
7import "nx_syscalls.nx"
8import "nx_tier.nx"
9import "nx_q14_math.nx"
10import "nx_methyl.nx"
11import "nx_chromatin.nx"
12import "nx_audio_pcm.nx"
13import "nx_collision_aabb.nx"
14import "nx_animation_timeline.nx"
15import "nx_save_slot.nx"
16
17func main() -> i64 {
18 // ===== Step 1: player + voxel collision setup ===================
19 // Player AABB at (3.5, 3.5, 3.5) sized 0.5
20 let player: *NxAabb = nx_aabb_new(
21 3 * NX_Q14 + NX_Q14 / 2 - NX_Q14 / 4,
22 3 * NX_Q14 + NX_Q14 / 2 - NX_Q14 / 4,
23 3 * NX_Q14 + NX_Q14 / 2 - NX_Q14 / 4,
24 3 * NX_Q14 + NX_Q14 / 2 + NX_Q14 / 4,
25 3 * NX_Q14 + NX_Q14 / 2 + NX_Q14 / 4,
26 3 * NX_Q14 + NX_Q14 / 2 + NX_Q14 / 4)
27 // Stone block at (4,3,3)
28 let block: *NxAabb = nx_aabb_from_voxel(4, 3, 3)
29
30 // No overlap yet
31 if nx_aabb_overlap(player, block) != NX_AABB_NO_OVERLAP { return 1 }
32
33 // Walk player +X by 0.5 -> should overlap block at (4,3,3)
34 nx_aabb_translate(player, NX_Q14 / 2, 0, 0)
35 if nx_aabb_overlap(player, block) != NX_AABB_OVERLAP { return 2 }
36
37 // Bounce back: translate -0.5 in x
38 nx_aabb_translate(player, 0 - NX_Q14 / 2, 0, 0)
39 if nx_aabb_overlap(player, block) != NX_AABB_NO_OVERLAP { return 3 }
40
41 // ===== Step 2: animation timeline for player camera bob =========
42 // Bob: 0 at t=0, 1024 (Q14 0.0625 amplitude) at t=500ms, 0 at t=1000ms
43 let bob: *NxTimeline = nx_timeline_new(8)
44 nx_timeline_add_keyframe(bob, 0, 0)
45 nx_timeline_add_keyframe(bob, 500000, 1024)
46 nx_timeline_add_keyframe(bob, 1000000, 0)
47 if nx_timeline_count(bob) != 3 { return 4 }
48 if nx_timeline_duration_us(bob) != 1000000 { return 5 }
49
50 // Sample peaks at midpoint
51 if nx_timeline_sample(bob, 500000) != 1024 { return 6 }
52 // Quarter-point interpolation: t=250000 -> 512 (half of 1024)
53 if nx_timeline_sample(bob, 250000) != 512 { return 7 }
54
55 // Loop sample: t=1500000 wraps to t=500000 (peak)
56 if nx_timeline_sample_loop(bob, 1500000, 1000000) != 1024 { return 8 }
57
58 // ===== Step 3: audio cue on collision ===========================
59 // Short 16-sample beep at 8 kHz (2ms duration)
60 let beep_samples: *u8 = (sys_mmap(32)) as *u8
61 var i: nx_size = 0
62 while i < 16 {
63 // Alternating loud/quiet for a buzz effect
64 if (i & 1) == 0 {
65 beep_samples[i] = 200 as u8
66 } else {
67 beep_samples[i] = 56 as u8
68 }
69 i = i + 1
70 }
71 let beep: *NxPcmBuffer = nx_pcm_buffer_new(beep_samples, 16, 8000)
72 if nx_pcm_duration_us(beep) != 2000 { return 9 }
73 // Peak amplitude near full-scale
74 if nx_pcm_peak_amplitude(beep) < 50 { return 10 }
75 if nx_pcm_peak_amplitude(beep) > 80 { return 11 }
76
77 // Halve volume
78 nx_pcm_amplitude_scale_q10(beep, 512)
79 // After halving, peak should be ~36
80 let new_peak: nx_int = nx_pcm_peak_amplitude(beep)
81 if new_peak < 30 { return 12 }
82 if new_peak > 45 { return 13 }
83
84 // Resample to 16 kHz for higher-fidelity output
85 let beep_16k: *u8 = (sys_mmap(64)) as *u8
86 let n_16k: nx_int = nx_pcm_resample_linear(beep, beep_16k, 64, 16000)
87 if n_16k != 32 { return 14 }
88
89 // ===== Step 4: save the game state =============================
90 let sig_key_msg: *u8 = (sys_mmap(96)) as *u8
91 sig_key_msg[0] = 1 as u8
92 let mark_p: *NxMethylMark = nx_methyl_new(10, 0xaaaa, 1000, sig_key_msg, 96)
93 let mark_w: *NxMethylMark = nx_methyl_new(20, 0xbbbb, 1000, sig_key_msg, 96)
94
95 let snap_player: *u8 = (sys_mmap(64)) as *u8
96 snap_player[0] = 42 as u8
97 snap_player[1] = (player.min_x_q14 & 255) as u8 // serialize player position
98 let snap_world: *u8 = (sys_mmap(64)) as *u8
99 snap_world[0] = 99 as u8
100
101 let player_ch: *NxChromatin = nx_chromatin_capture(10, 0xc101, 2000,
102 snap_player, 64, mark_p, 1)
103 let world_ch: *NxChromatin = nx_chromatin_capture(20, 0xc202, 2000,
104 snap_world, 64, mark_w, 1)
105
106 let sk: *u8 = (sys_mmap(32)) as *u8
107 var j: nx_size = 0
108 while j < 32 {
109 sk[j] = (50 + j) as u8
110 j = j + 1
111 }
112 let sig_buf: *u8 = (sys_mmap(128)) as *u8
113 let slot: *NxSaveSlot = nx_save_slot_create(7777, player_ch, world_ch, 5000,
114 sk, 32, sig_buf, 128, 410)
115 if (slot as i64) == 0 { return 15 }
116
117 // Verify save integrity
118 let v: nx_int = nx_save_slot_verify(slot, sk, 32, 410)
119 if v != NX_SS_VALID { return 16 }
120
121 // ===== Step 5: ray-AABB pickaxe test ===========================
122 // Player at (1.5, 3.5, 3.5) looking +X; should hit block at (4,3,3) at t~2.5
123 let out_t: *i64 = (sys_mmap(8)) as *i64
124 let hit: nx_int = nx_aabb_intersect_ray(block,
125 NX_Q14 + NX_Q14 / 2, // 1.5
126 3 * NX_Q14 + NX_Q14 / 2, // 3.5
127 3 * NX_Q14 + NX_Q14 / 2, // 3.5
128 NX_Q14, 0, 0, // +X
129 10 * NX_Q14,
130 out_t)
131 if hit != 1 { return 17 }
132 if out_t[0] < 40000 { return 18 }
133 if out_t[0] > 42000 { return 19 } // ~2.5 in Q14 = 40960
134
135 // ===== Step 6: forensic asserts =================================
136 if nx_pcm_duration_us(beep) != 2000 { return 20 }
137 if nx_timeline_count(bob) != 3 { return 21 }
138 if nx_save_slot_id(slot) != 7777 { return 22 }
139
140 return 0
141}