@adatechnology/game-crowd 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3655 @@
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
+ var pose_clips_default = {
997
+ clips: [
998
+ {
999
+ id: "grabbed_bitten_human",
1000
+ source: {
1001
+ file: "site/pages/zumbis-personagens/bestiario.html",
1002
+ scene: "ataque (Agarr\xE3o e mordida)",
1003
+ variable: "HK"
1004
+ },
1005
+ duration: 8,
1006
+ loop: false,
1007
+ channels: "full",
1008
+ keys: [
1009
+ {
1010
+ t: 0,
1011
+ pose: {
1012
+ lean: 0,
1013
+ headY: 0,
1014
+ armL: -0.1,
1015
+ armR: 0.05,
1016
+ elbL: -0.2,
1017
+ elbR: -0.2,
1018
+ drop: 0,
1019
+ kneeL: 0.05,
1020
+ kneeR: 0.05,
1021
+ legL: -0.05,
1022
+ legR: 0.05,
1023
+ headX: 0
1024
+ }
1025
+ },
1026
+ {
1027
+ t: 1.5,
1028
+ pose: {
1029
+ lean: -0.1,
1030
+ headY: -0.6,
1031
+ armL: -0.1,
1032
+ armR: 0.05,
1033
+ elbL: -0.2,
1034
+ elbR: -0.2,
1035
+ drop: 0,
1036
+ kneeL: 0.05,
1037
+ kneeR: 0.05,
1038
+ legL: -0.05,
1039
+ legR: 0.05,
1040
+ headX: -0.1
1041
+ }
1042
+ },
1043
+ {
1044
+ t: 2.1,
1045
+ pose: {
1046
+ lean: -0.3,
1047
+ headY: -0.2,
1048
+ armL: -1.3,
1049
+ armR: -1.2,
1050
+ elbL: -1.4,
1051
+ elbR: -1.5,
1052
+ drop: -0.04,
1053
+ kneeL: 0.25,
1054
+ kneeR: 0.2,
1055
+ legL: -0.3,
1056
+ legR: 0.25,
1057
+ headX: -0.25
1058
+ }
1059
+ },
1060
+ {
1061
+ t: 3.6,
1062
+ pose: {
1063
+ lean: -0.35,
1064
+ headY: 0.3,
1065
+ armL: -1.5,
1066
+ armR: -1,
1067
+ elbL: -0.6,
1068
+ elbR: -1.8,
1069
+ drop: -0.05,
1070
+ kneeL: 0.3,
1071
+ kneeR: 0.2,
1072
+ legL: -0.4,
1073
+ legR: 0.35,
1074
+ headX: -0.2
1075
+ }
1076
+ },
1077
+ {
1078
+ t: 4.3,
1079
+ pose: {
1080
+ lean: -0.2,
1081
+ headY: 0.7,
1082
+ headZ: -0.5,
1083
+ armL: -0.6,
1084
+ armR: -1.9,
1085
+ elbL: -0.4,
1086
+ elbR: -1.2,
1087
+ drop: -0.06,
1088
+ kneeL: 0.3,
1089
+ kneeR: 0.3,
1090
+ legL: -0.3,
1091
+ legR: 0.3,
1092
+ headX: -0.3
1093
+ }
1094
+ },
1095
+ {
1096
+ t: 5.6,
1097
+ pose: {
1098
+ lean: 0.55,
1099
+ headY: 0.4,
1100
+ headZ: -0.3,
1101
+ armL: -0.3,
1102
+ armR: -0.5,
1103
+ elbL: -0.6,
1104
+ elbR: -0.8,
1105
+ drop: -0.5,
1106
+ kneeL: 1.9,
1107
+ kneeR: 1.7,
1108
+ legL: -1.1,
1109
+ legR: -0.9,
1110
+ headX: 0.3
1111
+ }
1112
+ },
1113
+ {
1114
+ t: 8,
1115
+ pose: {
1116
+ lean: 0.6,
1117
+ headY: 0.4,
1118
+ headZ: -0.3,
1119
+ armL: -0.25,
1120
+ armR: -0.45,
1121
+ elbL: -0.6,
1122
+ elbR: -0.8,
1123
+ drop: -0.52,
1124
+ kneeL: 1.95,
1125
+ kneeR: 1.75,
1126
+ legL: -1.1,
1127
+ legR: -0.95,
1128
+ headX: 0.4
1129
+ }
1130
+ }
1131
+ ],
1132
+ events: [
1133
+ {
1134
+ t: 0,
1135
+ type: "phase",
1136
+ data: {
1137
+ label: "pose_clip.grab_bite.phase.approach"
1138
+ }
1139
+ },
1140
+ {
1141
+ t: 1.7,
1142
+ type: "phase",
1143
+ data: {
1144
+ label: "pose_clip.grab_bite.phase.pounce"
1145
+ }
1146
+ },
1147
+ {
1148
+ t: 2.1,
1149
+ type: "phase",
1150
+ data: {
1151
+ label: "pose_clip.grab_bite.phase.grab"
1152
+ }
1153
+ },
1154
+ {
1155
+ t: 4.3,
1156
+ type: "phase",
1157
+ data: {
1158
+ label: "pose_clip.grab_bite.phase.bite"
1159
+ }
1160
+ },
1161
+ {
1162
+ t: 5.6,
1163
+ type: "phase",
1164
+ data: {
1165
+ label: "pose_clip.grab_bite.phase.fall"
1166
+ }
1167
+ },
1168
+ {
1169
+ t: 4.4,
1170
+ type: "impact",
1171
+ data: {
1172
+ kind: "bite",
1173
+ source: "bite.visible=T>4.4 in the scene's step()"
1174
+ }
1175
+ }
1176
+ ]
1177
+ },
1178
+ {
1179
+ id: "grab_bite_zombie",
1180
+ source: {
1181
+ file: "site/pages/zumbis-personagens/bestiario.html",
1182
+ scene: "ataque (Agarr\xE3o e mordida)",
1183
+ variable: "ZK"
1184
+ },
1185
+ duration: 8,
1186
+ loop: false,
1187
+ channels: "full",
1188
+ keys: [
1189
+ {
1190
+ t: 0,
1191
+ pose: {
1192
+ x: -6,
1193
+ lean: 0.75,
1194
+ armL: -1.35,
1195
+ armR: -1.05,
1196
+ elbL: -0.08,
1197
+ elbR: -0.12,
1198
+ headX: -0.55,
1199
+ drop: -0.1,
1200
+ run: 1
1201
+ }
1202
+ },
1203
+ {
1204
+ t: 1.7,
1205
+ pose: {
1206
+ x: -0.9,
1207
+ lean: 0.75,
1208
+ armL: -1.35,
1209
+ armR: -1.05,
1210
+ elbL: -0.08,
1211
+ elbR: -0.12,
1212
+ headX: -0.55,
1213
+ drop: -0.1,
1214
+ run: 1
1215
+ }
1216
+ },
1217
+ {
1218
+ t: 2.1,
1219
+ pose: {
1220
+ x: -0.32,
1221
+ lean: 0.45,
1222
+ armL: -1.65,
1223
+ armR: -1.6,
1224
+ elbL: -0.25,
1225
+ elbR: -0.3,
1226
+ headX: -0.3,
1227
+ drop: -0.08,
1228
+ legL: -0.6,
1229
+ legR: 0.4,
1230
+ kneeL: 0.5,
1231
+ kneeR: 0.6,
1232
+ run: 0
1233
+ }
1234
+ },
1235
+ {
1236
+ t: 3.6,
1237
+ pose: {
1238
+ x: -0.2,
1239
+ lean: 0.35,
1240
+ armL: -1.5,
1241
+ armR: -1.45,
1242
+ elbL: -1.1,
1243
+ elbR: -1.2,
1244
+ headX: -0.2,
1245
+ drop: -0.06,
1246
+ legL: -0.4,
1247
+ legR: 0.3,
1248
+ kneeL: 0.4,
1249
+ kneeR: 0.4,
1250
+ run: 0
1251
+ }
1252
+ },
1253
+ {
1254
+ t: 4.3,
1255
+ pose: {
1256
+ x: -0.1,
1257
+ lean: 0.5,
1258
+ armL: -1.4,
1259
+ armR: -1.3,
1260
+ elbL: -1.5,
1261
+ elbR: -1.5,
1262
+ headX: 0.35,
1263
+ headY: 0.35,
1264
+ drop: -0.08,
1265
+ legL: -0.35,
1266
+ legR: 0.3,
1267
+ kneeL: 0.4,
1268
+ kneeR: 0.4,
1269
+ run: 0
1270
+ }
1271
+ },
1272
+ {
1273
+ t: 5.6,
1274
+ pose: {
1275
+ x: 0,
1276
+ lean: 0.95,
1277
+ armL: -1.2,
1278
+ armR: -1,
1279
+ elbL: -1.4,
1280
+ elbR: -1.3,
1281
+ headX: 0.5,
1282
+ headY: 0.3,
1283
+ drop: -0.45,
1284
+ legL: -1.2,
1285
+ legR: -0.8,
1286
+ kneeL: 1.8,
1287
+ kneeR: 1.5,
1288
+ run: 0
1289
+ }
1290
+ },
1291
+ {
1292
+ t: 8,
1293
+ pose: {
1294
+ x: 0,
1295
+ lean: 1,
1296
+ armL: -1.15,
1297
+ armR: -0.95,
1298
+ elbL: -1.4,
1299
+ elbR: -1.3,
1300
+ headX: 0.55,
1301
+ headY: 0.3,
1302
+ drop: -0.47,
1303
+ legL: -1.2,
1304
+ legR: -0.8,
1305
+ kneeL: 1.85,
1306
+ kneeR: 1.5,
1307
+ run: 0
1308
+ }
1309
+ }
1310
+ ],
1311
+ events: [
1312
+ {
1313
+ t: 0,
1314
+ type: "phase",
1315
+ data: {
1316
+ label: "pose_clip.grab_bite.phase.approach"
1317
+ }
1318
+ },
1319
+ {
1320
+ t: 1.7,
1321
+ type: "phase",
1322
+ data: {
1323
+ label: "pose_clip.grab_bite.phase.pounce"
1324
+ }
1325
+ },
1326
+ {
1327
+ t: 2.1,
1328
+ type: "phase",
1329
+ data: {
1330
+ label: "pose_clip.grab_bite.phase.grab"
1331
+ }
1332
+ },
1333
+ {
1334
+ t: 4.3,
1335
+ type: "phase",
1336
+ data: {
1337
+ label: "pose_clip.grab_bite.phase.bite"
1338
+ }
1339
+ },
1340
+ {
1341
+ t: 5.6,
1342
+ type: "phase",
1343
+ data: {
1344
+ label: "pose_clip.grab_bite.phase.fall"
1345
+ }
1346
+ }
1347
+ ]
1348
+ }
1349
+ ],
1350
+ locales: {
1351
+ "pt-BR": {
1352
+ "pose_clip.grab_bite.phase.approach": "1. Aproxima\xE7\xE3o",
1353
+ "pose_clip.grab_bite.phase.pounce": "2. O bote",
1354
+ "pose_clip.grab_bite.phase.grab": "3. Agarr\xE3o: 3 s para se soltar",
1355
+ "pose_clip.grab_bite.phase.bite": "4. Mordida: infec\xE7\xE3o 100%",
1356
+ "pose_clip.grab_bite.phase.fall": "5. Queda"
1357
+ },
1358
+ en: {
1359
+ "pose_clip.grab_bite.phase.approach": "1. Approach",
1360
+ "pose_clip.grab_bite.phase.pounce": "2. The pounce",
1361
+ "pose_clip.grab_bite.phase.grab": "3. Grab: 3 s to break free",
1362
+ "pose_clip.grab_bite.phase.bite": "4. Bite: 100% infection",
1363
+ "pose_clip.grab_bite.phase.fall": "5. The fall"
1364
+ }
1365
+ }
1366
+ };
1367
+ var POSE_CLIPS = pose_clips_default.clips;
1368
+ var POSE_CLIP_LOCALES = pose_clips_default.locales;
1369
+
1370
+ // src/data/defaults.json
1371
+ var defaults_default = {
1372
+ sources: {
1373
+ store: "jogo.js G 66714-66734 (Z, ZMAX 6000, FIELDS)",
1374
+ bands: "jogo.js G 62019-62020 (BANDS, HYST)",
1375
+ flow: "jogo.js G 66735-66737, 66773-66825 (FLOW_R 192, flowStart, flowStep; budget 1 ms a frame, the first field in one go)",
1376
+ update: "jogo.js G 66835-66977 hordeUpdate",
1377
+ alerts: "jogo.js G 66978-66993 groan, screamAt",
1378
+ attack: "jogo.js G 66996-67006 zAttack",
1379
+ damage: "jogo.js G 67010-67045 zDamage",
1380
+ kill: "jogo.js G 67046-67059 zKill, inchadoBurst",
1381
+ corpses: "jogo.js G 62021 (CORPOS_MAX 400, CORPO_VIDA 900), 67062-67075 corpseAdd, corpsesUpdate",
1382
+ population: "jogo.js G 67139-67177 spawnAway, populationUpdate (popTarget 1200, migration 120 every 90 s, the first after 60 s)",
1383
+ pile: "jogo.js G 67184-67227 (PILE_STEP 0.48, PILE_TOP 3.7, PILE_MAX 160, groundAt, zBlockedAt, tryStep, stepRelease, pileBlast)",
1384
+ trail: "jogo.js G 67238-67246 trailUpdate",
1385
+ abilities: "jogo.js G 67266-67356 zAbility, zChaseSpeed, zHurtOther, killQ",
1386
+ frame: "jogo.js G 67377-67402 zFrame",
1387
+ cadence: "jogo.js G 65507 walkCad"
1388
+ },
1389
+ bands: [
1390
+ { max: 35, dt: 0.05 },
1391
+ { max: 120, dt: 0.2 },
1392
+ { max: 400, dt: 1 },
1393
+ { max: null, dt: 5 }
1394
+ ],
1395
+ numbers: {
1396
+ capacity: 6e3,
1397
+ hysteresis: 10,
1398
+ flowRadius: 192,
1399
+ flowBudgetMs: 1,
1400
+ hashCell: 1,
1401
+ nightSight: 0.4,
1402
+ crouchSight: 0.6,
1403
+ sightCone: 0.5,
1404
+ sightNear: 2.5,
1405
+ loseAfter: 15,
1406
+ hearingBase: 40,
1407
+ heardLost: 5,
1408
+ alertRadius: 8,
1409
+ alertDelay: 0.2,
1410
+ alertDelayRandom: 0.4,
1411
+ voiceDistance: 40,
1412
+ voiceEvery: 0.25,
1413
+ directRange: 4,
1414
+ pyramidRange: 14,
1415
+ screamSpeed: 1.2,
1416
+ attackRange: 1.15,
1417
+ attackHeight: 1.3,
1418
+ attackSlots: 6,
1419
+ attackEvery: 1.5,
1420
+ crouchBite: 1.3,
1421
+ crawlDamage: 0.6,
1422
+ wanderSpan: 24,
1423
+ crawlSpeed: 0.4,
1424
+ limpSpeed: 0.5,
1425
+ separation: 0.31,
1426
+ separationPush: 2.2,
1427
+ doorDamage: 10,
1428
+ doorMessageDistance: 25,
1429
+ skullMinDamage: 20,
1430
+ skullTakeDamage: 60,
1431
+ skullTakeWithin: { "12ga": 5 },
1432
+ jawDamage: 25,
1433
+ jawChance: 0.3,
1434
+ hitStunDamage: 35,
1435
+ hitStun: 0.45,
1436
+ dormantWake: 3,
1437
+ dormantWakeBehind: 10,
1438
+ pileStep: 0.48,
1439
+ pileTop: 3.7,
1440
+ pileMax: 160,
1441
+ pileReleaseDistance: 30,
1442
+ gravity: 9.8,
1443
+ fallStun: 3.2,
1444
+ climbSpeed: 1.9,
1445
+ cadenceBase: 0.5,
1446
+ cadencePerSpeed: 0.3,
1447
+ corpsesMax: 400,
1448
+ corpseSeconds: 900,
1449
+ trailSeconds: 120,
1450
+ trailStep: 1.5,
1451
+ trailMax: 200,
1452
+ population: 1200,
1453
+ refillMin: 70,
1454
+ refillMax: 160,
1455
+ migrationFirst: 60
1456
+ }
1457
+ };
1458
+
1459
+ // src/doors.ts
1460
+ var DOOR_DEFAULTS = { doorHp: 300, plankHp: 150, height: 2.2, thickness: 0.1, soundChance: 0.3, soundVolume: 0.8, breakVolume: 1.5, breakNoise: 30 };
1461
+ function createDoors(grid, options) {
1462
+ const N = { ...DOOR_DEFAULTS, ...options.numbers };
1463
+ const random = options.random;
1464
+ const list = [];
1465
+ const doorAt = new Int16Array(grid.n * grid.n).fill(-1);
1466
+ const listeners = /* @__PURE__ */ new Map();
1467
+ const emit = (event, payload) => {
1468
+ for (const listener of listeners.get(event) ?? []) listener(payload);
1469
+ };
1470
+ const set = (index, open) => {
1471
+ const d = list[index];
1472
+ if (!d) return;
1473
+ d.open = open;
1474
+ grid.fillRect(...d.rect, open || d.broken ? 3 : 5, N.height);
1475
+ };
1476
+ return {
1477
+ list,
1478
+ at: (cell) => cell < 0 ? -1 : doorAt[cell],
1479
+ add(x, z, axis, width, data) {
1480
+ const i = list.length;
1481
+ const t = N.thickness;
1482
+ const x0 = axis === "x" ? x - width / 2 : x - t;
1483
+ const x1 = axis === "x" ? x + width / 2 : x + t;
1484
+ const z0 = axis === "x" ? z - t : z - width / 2;
1485
+ const z1 = axis === "x" ? z + t : z + width / 2;
1486
+ list.push({ x, z, axis, width, hp: N.doorHp, hpMax: N.doorHp, open: false, broken: false, planks: 0, rect: [x0, z0, x1, z1], data });
1487
+ grid.fillRect(x0, z0, x1, z1, 5, N.height);
1488
+ const a = Math.floor((x0 + grid.half) / grid.cell);
1489
+ const b = Math.ceil((x1 + grid.half) / grid.cell);
1490
+ const c = Math.floor((z0 + grid.half) / grid.cell);
1491
+ const e = Math.ceil((z1 + grid.half) / grid.cell);
1492
+ for (let gz = c; gz < e; gz++) for (let gx = a; gx < b; gx++) doorAt[gz * grid.n + gx] = i;
1493
+ return i;
1494
+ },
1495
+ set,
1496
+ hit(index, damage) {
1497
+ const d = list[index];
1498
+ if (!d || d.open || d.broken) return;
1499
+ d.hp -= damage;
1500
+ if (random() < N.soundChance) emit("sound", { sound: "wood", volume: N.soundVolume, position: [d.x, 1, d.z] });
1501
+ if (d.planks && d.hp < d.hpMax - d.planks * N.plankHp + N.plankHp * (d.planks - 1)) d.planks = Math.max(0, d.planks - 1);
1502
+ if (d.hp <= 0) {
1503
+ d.broken = true;
1504
+ d.planks = 0;
1505
+ set(index, true);
1506
+ emit("noise", { x: d.x, z: d.z, radius: N.breakNoise });
1507
+ emit("sound", { sound: "wood", volume: N.breakVolume, position: [d.x, 1, d.z] });
1508
+ emit("effect", { effect: "dust", point: [d.x, 1, d.z], color: 9071178 });
1509
+ emit("broken", { door: index });
1510
+ }
1511
+ },
1512
+ plank(index) {
1513
+ const d = list[index];
1514
+ if (!d) return;
1515
+ d.planks += 1;
1516
+ d.hp += N.plankHp;
1517
+ d.hpMax += N.plankHp;
1518
+ },
1519
+ on(event, listener) {
1520
+ let set_ = listeners.get(event);
1521
+ if (!set_) listeners.set(event, set_ = /* @__PURE__ */ new Set());
1522
+ set_.add(listener);
1523
+ return () => void set_.delete(listener);
1524
+ }
1525
+ };
1526
+ }
1527
+
1528
+ // src/grid.ts
1529
+ var CELL = { FREE: 0, SOLID: 1, LOW_WALL: 2, OPEN_DOOR: 3, GLASS: 4, CLOSED_DOOR: 5 };
1530
+ var BORDER_WIDTH = 1;
1531
+ var BORDER_HEIGHT = 50;
1532
+ var LOS_STEP = 0.5;
1533
+ var LOS_HEIGHT = 1.5;
1534
+ function createNavGrid(options = {}) {
1535
+ const size = options.size ?? 768;
1536
+ const half = size / 2;
1537
+ const cell = options.cell ?? 0.5;
1538
+ const n = size / cell;
1539
+ const occ = new Uint8Array(n * n);
1540
+ const occH = new Float32Array(n * n);
1541
+ const cellOf = (x, z) => {
1542
+ const gx = Math.floor((x + half) / cell);
1543
+ const gz = Math.floor((z + half) / cell);
1544
+ return gx < 0 || gz < 0 || gx >= n || gz >= n ? -1 : gz * n + gx;
1545
+ };
1546
+ const solidAt = (x, z, y = 0.5) => {
1547
+ const c = cellOf(x, z);
1548
+ return c < 0 || occ[c] !== 0 && occ[c] !== 3 && occH[c] > y;
1549
+ };
1550
+ const fillRect = (x0, z0, x1, z1, kind, h) => {
1551
+ const a = Math.floor((x0 + half) / cell);
1552
+ const b = Math.ceil((x1 + half) / cell);
1553
+ const c = Math.floor((z0 + half) / cell);
1554
+ const d = Math.ceil((z1 + half) / cell);
1555
+ for (let gz = Math.max(0, c); gz < Math.min(n, d); gz++)
1556
+ for (let gx = Math.max(0, a); gx < Math.min(n, b); gx++) {
1557
+ const i = gz * n + gx;
1558
+ occ[i] = kind;
1559
+ occH[i] = h;
1560
+ }
1561
+ };
1562
+ if (options.border ?? true) {
1563
+ fillRect(-half, -half, half, -half + BORDER_WIDTH, 1, BORDER_HEIGHT);
1564
+ fillRect(-half, half - BORDER_WIDTH, half, half, 1, BORDER_HEIGHT);
1565
+ fillRect(-half, -half, -half + BORDER_WIDTH, half, 1, BORDER_HEIGHT);
1566
+ fillRect(half - BORDER_WIDTH, -half, half, half, 1, BORDER_HEIGHT);
1567
+ }
1568
+ return {
1569
+ size,
1570
+ half,
1571
+ cell,
1572
+ n,
1573
+ occ,
1574
+ occH,
1575
+ cellOf,
1576
+ solidAt,
1577
+ cellKind: (x, z) => {
1578
+ const c = cellOf(x, z);
1579
+ return c < 0 ? -1 : occ[c];
1580
+ },
1581
+ fillRect,
1582
+ blocked(x, z) {
1583
+ const c = cellOf(x, z);
1584
+ return c < 0 || occ[c] !== 0 && occ[c] !== 3;
1585
+ },
1586
+ losClear(x0, z0, x1, z1) {
1587
+ const d = Math.hypot(x1 - x0, z1 - z0);
1588
+ const steps = Math.ceil(d / LOS_STEP);
1589
+ for (let k = 1; k < steps; k++) {
1590
+ const t = k / steps;
1591
+ const c = cellOf(x0 + (x1 - x0) * t, z0 + (z1 - z0) * t);
1592
+ if (c < 0 || occ[c] === 1 && occH[c] > LOS_HEIGHT) return false;
1593
+ }
1594
+ return true;
1595
+ }
1596
+ };
1597
+ }
1598
+
1599
+ // src/rng.ts
1600
+ function createRng(seed) {
1601
+ let state = seed >>> 0;
1602
+ return () => {
1603
+ state = state + 1831565813 >>> 0;
1604
+ let mixed = Math.imul(state ^ state >>> 15, state | 1);
1605
+ mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, mixed | 61);
1606
+ return ((mixed ^ mixed >>> 14) >>> 0) / 4294967296;
1607
+ };
1608
+ }
1609
+
1610
+ // src/store.ts
1611
+ var createStore = (capacity) => {
1612
+ const f32 = () => new Float32Array(capacity);
1613
+ const u8 = () => new Uint8Array(capacity);
1614
+ return {
1615
+ n: 0,
1616
+ capacity,
1617
+ x: f32(),
1618
+ z: f32(),
1619
+ vx: f32(),
1620
+ vz: f32(),
1621
+ yaw: f32(),
1622
+ phase: f32(),
1623
+ type: u8(),
1624
+ age: u8(),
1625
+ outfit: u8(),
1626
+ height: f32(),
1627
+ build: f32(),
1628
+ chaseSpeed: f32(),
1629
+ wanderSpeed: f32(),
1630
+ hp: f32(),
1631
+ hpMax: f32(),
1632
+ headHp: f32(),
1633
+ armHp: new Float32Array(capacity * 2),
1634
+ legHp: new Float32Array(capacity * 2),
1635
+ missing: new Uint16Array(capacity),
1636
+ helmet: u8(),
1637
+ state: u8(),
1638
+ stateTimer: f32(),
1639
+ lost: f32(),
1640
+ attackTimer: f32(),
1641
+ band: u8(),
1642
+ next: f32(),
1643
+ wx: f32(),
1644
+ wz: f32(),
1645
+ crawl: u8(),
1646
+ limp: u8(),
1647
+ color: new Uint32Array(capacity),
1648
+ skin: f32(),
1649
+ alertIn: f32(),
1650
+ cooldown: f32(),
1651
+ y: f32(),
1652
+ floor: u8(),
1653
+ floorLocked: u8(),
1654
+ vy: f32(),
1655
+ abilityTimer: f32(),
1656
+ eaten: u8(),
1657
+ stuck: f32(),
1658
+ stepCell: new Int32Array(capacity),
1659
+ burn: f32(),
1660
+ hunts: new Int8Array(capacity),
1661
+ named: new Int8Array(capacity),
1662
+ dvx: f32(),
1663
+ dvz: f32(),
1664
+ steering: u8(),
1665
+ renderYaw: f32(),
1666
+ renderSpeed: f32(),
1667
+ pause: f32(),
1668
+ lod: u8(),
1669
+ variant: u8(),
1670
+ attackPose: u8(),
1671
+ arms: f32()
1672
+ };
1673
+ };
1674
+ var AGENT_FIELDS = [
1675
+ "x",
1676
+ "z",
1677
+ "vx",
1678
+ "vz",
1679
+ "yaw",
1680
+ "phase",
1681
+ "type",
1682
+ "age",
1683
+ "outfit",
1684
+ "height",
1685
+ "build",
1686
+ "chaseSpeed",
1687
+ "wanderSpeed",
1688
+ "hp",
1689
+ "hpMax",
1690
+ "headHp",
1691
+ "missing",
1692
+ "helmet",
1693
+ "state",
1694
+ "stateTimer",
1695
+ "lost",
1696
+ "attackTimer",
1697
+ "band",
1698
+ "next",
1699
+ "wx",
1700
+ "wz",
1701
+ "crawl",
1702
+ "limp",
1703
+ "color",
1704
+ "skin",
1705
+ "alertIn",
1706
+ "cooldown",
1707
+ "y",
1708
+ "floor",
1709
+ "floorLocked",
1710
+ "vy",
1711
+ "abilityTimer",
1712
+ "eaten",
1713
+ "stuck",
1714
+ "stepCell",
1715
+ "burn",
1716
+ "hunts",
1717
+ "named",
1718
+ "dvx",
1719
+ "dvz",
1720
+ "steering",
1721
+ "renderYaw",
1722
+ "renderSpeed",
1723
+ "pause",
1724
+ "lod",
1725
+ "variant",
1726
+ "attackPose",
1727
+ "arms"
1728
+ ];
1729
+
1730
+ // src/types.ts
1731
+ var STATE = {
1732
+ WANDER: 0,
1733
+ CHASE: 1,
1734
+ GRAB: 2,
1735
+ STUNNED: 3,
1736
+ CALLING: 4,
1737
+ AIRBORNE: 5,
1738
+ DORMANT: 6,
1739
+ SWELLING: 7,
1740
+ SWING: 8,
1741
+ EATING: 9,
1742
+ PILE_STEP: 10,
1743
+ SEEKING_SOUND: 12
1744
+ };
1745
+ 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 };
1746
+
1747
+ // src/crowd.ts
1748
+ var CROWD_DEFAULTS = {
1749
+ sources: defaults_default.sources,
1750
+ bands: defaults_default.bands.map((band) => ({ max: band.max ?? Infinity, dt: band.dt })),
1751
+ numbers: defaults_default.numbers
1752
+ };
1753
+ var clamp = (v, a, b) => v < a ? a : v > b ? b : v;
1754
+ var TAU_APPROX = 6.2832;
1755
+ var TURN = 6.28;
1756
+ function createCrowd(options) {
1757
+ const random = options.random ?? createRng(options.seed ?? 1);
1758
+ const R = random;
1759
+ const pack = options.pack;
1760
+ const types = pack.types;
1761
+ const N = { ...CROWD_DEFAULTS.numbers, ...options.numbers };
1762
+ const BANDS = options.bands ?? CROWD_DEFAULTS.bands;
1763
+ const HYST = N.hysteresis;
1764
+ const grid = options.grid ?? createNavGrid();
1765
+ const doors = options.doors ?? createDoors(grid, { random });
1766
+ const water = options.water ?? (() => false);
1767
+ const plugins = options.plugins ?? [];
1768
+ const clock = options.flow?.clock ?? (() => typeof performance === "undefined" ? Date.now() : performance.now());
1769
+ const FLOW_R = options.flow?.radius ?? N.flowRadius;
1770
+ const Z = createStore(options.capacity ?? N.capacity);
1771
+ const { occ, occH, cellOf, solidAt } = grid;
1772
+ const HALF = grid.half;
1773
+ const GC = grid.cell;
1774
+ const FW = grid.n;
1775
+ const HC = N.hashCell;
1776
+ const HN = Math.floor(grid.size / HC);
1777
+ const hHead = new Int32Array(HN * HN);
1778
+ const hNext = new Int32Array(Z.capacity);
1779
+ const pile = new Float32Array(FW * FW);
1780
+ let pileN = 0;
1781
+ const flow = {
1782
+ dir: new Int8Array(FW * FW * 2),
1783
+ back: new Int8Array(FW * FW * 2),
1784
+ dist: new Float32Array(FW * FW),
1785
+ q: new Int32Array((2 * FLOW_R + 1) ** 2 * 2),
1786
+ phase: 0,
1787
+ qh: 0,
1788
+ qt: 0,
1789
+ c: 0,
1790
+ t: 0,
1791
+ cell: -1,
1792
+ ms: 0,
1793
+ win: [0, 0, -1, -1],
1794
+ winB: [0, 0, -1, -1]
1795
+ };
1796
+ const catTable = [];
1797
+ types.forEach((c, i) => {
1798
+ for (let k = 0; k < c.weight; k++) catTable.push(i);
1799
+ });
1800
+ const variantsOf = [];
1801
+ {
1802
+ let next = 0;
1803
+ types.forEach((type) => {
1804
+ const list = [];
1805
+ for (let v = 0; v < (type.variants ?? 1); v++) list.push(next++);
1806
+ variantsOf.push(list);
1807
+ });
1808
+ }
1809
+ const typeIndexById = new Map(types.map((type, index) => [type.id, index]));
1810
+ const noises = [];
1811
+ const stats = { bands: [0, 0, 0, 0], slots: 0, kills: 0, heads: 0 };
1812
+ const corpses = createCorpseLedger({ max: N.corpsesMax, seconds: N.corpseSeconds });
1813
+ const trail = [];
1814
+ let trailLast = { x: 1e9, z: 1e9 };
1815
+ const killQ = [];
1816
+ let groanBudget = 0;
1817
+ let migrateT = N.migrationFirst;
1818
+ let hunt = null;
1819
+ let target = options.target ?? null;
1820
+ let nightF = 0;
1821
+ let viewYaw = 0;
1822
+ const listeners = /* @__PURE__ */ new Map();
1823
+ const emit = (event, payload) => {
1824
+ const set = listeners.get(event);
1825
+ if (set) for (const listener of set) listener(payload);
1826
+ };
1827
+ const emitNoise = (x, z, r) => void noises.push({ x, z, r });
1828
+ doors.on("noise", (e) => emitNoise(e.x, e.z, e.radius));
1829
+ doors.on("sound", (e) => emit("sound", { ...e, distance: target ? Math.hypot(e.position[0] - target.state.x, e.position[2] - target.state.z) : 0 }));
1830
+ doors.on("effect", (e) => emit("effect", e));
1831
+ doors.on("broken", (e) => {
1832
+ const d = doors.list[e.door];
1833
+ if (target && Math.hypot(d.x - target.state.x, d.z - target.state.z) < N.doorMessageDistance) emit("message", { id: "door_broken", seconds: 2 });
1834
+ });
1835
+ const sound = (id, volume, x, y, z, d) => emit("sound", { sound: id, volume, position: [x, y, z], distance: d });
1836
+ const pick = (list) => {
1837
+ const r = R();
1838
+ let acc = 0;
1839
+ for (const it of list) {
1840
+ acc += it.p;
1841
+ if (r < acc) return it;
1842
+ }
1843
+ return list[0];
1844
+ };
1845
+ const T = (i) => types[Z.type[i]];
1846
+ const traits = (i) => T(i).traits ?? {};
1847
+ const special = (i) => {
1848
+ const tr = traits(i);
1849
+ Z.y[i] = 0;
1850
+ Z.floor[i] = 0;
1851
+ Z.floorLocked[i] = 0;
1852
+ Z.vy[i] = 0;
1853
+ Z.abilityTimer[i] = 0;
1854
+ Z.eaten[i] = 0;
1855
+ Z.stuck[i] = 0;
1856
+ Z.stepCell[i] = -1;
1857
+ Z.burn[i] = 0;
1858
+ Z.hunts[i] = -1;
1859
+ Z.named[i] = -1;
1860
+ if (tr.color !== void 0 && !tr.submerged) {
1861
+ Z.color[i] = tr.color;
1862
+ if (tr.skin !== void 0) Z.skin[i] = tr.skin;
1863
+ }
1864
+ if (tr.ambush && R() < tr.ambush.chance) {
1865
+ Z.state[i] = STATE.DORMANT;
1866
+ Z.yaw[i] = R() * TURN;
1867
+ }
1868
+ if (tr.submerged) {
1869
+ Z.state[i] = STATE.DORMANT;
1870
+ Z.y[i] = tr.submerged.depth;
1871
+ if (tr.color !== void 0) Z.color[i] = tr.color;
1872
+ }
1873
+ };
1874
+ const spawn = (x, z, catForce = -1) => {
1875
+ if (Z.n >= Z.capacity) return -1;
1876
+ const i = Z.n++;
1877
+ const c = catForce >= 0 ? catForce : catTable[Math.floor(R() * catTable.length)];
1878
+ const C = types[c];
1879
+ const ai = pack.ages.indexOf(pick(pack.ages));
1880
+ const maxAge = C.traits?.maxAge;
1881
+ const A = pack.ages[maxAge !== void 0 ? Math.min(ai, maxAge) : ai];
1882
+ const ri = pack.outfits.indexOf(pick(pack.outfits));
1883
+ const RO = pack.outfits[ri];
1884
+ Z.x[i] = x;
1885
+ Z.z[i] = z;
1886
+ Z.vx[i] = Z.vz[i] = 0;
1887
+ Z.type[i] = c;
1888
+ Z.age[i] = pack.ages.indexOf(A);
1889
+ Z.outfit[i] = ri;
1890
+ const g = (R() + R() + R() - 1.5) * 0.09;
1891
+ Z.height[i] = clamp(1 + g, 0.9, 1.1) * (C.scale || 1);
1892
+ const bb = R();
1893
+ 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));
1894
+ const heavy = bb >= 0.8;
1895
+ const thin = bb < 0.3;
1896
+ Z.chaseSpeed[i] = C.speed.chase * A.speed * (thin ? 1.05 : heavy ? 0.9 : 1) * (0.92 + R() * 0.16);
1897
+ Z.wanderSpeed[i] = C.speed.wander * A.speed * (0.8 + R() * 0.4);
1898
+ Z.hpMax[i] = Z.hp[i] = C.health * A.health * (thin ? 0.9 : heavy ? 1.15 : 1);
1899
+ Z.headHp[i] = pack.segments.head;
1900
+ Z.armHp[i * 2] = Z.armHp[i * 2 + 1] = pack.segments.arm;
1901
+ Z.legHp[i * 2] = Z.legHp[i * 2 + 1] = pack.segments.leg;
1902
+ Z.missing[i] = 0;
1903
+ Z.helmet[i] = RO.helmet ? 1 : 0;
1904
+ Z.crawl[i] = C.crawls ? 1 : 0;
1905
+ Z.limp[i] = 0;
1906
+ Z.state[i] = 0;
1907
+ Z.stateTimer[i] = 0;
1908
+ Z.lost[i] = 0;
1909
+ Z.attackTimer[i] = R();
1910
+ Z.alertIn[i] = 0;
1911
+ Z.cooldown[i] = 0;
1912
+ {
1913
+ const vs = variantsOf[c];
1914
+ Z.variant[i] = vs[Math.floor(R() * vs.length)];
1915
+ }
1916
+ Z.yaw[i] = Z.renderYaw[i] = R() * TURN;
1917
+ Z.renderSpeed[i] = 0;
1918
+ Z.steering[i] = 0;
1919
+ Z.pause[i] = 0;
1920
+ Z.attackPose[i] = 0;
1921
+ Z.arms[i] = 0;
1922
+ Z.phase[i] = R() * 10;
1923
+ Z.band[i] = 3;
1924
+ Z.next[i] = R() * 0.05;
1925
+ Z.wx[i] = x;
1926
+ Z.wz[i] = z;
1927
+ Z.color[i] = RO.color || pack.palette[Math.floor(R() * pack.palette.length)];
1928
+ Z.skin[i] = R();
1929
+ special(i);
1930
+ emit("spawn", { agent: i, type: C.id });
1931
+ return i;
1932
+ };
1933
+ const remove = (i) => {
1934
+ if (Z.state[i] === STATE.PILE_STEP) stepRelease(i);
1935
+ const j = --Z.n;
1936
+ if (i === j) {
1937
+ emit("remove", { agent: i, moved: -1 });
1938
+ return;
1939
+ }
1940
+ for (const k of AGENT_FIELDS) Z[k][i] = Z[k][j];
1941
+ Z.armHp[i * 2] = Z.armHp[j * 2];
1942
+ Z.armHp[i * 2 + 1] = Z.armHp[j * 2 + 1];
1943
+ Z.legHp[i * 2] = Z.legHp[j * 2];
1944
+ Z.legHp[i * 2 + 1] = Z.legHp[j * 2 + 1];
1945
+ if (target) {
1946
+ if (target.state.grabbedBy === j) target.state.grabbedBy = i;
1947
+ else if (target.state.grabbedBy === i) target.state.grabbedBy = -1;
1948
+ }
1949
+ emit("remove", { agent: i, moved: j });
1950
+ };
1951
+ const flowStart = (x, z) => {
1952
+ const gx = clamp(Math.floor((x + HALF) / GC), 0, FW - 1);
1953
+ const gz = clamp(Math.floor((z + HALF) / GC), 0, FW - 1);
1954
+ const W = flow.winB;
1955
+ W[0] = Math.max(0, gx - FLOW_R);
1956
+ W[1] = Math.max(0, gz - FLOW_R);
1957
+ W[2] = Math.min(FW - 1, gx + FLOW_R);
1958
+ W[3] = Math.min(FW - 1, gz + FLOW_R);
1959
+ for (let r = W[1]; r <= W[3]; r++) flow.dist.fill(1e9, r * FW + W[0], r * FW + W[2] + 1);
1960
+ const s = gz * FW + gx;
1961
+ flow.dist[s] = 0;
1962
+ flow.q[0] = s;
1963
+ flow.qh = 0;
1964
+ flow.qt = 1;
1965
+ flow.phase = 1;
1966
+ flow.c = 0;
1967
+ flow.t = 0;
1968
+ };
1969
+ const flowStep = (budget) => {
1970
+ if (!flow.phase) return;
1971
+ const t0 = clock();
1972
+ const D = flow.dist;
1973
+ const Q = flow.q;
1974
+ const F = flow.back;
1975
+ const W = flow.winB;
1976
+ let n = 0;
1977
+ while (flow.phase === 1) {
1978
+ if (flow.qh >= flow.qt) {
1979
+ flow.phase = 2;
1980
+ break;
1981
+ }
1982
+ const c = Q[flow.qh++];
1983
+ const cx = c % FW;
1984
+ const cz = c / FW | 0;
1985
+ const dc = D[c];
1986
+ for (let dz = -1; dz <= 1; dz++)
1987
+ for (let dx = -1; dx <= 1; dx++) {
1988
+ if (!dx && !dz) continue;
1989
+ const nx = cx + dx;
1990
+ const nz = cz + dz;
1991
+ if (nx < W[0] || nz < W[1] || nx > W[2] || nz > W[3]) continue;
1992
+ const k = nz * FW + nx;
1993
+ if (occ[k] && occ[k] < 3) continue;
1994
+ if (dx && dz && (occ[cz * FW + nx] && occ[cz * FW + nx] < 3 || occ[nz * FW + cx] && occ[nz * FW + cx] < 3)) continue;
1995
+ const nd = dc + (dx && dz ? 1.414 : 1);
1996
+ if (nd < D[k] - 1e-3) {
1997
+ D[k] = nd;
1998
+ if (flow.qt < Q.length) Q[flow.qt++] = k;
1999
+ }
2000
+ }
2001
+ if ((++n & 2047) === 0 && clock() - t0 > budget) {
2002
+ flow.t += clock() - t0;
2003
+ return;
2004
+ }
2005
+ }
2006
+ const ww = W[2] - W[0] + 1;
2007
+ while (flow.phase === 2) {
2008
+ const j = flow.c++;
2009
+ if (j >= ww * (W[3] - W[1] + 1)) {
2010
+ const tmp = flow.dir;
2011
+ flow.dir = F;
2012
+ flow.back = tmp;
2013
+ const tw = flow.win;
2014
+ flow.win = W;
2015
+ flow.winB = tw;
2016
+ flow.phase = 0;
2017
+ flow.ms = flow.t + clock() - t0;
2018
+ return;
2019
+ }
2020
+ const cx = W[0] + j % ww;
2021
+ const cz = W[1] + (j / ww | 0);
2022
+ const c = cz * FW + cx;
2023
+ if (occ[c] && occ[c] < 3) {
2024
+ F[c * 2] = F[c * 2 + 1] = 0;
2025
+ continue;
2026
+ }
2027
+ let best = D[c];
2028
+ let bx = 0;
2029
+ let bz = 0;
2030
+ for (let dz = -1; dz <= 1; dz++)
2031
+ for (let dx = -1; dx <= 1; dx++) {
2032
+ const nx = cx + dx;
2033
+ const nz = cz + dz;
2034
+ if (nx < W[0] || nz < W[1] || nx > W[2] || nz > W[3]) continue;
2035
+ const d = D[nz * FW + nx];
2036
+ if (d < best) {
2037
+ best = d;
2038
+ bx = dx;
2039
+ bz = dz;
2040
+ }
2041
+ }
2042
+ F[c * 2] = bx;
2043
+ F[c * 2 + 1] = bz;
2044
+ if ((j & 8191) === 0 && clock() - t0 > budget) {
2045
+ flow.t += clock() - t0;
2046
+ return;
2047
+ }
2048
+ }
2049
+ };
2050
+ const losClear = grid.losClear;
2051
+ const groundAt = (c, _y) => {
2052
+ if (c < 0) return 0;
2053
+ const k = occ[c];
2054
+ if (k === 2 || k === 1 && occH[c] <= _y + 0.05) return occH[c];
2055
+ return pile[c];
2056
+ };
2057
+ const blockedAt = (i, x, z) => {
2058
+ const c = cellOf(x, z);
2059
+ if (c < 0) return true;
2060
+ const k = occ[c];
2061
+ const y = Z.y[i];
2062
+ if (k === 0 || k === 3) return pile[c] > y + 0.5 && Z.state[i] !== STATE.AIRBORNE;
2063
+ if (k === 2 && y + 0.5 >= occH[c]) return false;
2064
+ if (Z.state[i] === STATE.AIRBORNE && occH[c] <= y) return false;
2065
+ return true;
2066
+ };
2067
+ const tryStep = (i, hitCell) => {
2068
+ if (pileN >= N.pileMax) return false;
2069
+ const c = cellOf(Z.x[i], Z.z[i]);
2070
+ if (c < 0 || occ[c] !== 0) return false;
2071
+ const dir = hitCell - c;
2072
+ const w = c + dir;
2073
+ const back = c - dir;
2074
+ const h = pile[c] + N.pileStep;
2075
+ if (h > N.pileTop) return false;
2076
+ const cap = occ[w] === 2 ? (back >= 0 ? pile[back] : 0) + 1 : pile[w];
2077
+ if (h > cap + 1e-3) return false;
2078
+ pile[c] = h;
2079
+ pileN++;
2080
+ Z.state[i] = STATE.PILE_STEP;
2081
+ Z.stepCell[i] = c;
2082
+ Z.vx[i] = Z.vz[i] = 0;
2083
+ Z.y[i] = h - N.pileStep;
2084
+ Z.stateTimer[i] = 6 + R() * 6;
2085
+ return true;
2086
+ };
2087
+ function stepRelease(i) {
2088
+ const c = Z.stepCell[i];
2089
+ if (c >= 0) {
2090
+ pile[c] = Math.max(0, pile[c] - N.pileStep);
2091
+ pileN = Math.max(0, pileN - 1);
2092
+ }
2093
+ Z.stepCell[i] = -1;
2094
+ Z.state[i] = STATE.CHASE;
2095
+ Z.stuck[i] = 0;
2096
+ }
2097
+ const pileBlast = (x, z, r) => {
2098
+ for (let i = 0; i < Z.n; i++)
2099
+ if (Z.state[i] === STATE.PILE_STEP && (Z.x[i] - x) ** 2 + (Z.z[i] - z) ** 2 < r * r) {
2100
+ stepRelease(i);
2101
+ Z.state[i] = STATE.STUNNED;
2102
+ Z.stateTimer[i] = 1.2;
2103
+ }
2104
+ };
2105
+ const voice = (i) => traits(i).voice ?? "groan";
2106
+ const groan = (i, d) => {
2107
+ const r2 = N.alertRadius * N.alertRadius;
2108
+ for (let j = 0; j < Z.n; j++) {
2109
+ if (Z.state[j] !== 0 || Z.alertIn[j] > 0) continue;
2110
+ const ex = Z.x[j] - Z.x[i];
2111
+ const ez = Z.z[j] - Z.z[i];
2112
+ if (ex * ex + ez * ez < r2) Z.alertIn[j] = N.alertDelay + R() * N.alertDelayRandom;
2113
+ }
2114
+ emit("alert", { agent: i });
2115
+ if (d < N.voiceDistance && groanBudget <= 0) {
2116
+ sound(voice(i), 1, Z.x[i], Z.height[i] * 1.6, Z.z[i], d);
2117
+ groanBudget = N.voiceEvery;
2118
+ }
2119
+ };
2120
+ const screamAt = (i, x, z) => {
2121
+ const tr = traits(i).caller;
2122
+ sound("scream_hoarse", 1, x, 1.6, z, target ? Math.hypot(x - target.state.x, z - target.state.z) : 0);
2123
+ for (let j = 0; j < Z.n; j++) {
2124
+ const ex = Z.x[j] - x;
2125
+ const ez = Z.z[j] - z;
2126
+ if (ex * ex + ez * ez < tr.radius * tr.radius) {
2127
+ Z.state[j] = STATE.CHASE;
2128
+ Z.lost[j] = 0;
2129
+ }
2130
+ }
2131
+ if (target) target.state.screamBoost = tr.boost;
2132
+ emit("message", { id: "called", seconds: 3, type: T(i).id });
2133
+ emit("scream", { agent: i, x, z });
2134
+ };
2135
+ const attack = (i, d, tdt) => {
2136
+ Z.attackTimer[i] -= tdt;
2137
+ if (Z.attackTimer[i] > 0) return;
2138
+ Z.attackTimer[i] = N.attackEvery;
2139
+ const C = T(i);
2140
+ const tr = C.traits ?? {};
2141
+ if (tr.heavySwing && R() < tr.heavySwing.chance) {
2142
+ Z.state[i] = STATE.SWING;
2143
+ Z.stateTimer[i] = tr.heavySwing.windUp;
2144
+ return;
2145
+ }
2146
+ const noJaw = Z.missing[i] & 512;
2147
+ const noArms = (Z.missing[i] & 6) === 6;
2148
+ const P = target;
2149
+ if (P.state.grabbedBy < 0 && !noArms && !Z.crawl[i] && R() < C.bite * (P.state.crouch ? N.crouchBite : 1)) {
2150
+ P.grab(i, !!noJaw, !!tr.drownGrab && water(P.state.x, P.state.z));
2151
+ Z.state[i] = STATE.GRAB;
2152
+ } else {
2153
+ P.hurt(C.damage * (Z.crawl[i] ? N.crawlDamage : 1), i);
2154
+ if (tr.burns && Z.abilityTimer[i] >= 0) P.state.burnTimer = tr.burns.seconds;
2155
+ }
2156
+ void d;
2157
+ };
2158
+ const effect = (payload) => emit("effect", payload);
2159
+ const blood = (x, y, z, age, amount, died, agent) => effect({ effect: "blood", point: [x, y, z], age, amount, died, ...agent === void 0 ? {} : { agent } });
2160
+ const damage = (i, hit) => {
2161
+ const C = T(i);
2162
+ const A = pack.ages[Z.age[i]];
2163
+ const RO = pack.outfits[Z.outfit[i]];
2164
+ const cal = hit.caliber ?? null;
2165
+ const proj = !!cal;
2166
+ const dano = hit.amount;
2167
+ const dist = hit.distance;
2168
+ const corte = hit.cut ?? 0;
2169
+ const regiao = hit.region;
2170
+ let morreu = false;
2171
+ let headshot = false;
2172
+ if (regiao === 0) {
2173
+ if (proj && Z.helmet[i] && cal.class === "pistol") {
2174
+ Z.helmet[i] = 0;
2175
+ effect({ effect: "spark", point: [Z.x[i], Z.height[i] * 1.75, Z.z[i]], agent: i });
2176
+ sound("impact", 1, Z.x[i], Z.height[i] * 1.75, Z.z[i], dist);
2177
+ return { died: false };
2178
+ }
2179
+ const mata = proj ? (cal.skull === 1 || dist <= cal.skull) && dano >= N.skullMinDamage : dano * 3 >= Z.headHp[i];
2180
+ if (mata) {
2181
+ morreu = true;
2182
+ headshot = true;
2183
+ if (dano > N.skullTakeDamage || cal && N.skullTakeWithin[cal.id] !== void 0 && dist < N.skullTakeWithin[cal.id]) Z.missing[i] |= 1;
2184
+ } else {
2185
+ Z.hp[i] -= dano * 3;
2186
+ Z.headHp[i] -= dano;
2187
+ if (dano > N.jawDamage && R() < N.jawChance) Z.missing[i] |= 512;
2188
+ }
2189
+ } else if (regiao === 1) {
2190
+ let m = C.multipliers.torso;
2191
+ if (RO.vest && proj) m *= cal.class === "pistol" ? RO.vest.pistol : RO.vest.rifle;
2192
+ Z.hp[i] -= dano * m;
2193
+ } else {
2194
+ const perna = regiao >= 4;
2195
+ const lado = regiao & 1;
2196
+ const mm = (perna ? C.multipliers.legs : C.multipliers.arms) * A.limb * (RO.limb || 1);
2197
+ const segArr = perna ? Z.legHp : Z.armHp;
2198
+ const k = i * 2 + (perna ? lado : regiao === 3 ? 1 : 0);
2199
+ segArr[k] -= dano * mm;
2200
+ Z.hp[i] -= dano * mm * 0.5;
2201
+ const max = perna ? pack.segments.leg : pack.segments.arm;
2202
+ if (perna && segArr[k] <= max * 0.5) Z.limp[i] = 1;
2203
+ if (segArr[k] <= 0) {
2204
+ const arranca = corte >= 0.5 || dano * mm > max * 0.9;
2205
+ if (perna) {
2206
+ Z.crawl[i] = 1;
2207
+ if (arranca) Z.missing[i] |= lado ? 16 : 8;
2208
+ else Z.missing[i] |= lado ? 256 : 128;
2209
+ } else if (arranca) Z.missing[i] |= regiao === 3 ? 4 : 2;
2210
+ else Z.missing[i] |= regiao === 3 ? 64 : 32;
2211
+ effect({ effect: "limb", point: [Z.x[i], Z.height[i] * (perna ? 0.5 : 1.2), Z.z[i]], agent: i, region: regiao, severed: arranca });
2212
+ }
2213
+ }
2214
+ if (Z.hp[i] <= 0) morreu = true;
2215
+ 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);
2216
+ if (morreu) {
2217
+ kill(i, headshot);
2218
+ return { died: morreu, headshot };
2219
+ }
2220
+ const st = Z.state[i];
2221
+ if (dano >= N.hitStunDamage && st !== STATE.GRAB && st !== STATE.PILE_STEP && st !== STATE.AIRBORNE) {
2222
+ Z.state[i] = STATE.STUNNED;
2223
+ Z.stateTimer[i] = C.traits?.hitStun ?? N.hitStun;
2224
+ }
2225
+ const st2 = Z.state[i];
2226
+ if (st2 === 0 || st2 === STATE.DORMANT || st2 === STATE.EATING || st2 === STATE.SEEKING_SOUND) {
2227
+ Z.state[i] = STATE.CHASE;
2228
+ Z.lost[i] = 0;
2229
+ const sub = C.traits?.submerged;
2230
+ if (sub) Z.y[i] = Math.max(Z.y[i], sub.surface);
2231
+ }
2232
+ return { died: morreu };
2233
+ };
2234
+ function kill(i, headshot = false) {
2235
+ stats.kills++;
2236
+ if (headshot) stats.heads++;
2237
+ const tr = traits(i);
2238
+ if (tr.burst) burstAt(i, Z.x[i], Z.z[i]);
2239
+ const C = T(i);
2240
+ emit("kill", { agent: i, type: C.id, headshot, x: Z.x[i], z: Z.z[i] });
2241
+ corpseAdd(i);
2242
+ if (target && target.state.grabbedBy === i) target.release();
2243
+ remove(i);
2244
+ }
2245
+ const burstAt = (i, x, z) => {
2246
+ const b = traits(i).burst;
2247
+ effect({ effect: "cloud", point: [x, 0, z], radius: 4, color: b.cloudColor, amount: 8 });
2248
+ effect({ effect: "blood", point: [x, 1, z], amount: 1500, died: true, color: b.bloodColor });
2249
+ if (target) {
2250
+ const d = Math.hypot(x - target.state.x, z - target.state.z);
2251
+ if (d < b.radius) {
2252
+ target.state.blind = b.blind;
2253
+ target.state.scentTimer = b.scent;
2254
+ emit("message", { id: "burst_hit", seconds: 2.5, type: T(i).id });
2255
+ emitNoise(target.state.x, target.state.z, b.noiseNear);
2256
+ }
2257
+ }
2258
+ emitNoise(x, z, b.noise);
2259
+ };
2260
+ const corpseAdd = (i) => {
2261
+ corpses.add({
2262
+ x: Z.x[i],
2263
+ z: Z.z[i],
2264
+ yaw: Z.renderYaw[i],
2265
+ height: Z.height[i],
2266
+ build: Z.build[i],
2267
+ named: Z.named[i],
2268
+ missing: Z.missing[i],
2269
+ color: Z.color[i],
2270
+ variant: Z.variant[i],
2271
+ skin: Z.skin[i],
2272
+ seed: R() * 10
2273
+ });
2274
+ };
2275
+ const ability = (i, d, dx, dz, tdt, b, alive) => {
2276
+ const C = T(i);
2277
+ const tr = C.traits ?? {};
2278
+ const st = Z.state[i];
2279
+ const P = target;
2280
+ if (st === STATE.DORMANT) {
2281
+ Z.vx[i] = Z.vz[i] = 0;
2282
+ if (!alive || b > 1) return true;
2283
+ if (tr.submerged) {
2284
+ if (d < tr.submerged.wake) {
2285
+ Z.state[i] = STATE.CHASE;
2286
+ groan(i, d);
2287
+ sound("step", 2, Z.x[i], 0, Z.z[i], d);
2288
+ }
2289
+ return true;
2290
+ }
2291
+ const am = tr.ambush;
2292
+ const wake = am?.wake ?? N.dormantWake;
2293
+ const behind = am?.wakeBehind ?? N.dormantWakeBehind;
2294
+ const fx = -Math.sin(viewYaw);
2295
+ const fz = -Math.cos(viewYaw);
2296
+ const costas = (dx * fx + dz * fz) / (d || 1) < (am?.behindCos ?? -0.3);
2297
+ if (d < wake || costas && d < behind) {
2298
+ Z.state[i] = STATE.CHASE;
2299
+ Z.abilityTimer[i] = am?.sprintSeconds ?? 3;
2300
+ Z.lost[i] = 0;
2301
+ sound("runner_groan", 1, Z.x[i], 1.6, Z.z[i], d);
2302
+ }
2303
+ return true;
2304
+ }
2305
+ if (st === STATE.AIRBORNE || st === STATE.PILE_STEP) return true;
2306
+ if (st === STATE.SWELLING) {
2307
+ Z.vx[i] = Z.vz[i] = 0;
2308
+ return true;
2309
+ }
2310
+ if (st === STATE.SWING) {
2311
+ Z.vx[i] = Z.vz[i] = 0;
2312
+ Z.stateTimer[i] -= tdt;
2313
+ if (Z.stateTimer[i] <= 0) {
2314
+ const hs = tr.heavySwing;
2315
+ Z.state[i] = STATE.CHASE;
2316
+ Z.attackTimer[i] = 2;
2317
+ sound("impact", 1.4, Z.x[i], 1, Z.z[i], d);
2318
+ if (d < hs.range && alive && P) {
2319
+ P.hurt(C.damage, i);
2320
+ P.knock(hs.knock);
2321
+ emit("message", { id: "swing_knock", seconds: 1.5, type: C.id });
2322
+ }
2323
+ }
2324
+ return true;
2325
+ }
2326
+ if (st === STATE.EATING) {
2327
+ const ec2 = tr.eatsCorpses;
2328
+ Z.vx[i] = Z.vz[i] = 0;
2329
+ Z.stateTimer[i] -= tdt;
2330
+ if (alive && d < ec2.defend) {
2331
+ Z.state[i] = STATE.CHASE;
2332
+ Z.lost[i] = 0;
2333
+ groan(i, d);
2334
+ return false;
2335
+ }
2336
+ if (Z.stateTimer[i] <= 0) {
2337
+ let best = -1;
2338
+ let bd = ec2.grabRange;
2339
+ const list = corpses.entries;
2340
+ for (let k = 0; k < list.length; k++) {
2341
+ const e = Math.hypot(list[k].data.x - Z.x[i], list[k].data.z - Z.z[i]);
2342
+ if (e < bd) {
2343
+ bd = e;
2344
+ best = k;
2345
+ }
2346
+ }
2347
+ if (best >= 0) {
2348
+ corpses.remove(best);
2349
+ Z.hp[i] += ec2.health;
2350
+ Z.hpMax[i] += ec2.health;
2351
+ Z.height[i] *= ec2.grow;
2352
+ Z.build[i] *= ec2.grow;
2353
+ Z.eaten[i]++;
2354
+ }
2355
+ Z.state[i] = 0;
2356
+ Z.abilityTimer[i] = ec2.rest;
2357
+ }
2358
+ return true;
2359
+ }
2360
+ if (st === STATE.SEEKING_SOUND) {
2361
+ const ss = tr.soundSeeker ?? { arrive: 1, loseChase: 3, hitRadius2: 1.1 };
2362
+ if (alive && d < 2.5) {
2363
+ Z.state[i] = STATE.CHASE;
2364
+ return false;
2365
+ }
2366
+ const wx = Z.wx[i] - Z.x[i];
2367
+ const wz = Z.wz[i] - Z.z[i];
2368
+ const wl = Math.hypot(wx, wz);
2369
+ if (wl < ss.arrive) {
2370
+ Z.state[i] = 0;
2371
+ return false;
2372
+ }
2373
+ Z.vx[i] = wx / wl * Z.chaseSpeed[i];
2374
+ Z.vz[i] = wz / wl * Z.chaseSpeed[i];
2375
+ Z.yaw[i] = Math.atan2(wx, wz);
2376
+ if (b === 0) {
2377
+ Z.attackTimer[i] -= tdt;
2378
+ if (Z.attackTimer[i] <= 0) {
2379
+ const hx = Math.floor(Z.x[i] + HALF);
2380
+ const hz = Math.floor(Z.z[i] + HALF);
2381
+ if (hx >= 0 && hz >= 0 && hx < HN && hz < HN)
2382
+ for (let j = hHead[hz * HN + hx]; j !== -1; j = hNext[j]) {
2383
+ if (j !== i && (Z.x[j] - Z.x[i]) ** 2 + (Z.z[j] - Z.z[i]) ** 2 < ss.hitRadius2) {
2384
+ Z.attackTimer[i] = N.attackEvery;
2385
+ hurtOther(j, C.damage);
2386
+ break;
2387
+ }
2388
+ }
2389
+ }
2390
+ }
2391
+ return true;
2392
+ }
2393
+ for (const plugin of plugins) if (plugin.ability?.(crowd, i, { d, dx, dz, tdt, band: b, alive })) return true;
2394
+ if (!alive) return false;
2395
+ if (Z.hunts[i] >= 0 && st <= 1 && hunt) return hunt(i, tdt);
2396
+ if (tr.soundSeeker && st === STATE.CHASE && d > tr.soundSeeker.loseChase) {
2397
+ Z.state[i] = STATE.SEEKING_SOUND;
2398
+ Z.wx[i] = P.state.x;
2399
+ Z.wz[i] = P.state.z;
2400
+ return false;
2401
+ }
2402
+ const po = tr.pounce;
2403
+ 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)) {
2404
+ const sp = Math.min(po.speedCap, d / po.time);
2405
+ Z.state[i] = STATE.AIRBORNE;
2406
+ Z.vy[i] = P.state.y > po.highAbove ? po.liftHigh : po.lift;
2407
+ Z.vx[i] = -dx / d * sp;
2408
+ Z.vz[i] = -dz / d * sp;
2409
+ Z.yaw[i] = Math.atan2(-dx, -dz);
2410
+ Z.cooldown[i] = po.cooldown;
2411
+ Z.abilityTimer[i] = 1;
2412
+ sound("runner_groan", 1, Z.x[i], 1.6, Z.z[i], d);
2413
+ return true;
2414
+ }
2415
+ if (tr.burst && st === STATE.CHASE && d < tr.burst.range) {
2416
+ Z.state[i] = STATE.SWELLING;
2417
+ Z.stateTimer[i] = tr.burst.swell;
2418
+ sound("flesh", 1.2, Z.x[i], 1, Z.z[i], d);
2419
+ return true;
2420
+ }
2421
+ const ec = tr.eatsCorpses;
2422
+ if (ec && st === 0 && Z.eaten[i] < ec.max && Z.abilityTimer[i] <= 0 && corpses.count) {
2423
+ let best = -1;
2424
+ let bd = ec.search;
2425
+ const list = corpses.entries;
2426
+ for (let k = 0; k < list.length; k++) {
2427
+ const e = Math.hypot(list[k].data.x - Z.x[i], list[k].data.z - Z.z[i]);
2428
+ if (e < bd) {
2429
+ bd = e;
2430
+ best = k;
2431
+ }
2432
+ }
2433
+ if (best >= 0) {
2434
+ if (bd < ec.reach) {
2435
+ Z.state[i] = STATE.EATING;
2436
+ Z.stateTimer[i] = ec.seconds;
2437
+ Z.vx[i] = Z.vz[i] = 0;
2438
+ return true;
2439
+ }
2440
+ const cp = list[best].data;
2441
+ const ex = cp.x - Z.x[i];
2442
+ const ez = cp.z - Z.z[i];
2443
+ const sp = Z.wanderSpeed[i] * ec.approach;
2444
+ Z.vx[i] = ex / bd * sp;
2445
+ Z.vz[i] = ez / bd * sp;
2446
+ Z.yaw[i] = Math.atan2(ex, ez);
2447
+ Z.wx[i] = cp.x;
2448
+ Z.wz[i] = cp.z;
2449
+ return true;
2450
+ } else Z.abilityTimer[i] = ec.retry;
2451
+ }
2452
+ const tk = tr.tracker;
2453
+ if (tk && st === 0 && trail.length) {
2454
+ let best = null;
2455
+ for (let k = trail.length - 1; k >= 0; k--) {
2456
+ const p = trail[k];
2457
+ if ((p.x - Z.x[i]) ** 2 + (p.z - Z.z[i]) ** 2 < tk.range * tk.range) {
2458
+ best = p;
2459
+ break;
2460
+ }
2461
+ }
2462
+ if (best) {
2463
+ Z.wx[i] = best.x;
2464
+ Z.wz[i] = best.z;
2465
+ let led = 0;
2466
+ const leads = typeIndexById.get(tk.leads) ?? -1;
2467
+ for (let j = 0; j < Z.n && led < tk.max; j++)
2468
+ 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) {
2469
+ Z.wx[j] = Z.x[i];
2470
+ Z.wz[j] = Z.z[i];
2471
+ led++;
2472
+ }
2473
+ }
2474
+ }
2475
+ return false;
2476
+ };
2477
+ const chaseSpeed = (i) => {
2478
+ const tr = traits(i);
2479
+ if (tr.ambush) return Z.abilityTimer[i] > 0 ? tr.ambush.sprint : tr.ambush.slow;
2480
+ if (tr.submerged && water(Z.x[i], Z.z[i])) return tr.submerged.swim;
2481
+ return Z.chaseSpeed[i];
2482
+ };
2483
+ const hurtOther = (j, amount) => {
2484
+ Z.hp[j] -= amount;
2485
+ blood(Z.x[j], Z.height[j] * 1.2, Z.z[j], Z.age[j], 30, false, j);
2486
+ if (Z.hp[j] <= 0) deferKill(j);
2487
+ };
2488
+ const deferKill = (j) => {
2489
+ if (!killQ.includes(j)) killQ.push(j);
2490
+ };
2491
+ const killQFlush = () => {
2492
+ if (!killQ.length) return;
2493
+ killQ.sort((a, b) => b - a);
2494
+ for (const j of killQ) if (j < Z.n) kill(j, false);
2495
+ killQ.length = 0;
2496
+ };
2497
+ const frame = (i, dt, d, alive) => {
2498
+ const st = Z.state[i];
2499
+ const tr = traits(i);
2500
+ if (st === STATE.PILE_STEP) {
2501
+ Z.stateTimer[i] -= dt;
2502
+ if (!alive || d > N.pileReleaseDistance || Z.stateTimer[i] <= 0) stepRelease(i);
2503
+ return;
2504
+ }
2505
+ if (st === STATE.SWELLING) {
2506
+ Z.stateTimer[i] -= dt;
2507
+ if (Z.stateTimer[i] <= 0) deferKill(i);
2508
+ return;
2509
+ }
2510
+ if (tr.ambush && Z.abilityTimer[i] > 0) Z.abilityTimer[i] -= dt;
2511
+ if (tr.eatsCorpses && Z.abilityTimer[i] > 0 && st === 0) Z.abilityTimer[i] -= dt;
2512
+ if (tr.waterWeak && Z.abilityTimer[i] === 0 && water(Z.x[i], Z.z[i])) {
2513
+ Z.abilityTimer[i] = -1;
2514
+ Z.hp[i] *= tr.waterWeak.keep;
2515
+ effect({ effect: "cloud", point: [Z.x[i], 0, Z.z[i]], radius: 1.5, color: tr.waterWeak.cloud, amount: 3, agent: i });
2516
+ }
2517
+ const c = cellOf(Z.x[i], Z.z[i]);
2518
+ const gy = groundAt(c, Z.y[i]);
2519
+ if (st === STATE.DORMANT || Z.floorLocked[i]) return;
2520
+ if (st === STATE.AIRBORNE || Z.y[i] > gy + 0.02 || Z.vy[i] > 0) {
2521
+ Z.vy[i] -= N.gravity * dt;
2522
+ Z.y[i] += Z.vy[i] * dt;
2523
+ if (Z.y[i] <= gy) {
2524
+ const fall = -Z.vy[i];
2525
+ Z.y[i] = gy;
2526
+ Z.vy[i] = 0;
2527
+ if (st === STATE.AIRBORNE) {
2528
+ Z.state[i] = STATE.CHASE;
2529
+ if (tr.pounce && alive && d < tr.pounce.landRange && target) {
2530
+ target.knock(tr.pounce.knock);
2531
+ target.hurt(T(i).damage, i);
2532
+ emit("message", { id: "pounce_knock", seconds: 1.5, type: T(i).id });
2533
+ }
2534
+ } else if (fall > N.fallStun && Z.state[i] <= 1) {
2535
+ Z.state[i] = STATE.STUNNED;
2536
+ Z.stateTimer[i] = 0.9;
2537
+ }
2538
+ }
2539
+ } else if (gy > Z.y[i]) Z.y[i] = Math.min(gy, Z.y[i] + N.climbSpeed * dt);
2540
+ };
2541
+ const update = (dt, context = {}) => {
2542
+ if (context.target !== void 0) target = context.target;
2543
+ groanBudget -= dt;
2544
+ nightF = context.nightFactor ?? 0;
2545
+ const P = target;
2546
+ const px = P ? P.state.x : context.flowTarget?.[0] ?? 0;
2547
+ const pz = P ? P.state.z : context.flowTarget?.[1] ?? 0;
2548
+ const alive = !!P && P.state.alive && !P.state.turned;
2549
+ viewYaw = context.viewYaw ?? (P ? P.state.yaw - Math.PI : 0);
2550
+ const fx0 = context.flowTarget?.[0] ?? px;
2551
+ const fz0 = context.flowTarget?.[1] ?? pz;
2552
+ const pc = cellOf(fx0, fz0);
2553
+ if (flow.cell < 0) {
2554
+ flowStart(fx0, fz0);
2555
+ flowStep(1e9);
2556
+ flow.cell = pc;
2557
+ } else if (pc !== flow.cell && !flow.phase) {
2558
+ flowStart(fx0, fz0);
2559
+ flow.cell = pc;
2560
+ }
2561
+ flowStep(N.flowBudgetMs);
2562
+ hHead.fill(-1);
2563
+ for (let i = 0; i < Z.n; i++) {
2564
+ if (Z.band[i] !== 0) continue;
2565
+ const hx = Math.floor(Z.x[i] + HALF);
2566
+ const hz = Math.floor(Z.z[i] + HALF);
2567
+ if (hx < 0 || hz < 0 || hx >= HN || hz >= HN) continue;
2568
+ const h = hz * HN + hx;
2569
+ hNext[i] = hHead[h];
2570
+ hHead[h] = i;
2571
+ }
2572
+ const B = stats.bands;
2573
+ B[0] = B[1] = B[2] = B[3] = 0;
2574
+ let slots = 0;
2575
+ const F = flow.dir;
2576
+ for (let i = 0; i < Z.n; i++) {
2577
+ const dx = Z.x[i] - px;
2578
+ const dz = Z.z[i] - pz;
2579
+ const d = Math.sqrt(dx * dx + dz * dz);
2580
+ let b = Z.band[i];
2581
+ while (b > 0 && d < BANDS[b - 1].max - HYST) b--;
2582
+ while (b < 3 && d > BANDS[b].max + HYST) b++;
2583
+ Z.band[i] = b;
2584
+ B[b]++;
2585
+ const C = T(i);
2586
+ const tr = C.traits ?? {};
2587
+ if (Z.state[i] === STATE.STUNNED) {
2588
+ Z.stateTimer[i] -= dt;
2589
+ if (Z.stateTimer[i] <= 0) Z.state[i] = STATE.CHASE;
2590
+ Z.vx[i] *= 0.9;
2591
+ Z.vz[i] *= 0.9;
2592
+ }
2593
+ if (Z.state[i] === STATE.CALLING) {
2594
+ Z.stateTimer[i] -= dt;
2595
+ Z.vx[i] = Z.vz[i] = 0;
2596
+ if (Z.stateTimer[i] <= 0) {
2597
+ Z.state[i] = STATE.CHASE;
2598
+ screamAt(i, Z.x[i], Z.z[i]);
2599
+ Z.cooldown[i] = tr.caller.cooldown;
2600
+ }
2601
+ }
2602
+ Z.cooldown[i] -= dt;
2603
+ if (Z.alertIn[i] > 0) {
2604
+ Z.alertIn[i] -= dt;
2605
+ if (Z.alertIn[i] <= 0 && Z.state[i] === 0 && !tr.soundSeeker) {
2606
+ Z.state[i] = STATE.CHASE;
2607
+ Z.lost[i] = 0;
2608
+ }
2609
+ }
2610
+ Z.next[i] -= dt;
2611
+ if (Z.next[i] <= 0) {
2612
+ const tdt = BANDS[b].dt;
2613
+ Z.next[i] += tdt;
2614
+ if (alive && b <= 1 && Z.state[i] <= 1) {
2615
+ let sees = false;
2616
+ const A = pack.ages[Z.age[i]];
2617
+ const vis = C.perception.sight * (1 - N.nightSight * nightF) * (P.state.crouch ? N.crouchSight : 1) * A.sight;
2618
+ if (d < vis) {
2619
+ const fx = Math.sin(Z.yaw[i]);
2620
+ const fz = Math.cos(Z.yaw[i]);
2621
+ const cone = (-dx * fx - dz * fz) / (d || 1);
2622
+ 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;
2623
+ }
2624
+ if (C.perception.smell && d < C.perception.smell * (P.state.bleeding || P.state.scentTimer > 0 ? 2 : 1)) sees = true;
2625
+ if (sees) {
2626
+ if (Z.state[i] === 0) groan(i, d);
2627
+ Z.state[i] = STATE.CHASE;
2628
+ Z.lost[i] = 0;
2629
+ } else if (Z.state[i] === STATE.CHASE) {
2630
+ Z.lost[i] += tdt;
2631
+ if (Z.lost[i] > N.loseAfter) {
2632
+ Z.state[i] = 0;
2633
+ Z.wx[i] = px;
2634
+ Z.wz[i] = pz;
2635
+ }
2636
+ }
2637
+ }
2638
+ for (let k = 0; k < noises.length && (Z.state[i] === 0 || Z.state[i] === STATE.SEEKING_SOUND); k++) {
2639
+ const Nz = noises[k];
2640
+ const hear = Nz.r * (C.perception.hearing / N.hearingBase) * pack.ages[Z.age[i]].hearing;
2641
+ if ((Z.x[i] - Nz.x) ** 2 + (Z.z[i] - Nz.z) ** 2 < hear * hear) {
2642
+ if (tr.soundSeeker) {
2643
+ Z.state[i] = STATE.SEEKING_SOUND;
2644
+ Z.wx[i] = Nz.x;
2645
+ Z.wz[i] = Nz.z;
2646
+ break;
2647
+ }
2648
+ Z.state[i] = STATE.CHASE;
2649
+ Z.lost[i] = N.heardLost;
2650
+ groan(i, d);
2651
+ }
2652
+ }
2653
+ if (tr.caller && Z.state[i] === STATE.CHASE && d < tr.caller.range && Z.cooldown[i] <= 0) {
2654
+ Z.state[i] = STATE.CALLING;
2655
+ Z.stateTimer[i] = tr.caller.windUp;
2656
+ }
2657
+ Z.steering[i] = 0;
2658
+ if (!ability(i, d, dx, dz, tdt, b, alive)) {
2659
+ let tx = 0;
2660
+ let tz = 0;
2661
+ let sp = 0;
2662
+ if (Z.state[i] === STATE.CHASE || Z.state[i] === STATE.GRAB) {
2663
+ const c = cellOf(Z.x[i], Z.z[i]);
2664
+ const sameL = !P || Z.floor[i] === P.state.floor;
2665
+ if (d < N.directRange && alive && sameL) {
2666
+ tx = -dx / (d || 1);
2667
+ tz = -dz / (d || 1);
2668
+ } 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)) {
2669
+ tx = -dx / d;
2670
+ tz = -dz / d;
2671
+ } else if (c >= 0) {
2672
+ const W = flow.win;
2673
+ const cx = c % FW;
2674
+ const cz = c / FW | 0;
2675
+ if (cx >= W[0] && cx <= W[2] && cz >= W[1] && cz <= W[3]) {
2676
+ tx = F[c * 2];
2677
+ tz = F[c * 2 + 1];
2678
+ } else {
2679
+ tx = -dx;
2680
+ tz = -dz;
2681
+ }
2682
+ const l = Math.hypot(tx, tz) || 1;
2683
+ tx /= l;
2684
+ tz /= l;
2685
+ }
2686
+ if (tr.caller && Z.cooldown[i] > tr.caller.fleeAbove && d < tr.caller.fleeRange && alive) {
2687
+ tx = dx / (d || 1);
2688
+ tz = dz / (d || 1);
2689
+ }
2690
+ sp = chaseSpeed(i) * (P && P.state.screamBoost > 0 ? N.screamSpeed : 1);
2691
+ if (!alive) sp = Z.wanderSpeed[i];
2692
+ if (d < N.attackRange && alive && Math.abs(P.state.y - Z.y[i]) < N.attackHeight) {
2693
+ sp = 0;
2694
+ if (slots < N.attackSlots) {
2695
+ slots++;
2696
+ attack(i, d, tdt);
2697
+ }
2698
+ }
2699
+ } else if (Z.state[i] === 0) {
2700
+ const wx = Z.wx[i] - Z.x[i];
2701
+ const wz = Z.wz[i] - Z.z[i];
2702
+ const wl = Math.hypot(wx, wz);
2703
+ if (wl < 1 || R() < 0.01 * tdt * 60) {
2704
+ Z.wx[i] = clamp(Z.x[i] + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2);
2705
+ Z.wz[i] = clamp(Z.z[i] + (R() - 0.5) * N.wanderSpan, -HALF + 2, HALF - 2);
2706
+ } else {
2707
+ tx = wx / wl;
2708
+ tz = wz / wl;
2709
+ Z.pause[i] -= tdt;
2710
+ if (Z.pause[i] < -4 - R() * 6) Z.pause[i] = 1 + R() * 2.5;
2711
+ sp = Z.pause[i] > 0 ? 0 : Z.wanderSpeed[i];
2712
+ }
2713
+ }
2714
+ if (Z.crawl[i]) sp = Math.min(sp, N.crawlSpeed);
2715
+ else if (Z.limp[i]) sp *= N.limpSpeed;
2716
+ let sx = 0;
2717
+ let sz = 0;
2718
+ if (b === 0) {
2719
+ const hx = Math.floor(Z.x[i] + HALF);
2720
+ const hz = Math.floor(Z.z[i] + HALF);
2721
+ for (let oz = -1; oz <= 1; oz++)
2722
+ for (let ox = -1; ox <= 1; ox++) {
2723
+ const qx = hx + ox;
2724
+ const qz = hz + oz;
2725
+ if (qx < 0 || qz < 0 || qx >= HN || qz >= HN) continue;
2726
+ for (let j = hHead[qz * HN + qx]; j !== -1; j = hNext[j]) {
2727
+ if (j === i || Z.state[j] === STATE.PILE_STEP || Z.state[j] === STATE.DORMANT) continue;
2728
+ const ex = Z.x[i] - Z.x[j];
2729
+ const ez = Z.z[i] - Z.z[j];
2730
+ const e2 = ex * ex + ez * ez;
2731
+ const rr = N.separation * (Z.build[i] + Z.build[j]);
2732
+ if (e2 < rr * rr * 1.6 && e2 > 1e-6) {
2733
+ const e = Math.sqrt(e2);
2734
+ const w = (rr * 1.26 - e) / e;
2735
+ sx += ex * w;
2736
+ sz += ez * w;
2737
+ }
2738
+ }
2739
+ }
2740
+ }
2741
+ if (Z.state[i] <= 2) {
2742
+ Z.dvx[i] = tx * sp + sx * N.separationPush;
2743
+ Z.dvz[i] = tz * sp + sz * N.separationPush;
2744
+ Z.steering[i] = 1;
2745
+ if (sp > 0.05) Z.yaw[i] = Math.atan2(tx, tz);
2746
+ else if (d < 3 && alive) Z.yaw[i] = Math.atan2(-dx, -dz);
2747
+ }
2748
+ }
2749
+ }
2750
+ if (Z.steering[i] && Z.state[i] <= 2) {
2751
+ const k = 1 - Math.exp(-dt * (b === 0 ? 9 : 5));
2752
+ Z.vx[i] += (Z.dvx[i] - Z.vx[i]) * k;
2753
+ Z.vz[i] += (Z.dvz[i] - Z.vz[i]) * k;
2754
+ }
2755
+ const nx = Z.x[i] + Z.vx[i] * dt;
2756
+ const nz = Z.z[i] + Z.vz[i] * dt;
2757
+ let hitCell = -1;
2758
+ if (!blockedAt(i, nx, Z.z[i])) Z.x[i] = nx;
2759
+ else {
2760
+ if (Z.state[i] !== STATE.AIRBORNE) Z.vx[i] = 0;
2761
+ hitCell = cellOf(nx, Z.z[i]);
2762
+ }
2763
+ if (!blockedAt(i, Z.x[i], nz)) Z.z[i] = nz;
2764
+ else {
2765
+ if (Z.state[i] !== STATE.AIRBORNE) Z.vz[i] = 0;
2766
+ hitCell = cellOf(Z.x[i], nz);
2767
+ }
2768
+ if (hitCell >= 0 && Z.state[i] === STATE.CHASE && b === 0) {
2769
+ const vl = tr.vaultsLow;
2770
+ if (vl && occ[hitCell] !== 5 && occH[hitCell] <= vl.maxHeight && occH[hitCell] > Z.y[i] + vl.minRise) {
2771
+ Z.state[i] = STATE.AIRBORNE;
2772
+ Z.vy[i] = Math.sqrt(2 * N.gravity * (occH[hitCell] - Z.y[i] + vl.clearance));
2773
+ const s2 = Z.chaseSpeed[i] * vl.speedFactor;
2774
+ Z.vx[i] = Math.sin(Z.yaw[i]) * s2;
2775
+ Z.vz[i] = Math.cos(Z.yaw[i]) * s2;
2776
+ } else if (occ[hitCell] === 2 || occ[hitCell] === 0 && pile[hitCell] > 0) {
2777
+ Z.stuck[i] += dt;
2778
+ if (Z.stuck[i] > 0.35 + 0.5 * Z.skin[i]) {
2779
+ Z.stuck[i] = 0;
2780
+ tryStep(i, hitCell);
2781
+ }
2782
+ }
2783
+ } else if (Z.stuck[i] > 0) Z.stuck[i] -= dt;
2784
+ if (hitCell >= 0 && Z.state[i] === STATE.CHASE && doors.at(hitCell) >= 0) {
2785
+ Z.attackTimer[i] -= dt;
2786
+ if (Z.attackTimer[i] <= 0) {
2787
+ Z.attackTimer[i] = N.attackEvery;
2788
+ doors.hit(doors.at(hitCell), N.doorDamage * (tr.doorDamage ?? 1));
2789
+ }
2790
+ }
2791
+ frame(i, dt, d, alive);
2792
+ const spd = Math.hypot(Z.vx[i], Z.vz[i]);
2793
+ Z.renderSpeed[i] += (spd - Z.renderSpeed[i]) * Math.min(1, dt * 6);
2794
+ if (Z.band[i] <= 1) {
2795
+ const s = Z.state[i];
2796
+ let goal = s === STATE.CHASE || s === STATE.GRAB || s === STATE.PILE_STEP ? 1 : 0;
2797
+ 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;
2798
+ Z.arms[i] += (goal - Z.arms[i]) * Math.min(1, dt * (goal > Z.arms[i] ? 3 : 5));
2799
+ }
2800
+ Z.phase[i] += dt * (Z.renderSpeed[i] < 0.05 ? 0.15 : N.cadenceBase + N.cadencePerSpeed * Z.renderSpeed[i]);
2801
+ if (spd > 0.35 && Z.state[i] <= 1 && !Z.attackPose[i]) Z.yaw[i] = Math.atan2(Z.vx[i], Z.vz[i]);
2802
+ let dy = Z.yaw[i] - Z.renderYaw[i];
2803
+ dy -= Math.round(dy / TAU_APPROX) * TAU_APPROX;
2804
+ Z.renderYaw[i] += dy * Math.min(1, dt * (Z.state[i] === STATE.AIRBORNE ? 20 : 9));
2805
+ }
2806
+ killQFlush();
2807
+ noises.length = 0;
2808
+ stats.slots = slots;
2809
+ };
2810
+ const updateTrail = (dt, who = target) => {
2811
+ for (const p of trail) p.t += dt;
2812
+ while (trail.length && trail[0].t > N.trailSeconds) trail.shift();
2813
+ if (!who) return;
2814
+ const s = who.state;
2815
+ if ((s.bleeding || s.scentTimer > 0) && s.alive && Math.hypot(s.x - trailLast.x, s.z - trailLast.z) > N.trailStep) {
2816
+ trail.push({ x: s.x, z: s.z, t: 0 });
2817
+ trailLast = { x: s.x, z: s.z };
2818
+ if (trail.length > N.trailMax) trail.shift();
2819
+ }
2820
+ };
2821
+ const spawnAway = (minD, maxD, view) => {
2822
+ const ox = target ? target.state.x : view.x;
2823
+ const oz = target ? target.state.z : view.z;
2824
+ for (let t = 0; t < 30; t++) {
2825
+ const a = R() * TURN;
2826
+ const r = minD + Math.sqrt(R()) * (maxD - minD);
2827
+ const x = ox + Math.cos(a) * r;
2828
+ const z = oz + Math.sin(a) * r;
2829
+ const vx = x - view.x;
2830
+ const vz = z - view.z;
2831
+ const vd = Math.hypot(vx, vz);
2832
+ if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > vd * 0.5) continue;
2833
+ if (Math.abs(x) < HALF - 3 && Math.abs(z) < HALF - 3 && !grid.blocked(x, z)) return spawn(x, z);
2834
+ }
2835
+ return -1;
2836
+ };
2837
+ const updatePopulation = (dt, view, opts = {}) => {
2838
+ const popTarget = opts.target ?? N.population;
2839
+ let k = 0;
2840
+ while (Z.n < popTarget && k++ < 20) spawnAway(N.refillMin, N.refillMax, view);
2841
+ if (Z.n > popTarget + 120)
2842
+ for (let t = 0, rm = 0; t < 40 && rm < 4; t++) {
2843
+ const i = Math.floor(R() * Z.n);
2844
+ if (Z.band[i] < 2 || Z.named[i] >= 0 || target && i === target.state.grabbedBy) continue;
2845
+ const vx = Z.x[i] - view.x;
2846
+ const vz = Z.z[i] - view.z;
2847
+ const vd = Math.hypot(vx, vz);
2848
+ if (vd < view.far && vx * -Math.sin(view.yaw) + vz * -Math.cos(view.yaw) > -vd * 0.35) continue;
2849
+ remove(i);
2850
+ rm++;
2851
+ }
2852
+ const mg = opts.migration === void 0 ? null : opts.migration;
2853
+ if (!mg) return;
2854
+ migrateT -= dt;
2855
+ if (migrateT <= 0) {
2856
+ migrateT = mg.every;
2857
+ let m = 0;
2858
+ for (let q = 0; q < mg.count && Z.n < Z.capacity; q++) {
2859
+ const i = spawn(mg.x + (R() - 0.5) * mg.width, mg.z + R() * mg.depth);
2860
+ if (i >= 0) {
2861
+ Z.wx[i] = mg.goal[0];
2862
+ Z.wz[i] = mg.goal[1];
2863
+ m++;
2864
+ }
2865
+ }
2866
+ if (m) emit("message", { id: "migration", seconds: 4 });
2867
+ }
2868
+ };
2869
+ const crowd = {
2870
+ store: Z,
2871
+ pack,
2872
+ grid,
2873
+ doors,
2874
+ corpses,
2875
+ numbers: N,
2876
+ stats,
2877
+ pile,
2878
+ pileCount: () => pileN,
2879
+ trail,
2880
+ variantsOf,
2881
+ get target() {
2882
+ return target;
2883
+ },
2884
+ set target(next) {
2885
+ target = next;
2886
+ },
2887
+ random,
2888
+ sound,
2889
+ effect,
2890
+ typeIndex: (id) => typeIndexById.get(id) ?? -1,
2891
+ spawn: (x, z, type = -1) => spawn(x, z, type),
2892
+ remove,
2893
+ update,
2894
+ emitNoise,
2895
+ damage,
2896
+ kill: (i, headshot = false) => kill(i, headshot),
2897
+ updateTrail,
2898
+ updateCorpses: (dt) => void corpses.update(dt),
2899
+ updatePopulation,
2900
+ spawnAway,
2901
+ pileBlast,
2902
+ setHunt(next) {
2903
+ hunt = next;
2904
+ },
2905
+ chaseSpeed,
2906
+ asPlayerCrowd: () => ({
2907
+ count: () => Z.n,
2908
+ active: (i) => Z.band[i] === 0,
2909
+ x: (i) => Z.x[i],
2910
+ z: (i) => Z.z[i],
2911
+ y: (i) => Z.y[i],
2912
+ floor: (i) => Z.floor[i],
2913
+ chasing: (i) => Z.state[i] === STATE.CHASE,
2914
+ down: (i) => Z.crawl[i] !== 0 || Z.state[i] === STATE.STUNNED,
2915
+ panicWeight: (i) => traits(i).panicWeight ?? 1,
2916
+ hold(i, x, z, k, lookX, lookZ) {
2917
+ Z.x[i] += (x - Z.x[i]) * k;
2918
+ Z.z[i] += (z - Z.z[i]) * k;
2919
+ Z.vx[i] = Z.vz[i] = Z.dvx[i] = Z.dvz[i] = 0;
2920
+ Z.yaw[i] = Math.atan2(lookX - Z.x[i], lookZ - Z.z[i]);
2921
+ },
2922
+ stun(i, seconds, vx, vz) {
2923
+ Z.state[i] = STATE.STUNNED;
2924
+ Z.stateTimer[i] = seconds;
2925
+ Z.vx[i] = vx;
2926
+ Z.vz[i] = vz;
2927
+ },
2928
+ chase(i, cooldown) {
2929
+ Z.state[i] = STATE.CHASE;
2930
+ Z.attackTimer[i] = cooldown;
2931
+ },
2932
+ damage: (i, hit) => void damage(i, { region: hit.region, amount: hit.amount, distance: hit.distance, cut: hit.cut, caliber: null })
2933
+ }),
2934
+ on(event, listener) {
2935
+ let set = listeners.get(event);
2936
+ if (!set) listeners.set(event, set = /* @__PURE__ */ new Set());
2937
+ set.add(listener);
2938
+ return () => void set.delete(listener);
2939
+ }
2940
+ };
2941
+ return crowd;
2942
+ }
2943
+
2944
+ // src/behaviours/civilians.ts
2945
+ var CIVILIAN_DEFAULTS = {
2946
+ hpAdult: 100,
2947
+ hpChild: 40,
2948
+ childOffset: 0.6,
2949
+ sightSquared: 100,
2950
+ leaveDistance: 18,
2951
+ attackRange: 1,
2952
+ attackEvery: 1.5,
2953
+ biteFactor: 1.5,
2954
+ biteSeconds: 120,
2955
+ fearAfterHit: 10,
2956
+ nearMessage: 30,
2957
+ fearRange: 6,
2958
+ fearSpeed: 3.4,
2959
+ walkSpeed: 1.3,
2960
+ childSpeed: 0.85,
2961
+ bittenSpeed: 0.8,
2962
+ groupRunBeyond: 6,
2963
+ groupRunSpeed: 3.6,
2964
+ fleeSpeed: 2.6,
2965
+ childFleeFactor: 1.1,
2966
+ stuckSeconds: 8,
2967
+ arrive: 0.25,
2968
+ probe: 0.45,
2969
+ whiskers: [0, 0.7, -0.7, 1.4, -1.4],
2970
+ doorProbe: 0.6,
2971
+ steer: 8,
2972
+ bodyHeight: 0.4,
2973
+ patrolChance: 0.2,
2974
+ childHeight: 0.65,
2975
+ childBuild: 0.7,
2976
+ cadenceBase: 0.5,
2977
+ cadencePerSpeed: 0.3,
2978
+ inside: 0.8,
2979
+ doorInside: 0.8,
2980
+ doorOutside: 0.9,
2981
+ gate: 7.6,
2982
+ sidewalk: 6,
2983
+ houseOffset: 0.8
2984
+ };
2985
+ function createCivilians(crowd, options) {
2986
+ const N = { ...CIVILIAN_DEFAULTS, ...options.numbers };
2987
+ const Z = crowd.store;
2988
+ const grid = crowd.grid;
2989
+ const doors = crowd.doors;
2990
+ const hourNow = options.hour ?? (() => 12);
2991
+ const turnInto = options.turnInto ?? 0;
2992
+ const listeners = /* @__PURE__ */ new Map();
2993
+ const emit = (event, payload) => {
2994
+ for (const listener of listeners.get(event) ?? []) listener(payload);
2995
+ };
2996
+ const random = crowd.random;
2997
+ const R = random;
2998
+ const lerp = (a, b, t) => a + (b - a) * t;
2999
+ const casaDe = (name) => options.buildings[name];
3000
+ const pontos = options.waypoints ?? ((b) => {
3001
+ const casa = !!b.house;
3002
+ const side = b.front;
3003
+ const front = side < 0 ? b.z + b.d : b.z;
3004
+ const back = side < 0 ? b.z : b.z + b.d;
3005
+ const cx = b.x + b.w / 2;
3006
+ const inw = side < 0 ? -1 : 1;
3007
+ const calc = side < 0 ? -N.sidewalk : N.sidewalk;
3008
+ const pts = [
3009
+ [cx + (casa ? N.houseOffset : 0), (front + (front + back) / 2) / 2],
3010
+ [cx, front + inw * N.doorInside],
3011
+ [cx, front - inw * N.doorOutside]
3012
+ ];
3013
+ if (casa) pts.push([cx, side < 0 ? -N.gate : N.gate]);
3014
+ pts.push([cx, calc]);
3015
+ return pts;
3016
+ });
3017
+ const rota = (bA, bB) => {
3018
+ const a = pontos(bA);
3019
+ const b = pontos(bB).reverse();
3020
+ const out = a.slice();
3021
+ if (Math.sign(a[a.length - 1][1]) !== Math.sign(b[0][1])) out.push([a[a.length - 1][0], b[0][1]]);
3022
+ return out.concat(b);
3023
+ };
3024
+ const list = options.people.map((spec, k) => {
3025
+ const b = casaDe(spec.home);
3026
+ const p = pontos(b)[0];
3027
+ return {
3028
+ id: spec.id,
3029
+ spec,
3030
+ k,
3031
+ x: p[0] + (spec.child ? N.childOffset : 0),
3032
+ z: p[1],
3033
+ yaw: 0,
3034
+ phase: 0,
3035
+ vx: 0,
3036
+ vz: 0,
3037
+ hp: spec.child ? N.hpChild : N.hpAdult,
3038
+ state: "home",
3039
+ path: [],
3040
+ pathIndex: 0,
3041
+ biteTimer: 0,
3042
+ attackTimer: 0,
3043
+ fear: 0,
3044
+ goal: spec.home,
3045
+ stuckTimer: 0,
3046
+ agent: -1
3047
+ };
3048
+ });
3049
+ const byId = new Map(list.map((c) => [c.id, c]));
3050
+ const exposed = (n) => n.state !== "dead" && n.state !== "undead" && n.state !== "gone";
3051
+ const target = () => crowd.target?.state ?? null;
3052
+ const distToTarget = (x, z) => {
3053
+ const t = target();
3054
+ return t ? Math.hypot(x - t.x, z - t.z) : 0;
3055
+ };
3056
+ const turn = (n) => {
3057
+ n.state = "undead";
3058
+ const i = crowd.spawn(n.x, n.z, turnInto);
3059
+ if (i >= 0) {
3060
+ Z.color[i] = n.spec.color ?? Z.color[i];
3061
+ Z.named[i] = n.k;
3062
+ Z.height[i] = n.spec.child ? N.childHeight : Z.height[i];
3063
+ Z.build[i] = n.spec.child ? N.childBuild : Z.build[i];
3064
+ Z.age[i] = 0;
3065
+ Z.state[i] = STATE.CHASE;
3066
+ n.agent = i;
3067
+ }
3068
+ emit("turned", { id: n.id, agent: i });
3069
+ };
3070
+ const die = (n) => {
3071
+ if (n.biteTimer > 0) return turn(n);
3072
+ n.state = "dead";
3073
+ n.vx = n.vz = 0;
3074
+ crowd.corpses.add({
3075
+ x: n.x,
3076
+ z: n.z,
3077
+ yaw: n.yaw,
3078
+ height: n.spec.child ? N.childHeight : 1,
3079
+ build: n.spec.child ? N.childBuild : 1,
3080
+ missing: 0,
3081
+ color: n.spec.color ?? 0,
3082
+ variant: -1,
3083
+ skin: 0.4,
3084
+ seed: R() * 10,
3085
+ named: n.k
3086
+ });
3087
+ crowd.effect({ effect: "blood", point: [n.x, 1, n.z], age: 0, amount: 300, died: true });
3088
+ emit("died", { id: n.id, cause: "attacked" });
3089
+ };
3090
+ const agentsAgainst = (dt) => {
3091
+ const t = target();
3092
+ for (let i = 0; i < Z.n; i++) {
3093
+ if (Z.band[i] > 1) continue;
3094
+ const k = Z.hunts[i];
3095
+ if (k >= 0) {
3096
+ const n = list[k];
3097
+ if (!exposed(n)) {
3098
+ Z.hunts[i] = -1;
3099
+ continue;
3100
+ }
3101
+ const dx = n.x - Z.x[i];
3102
+ const dz = n.z - Z.z[i];
3103
+ const d = Math.hypot(dx, dz);
3104
+ if (d > N.leaveDistance) {
3105
+ Z.hunts[i] = -1;
3106
+ continue;
3107
+ }
3108
+ if (d < N.attackRange) {
3109
+ Z.attackTimer[i] -= dt;
3110
+ if (Z.attackTimer[i] <= 0) {
3111
+ Z.attackTimer[i] = N.attackEvery;
3112
+ const C = crowd.pack.types[Z.type[i]];
3113
+ n.hp -= C.damage;
3114
+ n.fear = N.fearAfterHit;
3115
+ crowd.sound("flesh", 0.7, n.x, 1, n.z, distToTarget(n.x, n.z));
3116
+ crowd.effect({ effect: "blood", point: [n.x, 1.2, n.z], age: 0, amount: 30, died: false });
3117
+ if (n.biteTimer <= 0 && R() < C.bite * N.biteFactor) {
3118
+ n.biteTimer = N.biteSeconds;
3119
+ emit("bitten", { id: n.id, near: distToTarget(n.x, n.z) < N.nearMessage });
3120
+ }
3121
+ if (n.hp <= 0) die(n);
3122
+ }
3123
+ }
3124
+ } else if (Z.state[i] <= 1) {
3125
+ for (const n of list) {
3126
+ if (!exposed(n) || n.state === "home") continue;
3127
+ const dx = n.x - Z.x[i];
3128
+ const dz = n.z - Z.z[i];
3129
+ const d2 = dx * dx + dz * dz;
3130
+ const dp = t ? (t.x - Z.x[i]) ** 2 + (t.z - Z.z[i]) ** 2 : Infinity;
3131
+ if (d2 < N.sightSquared && d2 < dp && grid.losClear(Z.x[i], Z.z[i], n.x, n.z)) {
3132
+ Z.hunts[i] = n.k;
3133
+ Z.state[i] = STATE.CHASE;
3134
+ break;
3135
+ }
3136
+ }
3137
+ }
3138
+ }
3139
+ };
3140
+ crowd.setHunt((i) => {
3141
+ const n = list[Z.hunts[i]];
3142
+ if (!n) return false;
3143
+ const dx = n.x - Z.x[i];
3144
+ const dz = n.z - Z.z[i];
3145
+ const d = Math.hypot(dx, dz) || 1;
3146
+ const sp = d < 1 ? 0 : crowd.chaseSpeed(i);
3147
+ Z.vx[i] = dx / d * sp;
3148
+ Z.vz[i] = dz / d * sp;
3149
+ Z.yaw[i] = Math.atan2(dx, dz);
3150
+ return true;
3151
+ });
3152
+ const move = (n, dt, tx, tz, sp) => {
3153
+ const dx = tx - n.x;
3154
+ const dz = tz - n.z;
3155
+ const d = Math.hypot(dx, dz);
3156
+ if (d < N.arrive) {
3157
+ n.vx = n.vz = 0;
3158
+ return true;
3159
+ }
3160
+ let ax = dx / d;
3161
+ let az = dz / d;
3162
+ for (const a of N.whiskers) {
3163
+ const c2 = Math.cos(a);
3164
+ const s = Math.sin(a);
3165
+ const bx = ax * c2 - az * s;
3166
+ const bz = ax * s + az * c2;
3167
+ if (!grid.solidAt(n.x + bx * N.probe, n.z + bz * N.probe, N.bodyHeight)) {
3168
+ ax = bx;
3169
+ az = bz;
3170
+ break;
3171
+ }
3172
+ }
3173
+ const c = grid.cellOf(n.x + ax * N.doorProbe, n.z + az * N.doorProbe);
3174
+ const di = doors.at(c);
3175
+ if (c >= 0 && di >= 0) {
3176
+ const D = doors.list[di];
3177
+ if (!D.open && !D.broken && !D.planks) {
3178
+ doors.set(di, true);
3179
+ crowd.sound("wood", 0.3, D.x, 1, D.z, distToTarget(D.x, D.z));
3180
+ }
3181
+ }
3182
+ n.vx = lerp(n.vx, ax * sp, Math.min(1, dt * N.steer));
3183
+ n.vz = lerp(n.vz, az * sp, Math.min(1, dt * N.steer));
3184
+ const nx = n.x + n.vx * dt;
3185
+ const nz = n.z + n.vz * dt;
3186
+ if (!grid.solidAt(nx, n.z, N.bodyHeight)) n.x = nx;
3187
+ if (!grid.solidAt(n.x, nz, N.bodyHeight)) n.z = nz;
3188
+ n.yaw = Math.atan2(n.vx, n.vz);
3189
+ return false;
3190
+ };
3191
+ const update = (dt) => {
3192
+ agentsAgainst(dt);
3193
+ const h = hourNow();
3194
+ const t = target();
3195
+ for (const n of list) {
3196
+ if (n.state === "dead" || n.state === "undead" || n.state === "gone") continue;
3197
+ if (n.biteTimer > 0) {
3198
+ n.biteTimer -= dt;
3199
+ if (n.biteTimer <= 0) {
3200
+ turn(n);
3201
+ continue;
3202
+ }
3203
+ }
3204
+ let perto = 99;
3205
+ for (let i = 0; i < Z.n; i++) {
3206
+ if (Z.band[i]) continue;
3207
+ const d = Math.hypot(Z.x[i] - n.x, Z.z[i] - n.z);
3208
+ if (d < perto) perto = d;
3209
+ }
3210
+ const sp = (perto < N.fearRange ? N.fearSpeed : N.walkSpeed) * (n.spec.child ? N.childSpeed : 1) * (n.biteTimer > 0 ? N.bittenSpeed : 1);
3211
+ if (n.state === "group") {
3212
+ const leaderCivilian = n.spec.leader ? byId.get(n.spec.leader) : void 0;
3213
+ const lider = leaderCivilian && leaderCivilian.state === "group" ? leaderCivilian : t;
3214
+ if (lider) {
3215
+ const dd = Math.hypot(lider.x - n.x, lider.z - n.z);
3216
+ if (dd > (n.spec.followDistance ?? 2.2)) move(n, dt, lider.x, lider.z, dd > N.groupRunBeyond ? N.groupRunSpeed : sp);
3217
+ else {
3218
+ n.vx *= 0.8;
3219
+ n.vz *= 0.8;
3220
+ }
3221
+ }
3222
+ } else if (n.state === "fleeing") {
3223
+ const p = n.path[n.pathIndex];
3224
+ if (!p) {
3225
+ n.state = "gone";
3226
+ emit("gone", { id: n.id });
3227
+ continue;
3228
+ }
3229
+ n.stuckTimer += dt;
3230
+ if (move(n, dt, p[0], p[1], N.fleeSpeed * (n.spec.child ? N.childFleeFactor : 1)) || n.stuckTimer > N.stuckSeconds) {
3231
+ n.pathIndex++;
3232
+ n.stuckTimer = 0;
3233
+ }
3234
+ } else if (n.spec.routine) {
3235
+ let alvo = n.spec.home;
3236
+ for (const [hh, onde] of n.spec.routine) if (h >= hh && h < hh + 1) alvo = onde;
3237
+ if (perto < N.fearRange) alvo = n.spec.home;
3238
+ if (alvo !== n.goal) {
3239
+ const home = casaDe(n.spec.home);
3240
+ n.path = rota(casaDe(n.goal) ?? home, casaDe(alvo) ?? home);
3241
+ n.pathIndex = 0;
3242
+ n.goal = alvo;
3243
+ n.state = "walking";
3244
+ }
3245
+ if (n.state === "walking") {
3246
+ const p = n.path[n.pathIndex];
3247
+ n.stuckTimer += dt;
3248
+ if (!p) n.state = "home";
3249
+ else if (move(n, dt, p[0], p[1], sp) || n.stuckTimer > N.stuckSeconds) {
3250
+ n.pathIndex++;
3251
+ n.stuckTimer = 0;
3252
+ }
3253
+ } else {
3254
+ n.vx *= 0.8;
3255
+ n.vz *= 0.8;
3256
+ }
3257
+ } else {
3258
+ n.vx *= 0.8;
3259
+ n.vz *= 0.8;
3260
+ }
3261
+ {
3262
+ const v = Math.hypot(n.vx, n.vz);
3263
+ n.phase += dt * (v < 0.05 ? 0 : N.cadenceBase + N.cadencePerSpeed * v);
3264
+ }
3265
+ }
3266
+ for (let i = 0; i < Z.n; i++)
3267
+ if (Z.named[i] >= 0 && Z.state[i] === STATE.WANDER && R() < dt * N.patrolChance) {
3268
+ const n = list[Z.named[i]];
3269
+ if (!n) continue;
3270
+ const p = pontos(casaDe(n.spec.home));
3271
+ const q = p[Math.floor(R() * p.length)];
3272
+ Z.wx[i] = q[0];
3273
+ Z.wz[i] = q[1];
3274
+ }
3275
+ };
3276
+ return {
3277
+ list,
3278
+ get: (id) => byId.get(id),
3279
+ update,
3280
+ join(id) {
3281
+ const n = byId.get(id);
3282
+ if (n && exposed(n)) n.state = "group";
3283
+ },
3284
+ flee(id, path) {
3285
+ const n = byId.get(id);
3286
+ if (!n || !exposed(n)) return;
3287
+ n.state = "fleeing";
3288
+ n.path = path.map(([x, z]) => [x, z]);
3289
+ n.pathIndex = 0;
3290
+ },
3291
+ route(from, to) {
3292
+ return rota(casaDe(from), casaDe(to));
3293
+ },
3294
+ on(event, listener) {
3295
+ let set = listeners.get(event);
3296
+ if (!set) listeners.set(event, set = /* @__PURE__ */ new Set());
3297
+ set.add(listener);
3298
+ return () => void set.delete(listener);
3299
+ }
3300
+ };
3301
+ }
3302
+
3303
+ // src/behaviours/herd.ts
3304
+ var HERD_DEFAULTS = {
3305
+ range: 8,
3306
+ fear: 5,
3307
+ walkFactor: 1.05,
3308
+ walkBase: 0.25,
3309
+ runFactor: 2.2,
3310
+ runBase: 0.8,
3311
+ activeDistance: 70,
3312
+ companionBehind: 2.2,
3313
+ companionRun: 6,
3314
+ companionStop: 0.8,
3315
+ retargetTries: 6,
3316
+ restBase: 4,
3317
+ restRandom: 7,
3318
+ lookEvery: 7,
3319
+ lookShare: 0.25,
3320
+ lookRadius: 40,
3321
+ fearAgentFactor: 1.5,
3322
+ fearAgentBase: 2,
3323
+ fleeStep: 6,
3324
+ arrive: 0.3,
3325
+ bodyHeight: 0.3,
3326
+ turn: 6,
3327
+ freeHeight: 0.6,
3328
+ worldMargin: 4
3329
+ };
3330
+ var HERD_ROLES = {
3331
+ dog: { range: 6, fear: 0, guard: { bark: 12, barkNoise: 60, barkCooldown: 3, growl: 40, growlCooldown: 10, sound: "bark" } },
3332
+ cat: { range: 6, fear: 5 },
3333
+ chicken: { range: 5, fear: 4, fleeCall: { sound: "cluck", noise: 30, cooldown: 6 } },
3334
+ southern_lapwing: { range: 5, fear: 0, alarm: { range: 12, sound: "lapwing", noise: 40, cooldown: 8, fly: 4 } },
3335
+ cow: { range: 10, fear: 6, call: { chance: 0.02, sound: "moo", noise: 80, cooldown: 30 } },
3336
+ horse: { range: 10, fear: 7 },
3337
+ pig: { range: 5, fear: 3 },
3338
+ capybara: { range: 8, fear: 15 },
3339
+ vulture: { sky: true }
3340
+ };
3341
+ function createHerd(crowd, options = {}) {
3342
+ const N = { ...HERD_DEFAULTS, ...options.numbers };
3343
+ const R = crowd.random;
3344
+ const Z = crowd.store;
3345
+ const grid = crowd.grid;
3346
+ const list = [];
3347
+ let time = 0;
3348
+ const listeners = /* @__PURE__ */ new Map();
3349
+ const emit = (event, payload) => {
3350
+ for (const listener of listeners.get(event) ?? []) listener(payload);
3351
+ };
3352
+ const view = options.view ?? (() => crowd.target?.state ?? { x: 0, z: 0 });
3353
+ const free = (x0, z0, r0, r1, tries = 40) => {
3354
+ for (let k = 0; k < tries; k++) {
3355
+ const a = R() * 6.28;
3356
+ const r = r0 + R() * (r1 - r0);
3357
+ const x = x0 + Math.cos(a) * r;
3358
+ const z = z0 + Math.sin(a) * r;
3359
+ 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];
3360
+ }
3361
+ return null;
3362
+ };
3363
+ const nearestAgent = (x, z, r) => {
3364
+ let best = -1;
3365
+ let bd = r * r;
3366
+ for (let i = 0; i < Z.n; i++) {
3367
+ if (Z.state[i] === STATE.DORMANT) continue;
3368
+ const dx = Z.x[i] - x;
3369
+ const dz = Z.z[i] - z;
3370
+ const d = dx * dx + dz * dz;
3371
+ if (d < bd) {
3372
+ bd = d;
3373
+ best = i;
3374
+ }
3375
+ }
3376
+ return best < 0 ? null : [best, Math.sqrt(bd)];
3377
+ };
3378
+ const call = (E, sound, noise, dc) => {
3379
+ crowd.emitNoise(E.x, E.z, noise);
3380
+ emit("noise", { x: E.x, z: E.z, radius: noise, animal: E.index });
3381
+ emit("sound", { sound, volume: 1, position: [E.x, 0.5, E.z], distance: dc, animal: E.index });
3382
+ };
3383
+ const update = (dt) => {
3384
+ time += dt;
3385
+ const t = crowd.target?.state;
3386
+ const px = t?.x ?? 0;
3387
+ const pz = t?.z ?? 0;
3388
+ const v = view();
3389
+ for (const E of list) {
3390
+ if (E.spec.sky) continue;
3391
+ const dc = Math.hypot(E.x - v.x, E.z - v.z);
3392
+ if (dc >= N.activeDistance || E.dead) continue;
3393
+ E.timer -= dt;
3394
+ E.cooldown -= dt;
3395
+ let alvoX = E.targetX;
3396
+ let alvoZ = E.targetZ;
3397
+ let corre = false;
3398
+ const dp = t ? Math.hypot(E.x - px, E.z - pz) : Infinity;
3399
+ if (E.companion && t) {
3400
+ const bx = px - Math.sin(t.yaw) * N.companionBehind;
3401
+ const bz = pz - Math.cos(t.yaw) * N.companionBehind;
3402
+ alvoX = bx;
3403
+ alvoZ = bz;
3404
+ corre = dp > N.companionRun;
3405
+ if (Math.hypot(bx - E.x, bz - E.z) < N.companionStop) {
3406
+ alvoX = E.x;
3407
+ alvoZ = E.z;
3408
+ }
3409
+ } else if (E.timer <= 0) {
3410
+ const q = free(E.homeX, E.homeZ, 0, E.range, N.retargetTries);
3411
+ if (q) {
3412
+ E.targetX = q[0];
3413
+ E.targetZ = q[1];
3414
+ }
3415
+ E.timer = N.restBase + R() * N.restRandom;
3416
+ alvoX = E.targetX;
3417
+ alvoZ = E.targetZ;
3418
+ }
3419
+ 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;
3420
+ let fx = 0;
3421
+ let fz = 0;
3422
+ if (E.fear && !E.companion && dp < E.fear) {
3423
+ fx += E.x - px;
3424
+ fz += E.z - pz;
3425
+ }
3426
+ if (E.fear && zp && zp[1] < E.fear * N.fearAgentFactor + N.fearAgentBase) {
3427
+ fx += E.x - Z.x[zp[0]];
3428
+ fz += E.z - Z.z[zp[0]];
3429
+ }
3430
+ if (fx || fz) {
3431
+ const l = Math.hypot(fx, fz) || 1;
3432
+ alvoX = E.x + fx / l * N.fleeStep;
3433
+ alvoZ = E.z + fz / l * N.fleeStep;
3434
+ corre = true;
3435
+ const fc = E.spec.fleeCall;
3436
+ if (fc && E.cooldown <= 0) {
3437
+ E.cooldown = fc.cooldown;
3438
+ call(E, fc.sound, fc.noise, dc);
3439
+ }
3440
+ }
3441
+ const al = E.spec.alarm;
3442
+ if (al && dp < al.range && E.cooldown <= 0) {
3443
+ E.cooldown = al.cooldown;
3444
+ E.fly = al.fly;
3445
+ call(E, al.sound, al.noise, dc);
3446
+ emit("message", { id: "alarm", seconds: 2.5, animal: E.index });
3447
+ }
3448
+ const cl = E.spec.call;
3449
+ if (cl && E.cooldown <= 0 && R() < dt * cl.chance) {
3450
+ E.cooldown = cl.cooldown;
3451
+ call(E, cl.sound, cl.noise, dc);
3452
+ }
3453
+ const g = E.spec.guard;
3454
+ if (g && zp && E.cooldown <= 0) {
3455
+ if (zp[1] < g.bark) {
3456
+ E.cooldown = g.barkCooldown;
3457
+ call(E, g.sound, g.barkNoise, dc);
3458
+ if (E.companion) emit("message", { id: "bark", seconds: 1.5, animal: E.index, distance: Math.round(zp[1]) });
3459
+ } else if (E.companion && zp[1] < g.growl) {
3460
+ E.cooldown = g.growlCooldown;
3461
+ emit("message", { id: "growl", seconds: 2.5, animal: E.index, distance: Math.round(zp[1]) });
3462
+ }
3463
+ }
3464
+ const ax = alvoX - E.x;
3465
+ const az = alvoZ - E.z;
3466
+ const ad = Math.hypot(ax, az);
3467
+ let sp = 0;
3468
+ if (E.fly > 0) {
3469
+ E.fly -= dt;
3470
+ E.clip = "run";
3471
+ } else if (ad > N.arrive) {
3472
+ sp = corre ? E.runSpeed : E.walkSpeed;
3473
+ E.clip = corre ? "run" : "walk";
3474
+ } else E.clip = "idle";
3475
+ if (sp) {
3476
+ const k = Math.min(1, sp * dt / ad);
3477
+ const nx = E.x + ax * k;
3478
+ const nz = E.z + az * k;
3479
+ if (!grid.solidAt(nx, nz, N.bodyHeight)) {
3480
+ E.x = nx;
3481
+ E.z = nz;
3482
+ } else E.timer = 0;
3483
+ let dy = Math.atan2(ax, az) - E.yaw;
3484
+ dy -= Math.round(dy / 6.2832) * 6.2832;
3485
+ E.yaw += dy * Math.min(1, dt * N.turn);
3486
+ }
3487
+ }
3488
+ };
3489
+ return {
3490
+ list,
3491
+ add(spec) {
3492
+ const E = {
3493
+ index: list.length,
3494
+ spec,
3495
+ x: spec.x,
3496
+ z: spec.z,
3497
+ yaw: R() * 6.28,
3498
+ homeX: spec.x,
3499
+ homeZ: spec.z,
3500
+ range: spec.range ?? N.range,
3501
+ clip: "idle",
3502
+ timer: R() * 4,
3503
+ targetX: spec.x,
3504
+ targetZ: spec.z,
3505
+ fear: spec.fear ?? N.fear,
3506
+ fly: 0,
3507
+ cooldown: 0,
3508
+ walkSpeed: spec.walkHz * spec.size * N.walkFactor + N.walkBase,
3509
+ runSpeed: spec.runHz * spec.size * N.runFactor + N.runBase,
3510
+ companion: !!spec.companion,
3511
+ dead: !!spec.dead,
3512
+ near: void 0
3513
+ };
3514
+ list.push(E);
3515
+ return E;
3516
+ },
3517
+ free,
3518
+ update,
3519
+ get time() {
3520
+ return time;
3521
+ },
3522
+ setCompanion(index, on) {
3523
+ const E = list[index];
3524
+ if (!E) return;
3525
+ E.companion = on;
3526
+ E.homeX = E.x;
3527
+ E.homeZ = E.z;
3528
+ },
3529
+ on(event, listener) {
3530
+ let set = listeners.get(event);
3531
+ if (!set) listeners.set(event, set = /* @__PURE__ */ new Set());
3532
+ set.add(listener);
3533
+ return () => void set.delete(listener);
3534
+ }
3535
+ };
3536
+ }
3537
+
3538
+ // src/behaviours/flock.ts
3539
+ var carrionCenter = (corpses, fallback) => {
3540
+ let ux = 0;
3541
+ let uz = 0;
3542
+ let un = 0;
3543
+ for (const c of corpses) {
3544
+ ux += c.x;
3545
+ uz += c.z;
3546
+ un++;
3547
+ }
3548
+ if (un >= 3) return [ux / un, uz / un];
3549
+ return fallback ? [fallback[0], fallback[1]] : [ux, uz];
3550
+ };
3551
+ function createFlock(options) {
3552
+ const birds = Array.from({ length: options.count }, () => ({ x: 0, y: 0, z: 0, yaw: 0, roll: 0, wing: 0, visible: true }));
3553
+ const within = options.visibleWithin ?? (options.mode === "carrion" ? 260 : Infinity);
3554
+ return {
3555
+ birds,
3556
+ update(t, dt, [cx, cz], view) {
3557
+ birds.forEach((U, i) => {
3558
+ if (options.mode === "carrion") {
3559
+ const a = t * (0.18 + i * 0.04) + i * 2.1;
3560
+ const r = 7 + i * 2;
3561
+ U.x = cx + Math.cos(a) * r;
3562
+ U.y = 16 + i * 2.5;
3563
+ U.z = cz + Math.sin(a) * r;
3564
+ U.yaw = -a;
3565
+ U.roll = 0.35;
3566
+ U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within;
3567
+ if (U.visible) U.wing += dt * 1.2 * 2 * Math.PI;
3568
+ } else {
3569
+ const a = t * 0.3 + i * 0.4;
3570
+ const r = 20 + i % 7 * 4;
3571
+ U.x = cx + Math.cos(a) * r;
3572
+ U.y = 25 + Math.sin(t + i) * 2;
3573
+ U.z = cz + Math.sin(a) * r;
3574
+ U.yaw = -a;
3575
+ U.roll = 0;
3576
+ U.visible = !view || Math.hypot(cx - view[0], cz - view[1]) < within;
3577
+ }
3578
+ });
3579
+ }
3580
+ };
3581
+ }
3582
+
3583
+ // src/behaviours/infection.ts
3584
+ function bindInfection(crowd, model, options) {
3585
+ const tracked = /* @__PURE__ */ new Map();
3586
+ const band = options.giveBackBand ?? 3;
3587
+ const offKill = crowd.on("kill", ({ agent }) => {
3588
+ const entry = tracked.get(agent);
3589
+ if (!entry) return;
3590
+ model.giveBack(entry.region, [{ state: "removed", seed: entry.seed }]);
3591
+ tracked.delete(agent);
3592
+ });
3593
+ const offRemove = crowd.on("remove", ({ agent, moved }) => {
3594
+ tracked.delete(agent);
3595
+ if (moved < 0) return;
3596
+ const entry = tracked.get(moved);
3597
+ if (!entry) return;
3598
+ tracked.delete(moved);
3599
+ tracked.set(agent, entry);
3600
+ });
3601
+ const offBite = options.civilians?.on("bitten", ({ id }) => {
3602
+ const n = options.civilians?.get(id);
3603
+ const region = n && options.regionOf?.(n.x, n.z);
3604
+ if (region) model.infect(region, 1, 0);
3605
+ });
3606
+ return {
3607
+ materialize(region, n) {
3608
+ const batch = model.materialize(region, n);
3609
+ const born = [];
3610
+ const back = [];
3611
+ batch.forEach((agent, k) => {
3612
+ const at = options.place(region, agent.seed, k);
3613
+ const i = at ? crowd.spawn(at[0], at[1], options.typeFor?.(agent, region) ?? -1) : -1;
3614
+ if (i < 0) {
3615
+ back.push(agent);
3616
+ return;
3617
+ }
3618
+ tracked.set(i, { region, state: agent.state, seed: agent.seed });
3619
+ born.push(i);
3620
+ });
3621
+ if (back.length) model.giveBack(region, back);
3622
+ return born;
3623
+ },
3624
+ giveBack(region, opts = {}) {
3625
+ const byRegion = /* @__PURE__ */ new Map();
3626
+ const indices = [...tracked.keys()].sort((a, b) => b - a);
3627
+ for (const i of indices) {
3628
+ const entry = tracked.get(i);
3629
+ if (!entry || region !== void 0 && entry.region !== region) continue;
3630
+ if (!opts.all && crowd.store.band[i] < band) continue;
3631
+ let list = byRegion.get(entry.region);
3632
+ if (!list) byRegion.set(entry.region, list = []);
3633
+ list.push({ state: entry.state, seed: entry.seed });
3634
+ tracked.delete(i);
3635
+ crowd.remove(i);
3636
+ }
3637
+ let count = 0;
3638
+ for (const [r, list] of byRegion) {
3639
+ model.giveBack(r, list);
3640
+ count += list.length;
3641
+ }
3642
+ return count;
3643
+ },
3644
+ regionOf: (agent) => tracked.get(agent)?.region ?? null,
3645
+ unbind() {
3646
+ offKill();
3647
+ offRemove();
3648
+ offBite?.();
3649
+ tracked.clear();
3650
+ }
3651
+ };
3652
+ }
3653
+ return __toCommonJS(index_exports);
3654
+ })();
3655
+ //# sourceMappingURL=index.global.js.map