@adatechnology/game-weapons-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.
@@ -0,0 +1,2094 @@
1
+ "use strict";
2
+ var GameWeaponsThree = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ BODY_ACTIONS: () => BODY_ACTIONS,
25
+ COUNTER_ITEM_ID: () => COUNTER_ITEM_ID,
26
+ WEAPON_ITEM_PREFIX: () => WEAPON_ITEM_PREFIX,
27
+ buildProp: () => buildProp,
28
+ buildWeaponModel: () => buildWeaponModel,
29
+ createWeaponAudio: () => createWeaponAudio,
30
+ createWeaponFx: () => createWeaponFx,
31
+ createWeaponsTestModule: () => createWeaponsTestModule,
32
+ weaponProp: () => weaponProp,
33
+ weaponsLayout: () => weaponsLayout
34
+ });
35
+
36
+ // ../game-weapons/dist/index.js
37
+ var __defProp2 = Object.defineProperty;
38
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
39
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
40
+ var calibers_default = [
41
+ {
42
+ id: "22lr",
43
+ label: ".22 LR",
44
+ diameter: 5.7,
45
+ bulletGrams: 2.6,
46
+ muzzleSpeed: 350,
47
+ dragPer100m: 0.15,
48
+ kind: "rimfire"
49
+ },
50
+ {
51
+ id: "9x19",
52
+ label: "9\xD719 mm",
53
+ diameter: 9,
54
+ bulletGrams: 7.5,
55
+ muzzleSpeed: 365,
56
+ dragPer100m: 0.2,
57
+ kind: "fmj"
58
+ },
59
+ {
60
+ id: "38spl",
61
+ label: ".38 Special",
62
+ diameter: 9.1,
63
+ bulletGrams: 10.2,
64
+ muzzleSpeed: 247,
65
+ dragPer100m: 0.2,
66
+ kind: "fmj"
67
+ },
68
+ {
69
+ id: "357mag",
70
+ label: ".357 Magnum",
71
+ diameter: 9.1,
72
+ bulletGrams: 10.2,
73
+ muzzleSpeed: 396,
74
+ dragPer100m: 0.18,
75
+ kind: "hp"
76
+ },
77
+ {
78
+ id: "40sw",
79
+ label: ".40 S&W",
80
+ diameter: 10.2,
81
+ bulletGrams: 11.7,
82
+ muzzleSpeed: 310,
83
+ dragPer100m: 0.2,
84
+ kind: "fmj"
85
+ },
86
+ {
87
+ id: "45acp",
88
+ label: ".45 ACP",
89
+ diameter: 11.5,
90
+ bulletGrams: 14.9,
91
+ muzzleSpeed: 254,
92
+ dragPer100m: 0.2,
93
+ kind: "fmj"
94
+ },
95
+ {
96
+ id: "380acp",
97
+ label: ".380 ACP",
98
+ diameter: 9,
99
+ bulletGrams: 6.2,
100
+ muzzleSpeed: 295,
101
+ dragPer100m: 0.22,
102
+ kind: "fmj"
103
+ },
104
+ {
105
+ id: "5_56x45",
106
+ label: "5.56\xD745 mm",
107
+ diameter: 5.7,
108
+ bulletGrams: 4,
109
+ muzzleSpeed: 935,
110
+ dragPer100m: 0.1,
111
+ kind: "fmj"
112
+ },
113
+ {
114
+ id: "7_62x39",
115
+ label: "7.62\xD739 mm",
116
+ diameter: 7.9,
117
+ bulletGrams: 8,
118
+ muzzleSpeed: 725,
119
+ dragPer100m: 0.1,
120
+ kind: "fmj"
121
+ },
122
+ {
123
+ id: "7_62x51",
124
+ label: "7.62\xD751 mm (.308)",
125
+ diameter: 7.8,
126
+ bulletGrams: 9.7,
127
+ muzzleSpeed: 850,
128
+ dragPer100m: 0.08,
129
+ kind: "fmj"
130
+ },
131
+ {
132
+ id: "338lapua",
133
+ label: ".338 Lapua Magnum",
134
+ diameter: 8.6,
135
+ bulletGrams: 16.2,
136
+ muzzleSpeed: 903,
137
+ dragPer100m: 0.06,
138
+ kind: "fmj"
139
+ },
140
+ {
141
+ id: "50bmg",
142
+ label: ".50 BMG",
143
+ diameter: 12.7,
144
+ bulletGrams: 42,
145
+ muzzleSpeed: 925,
146
+ dragPer100m: 0.05,
147
+ kind: "ap"
148
+ },
149
+ {
150
+ id: "12ga_00buck",
151
+ label: "12 ga 00 buck",
152
+ diameter: 8.4,
153
+ bulletGrams: 3.5,
154
+ muzzleSpeed: 414,
155
+ pellets: 9,
156
+ dragPer100m: 0.45,
157
+ kind: "buck"
158
+ },
159
+ {
160
+ id: "12ga_slug",
161
+ label: "12 ga slug",
162
+ diameter: 18.5,
163
+ bulletGrams: 28,
164
+ muzzleSpeed: 463,
165
+ dragPer100m: 0.3,
166
+ kind: "slug"
167
+ },
168
+ {
169
+ id: "12ga_birdshot",
170
+ label: "12 ga birdshot",
171
+ diameter: 2.4,
172
+ bulletGrams: 0.2,
173
+ muzzleSpeed: 400,
174
+ pellets: 90,
175
+ dragPer100m: 0.6,
176
+ kind: "birdshot"
177
+ },
178
+ {
179
+ id: "20ga_buck",
180
+ label: "20 ga buck",
181
+ diameter: 6.9,
182
+ bulletGrams: 1.3,
183
+ muzzleSpeed: 400,
184
+ pellets: 20,
185
+ dragPer100m: 0.5,
186
+ kind: "buck"
187
+ },
188
+ {
189
+ id: "arrow",
190
+ label: "Arrow (compound bow)",
191
+ diameter: 7,
192
+ bulletGrams: 25,
193
+ muzzleSpeed: 90,
194
+ dragPer100m: 0.12,
195
+ kind: "arrow"
196
+ },
197
+ {
198
+ id: "crossbow_bolt",
199
+ label: "Crossbow bolt",
200
+ diameter: 8,
201
+ bulletGrams: 20,
202
+ muzzleSpeed: 122,
203
+ dragPer100m: 0.12,
204
+ kind: "bolt"
205
+ }
206
+ ];
207
+ var weapons_default = [
208
+ {
209
+ id: "hand",
210
+ name: "Hand",
211
+ category: "hand",
212
+ action: {
213
+ kind: "hand",
214
+ reach: 2
215
+ },
216
+ mass: 0,
217
+ tags: [
218
+ "grab"
219
+ ]
220
+ },
221
+ {
222
+ id: "kick",
223
+ name: "Kick",
224
+ category: "melee_blunt",
225
+ action: {
226
+ kind: "melee",
227
+ damage: "blunt",
228
+ energy: 250,
229
+ radius: 0.08,
230
+ reach: 1.1,
231
+ rpm: 60,
232
+ noise: 8
233
+ },
234
+ mass: 0
235
+ },
236
+ {
237
+ id: "shove",
238
+ name: "Shove",
239
+ category: "melee_blunt",
240
+ action: {
241
+ kind: "melee",
242
+ damage: "blunt",
243
+ energy: 80,
244
+ radius: 0.1,
245
+ reach: 1.5,
246
+ rpm: 90,
247
+ noise: 5
248
+ },
249
+ mass: 0
250
+ },
251
+ {
252
+ id: "pistol_9mm",
253
+ name: "9 mm service pistol",
254
+ category: "pistol",
255
+ action: {
256
+ kind: "firearm",
257
+ caliber: "9x19",
258
+ rpm: 300,
259
+ modes: [
260
+ "semi"
261
+ ],
262
+ magazine: 15,
263
+ reload: 2,
264
+ barrel: 1,
265
+ spreadDeg: 1,
266
+ bloomDeg: 0.6,
267
+ recoveryDegPerS: 4,
268
+ recoil: [
269
+ 1.2,
270
+ 0.4
271
+ ],
272
+ range: 50,
273
+ noise: 150
274
+ },
275
+ mass: 0.9,
276
+ tags: [
277
+ "sidearm"
278
+ ]
279
+ },
280
+ {
281
+ id: "pistol_45",
282
+ name: ".45 ACP pistol",
283
+ category: "pistol",
284
+ action: {
285
+ kind: "firearm",
286
+ caliber: "45acp",
287
+ rpm: 280,
288
+ modes: [
289
+ "semi"
290
+ ],
291
+ magazine: 8,
292
+ reload: 2.2,
293
+ barrel: 1,
294
+ spreadDeg: 1.1,
295
+ bloomDeg: 0.8,
296
+ recoveryDegPerS: 4,
297
+ recoil: [
298
+ 1.8,
299
+ 0.6
300
+ ],
301
+ range: 50,
302
+ noise: 160
303
+ },
304
+ mass: 1.1,
305
+ tags: [
306
+ "sidearm"
307
+ ]
308
+ },
309
+ {
310
+ id: "pistol_22",
311
+ name: ".22 LR pistol",
312
+ category: "pistol",
313
+ action: {
314
+ kind: "firearm",
315
+ caliber: "22lr",
316
+ rpm: 360,
317
+ modes: [
318
+ "semi"
319
+ ],
320
+ magazine: 10,
321
+ reload: 1.8,
322
+ barrel: 1,
323
+ spreadDeg: 0.9,
324
+ bloomDeg: 0.3,
325
+ recoveryDegPerS: 5,
326
+ recoil: [
327
+ 0.5,
328
+ 0.2
329
+ ],
330
+ range: 40,
331
+ noise: 90
332
+ },
333
+ mass: 0.7,
334
+ tags: [
335
+ "sidearm"
336
+ ]
337
+ },
338
+ {
339
+ id: "revolver_357",
340
+ name: ".357 Magnum revolver",
341
+ category: "revolver",
342
+ action: {
343
+ kind: "firearm",
344
+ caliber: "357mag",
345
+ rpm: 150,
346
+ modes: [
347
+ "single"
348
+ ],
349
+ magazine: 6,
350
+ reload: 4,
351
+ barrel: 1.05,
352
+ spreadDeg: 0.8,
353
+ bloomDeg: 1,
354
+ recoveryDegPerS: 3,
355
+ recoil: [
356
+ 3,
357
+ 0.9
358
+ ],
359
+ range: 60,
360
+ noise: 220
361
+ },
362
+ mass: 1.2,
363
+ tags: [
364
+ "sidearm"
365
+ ]
366
+ },
367
+ {
368
+ id: "revolver_38",
369
+ name: ".38 Special revolver",
370
+ category: "revolver",
371
+ action: {
372
+ kind: "firearm",
373
+ caliber: "38spl",
374
+ rpm: 160,
375
+ modes: [
376
+ "single"
377
+ ],
378
+ magazine: 6,
379
+ reload: 3.8,
380
+ barrel: 0.95,
381
+ spreadDeg: 0.9,
382
+ bloomDeg: 0.7,
383
+ recoveryDegPerS: 3.5,
384
+ recoil: [
385
+ 1.6,
386
+ 0.5
387
+ ],
388
+ range: 45,
389
+ noise: 170
390
+ },
391
+ mass: 0.9,
392
+ tags: [
393
+ "sidearm"
394
+ ]
395
+ },
396
+ {
397
+ id: "smg_9mm",
398
+ name: "9 mm submachine gun",
399
+ category: "smg",
400
+ action: {
401
+ kind: "firearm",
402
+ caliber: "9x19",
403
+ rpm: 800,
404
+ modes: [
405
+ "auto",
406
+ "burst",
407
+ "semi"
408
+ ],
409
+ burst: 3,
410
+ magazine: 30,
411
+ reload: 2.4,
412
+ barrel: 1.1,
413
+ spreadDeg: 1.5,
414
+ bloomDeg: 0.35,
415
+ recoveryDegPerS: 6,
416
+ recoil: [
417
+ 0.9,
418
+ 0.5
419
+ ],
420
+ range: 80,
421
+ noise: 180
422
+ },
423
+ mass: 2.6,
424
+ tags: [
425
+ "automatic"
426
+ ]
427
+ },
428
+ {
429
+ id: "carbine_22",
430
+ name: ".22 LR carbine",
431
+ category: "carbine",
432
+ action: {
433
+ kind: "firearm",
434
+ caliber: "22lr",
435
+ rpm: 300,
436
+ modes: [
437
+ "semi"
438
+ ],
439
+ magazine: 10,
440
+ reload: 2.2,
441
+ barrel: 1.4,
442
+ spreadDeg: 0.5,
443
+ bloomDeg: 0.15,
444
+ recoveryDegPerS: 5,
445
+ recoil: [
446
+ 0.4,
447
+ 0.15
448
+ ],
449
+ range: 80,
450
+ noise: 100
451
+ },
452
+ mass: 2.4,
453
+ tags: [
454
+ "small_game"
455
+ ]
456
+ },
457
+ {
458
+ id: "rifle_5_56",
459
+ name: "5.56 mm assault rifle",
460
+ category: "rifle",
461
+ action: {
462
+ kind: "firearm",
463
+ caliber: "5_56x45",
464
+ rpm: 750,
465
+ modes: [
466
+ "auto",
467
+ "burst",
468
+ "semi"
469
+ ],
470
+ burst: 3,
471
+ magazine: 30,
472
+ reload: 2.5,
473
+ barrel: 1,
474
+ spreadDeg: 0.6,
475
+ bloomDeg: 0.25,
476
+ recoveryDegPerS: 5,
477
+ recoil: [
478
+ 1,
479
+ 0.6
480
+ ],
481
+ range: 400,
482
+ noise: 300
483
+ },
484
+ mass: 3.4,
485
+ tags: [
486
+ "automatic"
487
+ ]
488
+ },
489
+ {
490
+ id: "rifle_7_62x39",
491
+ name: "7.62\xD739 mm assault rifle",
492
+ category: "rifle",
493
+ action: {
494
+ kind: "firearm",
495
+ caliber: "7_62x39",
496
+ rpm: 600,
497
+ modes: [
498
+ "auto",
499
+ "semi"
500
+ ],
501
+ magazine: 30,
502
+ reload: 2.8,
503
+ barrel: 1,
504
+ spreadDeg: 0.8,
505
+ bloomDeg: 0.35,
506
+ recoveryDegPerS: 4.5,
507
+ recoil: [
508
+ 1.5,
509
+ 0.8
510
+ ],
511
+ range: 350,
512
+ noise: 320
513
+ },
514
+ mass: 3.8,
515
+ tags: [
516
+ "automatic"
517
+ ]
518
+ },
519
+ {
520
+ id: "rifle_bolt_308",
521
+ name: ".308 bolt-action rifle",
522
+ category: "rifle",
523
+ action: {
524
+ kind: "firearm",
525
+ caliber: "7_62x51",
526
+ rpm: 40,
527
+ modes: [
528
+ "bolt"
529
+ ],
530
+ magazine: 5,
531
+ reload: 3.5,
532
+ barrel: 1.05,
533
+ spreadDeg: 0.25,
534
+ bloomDeg: 0.3,
535
+ recoveryDegPerS: 3,
536
+ recoil: [
537
+ 3,
538
+ 0.5
539
+ ],
540
+ range: 800,
541
+ noise: 350
542
+ },
543
+ mass: 4,
544
+ tags: [
545
+ "hunting"
546
+ ]
547
+ },
548
+ {
549
+ id: "sniper_338",
550
+ name: ".338 Lapua sniper rifle",
551
+ category: "sniper",
552
+ action: {
553
+ kind: "firearm",
554
+ caliber: "338lapua",
555
+ rpm: 35,
556
+ modes: [
557
+ "bolt"
558
+ ],
559
+ magazine: 5,
560
+ reload: 4,
561
+ barrel: 1.05,
562
+ spreadDeg: 0.1,
563
+ bloomDeg: 0.3,
564
+ recoveryDegPerS: 2.5,
565
+ recoil: [
566
+ 4.5,
567
+ 0.7
568
+ ],
569
+ range: 1500,
570
+ noise: 450
571
+ },
572
+ mass: 6.5,
573
+ tags: [
574
+ "long_range"
575
+ ]
576
+ },
577
+ {
578
+ id: "anti_materiel_50",
579
+ name: ".50 BMG anti-materiel rifle",
580
+ category: "sniper",
581
+ action: {
582
+ kind: "firearm",
583
+ caliber: "50bmg",
584
+ rpm: 30,
585
+ modes: [
586
+ "semi"
587
+ ],
588
+ magazine: 10,
589
+ reload: 4.5,
590
+ barrel: 1.05,
591
+ spreadDeg: 0.15,
592
+ bloomDeg: 0.5,
593
+ recoveryDegPerS: 2,
594
+ recoil: [
595
+ 7,
596
+ 1.2
597
+ ],
598
+ range: 1800,
599
+ noise: 600
600
+ },
601
+ mass: 12.5,
602
+ tags: [
603
+ "long_range",
604
+ "heavy"
605
+ ]
606
+ },
607
+ {
608
+ id: "shotgun_pump_12",
609
+ name: "12 gauge pump-action shotgun",
610
+ category: "shotgun",
611
+ action: {
612
+ kind: "firearm",
613
+ caliber: "12ga_00buck",
614
+ rpm: 60,
615
+ modes: [
616
+ "pump"
617
+ ],
618
+ magazine: 6,
619
+ reload: 0.6,
620
+ perRound: true,
621
+ barrel: 1,
622
+ spreadDeg: 1,
623
+ bloomDeg: 0.5,
624
+ recoveryDegPerS: 3,
625
+ pelletSpreadDeg: 3,
626
+ recoil: [
627
+ 4,
628
+ 1
629
+ ],
630
+ range: 40,
631
+ noise: 400
632
+ },
633
+ mass: 3.2,
634
+ tags: [
635
+ "shotgun"
636
+ ]
637
+ },
638
+ {
639
+ id: "shotgun_double_12",
640
+ name: "12 gauge double-barrel shotgun",
641
+ category: "shotgun",
642
+ action: {
643
+ kind: "firearm",
644
+ caliber: "12ga_00buck",
645
+ rpm: 120,
646
+ modes: [
647
+ "break"
648
+ ],
649
+ magazine: 2,
650
+ reload: 3.5,
651
+ barrel: 1,
652
+ spreadDeg: 1,
653
+ bloomDeg: 0.5,
654
+ recoveryDegPerS: 3,
655
+ pelletSpreadDeg: 3,
656
+ recoil: [
657
+ 4.5,
658
+ 1.2
659
+ ],
660
+ range: 35,
661
+ noise: 400
662
+ },
663
+ mass: 3,
664
+ tags: [
665
+ "shotgun"
666
+ ]
667
+ },
668
+ {
669
+ id: "shotgun_semi_12",
670
+ name: "12 gauge semi-automatic shotgun",
671
+ category: "shotgun",
672
+ action: {
673
+ kind: "firearm",
674
+ caliber: "12ga_00buck",
675
+ rpm: 180,
676
+ modes: [
677
+ "semi"
678
+ ],
679
+ magazine: 5,
680
+ reload: 0.7,
681
+ perRound: true,
682
+ barrel: 1,
683
+ spreadDeg: 1,
684
+ bloomDeg: 0.5,
685
+ recoveryDegPerS: 3,
686
+ pelletSpreadDeg: 3,
687
+ recoil: [
688
+ 3.4,
689
+ 1
690
+ ],
691
+ range: 40,
692
+ noise: 400
693
+ },
694
+ mass: 3.4,
695
+ tags: [
696
+ "shotgun"
697
+ ]
698
+ },
699
+ {
700
+ id: "bow_compound",
701
+ name: "Compound bow",
702
+ category: "bow",
703
+ action: {
704
+ kind: "firearm",
705
+ caliber: "arrow",
706
+ rpm: 20,
707
+ modes: [
708
+ "single"
709
+ ],
710
+ magazine: 1,
711
+ reload: 1.5,
712
+ barrel: 1,
713
+ spreadDeg: 0.4,
714
+ bloomDeg: 0.4,
715
+ recoveryDegPerS: 2,
716
+ recoil: [
717
+ 0.3,
718
+ 0.1
719
+ ],
720
+ range: 60,
721
+ noise: 20
722
+ },
723
+ mass: 1.8,
724
+ tags: [
725
+ "silent"
726
+ ]
727
+ },
728
+ {
729
+ id: "crossbow",
730
+ name: "Crossbow",
731
+ category: "crossbow",
732
+ action: {
733
+ kind: "firearm",
734
+ caliber: "crossbow_bolt",
735
+ rpm: 8,
736
+ modes: [
737
+ "single"
738
+ ],
739
+ magazine: 1,
740
+ reload: 4,
741
+ barrel: 1,
742
+ spreadDeg: 0.3,
743
+ bloomDeg: 0.3,
744
+ recoveryDegPerS: 2,
745
+ recoil: [
746
+ 0.5,
747
+ 0.1
748
+ ],
749
+ range: 70,
750
+ noise: 25
751
+ },
752
+ mass: 3,
753
+ tags: [
754
+ "silent"
755
+ ]
756
+ },
757
+ {
758
+ id: "hammer",
759
+ name: "Hammer",
760
+ category: "melee_blunt",
761
+ action: {
762
+ kind: "melee",
763
+ damage: "blunt",
764
+ energy: 60,
765
+ radius: 0.08,
766
+ reach: 0.7,
767
+ rpm: 70,
768
+ noise: 6
769
+ },
770
+ mass: 1
771
+ },
772
+ {
773
+ id: "sledgehammer",
774
+ name: "Sledgehammer",
775
+ category: "melee_blunt",
776
+ action: {
777
+ kind: "melee",
778
+ damage: "blunt",
779
+ energy: 300,
780
+ radius: 0.15,
781
+ reach: 1.1,
782
+ rpm: 30,
783
+ noise: 8
784
+ },
785
+ mass: 5.5,
786
+ tags: [
787
+ "heavy"
788
+ ]
789
+ },
790
+ {
791
+ id: "baseball_bat",
792
+ name: "Baseball bat",
793
+ category: "melee_blunt",
794
+ action: {
795
+ kind: "melee",
796
+ damage: "blunt",
797
+ energy: 150,
798
+ radius: 0.1,
799
+ reach: 1,
800
+ rpm: 60,
801
+ noise: 7
802
+ },
803
+ mass: 0.9
804
+ },
805
+ {
806
+ id: "crowbar",
807
+ name: "Crowbar",
808
+ category: "melee_blunt",
809
+ action: {
810
+ kind: "melee",
811
+ damage: "blunt",
812
+ energy: 120,
813
+ radius: 0.08,
814
+ reach: 0.9,
815
+ rpm: 60,
816
+ noise: 7
817
+ },
818
+ mass: 2,
819
+ tags: [
820
+ "pry"
821
+ ]
822
+ },
823
+ {
824
+ id: "machete",
825
+ name: "Machete",
826
+ category: "melee_blade",
827
+ action: {
828
+ kind: "melee",
829
+ damage: "slash",
830
+ energy: 140,
831
+ radius: 0.06,
832
+ reach: 0.8,
833
+ rpm: 80,
834
+ noise: 6
835
+ },
836
+ mass: 0.8
837
+ },
838
+ {
839
+ id: "axe",
840
+ name: "Axe",
841
+ category: "melee_blade",
842
+ action: {
843
+ kind: "melee",
844
+ damage: "slash",
845
+ energy: 250,
846
+ radius: 0.08,
847
+ reach: 0.9,
848
+ rpm: 45,
849
+ noise: 8
850
+ },
851
+ mass: 1.6
852
+ },
853
+ {
854
+ id: "knife",
855
+ name: "Knife",
856
+ category: "melee_blade",
857
+ action: {
858
+ kind: "melee",
859
+ damage: "slash",
860
+ energy: 40,
861
+ radius: 0.03,
862
+ reach: 0.4,
863
+ rpm: 100,
864
+ noise: 5
865
+ },
866
+ mass: 0.2
867
+ },
868
+ {
869
+ id: "fists",
870
+ name: "Fists",
871
+ category: "melee_blunt",
872
+ action: {
873
+ kind: "melee",
874
+ damage: "blunt",
875
+ energy: 25,
876
+ radius: 0.1,
877
+ reach: 0.6,
878
+ rpm: 120,
879
+ noise: 5
880
+ },
881
+ mass: 0
882
+ },
883
+ {
884
+ id: "lighter",
885
+ name: "Lighter",
886
+ category: "flame",
887
+ action: {
888
+ kind: "flame",
889
+ temperature: 400,
890
+ radius: 0.03,
891
+ range: 0.15,
892
+ fuelPerSecond: 0.02,
893
+ fuel: 60,
894
+ noise: 0
895
+ },
896
+ mass: 0.05,
897
+ tags: [
898
+ "fire"
899
+ ]
900
+ },
901
+ {
902
+ id: "flamethrower",
903
+ name: "Flamethrower",
904
+ category: "flame",
905
+ action: {
906
+ kind: "flame",
907
+ temperature: 900,
908
+ radius: 0.6,
909
+ range: 6,
910
+ fuelPerSecond: 1,
911
+ fuel: 20,
912
+ cone: true,
913
+ noise: 20
914
+ },
915
+ mass: 15,
916
+ tags: [
917
+ "fire"
918
+ ]
919
+ },
920
+ {
921
+ id: "molotov",
922
+ name: "Molotov cocktail",
923
+ category: "throwable",
924
+ action: {
925
+ kind: "throwable",
926
+ energy: 500,
927
+ radius: 1.5,
928
+ fuse: 0,
929
+ throwSpeed: 12,
930
+ fire: {
931
+ temperature: 700,
932
+ radius: 1.5,
933
+ seconds: 12
934
+ },
935
+ noise: 60
936
+ },
937
+ mass: 0.8,
938
+ tags: [
939
+ "fire",
940
+ "improvised"
941
+ ]
942
+ },
943
+ {
944
+ id: "cutting_torch",
945
+ name: "Oxy-fuel cutting torch",
946
+ category: "heat",
947
+ action: {
948
+ kind: "heat",
949
+ temperature: 3e3,
950
+ radius: 0.02,
951
+ range: 0.25,
952
+ fuelPerSecond: 0.05,
953
+ fuel: 30,
954
+ noise: 15
955
+ },
956
+ mass: 4,
957
+ tags: [
958
+ "fire",
959
+ "cutting"
960
+ ]
961
+ },
962
+ {
963
+ id: "blowtorch",
964
+ name: "Blowtorch",
965
+ category: "heat",
966
+ action: {
967
+ kind: "heat",
968
+ temperature: 1300,
969
+ radius: 0.04,
970
+ range: 0.3,
971
+ fuelPerSecond: 0.04,
972
+ fuel: 30,
973
+ noise: 10
974
+ },
975
+ mass: 0.6,
976
+ tags: [
977
+ "fire"
978
+ ]
979
+ },
980
+ {
981
+ id: "frag_grenade",
982
+ name: "Fragmentation grenade",
983
+ category: "throwable",
984
+ action: {
985
+ kind: "throwable",
986
+ energy: 2e5,
987
+ radius: 6,
988
+ fuse: 4,
989
+ throwSpeed: 15,
990
+ noise: 400
991
+ },
992
+ mass: 0.45,
993
+ tags: [
994
+ "explosive"
995
+ ]
996
+ },
997
+ {
998
+ id: "pipe_bomb",
999
+ name: "Pipe bomb",
1000
+ category: "throwable",
1001
+ action: {
1002
+ kind: "throwable",
1003
+ energy: 1e5,
1004
+ radius: 4,
1005
+ fuse: 3,
1006
+ throwSpeed: 13,
1007
+ noise: 350
1008
+ },
1009
+ mass: 0.7,
1010
+ tags: [
1011
+ "explosive",
1012
+ "improvised"
1013
+ ]
1014
+ },
1015
+ {
1016
+ id: "screwdriver",
1017
+ name: "Screwdriver",
1018
+ category: "tool",
1019
+ action: {
1020
+ kind: "tool",
1021
+ tool: "screwdriver",
1022
+ reach: 0.5,
1023
+ noise: 5
1024
+ },
1025
+ mass: 0.15
1026
+ },
1027
+ {
1028
+ id: "wrench",
1029
+ name: "Wrench",
1030
+ category: "tool",
1031
+ action: {
1032
+ kind: "tool",
1033
+ tool: "wrench",
1034
+ reach: 0.6,
1035
+ noise: 6
1036
+ },
1037
+ mass: 0.5
1038
+ },
1039
+ {
1040
+ id: "pry_bar",
1041
+ name: "Pry bar",
1042
+ category: "tool",
1043
+ action: {
1044
+ kind: "tool",
1045
+ tool: "pry_bar",
1046
+ reach: 0.8,
1047
+ noise: 8
1048
+ },
1049
+ mass: 0.8
1050
+ },
1051
+ {
1052
+ id: "hacksaw",
1053
+ name: "Hacksaw",
1054
+ category: "tool",
1055
+ action: {
1056
+ kind: "tool",
1057
+ tool: "saw",
1058
+ reach: 0.7,
1059
+ noise: 10
1060
+ },
1061
+ mass: 0.6
1062
+ },
1063
+ {
1064
+ id: "utility_knife",
1065
+ name: "Utility knife",
1066
+ category: "tool",
1067
+ action: {
1068
+ kind: "tool",
1069
+ tool: "knife",
1070
+ reach: 0.3,
1071
+ noise: 3
1072
+ },
1073
+ mass: 0.1
1074
+ },
1075
+ {
1076
+ id: "claw_hammer",
1077
+ name: "Claw hammer",
1078
+ category: "tool",
1079
+ action: {
1080
+ kind: "tool",
1081
+ tool: "hammer",
1082
+ reach: 0.5,
1083
+ noise: 8
1084
+ },
1085
+ mass: 0.7
1086
+ },
1087
+ {
1088
+ id: "extinguisher",
1089
+ name: "Fire extinguisher",
1090
+ category: "extinguisher",
1091
+ action: {
1092
+ kind: "extinguisher",
1093
+ range: 4,
1094
+ coolC: 300,
1095
+ charge: 12,
1096
+ noise: 25
1097
+ },
1098
+ mass: 5,
1099
+ tags: [
1100
+ "safety"
1101
+ ]
1102
+ }
1103
+ ];
1104
+ var modesEn = { single: "single action", semi: "semi-automatic", burst: "burst", auto: "automatic", pump: "pump action", bolt: "bolt action", lever: "lever action", break: "break action" };
1105
+ var categoriesEn = {
1106
+ pistol: "pistol",
1107
+ revolver: "revolver",
1108
+ smg: "submachine gun",
1109
+ rifle: "rifle",
1110
+ carbine: "carbine",
1111
+ sniper: "sniper rifle",
1112
+ shotgun: "shotgun",
1113
+ bow: "bow",
1114
+ crossbow: "crossbow",
1115
+ melee_blunt: "blunt melee",
1116
+ melee_blade: "bladed melee",
1117
+ flame: "flame",
1118
+ heat: "heat",
1119
+ throwable: "throwable",
1120
+ tool: "tool",
1121
+ extinguisher: "extinguisher",
1122
+ hand: "hand"
1123
+ };
1124
+ var locales = {
1125
+ en: {
1126
+ weapons: {
1127
+ hand: "Hand",
1128
+ kick: "Kick",
1129
+ shove: "Shove",
1130
+ pistol_9mm: "9 mm service pistol",
1131
+ pistol_45: ".45 ACP pistol",
1132
+ pistol_22: ".22 LR pistol",
1133
+ revolver_357: ".357 Magnum revolver",
1134
+ revolver_38: ".38 Special revolver",
1135
+ smg_9mm: "9 mm submachine gun",
1136
+ carbine_22: ".22 LR carbine",
1137
+ rifle_5_56: "5.56 mm assault rifle",
1138
+ rifle_7_62x39: "7.62\xD739 mm assault rifle",
1139
+ rifle_bolt_308: ".308 bolt-action rifle",
1140
+ sniper_338: ".338 Lapua sniper rifle",
1141
+ anti_materiel_50: ".50 BMG anti-materiel rifle",
1142
+ shotgun_pump_12: "12 gauge pump-action shotgun",
1143
+ shotgun_double_12: "12 gauge double-barrel shotgun",
1144
+ shotgun_semi_12: "12 gauge semi-automatic shotgun",
1145
+ bow_compound: "Compound bow",
1146
+ crossbow: "Crossbow",
1147
+ hammer: "Hammer",
1148
+ sledgehammer: "Sledgehammer",
1149
+ baseball_bat: "Baseball bat",
1150
+ crowbar: "Crowbar",
1151
+ machete: "Machete",
1152
+ axe: "Axe",
1153
+ knife: "Knife",
1154
+ fists: "Fists",
1155
+ lighter: "Lighter",
1156
+ flamethrower: "Flamethrower",
1157
+ molotov: "Molotov cocktail",
1158
+ cutting_torch: "Oxy-fuel cutting torch",
1159
+ blowtorch: "Blowtorch",
1160
+ frag_grenade: "Fragmentation grenade",
1161
+ pipe_bomb: "Pipe bomb",
1162
+ screwdriver: "Screwdriver",
1163
+ wrench: "Wrench",
1164
+ pry_bar: "Pry bar",
1165
+ hacksaw: "Hacksaw",
1166
+ utility_knife: "Utility knife",
1167
+ claw_hammer: "Claw hammer",
1168
+ extinguisher: "Fire extinguisher"
1169
+ },
1170
+ modes: modesEn,
1171
+ categories: categoriesEn
1172
+ },
1173
+ "pt-BR": {
1174
+ weapons: {
1175
+ hand: "m\xE3o",
1176
+ kick: "chute",
1177
+ shove: "empurr\xE3o",
1178
+ pistol_9mm: "pistola 9 mm",
1179
+ pistol_45: "pistola .45",
1180
+ pistol_22: "pistola .22",
1181
+ revolver_357: "rev\xF3lver .357",
1182
+ revolver_38: "rev\xF3lver .38",
1183
+ smg_9mm: "submetralhadora 9 mm",
1184
+ carbine_22: "carabina .22",
1185
+ rifle_5_56: "fuzil 5,56",
1186
+ rifle_7_62x39: "fuzil 7,62\xD739",
1187
+ rifle_bolt_308: "fuzil de ferrolho .308",
1188
+ sniper_338: "fuzil de precis\xE3o .338",
1189
+ anti_materiel_50: "fuzil antimaterial .50",
1190
+ shotgun_pump_12: "espingarda de repeti\xE7\xE3o calibre 12",
1191
+ shotgun_double_12: "espingarda de dois canos calibre 12",
1192
+ shotgun_semi_12: "espingarda semiautom\xE1tica calibre 12",
1193
+ bow_compound: "arco composto",
1194
+ crossbow: "besta",
1195
+ hammer: "martelo",
1196
+ sledgehammer: "marreta",
1197
+ baseball_bat: "taco de beisebol",
1198
+ crowbar: "p\xE9 de cabra",
1199
+ machete: "fac\xE3o",
1200
+ axe: "machado",
1201
+ knife: "faca",
1202
+ fists: "punhos",
1203
+ lighter: "isqueiro",
1204
+ flamethrower: "lan\xE7a-chamas",
1205
+ molotov: "coquetel molotov",
1206
+ cutting_torch: "ma\xE7arico de corte",
1207
+ blowtorch: "ma\xE7arico",
1208
+ frag_grenade: "granada de fragmenta\xE7\xE3o",
1209
+ pipe_bomb: "bomba caseira",
1210
+ screwdriver: "chave de fenda",
1211
+ wrench: "chave inglesa",
1212
+ pry_bar: "alavanca",
1213
+ hacksaw: "serra de arco",
1214
+ utility_knife: "estilete",
1215
+ claw_hammer: "martelo de unha",
1216
+ extinguisher: "extintor"
1217
+ },
1218
+ modes: { single: "tiro \xFAnico", semi: "semiautom\xE1tico", burst: "rajada", auto: "autom\xE1tico", pump: "repeti\xE7\xE3o", bolt: "ferrolho", lever: "alavanca", break: "basculante" },
1219
+ categories: {
1220
+ pistol: "pistola",
1221
+ revolver: "rev\xF3lver",
1222
+ smg: "submetralhadora",
1223
+ rifle: "fuzil",
1224
+ carbine: "carabina",
1225
+ sniper: "fuzil de precis\xE3o",
1226
+ shotgun: "espingarda",
1227
+ bow: "arco",
1228
+ crossbow: "besta",
1229
+ melee_blunt: "corpo a corpo contundente",
1230
+ melee_blade: "corpo a corpo cortante",
1231
+ flame: "chama",
1232
+ heat: "calor",
1233
+ throwable: "arremess\xE1vel",
1234
+ tool: "ferramenta",
1235
+ extinguisher: "extintor",
1236
+ hand: "m\xE3o"
1237
+ }
1238
+ }
1239
+ };
1240
+ var resolveStrings = (locale) => locales[locale ?? "en"] ?? locales.en;
1241
+ var createRng = (seed) => {
1242
+ let a = seed >>> 0;
1243
+ return () => {
1244
+ a = a + 1831565813 >>> 0;
1245
+ let t = a;
1246
+ t = Math.imul(t ^ t >>> 15, t | 1);
1247
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
1248
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
1249
+ };
1250
+ };
1251
+ var normalize = (v) => {
1252
+ const l = Math.hypot(v[0], v[1], v[2]);
1253
+ return l > 1e-12 ? [v[0] / l, v[1] / l, v[2] / l] : [0, 0, -1];
1254
+ };
1255
+ var cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
1256
+ var sampleCone = (dir, deg, rng) => {
1257
+ if (deg <= 0) return dir;
1258
+ const cosMax = Math.cos(deg * Math.PI / 180);
1259
+ const cosT = 1 - rng() * (1 - cosMax);
1260
+ const sinT = Math.sqrt(Math.max(0, 1 - cosT * cosT));
1261
+ const phi = rng() * Math.PI * 2;
1262
+ const helper = Math.abs(dir[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0];
1263
+ const u = normalize(cross(helper, dir));
1264
+ const v = cross(dir, u);
1265
+ const cx = Math.cos(phi) * sinT;
1266
+ const cy = Math.sin(phi) * sinT;
1267
+ return normalize([
1268
+ u[0] * cx + v[0] * cy + dir[0] * cosT,
1269
+ u[1] * cx + v[1] * cy + dir[1] * cosT,
1270
+ u[2] * cx + v[2] * cy + dir[2] * cosT
1271
+ ]);
1272
+ };
1273
+ var EPS = 1e-9;
1274
+ var DEFAULT_THROWABLES = 3;
1275
+ var BLOOM_CAP = 4;
1276
+ var THROW_LIFT = 0.15;
1277
+ var muzzleEnergy = (caliber, barrel = 1) => {
1278
+ const v = caliber.muzzleSpeed * barrel;
1279
+ return 0.5 * (caliber.bulletGrams / 1e3) * v * v;
1280
+ };
1281
+ var result = (status, extra = {}) => ({
1282
+ shots: [],
1283
+ uses: [],
1284
+ status,
1285
+ recoil: [0, 0],
1286
+ noise: 0,
1287
+ ...extra
1288
+ });
1289
+ var WeaponRuntime = class {
1290
+ constructor(init) {
1291
+ __publicField(this, "def");
1292
+ __publicField(this, "caliber");
1293
+ __publicField(this, "reserve");
1294
+ __publicField(this, "mode");
1295
+ __publicField(this, "_ammo");
1296
+ __publicField(this, "_capacity");
1297
+ __publicField(this, "action");
1298
+ __publicField(this, "rng");
1299
+ __publicField(this, "initialReserve");
1300
+ __publicField(this, "clock", 0);
1301
+ __publicField(this, "pend", null);
1302
+ __publicField(this, "readyAt", 0);
1303
+ __publicField(this, "latched", false);
1304
+ __publicField(this, "_reloading", false);
1305
+ __publicField(this, "reloadEnd", 0);
1306
+ __publicField(this, "extra", 0);
1307
+ __publicField(this, "extraClock", 0);
1308
+ this.def = init.def;
1309
+ this.caliber = init.caliber;
1310
+ this.action = init.def.action;
1311
+ this.rng = createRng(init.seed);
1312
+ const a = this.action;
1313
+ this.mode = a.kind === "firearm" ? a.modes[0] ?? "single" : "single";
1314
+ switch (a.kind) {
1315
+ case "firearm":
1316
+ this._capacity = a.magazine;
1317
+ break;
1318
+ case "flame":
1319
+ case "heat":
1320
+ this._capacity = a.fuel;
1321
+ break;
1322
+ case "extinguisher":
1323
+ this._capacity = a.charge;
1324
+ break;
1325
+ case "throwable":
1326
+ this._capacity = a.count ?? DEFAULT_THROWABLES;
1327
+ break;
1328
+ default:
1329
+ this._capacity = 0;
1330
+ }
1331
+ this._ammo = this._capacity;
1332
+ this.initialReserve = init.reserve ?? (a.kind === "firearm" ? a.magazine * 3 : 0);
1333
+ this.reserve = this.initialReserve;
1334
+ }
1335
+ get ammo() {
1336
+ return this._ammo;
1337
+ }
1338
+ get capacity() {
1339
+ return this._capacity;
1340
+ }
1341
+ get reloading() {
1342
+ return this._reloading;
1343
+ }
1344
+ get spread() {
1345
+ if (this.action.kind !== "firearm") return 0;
1346
+ return this.action.spreadDeg + this.extraAt(this.clock);
1347
+ }
1348
+ extraAt(t) {
1349
+ if (this.action.kind !== "firearm") return 0;
1350
+ const rec = this.action.recoveryDegPerS ?? 4;
1351
+ return Math.max(0, this.extra - rec * Math.max(0, t - this.extraClock));
1352
+ }
1353
+ // --- time -----------------------------------------------------------------------------------------------------------------------
1354
+ update(dt) {
1355
+ const d = Math.max(0, dt);
1356
+ let eff = d;
1357
+ if (this.pend && this.pend.src === "trigger") {
1358
+ eff = Math.max(0, d - this.pend.amt);
1359
+ this.pend = null;
1360
+ } else {
1361
+ this.pend = { src: "update", amt: d };
1362
+ }
1363
+ this.clock += eff;
1364
+ this.settle(this.clock);
1365
+ }
1366
+ /** Complete the reload steps that finish at or before `t`. */
1367
+ settle(t) {
1368
+ const a = this.action;
1369
+ if (a.kind !== "firearm") return;
1370
+ while (this._reloading && this.reloadEnd <= t + EPS) {
1371
+ if (a.perRound) {
1372
+ if (this.reserve > 0 && this._ammo < this._capacity) {
1373
+ this._ammo += 1;
1374
+ this.reserve -= 1;
1375
+ }
1376
+ if (this._ammo >= this._capacity || this.reserve <= 0) this._reloading = false;
1377
+ else this.reloadEnd += a.reload;
1378
+ } else {
1379
+ const take = Math.min(this._capacity - this._ammo, this.reserve);
1380
+ this._ammo += take;
1381
+ this.reserve -= take;
1382
+ this._reloading = false;
1383
+ }
1384
+ }
1385
+ }
1386
+ // --- commands -------------------------------------------------------------------------------------------------------------------
1387
+ release() {
1388
+ this.latched = false;
1389
+ }
1390
+ reload() {
1391
+ const a = this.action;
1392
+ if (a.kind === "throwable") {
1393
+ if (this._ammo >= this._capacity || this.reserve <= 0) return false;
1394
+ this._ammo += 1;
1395
+ this.reserve -= 1;
1396
+ return true;
1397
+ }
1398
+ if (a.kind !== "firearm" || this._reloading) return false;
1399
+ if (this._ammo >= this._capacity || this.reserve <= 0) return false;
1400
+ this._reloading = true;
1401
+ this.reloadEnd = this.clock + a.reload;
1402
+ return true;
1403
+ }
1404
+ cycleMode() {
1405
+ if (this.action.kind !== "firearm") return this.mode;
1406
+ const modes = this.action.modes;
1407
+ const i = modes.indexOf(this.mode);
1408
+ this.mode = modes[(i + 1) % modes.length] ?? this.mode;
1409
+ return this.mode;
1410
+ }
1411
+ refill() {
1412
+ this._ammo = this._capacity;
1413
+ this.reserve = this.initialReserve;
1414
+ this._reloading = false;
1415
+ this.readyAt = this.clock;
1416
+ this.latched = false;
1417
+ this.extra = 0;
1418
+ }
1419
+ // --- trigger --------------------------------------------------------------------------------------------------------------------
1420
+ trigger(origin, direction, dt, held = false, source) {
1421
+ const d = Math.max(0, dt);
1422
+ let overlap = 0;
1423
+ if (this.pend && this.pend.src === "update") {
1424
+ overlap = Math.min(this.pend.amt, d);
1425
+ this.pend = null;
1426
+ } else {
1427
+ this.pend = { src: "trigger", amt: d };
1428
+ }
1429
+ const start = this.clock - overlap;
1430
+ const end = start + d;
1431
+ this.settle(start);
1432
+ const dir = normalize(direction);
1433
+ let res;
1434
+ const a = this.action;
1435
+ switch (a.kind) {
1436
+ case "firearm":
1437
+ res = this.fireFirearm(a, origin, dir, start, end, held, source);
1438
+ break;
1439
+ case "melee":
1440
+ res = this.fireMelee(a, origin, dir, start, end);
1441
+ break;
1442
+ case "flame":
1443
+ case "heat":
1444
+ res = this.fireFuel(a, origin, dir, d);
1445
+ break;
1446
+ case "extinguisher":
1447
+ res = this.fireExtinguisher(a, origin, dir, d);
1448
+ break;
1449
+ case "throwable":
1450
+ res = this.fireThrowable(a, origin, dir, held);
1451
+ break;
1452
+ case "tool":
1453
+ res = d > 0 ? result("ok", { uses: [{ kind: "tool", tool: a.tool, reach: a.reach, duration: d, origin, direction: dir }], noise: a.noise }) : result("ok");
1454
+ break;
1455
+ case "hand":
1456
+ res = this.oncePerPull(held) ? result("ok", { uses: [{ kind: "hand", reach: a.reach, origin, direction: dir }] }) : result("cooldown");
1457
+ break;
1458
+ }
1459
+ this.clock = Math.max(this.clock, end);
1460
+ this.settle(this.clock);
1461
+ return res;
1462
+ }
1463
+ /** Semi-style gating: a held trigger that already fired does nothing until `release()` or a new pull (`held` false). */
1464
+ oncePerPull(held) {
1465
+ if (!held) this.latched = false;
1466
+ if (this.latched) return false;
1467
+ this.latched = true;
1468
+ return true;
1469
+ }
1470
+ fireFirearm(a, origin, dir, start, end, held, source) {
1471
+ if (this._reloading) {
1472
+ if (a.perRound && this._ammo > 0) this._reloading = false;
1473
+ else return result("reloading");
1474
+ }
1475
+ const auto = this.mode === "auto";
1476
+ if (!auto && !this.oncePerPull(held)) return result("cooldown");
1477
+ if (this._ammo <= 0) return result("empty");
1478
+ const iv = 60 / a.rpm;
1479
+ const t0 = Math.max(start, this.readyAt);
1480
+ if (t0 > end + EPS) return result("cooldown");
1481
+ const res = result("ok");
1482
+ const fire = (t) => {
1483
+ this.fireOne(a, origin, dir, t, res, source);
1484
+ };
1485
+ if (auto) {
1486
+ for (let t = t0, n = 0; this._ammo > 0 && (n === 0 ? t <= end + EPS : t < end - EPS); t += iv, n++) fire(t);
1487
+ } else if (this.mode === "burst") {
1488
+ const count = Math.min(a.burst ?? 3, this._ammo);
1489
+ for (let k = 0; k < count; k++) fire(t0 + k * iv);
1490
+ } else {
1491
+ fire(t0);
1492
+ }
1493
+ return res;
1494
+ }
1495
+ fireOne(a, origin, dir, t, res, source) {
1496
+ const cal = this.caliber;
1497
+ const barrel = a.barrel ?? 1;
1498
+ const speed = cal.muzzleSpeed * barrel;
1499
+ const energy = muzzleEnergy(cal, barrel);
1500
+ const drag = cal.dragPer100m ?? 0.1;
1501
+ const energyAt = (distance) => {
1502
+ const f = Math.pow(1 - drag, Math.max(0, distance) / 100);
1503
+ return energy * f * f;
1504
+ };
1505
+ const bloomNow = this.extraAt(t);
1506
+ const spread = a.spreadDeg + bloomNow;
1507
+ const aim = sampleCone(dir, spread, this.rng);
1508
+ const pellets = cal.pellets ?? 1;
1509
+ const pelletSpread = pellets > 1 ? a.pelletSpreadDeg ?? 3 : 0;
1510
+ for (let p = 0; p < pellets; p++) {
1511
+ const shot = {
1512
+ origin: [origin[0], origin[1], origin[2]],
1513
+ direction: sampleCone(aim, pelletSpread, this.rng),
1514
+ energy,
1515
+ caliber: cal.diameter,
1516
+ caliberId: cal.id,
1517
+ speed,
1518
+ energyAt,
1519
+ pellet: p,
1520
+ weapon: this.def.id
1521
+ };
1522
+ if (source !== void 0) shot.source = source;
1523
+ res.shots.push(shot);
1524
+ }
1525
+ this._ammo -= 1;
1526
+ this.readyAt = t + 60 / a.rpm;
1527
+ this.extra = Math.min(bloomNow + (a.bloomDeg ?? 0), a.spreadDeg * (BLOOM_CAP - 1));
1528
+ this.extraClock = t;
1529
+ res.recoil = [res.recoil[0] + a.recoil[0], res.recoil[1] + (this.rng() * 2 - 1) * a.recoil[1]];
1530
+ res.noise = a.noise;
1531
+ }
1532
+ fireMelee(a, origin, dir, start, end) {
1533
+ const t0 = Math.max(start, this.readyAt);
1534
+ if (t0 > end + EPS) return result("cooldown");
1535
+ this.readyAt = t0 + 60 / a.rpm;
1536
+ const use = { kind: "melee", damage: a.damage, energy: a.energy, radius: a.radius, reach: a.reach, origin, direction: dir };
1537
+ return result("ok", { uses: [use], noise: a.noise });
1538
+ }
1539
+ fireFuel(a, origin, dir, dt) {
1540
+ if (this._ammo <= EPS) return result("no_fuel");
1541
+ if (dt <= 0) return result("ok");
1542
+ const need = a.fuelPerSecond * dt;
1543
+ let duration = dt;
1544
+ if (need > this._ammo) {
1545
+ duration = dt * (this._ammo / need);
1546
+ this._ammo = 0;
1547
+ } else {
1548
+ this._ammo -= need;
1549
+ }
1550
+ const use = a.kind === "flame" ? { kind: "flame", temperature: a.temperature, radius: a.radius, range: a.range, cone: a.cone ?? false, duration, origin, direction: dir } : { kind: "heat", temperature: a.temperature, radius: a.radius, range: a.range, duration, origin, direction: dir };
1551
+ return result("ok", { uses: [use], noise: a.noise });
1552
+ }
1553
+ fireExtinguisher(a, origin, dir, dt) {
1554
+ if (this._ammo <= EPS) return result("no_fuel");
1555
+ if (dt <= 0) return result("ok");
1556
+ const duration = Math.min(dt, this._ammo);
1557
+ this._ammo -= duration;
1558
+ return result("ok", { uses: [{ kind: "extinguish", range: a.range, coolC: a.coolC, duration, origin, direction: dir }], noise: a.noise });
1559
+ }
1560
+ fireThrowable(a, origin, dir, held) {
1561
+ if (!this.oncePerPull(held)) return result("cooldown");
1562
+ if (this._ammo <= 0) return result("empty");
1563
+ this._ammo -= 1;
1564
+ const velocity = [dir[0] * a.throwSpeed, dir[1] * a.throwSpeed + a.throwSpeed * THROW_LIFT, dir[2] * a.throwSpeed];
1565
+ const use = { kind: "throw", energy: a.energy, radius: a.radius, fuse: a.fuse, velocity, origin };
1566
+ if (a.fire) use.fire = { ...a.fire };
1567
+ return result("ok", { uses: [use], noise: a.noise });
1568
+ }
1569
+ };
1570
+ var CALIBERS = calibers_default;
1571
+ var WEAPONS = weapons_default;
1572
+ var createArsenal = (options = {}) => {
1573
+ const calibers = /* @__PURE__ */ new Map();
1574
+ for (const c of [...CALIBERS, ...options.calibers ?? []]) calibers.set(c.id, c);
1575
+ const weapons = /* @__PURE__ */ new Map();
1576
+ for (const w of [...WEAPONS, ...options.weapons ?? []]) weapons.set(w.id, w);
1577
+ const strings = resolveStrings(options.locale);
1578
+ const baseSeed = options.seed ?? 1;
1579
+ let counter = 0;
1580
+ const create = (id, opts = {}) => {
1581
+ const def = weapons.get(id);
1582
+ if (!def) throw new Error(`game-weapons: unknown weapon "${id}"`);
1583
+ let caliber = null;
1584
+ if (def.action.kind === "firearm") {
1585
+ caliber = calibers.get(def.action.caliber) ?? null;
1586
+ if (!caliber) throw new Error(`game-weapons: weapon "${id}" uses unknown calibre "${def.action.caliber}"`);
1587
+ }
1588
+ counter += 1;
1589
+ const seed = opts.seed ?? baseSeed + Math.imul(counter, 2654435761) >>> 0;
1590
+ return new WeaponRuntime({ def, caliber, seed, reserve: opts.reserve });
1591
+ };
1592
+ return {
1593
+ calibers,
1594
+ weapons,
1595
+ create,
1596
+ muzzleEnergy(caliber, barrel = 1) {
1597
+ const c = calibers.get(caliber);
1598
+ if (!c) throw new Error(`game-weapons: unknown calibre "${caliber}"`);
1599
+ return muzzleEnergy(c, barrel);
1600
+ },
1601
+ name(id) {
1602
+ const w = weapons.get(id);
1603
+ if (w) return strings.weapons[id] ?? w.name ?? id;
1604
+ return strings.modes[id] ?? strings.categories[id] ?? calibers.get(id)?.label ?? id;
1605
+ },
1606
+ list(category) {
1607
+ const all = [...weapons.values()];
1608
+ return category ? all.filter((w) => w.category === category) : all;
1609
+ }
1610
+ };
1611
+ };
1612
+
1613
+ // src/audio.ts
1614
+ var NOISE_SECONDS = 0.6;
1615
+ function createWeaponAudio(volume = 0.6) {
1616
+ const Ctor = globalThis.AudioContext ?? globalThis.webkitAudioContext;
1617
+ let ctx = null;
1618
+ let master = null;
1619
+ let noise = null;
1620
+ let level = volume;
1621
+ const ready = () => {
1622
+ if (!Ctor) return null;
1623
+ if (!ctx) {
1624
+ ctx = new Ctor();
1625
+ master = ctx.createGain();
1626
+ master.gain.value = level;
1627
+ master.connect(ctx.destination);
1628
+ noise = ctx.createBuffer(1, Math.floor(ctx.sampleRate * NOISE_SECONDS), ctx.sampleRate);
1629
+ const data = noise.getChannelData(0);
1630
+ for (let i = 0; i < data.length; i++) data[i] = Math.random() * 2 - 1;
1631
+ }
1632
+ if (ctx.state === "suspended") void ctx.resume();
1633
+ return ctx;
1634
+ };
1635
+ const burst = (options) => {
1636
+ const audio = ready();
1637
+ if (!audio || !noise || !master) return;
1638
+ const start = audio.currentTime + (options.at ?? 0);
1639
+ const source = audio.createBufferSource();
1640
+ source.buffer = noise;
1641
+ const filter = audio.createBiquadFilter();
1642
+ filter.type = options.filter;
1643
+ filter.frequency.value = options.frequency;
1644
+ filter.Q.value = options.q ?? 0.8;
1645
+ const gain = audio.createGain();
1646
+ gain.gain.setValueAtTime(1e-4, start);
1647
+ gain.gain.exponentialRampToValueAtTime(options.gain, start + 4e-3);
1648
+ gain.gain.exponentialRampToValueAtTime(1e-4, start + options.seconds);
1649
+ source.connect(filter).connect(gain).connect(master);
1650
+ source.start(start);
1651
+ source.stop(start + options.seconds + 0.05);
1652
+ };
1653
+ const tone = (options) => {
1654
+ const audio = ready();
1655
+ if (!audio || !master) return;
1656
+ const start = audio.currentTime + (options.at ?? 0);
1657
+ const osc = audio.createOscillator();
1658
+ osc.type = options.type ?? "sine";
1659
+ osc.frequency.setValueAtTime(options.frequency, start);
1660
+ if (options.to) osc.frequency.exponentialRampToValueAtTime(options.to, start + options.seconds);
1661
+ const gain = audio.createGain();
1662
+ gain.gain.setValueAtTime(1e-4, start);
1663
+ gain.gain.exponentialRampToValueAtTime(options.gain, start + 3e-3);
1664
+ gain.gain.exponentialRampToValueAtTime(1e-4, start + options.seconds);
1665
+ osc.connect(gain).connect(master);
1666
+ osc.start(start);
1667
+ osc.stop(start + options.seconds + 0.05);
1668
+ };
1669
+ const metal = (at, frequency, gain) => {
1670
+ burst({ at, seconds: 0.05, gain, filter: "highpass", frequency, q: 2 });
1671
+ tone({ at, seconds: 0.04, gain: gain * 0.6, frequency: frequency * 0.6, type: "square" });
1672
+ };
1673
+ return {
1674
+ shot(energyJ, category) {
1675
+ const k = Math.min(1, Math.max(0, Math.log10(Math.max(energyJ, 50) / 50) / 2.5));
1676
+ const shotgun = category === "shotgun";
1677
+ burst({ seconds: 0.12 + k * 0.35 + (shotgun ? 0.1 : 0), gain: 0.5 + k * 0.5, filter: "lowpass", frequency: 4200 - k * 2600 });
1678
+ burst({ seconds: 0.03, gain: 0.6, filter: "highpass", frequency: 3e3 });
1679
+ tone({ seconds: 0.14 + k * 0.2, gain: 0.35 + k * 0.4, frequency: 140 - k * 70, to: 40 });
1680
+ },
1681
+ click() {
1682
+ metal(0, 2600, 0.25);
1683
+ },
1684
+ reloadStart(category) {
1685
+ metal(0, 1500, 0.35);
1686
+ if (category !== "shotgun" && category !== "revolver") metal(0.12, 900, 0.2);
1687
+ },
1688
+ reloadEnd(category) {
1689
+ metal(0, 1100, 0.45);
1690
+ if (category !== "revolver") metal(0.18, 1900, 0.4);
1691
+ },
1692
+ cycle() {
1693
+ metal(0.22, 900, 0.35);
1694
+ metal(0.36, 1300, 0.35);
1695
+ },
1696
+ clink() {
1697
+ const f = 3200 + Math.random() * 2e3;
1698
+ tone({ seconds: 0.09, gain: 0.12, frequency: f });
1699
+ tone({ seconds: 0.06, gain: 0.06, frequency: f * 2.7 });
1700
+ },
1701
+ thud() {
1702
+ burst({ seconds: 0.09, gain: 0.35, filter: "lowpass", frequency: 380 });
1703
+ },
1704
+ setVolume(next) {
1705
+ level = Math.max(0, Math.min(1, next));
1706
+ if (master) master.gain.value = level;
1707
+ },
1708
+ dispose() {
1709
+ void ctx?.close();
1710
+ ctx = null;
1711
+ master = null;
1712
+ noise = null;
1713
+ }
1714
+ };
1715
+ }
1716
+
1717
+ // src/props.ts
1718
+ var box = (id, material, size, position, extra = {}) => ({ id, material, shape: { kind: "box", size }, position, ...extra });
1719
+ var joint = (a, b, kind) => ({ a, b, kind });
1720
+ var handled = (name, handle, head) => {
1721
+ const [handleMaterial, rawHandle] = handle;
1722
+ const [headMaterial, rawHead] = head;
1723
+ const thickness = Math.max(rawHandle[1], rawHead[1]);
1724
+ const handleSize = [rawHandle[0], thickness, rawHandle[2]];
1725
+ const headSize = [rawHead[0], thickness, rawHead[2]];
1726
+ const shift = (handleSize[0] - headSize[0]) / 2;
1727
+ return {
1728
+ spec: {
1729
+ name,
1730
+ parts: [box("handle", handleMaterial, handleSize, [-handleSize[0] / 2 + shift, thickness / 2, 0]), box("head", headMaterial, headSize, [headSize[0] / 2 + shift, thickness / 2, 0])],
1731
+ joints: [joint("handle", "head", "screw")],
1732
+ tags: ["weapon"]
1733
+ },
1734
+ colors: { handle: handleMaterial === "steel" ? 3816770 : handleMaterial === "plastic" ? 2829616 : 8017200, head: headMaterial === "steel" ? 9410206 : headMaterial === "copper" ? 12088115 : 7031338 }
1735
+ };
1736
+ };
1737
+ var solid = (name, material, size, color) => ({
1738
+ spec: { name, parts: [box("body", material, size, [0, size[1] / 2, 0])], joints: [], tags: ["weapon"] },
1739
+ colors: { body: color }
1740
+ });
1741
+ var SHAPES = {
1742
+ pistol: (n) => handled(n, ["plastic", [0.06, 0.03, 0.11]], ["steel", [0.2, 0.03, 0.04]]),
1743
+ revolver: (n) => handled(n, ["wood_soft", [0.06, 0.035, 0.11]], ["steel", [0.24, 0.04, 0.05]]),
1744
+ smg: (n) => handled(n, ["plastic", [0.2, 0.05, 0.14]], ["steel", [0.45, 0.05, 0.06]]),
1745
+ carbine: (n) => handled(n, ["wood_soft", [0.35, 0.05, 0.12]], ["steel", [0.6, 0.04, 0.05]]),
1746
+ rifle: (n) => handled(n, ["wood_soft", [0.35, 0.06, 0.13]], ["steel", [0.75, 0.05, 0.07]]),
1747
+ sniper: (n) => handled(n, ["plastic", [0.4, 0.06, 0.14]], ["steel", [0.95, 0.06, 0.08]]),
1748
+ shotgun: (n) => handled(n, ["wood_soft", [0.38, 0.06, 0.13]], ["steel", [0.7, 0.05, 0.06]]),
1749
+ bow: (n) => solid(n, "wood_soft", [0.95, 0.03, 0.22], 5913890),
1750
+ crossbow: (n) => handled(n, ["wood_soft", [0.6, 0.06, 0.08]], ["steel", [0.12, 0.04, 0.26]]),
1751
+ 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]]),
1752
+ 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]]),
1753
+ tool: (n) => handled(n, ["plastic", [0.12, 0.025, 0.03]], ["steel", [0.16, 0.02, 0.03]]),
1754
+ 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),
1755
+ heat: (n) => handled(n, ["steel", [0.2, 0.08, 0.08]], ["copper", [0.25, 0.03, 0.03]]),
1756
+ 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),
1757
+ extinguisher: (n) => solid(n, "steel", [0.5, 0.15, 0.15], 12597547)
1758
+ };
1759
+ var BODY_ACTIONS = /* @__PURE__ */ new Set(["hand", "kick", "shove", "fists"]);
1760
+ var withMass = (prop, kg) => {
1761
+ const volumes = prop.spec.parts.map((part) => part.shape.kind === "box" ? part.shape.size[0] * part.shape.size[1] * part.shape.size[2] : 0);
1762
+ const total = volumes.reduce((sum, volume) => sum + volume, 0) || 1;
1763
+ return { ...prop, spec: { ...prop.spec, parts: prop.spec.parts.map((part, index) => ({ ...part, mass: kg * volumes[index] / total })) } };
1764
+ };
1765
+ function weaponProp(def, name = def.id) {
1766
+ const shape = SHAPES[def.category] ?? SHAPES.tool;
1767
+ return withMass(shape(name, def.id), def.mass);
1768
+ }
1769
+ function buildProp(THREE, prop) {
1770
+ const group = new THREE.Group();
1771
+ group.name = prop.spec.name ?? "weapon";
1772
+ for (const part of prop.spec.parts) {
1773
+ if (part.shape.kind !== "box") continue;
1774
+ const [w, h, d] = part.shape.size;
1775
+ const material = new THREE.MeshStandardMaterial({ color: prop.colors[part.id] ?? 7829367, roughness: 0.6, metalness: part.material === "steel" ? 0.6 : 0.05 });
1776
+ const mesh = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), material);
1777
+ mesh.name = part.id;
1778
+ mesh.position.set(part.position[0], part.position[1], part.position[2]);
1779
+ mesh.castShadow = true;
1780
+ mesh.receiveShadow = true;
1781
+ group.add(mesh);
1782
+ }
1783
+ return group;
1784
+ }
1785
+ function buildWeaponModel(THREE, def) {
1786
+ return buildProp(THREE, weaponProp(def));
1787
+ }
1788
+
1789
+ // src/fx.ts
1790
+ var EJECTS_ON_SHOT = /* @__PURE__ */ new Set(["pistol", "smg", "carbine", "rifle", "sniper", "shotgun"]);
1791
+ var DROPS_MAGAZINE = /* @__PURE__ */ new Set(["pistol", "smg", "carbine", "rifle", "sniper"]);
1792
+ var CYCLES = /* @__PURE__ */ new Set(["shotgun_pump_12", "rifle_bolt_308"]);
1793
+ var MAX_CASINGS = 120;
1794
+ var MAX_MAGAZINES = 12;
1795
+ var LIFETIME_S = 25;
1796
+ var HAND_OFFSET = [0.2, -0.2, -0.42];
1797
+ var GRAVITY = 9.81;
1798
+ function createWeaponFx(options) {
1799
+ const { THREE, scene, camera, arsenal, groundAt } = options;
1800
+ const audio = options.audio ?? null;
1801
+ const bodies = [];
1802
+ const brass = new THREE.MeshStandardMaterial({ color: 13214282, metalness: 0.8, roughness: 0.35 });
1803
+ const shell = new THREE.MeshStandardMaterial({ color: 11022891, roughness: 0.6 });
1804
+ const steel = new THREE.MeshStandardMaterial({ color: 3093047, metalness: 0.5, roughness: 0.5 });
1805
+ const hand = new THREE.Group();
1806
+ hand.name = "weapon-in-hand";
1807
+ scene.add(hand);
1808
+ let model = null;
1809
+ let inHand = null;
1810
+ let def = null;
1811
+ let kick = 0;
1812
+ let dip = 0;
1813
+ let reloading = false;
1814
+ const v = new THREE.Vector3();
1815
+ const q = new THREE.Quaternion();
1816
+ const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
1817
+ const equip = (weapon) => {
1818
+ if (model) hand.remove(model);
1819
+ model = null;
1820
+ inHand = null;
1821
+ def = arsenal.weapons.get(weapon) ?? null;
1822
+ if (!def || BODY_ACTIONS.has(def.id)) return;
1823
+ model = buildWeaponModel(THREE, def);
1824
+ const length = new THREE.Box3().setFromObject(model).getSize(v).x;
1825
+ model.scale.setScalar(length > 0.8 ? 0.55 : length > 0.4 ? 0.75 : 1);
1826
+ model.quaternion.copy(pointForward);
1827
+ hand.add(model);
1828
+ inHand = def.id;
1829
+ };
1830
+ 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);
1831
+ const throwBody = (kind, mesh, from, velocity, radius) => {
1832
+ const limit = kind === "casing" ? MAX_CASINGS : MAX_MAGAZINES;
1833
+ const same = bodies.filter((b) => b.kind === kind);
1834
+ if (same.length >= limit) removeBody(same[0]);
1835
+ mesh.position.copy(from);
1836
+ mesh.castShadow = true;
1837
+ scene.add(mesh);
1838
+ 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 });
1839
+ };
1840
+ const ejectCasing = (caliberMm, isShell, downward) => {
1841
+ const radius = isShell ? 0.0105 : Math.max(3e-3, caliberMm / 1e3 * 0.6);
1842
+ const length = isShell ? 0.068 : Math.min(0.07, Math.max(0.015, caliberMm / 1e3 * 2.6));
1843
+ const mesh = new THREE.Mesh(new THREE.CylinderGeometry(radius, radius, length, 8), isShell ? shell : brass);
1844
+ mesh.name = "casing";
1845
+ const from = handPoint(0.03, 0.03, 0.05, new THREE.Vector3());
1846
+ const right = new THREE.Vector3(1, 0, 0).applyQuaternion(camera.quaternion);
1847
+ const up = new THREE.Vector3(0, 1, 0);
1848
+ 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);
1849
+ throwBody("casing", mesh, from, velocity, radius);
1850
+ };
1851
+ const dropMagazine = (category) => {
1852
+ const long = category === "rifle" || category === "sniper" || category === "carbine";
1853
+ const size = long ? [0.03, 0.17, 0.07] : [0.025, 0.11, 0.035];
1854
+ const mesh = new THREE.Mesh(new THREE.BoxGeometry(...size), steel);
1855
+ mesh.name = "magazine";
1856
+ const from = handPoint(0, -0.06, 0, new THREE.Vector3());
1857
+ throwBody("magazine", mesh, from, new THREE.Vector3(0, -0.4, 0), size[1] / 2);
1858
+ audio?.reloadStart(category);
1859
+ };
1860
+ const caliberOf = (weapon) => {
1861
+ if (weapon.action.kind !== "firearm") return 9;
1862
+ return arsenal.calibers.get(weapon.action.caliber)?.diameter ?? 9;
1863
+ };
1864
+ function removeBody(body) {
1865
+ body.mesh.removeFromParent();
1866
+ body.mesh.geometry.dispose();
1867
+ bodies.splice(bodies.indexOf(body), 1);
1868
+ }
1869
+ const onShot = (weapon) => {
1870
+ kick = 1;
1871
+ const caliber = weapon.action.kind === "firearm" ? arsenal.calibers.get(weapon.action.caliber) : void 0;
1872
+ const energy = caliber ? 0.5 * (caliber.bulletGrams / 1e3) * caliber.muzzleSpeed ** 2 * (caliber.pellets ?? 1) : 300;
1873
+ audio?.shot(energy, weapon.category);
1874
+ if (CYCLES.has(weapon.id)) audio?.cycle();
1875
+ if (EJECTS_ON_SHOT.has(weapon.category) && weapon.id !== "shotgun_double_12") ejectCasing(caliberOf(weapon), weapon.category === "shotgun", false);
1876
+ };
1877
+ const onReloadStart = (weapon, tool) => {
1878
+ if (DROPS_MAGAZINE.has(weapon.category)) dropMagazine(weapon.category);
1879
+ else audio?.reloadStart(weapon.category);
1880
+ if (weapon.category === "revolver" || weapon.id === "shotgun_double_12") {
1881
+ const spent = Math.max(1, tool.capacity - tool.ammo);
1882
+ for (let i = 0; i < spent; i++) ejectCasing(caliberOf(weapon), weapon.category === "shotgun", true);
1883
+ }
1884
+ };
1885
+ const stepBody = (body, dt) => {
1886
+ body.age += dt;
1887
+ if (body.resting) return;
1888
+ body.velocity.y -= GRAVITY * dt;
1889
+ body.mesh.position.addScaledVector(body.velocity, dt);
1890
+ body.mesh.rotation.x += body.spin.x * dt;
1891
+ body.mesh.rotation.y += body.spin.y * dt;
1892
+ body.mesh.rotation.z += body.spin.z * dt;
1893
+ const floor = groundAt(body.mesh.position.x, body.mesh.position.z) + body.radius;
1894
+ if (body.mesh.position.y > floor) return;
1895
+ body.mesh.position.y = floor;
1896
+ if (Math.abs(body.velocity.y) > 0.7 && body.bounces < 4) {
1897
+ body.bounces++;
1898
+ body.velocity.y = -body.velocity.y * 0.35;
1899
+ body.velocity.x *= 0.55;
1900
+ body.velocity.z *= 0.55;
1901
+ body.spin.multiplyScalar(0.5);
1902
+ if (body.kind === "casing") audio?.clink();
1903
+ else audio?.thud();
1904
+ return;
1905
+ }
1906
+ if (body.kind === "magazine" && body.bounces === 0) audio?.thud();
1907
+ body.resting = true;
1908
+ body.mesh.rotation.x = Math.PI / 2;
1909
+ body.mesh.rotation.z = 0;
1910
+ };
1911
+ return {
1912
+ handle(event, tool) {
1913
+ const weapon = arsenal.weapons.get(event.weapon);
1914
+ if (event.type === "tool") equip(event.weapon);
1915
+ if (!weapon) return;
1916
+ if (event.type === "shot") onShot(weapon);
1917
+ else if (event.type === "empty") audio?.click();
1918
+ else if (event.type === "reload" && event.phase === "start") onReloadStart(weapon, tool);
1919
+ else if (event.type === "reload" && event.phase === "end") audio?.reloadEnd(weapon.category);
1920
+ reloading = tool.reloading;
1921
+ },
1922
+ update(dt) {
1923
+ kick *= Math.exp(-dt * 14);
1924
+ dip += ((reloading ? 1 : 0) - dip) * Math.min(1, dt * 8);
1925
+ if (inHand) {
1926
+ handPoint(0, -0.12 * dip, 0.05 * kick, hand.position);
1927
+ q.setFromEuler(new THREE.Euler(0.14 * kick - 0.25 * dip, 0, 0.6 * dip));
1928
+ hand.quaternion.copy(camera.quaternion).multiply(q);
1929
+ }
1930
+ for (const body of [...bodies]) {
1931
+ stepBody(body, dt);
1932
+ if (body.age > LIFETIME_S) removeBody(body);
1933
+ }
1934
+ },
1935
+ get inHand() {
1936
+ return inHand;
1937
+ },
1938
+ get casings() {
1939
+ return bodies.filter((b) => b.kind === "casing").length;
1940
+ },
1941
+ get magazines() {
1942
+ return bodies.filter((b) => b.kind === "magazine").length;
1943
+ },
1944
+ dispose() {
1945
+ for (const body of [...bodies]) removeBody(body);
1946
+ hand.removeFromParent();
1947
+ brass.dispose();
1948
+ shell.dispose();
1949
+ steel.dispose();
1950
+ }
1951
+ };
1952
+ }
1953
+
1954
+ // src/area.ts
1955
+ var WEAPON_ITEM_PREFIX = "weapon_";
1956
+ var COUNTER_ITEM_ID = "counter_weapons";
1957
+ var COUNTER = { length: 4.8, depth: 1.6, height: 0.9 };
1958
+ var COUNTER_X = [-6.5, -9, -11.5];
1959
+ var COUNTER_Z = -1;
1960
+ var END_MARGIN = 0.6;
1961
+ var SLOT_GAP = 0.3;
1962
+ var TARGETS = [
1963
+ { id: "target_steel_plate", at: [-1.5, 0, -7] },
1964
+ { id: "target_plywood", at: [0, 0, -7] },
1965
+ { id: "target_concrete", at: [1.5, 0, -7] }
1966
+ ];
1967
+ var TEXT = {
1968
+ en: {
1969
+ group: "Weapons",
1970
+ name: "Weapons area",
1971
+ counter: "Weapon counter",
1972
+ targets: { target_steel_plate: "Steel plate target", target_plywood: "Plywood target", target_concrete: "Concrete block target" },
1973
+ reloadStart: "Reload started: {weapon} ({ammo}/{capacity})",
1974
+ reloadEnd: "Reload done: {weapon} ({ammo}/{capacity})"
1975
+ },
1976
+ "pt-BR": {
1977
+ group: "Armas",
1978
+ name: "\xC1rea de armas",
1979
+ counter: "Balc\xE3o de armas",
1980
+ targets: { target_steel_plate: "Alvo: chapa de a\xE7o", target_plywood: "Alvo: compensado", target_concrete: "Alvo: bloco de concreto" },
1981
+ reloadStart: "Recarga iniciada: {weapon} ({ammo}/{capacity})",
1982
+ reloadEnd: "Recarga completa: {weapon} ({ammo}/{capacity})"
1983
+ }
1984
+ };
1985
+ var textOf = (locale) => TEXT[locale] ?? TEXT.en;
1986
+ var fill = (template, vars) => template.replace(/\{(\w+)\}/g, (_, key) => String(vars[key] ?? ""));
1987
+ var counterProp = (name) => {
1988
+ const { length, depth, height } = COUNTER;
1989
+ return {
1990
+ spec: {
1991
+ name,
1992
+ parts: [
1993
+ { id: "top", material: "wood_soft", shape: { kind: "box", size: [depth, 0.05, length] }, position: [0, height - 0.025, 0] },
1994
+ { 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 },
1995
+ { 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 }
1996
+ ],
1997
+ joints: [
1998
+ { a: "top", b: "side_a", kind: "screw" },
1999
+ { a: "top", b: "side_b", kind: "screw" }
2000
+ ],
2001
+ tags: ["furniture"]
2002
+ },
2003
+ colors: { top: 9067056, side_a: 7029798, side_b: 7029798 }
2004
+ };
2005
+ };
2006
+ var targetProp = (id, name) => {
2007
+ const face = {
2008
+ target_steel_plate: ["steel", [0.5, 0.5, 0.01], 11910340],
2009
+ target_plywood: ["plywood", [0.6, 0.8, 0.018], 13214315],
2010
+ target_concrete: ["concrete", [0.4, 0.2, 0.2], 10132116]
2011
+ };
2012
+ const [material, size, color] = face[id];
2013
+ const spec = {
2014
+ name,
2015
+ parts: [
2016
+ { id: "post", material: "steel", shape: { kind: "box", size: [0.05, 1.1, 0.05] }, position: [0, 0.55, -0.05], anchored: true },
2017
+ { id: "face", material, shape: { kind: "box", size }, position: [0, 1.1 + size[1] / 2, 0] }
2018
+ ],
2019
+ joints: [{ a: "post", b: "face", kind: "bolt" }],
2020
+ tags: ["target"]
2021
+ };
2022
+ return { spec, colors: { post: 3816770, face: color } };
2023
+ };
2024
+ var asItem = (id, name, prop, extra = {}) => ({
2025
+ id,
2026
+ name,
2027
+ build: (THREE) => buildProp(THREE, prop),
2028
+ spec: prop.spec,
2029
+ ...extra
2030
+ });
2031
+ function weaponsLayout(weaponItemIds) {
2032
+ const perCounter = Math.floor((COUNTER.length - 2 * END_MARGIN) / SLOT_GAP) + 1;
2033
+ const counters = COUNTER_X.slice(0, Math.max(1, Math.ceil(weaponItemIds.length / perCounter)));
2034
+ 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 } }));
2035
+ weaponItemIds.forEach((item, index) => {
2036
+ const x = counters[Math.min(counters.length - 1, Math.floor(index / perCounter))];
2037
+ const z = COUNTER_Z - COUNTER.length / 2 + END_MARGIN + index % perCounter * SLOT_GAP;
2038
+ placements.push({ item, position: [x, COUNTER.height + 0.01, z], asleep: true });
2039
+ });
2040
+ for (const target of TARGETS) placements.push({ item: target.id, position: target.at });
2041
+ return placements;
2042
+ }
2043
+ function createWeaponsTestModule(options = {}) {
2044
+ const locale = options.locale ?? "pt-BR";
2045
+ const arsenal = options.arsenal ?? createArsenal({ locale });
2046
+ const text = textOf(locale);
2047
+ const audio = options.sound === false ? null : createWeaponAudio();
2048
+ const weaponDefs = [...arsenal.weapons.values()].filter((def) => !BODY_ACTIONS.has(def.id));
2049
+ const weaponIds = weaponDefs.map((def) => `${WEAPON_ITEM_PREFIX}${def.id}`);
2050
+ let fx = null;
2051
+ return {
2052
+ id: "weapons_area",
2053
+ name: text.name,
2054
+ group: text.group,
2055
+ audio,
2056
+ get fx() {
2057
+ return fx;
2058
+ },
2059
+ items: () => [
2060
+ ...weaponDefs.map((def) => asItem(`${WEAPON_ITEM_PREFIX}${def.id}`, arsenal.name(def.id), weaponProp(def, arsenal.name(def.id)), { weapon: def.id, movable: true })),
2061
+ asItem(COUNTER_ITEM_ID, text.counter, counterProp(text.counter), { movable: false }),
2062
+ ...TARGETS.map((target) => asItem(target.id, text.targets[target.id], targetProp(target.id, text.targets[target.id]), { movable: false }))
2063
+ ],
2064
+ area: () => ({ placements: weaponsLayout(weaponIds), view: { eye: [-3.4, 1.7, 2.6], target: [-9, 0.9, -1.2] } }),
2065
+ attach(host) {
2066
+ const created = createWeaponFx({
2067
+ THREE: host.THREE,
2068
+ scene: host.scene,
2069
+ camera: host.camera,
2070
+ arsenal,
2071
+ groundAt: host.groundAt,
2072
+ audio
2073
+ });
2074
+ fx = created;
2075
+ const offEvents = host.on((event) => {
2076
+ const tool = host.tool();
2077
+ created.handle(event, tool);
2078
+ 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 });
2079
+ });
2080
+ const offFrame = host.onFrame((dt) => created.update(dt));
2081
+ created.handle({ type: "tool", weapon: host.tool().weapon }, host.tool());
2082
+ return () => {
2083
+ offEvents();
2084
+ offFrame();
2085
+ created.dispose();
2086
+ audio?.dispose();
2087
+ fx = null;
2088
+ };
2089
+ }
2090
+ };
2091
+ }
2092
+ return __toCommonJS(index_exports);
2093
+ })();
2094
+ //# sourceMappingURL=index.global.js.map