@adatechnology/game-test-room 0.24.0 → 0.25.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.
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);
@@ -28830,6 +28875,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28830
28875
  lethalRadius: 5,
28831
28876
  injuryRadius: 15,
28832
28877
  energy: 2e5,
28878
+ tnt: 0.24,
28833
28879
  destructionLevel: 2,
28834
28880
  noise: 500,
28835
28881
  fuse: 4,
@@ -28875,6 +28921,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28875
28921
  lethalRadius: 3,
28876
28922
  injuryRadius: 10,
28877
28923
  energy: 43200,
28924
+ tnt: 0.15,
28878
28925
  destructionLevel: 2,
28879
28926
  noise: 450,
28880
28927
  fuse: 3,
@@ -28894,6 +28941,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28894
28941
  lethalRadius: 5,
28895
28942
  injuryRadius: 14,
28896
28943
  energy: 2e5,
28944
+ tnt: 1.5,
28897
28945
  destructionLevel: 2,
28898
28946
  noise: 550,
28899
28947
  fire: {
@@ -28940,6 +28988,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28940
28988
  lethalRadius: 4,
28941
28989
  injuryRadius: 12,
28942
28990
  energy: 102400,
28991
+ tnt: 0.25,
28943
28992
  destructionLevel: 3,
28944
28993
  noise: 600,
28945
28994
  fuseLength: 0.03,
@@ -28962,6 +29011,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28962
29011
  lethalRadius: 6,
28963
29012
  injuryRadius: 18,
28964
29013
  energy: 345600,
29014
+ tnt: 0.76,
28965
29015
  destructionLevel: 3,
28966
29016
  noise: 700,
28967
29017
  triggers: [
@@ -28985,6 +29035,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
28985
29035
  length: 50
28986
29036
  },
28987
29037
  energy: 102400,
29038
+ tnt: 0.9,
28988
29039
  destructionLevel: 1,
28989
29040
  noise: 500,
28990
29041
  triggers: [
@@ -29008,6 +29059,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29008
29059
  lethalRadius: 0,
29009
29060
  injuryRadius: 10,
29010
29061
  energy: 0,
29062
+ tnt: 0.01,
29011
29063
  destructionLevel: 0,
29012
29064
  noise: 400,
29013
29065
  fuseRange: [
@@ -29032,6 +29084,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29032
29084
  lethalRadius: 4,
29033
29085
  injuryRadius: 12,
29034
29086
  energy: 102400,
29087
+ tnt: 0.045,
29035
29088
  destructionLevel: 2,
29036
29089
  noise: 450,
29037
29090
  triggers: [
@@ -29050,6 +29103,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29050
29103
  lethalRadius: 5,
29051
29104
  injuryRadius: 15,
29052
29105
  energy: 2e5,
29106
+ tnt: 0.45,
29053
29107
  destructionLevel: 3,
29054
29108
  noise: 700,
29055
29109
  triggers: [
@@ -29068,6 +29122,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29068
29122
  lethalRadius: 5,
29069
29123
  injuryRadius: 16,
29070
29124
  energy: 2e5,
29125
+ tnt: 0.7,
29071
29126
  destructionLevel: 3,
29072
29127
  noise: 750,
29073
29128
  triggers: [
@@ -29086,6 +29141,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29086
29141
  lethalRadius: 5,
29087
29142
  injuryRadius: 16,
29088
29143
  energy: 2e5,
29144
+ tnt: 0.45,
29089
29145
  destructionLevel: 3,
29090
29146
  noise: 750,
29091
29147
  triggers: [
@@ -29104,6 +29160,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29104
29160
  lethalRadius: 5,
29105
29161
  injuryRadius: 16,
29106
29162
  energy: 2e5,
29163
+ tnt: 0.7,
29107
29164
  destructionLevel: 3,
29108
29165
  noise: 750,
29109
29166
  triggers: [
@@ -29122,6 +29179,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29122
29179
  lethalRadius: 4,
29123
29180
  injuryRadius: 12,
29124
29181
  energy: 102400,
29182
+ tnt: 1,
29125
29183
  destructionLevel: 2,
29126
29184
  noise: 550,
29127
29185
  fire: {
@@ -29155,6 +29213,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29155
29213
  lethalRadius: 5,
29156
29214
  injuryRadius: 14,
29157
29215
  energy: 2e5,
29216
+ tnt: 0.5,
29158
29217
  destructionLevel: 2,
29159
29218
  noise: 550,
29160
29219
  fire: {
@@ -29192,6 +29251,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29192
29251
  lethalRadius: 4,
29193
29252
  injuryRadius: 12,
29194
29253
  energy: 102400,
29254
+ tnt: 0.5,
29195
29255
  destructionLevel: 3,
29196
29256
  noise: 700,
29197
29257
  fire: {
@@ -29221,6 +29281,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29221
29281
  lethalRadius: 12,
29222
29282
  injuryRadius: 30,
29223
29283
  energy: 2764800,
29284
+ tnt: 20,
29224
29285
  destructionLevel: 3,
29225
29286
  noise: 700,
29226
29287
  fire: {
@@ -29248,6 +29309,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
29248
29309
  lethalRadius: 4,
29249
29310
  injuryRadius: 12,
29250
29311
  energy: 102400,
29312
+ tnt: 0.045,
29251
29313
  destructionLevel: 2,
29252
29314
  noise: 450,
29253
29315
  triggers: [
@@ -37966,6 +38028,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
37966
38028
  direction: this.facing,
37967
38029
  at: at3,
37968
38030
  energy: def.energy,
38031
+ ...def.tnt !== void 0 ? { tnt: def.tnt } : {},
37969
38032
  lethalRadius: def.lethalRadius,
37970
38033
  injuryRadius: def.injuryRadius,
37971
38034
  noise: def.noise,
@@ -45768,7 +45831,17 @@ void main(){
45768
45831
  if (onPlayer.zone !== "none") {
45769
45832
  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
45833
  }
45771
- context.emitHost({ type: "explosion", source: projectile.weapon, point: detonation.point, energy: detonation.energy, radius: reach, cause: detonation.cause, explosive: detonation.explosive });
45834
+ context.emitHost({
45835
+ type: "explosion",
45836
+ source: projectile.weapon,
45837
+ point: detonation.point,
45838
+ energy: detonation.energy,
45839
+ radius: reach,
45840
+ cause: detonation.cause,
45841
+ explosive: detonation.explosive,
45842
+ lethalRadius: detonation.lethalRadius,
45843
+ ...detonation.tnt !== void 0 ? { tnt: detonation.tnt } : {}
45844
+ });
45772
45845
  if (detonation.fire) context.startFire(projectile.weapon, detonation.point, detonation.fire, detonation.cause === "impact");
45773
45846
  };
45774
45847
  const detonateBare = (projectile, isImpact) => {
@@ -47937,6 +48010,9 @@ void main(){
47937
48010
  var LYING_PITCH = 1.05;
47938
48011
  var LYING_ROLL = 0.35;
47939
48012
  var LYING_PITCH_FACE_DOWN = -1.05;
48013
+ var STUN_PITCH = 0.05;
48014
+ var STUN_ROLL = 0.08;
48015
+ var STUN_FULL = 2;
47940
48016
  function createPlayer(THREE, o) {
47941
48017
  const cam = o.camera;
47942
48018
  let yaw = 0;
@@ -47976,11 +48052,16 @@ void main(){
47976
48052
  };
47977
48053
  let knocked = null;
47978
48054
  let knockedForward = false;
48055
+ let stunLeft = 0;
48056
+ let stunPhase = 0;
47979
48057
  let rollSide = 1;
47980
48058
  const apply = () => {
47981
48059
  const lying = knocked?.lying ?? 0;
47982
48060
  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");
48061
+ const sway = Math.min(1, stunLeft / STUN_FULL);
48062
+ const swayPitch = STUN_PITCH * sway * Math.sin(stunPhase * 1.3);
48063
+ const swayRoll = STUN_ROLL * sway * Math.sin(stunPhase * 0.9 + 1);
48064
+ euler.set(pitch + ((knockedForward ? LYING_PITCH_FACE_DOWN : LYING_PITCH) - pitch) * fallen + swayPitch, yaw, LYING_ROLL * rollSide * fallen + swayRoll, "YXZ");
47984
48065
  cam.quaternion.setFromEuler(euler);
47985
48066
  const standing = eye - CROUCH_DROP * crouch - bend;
47986
48067
  cam.position.set(px.x, floorLevel + y + standing + (LYING_EYE - standing) * fallen, px.z);
@@ -48254,11 +48335,15 @@ void main(){
48254
48335
  knocked.velocity = [knocked.velocity[0] + next.velocity[0], knocked.velocity[1] + next.velocity[1], knocked.velocity[2] + next.velocity[2]];
48255
48336
  knocked.airborne || (knocked.airborne = next.airborne);
48256
48337
  knocked.down || (knocked.down = next.down);
48338
+ knocked.wounded || (knocked.wounded = next.wounded);
48339
+ knocked.lieLeft = Math.max(knocked.lieLeft, next.lieLeft);
48257
48340
  } else knocked = { ...next, velocity: [...next.velocity] };
48341
+ stunLeft = Math.max(stunLeft, next.stunSeconds ?? 0);
48258
48342
  if (next.down) {
48259
- const right = Math.cos(yaw) * next.velocity[0] - Math.sin(yaw) * next.velocity[2];
48343
+ const away = next.away && (next.away[0] !== 0 || next.away[2] !== 0) ? next.away : next.velocity;
48344
+ const right = Math.cos(yaw) * away[0] - Math.sin(yaw) * away[2];
48260
48345
  rollSide = right >= 0 ? -1 : 1;
48261
- knockedForward = -Math.sin(yaw) * next.velocity[0] - Math.cos(yaw) * next.velocity[2] > 0;
48346
+ knockedForward = -Math.sin(yaw) * away[0] - Math.cos(yaw) * away[2] > 0;
48262
48347
  crouchLatched = false;
48263
48348
  }
48264
48349
  if (knocked.airborne) vy2 = Math.max(vy2, knocked.velocity[1]);
@@ -48269,7 +48354,14 @@ void main(){
48269
48354
  get knockedForward() {
48270
48355
  return knockedForward;
48271
48356
  },
48357
+ get stunned() {
48358
+ return stunLeft;
48359
+ },
48272
48360
  update(dt) {
48361
+ if (stunLeft > 0) {
48362
+ stunLeft = Math.max(0, stunLeft - dt);
48363
+ stunPhase += dt * 2.4;
48364
+ }
48273
48365
  if (knocked) {
48274
48366
  stepKnocked(dt);
48275
48367
  return;
@@ -51418,6 +51510,9 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
51418
51510
  "act.player_slammed": "The player was thrown into {what} at {speed} m/s",
51419
51511
  "act.player_fell": "The player hit the floor at {speed} m/s",
51420
51512
  "act.player_shoved": "The blast pushed the player at {speed} m/s ({reach}% got through the cover)",
51513
+ "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)",
51514
+ "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)",
51515
+ "act.player_stunned": "The blast stunned the player for {stun} s, {distance} m from it: dizzy, ears ringing",
51421
51516
  "act.came_apart": "{name} came apart: what stayed is loose and falls",
51422
51517
  "act.shoved": "{name} stands free: the shots moved it",
51423
51518
  "act.impact": "{name} hit {where} at {speed} m/s: {energy} J of blunt damage",
@@ -51780,6 +51875,9 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
51780
51875
  "act.player_slammed": "O jogador foi arremessado contra {what} a {speed} m/s",
51781
51876
  "act.player_fell": "O jogador bateu no ch\xE3o a {speed} m/s",
51782
51877
  "act.player_shoved": "A explos\xE3o empurrou o jogador a {speed} m/s ({reach}% passou pela cobertura)",
51878
+ "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)",
51879
+ "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)",
51880
+ "act.player_stunned": "A explos\xE3o atordoou o jogador por {stun} s, a {distance} m dela: tonto, com os ouvidos zumbindo",
51783
51881
  "act.came_apart": "{name} se desmontou: o que ficou est\xE1 solto e cai",
51784
51882
  "act.shoved": "{name} estava solto no ch\xE3o: os tiros o moveram",
51785
51883
  "act.impact": "{name} bateu em {where} a {speed} m/s: {energy} J de dano de impacto",
@@ -53509,7 +53607,19 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
53509
53607
  for (const ev of batch) {
53510
53608
  if (ev.type === "leak") puncture(ev);
53511
53609
  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 } : {} });
53610
+ else if (ev.type === "explode") {
53611
+ emitHost({
53612
+ type: "explosion",
53613
+ source: ev.object,
53614
+ point: ev.point,
53615
+ energy: ev.energy,
53616
+ radius: ev.radius,
53617
+ cause: "part",
53618
+ ...ev.effect !== void 0 ? { explosive: ev.effect } : {},
53619
+ ...ev.tnt !== void 0 ? { tnt: ev.tnt } : {},
53620
+ ...ev.lethalRadius !== void 0 ? { lethalRadius: ev.lethalRadius } : {}
53621
+ });
53622
+ }
53513
53623
  fire2("event", ev);
53514
53624
  options.onEvent?.(ev);
53515
53625
  }
@@ -54205,14 +54315,27 @@ ${SURFACE_GLSL}`).replace("#include <color_fragment>", "#include <color_fragment
54205
54315
  const knockPlayer = (event) => {
54206
54316
  if (seating.seat) return;
54207
54317
  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);
54318
+ const chest = [feet[0], feet[1] + KNOCK_CHEST, feet[2]];
54319
+ const reach = world.blastReach(event.point, chest, event.source ? { ignore: [event.source] } : {});
54320
+ const blast3 = {
54321
+ point: event.point,
54322
+ energy: event.energy,
54323
+ radius: event.radius,
54324
+ reach,
54325
+ ...event.tnt !== void 0 ? { tnt: event.tnt } : {},
54326
+ ...event.lethalRadius !== void 0 ? { lethalRadius: event.lethalRadius } : {}
54327
+ };
54328
+ const knock2 = knockOf(blast3, feet, playerMass, options.blastShove, options.blastImpulse);
54210
54329
  if (!knock2) return;
54211
54330
  player.knock(knock2);
54212
54331
  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
- });
54332
+ const distance4 = Math.hypot(chest[0] - event.point[0], chest[1] - event.point[1], chest[2] - event.point[2]);
54333
+ const data = { speed, reach, distance: distance4, down: knock2.down, wounded: Boolean(knock2.wounded), stunSeconds: knock2.stunSeconds ?? 0, source: event.source };
54334
+ const values = { speed: num(speed, 1), reach: num(reach * 100, 0), distance: num(distance4, 1), stun: num(knock2.stunSeconds ?? 0, 1) };
54335
+ if (knock2.wounded) action("player_wounded", t(speed >= KNOCK_DOWN_SPEED ? "act.player_thrown_wounded" : "act.player_wounded", values), { data });
54336
+ else if (knock2.down) action("player_knocked_down", t("act.knocked_down", values), { data });
54337
+ else if (speed > 0) action("player_shoved", t("act.player_shoved", values), { data });
54338
+ else action("player_stunned", t("act.player_stunned", values), { data });
54216
54339
  };
54217
54340
  const avatarState = { speed: 0, running: false, crouch: false, down: false, armed: false, pitch: 0 };
54218
54341
  let feetPrev = null;