nx_decl_combat_arena.nx source
↩ module page · 127 lines · 4791 B
1// nx_decl_combat_arena.nx -- third declarative #tag-style primitive.
2//
3// license_tier: ORIGINAL
4//
5// Composes a bounded combat arena from one substrate call.
6// Equivalent of TypeScript `#combat_arena <modifier>` directive.
7// Differs from nx_decl_simple_scene in that the environment is
8// BOUNDED (smaller, square, deterministic spawns) for gameplay,
9// not for landscape rendering.
10//
11// Per S-class roadmap §A3 -- third slice toward the user's vision.
12
13// nx_safety_envelope:
14// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
15// sil_target: SIL1
16// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
17// verdict: NOT_YET_EVALUATED
18
19import "nx_syscalls.nx"
20import "nx_types.nx"
21import "nx_tier.nx"
22import "nx_decl_random_environment.nx"
23import "nx_decl_simple_scene.nx"
24
25// ---- Sealed enum: arena shape -------------------------------------
26const NX_ARENA_SHAPE_SQUARE: nx_int = 0
27const NX_ARENA_SHAPE_CIRCLE: nx_int = 1
28const NX_ARENA_SHAPE_OCTAGON: nx_int = 2
29const NX_ARENA_SHAPE_HEXAGON: nx_int = 3
30const NX_ARENA_SHAPE_N: nx_int = 4
31
32func nx_arena_shape_is_valid(s: nx_int) -> nx_int {
33 if s < 0 { return 0 }
34 if s >= NX_ARENA_SHAPE_N { return 0 }
35 return 1
36}
37
38// Elm-style reflection: shape -> human name.
39func nx_arena_shape_name(s: nx_int) -> *u8 {
40 if s == NX_ARENA_SHAPE_SQUARE { return "SQUARE" as *u8 }
41 if s == NX_ARENA_SHAPE_CIRCLE { return "CIRCLE" as *u8 }
42 if s == NX_ARENA_SHAPE_OCTAGON { return "OCTAGON" as *u8 }
43 if s == NX_ARENA_SHAPE_HEXAGON { return "HEXAGON" as *u8 }
44 return "<invalid shape>" as *u8
45}
46
47// ---- Output struct -----------------------------------------------
48struct NxCombatArena {
49 seed: nx_int,
50 modifier: nx_int,
51 // Scene context.
52 scene: *NxSimpleScene,
53 // Arena geometry.
54 shape: nx_int, // sealed enum NX_ARENA_SHAPE_*
55 radius_m: nx_int, // bounding radius
56 obstacle_count: nx_int, // procgen obstacles inside the bound
57 // Spawn points. Spawn locations are deterministic from seed;
58 // caller computes positions via spawn_point_xz(seed, i).
59 spawn_count_per_team: nx_int, // typical: 1, 2, or 4
60 team_count: nx_int, // typical: 2
61 is_valid: nx_int,
62}
63
64// ---- Shape defaults per modifier ---------------------------------
65// REAL: square (predictable for melee). FANTASY: octagon (more
66// variety). SCI_FI: circle (clean futuristic look).
67func _arena_default_shape(modifier: nx_int) -> nx_int {
68 if modifier == NX_RE_MODIFIER_FANTASY { return NX_ARENA_SHAPE_OCTAGON }
69 if modifier == NX_RE_MODIFIER_SCI_FI { return NX_ARENA_SHAPE_CIRCLE }
70 return NX_ARENA_SHAPE_SQUARE
71}
72
73func _arena_default_radius(modifier: nx_int) -> nx_int {
74 if modifier == NX_RE_MODIFIER_FANTASY { return 32 } // 32m (medium arena)
75 if modifier == NX_RE_MODIFIER_SCI_FI { return 48 } // 48m (large)
76 return 24 // 24m (compact melee)
77}
78
79func _arena_default_obstacles(modifier: nx_int, seed: nx_int) -> nx_int {
80 // Roll 0..6 obstacles deterministically.
81 let base: nx_int = _re_hash_mod(seed, 11, 7)
82 if modifier == NX_RE_MODIFIER_FANTASY { return base + 2 }
83 if modifier == NX_RE_MODIFIER_SCI_FI { return base + 1 }
84 return base
85}
86
87func _arena_default_spawn_per_team(modifier: nx_int) -> nx_int {
88 if modifier == NX_RE_MODIFIER_FANTASY { return 4 } // 4v4
89 if modifier == NX_RE_MODIFIER_SCI_FI { return 2 } // 2v2
90 return 1 // 1v1
91}
92
93// ---- Main declarator ---------------------------------------------
94func nx_decl_combat_arena(out: *NxCombatArena, modifier: nx_int, seed: nx_int) -> nx_int {
95 if nx_re_modifier_is_valid(modifier) == 0 {
96 out.is_valid = 0
97 return 0
98 }
99 out.seed = seed
100 out.modifier = modifier
101
102 // Compose: a simple_scene with the combat modifier. Note the
103 // scene picks the light kind; we don't override it here but
104 // could (combat usually wants noon/overcast for visibility).
105 let scene: *NxSimpleScene = nx_ss_alloc()
106 nx_decl_simple_scene(scene, modifier, seed)
107 if scene.is_valid == 0 {
108 out.is_valid = 0
109 return 0
110 }
111 out.scene = scene
112
113 out.shape = _arena_default_shape(modifier)
114 out.radius_m = _arena_default_radius(modifier)
115 out.obstacle_count = _arena_default_obstacles(modifier, seed)
116 out.spawn_count_per_team = _arena_default_spawn_per_team(modifier)
117 out.team_count = 2
118
119 out.is_valid = 1
120 return 1
121}
122
123func nx_arena_alloc() -> *NxCombatArena {
124 let raw: *u8 = sys_mmap(256)
125 let a: *NxCombatArena = raw as *NxCombatArena
126 return a
127}