@adatechnology/game-weapons-three 0.0.0-stage → 0.1.0

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,495 @@
1
+ // src/area.ts
2
+ import { createArsenal } from "@adatechnology/game-weapons";
3
+
4
+ // src/audio.ts
5
+ var NOISE_SECONDS = 0.6;
6
+ function createWeaponAudio(volume = 0.6) {
7
+ const Ctor = globalThis.AudioContext ?? globalThis.webkitAudioContext;
8
+ let ctx = null;
9
+ let master = null;
10
+ let noise = null;
11
+ let level = volume;
12
+ const ready = () => {
13
+ if (!Ctor) return null;
14
+ if (!ctx) {
15
+ ctx = new Ctor();
16
+ master = ctx.createGain();
17
+ master.gain.value = level;
18
+ master.connect(ctx.destination);
19
+ noise = ctx.createBuffer(1, Math.floor(ctx.sampleRate * NOISE_SECONDS), ctx.sampleRate);
20
+ const data = noise.getChannelData(0);
21
+ for (let i = 0; i < data.length; i++) data[i] = Math.random() * 2 - 1;
22
+ }
23
+ if (ctx.state === "suspended") void ctx.resume();
24
+ return ctx;
25
+ };
26
+ const burst = (options) => {
27
+ const audio = ready();
28
+ if (!audio || !noise || !master) return;
29
+ const start = audio.currentTime + (options.at ?? 0);
30
+ const source = audio.createBufferSource();
31
+ source.buffer = noise;
32
+ const filter = audio.createBiquadFilter();
33
+ filter.type = options.filter;
34
+ filter.frequency.value = options.frequency;
35
+ filter.Q.value = options.q ?? 0.8;
36
+ const gain = audio.createGain();
37
+ gain.gain.setValueAtTime(1e-4, start);
38
+ gain.gain.exponentialRampToValueAtTime(options.gain, start + 4e-3);
39
+ gain.gain.exponentialRampToValueAtTime(1e-4, start + options.seconds);
40
+ source.connect(filter).connect(gain).connect(master);
41
+ source.start(start);
42
+ source.stop(start + options.seconds + 0.05);
43
+ };
44
+ const tone = (options) => {
45
+ const audio = ready();
46
+ if (!audio || !master) return;
47
+ const start = audio.currentTime + (options.at ?? 0);
48
+ const osc = audio.createOscillator();
49
+ osc.type = options.type ?? "sine";
50
+ osc.frequency.setValueAtTime(options.frequency, start);
51
+ if (options.to) osc.frequency.exponentialRampToValueAtTime(options.to, start + options.seconds);
52
+ const gain = audio.createGain();
53
+ gain.gain.setValueAtTime(1e-4, start);
54
+ gain.gain.exponentialRampToValueAtTime(options.gain, start + 3e-3);
55
+ gain.gain.exponentialRampToValueAtTime(1e-4, start + options.seconds);
56
+ osc.connect(gain).connect(master);
57
+ osc.start(start);
58
+ osc.stop(start + options.seconds + 0.05);
59
+ };
60
+ const metal = (at, frequency, gain) => {
61
+ burst({ at, seconds: 0.05, gain, filter: "highpass", frequency, q: 2 });
62
+ tone({ at, seconds: 0.04, gain: gain * 0.6, frequency: frequency * 0.6, type: "square" });
63
+ };
64
+ return {
65
+ shot(energyJ, category) {
66
+ const k = Math.min(1, Math.max(0, Math.log10(Math.max(energyJ, 50) / 50) / 2.5));
67
+ const shotgun = category === "shotgun";
68
+ burst({ seconds: 0.12 + k * 0.35 + (shotgun ? 0.1 : 0), gain: 0.5 + k * 0.5, filter: "lowpass", frequency: 4200 - k * 2600 });
69
+ burst({ seconds: 0.03, gain: 0.6, filter: "highpass", frequency: 3e3 });
70
+ tone({ seconds: 0.14 + k * 0.2, gain: 0.35 + k * 0.4, frequency: 140 - k * 70, to: 40 });
71
+ },
72
+ click() {
73
+ metal(0, 2600, 0.25);
74
+ },
75
+ reloadStart(category) {
76
+ metal(0, 1500, 0.35);
77
+ if (category !== "shotgun" && category !== "revolver") metal(0.12, 900, 0.2);
78
+ },
79
+ reloadEnd(category) {
80
+ metal(0, 1100, 0.45);
81
+ if (category !== "revolver") metal(0.18, 1900, 0.4);
82
+ },
83
+ cycle() {
84
+ metal(0.22, 900, 0.35);
85
+ metal(0.36, 1300, 0.35);
86
+ },
87
+ clink() {
88
+ const f = 3200 + Math.random() * 2e3;
89
+ tone({ seconds: 0.09, gain: 0.12, frequency: f });
90
+ tone({ seconds: 0.06, gain: 0.06, frequency: f * 2.7 });
91
+ },
92
+ thud() {
93
+ burst({ seconds: 0.09, gain: 0.35, filter: "lowpass", frequency: 380 });
94
+ },
95
+ setVolume(next) {
96
+ level = Math.max(0, Math.min(1, next));
97
+ if (master) master.gain.value = level;
98
+ },
99
+ dispose() {
100
+ void ctx?.close();
101
+ ctx = null;
102
+ master = null;
103
+ noise = null;
104
+ }
105
+ };
106
+ }
107
+
108
+ // src/props.ts
109
+ var box = (id, material, size, position, extra = {}) => ({ id, material, shape: { kind: "box", size }, position, ...extra });
110
+ var joint = (a, b, kind) => ({ a, b, kind });
111
+ var handled = (name, handle, head) => {
112
+ const [handleMaterial, rawHandle] = handle;
113
+ const [headMaterial, rawHead] = head;
114
+ const thickness = Math.max(rawHandle[1], rawHead[1]);
115
+ const handleSize = [rawHandle[0], thickness, rawHandle[2]];
116
+ const headSize = [rawHead[0], thickness, rawHead[2]];
117
+ const shift = (handleSize[0] - headSize[0]) / 2;
118
+ return {
119
+ spec: {
120
+ name,
121
+ parts: [box("handle", handleMaterial, handleSize, [-handleSize[0] / 2 + shift, thickness / 2, 0]), box("head", headMaterial, headSize, [headSize[0] / 2 + shift, thickness / 2, 0])],
122
+ joints: [joint("handle", "head", "screw")],
123
+ tags: ["weapon"]
124
+ },
125
+ colors: { handle: handleMaterial === "steel" ? 3816770 : handleMaterial === "plastic" ? 2829616 : 8017200, head: headMaterial === "steel" ? 9410206 : headMaterial === "copper" ? 12088115 : 7031338 }
126
+ };
127
+ };
128
+ var solid = (name, material, size, color) => ({
129
+ spec: { name, parts: [box("body", material, size, [0, size[1] / 2, 0])], joints: [], tags: ["weapon"] },
130
+ colors: { body: color }
131
+ });
132
+ var SHAPES = {
133
+ pistol: (n) => handled(n, ["plastic", [0.06, 0.03, 0.11]], ["steel", [0.2, 0.03, 0.04]]),
134
+ revolver: (n) => handled(n, ["wood_soft", [0.06, 0.035, 0.11]], ["steel", [0.24, 0.04, 0.05]]),
135
+ smg: (n) => handled(n, ["plastic", [0.2, 0.05, 0.14]], ["steel", [0.45, 0.05, 0.06]]),
136
+ carbine: (n) => handled(n, ["wood_soft", [0.35, 0.05, 0.12]], ["steel", [0.6, 0.04, 0.05]]),
137
+ rifle: (n) => handled(n, ["wood_soft", [0.35, 0.06, 0.13]], ["steel", [0.75, 0.05, 0.07]]),
138
+ sniper: (n) => handled(n, ["plastic", [0.4, 0.06, 0.14]], ["steel", [0.95, 0.06, 0.08]]),
139
+ shotgun: (n) => handled(n, ["wood_soft", [0.38, 0.06, 0.13]], ["steel", [0.7, 0.05, 0.06]]),
140
+ bow: (n) => solid(n, "wood_soft", [0.95, 0.03, 0.22], 5913890),
141
+ crossbow: (n) => handled(n, ["wood_soft", [0.6, 0.06, 0.08]], ["steel", [0.12, 0.04, 0.26]]),
142
+ melee_blunt: (n, id) => id === "baseball_bat" ? solid(n, "wood_soft", [0.84, 0.06, 0.06], 11568464) : handled(n, ["wood_soft", [id === "sledgehammer" ? 0.8 : 0.32, 0.03, 0.03]], ["steel", id === "sledgehammer" ? [0.08, 0.08, 0.2] : [0.06, 0.04, 0.12]]),
143
+ melee_blade: (n, id) => handled(n, ["wood_soft", [0.13, 0.03, 0.03]], ["steel", id === "axe" ? [0.5, 0.03, 0.16] : id === "knife" ? [0.16, 0.01, 0.03] : [0.45, 0.01, 0.05]]),
144
+ tool: (n) => handled(n, ["plastic", [0.12, 0.025, 0.03]], ["steel", [0.16, 0.02, 0.03]]),
145
+ flame: (n, id) => id === "flamethrower" ? handled(n, ["steel", [0.3, 0.14, 0.14]], ["steel", [0.6, 0.05, 0.05]]) : solid(n, "plastic", [0.08, 0.02, 0.04], 14239787),
146
+ heat: (n) => handled(n, ["steel", [0.2, 0.08, 0.08]], ["copper", [0.25, 0.03, 0.03]]),
147
+ throwable: (n, id) => id === "molotov" ? solid(n, "glass", [0.25, 0.07, 0.07], 8378032) : solid(n, "steel", [id === "pipe_bomb" ? 0.25 : 0.1, 0.06, 0.06], 5003834),
148
+ extinguisher: (n) => solid(n, "steel", [0.5, 0.15, 0.15], 12597547)
149
+ };
150
+ var BODY_ACTIONS = /* @__PURE__ */ new Set(["hand", "kick", "shove", "fists"]);
151
+ var withMass = (prop, kg) => {
152
+ const volumes = prop.spec.parts.map((part) => part.shape.kind === "box" ? part.shape.size[0] * part.shape.size[1] * part.shape.size[2] : 0);
153
+ const total = volumes.reduce((sum, volume) => sum + volume, 0) || 1;
154
+ return { ...prop, spec: { ...prop.spec, parts: prop.spec.parts.map((part, index) => ({ ...part, mass: kg * volumes[index] / total })) } };
155
+ };
156
+ function weaponProp(def, name = def.id) {
157
+ const shape = SHAPES[def.category] ?? SHAPES.tool;
158
+ return withMass(shape(name, def.id), def.mass);
159
+ }
160
+ function buildProp(THREE, prop) {
161
+ const group = new THREE.Group();
162
+ group.name = prop.spec.name ?? "weapon";
163
+ for (const part of prop.spec.parts) {
164
+ if (part.shape.kind !== "box") continue;
165
+ const [w, h, d] = part.shape.size;
166
+ const material = new THREE.MeshStandardMaterial({ color: prop.colors[part.id] ?? 7829367, roughness: 0.6, metalness: part.material === "steel" ? 0.6 : 0.05 });
167
+ const mesh = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), material);
168
+ mesh.name = part.id;
169
+ mesh.position.set(part.position[0], part.position[1], part.position[2]);
170
+ mesh.castShadow = true;
171
+ mesh.receiveShadow = true;
172
+ group.add(mesh);
173
+ }
174
+ return group;
175
+ }
176
+ function buildWeaponModel(THREE, def) {
177
+ return buildProp(THREE, weaponProp(def));
178
+ }
179
+
180
+ // src/fx.ts
181
+ var EJECTS_ON_SHOT = /* @__PURE__ */ new Set(["pistol", "smg", "carbine", "rifle", "sniper", "shotgun"]);
182
+ var DROPS_MAGAZINE = /* @__PURE__ */ new Set(["pistol", "smg", "carbine", "rifle", "sniper"]);
183
+ var CYCLES = /* @__PURE__ */ new Set(["shotgun_pump_12", "rifle_bolt_308"]);
184
+ var MAX_CASINGS = 120;
185
+ var MAX_MAGAZINES = 12;
186
+ var LIFETIME_S = 25;
187
+ var HAND_OFFSET = [0.2, -0.2, -0.42];
188
+ var GRAVITY = 9.81;
189
+ function createWeaponFx(options) {
190
+ const { THREE, scene, camera, arsenal, groundAt } = options;
191
+ const audio = options.audio ?? null;
192
+ const bodies = [];
193
+ const brass = new THREE.MeshStandardMaterial({ color: 13214282, metalness: 0.8, roughness: 0.35 });
194
+ const shell = new THREE.MeshStandardMaterial({ color: 11022891, roughness: 0.6 });
195
+ const steel = new THREE.MeshStandardMaterial({ color: 3093047, metalness: 0.5, roughness: 0.5 });
196
+ const hand = new THREE.Group();
197
+ hand.name = "weapon-in-hand";
198
+ scene.add(hand);
199
+ let model = null;
200
+ let inHand = null;
201
+ let def = null;
202
+ let kick = 0;
203
+ let dip = 0;
204
+ let reloading = false;
205
+ const v = new THREE.Vector3();
206
+ const q = new THREE.Quaternion();
207
+ const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
208
+ const equip = (weapon) => {
209
+ if (model) hand.remove(model);
210
+ model = null;
211
+ inHand = null;
212
+ def = arsenal.weapons.get(weapon) ?? null;
213
+ if (!def || BODY_ACTIONS.has(def.id)) return;
214
+ model = buildWeaponModel(THREE, def);
215
+ const length = new THREE.Box3().setFromObject(model).getSize(v).x;
216
+ model.scale.setScalar(length > 0.8 ? 0.55 : length > 0.4 ? 0.75 : 1);
217
+ model.quaternion.copy(pointForward);
218
+ hand.add(model);
219
+ inHand = def.id;
220
+ };
221
+ const handPoint = (x, y, z, out) => out.set(HAND_OFFSET[0] + x, HAND_OFFSET[1] + y, HAND_OFFSET[2] + z).applyQuaternion(camera.quaternion).add(camera.position);
222
+ const throwBody = (kind, mesh, from, velocity, radius) => {
223
+ const limit = kind === "casing" ? MAX_CASINGS : MAX_MAGAZINES;
224
+ const same = bodies.filter((b) => b.kind === kind);
225
+ if (same.length >= limit) removeBody(same[0]);
226
+ mesh.position.copy(from);
227
+ mesh.castShadow = true;
228
+ scene.add(mesh);
229
+ bodies.push({ mesh, velocity, spin: new THREE.Vector3(Math.random() * 20 - 10, Math.random() * 30 - 15, Math.random() * 20 - 10), radius, age: 0, resting: false, bounces: 0, kind });
230
+ };
231
+ const ejectCasing = (caliberMm, isShell, downward) => {
232
+ const radius = isShell ? 0.0105 : Math.max(3e-3, caliberMm / 1e3 * 0.6);
233
+ const length = isShell ? 0.068 : Math.min(0.07, Math.max(0.015, caliberMm / 1e3 * 2.6));
234
+ const mesh = new THREE.Mesh(new THREE.CylinderGeometry(radius, radius, length, 8), isShell ? shell : brass);
235
+ mesh.name = "casing";
236
+ const from = handPoint(0.03, 0.03, 0.05, new THREE.Vector3());
237
+ const right = new THREE.Vector3(1, 0, 0).applyQuaternion(camera.quaternion);
238
+ const up = new THREE.Vector3(0, 1, 0);
239
+ const velocity = downward ? up.clone().multiplyScalar(-0.5).addScaledVector(right, (Math.random() - 0.5) * 0.4) : right.multiplyScalar(1.6 + Math.random() * 0.8).addScaledVector(up, 1.4 + Math.random() * 0.8);
240
+ throwBody("casing", mesh, from, velocity, radius);
241
+ };
242
+ const dropMagazine = (category) => {
243
+ const long = category === "rifle" || category === "sniper" || category === "carbine";
244
+ const size = long ? [0.03, 0.17, 0.07] : [0.025, 0.11, 0.035];
245
+ const mesh = new THREE.Mesh(new THREE.BoxGeometry(...size), steel);
246
+ mesh.name = "magazine";
247
+ const from = handPoint(0, -0.06, 0, new THREE.Vector3());
248
+ throwBody("magazine", mesh, from, new THREE.Vector3(0, -0.4, 0), size[1] / 2);
249
+ audio?.reloadStart(category);
250
+ };
251
+ const caliberOf = (weapon) => {
252
+ if (weapon.action.kind !== "firearm") return 9;
253
+ return arsenal.calibers.get(weapon.action.caliber)?.diameter ?? 9;
254
+ };
255
+ function removeBody(body) {
256
+ body.mesh.removeFromParent();
257
+ body.mesh.geometry.dispose();
258
+ bodies.splice(bodies.indexOf(body), 1);
259
+ }
260
+ const onShot = (weapon) => {
261
+ kick = 1;
262
+ const caliber = weapon.action.kind === "firearm" ? arsenal.calibers.get(weapon.action.caliber) : void 0;
263
+ const energy = caliber ? 0.5 * (caliber.bulletGrams / 1e3) * caliber.muzzleSpeed ** 2 * (caliber.pellets ?? 1) : 300;
264
+ audio?.shot(energy, weapon.category);
265
+ if (CYCLES.has(weapon.id)) audio?.cycle();
266
+ if (EJECTS_ON_SHOT.has(weapon.category) && weapon.id !== "shotgun_double_12") ejectCasing(caliberOf(weapon), weapon.category === "shotgun", false);
267
+ };
268
+ const onReloadStart = (weapon, tool) => {
269
+ if (DROPS_MAGAZINE.has(weapon.category)) dropMagazine(weapon.category);
270
+ else audio?.reloadStart(weapon.category);
271
+ if (weapon.category === "revolver" || weapon.id === "shotgun_double_12") {
272
+ const spent = Math.max(1, tool.capacity - tool.ammo);
273
+ for (let i = 0; i < spent; i++) ejectCasing(caliberOf(weapon), weapon.category === "shotgun", true);
274
+ }
275
+ };
276
+ const stepBody = (body, dt) => {
277
+ body.age += dt;
278
+ if (body.resting) return;
279
+ body.velocity.y -= GRAVITY * dt;
280
+ body.mesh.position.addScaledVector(body.velocity, dt);
281
+ body.mesh.rotation.x += body.spin.x * dt;
282
+ body.mesh.rotation.y += body.spin.y * dt;
283
+ body.mesh.rotation.z += body.spin.z * dt;
284
+ const floor = groundAt(body.mesh.position.x, body.mesh.position.z) + body.radius;
285
+ if (body.mesh.position.y > floor) return;
286
+ body.mesh.position.y = floor;
287
+ if (Math.abs(body.velocity.y) > 0.7 && body.bounces < 4) {
288
+ body.bounces++;
289
+ body.velocity.y = -body.velocity.y * 0.35;
290
+ body.velocity.x *= 0.55;
291
+ body.velocity.z *= 0.55;
292
+ body.spin.multiplyScalar(0.5);
293
+ if (body.kind === "casing") audio?.clink();
294
+ else audio?.thud();
295
+ return;
296
+ }
297
+ if (body.kind === "magazine" && body.bounces === 0) audio?.thud();
298
+ body.resting = true;
299
+ body.mesh.rotation.x = Math.PI / 2;
300
+ body.mesh.rotation.z = 0;
301
+ };
302
+ return {
303
+ handle(event, tool) {
304
+ const weapon = arsenal.weapons.get(event.weapon);
305
+ if (event.type === "tool") equip(event.weapon);
306
+ if (!weapon) return;
307
+ if (event.type === "shot") onShot(weapon);
308
+ else if (event.type === "empty") audio?.click();
309
+ else if (event.type === "reload" && event.phase === "start") onReloadStart(weapon, tool);
310
+ else if (event.type === "reload" && event.phase === "end") audio?.reloadEnd(weapon.category);
311
+ reloading = tool.reloading;
312
+ },
313
+ update(dt) {
314
+ kick *= Math.exp(-dt * 14);
315
+ dip += ((reloading ? 1 : 0) - dip) * Math.min(1, dt * 8);
316
+ if (inHand) {
317
+ handPoint(0, -0.12 * dip, 0.05 * kick, hand.position);
318
+ q.setFromEuler(new THREE.Euler(0.14 * kick - 0.25 * dip, 0, 0.6 * dip));
319
+ hand.quaternion.copy(camera.quaternion).multiply(q);
320
+ }
321
+ for (const body of [...bodies]) {
322
+ stepBody(body, dt);
323
+ if (body.age > LIFETIME_S) removeBody(body);
324
+ }
325
+ },
326
+ get inHand() {
327
+ return inHand;
328
+ },
329
+ get casings() {
330
+ return bodies.filter((b) => b.kind === "casing").length;
331
+ },
332
+ get magazines() {
333
+ return bodies.filter((b) => b.kind === "magazine").length;
334
+ },
335
+ dispose() {
336
+ for (const body of [...bodies]) removeBody(body);
337
+ hand.removeFromParent();
338
+ brass.dispose();
339
+ shell.dispose();
340
+ steel.dispose();
341
+ }
342
+ };
343
+ }
344
+
345
+ // src/area.ts
346
+ var WEAPON_ITEM_PREFIX = "weapon_";
347
+ var COUNTER_ITEM_ID = "counter_weapons";
348
+ var COUNTER = { length: 4.8, depth: 1.6, height: 0.9 };
349
+ var COUNTER_X = [-6.5, -9, -11.5];
350
+ var COUNTER_Z = -1;
351
+ var END_MARGIN = 0.6;
352
+ var SLOT_GAP = 0.3;
353
+ var TARGETS = [
354
+ { id: "target_steel_plate", at: [-1.5, 0, -7] },
355
+ { id: "target_plywood", at: [0, 0, -7] },
356
+ { id: "target_concrete", at: [1.5, 0, -7] }
357
+ ];
358
+ var TEXT = {
359
+ en: {
360
+ group: "Weapons",
361
+ name: "Weapons area",
362
+ counter: "Weapon counter",
363
+ targets: { target_steel_plate: "Steel plate target", target_plywood: "Plywood target", target_concrete: "Concrete block target" },
364
+ reloadStart: "Reload started: {weapon} ({ammo}/{capacity})",
365
+ reloadEnd: "Reload done: {weapon} ({ammo}/{capacity})"
366
+ },
367
+ "pt-BR": {
368
+ group: "Armas",
369
+ name: "\xC1rea de armas",
370
+ counter: "Balc\xE3o de armas",
371
+ targets: { target_steel_plate: "Alvo: chapa de a\xE7o", target_plywood: "Alvo: compensado", target_concrete: "Alvo: bloco de concreto" },
372
+ reloadStart: "Recarga iniciada: {weapon} ({ammo}/{capacity})",
373
+ reloadEnd: "Recarga completa: {weapon} ({ammo}/{capacity})"
374
+ }
375
+ };
376
+ var textOf = (locale) => TEXT[locale] ?? TEXT.en;
377
+ var fill = (template, vars) => template.replace(/\{(\w+)\}/g, (_, key) => String(vars[key] ?? ""));
378
+ var counterProp = (name) => {
379
+ const { length, depth, height } = COUNTER;
380
+ return {
381
+ spec: {
382
+ name,
383
+ parts: [
384
+ { id: "top", material: "wood_soft", shape: { kind: "box", size: [depth, 0.05, length] }, position: [0, height - 0.025, 0] },
385
+ { id: "side_a", material: "plywood", shape: { kind: "box", size: [depth - 0.1, height - 0.05, 0.05] }, position: [0, (height - 0.05) / 2, -length / 2 + 0.05], anchored: true },
386
+ { id: "side_b", material: "plywood", shape: { kind: "box", size: [depth - 0.1, height - 0.05, 0.05] }, position: [0, (height - 0.05) / 2, length / 2 - 0.05], anchored: true }
387
+ ],
388
+ joints: [
389
+ { a: "top", b: "side_a", kind: "screw" },
390
+ { a: "top", b: "side_b", kind: "screw" }
391
+ ],
392
+ tags: ["furniture"]
393
+ },
394
+ colors: { top: 9067056, side_a: 7029798, side_b: 7029798 }
395
+ };
396
+ };
397
+ var targetProp = (id, name) => {
398
+ const face = {
399
+ target_steel_plate: ["steel", [0.5, 0.5, 0.01], 11910340],
400
+ target_plywood: ["plywood", [0.6, 0.8, 0.018], 13214315],
401
+ target_concrete: ["concrete", [0.4, 0.2, 0.2], 10132116]
402
+ };
403
+ const [material, size, color] = face[id];
404
+ const spec = {
405
+ name,
406
+ parts: [
407
+ { id: "post", material: "steel", shape: { kind: "box", size: [0.05, 1.1, 0.05] }, position: [0, 0.55, -0.05], anchored: true },
408
+ { id: "face", material, shape: { kind: "box", size }, position: [0, 1.1 + size[1] / 2, 0] }
409
+ ],
410
+ joints: [{ a: "post", b: "face", kind: "bolt" }],
411
+ tags: ["target"]
412
+ };
413
+ return { spec, colors: { post: 3816770, face: color } };
414
+ };
415
+ var asItem = (id, name, prop, extra = {}) => ({
416
+ id,
417
+ name,
418
+ build: (THREE) => buildProp(THREE, prop),
419
+ spec: prop.spec,
420
+ ...extra
421
+ });
422
+ function weaponsLayout(weaponItemIds) {
423
+ const perCounter = Math.floor((COUNTER.length - 2 * END_MARGIN) / SLOT_GAP) + 1;
424
+ const counters = COUNTER_X.slice(0, Math.max(1, Math.ceil(weaponItemIds.length / perCounter)));
425
+ const placements = counters.map((x) => ({ item: COUNTER_ITEM_ID, position: [x, 0, COUNTER_Z], surface: { halfSize: [COUNTER.depth / 2, COUNTER.length / 2], height: COUNTER.height } }));
426
+ weaponItemIds.forEach((item, index) => {
427
+ const x = counters[Math.min(counters.length - 1, Math.floor(index / perCounter))];
428
+ const z = COUNTER_Z - COUNTER.length / 2 + END_MARGIN + index % perCounter * SLOT_GAP;
429
+ placements.push({ item, position: [x, COUNTER.height + 0.01, z], asleep: true });
430
+ });
431
+ for (const target of TARGETS) placements.push({ item: target.id, position: target.at });
432
+ return placements;
433
+ }
434
+ function createWeaponsTestModule(options = {}) {
435
+ const locale = options.locale ?? "pt-BR";
436
+ const arsenal = options.arsenal ?? createArsenal({ locale });
437
+ const text = textOf(locale);
438
+ const audio = options.sound === false ? null : createWeaponAudio();
439
+ const weaponDefs = [...arsenal.weapons.values()].filter((def) => !BODY_ACTIONS.has(def.id));
440
+ const weaponIds = weaponDefs.map((def) => `${WEAPON_ITEM_PREFIX}${def.id}`);
441
+ let fx = null;
442
+ return {
443
+ id: "weapons_area",
444
+ name: text.name,
445
+ group: text.group,
446
+ audio,
447
+ get fx() {
448
+ return fx;
449
+ },
450
+ items: () => [
451
+ ...weaponDefs.map((def) => asItem(`${WEAPON_ITEM_PREFIX}${def.id}`, arsenal.name(def.id), weaponProp(def, arsenal.name(def.id)), { weapon: def.id, movable: true })),
452
+ asItem(COUNTER_ITEM_ID, text.counter, counterProp(text.counter), { movable: false }),
453
+ ...TARGETS.map((target) => asItem(target.id, text.targets[target.id], targetProp(target.id, text.targets[target.id]), { movable: false }))
454
+ ],
455
+ area: () => ({ placements: weaponsLayout(weaponIds), view: { eye: [-3.4, 1.7, 2.6], target: [-9, 0.9, -1.2] } }),
456
+ attach(host) {
457
+ const created = createWeaponFx({
458
+ THREE: host.THREE,
459
+ scene: host.scene,
460
+ camera: host.camera,
461
+ arsenal,
462
+ groundAt: host.groundAt,
463
+ audio
464
+ });
465
+ fx = created;
466
+ const offEvents = host.on((event) => {
467
+ const tool = host.tool();
468
+ created.handle(event, tool);
469
+ if (event.type === "reload") host.log(fill(event.phase === "start" ? text.reloadStart : text.reloadEnd, { weapon: arsenal.name(event.weapon), ammo: tool.ammo, capacity: tool.capacity }), { weapon: event.weapon, phase: event.phase });
470
+ });
471
+ const offFrame = host.onFrame((dt) => created.update(dt));
472
+ created.handle({ type: "tool", weapon: host.tool().weapon }, host.tool());
473
+ return () => {
474
+ offEvents();
475
+ offFrame();
476
+ created.dispose();
477
+ audio?.dispose();
478
+ fx = null;
479
+ };
480
+ }
481
+ };
482
+ }
483
+ export {
484
+ BODY_ACTIONS,
485
+ COUNTER_ITEM_ID,
486
+ WEAPON_ITEM_PREFIX,
487
+ buildProp,
488
+ buildWeaponModel,
489
+ createWeaponAudio,
490
+ createWeaponFx,
491
+ createWeaponsTestModule,
492
+ weaponProp,
493
+ weaponsLayout
494
+ };
495
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/area.ts","../src/audio.ts","../src/props.ts","../src/fx.ts"],"sourcesContent":["import type * as THREE_NS from 'three'\nimport type { AreaPlacement, DestructibleSpec, ModuleArea, TestableItem, TestableModule, Vec3 } from '@adatechnology/game-contracts'\nimport { createArsenal } from '@adatechnology/game-weapons'\nimport type { Arsenal } from '@adatechnology/game-weapons'\nimport { createWeaponAudio } from './audio'\nimport type { WeaponAudio } from './audio'\nimport { createWeaponFx } from './fx'\nimport type { WeaponFx } from './fx'\nimport { BODY_ACTIONS, buildProp, weaponProp } from './props'\nimport type { WeaponProp } from './props'\n\nexport interface WeaponsTestModuleOptions {\n /** `en` or `pt-BR` (default). */\n locale?: string\n /** The arsenal to lay out (default `createArsenal` with the locale). */\n arsenal?: Arsenal\n /** Play sounds (default true; silent anyway where there is no AudioContext). */\n sound?: boolean\n}\n\n/** Prefix of the item ids of the weapon props (`weapon_pistol_9mm`). */\nexport const WEAPON_ITEM_PREFIX = 'weapon_'\n// must not start with WEAPON_ITEM_PREFIX: hosts and tests tell the props apart by it\nexport const COUNTER_ITEM_ID = 'counter_weapons'\n\nconst COUNTER = { length: 4.8, depth: 1.6, height: 0.9 }\nconst COUNTER_X = [-6.5, -9, -11.5]\nconst COUNTER_Z = -1\n// props closer than this to a counter end have nothing next to them on one side\nconst END_MARGIN = 0.6\nconst SLOT_GAP = 0.3\n// targets in front of the brick wall of the room, at shooting distances\nconst TARGETS: Array<{ id: string; at: Vec3 }> = [\n { id: 'target_steel_plate', at: [-1.5, 0, -7] },\n { id: 'target_plywood', at: [0, 0, -7] },\n { id: 'target_concrete', at: [1.5, 0, -7] },\n]\n\nconst TEXT: Record<string, { group: string; name: string; counter: string; targets: Record<string, string>; reloadStart: string; reloadEnd: string }> = {\n en: {\n group: 'Weapons',\n name: 'Weapons area',\n counter: 'Weapon counter',\n targets: { target_steel_plate: 'Steel plate target', target_plywood: 'Plywood target', target_concrete: 'Concrete block target' },\n reloadStart: 'Reload started: {weapon} ({ammo}/{capacity})',\n reloadEnd: 'Reload done: {weapon} ({ammo}/{capacity})',\n },\n 'pt-BR': {\n group: 'Armas',\n name: 'Área de armas',\n counter: 'Balcão de armas',\n targets: { target_steel_plate: 'Alvo: chapa de aço', target_plywood: 'Alvo: compensado', target_concrete: 'Alvo: bloco de concreto' },\n reloadStart: 'Recarga iniciada: {weapon} ({ammo}/{capacity})',\n reloadEnd: 'Recarga completa: {weapon} ({ammo}/{capacity})',\n },\n}\nconst textOf = (locale: string): (typeof TEXT)['en'] => TEXT[locale] ?? TEXT.en\nconst fill = (template: string, vars: Record<string, string | number>): string => template.replace(/\\{(\\w+)\\}/g, (_, key: string) => String(vars[key] ?? ''))\n\nconst counterProp = (name: string): WeaponProp => {\n const { length, depth, height } = COUNTER\n return {\n spec: {\n name,\n parts: [\n { id: 'top', material: 'wood_soft', shape: { kind: 'box', size: [depth, 0.05, length] }, position: [0, height - 0.025, 0] },\n { id: 'side_a', material: 'plywood', shape: { kind: 'box', size: [depth - 0.1, height - 0.05, 0.05] }, position: [0, (height - 0.05) / 2, -length / 2 + 0.05], anchored: true },\n { id: 'side_b', material: 'plywood', shape: { kind: 'box', size: [depth - 0.1, height - 0.05, 0.05] }, position: [0, (height - 0.05) / 2, length / 2 - 0.05], anchored: true },\n ],\n joints: [\n { a: 'top', b: 'side_a', kind: 'screw' },\n { a: 'top', b: 'side_b', kind: 'screw' },\n ],\n tags: ['furniture'],\n },\n colors: { top: 0x8a5a30, side_a: 0x6b4426, side_b: 0x6b4426 },\n }\n}\n\nconst targetProp = (id: string, name: string): WeaponProp => {\n const face: Record<string, [string, Vec3, number]> = {\n target_steel_plate: ['steel', [0.5, 0.5, 0.01], 0xb5bcc4],\n target_plywood: ['plywood', [0.6, 0.8, 0.018], 0xc9a26b],\n target_concrete: ['concrete', [0.4, 0.2, 0.2], 0x9a9a94],\n }\n const [material, size, color] = face[id]\n const spec: DestructibleSpec = {\n name,\n parts: [\n { id: 'post', material: 'steel', shape: { kind: 'box', size: [0.05, 1.1, 0.05] }, position: [0, 0.55, -0.05], anchored: true },\n { id: 'face', material, shape: { kind: 'box', size }, position: [0, 1.1 + size[1] / 2, 0] },\n ],\n joints: [{ a: 'post', b: 'face', kind: 'bolt' }],\n tags: ['target'],\n }\n return { spec, colors: { post: 0x3a3d42, face: color } }\n}\n\nconst asItem = (id: string, name: string, prop: WeaponProp, extra: Partial<TestableItem> = {}): TestableItem => ({\n id,\n name,\n build: (THREE: unknown) => buildProp(THREE as typeof THREE_NS, prop),\n spec: prop.spec,\n ...extra,\n})\n\n/** Where the counters go and where each weapon lies on them: three counters on the free left side of the room, props across them. */\nexport function weaponsLayout(weaponItemIds: string[]): AreaPlacement[] {\n const perCounter = Math.floor((COUNTER.length - 2 * END_MARGIN) / SLOT_GAP) + 1\n const counters = COUNTER_X.slice(0, Math.max(1, Math.ceil(weaponItemIds.length / perCounter)))\n const placements: AreaPlacement[] = counters.map((x) => ({ item: COUNTER_ITEM_ID, position: [x, 0, COUNTER_Z], surface: { halfSize: [COUNTER.depth / 2, COUNTER.length / 2], height: COUNTER.height } }))\n weaponItemIds.forEach((item, index) => {\n const x = counters[Math.min(counters.length - 1, Math.floor(index / perCounter))]\n const z = COUNTER_Z - COUNTER.length / 2 + END_MARGIN + (index % perCounter) * SLOT_GAP\n // long props side by side overlap in the gentle piece separation of the renderer: asleep, they stay until grabbed\n placements.push({ item, position: [x, COUNTER.height + 0.01, z], asleep: true })\n })\n for (const target of TARGETS) placements.push({ item: target.id, position: target.at })\n return placements\n}\n\n/**\n * The weapons test area as a `TestableModule` of @adatechnology/game-contracts: one prop per weapon of the arsenal on counters (aim at one and\n * press H to equip it), targets at shooting distance, and, through `attach`, the weapon in the hands with its fire and reload motion,\n * ejected casings, dropped magazines and sounds, driven by the host's tool events.\n */\nexport function createWeaponsTestModule(options: WeaponsTestModuleOptions = {}): TestableModule & { readonly audio: WeaponAudio | null; readonly fx: WeaponFx | null } {\n const locale = options.locale ?? 'pt-BR'\n const arsenal = options.arsenal ?? createArsenal({ locale })\n const text = textOf(locale)\n const audio = options.sound === false ? null : createWeaponAudio()\n const weaponDefs = [...arsenal.weapons.values()].filter((def) => !BODY_ACTIONS.has(def.id))\n const weaponIds = weaponDefs.map((def) => `${WEAPON_ITEM_PREFIX}${def.id}`)\n let fx: WeaponFx | null = null\n return {\n id: 'weapons_area',\n name: text.name,\n group: text.group,\n audio,\n get fx() {\n return fx\n },\n items: () => [\n ...weaponDefs.map((def) => asItem(`${WEAPON_ITEM_PREFIX}${def.id}`, arsenal.name(def.id), weaponProp(def, arsenal.name(def.id)), { weapon: def.id, movable: true })),\n asItem(COUNTER_ITEM_ID, text.counter, counterProp(text.counter), { movable: false }),\n ...TARGETS.map((target) => asItem(target.id, text.targets[target.id], targetProp(target.id, text.targets[target.id]), { movable: false })),\n ],\n area: (): ModuleArea => ({ placements: weaponsLayout(weaponIds), view: { eye: [-3.4, 1.7, 2.6], target: [-9, 0.9, -1.2] } }),\n attach(host) {\n const created = createWeaponFx({\n THREE: host.THREE as typeof THREE_NS,\n scene: host.scene as THREE_NS.Scene,\n camera: host.camera as THREE_NS.Camera,\n arsenal,\n groundAt: host.groundAt,\n audio,\n })\n fx = created\n const offEvents = host.on((event) => {\n const tool = host.tool()\n created.handle(event, tool)\n if (event.type === 'reload') host.log(fill(event.phase === 'start' ? text.reloadStart : text.reloadEnd, { weapon: arsenal.name(event.weapon), ammo: tool.ammo, capacity: tool.capacity }), { weapon: event.weapon, phase: event.phase })\n })\n const offFrame = host.onFrame((dt) => created.update(dt))\n created.handle({ type: 'tool', weapon: host.tool().weapon }, host.tool())\n return () => {\n offEvents()\n offFrame()\n created.dispose()\n audio?.dispose()\n fx = null\n }\n },\n }\n}\n","// Weapon sounds synthesized with WebAudio: no sound files, so the kit stays offline and small. Every call is a no-op where there is no\n// AudioContext (tests, workers), and the context is only created on the first sound, which comes from a click or a key (browsers allow it).\n\n/** Sounds the weapon presentation plays. */\nexport interface WeaponAudio {\n /** A shot: louder and longer the more muzzle energy (joules). */\n shot(energyJ: number, category: string): void\n /** Dry click of an empty weapon. */\n click(): void\n /** Magazine out, or the first shell in. */\n reloadStart(category: string): void\n /** Magazine seated and the action racked. */\n reloadEnd(category: string): void\n /** The pump or lever cycling after a shot. */\n cycle(): void\n /** A casing bouncing on the floor. */\n clink(): void\n /** A magazine landing on the floor. */\n thud(): void\n /** Master volume, 0 to 1. */\n setVolume(volume: number): void\n dispose(): void\n}\n\ntype AudioContextCtor = new () => AudioContext\n\nconst NOISE_SECONDS = 0.6\n\nexport function createWeaponAudio(volume = 0.6): WeaponAudio {\n const Ctor = (globalThis as { AudioContext?: AudioContextCtor; webkitAudioContext?: AudioContextCtor }).AudioContext ?? (globalThis as { webkitAudioContext?: AudioContextCtor }).webkitAudioContext\n let ctx: AudioContext | null = null\n let master: GainNode | null = null\n let noise: AudioBuffer | null = null\n let level = volume\n\n const ready = (): AudioContext | null => {\n if (!Ctor) return null\n if (!ctx) {\n ctx = new Ctor()\n master = ctx.createGain()\n master.gain.value = level\n master.connect(ctx.destination)\n noise = ctx.createBuffer(1, Math.floor(ctx.sampleRate * NOISE_SECONDS), ctx.sampleRate)\n const data = noise.getChannelData(0)\n for (let i = 0; i < data.length; i++) data[i] = Math.random() * 2 - 1\n }\n if (ctx.state === 'suspended') void ctx.resume()\n return ctx\n }\n\n // a filtered noise burst with a fast attack and an exponential tail\n const burst = (options: { at?: number; seconds: number; gain: number; filter: BiquadFilterType; frequency: number; q?: number }): void => {\n const audio = ready()\n if (!audio || !noise || !master) return\n const start = audio.currentTime + (options.at ?? 0)\n const source = audio.createBufferSource()\n source.buffer = noise\n const filter = audio.createBiquadFilter()\n filter.type = options.filter\n filter.frequency.value = options.frequency\n filter.Q.value = options.q ?? 0.8\n const gain = audio.createGain()\n gain.gain.setValueAtTime(0.0001, start)\n gain.gain.exponentialRampToValueAtTime(options.gain, start + 0.004)\n gain.gain.exponentialRampToValueAtTime(0.0001, start + options.seconds)\n source.connect(filter).connect(gain).connect(master)\n source.start(start)\n source.stop(start + options.seconds + 0.05)\n }\n\n const tone = (options: { at?: number; seconds: number; gain: number; frequency: number; to?: number; type?: OscillatorType }): void => {\n const audio = ready()\n if (!audio || !master) return\n const start = audio.currentTime + (options.at ?? 0)\n const osc = audio.createOscillator()\n osc.type = options.type ?? 'sine'\n osc.frequency.setValueAtTime(options.frequency, start)\n if (options.to) osc.frequency.exponentialRampToValueAtTime(options.to, start + options.seconds)\n const gain = audio.createGain()\n gain.gain.setValueAtTime(0.0001, start)\n gain.gain.exponentialRampToValueAtTime(options.gain, start + 0.003)\n gain.gain.exponentialRampToValueAtTime(0.0001, start + options.seconds)\n osc.connect(gain).connect(master)\n osc.start(start)\n osc.stop(start + options.seconds + 0.05)\n }\n\n const metal = (at: number, frequency: number, gain: number): void => {\n burst({ at, seconds: 0.05, gain, filter: 'highpass', frequency, q: 2 })\n tone({ at, seconds: 0.04, gain: gain * 0.6, frequency: frequency * 0.6, type: 'square' })\n }\n\n return {\n shot(energyJ, category) {\n // 200 J (.22) to 15 000 J (.50 BMG) mapped to 0..1\n const k = Math.min(1, Math.max(0, Math.log10(Math.max(energyJ, 50) / 50) / 2.5))\n const shotgun = category === 'shotgun'\n burst({ seconds: 0.12 + k * 0.35 + (shotgun ? 0.1 : 0), gain: 0.5 + k * 0.5, filter: 'lowpass', frequency: 4200 - k * 2600 })\n burst({ seconds: 0.03, gain: 0.6, filter: 'highpass', frequency: 3000 })\n tone({ seconds: 0.14 + k * 0.2, gain: 0.35 + k * 0.4, frequency: 140 - k * 70, to: 40 })\n },\n click() {\n metal(0, 2600, 0.25)\n },\n reloadStart(category) {\n metal(0, 1500, 0.35)\n if (category !== 'shotgun' && category !== 'revolver') metal(0.12, 900, 0.2)\n },\n reloadEnd(category) {\n metal(0, 1100, 0.45)\n if (category !== 'revolver') metal(0.18, 1900, 0.4)\n },\n cycle() {\n metal(0.22, 900, 0.35)\n metal(0.36, 1300, 0.35)\n },\n clink() {\n const f = 3200 + Math.random() * 2000\n tone({ seconds: 0.09, gain: 0.12, frequency: f })\n tone({ seconds: 0.06, gain: 0.06, frequency: f * 2.7 })\n },\n thud() {\n burst({ seconds: 0.09, gain: 0.35, filter: 'lowpass', frequency: 380 })\n },\n setVolume(next) {\n level = Math.max(0, Math.min(1, next))\n if (master) master.gain.value = level\n },\n dispose() {\n void ctx?.close()\n ctx = null\n master = null\n noise = null\n },\n }\n}\n","import type * as THREE_NS from 'three'\nimport type { DestructibleSpec, JointSpec, PartSpec, Vec3 } from '@adatechnology/game-contracts'\nimport type { WeaponDef } from '@adatechnology/game-weapons'\n\n/** A low-poly prop: its destruction spec (box parts only) and one colour per part. */\nexport interface WeaponProp {\n spec: DestructibleSpec\n colors: Record<string, number>\n}\n\ntype Extra = Partial<Omit<PartSpec, 'id' | 'material' | 'shape' | 'position'>>\nconst box = (id: string, material: string, size: Vec3, position: Vec3, extra: Extra = {}): PartSpec => ({ id, material, shape: { kind: 'box', size }, position, ...extra })\nconst joint = (a: string, b: string, kind: JointSpec['kind']): JointSpec => ({ a, b, kind })\n\n// A grip and a body lying flat end to end, long axis along +x, the muzzle or head at +x. One thickness for both parts so the prop lies flat,\n// and the parts touch without overlapping: overlapping parts are pushed apart by the physics and the prop slides away.\nconst handled = (name: string, handle: [string, Vec3], head: [string, Vec3]): WeaponProp => {\n const [handleMaterial, rawHandle] = handle\n const [headMaterial, rawHead] = head\n const thickness = Math.max(rawHandle[1], rawHead[1])\n const handleSize: Vec3 = [rawHandle[0], thickness, rawHandle[2]]\n const headSize: Vec3 = [rawHead[0], thickness, rawHead[2]]\n const shift = (handleSize[0] - headSize[0]) / 2\n return {\n spec: {\n name,\n parts: [box('handle', handleMaterial, handleSize, [-handleSize[0] / 2 + shift, thickness / 2, 0]), box('head', headMaterial, headSize, [headSize[0] / 2 + shift, thickness / 2, 0])],\n joints: [joint('handle', 'head', 'screw')],\n tags: ['weapon'],\n },\n colors: { handle: handleMaterial === 'steel' ? 0x3a3d42 : handleMaterial === 'plastic' ? 0x2b2d30 : 0x7a5530, head: headMaterial === 'steel' ? 0x8f969e : headMaterial === 'copper' ? 0xb87333 : 0x6b4a2a },\n }\n}\nconst solid = (name: string, material: string, size: Vec3, color: number): WeaponProp => ({\n spec: { name, parts: [box('body', material, size, [0, size[1] / 2, 0])], joints: [], tags: ['weapon'] },\n colors: { body: color },\n})\n\nconst SHAPES: Record<string, (name: string, id: string) => WeaponProp> = {\n pistol: (n) => handled(n, ['plastic', [0.06, 0.03, 0.11]], ['steel', [0.2, 0.03, 0.04]]),\n revolver: (n) => handled(n, ['wood_soft', [0.06, 0.035, 0.11]], ['steel', [0.24, 0.04, 0.05]]),\n smg: (n) => handled(n, ['plastic', [0.2, 0.05, 0.14]], ['steel', [0.45, 0.05, 0.06]]),\n carbine: (n) => handled(n, ['wood_soft', [0.35, 0.05, 0.12]], ['steel', [0.6, 0.04, 0.05]]),\n rifle: (n) => handled(n, ['wood_soft', [0.35, 0.06, 0.13]], ['steel', [0.75, 0.05, 0.07]]),\n sniper: (n) => handled(n, ['plastic', [0.4, 0.06, 0.14]], ['steel', [0.95, 0.06, 0.08]]),\n shotgun: (n) => handled(n, ['wood_soft', [0.38, 0.06, 0.13]], ['steel', [0.7, 0.05, 0.06]]),\n bow: (n) => solid(n, 'wood_soft', [0.95, 0.03, 0.22], 0x5a3d22),\n crossbow: (n) => handled(n, ['wood_soft', [0.6, 0.06, 0.08]], ['steel', [0.12, 0.04, 0.26]]),\n melee_blunt: (n, id) =>\n id === 'baseball_bat'\n ? solid(n, 'wood_soft', [0.84, 0.06, 0.06], 0xb08550)\n : handled(n, ['wood_soft', [id === 'sledgehammer' ? 0.8 : 0.32, 0.03, 0.03]], ['steel', id === 'sledgehammer' ? [0.08, 0.08, 0.2] : [0.06, 0.04, 0.12]]),\n melee_blade: (n, id) => handled(n, ['wood_soft', [0.13, 0.03, 0.03]], ['steel', id === 'axe' ? [0.5, 0.03, 0.16] : id === 'knife' ? [0.16, 0.01, 0.03] : [0.45, 0.01, 0.05]]),\n tool: (n) => handled(n, ['plastic', [0.12, 0.025, 0.03]], ['steel', [0.16, 0.02, 0.03]]),\n flame: (n, id) => (id === 'flamethrower' ? handled(n, ['steel', [0.3, 0.14, 0.14]], ['steel', [0.6, 0.05, 0.05]]) : solid(n, 'plastic', [0.08, 0.02, 0.04], 0xd9482b)),\n heat: (n) => handled(n, ['steel', [0.2, 0.08, 0.08]], ['copper', [0.25, 0.03, 0.03]]),\n throwable: (n, id) => (id === 'molotov' ? solid(n, 'glass', [0.25, 0.07, 0.07], 0x7fd6b0) : solid(n, 'steel', [id === 'pipe_bomb' ? 0.25 : 0.1, 0.06, 0.06], 0x4c5a3a)),\n extinguisher: (n) => solid(n, 'steel', [0.5, 0.15, 0.15], 0xc0392b),\n}\n\n/** Body actions of the arsenal: nothing to hold or lay on a counter. */\nexport const BODY_ACTIONS: ReadonlySet<string> = new Set(['hand', 'kick', 'shove', 'fists'])\n\n// Real weights from the arsenal data: solid steel boxes would weigh several times more, go past a carry limit and stop being props.\nconst withMass = (prop: WeaponProp, kg: number): WeaponProp => {\n const volumes = prop.spec.parts.map((part) => (part.shape.kind === 'box' ? part.shape.size[0] * part.shape.size[1] * part.shape.size[2] : 0))\n const total = volumes.reduce((sum, volume) => sum + volume, 0) || 1\n return { ...prop, spec: { ...prop.spec, parts: prop.spec.parts.map((part, index) => ({ ...part, mass: (kg * volumes[index]) / total })) } }\n}\n\n/** The prop of a weapon: low-poly parts by category, the real mass of the weapon spread over them. */\nexport function weaponProp(def: Pick<WeaponDef, 'id' | 'category' | 'mass'>, name = def.id): WeaponProp {\n const shape = SHAPES[def.category] ?? SHAPES.tool\n return withMass(shape(name, def.id), def.mass)\n}\n\n/** One mesh per box part, named after the part (what destruction renderers bind to), in a group with its origin on the floor. */\nexport function buildProp(THREE: typeof THREE_NS, prop: WeaponProp): THREE_NS.Group {\n const group = new THREE.Group()\n group.name = prop.spec.name ?? 'weapon'\n for (const part of prop.spec.parts) {\n if (part.shape.kind !== 'box') continue\n const [w, h, d] = part.shape.size\n const material = new THREE.MeshStandardMaterial({ color: prop.colors[part.id] ?? 0x777777, roughness: 0.6, metalness: part.material === 'steel' ? 0.6 : 0.05 })\n const mesh = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), material)\n mesh.name = part.id\n mesh.position.set(part.position[0], part.position[1], part.position[2])\n mesh.castShadow = true\n mesh.receiveShadow = true\n group.add(mesh)\n }\n return group\n}\n\n/** The model of a weapon for the hands or a shelf: the same prop the counters show. */\nexport function buildWeaponModel(THREE: typeof THREE_NS, def: Pick<WeaponDef, 'id' | 'category' | 'mass'>): THREE_NS.Group {\n return buildProp(THREE, weaponProp(def))\n}\n","import type * as THREE_NS from 'three'\nimport type { HostEvent, HostTool } from '@adatechnology/game-contracts'\nimport type { Arsenal, WeaponDef } from '@adatechnology/game-weapons'\nimport type { WeaponAudio } from './audio'\nimport { BODY_ACTIONS, buildWeaponModel } from './props'\n\nexport interface WeaponFxOptions {\n THREE: typeof THREE_NS\n scene: THREE_NS.Scene\n camera: THREE_NS.Camera\n arsenal: Arsenal\n /** Height things rest on at (x, z): casings and magazines land there. */\n groundAt: (x: number, z: number) => number\n /** Sounds (`createWeaponAudio`); null or absent: silent. */\n audio?: WeaponAudio | null\n}\n\n/** The weapon in the hands, its fire and reload motion, ejected casings and dropped magazines, and their sounds. */\nexport interface WeaponFx {\n /** Feed every tool event of the host, with the tool as it is after the event. */\n handle(event: HostEvent, tool: HostTool): void\n update(dt: number): void\n /** Weapon shown in the hands, or null. */\n readonly inHand: string | null\n /** Casings and magazines currently on the floor or in the air. */\n readonly casings: number\n readonly magazines: number\n dispose(): void\n}\n\n/** Weapons that throw the spent case out on every shot. Revolvers keep them in the cylinder; break-actions open on reload. */\nconst EJECTS_ON_SHOT = new Set(['pistol', 'smg', 'carbine', 'rifle', 'sniper', 'shotgun'])\n/** Weapons whose reload drops a magazine. */\nconst DROPS_MAGAZINE = new Set(['pistol', 'smg', 'carbine', 'rifle', 'sniper'])\n/** Manual actions that cycle (pump, bolt) after each shot: the sound of the cycle. */\nconst CYCLES = new Set(['shotgun_pump_12', 'rifle_bolt_308'])\nconst MAX_CASINGS = 120\nconst MAX_MAGAZINES = 12\nconst LIFETIME_S = 25\nconst HAND_OFFSET: [number, number, number] = [0.2, -0.2, -0.42]\nconst GRAVITY = 9.81\n\ninterface Body {\n mesh: THREE_NS.Mesh\n velocity: THREE_NS.Vector3\n spin: THREE_NS.Vector3\n radius: number\n age: number\n resting: boolean\n bounces: number\n kind: 'casing' | 'magazine'\n}\n\nexport function createWeaponFx(options: WeaponFxOptions): WeaponFx {\n const { THREE, scene, camera, arsenal, groundAt } = options\n const audio = options.audio ?? null\n const bodies: Body[] = []\n const brass = new THREE.MeshStandardMaterial({ color: 0xc9a24a, metalness: 0.8, roughness: 0.35 })\n const shell = new THREE.MeshStandardMaterial({ color: 0xa8322b, roughness: 0.6 })\n const steel = new THREE.MeshStandardMaterial({ color: 0x2f3237, metalness: 0.5, roughness: 0.5 })\n const hand = new THREE.Group()\n hand.name = 'weapon-in-hand'\n scene.add(hand)\n let model: THREE_NS.Object3D | null = null\n let inHand: string | null = null\n let def: WeaponDef | null = null\n let kick = 0\n let dip = 0\n let reloading = false\n const v = new THREE.Vector3()\n const q = new THREE.Quaternion()\n const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2)\n\n const equip = (weapon: string): void => {\n if (model) hand.remove(model)\n model = null\n inHand = null\n def = arsenal.weapons.get(weapon) ?? null\n if (!def || BODY_ACTIONS.has(def.id)) return\n model = buildWeaponModel(THREE, def)\n const length = new THREE.Box3().setFromObject(model).getSize(v).x\n // long guns are drawn smaller so they do not fill the view\n model.scale.setScalar(length > 0.8 ? 0.55 : length > 0.4 ? 0.75 : 1)\n model.quaternion.copy(pointForward)\n hand.add(model)\n inHand = def.id\n }\n\n // a point on the gun in camera space, turned into the world\n const handPoint = (x: number, y: number, z: number, out: THREE_NS.Vector3): THREE_NS.Vector3 =>\n out.set(HAND_OFFSET[0] + x, HAND_OFFSET[1] + y, HAND_OFFSET[2] + z).applyQuaternion(camera.quaternion).add(camera.position)\n\n const throwBody = (kind: Body['kind'], mesh: THREE_NS.Mesh, from: THREE_NS.Vector3, velocity: THREE_NS.Vector3, radius: number): void => {\n const limit = kind === 'casing' ? MAX_CASINGS : MAX_MAGAZINES\n const same = bodies.filter((b) => b.kind === kind)\n if (same.length >= limit) removeBody(same[0])\n mesh.position.copy(from)\n mesh.castShadow = true\n scene.add(mesh)\n bodies.push({ mesh, velocity, spin: new THREE.Vector3(Math.random() * 20 - 10, Math.random() * 30 - 15, Math.random() * 20 - 10), radius, age: 0, resting: false, bounces: 0, kind })\n }\n\n const ejectCasing = (caliberMm: number, isShell: boolean, downward: boolean): void => {\n const radius = isShell ? 0.0105 : Math.max(0.003, (caliberMm / 1000) * 0.6)\n const length = isShell ? 0.068 : Math.min(0.07, Math.max(0.015, (caliberMm / 1000) * 2.6))\n const mesh = new THREE.Mesh(new THREE.CylinderGeometry(radius, radius, length, 8), isShell ? shell : brass)\n mesh.name = 'casing'\n const from = handPoint(0.03, 0.03, 0.05, new THREE.Vector3())\n const right = new THREE.Vector3(1, 0, 0).applyQuaternion(camera.quaternion)\n const up = new THREE.Vector3(0, 1, 0)\n const velocity = downward\n ? up.clone().multiplyScalar(-0.5).addScaledVector(right, (Math.random() - 0.5) * 0.4)\n : right.multiplyScalar(1.6 + Math.random() * 0.8).addScaledVector(up, 1.4 + Math.random() * 0.8)\n throwBody('casing', mesh, from, velocity, radius)\n }\n\n const dropMagazine = (category: string): void => {\n const long = category === 'rifle' || category === 'sniper' || category === 'carbine'\n const size: [number, number, number] = long ? [0.03, 0.17, 0.07] : [0.025, 0.11, 0.035]\n const mesh = new THREE.Mesh(new THREE.BoxGeometry(...size), steel)\n mesh.name = 'magazine'\n const from = handPoint(0, -0.06, 0, new THREE.Vector3())\n throwBody('magazine', mesh, from, new THREE.Vector3(0, -0.4, 0), size[1] / 2)\n audio?.reloadStart(category)\n }\n\n const caliberOf = (weapon: WeaponDef): number => {\n if (weapon.action.kind !== 'firearm') return 9\n return arsenal.calibers.get(weapon.action.caliber)?.diameter ?? 9\n }\n\n function removeBody(body: Body): void {\n body.mesh.removeFromParent()\n body.mesh.geometry.dispose()\n bodies.splice(bodies.indexOf(body), 1)\n }\n\n const onShot = (weapon: WeaponDef): void => {\n kick = 1\n const caliber = weapon.action.kind === 'firearm' ? arsenal.calibers.get(weapon.action.caliber) : undefined\n const energy = caliber ? 0.5 * (caliber.bulletGrams / 1000) * caliber.muzzleSpeed ** 2 * (caliber.pellets ?? 1) : 300\n audio?.shot(energy, weapon.category)\n if (CYCLES.has(weapon.id)) audio?.cycle()\n if (EJECTS_ON_SHOT.has(weapon.category) && weapon.id !== 'shotgun_double_12') ejectCasing(caliberOf(weapon), weapon.category === 'shotgun', false)\n }\n\n const onReloadStart = (weapon: WeaponDef, tool: HostTool): void => {\n if (DROPS_MAGAZINE.has(weapon.category)) dropMagazine(weapon.category)\n else audio?.reloadStart(weapon.category)\n // revolvers and break-action shotguns dump their spent cases when opened\n if (weapon.category === 'revolver' || weapon.id === 'shotgun_double_12') {\n const spent = Math.max(1, tool.capacity - tool.ammo)\n for (let i = 0; i < spent; i++) ejectCasing(caliberOf(weapon), weapon.category === 'shotgun', true)\n }\n }\n\n const stepBody = (body: Body, dt: number): void => {\n body.age += dt\n if (body.resting) return\n body.velocity.y -= GRAVITY * dt\n body.mesh.position.addScaledVector(body.velocity, dt)\n body.mesh.rotation.x += body.spin.x * dt\n body.mesh.rotation.y += body.spin.y * dt\n body.mesh.rotation.z += body.spin.z * dt\n const floor = groundAt(body.mesh.position.x, body.mesh.position.z) + body.radius\n if (body.mesh.position.y > floor) return\n body.mesh.position.y = floor\n if (Math.abs(body.velocity.y) > 0.7 && body.bounces < 4) {\n body.bounces++\n body.velocity.y = -body.velocity.y * 0.35\n body.velocity.x *= 0.55\n body.velocity.z *= 0.55\n body.spin.multiplyScalar(0.5)\n if (body.kind === 'casing') audio?.clink()\n else audio?.thud()\n return\n }\n if (body.kind === 'magazine' && body.bounces === 0) audio?.thud()\n body.resting = true\n body.mesh.rotation.x = Math.PI / 2\n body.mesh.rotation.z = 0\n }\n\n return {\n handle(event, tool) {\n const weapon = arsenal.weapons.get(event.weapon)\n if (event.type === 'tool') equip(event.weapon)\n if (!weapon) return\n if (event.type === 'shot') onShot(weapon)\n else if (event.type === 'empty') audio?.click()\n else if (event.type === 'reload' && event.phase === 'start') onReloadStart(weapon, tool)\n else if (event.type === 'reload' && event.phase === 'end') audio?.reloadEnd(weapon.category)\n reloading = tool.reloading\n },\n update(dt) {\n kick *= Math.exp(-dt * 14)\n dip += ((reloading ? 1 : 0) - dip) * Math.min(1, dt * 8)\n if (inHand) {\n handPoint(0, -0.12 * dip, 0.05 * kick, hand.position)\n q.setFromEuler(new THREE.Euler(0.14 * kick - 0.25 * dip, 0, 0.6 * dip))\n hand.quaternion.copy(camera.quaternion).multiply(q)\n }\n for (const body of [...bodies]) {\n stepBody(body, dt)\n if (body.age > LIFETIME_S) removeBody(body)\n }\n },\n get inHand() {\n return inHand\n },\n get casings() {\n return bodies.filter((b) => b.kind === 'casing').length\n },\n get magazines() {\n return bodies.filter((b) => b.kind === 'magazine').length\n },\n dispose() {\n for (const body of [...bodies]) removeBody(body)\n hand.removeFromParent()\n brass.dispose()\n shell.dispose()\n steel.dispose()\n },\n }\n}\n"],"mappings":"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","names":[]}