@adatechnology/game-test-room 0.24.0 → 0.25.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.
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { AudioEngine } from '@adatechnology/game-audio';
4
4
  import { Vec3 as Vec3$1, DestructionWorld, BlastOptions, DestructibleSpec, DestructionOptions, DestructionEvent } from '@adatechnology/game-destruction';
5
5
  export { DestructionEvent, Vec3 } from '@adatechnology/game-destruction';
6
6
  import { DoorUsePhase, FurniturePushPhase, FurniturePushBlock, FurniturePushRules, DoorUseRules } from '@adatechnology/game-player';
7
- import { BusySink, ItemSeat, Vec3, HandTargets, ModuleControl, ControlValue, ModuleLayer, EnvironmentBus, Knock, BusyTask, BusyOptions, TestableModule, ModuleItemSeal, DestructibleImpl, AnimationClipSpec, ItemSounds, GroundLayout, BlastShoveOptions } from '@adatechnology/game-contracts';
7
+ import { BusySink, ItemSeat, Vec3, HandTargets, ModuleControl, ControlValue, ModuleLayer, EnvironmentBus, Knock, BusyTask, BusyOptions, TestableModule, ModuleItemSeal, DestructibleImpl, AnimationClipSpec, ItemSounds, GroundLayout, BlastImpulseOptions, BlastShoveOptions } from '@adatechnology/game-contracts';
8
8
  export { AnimationClipSpec, DestructibleImpl, ItemAnimationContract, ModuleScenario, TestableItem, TestableModule } from '@adatechnology/game-contracts';
9
9
  import { DestructionRenderer, SpecFromObjectOptions, Quality, SoundDetail, FragmentLifeRule } from '@adatechnology/game-destruction-three';
10
10
  import { StorageLike, GraphicsController } from '@adatechnology/game-graphics';
