nx_decl_townscape.nx source
↩ module page · 168 lines · 6361 B
1// nx_decl_townscape.nx -- fifth declarative #tag-style primitive.
2//
3// license_tier: ORIGINAL
4//
5// Composes a populated outdoor settlement: an environment with a
6// settlement guaranteed + building counts + road density + market
7// presence. Distinct from simple_scene: emphasis on inhabited
8// structure, not landscape. Equivalent of `#townscape <modifier>`.
9//
10// Per S-class roadmap §A3 -- fifth slice.
11
12// nx_safety_envelope:
13// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
14// sil_target: SIL1
15// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
16// verdict: NOT_YET_EVALUATED
17
18import "nx_syscalls.nx"
19import "nx_types.nx"
20import "nx_tier.nx"
21import "nx_biome_classifier.nx"
22import "nx_decl_random_environment.nx"
23import "nx_decl_simple_scene.nx"
24const NX_MAGIC_1200: i64 = 1200
25const NX_MAGIC_8000: i64 = 8000
26
27// ---- Sealed enum: settlement kind --------------------------------
28const NX_TOWN_KIND_HAMLET: nx_int = 0 // < 50 people
29const NX_TOWN_KIND_VILLAGE: nx_int = 1 // 50..500
30const NX_TOWN_KIND_TOWN: nx_int = 2 // 500..5000
31const NX_TOWN_KIND_CITY: nx_int = 3 // 5000+
32const NX_TOWN_KIND_OUTPOST: nx_int = 4 // small frontier (sci-fi or fantasy)
33const NX_TOWN_KIND_METROPOLIS: nx_int = 5 // 100k+ (sci-fi)
34const NX_TOWN_KIND_N: nx_int = 6
35
36func nx_town_kind_is_valid(k: nx_int) -> nx_int {
37 if k < 0 { return 0 }
38 if k >= NX_TOWN_KIND_N { return 0 }
39 return 1
40}
41
42func nx_town_kind_name(k: nx_int) -> *u8 {
43 if k == NX_TOWN_KIND_HAMLET { return "HAMLET" as *u8 }
44 if k == NX_TOWN_KIND_VILLAGE { return "VILLAGE" as *u8 }
45 if k == NX_TOWN_KIND_TOWN { return "TOWN" as *u8 }
46 if k == NX_TOWN_KIND_CITY { return "CITY" as *u8 }
47 if k == NX_TOWN_KIND_OUTPOST { return "OUTPOST" as *u8 }
48 if k == NX_TOWN_KIND_METROPOLIS { return "METROPOLIS" as *u8 }
49 return "<invalid town>" as *u8
50}
51
52// ---- Output struct -----------------------------------------------
53struct NxTownscape {
54 seed: nx_int,
55 modifier: nx_int,
56 scene: *NxSimpleScene,
57 kind: nx_int, // sealed enum NX_TOWN_KIND_*
58 building_count: nx_int,
59 road_count: nx_int,
60 market_present: nx_int, // 0 / 1
61 temple_present: nx_int, // 0 / 1 (or sci-fi: ritual-comp center)
62 population_log10: nx_int, // log10 of population estimate * 10 (Q1)
63 is_valid: nx_int,
64}
65
66// ---- Kind selection per modifier + seed --------------------------
67func _ts_default_kind(modifier: nx_int, seed: nx_int) -> nx_int {
68 if modifier == NX_RE_MODIFIER_SCI_FI {
69 let r: nx_int = _re_hash_mod(seed, 31, 3)
70 if r == 0 { return NX_TOWN_KIND_OUTPOST }
71 if r == 1 { return NX_TOWN_KIND_CITY }
72 return NX_TOWN_KIND_METROPOLIS
73 }
74 if modifier == NX_RE_MODIFIER_FANTASY {
75 let r: nx_int = _re_hash_mod(seed, 31, 4)
76 if r == 0 { return NX_TOWN_KIND_HAMLET }
77 if r == 1 { return NX_TOWN_KIND_VILLAGE }
78 if r == 2 { return NX_TOWN_KIND_TOWN }
79 return NX_TOWN_KIND_CITY
80 }
81 // REAL: more weight on village/town (common in real worlds)
82 let r: nx_int = _re_hash_mod(seed, 31, 5)
83 if r == 0 { return NX_TOWN_KIND_HAMLET }
84 if r == 1 { return NX_TOWN_KIND_VILLAGE }
85 if r == 2 { return NX_TOWN_KIND_VILLAGE }
86 if r == 3 { return NX_TOWN_KIND_TOWN }
87 return NX_TOWN_KIND_CITY
88}
89
90func _ts_building_count(kind: nx_int) -> nx_int {
91 if kind == NX_TOWN_KIND_HAMLET { return 8 }
92 if kind == NX_TOWN_KIND_VILLAGE { return 40 }
93 if kind == NX_TOWN_KIND_TOWN { return 200 }
94 if kind == NX_TOWN_KIND_CITY { return NX_MAGIC_1200 }
95 if kind == NX_TOWN_KIND_OUTPOST { return 6 }
96 if kind == NX_TOWN_KIND_METROPOLIS { return NX_MAGIC_8000 }
97 return 0
98}
99
100func _ts_road_count(kind: nx_int) -> nx_int {
101 if kind == NX_TOWN_KIND_HAMLET { return 2 }
102 if kind == NX_TOWN_KIND_VILLAGE { return 4 }
103 if kind == NX_TOWN_KIND_TOWN { return 12 }
104 if kind == NX_TOWN_KIND_CITY { return 40 }
105 if kind == NX_TOWN_KIND_OUTPOST { return 1 }
106 if kind == NX_TOWN_KIND_METROPOLIS { return 200 }
107 return 0
108}
109
110// population_log10 returns log10 of approximate population * 10 (Q1).
111// HAMLET 25 (1.4 in log10 * 10 = 14), VILLAGE 200 -> 23, TOWN 2000 -> 33,
112// CITY 20000 -> 43, METROPOLIS 200000 -> 53, OUTPOST 20 -> 13.
113func _ts_population_log10(kind: nx_int) -> nx_int {
114 if kind == NX_TOWN_KIND_HAMLET { return 14 }
115 if kind == NX_TOWN_KIND_VILLAGE { return 23 }
116 if kind == NX_TOWN_KIND_TOWN { return 33 }
117 if kind == NX_TOWN_KIND_CITY { return 43 }
118 if kind == NX_TOWN_KIND_OUTPOST { return 13 }
119 if kind == NX_TOWN_KIND_METROPOLIS { return 53 }
120 return 0
121}
122
123// ---- Main declarator ---------------------------------------------
124func nx_decl_townscape(out: *NxTownscape, modifier: nx_int, seed: nx_int) -> nx_int {
125 if nx_re_modifier_is_valid(modifier) == 0 {
126 out.is_valid = 0
127 return 0
128 }
129 out.seed = seed
130 out.modifier = modifier
131
132 // Phase 1: scene composition.
133 let scene: *NxSimpleScene = nx_ss_alloc()
134 nx_decl_simple_scene(scene, modifier, seed)
135 if scene.is_valid == 0 {
136 out.is_valid = 0
137 return 0
138 }
139 out.scene = scene
140
141 // Phase 2: settlement kind + counts.
142 out.kind = _ts_default_kind(modifier, seed)
143 out.building_count = _ts_building_count(out.kind)
144 out.road_count = _ts_road_count(out.kind)
145 out.population_log10 = _ts_population_log10(out.kind)
146
147 // Phase 3: amenities. Market present iff kind >= VILLAGE.
148 var market: nx_int = 0
149 if out.kind != NX_TOWN_KIND_HAMLET {
150 if out.kind != NX_TOWN_KIND_OUTPOST { market = 1 }
151 }
152 out.market_present = market
153 // Temple/ritual-center present iff kind >= TOWN.
154 var temple: nx_int = 0
155 if out.kind == NX_TOWN_KIND_TOWN { temple = 1 }
156 if out.kind == NX_TOWN_KIND_CITY { temple = 1 }
157 if out.kind == NX_TOWN_KIND_METROPOLIS { temple = 1 }
158 out.temple_present = temple
159
160 out.is_valid = 1
161 return 1
162}
163
164func nx_ts_alloc() -> *NxTownscape {
165 let raw: *u8 = sys_mmap(256)
166 let t: *NxTownscape = raw as *NxTownscape
167 return t
168}