code wiki / _hdl_build / nx_swap.nx
nx_swap.nx source
↩ module page · 389 lines · 20115 B
1// nx_swap.nx -- LIB: the GAMIFIED COMMUNITY PLANT/HERB SWAP (operator: "a community swap that's like a gamified
2// fun exchange like Pokemon GO but for gardening without the licensing and other issues but allows a person to
3// ask and give say basil or rosemary instead of buying at a store"). Pokemon-GO-style FUN, but our OWN IP: the
4// "collection" is REAL PLANTS (a garden-dex), the mechanics are ours (no third-party IP/licensing). A GIFT
5// economy -- ask + give, NO money -- so you get basil/rosemary by swapping with neighbours instead of buying.
6//
7// Gamification: a garden DEX (species you grow/swapped), XP for giving + collecting, LEVELS, SETS + BADGES
8// ("gotta grow 'em all"), rarity. Area-based local discovery (privacy-first: an AREA, not GPS). Ties to the
9// seed-to-feed lineage (a swapped seed records origin=swapped). Sovereign seg-store (knowledge/store/swap-*, NO
10// TSV), integer-only. Composes nx_ads for a sovereign in-board ad slot. license_tier: ORIGINAL
11import "nx_ads.nx"
12import "nx_food_science.nx"
13import "nx_seg_store.nx"
14import "nx_syscalls.nx"
15
16func sw_put(prefix: *u8, key: *u8, val: *u8) -> i64 {
17 let vl: i64 = as_len(val)
18 if fd_streq_store(prefix, key, val, vl) == 1 { return 0 }
19 let w: *i64 = ss_begin()
20 ss_add(w, 1, key, val, vl)
21 let seg: i64 = fd_seg_next(prefix)
22 ss_commit(prefix, w, seg)
23 return 1
24}
25func sw_split(buf: *u8, len: i64, out: *i64) -> i64 {
26 var c: i64 = 0
27 var i: i64 = 0
28 var ls: i64 = 0
29 while i <= len {
30 var sep: i64 = 0
31 if i == len { sep = 1 } else { if buf[i] == (9 as u8) { sep = 1 } }
32 if sep == 1 {
33 let tl: i64 = i - ls
34 if tl > 0 {
35 let tok: *u8 = sys_mmap(48)
36 var t: i64 = 0
37 while t < tl { tok[t] = buf[ls + t]; t = t + 1 } tok[tl] = 0 as u8
38 out[c] = tok as i64; c = c + 1
39 }
40 ls = i + 1
41 }
42 i = i + 1
43 }
44 return c
45}
46// idempotent append of token to the TAB list at key
47func sw_list_append(prefix: *u8, key: *u8, token: *u8) -> i64 {
48 let pq: *i64 = sys_mmap(16) as *i64
49 let lq: *i64 = sys_mmap(16) as *i64
50 let cur: *u8 = sys_mmap(4096)
51 var o: i64 = 0
52 if ss_get(prefix, key, pq, lq) == 1 {
53 let b: *u8 = pq[0] as *u8; let n: i64 = lq[0]
54 let toks: *i64 = sys_mmap(8 * 256) as *i64
55 let nt: i64 = sw_split(b, n, toks)
56 var i: i64 = 0
57 while i < nt { if fd_streq(toks[i] as *u8, token) == 1 { return 0 } i = i + 1 }
58 var u: i64 = 0; while u < n { cur[u] = b[u]; u = u + 1 } o = n
59 cur[o] = 9 as u8; o = o + 1
60 }
61 o = as_append(cur, o, token); cur[o] = 0 as u8
62 return sw_put(prefix, key, cur)
63}
64
65// ---- config ----
66func sw_seed_config(prefix: *u8) -> i64 { return sw_put(prefix, "swap:xpconfig" as *u8, "10\t10\t25\t100\t50" as *u8) } // give,receive,newspecies,setcomplete,per_level
67func sw_cfg(prefix: *u8, f: i64) -> i64 {
68 let pq: *i64 = sys_mmap(16) as *i64
69 let lq: *i64 = sys_mmap(16) as *i64
70 if ss_get(prefix, "swap:xpconfig" as *u8, pq, lq) != 1 { return 0 }
71 let fb: *u8 = sys_mmap(16)
72 let fl: i64 = fd_field(pq[0] as *u8, lq[0], f, fb)
73 return fd_atoi(fb, fl)
74}
75
76// ---- species catalog (the "creatures" = real plants) ----
77func sw_add_species(prefix: *u8, sid: *u8, name: *u8, category: *u8, rarity: *u8) -> i64 {
78 let key: *u8 = sys_mmap(96); var o: i64 = 0; o = as_append(key, o, "swap:species:" as *u8); o = as_append(key, o, sid); key[o] = 0 as u8
79 let val: *u8 = sys_mmap(160); var v: i64 = 0
80 v = as_append(val, v, name); val[v] = 9 as u8; v = v + 1
81 v = as_append(val, v, category); val[v] = 9 as u8; v = v + 1
82 v = as_append(val, v, rarity); val[v] = 0 as u8
83 return sw_put(prefix, key, val)
84}
85func sw_species_field(prefix: *u8, sid: *u8, f: i64, out: *u8) -> i64 {
86 let key: *u8 = sys_mmap(96); var o: i64 = 0; o = as_append(key, o, "swap:species:" as *u8); o = as_append(key, o, sid); key[o] = 0 as u8
87 let pq: *i64 = sys_mmap(16) as *i64
88 let lq: *i64 = sys_mmap(16) as *i64
89 if ss_get(prefix, key, pq, lq) != 1 { out[0] = 0 as u8; return 0 }
90 return fd_field(pq[0] as *u8, lq[0], f, out)
91}
92
93// ---- gardeners + XP + levels ----
94func sw_g_key(gid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "swap:gardener:" as *u8); o = as_append(out, o, gid); out[o] = 0 as u8; return o }
95func sw_xp_key(gid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "swap:xp:" as *u8); o = as_append(out, o, gid); out[o] = 0 as u8; return o }
96func sw_add_gardener(prefix: *u8, gid: *u8, name: *u8, area: *u8) -> i64 {
97 let key: *u8 = sys_mmap(96); sw_g_key(gid, key)
98 let val: *u8 = sys_mmap(160); var v: i64 = 0
99 v = as_append(val, v, name); val[v] = 9 as u8; v = v + 1
100 v = as_append(val, v, area); val[v] = 0 as u8
101 let w: i64 = sw_put(prefix, key, val)
102 let xk: *u8 = sys_mmap(96); sw_xp_key(gid, xk)
103 let pq: *i64 = sys_mmap(16) as *i64
104 let lq: *i64 = sys_mmap(16) as *i64
105 if ss_get(prefix, xk, pq, lq) != 1 { sw_put(prefix, xk, "0" as *u8) }
106 return w
107}
108func sw_g_field(prefix: *u8, gid: *u8, f: i64, out: *u8) -> i64 {
109 let key: *u8 = sys_mmap(96); sw_g_key(gid, key)
110 let pq: *i64 = sys_mmap(16) as *i64
111 let lq: *i64 = sys_mmap(16) as *i64
112 if ss_get(prefix, key, pq, lq) != 1 { out[0] = 0 as u8; return 0 }
113 return fd_field(pq[0] as *u8, lq[0], f, out)
114}
115func sw_xp(prefix: *u8, gid: *u8) -> i64 {
116 let xk: *u8 = sys_mmap(96); sw_xp_key(gid, xk)
117 let pq: *i64 = sys_mmap(16) as *i64
118 let lq: *i64 = sys_mmap(16) as *i64
119 if ss_get(prefix, xk, pq, lq) != 1 { return 0 }
120 return fd_atoi(pq[0] as *u8, lq[0])
121}
122func sw_add_xp(prefix: *u8, gid: *u8, amt: i64) -> i64 {
123 let cur: i64 = sw_xp(prefix, gid)
124 let xk: *u8 = sys_mmap(96); sw_xp_key(gid, xk)
125 let nv: *u8 = sys_mmap(24); var no: i64 = fd_apnum(nv, 0, cur + amt); nv[no] = 0 as u8
126 sw_put(prefix, xk, nv)
127 return cur + amt
128}
129func sw_level(prefix: *u8, gid: *u8) -> i64 {
130 let per: i64 = sw_cfg(prefix, 4)
131 if per <= 0 { return 1 }
132 return 1 + sw_xp(prefix, gid) / per
133}
134
135// ---- garden dex (collection) ----
136func sw_dex_key(gid: *u8, species: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "swap:dex:" as *u8); o = as_append(out, o, gid); out[o] = 58 as u8; o = o + 1; o = as_append(out, o, species); out[o] = 0 as u8; return o }
137func sw_dexn_key(gid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "swap:dexn:" as *u8); o = as_append(out, o, gid); out[o] = 0 as u8; return o }
138func sw_dex_has(prefix: *u8, gid: *u8, species: *u8) -> i64 {
139 let key: *u8 = sys_mmap(96); sw_dex_key(gid, species, key)
140 let pq: *i64 = sys_mmap(16) as *i64
141 let lq: *i64 = sys_mmap(16) as *i64
142 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
143 return 1
144}
145func sw_dex_count(prefix: *u8, gid: *u8) -> i64 {
146 let key: *u8 = sys_mmap(96); sw_dexn_key(gid, key)
147 let pq: *i64 = sys_mmap(16) as *i64
148 let lq: *i64 = sys_mmap(16) as *i64
149 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
150 return fd_atoi(pq[0] as *u8, lq[0])
151}
152func sw_dex_add(prefix: *u8, gid: *u8, species: *u8) -> i64 {
153 if sw_dex_has(prefix, gid, species) == 1 { return 0 }
154 let key: *u8 = sys_mmap(96); sw_dex_key(gid, species, key)
155 sw_put(prefix, key, "1" as *u8)
156 let cnt: i64 = sw_dex_count(prefix, gid)
157 let nk: *u8 = sys_mmap(96); sw_dexn_key(gid, nk)
158 let nv: *u8 = sys_mmap(24); var no: i64 = fd_apnum(nv, 0, cnt + 1); nv[no] = 0 as u8
159 sw_put(prefix, nk, nv)
160 return 1
161}
162
163// ---- offers (give) + requests (ask) ----
164func sw_offer_key(oid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "swap:offer:" as *u8); o = as_append(out, o, oid); out[o] = 0 as u8; return o }
165func sw_ostatus_key(oid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "swap:ostatus:" as *u8); o = as_append(out, o, oid); out[o] = 0 as u8; return o }
166// post an offer (I HAVE X to give). Implies you grow it -> adds X to your dex. status=open.
167func sw_post_offer(prefix: *u8, oid: *u8, gid: *u8, species: *u8, form: *u8, qty: i64, area: *u8, note: *u8) -> i64 {
168 let key: *u8 = sys_mmap(96); sw_offer_key(oid, key)
169 let val: *u8 = sys_mmap(384); var v: i64 = 0
170 v = as_append(val, v, gid); val[v] = 9 as u8; v = v + 1
171 v = as_append(val, v, species); val[v] = 9 as u8; v = v + 1
172 v = as_append(val, v, form); val[v] = 9 as u8; v = v + 1
173 v = fd_apnum(val, v, qty); val[v] = 9 as u8; v = v + 1
174 v = as_append(val, v, area); val[v] = 9 as u8; v = v + 1
175 v = as_append(val, v, note); val[v] = 0 as u8
176 sw_put(prefix, key, val)
177 let sk: *u8 = sys_mmap(96); sw_ostatus_key(oid, sk)
178 let pq: *i64 = sys_mmap(16) as *i64
179 let lq: *i64 = sys_mmap(16) as *i64
180 if ss_get(prefix, sk, pq, lq) != 1 { sw_put(prefix, sk, "open" as *u8) }
181 sw_list_append(prefix, "swap:offerids" as *u8, oid)
182 sw_dex_add(prefix, gid, species)
183 return 1
184}
185func sw_offer_field(prefix: *u8, oid: *u8, f: i64, out: *u8) -> i64 {
186 let key: *u8 = sys_mmap(96); sw_offer_key(oid, key)
187 let pq: *i64 = sys_mmap(16) as *i64
188 let lq: *i64 = sys_mmap(16) as *i64
189 if ss_get(prefix, key, pq, lq) != 1 { out[0] = 0 as u8; return 0 }
190 return fd_field(pq[0] as *u8, lq[0], f, out)
191}
192func sw_offer_open(prefix: *u8, oid: *u8) -> i64 {
193 let sk: *u8 = sys_mmap(96); sw_ostatus_key(oid, sk)
194 let pq: *i64 = sys_mmap(16) as *i64
195 let lq: *i64 = sys_mmap(16) as *i64
196 if ss_get(prefix, sk, pq, lq) != 1 { return 0 }
197 return fd_streq(pq[0] as *u8, "open" as *u8)
198}
199func sw_post_request(prefix: *u8, rid: *u8, gid: *u8, species: *u8, area: *u8) -> i64 {
200 let key: *u8 = sys_mmap(96); var o: i64 = 0; o = as_append(key, o, "swap:request:" as *u8); o = as_append(key, o, rid); key[o] = 0 as u8
201 let val: *u8 = sys_mmap(160); var v: i64 = 0
202 v = as_append(val, v, gid); val[v] = 9 as u8; v = v + 1
203 v = as_append(val, v, species); val[v] = 9 as u8; v = v + 1
204 v = as_append(val, v, area); val[v] = 0 as u8
205 sw_put(prefix, key, val)
206 sw_list_append(prefix, "swap:requestids" as *u8, rid)
207 return 1
208}
209
210// find OPEN offers of `species` in `area` (the local match: who near me can give me basil). Returns count.
211func sw_find_offers(prefix: *u8, species: *u8, area: *u8, outoids: *i64) -> i64 {
212 let pq: *i64 = sys_mmap(16) as *i64
213 let lq: *i64 = sys_mmap(16) as *i64
214 if ss_get(prefix, "swap:offerids" as *u8, pq, lq) != 1 { return 0 }
215 let ids: *i64 = sys_mmap(8 * 512) as *i64
216 let n: i64 = sw_split(pq[0] as *u8, lq[0], ids)
217 var c: i64 = 0
218 var i: i64 = 0
219 while i < n {
220 let oid: *u8 = ids[i] as *u8
221 let sp: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 1, sp)
222 let ar: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 4, ar)
223 if fd_streq(sp, species) == 1 {
224 if fd_streq(ar, area) == 1 {
225 if sw_offer_open(prefix, oid) == 1 { outoids[c] = oid as i64; c = c + 1 }
226 }
227 }
228 i = i + 1
229 }
230 return c
231}
232
233// THE SWAP: a taker claims an open offer = the gamified exchange. Marks claimed, adds the species to the taker's
234// dex (if new), awards XP to giver (gave) + taker (received, +bonus if new species). Returns 1 on success.
235func sw_claim_offer(prefix: *u8, oid: *u8, taker_gid: *u8) -> i64 {
236 if sw_offer_open(prefix, oid) == 0 { return 0 }
237 let giver: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 0, giver)
238 let species: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 1, species)
239 let sk: *u8 = sys_mmap(96); sw_ostatus_key(oid, sk)
240 sw_put(prefix, sk, "claimed" as *u8)
241 var newsp: i64 = 0
242 if sw_dex_has(prefix, taker_gid, species) == 0 { newsp = 1; sw_dex_add(prefix, taker_gid, species) }
243 sw_add_xp(prefix, giver, sw_cfg(prefix, 0)) // give
244 var recv: i64 = sw_cfg(prefix, 1) // receive
245 if newsp == 1 { recv = recv + sw_cfg(prefix, 2) } // + new-species bonus
246 sw_add_xp(prefix, taker_gid, recv)
247 return 1
248}
249
250// ---- sets + badges ("gotta grow 'em all") ----
251// set record: swap:set:<setid> -> name <t> species1 <t> species2 ...
252func sw_add_set(prefix: *u8, setid: *u8, record: *u8) -> i64 {
253 let key: *u8 = sys_mmap(96); var o: i64 = 0; o = as_append(key, o, "swap:set:" as *u8); o = as_append(key, o, setid); key[o] = 0 as u8
254 return sw_put(prefix, key, record)
255}
256// 1 if the gardener's dex covers every species in the set.
257func sw_set_complete(prefix: *u8, gid: *u8, setid: *u8) -> i64 {
258 let key: *u8 = sys_mmap(96); var o: i64 = 0; o = as_append(key, o, "swap:set:" as *u8); o = as_append(key, o, setid); key[o] = 0 as u8
259 let pq: *i64 = sys_mmap(16) as *i64
260 let lq: *i64 = sys_mmap(16) as *i64
261 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
262 let rec: *u8 = pq[0] as *u8
263 let rl: i64 = lq[0]
264 var f: i64 = 1
265 var checked: i64 = 0
266 var go: i64 = 1
267 while go == 1 {
268 let fb: *u8 = sys_mmap(48)
269 let fl: i64 = fd_field(rec, rl, f, fb)
270 if fl == 0 { go = 0 } else {
271 if sw_dex_has(prefix, gid, fb) == 0 { return 0 }
272 checked = checked + 1
273 f = f + 1
274 }
275 }
276 if checked == 0 { return 0 }
277 return 1
278}
279func sw_badge_key(gid: *u8, setid: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "swap:badge:" as *u8); o = as_append(out, o, gid); out[o] = 58 as u8; o = o + 1; o = as_append(out, o, setid); out[o] = 0 as u8; return o }
280func sw_has_badge(prefix: *u8, gid: *u8, setid: *u8) -> i64 {
281 let key: *u8 = sys_mmap(96); sw_badge_key(gid, setid, key)
282 let pq: *i64 = sys_mmap(16) as *i64
283 let lq: *i64 = sys_mmap(16) as *i64
284 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
285 return 1
286}
287// award the set badge if complete + not already held (gives the set-complete XP bonus). Returns 1 if newly awarded.
288func sw_award_badge(prefix: *u8, gid: *u8, setid: *u8) -> i64 {
289 if sw_set_complete(prefix, gid, setid) == 0 { return 0 }
290 if sw_has_badge(prefix, gid, setid) == 1 { return 0 }
291 let key: *u8 = sys_mmap(96); sw_badge_key(gid, setid, key)
292 sw_put(prefix, key, "1" as *u8)
293 sw_add_xp(prefix, gid, sw_cfg(prefix, 3))
294 return 1
295}
296
297// count how many of a set's species the gardener has (for progress display).
298func sw_set_progress(prefix: *u8, gid: *u8, setid: *u8, total_out: *i64) -> i64 {
299 let key: *u8 = sys_mmap(96); var o: i64 = 0; o = as_append(key, o, "swap:set:" as *u8); o = as_append(key, o, setid); key[o] = 0 as u8
300 let pq: *i64 = sys_mmap(16) as *i64
301 let lq: *i64 = sys_mmap(16) as *i64
302 total_out[0] = 0
303 if ss_get(prefix, key, pq, lq) != 1 { return 0 }
304 let rec: *u8 = pq[0] as *u8
305 let rl: i64 = lq[0]
306 var f: i64 = 1
307 var have: i64 = 0
308 var tot: i64 = 0
309 var go: i64 = 1
310 while go == 1 {
311 let fb: *u8 = sys_mmap(48)
312 let fl: i64 = fd_field(rec, rl, f, fb)
313 if fl == 0 { go = 0 } else {
314 tot = tot + 1
315 if sw_dex_has(prefix, gid, fb) == 1 { have = have + 1 }
316 f = f + 1
317 }
318 }
319 total_out[0] = tot
320 return have
321}
322
323// ---- the sovereign gamified BOARD (with an in-board contextual ad slot) ----
324func sw_render_board(prefix: *u8, ads_prefix: *u8, area: *u8, gid: *u8, setid: *u8, out: *u8) -> i64 {
325 let nm: *u8 = sys_mmap(96); sw_g_field(prefix, gid, 0, nm)
326 var o: i64 = 0
327 o = as_append(out, o, "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Nishi Garden Swap</title><style>body{margin:0;font-family:system-ui,sans-serif;color:#15301c;background:#eef6ea;line-height:1.5}header{background:#2f7d3a;color:#fff;padding:20px}header h1{margin:0;font-size:1.4rem}header p{margin:4px 0 0;color:#d7efce}main{max-width:680px;margin:0 auto;padding:16px}section{background:#fff;border-radius:14px;padding:16px 18px;margin-top:16px;box-shadow:0 1px 0 #dcebd3}h2{font-size:1.1rem;margin:0 0 10px;color:#2f7d3a}.lvl{display:inline-block;background:#2f7d3a;color:#fff;border-radius:20px;padding:3px 12px;font-weight:800}.bar{height:12px;background:#e3eedd;border-radius:6px;overflow:hidden;margin-top:6px}.bar > i{display:block;height:100%;background:#7cc15a}ul{list-style:none;padding:0;margin:0}li{padding:8px 0;border-bottom:1px solid #eef3ea}.give{color:#2f7d3a;font-weight:700}.adslot{background:#fbfdf8;border:1px dashed #b9d6a6;border-radius:10px;padding:12px 14px;margin-top:16px}.adlabel{display:inline-block;background:#e3eedd;color:#496b3a;font-size:.72rem;text-transform:uppercase;letter-spacing:.05em;padding:2px 8px;border-radius:6px;margin-bottom:6px}.adslot b{display:block}.adslot small{color:#5b6a52}.muted{color:#5b6a52;font-size:.88rem}</style></head><body><header><h1>Nishi Garden Swap</h1><p>Ask & give plants with neighbours — grow your collection instead of buying.</p></header><main>" as *u8)
328
329 // player card
330 o = as_append(out, o, "<section><h2>Your garden</h2><p><span class='lvl'>Level " as *u8)
331 o = fd_apnum(out, o, sw_level(prefix, gid))
332 o = as_append(out, o, "</span> — " as *u8)
333 o = as_append_escaped(out, o, nm, as_len(nm))
334 o = as_append(out, o, "</p><p>" as *u8)
335 o = fd_apnum(out, o, sw_xp(prefix, gid))
336 o = as_append(out, o, " XP · " as *u8)
337 o = fd_apnum(out, o, sw_dex_count(prefix, gid))
338 o = as_append(out, o, " species collected</p>" as *u8)
339 // set progress
340 let tot: *i64 = sys_mmap(16) as *i64
341 let have: i64 = sw_set_progress(prefix, gid, setid, tot)
342 o = as_append(out, o, "<p class='muted'>Set progress: " as *u8); o = fd_apnum(out, o, have); o = as_append(out, o, " / " as *u8); o = fd_apnum(out, o, tot[0])
343 if sw_has_badge(prefix, gid, setid) == 1 { o = as_append(out, o, " — badge earned!" as *u8) }
344 o = as_append(out, o, "</p><div class='bar'><i style='width:" as *u8)
345 var pct: i64 = 0
346 if tot[0] > 0 { pct = have * 100 / tot[0] }
347 o = fd_apnum(out, o, pct)
348 o = as_append(out, o, "%'></i></div></section>" as *u8)
349
350 // swaps near you
351 o = as_append(out, o, "<section><h2>Swaps near you (" as *u8)
352 o = as_append_escaped(out, o, area, as_len(area))
353 o = as_append(out, o, ")</h2><ul>" as *u8)
354 let pq: *i64 = sys_mmap(16) as *i64
355 let lq: *i64 = sys_mmap(16) as *i64
356 if ss_get(prefix, "swap:offerids" as *u8, pq, lq) == 1 {
357 let ids: *i64 = sys_mmap(8 * 512) as *i64
358 let n: i64 = sw_split(pq[0] as *u8, lq[0], ids)
359 var i: i64 = 0
360 while i < n {
361 let oid: *u8 = ids[i] as *u8
362 let ar: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 4, ar)
363 if fd_streq(ar, area) == 1 {
364 if sw_offer_open(prefix, oid) == 1 {
365 let og: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 0, og)
366 let sp: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 1, sp)
367 let fm: *u8 = sys_mmap(48); sw_offer_field(prefix, oid, 2, fm)
368 let ognm: *u8 = sys_mmap(96); sw_g_field(prefix, og, 0, ognm)
369 o = as_append(out, o, "<li><span class='give'>" as *u8)
370 o = as_append_escaped(out, o, sp, as_len(sp))
371 o = as_append(out, o, "</span> (" as *u8)
372 o = as_append_escaped(out, o, fm, as_len(fm))
373 o = as_append(out, o, ") — from " as *u8)
374 o = as_append_escaped(out, o, ognm, as_len(ognm))
375 o = as_append(out, o, "</li>" as *u8)
376 }
377 }
378 i = i + 1
379 }
380 }
381 o = as_append(out, o, "</ul></section>" as *u8)
382
383 // sovereign contextual ad slot (context = herbs; selected by topic, not by viewer)
384 o = ads_render_slot(ads_prefix, "herbs" as *u8, out, o)
385
386 o = as_append(out, o, "<p class='muted'>A gift economy — no money, just neighbours sharing. Our own game, our own plants; no third-party characters or licensing.</p></main></body></html>" as *u8)
387 out[o] = 0 as u8
388 return o
389}