nx_decl_dungeon_floor.nx source
↩ module page · 172 lines · 6785 B
1// nx_decl_dungeon_floor.nx -- fourth declarative #tag-style primitive.
2//
3// license_tier: ORIGINAL
4//
5// Composes an indoor dungeon-floor layout from one substrate call.
6// Distinct from outdoor declarators: dungeons are bounded grid-like
7// structures with rooms + corridors + treasures + traps, no biome,
8// no sky. Equivalent of `#dungeon_floor <modifier>`.
9//
10// Per S-class roadmap §A3 -- fourth slice, validates the #tag
11// pattern scales to non-environment scenes.
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"
23const NX_MAGIC_1024: i64 = 1024
24
25// ---- Sealed enum: dungeon mood -----------------------------------
26const NX_DUNGEON_MOOD_CRYPT: nx_int = 0 // dark, undead-themed
27const NX_DUNGEON_MOOD_RUIN: nx_int = 1 // collapsed, overgrown
28const NX_DUNGEON_MOOD_FORGE: nx_int = 2 // industrial, hot
29const NX_DUNGEON_MOOD_LIBRARY: nx_int = 3 // quiet, archive
30const NX_DUNGEON_MOOD_GARDEN: nx_int = 4 // verdant, fey-themed
31const NX_DUNGEON_MOOD_REACTOR: nx_int = 5 // sci-fi power core
32const NX_DUNGEON_MOOD_LAB: nx_int = 6 // sci-fi research wing
33const NX_DUNGEON_MOOD_N: nx_int = 7
34
35func nx_dungeon_mood_is_valid(m: nx_int) -> nx_int {
36 if m < 0 { return 0 }
37 if m >= NX_DUNGEON_MOOD_N { return 0 }
38 return 1
39}
40
41func nx_dungeon_mood_name(m: nx_int) -> *u8 {
42 if m == NX_DUNGEON_MOOD_CRYPT { return "CRYPT" as *u8 }
43 if m == NX_DUNGEON_MOOD_RUIN { return "RUIN" as *u8 }
44 if m == NX_DUNGEON_MOOD_FORGE { return "FORGE" as *u8 }
45 if m == NX_DUNGEON_MOOD_LIBRARY { return "LIBRARY" as *u8 }
46 if m == NX_DUNGEON_MOOD_GARDEN { return "GARDEN" as *u8 }
47 if m == NX_DUNGEON_MOOD_REACTOR { return "REACTOR" as *u8 }
48 if m == NX_DUNGEON_MOOD_LAB { return "LAB" as *u8 }
49 return "<invalid mood>" as *u8
50}
51
52// ---- Output struct -----------------------------------------------
53struct NxDungeonFloor {
54 seed: nx_int,
55 modifier: nx_int,
56 mood: nx_int, // sealed enum NX_DUNGEON_MOOD_*
57 grid_width: nx_int, // tiles
58 grid_height: nx_int, // tiles
59 room_count: nx_int,
60 corridor_count: nx_int,
61 treasure_count: nx_int,
62 trap_count: nx_int,
63 boss_present: nx_int, // 0 / 1
64 light_level_q10: nx_int, // ambient light, 0..NX_MAGIC_1024
65 is_valid: nx_int,
66}
67
68// ---- Mood selection per modifier ---------------------------------
69// REAL: roll CRYPT/RUIN/FORGE/LIBRARY/GARDEN (fantasy-genre dungeons).
70// FANTASY: same plus more variety from seed.
71// SCI_FI: REACTOR or LAB only.
72func _df_default_mood(modifier: nx_int, seed: nx_int) -> nx_int {
73 if modifier == NX_RE_MODIFIER_SCI_FI {
74 let r: nx_int = _re_hash_mod(seed, 17, 2)
75 if r == 0 { return NX_DUNGEON_MOOD_REACTOR }
76 return NX_DUNGEON_MOOD_LAB
77 }
78 let r: nx_int = _re_hash_mod(seed, 17, 5)
79 if r == 0 { return NX_DUNGEON_MOOD_CRYPT }
80 if r == 1 { return NX_DUNGEON_MOOD_RUIN }
81 if r == 2 { return NX_DUNGEON_MOOD_FORGE }
82 if r == 3 { return NX_DUNGEON_MOOD_LIBRARY }
83 return NX_DUNGEON_MOOD_GARDEN
84}
85
86// ---- Grid size per modifier --------------------------------------
87func _df_default_grid_width(modifier: nx_int) -> nx_int {
88 if modifier == NX_RE_MODIFIER_FANTASY { return 48 }
89 if modifier == NX_RE_MODIFIER_SCI_FI { return 32 }
90 return 24
91}
92
93func _df_default_grid_height(modifier: nx_int) -> nx_int {
94 if modifier == NX_RE_MODIFIER_FANTASY { return 48 }
95 if modifier == NX_RE_MODIFIER_SCI_FI { return 32 }
96 return 24
97}
98
99// ---- Feature counts per modifier ---------------------------------
100func _df_default_room_count(modifier: nx_int, seed: nx_int) -> nx_int {
101 let base: nx_int = _re_hash_mod(seed, 21, 3) // 0..2
102 if modifier == NX_RE_MODIFIER_FANTASY { return base + 8 }
103 if modifier == NX_RE_MODIFIER_SCI_FI { return base + 5 }
104 return base + 4
105}
106
107func _df_default_corridor_count(modifier: nx_int, rooms: nx_int) -> nx_int {
108 // Roughly one fewer corridor than rooms (forms a spanning graph).
109 if rooms <= 1 { return 0 }
110 return rooms - 1
111}
112
113func _df_default_treasure_count(modifier: nx_int, rooms: nx_int) -> nx_int {
114 // ~25% of rooms get treasure (rounded), with modifier bonus.
115 var t: nx_int = rooms / 4
116 if modifier == NX_RE_MODIFIER_FANTASY { t = t + 2 }
117 if t < 1 { t = 1 }
118 return t
119}
120
121func _df_default_trap_count(modifier: nx_int, rooms: nx_int, mood: nx_int) -> nx_int {
122 // GARDEN has fewer traps; CRYPT has more.
123 var t: nx_int = rooms / 3
124 if mood == NX_DUNGEON_MOOD_GARDEN { t = t / 2 }
125 if mood == NX_DUNGEON_MOOD_CRYPT { t = t + 1 }
126 if mood == NX_DUNGEON_MOOD_REACTOR { t = t + 1 }
127 if t < 0 { t = 0 }
128 return t
129}
130
131func _df_default_light_q10(mood: nx_int) -> nx_int {
132 if mood == NX_DUNGEON_MOOD_CRYPT { return 102 } // dim
133 if mood == NX_DUNGEON_MOOD_RUIN { return 256 }
134 if mood == NX_DUNGEON_MOOD_FORGE { return 768 } // bright forge
135 if mood == NX_DUNGEON_MOOD_LIBRARY { return 384 }
136 if mood == NX_DUNGEON_MOOD_GARDEN { return 640 }
137 if mood == NX_DUNGEON_MOOD_REACTOR { return 896 } // very bright
138 if mood == NX_DUNGEON_MOOD_LAB { return 512 }
139 return 512
140}
141
142// ---- Main declarator ---------------------------------------------
143func nx_decl_dungeon_floor(out: *NxDungeonFloor, modifier: nx_int, seed: nx_int) -> nx_int {
144 if nx_re_modifier_is_valid(modifier) == 0 {
145 out.is_valid = 0
146 return 0
147 }
148 out.seed = seed
149 out.modifier = modifier
150 out.mood = _df_default_mood(modifier, seed)
151 out.grid_width = _df_default_grid_width(modifier)
152 out.grid_height = _df_default_grid_height(modifier)
153 out.room_count = _df_default_room_count(modifier, seed)
154 out.corridor_count = _df_default_corridor_count(modifier, out.room_count)
155 out.treasure_count = _df_default_treasure_count(modifier, out.room_count)
156 out.trap_count = _df_default_trap_count(modifier, out.room_count, out.mood)
157 out.light_level_q10 = _df_default_light_q10(out.mood)
158 // Boss present iff seed rolls a 1-in-3 AND >= 5 rooms.
159 let roll: nx_int = _re_hash_mod(seed, 29, 3)
160 var boss: nx_int = 0
161 if roll == 0 { boss = 1 }
162 if out.room_count < 5 { boss = 0 }
163 out.boss_present = boss
164 out.is_valid = 1
165 return 1
166}
167
168func nx_df_alloc() -> *NxDungeonFloor {
169 let raw: *u8 = sys_mmap(128)
170 let d: *NxDungeonFloor = raw as *NxDungeonFloor
171 return d
172}