@adatechnology/game-weapons-three 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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HostTool, HostEvent, TestableModule, AreaPlacement, TestableItem, DestructibleSpec, ToolClipSet } from '@adatechnology/game-contracts';
1
+ import { HostTool, HostEvent, TestableModule, AreaPlacement, TestableItem, EffectSink, DestructibleSpec, ToolClipSet } from '@adatechnology/game-contracts';
2
2
  import { CaliberDef, ReloadStageId, WeaponEvent, MountKind, AttachmentDef, FittedAttachment, Arsenal, ExplosiveDef, WeaponDef, CartridgeDims, CaseType, ReloadFamily } from '@adatechnology/game-weapons';
3
3
  import * as THREE_NS from 'three';
4
4
 
@@ -607,6 +607,27 @@ declare const WORLD_EXPLOSIVE_IDS: readonly string[];
607
607
  /** An explosive of the arsenal standing in the world: it goes off by a bullet, fire or another blast, after the delay its `chain` gives. */
608
608
  declare const worldExplosiveItem: (def: ExplosiveDef, name: string) => TestableItem | undefined;
609
609
 
610
+ /** The effect ids a weapon of a case type shows when it is seen from outside: its muzzle flash and its ejected case (the host's catalogue names them). */
611
+ interface WorldEffectIds {
612
+ readonly muzzle: string;
613
+ readonly casing: string;
614
+ }
615
+ declare const WORLD_EFFECTS: Readonly<Record<string, WorldEffectIds>>;
616
+ interface WorldEffectsOptions {
617
+ readonly arsenal: Arsenal;
618
+ /** The case type of a weapon (`pistol`, `rifle`, `shell`, `big`, `tiny`); default from its caliber. */
619
+ readonly caseTypeOf?: (weapon: string) => string | undefined;
620
+ /** The ids by case type; default `WORLD_EFFECTS`. A game adds or changes entries. */
621
+ readonly ids?: Readonly<Record<string, WorldEffectIds>>;
622
+ }
623
+ /**
624
+ * The shooter's flash and ejected case for a shot the viewer sees from outside (a `shot` event that carries `origin`), played through the host's
625
+ * effect sink. The player's own first-person shot has no `origin` and is drawn by the weapon in the hands, so nothing doubles.
626
+ */
627
+ declare function createWorldEffects(options: WorldEffectsOptions): {
628
+ handle(event: HostEvent, sink: EffectSink | undefined): number;
629
+ };
630
+
610
631
  /** A low-poly prop: its destruction spec (box parts only) and one colour per part. */
