@adatechnology/game-crowd 0.0.0-stage → 0.2.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.
@@ -0,0 +1,3282 @@
1
+ "use strict";
2
+ var GameCrowd = (() => {
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
+ AGENT_FIELDS: () => AGENT_FIELDS,
25
+ CELL: () => CELL,
26
+ CIVILIAN_DEFAULTS: () => CIVILIAN_DEFAULTS,
27
+ CROWD_DEFAULTS: () => CROWD_DEFAULTS,
28
+ DOOR_DEFAULTS: () => DOOR_DEFAULTS,
29
+ HERD_DEFAULTS: () => HERD_DEFAULTS,
30
+ HERD_ROLES: () => HERD_ROLES,
31
+ MISSING: () => MISSING,
32
+ STATE: () => STATE,
33
+ bindInfection: () => bindInfection,
34
+ carrionCenter: () => carrionCenter,
35
+ createCivilians: () => createCivilians,
36
+ createCrowd: () => createCrowd,
37
+ createDoors: () => createDoors,
38
+ createFlock: () => createFlock,
39
+ createHerd: () => createHerd,
40
+ createNavGrid: () => createNavGrid,
41
+ createRng: () => createRng,
42
+ createStore: () => createStore
43
+ });
44
+
45
+ // ../game-contracts/dist/index.js
46
+ var MISSING_BITS = {
47
+ head: 1,
48
+ armL: 2,
49
+ armR: 4,
50
+ legL: 8,
51
+ legR: 16,
52
+ armLBroken: 32,
53
+ armRBroken: 64,
54
+ legLBroken: 128,
55
+ legRBroken: 256,
56
+ jaw: 512
57
+ };
58
+
59
+ // ../game-characters/dist/index.js
60
+ var __defProp2 = Object.defineProperty;
61
+ var __export2 = (target, all) => {
62
+ for (var name in all)
63
+ __defProp2(target, name, { get: all[name], enumerable: true });
64
+ };
65
+ var body_default = {
66
+ guide: {
67
+ heightMetres: 1.8,
68
+ shoulderWidthMetres: 0.61,
69
+ shoulderWidthWomanMetres: 0.57,
70
+ shoulderHeightMetres: 1.475,
71
+ headToHeight: 0.1667
72
+ },
73
+ teamGame: {
74
+ source: "G 65336 J (shoulders +-0.24 m)",
75
+ joints: {
76
+ quadril: [
77
+ 0,
78
+ 0.95,
79
+ 0
80
+ ],
81
+ pescoco: [
82
+ 0,
83
+ 1.52,
84
+ 0
85
+ ],
86
+ ombroE: [
87
+ -0.24,
88
+ 1.44,
89
+ 0
90
+ ],
91
+ ombroD: [
92
+ 0.24,
93
+ 1.44,
94
+ 0
95
+ ],
96
+ cotoveloE: [
97
+ -0.27,
98
+ 1.13,
99
+ 0
100
+ ],
101
+ cotoveloD: [
102
+ 0.27,
103
+ 1.13,
104
+ 0
105
+ ],
106
+ coxaE: [
107
+ -0.1,
108
+ 0.92,
109
+ 0
110
+ ],
111
+ coxaD: [
112
+ 0.1,
113
+ 0.92,
114
+ 0
115
+ ],
116
+ joelhoE: [
117
+ -0.1,
118
+ 0.5,
119
+ 0
120
+ ],
121
+ joelhoD: [
122
+ 0.1,
123
+ 0.5,
124
+ 0
125
+ ]
126
+ },
127
+ heightMetres: 1.8
128
+ },
129
+ lodDistancesMetres: [
130
+ 18,
131
+ 55
132
+ ],
133
+ triangleBudget: {
134
+ lod0: 2500,
135
+ lod1: 800,
136
+ lod2: 250
137
+ }
138
+ };
139
+ var SEGMENT_RADIUS = {
140
+ head: 0.1,
141
+ jaw: 0.05,
142
+ neck: 0.06,
143
+ torso: 0.17,
144
+ pelvis: 0.16,
145
+ upperArm: 0.085,
146
+ forearm: 0.065,
147
+ hand: 0.05,
148
+ thigh: 0.1,
149
+ shin: 0.08,
150
+ foot: 0.05
151
+ };
152
+ var { guide } = body_default;
153
+ var shoulderX = guide.shoulderWidthMetres / 2;
154
+ var shoulderY = guide.shoulderHeightMetres;
155
+ var GUIDE = {
156
+ id: "guide",
157
+ heightMetres: guide.heightMetres,
158
+ joints: {
159
+ hip: [0, 0.92, 0],
160
+ spine: [0, 1.05, 0],
161
+ neck: [0, 1.56, 0],
162
+ head: [0, 1.64, 0],
163
+ jaw: [0, 1.66, 0.06],
164
+ shoulderL: [shoulderX, shoulderY, 0],
165
+ shoulderR: [-shoulderX, shoulderY, 0],
166
+ elbowL: [shoulderX, shoulderY - 0.31, 0],
167
+ elbowR: [-shoulderX, shoulderY - 0.31, 0],
168
+ wristL: [shoulderX, shoulderY - 0.61, 0],
169
+ wristR: [-shoulderX, shoulderY - 0.61, 0],
170
+ hipL: [0.105, 0.9, 0],
171
+ hipR: [-0.105, 0.9, 0],
172
+ kneeL: [0.105, 0.48, 0],
173
+ kneeR: [-0.105, 0.48, 0],
174
+ ankleL: [0.105, 0.1, 0],
175
+ ankleR: [-0.105, 0.1, 0]
176
+ },
177
+ segmentRadius: SEGMENT_RADIUS
178
+ };
179
+ var J = body_default.teamGame.joints;
180
+ var below = (point, drop) => [point[0], point[1] - drop, point[2]];
181
+ var TEAM_GAME = {
182
+ id: "team-game",
183
+ heightMetres: body_default.teamGame.heightMetres,
184
+ joints: {
185
+ hip: J.quadril,
186
+ spine: J.quadril,
187
+ neck: J.pescoco,
188
+ head: J.pescoco,
189
+ jaw: [0, J.pescoco[1] + 0.08, 0.06],
190
+ shoulderL: J.ombroE,
191
+ shoulderR: J.ombroD,
192
+ elbowL: J.cotoveloE,
193
+ elbowR: J.cotoveloD,
194
+ wristL: below(J.cotoveloE, 0.3),
195
+ wristR: below(J.cotoveloD, 0.3),
196
+ hipL: J.coxaE,
197
+ hipR: J.coxaD,
198
+ kneeL: J.joelhoE,
199
+ kneeR: J.joelhoD,
200
+ ankleL: below(J.joelhoE, 0.42),
201
+ ankleR: below(J.joelhoD, 0.42)
202
+ },
203
+ segmentRadius: SEGMENT_RADIUS
204
+ };
205
+ var createCorpseLedger = (options) => {
206
+ const entries = [];
207
+ return {
208
+ entries,
209
+ get count() {
210
+ return entries.length;
211
+ },
212
+ add(data) {
213
+ if (entries.length < options.max) {
214
+ entries.push({ age: 0, data });
215
+ return void 0;
216
+ }
217
+ let oldest = 0;
218
+ for (let index = 1; index < entries.length; index++) if (entries[index].age > entries[oldest].age) oldest = index;
219
+ const replaced = entries[oldest].data;
220
+ entries[oldest] = { age: 0, data };
221
+ return replaced;
222
+ },
223
+ update(dt) {
224
+ const removed = [];
225
+ for (let index = 0; index < entries.length; index++) {
226
+ const entry = entries[index];
227
+ entry.age += dt;
228
+ if (entry.age <= options.seconds) continue;
229
+ removed.push(entry.data);
230
+ entries[index] = entries[entries.length - 1];
231
+ entries.pop();
232
+ index--;
233
+ }
234
+ return removed;
235
+ },
236
+ remove(index) {
237
+ const entry = entries[index];
238
+ if (!entry) return void 0;
239
+ entries[index] = entries[entries.length - 1];
240
+ entries.pop();
241
+ return entry.data;
242
+ },
243
+ clear() {
244
+ entries.length = 0;
245
+ }
246
+ };
247
+ };
248
+ var team_game_default = {
249
+ id: "team-game",
250
+ segments: {
251
+ head: 40,
252
+ arm: 60,
253
+ leg: 80
254
+ },
255
+ calibers: {
256
+ "7_62x51": {
257
+ gameKey: "308",
258
+ name: "7,62\xD751 (.308)",
259
+ points: 75,
260
+ penetration: 4,
261
+ effectiveRangeMetres: 600,
262
+ noiseMetres: 130,
263
+ skullRange: 1,
264
+ class: "rifle"
265
+ },
266
+ "9x19": {
267
+ gameKey: "9x19",
268
+ name: "9\xD719 mm",
269
+ points: 35,
270
+ penetration: 1.2,
271
+ effectiveRangeMetres: 35,
272
+ noiseMetres: 60,
273
+ skullRange: 1,
274
+ class: "pistol"
275
+ },
276
+ "12ga_00buck": {
277
+ gameKey: "12ga",
278
+ name: "12 ga 00",
279
+ points: 12,
280
+ pellets: 9,
281
+ penetration: 0.5,
282
+ effectiveRangeMetres: 15,
283
+ noiseMetres: 90,
284
+ skullRange: 8,
285
+ class: "pistol"
286
+ },
287
+ "7_62x39": {
288
+ gameKey: "762x39",
289
+ name: "7,62\xD739 mm",
290
+ points: 55,
291
+ penetration: 3,
292
+ effectiveRangeMetres: 250,
293
+ noiseMetres: 110,
294
+ skullRange: 1,
295
+ class: "rifle"
296
+ }
297
+ },
298
+ regionCost: {
299
+ head: 0.7,
300
+ torso: 1,
301
+ bruteTorso: 2,
302
+ limb: 0.5
303
+ },
304
+ damageKeptPerBody: 0.75,
305
+ falloff: {
306
+ fullAtRange: 1,
307
+ atTwiceRange: 0.5
308
+ },
309
+ helmetAbsorbs: "pistol",
310
+ regions: [
311
+ "head",
312
+ "torso",
313
+ "armL",
314
+ "armR",
315
+ "legL",
316
+ "legR"
317
+ ]
318
+ };
319
+ var REGION_HIT_POINTS = {
320
+ head: team_game_default.segments.head,
321
+ armL: team_game_default.segments.arm,
322
+ armR: team_game_default.segments.arm,
323
+ legL: team_game_default.segments.leg,
324
+ legR: team_game_default.segments.leg
325
+ };
326
+ var segments_default = {
327
+ segments: [
328
+ {
329
+ id: "head",
330
+ region: "head",
331
+ hitPoints: 40,
332
+ multiplier: 3,
333
+ bleed: 40
334
+ },
335
+ {
336
+ id: "jaw",
337
+ region: "head",
338
+ hitPoints: 12,
339
+ multiplier: 2,
340
+ bleed: 8
341
+ },
342
+ {
343
+ id: "neck",
344
+ region: "head",
345
+ hitPoints: 25,
346
+ multiplier: 2,
347
+ bleed: 46
348
+ },
349
+ {
350
+ id: "torso",
351
+ region: "torso",
352
+ hitPoints: 150,
353
+ multiplier: 1,
354
+ bleed: 10
355
+ },
356
+ {
357
+ id: "pelvis",
358
+ region: "torso",
359
+ hitPoints: 90,
360
+ multiplier: 1,
361
+ bleed: 12
362
+ },
363
+ {
364
+ id: "upperArm",
365
+ region: "arm",
366
+ hitPoints: 45,
367
+ multiplier: 0.6,
368
+ bleed: 15
369
+ },
370
+ {
371
+ id: "forearm",
372
+ region: "arm",
373
+ hitPoints: 30,
374
+ multiplier: 0.6,
375
+ bleed: 8
376
+ },
377
+ {
378
+ id: "hand",
379
+ region: "arm",
380
+ hitPoints: 12,
381
+ multiplier: 0.6,
382
+ bleed: 3
383
+ },
384
+ {
385
+ id: "thigh",
386
+ region: "leg",
387
+ hitPoints: 55,
388
+ multiplier: 0.7,
389
+ bleed: 19
390
+ },
391
+ {
392
+ id: "shin",
393
+ region: "leg",
394
+ hitPoints: 40,
395
+ multiplier: 0.7,
396
+ bleed: 9
397
+ },
398
+ {
399
+ id: "foot",
400
+ region: "leg",
401
+ hitPoints: 15,
402
+ multiplier: 0.7,
403
+ bleed: 3
404
+ }
405
+ ],
406
+ regions: {
407
+ head: {
408
+ hitPoints: 40,
409
+ segmentSum: 77,
410
+ scale: 0.5195
411
+ },
412
+ arm: {
413
+ hitPoints: 60,
414
+ segmentSum: 87,
415
+ scale: 0.6897
416
+ },
417
+ leg: {
418
+ hitPoints: 80,
419
+ segmentSum: 110,
420
+ scale: 0.7273
421
+ }
422
+ },
423
+ arsenalWeapons: {
424
+ pistola: {
425
+ n: "Pistola 9\xD719",
426
+ dmg: 35,
427
+ pel: 1
428
+ },
429
+ fuzil: {
430
+ n: "Fuzil 7,62\xD739",
431
+ dmg: 55,
432
+ pel: 1
433
+ },
434
+ escopeta: {
435
+ n: "Escopeta 12 ga",
436
+ dmg: 12,
437
+ pel: 9
438
+ },
439
+ cinquenta: {
440
+ n: ".50 BMG",
441
+ dmg: 180,
442
+ pel: 1
443
+ },
444
+ granada: {
445
+ n: "Granada",
446
+ dmg: 300,
447
+ pel: 0
448
+ }
449
+ },
450
+ elenco: {
451
+ segments: {
452
+ head: {
453
+ hp: 40,
454
+ radius: 0.07
455
+ },
456
+ torso: {
457
+ hp: 120
458
+ },
459
+ pelvis: {
460
+ hp: 90
461
+ },
462
+ upperArm: {
463
+ hp: 60,
464
+ radius: 0.085
465
+ },
466
+ forearm: {
467
+ hp: 45,
468
+ radius: 0.065
469
+ },
470
+ hand: {
471
+ hp: 25,
472
+ radius: 0.05
473
+ },
474
+ thigh: {
475
+ hp: 80,
476
+ radius: 0.1
477
+ },
478
+ shin: {
479
+ hp: 55,
480
+ radius: 0.08
481
+ }
482
+ },
483
+ below: {
484
+ upperArm: [
485
+ "forearm",
486
+ "hand"
487
+ ],
488
+ forearm: [
489
+ "hand"
490
+ ],
491
+ thigh: [
492
+ "shin"
493
+ ]
494
+ },
495
+ falls: {
496
+ upperArm: "o bra\xE7o inteiro",
497
+ forearm: "o antebra\xE7o com a m\xE3o",
498
+ hand: "a m\xE3o",
499
+ thigh: "a perna inteira",
500
+ shin: "a canela com o p\xE9",
501
+ head: "a cabe\xE7a"
502
+ },
503
+ cutWeapons: [
504
+ {
505
+ id: "faca",
506
+ n: "Faca",
507
+ dmg: 22,
508
+ cut: 0.7
509
+ },
510
+ {
511
+ id: "facao",
512
+ n: "Fac\xE3o",
513
+ dmg: 55,
514
+ cut: 1
515
+ },
516
+ {
517
+ id: "machado",
518
+ n: "Machado",
519
+ dmg: 85,
520
+ cut: 1
521
+ },
522
+ {
523
+ id: "p9",
524
+ n: "Pistola 9 mm",
525
+ dmg: 30,
526
+ cut: 0.15
527
+ },
528
+ {
529
+ id: "e12",
530
+ n: "Espingarda 12",
531
+ dmg: 95,
532
+ cut: 0.75
533
+ },
534
+ {
535
+ id: "f762",
536
+ n: "Fuzil 7,62",
537
+ dmg: 65,
538
+ cut: 0.45
539
+ }
540
+ ]
541
+ },
542
+ severity: {
543
+ severAtCut: 0.5,
544
+ severAtDamageFraction: 0.9,
545
+ destroyAtDamageMultiple: 2,
546
+ limpAtHpFraction: 0.5,
547
+ splitAtDamage: 150,
548
+ headKillDamage: 20,
549
+ jawChance: 0.3,
550
+ jawDamage: 25,
551
+ headBitDamage: 60,
552
+ headBitShotgunMetres: 5
553
+ },
554
+ defaultCut: {
555
+ slash: 0.85,
556
+ blunt: 0,
557
+ explosive: 1,
558
+ ballistic: {
559
+ pistol: 0.15,
560
+ buckshot: 0.75,
561
+ rifle: 0.45
562
+ }
563
+ },
564
+ deathRule: {
565
+ default: "vitality-or-head",
566
+ options: [
567
+ "vitality-or-head",
568
+ "head-only"
569
+ ]
570
+ }
571
+ };
572
+ var limb = (kind, side, joint, parent, slots) => {
573
+ const isArm2 = kind === "upperArm" || kind === "forearm" || kind === "hand";
574
+ const region = isArm2 ? side === "L" ? "armL" : "armR" : side === "L" ? "legL" : "legR";
575
+ return { id: `${kind}${side}`, kind, side, joint, region, slots, ...parent ? { parent } : {} };
576
+ };
577
+ var sideOf = (side) => [
578
+ limb("upperArm", side, `shoulder${side}`, "torso", ["arms"]),
579
+ limb("forearm", side, `elbow${side}`, `upperArm${side}`, ["arms"]),
580
+ limb("hand", side, `wrist${side}`, `forearm${side}`, ["hands"]),
581
+ limb("thigh", side, `hip${side}`, "pelvis", ["legs"]),
582
+ limb("shin", side, `knee${side}`, `thigh${side}`, ["legs"]),
583
+ limb("foot", side, `ankle${side}`, `shin${side}`, ["feet"])
584
+ ];
585
+ var SEGMENT_TREE = [
586
+ { id: "pelvis", kind: "pelvis", joint: "hip", region: "torso", slots: ["torso"] },
587
+ { id: "torso", kind: "torso", joint: "spine", parent: "pelvis", region: "torso", slots: ["torso"] },
588
+ { id: "neck", kind: "neck", joint: "neck", parent: "torso", region: "head", slots: [] },
589
+ { id: "head", kind: "head", joint: "head", parent: "neck", region: "head", slots: ["head", "eyes"] },
590
+ { id: "jaw", kind: "jaw", joint: "jaw", parent: "head", region: "head", slots: [] },
591
+ ...sideOf("L"),
592
+ ...sideOf("R")
593
+ ];
594
+ var SEGMENT_IDS = SEGMENT_TREE.map((node) => node.id);
595
+ var NODES = new Map(SEGMENT_TREE.map((node) => [node.id, node]));
596
+ var SEGMENT_NUMBERS = Object.fromEntries(
597
+ segments_default.segments.map((entry) => [entry.id, { hitPoints: entry.hitPoints, multiplier: entry.multiplier, bleed: entry.bleed }])
598
+ );
599
+ var LIMB_MASK = [
600
+ { ids: ["upperArmL", "forearmL"], lost: MISSING_BITS.armL, broken: MISSING_BITS.armLBroken },
601
+ { ids: ["upperArmR", "forearmR"], lost: MISSING_BITS.armR, broken: MISSING_BITS.armRBroken },
602
+ { ids: ["thighL", "shinL"], lost: MISSING_BITS.legL, broken: MISSING_BITS.legLBroken },
603
+ { ids: ["thighR", "shinR"], lost: MISSING_BITS.legR, broken: MISSING_BITS.legRBroken }
604
+ ];
605
+ var physical_default = {
606
+ id: "physical",
607
+ tissuePerCentimetre: {
608
+ flesh: 0.25,
609
+ bone: 1
610
+ },
611
+ torsoPath: {
612
+ fleshCentimetres: 22,
613
+ boneCentimetres: 2
614
+ },
615
+ regionFactor: {
616
+ head: 0.7,
617
+ limb: 0.5,
618
+ torso: 1
619
+ },
620
+ bruteTorsoFactor: 2,
621
+ energyKept: "P / (P + R)",
622
+ points: {
623
+ coefficient: 3.5,
624
+ exponent: 0.37
625
+ },
626
+ tolerance: {
627
+ bodies: 0,
628
+ energyKept: 0.05
629
+ },
630
+ calibration: [
631
+ {
632
+ caliber: "9x19",
633
+ force: 7.67,
634
+ bodies: 2,
635
+ energyKeptFirstBody: 0.51,
636
+ expectedBodies: 2,
637
+ bestiaryEnergyKept: 0.5
638
+ },
639
+ {
640
+ caliber: "5_56x45",
641
+ force: 16.56,
642
+ bodies: 3,
643
+ energyKeptFirstBody: 0.69,
644
+ expectedBodies: 3,
645
+ bestiaryEnergyKept: 0.65
646
+ },
647
+ {
648
+ caliber: "7_62x51",
649
+ force: 26.67,
650
+ bodies: 4,
651
+ energyKeptFirstBody: 0.78,
652
+ expectedBodies: 4,
653
+ bestiaryEnergyKept: 0.75
654
+ },
655
+ {
656
+ caliber: "7_62x39",
657
+ force: 18.52,
658
+ bodies: 3,
659
+ energyKeptFirstBody: 0.71,
660
+ expectedBodies: 3
661
+ },
662
+ {
663
+ caliber: "12ga_00buck",
664
+ force: 4.35,
665
+ bodies: 1,
666
+ energyKeptFirstBody: 0.37,
667
+ expectedBodies: 1
668
+ }
669
+ ],
670
+ torsoResistance: 7.5
671
+ };
672
+ var { severity } = segments_default;
673
+ var DEFAULT_DAMAGE_RULES = {
674
+ deathRule: "vitality-or-head",
675
+ segmentRule: "kit",
676
+ severity: { ...severity, stunAtPoints: 35, limbVitalityShare: 0.3, regionLimbVitalityShare: 0.5 },
677
+ blast: { minimumPoints: 8, headDestroyPoints: 40, bigPoints: 120 },
678
+ defaultCut: segments_default.defaultCut,
679
+ points: physical_default.points,
680
+ spread: 0,
681
+ segments: SEGMENT_NUMBERS,
682
+ burnPointsPerSecond: 3
683
+ };
684
+ var ARSENAL_RULES = { ...DEFAULT_DAMAGE_RULES, segmentRule: "arsenal" };
685
+ var ELENCO_HIT_POINTS = segments_default.elenco.segments;
686
+ var ELENCO_RULES = {
687
+ ...DEFAULT_DAMAGE_RULES,
688
+ segmentRule: "elenco",
689
+ spread: 0.15,
690
+ segments: Object.fromEntries(
691
+ Object.entries(SEGMENT_NUMBERS).map(([kind, numbers]) => [kind, { ...numbers, multiplier: 1, hitPoints: ELENCO_HIT_POINTS[kind]?.hp ?? numbers.hitPoints }])
692
+ )
693
+ };
694
+ var CLASS_OF = { pistol: "pistol", rifle: "rifle" };
695
+ var TEAM_GAME_CALIBERS = Object.entries(team_game_default.calibers).map(([id, entry]) => ({
696
+ id,
697
+ points: entry.points,
698
+ penetration: entry.penetration,
699
+ class: CLASS_OF[entry.class] ?? "pistol",
700
+ skullRange: entry.skullRange,
701
+ pellets: "pellets" in entry ? entry.pellets : 1,
702
+ effectiveRangeMetres: entry.effectiveRangeMetres
703
+ }));
704
+ var TEAM_GAME_CALIBER_KEYS = Object.fromEntries(Object.entries(team_game_default.calibers).map(([id, entry]) => [entry.gameKey, id]));
705
+ var TEAM_GAME_RULES = { regionCost: team_game_default.regionCost, damageKeptPerBody: team_game_default.damageKeptPerBody, falloff: team_game_default.falloff };
706
+ var PHYSICAL_RULES = physical_default;
707
+ var { tissuePerCentimetre, torsoPath, regionFactor } = PHYSICAL_RULES;
708
+ var TORSO_RESISTANCE = torsoPath.fleshCentimetres * tissuePerCentimetre.flesh + torsoPath.boneCentimetres * tissuePerCentimetre.bone;
709
+ var HEAD_KINDS = /* @__PURE__ */ new Set(["head", "jaw", "neck"]);
710
+ var TORSO_KINDS = /* @__PURE__ */ new Set(["torso", "pelvis"]);
711
+ var segmentResistance = ({ kind, bodyCount, protection }) => {
712
+ if (TORSO_KINDS.has(kind)) return TORSO_RESISTANCE * regionFactor.torso * bodyCount + protection;
713
+ if (HEAD_KINDS.has(kind)) return TORSO_RESISTANCE * regionFactor.head + protection;
714
+ return TORSO_RESISTANCE * regionFactor.limb + protection;
715
+ };
716
+ var LIMB_BITS = {
717
+ armL: { severed: MISSING_BITS.armL, broken: MISSING_BITS.armLBroken },
718
+ armR: { severed: MISSING_BITS.armR, broken: MISSING_BITS.armRBroken },
719
+ legL: { severed: MISSING_BITS.legL, broken: MISSING_BITS.legLBroken },
720
+ legR: { severed: MISSING_BITS.legR, broken: MISSING_BITS.legRBroken }
721
+ };
722
+ var HEAD_RESISTANCE = segmentResistance({ kind: "head", bodyCount: 1, protection: 0 });
723
+ var gait_default = {
724
+ cascade: {
725
+ hip: 16,
726
+ spine: 11,
727
+ shoulder: 9,
728
+ elbow: 7,
729
+ leg: 14,
730
+ knee: 11,
731
+ head: 6
732
+ },
733
+ moves: {
734
+ idle: {
735
+ gait: 0,
736
+ freq: 0,
737
+ amp: 0,
738
+ knee: 0,
739
+ bob: 0,
740
+ lean: 0,
741
+ arm: 0,
742
+ elb: 0.12,
743
+ crouch: 0,
744
+ aim: 0,
745
+ turn: 0,
746
+ grip: 0.35
747
+ },
748
+ walk: {
749
+ gait: 1,
750
+ freq: 5.6,
751
+ amp: 0.42,
752
+ knee: 0.85,
753
+ bob: 0.035,
754
+ lean: 0.05,
755
+ arm: 0.42,
756
+ elb: 0.3,
757
+ crouch: 0,
758
+ aim: 0,
759
+ turn: 0,
760
+ grip: 0.4
761
+ },
762
+ run: {
763
+ gait: 1,
764
+ freq: 9.2,
765
+ amp: 0.72,
766
+ knee: 1.5,
767
+ bob: 0.06,
768
+ lean: 0.3,
769
+ arm: 0.85,
770
+ elb: 1.35,
771
+ crouch: 0,
772
+ aim: 0,
773
+ turn: 0,
774
+ grip: 1.1
775
+ },
776
+ crouch: {
777
+ gait: 1,
778
+ freq: 3.8,
779
+ amp: 0.3,
780
+ knee: 0.6,
781
+ bob: 0.02,
782
+ lean: 0.42,
783
+ arm: 0.15,
784
+ elb: 0.7,
785
+ crouch: 1,
786
+ aim: 0,
787
+ turn: 0,
788
+ grip: 0.8
789
+ },
790
+ aim: {
791
+ gait: 0,
792
+ freq: 0,
793
+ amp: 0,
794
+ knee: 0,
795
+ bob: 0,
796
+ lean: 0.06,
797
+ arm: 0,
798
+ elb: 0.2,
799
+ crouch: 0.25,
800
+ aim: 1,
801
+ turn: 0,
802
+ grip: 1.2
803
+ },
804
+ turn: {
805
+ gait: 0.5,
806
+ freq: 4.2,
807
+ amp: 0.14,
808
+ knee: 0.7,
809
+ bob: 0.012,
810
+ lean: 0.03,
811
+ arm: 0.12,
812
+ elb: 0.25,
813
+ crouch: 0,
814
+ aim: 0,
815
+ turn: 1,
816
+ grip: 0.4
817
+ }
818
+ },
819
+ poses: [
820
+ {
821
+ index: 0,
822
+ id: "walk",
823
+ name: {
824
+ "pt-BR": "anda"
825
+ }
826
+ },
827
+ {
828
+ index: 1,
829
+ id: "crawl",
830
+ name: {
831
+ "pt-BR": "rasteja"
832
+ }
833
+ },
834
+ {
835
+ index: 2,
836
+ id: "fallen",
837
+ name: {
838
+ "pt-BR": "ca\xEDdo"
839
+ }
840
+ },
841
+ {
842
+ index: 3,
843
+ id: "attack",
844
+ name: {
845
+ "pt-BR": "ataca"
846
+ }
847
+ },
848
+ {
849
+ index: 4,
850
+ id: "scream",
851
+ name: {
852
+ "pt-BR": "grito"
853
+ }
854
+ },
855
+ {
856
+ index: 5,
857
+ id: "crouch",
858
+ name: {
859
+ "pt-BR": "agachado"
860
+ }
861
+ },
862
+ {
863
+ index: 6,
864
+ id: "leap",
865
+ name: {
866
+ "pt-BR": "salto"
867
+ }
868
+ },
869
+ {
870
+ index: 7,
871
+ id: "eat",
872
+ name: {
873
+ "pt-BR": "comendo"
874
+ }
875
+ }
876
+ ],
877
+ walk: {
878
+ cadenceBase: 0.5,
879
+ cadencePerSpeed: 0.3,
880
+ stopBelowSpeed: 0.05,
881
+ maxSine: 0.9,
882
+ strideFactor: 4,
883
+ legLengthFactor: 0.9
884
+ }
885
+ };
886
+ var WALK = gait_default.walk;
887
+ var CASCADE = gait_default.cascade;
888
+ var TWO_PI = Math.PI * 2;
889
+ var MOVES = gait_default.moves;
890
+ var appearance_default = {
891
+ variance: {
892
+ height: {
893
+ gaussianSpread: 0.09,
894
+ min: 0.9,
895
+ max: 1.1
896
+ },
897
+ buildBands: [
898
+ {
899
+ below: 0.3,
900
+ base: 0.85,
901
+ range: 0.1
902
+ },
903
+ {
904
+ below: 0.8,
905
+ base: 0.95,
906
+ range: 0.13
907
+ },
908
+ {
909
+ base: 1.08,
910
+ range: 0.17
911
+ }
912
+ ],
913
+ buildSpeed: {
914
+ lean: 1.05,
915
+ heavy: 0.9
916
+ },
917
+ buildHealth: {
918
+ lean: 0.9,
919
+ heavy: 1.15
920
+ },
921
+ speedJitter: {
922
+ base: 0.92,
923
+ range: 0.16
924
+ },
925
+ wanderJitter: {
926
+ base: 0.8,
927
+ range: 0.4
928
+ },
929
+ runnerAgeClamp: 1,
930
+ variantCount: {
931
+ walker: 4,
932
+ runner: 3,
933
+ crawler: 2,
934
+ brute: 2
935
+ },
936
+ variantSeedBase: 7e3
937
+ },
938
+ civilianColors: [
939
+ "6D5A4A",
940
+ "4D5D6B",
941
+ "7A3B33",
942
+ "5B6B4A",
943
+ "8A7A5A",
944
+ "3E4A5C",
945
+ "6A4A6A",
946
+ "9A8A70",
947
+ "40403A"
948
+ ],
949
+ corpses: {
950
+ max: 400,
951
+ lifeSeconds: 900
952
+ }
953
+ };
954
+ var { variance } = appearance_default;
955
+ var palette_exports = {};
956
+ __export2(palette_exports, {
957
+ BAG_COLORS: () => BAG_COLORS,
958
+ BLOOD_COLORS: () => BLOOD_COLORS,
959
+ CAP_COLORS: () => CAP_COLORS,
960
+ DECAY_SKIN: () => DECAY_SKIN,
961
+ GRAY: () => GRAY,
962
+ HAIR_COLORS: () => HAIR_COLORS,
963
+ HAIR_STYLES_FEMALE: () => HAIR_STYLES_FEMALE,
964
+ HAIR_STYLES_MALE: () => HAIR_STYLES_MALE,
965
+ OLD_HAIR: () => OLD_HAIR,
966
+ PANTS_COLORS: () => PANTS_COLORS,
967
+ RED: () => RED,
968
+ SHOE_COLORS: () => SHOE_COLORS,
969
+ SKIN_DECAY_BLEND: () => SKIN_DECAY_BLEND,
970
+ SKIN_TONES: () => SKIN_TONES,
971
+ SLEEVES: () => SLEEVES,
972
+ TOP_COLORS: () => TOP_COLORS,
973
+ UNIFORM_PANTS: () => UNIFORM_PANTS,
974
+ UNIFORM_SHIRTS: () => UNIFORM_SHIRTS,
975
+ UNIFORM_SHOES: () => UNIFORM_SHOES
976
+ });
977
+ var SKIN_TONES = [15845797, 14724224, 13012066, 11039050, 9067066, 6175526];
978
+ var TOP_COLORS = [4878230, 9321275, 6113850, 5208643, 7040888, 4153146, 5917294, 9071162, 12104354, 2894896, 10111530, 3104099, 13216074, 8007242, 14210248, 3820138];
979
+ var PANTS_COLORS = [2896704, 2040360, 4866096, 5922662, 3820138, 6969920, 2767402];
980
+ var SLEEVES = ["short", "short", "long", "tank"];
981
+ var HAIR_STYLES_FEMALE = ["long", "long", "bun", "short", "afro", "bald"];
982
+ var HAIR_STYLES_MALE = ["short", "short", "buzz", "bald", "afro", "long"];
983
+ var CAP_COLORS = [9321275, 2894896, 3820138, 13216074];
984
+ var BAG_COLORS = [3820090, 5909028, 2896704];
985
+ var HAIR_COLORS = [1709072, 2760214, 3877920, 6965802, 11569744, 8006170];
986
+ var SHOE_COLORS = [1908514, 3811866, 12104872, 4856346];
987
+ var DECAY_SKIN = [13482920, 9740420, 8090698, 9206878];
988
+ var UNIFORM_SHIRTS = [4878230, 9321275, 6113850, 5208643, 7040888, 4153146, 5917294, 9071162];
989
+ var UNIFORM_PANTS = 2896704;
990
+ var UNIFORM_SHOES = 1908514;
991
+ var SKIN_DECAY_BLEND = [0.45, 0.65, 0.8, 0.88];
992
+ var GRAY = 4868678;
993
+ var RED = 8000012;
994
+ var BLOOD_COLORS = [10361618, 7214096, 4860440, 1840658];
995
+ var OLD_HAIR = 9078656;
996
+
997
+ // src/data/defaults.json
998
+ var defaults_default = {
999
+ sources: {
1000
+ store: "jogo.js G 66714-66734 (Z, ZMAX 6000, FIELDS)",
1001
+ bands: "jogo.js G 62019-62020 (BANDS, HYST)",
1002
+ flow: "jogo.js G 66735-66737, 66773-66825 (FLOW_R 192, flowStart, flowStep; budget 1 ms a frame, the first field in one go)",
1003
+ update: "jogo.js G 66835-66977 hordeUpdate",
1004
+ alerts: "jogo.js G 66978-66993 groan, screamAt",
1005
+ attack: "jogo.js G 66996-67006 zAttack",
1006
+ damage: "jogo.js G 67010-67045 zDamage",
1007
+ kill: "jogo.js G 67046-67059 zKill, inchadoBurst",
1008
+ corpses: "jogo.js G 62021 (CORPOS_MAX 400, CORPO_VIDA 900), 67062-67075 corpseAdd, corpsesUpdate",
1009
+ population: "jogo.js G 67139-67177 spawnAway, populationUpdate (popTarget 1200, migration 120 every 90 s, the first after 60 s)",
1010
+ pile: "jogo.js G 67184-67227 (PILE_STEP 0.48, PILE_TOP 3.7, PILE_MAX 160, groundAt, zBlockedAt, tryStep, stepRelease, pileBlast)",
1011
+ trail: "jogo.js G 67238-67246 trailUpdate",
1012
+ abilities: "jogo.js G 67266-67356 zAbility, zChaseSpeed, zHurtOther, killQ",
1013
+ frame: "jogo.js G 67377-67402 zFrame",
1014
+ cadence: "jogo.js G 65507 walkCad"
1015
+ },
1016
+ bands: [
1017
+ { max: 35, dt: 0.05 },
1018
+ { max: 120, dt: 0.2 },
1019
+ { max: 400, dt: 1 },
1020
+ { max: null, dt: 5 }
1021
+ ],
1022
+ numbers: {
1023
+ capacity: 6e3,
1024
+ hysteresis: 10,
1025
+ flowRadius: 192,
1026
+ flowBudgetMs: 1,
1027
+ hashCell: 1,
1028
+ nightSight: 0.4,
1029
+ crouchSight: 0.6,
1030
+ sightCone: 0.5,
1031
+ sightNear: 2.5,
1032
+ loseAfter: 15,
1033
+ hearingBase: 40,
1034
+ heardLost: 5,
1035
+ alertRadius: 8,
1036
+ alertDelay: 0.2,
1037
+ alertDelayRandom: 0.4,
1038
+ voiceDistance: 40,
1039
+ voiceEvery: 0.25,
1040
+ directRange: 4,
1041
+ pyramidRange: 14,
1042
+ screamSpeed: 1.2,
1043
+ attackRange: 1.15,
1044
+ attackHeight: 1.3,
1045
+ attackSlots: 6,
1046
+ attackEvery: 1.5,
1047
+ crouchBite: 1.3,
1048
+ crawlDamage: 0.6,
1049
+ wanderSpan: 24,
1050
+ crawlSpeed: 0.4,
1051
+ limpSpeed: 0.5,
1052
+ separation: 0.31,
1053
+ separationPush: 2.2,
1054
+ doorDamage: 10,
1055
+ doorMessageDistance: 25,
1056
+ skullMinDamage: 20,
1057
+ skullTakeDamage: 60,
1058
+ skullTakeWithin: { "12ga": 5 },
1059
+ jawDamage: 25,
1060
+ jawChance: 0.3,
1061
+ hitStunDamage: 35,
1062
+ hitStun: 0.45,
1063
+ dormantWake: 3,
1064
+ dormantWakeBehind: 10,
1065
+ pileStep: 0.48,
1066
+ pileTop: 3.7,
1067
+ pileMax: 160,
1068
+ pileReleaseDistance: 30,
1069
+ gravity: 9.8,
1070
+ fallStun: 3.2,
1071
+ climbSpeed: 1.9,
1072
+ cadenceBase: 0.5,
1073
+ cadencePerSpeed: 0.3,
1074
+ corpsesMax: 400,
1075
+ corpseSeconds: 900,
1076
+ trailSeconds: 120,
1077
+ trailStep: 1.5,
1078
+ trailMax: 200,
1079
+ population: 1200,
1080
+ refillMin: 70,
1081
+ refillMax: 160,
1082
+ migrationFirst: 60
1083
+ }
1084
+ };
1085
+
1086
+ // src/doors.ts
1087
+ var DOOR_DEFAULTS = { doorHp: 300, plankHp: 150, height: 2.2, thickness: 0.1, soundChance: 0.3, soundVolume: 0.8, breakVolume: 1.5, breakNoise: 30 };
1088
+ function createDoors(grid, options) {
1089
+ const N = { ...DOOR_DEFAULTS, ...options.numbers };
1090
+ const random = options.random;
1091
+ const list = [];
1092
+ const doorAt = new Int16Array(grid.n * grid.n).fill(-1);
1093
+ const listeners = /* @__PURE__ */ new Map();
1094
+ const emit = (event, payload) => {
1095
+ for (const listener of listeners.get(event) ?? []) listener(payload);
1096
+ };
1097
+ const set = (index, open) => {
1098
+ const d = list[index];
1099
+ if (!d) return;
1100
+ d.open = open;
1101
+ grid.fillRect(...d.rect, open || d.broken ? 3 : 5, N.height);
1102
+ };
1103
+ return {
1104
+ list,
1105
+ at: (cell) => cell < 0 ? -1 : doorAt[cell],
1106
+ add(x, z, axis, width, data) {
1107
+ const i = list.length;
1108
+ const t = N.thickness;
1109
+ const x0 = axis === "x" ? x - width / 2 : x - t;
1110
+ const x1 = axis === "x" ? x + width / 2 : x + t;
1111
+ const z0 = axis === "x" ? z - t : z - width / 2;
1112
+ const z1 = axis === "x" ? z + t : z + width / 2;
1113
+ list.push({ x, z, axis, width, hp: N.doorHp, hpMax: N.doorHp, open: false, broken: false, planks: 0, rect: [x0, z0, x1, z1], data });
1114
+ grid.fillRect(x0, z0, x1, z1, 5, N.height);
1115
+ const a = Math.floor((x0 + grid.half) / grid.cell);
1116
+ const b = Math.ceil((x1 + grid.half) / grid.cell);
1117
+ const c = Math.floor((z0 + grid.half) / grid.cell);
1118
+ const e = Math.ceil((z1 + grid.half) / grid.cell);
1119
+ for (let gz = c; gz < e; gz++) for (let gx = a; gx < b; gx++) doorAt[gz * grid.n + gx] = i;
1120
+ return i;
1121
+ },
1122
+ set,
1123
+ hit(index, damage) {
1124
+ const d = list[index];
1125
+ if (!d || d.open || d.broken) return;
1126
+ d.hp -= damage;
1127
+ if (random() < N.soundChance) emit("sound", { sound: "wood", volume: N.soundVolume, position: [d.x, 1, d.z] });
1128
+ if (d.planks && d.hp < d.hpMax - d.planks * N.plankHp + N.plankHp * (d.planks - 1)) d.planks = Math.max(0, d.planks - 1);
1129
+ if (d.hp <= 0) {
1130
+ d.broken = true;
1131
+ d.planks = 0;
1132
+ set(index, true);
1133
+ emit("noise", { x: d.x, z: d.z, radius: N.breakNoise });
1134
+ emit("sound", { sound: "wood", volume: N.breakVolume, position: [d.x, 1, d.z] });
1135
+ emit("effect", { effect: "dust", point: [d.x, 1, d.z], color: 9071178 });
1136
+ emit("broken", { door: index });
1137
+ }
1138
+ },
1139
+ plank(index) {
1140
+ const d = list[index];
1141
+ if (!d) return;
1142
+ d.planks += 1;
1143
+ d.hp += N.plankHp;
1144
+ d.hpMax += N.plankHp;
1145
+ },
1146
+ on(event, listener) {
1147
+ let set_ = listeners.get(event);
1148
+ if (!set_) listeners.set(event, set_ = /* @__PURE__ */ new Set());
1149
+ set_.add(listener);
1150
+ return () => void set_.delete(listener);
1151
+ }
1152
+ };
1153
+ }
1154
+
1155
+ // src/grid.ts
1156
+ var CELL = { FREE: 0, SOLID: 1, LOW_WALL: 2, OPEN_DOOR: 3, GLASS: 4, CLOSED_DOOR: 5 };
1157
+ var BORDER_WIDTH = 1;
1158
+ var BORDER_HEIGHT = 50;
1159
+ var LOS_STEP = 0.5;
1160
+ var LOS_HEIGHT = 1.5;
1161
+ function createNavGrid(options = {}) {
1162
+ const size = options.size ?? 768;
1163
+ const half = size / 2;
1164
+ const cell = options.cell ?? 0.5;
1165
+ const n = size / cell;
1166
+ const occ = new Uint8Array(n * n);
1167
+ const occH = new Float32Array(n * n);
1168
+ const cellOf = (x, z) => {
1169
+ const gx = Math.floor((x + half) / cell);
1170
+ const gz = Math.floor((z + half) / cell);
1171
+ return gx < 0 || gz < 0 || gx >= n || gz >= n ? -1 : gz * n + gx;
1172
+ };
1173
+ const solidAt = (x, z, y = 0.5) => {
1174
+ const c = cellOf(x, z);
1175
+ return c < 0 || occ[c] !== 0 && occ[c] !== 3 && occH[c] > y;
1176
+ };
1177
+ const fillRect = (x0, z0, x1, z1, kind, h) => {
1178
+ const a = Math.floor((x0 + half) / cell);
1179
+ const b = Math.ceil((x1 + half) / cell);
1180
+ const c = Math.floor((z0 + half) / cell);
1181
+ const d = Math.ceil((z1 + half) / cell);
1182
+ for (let gz = Math.max(0, c); gz < Math.min(n, d); gz++)
1183
+ for (let gx = Math.max(0, a); gx < Math.min(n, b); gx++) {
1184
+ const i = gz * n + gx;
1185
+ occ[i] = kind;
1186
+ occH[i] = h;
1187
+ }
1188
+ };
1189
+ if (options.border ?? true) {
1190
+ fillRect(-half, -half, half, -half + BORDER_WIDTH, 1, BORDER_HEIGHT);
1191
+ fillRect(-half, half - BORDER_WIDTH, half, half, 1, BORDER_HEIGHT);
1192
+ fillRect(-half, -half, -half + BORDER_WIDTH, half, 1, BORDER_HEIGHT);
1193
+ fillRect(half - BORDER_WIDTH, -half, half, half, 1, BORDER_HEIGHT);
1194
+ }
1195
+ return {
1196
+ size,
1197
+ half,
1198
+ cell,
1199
+ n,
1200
+ occ,
1201
+ occH,
1202
+ cellOf,
1203
+ solidAt,
1204
+ cellKind: (x, z) => {
1205
+ const c = cellOf(x, z);
1206
+ return c < 0 ? -1 : occ[c];
1207
+ },
1208
+ fillRect,
1209
+ blocked(x, z) {
1210
+ const c = cellOf(x, z);
1211
+ return c < 0 || occ[c] !== 0 && occ[c] !== 3;
1212
+ },
1213
+ losClear(x0, z0, x1, z1) {
1214
+ const d = Math.hypot(x1 - x0, z1 - z0);
1215
+ const steps = Math.ceil(d / LOS_STEP);
1216
+ for (let k = 1; k < steps; k++) {
1217
+ const t = k / steps;
1218
+ const c = cellOf(x0 + (x1 - x0) * t, z0 + (z1 - z0) * t);
1219
+ if (c < 0 || occ[c] === 1 && occH[c] > LOS_HEIGHT) return false;
1220
+ }
1221
+ return true;
1222
+ }
1223
+ };
1224
+ }
1225
+
1226
+ // src/rng.ts
1227
+ function createRng(seed) {
1228
+ let state = seed >>> 0;
1229
+ return () => {
1230
+ state = state + 1831565813 >>> 0;
1231
+ let mixed = Math.imul(state ^ state >>> 15, state | 1);
1232
+ mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, mixed | 61);
1233
+ return ((mixed ^ mixed >>> 14) >>> 0) / 4294967296;
1234
+ };
1235
+ }
1236
+
1237
+ // src/store.ts
1238
+ var createStore = (capacity) => {
1239
+ const f32 = () => new Float32Array(capacity);
1240
+ const u8 = () => new Uint8Array(capacity);
1241
+ return {
1242
+ n: 0,
1243
+ capacity,
1244
+ x: f32(),
1245
+ z: f32(),
1246
+ vx: f32(),
1247
+ vz: f32(),
1248
+ yaw: f32(),
1249
+ phase: f32(),
1250
+ type: u8(),
1251
+ age: u8(),
1252
+ outfit: u8(),
1253
+ height: f32(),
1254
+ build: f32(),
1255
+ chaseSpeed: f32(),
1256
+ wanderSpeed: f32(),
1257
+ hp: f32(),
1258
+ hpMax: f32(),
1259
+ headHp: f32(),
1260
+ armHp: new Float32Array(capacity * 2),
1261
+ legHp: new Float32Array(capacity * 2),
1262
+ missing: new Uint16Array(capacity),
1263
+ helmet: u8(),
1264
+ state: u8(),
1265
+ stateTimer: f32(),
1266
+ lost: f32(),
1267
+ attackTimer: f32(),
1268
+ band: u8(),
1269
+ next: f32(),
1270
+ wx: f32(),
1271
+ wz: f32(),
1272
+ crawl: u8(),
1273
+ limp: u8(),
1274
+ color: new Uint32Array(capacity),
1275
+ skin: f32(),
1276
+ alertIn: f32(),
1277
+ cooldown: f32(),
1278
+ y: f32(),
1279
+ floor: u8(),
1280
+ floorLocked: u8(),
1281
+ vy: f32(),
1282
+ abilityTimer: f32(),
1283
+ eaten: u8(),
1284
+ stuck: f32(),
1285
+ stepCell: new Int32Array(capacity),
1286
+ burn: f32(),
1287
+ hunts: new Int8Array(capacity),
1288
+ named: new Int8Array(capacity),
1289
+ dvx: f32(),
1290
+ dvz: f32(),
1291
+ steering: u8(),
1292
+ renderYaw: f32(),
1293
+ renderSpeed: f32(),
1294
+ pause: f32(),
1295
+ lod: u8(),
1296
+ variant: u8(),
1297
+ attackPose: u8(),
1298
+ arms: f32()
1299
+ };
1300
+ };
1301
+ var AGENT_FIELDS = [
1302
+ "x",
1303
+ "z",
1304
+ "vx",
1305
+ "vz",
1306
+ "yaw",
1307
+ "phase",
1308
+ "type",
1309
+ "age",
1310
+ "outfit",
1311
+ "height",
1312
+ "build",
1313
+ "chaseSpeed",
1314
+ "wanderSpeed",
1315
+ "hp",
1316
+ "hpMax",
1317
+ "headHp",
1318
+ "missing",
1319
+ "helmet",
1320
+ "state",
1321
+ "stateTimer",
1322
+ "lost",
1323
+ "attackTimer",
1324
+ "band",
1325
+ "next",
1326
+ "wx",
1327
+ "wz",
1328
+ "crawl",
1329
+ "limp",
1330
+ "color",
1331
+ "skin",
1332
+ "alertIn",
1333
+ "cooldown",
1334
+ "y",
1335
+ "floor",
1336
+ "floorLocked",
1337
+ "vy",
1338
+ "abilityTimer",
1339
+ "eaten",
1340
+ "stuck",
1341
+ "stepCell",
1342
+ "burn",
1343
+ "hunts",
1344
+ "named",
1345
+ "dvx",
1346
+ "dvz",
1347
+ "steering",
1348
+ "renderYaw",
1349
+ "renderSpeed",
1350
+ "pause",
1351
+ "lod",
1352
+ "variant",
1353
+ "attackPose",
1354
+ "arms"
1355
+ ];
1356
+
1357
+ // src/types.ts
1358
+ var STATE = {
1359
+ WANDER: 0,
1360
+ CHASE: 1,
1361
+ GRAB: 2,
1362
+ STUNNED: 3,
1363
+ CALLING: 4,
1364
+ AIRBORNE: 5,
1365
+ DORMANT: 6,
1366
+ SWELLING: 7,
1367
+ SWING: 8,
1368
+ EATING: 9,
1369
+ PILE_STEP: 10,
1370
+ SEEKING_SOUND: 12
1371
+ };
1372
+ var MISSING = { SKULL: 1, ARM_L: 2, ARM_R: 4, LEG_L: 8, LEG_R: 16, ARM_L_BROKEN: 32, ARM_R_BROKEN: 64, LEG_L_BROKEN: 128, LEG_R_BROKEN: 256, JAW: 512 };
1373
+
1374
+ // src/crowd.ts
1375
+ var CROWD_DEFAULTS = {
1376
+ sources: defaults_default.sources,
1377
+ bands: defaults_default.bands.map((band) => ({ max: band.max ?? Infinity, dt: band.dt })),
1378
+ numbers: defaults_default.numbers
1379
+ };
1380
+ var clamp = (v, a, b) => v < a ? a : v > b ? b : v;
1381
+ var TAU_APPROX = 6.2832;
1382
+ var TURN = 6.28;
1383
+ function createCrowd(options) {
1384
+ const random = options.random ?? createRng(options.seed ?? 1);
1385
+ const R = random;
1386
+ const pack = options.pack;
1387
+ const types = pack.types;
1388
+ const N = { ...CROWD_DEFAULTS.numbers, ...options.numbers };
1389
+ const BANDS = options.bands ?? CROWD_DEFAULTS.bands;
1390
+ const HYST = N.hysteresis;
1391
+ const grid = options.grid ?? createNavGrid();
1392
+ const doors = options.doors ?? createDoors(grid, { random });
1393
+ const water = options.water ?? (() => false);
1394
+ const plugins = options.plugins ?? [];
1395
+ const clock = options.flow?.clock ?? (() => typeof performance === "undefined" ? Date.now() : performance.now());
1396
+ const FLOW_R = options.flow?.radius ?? N.flowRadius;
1397
+ const Z = createStore(options.capacity ?? N.capacity);
1398
+ const { occ, occH, cellOf, solidAt } = grid;
1399
+ const HALF = grid.half;
1400
+ const GC = grid.cell;
1401
+ const FW = grid.n;
1402
+ const HC = N.hashCell;
1403
+ const HN = Math.floor(grid.size / HC);
1404
+ const hHead = new Int32Array(HN * HN);
1405
+ const hNext = new Int32Array(Z.capacity);
1406
+ const pile = new Float32Array(FW * FW);
1407
+ let pileN = 0;
1408
+ const flow = {
1409
+ dir: new Int8Array(FW * FW * 2),
1410
+ back: new Int8Array(FW * FW * 2),
1411
+ dist: new Float32Array(FW * FW),
1412
+ q: new Int32Array((2 * FLOW_R + 1) ** 2 * 2),
1413
+ phase: 0,
1414
+ qh: 0,
1415
+ qt: 0,
1416
+ c: 0,
1417
+ t: 0,
1418
+ cell: -1,
1419
+ ms: 0,
1420
+ win: [0, 0, -1, -1],
1421
+ winB: [0, 0, -1, -1]
1422
+ };
1423
+ const catTable = [];
1424
+ types.forEach((c, i) => {
1425
+ for (let k = 0; k < c.weight; k++) catTable.push(i);
1426
+ });
1427
+ const variantsOf = [];
1428
+ {
1429
+ let next = 0;
1430
+ types.forEach((type) => {
1431
+ const list = [];
1432
+ for (let v = 0; v < (type.variants ?? 1); v++) list.push(next++);
1433
+ variantsOf.push(list);
1434
+ });
1435
+ }
1436
+ const typeIndexById = new Map(types.map((type, index) => [type.id, index]));
1437
+ const noises = [];
1438
+ const stats = { bands: [0, 0, 0, 0], slots: 0, kills: 0, heads: 0 };
1439
+ const corpses = createCorpseLedger({ max: N.corpsesMax, seconds: N.corpseSeconds });
1440
+ const trail = [];
1441
+ let trailLast = { x: 1e9, z: 1e9 };
1442
+ const killQ = [];
1443
+ let groanBudget = 0;
1444
+ let migrateT = N.migrationFirst;
1445
+ let hunt = null;
1446
+ let target = options.target ?? null;
1447
+ let nightF = 0;
1448
+ let viewYaw = 0;
1449
+ const listeners = /* @__PURE__ */ new Map();
1450
+ const emit = (event, payload) => {
1451
+ const set = listeners.get(event);
1452
+ if (set) for (const listener of set) listener(payload);
1453
+ };
1454
+ const emitNoise = (x, z, r) => void noises.push({ x, z, r });
1455
+ doors.on("noise", (e) => emitNoise(e.x, e.z, e.radius));
1456
+ doors.on("sound", (e) => emit("sound", { ...e, distance: target ? Math.hypot(e.position[0] - target.state.x, e.position[2] - target.state.z) : 0 }));
1457
+ doors.on("effect", (e) => emit("effect", e));
1458
+ doors.on("broken", (e) => {
1459
+ const d = doors.list[e.door];
1460
+ if (target && Math.hypot(d.x - target.state.x, d.z - target.state.z) < N.doorMessageDistance) emit("message", { id: "door_broken", seconds: 2 });
1461
+ });
1462
+ const sound = (id, volume, x, y, z, d) => emit("sound", { sound: id, volume, position: [x, y, z], distance: d });
1463
+ const pick = (list) => {
1464
+ const r = R();
1465
+ let acc = 0;
1466
+ for (const it of list) {
1467
+ acc += it.p;
1468
+ if (r < acc) return it;
1469
+ }
1470
+ return list[0];
1471
+ };
1472
+ const T = (i) => types[Z.type[i]];
1473
+ const traits = (i) => T(i).traits ?? {};
1474
+ const special = (i) => {
1475
+ const tr = traits(i);
1476
+ Z.y[i] = 0;
1477
+ Z.floor[i] = 0;
1478
+ Z.floorLocked[i] = 0;
1479
+ Z.vy[i] = 0;
1480
+ Z.abilityTimer[i] = 0;
1481
+ Z.eaten[i] = 0;
1482
+ Z.stuck[i] = 0;
1483
+ Z.stepCell[i] = -1;
1484
+ Z.burn[i] = 0;
1485
+ Z.hunts[i] = -1;
1486
+ Z.named[i] = -1;
1487
+ if (tr.color !== void 0 && !tr.submerged) {
1488
+ Z.color[i] = tr.color;
1489
+ if (tr.skin !== void 0) Z.skin[i] = tr.skin;
1490
+ }
1491
+ if (tr.ambush && R() < tr.ambush.chance) {
1492
+ Z.state[i] = STATE.DORMANT;
1493
+ Z.yaw[i] = R() * TURN;
1494
+ }
1495
+ if (tr.submerged) {
1496
+ Z.state[i] = STATE.DORMANT;
1497
+ Z.y[i] = tr.submerged.depth;
1498
+ if (tr.color !== void 0) Z.color[i] = tr.color;
1499
+ }
1500
+ };
1501
+ const spawn = (x, z, catForce = -1) => {
1502
+ if (Z.n >= Z.capacity) return -1;
1503
+ const i = Z.n++;
1504
+ const c = catForce >= 0 ? catForce : catTable[Math.floor(R() * catTable.length)];
1505
+ const C = types[c];
1506
+ const ai = pack.ages.indexOf(pick(pack.ages));
1507
+ const maxAge = C.traits?.maxAge;
1508
+ const A = pack.ages[maxAge !== void 0 ? Math.min(ai, maxAge) : ai];
1509
+ const ri = pack.outfits.indexOf(pick(pack.outfits));
1510
+ const RO = pack.outfits[ri];
1511
+ Z.x[i] = x;
1512
+ Z.z[i] = z;
1513
+ Z.vx[i] = Z.vz[i] = 0;
1514
+ Z.type[i] = c;
1515
+ Z.age[i] = pack.ages.indexOf(A);
1516
+ Z.outfit[i] = ri;
1517
+ const g = (R() + R() + R() - 1.5) * 0.09;
1518
+ Z.height[i] = clamp(1 + g, 0.9, 1.1) * (C.scale || 1);
1519
+ const bb = R();
1520
+ Z.build[i] = (bb < 0.3 ? 0.85 + R() * 0.1 : bb < 0.8 ? 0.95 + R() * 0.13 : 1.08 + R() * 0.17) * (C.build || (C.scale ? 1.3 : 1));
1521
+ const heavy = bb >= 0.8;
1522
+ const thin = bb < 0.3;
1523
+ Z.chaseSpeed[i] = C.speed.chase * A.speed * (thin ? 1.05 : heavy ? 0.9 : 1) * (0.92 + R() * 0.16);
1524
+ Z.wanderSpeed[i] = C.speed.wander * A.speed * (0.8 + R() * 0.4);
1525
+ Z.hpMax[i] = Z.hp[i] = C.health * A.health * (thin ? 0.9 : heavy ? 1.15 : 1);
1526
+ Z.headHp[i] = pack.segments.head;
1527
+ Z.armHp[i * 2] = Z.armHp[i * 2 + 1] = pack.segments.arm;
1528
+ Z.legHp[i * 2] = Z.legHp[i * 2 + 1] = pack.segments.leg;
1529
+ Z.missing[i] = 0;
1530
+ Z.helmet[i] = RO.helmet ? 1 : 0;
1531
+ Z.crawl[i] = C.crawls ? 1 : 0;
1532
+ Z.limp[i] = 0;
1533
+ Z.state[i] = 0;
1534
+ Z.stateTimer[i] = 0;
1535
+ Z.lost[i] = 0;
1536
+ Z.attackTimer[i] = R();
1537
+ Z.alertIn[i] = 0;
1538
+ Z.cooldown[i] = 0;
1539
+ {
1540
+ const vs = variantsOf[c];
1541
+ Z.variant[i] = vs[Math.floor(R() * vs.length)];
1542
+ }
1543
+ Z.yaw[i] = Z.renderYaw[i] = R() * TURN;
1544
+ Z.renderSpeed[i] = 0;
1545
+ Z.steering[i] = 0;
1546
+ Z.pause[i] = 0;
1547
+ Z.attackPose[i] = 0;
1548
+ Z.arms[i] = 0;
1549
+ Z.phase[i] = R() * 10;
1550
+ Z.band[i] = 3;
1551
+ Z.next[i] = R() * 0.05;
1552
+ Z.wx[i] = x;
1553
+ Z.wz[i] = z;
1554
+ Z.color[i] = RO.color || pack.palette[Math.floor(R() * pack.palette.length)];
1555
+ Z.skin[i] = R();
1556
+ special(i);
1557
+ emit("spawn", { agent: i, type: C.id });
1558
+ return i;
1559
+ };
1560
+ const remove = (i) => {
1561
+ if (Z.state[i] === STATE.PILE_STEP) stepRelease(i);
1562
+ const j = --Z.n;
1563
+ if (i === j) {
1564
+ emit("remove", { agent: i, moved: -1 });
1565
+ return;
1566
+ }
1567
+ for (const k of AGENT_FIELDS) Z[k][i] = Z[k][j];
1568
+ Z.armHp[i * 2] = Z.armHp[j * 2];
1569
+ Z.armHp[i * 2 + 1] = Z.armHp[j * 2 + 1];
1570
+ Z.legHp[i * 2] = Z.legHp[j * 2];
1571
+ Z.legHp[i * 2 + 1] = Z.legHp[j * 2 + 1];
1572
+ if (target) {
1573
+ if (target.state.grabbedBy === j) target.state.grabbedBy = i;
1574
+ else if (target.state.grabbedBy === i) target.state.grabbedBy = -1;
1575
+ }
1576
+ emit("remove", { agent: i, moved: j });
1577
+ };
1578
+ const flowStart = (x, z) => {
1579
+ const gx = clamp(Math.floor((x + HALF) / GC), 0, FW - 1);
1580
+ const gz = clamp(Math.floor((z + HALF) / GC), 0, FW - 1);
1581
+ const W = flow.winB;
1582
+ W[0] = Math.max(0, gx - FLOW_R);
1583
+ W[1] = Math.max(0, gz - FLOW_R);
1584
+ W[2] = Math.min(FW - 1, gx + FLOW_R);
1585
+ W[3] = Math.min(FW - 1, gz + FLOW_R);
1586
+ for (let r = W[1]; r <= W[3]; r++) flow.dist.fill(1e9, r * FW + W[0], r * FW + W[2] + 1);
1587
+ const s = gz * FW + gx;
1588
+ flow.dist[s] = 0;
1589
+ flow.q[0] = s;
1590
+ flow.qh = 0;
1591
+ flow.qt = 1;
1592
+ flow.phase = 1;
1593
+ flow.c = 0;
1594
+ flow.t = 0;
1595
+ };
1596
+ const flowStep = (budget) => {
1597
+ if (!flow.phase) return;
1598
+ const t0 = clock();
1599
+ const D = flow.dist;
1600
+ const Q = flow.q;
1601
+ const F = flow.back;
1602
+ const W = flow.winB;
1603
+ let n = 0;
1604
+ while (flow.phase === 1) {
1605
+ if (flow.qh >= flow.qt) {
1606
+ flow.phase = 2;
1607
+ break;
1608
+ }
1609
+ const c = Q[flow.qh++];
1610
+ const cx = c % FW;
1611
+ const cz = c / FW | 0;
1612
+ const dc = D[c];
1613
+ for (let dz = -1; dz <= 1; dz++)
1614
+ for (let dx = -1; dx <= 1; dx++) {
1615
+ if (!dx && !dz) continue;
1616
+ const nx = cx + dx;
1617
+ const nz = cz + dz;
1618
+ if (nx < W[0] || nz < W[1] || nx > W[2] || nz > W[3]) continue;
1619
+ const k = nz * FW + nx;
1620
+ if (occ[k] && occ[k] < 3) continue;
1621
+ if (dx && dz && (occ[cz * FW + nx] && occ[cz * FW + nx] < 3 || occ[nz * FW + cx] && occ[nz * FW + cx] < 3)) continue;
1622
+ const nd = dc + (dx && dz ? 1.414 : 1);
1623
+ if (nd < D[k] - 1e-3) {
1624
+ D[k] = nd;
1625
+ if (flow.qt < Q.length) Q[flow.qt++] = k;
1626
+ }
1627
+ }
1628
+ if ((++n & 2047) === 0 && clock() - t0 > budget) {
1629
+ flow.t += clock() - t0;
1630
+ return;
1631
+ }
1632
+ }
1633
+ const ww = W[2] - W[0] + 1;
1634
+ while (flow.phase === 2) {
1635
+ const j = flow.c++;
1636
+ if (j >= ww * (W[3] - W[1] + 1)) {
1637
+ const tmp = flow.dir;
1638
+ flow.dir = F;
1639
+ flow.back = tmp;
1640
+ const tw = flow.win;
1641
+ flow.win = W;
1642
+ flow.winB = tw;
1643
+ flow.phase = 0;
1644
+ flow.ms = flow.t + clock() - t0;
1645
+ return;
1646
+ }
1647
+ const cx = W[0] + j % ww;
1648
+ const cz = W[1] + (j / ww | 0);
1649
+ const c = cz * FW + cx;
1650
+ if (occ[c] && occ[c] < 3) {
1651
+ F[c * 2] = F[c * 2 + 1] = 0;
1652
+ continue;
1653
+ }
1654
+ let best = D[c];
1655
+ let bx = 0;
1656
+ let bz = 0;
1657
+ for (let dz = -1; dz <= 1; dz++)
1658
+ for (let dx = -1; dx <= 1; dx++) {
1659
+ const nx = cx + dx;
1660
+ const nz = cz + dz;
1661
+ if (nx < W[0] || nz < W[1] || nx > W[2] || nz > W[3]) continue;
1662
+ const d = D[nz * FW + nx];
1663
+ if (d < best) {
1664
+ best = d;
1665
+ bx = dx;
1666
+ bz = dz;
1667
+ }
1668
+ }
1669
+ F[c * 2] = bx;
1670
+ F[c * 2 + 1] = bz;
1671
+ if ((j & 8191) === 0 && clock() - t0 > budget) {
1672
+ flow.t += clock() - t0;
1673
+ return;
1674
+ }
1675
+ }
1676
+ };
1677
+ const losClear = grid.losClear;
1678
+ const groundAt = (c, _y) => {
1679
+ if (c < 0) return 0;
1680
+ const k = occ[c];
1681
+ if (k === 2 || k === 1 && occH[c] <= _y + 0.05) return occH[c];
1682
+ return pile[c];
1683
+ };
1684
+ const blockedAt = (i, x, z) => {
1685
+ const c = cellOf(x, z);
1686
+ if (c < 0) return true;
1687
+ const k = occ[c];
1688
+ const y = Z.y[i];
1689
+ if (k === 0 || k === 3) return pile[c] > y + 0.5 && Z.state[i] !== STATE.AIRBORNE;
1690
+ if (k === 2 && y + 0.5 >= occH[c]) return false;
1691
+ if (Z.state[i] === STATE.AIRBORNE && occH[c] <= y) return false;
1692
+ return true;
1693
+ };
1694
+ const tryStep = (i, hitCell) => {
1695
+ if (pileN >= N.pileMax) return false;
1696
+ const c = cellOf(Z.x[i], Z.z[i]);
1697
+ if (c < 0 || occ[c] !== 0) return false;
1698
+ const dir = hitCell - c;
1699
+ const w = c + dir;
1700
+ const back = c - dir;
1701
+ const h = pile[c] + N.pileStep;
1702
+ if (h > N.pileTop) return false;
1703
+ const cap = occ[w] === 2 ? (back >= 0 ? pile[back] : 0) + 1 : pile[w];
1704
+ if (h > cap + 1e-3) return false;
1705
+ pile[c] = h;
1706
+ pileN++;
1707
+ Z.state[i] = STATE.PILE_STEP;
1708
+ Z.stepCell[i] = c;
1709
+ Z.vx[i] = Z.vz[i] = 0;
1710
+ Z.y[i] = h - N.pileStep;
1711
+ Z.stateTimer[i] = 6 + R() * 6;
1712
+ return true;
1713
+ };
1714
+ function stepRelease(i) {
1715
+ const c = Z.stepCell[i];
1716
+ if (c >= 0) {
1717
+ pile[c] = Math.max(0, pile[c] - N.pileStep);
1718
+ pileN = Math.max(0, pileN - 1);
1719
+ }
1720
+ Z.stepCell[i] = -1;
1721
+ Z.state[i] = STATE.CHASE;
1722
+ Z.stuck[i] = 0;
1723
+ }
1724
+ const pileBlast = (x, z, r) => {
1725
+ for (let i = 0; i < Z.n; i++)
1726
+ if (Z.state[i] === STATE.PILE_STEP && (Z.x[i] - x) ** 2 + (Z.z[i] - z) ** 2 < r * r) {
1727
+ stepRelease(i);
1728
+ Z.state[i] = STATE.STUNNED;
1729
+ Z.stateTimer[i] = 1.2;
1730
+ }
1731
+ };
1732
+ const voice = (i) => traits(i).voice ?? "groan";
1733
+ const groan = (i, d) => {
1734
+ const r2 = N.alertRadius * N.alertRadius;
1735
+ for (let j = 0; j < Z.n; j++) {
1736
+ if (Z.state[j] !== 0 || Z.alertIn[j] > 0) continue;
1737
+ const ex = Z.x[j] - Z.x[i];
1738
+ const ez = Z.z[j] - Z.z[i];
1739
+ if (ex * ex + ez * ez < r2) Z.alertIn[j] = N.alertDelay + R() * N.alertDelayRandom;
1740
+ }
1741
+ emit("alert", { agent: i });
1742
+ if (d < N.voiceDistance && groanBudget <= 0) {
1743
+ sound(voice(i), 1, Z.x[i], Z.height[i] * 1.6, Z.z[i], d);
1744
+ groanBudget = N.voiceEvery;
1745
+ }
1746
+ };
1747
+ const screamAt = (i, x, z) => {
1748
+ const tr = traits(i).caller;
1749
+ sound("scream_hoarse", 1, x, 1.6, z, target ? Math.hypot(x - target.state.x, z - target.state.z) : 0);
1750
+ for (let j = 0; j < Z.n; j++) {
1751
+ const ex = Z.x[j] - x;
1752
+ const ez = Z.z[j] - z;
1753
+ if (ex * ex + ez * ez < tr.radius * tr.radius) {
1754
+ Z.state[j] = STATE.CHASE;
1755
+ Z.lost[j] = 0;
1756
+ }
1757
+ }
1758
+ if (target) target.state.screamBoost = tr.boost;
1759
+ emit("message", { id: "called", seconds: 3, type: T(i).id });
1760
+ emit("scream", { agent: i, x, z });
1761
+ };
1762
+ const attack = (i, d, tdt) => {
1763
+ Z.attackTimer[i] -= tdt;
1764
+ if (Z.attackTimer[i] > 0) return;
1765
+ Z.attackTimer[i] = N.attackEvery;
1766
+ const C = T(i);
1767
+ const tr = C.traits ?? {};
1768
+ if (tr.heavySwing && R() < tr.heavySwing.chance) {
1769
+ Z.state[i] = STATE.SWING;
1770
+ Z.stateTimer[i] = tr.heavySwing.windUp;
1771
+ return;
1772
+ }
1773
+ const noJaw = Z.missing[i] & 512;
1774
+ const noArms = (Z.missing[i] & 6) === 6;
1775
+ const P = target;
1776
+ if (P.state.grabbedBy < 0 && !noArms && !Z.crawl[i] && R() < C.bite * (P.state.crouch ? N.crouchBite : 1)) {
1777
+ P.grab(i, !!noJaw, !!tr.drownGrab && water(P.state.x, P.state.z));
1778
+ Z.state[i] = STATE.GRAB;
1779
+ } else {
1780
+ P.hurt(C.damage * (Z.crawl[i] ? N.crawlDamage : 1), i);
1781
+ if (tr.burns && Z.abilityTimer[i] >= 0) P.state.burnTimer = tr.burns.seconds;
1782
+ }
1783
+ void d;
1784
+ };
1785
+ const effect = (payload) => emit("effect", payload);
1786
+ const blood = (x, y, z, age, amount, died, agent) => effect({ effect: "blood", point: [x, y, z], age, amount, died, ...agent === void 0 ? {} : { agent } });
1787
+ const damage = (i, hit) => {
1788
+ const C = T(i);
1789
+ const A = pack.ages[Z.age[i]];
1790
+ const RO = pack.outfits[Z.outfit[i]];
1791
+ const cal = hit.caliber ?? null;
1792
+ const proj = !!cal;
1793
+ const dano = hit.amount;
1794
+ const dist = hit.distance;
1795
+ const corte = hit.cut ?? 0;
1796
+ const regiao = hit.region;
1797
+ let morreu = false;
1798
+ let headshot = false;
1799
+ if (regiao === 0) {
1800
+ if (proj && Z.helmet[i] && cal.class === "pistol") {
1801
+ Z.helmet[i] = 0;
1802
+ effect({ effect: "spark", point: [Z.x[i], Z.height[i] * 1.75, Z.z[i]], agent: i });
1803
+ sound("impact", 1, Z.x[i], Z.height[i] * 1.75, Z.z[i], dist);
1804
+ return { died: false };
1805
+ }
1806
+ const mata = proj ? (cal.skull === 1 || dist <= cal.skull) && dano >= N.skullMinDamage : dano * 3 >= Z.headHp[i];
1807
+ if (mata) {
1808
+ morreu = true;
1809
+ headshot = true;
1810
+ if (dano > N.skullTakeDamage || cal && N.skullTakeWithin[cal.id] !== void 0 && dist < N.skullTakeWithin[cal.id]) Z.missing[i] |= 1;
1811
+ } else {
1812
+ Z.hp[i] -= dano * 3;
1813
+ Z.headHp[i] -= dano;
1814
+ if (dano > N.jawDamage && R() < N.jawChance) Z.missing[i] |= 512;
1815
+ }
1816
+ } else if (regiao === 1) {
1817
+ let m = C.multipliers.torso;
1818
+ if (RO.vest && proj) m *= cal.class === "pistol" ? RO.vest.pistol : RO.vest.rifle;
1819
+ Z.hp[i] -= dano * m;
1820
+ } else {
1821
+ const perna = regiao >= 4;
1822
+ const lado = regiao & 1;
1823
+ const mm = (perna ? C.multipliers.legs : C.multipliers.arms) * A.limb * (RO.limb || 1);
1824
+ const segArr = perna ? Z.legHp : Z.armHp;
1825
+ const k = i * 2 + (perna ? lado : regiao === 3 ? 1 : 0);
1826
+ segArr[k] -= dano * mm;
1827
+ Z.hp[i] -= dano * mm * 0.5;
1828
+ const max = perna ? pack.segments.leg : pack.segments.arm;
1829
+ if (perna && segArr[k] <= max * 0.5) Z.limp[i] = 1;
1830
+ if (segArr[k] <= 0) {
1831
+ const arranca = corte >= 0.5 || dano * mm > max * 0.9;
1832
+ if (perna) {
1833
+ Z.crawl[i] = 1;
1834
+ if (arranca) Z.missing[i] |= lado ? 16 : 8;
1835
+ else Z.missing[i] |= lado ? 256 : 128;
1836
+ } else if (arranca) Z.missing[i] |= regiao === 3 ? 4 : 2;
1837
+ else Z.missing[i] |= regiao === 3 ? 64 : 32;
1838
+ effect({ effect: "limb", point: [Z.x[i], Z.height[i] * (perna ? 0.5 : 1.2), Z.z[i]], agent: i, region: regiao, severed: arranca });
1839
+ }
1840
+ }
1841
+ if (Z.hp[i] <= 0) morreu = true;
1842
+ blood(Z.x[i], regiao === 0 ? Z.height[i] * 1.65 : regiao === 1 ? Z.height[i] * 1.2 : Z.height[i] * 0.7, Z.z[i], Z.age[i], regiao === 0 ? 120 : 30, morreu, i);
1843
+ if (morreu) {
1844
+ kill(i, headshot);
1845
+ return { died: morreu, headshot };
1846
+ }
1847
+ const st = Z.state[i];
1848
+ if (dano >= N.hitStunDamage && st !== STATE.GRAB && st !== STATE.PILE_STEP && st !== STATE.AIRBORNE) {
1849
+ Z.state[i] = STATE.STUNNED;
1850
+ Z.stateTimer[i] = C.traits?.hitStun ?? N.hitStun;
1851
+ }
1852
+ const st2 = Z.state[i];
1853
+ if (st2 === 0 || st2 === STATE.DORMANT || st2 === STATE.EATING || st2 === STATE.SEEKING_SOUND) {
1854
+ Z.state[i] = STATE.CHASE;
1855
+ Z.lost[i] = 0;
1856
+ const sub = C.traits?.submerged;
1857
+ if (sub) Z.y[i] = Math.max(Z.y[i], sub.surface);
1858
+ }
1859
+ return { died: morreu };
1860
+ };
1861
+ function kill(i, headshot = false) {
1862
+ stats.kills++;
1863
+ if (headshot) stats.heads++;
1864
+ const tr = traits(i);
1865
+ if (tr.burst) burstAt(i, Z.x[i], Z.z[i]);
1866
+ const C = T(i);
1867
+ emit("kill", { agent: i, type: C.id, headshot, x: Z.x[i], z: Z.z[i] });
1868
+ corpseAdd(i);
1869
+ if (target && target.state.grabbedBy === i) target.release();
1870
+ remove(i);
1871
+ }
1872
+ const burstAt = (i, x, z) => {
1873
+ const b = traits(i).burst;
1874
+ effect({ effect: "cloud", point: [x, 0, z], radius: 4, color: b.cloudColor, amount: 8 });
1875
+ effect({ effect: "blood", point: [x, 1, z], amount: 1500, died: true, color: b.bloodColor });
1876
+ if (target) {
1877
+ const d = Math.hypot(x - target.state.x, z - target.state.z);
1878
+ if (d < b.radius) {
1879
+ target.state.blind = b.blind;
1880
+ target.state.scentTimer = b.scent;
1881
+ emit("message", { id: "burst_hit", seconds: 2.5, type: T(i).id });
1882
+ emitNoise(target.state.x, target.state.z, b.noiseNear);
1883
+ }
1884
+ }
1885
+ emitNoise(x, z, b.noise);
1886
+ };
1887
+ const corpseAdd = (i) => {
1888
+ corpses.add({
1889
+ x: Z.x[i],
1890
+ z: Z.z[i],
1891
+ yaw: Z.renderYaw[i],
1892
+ height: Z.height[i],
1893
+ build: Z.build[i],
1894
+ named: Z.named[i],
1895
+ missing: Z.missing[i],
1896
+ color: Z.color[i],
1897
+ variant: Z.variant[i],
1898
+ skin: Z.skin[i],
1899
+ seed: R() * 10
1900
+ });
1901
+ };
1902
+ const ability = (i, d, dx, dz, tdt, b, alive) => {
1903
+ const C = T(i);
1904
+ const tr = C.traits ?? {};
1905
+ const st = Z.state[i];
1906
+ const P = target;
1907
+ if (st === STATE.DORMANT) {
1908
+ Z.vx[i] = Z.vz[i] = 0;
1909
+ if (!alive || b > 1) return true;
1910
+ if (tr.submerged) {
1911
+ if (d < tr.submerged.wake) {
1912
+ Z.state[i] = STATE.CHASE;
1913
+ groan(i, d);
1914
+ sound("step", 2, Z.x[i], 0, Z.z[i], d);
1915
+ }
1916
+ return true;
1917
+ }
1918
+ const am = tr.ambush;
1919
+ const wake = am?.wake ?? N.dormantWake;
1920
+ const behind = am?.wakeBehind ?? N.dormantWakeBehind;
1921
+ const fx = -Math.sin(viewYaw);
1922
+ const fz = -Math.cos(viewYaw);
1923
+ const costas = (dx * fx + dz * fz) / (d || 1) < (am?.behindCos ?? -0.3);
1924
+ if (d < wake || costas && d < behind) {
1925
+ Z.state[i] = STATE.CHASE;
1926
+ Z.abilityTimer[i] = am?.sprintSeconds ?? 3;
1927
+ Z.lost[i] = 0;
1928
+ sound("runner_groan", 1, Z.x[i], 1.6, Z.z[i], d);
1929
+ }
1930
+ return true;
1931
+ }
1932
+ if (st === STATE.AIRBORNE || st === STATE.PILE_STEP) return true;
1933
+ if (st === STATE.SWELLING) {
1934
+ Z.vx[i] = Z.vz[i] = 0;
1935
+ return true;
1936
+ }
1937
+ if (st === STATE.SWING) {
1938
+ Z.vx[i] = Z.vz[i] = 0;
1939
+ Z.stateTimer[i] -= tdt;
1940
+ if (Z.stateTimer[i] <= 0) {
1941
+ const hs = tr.heavySwing;
1942
+ Z.state[i] = STATE.CHASE;
1943
+ Z.attackTimer[i] = 2;
1944
+ sound("impact", 1.4, Z.x[i], 1, Z.z[i], d);
1945
+ if (d < hs.range && alive && P) {
1946
+ P.hurt(C.damage, i);
1947
+ P.knock(hs.knock);
1948
+ emit("message", { id: "swing_knock", seconds: 1.5, type: C.id });
1949
+ }
1950
+ }
1951
+ return true;
1952
+ }
1953
+ if (st === STATE.EATING) {
1954
+ const ec2 = tr.eatsCorpses;
1955
+ Z.vx[i] = Z.vz[i] = 0;
1956
+ Z.stateTimer[i] -= tdt;
1957
+ if (alive && d < ec2.defend) {
1958
+ Z.state[i] = STATE.CHASE;
1959
+ Z.lost[i] = 0;
1960
+ groan(i, d);
1961
+ return false;
1962
+ }
1963
+ if (Z.stateTimer[i] <= 0) {
1964
+ let best = -1;
1965
+ let bd = ec2.grabRange;
1966
+ const list = corpses.entries;
1967
+ for (let k = 0; k < list.length; k++) {
1968
+ const e = Math.hypot(list[k].data.x - Z.x[i], list[k].data.z - Z.z[i]);
1969
+ if (e < bd) {
1970
+ bd = e;
1971
+ best = k;
1972
+ }
1973
+ }
1974
+ if (best >= 0) {
1975
+ corpses.remove(best);
1976
+ Z.hp[i] += ec2.health;
1977
+ Z.hpMax[i] += ec2.health;
1978
+ Z.height[i] *= ec2.grow;
1979
+ Z.build[i] *= ec2.grow;
1980
+ Z.eaten[i]++;
1981
+ }
1982
+ Z.state[i] = 0;
1983
+ Z.abilityTimer[i] = ec2.rest;
1984
+ }
1985
+ return true;
1986
+ }
1987
+ if (st === STATE.SEEKING_SOUND) {
1988
+ const ss = tr.soundSeeker ?? { arrive: 1, loseChase: 3, hitRadius2: 1.1 };
1989
+ if (alive && d < 2.5) {
1990
+ Z.state[i] = STATE.CHASE;
1991
+ return false;
1992
+ }
1993
+ const wx = Z.wx[i] - Z.x[i];
1994
+ const wz = Z.wz[i] - Z.z[i];
1995
+ const wl = Math.hypot(wx, wz);
1996
+ if (wl < ss.arrive) {
1997
+ Z.state[i] = 0;
1998
+ return false;
1999
+ }
2000
+ Z.vx[i] = wx / wl * Z.chaseSpeed[i];
2001
+ Z.vz[i] = wz / wl * Z.chaseSpeed[i];
2002
+ Z.yaw[i] = Math.atan2(wx, wz);
2003
+ if (b === 0) {
2004
+ Z.attackTimer[i] -= tdt;
2005
+ if (Z.attackTimer[i] <= 0) {
2006
+ const hx = Math.floor(Z.x[i] + HALF);
2007
+ const hz = Math.floor(Z.z[i] + HALF);
2008
+ if (hx >= 0 && hz >= 0 && hx < HN && hz < HN)
2009
+ for (let j = hHead[hz * HN + hx]; j !== -1; j = hNext[j]) {
2010
+ if (j !== i && (Z.x[j] - Z.x[i]) ** 2 + (Z.z[j] - Z.z[i]) ** 2 < ss.hitRadius2) {
2011
+ Z.attackTimer[i] = N.attackEvery;
2012
+ hurtOther(j, C.damage);
2013
+ break;
2014
+ }
2015
+ }
2016
+ }
2017
+ }
2018
+ return true;
2019
+ }
2020
+ for (const plugin of plugins) if (plugin.ability?.(crowd, i, { d, dx, dz, tdt, band: b, alive })) return true;
2021
+ if (!alive) return false;
2022
+ if (Z.hunts[i] >= 0 && st <= 1 && hunt) return hunt(i, tdt);
2023
+ if (tr.soundSeeker && st === STATE.CHASE && d > tr.soundSeeker.loseChase) {
2024
+ Z.state[i] = STATE.SEEKING_SOUND;
2025
+ Z.wx[i] = P.state.x;
2026
+ Z.wz[i] = P.state.z;
2027
+ return false;
2028
+ }
2029
+ const po = tr.pounce;
2030
+ if (po && st === STATE.CHASE && Z.cooldown[i] <= 0 && d > po.min && d < po.max && b === 0 && losClear(Z.x[i], Z.z[i], P.state.x, P.state.z)) {
2031
+ const sp = Math.min(po.speedCap, d / po.time);
2032
+ Z.state[i] = STATE.AIRBORNE;
2033
+ Z.vy[i] = P.state.y > po.highAbove ? po.liftHigh : po.lift;
2034
+ Z.vx[i] = -dx / d * sp;
2035
+ Z.vz[i] = -dz / d * sp;
2036
+ Z.yaw[i] = Math.atan2(-dx, -dz);
2037
+ Z.cooldown[i] = po.cooldown;
2038
+ Z.abilityTimer[i] = 1;
2039
+ sound("runner_groan", 1, Z.x[i], 1.6, Z.z[i], d);
2040
+ return true;
2041
+ }
2042
+ if (tr.burst && st === STATE.CHASE && d < tr.burst.range) {
2043
+ Z.state[i] = STATE.SWELLING;
2044
+ Z.stateTimer[i] = tr.burst.swell;
2045
+ sound("flesh", 1.2, Z.x[i], 1, Z.z[i], d);
2046
+ return true;
2047
+ }
2048
+ const ec = tr.eatsCorpses;
2049
+ if (ec && st === 0 && Z.eaten[i] < ec.max && Z.abilityTimer[i] <= 0 && corpses.count) {
2050
+ let best = -1;
2051
+ let bd = ec.search;
2052
+ const list = corpses.entries;
2053
+ for (let k = 0; k < list.length; k++) {
2054
+ const e = Math.hypot(list[k].data.x - Z.x[i], list[k].data.z - Z.z[i]);
2055
+ if (e < bd) {
2056
+ bd = e;
2057
+ best = k;
2058
+ }
2059
+ }
2060
+ if (best >= 0) {
2061
+ if (bd < ec.reach) {
2062
+ Z.state[i] = STATE.EATING;
2063
+ Z.stateTimer[i] = ec.seconds;
2064
+ Z.vx[i] = Z.vz[i] = 0;
2065
+ return true;
2066
+ }
2067
+ const cp = list[best].data;
2068
+ const ex = cp.x - Z.x[i];
2069
+ const ez = cp.z - Z.z[i];
2070
+ const sp = Z.wanderSpeed[i] * ec.approach;
2071
+ Z.vx[i] = ex / bd * sp;
2072
+ Z.vz[i] = ez / bd * sp;
2073
+ Z.yaw[i] = Math.atan2(ex, ez);
2074
+ Z.wx[i] = cp.x;
2075
+ Z.wz[i] = cp.z;
2076
+ return true;
2077
+ } else Z.abilityTimer[i] = ec.retry;
2078
+ }
2079
+ const tk = tr.tracker;
2080
+ if (tk && st === 0 && trail.length) {
2081
+ let best = null;
2082
+ for (let k = trail.length - 1; k >= 0; k--) {
2083
+ const p = trail[k];
2084
+ if ((p.x - Z.x[i]) ** 2 + (p.z - Z.z[i]) ** 2 < tk.range * tk.range) {
2085
+ best = p;
2086
+ break;
2087
+ }
2088
+ }
2089
+ if (best) {
2090
+ Z.wx[i] = best.x;
2091
+ Z.wz[i] = best.z;
2092
+ let led = 0;
2093
+ const leads = typeIndexById.get(tk.leads) ?? -1;
2094
+ for (let j = 0; j < Z.n && led < tk.max; j++)
2095
+ if (Z.type[j] === leads && Z.state[j] === 0 && (Z.x[j] - Z.x[i]) ** 2 + (Z.z[j] - Z.z[i]) ** 2 < tk.leadRadius * tk.leadRadius) {
2096
+ Z.wx[j] = Z.x[i];
2097
+ Z.wz[j] = Z.z[i];
2098
+ led++;
2099
+ }
2100
+ }
2101
+ }
2102
+ return false;
2103
+ };
2104
+ const chaseSpeed = (i) => {
2105
+ const tr = traits(i);
2106
+ if (tr.ambush) return Z.abilityTimer[i] > 0 ? tr.ambush.sprint : tr.ambush.slow;
2107
+ if (tr.submerged && water(Z.x[i], Z.z[i])) return tr.submerged.swim;
2108
+ return Z.chaseSpeed[i];
2109
+ };
2110
+ const hurtOther = (j, amount) => {
2111
+ Z.hp[j] -= amount;
2112
+ blood(Z.x[j], Z.height[j] * 1.2, Z.z[j], Z.age[j], 30, false, j);
2113
+ if (Z.hp[j] <= 0) deferKill(j);
2114
+ };
2115
+ const deferKill = (j) => {
2116
+ if (!killQ.includes(j)) killQ.push(j);
2117
+ };
2118
+ const killQFlush = () => {
2119
+ if (!killQ.length) return;
2120
+ killQ.sort((a, b) => b - a);
2121
+ for (const j of killQ) if (j < Z.n) kill(j, false);
2122
+ killQ.length = 0;
2123
+ };
2124
+ const frame = (i, dt, d, alive) => {
2125
+ const st = Z.state[i];
2126
+ const tr = traits(i);
2127
+ if (st === STATE.PILE_STEP) {
2128
+ Z.stateTimer[i] -= dt;
2129
+ if (!alive || d > N.pileReleaseDistance || Z.stateTimer[i] <= 0) stepRelease(i);
2130
+ return;
2131
+ }
2132
+ if (st === STATE.SWELLING) {
2133
+ Z.stateTimer[i] -= dt;
2134
+ if (Z.stateTimer[i] <= 0) deferKill(i);
2135
+ return;
2136
+ }
2137
+ if (tr.ambush && Z.abilityTimer[i] > 0) Z.abilityTimer[i] -= dt;
2138
+ if (tr.eatsCorpses && Z.abilityTimer[i] > 0 && st === 0) Z.abilityTimer[i] -= dt;
2139
+ if (tr.waterWeak && Z.abilityTimer[i] === 0 && water(Z.x[i], Z.z[i])) {
2140
+ Z.abilityTimer[i] = -1;
2141
+ Z.hp[i] *= tr.waterWeak.keep;
2142
+ effect({ effect: "cloud", point: [Z.x[i], 0, Z.z[i]], radius: 1.5, color: tr.waterWeak.cloud, amount: 3, agent: i });
2143
+ }
2144
+ const c = cellOf(Z.x[i], Z.z[i]);
2145
+ const gy = groundAt(c, Z.y[i]);
2146
+ if (st === STATE.DORMANT || Z.floorLocked[i]) return;
2147
+ if (st === STATE.AIRBORNE || Z.y[i] > gy + 0.02 || Z.vy[i] > 0) {
2148
+ Z.vy[i] -= N.gravity * dt;
2149
+ Z.y[i] += Z.vy[i] * dt;
2150
+ if (Z.y[i] <= gy) {
2151
+ const fall = -Z.vy[i];
2152
+ Z.y[i] = gy;
2153
+ Z.vy[i] = 0;
2154
+ if (st === STATE.AIRBORNE) {
2155
+ Z.state[i] = STATE.CHASE;
2156
+ if (tr.pounce && alive && d < tr.pounce.landRange && target) {
2157
+ target.knock(tr.pounce.knock);
2158
+ target.hurt(T(i).damage, i);
2159
+ emit("message", { id: "pounce_knock", seconds: 1.5, type: T(i).id });
2160
+ }
2161
+ } else if (fall > N.fallStun && Z.state[i] <= 1) {
2162
+ Z.state[i] = STATE.STUNNED;
2163
+ Z.stateTimer[i] = 0.9;
2164
+ }
2165
+ }
2166
+ } else if (gy > Z.y[i]) Z.y[i] = Math.min(gy, Z.y[i] + N.climbSpeed * dt);
2167
+ };
2168
+ const update = (dt, context = {}) => {
2169
+ if (context.target !== void 0) target = context.target;
2170
+ groanBudget -= dt;
2171
+ nightF = context.nightFactor ?? 0;
2172
+ const P = target;
2173
+ const px = P ? P.state.x : context.flowTarget?.[0] ?? 0;
2174
+ const pz = P ? P.state.z : context.flowTarget?.[1] ?? 0;
2175
+ const alive = !!P && P.state.alive && !P.state.turned;
2176
+ viewYaw = context.viewYaw ?? (P ? P.state.yaw - Math.PI : 0);
2177
+ const fx0 = context.flowTarget?.[0] ?? px;
2178
+ const fz0 = context.flowTarget?.[1] ?? pz;
2179
+ const pc = cellOf(fx0, fz0);
2180
+ if (flow.cell < 0) {
2181
+ flowStart(fx0, fz0);
2182
+ flowStep(1e9);
2183
+ flow.cell = pc;
2184
+ } else if (pc !== flow.cell && !flow.phase) {
2185
+ flowStart(fx0, fz0);
2186
+ flow.cell = pc;
2187
+ }
2188
+ flowStep(N.flowBudgetMs);
2189
+ hHead.fill(-1);
2190
+ for (let i = 0; i < Z.n; i++) {
2191
+ if (Z.band[i] !== 0) continue;
2192
+ const hx = Math.floor(Z.x[i] + HALF);
2193
+ const hz = Math.floor(Z.z[i] + HALF);
2194
+ if (hx < 0 || hz < 0 || hx >= HN || hz >= HN) continue;
2195
+ const h = hz * HN + hx;
2196
+ hNext[i] = hHead[h];
2197
+ hHead[h] = i;
2198
+ }
2199
+ const B = stats.bands;
2200
+ B[0] = B[1] = B[2] = B[3] = 0;
2201
+ let slots = 0;
2202
+ const F = flow.dir;
2203
+ for (let i = 0; i < Z.n; i++) {
2204
+ const dx = Z.x[i] - px;
2205
+ const dz = Z.z[i] - pz;
2206
+ const d = Math.sqrt(dx * dx + dz * dz);
2207
+ let b = Z.band[i];
2208
+ while (b > 0 && d < BANDS[b - 1].max - HYST) b--;
2209
+ while (b < 3 && d > BANDS[b].max + HYST) b++;
2210
+ Z.band[i] = b;
2211
+ B[b]++;
2212
+ const C = T(i);
2213
+ const tr = C.traits ?? {};
2214
+ if (Z.state[i] === STATE.STUNNED) {
2215
+ Z.stateTimer[i] -= dt;
2216
+ if (Z.stateTimer[i] <= 0) Z.state[i] = STATE.CHASE;
2217
+ Z.vx[i] *= 0.9;
2218
+ Z.vz[i] *= 0.9;
2219
+ }
2220
+ if (Z.state[i] === STATE.CALLING) {
2221
+ Z.stateTimer[i] -= dt;
2222
+ Z.vx[i] = Z.vz[i] = 0;
2223
+ if (Z.stateTimer[i] <= 0) {
2224
+ Z.state[i] = STATE.CHASE;
2225
+ screamAt(i, Z.x[i], Z.z[i]);
2226
+ Z.cooldown[i] = tr.caller.cooldown;
2227
+ }
2228
+ }
2229
+ Z.cooldown[i] -= dt;
2230
+ if (Z.alertIn[i] > 0) {
2231
+ Z.alertIn[i] -= dt;
2232
+ if (Z.alertIn[i] <= 0 && Z.state[i] === 0 && !tr.soundSeeker) {
2233
+ Z.state[i] = STATE.CHASE;
2234
+ Z.lost[i] = 0;
2235
+ }
2236
+ }
2237
+ Z.next[i] -= dt;
2238
+ if (Z.next[i] <= 0) {
2239
+ const tdt = BANDS[b].dt;
2240
+ Z.next[i] += tdt;
2241
+ if (alive && b <= 1 && Z.state[i] <= 1) {
2242
+ let sees = false;
2243
+ const A = pack.ages[Z.age[i]];
2244
+ const vis = C.perception.sight * (1 - N.nightSight * nightF) * (P.state.crouch ? N.crouchSight : 1) * A.sight;
2245
+ if (d < vis) {
2246
+ const fx = Math.sin(Z.yaw[i]);
2247
+ const fz = Math.cos(Z.yaw[i]);
2248
+ const cone = (-dx * fx - dz * fz) / (d || 1);
2249
+ if ((cone > N.sightCone || d < N.sightNear) && (Z.floor[i] === P.state.floor || d < 5) && (Z.floor[i] || P.state.floor || losClear(Z.x[i], Z.z[i], px, pz))) sees = true;
2250
+ }
2251
+ if (C.perception.smell && d < C.perception.smell * (P.state.bleeding || P.state.scentTimer > 0 ? 2 : 1)) sees = true;
2252
+ if (sees) {
2253
+ if (Z.state[i] === 0) groan(i, d);
2254
+ Z.state[i] = STATE.CHASE;
2255
+ Z.lost[i] = 0;
2256
+ } else if (Z.state[i] === STATE.CHASE) {
2257
+ Z.lost[i] += tdt;
2258
+ if (Z.lost[i] > N.loseAfter) {
2259
+ Z.state[i] = 0;
2260
+ Z.wx[i] = px;
2261
+ Z.wz[i] = pz;
2262
+ }
2263
+ }
2264
+ }
2265
+ for (let k = 0; k < noises.length && (Z.state[i] === 0 || Z.state[i] === STATE.SEEKING_SOUND); k++) {
2266
+ const Nz = noises[k];
2267
+ const hear = Nz.r * (C.perception.hearing / N.hearingBase) * pack.ages[Z.age[i]].hearing;
2268
+ if ((Z.x[i] - Nz.x) ** 2 + (Z.z[i] - Nz.z) ** 2 < hear * hear) {
2269
+ if (tr.soundSeeker) {
2270
+ Z.state[i] = STATE.SEEKING_SOUND;
2271
+ Z.wx[i] = Nz.x;
2272
+ Z.wz[i] = Nz.z;
2273
+ break;
2274
+ }
2275
+ Z.state[i] = STATE.CHASE;
2276
+ Z.lost[i] = N.heardLost;
2277
+ groan(i, d);
2278
+ }
2279
+ }
2280
+ if (tr.caller && Z.state[i] === STATE.CHASE && d < tr.caller.range && Z.cooldown[i] <= 0) {
2281
+ Z.state[i] = STATE.CALLING;
2282
+ Z.stateTimer[i] = tr.caller.windUp;
2283
+ }
2284
+ Z.steering[i] = 0;
2285
+ if (!ability(i, d, dx, dz, tdt, b, alive)) {
2286
+ let tx = 0;
2287
+ let tz = 0;
2288
+ let sp = 0;
2289
+ if (Z.state[i] === STATE.CHASE || Z.state[i] === STATE.GRAB) {
2290
+ const c = cellOf(Z.x[i], Z.z[i]);
2291
+ const sameL = !P || Z.floor[i] === P.state.floor;
2292
+ if (d < N.directRange && alive && sameL) {
2293
+ tx = -dx / (d || 1);
2294
+ tz = -dz / (d || 1);
2295
+ } else if (c >= 0 && b === 0 && d < N.pyramidRange && sameL && flow.dist[c] < 1e8 && flow.dist[c] * GC > d * 2 + 6 && losClear(Z.x[i], Z.z[i], px, pz)) {
2296
+ tx = -dx / d;
2297
+ tz = -dz / d;
2298
+ } else if (c >= 0) {
2299
+ const W = flow.win;
2300
+ const cx = c % FW;
2301
+ const cz = c / FW | 0;
2302
+ if (cx >= W[0] && cx <= W[2] && cz >= W[1] && cz <= W[3]) {
2303
+ tx = F[c * 2];
2304
+ tz = F[c * 2 + 1];
2305
+ } else {
2306
+ tx = -dx;
2307
+ tz = -dz;
2308
+ }
2309
+ const l = Math.hypot(tx, tz) || 1;
2310
+ tx /= l;
2311
+ tz /= l;
2312
+ }
2313
+ if (tr.caller && Z.cooldown[i] > tr.caller.fleeAbove && d < tr.caller.fleeRange && alive) {
2314
+ tx = dx / (d || 1);
2315
+ tz = dz / (d || 1);
2316
+ }
2317
+ sp = chaseSpeed(i) * (P && P.state.screamBoost > 0 ? N.screamSpeed : 1);
2318
+ if (!alive) sp = Z.wanderSpeed[i];
2319
+ if (d < N.attackRange && alive && Math.abs(P.state.y - Z.y[i]) < N.attackHeight) {
2320
+ sp = 0;
2321
+ if (slots < N.attackSlots) {
2322
+ slots++;
2323
+ attack(i, d, tdt);
2324
+ }
2325
+ }
2326
+ } else if (Z.state[i] === 0) {
2327
+ const wx = Z.wx[i] - Z.x[i];
2328
+ const wz = Z.wz[i] - Z.z[i];
2329
+ const wl = Math.hypot(wx, wz);
2330
+ if (wl < 1 || R() < 0.01 * tdt * 60) {
2331
+ Z.wx[i] = clamp(Z.x[i] + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2);
2332
+ Z.wz[i] = clamp(Z.z[i] + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2);
2333
+ } else {
2334
+ tx = wx / wl;
2335
+ tz = wz / wl;
2336
+ Z.pause[i] -= tdt;
2337
+ if (Z.pause[i] < -4 - R() * 6) Z.pause[i] = 1 + R() * 2.5;
2338
+ sp = Z.pause[i] > 0 ? 0 : Z.wanderSpeed[i];
2339
+ }
2340
+ }
2341
+ if (Z.crawl[i]) sp = Math.min(sp, N.crawlSpeed);
2342
+ else if (Z.limp[i]) sp *= N.limpSpeed;
2343
+ let sx = 0;
2344
+ let sz = 0;
2345
+ if (b === 0) {
2346
+ const hx = Math.floor(Z.x[i] + HALF);
2347
+ const hz = Math.floor(Z.z[i] + HALF);
2348
+ for (let oz = -1; oz <= 1; oz++)
2349
+ for (let ox = -1; ox <= 1; ox++) {
2350
+ const qx = hx + ox;
2351
+ const qz = hz + oz;
2352
+ if (qx < 0 || qz < 0 || qx >= HN || qz >= HN) continue;
2353
+ for (let j = hHead[qz * HN + qx]; j !== -1; j = hNext[j]) {
2354
+ if (j === i || Z.state[j] === STATE.PILE_STEP || Z.state[j] === STATE.DORMANT) continue;
2355
+ const ex = Z.x[i] - Z.x[j];
2356
+ const ez = Z.z[i] - Z.z[j];
2357
+ const e2 = ex * ex + ez * ez;
2358
+ const rr = N.separation * (Z.build[i] + Z.build[j]);
2359
+ if (e2 < rr * rr * 1.6 && e2 > 1e-6) {
2360
+ const e = Math.sqrt(e2);
2361
+ const w = (rr * 1.26 - e) / e;
2362
+ sx += ex * w;
2363
+ sz += ez * w;
2364
+ }
2365
+ }
2366
+ }
2367
+ }
2368
+ if (Z.state[i] <= 2) {
2369
+ Z.dvx[i] = tx * sp + sx * N.separationPush;
2370
+ Z.dvz[i] = tz * sp + sz * N.separationPush;
2371
+ Z.steering[i] = 1;
2372
+ if (sp > 0.05) Z.yaw[i] = Math.atan2(tx, tz);
2373
+ else if (d < 3 && alive) Z.yaw[i] = Math.atan2(-dx, -dz);
2374
+ }
2375
+ }
2376
+ }
2377
+ if (Z.steering[i] && Z.state[i] <= 2) {
2378
+ const k = 1 - Math.exp(-dt * (b === 0 ? 9 : 5));
2379
+ Z.vx[i] += (Z.dvx[i] - Z.vx[i]) * k;
2380
+ Z.vz[i] += (Z.dvz[i] - Z.vz[i]) * k;
2381
+ }
2382
+ const nx = Z.x[i] + Z.vx[i] * dt;
2383
+ const nz = Z.z[i] + Z.vz[i] * dt;
2384
+ let hitCell = -1;
2385
+ if (!blockedAt(i, nx, Z.z[i])) Z.x[i] = nx;
2386
+ else {
2387
+ if (Z.state[i] !== STATE.AIRBORNE) Z.vx[i] = 0;
2388
+ hitCell = cellOf(nx, Z.z[i]);
2389
+ }
2390
+ if (!blockedAt(i, Z.x[i], nz)) Z.z[i] = nz;
2391
+ else {
2392
+ if (Z.state[i] !== STATE.AIRBORNE) Z.vz[i] = 0;
2393
+ hitCell = cellOf(Z.x[i], nz);
2394
+ }
2395
+ if (hitCell >= 0 && Z.state[i] === STATE.CHASE && b === 0) {
2396
+ const vl = tr.vaultsLow;
2397
+ if (vl && occ[hitCell] !== 5 && occH[hitCell] <= vl.maxHeight && occH[hitCell] > Z.y[i] + vl.minRise) {
2398
+ Z.state[i] = STATE.AIRBORNE;
2399
+ Z.vy[i] = Math.sqrt(2 * N.gravity * (occH[hitCell] - Z.y[i] + vl.clearance));
2400
+ const s2 = Z.chaseSpeed[i] * vl.speedFactor;
2401
+ Z.vx[i] = Math.sin(Z.yaw[i]) * s2;
2402
+ Z.vz[i] = Math.cos(Z.yaw[i]) * s2;
2403
+ } else if (occ[hitCell] === 2 || occ[hitCell] === 0 && pile[hitCell] > 0) {
2404
+ Z.stuck[i] += dt;
2405
+ if (Z.stuck[i] > 0.35 + 0.5 * Z.skin[i]) {
2406
+ Z.stuck[i] = 0;
2407
+ tryStep(i, hitCell);
2408
+ }
2409
+ }
2410
+ } else if (Z.stuck[i] > 0) Z.stuck[i] -= dt;
2411
+ if (hitCell >= 0 && Z.state[i] === STATE.CHASE && doors.at(hitCell) >= 0) {
2412
+ Z.attackTimer[i] -= dt;
2413
+ if (Z.attackTimer[i] <= 0) {
2414
+ Z.attackTimer[i] = N.attackEvery;
2415
+ doors.hit(doors.at(hitCell), N.doorDamage * (tr.doorDamage ?? 1));
2416
+ }
2417
+ }
2418
+ frame(i, dt, d, alive);
2419
+ const spd = Math.hypot(Z.vx[i], Z.vz[i]);
2420
+ Z.renderSpeed[i] += (spd - Z.renderSpeed[i]) * Math.min(1, dt * 6);
2421
+ if (Z.band[i] <= 1) {
2422
+ const s = Z.state[i];
2423
+ let goal = s === STATE.CHASE || s === STATE.GRAB || s === STATE.PILE_STEP ? 1 : 0;
2424
+ if (goal && solidAt(Z.x[i] + Math.sin(Z.renderYaw[i]) * 0.62, Z.z[i] + Math.cos(Z.renderYaw[i]) * 0.62, 1)) goal = 0.1;
2425
+ Z.arms[i] += (goal - Z.arms[i]) * Math.min(1, dt * (goal > Z.arms[i] ? 3 : 5));
2426
+ }
2427
+ Z.phase[i] += dt * (Z.renderSpeed[i] < 0.05 ? 0.15 : N.cadenceBase + N.cadencePerSpeed * Z.renderSpeed[i]);
2428
+ if (spd > 0.35 && Z.state[i] <= 1 && !Z.attackPose[i]) Z.yaw[i] = Math.atan2(Z.vx[i], Z.vz[i]);
2429
+ let dy = Z.yaw[i] - Z.renderYaw[i];
2430
+ dy -= Math.round(dy / TAU_APPROX) * TAU_APPROX;
2431
+ Z.renderYaw[i] += dy * Math.min(1, dt * (Z.state[i] === STATE.AIRBORNE ? 20 : 9));
2432
+ }
2433
+ killQFlush();
2434
+ noises.length = 0;
2435
+ stats.slots = slots;
2436
+ };
2437
+ const updateTrail = (dt, who = target) => {
2438
+ for (const p of trail) p.t += dt;
2439
+ while (trail.length && trail[0].t > N.trailSeconds) trail.shift();
2440
+ if (!who) return;
2441
+ const s = who.state;
2442
+ if ((s.bleeding || s.scentTimer > 0) && s.alive && Math.hypot(s.x - trailLast.x, s.z - trailLast.z) > N.trailStep) {
2443
+ trail.push({ x: s.x, z: s.z, t: 0 });
2444
+ trailLast = { x: s.x, z: s.z };
2445
+ if (trail.length > N.trailMax) trail.shift();
2446
+ }
2447
+ };
2448
+ const spawnAway = (minD, maxD, view) => {
2449
+ const ox = target ? target.state.x : view.x;
2450
+ const oz = target ? target.state.z : view.z;
2451
+ for (let t = 0; t < 30; t++) {
2452
+ const a = R() * TURN;
2453
+ const r = minD + Math.sqrt(R()) * (maxD - minD);
2454
+ const x = ox + Math.cos(a) * r;
2455
+ const z = oz + Math.sin(a) * r;
2456
+ const vx = x - view.x;
2457
+ const vz = z - view.z;
2458
+ const vd = Math.hypot(vx, vz);
2459
+ if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > vd * 0.5) continue;
2460
+ if (Math.abs(x) < HALF - 3 && Math.abs(z) < HALF - 3 && !grid.blocked(x, z)) return spawn(x, z);
2461
+ }
2462
+ return -1;
2463
+ };
2464
+ const updatePopulation = (dt, view, opts = {}) => {
2465
+ const popTarget = opts.target ?? N.population;
2466
+ let k = 0;
2467
+ while (Z.n < popTarget && k++ < 20) spawnAway(N.refillMin, N.refillMax, view);
2468
+ if (Z.n > popTarget + 120)
2469
+ for (let t = 0, rm = 0; t < 40 && rm < 4; t++) {
2470
+ const i = Math.floor(R() * Z.n);
2471
+ if (Z.band[i] < 2 || Z.named[i] >= 0 || target && i === target.state.grabbedBy) continue;
2472
+ const vx = Z.x[i] - view.x;
2473
+ const vz = Z.z[i] - view.z;
2474
+ const vd = Math.hypot(vx, vz);
2475
+ if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > -vd * 0.35) continue;
2476
+ remove(i);
2477
+ rm++;
2478
+ }
2479
+ const mg = opts.migration === void 0 ? null : opts.migration;
2480
+ if (!mg) return;
2481
+ migrateT -= dt;
2482
+ if (migrateT <= 0) {
2483
+ migrateT = mg.every;
2484
+ let m = 0;
2485
+ for (let q = 0; q < mg.count && Z.n < Z.capacity; q++) {
2486
+ const i = spawn(mg.x + (R() - 0.5) * mg.width, mg.z + R() * mg.depth);
2487
+ if (i >= 0) {
2488
+ Z.wx[i] = mg.goal[0];
2489
+ Z.wz[i] = mg.goal[1];
2490
+ m++;
2491
+ }
2492
+ }
2493
+ if (m) emit("message", { id: "migration", seconds: 4 });
2494
+ }
2495
+ };
2496
+ const crowd = {
2497
+ store: Z,
2498
+ pack,
2499
+ grid,
2500
+ doors,
2501
+ corpses,
2502
+ numbers: N,
2503
+ stats,
2504
+ pile,
2505
+ pileCount: () => pileN,
2506
+ trail,
2507
+ variantsOf,
2508
+ get target() {
2509
+ return target;
2510
+ },
2511
+ set target(next) {
2512
+ target = next;
2513
+ },
2514
+ random,
2515
+ sound,
2516
+ effect,
2517
+ typeIndex: (id) => typeIndexById.get(id) ?? -1,
2518
+ spawn: (x, z, type = -1) => spawn(x, z, type),
2519
+ remove,
2520
+ update,
2521
+ emitNoise,
2522
+ damage,
2523
+ kill: (i, headshot = false) => kill(i, headshot),
2524
+ updateTrail,
2525
+ updateCorpses: (dt) => void corpses.update(dt),
2526
+ updatePopulation,
2527
+ spawnAway,
2528
+ pileBlast,
2529
+ setHunt(next) {
2530
+ hunt = next;
2531
+ },
2532
+ chaseSpeed,
2533
+ asPlayerCrowd: () => ({
2534
+ count: () => Z.n,
2535
+ active: (i) => Z.band[i] === 0,
2536
+ x: (i) => Z.x[i],
2537
+ z: (i) => Z.z[i],
2538
+ y: (i) => Z.y[i],
2539
+ floor: (i) => Z.floor[i],
2540
+ chasing: (i) => Z.state[i] === STATE.CHASE,
2541
+ down: (i) => Z.crawl[i] !== 0 || Z.state[i] === STATE.STUNNED,
2542
+ panicWeight: (i) => traits(i).panicWeight ?? 1,
2543
+ hold(i, x, z, k, lookX, lookZ) {
2544
+ Z.x[i] += (x - Z.x[i]) * k;
2545
+ Z.z[i] += (z - Z.z[i]) * k;
2546
+ Z.vx[i] = Z.vz[i] = Z.dvx[i] = Z.dvz[i] = 0;
2547
+ Z.yaw[i] = Math.atan2(lookX - Z.x[i], lookZ - Z.z[i]);
2548
+ },
2549
+ stun(i, seconds, vx, vz) {
2550
+ Z.state[i] = STATE.STUNNED;
2551
+ Z.stateTimer[i] = seconds;
2552
+ Z.vx[i] = vx;
2553
+ Z.vz[i] = vz;
2554
+ },
2555
+ chase(i, cooldown) {
2556
+ Z.state[i] = STATE.CHASE;
2557
+ Z.attackTimer[i] = cooldown;
2558
+ },
2559
+ damage: (i, hit) => void damage(i, { region: hit.region, amount: hit.amount, distance: hit.distance, cut: hit.cut, caliber: null })
2560
+ }),
2561
+ on(event, listener) {
2562
+ let set = listeners.get(event);
2563
+ if (!set) listeners.set(event, set = /* @__PURE__ */ new Set());
2564
+ set.add(listener);
2565
+ return () => void set.delete(listener);
2566
+ }
2567
+ };
2568
+ return crowd;
2569
+ }
2570
+
2571
+ // src/behaviours/civilians.ts
2572
+ var CIVILIAN_DEFAULTS = {
2573
+ hpAdult: 100,
2574
+ hpChild: 40,
2575
+ childOffset: 0.6,
2576
+ sightSquared: 100,
2577
+ leaveDistance: 18,
2578
+ attackRange: 1,
2579
+ attackEvery: 1.5,
2580
+ biteFactor: 1.5,
2581
+ biteSeconds: 120,
2582
+ fearAfterHit: 10,
2583
+ nearMessage: 30,
2584
+ fearRange: 6,
2585
+ fearSpeed: 3.4,
2586
+ walkSpeed: 1.3,
2587
+ childSpeed: 0.85,
2588
+ bittenSpeed: 0.8,
2589
+ groupRunBeyond: 6,
2590
+ groupRunSpeed: 3.6,
2591
+ fleeSpeed: 2.6,
2592
+ childFleeFactor: 1.1,
2593
+ stuckSeconds: 8,
2594
+ arrive: 0.25,
2595
+ probe: 0.45,
2596
+ whiskers: [0, 0.7, -0.7, 1.4, -1.4],
2597
+ doorProbe: 0.6,
2598
+ steer: 8,
2599
+ bodyHeight: 0.4,
2600
+ patrolChance: 0.2,
2601
+ childHeight: 0.65,
2602
+ childBuild: 0.7,
2603
+ cadenceBase: 0.5,
2604
+ cadencePerSpeed: 0.3,
2605
+ inside: 0.8,
2606
+ doorInside: 0.8,
2607
+ doorOutside: 0.9,
2608
+ gate: 7.6,
2609
+ sidewalk: 6,
2610
+ houseOffset: 0.8
2611
+ };
2612
+ function createCivilians(crowd, options) {
2613
+ const N = { ...CIVILIAN_DEFAULTS, ...options.numbers };
2614
+ const Z = crowd.store;
2615
+ const grid = crowd.grid;
2616
+ const doors = crowd.doors;
2617
+ const hourNow = options.hour ?? (() => 12);
2618
+ const turnInto = options.turnInto ?? 0;
2619
+ const listeners = /* @__PURE__ */ new Map();
2620
+ const emit = (event, payload) => {
2621
+ for (const listener of listeners.get(event) ?? []) listener(payload);
2622
+ };
2623
+ const random = crowd.random;
2624
+ const R = random;
2625
+ const lerp = (a, b, t) => a + (b - a) * t;
2626
+ const casaDe = (name) => options.buildings[name];
2627
+ const pontos = options.waypoints ?? ((b) => {
2628
+ const casa = !!b.house;
2629
+ const side = b.front;
2630
+ const front = side < 0 ? b.z + b.d : b.z;
2631
+ const back = side < 0 ? b.z : b.z + b.d;
2632
+ const cx = b.x + b.w / 2;
2633
+ const inw = side < 0 ? -1 : 1;
2634
+ const calc = side < 0 ? -N.sidewalk : N.sidewalk;
2635
+ const pts = [
2636
+ [cx + (casa ? N.houseOffset : 0), (front + (front + back) / 2) / 2],
2637
+ [cx, front + inw * N.doorInside],
2638
+ [cx, front - inw * N.doorOutside]
2639
+ ];
2640
+ if (casa) pts.push([cx, side < 0 ? -N.gate : N.gate]);
2641
+ pts.push([cx, calc]);
2642
+ return pts;
2643
+ });
2644
+ const rota = (bA, bB) => {
2645
+ const a = pontos(bA);
2646
+ const b = pontos(bB).reverse();
2647
+ const out = a.slice();
2648
+ if (Math.sign(a[a.length - 1][1]) !== Math.sign(b[0][1])) out.push([a[a.length - 1][0], b[0][1]]);
2649
+ return out.concat(b);
2650
+ };
2651
+ const list = options.people.map((spec, k) => {
2652
+ const b = casaDe(spec.home);
2653
+ const p = pontos(b)[0];
2654
+ return {
2655
+ id: spec.id,
2656
+ spec,
2657
+ k,
2658
+ x: p[0] + (spec.child ? N.childOffset : 0),
2659
+ z: p[1],
2660
+ yaw: 0,
2661
+ phase: 0,
2662
+ vx: 0,
2663
+ vz: 0,
2664
+ hp: spec.child ? N.hpChild : N.hpAdult,
2665
+ state: "home",
2666
+ path: [],
2667
+ pathIndex: 0,
2668
+ biteTimer: 0,
2669
+ attackTimer: 0,
2670
+ fear: 0,
2671
+ goal: spec.home,
2672
+ stuckTimer: 0,
2673
+ agent: -1
2674
+ };
2675
+ });
2676
+ const byId = new Map(list.map((c) => [c.id, c]));
2677
+ const exposed = (n) => n.state !== "dead" && n.state !== "undead" && n.state !== "gone";
2678
+ const target = () => crowd.target?.state ?? null;
2679
+ const distToTarget = (x, z) => {
2680
+ const t = target();
2681
+ return t ? Math.hypot(x - t.x, z - t.z) : 0;
2682
+ };
2683
+ const turn = (n) => {
2684
+ n.state = "undead";
2685
+ const i = crowd.spawn(n.x, n.z, turnInto);
2686
+ if (i >= 0) {
2687
+ Z.color[i] = n.spec.color ?? Z.color[i];
2688
+ Z.named[i] = n.k;
2689
+ Z.height[i] = n.spec.child ? N.childHeight : Z.height[i];
2690
+ Z.build[i] = n.spec.child ? N.childBuild : Z.build[i];
2691
+ Z.age[i] = 0;
2692
+ Z.state[i] = STATE.CHASE;
2693
+ n.agent = i;
2694
+ }
2695
+ emit("turned", { id: n.id, agent: i });
2696
+ };
2697
+ const die = (n) => {
2698
+ if (n.biteTimer > 0) return turn(n);
2699
+ n.state = "dead";
2700
+ n.vx = n.vz = 0;
2701
+ crowd.corpses.add({
2702
+ x: n.x,
2703
+ z: n.z,
2704
+ yaw: n.yaw,
2705
+ height: n.spec.child ? N.childHeight : 1,
2706
+ build: n.spec.child ? N.childBuild : 1,
2707
+ missing: 0,
2708
+ color: n.spec.color ?? 0,
2709
+ variant: -1,
2710
+ skin: 0.4,
2711
+ seed: R() * 10,
2712
+ named: n.k
2713
+ });
2714
+ crowd.effect({ effect: "blood", point: [n.x, 1, n.z], age: 0, amount: 300, died: true });
2715
+ emit("died", { id: n.id, cause: "attacked" });
2716
+ };
2717
+ const agentsAgainst = (dt) => {
2718
+ const t = target();
2719
+ for (let i = 0; i < Z.n; i++) {
2720
+ if (Z.band[i] > 1) continue;
2721
+ const k = Z.hunts[i];
2722
+ if (k >= 0) {
2723
+ const n = list[k];
2724
+ if (!exposed(n)) {
2725
+ Z.hunts[i] = -1;
2726
+ continue;
2727
+ }
2728
+ const dx = n.x - Z.x[i];
2729
+ const dz = n.z - Z.z[i];
2730
+ const d = Math.hypot(dx, dz);
2731
+ if (d > N.leaveDistance) {
2732
+ Z.hunts[i] = -1;
2733
+ continue;
2734
+ }
2735
+ if (d < N.attackRange) {
2736
+ Z.attackTimer[i] -= dt;
2737
+ if (Z.attackTimer[i] <= 0) {
2738
+ Z.attackTimer[i] = N.attackEvery;
2739
+ const C = crowd.pack.types[Z.type[i]];
2740
+ n.hp -= C.damage;
2741
+ n.fear = N.fearAfterHit;
2742
+ crowd.sound("flesh", 0.7, n.x, 1, n.z, distToTarget(n.x, n.z));
2743
+ crowd.effect({ effect: "blood", point: [n.x, 1.2, n.z], age: 0, amount: 30, died: false });
2744
+ if (n.biteTimer <= 0 && R() < C.bite * N.biteFactor) {
2745
+ n.biteTimer = N.biteSeconds;
2746
+ emit("bitten", { id: n.id, near: distToTarget(n.x, n.z) < N.nearMessage });
2747
+ }
2748
+ if (n.hp <= 0) die(n);
2749
+ }
2750
+ }
2751
+ } else if (Z.state[i] <= 1) {
2752
+ for (const n of list) {
2753
+ if (!exposed(n) || n.state === "home") continue;
2754
+ const dx = n.x - Z.x[i];
2755
+ const dz = n.z - Z.z[i];
2756
+ const d2 = dx * dx + dz * dz;
2757
+ const dp = t ? (t.x - Z.x[i]) ** 2 + (t.z - Z.z[i]) ** 2 : Infinity;
2758
+ if (d2 < N.sightSquared && d2 < dp && grid.losClear(Z.x[i], Z.z[i], n.x, n.z)) {
2759
+ Z.hunts[i] = n.k;
2760
+ Z.state[i] = STATE.CHASE;
2761
+ break;
2762
+ }
2763
+ }
2764
+ }
2765
+ }
2766
+ };
2767
+ crowd.setHunt((i) => {
2768
+ const n = list[Z.hunts[i]];
2769
+ if (!n) return false;
2770
+ const dx = n.x - Z.x[i];
2771
+ const dz = n.z - Z.z[i];
2772
+ const d = Math.hypot(dx, dz) || 1;
2773
+ const sp = d < 1 ? 0 : crowd.chaseSpeed(i);
2774
+ Z.vx[i] = dx / d * sp;
2775
+ Z.vz[i] = dz / d * sp;
2776
+ Z.yaw[i] = Math.atan2(dx, dz);
2777
+ return true;
2778
+ });
2779
+ const move = (n, dt, tx, tz, sp) => {
2780
+ const dx = tx - n.x;
2781
+ const dz = tz - n.z;
2782
+ const d = Math.hypot(dx, dz);
2783
+ if (d < N.arrive) {
2784
+ n.vx = n.vz = 0;
2785
+ return true;
2786
+ }
2787
+ let ax = dx / d;
2788
+ let az = dz / d;
2789
+ for (const a of N.whiskers) {
2790
+ const c2 = Math.cos(a);
2791
+ const s = Math.sin(a);
2792
+ const bx = ax * c2 - az * s;
2793
+ const bz = ax * s + az * c2;
2794
+ if (!grid.solidAt(n.x + bx * N.probe, n.z + bz * N.probe, N.bodyHeight)) {
2795
+ ax = bx;
2796
+ az = bz;
2797
+ break;
2798
+ }
2799
+ }
2800
+ const c = grid.cellOf(n.x + ax * N.doorProbe, n.z + az * N.doorProbe);
2801
+ const di = doors.at(c);
2802
+ if (c >= 0 && di >= 0) {
2803
+ const D = doors.list[di];
2804
+ if (!D.open && !D.broken && !D.planks) {
2805
+ doors.set(di, true);
2806
+ crowd.sound("wood", 0.3, D.x, 1, D.z, distToTarget(D.x, D.z));
2807
+ }
2808
+ }
2809
+ n.vx = lerp(n.vx, ax * sp, Math.min(1, dt * N.steer));
2810
+ n.vz = lerp(n.vz, az * sp, Math.min(1, dt * N.steer));
2811
+ const nx = n.x + n.vx * dt;
2812
+ const nz = n.z + n.vz * dt;
2813
+ if (!grid.solidAt(nx, n.z, N.bodyHeight)) n.x = nx;
2814
+ if (!grid.solidAt(n.x, nz, N.bodyHeight)) n.z = nz;
2815
+ n.yaw = Math.atan2(n.vx, n.vz);
2816
+ return false;
2817
+ };
2818
+ const update = (dt) => {
2819
+ agentsAgainst(dt);
2820
+ const h = hourNow();
2821
+ const t = target();
2822
+ for (const n of list) {
2823
+ if (n.state === "dead" || n.state === "undead" || n.state === "gone") continue;
2824
+ if (n.biteTimer > 0) {
2825
+ n.biteTimer -= dt;
2826
+ if (n.biteTimer <= 0) {
2827
+ turn(n);
2828
+ continue;
2829
+ }
2830
+ }
2831
+ let perto = 99;
2832
+ for (let i = 0; i < Z.n; i++) {
2833
+ if (Z.band[i]) continue;
2834
+ const d = Math.hypot(Z.x[i] - n.x, Z.z[i] - n.z);
2835
+ if (d < perto) perto = d;
2836
+ }
2837
+ const sp = (perto < N.fearRange ? N.fearSpeed : N.walkSpeed) * (n.spec.child ? N.childSpeed : 1) * (n.biteTimer > 0 ? N.bittenSpeed : 1);
2838
+ if (n.state === "group") {
2839
+ const leaderCivilian = n.spec.leader ? byId.get(n.spec.leader) : void 0;
2840
+ const lider = leaderCivilian && leaderCivilian.state === "group" ? leaderCivilian : t;
2841
+ if (lider) {
2842
+ const dd = Math.hypot(lider.x - n.x, lider.z - n.z);
2843
+ if (dd > (n.spec.followDistance ?? 2.2)) move(n, dt, lider.x, lider.z, dd > N.groupRunBeyond ? N.groupRunSpeed : sp);
2844
+ else {
2845
+ n.vx *= 0.8;
2846
+ n.vz *= 0.8;
2847
+ }
2848
+ }
2849
+ } else if (n.state === "fleeing") {
2850
+ const p = n.path[n.pathIndex];
2851
+ if (!p) {
2852
+ n.state = "gone";
2853
+ emit("gone", { id: n.id });
2854
+ continue;
2855
+ }
2856
+ n.stuckTimer += dt;
2857
+ if (move(n, dt, p[0], p[1], N.fleeSpeed * (n.spec.child ? N.childFleeFactor : 1)) || n.stuckTimer > N.stuckSeconds) {
2858
+ n.pathIndex++;
2859
+ n.stuckTimer = 0;
2860
+ }
2861
+ } else if (n.spec.routine) {
2862
+ let alvo = n.spec.home;
2863
+ for (const [hh, onde] of n.spec.routine) if (h >= hh && h < hh + 1) alvo = onde;
2864
+ if (perto < N.fearRange) alvo = n.spec.home;
2865
+ if (alvo !== n.goal) {
2866
+ const home = casaDe(n.spec.home);
2867
+ n.path = rota(casaDe(n.goal) ?? home, casaDe(alvo) ?? home);
2868
+ n.pathIndex = 0;
2869
+ n.goal = alvo;
2870
+ n.state = "walking";
2871
+ }
2872
+ if (n.state === "walking") {
2873
+ const p = n.path[n.pathIndex];
2874
+ n.stuckTimer += dt;
2875
+ if (!p) n.state = "home";
2876
+ else if (move(n, dt, p[0], p[1], sp) || n.stuckTimer > N.stuckSeconds) {
2877
+ n.pathIndex++;
2878
+ n.stuckTimer = 0;
2879
+ }
2880
+ } else {
2881
+ n.vx *= 0.8;
2882
+ n.vz *= 0.8;
2883
+ }
2884
+ } else {
2885
+ n.vx *= 0.8;
2886
+ n.vz *= 0.8;
2887
+ }
2888
+ {
2889
+ const v = Math.hypot(n.vx, n.vz);
2890
+ n.phase += dt * (v < 0.05 ? 0 : N.cadenceBase + N.cadencePerSpeed * v);
2891
+ }
2892
+ }
2893
+ for (let i = 0; i < Z.n; i++)
2894
+ if (Z.named[i] >= 0 && Z.state[i] === STATE.WANDER && R() < dt * N.patrolChance) {
2895
+ const n = list[Z.named[i]];
2896
+ if (!n) continue;
2897
+ const p = pontos(casaDe(n.spec.home));
2898
+ const q = p[Math.floor(R() * p.length)];
2899
+ Z.wx[i] = q[0];
2900
+ Z.wz[i] = q[1];
2901
+ }
2902
+ };
2903
+ return {
2904
+ list,
2905
+ get: (id) => byId.get(id),
2906
+ update,
2907
+ join(id) {
2908
+ const n = byId.get(id);
2909
+ if (n && exposed(n)) n.state = "group";
2910
+ },
2911
+ flee(id, path) {
2912
+ const n = byId.get(id);
2913
+ if (!n || !exposed(n)) return;
2914
+ n.state = "fleeing";
2915
+ n.path = path.map(([x, z]) => [x, z]);
2916
+ n.pathIndex = 0;
2917
+ },
2918
+ route(from, to) {
2919
+ return rota(casaDe(from), casaDe(to));
2920
+ },
2921
+ on(event, listener) {
2922
+ let set = listeners.get(event);
2923
+ if (!set) listeners.set(event, set = /* @__PURE__ */ new Set());
2924
+ set.add(listener);
2925
+ return () => void set.delete(listener);
2926
+ }
2927
+ };
2928
+ }
2929
+
2930
+ // src/behaviours/herd.ts
2931
+ var HERD_DEFAULTS = {
2932
+ range: 8,
2933
+ fear: 5,
2934
+ walkFactor: 1.05,
2935
+ walkBase: 0.25,
2936
+ runFactor: 2.2,
2937
+ runBase: 0.8,
2938
+ activeDistance: 70,
2939
+ companionBehind: 2.2,
2940
+ companionRun: 6,
2941
+ companionStop: 0.8,
2942
+ retargetTries: 6,
2943
+ restBase: 4,
2944
+ restRandom: 7,
2945
+ lookEvery: 7,
2946
+ lookShare: 0.25,
2947
+ lookRadius: 40,
2948
+ fearAgentFactor: 1.5,
2949
+ fearAgentBase: 2,
2950
+ fleeStep: 6,
2951
+ arrive: 0.3,
2952
+ bodyHeight: 0.3,
2953
+ turn: 6,
2954
+ freeHeight: 0.6,
2955
+ worldMargin: 4
2956
+ };
2957
+ var HERD_ROLES = {
2958
+ dog: { range: 6, fear: 0, guard: { bark: 12, barkNoise: 60, barkCooldown: 3, growl: 40, growlCooldown: 10, sound: "bark" } },
2959
+ cat: { range: 6, fear: 5 },
2960
+ chicken: { range: 5, fear: 4, fleeCall: { sound: "cluck", noise: 30, cooldown: 6 } },
2961
+ southern_lapwing: { range: 5, fear: 0, alarm: { range: 12, sound: "lapwing", noise: 40, cooldown: 8, fly: 4 } },
2962
+ cow: { range: 10, fear: 6, call: { chance: 0.02, sound: "moo", noise: 80, cooldown: 30 } },
2963
+ horse: { range: 10, fear: 7 },
2964
+ pig: { range: 5, fear: 3 },
2965
+ capybara: { range: 8, fear: 15 },
2966
+ vulture: { sky: true }
2967
+ };
2968
+ function createHerd(crowd, options = {}) {
2969
+ const N = { ...HERD_DEFAULTS, ...options.numbers };
2970
+ const R = crowd.random;
2971
+ const Z = crowd.store;
2972
+ const grid = crowd.grid;
2973
+ const list = [];
2974
+ let time = 0;
2975
+ const listeners = /* @__PURE__ */ new Map();
2976
+ const emit = (event, payload) => {
2977
+ for (const listener of listeners.get(event) ?? []) listener(payload);
2978
+ };
2979
+ const view = options.view ?? (() => crowd.target?.state ?? { x: 0, z: 0 });
2980
+ const free = (x0, z0, r0, r1, tries = 40) => {
2981
+ for (let k = 0; k < tries; k++) {
2982
+ const a = R() * 6.28;
2983
+ const r = r0 + R() * (r1 - r0);
2984
+ const x = x0 + Math.cos(a) * r;
2985
+ const z = z0 + Math.sin(a) * r;
2986
+ if (Math.abs(x) < grid.half - N.worldMargin && Math.abs(z) < grid.half - N.worldMargin && !grid.solidAt(x, z, N.freeHeight) && !options.blocked?.(x, z)) return [x, z];
2987
+ }
2988
+ return null;
2989
+ };
2990
+ const nearestAgent = (x, z, r) => {
2991
+ let best = -1;
2992
+ let bd = r * r;
2993
+ for (let i = 0; i < Z.n; i++) {
2994
+ if (Z.state[i] === STATE.DORMANT) continue;
2995
+ const dx = Z.x[i] - x;
2996
+ const dz = Z.z[i] - z;
2997
+ const d = dx * dx + dz * dz;
2998
+ if (d < bd) {
2999
+ bd = d;
3000
+ best = i;
3001
+ }
3002
+ }
3003
+ return best < 0 ? null : [best, Math.sqrt(bd)];
3004
+ };
3005
+ const call = (E, sound, noise, dc) => {
3006
+ crowd.emitNoise(E.x, E.z, noise);
3007
+ emit("noise", { x: E.x, z: E.z, radius: noise, animal: E.index });
3008
+ emit("sound", { sound, volume: 1, position: [E.x, 0.5, E.z], distance: dc, animal: E.index });
3009
+ };
3010
+ const update = (dt) => {
3011
+ time += dt;
3012
+ const t = crowd.target?.state;
3013
+ const px = t?.x ?? 0;
3014
+ const pz = t?.z ?? 0;
3015
+ const v = view();
3016
+ for (const E of list) {
3017
+ if (E.spec.sky) continue;
3018
+ const dc = Math.hypot(E.x - v.x, E.z - v.z);
3019
+ if (dc >= N.activeDistance || E.dead) continue;
3020
+ E.timer -= dt;
3021
+ E.cooldown -= dt;
3022
+ let alvoX = E.targetX;
3023
+ let alvoZ = E.targetZ;
3024
+ let corre = false;
3025
+ const dp = t ? Math.hypot(E.x - px, E.z - pz) : Infinity;
3026
+ if (E.companion && t) {
3027
+ const bx = px - Math.sin(t.yaw) * N.companionBehind;
3028
+ const bz = pz - Math.cos(t.yaw) * N.companionBehind;
3029
+ alvoX = bx;
3030
+ alvoZ = bz;
3031
+ corre = dp > N.companionRun;
3032
+ if (Math.hypot(bx - E.x, bz - E.z) < N.companionStop) {
3033
+ alvoX = E.x;
3034
+ alvoZ = E.z;
3035
+ }
3036
+ } else if (E.timer <= 0) {
3037
+ const q = free(E.homeX, E.homeZ, 0, E.range, N.retargetTries);
3038
+ if (q) {
3039
+ E.targetX = q[0];
3040
+ E.targetZ = q[1];
3041
+ }
3042
+ E.timer = N.restBase + R() * N.restRandom;
3043
+ alvoX = E.targetX;
3044
+ alvoZ = E.targetZ;
3045
+ }
3046
+ const zp = (time * N.lookEvery + E.x) % 1 < N.lookShare || E.near === void 0 ? E.near = nearestAgent(E.x, E.z, N.lookRadius) : E.near;
3047
+ let fx = 0;
3048
+ let fz = 0;
3049
+ if (E.fear && !E.companion && dp < E.fear) {
3050
+ fx += E.x - px;
3051
+ fz += E.z - pz;
3052
+ }
3053
+ if (E.fear && zp && zp[1] < E.fear * N.fearAgentFactor + N.fearAgentBase) {
3054
+ fx += E.x - Z.x[zp[0]];
3055
+ fz += E.z - Z.z[zp[0]];
3056
+ }
3057
+ if (fx || fz) {
3058
+ const l = Math.hypot(fx, fz) || 1;
3059
+ alvoX = E.x + fx / l * N.fleeStep;
3060
+ alvoZ = E.z + fz / l * N.fleeStep;
3061
+ corre = true;
3062
+ const fc = E.spec.fleeCall;
3063
+ if (fc && E.cooldown <= 0) {
3064
+ E.cooldown = fc.cooldown;
3065
+ call(E, fc.sound, fc.noise, dc);
3066
+ }
3067
+ }
3068
+ const al = E.spec.alarm;
3069
+ if (al && dp < al.range && E.cooldown <= 0) {
3070
+ E.cooldown = al.cooldown;
3071
+ E.fly = al.fly;
3072
+ call(E, al.sound, al.noise, dc);
3073
+ emit("message", { id: "alarm", seconds: 2.5, animal: E.index });
3074
+ }
3075
+ const cl = E.spec.call;
3076
+ if (cl && E.cooldown <= 0 && R() < dt * cl.chance) {
3077
+ E.cooldown = cl.cooldown;
3078
+ call(E, cl.sound, cl.noise, dc);
3079
+ }
3080
+ const g = E.spec.guard;
3081
+ if (g && zp && E.cooldown <= 0) {
3082
+ if (zp[1] < g.bark) {
3083
+ E.cooldown = g.barkCooldown;
3084
+ call(E, g.sound, g.barkNoise, dc);
3085
+ if (E.companion) emit("message", { id: "bark", seconds: 1.5, animal: E.index, distance: Math.round(zp[1]) });
3086
+ } else if (E.companion && zp[1] < g.growl) {
3087
+ E.cooldown = g.growlCooldown;
3088
+ emit("message", { id: "growl", seconds: 2.5, animal: E.index, distance: Math.round(zp[1]) });
3089
+ }
3090
+ }
3091
+ const ax = alvoX - E.x;
3092
+ const az = alvoZ - E.z;
3093
+ const ad = Math.hypot(ax, az);
3094
+ let sp = 0;
3095
+ if (E.fly > 0) {
3096
+ E.fly -= dt;
3097
+ E.clip = "run";
3098
+ } else if (ad > N.arrive) {
3099
+ sp = corre ? E.runSpeed : E.walkSpeed;
3100
+ E.clip = corre ? "run" : "walk";
3101
+ } else E.clip = "idle";
3102
+ if (sp) {
3103
+ const k = Math.min(1, sp * dt / ad);
3104
+ const nx = E.x + ax * k;
3105
+ const nz = E.z + az * k;
3106
+ if (!grid.solidAt(nx, nz, N.bodyHeight)) {
3107
+ E.x = nx;
3108
+ E.z = nz;
3109
+ } else E.timer = 0;
3110
+ let dy = Math.atan2(ax, az) - E.yaw;
3111
+ dy -= Math.round(dy / 6.2832) * 6.2832;
3112
+ E.yaw += dy * Math.min(1, dt * N.turn);
3113
+ }
3114
+ }
3115
+ };
3116
+ return {
3117
+ list,
3118
+ add(spec) {
3119
+ const E = {
3120
+ index: list.length,
3121
+ spec,
3122
+ x: spec.x,
3123
+ z: spec.z,
3124
+ yaw: R() * 6.28,
3125
+ homeX: spec.x,
3126
+ homeZ: spec.z,
3127
+ range: spec.range ?? N.range,
3128
+ clip: "idle",
3129
+ timer: R() * 4,
3130
+ targetX: spec.x,
3131
+ targetZ: spec.z,
3132
+ fear: spec.fear ?? N.fear,
3133
+ fly: 0,
3134
+ cooldown: 0,
3135
+ walkSpeed: spec.walkHz * spec.size * N.walkFactor + N.walkBase,
3136
+ runSpeed: spec.runHz * spec.size * N.runFactor + N.runBase,
3137
+ companion: !!spec.companion,
3138
+ dead: !!spec.dead,
3139
+ near: void 0
3140
+ };
3141
+ list.push(E);
3142
+ return E;
3143
+ },
3144
+ free,
3145
+ update,
3146
+ get time() {
3147
+ return time;
3148
+ },
3149
+ setCompanion(index, on) {
3150
+ const E = list[index];
3151
+ if (!E) return;
3152
+ E.companion = on;
3153
+ E.homeX = E.x;
3154
+ E.homeZ = E.z;
3155
+ },
3156
+ on(event, listener) {
3157
+ let set = listeners.get(event);
3158
+ if (!set) listeners.set(event, set = /* @__PURE__ */ new Set());
3159
+ set.add(listener);
3160
+ return () => void set.delete(listener);
3161
+ }
3162
+ };
3163
+ }
3164
+
3165
+ // src/behaviours/flock.ts
3166
+ var carrionCenter = (corpses, fallback) => {
3167
+ let ux = 0;
3168
+ let uz = 0;
3169
+ let un = 0;
3170
+ for (const c of corpses) {
3171
+ ux += c.x;
3172
+ uz += c.z;
3173
+ un++;
3174
+ }
3175
+ if (un >= 3) return [ux / un, uz / un];
3176
+ return fallback ? [fallback[0], fallback[1]] : [ux, uz];
3177
+ };
3178
+ function createFlock(options) {
3179
+ const birds = Array.from({ length: options.count }, () => ({ x: 0, y: 0, z: 0, yaw: 0, roll: 0, wing: 0, visible: true }));
3180
+ const within = options.visibleWithin ?? (options.mode === "carrion" ? 260 : Infinity);
3181
+ return {
3182
+ birds,
3183
+ update(t, dt, [cx, cz], view) {
3184
+ birds.forEach((U, i) => {
3185
+ if (options.mode === "carrion") {
3186
+ const a = t * (0.18 + i * 0.04) + i * 2.1;
3187
+ const r = 7 + i * 2;
3188
+ U.x = cx + Math.cos(a) * r;
3189
+ U.y = 16 + i * 2.5;
3190
+ U.z = cz + Math.sin(a) * r;
3191
+ U.yaw = -a;
3192
+ U.roll = 0.35;
3193
+ U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within;
3194
+ if (U.visible) U.wing += dt * 1.2 * 2 * Math.PI;
3195
+ } else {
3196
+ const a = t * 0.3 + i * 0.4;
3197
+ const r = 20 + i % 7 * 4;
3198
+ U.x = cx + Math.cos(a) * r;
3199
+ U.y = 25 + Math.sin(t + i) * 2;
3200
+ U.z = cz + Math.sin(a) * r;
3201
+ U.yaw = -a;
3202
+ U.roll = 0;
3203
+ U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within;
3204
+ }
3205
+ });
3206
+ }
3207
+ };
3208
+ }
3209
+
3210
+ // src/behaviours/infection.ts
3211
+ function bindInfection(crowd, model, options) {
3212
+ const tracked = /* @__PURE__ */ new Map();
3213
+ const band = options.giveBackBand ?? 3;
3214
+ const offKill = crowd.on("kill", ({ agent }) => {
3215
+ const entry = tracked.get(agent);
3216
+ if (!entry) return;
3217
+ model.giveBack(entry.region, [{ state: "removed", seed: entry.seed }]);
3218
+ tracked.delete(agent);
3219
+ });
3220
+ const offRemove = crowd.on("remove", ({ agent, moved }) => {
3221
+ tracked.delete(agent);
3222
+ if (moved < 0) return;
3223
+ const entry = tracked.get(moved);
3224
+ if (!entry) return;
3225
+ tracked.delete(moved);
3226
+ tracked.set(agent, entry);
3227
+ });
3228
+ const offBite = options.civilians?.on("bitten", ({ id }) => {
3229
+ const n = options.civilians?.get(id);
3230
+ const region = n && options.regionOf?.(n.x, n.z);
3231
+ if (region) model.infect(region, 1, 0);
3232
+ });
3233
+ return {
3234
+ materialize(region, n) {
3235
+ const batch = model.materialize(region, n);
3236
+ const born = [];
3237
+ const back = [];
3238
+ batch.forEach((agent, k) => {
3239
+ const at = options.place(region, agent.seed, k);
3240
+ const i = at ? crowd.spawn(at[0], at[1], options.typeFor?.(agent, region) ?? -1) : -1;
3241
+ if (i < 0) {
3242
+ back.push(agent);
3243
+ return;
3244
+ }
3245
+ tracked.set(i, { region, state: agent.state, seed: agent.seed });
3246
+ born.push(i);
3247
+ });
3248
+ if (back.length) model.giveBack(region, back);
3249
+ return born;
3250
+ },
3251
+ giveBack(region, opts = {}) {
3252
+ const byRegion = /* @__PURE__ */ new Map();
3253
+ const indices = [...tracked.keys()].sort((a, b) => b - a);
3254
+ for (const i of indices) {
3255
+ const entry = tracked.get(i);
3256
+ if (!entry || region !== void 0 && entry.region !== region) continue;
3257
+ if (!opts.all && crowd.store.band[i] < band) continue;
3258
+ let list = byRegion.get(entry.region);
3259
+ if (!list) byRegion.set(entry.region, list = []);
3260
+ list.push({ state: entry.state, seed: entry.seed });
3261
+ tracked.delete(i);
3262
+ crowd.remove(i);
3263
+ }
3264
+ let count = 0;
3265
+ for (const [r, list] of byRegion) {
3266
+ model.giveBack(r, list);
3267
+ count += list.length;
3268
+ }
3269
+ return count;
3270
+ },
3271
+ regionOf: (agent) => tracked.get(agent)?.region ?? null,
3272
+ unbind() {
3273
+ offKill();
3274
+ offRemove();
3275
+ offBite?.();
3276
+ tracked.clear();
3277
+ }
3278
+ };
3279
+ }
3280
+ return __toCommonJS(index_exports);
3281
+ })();
3282
+ //# sourceMappingURL=index.global.js.map