@adatechnology/game-items-three 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1761 @@
1
+ // src/kit.ts
2
+ var WEAR_TAGS = ["metal", "wood", "cloth", "plastic", "paper", "food", "glass"];
3
+ var channelsOf = (hex2) => [(hex2 >> 16 & 255) / 255, (hex2 >> 8 & 255) / 255, (hex2 & 255) / 255];
4
+ function createItemKit(T) {
5
+ const materials = [];
6
+ const M = (color, tag, options = {}) => {
7
+ const material = new T.MeshStandardMaterial(Object.assign({ color, roughness: 0.78, metalness: 0 }, options));
8
+ const base = new T.Color(color);
9
+ const data = { base, baseRgb: channelsOf(color), tag, rough: material.roughness };
10
+ material.userData = data;
11
+ materials.push(material);
12
+ return material;
13
+ };
14
+ const mesh = (geometry, material, position = [0, 0, 0], rotation = [0, 0, 0], scale) => {
15
+ const object = new T.Mesh(geometry, material);
16
+ object.position.set(...position);
17
+ object.rotation.set(...rotation);
18
+ if (scale) object.scale.set(...scale);
19
+ return object;
20
+ };
21
+ return {
22
+ T,
23
+ materials,
24
+ M,
25
+ mesh,
26
+ cyl: (radiusTop, radiusBottom, height, segments = 14) => new T.CylinderGeometry(radiusTop, radiusBottom, height, segments),
27
+ bx: (width, height, depth) => new T.BoxGeometry(width, height, depth),
28
+ lathe: (points, segments = 16) => new T.LatheGeometry(points.map(([x, y]) => new T.Vector2(x, y)), segments),
29
+ torus: (radius, tube2, arc = Math.PI * 2) => new T.TorusGeometry(radius, tube2, 8, 18, arc),
30
+ extrude: (points, depth, bevel = 0.015) => {
31
+ const shape = new T.Shape();
32
+ points.forEach(([x, y], index) => index ? shape.lineTo(x, y) : shape.moveTo(x, y));
33
+ return new T.ExtrudeGeometry(shape, { depth, bevelEnabled: true, bevelThickness: bevel, bevelSize: bevel, bevelSegments: 2 });
34
+ },
35
+ G: (...children) => {
36
+ const group = new T.Group();
37
+ children.forEach((child) => group.add(child));
38
+ return group;
39
+ },
40
+ metal: (color) => M(color, "metal", { metalness: 0.55, roughness: 0.45 }),
41
+ wood: (color) => M(color, "wood"),
42
+ cloth: (color) => M(color, "cloth", { roughness: 0.95 }),
43
+ plastic: (color) => M(color, "plastic", { roughness: 0.6 }),
44
+ paper: (color) => M(color, "paper", { roughness: 0.9 }),
45
+ food: (color) => M(color, "food", { roughness: 0.85 }),
46
+ glass: (color, opacity) => M(color, "glass", { roughness: 0.15, metalness: 0.1, transparent: true, opacity })
47
+ };
48
+ }
49
+
50
+ // src/wear.ts
51
+ var WEAR_TARGET = { food: 6122286, metal: 8011293, wood: 4141862, cloth: 4866099, plastic: 7038294, paper: 9075282, glass: 5591098 };
52
+ var WEAR_AMOUNT = [0, 0.3, 0.62];
53
+ var hexToRgb = (hex2) => [(hex2 >> 16 & 255) / 255, (hex2 >> 8 & 255) / 255, (hex2 & 255) / 255];
54
+ var dataOf = (material) => {
55
+ const data = material.userData;
56
+ return data.base && data.baseRgb && data.tag !== void 0 && data.rough !== void 0 ? data : void 0;
57
+ };
58
+ function wearableMaterials(root) {
59
+ const found = /* @__PURE__ */ new Set();
60
+ root.traverse((object) => {
61
+ const mesh = object;
62
+ if (!mesh.isMesh) return;
63
+ for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) if (dataOf(material)) found.add(material);
64
+ });
65
+ return [...found];
66
+ }
67
+ function setWear(target, stage) {
68
+ const materials = "traverse" in target ? wearableMaterials(target) : target;
69
+ if ("traverse" in target) target.userData.wear = stage;
70
+ const amount = WEAR_AMOUNT[stage];
71
+ for (const material of materials) {
72
+ const data = dataOf(material);
73
+ if (!data) continue;
74
+ const goal = WEAR_TARGET[data.tag];
75
+ if (goal === void 0) continue;
76
+ const [tr, tg, tb] = hexToRgb(goal);
77
+ const [r, g, b] = data.baseRgb;
78
+ material.color.setRGB(r + (tr - r) * amount, g + (tg - g) * amount, b + (tb - b) * amount, "srgb");
79
+ material.roughness = Math.min(1, data.rough + 0.2 * stage / 2);
80
+ if (data.tag === "metal") material.metalness = Math.max(0.1, 0.55 - 0.2 * stage);
81
+ }
82
+ }
83
+
84
+ // src/shapes.ts
85
+ var SHAPES = [
86
+ "can",
87
+ "bag",
88
+ "box",
89
+ "bottle",
90
+ "tool",
91
+ "roll",
92
+ "dish",
93
+ "cloth",
94
+ "shoe",
95
+ "jar",
96
+ "paper",
97
+ "fruit",
98
+ "cup",
99
+ "tube",
100
+ "blade",
101
+ "long_tool",
102
+ "garment",
103
+ "backpack",
104
+ "jerrycan",
105
+ "tank",
106
+ "book",
107
+ "key",
108
+ "battery",
109
+ "plank",
110
+ "device"
111
+ ];
112
+ var DEFAULT_COLOR = 9080724;
113
+ var PI = Math.PI;
114
+ function shade(color, factor) {
115
+ const channel = (shift) => Math.max(0, Math.min(255, Math.round((color >> shift & 255) * factor)));
116
+ return channel(16) << 16 | channel(8) << 8 | channel(0);
117
+ }
118
+ var seg = (c, n) => Math.max(5, Math.round(n * c.detail));
119
+ function canonicalFrame(size) {
120
+ const [w, h, d] = size;
121
+ if (h >= w && h >= d) return { axis: 1, L: h, a: w, b: d };
122
+ if (d >= w && d >= h) return { axis: 2, L: d, a: h, b: w };
123
+ return { axis: 0, L: w, a: h, b: d };
124
+ }
125
+ function orient(c, group, axis) {
126
+ if (axis === 0) return group;
127
+ const holder = c.k.G(group);
128
+ if (axis === 1) holder.rotation.z = PI / 2;
129
+ else holder.rotation.y = -PI / 2;
130
+ return holder;
131
+ }
132
+ var alongLong = (draw) => (c) => {
133
+ const { axis, L, a, b } = canonicalFrame([c.w, c.h, c.d]);
134
+ return orient(c, draw(c, L, a, b), axis);
135
+ };
136
+ var across = (c, group) => {
137
+ group.scale.set(c.w, 1, c.d);
138
+ return c.k.G(group);
139
+ };
140
+ var can = (c) => {
141
+ const { k, h } = c;
142
+ const body = k.metal(9277590);
143
+ return across(c, k.G(k.mesh(k.cyl(0.5, 0.5, h * 0.94, seg(c, 14)), body), k.mesh(k.cyl(0.5, 0.5, h * 0.5, seg(c, 14)), k.food(c.color), [0, 0, 0], [0, 0, 0], [1, 1, 1]), k.mesh(k.cyl(0.5, 0.5, h * 0.03, seg(c, 14)), body, [0, h * 0.485, 0]), k.mesh(k.cyl(0.5, 0.5, h * 0.03, seg(c, 14)), body, [0, -h * 0.485, 0])));
144
+ };
145
+ var bottle = (c) => {
146
+ const { k, h } = c;
147
+ const r = 0.5;
148
+ const profile = [[0, -h / 2], [r, -h / 2], [r, h * 0.12], [r * 0.45, h * 0.3], [r * 0.4, h * 0.4], [0, h * 0.4]];
149
+ return across(c, k.G(k.mesh(k.lathe(profile, seg(c, 14)), k.glass(c.color, 0.6)), k.mesh(k.cyl(r * 0.42, r * 0.42, h * 0.1, seg(c, 10)), k.plastic(shade(c.color, 0.5)), [0, h * 0.45, 0])));
150
+ };
151
+ var jar = (c) => {
152
+ const { k, h } = c;
153
+ return across(c, k.G(k.mesh(k.cyl(0.5, 0.5, h * 0.82, seg(c, 14)), k.glass(c.color, 0.55), [0, -h * 0.09, 0]), k.mesh(k.cyl(0.5, 0.5, h * 0.18, seg(c, 14)), k.metal(12095802), [0, h * 0.41, 0])));
154
+ };
155
+ var bag = (c) => {
156
+ const { k, w, h, d } = c;
157
+ return k.G(k.mesh(k.bx(w, h * 0.88, d), k.paper(c.color), [0, -h * 0.06, 0]), k.mesh(k.bx(w * 1, h * 0.12, d * 0.5), k.paper(shade(c.color, 0.85)), [0, h * 0.44, 0]), k.mesh(k.bx(w * 0.7, h * 0.3, d * 1), k.paper(shade(c.color, 1.2)), [0, 0, 0]));
158
+ };
159
+ var box = (c) => {
160
+ const { k, w, h, d } = c;
161
+ return k.G(k.mesh(k.bx(w, h, d), k.paper(c.color)), k.mesh(k.bx(w, h * 0.12, d), k.paper(shade(c.color, 0.8)), [0, h * 0.44, 0]), k.mesh(k.bx(w * 0.62, h * 0.4, d), k.paper(shade(c.color, 1.25)), [0, -h * 0.02, 0]));
162
+ };
163
+ var cloth = (c) => {
164
+ const { k, w, h, d } = c;
165
+ const material = k.cloth(c.color);
166
+ return k.G(k.mesh(k.bx(w, h * 0.55, d), material, [0, -h * 0.225, 0]), k.mesh(k.bx(w * 0.96, h * 0.45, d * 0.96), k.cloth(shade(c.color, 0.9)), [0, h * 0.275, 0]));
167
+ };
168
+ var paper = (c) => {
169
+ const { k, w, h, d } = c;
170
+ const sheet = k.paper(c.color);
171
+ return k.G(k.mesh(k.bx(w, h / 3, d), sheet, [0, -h / 3, 0]), k.mesh(k.bx(w * 0.98, h / 3, d * 0.98), k.paper(shade(c.color, 0.95)), [0, 0, 0]), k.mesh(k.bx(w, h / 3, d), sheet, [0, h / 3, 0]));
172
+ };
173
+ var fruit = (c) => {
174
+ const { k, w, h, d } = c;
175
+ const sphere = new k.T.SphereGeometry(0.5, seg(c, 12), seg(c, 8));
176
+ return k.G(k.mesh(sphere, k.food(c.color), [0, -h * 0.04, 0], [0, 0, 0], [w, h * 0.92, d]), k.mesh(k.cyl(h * 0.02, h * 0.02, h * 0.1, 6), k.wood(5913122), [0, h * 0.45, 0]));
177
+ };
178
+ var dish = (c) => {
179
+ const { k, w, h, d } = c;
180
+ return k.G(k.mesh(k.cyl(0.5, 0.35, h, seg(c, 16)), k.plastic(c.color), [0, 0, 0], [0, 0, 0], [w, 1, d]));
181
+ };
182
+ var cup = (c) => {
183
+ const { k, w, h, d } = c;
184
+ const profile = [[0, -h / 2], [0.38, -h / 2], [0.5, h / 2], [0.44, h / 2], [0.33, -h / 2 + h * 0.06], [0, -h / 2 + h * 0.06]];
185
+ return k.G(k.mesh(k.lathe(profile, seg(c, 14)), k.plastic(c.color), [0, 0, 0], [0, 0, 0], [w, 1, d]));
186
+ };
187
+ var tube = (c) => {
188
+ const { k, h } = c;
189
+ const material = k.plastic(c.color);
190
+ return across(c, k.G(k.mesh(k.cyl(0.5, 0.42, h * 0.86, seg(c, 12)), material, [0, -h * 0.04, 0]), k.mesh(k.cyl(0.35, 0.35, h * 0.12, seg(c, 12)), k.plastic(shade(c.color, 0.6)), [0, h * 0.44, 0]), k.mesh(k.bx(1, h * 0.06, 0.25), material, [0, -h * 0.47, 0])));
191
+ };
192
+ var roll = (c) => {
193
+ const { k, w, h, d } = c;
194
+ const body = k.G(k.mesh(k.cyl(0.5, 0.5, 1, seg(c, 18)), k.paper(c.color)), k.mesh(k.cyl(0.18, 0.18, 0.98, seg(c, 12)), k.paper(11902328)));
195
+ if (h <= w && h <= d) body.scale.set(w, h, d);
196
+ else if (w <= d) {
197
+ body.rotation.z = PI / 2;
198
+ body.scale.set(h, w, d);
199
+ } else {
200
+ body.rotation.x = PI / 2;
201
+ body.scale.set(w, d, h);
202
+ }
203
+ return k.G(body);
204
+ };
205
+ var shoe = (c) => {
206
+ const { k, w, h, d } = c;
207
+ const upper = k.cloth(c.color);
208
+ return k.G(k.mesh(k.bx(w, h * 0.22, d), k.plastic(2237727), [0, -h * 0.39, 0]), k.mesh(k.bx(w * 0.42, h * 0.78, d * 0.96), upper, [-w * 0.29, h * 0.11, 0]), k.mesh(k.bx(w * 0.5, h * 0.5, d * 0.96), upper, [w * 0.25, -h * 0.03, 0]));
209
+ };
210
+ var tool = alongLong((c, L, a, b) => {
211
+ const { k } = c;
212
+ const radius = Math.max(4e-3, Math.min(a, b) / 2);
213
+ return k.G(k.mesh(k.cyl(radius, radius, L * 0.74, seg(c, 10)), k.wood(9069628), [-L * 0.13, 0, 0], [0, 0, PI / 2]), k.mesh(k.bx(L * 0.26, a, b), k.metal(shade(c.color, 1)), [L * 0.37, 0, 0]));
214
+ });
215
+ var longTool = alongLong((c, L, a, b) => {
216
+ const { k } = c;
217
+ const radius = Math.max(4e-3, Math.min(a, b) / 2) * 0.55;
218
+ return k.G(k.mesh(k.cyl(radius, radius, L * 0.94, seg(c, 10)), k.wood(10122312), [0, 0, 0], [0, 0, PI / 2]), k.mesh(k.bx(L * 0.12, a, b), k.metal(c.color), [L * 0.44, 0, 0]));
219
+ });
220
+ var blade = alongLong((c, L, a, b) => {
221
+ const { k } = c;
222
+ const edge = Math.min(2e-4, b * 0.05);
223
+ const steel = k.extrude([[0, 0], [L * 0.66, 0], [L * 0.68, a * 0.4], [0, a]], Math.max(5e-4, b * 0.4 - 2 * edge), edge);
224
+ return k.G(k.mesh(steel, k.metal(11120048), [-L * 0.18, -a / 2, -b * 0.2 + edge]), k.mesh(k.bx(L * 0.34, a * 0.6, b), k.wood(3877408), [-L * 0.33, 0, 0]));
225
+ });
226
+ var garment = (c) => {
227
+ const { k, w, h, d } = c;
228
+ const material = k.cloth(c.color);
229
+ return k.G(k.mesh(k.bx(w * 0.56, h, d), material), k.mesh(k.bx(w * 0.22, h * 0.42, d * 0.9), material, [-w * 0.39, h * 0.29, 0]), k.mesh(k.bx(w * 0.22, h * 0.42, d * 0.9), material, [w * 0.39, h * 0.29, 0]));
230
+ };
231
+ var backpack = (c) => {
232
+ const { k, w, h, d } = c;
233
+ const material = k.cloth(c.color);
234
+ const dome = new k.T.SphereGeometry(0.5, seg(c, 12), seg(c, 6), 0, PI * 2, 0, PI / 2);
235
+ const strap = k.cloth(shade(c.color, 0.6));
236
+ return k.G(k.mesh(k.bx(w, h * 0.78, d * 0.68), material, [0, -h * 0.11, -d * 0.14]), k.mesh(dome, material, [0, h * 0.28, -d * 0.14], [0, 0, 0], [w, h * 0.44, d * 0.68]), k.mesh(k.bx(w * 0.7, h * 0.4, d * 0.3), k.cloth(shade(c.color, 0.75)), [0, -h * 0.18, d * 0.35]), k.mesh(k.bx(w * 0.1, h * 0.7, d * 0.04), strap, [-w * 0.25, -h * 0.05, -d * 0.48]), k.mesh(k.bx(w * 0.1, h * 0.7, d * 0.04), strap, [w * 0.25, -h * 0.05, -d * 0.48]));
237
+ };
238
+ var jerrycan = (c) => {
239
+ const { k, w, h, d } = c;
240
+ const material = k.plastic(c.color);
241
+ return k.G(k.mesh(k.bx(w, h * 0.86, d), material, [0, -h * 0.07, 0]), k.mesh(k.bx(w * 0.4, h * 0.1, d * 0.5), material, [-w * 0.2, h * 0.45, 0]), k.mesh(k.cyl(d * 0.14, d * 0.18, h * 0.12, seg(c, 10)), k.plastic(14263361), [w * 0.3, h * 0.44, 0]));
242
+ };
243
+ var tank = (c) => {
244
+ const { k, h } = c;
245
+ const r = 0.5;
246
+ const profile = [[0, -h / 2], [r * 0.9, -h / 2], [r, -h * 0.4], [r, h * 0.22], [r * 0.6, h * 0.36], [r * 0.2, h * 0.4], [0, h * 0.4]];
247
+ const metal = k.metal(c.color);
248
+ return across(c, k.G(k.mesh(k.lathe(profile, seg(c, 14)), metal), k.mesh(k.cyl(r * 0.62, r * 0.62, h * 0.05, seg(c, 14)), metal, [0, h * 0.3, 0]), k.mesh(k.cyl(r * 0.18, r * 0.18, h * 0.12, seg(c, 8)), k.metal(12095802), [0, h * 0.44, 0])));
249
+ };
250
+ var book = (c) => {
251
+ const { k, w, h, d } = c;
252
+ const cover = k.paper(c.color);
253
+ return k.G(k.mesh(k.bx(w, h, d * 0.14), cover, [0, 0, d * 0.43]), k.mesh(k.bx(w, h, d * 0.14), cover, [0, 0, -d * 0.43]), k.mesh(k.bx(w * 0.94, h * 0.96, d * 0.72), k.paper(15327692), [w * 0.02, 0, 0]), k.mesh(k.bx(w * 0.08, h, d), k.paper(shade(c.color, 0.8)), [-w * 0.46, 0, 0]));
254
+ };
255
+ var key = alongLong((c, L, a, b) => {
256
+ const { k } = c;
257
+ const metal = k.metal(12095802);
258
+ const ring = a * 0.5;
259
+ const tube2 = Math.min(b * 0.4, ring * 0.2);
260
+ return k.G(k.mesh(new c.k.T.TorusGeometry(ring * 0.8, tube2, 5, seg(c, 10)), k.metal(10132896), [-L / 2 + ring * 0.8 + tube2, 0, 0]), k.mesh(k.bx(L * 0.62, a * 0.28, b), metal, [L * 0.12, 0, 0]), k.mesh(k.bx(L * 0.08, a * 0.22, b), metal, [L * 0.36, -a * 0.2, 0]), k.mesh(k.bx(L * 0.06, a * 0.22, b), metal, [L * 0.2, -a * 0.2, 0]));
261
+ });
262
+ var battery = alongLong((c, L, a, b) => {
263
+ const { k } = c;
264
+ const r = Math.min(a, b) / 2;
265
+ return k.G(k.mesh(k.cyl(r, r, L * 0.94, seg(c, 14)), k.metal(c.color), [0, 0, 0], [0, 0, PI / 2]), k.mesh(k.cyl(r * 0.36, r * 0.36, L * 0.06, seg(c, 8)), k.metal(12172736), [L * 0.47, 0, 0], [0, 0, PI / 2]));
266
+ });
267
+ var plank = alongLong((c, L, a, b) => {
268
+ const { k } = c;
269
+ const nail = k.metal(8225158);
270
+ return k.G(k.mesh(k.bx(L, a, b), k.wood(11043929)), k.mesh(k.cyl(Math.min(a, b) * 0.1, Math.min(a, b) * 0.1, b * 0.98, 6), nail, [-L * 0.4, 0, 0], [PI / 2, 0, 0]), k.mesh(k.cyl(Math.min(a, b) * 0.1, Math.min(a, b) * 0.1, b * 0.98, 6), nail, [L * 0.4, 0, 0], [PI / 2, 0, 0]));
271
+ });
272
+ var device = (c) => {
273
+ const { k, w, h, d } = c;
274
+ const screen = k.M(10469258, "glass", { emissive: 3099170, emissiveIntensity: 0.6, roughness: 0.2 });
275
+ return k.G(k.mesh(k.bx(w, h, d * 0.96), k.plastic(c.color), [0, 0, -d * 0.02]), k.mesh(k.bx(w * 0.7, h * 0.32, d * 0.06), screen, [0, h * 0.22, d * 0.47]), k.mesh(k.cyl(Math.min(w, h) * 0.08, Math.min(w, h) * 0.08, d * 0.1, seg(c, 8)), k.metal(12172736), [w * 0.25, -h * 0.18, d * 0.45], [PI / 2, 0, 0]));
276
+ };
277
+ var DRAWERS = {
278
+ can,
279
+ bag,
280
+ box,
281
+ bottle,
282
+ tool,
283
+ roll,
284
+ dish,
285
+ cloth,
286
+ shoe,
287
+ jar,
288
+ paper,
289
+ fruit,
290
+ cup,
291
+ tube,
292
+ blade,
293
+ long_tool: longTool,
294
+ garment,
295
+ backpack,
296
+ jerrycan,
297
+ tank,
298
+ book,
299
+ key,
300
+ battery,
301
+ plank,
302
+ device
303
+ };
304
+ var isShape = (value) => value !== void 0 && value in DRAWERS;
305
+ function shapeOf(subject) {
306
+ const hint = subject.shape;
307
+ if (!isShape(hint)) return "box";
308
+ const [w, h, d] = subject.size;
309
+ const { L, a, b } = canonicalFrame(subject.size);
310
+ const long = L / Math.max(a, b, 1e-6) >= 4;
311
+ const flat = Math.min(w, h, d) / Math.max(w, h, d) <= 0.2;
312
+ switch (hint) {
313
+ case "tool":
314
+ if (subject.category === "keys") return "key";
315
+ if (subject.category === "electronics") return "device";
316
+ if (long) return subject.category === "melee" && Math.min(a, b) / L <= 0.1 && flat ? "blade" : "long_tool";
317
+ return hint;
318
+ case "cloth":
319
+ return subject.category === "clothing" ? "garment" : hint;
320
+ case "bag":
321
+ return subject.category === "bags" ? "backpack" : hint;
322
+ case "box":
323
+ if (subject.category === "electronics") return "device";
324
+ if (subject.category === "books") return "book";
325
+ if (subject.category === "keys") return "key";
326
+ return hint;
327
+ case "paper":
328
+ return subject.category === "books" ? "book" : hint;
329
+ default:
330
+ return hint;
331
+ }
332
+ }
333
+ function drawShape(k, shape, size, options = {}) {
334
+ const [w, h, d] = size;
335
+ return DRAWERS[shape]({ k, w, h, d, color: options.color ?? DEFAULT_COLOR, detail: options.detail ?? 1 });
336
+ }
337
+
338
+ // src/data/models.json
339
+ var models_default = [
340
+ {
341
+ id: "canned_beans",
342
+ teamId: "feijao_lata",
343
+ teamBox: [
344
+ 0.67,
345
+ 0.57,
346
+ 0.66
347
+ ],
348
+ scale: 0.119403
349
+ },
350
+ {
351
+ id: "water_bottle",
352
+ teamId: "agua_500",
353
+ teamBox: [
354
+ 0.452,
355
+ 1.25,
356
+ 0.452
357
+ ],
358
+ scale: 0.146018
359
+ },
360
+ {
361
+ id: "rice",
362
+ teamId: "arroz",
363
+ teamBox: [
364
+ 0.72,
365
+ 1.055,
366
+ 0.3
367
+ ],
368
+ scale: 0.236967
369
+ },
370
+ {
371
+ id: "military_ration",
372
+ teamId: "racao_militar",
373
+ teamBox: [
374
+ 0.77,
375
+ 1.03,
376
+ 0.235
377
+ ],
378
+ scale: 0.252427
379
+ },
380
+ {
381
+ id: "cachaca",
382
+ teamId: "cachaca",
383
+ teamBox: [
384
+ 0.552,
385
+ 1.31,
386
+ 0.552
387
+ ],
388
+ scale: 0.163043
389
+ },
390
+ {
391
+ id: "first_aid_kit",
392
+ teamId: "kit_ps",
393
+ teamBox: [
394
+ 1.07,
395
+ 0.915,
396
+ 0.41
397
+ ],
398
+ scale: 0.233645
399
+ },
400
+ {
401
+ id: "bandage",
402
+ teamId: "atadura",
403
+ teamBox: [
404
+ 0.9851,
405
+ 0.7575,
406
+ 0.43
407
+ ],
408
+ scale: 0.102528
409
+ },
410
+ {
411
+ id: "amoxicillin",
412
+ teamId: "amoxicilina",
413
+ teamBox: [
414
+ 0.8859,
415
+ 0.47,
416
+ 0.607
417
+ ],
418
+ scale: 0.11288
419
+ },
420
+ {
421
+ id: "crowbar",
422
+ teamId: "pe_cabra",
423
+ teamBox: [
424
+ 0.428,
425
+ 1.7087,
426
+ 0.11
427
+ ],
428
+ scale: 0.43893
429
+ },
430
+ {
431
+ id: "hammer",
432
+ teamId: "martelo",
433
+ teamBox: [
434
+ 0.6487,
435
+ 1.3477,
436
+ 0.19
437
+ ],
438
+ scale: 0.244862
439
+ },
440
+ {
441
+ id: "machete",
442
+ teamId: "facao",
443
+ teamBox: [
444
+ 1.8759,
445
+ 0.2906,
446
+ 0.12
447
+ ],
448
+ scale: 0.293193
449
+ },
450
+ {
451
+ id: "axe",
452
+ teamId: "machado",
453
+ teamBox: [
454
+ 0.5952,
455
+ 1.6693,
456
+ 0.14
457
+ ],
458
+ scale: 0.419337
459
+ },
460
+ {
461
+ id: "nail_bat",
462
+ teamId: "taco_pregos",
463
+ teamBox: [
464
+ 0.4384,
465
+ 1.69,
466
+ 0.4352
467
+ ],
468
+ scale: 0.508876
469
+ },
470
+ {
471
+ id: "hiking_backpack_40l",
472
+ teamId: "mochila_trilha",
473
+ teamBox: [
474
+ 0.8,
475
+ 1.07,
476
+ 0.62
477
+ ],
478
+ scale: 0.514019
479
+ },
480
+ {
481
+ id: "flashlight",
482
+ teamId: "lanterna",
483
+ teamBox: [
484
+ 0.38,
485
+ 1.08,
486
+ 0.38
487
+ ],
488
+ scale: 0.148148
489
+ },
490
+ {
491
+ id: "portable_radio",
492
+ teamId: "radio_portatil",
493
+ teamBox: [
494
+ 1.1041,
495
+ 1.4821,
496
+ 0.35
497
+ ],
498
+ scale: 0.199257
499
+ },
500
+ {
501
+ id: "house_key",
502
+ teamId: "chave_casa",
503
+ teamBox: [
504
+ 1.5433,
505
+ 0.8305,
506
+ 0.08
507
+ ],
508
+ scale: 0.055077
509
+ },
510
+ {
511
+ id: "survivor_diary",
512
+ teamId: "diario",
513
+ teamBox: [
514
+ 0.8,
515
+ 1.04,
516
+ 0.27
517
+ ],
518
+ scale: 0.201923
519
+ },
520
+ {
521
+ id: "motorcycle_helmet",
522
+ teamId: "capacete_moto",
523
+ teamBox: [
524
+ 1.11,
525
+ 1.0825,
526
+ 1.17
527
+ ],
528
+ scale: 0.234234
529
+ },
530
+ {
531
+ id: "lpg_cylinder_13kg",
532
+ teamId: "botijao",
533
+ teamBox: [
534
+ 0.9257,
535
+ 1.285,
536
+ 0.94
537
+ ],
538
+ scale: 0.381323
539
+ },
540
+ {
541
+ id: "fuel_jerrycan",
542
+ teamId: "galao",
543
+ teamBox: [
544
+ 0.8788,
545
+ 1.342,
546
+ 0.44
547
+ ],
548
+ scale: 0.350224
549
+ },
550
+ {
551
+ id: "walkie_talkies",
552
+ teamId: "walkie",
553
+ teamBox: [
554
+ 0.465,
555
+ 1.47,
556
+ 0.22
557
+ ],
558
+ scale: 0.14966
559
+ },
560
+ {
561
+ id: "batteries",
562
+ teamId: "pilha_aa",
563
+ teamBox: [
564
+ 0.744,
565
+ 1.005,
566
+ 0.344
567
+ ],
568
+ scale: 0.050249
569
+ },
570
+ {
571
+ id: "wooden_plank",
572
+ teamId: "tabua",
573
+ teamBox: [
574
+ 1.5203,
575
+ 0.4741,
576
+ 0.5377
577
+ ],
578
+ scale: 0.526212
579
+ }
580
+ ];
581
+
582
+ // src/models/canned-beans.ts
583
+ var cannedBeans = ({ mesh, cyl, torus, G, metal, paper: paper2, food }) => {
584
+ const m = metal(9277590);
585
+ return G(mesh(cyl(0.32, 0.32, 0.52), m), mesh(cyl(0.326, 0.326, 0.34, 14), food(10172964)), mesh(cyl(0.33, 0.33, 0.06), paper2(14861162), [0, 0.02, 0]), mesh(torus(0.31, 0.025), m, [0, 0.26, 0], [Math.PI / 2, 0, 0]), mesh(torus(0.31, 0.025), m, [0, -0.26, 0], [Math.PI / 2, 0, 0]));
586
+ };
587
+
588
+ // src/models/water-bottle.ts
589
+ var waterBottle = ({ mesh, cyl, lathe, G, plastic, paper: paper2, glass }) => G(mesh(lathe([[0, -0.6], [0.2, -0.6], [0.22, -0.5], [0.22, 0.2], [0.19, 0.32], [0.09, 0.45], [0.08, 0.55], [0, 0.55]]), glass(9421788, 0.55)), mesh(cyl(0.085, 0.085, 0.1, 12), plastic(2907530), [0, 0.6, 0]), mesh(cyl(0.225, 0.225, 0.22, 16), paper2(15330543), [0, -0.15, 0]), mesh(cyl(0.226, 0.226, 0.05, 16), plastic(3831976), [0, -0.12, 0]));
590
+
591
+ // src/models/rice.ts
592
+ var rice = ({ mesh, bx, G, paper: paper2, food }) => G(mesh(bx(0.7, 0.95, 0.28), paper2(15129792)), mesh(bx(0.72, 0.12, 0.1), paper2(14010792), [0, 0.52, 0]), mesh(bx(0.5, 0.3, 0.02), food(11883039), [0, 0.05, 0.15]), mesh(bx(0.3, 0.1, 0.02), paper2(4160069), [0, -0.25, 0.15]));
593
+
594
+ // src/models/military-ration.ts
595
+ var militaryRation = ({ mesh, bx, G, plastic, paper: paper2 }) => G(mesh(bx(0.75, 1, 0.22), plastic(5923142)), mesh(bx(0.77, 0.1, 0.23), plastic(4672823), [0, 0.48, 0]), mesh(bx(0.5, 0.14, 0.01), paper2(2237727), [0, 0.18, 0.115]), mesh(bx(0.36, 0.06, 0.01), paper2(2237727), [0, -0.02, 0.115]));
596
+
597
+ // src/models/cachaca.ts
598
+ var cachaca = ({ mesh, cyl, lathe, G, wood, paper: paper2, food, glass }) => G(mesh(lathe([[0, -0.6], [0.25, -0.6], [0.27, -0.52], [0.27, 0.1], [0.2, 0.25], [0.09, 0.35], [0.09, 0.58], [0, 0.58]]), glass(12749354, 0.78)), mesh(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0]), mesh(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
599
+
600
+ // src/models/first-aid-kit.ts
601
+ var firstAidKit = ({ mesh, bx, torus, G, plastic }) => {
602
+ const r = plastic(11680300);
603
+ return G(mesh(bx(1.05, 0.72, 0.38), plastic(15131608)), mesh(bx(1.07, 0.06, 0.4), plastic(12433578), [0, 0, 0]), mesh(bx(0.36, 0.1, 0.02), r, [0, 0.1, 0.2]), mesh(bx(0.1, 0.36, 0.02), r, [0, 0.1, 0.2]), mesh(torus(0.16, 0.035, Math.PI), plastic(5591884), [0, 0.36, 0]));
604
+ };
605
+
606
+ // src/models/bandage.ts
607
+ var bandage = ({ mesh, cyl, bx, G, cloth: cloth2 }) => {
608
+ const c = cloth2(15525330);
609
+ return G(mesh(cyl(0.34, 0.34, 0.38, 18), c, [0, 0, 0], [Math.PI / 2, 0, 0]), mesh(cyl(0.1, 0.1, 0.4, 12), cloth2(9078135), [0, 0, 0], [Math.PI / 2, 0, 0]), mesh(bx(0.36, 0.02, 0.36), c, [0.2, -0.33, 0.05], [0, 0, -0.25]), mesh(bx(0.3, 0.02, 0.36), c, [0.5, -0.4, 0.05], [0, 0, -0.05]));
610
+ };
611
+
612
+ // src/models/amoxicillin.ts
613
+ var amoxicillin = ({ T, mesh, bx, G, metal, plastic, paper: paper2 }) => G(mesh(bx(0.8, 0.42, 0.24), paper2(15789798)), mesh(bx(0.81, 0.1, 0.25), plastic(5141127), [0, 0.1, 0]), mesh(bx(0.6, 0.04, 0.3), metal(12172736), [0.15, -0.24, 0.25], [0, 0.3, 0]), ...[0, 1, 2, 3].map((k) => mesh(new T.SphereGeometry(0.05, 10, 8), plastic(14263361), [-0.05 + k * 0.14, -0.21, 0.3], [0, 0, 0], [1, 0.6, 1])));
614
+
615
+ // src/models/crowbar.ts
616
+ var crowbar = ({ T, mesh, cyl, G, metal }) => {
617
+ const c = new T.CatmullRomCurve3([[0, -0.8, 0], [0, 0.45, 0], [0.02, 0.62, 0], [0.12, 0.72, 0], [0.26, 0.7, 0], [0.32, 0.6, 0]].map((p) => new T.Vector3(...p)));
618
+ return G(mesh(new T.TubeGeometry(c, 30, 0.055, 8), metal(10168863)), mesh(cyl(0.02, 0.055, 0.14, 8), metal(3816510), [0, -0.86, 0]));
619
+ };
620
+
621
+ // src/models/hammer.ts
622
+ var hammer = ({ T, mesh, cyl, bx, G, metal, wood, plastic }) => G(mesh(cyl(0.05, 0.065, 1.1, 10), wood(9069628), [0, -0.15, 0]), mesh(cyl(0.07, 0.07, 0.3, 10), plastic(2237727), [0, -0.6, 0]), mesh(bx(0.28, 0.15, 0.15), metal(5593437), [0.05, 0.45, 0]), mesh(cyl(0.08, 0.08, 0.1, 12), metal(5593437), [0.22, 0.45, 0], [0, 0, Math.PI / 2]), mesh(new T.ConeGeometry(0.06, 0.3, 6), metal(5593437), [-0.22, 0.5, 0.035], [0, 0, 1.9]), mesh(new T.ConeGeometry(0.06, 0.3, 6), metal(5593437), [-0.22, 0.5, -0.035], [0, 0, 1.9]));
623
+
624
+ // src/models/machete.ts
625
+ var machete = ({ mesh, cyl, bx, extrude, G, metal, wood }) => G(mesh(extrude([[0, 0], [1.25, 0.05], [1.4, 0.14], [1.3, 0.26], [0, 0.2]], 0.03), metal(11120048), [-0.55, 0, 0]), mesh(bx(0.45, 0.2, 0.1), wood(3877408), [-0.78, 0.1, 0.015]), ...[-0.9, -0.68].map((x) => mesh(cyl(0.025, 0.025, 0.12, 8), metal(12095802), [x, 0.1, 0.015], [Math.PI / 2, 0, 0])));
626
+
627
+ // src/models/axe.ts
628
+ var axe = ({ M, mesh, cyl, bx, extrude, G, metal, wood }) => G(mesh(cyl(0.05, 0.06, 1.5, 10), wood(10122312), [0, -0.1, 0]), mesh(extrude([[0, 0], [0.42, -0.16], [0.5, 0.05], [0.48, 0.3], [0.4, 0.42], [0, 0.22]], 0.07), metal(7106677), [0.02, 0.38, -0.05]), mesh(bx(0.12, 0.26, 0.14), metal(4869456), [0, 0.5, 0]), mesh(bx(0.1, 0.2, 0.08), M(10168863, "plastic"), [0, 0.55, 0]));
629
+
630
+ // src/models/nail-bat.ts
631
+ var nailBat = ({ mesh, cyl, lathe, G, metal, wood }) => {
632
+ const g = G(mesh(lathe([[0, -0.85], [0.05, -0.85], [0.05, -0.8], [0.035, -0.75], [0.04, -0.2], [0.09, 0.3], [0.11, 0.7], [0.09, 0.82], [0, 0.84]], 14), wood(11569754)));
633
+ const nm = metal(8225158);
634
+ for (let k = 0; k < 9; k++) {
635
+ const a = k * 2.4, y = 0.25 + k % 5 * 0.12;
636
+ const n = mesh(cyl(0.012, 0.012, 0.2, 6), nm, [Math.cos(a) * 0.12, y, Math.sin(a) * 0.12], [0, 0, 0]);
637
+ n.lookAt(Math.cos(a) * 2, y, Math.sin(a) * 2);
638
+ n.rotateX(Math.PI / 2);
639
+ g.add(n);
640
+ }
641
+ return g;
642
+ };
643
+
644
+ // src/models/hiking-backpack-40l.ts
645
+ var hikingBackpack40L = ({ T, mesh, bx, G, cloth: cloth2, plastic }) => {
646
+ const c = cloth2(5204794), d = cloth2(3557162);
647
+ return G(mesh(bx(0.78, 0.9, 0.42), c), mesh(new T.SphereGeometry(0.4, 14, 8, 0, Math.PI * 2, 0, Math.PI / 2), c, [0, 0.44, 0], [0, 0, 0], [0.98, 0.45, 0.53]), mesh(bx(0.56, 0.4, 0.16), d, [0, -0.15, 0.27]), mesh(bx(0.8, 0.12, 0.46), d, [0, 0.2, 0]), mesh(bx(0.06, 0.9, 0.06), cloth2(2237727), [-0.22, 0, -0.24]), mesh(bx(0.06, 0.9, 0.06), cloth2(2237727), [0.22, 0, -0.24]), mesh(bx(0.1, 0.08, 0.04), plastic(14263361), [0, 0.2, 0.24]));
648
+ };
649
+
650
+ // src/models/flashlight.ts
651
+ var flashlight = ({ M, mesh, cyl, bx, torus, G, metal, plastic }) => G(mesh(cyl(0.11, 0.11, 0.8, 14), metal(2763822), [0, -0.15, 0]), mesh(cyl(0.19, 0.12, 0.28, 16), metal(3816768), [0, 0.37, 0]), mesh(cyl(0.17, 0.17, 0.02, 16), M(16773568, "glass", { emissive: 16769162, emissiveIntensity: 0.9 }), [0, 0.52, 0]), mesh(bx(0.06, 0.1, 0.05), plastic(11883039), [0, 0.05, 0.11]), ...[-0.4, -0.3, -0.2].map((y) => mesh(torus(0.112, 0.012), plastic(1776669), [0, y, 0], [Math.PI / 2, 0, 0])));
652
+
653
+ // src/models/portable-radio.ts
654
+ var portableRadio = ({ M, mesh, cyl, bx, torus, G, metal, plastic }) => {
655
+ const g = G(mesh(bx(1.05, 0.62, 0.3), plastic(3816757)), mesh(cyl(0.2, 0.2, 0.02, 18), plastic(1974043), [-0.24, -0.04, 0.16], [Math.PI / 2, 0, 0]), mesh(bx(0.36, 0.14, 0.02), M(14263361, "glass", { emissive: 6965776, emissiveIntensity: 0.6 }), [0.26, 0.1, 0.16]), mesh(cyl(0.06, 0.06, 0.06, 12), plastic(9276032), [0.18, -0.15, 0.17], [Math.PI / 2, 0, 0]), mesh(cyl(0.06, 0.06, 0.06, 12), plastic(9276032), [0.36, -0.15, 0.17], [Math.PI / 2, 0, 0]), mesh(torus(0.24, 0.03, Math.PI), plastic(2237727), [0, 0.3, 0]));
656
+ const a = mesh(cyl(0.012, 0.02, 0.95, 6), metal(12172736), [0.4, 0.72, 0], [0, 0, -0.35]);
657
+ g.add(a);
658
+ return g;
659
+ };
660
+
661
+ // src/models/house-key.ts
662
+ var houseKey = ({ mesh, bx, torus, extrude, G, metal, plastic }) => {
663
+ const b = metal(12095802);
664
+ return G(mesh(torus(0.22, 0.04), metal(10132896), [-0.55, 0.1, 0]), mesh(extrude([[0, -0.2], [0.25, -0.22], [0.3, -0.1], [0.3, 0.1], [0.25, 0.22], [0, 0.2]], 0.05), b, [-0.38, 0.02, -0.025]), mesh(extrude([[0, 0], [0.75, 0], [0.75, -0.08], [0.66, -0.08], [0.66, -0.14], [0.56, -0.08], [0.46, -0.14], [0.4, -0.08], [0, -0.08]], 0.035), b, [-0.1, 0.06, -0.017]), mesh(bx(0.24, 0.34, 0.03), plastic(11883039), [-0.72, -0.28, 0], [0, 0, 0.25]));
665
+ };
666
+
667
+ // src/models/survivor-diary.ts
668
+ var survivorDiary = ({ mesh, bx, G, cloth: cloth2, paper: paper2 }) => G(mesh(bx(0.78, 1.02, 0.05), paper2(7028522), [0, 0, 0.1]), mesh(bx(0.78, 1.02, 0.05), paper2(7028522), [0, 0, -0.1]), mesh(bx(0.74, 0.98, 0.16), paper2(15327692), [0.01, 0, 0]), mesh(bx(0.06, 1.02, 0.25), paper2(5911330), [-0.38, 0, 0]), mesh(bx(0.03, 1.04, 0.27), cloth2(2237727), [0.25, 0, 0]), mesh(bx(0.18, 0.3, 2e-3), paper2(14010792), [0.05, 0.2, 0.126], [0, 0, 0.08]));
669
+
670
+ // src/models/motorcycle-helmet.ts
671
+ var motorcycleHelmet = ({ T, M, mesh, cyl, bx, G, plastic }) => G(mesh(new T.SphereGeometry(0.55, 18, 12), plastic(2830138), [0, 0, 0], [0, 0, 0], [1, 0.95, 1.08]), mesh(new T.SphereGeometry(0.56, 18, 10, -0.9, 1.8, 1.05, 0.75), M(1316636, "glass", { roughness: 0.15, metalness: 0.4 }), [0, 0.02, 0.02], [0, 0, 0], [1, 0.95, 1.08]), mesh(bx(0.2, 0.12, 0.02), plastic(14710591), [0.42, 0.2, 0.22], [0, 0.95, 0]), mesh(cyl(0.5, 0.55, 0.12, 18), plastic(1908776), [0, -0.5, 0]));
672
+
673
+ // src/models/lpg-cylinder-13kg.ts
674
+ var lpgCylinder13Kg = ({ mesh, cyl, bx, lathe, torus, G, metal }) => G(mesh(lathe([[0, -0.6], [0.38, -0.6], [0.42, -0.55], [0.45, -0.3], [0.45, 0.15], [0.4, 0.32], [0.28, 0.42], [0.12, 0.46], [0, 0.46]], 18), metal(3497862)), mesh(cyl(0.47, 0.47, 0.1, 18), metal(2705514), [0, -0.62, 0]), mesh(torus(0.24, 0.035), metal(2705514), [0, 0.58, 0], [Math.PI / 2, 0, 0]), mesh(cyl(0.07, 0.07, 0.14, 10), metal(12095802), [0, 0.5, 0]), mesh(bx(0.04, 0.2, 0.04), metal(2705514), [0.22, 0.5, 0]), mesh(bx(0.04, 0.2, 0.04), metal(2705514), [-0.22, 0.5, 0]));
675
+
676
+ // src/models/fuel-jerrycan.ts
677
+ var fuelJerrycan = ({ mesh, cyl, bx, G, plastic }) => G(mesh(bx(0.85, 1, 0.42), plastic(10695210)), mesh(bx(0.6, 0.1, 0.3), plastic(8267296), [0, 0.45, 0]), mesh(bx(0.36, 0.2, 0.1), plastic(10695210), [-0.12, 0.62, 0]), mesh(cyl(0.05, 0.07, 0.35, 10), plastic(14263361), [0.3, 0.66, 0], [0, 0, -0.6]), mesh(bx(0.6, 0.5, 0.02), plastic(9054754), [0, -0.1, 0.22]));
678
+
679
+ // src/models/walkie-talkies.ts
680
+ var walkieTalkies = ({ M, mesh, cyl, bx, G, plastic }) => G(mesh(bx(0.42, 0.9, 0.2), plastic(2764071)), mesh(bx(0.34, 0.3, 0.02), plastic(1447956), [0, -0.12, 0.11]), mesh(bx(0.24, 0.12, 0.02), M(10469258, "glass", { emissive: 3099170, emissiveIntensity: 0.7 }), [0, 0.22, 0.11]), mesh(cyl(0.035, 0.045, 0.6, 8), plastic(1776666), [0.12, 0.72, 0]), mesh(cyl(0.04, 0.04, 0.08, 10), plastic(11883039), [-0.12, 0.48, 0]), mesh(bx(0.05, 0.2, 0.1), plastic(3816757), [-0.23, 0.05, 0]));
681
+
682
+ // src/models/batteries.ts
683
+ var batteries = ({ mesh, cyl, G, metal }) => {
684
+ const g = G();
685
+ [-0.2, 0.2].forEach((x, k) => g.add(mesh(cyl(0.17, 0.17, 0.95, 16), metal(k ? 11883039 : 2763822), [x, 0, 0]), mesh(cyl(0.172, 0.172, 0.3, 16), metal(13214247), [x, 0.33, 0]), mesh(cyl(0.06, 0.06, 0.06, 10), metal(12172736), [x, 0.5, 0])));
686
+ return g;
687
+ };
688
+
689
+ // src/models/wooden-plank.ts
690
+ var woodenPlank = ({ mesh, cyl, bx, G, metal, wood }) => {
691
+ const g = G();
692
+ [11043929, 9333829, 11833960].forEach((c, k) => g.add(mesh(bx(1.5, 0.07, 0.26), wood(c), [0, -0.15 + k * 0.12, (k - 1) * 0.05], [0, (k - 1) * 0.12, 0])));
693
+ const nm = metal(8225158);
694
+ [-0.5, -0.3, 0.4].forEach((x, k) => g.add(mesh(cyl(0.012, 0.012, 0.18, 6), nm, [x, 0.18 + k * 0.01, 0.2], [0, 0, (k - 1) * 0.3])));
695
+ return g;
696
+ };
697
+
698
+ // src/models/shipped.ts
699
+ var BUILDERS = {
700
+ canned_beans: cannedBeans,
701
+ water_bottle: waterBottle,
702
+ rice,
703
+ military_ration: militaryRation,
704
+ cachaca,
705
+ first_aid_kit: firstAidKit,
706
+ bandage,
707
+ amoxicillin,
708
+ crowbar,
709
+ hammer,
710
+ machete,
711
+ axe,
712
+ nail_bat: nailBat,
713
+ hiking_backpack_40l: hikingBackpack40L,
714
+ flashlight,
715
+ portable_radio: portableRadio,
716
+ house_key: houseKey,
717
+ survivor_diary: survivorDiary,
718
+ motorcycle_helmet: motorcycleHelmet,
719
+ lpg_cylinder_13kg: lpgCylinder13Kg,
720
+ fuel_jerrycan: fuelJerrycan,
721
+ walkie_talkies: walkieTalkies,
722
+ batteries,
723
+ wooden_plank: woodenPlank
724
+ };
725
+ var SHIPPED_MODELS = models_default.map((entry) => {
726
+ const builder = BUILDERS[entry.id];
727
+ if (!builder) throw new Error(`game-items-three: no builder for the model "${entry.id}"`);
728
+ return { entry, builder };
729
+ });
730
+
731
+ // src/models/registry.ts
732
+ var entries = new Map(SHIPPED_MODELS.map(({ entry }) => [entry.id, entry]));
733
+ var builders = new Map(SHIPPED_MODELS.map(({ entry, builder }) => [entry.id, builder]));
734
+ function registerItemModel(entry, builder) {
735
+ entries.set(entry.id, entry);
736
+ builders.set(entry.id, builder);
737
+ }
738
+ function unregisterItemModel(id) {
739
+ entries.delete(id);
740
+ builders.delete(id);
741
+ }
742
+ var itemModelEntry = (id) => entries.get(id);
743
+ var itemModelBuilder = (id) => builders.get(id);
744
+ var hasItemModel = (id) => builders.has(id);
745
+ var listItemModels = () => [...entries.keys()];
746
+
747
+ // src/build-item.ts
748
+ function restOnBase(T, inner) {
749
+ const box2 = new T.Box3().setFromObject(inner);
750
+ const centre = box2.getCenter(new T.Vector3());
751
+ inner.position.set(-centre.x, -box2.min.y, -centre.z);
752
+ }
753
+ function buildItem(T, item, options = {}) {
754
+ const kit = options.kit ?? createItemKit(T);
755
+ const first = kit.materials.length;
756
+ const root = new T.Group();
757
+ root.name = item.id;
758
+ const entry = item.model === void 0 ? void 0 : itemModelEntry(item.model);
759
+ const builder = item.model === void 0 ? void 0 : itemModelBuilder(item.model);
760
+ let kind = "shape";
761
+ let shape;
762
+ if (entry && builder && !options.simple) {
763
+ kind = "model";
764
+ const inner = builder(kit);
765
+ inner.name = entry.teamId;
766
+ root.scale.setScalar(entry.scale);
767
+ root.add(inner);
768
+ restOnBase(T, inner);
769
+ } else {
770
+ shape = shapeOf(item);
771
+ const inner = drawShape(kit, shape, item.size, { ...item.color === void 0 ? {} : { color: item.color }, ...options.detail === void 0 ? {} : { detail: options.detail } });
772
+ inner.name = shape;
773
+ root.add(inner);
774
+ restOnBase(T, inner);
775
+ }
776
+ const materials = kit.materials.slice(first);
777
+ if (options.wear) setWear(materials, options.wear);
778
+ root.userData.wear = options.wear ?? 0;
779
+ return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
780
+ }
781
+
782
+ // src/merge.ts
783
+ import { mergeByMaterial } from "@adatechnology/game-props3d";
784
+ function mergeItem(T, model) {
785
+ model.root.updateMatrixWorld(true);
786
+ const merged = new T.Group();
787
+ merged.name = model.root.name;
788
+ merged.scale.copy(model.root.scale);
789
+ merged.userData = { ...model.root.userData };
790
+ merged.add(...mergeByMaterial(T, model.root, model.root));
791
+ return { ...model, root: merged };
792
+ }
793
+
794
+ // src/budgets.ts
795
+ var ITEM_BUDGETS = {
796
+ drawCalls: 30,
797
+ small: { triangles: 400, cullDistance: 30 },
798
+ large: { triangles: 1200, cullDistance: 60 },
799
+ lod1Ratio: 0.5,
800
+ largeFrom: 0.6
801
+ };
802
+ var sizeClassOf = (size, budgets = ITEM_BUDGETS) => Math.max(...size) >= budgets.largeFrom ? "large" : "small";
803
+ var triangleBudgetOf = (size, budgets = ITEM_BUDGETS) => budgets[sizeClassOf(size, budgets)].triangles;
804
+ var cullDistanceOf = (size, budgets = ITEM_BUDGETS) => budgets[sizeClassOf(size, budgets)].cullDistance;
805
+ function countTriangles(root) {
806
+ let total = 0;
807
+ root.traverse((object) => {
808
+ const mesh = object;
809
+ if (!mesh.isMesh || !mesh.visible) return;
810
+ const geometry = mesh.geometry;
811
+ total += geometry.index ? geometry.index.count / 3 : geometry.getAttribute("position").count / 3;
812
+ });
813
+ return Math.round(total);
814
+ }
815
+ function countDrawCalls(root) {
816
+ let total = 0;
817
+ root.traverse((object) => {
818
+ const mesh = object;
819
+ if (!mesh.isMesh || !mesh.visible) return;
820
+ total += Array.isArray(mesh.material) ? Math.max(1, mesh.geometry.groups.length) : 1;
821
+ });
822
+ return total;
823
+ }
824
+ function checkBudget(root, size, budgets = ITEM_BUDGETS) {
825
+ const triangles = countTriangles(root);
826
+ const limit = triangleBudgetOf(size, budgets);
827
+ return { triangles, limit, drawCalls: countDrawCalls(root), sizeClass: sizeClassOf(size, budgets), ok: triangles <= limit };
828
+ }
829
+
830
+ // src/lod.ts
831
+ function buildItemLod(T, item, options = {}) {
832
+ const budgets = options.budgets ?? ITEM_BUDGETS;
833
+ const cullDistance = cullDistanceOf(item.size, budgets);
834
+ const lod1Distance = options.lod1Distance ?? cullDistance / 3;
835
+ const joined = (model) => options.merge === false ? model : mergeItem(T, model);
836
+ const wear = options.wear === void 0 ? {} : { wear: options.wear };
837
+ const near = joined(buildItem(T, item, { ...wear, kit: createItemKit(T) }));
838
+ const far = joined(buildItem(T, item, { ...wear, simple: true, detail: budgets.lod1Ratio, kit: createItemKit(T) }));
839
+ const root = new T.LOD();
840
+ root.name = item.id;
841
+ root.addLevel(near.root, 0);
842
+ root.addLevel(far.root, lod1Distance);
843
+ root.addLevel(new T.Group(), cullDistance);
844
+ root.userData.wear = options.wear ?? 0;
845
+ return { root, near, far, cullDistance, lod1Distance };
846
+ }
847
+
848
+ // src/models/deviations.ts
849
+ var MODEL_BUDGET_DEVIATIONS = {
850
+ canned_beans: 744,
851
+ amoxicillin: 596,
852
+ flashlight: 1060,
853
+ portable_radio: 504,
854
+ house_key: 472,
855
+ motorcycle_helmet: 840,
856
+ lpg_cylinder_13kg: 712
857
+ };
858
+
859
+ // src/thumbnails.ts
860
+ var hex = (color) => `#${(color & 16777215).toString(16).padStart(6, "0")}`;
861
+ var DEFAULT_COLOR2 = 9080724;
862
+ var STEEL = "#9aa0a6";
863
+ var BRASS = "#b8913a";
864
+ var WOOD = "#8a643c";
865
+ var PAPER = "#ece4d0";
866
+ var ART = {
867
+ can: (c, d, l) => `<rect x="19" y="13" width="26" height="42" rx="3" fill="${c}"/><rect x="19" y="24" width="26" height="18" fill="${l}"/><rect x="18" y="11" width="28" height="5" rx="2" fill="${STEEL}"/><rect x="18" y="52" width="28" height="4" rx="2" fill="${STEEL}"/>`,
868
+ bag: (c, d, l) => `<path d="M16 55 L13 25 Q32 14 51 25 L48 55 Z" fill="${c}"/><rect x="14" y="17" width="36" height="7" rx="2" fill="${d}"/><rect x="22" y="32" width="20" height="14" rx="2" fill="${l}"/>`,
869
+ box: (c, d, l) => `<rect x="11" y="18" width="42" height="37" fill="${c}"/><rect x="11" y="18" width="42" height="8" fill="${d}"/><rect x="22" y="33" width="20" height="13" fill="${l}"/>`,
870
+ bottle: (c, d) => `<rect x="28" y="7" width="8" height="8" rx="2" fill="${d}"/><path d="M23 56 V30 Q23 23 28 18 H36 Q41 23 41 30 V56 Z" fill="${c}" fill-opacity=".75"/><rect x="23" y="34" width="18" height="14" fill="${PAPER}" fill-opacity=".9"/>`,
871
+ tool: (c) => `<rect x="29" y="20" width="6" height="37" rx="2" fill="${WOOD}"/><rect x="17" y="9" width="30" height="13" rx="2" fill="${c === "#808080" ? STEEL : c}"/>`,
872
+ roll: (c, d) => `<circle cx="32" cy="34" r="22" fill="${c}"/><circle cx="32" cy="34" r="8" fill="${d}"/>`,
873
+ dish: (c, d, l) => `<ellipse cx="32" cy="44" rx="27" ry="9" fill="${c}"/><ellipse cx="32" cy="42" rx="19" ry="5" fill="${l}"/><ellipse cx="32" cy="42" rx="9" ry="2" fill="${d}"/>`,
874
+ cloth: (c, d) => `<rect x="9" y="34" width="46" height="21" rx="3" fill="${c}"/><rect x="13" y="22" width="38" height="14" rx="3" fill="${d}"/>`,
875
+ shoe: (c) => `<path d="M9 48 V27 H26 L30 36 H47 Q56 38 56 48 Z" fill="${c}"/><rect x="7" y="48" width="50" height="7" rx="2" fill="#22251f"/>`,
876
+ jar: (c) => `<rect x="17" y="22" width="30" height="33" rx="6" fill="${c}" fill-opacity=".7"/><rect x="16" y="13" width="32" height="10" rx="2" fill="${BRASS}"/>`,
877
+ paper: (c, d) => `<rect x="14" y="10" width="36" height="46" fill="${PAPER}"/><rect x="19" y="18" width="26" height="3" fill="${d}"/><rect x="19" y="26" width="26" height="3" fill="${d}"/><rect x="19" y="34" width="18" height="3" fill="${d}"/><rect x="19" y="42" width="22" height="3" fill="${c}"/>`,
878
+ fruit: (c) => `<circle cx="32" cy="38" r="18" fill="${c}"/><rect x="31" y="13" width="3" height="9" fill="#5a3a22"/><path d="M34 16 Q44 10 48 16 Q40 20 34 16 Z" fill="#4f7a3a"/>`,
879
+ cup: (c, d) => `<path d="M18 16 H46 L41 56 H23 Z" fill="${c}"/><rect x="18" y="14" width="28" height="4" fill="${d}"/>`,
880
+ tube: (c, d) => `<path d="M23 15 H41 V48 L37 56 H27 L23 48 Z" fill="${c}"/><rect x="25" y="7" width="14" height="9" rx="2" fill="${d}"/>`,
881
+ blade: () => `<rect x="5" y="33" width="18" height="8" rx="2" fill="#3b2a20"/><path d="M23 33 H55 L60 38 L23 42 Z" fill="#a9adb0"/><rect x="22" y="31" width="3" height="12" fill="${BRASS}"/>`,
882
+ long_tool: (c) => `<rect x="5" y="30" width="50" height="5" rx="2" fill="${WOOD}"/><rect x="47" y="21" width="12" height="22" rx="2" fill="${c === "#808080" ? STEEL : c}"/>`,
883
+ garment: (c, d) => `<path d="M20 12 L6 24 L13 33 L20 28 V56 H44 V28 L51 33 L58 24 L44 12 Q32 22 20 12 Z" fill="${c}"/><path d="M20 12 Q32 22 44 12 Q32 28 20 12 Z" fill="${d}"/>`,
884
+ backpack: (c, d) => `<path d="M24 18 Q32 4 40 18" fill="none" stroke="${d}" stroke-width="3"/><rect x="15" y="16" width="34" height="40" rx="9" fill="${c}"/><rect x="21" y="35" width="22" height="16" rx="3" fill="${d}"/>`,
885
+ jerrycan: (c, d) => `<rect x="13" y="17" width="38" height="39" rx="3" fill="${c}"/><rect x="17" y="8" width="16" height="10" rx="2" fill="${d}"/><rect x="39" y="9" width="9" height="9" fill="#d9a441"/>`,
886
+ tank: (c, d) => `<rect x="21" y="18" width="22" height="38" rx="10" fill="${c}"/><rect x="28" y="9" width="8" height="10" fill="${BRASS}"/><rect x="20" y="43" width="24" height="3" fill="${d}"/>`,
887
+ book: (c, d) => `<rect x="14" y="9" width="37" height="47" fill="${c}"/><rect x="14" y="9" width="6" height="47" fill="${d}"/><rect x="23" y="13" width="25" height="39" fill="#e9e1cc"/>`,
888
+ key: () => `<circle cx="16" cy="32" r="9" fill="none" stroke="#9a9da0" stroke-width="5"/><rect x="24" y="30" width="33" height="5" fill="${BRASS}"/><rect x="45" y="35" width="4" height="7" fill="${BRASS}"/><rect x="51" y="35" width="3" height="5" fill="${BRASS}"/>`,
889
+ battery: (c, d, l) => `<rect x="23" y="14" width="18" height="42" rx="2" fill="${c}"/><rect x="28" y="9" width="8" height="6" fill="#b9bdc0"/><rect x="23" y="26" width="18" height="16" fill="${l}"/><rect x="23" y="47" width="18" height="9" fill="${d}"/>`,
890
+ plank: (c) => `<rect x="5" y="26" width="54" height="13" fill="${WOOD}"/><rect x="5" y="30" width="54" height="2" fill="${c === "#808080" ? "#6b4a2c" : c}" fill-opacity=".5"/><circle cx="12" cy="32" r="2" fill="#7d8186"/><circle cx="52" cy="32" r="2" fill="#7d8186"/>`,
891
+ device: (c) => `<rect x="15" y="7" width="34" height="50" rx="5" fill="${c}"/><rect x="20" y="13" width="24" height="18" rx="2" fill="#9fbf8a"/><circle cx="26" cy="43" r="4" fill="#b9bdc0"/><rect x="35" y="40" width="9" height="6" rx="1" fill="#b9bdc0"/>`
892
+ };
893
+ function shapeThumbnailSvg(shape, color = DEFAULT_COLOR2) {
894
+ const art = ART[shape];
895
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">${art(hex(color), hex(shade(color, 0.7)), hex(shade(color, 1.3)))}</svg>`;
896
+ }
897
+ function itemThumbnail(item) {
898
+ if (item.model !== void 0 && hasItemModel(item.model)) return { render: true };
899
+ return { svg: shapeThumbnailSvg(shapeOf(item), item.color) };
900
+ }
901
+ var itemThumbnailSvg = (item) => shapeThumbnailSvg(shapeOf(item), item.color);
902
+ function thumbnailPatch(items) {
903
+ const patch = {};
904
+ for (const item of items) patch[item.id] = { thumbnail: itemThumbnail(item) };
905
+ return patch;
906
+ }
907
+ var FIT_MARGIN = 1.15;
908
+ var FIELD_OF_VIEW = 30;
909
+ function composeThumbnailScene(T, item, options = {}) {
910
+ const { yaw = -0.6, pitch = 0.45, distance = 1, wear = 0 } = options;
911
+ const scene = new T.Scene();
912
+ const model = buildItem(T, item);
913
+ if (wear) setWear(model.root, wear);
914
+ scene.add(model.root);
915
+ scene.add(new T.HemisphereLight(14673646, 3814440, 0.85));
916
+ const sun = new T.DirectionalLight(16769727, 1.1);
917
+ sun.position.set(2.5, 3.5, 2.5);
918
+ scene.add(sun);
919
+ const rim = new T.DirectionalLight(9417430, 0.45);
920
+ rim.position.set(-3, 1, -2.5);
921
+ scene.add(rim);
922
+ const box2 = new T.Box3().setFromObject(model.root);
923
+ const centre = box2.getCenter(new T.Vector3());
924
+ const radius = box2.getSize(new T.Vector3()).length() / 2;
925
+ const camera = new T.PerspectiveCamera(FIELD_OF_VIEW, 1, radius / 50, radius * 40);
926
+ const fit = radius * FIT_MARGIN / Math.sin(FIELD_OF_VIEW * Math.PI / 360);
927
+ const away = fit * distance;
928
+ camera.position.set(centre.x + away * Math.cos(pitch) * Math.sin(yaw), centre.y + away * Math.sin(pitch), centre.z + away * Math.cos(pitch) * Math.cos(yaw));
929
+ camera.lookAt(centre);
930
+ camera.updateMatrixWorld(true);
931
+ return { scene, camera };
932
+ }
933
+ function renderItemThumbnail(T, renderer, item, options = {}) {
934
+ const { scene, camera } = composeThumbnailScene(T, item, options);
935
+ const size = options.size ?? 128;
936
+ renderer.setSize(size, size);
937
+ renderer.setClearColor?.(0, 0);
938
+ renderer.render(scene, camera);
939
+ return renderer.domElement.toDataURL("image/png");
940
+ }
941
+
942
+ // src/area.ts
943
+ import { rng } from "@adatechnology/game-interactables";
944
+ import { canCraft, craft, createFindsProvider, freshness, ITEM_CATEGORIES } from "@adatechnology/game-items";
945
+
946
+ // src/area-layout.ts
947
+ var SHELF_HEIGHT = 0.9;
948
+ var ROW_LIMIT = 5;
949
+ var GAP = 0.06;
950
+ var EDGE = 0.12;
951
+ var COLUMNS = 4;
952
+ var COLUMN_PITCH = 6.2;
953
+ var ROW_GAP = 1.5;
954
+ var FIRST_Z = -11;
955
+ var SHELF_PREFIX = "shelf_";
956
+ var ITEM_PREFIX = "item_";
957
+ var SHOWCASE_PREFIX = "showcase_";
958
+ var rowsOf = (items) => {
959
+ const rows = [];
960
+ for (const item of items) {
961
+ const [w, , d] = item.size;
962
+ let row = rows[rows.length - 1];
963
+ if (!row || row.items.length > 0 && row.width + w + GAP > ROW_LIMIT) {
964
+ row = { items: [], width: 0, depth: 0 };
965
+ rows.push(row);
966
+ }
967
+ row.items.push(item);
968
+ row.width += w + GAP;
969
+ row.depth = Math.max(row.depth, d);
970
+ }
971
+ return rows;
972
+ };
973
+ function shelvesLayout(items, categories) {
974
+ const plans = categories.flatMap((category) => {
975
+ const own = items.filter((item) => item.category === category);
976
+ if (own.length === 0) return [];
977
+ const rows = rowsOf(own);
978
+ const length = Math.max(...rows.map((row) => row.width - GAP)) + 2 * EDGE;
979
+ const depth = rows.reduce((sum, row) => sum + row.depth + GAP, 0) - GAP + 2 * EDGE;
980
+ return [{ category, rows, length, depth }];
981
+ });
982
+ const result = [];
983
+ let z = FIRST_Z;
984
+ for (let start = 0; start < plans.length; start += COLUMNS) {
985
+ const line = plans.slice(start, start + COLUMNS);
986
+ const lineDepth = Math.max(...line.map((plan) => plan.depth));
987
+ line.forEach((plan, column) => {
988
+ const x = (column - (COLUMNS - 1) / 2) * COLUMN_PITCH;
989
+ const centre = [x, 0, z + plan.depth / 2];
990
+ const placed = [];
991
+ let rowZ = -plan.depth / 2 + EDGE;
992
+ for (const row of plan.rows) {
993
+ let cursor = -plan.length / 2 + EDGE;
994
+ for (const item of row.items) {
995
+ const [w, , d] = item.size;
996
+ placed.push({ item: `${ITEM_PREFIX}${item.id}`, position: [centre[0] + cursor + w / 2, SHELF_HEIGHT + 0.01, centre[2] + rowZ + d / 2], asleep: true });
997
+ cursor += w + GAP;
998
+ }
999
+ rowZ += row.depth + GAP;
1000
+ }
1001
+ result.push({ category: plan.category, id: `${SHELF_PREFIX}${plan.category}`, length: plan.length, depth: plan.depth, position: centre, items: placed });
1002
+ });
1003
+ z += lineDepth + ROW_GAP;
1004
+ }
1005
+ return result;
1006
+ }
1007
+
1008
+ // src/area.ts
1009
+ var SHOWCASE_STAND_ID = "stand_models";
1010
+ var RULER_ID = "ruler_155";
1011
+ var HUMAN_ID = "human_180";
1012
+ var FINDS_TABLE_ID = "finds_table";
1013
+ var RULER_HEIGHT = 1.55;
1014
+ var HUMAN_HEIGHT = 1.8;
1015
+ var STAND = { at: [0, 0, 9.5], radius: 2.1, height: 0.8, ring: 1.35 };
1016
+ var RULER_AT = [-6.5, 0, 9];
1017
+ var HUMAN_AT = [-5.4, 0, 9];
1018
+ var TABLE = { at: [7, 0, 9.5], length: 3.2, depth: 1, height: 0.8 };
1019
+ var WEARS = ["new", "used", "spoiled"];
1020
+ var WEAR_STAGE = { new: 0, used: 1, spoiled: 2 };
1021
+ var SPIN = 0.6;
1022
+ var MAX_COPIES = 5;
1023
+ var CATEGORY_ALL = "all";
1024
+ var BOX_COLOR = 5227511;
1025
+ var HIGHLIGHT = 6961664;
1026
+ var TEXT = {
1027
+ en: {
1028
+ group: "Items",
1029
+ name: "Items area",
1030
+ shelf: "Shelf of {category}",
1031
+ stand: "Showcase stand of the 3D models",
1032
+ ruler: "Ruler 1.55 m",
1033
+ human: "Human figure 1.80 m",
1034
+ table: "Finds table",
1035
+ control: {
1036
+ category: "Category",
1037
+ wear: "Wear",
1038
+ age_days: "Age",
1039
+ storage: "Storage",
1040
+ powered: "Storage has power",
1041
+ place: "Place",
1042
+ container: "Container",
1043
+ state: "State of the place",
1044
+ day: "Outbreak day",
1045
+ seed: "Seed",
1046
+ roll_finds: "Roll finds",
1047
+ recipe: "Recipe",
1048
+ craft: "Craft"
1049
+ },
1050
+ hint: {
1051
+ age_days: "Days the food has been kept: it spoils by its shelf life, faster or slower by the storage",
1052
+ powered: "A fridge or freezer only keeps the food while it has power",
1053
+ day: "Days since the outbreak: 0 is the normal world",
1054
+ roll_finds: "Draws what the container holds and lays the stacks on the finds table",
1055
+ craft: "Makes the recipe with exactly what it asks for and lays the result on the finds table"
1056
+ },
1057
+ all: "All",
1058
+ wears: { new: "New", used: "Used", spoiled: "Spoiled" },
1059
+ states: { normal: "Normal", fortified: "Fortified", abandoned: "Abandoned" },
1060
+ storages: {},
1061
+ layers: { labels: "Labels", boxes: "Size boxes", simple_shapes: "Highlight simple shapes" },
1062
+ metrics: { items: "Items shown", draw_calls: "Draw calls", triangles: "Triangles", modeled: "With a team model", finds: "Stacks on the table" },
1063
+ rolled: "Finds in {container} of {place} ({state}, day {day}): {stacks} stacks",
1064
+ rolledNothing: "{container} of {place} holds nothing",
1065
+ crafted: "Crafted {recipe}: {output} ({seconds} s)",
1066
+ craftMissing: "Cannot craft {recipe}: missing {missing}",
1067
+ craftEffect: "effect {effect}",
1068
+ scenarios: {
1069
+ real_size: "Every item is drawn at its real size",
1070
+ models: "The 24 models of the team are shipped and draw their items",
1071
+ budgets: "Models and shapes stay within the triangle and draw call budgets",
1072
+ ruler: "The ruler is 1.55 m and the human figure 1.80 m",
1073
+ finds: "The finds roll is the same for the same seed",
1074
+ freshness: "Food spoils with age and the fridge slows it down",
1075
+ craft: "A recipe made with what it asks for gives its output"
1076
+ }
1077
+ },
1078
+ "pt-BR": {
1079
+ group: "Itens",
1080
+ name: "\xC1rea de itens",
1081
+ shelf: "Prateleira de {category}",
1082
+ stand: "Pedestal dos modelos 3D",
1083
+ ruler: "R\xE9gua de 1,55 m",
1084
+ human: "Silhueta humana de 1,80 m",
1085
+ table: "Mesa de achados",
1086
+ control: {
1087
+ category: "Categoria",
1088
+ wear: "Desgaste",
1089
+ age_days: "Idade",
1090
+ storage: "Armazenamento",
1091
+ powered: "Armazenamento com energia",
1092
+ place: "Lugar",
1093
+ container: "Recipiente",
1094
+ state: "Estado do lugar",
1095
+ day: "Dia do surto",
1096
+ seed: "Semente",
1097
+ roll_finds: "Sortear achados",
1098
+ recipe: "Receita",
1099
+ craft: "Fabricar"
1100
+ },
1101
+ hint: {
1102
+ age_days: "Dias que a comida ficou guardada: estraga pela validade, mais r\xE1pido ou mais devagar conforme onde est\xE1",
1103
+ powered: "Geladeira e freezer s\xF3 conservam enquanto t\xEAm energia",
1104
+ day: "Dias desde o surto: 0 \xE9 o mundo normal",
1105
+ roll_finds: "Sorteia o que o recipiente guarda e p\xF5e as pilhas na mesa de achados",
1106
+ craft: "Faz a receita com exatamente o que ela pede e p\xF5e o resultado na mesa de achados"
1107
+ },
1108
+ all: "Todas",
1109
+ wears: { new: "Novo", used: "Usado", spoiled: "Estragado" },
1110
+ states: { normal: "Normal", fortified: "Fortificado", abandoned: "Abandonado" },
1111
+ storages: {},
1112
+ layers: { labels: "R\xF3tulos", boxes: "Caixas de tamanho", simple_shapes: "Destacar formas simples" },
1113
+ metrics: { items: "Itens \xE0 mostra", draw_calls: "Chamadas de desenho", triangles: "Tri\xE2ngulos", modeled: "Com modelo do time", finds: "Pilhas na mesa" },
1114
+ rolled: "Achados em {container} de {place} ({state}, dia {day}): {stacks} pilhas",
1115
+ rolledNothing: "{container} de {place} n\xE3o tem nada",
1116
+ crafted: "Fabricou {recipe}: {output} ({seconds} s)",
1117
+ craftMissing: "N\xE3o d\xE1 para fabricar {recipe}: falta {missing}",
1118
+ craftEffect: "efeito {effect}",
1119
+ scenarios: {
1120
+ real_size: "Todo item \xE9 desenhado no tamanho real",
1121
+ models: "Os 24 modelos do time v\xEAm no kit e desenham os seus itens",
1122
+ budgets: "Modelos e formas ficam dentro do or\xE7amento de tri\xE2ngulos e de chamadas de desenho",
1123
+ ruler: "A r\xE9gua tem 1,55 m e a silhueta humana 1,80 m",
1124
+ finds: "O sorteio de achados \xE9 o mesmo para a mesma semente",
1125
+ freshness: "A comida estraga com a idade e a geladeira atrasa isso",
1126
+ craft: "Uma receita feita com o que ela pede d\xE1 o que promete"
1127
+ }
1128
+ }
1129
+ };
1130
+ var textOf = (locale) => TEXT[locale] ?? TEXT.en;
1131
+ var fill = (template, vars) => template.replace(/\{(\w+)\}/g, (_, key2) => String(vars[key2] ?? ""));
1132
+ var humanize = (id) => id.replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase());
1133
+ var stageOf = (catalog, item, base, ageDays, storage, powered) => {
1134
+ if (item.shelfLife === void 0 || ageDays <= 0) return base;
1135
+ const state = freshness(item, { ageDays: 0, since: 0, storage, storedAt: 0 }, ageDays, { powered, rules: catalog.rules });
1136
+ const aged = state === "fresh" ? 0 : state === "stale" ? 1 : 2;
1137
+ return Math.max(base, aged);
1138
+ };
1139
+ var sizeOfModel = (id) => {
1140
+ const entry = itemModelEntry(id);
1141
+ return entry ? [entry.teamBox[0] * entry.scale, entry.teamBox[1] * entry.scale, entry.teamBox[2] * entry.scale] : void 0;
1142
+ };
1143
+ function createItemsTestModule(options) {
1144
+ const { catalog } = options;
1145
+ const locale = options.locale ?? "pt-BR";
1146
+ const text = textOf(locale);
1147
+ const label = (key2, fallback) => {
1148
+ const found = options.translate?.(key2, locale);
1149
+ return found !== void 0 && found !== key2 ? found : fallback;
1150
+ };
1151
+ const nameOf = (item) => label(item.name ?? `item.${item.id}`, label(`item.${item.id}`, humanize(item.id)));
1152
+ const categoryName = (id) => label(`category.${id}`, humanize(id));
1153
+ const provider = options.provider ?? createFindsProvider(catalog);
1154
+ const items = catalog.list();
1155
+ const categories = [...ITEM_CATEGORIES.map((c) => c.id), ...[...catalog.categories.keys()].filter((id) => !ITEM_CATEGORIES.some((c) => c.id === id))].filter((id) => items.some((item) => item.category === id));
1156
+ const shelves = shelvesLayout(items, categories);
1157
+ const showcase = listItemModels().map((id) => ({ id, size: sizeOfModel(id), def: catalog.has(id) ? catalog.get(id) : void 0 }));
1158
+ const state = {
1159
+ category: CATEGORY_ALL,
1160
+ wear: WEARS.includes(options.wear) ? options.wear : "new",
1161
+ ageDays: 0,
1162
+ storage: catalog.rules.storage.ambient ? "ambient" : Object.keys(catalog.rules.storage)[0] ?? "ambient",
1163
+ powered: true,
1164
+ place: catalog.places.has("house") ? "house" : [...catalog.places.keys()][0] ?? "house",
1165
+ container: catalog.containers.has("fridge") ? "fridge" : [...catalog.containers.keys()][0] ?? "fridge",
1166
+ state: "normal",
1167
+ day: 0,
1168
+ seed: options.seed ?? 1,
1169
+ recipe: [...catalog.recipes.keys()][0] ?? ""
1170
+ };
1171
+ const entries2 = [];
1172
+ let host = null;
1173
+ let latestTHREE = null;
1174
+ let stack = [];
1175
+ let findsGroup = null;
1176
+ let boxes = [];
1177
+ const layers = { labels: true, boxes: false, simple_shapes: false };
1178
+ const baseStage = () => WEAR_STAGE[state.wear] ?? 0;
1179
+ const stageFor = (def) => def ? stageOf(catalog, def, baseStage(), state.ageDays, state.storage, state.powered) : baseStage();
1180
+ const drawable = (def) => def;
1181
+ const register = (entry) => {
1182
+ entries2.push({ ...entry, triangles: countTriangles(entry.root), drawCalls: countDrawCalls(entry.root) });
1183
+ };
1184
+ const shelfItem = (plan) => ({
1185
+ id: plan.id,
1186
+ name: fill(text.shelf, { category: categoryName(plan.category) }),
1187
+ movable: false,
1188
+ build: (THREE) => {
1189
+ const T = THREE;
1190
+ const group = new T.Group();
1191
+ const wood = new T.MeshStandardMaterial({ color: 9071173, roughness: 0.85 });
1192
+ const frame = new T.MeshStandardMaterial({ color: 4869458, roughness: 0.7, metalness: 0.3 });
1193
+ const top = new T.Mesh(new T.BoxGeometry(plan.length, 0.04, plan.depth), wood);
1194
+ top.position.y = SHELF_HEIGHT - 0.02;
1195
+ group.add(top);
1196
+ for (const sx of [-1, 1]) for (const sz of [-1, 1]) {
1197
+ const leg = new T.Mesh(new T.BoxGeometry(0.05, SHELF_HEIGHT - 0.04, 0.05), frame);
1198
+ leg.position.set(sx * (plan.length - 0.1) / 2, (SHELF_HEIGHT - 0.04) / 2, sz * (plan.depth - 0.1) / 2);
1199
+ group.add(leg);
1200
+ }
1201
+ group.userData.category = plan.category;
1202
+ group.userData.shelf = true;
1203
+ shelfRoots.push(group);
1204
+ return group;
1205
+ }
1206
+ });
1207
+ const shelfRoots = [];
1208
+ const itemOf = (def) => ({
1209
+ id: `${ITEM_PREFIX}${def.id}`,
1210
+ name: nameOf(def),
1211
+ movable: true,
1212
+ thumbnail: itemThumbnailSvg(drawable(def)),
1213
+ build: (THREE) => {
1214
+ const T = THREE;
1215
+ latestTHREE = T;
1216
+ const model = buildItem(T, drawable(def), { wear: stageFor(def) });
1217
+ register({ id: `${ITEM_PREFIX}${def.id}`, itemId: def.id, group: "shelf", category: def.category, def, kind: model.kind, size: def.size, root: model.root });
1218
+ return model.root;
1219
+ }
1220
+ });
1221
+ const showcaseItem = (model) => ({
1222
+ id: `${SHOWCASE_PREFIX}${model.id}`,
1223
+ name: model.def ? nameOf(model.def) : label(`item.${model.id}`, humanize(model.id)),
1224
+ movable: false,
1225
+ build: (THREE) => {
1226
+ const T = THREE;
1227
+ const subject = { id: model.id, category: model.def?.category ?? "", size: model.size, model: model.id };
1228
+ const built = buildItem(T, subject, { wear: stageFor(model.def) });
1229
+ register({ id: `${SHOWCASE_PREFIX}${model.id}`, itemId: model.id, group: "showcase", category: model.def?.category ?? "", def: model.def, kind: built.kind, size: model.size, root: built.root });
1230
+ return built.root;
1231
+ }
1232
+ });
1233
+ const standItem = () => ({
1234
+ id: SHOWCASE_STAND_ID,
1235
+ name: text.stand,
1236
+ movable: false,
1237
+ build: (THREE) => {
1238
+ const T = THREE;
1239
+ const group = new T.Group();
1240
+ const base = new T.Mesh(new T.CylinderGeometry(STAND.radius, STAND.radius + 0.1, STAND.height - 0.04, 40), new T.MeshStandardMaterial({ color: 3816772, roughness: 0.8 }));
1241
+ base.position.y = (STAND.height - 0.04) / 2;
1242
+ const top = new T.Mesh(new T.CylinderGeometry(STAND.radius, STAND.radius, 0.04, 40), new T.MeshStandardMaterial({ color: 13617848, roughness: 0.6 }));
1243
+ top.position.y = STAND.height - 0.02;
1244
+ group.add(base, top);
1245
+ return group;
1246
+ }
1247
+ });
1248
+ const rulerItem = () => ({
1249
+ id: RULER_ID,
1250
+ name: text.ruler,
1251
+ movable: false,
1252
+ build: (THREE) => {
1253
+ const T = THREE;
1254
+ const group = new T.Group();
1255
+ const post = new T.Mesh(new T.BoxGeometry(0.06, RULER_HEIGHT, 0.02), new T.MeshStandardMaterial({ color: 15262408, roughness: 0.7 }));
1256
+ post.position.y = RULER_HEIGHT / 2;
1257
+ group.add(post);
1258
+ const tick = new T.MeshStandardMaterial({ color: 1776411 });
1259
+ for (let cm = 0; cm <= 155; cm += 5) {
1260
+ const long = cm % 10 === 0;
1261
+ const mark = new T.Mesh(new T.BoxGeometry(long ? 0.05 : 0.03, 4e-3, 0.024), tick);
1262
+ mark.position.set(0.03 + (long ? 0.025 : 0.015), cm / 100, 0);
1263
+ group.add(mark);
1264
+ }
1265
+ const base = new T.Mesh(new T.BoxGeometry(0.3, 0.03, 0.3), new T.MeshStandardMaterial({ color: 5593180 }));
1266
+ base.position.y = 0.015;
1267
+ group.add(base);
1268
+ group.userData.height = RULER_HEIGHT;
1269
+ return group;
1270
+ }
1271
+ });
1272
+ const humanItem = () => ({
1273
+ id: HUMAN_ID,
1274
+ name: text.human,
1275
+ movable: false,
1276
+ build: (THREE) => {
1277
+ const T = THREE;
1278
+ const group = new T.Group();
1279
+ const skin = new T.MeshStandardMaterial({ color: 10134445, roughness: 0.9 });
1280
+ const part = (geometry, x, y) => {
1281
+ const mesh = new T.Mesh(geometry, skin);
1282
+ mesh.position.set(x, y, 0);
1283
+ group.add(mesh);
1284
+ };
1285
+ for (const x of [-0.09, 0.09]) part(new T.CylinderGeometry(0.075, 0.06, 0.88, 10), x, 0.44);
1286
+ part(new T.BoxGeometry(0.4, 0.62, 0.22), 0, 0.88 + 0.31);
1287
+ for (const x of [-0.26, 0.26]) part(new T.CylinderGeometry(0.045, 0.04, 0.6, 8), x, 1.2);
1288
+ part(new T.CylinderGeometry(0.05, 0.055, 0.07, 8), 0, 1.535);
1289
+ part(new T.SphereGeometry(0.115, 14, 10), 0, 1.685);
1290
+ group.userData.height = HUMAN_HEIGHT;
1291
+ return group;
1292
+ }
1293
+ });
1294
+ const tableItem = () => ({
1295
+ id: FINDS_TABLE_ID,
1296
+ name: text.table,
1297
+ movable: false,
1298
+ build: (THREE) => {
1299
+ const T = THREE;
1300
+ const group = new T.Group();
1301
+ const top = new T.Mesh(new T.BoxGeometry(TABLE.length, 0.05, TABLE.depth), new T.MeshStandardMaterial({ color: 8022613, roughness: 0.8 }));
1302
+ top.position.y = TABLE.height - 0.025;
1303
+ group.add(top);
1304
+ const frame = new T.MeshStandardMaterial({ color: 4013892 });
1305
+ for (const sx of [-1, 1]) for (const sz of [-1, 1]) {
1306
+ const leg = new T.Mesh(new T.BoxGeometry(0.06, TABLE.height - 0.05, 0.06), frame);
1307
+ leg.position.set(sx * (TABLE.length - 0.12) / 2, (TABLE.height - 0.05) / 2, sz * (TABLE.depth - 0.12) / 2);
1308
+ group.add(leg);
1309
+ }
1310
+ return group;
1311
+ }
1312
+ });
1313
+ const liveEntries = () => entries2.filter((entry) => entry.root.parent !== null);
1314
+ const visibleEntries = () => liveEntries().filter((entry) => entry.root.visible);
1315
+ const applyCategory = () => {
1316
+ for (const entry of entries2) if (entry.group === "shelf") entry.root.visible = state.category === CATEGORY_ALL || entry.category === state.category;
1317
+ for (const shelf of shelfRoots) shelf.visible = state.category === CATEGORY_ALL || shelf.userData.category === state.category;
1318
+ };
1319
+ const applyWear = () => {
1320
+ for (const entry of entries2) setWear(entry.root, stageFor(entry.def));
1321
+ };
1322
+ const applyHighlight = () => {
1323
+ for (const entry of entries2) {
1324
+ entry.root.traverse((object) => {
1325
+ const mesh = object;
1326
+ if (!mesh.isMesh) return;
1327
+ for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
1328
+ const standard = material;
1329
+ if (standard.emissive) standard.emissive.setHex(layers.simple_shapes && entry.kind === "shape" ? HIGHLIGHT : 0);
1330
+ }
1331
+ });
1332
+ }
1333
+ };
1334
+ const clearBoxes = () => {
1335
+ for (const box2 of boxes) {
1336
+ box2.parent?.remove(box2);
1337
+ }
1338
+ boxes = [];
1339
+ };
1340
+ const applyBoxes = () => {
1341
+ clearBoxes();
1342
+ if (!layers.boxes || !host || !latestTHREE) return;
1343
+ const T = latestTHREE;
1344
+ const scene = host.scene;
1345
+ for (const entry of liveEntries()) {
1346
+ if (!entry.root.visible) continue;
1347
+ const box2 = new T.Box3().setFromObject(entry.root);
1348
+ if (box2.isEmpty()) continue;
1349
+ const helper = new T.Box3Helper(box2, BOX_COLOR);
1350
+ scene.add(helper);
1351
+ boxes.push(helper);
1352
+ }
1353
+ };
1354
+ const clearFinds = () => {
1355
+ if (findsGroup) {
1356
+ findsGroup.parent?.remove(findsGroup);
1357
+ findsGroup = null;
1358
+ }
1359
+ };
1360
+ const labelSprite = (T, content) => {
1361
+ if (typeof document === "undefined") return null;
1362
+ const canvas = document.createElement("canvas");
1363
+ const context = canvas.getContext("2d");
1364
+ if (!context) return null;
1365
+ canvas.width = 256;
1366
+ canvas.height = 64;
1367
+ context.fillStyle = "rgba(15,17,20,0.78)";
1368
+ context.fillRect(0, 0, 256, 64);
1369
+ context.fillStyle = "#ffffff";
1370
+ context.font = "24px sans-serif";
1371
+ context.textAlign = "center";
1372
+ context.textBaseline = "middle";
1373
+ context.fillText(content.length > 22 ? `${content.slice(0, 21)}\u2026` : content, 128, 32);
1374
+ const texture = new T.CanvasTexture(canvas);
1375
+ const sprite = new T.Sprite(new T.SpriteMaterial({ map: texture, depthTest: false, transparent: true }));
1376
+ sprite.scale.set(0.5, 0.125, 1);
1377
+ sprite.renderOrder = 10;
1378
+ return sprite;
1379
+ };
1380
+ const showFinds = () => {
1381
+ clearFinds();
1382
+ if (!host || !latestTHREE) return;
1383
+ const T = latestTHREE;
1384
+ const group = new T.Group();
1385
+ group.position.set(TABLE.at[0], TABLE.height + 5e-3, TABLE.at[2]);
1386
+ let cursor = -TABLE.length / 2 + 0.1;
1387
+ const scale = 1;
1388
+ for (const found of stack) {
1389
+ const def = catalog.get(found.itemId);
1390
+ const copies = Math.min(found.count, MAX_COPIES);
1391
+ const start = cursor;
1392
+ for (let i = 0; i < copies; i++) {
1393
+ const model = buildItem(T, drawable(def), { wear: stageFor(def) });
1394
+ model.root.position.set(cursor + def.size[0] * scale / 2, 0, 0);
1395
+ group.add(model.root);
1396
+ cursor += def.size[0] + 0.03;
1397
+ }
1398
+ const sprite = labelSprite(T, `${nameOf(def)} x${found.count}`);
1399
+ if (sprite) {
1400
+ sprite.position.set((start + cursor) / 2, Math.max(def.size[1], 0.1) + 0.12, 0);
1401
+ sprite.visible = layers.labels;
1402
+ sprite.userData.label = true;
1403
+ group.add(sprite);
1404
+ }
1405
+ cursor += 0.1;
1406
+ }
1407
+ ;
1408
+ host.scene.add(group);
1409
+ findsGroup = group;
1410
+ };
1411
+ const applyLabels = () => {
1412
+ findsGroup?.traverse((object) => {
1413
+ if (object.userData.label) object.visible = layers.labels;
1414
+ });
1415
+ };
1416
+ const rollFinds = () => {
1417
+ const query = { place: state.place, container: state.container, state: state.state, rng: rng(state.seed), day: state.day };
1418
+ stack = [...provider.findsFor(query) ?? []];
1419
+ showFinds();
1420
+ const container = label(`container.${state.container}`, humanize(state.container));
1421
+ const place = label(`place.${state.place}`, humanize(state.place));
1422
+ host?.log(stack.length > 0 ? fill(text.rolled, { container, place, state: text.states[state.state] ?? state.state, day: state.day, stacks: stack.length }) : fill(text.rolledNothing, { container, place }), { place: state.place, container: state.container, seed: state.seed });
1423
+ return stack;
1424
+ };
1425
+ const kitFor = (recipe) => {
1426
+ const inventory = /* @__PURE__ */ new Map();
1427
+ const world = /* @__PURE__ */ new Set();
1428
+ const add = (id, count) => void inventory.set(id, (inventory.get(id) ?? 0) + count);
1429
+ for (const input of recipe.inputs) {
1430
+ if (input.world) for (const w of input.world) world.add(w);
1431
+ else if (input.item !== void 0) add(input.item, input.qty ?? 1);
1432
+ else if (input.any?.[0] !== void 0) add(input.any[0], input.qty ?? 1);
1433
+ else if (input.match) {
1434
+ const pick = catalog.list().find((item) => (input.match?.category === void 0 || item.category === input.match.category) && (input.match?.cookable === void 0 || item.cookable === input.match.cookable));
1435
+ if (pick) add(pick.id, input.qty ?? 1);
1436
+ }
1437
+ }
1438
+ for (const tool2 of recipe.tools) {
1439
+ if (tool2.anyOf[0] !== void 0) add(tool2.anyOf[0], 1);
1440
+ for (const w of tool2.world ?? []) world.add(w);
1441
+ }
1442
+ return { inventory, world };
1443
+ };
1444
+ const craftSelected = () => {
1445
+ const recipe = catalog.recipes.get(state.recipe);
1446
+ if (!recipe) return { ok: false, missing: [state.recipe] };
1447
+ const { inventory, world } = kitFor(recipe);
1448
+ const fluids = {};
1449
+ for (const input of recipe.inputs) if (input.fluid !== void 0) fluids[input.fluid] = (fluids[input.fluid] ?? 0) + (input.liters ?? 1);
1450
+ const context = { catalog, inventory, world, fluids };
1451
+ const name = label(`recipe.${recipe.id}`, humanize(recipe.id));
1452
+ const check = canCraft(recipe, context);
1453
+ if (!check.isPossible) {
1454
+ host?.log(fill(text.craftMissing, { recipe: name, missing: check.missing.join(", ") }), { recipe: recipe.id });
1455
+ return { ok: false, missing: check.missing };
1456
+ }
1457
+ const result = craft(recipe, context, rng(state.seed));
1458
+ if (!result.isCrafted) return { ok: false, missing: result.missing };
1459
+ if ("item" in result.output) {
1460
+ stack = [{ itemId: result.output.item, count: result.output.count }];
1461
+ showFinds();
1462
+ }
1463
+ const output = "item" in result.output ? `${nameOf(catalog.get(result.output.item))} x${result.output.count}` : fill(text.craftEffect, { effect: result.output.effect });
1464
+ host?.log(fill(text.crafted, { recipe: name, output, seconds: result.seconds }), { recipe: recipe.id });
1465
+ return { ok: true, missing: [] };
1466
+ };
1467
+ const areaPlacements = () => {
1468
+ const placements = [];
1469
+ for (const shelf of shelves) {
1470
+ placements.push({ item: shelf.id, position: shelf.position, surface: { halfSize: [shelf.length / 2, shelf.depth / 2], height: SHELF_HEIGHT } }, ...shelf.items);
1471
+ }
1472
+ placements.push({ item: SHOWCASE_STAND_ID, position: STAND.at, surface: { halfSize: [STAND.radius, STAND.radius], height: STAND.height } });
1473
+ showcase.forEach((model, index) => {
1474
+ const angle = index / showcase.length * Math.PI * 2;
1475
+ placements.push({ item: `${SHOWCASE_PREFIX}${model.id}`, position: [STAND.at[0] + Math.cos(angle) * STAND.ring, STAND.height + 0.01, STAND.at[2] + Math.sin(angle) * STAND.ring], asleep: true });
1476
+ });
1477
+ placements.push({ item: RULER_ID, position: RULER_AT }, { item: HUMAN_ID, position: HUMAN_AT });
1478
+ placements.push({ item: FINDS_TABLE_ID, position: TABLE.at, surface: { halfSize: [TABLE.length / 2, TABLE.depth / 2], height: TABLE.height } });
1479
+ return placements;
1480
+ };
1481
+ const onChange = (change) => {
1482
+ if (change.kind === "layer") {
1483
+ if (change.id === "labels" || change.id === "boxes" || change.id === "simple_shapes") layers[change.id] = change.on;
1484
+ if (change.id === "labels") applyLabels();
1485
+ else if (change.id === "boxes") applyBoxes();
1486
+ else if (change.id === "simple_shapes") applyHighlight();
1487
+ return;
1488
+ }
1489
+ if (change.kind === "action") {
1490
+ if (change.id === "roll_finds") rollFinds();
1491
+ else if (change.id === "craft") craftSelected();
1492
+ return;
1493
+ }
1494
+ const value = change.value;
1495
+ switch (change.id) {
1496
+ case "category":
1497
+ state.category = String(value);
1498
+ applyCategory();
1499
+ if (layers.boxes) applyBoxes();
1500
+ break;
1501
+ case "wear":
1502
+ state.wear = String(value);
1503
+ applyWear();
1504
+ break;
1505
+ case "age_days":
1506
+ state.ageDays = Number(value);
1507
+ applyWear();
1508
+ break;
1509
+ case "storage":
1510
+ state.storage = String(value);
1511
+ applyWear();
1512
+ break;
1513
+ case "powered":
1514
+ state.powered = Boolean(value);
1515
+ applyWear();
1516
+ break;
1517
+ case "place":
1518
+ state.place = String(value);
1519
+ break;
1520
+ case "container":
1521
+ state.container = String(value);
1522
+ break;
1523
+ case "state":
1524
+ state.state = String(value);
1525
+ break;
1526
+ case "day":
1527
+ state.day = Number(value);
1528
+ break;
1529
+ case "seed":
1530
+ state.seed = Number(value);
1531
+ break;
1532
+ case "recipe":
1533
+ state.recipe = String(value);
1534
+ break;
1535
+ }
1536
+ };
1537
+ const choice = (id, values, def) => ({ kind: "choice", id, name: text.control[id], options: values, default: def });
1538
+ const module = {
1539
+ id: "items_area",
1540
+ name: text.name,
1541
+ group: text.group,
1542
+ state,
1543
+ placed: () => entries2.map((entry) => ({ id: entry.id, itemId: entry.itemId, group: entry.group, kind: entry.kind, size: entry.size, root: entry.root })),
1544
+ finds: () => stack,
1545
+ shelves: () => shelves,
1546
+ rollFinds,
1547
+ craftSelected,
1548
+ selectRecipe: (id) => {
1549
+ state.recipe = id;
1550
+ },
1551
+ measure: (root) => {
1552
+ if (!latestTHREE) return null;
1553
+ const size = new latestTHREE.Box3().setFromObject(root).getSize(new latestTHREE.Vector3());
1554
+ return [size.x, size.y, size.z];
1555
+ },
1556
+ items: () => [
1557
+ ...items.map(itemOf),
1558
+ ...shelves.map(shelfItem),
1559
+ standItem(),
1560
+ ...showcase.map(showcaseItem),
1561
+ rulerItem(),
1562
+ humanItem(),
1563
+ tableItem()
1564
+ ],
1565
+ area: () => ({ placements: areaPlacements(), view: { eye: [0, 5.5, 16], target: [0, 0.8, 0] } }),
1566
+ controls: () => [
1567
+ choice("category", [{ id: CATEGORY_ALL, name: text.all }, ...categories.map((id) => ({ id, name: categoryName(id) }))], CATEGORY_ALL),
1568
+ choice("wear", WEARS.map((id) => ({ id, name: text.wears[id] })), state.wear),
1569
+ { kind: "number", id: "age_days", name: text.control.age_days, hint: text.hint.age_days, unit: "d", min: 0, max: 400, step: 5, default: 0 },
1570
+ choice("storage", Object.keys(catalog.rules.storage).map((id) => ({ id, name: label(`storage.${id}`, humanize(id)) })), state.storage),
1571
+ { kind: "boolean", id: "powered", name: text.control.powered, hint: text.hint.powered, default: true },
1572
+ choice("place", [...catalog.places.keys()].map((id) => ({ id, name: label(`place.${id}`, humanize(id)) })), state.place),
1573
+ choice("container", [...catalog.containers.keys()].map((id) => ({ id, name: label(`container.${id}`, humanize(id)) })), state.container),
1574
+ choice("state", ["normal", "fortified", "abandoned"].map((id) => ({ id, name: text.states[id] })), "normal"),
1575
+ { kind: "number", id: "day", name: text.control.day, hint: text.hint.day, unit: "d", min: 0, max: 60, step: 1, default: 0 },
1576
+ { kind: "number", id: "seed", name: text.control.seed, min: 1, max: 9999, step: 1, default: state.seed },
1577
+ { kind: "action", id: "roll_finds", name: text.control.roll_finds, hint: text.hint.roll_finds },
1578
+ ...state.recipe ? [choice("recipe", [...catalog.recipes.keys()].map((id) => ({ id, name: label(`recipe.${id}`, humanize(id)) })), state.recipe)] : [],
1579
+ ...state.recipe ? [{ kind: "action", id: "craft", name: text.control.craft, hint: text.hint.craft }] : []
1580
+ ],
1581
+ layers: () => [
1582
+ { id: "labels", name: text.layers.labels },
1583
+ { id: "boxes", name: text.layers.boxes, default: false },
1584
+ { id: "simple_shapes", name: text.layers.simple_shapes, default: false }
1585
+ ],
1586
+ metrics: () => [
1587
+ { id: "items", name: text.metrics.items, digits: 0, read: () => visibleEntries().length },
1588
+ { id: "draw_calls", name: text.metrics.draw_calls, digits: 0, read: () => visibleEntries().reduce((sum, entry) => sum + entry.drawCalls, 0) },
1589
+ { id: "triangles", name: text.metrics.triangles, digits: 0, read: () => visibleEntries().reduce((sum, entry) => sum + entry.triangles, 0) },
1590
+ {
1591
+ id: "modeled",
1592
+ name: text.metrics.modeled,
1593
+ read: () => {
1594
+ const shown = visibleEntries().filter((entry) => entry.group === "shelf");
1595
+ return `${shown.filter((entry) => entry.kind === "model").length}/${shown.length}`;
1596
+ }
1597
+ },
1598
+ { id: "finds", name: text.metrics.finds, digits: 0, read: () => stack.length }
1599
+ ],
1600
+ scenarios: () => scenariosOf(module, catalog, text, provider),
1601
+ attach(lent) {
1602
+ host = lent;
1603
+ latestTHREE = lent.THREE;
1604
+ const offChange = lent.bench?.onChange(onChange);
1605
+ const offFrame = lent.onFrame((dt) => {
1606
+ for (const entry of entries2) if (entry.group === "showcase" && entry.root.parent) entry.root.rotation.y += dt * SPIN;
1607
+ });
1608
+ return () => {
1609
+ offChange?.();
1610
+ offFrame();
1611
+ clearBoxes();
1612
+ clearFinds();
1613
+ host = null;
1614
+ };
1615
+ }
1616
+ };
1617
+ return module;
1618
+ }
1619
+ var OVER = 0.02;
1620
+ var UNDER = 0.08;
1621
+ function scenariosOf(module, catalog, text, provider) {
1622
+ const names = text.scenarios;
1623
+ const shelf = () => module.placed().filter((entry) => entry.group === "shelf");
1624
+ return [
1625
+ {
1626
+ id: "real_size",
1627
+ name: names.real_size,
1628
+ run: ({ expect }) => {
1629
+ const shown = shelf();
1630
+ expect(shown.length > 0, "items are on the shelves", { count: shown.length });
1631
+ for (const entry of shown) {
1632
+ const measured = module.measure(entry.root);
1633
+ if (!measured) continue;
1634
+ const longest = Math.max(...entry.size);
1635
+ const axis = entry.size.indexOf(longest);
1636
+ const ok = measured.every((side, a) => side <= entry.size[a] * (1 + OVER) + 2e-3) && Math.abs(measured[axis] - longest) <= longest * UNDER + 2e-3;
1637
+ expect(ok, `${entry.itemId} measures its real size`, { measured, size: entry.size });
1638
+ }
1639
+ }
1640
+ },
1641
+ {
1642
+ id: "models",
1643
+ name: names.models,
1644
+ run: ({ expect }) => {
1645
+ const models = listItemModels();
1646
+ expect(models.length === 24, "24 models are shipped", { count: models.length });
1647
+ const withModel = catalog.list().filter((item) => item.model !== void 0 && hasItemModel(item.model));
1648
+ expect(withModel.length === 23, "23 items of the catalog name a model", { count: withModel.length });
1649
+ const stand = module.placed().filter((entry) => entry.group === "showcase");
1650
+ expect(stand.length === 0 || stand.every((entry) => entry.kind === "model"), "every piece of the stand is drawn by the model of the team");
1651
+ }
1652
+ },
1653
+ {
1654
+ id: "budgets",
1655
+ name: names.budgets,
1656
+ run: ({ expect }) => {
1657
+ for (const entry of module.placed()) {
1658
+ const report = checkBudget(entry.root, entry.size);
1659
+ expect(report.drawCalls <= ITEM_BUDGETS.drawCalls, `${entry.itemId} stays within ${ITEM_BUDGETS.drawCalls} draw calls`, { drawCalls: report.drawCalls });
1660
+ const deviation = entry.kind === "model" ? MODEL_BUDGET_DEVIATIONS[entry.itemId] : void 0;
1661
+ expect(report.ok || deviation !== void 0 && report.triangles <= deviation, `${entry.itemId} stays within its triangle budget`, { triangles: report.triangles, limit: report.limit });
1662
+ }
1663
+ }
1664
+ },
1665
+ {
1666
+ id: "ruler",
1667
+ name: names.ruler,
1668
+ run: ({ expect }) => {
1669
+ expect(RULER_HEIGHT === 1.55 && HUMAN_HEIGHT === 1.8, "the references have the measures of the page");
1670
+ }
1671
+ },
1672
+ {
1673
+ id: "finds",
1674
+ name: names.finds,
1675
+ run: ({ expect }) => {
1676
+ const query = (seed) => ({ place: module.state.place, container: module.state.container, state: "normal", rng: rng(seed), day: 0 });
1677
+ const a = JSON.stringify(provider.findsFor(query(7)));
1678
+ const b = JSON.stringify(provider.findsFor(query(7)));
1679
+ expect(a === b, "the same seed gives the same finds");
1680
+ expect((provider.findsFor(query(7)) ?? []).every((found) => catalog.has(found.itemId)), "every find is an item of the catalog");
1681
+ }
1682
+ },
1683
+ {
1684
+ id: "freshness",
1685
+ name: names.freshness,
1686
+ run: ({ expect }) => {
1687
+ const food = catalog.list().find((item) => item.shelfLife !== void 0 && item.shelfLife > 0);
1688
+ if (!food) return expect(false, "the catalog has a food that spoils");
1689
+ const at = (storage, days2) => freshness(food, { ageDays: 0, since: 0, storage, storedAt: 0 }, days2, { rules: catalog.rules });
1690
+ const days = food.shelfLife * 1.2;
1691
+ expect(at("ambient", days) !== "fresh", "at room temperature it is past its best", { food: food.id });
1692
+ if ("fridge" in catalog.rules.storage) expect(at("fridge", days) !== at("ambient", days) || at("fridge", days) === "fresh", "in the fridge it keeps better", { food: food.id });
1693
+ }
1694
+ },
1695
+ {
1696
+ id: "craft",
1697
+ name: names.craft,
1698
+ run: ({ expect }) => {
1699
+ const before = module.state.recipe;
1700
+ for (const id of catalog.recipes.keys()) {
1701
+ module.selectRecipe(id);
1702
+ const result = module.craftSelected();
1703
+ expect(result.ok, `recipe ${id} is made with what it asks for`, { missing: result.missing });
1704
+ }
1705
+ module.selectRecipe(before);
1706
+ }
1707
+ }
1708
+ ];
1709
+ }
1710
+ export {
1711
+ FINDS_TABLE_ID,
1712
+ HUMAN_HEIGHT,
1713
+ HUMAN_ID,
1714
+ ITEM_BUDGETS,
1715
+ ITEM_PREFIX,
1716
+ MODEL_BUDGET_DEVIATIONS,
1717
+ ROW_LIMIT,
1718
+ RULER_HEIGHT,
1719
+ RULER_ID,
1720
+ SHAPES,
1721
+ SHELF_HEIGHT,
1722
+ SHELF_PREFIX,
1723
+ SHIPPED_MODELS,
1724
+ SHOWCASE_PREFIX,
1725
+ SHOWCASE_STAND_ID,
1726
+ WEAR_AMOUNT,
1727
+ WEAR_TAGS,
1728
+ WEAR_TARGET,
1729
+ buildItem,
1730
+ buildItemLod,
1731
+ canonicalFrame,
1732
+ checkBudget,
1733
+ composeThumbnailScene,
1734
+ countDrawCalls,
1735
+ countTriangles,
1736
+ createItemKit,
1737
+ createItemsTestModule,
1738
+ cullDistanceOf,
1739
+ drawShape,
1740
+ hasItemModel,
1741
+ isShape,
1742
+ itemModelBuilder,
1743
+ itemModelEntry,
1744
+ itemThumbnail,
1745
+ itemThumbnailSvg,
1746
+ listItemModels,
1747
+ mergeItem,
1748
+ registerItemModel,
1749
+ renderItemThumbnail,
1750
+ setWear,
1751
+ shade,
1752
+ shapeOf,
1753
+ shapeThumbnailSvg,
1754
+ shelvesLayout,
1755
+ sizeClassOf,
1756
+ thumbnailPatch,
1757
+ triangleBudgetOf,
1758
+ unregisterItemModel,
1759
+ wearableMaterials
1760
+ };
1761
+ //# sourceMappingURL=index.js.map