@adatechnology/game-player 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,6 +39,121 @@ var GamePlayer = (() => {
39
39
  swingSecondsFor: () => swingSecondsFor
40
40
  });
41
41
 
42
+ // ../game-contracts/dist/index.js
43
+ var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
44
+ var DEFERRED_QUEUE_ID = {
45
+ DESTRUCTION_EVENTS: "destruction.events",
46
+ BREACH_PAINT: "floorplan.breach-paint",
47
+ AUDIO_VOICES: "audio.voices",
48
+ VEHICLE_CREASE: "vehicles.crease"
49
+ };
50
+ var DEFERRED_PRIORITY = {
51
+ [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
52
+ [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
53
+ [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
54
+ [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
55
+ };
56
+ var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
57
+ var DEFAULT_SURFACE = "concrete";
58
+ var GAIT_IDS = ["walk", "run"];
59
+ var LANDING_IDS = ["light", "heavy"];
60
+ var HEAVY_LANDING_SPEED = 5.5;
61
+ var BODY_SOUND_IDS = [
62
+ "jump",
63
+ "cloth_rustle",
64
+ "body_fall",
65
+ "fall_damage",
66
+ "player_hurt",
67
+ "player_breath_run",
68
+ "player_death",
69
+ "bite",
70
+ "grab",
71
+ "punch_swing",
72
+ "punch_hit_body",
73
+ "punch_hit_face",
74
+ "punch_hit_object",
75
+ "kick_swing",
76
+ "kick_hit",
77
+ "kick_hit_object",
78
+ "shove_two_hands",
79
+ "knife_swing",
80
+ "knife_stab",
81
+ "knife_slash",
82
+ "knife_hit_object",
83
+ "blunt_swing",
84
+ "blunt_hit",
85
+ "blunt_hit_object",
86
+ "axe_swing",
87
+ "axe_chop",
88
+ "axe_hit_object"
89
+ ];
90
+ var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
91
+ var landSoundId = (surface, fallSpeed) => `land_${surface}_${fallSpeed >= HEAVY_LANDING_SPEED ? "heavy" : "light"}`;
92
+ var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
93
+ var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing) => `land_${surface}_${landing}`));
94
+ var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
95
+ var SWING = {
96
+ punch: "punch_swing",
97
+ kick: "kick_swing",
98
+ // two hands pushed out do not whistle: the shove is one sound, at the contact
99
+ shove: null,
100
+ blade: "knife_swing",
101
+ blunt: "blunt_swing",
102
+ chop: "axe_swing"
103
+ };
104
+ var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
105
+ var swingSoundId = (blow) => SWING[blow];
106
+ var HIT = {
107
+ punch: { body: "punch_hit_body", head: "punch_hit_face", object: "punch_hit_object" },
108
+ kick: { body: "kick_hit", head: "kick_hit", object: "kick_hit_object" },
109
+ shove: { body: "shove_two_hands", head: "shove_two_hands", object: "shove_two_hands" },
110
+ blade: { body: "knife_stab", head: "knife_stab", object: "knife_hit_object" },
111
+ blunt: { body: "blunt_hit", head: "blunt_hit", object: "blunt_hit_object" },
112
+ chop: { body: "axe_chop", head: "axe_chop", object: "axe_hit_object" }
113
+ };
114
+ var hitSoundId = (blow, target) => HIT[blow][target];
115
+ var MATERIAL_SURFACES = {
116
+ concrete: "concrete",
117
+ mortar: "concrete",
118
+ stone: "concrete",
119
+ brick: "concrete",
120
+ plaster: "concrete",
121
+ asphalt: "asphalt",
122
+ wood_soft: "wood",
123
+ wood_hard: "wood",
124
+ green_wood: "wood",
125
+ plywood: "wood",
126
+ mdf: "wood",
127
+ ceramic: "tile",
128
+ plastic: "tile",
129
+ soil: "dirt",
130
+ wax: "dirt",
131
+ flesh_fruit: "dirt",
132
+ turf: "grass",
133
+ foliage: "grass",
134
+ cactus: "grass",
135
+ gravel: "gravel",
136
+ sand: "sand",
137
+ steel: "metal",
138
+ cast_iron: "metal",
139
+ aluminium: "metal",
140
+ copper: "metal",
141
+ water: "water",
142
+ glass: "glass",
143
+ tempered_glass: "glass",
144
+ fabric: "carpet",
145
+ foam: "carpet",
146
+ leather: "carpet",
147
+ rubber: "carpet",
148
+ paper: "carpet"
149
+ };
150
+ var isSurfaceId = (value) => typeof value === "string" && SURFACE_IDS.includes(value);
151
+ function surfaceOfMaterial(material, table = MATERIAL_SURFACES, fallback = DEFAULT_SURFACE) {
152
+ if (material === void 0) return fallback;
153
+ if (isSurfaceId(material)) return material;
154
+ return table[material] ?? fallback;
155
+ }
156
+
42
157
  // src/data/defaults.json
43
158
  var defaults_default = {
44
159
  sources: {
@@ -264,7 +379,11 @@ var GamePlayer = (() => {
264
379
  const set = listeners.get(event);
265
380
  if (set) for (const listener of set) listener(payload);
266
381
  };
267
- const sound = (id, volume) => emit("sound", { sound: id, volume, position: [P.x, P.y, P.z] });
382
+ const sound = (id, volume, ids, surface, at) => emit("sound", { sound: id, ids, volume, position: [P.x, P.y, P.z], ...surface ? { surface } : {}, ...at ? { at } : {} });
383
+ let surfaceHint;
384
+ const surfaceHere = () => inWater(P.x, P.z) ? "water" : surfaceOfMaterial(surfaceHint ?? world.surfaceAt?.(P.x, P.z));
385
+ let hostFeet = false;
386
+ const blowOf = (item) => item.blow ?? (item === fist ? "punch" : (item.crit ?? 0) > 0 ? "blade" : "blunt");
268
387
  const noise = (x, z, radius) => emit("noise", { x, z, radius });
269
388
  const message = (id, seconds) => emit("message", { id, seconds });
270
389
  const inWater = (x, z) => world.inWater?.(x, z) ?? false;
@@ -297,7 +416,7 @@ var GamePlayer = (() => {
297
416
  if (!P.alive) return;
298
417
  P.health -= amount;
299
418
  P.hurtFlash = numbers.hurtFlash;
300
- sound("flesh", numbers.hurtVolume);
419
+ sound("flesh", numbers.hurtVolume, ["player_hurt"]);
301
420
  const kind = drawWound(wounds, random);
302
421
  P.wounds.push(kind);
303
422
  if (wounds[kind]?.bleeds) P.bleeding = 1;
@@ -310,7 +429,7 @@ var GamePlayer = (() => {
310
429
  P.hurtFlash = numbers.biteFlash;
311
430
  P.bleeding = 1;
312
431
  P.wounds.push("bite");
313
- sound("flesh", numbers.biteVolume);
432
+ sound("flesh", numbers.biteVolume, ["bite", "player_hurt"]);
314
433
  emit("hurt", { amount: numbers.biteDamage, wound: "bite" });
315
434
  infect("bite");
316
435
  message("bitten", 5);
@@ -323,7 +442,7 @@ var GamePlayer = (() => {
323
442
  P.grabNoJaw = noJaw;
324
443
  P.grabDrown = drown;
325
444
  message(drown ? "grabbed_drown" : "grabbed", 3);
326
- sound("flesh", numbers.grabVolume);
445
+ sound("flesh", numbers.grabVolume, ["grab"]);
327
446
  emit("grabbed", { by, noJaw, drown });
328
447
  };
329
448
  const front = (range, cosMin, yaw, crowd = world.crowd) => {
@@ -342,7 +461,7 @@ var GamePlayer = (() => {
342
461
  return out.sort((a, b) => a[0] - b[0]);
343
462
  };
344
463
  const shove = (yaw) => {
345
- sound("step", numbers.shoveVolume);
464
+ sound("step", numbers.shoveVolume, ["shove_two_hands"]);
346
465
  const crowd = world.crowd;
347
466
  const targets = [];
348
467
  for (const [d, i] of front(numbers.shoveRange, numbers.shoveCone, yaw)) {
@@ -353,12 +472,20 @@ var GamePlayer = (() => {
353
472
  };
354
473
  const melee = (item, yaw) => {
355
474
  const t = front(numbers.meleeRangeBase + (item.reach ?? 0), numbers.meleeCone, yaw)[0];
356
- sound("flesh", numbers.meleeVolume);
357
- noise(P.x, P.z, item.noise || 1);
358
- if (!t) return;
475
+ const blow = blowOf(item);
476
+ const swing = swingSoundId(blow);
477
+ if (!t) {
478
+ sound("flesh", numbers.meleeVolume, swing ? [swing] : []);
479
+ noise(P.x, P.z, item.noise || 1);
480
+ return;
481
+ }
359
482
  const crowd = world.crowd;
360
483
  const i = t[1];
361
484
  const region = crowd.down(i) ? 0 : random() < numbers.meleeHeadChance ? 0 : random() < numbers.meleeArmChance ? 1 : 2 + Math.floor(random() * 2);
485
+ const hit = hitSoundId(blow, region === 0 ? "head" : "body");
486
+ if (swing) sound("flesh", numbers.meleeVolume, [swing, hit], void 0, [0, SWING_LEAD_SECONDS[blow]]);
487
+ else sound("flesh", numbers.meleeVolume, [hit]);
488
+ noise(P.x, P.z, item.noise || 1);
362
489
  const damage = item.damage * (1 + (attributes.strength - numbers.meleeStrengthBase) * numbers.meleeStrengthPerPoint);
363
490
  const cut = Math.min(1, (item.crit || 0) / numbers.meleeCritToCut);
364
491
  crowd.damage(i, { region, amount: damage, distance: t[0], cut });
@@ -366,6 +493,7 @@ var GamePlayer = (() => {
366
493
  };
367
494
  const update = (dt, input = {}, frameWorld) => {
368
495
  world = frameWorld ?? defaultWorld;
496
+ surfaceHint = input.surface;
369
497
  if (!P.alive) {
370
498
  P.deathTime += dt;
371
499
  if (P.infected && infection && !P.turned && P.deathTime >= infection.reanimateSeconds) {
@@ -393,7 +521,7 @@ var GamePlayer = (() => {
393
521
  const every = 1 / infection.feverNoiseEvery;
394
522
  if (P.infectionTime > infection.feverSeconds && Math.floor(P.infectionTime * every) !== Math.floor((P.infectionTime - dt) * every) && random() < infection.feverNoiseChance) {
395
523
  noise(P.x, P.z, infection.feverNoiseRadius);
396
- sound("step", infection.feverNoiseVolume);
524
+ sound("step", infection.feverNoiseVolume, ["cloth_rustle"]);
397
525
  }
398
526
  if (P.infectionTime > infection.deathSeconds) {
399
527
  P.health = 0;
@@ -428,7 +556,7 @@ var GamePlayer = (() => {
428
556
  if (wasPressed("push") && P.stamina > numbers.grabPushMinStamina) {
429
557
  P.grabPushes++;
430
558
  P.stamina -= numbers.grabPushCost;
431
- sound("step", numbers.grabPushVolume);
559
+ sound("step", numbers.grabPushVolume, ["shove_two_hands"]);
432
560
  }
433
561
  if (wasPressed("knife")) {
434
562
  if (random() < numbers.grabKnifeChance) {
@@ -482,7 +610,8 @@ var GamePlayer = (() => {
482
610
  if (P.vaultTimer <= 0) {
483
611
  P.y = 0;
484
612
  noise(P.x, P.z, numbers.vaultNoise);
485
- sound("step", numbers.vaultLandVolume);
613
+ const surface = surfaceHere();
614
+ sound("step", numbers.vaultLandVolume, [landSoundId(surface, 0)], surface);
486
615
  }
487
616
  return;
488
617
  }
@@ -499,7 +628,7 @@ var GamePlayer = (() => {
499
628
  P.vault = [P.x, P.z, ex, ez];
500
629
  P.vaultTimer = numbers.vaultSeconds;
501
630
  P.stamina -= numbers.vaultCost;
502
- sound("step", numbers.vaultStartVolume);
631
+ sound("step", numbers.vaultStartVolume, ["jump"]);
503
632
  emit("vault", { from: [P.x, P.z], to: [ex, ez] });
504
633
  return;
505
634
  }
@@ -572,11 +701,14 @@ var GamePlayer = (() => {
572
701
  dy -= Math.round(dy / TAU_APPROX) * TAU_APPROX;
573
702
  P.yaw += input.aim || input.firing ? dy : dy * Math.min(1, dt * numbers.turnFollow);
574
703
  }
704
+ if (input.footfall) hostFeet = true;
575
705
  P.stepTimer -= dt * spd;
576
- if (P.stepTimer <= 0 && spd > numbers.stepMinSpeed) {
706
+ const stepNow = hostFeet ? input.footfall === true : P.stepTimer <= 0 && spd > numbers.stepMinSpeed;
707
+ if (stepNow) {
577
708
  P.stepTimer = numbers.stepEvery;
578
709
  noise(P.x, P.z, (P.running ? numbers.stepNoiseRun : P.crouch ? numbers.stepNoiseCrouch : numbers.stepNoiseWalk) * derived.footstepNoise * (wet ? numbers.stepNoiseWater : 1));
579
- sound("step", P.running ? numbers.stepVolumeRun : numbers.stepVolumeWalk);
710
+ const surface = surfaceHere();
711
+ sound("step", P.running ? numbers.stepVolumeRun : numbers.stepVolumeWalk, [footstepSoundId(surface, P.running ? "run" : "walk")], surface);
580
712
  emit("step", { x: P.x, z: P.z, yaw: P.yaw });
581
713
  }
582
714
  if (wasPressed("push") && P.stamina > numbers.shoveCost) {
@@ -904,6 +1036,7 @@ var GamePlayer = (() => {
904
1036
 
905
1037
  // src/bench/module.ts
906
1038
  var MOVING_SPEED = 0.2;
1039
+ var TELEPORT_SPEED = 10;
907
1040
  var SPRINT_SPEED = 3.4;
908
1041
  var FRAME = 1 / 30;
909
1042
  var createGrabber = () => {
@@ -948,6 +1081,9 @@ var GamePlayer = (() => {
948
1081
  let lastMessage = "";
949
1082
  let pending = {};
950
1083
  let bandage = false;
1084
+ let footfall = false;
1085
+ let surface;
1086
+ let gait;
951
1087
  let last = null;
952
1088
  const offs = [];
953
1089
  const build = () => {
@@ -956,7 +1092,15 @@ var GamePlayer = (() => {
956
1092
  offs.push(made.on("noise", (e) => void (lastNoise = e.radius)));
957
1093
  offs.push(made.on("step", () => void (steps += 1)));
958
1094
  offs.push(made.on("message", (e) => void (lastMessage = playerTextOf(e.id, locale))));
959
- offs.push(made.on("sound", (e) => void host?.audio?.play({ sound: e.sound, volume: e.volume, position: e.position })));
1095
+ offs.push(
1096
+ made.on("sound", (e) => {
1097
+ if (e.ids.length === 0) return;
1098
+ e.ids.forEach((id, index) => {
1099
+ const delay = e.at?.[index] ?? 0;
1100
+ host?.audio?.play({ sound: id, volume: e.volume, position: e.position, ...delay > 0 ? { delay } : {} });
1101
+ });
1102
+ })
1103
+ );
960
1104
  offs.push(made.on("died", (e) => host?.log(`player died: ${e.cause}`, { cause: e.cause })));
961
1105
  lastNoise = 0;
962
1106
  steps = 0;
@@ -976,13 +1120,15 @@ var GamePlayer = (() => {
976
1120
  };
977
1121
  const frame = (dt) => {
978
1122
  const here = view();
979
- const speed = here && last && dt > 0 ? Math.hypot(here.x - last.x, here.z - last.z) / dt : 0;
1123
+ const raw = here && last && dt > 0 ? Math.hypot(here.x - last.x, here.z - last.z) / dt : 0;
1124
+ const speed = raw > TELEPORT_SPEED ? 0 : raw;
980
1125
  if (here) {
981
1126
  player.state.x = here.x;
982
1127
  player.state.z = here.z;
983
1128
  }
984
- const input = { forward: speed > MOVING_SPEED ? 1 : 0, run: speed > SPRINT_SPEED, yaw: here?.yaw ?? 0, bandage, pressed: pending };
1129
+ const input = { forward: speed > MOVING_SPEED ? 1 : 0, run: gait ? gait === "run" : speed > SPRINT_SPEED, yaw: here?.yaw ?? 0, bandage, pressed: pending, footfall, ...surface ? { surface } : {} };
985
1130
  pending = {};
1131
+ footfall = false;
986
1132
  bandage = false;
987
1133
  player.update(dt, input, { crowd: grabber.crowd });
988
1134
  if (here) {
@@ -1150,9 +1296,16 @@ var GamePlayer = (() => {
1150
1296
  last = null;
1151
1297
  const off = lent.bench?.onChange(onChange);
1152
1298
  const offFrame = lent.onFrame(frame);
1299
+ const offFeet = lent.on((event) => {
1300
+ if (event.type !== "footfall") return;
1301
+ footfall = true;
1302
+ surface = event.surface;
1303
+ gait = event.gait;
1304
+ });
1153
1305
  return () => {
1154
1306
  off?.();
1155
1307
  offFrame();
1308
+ offFeet();
1156
1309
  host = null;
1157
1310
  };
1158
1311
  }