611
632
  interface WeaponProp {
612
633
  spec: DestructibleSpec;
@@ -702,4 +723,4 @@ declare function rigUnderbarrel(THREE: typeof THREE_NS, model: WeaponModel, def:
702
723
  */
703
724
  declare function rigStock(THREE: typeof THREE_NS, model: WeaponModel, def: Pick<WeaponDef, 'stock' | 'dimensions'>): void;
704
725
 
705
- export { ARCHETYPE_MODELS, ATTACHMENT_MODELS, type AttachmentBuildContext, type AttachmentModel, type AttachmentModelBuilder, type AttachmentModelRegistry, type AttachmentRig, BODY_ACTIONS, CLASS_REFERENCE_ENERGY, COUNTER_ITEM_ID, type CartridgeShape, type ClipContext, DEFAULT_ENERGY_CURVE, DEFAULT_THUMBNAIL_BASE, type DistanceCurve, type EnergyCurve, type FitAttachmentOptions, type FxLayers, GAME_DISTANCE_CURVE, GAME_SOUND_CLASSES, type LaserRaycast, MECHANICAL_SOUNDS, MODEL_ENTRIES, MOUNTED_M134_ITEM_ID, type MechanicalSoundId, type ModelEntry, type ModelFamily, type ModelKit, type ModelMaterials, type ModelOptions, type ModelParts, type MountFrame, type ShotExtras, type ShotSound, type ShotSoundClass, type ShotSoundInput, type ShotSoundOptions, type SoundClassDef, type SoundStep, WEAPON_ITEM_PREFIX, WEAPON_THUMBNAILS, WORLD_EXPLOSIVE_IDS, WORLD_EXPLOSIVE_PREFIX, type WeaponAudio, type WeaponAudioOptions, type WeaponFx, type WeaponFxOptions, type WeaponModel, type WeaponProp, type WeaponThumbnailEntry, type WeaponsTestModuleOptions, applyCondition, buildCartridge, buildCasing, buildProp, buildRound, buildToolClips, buildWeaponModel, buildWeaponModelById, cartridgeShape, computeShotSound, createWeaponAudio, createWeaponFx, createWeaponsTestModule, deriveFrames, fitAttachmentModels, hasWeaponModel, mechanicalSoundOf, modelKit, mountFrame, partRegistry, rigStock, rigUnderbarrel, weaponProp, weaponThumbnailCredit, weaponThumbnailUrl, weaponsLayout, worldExplosiveItem };
726
+ export { ARCHETYPE_MODELS, ATTACHMENT_MODELS, type AttachmentBuildContext, type AttachmentModel, type AttachmentModelBuilder, type AttachmentModelRegistry, type AttachmentRig, BODY_ACTIONS, CLASS_REFERENCE_ENERGY, COUNTER_ITEM_ID, type CartridgeShape, type ClipContext, DEFAULT_ENERGY_CURVE, DEFAULT_THUMBNAIL_BASE, type DistanceCurve, type EnergyCurve, type FitAttachmentOptions, type FxLayers, GAME_DISTANCE_CURVE, GAME_SOUND_CLASSES, type LaserRaycast, MECHANICAL_SOUNDS, MODEL_ENTRIES, MOUNTED_M134_ITEM_ID, type MechanicalSoundId, type ModelEntry, type ModelFamily, type ModelKit, type ModelMaterials, type ModelOptions, type ModelParts, type MountFrame, type ShotExtras, type ShotSound, type ShotSoundClass, type ShotSoundInput, type ShotSoundOptions, type SoundClassDef, type SoundStep, WEAPON_ITEM_PREFIX, WEAPON_THUMBNAILS, WORLD_EFFECTS, WORLD_EXPLOSIVE_IDS, WORLD_EXPLOSIVE_PREFIX, type WeaponAudio, type WeaponAudioOptions, type WeaponFx, type WeaponFxOptions, type WeaponModel, type WeaponProp, type WeaponThumbnailEntry, type WeaponsTestModuleOptions, type WorldEffectIds, type WorldEffectsOptions, applyCondition, buildCartridge, buildCasing, buildProp, buildRound, buildToolClips, buildWeaponModel, buildWeaponModelById, cartridgeShape, computeShotSound, createWeaponAudio, createWeaponFx, createWeaponsTestModule, createWorldEffects, deriveFrames, fitAttachmentModels, hasWeaponModel, mechanicalSoundOf, modelKit, mountFrame, partRegistry, rigStock, rigUnderbarrel, weaponProp, weaponThumbnailCredit, weaponThumbnailUrl, weaponsLayout, worldExplosiveItem };
@@ -35,6 +35,7 @@ var GameWeaponsThree = (() => {
35
35
  MOUNTED_M134_ITEM_ID: () => MOUNTED_M134_ITEM_ID,
36
36
  WEAPON_ITEM_PREFIX: () => WEAPON_ITEM_PREFIX,
37
37
  WEAPON_THUMBNAILS: () => WEAPON_THUMBNAILS,
38
+ WORLD_EFFECTS: () => WORLD_EFFECTS,
38
39
  WORLD_EXPLOSIVE_IDS: () => WORLD_EXPLOSIVE_IDS,
39
40
  WORLD_EXPLOSIVE_PREFIX: () => WORLD_EXPLOSIVE_PREFIX,
40
41
  applyCondition: () => applyCondition,
@@ -50,6 +51,7 @@ var GameWeaponsThree = (() => {
50
51
  createWeaponAudio: () => createWeaponAudio,
51
52
  createWeaponFx: () => createWeaponFx,
52
53
  createWeaponsTestModule: () => createWeaponsTestModule,
54
+ createWorldEffects: () => createWorldEffects,
53
55
  deriveFrames: () => deriveFrames,
54
56
  fitAttachmentModels: () => fitAttachmentModels,
55
57
  hasWeaponModel: () => hasWeaponModel,
@@ -19984,7 +19986,7 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
19984
19986
  shape: shape.shape,
19985
19987
  position: shape.position,
19986
19988
  ...SHELL,
19987
- explodes: { energy: def.energy, radius: def.injuryRadius, ...def.chain ? { chain: { ...def.chain } } : {} }
19989
+ explodes: { energy: def.energy, radius: def.injuryRadius, effect: def.id, ...def.chain ? { chain: { ...def.chain } } : {} }
19988
19990
  });
19989
19991
  var meshOf = (THREE, shape) => {
19990
19992
  const geometry = shape.shape.kind === "cylinder" ? new THREE.CylinderGeometry(shape.shape.radius, shape.shape.radius, shape.shape.height, 16) : shape.shape.kind === "box" ? new THREE.BoxGeometry(...shape.shape.size) : new THREE.SphereGeometry(0.2);
@@ -20012,6 +20014,51 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
20012
20014
  };
20013
20015
  };
20014
20016
 
20017
+ // src/data/world-effects.json
20018
+ var world_effects_default = {
20019
+ pistol: { muzzle: "muzzle_glock", casing: "casing_brass" },
20020
+ tiny: { muzzle: "muzzle_glock", casing: "casing_brass" },
20021
+ rifle: { muzzle: "muzzle_ak", casing: "casing_brass" },
20022
+ shell: { muzzle: "muzzle_r870", casing: "casing_shell" },
20023
+ big: { muzzle: "muzzle_barrett", casing: "casing_50" }
20024
+ };
20025
+
20026
+ // src/world-effects.ts
20027
+ var WORLD_EFFECTS = world_effects_default;
20028
+ var PORT_BACK = 0.35;
20029
+ var PORT_RIGHT = 0.04;
20030
+ function createWorldEffects(options) {
20031
+ const ids = options.ids ?? WORLD_EFFECTS;
20032
+ const caseTypeOf = options.caseTypeOf ?? ((weapon) => {
20033
+ const def = options.arsenal.weapons.get(weapon);
20034
+ return def && def.action.kind === "firearm" ? options.arsenal.calibers.get(def.action.caliber)?.caseType : void 0;
20035
+ });
20036
+ let counter = 0;
20037
+ return {
20038
+ /** Plays what the event asks for and answers how many effects started. */
20039
+ handle(event, sink) {
20040
+ if (!sink || event.type !== "shot" || !event.origin) return 0;
20041
+ const caseType = caseTypeOf(event.weapon) ?? "rifle";
20042
+ const entry = ids[caseType];
20043
+ if (!entry) return 0;
20044
+ const direction = event.direction ?? [0, 0, -1];
20045
+ const seed = ++counter;
20046
+ let started = 0;
20047
+ for (let shot = 0; shot < Math.max(1, event.rounds ?? 1); shot++) {
20048
+ if (sink.play({ effect: entry.muzzle, point: event.origin, direction, seed: seed * 8 + shot }) !== null) started++;
20049
+ }
20050
+ const side = [direction[2], 0, -direction[0]];
20051
+ const port = [
20052
+ event.origin[0] - direction[0] * PORT_BACK + side[0] * PORT_RIGHT,
20053
+ event.origin[1] - direction[1] * PORT_BACK,
20054
+ event.origin[2] - direction[2] * PORT_BACK + side[2] * PORT_RIGHT
20055
+ ];
20056
+ if (sink.play({ effect: entry.casing, point: port, direction, seed: seed * 8 + 7 }) !== null) started++;
20057
+ return started;
20058
+ }
20059
+ };
20060
+ }
20061
+
20015
20062
  // src/area.ts
20016
20063
  var WEAPON_ITEM_PREFIX = "weapon_";
20017
20064
  var COUNTER_ITEM_ID = "counter_weapons";
@@ -20288,7 +20335,9 @@ ${problems.map((problem) => ` - ${problem}`).join("\n")}`);
20288
20335
  } else if (change.kind === "control" && change.id === "sound_distance" && typeof change.value === "number") soundDistance = change.value;
20289
20336
  else if (change.kind === "control" && change.id === "fuse_length" && typeof change.value === "number") lent.configureTool?.({ fuse_length: String(change.value) });
20290
20337
  });
20338
+ const worldEffects = createWorldEffects({ arsenal });
20291
20339
  const offEvents = lent.on((event) => {
20340
+ worldEffects.handle(event, lent.effects);
20292
20341
  if (event.type === "explosion") {
20293
20342
  const away = Math.hypot(event.point[0] - camera.position.x, event.point[1] - camera.position.y, event.point[2] - camera.position.z);
20294
20343
  audio?.play("explosion", Math.min(1, EXPLOSION_VOLUME_BASE + event.radius * EXPLOSION_VOLUME_PER_METRE), away);