@@ -831,6 +831,8 @@ interface TestRoomOptions {
831
831
  * The numbers of the shove an explosion gives to what is around it: loose items, furniture, and the bodies of the modules (people), which read them from `ModuleHost.blastShove`.
832
832
  * Default: the SDK's (`DEFAULT_BLAST_SHOVE` of game-contracts: 2 % of the energy becomes speed, 20 m/s at most, falloff `(1 - d / r)^2`).
833
833
  */
834
+ /** The body the blast wave pushes (the player's area, the charge read from a blast without one): `knockOf` of `game-contracts`. Default `DEFAULT_BLAST_IMPULSE`. */
835
+ blastImpulse?: BlastImpulseOptions;
834
836
  blastShove?: BlastShoveOptions;
835
837
  /** Mass of the player's body in kg: how far a blast throws it and whether it knocks it down (default 70, the `bodyMass` of 1.75 m). */
836
838
  playerMass?: number;
@@ -62,8 +62,29 @@ var GameTestRoom = (() => {
62
62
  const length3 = Math.hypot(direction[0], direction[1], direction[2]);
63
63
  return { speed, direction: [direction[0] / length3, direction[1] / length3, direction[2] / length3], distance: distance4, falloff };
64
64
  }
65
+ var DEFAULT_BLAST_IMPULSE = { bodyArea: 0.7, lift: 0.15, minSpeed: 0.3, tntPerJoule: 0.24 / 2e5 };
66
+ var NEAREST = 0.3;
67
+ function sideOnImpulse(tnt, distance4) {
68
+ if (!(tnt > 0)) return 0;
69
+ const w3 = Math.cbrt(tnt);
70
+ const z = Math.max(distance4, NEAREST) / w3;
71
+ const scaled2 = 0.067 * Math.sqrt(1 + (z / 0.23) ** 4) / (z * z * Math.cbrt(1 + (z / 1.55) ** 3));
72
+ return scaled2 * w3 * 100;
73
+ }
74
+ function bodyImpulse(tnt, distance4) {
75
+ if (!(tnt > 0)) return 0;
76
+ const z = Math.max(distance4, NEAREST) / Math.cbrt(tnt);
77
+ return sideOnImpulse(tnt, distance4) * (2 + 4 / (1 + z * z));
78
+ }
79
+ function blastBodySpeed(tnt, distance4, mass, reach = 1, options = {}) {
80
+ const area = options.bodyArea ?? DEFAULT_BLAST_IMPULSE.bodyArea;
81
+ return bodyImpulse(tnt, distance4) * area * Math.max(0, reach) / Math.max(mass, 1);
82
+ }
65
83
  var KNOCK_DOWN_SPEED = 2.5;
66
84
  var LIE_SECONDS = 1.6;
85
+ var WOUNDED_LIE_SECONDS = 6;
86
+ var FRAGMENT_COVER = 0.5;
87
+ var STUN_SECONDS = 4;
67
88
  var STAND_UP_PER_SPEED = 0.25;
68
89
  var FALL_SECONDS = 0.25;
69
90
  var RISE_SECONDS = 0.6;
@@ -72,11 +93,38 @@ var GameTestRoom = (() => {
72
93
  var KNOCK_CHEST = 1.1;
73
94
  var CHEST = KNOCK_CHEST;
74
95
  var bodyMass = (height, build = 1) => 70 * (height / 1.75) ** 2 * build;
75
- function knockOf(blast3, feet, mass, options) {
76
- const hit = blastShove(blast3, { position: [feet[0], feet[1] + CHEST, feet[2]], mass }, options);
77
- if (!hit) return void 0;
78
- const [dx, dy, dz] = hit.direction;
79
- return { velocity: [dx * hit.speed, Math.max(0, dy * hit.speed), dz * hit.speed], airborne: dy > 0, lying: 0, lieLeft: 0, down: hit.speed >= KNOCK_DOWN_SPEED };
96
+ function knockOf(blast3, feet, mass, options, impulse = {}) {
97
+ const o = { ...DEFAULT_BLAST_IMPULSE, ...impulse };
98
+ const chest = [feet[0], feet[1] + CHEST, feet[2]];
99
+ const dx = chest[0] - blast3.point[0];
100
+ const dy = chest[1] - blast3.point[1];
101
+ const dz = chest[2] - blast3.point[2];
102
+ const distance4 = Math.hypot(dx, dy, dz);
103
+ if (!(blast3.radius > 0) || distance4 > blast3.radius) return void 0;
104
+ const reach = Math.max(0, Math.min(1, blast3.reach ?? 1));
105
+ const tnt = blast3.tnt ?? blast3.energy * o.tntPerJoule;
106
+ let speed = blastBodySpeed(tnt, distance4, mass, reach, o);
107
+ const minSpeed = impulse.minSpeed ?? options?.minSpeed ?? o.minSpeed;
108
+ if (speed < minSpeed) speed = 0;
109
+ const struck = reach >= FRAGMENT_COVER;
110
+ const wounded = struck && blast3.lethalRadius !== void 0 && distance4 <= blast3.lethalRadius;
111
+ const edge = 1 - distance4 / blast3.radius;
112
+ const stunSeconds = struck ? STUN_SECONDS * edge * edge * reach : 0;
113
+ if (speed === 0 && !wounded && stunSeconds < 0.25) return void 0;
114
+ const horizontal = Math.hypot(dx, dz);
115
+ const away = horizontal > 1e-4 ? [dx / horizontal, o.lift, dz / horizontal] : [0, 1, 0];
116
+ const length3 = Math.hypot(away[0], away[1], away[2]);
117
+ const velocity = [away[0] / length3 * speed, away[1] / length3 * speed, away[2] / length3 * speed];
118
+ return {
119
+ velocity,
120
+ airborne: velocity[1] > 0.5,
121
+ lying: 0,
122
+ lieLeft: wounded ? WOUNDED_LIE_SECONDS : 0,
123
+ away: horizontal > 1e-4 ? [dx / horizontal, 0, dz / horizontal] : [0, 0, 0],
124
+ down: speed >= KNOCK_DOWN_SPEED || wounded,
125
+ ...wounded ? { wounded } : {},
126
+ ...stunSeconds >= 0.25 ? { stunSeconds } : {}
127
+ };
80
128
  }
81
129
  function stepKnock(knock2, position, dt, groundAt, alive) {
82
130
  const v = knock2.velocity;
@@ -100,14 +148,11 @@ var GameTestRoom = (() => {
100
148
  }
101
149
  const moving = horizontal > 0.05 || knock2.airborne;
102
150
  if (knock2.down) {
103
- if (moving) knock2.lying = Math.min(1, knock2.lying + dt / FALL_SECONDS);
104
- else {
105
- knock2.lying = 1;
106
- if (alive) {
107
- knock2.lieLeft = knock2.lieLeft > 0 ? knock2.lieLeft : LIE_SECONDS + Math.hypot(v[0], v[2]) * STAND_UP_PER_SPEED;
108
- knock2.lieLeft -= dt;
109
- if (knock2.lieLeft <= 0) knock2.down = false;
110
- }
151
+ if (moving || knock2.lying < 1) knock2.lying = Math.min(1, knock2.lying + dt / FALL_SECONDS);
152
+ else if (alive) {
153
+ knock2.lieLeft = knock2.lieLeft > 0 ? knock2.lieLeft : LIE_SECONDS + Math.hypot(v[0], v[2]) * STAND_UP_PER_SPEED;
154
+ knock2.lieLeft -= dt;
155
+ if (knock2.lieLeft <= 0) knock2.down = false;
111
156
  }
112
157
  } else if (knock2.lying > 0) knock2.lying = Math.max(0, knock2.lying - dt / RISE_SECONDS);
113
158
  return !moving && !knock2.down && knock2.lying === 0;
@@ -5020,7 +5065,7 @@ var GameTestRoom = (() => {
5020
5065
  return;
5021
5066
  }
5022
5067
  if (ex) {
5023
- emit2({ ...base(o, src), type: "explode", part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, ...ex.effect !== void 0 ? { effect: ex.effect } : {} });
5068
+ emit2({ ...base(o, src), type: "explode", part: p.spec.id, point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, ...ex.tnt !== void 0 ? { tnt: ex.tnt } : {}, ...ex.effect !== void 0 ? { effect: ex.effect } : {} });
5024
5069
  blasts.push({ point: c, energy: ex.energy, radius: ex.radius, ...ex.lethalRadius !== void 0 ? { lethalRadius: ex.lethalRadius } : {}, source: src });
5025
5070
  }
5026
5071
  partLost(o, p, src);
@@ -18506,7 +18551,44 @@ var GameTestRoom = (() => {
18506
18551
  renderer.compile(scene2, camera2);
18507
18552
  renderer.render(scene2, camera2);
18508
18553
  }
18554
+ function drawOnly(scene2, root) {
18555
+ const hidden = [];
18556
+ const visit = (object) => {
18557
+ if (object === root || !object.visible) return;
18558
+ const drawable = object;
18559
+ if (drawable.isMesh === true || drawable.isPoints === true || drawable.isLine === true || drawable.isSprite === true) {
18560
+ if (!holdsLight(object) && !holdsObject(object, root)) {
18561
+ object.visible = false;
18562
+ hidden.push(object);
18563
+ return;
18564
+ }
18565
+ }
18566
+ for (const child of object.children) visit(child);
18567
+ };
18568
+ for (const child of scene2.children) visit(child);
18569
+ return () => hidden.forEach((object) => object.visible = true);
18570
+ }
18571
+ function holdsLight(object) {
18572
+ let found = false;
18573
+ object.traverse((each) => {
18574
+ if (each.isLight === true) found = true;
18575
+ });
18576
+ return found;
18577
+ }
18578
+ function holdsObject(object, target) {
18579
+ for (let at3 = target; at3 !== null; at3 = at3.parent) if (at3 === object) return true;
18580
+ return false;
18581
+ }
18509
18582
  function playEffect(renderer, scene2, camera2, effect, target) {
18583
+ const root = effect.root?.();
18584
+ const restore = root === null || root === void 0 ? () => void 0 : drawOnly(scene2, root);
18585
+ try {
18586
+ playFrames(renderer, scene2, camera2, effect, target);
18587
+ } finally {
18588
+ restore();
18589
+ }
18590
+ }
18591
+ function playFrames(renderer, scene2, camera2, effect, target) {
18510
18592
  effect.play();
18511
18593
  try {
18512
18594
  for (let frame2 = 0; frame2 < (effect.frames ?? DEFAULT_EFFECT_FRAMES); frame2++) {
@@ -19013,7 +19095,7 @@ var GameTestRoom = (() => {
19013
19095
  return sources;
19014
19096
  }
19015
19097
 
19016
- // ../game-vfx-three/dist/chunk-ALDCQ5RH.js
19098
+ // ../game-vfx-three/dist/chunk-2MZDWTHX.js
19017
19099
  var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
19018
19100
  void main(){vec3 R=vec3(viewMatrix[0][0],viewMatrix[1][0],viewMatrix[2][0]);vec3 Up=vec3(viewMatrix[0][1],viewMatrix[1][1],viewMatrix[2][1]);
19019
19101
  vec3 wp=aP.xyz+(R*position.x+Up*position.y)*aP.w;vU=position.xy;vC=aC;gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);}`;
@@ -20393,6 +20475,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
20393
20475
  drawCalls = Number(lastDrawn.puffs > 0) + Number(lastDrawn.sparks > 0) + Number(lastDrawn.glows > 0) + Number(chunks.rocks.count > 0) + Number(chunks.plates.count > 0) + Number(sprites.additive.geometry.drawRange.count > 0) + Number(sprites.normal.geometry.drawRange.count > 0) + lastDrawn.shocks * 2 + marks.count() + workshopCalls;
20394
20476
  }
20395
20477
  return {
20478
+ root,
20396
20479
  world,
20397
20480
  play(request) {
20398
20481
  followCamera();
@@ -28830,6 +28913,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28830
28913
  lethalRadius: 5,
28831
28914
  injuryRadius: 15,
28832
28915
  energy: 2e5,
28916
+ tnt: 0.24,
28833
28917
  destructionLevel: 2,
28834
28918
  noise: 500,
28835
28919
  fuse: 4,
@@ -28875,6 +28959,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28875
28959
  lethalRadius: 3,
28876
28960
  injuryRadius: 10,
28877
28961
  energy: 43200,
28962
+ tnt: 0.15,
28878
28963
  destructionLevel: 2,
28879
28964
  noise: 450,
28880
28965
  fuse: 3,
@@ -28894,6 +28979,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28894
28979
  lethalRadius: 5,
28895
28980
  injuryRadius: 14,
28896
28981
  energy: 2e5,
28982
+ tnt: 1.5,
28897
28983
  destructionLevel: 2,
28898
28984
  noise: 550,
28899
28985
  fire: {
@@ -28940,6 +29026,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28940
29026
  lethalRadius: 4,
28941
29027
  injuryRadius: 12,
28942
29028
  energy: 102400,
29029
+ tnt: 0.25,
28943
29030
  destructionLevel: 3,
28944
29031
  noise: 600,
28945
29032
  fuseLength: 0.03,
@@ -28962,6 +29049,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28962
29049
  lethalRadius: 6,
28963
29050
  injuryRadius: 18,
28964
29051
  energy: 345600,
29052
+ tnt: 0.76,
28965
29053
  destructionLevel: 3,
28966
29054
  noise: 700,
28967
29055
  triggers: [
@@ -28985,6 +29073,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28985
29073
  length: 50
28986
29074
  },
28987
29075
  energy: 102400,
29076
+ tnt: 0.9,
28988
29077
  destructionLevel: 1,
28989
29078
  noise: 500,
28990
29079
  triggers: [
@@ -29008,6 +29097,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29008
29097
  lethalRadius: 0,
29009
29098
  injuryRadius: 10,
29010
29099
  energy: 0,
29100
+ tnt: 0.01,
29011
29101
  destructionLevel: 0,
29012
29102
  noise: 400,
29013
29103
  fuseRange: [
@@ -29032,6 +29122,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29032
29122
  lethalRadius: 4,
29033
29123
  injuryRadius: 12,
29034
29124
  energy: 102400,
29125
+ tnt: 0.045,
29035
29126
  destructionLevel: 2,
29036
29127
  noise: 450,
29037
29128
  triggers: [
@@ -29050,6 +29141,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29050
29141
  lethalRadius: 5,
29051
29142
  injuryRadius: 15,
29052
29143
  energy: 2e5,
29144
+ tnt: 0.45,
29053
29145
  destructionLevel: 3,
29054
29146
  noise: 700,
29055
29147
  triggers: [
@@ -29068,6 +29160,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29068
29160
  lethalRadius: 5,
29069
29161
  injuryRadius: 16,
29070
29162
  energy: 2e5,
29163
+ tnt: 0.7,
29071
29164
  destructionLevel: 3,
29072
29165
  noise: 750,
29073
29166
  triggers: [
@@ -29086,6 +29179,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29086
29179
  lethalRadius: 5,
29087
29180
  injuryRadius: 16,
29088
29181
  energy: 2e5,
29182
+ tnt: 0.45,
29089
29183
  destructionLevel: 3,
29090
29184
  noise: 750,
29091
29185
  triggers: [
@@ -29104,6 +29198,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29104
29198
  lethalRadius: 5,
29105
29199
  injuryRadius: 16,
29106
29200
  energy: 2e5,
29201
+ tnt: 0.7,
29107
29202
  destructionLevel: 3,
29108
29203
  noise: 750,
29109
29204
  triggers: [
@@ -29122,6 +29217,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29122
29217
  lethalRadius: 4,
29123
29218
  injuryRadius: 12,
29124
29219
  energy: 102400,
29220
+ tnt: 1,
29125
29221
  destructionLevel: 2,
29126
29222
  noise: 550,
29127
29223
  fire: {
@@ -29155,6 +29251,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29155
29251
  lethalRadius: 5,
29156
29252
  injuryRadius: 14,
29157
29253
  energy: 2e5,
29254
+ tnt: 0.5,
29158
29255
  destructionLevel: 2,
29159
29256
  noise: 550,
29160
29257
  fire: {
@@ -29192,6 +29289,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29192
29289
  lethalRadius: 4,
29193
29290
  injuryRadius: 12,
29194
29291
  energy: 102400,
29292
+ tnt: 0.5,
29195
29293
  destructionLevel: 3,
29196
29294
  noise: 700,
29197
29295
  fire: {
@@ -29221,6 +29319,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29221
29319
  lethalRadius: 12,
29222
29320
  injuryRadius: 30,
29223
29321
  energy: 2764800,
29322
+ tnt: 20,
29224
29323
  destructionLevel: 3,
29225
29324
  noise: 700,
29226
29325
  fire: {
@@ -29248,6 +29347,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29248
29347
  lethalRadius: 4,
29249
29348
  injuryRadius: 12,
29250
29349
  energy: 102400,
29350
+ tnt: 0.045,
29251
29351
  destructionLevel: 2,
29252
29352
  noise: 450,
29253
29353
  triggers: [
@@ -37966,6 +38066,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
37966
38066
  direction: this.facing,
37967
38067
  at: at3,
37968
38068
  energy: def.energy,
38069
+ ...def.tnt !== void 0 ? { tnt: def.tnt } : {},
37969
38070
  lethalRadius: def.lethalRadius,
37970
38071
  injuryRadius: def.injuryRadius,
37971
38072
  noise: def.noise,
@@ -45768,7 +45869,17 @@ void main(){
45768
45869
  if (onPlayer.zone !== "none") {
45769
45870
  action("blast_player", translate("act.blast_player", { weapon: weaponName, zone: translate(`blast.zone.${onPlayer.zone}`), distance: format(onPlayer.distance, 1), stun: format(onPlayer.stunSeconds, 1) }), { data: { ...onPlayer } });
45770
45871
  }
45771
- context.emitHost({ type: "explosion", source: projectile.weapon, point: detonation.point, energy: detonation.energy, radius: reach, cause: detonation.cause, explosive: detonation.explosive });
45872
+ context.emitHost({
45873
+ type: "explosion",
45874
+ source: projectile.weapon,
45875
+ point: detonation.point,
45876
+ energy: detonation.energy,
45877
+ radius: reach,
45878
+ cause: detonation.cause,
45879
+ explosive: detonation.explosive,
45880
+ lethalRadius: detonation.lethalRadius,
45881
+ ...detonation.tnt !== void 0 ? { tnt: detonation.tnt } : {}
45882
+ });
45772
45883
  if (detonation.fire) context.startFire(projectile.weapon, detonation.point, detonation.fire, detonation.cause === "impact");
45773
45884
  };
45774
45885
  const detonateBare = (projectile, isImpact) => {
@@ -47937,6 +48048,9 @@ void main(){
47937
48048
  var LYING_PITCH = 1.05;
47938
48049
  var LYING_ROLL = 0.35;
47939
48050
  var LYING_PITCH_FACE_DOWN = -1.05;
48051
+ var STUN_PITCH = 0.05;
48052
+ var STUN_ROLL = 0.08;
48053
+ var STUN_FULL = 2;
47940
48054
  function createPlayer(THREE, o) {
47941
48055
  const cam = o.camera;
47942
48056
  let yaw = 0;
@@ -47976,11 +48090,16 @@ void main(){
47976
48090
  };
47977
48091
  let knocked = null;
47978
48092
  let knockedForward = false;
48093
+ let stunLeft = 0;
48094
+ let stunPhase = 0;
47979
48095
  let rollSide = 1;
47980
48096
  const apply = () => {
47981
48097
  const lying = knocked?.lying ?? 0;
47982
48098
  const fallen = lying * lying * (3 - 2 * lying);
47983
- euler.set(pitch + ((knockedForward ? LYING_PITCH_FACE_DOWN : LYING_PITCH) - pitch) * fallen, yaw, LYING_ROLL * rollSide * fallen, "YXZ");
48099
+ const sway = Math.min(1, stunLeft / STUN_FULL);
48100
+ const swayPitch = STUN_PITCH * sway * Math.sin(stunPhase * 1.3);
48101
+ const swayRoll = STUN_ROLL * sway * Math.sin(stunPhase * 0.9 + 1);
48102
+ euler.set(pitch + ((knockedForward ? LYING_PITCH_FACE_DOWN : LYING_PITCH) - pitch) * fallen + swayPitch, yaw, LYING_ROLL * rollSide * fallen + swayRoll, "YXZ");
47984
48103
  cam.quaternion.setFromEuler(euler);
47985
48104
  const standing = eye - CROUCH_DROP * crouch - bend;
47986
48105
  cam.position.set(px.x, floorLevel + y + standing + (LYING_EYE - standing) * fallen, px.z);
@@ -48254,11 +48373,15 @@ void main(){
48254
48373
  knocked.velocity = [knocked.velocity[0] + next.velocity[0], knocked.velocity[1] + next.velocity[1], knocked.velocity[2] + next.velocity[2]];
48255
48374
  knocked.airborne || (knocked.airborne = next.airborne);
48256
48375
  knocked.down || (knocked.down = next.down);
48376
+ knocked.wounded || (knocked.wounded = next.wounded);
48377
+ knocked.lieLeft = Math.max(knocked.lieLeft, next.lieLeft);
48257
48378
  } else knocked = { ...next, velocity: [...next.velocity] };
48379
+ stunLeft = Math.max(stunLeft, next.stunSeconds ?? 0);
48258
48380
  if (next.down) {
48259
- const right = Math.cos(yaw) * next.velocity[0] - Math.sin(yaw) * next.velocity[2];
48381
+ const away = next.away && (next.away[0] !== 0 || next.away[2] !== 0) ? next.away : next.velocity;
48382
+ const right = Math.cos(yaw) * away[0] - Math.sin(yaw) * away[2];
48260
48383
  rollSide = right >= 0 ? -1 : 1;
48261
- knockedForward = -Math.sin(yaw) * next.velocity[0] - Math.cos(yaw) * next.velocity[2] > 0;
48384
+ knockedForward = -Math.sin(yaw) * away[0] - Math.cos(yaw) * away[2] > 0;
48262
48385
  crouchLatched = false;
48263
48386
  }
48264
48387
  if (knocked.airborne) vy2 = Math.max(vy2, knocked.velocity[1]);
@@ -48269,7 +48392,14 @@ void main(){
48269
48392
  get knockedForward() {
48270
48393
  return knockedForward;
48271
48394
  },
48395
+ get stunned() {
48396
+ return stunLeft;
48397
+ },
48272
48398
  update(dt) {
48399
+ if (stunLeft > 0) {
48400
+ stunLeft = Math.max(0, stunLeft - dt);
48401
+ stunPhase += dt * 2.4;
48402
+ }
48273
48403
  if (knocked) {
48274
48404
  stepKnocked(dt);
48275
48405
  return;
@@ -51418,6 +51548,9 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
51418
51548
  "act.player_slammed": "The player was thrown into {what} at {speed} m/s",
51419
51549
  "act.player_fell": "The player hit the floor at {speed} m/s",
51420
51550
  "act.player_shoved": "The blast pushed the player at {speed} m/s ({reach}% got through the cover)",
51551
+ "act.player_wounded": "The blast felled the player where they stood, {distance} m from it: badly wounded, stunned for {stun} s ({reach}% got through the cover)",
51552
+ "act.player_thrown_wounded": "The blast threw the player at {speed} m/s, {distance} m from it: badly wounded, stunned for {stun} s ({reach}% got through the cover)",
51553
+ "act.player_stunned": "The blast stunned the player for {stun} s, {distance} m from it: dizzy, ears ringing",
51421
51554
  "act.came_apart": "{name} came apart: what stayed is loose and falls",
51422
51555
  "act.shoved": "{name} stands free: the shots moved it",
51423
51556
  "act.impact": "{name} hit {where} at {speed} m/s: {energy} J of blunt damage",
@@ -51780,6 +51913,9 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
51780
51913
  "act.player_slammed": "O jogador foi arremessado contra {what} a {speed} m/s",
51781
51914
  "act.player_fell": "O jogador bateu no ch\xE3o a {speed} m/s",
51782
51915
  "act.player_shoved": "A explos\xE3o empurrou o jogador a {speed} m/s ({reach}% passou pela cobertura)",
51916
+ "act.player_wounded": "A explos\xE3o derrubou o jogador onde estava, a {distance} m dela: ferido gravemente, atordoado por {stun} s ({reach}% passou pela cobertura)",
51917
+ "act.player_thrown_wounded": "A explos\xE3o arremessou o jogador a {speed} m/s, a {distance} m dela: ferido gravemente, atordoado por {stun} s ({reach}% passou pela cobertura)",
51918
+ "act.player_stunned": "A explos\xE3o atordoou o jogador por {stun} s, a {distance} m dela: tonto, com os ouvidos zumbindo",
51783
51919
  "act.came_apart": "{name} se desmontou: o que ficou est\xE1 solto e cai",
51784
51920
  "act.shoved": "{name} estava solto no ch\xE3o: os tiros o moveram",
51785
51921
  "act.impact": "{name} bateu em {where} a {speed} m/s: {energy} J de dano de impacto",
@@ -53509,7 +53645,19 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
53509
53645
  for (const ev of batch) {
53510
53646
  if (ev.type === "leak") puncture(ev);
53511
53647
  else if (ev.type === "ignite") igniteNear(ev.point, 0.9);
53512
- else if (ev.type === "explode") emitHost({ type: "explosion", source: ev.object, point: ev.point, energy: ev.energy, radius: ev.radius, cause: "part", ...ev.effect !== void 0 ? { explosive: ev.effect } : {} });
53648
+ else if (ev.type === "explode") {
53649
+ emitHost({
53650
+ type: "explosion",
53651
+ source: ev.object,
53652
+ point: ev.point,
53653
+ energy: ev.energy,
53654
+ radius: ev.radius,
53655
+ cause: "part",
53656
+ ...ev.effect !== void 0 ? { explosive: ev.effect } : {},
53657
+ ...ev.tnt !== void 0 ? { tnt: ev.tnt } : {},
53658
+ ...ev.lethalRadius !== void 0 ? { lethalRadius: ev.lethalRadius } : {}
53659
+ });
53660
+ }
53513
53661
  fire2("event", ev);
53514
53662
  options.onEvent?.(ev);
53515
53663
  }
@@ -54205,14 +54353,27 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
54205
54353
  const knockPlayer = (event) => {
54206
54354
  if (seating.seat) return;
54207
54355
  const feet = [player.x, player.feet, player.z];
54208
- const reach = world.blastReach(event.point, [feet[0], feet[1] + KNOCK_CHEST, feet[2]], event.source ? { ignore: [event.source] } : {});
54209
- const knock2 = knockOf({ point: event.point, energy: event.energy, radius: event.radius, reach }, feet, playerMass, options.blastShove);
54356
+ const chest = [feet[0], feet[1] + KNOCK_CHEST, feet[2]];
54357
+ const reach = world.blastReach(event.point, chest, event.source ? { ignore: [event.source] } : {});
54358
+ const blast3 = {
54359
+ point: event.point,
54360
+ energy: event.energy,
54361
+ radius: event.radius,
54362
+ reach,
54363
+ ...event.tnt !== void 0 ? { tnt: event.tnt } : {},
54364
+ ...event.lethalRadius !== void 0 ? { lethalRadius: event.lethalRadius } : {}
54365
+ };
54366
+ const knock2 = knockOf(blast3, feet, playerMass, options.blastShove, options.blastImpulse);
54210
54367
  if (!knock2) return;
54211
54368
  player.knock(knock2);
54212
54369
  const speed = Math.hypot(knock2.velocity[0], knock2.velocity[1], knock2.velocity[2]);
54213
- action(knock2.down ? "player_knocked_down" : "player_shoved", t(knock2.down ? "act.knocked_down" : "act.player_shoved", { speed: num(speed, 1), reach: num(reach * 100, 0) }), {
54214
- data: { speed, reach, down: knock2.down, source: event.source }
54215
- });
54370
+ const distance4 = Math.hypot(chest[0] - event.point[0], chest[1] - event.point[1], chest[2] - event.point[2]);
54371
+ const data = { speed, reach, distance: distance4, down: knock2.down, wounded: Boolean(knock2.wounded), stunSeconds: knock2.stunSeconds ?? 0, source: event.source };
54372
+ const values = { speed: num(speed, 1), reach: num(reach * 100, 0), distance: num(distance4, 1), stun: num(knock2.stunSeconds ?? 0, 1) };
54373
+ if (knock2.wounded) action("player_wounded", t(speed >= KNOCK_DOWN_SPEED ? "act.player_thrown_wounded" : "act.player_wounded", values), { data });
54374
+ else if (knock2.down) action("player_knocked_down", t("act.knocked_down", values), { data });
54375
+ else if (speed > 0) action("player_shoved", t("act.player_shoved", values), { data });
54376
+ else action("player_stunned", t("act.player_stunned", values), { data });
54216
54377
  };
54217
54378
  const avatarState = { speed: 0, running: false, crouch: false, down: false, armed: false, pitch: 0 };
54218
54379
  let feetPrev = null;
@@ -56172,6 +56333,8 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
56172
56333
  let handle = null;
56173
56334
  return {
56174
56335
  id: `vfx:${id}`,
56336
+ // the room was drawn in every fog state just before: each frame of a blast draws the effects alone
56337
+ root: () => vfx.root,
56175
56338
  play() {
56176
56339
  handle = vfx.play({ effect: id, point: [0, 0.1, -4], direction: [0, 1, 0], normal: [0, 1, 0] });
56177
56340
  },