@adatechnology/game-weapons-three 0.14.2 → 0.15.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.
@@ -1,9 +1,12 @@
1
1
  // src/test-area/area.ts
2
- import { markKitOrigin as markKitOrigin5 } from "@adatechnology/game-contracts";
3
- import { createArsenal } from "@adatechnology/game-weapons";
2
+ import { markKitOrigin as markKitOrigin6 } from "@adatechnology/game-contracts";
3
+ import { createArsenal, genericWeapons } from "@adatechnology/game-weapons";
4
4
  import { furnitureSpec } from "@adatechnology/game-floorplan-three";
5
5
  import { isLightBudget, isRenderPools } from "@adatechnology/game-render-three";
6
6
 
7
+ // src/test-area/accessory-bench.ts
8
+ import { aimZoomOf } from "@adatechnology/game-weapons";
9
+
7
10
  // src/attachments-key.ts
8
11
  var attachmentsKey = (parts) => {
9
12
  if (!parts) return "";
@@ -119,11 +122,11 @@ function createAccessoryBench(options) {
119
122
  else return false;
120
123
  return true;
121
124
  };
122
- const zoomOf = (tool) => {
123
- const optic = (tool.attachments ?? []).map((part) => arsenal.attachments.get(part.id)?.optic).find((entry) => entry !== void 0);
124
- return optic?.zoom[0] ?? 1;
125
+ const zoomOf = (tool) => aimZoomOf(arsenal, tool.weapon, tool.attachments?.map((part) => part.id));
126
+ const aims = (tool) => {
127
+ const kind = arsenal.weapons.get(tool.weapon)?.action.kind;
128
+ return kind === "firearm" || kind === "launcher";
125
129
  };
126
- const isFirearm = (tool) => arsenal.weapons.get(tool.weapon)?.action.kind === "firearm";
127
130
  const setAim = (on) => {
128
131
  if (on === aiming) return;
129
132
  aiming = on;
@@ -150,7 +153,7 @@ function createAccessoryBench(options) {
150
153
  };
151
154
  const isOnRoom = (event) => Boolean(document.pointerLockElement) || event.target instanceof HTMLCanvasElement;
152
155
  const onMouseDown = (event) => {
153
- if (event.button !== SECONDARY_BUTTON || !isOnRoom(event) || !isFirearm(host.tool())) return;
156
+ if (event.button !== SECONDARY_BUTTON || !isOnRoom(event) || !aims(host.tool())) return;
154
157
  event.preventDefault();
155
158
  event.stopImmediatePropagation();
156
159
  setAim(true);
@@ -2833,6 +2836,279 @@ function drawItemWeapon({ THREE, def, items, itemId, turn: turn2, flip }) {
2833
2836
  return group;
2834
2837
  }
2835
2838
 
2839
+ // src/models/explosives.ts
2840
+ import { markKitOrigin as markKitOrigin2 } from "@adatechnology/game-contracts";
2841
+
2842
+ // src/models/kit.ts
2843
+ import { markShared, shapeGeometries as shapeGeometries2 } from "@adatechnology/game-shapes-three";
2844
+
2845
+ // src/models/rounded-box.ts
2846
+ var uvAlong = (THREE, faceDirection, normal, uvAxis, projectionAxis, radius, sideLength) => {
2847
+ const arcLength = 2 * Math.PI * radius / 4;
2848
+ const centerLength = Math.max(sideLength - 2 * radius, 0);
2849
+ const halfArc = Math.PI / 4;
2850
+ const projected = new THREE.Vector3().copy(normal);
2851
+ projected[projectionAxis] = 0;
2852
+ projected.normalize();
2853
+ const arcUvRatio = 0.5 * arcLength / (arcLength + centerLength);
2854
+ const arcAngleRatio = 1 - projected.angleTo(faceDirection) / halfArc;
2855
+ if (Math.sign(projected[uvAxis]) === 1) return arcAngleRatio * arcUvRatio;
2856
+ const centerUv = centerLength / (arcLength + centerLength);
2857
+ return centerUv + arcUvRatio + arcUvRatio * (1 - arcAngleRatio);
2858
+ };
2859
+ var FACES = [
2860
+ { direction: [1, 0, 0], u: ["z", "y", false], v: ["y", "z", true] },
2861
+ { direction: [-1, 0, 0], u: ["z", "y", true], v: ["y", "z", true] },
2862
+ { direction: [0, 1, 0], u: ["x", "z", true], v: ["z", "x", false] },
2863
+ { direction: [0, -1, 0], u: ["x", "z", true], v: ["z", "x", true] },
2864
+ { direction: [0, 0, 1], u: ["x", "y", true], v: ["y", "x", true] },
2865
+ { direction: [0, 0, -1], u: ["x", "y", false], v: ["y", "x", true] }
2866
+ ];
2867
+ function createRoundedBoxGeometry(THREE, width, height, depth, segments, radius) {
2868
+ const count = segments * 2 + 1;
2869
+ const rounding = Math.min(width / 2, height / 2, depth / 2, radius);
2870
+ const geometry = new THREE.BoxGeometry(1, 1, 1, count, count, count);
2871
+ const flat2 = geometry.toNonIndexed();
2872
+ geometry.setIndex(null);
2873
+ geometry.setAttribute("position", flat2.attributes.position);
2874
+ geometry.setAttribute("normal", flat2.attributes.normal);
2875
+ geometry.setAttribute("uv", flat2.attributes.uv);
2876
+ const position = new THREE.Vector3();
2877
+ const normal = new THREE.Vector3();
2878
+ const box2 = new THREE.Vector3(width, height, depth).divideScalar(2).subScalar(rounding);
2879
+ const positions = geometry.attributes.position.array;
2880
+ const normals = geometry.attributes.normal.array;
2881
+ const uvs = geometry.attributes.uv.array;
2882
+ const faceTriangles = positions.length / 6;
2883
+ const halfSegment = 0.5 / count;
2884
+ const sides = { x: width, y: height, z: depth };
2885
+ for (let i = 0, j = 0; i < positions.length; i += 3, j += 2) {
2886
+ position.fromArray(positions, i);
2887
+ normal.copy(position);
2888
+ normal.x -= Math.sign(normal.x) * halfSegment;
2889
+ normal.y -= Math.sign(normal.y) * halfSegment;
2890
+ normal.z -= Math.sign(normal.z) * halfSegment;
2891
+ normal.normalize();
2892
+ positions[i] = box2.x * Math.sign(position.x) + normal.x * rounding;
2893
+ positions[i + 1] = box2.y * Math.sign(position.y) + normal.y * rounding;
2894
+ positions[i + 2] = box2.z * Math.sign(position.z) + normal.z * rounding;
2895
+ normals[i] = normal.x;
2896
+ normals[i + 1] = normal.y;
2897
+ normals[i + 2] = normal.z;
2898
+ const face = FACES[Math.floor(i / faceTriangles)];
2899
+ if (!face) continue;
2900
+ const direction = new THREE.Vector3(...face.direction);
2901
+ for (const [offset, [uvAxis, projectionAxis, mirrored]] of [[0, face.u], [1, face.v]]) {
2902
+ const value = uvAlong(THREE, direction, normal, uvAxis, projectionAxis, rounding, sides[uvAxis]);
2903
+ uvs[j + offset] = mirrored ? 1 - value : value;
2904
+ }
2905
+ }
2906
+ return geometry;
2907
+ }
2908
+
2909
+ // src/models/kit.ts
2910
+ var PI = Math.PI;
2911
+ var createMaterials = (THREE) => {
2912
+ const standard = (color, roughness = 0.7, metalness = 0.2, extra = {}) => new THREE.MeshStandardMaterial({ color, roughness, metalness, ...extra });
2913
+ const basic = (color, extra = {}) => new THREE.MeshBasicMaterial({ color, ...extra });
2914
+ const additive = { transparent: true, opacity: 0.95, blending: THREE.AdditiveBlending, depthWrite: false };
2915
+ const materials = {
2916
+ metal: standard(3422010, 0.42, 0.75),
2917
+ steel: standard(7106418, 0.32, 0.85),
2918
+ steelL: standard(9080208, 0.28, 0.9),
2919
+ dark: standard(1711133, 0.55, 0.5),
2920
+ poly: standard(2369058, 0.8, 0.05),
2921
+ olive: standard(4936241, 0.8, 0.05),
2922
+ tan: standard(10259043, 0.8, 0.05),
2923
+ wood: standard(4860951, 0.55, 0),
2924
+ woodL: standard(6175263, 0.55, 0),
2925
+ brass: standard(12819013, 0.28, 0.9),
2926
+ copper: standard(11561531, 0.3, 0.85),
2927
+ lead: standard(7171955, 0.5, 0.5),
2928
+ shell: standard(10692380, 0.5, 0.05),
2929
+ glass: standard(8364968, 0.05, 0.1, { transparent: true, opacity: 0.42 }),
2930
+ lens: standard(2311770, 0.05, 0.3, { transparent: true, opacity: 0.9, emissive: 399392 }),
2931
+ rubber: standard(1381653, 0.85, 0),
2932
+ white: standard(13617332, 0.8, 0),
2933
+ hole: standard(526343, 1, 0),
2934
+ bake: standard(5911072, 0.55, 0),
2935
+ rust: standard(7027230, 0.95, 0.3),
2936
+ rustD: standard(4860440, 0.95, 0.25),
2937
+ worn: standard(5593178, 0.6, 0.6),
2938
+ fire: basic(16742946),
2939
+ fire2: basic(16764762),
2940
+ flashA: basic(16757575, additive),
2941
+ flashB: basic(16773568, additive),
2942
+ smokeP: standard(9079430, 1, 0, { transparent: true, opacity: 0.35, depthWrite: false }),
2943
+ /** Launcher paints. */
2944
+ yellow: standard(11570978, 0.6, 0),
2945
+ red: standard(9313300, 0.45, 0.1),
2946
+ redPaint: standard(10691354, 0.35, 0.2),
2947
+ tape: standard(9079944, 0.9, 0),
2948
+ pipe: standard(10115634, 0.35, 0.8),
2949
+ green: standard(3818536, 0.75, 0.05),
2950
+ /** Explosive finishes: the molotov's bottle, the dynamite's paper, the C4's putty and the detonator's wire. */
2951
+ bottle: standard(3103284, 0.1, 0.1, { transparent: true, opacity: 0.72 }),
2952
+ dynamite: standard(10365471, 0.7, 0),
2953
+ c4: standard(7170636, 0.95, 0),
2954
+ wire: standard(11542560, 0.5, 0),
2955
+ /** Magazine finishes named by the options. */
2956
+ bakelite: standard(6958620, 0.6, 0),
2957
+ polyMag: standard(2763302, 0.85, 0)
2958
+ };
2959
+ markShared(...Object.values(materials));
2960
+ return materials;
2961
+ };
2962
+ var kits = /* @__PURE__ */ new WeakMap();
2963
+ function modelKit(host) {
2964
+ const cached = kits.get(host);
2965
+ if (cached) return cached;
2966
+ const THREE = shapeGeometries2(host);
2967
+ const geometries = /* @__PURE__ */ new Map();
2968
+ const shadowed = (mesh) => {
2969
+ mesh.castShadow = true;
2970
+ mesh.receiveShadow = true;
2971
+ return mesh;
2972
+ };
2973
+ const place = (parent, mesh, x, y, z) => {
2974
+ mesh.position.set(x, y, z);
2975
+ parent.add(mesh);
2976
+ return mesh;
2977
+ };
2978
+ const kit = {
2979
+ THREE,
2980
+ M: createMaterials(THREE),
2981
+ roundness: null,
2982
+ group: () => new THREE.Group(),
2983
+ box(parent, w, h, d, material, x, y, z, rz = 0, ry = 0, rx = 0) {
2984
+ const radius = Math.min(w, h, d) * (kit.roundness ?? 0.2);
2985
+ const key = `${w.toFixed(4)},${h.toFixed(4)},${d.toFixed(4)},${radius.toFixed(5)}`;
2986
+ let geometry = geometries.get(key);
2987
+ if (!geometry) {
2988
+ geometry = radius > 25e-5 ? createRoundedBoxGeometry(THREE, w, h, d, 2, radius) : new THREE.BoxGeometry(w, h, d);
2989
+ markShared(geometry);
2990
+ geometries.set(key, geometry);
2991
+ }
2992
+ const mesh = place(parent, shadowed(new THREE.Mesh(geometry, material)), x, y, z);
2993
+ mesh.rotation.set(rx, ry, rz);
2994
+ return mesh;
2995
+ },
2996
+ cylinderX(parent, radius, length, material, x, y, z, segments = 16, radiusEnd) {
2997
+ const mesh = shadowed(new THREE.Mesh(new THREE.CylinderGeometry(radiusEnd ?? radius, radius, length, segments), material));
2998
+ mesh.rotation.z = -PI / 2;
2999
+ return place(parent, mesh, x, y, z);
3000
+ },
3001
+ cylinderY(parent, radius, height, material, x, y, z, segments = 16, radiusEnd) {
3002
+ return place(parent, shadowed(new THREE.Mesh(new THREE.CylinderGeometry(radiusEnd ?? radius, radius, height, segments), material)), x, y, z);
3003
+ },
3004
+ cylinderZ(parent, radius, length, material, x, y, z, segments = 16) {
3005
+ const mesh = shadowed(new THREE.Mesh(new THREE.CylinderGeometry(radius, radius, length, segments), material));
3006
+ mesh.rotation.x = PI / 2;
3007
+ return place(parent, mesh, x, y, z);
3008
+ },
3009
+ sphere(parent, radius, material, x, y, z, detail = 1) {
3010
+ return place(parent, shadowed(new THREE.Mesh(new THREE.IcosahedronGeometry(radius, detail), material)), x, y, z);
3011
+ },
3012
+ torus: (parent, geometry, material, x, y, z) => place(parent, shadowed(new THREE.Mesh(geometry, material)), x, y, z),
3013
+ mark(parent, x, y, z) {
3014
+ const object = new THREE.Object3D();
3015
+ object.position.set(x, y, z);
3016
+ parent.add(object);
3017
+ return object;
3018
+ },
3019
+ pivot(parent, x = 0, y = 0, z = 0) {
3020
+ const group = new THREE.Group();
3021
+ group.position.set(x, y, z);
3022
+ parent.add(group);
3023
+ return group;
3024
+ }
3025
+ };
3026
+ kits.set(host, kit);
3027
+ return kit;
3028
+ }
3029
+ function heatify(glow, mesh) {
3030
+ mesh.material = mesh.material.clone();
3031
+ glow.push(mesh);
3032
+ return mesh;
3033
+ }
3034
+
3035
+ // src/models/explosives.ts
3036
+ var grenade = (kit, group) => {
3037
+ const { M, sphere, cylinderY, box: box2, torus, THREE } = kit;
3038
+ sphere(group, 0.032, M.olive, 0, 0.032, 0, 1);
3039
+ cylinderY(group, 0.011, 0.022, M.metal, 0, 0.072, 0);
3040
+ box2(group, 0.012, 0.07, 6e-3, M.metal, 0.016, 0.05, 0, 0.2);
3041
+ torus(group, new THREE.TorusGeometry(85e-4, 16e-4, 6, 12), M.steel, -0.012, 0.082, 0);
3042
+ kit.mark(group, 0, 0.032, 0).name = "handle";
3043
+ };
3044
+ var molotov = (kit, group) => {
3045
+ const { M, cylinderY, box: box2 } = kit;
3046
+ cylinderY(group, 0.036, 0.17, M.bottle, 0, 0.085, 0, 10);
3047
+ cylinderY(group, 0.012, 0.06, M.bottle, 0, 0.2, 0, 8, 0.036);
3048
+ box2(group, 0.03, 0.08, 0.02, M.white, 5e-3, 0.25, 0, 0.3);
3049
+ cylinderY(group, 0.02, 0.07, M.fire, 0.01, 0.31, 0, 6, 0).userData.flame = true;
3050
+ kit.mark(group, 0, 0.085, 0).name = "handle";
3051
+ };
3052
+ var pipeBomb = (kit, group) => {
3053
+ const { M, cylinderX, cylinderY } = kit;
3054
+ cylinderX(group, 0.025, 0.2, M.metal, 0, 0.03, 0, 8);
3055
+ cylinderX(group, 0.031, 0.025, M.dark, -0.1, 0.03, 0, 6);
3056
+ cylinderX(group, 0.031, 0.025, M.dark, 0.1, 0.03, 0, 6);
3057
+ cylinderY(group, 2e-3, 0.06, M.white, 0.1, 0.07, 0);
3058
+ kit.mark(group, 0, 0.03, 0).name = "handle";
3059
+ };
3060
+ var dynamite = (kit, group) => {
3061
+ const { M, cylinderX } = kit;
3062
+ for (const [z, y] of [[-0.015, 0], [0.015, 0], [0, 0.026]]) cylinderX(group, 0.014, 0.2, M.dynamite, 0, 0.016 + y, z, 8);
3063
+ cylinderX(group, 0.031, 0.03, M.dark, -0.04, 0.025, 0, 8);
3064
+ cylinderX(group, 0.031, 0.03, M.dark, 0.05, 0.025, 0, 8);
3065
+ cylinderX(group, 2e-3, 0.1, M.white, 0.14, 0.03, 0, 4);
3066
+ kit.mark(group, 0, 0.025, 0).name = "handle";
3067
+ };
3068
+ var c4 = (kit, group) => {
3069
+ const { M, box: box2, cylinderY } = kit;
3070
+ box2(group, 0.28, 0.05, 0.11, M.c4, 0, 0.025, 0);
3071
+ box2(group, 0.05, 0.03, 0.04, M.dark, 0.06, 0.065, 0);
3072
+ cylinderY(group, 4e-3, 0.05, M.metal, 0.03, 0.07, 0);
3073
+ box2(group, 0.24, 3e-3, 3e-3, M.wire, 0.18, 0.055, 0.02);
3074
+ box2(group, 0.24, 3e-3, 3e-3, M.metal, 0.18, 0.055, 0.03);
3075
+ kit.mark(group, 0, 0.025, 0).name = "handle";
3076
+ };
3077
+ var claymore = (kit, group) => {
3078
+ const { M, box: box2 } = kit;
3079
+ for (const i of [-1, 0, 1]) box2(group, 0.075, 0.083, 0.035, M.olive, i * 0.072, 0.12, Math.abs(i) * 0.012, 0, -i * 0.25);
3080
+ for (const [x, z] of [[-0.07, 0.03], [-0.07, -0.03], [0.07, 0.03], [0.07, -0.03]]) box2(group, 4e-3, 0.09, 4e-3, M.metal, x, 0.04, z, x > 0 ? -0.25 : 0.25);
3081
+ kit.mark(group, 0, 0.12, 0).name = "handle";
3082
+ };
3083
+ var flashbang = (kit, group) => {
3084
+ const { M, cylinderY, box: box2 } = kit;
3085
+ cylinderY(group, 0.022, 0.13, M.olive, 0, 0.065, 0, 10);
3086
+ for (let i = 0; i < 4; i++) cylinderY(group, 0.023, 8e-3, M.dark, 0, 0.025 + i * 0.028, 0, 10);
3087
+ cylinderY(group, 0.012, 0.02, M.metal, 0, 0.14, 0);
3088
+ box2(group, 0.01, 0.08, 5e-3, M.metal, 0.024, 0.1, 0, 0.15);
3089
+ kit.mark(group, 0, 0.065, 0).name = "handle";
3090
+ };
3091
+ var BUILDERS = {
3092
+ m67: grenade,
3093
+ molotov,
3094
+ pipe_bomb: pipeBomb,
3095
+ dynamite,
3096
+ c4,
3097
+ claymore,
3098
+ flashbang
3099
+ };
3100
+ var EXPLOSIVE_MODEL_IDS = Object.keys(BUILDERS);
3101
+ var hasExplosiveModel = (explosiveId) => explosiveId in BUILDERS;
3102
+ function buildExplosiveModel(THREE, explosiveId) {
3103
+ const builder = BUILDERS[explosiveId];
3104
+ if (!builder) return void 0;
3105
+ const kit = modelKit(THREE);
3106
+ const group = markKitOrigin2(kit.group(), { owner: "game-weapons-three", kind: "explosive", id: explosiveId });
3107
+ group.name = explosiveId;
3108
+ builder(kit, group);
3109
+ return group;
3110
+ }
3111
+
2836
3112
  // src/weapon-item-model.ts
2837
3113
  var SHIPPED_WEAPON_ITEM_MODELS = [
2838
3114
  ["wrench", { item: "adjustable_wrench" }],
@@ -2841,8 +3117,12 @@ var SHIPPED_WEAPON_ITEM_MODELS = [
2841
3117
  ["crossbow", { turn: 1 }],
2842
3118
  ["knife", { flip: true }],
2843
3119
  ["meat_cleaver", { flip: true }],
2844
- ["screwdriver", { flip: true }]
3120
+ ["screwdriver", { flip: true }],
3121
+ // the Itens do Vale Cinzento hammer is a claw hammer: the head and the two-pronged claw
3122
+ ["claw_hammer", { item: "hammer" }]
2845
3123
  ];
3124
+ var hasThrowableModel = (def) => def.action?.kind === "throwable" && def.action.explosive !== void 0 && hasExplosiveModel(def.action.explosive);
3125
+ var buildThrowableModel = (THREE, def) => def.action?.kind === "throwable" && def.action.explosive !== void 0 ? buildExplosiveModel(THREE, def.action.explosive) : void 0;
2846
3126
  var weaponPropsOver = (models) => {
2847
3127
  const itemModelOf = (weaponId, items) => {
2848
3128
  const id = models.get(weaponId)?.item ?? weaponId;
@@ -2858,7 +3138,7 @@ var weaponPropsOver = (models) => {
2858
3138
  get: (weaponId) => models.get(weaponId),
2859
3139
  itemModelOf,
2860
3140
  buildItemModel,
2861
- buildModel: (THREE, def, items) => buildItemModel(THREE, def, items) ?? buildProp(THREE, weaponProp(def))
3141
+ buildModel: (THREE, def, items) => buildItemModel(THREE, def, items) ?? buildThrowableModel(THREE, def) ?? buildProp(THREE, weaponProp(def))
2862
3142
  };
2863
3143
  };
2864
3144
  var defaultProps;
@@ -2870,6 +3150,7 @@ var buildWeaponModel = (THREE, def, items) => defaultWeaponProps().buildModel(TH
2870
3150
 
2871
3151
  // src/box-fx.ts
2872
3152
  var EJECTS_ON_SHOT = /* @__PURE__ */ new Set(["pistol", "smg", "carbine", "rifle", "sniper", "shotgun"]);
3153
+ var SPOUTS = /* @__PURE__ */ new Set(["firearm", "flame", "heat", "extinguisher"]);
2873
3154
  var DROPS_MAGAZINE = /* @__PURE__ */ new Set(["pistol", "smg", "carbine", "rifle", "sniper"]);
2874
3155
  var CYCLES = /* @__PURE__ */ new Set(["shotgun_pump_12", "rifle_bolt_308"]);
2875
3156
  var FALLBACK_ENERGY = 300;
@@ -2904,6 +3185,7 @@ function createBoxFx(options) {
2904
3185
  const q = new THREE.Quaternion();
2905
3186
  const q2 = new THREE.Quaternion();
2906
3187
  const pointForward = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
3188
+ const frontOf = new THREE.Vector3();
2907
3189
  const equip = (weapon) => {
2908
3190
  if (model) hand.remove(model);
2909
3191
  model = null;
@@ -2914,6 +3196,7 @@ function createBoxFx(options) {
2914
3196
  const length = new THREE.Box3().setFromObject(model).getSize(v).x;
2915
3197
  rear = new THREE.Box3().setFromObject(model).min.x;
2916
3198
  lengthMetres = length;
3199
+ frontOf.copy(new THREE.Box3().setFromObject(model).getCenter(v)).setX(rear + length);
2917
3200
  model.quaternion.copy(pointForward);
2918
3201
  hand.add(model);
2919
3202
  inHand = def.id;
@@ -3014,6 +3297,13 @@ function createBoxFx(options) {
3014
3297
  inHand = null;
3015
3298
  hold = null;
3016
3299
  },
3300
+ muzzle() {
3301
+ if (!model || !def || !SPOUTS.has(def.action.kind)) return null;
3302
+ hand.updateMatrixWorld(true);
3303
+ const at = model.localToWorld(v.copy(frontOf));
3304
+ const along = v2.set(1, 0, 0).applyQuaternion(model.getWorldQuaternion(q2)).normalize();
3305
+ return { position: [at.x, at.y, at.z], direction: [along.x, along.y, along.z], drawsFlash: false };
3306
+ },
3017
3307
  handTargets() {
3018
3308
  if (!model || !hold) return null;
3019
3309
  hand.updateMatrixWorld(true);
@@ -3065,7 +3355,7 @@ function createBoxFx(options) {
3065
3355
  }
3066
3356
 
3067
3357
  // src/models/index.ts
3068
- import { markKitOrigin as markKitOrigin2 } from "@adatechnology/game-contracts";
3358
+ import { markKitOrigin as markKitOrigin3 } from "@adatechnology/game-contracts";
3069
3359
  import { WEAPONS } from "@adatechnology/game-weapons";
3070
3360
 
3071
3361
  // src/data/models.json
@@ -4618,302 +4908,74 @@ var models_default = {
4618
4908
  options: {
4619
4909
  rpm: 3e3
4620
4910
  }
4911
+ },
4912
+ m79: {
4913
+ builder: "breakLauncher",
4914
+ caliber: "40\xD746",
4915
+ length: 730,
4916
+ barrel: 356,
4917
+ options: {
4918
+ stock: "fixed",
4919
+ wood: 1,
4920
+ mag: "none",
4921
+ hg: "wood",
4922
+ hgFrac: 0.4,
4923
+ br: 0.022,
4924
+ hR: 0.05
4925
+ }
4621
4926
  }
4622
4927
  };
4623
4928
 
4624
- // src/models/kit.ts
4625
- import { markShared, shapeGeometries as shapeGeometries2 } from "@adatechnology/game-shapes-three";
4929
+ // src/models/break-action.ts
4930
+ function buildBreakAction(kit, options) {
4931
+ const { M, box: box2, cylinderX, sphere } = kit;
4932
+ const group = kit.group();
4933
+ const L = (options.L ?? 0) / 1e3;
4934
+ const barrel = (options.B ?? 0) / 1e3;
4935
+ const parts = { family: "break", kick: 1.6, glow: [] };
4936
+ box2(group, 0.09, 0.05, 0.042, M.steel, L - barrel - 0.04, -5e-3, 0);
4937
+ for (let i = 0; i < 4; i++) box2(group, 0.05, 2e-3, 0.043, M.dark, L - barrel - 0.04, 0.01 - i * 8e-3, 0);
4938
+ const barrels = kit.pivot(group, L - barrel, -0.01, 0);
4939
+ parts.barrels = barrels;
4940
+ heatify(parts.glow, cylinderX(barrels, 0.0105, barrel, M.steel, barrel / 2, 0.01, 0.011, 18));
4941
+ heatify(parts.glow, cylinderX(barrels, 0.0105, barrel, M.steel, barrel / 2, 0.01, -0.011, 18));
4942
+ box2(barrels, barrel, 4e-3, 8e-3, M.metal, barrel / 2, 0.022, 0);
4943
+ sphere(barrels, 2e-3, M.white, barrel - 0.01, 0.026, 0, 1);
4944
+ cylinderX(barrels, 75e-4, 4e-3, M.hole, barrel + 1e-3, 0.01, 0.011, 12);
4945
+ cylinderX(barrels, 75e-4, 4e-3, M.hole, barrel + 1e-3, 0.01, -0.011, 12);
4946
+ box2(barrels, 0.22, 0.03, 0.038, M.wood, 0.12, -0.01, 0);
4947
+ parts.breech = [kit.mark(barrels, 4e-3, 0.01, 0.011), kit.mark(barrels, 4e-3, 0.01, -0.011)];
4948
+ box2(group, 0.12, 0.042, 0.032, M.wood, L - barrel - 0.14, -0.022, 0, 0.14);
4949
+ box2(group, 0.28, 0.07, 0.036, M.wood, 0.15, -0.05, 0, 0.08);
4950
+ box2(group, 0.012, 0.13, 0.04, M.rubber, 6e-3, -0.06, 0);
4951
+ box2(group, 4e-3, 0.014, 4e-3, M.dark, L - barrel - 0.05, -0.04, 0);
4952
+ parts.trigger = box2(group, 4e-3, 0.014, 4e-3, M.dark, L - barrel - 0.035, -0.04, 0);
4953
+ box2(group, 0.04, 4e-3, 0.01, M.steel, L - barrel - 0.045, -0.05, 0);
4954
+ box2(group, 0.012, 0.016, 6e-3, M.dark, L - barrel - 0.08, 0.022, 0.012, 0.5);
4955
+ box2(group, 0.012, 0.016, 6e-3, M.dark, L - barrel - 0.08, 0.022, -0.012, 0.5);
4956
+ parts.safety = box2(group, 0.02, 6e-3, 8e-3, M.steel, L - barrel - 0.07, 0.025, 0);
4957
+ parts.muzzle = kit.mark(barrels, barrel + 0.01, 0.01, 0);
4958
+ parts.eject = parts.breech[0];
4959
+ return { group, parts };
4960
+ }
4626
4961
 
4627
- // src/models/rounded-box.ts
4628
- var uvAlong = (THREE, faceDirection, normal, uvAxis, projectionAxis, radius, sideLength) => {
4629
- const arcLength = 2 * Math.PI * radius / 4;
4630
- const centerLength = Math.max(sideLength - 2 * radius, 0);
4631
- const halfArc = Math.PI / 4;
4632
- const projected = new THREE.Vector3().copy(normal);
4633
- projected[projectionAxis] = 0;
4634
- projected.normalize();
4635
- const arcUvRatio = 0.5 * arcLength / (arcLength + centerLength);
4636
- const arcAngleRatio = 1 - projected.angleTo(faceDirection) / halfArc;
4637
- if (Math.sign(projected[uvAxis]) === 1) return arcAngleRatio * arcUvRatio;
4638
- const centerUv = centerLength / (arcLength + centerLength);
4639
- return centerUv + arcUvRatio + arcUvRatio * (1 - arcAngleRatio);
4640
- };
4641
- var FACES = [
4642
- { direction: [1, 0, 0], u: ["z", "y", false], v: ["y", "z", true] },
4643
- { direction: [-1, 0, 0], u: ["z", "y", true], v: ["y", "z", true] },
4644
- { direction: [0, 1, 0], u: ["x", "z", true], v: ["z", "x", false] },
4645
- { direction: [0, -1, 0], u: ["x", "z", true], v: ["z", "x", true] },
4646
- { direction: [0, 0, 1], u: ["x", "y", true], v: ["y", "x", true] },
4647
- { direction: [0, 0, -1], u: ["x", "y", false], v: ["y", "x", true] }
4648
- ];
4649
- function createRoundedBoxGeometry(THREE, width, height, depth, segments, radius) {
4650
- const count = segments * 2 + 1;
4651
- const rounding = Math.min(width / 2, height / 2, depth / 2, radius);
4652
- const geometry = new THREE.BoxGeometry(1, 1, 1, count, count, count);
4653
- const flat2 = geometry.toNonIndexed();
4654
- geometry.setIndex(null);
4655
- geometry.setAttribute("position", flat2.attributes.position);
4656
- geometry.setAttribute("normal", flat2.attributes.normal);
4657
- geometry.setAttribute("uv", flat2.attributes.uv);
4658
- const position = new THREE.Vector3();
4659
- const normal = new THREE.Vector3();
4660
- const box2 = new THREE.Vector3(width, height, depth).divideScalar(2).subScalar(rounding);
4661
- const positions = geometry.attributes.position.array;
4662
- const normals = geometry.attributes.normal.array;
4663
- const uvs = geometry.attributes.uv.array;
4664
- const faceTriangles = positions.length / 6;
4665
- const halfSegment = 0.5 / count;
4666
- const sides = { x: width, y: height, z: depth };
4667
- for (let i = 0, j = 0; i < positions.length; i += 3, j += 2) {
4668
- position.fromArray(positions, i);
4669
- normal.copy(position);
4670
- normal.x -= Math.sign(normal.x) * halfSegment;
4671
- normal.y -= Math.sign(normal.y) * halfSegment;
4672
- normal.z -= Math.sign(normal.z) * halfSegment;
4673
- normal.normalize();
4674
- positions[i] = box2.x * Math.sign(position.x) + normal.x * rounding;
4675
- positions[i + 1] = box2.y * Math.sign(position.y) + normal.y * rounding;
4676
- positions[i + 2] = box2.z * Math.sign(position.z) + normal.z * rounding;
4677
- normals[i] = normal.x;
4678
- normals[i + 1] = normal.y;
4679
- normals[i + 2] = normal.z;
4680
- const face = FACES[Math.floor(i / faceTriangles)];
4681
- if (!face) continue;
4682
- const direction = new THREE.Vector3(...face.direction);
4683
- for (const [offset, [uvAxis, projectionAxis, mirrored]] of [[0, face.u], [1, face.v]]) {
4684
- const value = uvAlong(THREE, direction, normal, uvAxis, projectionAxis, rounding, sides[uvAxis]);
4685
- uvs[j + offset] = mirrored ? 1 - value : value;
4686
- }
4687
- }
4688
- return geometry;
4689
- }
4690
-
4691
- // src/models/kit.ts
4692
- var PI = Math.PI;
4693
- var createMaterials = (THREE) => {
4694
- const standard = (color, roughness = 0.7, metalness = 0.2, extra = {}) => new THREE.MeshStandardMaterial({ color, roughness, metalness, ...extra });
4695
- const basic = (color, extra = {}) => new THREE.MeshBasicMaterial({ color, ...extra });
4696
- const additive = { transparent: true, opacity: 0.95, blending: THREE.AdditiveBlending, depthWrite: false };
4697
- const materials = {
4698
- metal: standard(3422010, 0.42, 0.75),
4699
- steel: standard(7106418, 0.32, 0.85),
4700
- steelL: standard(9080208, 0.28, 0.9),
4701
- dark: standard(1711133, 0.55, 0.5),
4702
- poly: standard(2369058, 0.8, 0.05),
4703
- olive: standard(4936241, 0.8, 0.05),
4704
- tan: standard(10259043, 0.8, 0.05),
4705
- wood: standard(4860951, 0.55, 0),
4706
- woodL: standard(6175263, 0.55, 0),
4707
- brass: standard(12819013, 0.28, 0.9),
4708
- copper: standard(11561531, 0.3, 0.85),
4709
- lead: standard(7171955, 0.5, 0.5),
4710
- shell: standard(10692380, 0.5, 0.05),
4711
- glass: standard(8364968, 0.05, 0.1, { transparent: true, opacity: 0.42 }),
4712
- lens: standard(2311770, 0.05, 0.3, { transparent: true, opacity: 0.9, emissive: 399392 }),
4713
- rubber: standard(1381653, 0.85, 0),
4714
- white: standard(13617332, 0.8, 0),
4715
- hole: standard(526343, 1, 0),
4716
- bake: standard(5911072, 0.55, 0),
4717
- rust: standard(7027230, 0.95, 0.3),
4718
- rustD: standard(4860440, 0.95, 0.25),
4719
- worn: standard(5593178, 0.6, 0.6),
4720
- fire: basic(16742946),
4721
- fire2: basic(16764762),
4722
- flashA: basic(16757575, additive),
4723
- flashB: basic(16773568, additive),
4724
- smokeP: standard(9079430, 1, 0, { transparent: true, opacity: 0.35, depthWrite: false }),
4725
- /** Launcher paints. */
4726
- yellow: standard(11570978, 0.6, 0),
4727
- red: standard(9313300, 0.45, 0.1),
4728
- redPaint: standard(10691354, 0.35, 0.2),
4729
- tape: standard(9079944, 0.9, 0),
4730
- pipe: standard(10115634, 0.35, 0.8),
4731
- green: standard(3818536, 0.75, 0.05),
4732
- /** Magazine finishes named by the options. */
4733
- bakelite: standard(6958620, 0.6, 0),
4734
- polyMag: standard(2763302, 0.85, 0)
4735
- };
4736
- markShared(...Object.values(materials));
4737
- return materials;
4738
- };
4739
- var kits = /* @__PURE__ */ new WeakMap();
4740
- function modelKit(host) {
4741
- const cached = kits.get(host);
4742
- if (cached) return cached;
4743
- const THREE = shapeGeometries2(host);
4744
- const geometries = /* @__PURE__ */ new Map();
4745
- const shadowed = (mesh) => {
4746
- mesh.castShadow = true;
4747
- mesh.receiveShadow = true;
4748
- return mesh;
4749
- };
4750
- const place = (parent, mesh, x, y, z) => {
4751
- mesh.position.set(x, y, z);
4752
- parent.add(mesh);
4753
- return mesh;
4754
- };
4755
- const kit = {
4756
- THREE,
4757
- M: createMaterials(THREE),
4758
- roundness: null,
4759
- group: () => new THREE.Group(),
4760
- box(parent, w, h, d, material, x, y, z, rz = 0, ry = 0, rx = 0) {
4761
- const radius = Math.min(w, h, d) * (kit.roundness ?? 0.2);
4762
- const key = `${w.toFixed(4)},${h.toFixed(4)},${d.toFixed(4)},${radius.toFixed(5)}`;
4763
- let geometry = geometries.get(key);
4764
- if (!geometry) {
4765
- geometry = radius > 25e-5 ? createRoundedBoxGeometry(THREE, w, h, d, 2, radius) : new THREE.BoxGeometry(w, h, d);
4766
- markShared(geometry);
4767
- geometries.set(key, geometry);
4768
- }
4769
- const mesh = place(parent, shadowed(new THREE.Mesh(geometry, material)), x, y, z);
4770
- mesh.rotation.set(rx, ry, rz);
4771
- return mesh;
4772
- },
4773
- cylinderX(parent, radius, length, material, x, y, z, segments = 16, radiusEnd) {
4774
- const mesh = shadowed(new THREE.Mesh(new THREE.CylinderGeometry(radiusEnd ?? radius, radius, length, segments), material));
4775
- mesh.rotation.z = -PI / 2;
4776
- return place(parent, mesh, x, y, z);
4777
- },
4778
- cylinderY(parent, radius, height, material, x, y, z, segments = 16, radiusEnd) {
4779
- return place(parent, shadowed(new THREE.Mesh(new THREE.CylinderGeometry(radiusEnd ?? radius, radius, height, segments), material)), x, y, z);
4780
- },
4781
- cylinderZ(parent, radius, length, material, x, y, z, segments = 16) {
4782
- const mesh = shadowed(new THREE.Mesh(new THREE.CylinderGeometry(radius, radius, length, segments), material));
4783
- mesh.rotation.x = PI / 2;
4784
- return place(parent, mesh, x, y, z);
4785
- },
4786
- sphere(parent, radius, material, x, y, z, detail = 1) {
4787
- return place(parent, shadowed(new THREE.Mesh(new THREE.IcosahedronGeometry(radius, detail), material)), x, y, z);
4788
- },
4789
- torus: (parent, geometry, material, x, y, z) => place(parent, shadowed(new THREE.Mesh(geometry, material)), x, y, z),
4790
- mark(parent, x, y, z) {
4791
- const object = new THREE.Object3D();
4792
- object.position.set(x, y, z);
4793
- parent.add(object);
4794
- return object;
4795
- },
4796
- pivot(parent, x = 0, y = 0, z = 0) {
4797
- const group = new THREE.Group();
4798
- group.position.set(x, y, z);
4799
- parent.add(group);
4800
- return group;
4801
- }
4802
- };
4803
- kits.set(host, kit);
4804
- return kit;
4805
- }
4806
- function heatify(glow, mesh) {
4807
- mesh.material = mesh.material.clone();
4808
- glow.push(mesh);
4809
- return mesh;
4810
- }
4811
-
4812
- // src/models/break-action.ts
4813
- function buildBreakAction(kit, options) {
4814
- const { M, box: box2, cylinderX, sphere } = kit;
4815
- const group = kit.group();
4816
- const L = (options.L ?? 0) / 1e3;
4817
- const barrel = (options.B ?? 0) / 1e3;
4818
- const parts = { family: "break", kick: 1.6, glow: [] };
4819
- box2(group, 0.09, 0.05, 0.042, M.steel, L - barrel - 0.04, -5e-3, 0);
4820
- for (let i = 0; i < 4; i++) box2(group, 0.05, 2e-3, 0.043, M.dark, L - barrel - 0.04, 0.01 - i * 8e-3, 0);
4821
- const barrels = kit.pivot(group, L - barrel, -0.01, 0);
4822
- parts.barrels = barrels;
4823
- heatify(parts.glow, cylinderX(barrels, 0.0105, barrel, M.steel, barrel / 2, 0.01, 0.011, 18));
4824
- heatify(parts.glow, cylinderX(barrels, 0.0105, barrel, M.steel, barrel / 2, 0.01, -0.011, 18));
4825
- box2(barrels, barrel, 4e-3, 8e-3, M.metal, barrel / 2, 0.022, 0);
4826
- sphere(barrels, 2e-3, M.white, barrel - 0.01, 0.026, 0, 1);
4827
- cylinderX(barrels, 75e-4, 4e-3, M.hole, barrel + 1e-3, 0.01, 0.011, 12);
4828
- cylinderX(barrels, 75e-4, 4e-3, M.hole, barrel + 1e-3, 0.01, -0.011, 12);
4829
- box2(barrels, 0.22, 0.03, 0.038, M.wood, 0.12, -0.01, 0);
4830
- parts.breech = [kit.mark(barrels, 4e-3, 0.01, 0.011), kit.mark(barrels, 4e-3, 0.01, -0.011)];
4831
- box2(group, 0.12, 0.042, 0.032, M.wood, L - barrel - 0.14, -0.022, 0, 0.14);
4832
- box2(group, 0.28, 0.07, 0.036, M.wood, 0.15, -0.05, 0, 0.08);
4833
- box2(group, 0.012, 0.13, 0.04, M.rubber, 6e-3, -0.06, 0);
4834
- box2(group, 4e-3, 0.014, 4e-3, M.dark, L - barrel - 0.05, -0.04, 0);
4835
- parts.trigger = box2(group, 4e-3, 0.014, 4e-3, M.dark, L - barrel - 0.035, -0.04, 0);
4836
- box2(group, 0.04, 4e-3, 0.01, M.steel, L - barrel - 0.045, -0.05, 0);
4837
- box2(group, 0.012, 0.016, 6e-3, M.dark, L - barrel - 0.08, 0.022, 0.012, 0.5);
4838
- box2(group, 0.012, 0.016, 6e-3, M.dark, L - barrel - 0.08, 0.022, -0.012, 0.5);
4839
- parts.safety = box2(group, 0.02, 6e-3, 8e-3, M.steel, L - barrel - 0.07, 0.025, 0);
4840
- parts.muzzle = kit.mark(barrels, barrel + 0.01, 0.01, 0);
4841
- parts.eject = parts.breech[0];
4842
- return { group, parts };
4962
+ // src/models/attachments/muzzle-brake.ts
4963
+ function buildBrake(kit, parent, kind, x, y, barrelRadius, heat = () => void 0) {
4964
+ const { M, box: box2, cylinderX } = kit;
4965
+ const scale = kind === 2 ? 3.2 : 2;
4966
+ const length = kind === 2 ? 0.12 : 0.06;
4967
+ heat(cylinderX(parent, barrelRadius * scale, length, M.dark, x - length / 2, y, 0, kind === 2 ? 8 : 16));
4968
+ for (let i = 0; i < 3; i++) box2(parent, length * 0.16, barrelRadius * scale * 1.3, barrelRadius * scale * 2.2, M.hole, x - length * 0.25 - i * length * 0.25, y, 0);
4843
4969
  }
4844
4970
 
4845
- // src/models/attachments/mount-frames.ts
4846
- var DEFAULT_BARREL_RADIUS = 85e-4;
4847
- var DEFAULT_RECEIVER_WIDTH = 0.034;
4848
- var RECEIVER_START_FRACTION = 0.25;
4849
- var RECEIVER_END_FRACTION = 0.55;
4850
- var RECEIVER_HEIGHT_FRACTION = 0.4;
4851
- var DEFAULT_GRIP_OFFSET = 0.07;
4852
- var HANDGUARD_FRACTION = 0.5;
4853
- function deriveFrames(THREE, model) {
4854
- const { root, inner, parts } = model;
4855
- root.updateMatrixWorld(true);
4856
- const box2 = new THREE.Box3().setFromObject(root);
4857
- const span = box2.max.x - box2.min.x;
4858
- const toInnerX = (x) => (x - inner.position.x) / inner.scale.x;
4859
- const muzzle = parts.muzzle?.position;
4860
- const length = muzzle ? muzzle.x - 0.01 : toInnerX(box2.max.x);
4861
- const receiverStart = toInnerX(box2.min.x + span * RECEIVER_START_FRACTION);
4862
- const receiverEnd = toInnerX(box2.min.x + span * RECEIVER_END_FRACTION);
4863
- return {
4864
- length,
4865
- barrelY: muzzle?.y ?? (box2.max.y + box2.min.y) / 2,
4866
- barrelRadius: DEFAULT_BARREL_RADIUS,
4867
- receiverStart,
4868
- receiverEnd,
4869
- receiverHeight: (box2.max.y - box2.min.y) * RECEIVER_HEIGHT_FRACTION,
4870
- receiverWidth: Math.min(DEFAULT_RECEIVER_WIDTH, box2.max.z - box2.min.z),
4871
- handguardEnd: receiverEnd + (length - receiverEnd) * HANDGUARD_FRACTION,
4872
- magazineX: parts.mag ? parts.mag.position.x : (receiverStart + receiverEnd) / 2,
4873
- gripX: receiverStart + DEFAULT_GRIP_OFFSET,
4874
- hasRail: false
4875
- };
4971
+ // src/models/capture.ts
4972
+ function captured(parent, draw) {
4973
+ const before = parent.children.length;
4974
+ draw();
4975
+ return parent.children.slice(before);
4876
4976
  }
4877
- function mountFrame(model, mount) {
4878
- const frames = model.parts.frames;
4879
- if (!frames) return void 0;
4880
- const { inner } = model;
4881
- const toRoot = (x, y, z) => [inner.position.x + x * inner.scale.x, y, z];
4882
- const handguardHeight = frames.receiverHeight * 0.8;
4883
- const railTopY = frames.receiverHeight / 2 + (frames.hasRail ? 6e-3 : 0);
4884
- const forendMiddle = (frames.receiverEnd + frames.handguardEnd) / 2;
4885
- const base = { mount, barrelRadius: frames.barrelRadius, receiverWidth: frames.receiverWidth, receiverHeight: frames.receiverHeight, hasRail: frames.hasRail };
4886
- switch (mount) {
4887
- case "muzzle":
4888
- return { ...base, position: toRoot(frames.length, frames.barrelY, 0), side: 0 };
4889
- case "rail_top":
4890
- return { ...base, position: toRoot((frames.receiverStart + frames.receiverEnd) / 2 + 0.03, railTopY, 0), side: 0 };
4891
- case "rail_bottom":
4892
- return { ...base, position: toRoot(forendMiddle, frames.barrelY - handguardHeight / 2, 0), side: 0 };
4893
- case "rail_left":
4894
- return { ...base, position: toRoot(forendMiddle, frames.barrelY, -(frames.receiverWidth * 0.54 + 4e-3)), side: -1 };
4895
- case "rail_right":
4896
- return { ...base, position: toRoot(forendMiddle, frames.barrelY, frames.receiverWidth * 0.54 + 4e-3), side: 1 };
4897
- case "underbarrel": {
4898
- const [x, y] = frames.underbarrel ?? [frames.receiverEnd + (frames.handguardEnd - frames.receiverEnd) * 0.25, frames.barrelY - handguardHeight / 2 - 0.02];
4899
- return { ...base, position: toRoot(x, y, 0), side: 0 };
4900
- }
4901
- case "magazine":
4902
- return { ...base, position: toRoot(frames.magazineX, -frames.receiverHeight / 2, 0), side: 0 };
4903
- case "feed": {
4904
- const belt = model.parts.belt?.position;
4905
- return { ...base, position: belt ? toRoot(belt.x, belt.y, belt.z) : toRoot(frames.magazineX + 0.02, -frames.receiverHeight / 2 - 0.05, -0.045), side: -1 };
4906
- }
4907
- case "sling":
4908
- return {
4909
- ...base,
4910
- position: toRoot(frames.receiverEnd + (frames.length - frames.receiverEnd) * 0.45, -frames.receiverHeight / 2 - 0.03, 0),
4911
- secondary: toRoot(0.08, -0.09, 0),
4912
- side: 0
4913
- };
4914
- default:
4915
- return void 0;
4916
- }
4977
+ function recordFitting(parts, id, parent, draw) {
4978
+ parts.fittings = { ...parts.fittings, [id]: captured(parent, draw) };
4917
4979
  }
4918
4980
 
4919
4981
  // src/models/details.ts
@@ -4946,1153 +5008,1231 @@ function scope(kit, parent, kind, cx, cy, cz = 0) {
4946
5008
  cylinderX(parent, 0.017, 2e-3, M.lens, cx + 0.073, cy + 0.032, cz, 18);
4947
5009
  cylinderY(parent, 7e-3, 0.012, M.dark, cx, cy + 0.052, cz, 12);
4948
5010
  box2(parent, 0.03, 0.01, 0.012, M.dark, cx - 0.01, cy + 0.054, cz);
4949
- return;
4950
- }
4951
- const sleeve = kind === 4 ? 0.2 : 0.32;
4952
- cylinderX(parent, 0.0135, sleeve * 0.55, M.dark, cx, cy + 0.045, cz, 18);
4953
- cylinderX(parent, 0.0135, sleeve * 0.25, M.dark, cx + sleeve * 0.36, cy + 0.045, cz, 18, 0.026);
4954
- cylinderX(parent, 0.026, 0.05, M.dark, cx + sleeve * 0.5, cy + 0.045, cz, 18);
4955
- cylinderX(parent, 0.024, 2e-3, M.lens, cx + sleeve * 0.5 + 0.026, cy + 0.045, cz, 18);
4956
- cylinderX(parent, 0.019, 0.06, M.dark, cx - sleeve * 0.38, cy + 0.045, cz, 18, 0.0135);
4957
- cylinderX(parent, 0.019, 0.035, M.rubber, cx - sleeve * 0.38 - 0.045, cy + 0.045, cz, 18);
4958
- cylinderX(parent, 0.017, 2e-3, M.lens, cx - sleeve * 0.38 - 0.063, cy + 0.045, cz, 18);
4959
- cylinderY(parent, 0.01, 0.022, M.dark, cx, cy + 0.066, cz, 14);
4960
- cylinderY(parent, 0.011, 4e-3, M.metal, cx, cy + 0.078, cz, 14);
4961
- cylinderZ(parent, 0.01, 0.022, M.dark, cx, cy + 0.045, cz + 0.022, 14);
4962
- for (const offset of [-0.07, 0.07]) {
4963
- const ring = kit.torus(parent, new THREE.TorusGeometry(0.0155, 3e-3, 8, 20), M.dark, cx + offset, cy + 0.045, cz);
4964
- ring.rotation.y = PI2 / 2;
4965
- box2(parent, 0.016, 0.03, 0.022, M.dark, cx + offset, cy + 0.018, cz);
4966
- }
4967
- }
4968
- var buttpad = (kit, parent, x, y, height, width) => void kit.box(parent, 0.012, height, width, kit.M.rubber, x, y, 0);
4969
- var swivel = (kit, parent, x, y) => void kit.torus(parent, new kit.THREE.TorusGeometry(6e-3, 15e-4, 6, 12), kit.M.metal, x, y, 0);
4970
-
4971
- // src/models/bullpup.ts
4972
- function buildBullpup(kit, options) {
4973
- const { M, box: box2, cylinderX } = kit;
4974
- const group = kit.group();
4975
- const L = (options.L ?? 0) / 1e3;
4976
- const barrel = (options.B ?? 0) / 1e3;
4977
- const bodyLength = Math.max(0.32, L - barrel * 0.72);
4978
- const body = options.shot ? M.poly : M.olive;
4979
- const parts = { family: options.shot ? "pump" : "rifle", stroke: 0.06, glow: [] };
4980
- box2(group, bodyLength, 0.075, 0.042, body, bodyLength / 2, 0, 0);
4981
- box2(group, bodyLength * 0.9, 0.02, 0.044, M.dark, bodyLength * 0.5, -0.02, 0);
4982
- box2(group, 0.032, 0.13, 0.044, M.rubber, 0.016, -0.025, 0);
4983
- const gx = bodyLength * 0.8;
4984
- const grip = kit.pivot(group, gx, -0.085, 0);
4985
- grip.rotation.z = -0.25;
4986
- parts.grip = grip;
4987
- box2(grip, 0.03, 0.1, 0.03, M.poly, 0, 0, 0);
4988
- box2(group, 0.1, 6e-3, 0.03, body, gx + 0.01, -0.06, 0);
4989
- parts.trigger = box2(group, 4e-3, 0.016, 4e-3, M.dark, gx + 0.02, -0.05, 0, 0.15);
4990
- heatify(parts.glow, cylinderX(group, options.shot ? 0.0105 : 85e-4, barrel, M.metal, L - barrel / 2, 0.012, 0, 16));
4991
- cylinderX(group, 6e-3, 4e-3, M.hole, L + 1e-3, 0.012, 0, 12);
4992
- box2(group, 0.05, 0.024, 4e-3, M.hole, bodyLength * 0.25, 5e-3, 0.0212);
4993
- const bolt = kit.pivot(group);
4994
- parts.bolt = bolt;
4995
- box2(bolt, 0.044, 0.02, 6e-3, M.steelL, bodyLength * 0.25, 5e-3, 0.02);
4996
- if (options.shot) {
4997
- cylinderX(group, 0.012, bodyLength * 0.75, M.metal, bodyLength * 0.55, -0.025, 0.014, 14);
4998
- cylinderX(group, 0.012, bodyLength * 0.75, M.metal, bodyLength * 0.55, -0.025, -0.014, 14);
4999
- const pump = kit.pivot(group);
5000
- parts.pump = pump;
5001
- box2(pump, 0.15, 0.045, 0.052, M.poly, bodyLength + 0.06, -0.015, 0);
5002
- for (let i = 0; i < 6; i++) box2(pump, 6e-3, 0.047, 0.054, M.dark, bodyLength + 5e-3 + i * 0.022, -0.015, 0);
5003
- rail(kit, group, bodyLength * 0.15, bodyLength * 0.8, 0.04, 0, 0.02);
5004
- parts.loadPort = kit.mark(group, bodyLength * 0.2, -0.04, 0);
5005
- } else {
5006
- const magazine = kit.pivot(group, gx - 0.11, -0.03, 0);
5007
- magazine.rotation.z = 0.14;
5008
- parts.mag = magazine;
5009
- box2(magazine, 0.05, 0.14, 0.026, M.glass, 0, -0.07, 0);
5010
- for (let i = 0; i < 6; i++) cylinderX(magazine, 3e-3, 0.04, M.brass, 0, -0.01 - i * 0.02, 0, 6);
5011
- parts.magDrop = 0.18;
5012
- scope(kit, group, 2, bodyLength * 0.5, 0.04, 0);
5013
- box2(group, 0.2, 0.03, 0.02, body, bodyLength * 0.62, 0.048, 0);
5014
- box2(group, 0.026, 0.075, 0.026, body, bodyLength + 0.05, -0.05, 0, 0.1);
5015
- }
5016
- parts.muzzle = kit.mark(group, L + 0.01, 0.012, 0);
5017
- parts.eject = kit.mark(group, bodyLength * 0.25, 5e-3, 0.03);
5018
- return { group, parts };
5019
- }
5020
-
5021
- // src/models/launcher-flame.ts
5022
- var hose = (kit, group, points, radius, material) => {
5023
- const { THREE } = kit;
5024
- const curve = new THREE.CatmullRomCurve3(points.map((point) => new THREE.Vector3(...point)));
5025
- const tube = new THREE.Mesh(new THREE.TubeGeometry(curve, 40, radius, 8, false), material);
5026
- tube.castShadow = tube.receiveShadow = true;
5027
- group.add(tube);
5028
- };
5029
- var pilot = (kit, group, x, y) => {
5030
- const { M, cylinderX } = kit;
5031
- cylinderX(group, 6e-3, 0.03, M.fire2, x + 0.015, y, 0, 6, 0).userData.flame = 1;
5032
- cylinderX(group, 4e-3, 0.022, M.fire, x + 0.012, y, 0, 6, 0).userData.flame = 1;
5033
- };
5034
- function buildFlamethrower(kit, group, parts) {
5035
- const { M, box: box2, cylinderX, cylinderY, sphere } = kit;
5036
- parts.kick = 0.15;
5037
- heatify(parts.glow, cylinderX(group, 0.016, 0.62, M.steel, 0.42, 0, 0, 14));
5038
- cylinderX(group, 0.022, 0.1, M.dark, 0.78, 0, 0, 14);
5039
- heatify(parts.glow, cylinderX(group, 0.013, 0.1, M.steel, 0.88, 0, 0, 12, 9e-3));
5040
- cylinderX(group, 0.018, 0.02, M.metal, 0.73, 0, 0, 14);
5041
- cylinderX(group, 0.012, 0.06, M.metal, 0.84, -0.025, 0, 10);
5042
- pilot(kit, group, 0.92, -0.012);
5043
- const foreGrip = kit.pivot(group, 0.6, -0.02, 0);
5044
- foreGrip.rotation.z = -0.1;
5045
- box2(foreGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5046
- const lever = kit.pivot(group, 0.62, -0.02, 0);
5047
- parts.trigger = box2(lever, 8e-3, 0.05, 6e-3, M.metal, 0.02, -0.03, 0, 0.3);
5048
- const rearGrip = kit.pivot(group, 0.17, -0.02, 0);
5049
- rearGrip.rotation.z = -0.28;
5050
- box2(rearGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5051
- box2(group, 0.05, 4e-3, 0.012, M.metal, 0.2, -0.035, 0);
5052
- cylinderX(group, 0.02, 0.04, M.metal, 0.1, 0, 0, 12);
5053
- hose(kit, group, [[0.08, 0, 0], [0, -0.04, 0], [-0.08, -0.2, 0.05], [-0.18, -0.32, 0.08], [-0.25, -0.33, 0.08]], 0.011, M.rubber);
5054
- for (const z of [-0.1, 0.1]) {
5055
- cylinderY(group, 0.085, 0.5, M.olive, -0.3, -0.05, z, 20);
5056
- sphere(group, 0.085, M.olive, -0.3, 0.2, z, 2).scale.y = 0.45;
5057
- sphere(group, 0.085, M.olive, -0.3, -0.3, z, 2).scale.y = 0.45;
5058
- cylinderY(group, 0.03, 0.03, M.metal, -0.3, 0.24, z, 12);
5059
- }
5060
- sphere(group, 0.075, M.olive, -0.3, 0.1, 0, 2).position.x = -0.39;
5061
- box2(group, 0.03, 0.6, 0.24, M.dark, -0.4, -0.05, 0);
5062
- cylinderY(group, 0.03, 0.012, M.metal, -0.39, 0.18, 0, 14);
5063
- const gauge = kit.pivot(group, -0.39, 0.18, 3e-3);
5064
- parts.gauge = gauge;
5065
- box2(gauge, 2e-3, 0.022, 2e-3, M.red, 0, 8e-3, 0.012);
5066
- parts.muzzle = kit.pivot(group, 0.94, 0, 0);
5067
- parts.rear = kit.pivot(group, 0.1, 0, 0);
5068
- }
5069
- function buildImprovisedFlamethrower(kit, group, parts) {
5070
- const { M, box: box2, cylinderX, cylinderY, sphere } = kit;
5071
- parts.kick = 0.12;
5072
- heatify(parts.glow, cylinderX(group, 0.011, 0.66, M.pipe, 0.38, 0, 0, 12));
5073
- heatify(parts.glow, cylinderX(group, 0.015, 0.06, M.steel, 0.74, 0, 0, 12, 0.01));
5074
- cylinderX(group, 6e-3, 0.05, M.metal, 0.735, -0.02, 0, 8);
5075
- pilot(kit, group, 0.77, -0.02);
5076
- cylinderY(group, 0.012, 0.04, M.redPaint, 0.72, -0.055, 0, 10);
5077
- box2(group, 0.02, 0.03, 0.012, M.dark, 0.72, -0.085, 0);
5078
- cylinderX(group, 0.06, 0.42, M.redPaint, 0.3, -0.09, 0, 22);
5079
- sphere(group, 0.06, M.redPaint, 0.51, -0.09, 0, 2).scale.x = 0.5;
5080
- sphere(group, 0.06, M.redPaint, 0.09, -0.09, 0, 2).scale.x = 0.5;
5081
- cylinderX(group, 0.02, 0.04, M.dark, 0.04, -0.09, 0, 12);
5082
- box2(group, 0.02, 0.06, 5e-3, M.white, 0.3, -0.09, 0.06);
5083
- for (const x of [0.15, 0.3, 0.45]) cylinderX(group, 0.063, 0.03, M.tape, x, -0.09, 0, 22);
5084
- cylinderX(group, 0.016, 0.03, M.tape, 0.3, 0, 0, 12);
5085
- hose(kit, group, [[0.04, -0.09, 0], [-0.02, -0.06, 0.02], [0, 0, 0.02], [0.05, 0, 0]], 7e-3, M.rubber);
5086
- const rearGrip = kit.pivot(group, 0.12, -0.01, 0);
5087
- rearGrip.rotation.z = -0.3;
5088
- box2(rearGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5089
- const lever = kit.pivot(group, 0.17, -0.02, 0);
5090
- parts.trigger = box2(lever, 8e-3, 0.04, 6e-3, M.metal, 0, -0.02, 0, 0.2);
5091
- parts.muzzle = kit.pivot(group, 0.78, 0, 0);
5092
- parts.rear = kit.pivot(group, 0.05, 0, 0);
5093
- }
5094
-
5095
- // src/models/launcher-rounds.ts
5096
- var PI3 = Math.PI;
5097
- function buildRocketGrenade(kit) {
5098
- const { M, cylinderX } = kit;
5099
- const round2 = kit.group();
5100
- cylinderX(round2, 0.018, 0.15, M.olive, -0.05, 0, 0, 14);
5101
- cylinderX(round2, 0.02, 0.07, M.olive, 0.035, 0, 0, 18, 0.0425);
5102
- cylinderX(round2, 0.0425, 0.12, M.olive, 0.13, 0, 0, 20);
5103
- cylinderX(round2, 0.0428, 0.012, M.dark, 0.09, 0, 0, 20);
5104
- cylinderX(round2, 0.0425, 0.13, M.olive, 0.255, 0, 0, 20, 0.011);
5105
- cylinderX(round2, 0.011, 0.03, M.metal, 0.335, 0, 0, 10, 4e-3);
5106
- return round2;
5107
- }
5108
- function buildRecoillessRound(kit) {
5109
- const { M, cylinderX } = kit;
5110
- const round2 = kit.group();
5111
- cylinderX(round2, 0.041, 0.32, M.green, -0.16, 0, 0, 20);
5112
- cylinderX(round2, 0.0415, 0.02, M.yellow, -0.05, 0, 0, 20);
5113
- cylinderX(round2, 0.041, 0.12, M.green, 0.06, 0, 0, 20, 0.012);
5114
- cylinderX(round2, 0.043, 0.03, M.brass, -0.33, 0, 0, 20);
5115
- return round2;
5116
- }
5117
- function buildFlyingRocket(kit, launcherType) {
5118
- const { M, box: box2, cylinderX } = kit;
5119
- const rocket = kit.group();
5120
- const diameter = launcherType === "at4" || launcherType === "carl" ? 0.041 : 0.032;
5121
- cylinderX(rocket, diameter * 0.5, 0.2, M.dark, -0.1, 0, 0, 10);
5122
- cylinderX(rocket, diameter, 0.3, M.olive, 0.15, 0, 0, 18);
5123
- cylinderX(rocket, diameter, 0.12, M.olive, 0.36, 0, 0, 18, diameter * 0.25);
5124
- for (let i = 0; i < 4; i++) {
5125
- const fin = box2(rocket, 0.06, 2e-3, diameter * 1.4, M.metal, -0.17, 0, 0);
5126
- fin.rotation.x = i * PI3 / 4;
5011
+ return;
5012
+ }
5013
+ const sleeve = kind === 4 ? 0.2 : 0.32;
5014
+ cylinderX(parent, 0.0135, sleeve * 0.55, M.dark, cx, cy + 0.045, cz, 18);
5015
+ cylinderX(parent, 0.0135, sleeve * 0.25, M.dark, cx + sleeve * 0.36, cy + 0.045, cz, 18, 0.026);
5016
+ cylinderX(parent, 0.026, 0.05, M.dark, cx + sleeve * 0.5, cy + 0.045, cz, 18);
5017
+ cylinderX(parent, 0.024, 2e-3, M.lens, cx + sleeve * 0.5 + 0.026, cy + 0.045, cz, 18);
5018
+ cylinderX(parent, 0.019, 0.06, M.dark, cx - sleeve * 0.38, cy + 0.045, cz, 18, 0.0135);
5019
+ cylinderX(parent, 0.019, 0.035, M.rubber, cx - sleeve * 0.38 - 0.045, cy + 0.045, cz, 18);
5020
+ cylinderX(parent, 0.017, 2e-3, M.lens, cx - sleeve * 0.38 - 0.063, cy + 0.045, cz, 18);
5021
+ cylinderY(parent, 0.01, 0.022, M.dark, cx, cy + 0.066, cz, 14);
5022
+ cylinderY(parent, 0.011, 4e-3, M.metal, cx, cy + 0.078, cz, 14);
5023
+ cylinderZ(parent, 0.01, 0.022, M.dark, cx, cy + 0.045, cz + 0.022, 14);
5024
+ for (const offset of [-0.07, 0.07]) {
5025
+ const ring = kit.torus(parent, new THREE.TorusGeometry(0.0155, 3e-3, 8, 20), M.dark, cx + offset, cy + 0.045, cz);
5026
+ ring.rotation.y = PI2 / 2;
5027
+ box2(parent, 0.016, 0.03, 0.022, M.dark, cx + offset, cy + 0.018, cz);
5127
5028
  }
5128
- return rocket;
5129
5029
  }
5030
+ var buttpad = (kit, parent, x, y, height, width) => void kit.box(parent, 0.012, height, width, kit.M.rubber, x, y, 0);
5031
+ var swivel = (kit, parent, x, y) => void kit.torus(parent, new kit.THREE.TorusGeometry(6e-3, 15e-4, 6, 12), kit.M.metal, x, y, 0);
5130
5032
 
5131
- // src/models/launcher-rocket.ts
5132
- function buildRpg(kit, group, parts) {
5033
+ // src/models/attachments/bipod.ts
5034
+ function buildBipod(kit, parent, x, y) {
5133
5035
  const { M, box: box2, cylinderX } = kit;
5134
- parts.kick = 1.6;
5135
- cylinderX(group, 0.036, 0.13, M.dark, 0.065, 0, 0, 20, 0.021);
5136
- cylinderX(group, 0.038, 0.012, M.metal, 0, 0, 0, 20);
5137
- heatify(parts.glow, cylinderX(group, 0.021, 0.83, M.steel, 0.545, 0, 0, 18));
5138
- cylinderX(group, 0.03, 0.3, M.wood, 0.47, 0, 0, 16);
5139
- cylinderX(group, 0.031, 0.012, M.metal, 0.32, 0, 0, 16);
5140
- cylinderX(group, 0.031, 0.012, M.metal, 0.62, 0, 0, 16);
5141
- cylinderX(group, 0.024, 0.02, M.metal, 0.94, 0, 0, 16);
5142
- box2(group, 0.09, 0.03, 0.032, M.metal, 0.46, -0.028, 0);
5143
- const frontGrip = kit.pivot(group, 0.4, -0.04, 0);
5144
- frontGrip.rotation.z = -0.22;
5145
- box2(frontGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5146
- const rearGrip = kit.pivot(group, 0.5, -0.04, 0);
5147
- rearGrip.rotation.z = -0.18;
5148
- box2(rearGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5149
- box2(group, 0.06, 4e-3, 0.012, M.metal, 0.53, -0.05, 0);
5150
- box2(group, 4e-3, 0.025, 0.012, M.metal, 0.56, -0.04, 0);
5151
- parts.trigger = box2(group, 4e-3, 0.018, 4e-3, M.dark, 0.535, -0.045, 0, 0.15);
5152
- const hammer = kit.pivot(group, 0.44, -0.012, 0);
5153
- parts.hammer = hammer;
5154
- box2(hammer, 0.012, 0.02, 0.01, M.dark, 0, 0, 0);
5155
- box2(group, 6e-3, 0.03, 6e-3, M.dark, 0.78, 0.032, 0);
5156
- box2(group, 0.02, 0.022, 0.014, M.dark, 0.46, 0.032, 0);
5157
- box2(group, 0.12, 0.042, 0.032, M.dark, 0.5, 0.04, -0.04);
5158
- cylinderX(group, 0.013, 0.05, M.rubber, 0.42, 0.048, -0.04, 14);
5159
- cylinderX(group, 0.016, 4e-3, M.lens, 0.565, 0.048, -0.04, 14);
5160
- box2(group, 0.04, 0.012, 0.012, M.metal, 0.5, 0.016, -0.03);
5161
- const round2 = buildRocketGrenade(kit);
5162
- round2.position.x = 0.95;
5163
- group.add(round2);
5164
- parts.round = round2;
5165
- parts.roundRest = 0.95;
5166
- parts.makeRound = () => buildRocketGrenade(kit);
5167
- parts.muzzle = kit.pivot(group, 0.95, 0, 0);
5168
- parts.rear = kit.pivot(group, 0, 0, 0);
5169
- }
5170
- function buildM72(kit, group, parts) {
5171
- const { M, box: box2, cylinderX, cylinderY } = kit;
5172
- parts.kick = 1.1;
5173
- parts.disposable = true;
5174
- heatify(parts.glow, cylinderX(group, 0.0365, 0.6, M.olive, 0.58, 0, 0, 20));
5175
- cylinderX(group, 0.032, 0.32, M.olive, 0.16, 0, 0, 20);
5176
- cylinderX(group, 0.039, 0.03, M.rubber, 0.866, 0, 0, 20);
5177
- cylinderX(group, 0.035, 0.025, M.rubber, 0.012, 0, 0, 20);
5178
- cylinderX(group, 0.037, 0.015, M.yellow, 0.8, 0, 0, 20);
5179
- cylinderX(group, 0.037, 0.012, M.dark, 0.3, 0, 0, 20);
5180
- box2(group, 4e-3, 0.05, 0.05, M.dark, 0.79, 0.055, 0);
5181
- box2(group, 4e-3, 6e-3, 0.04, M.dark, 0.79, 0.078, 0);
5182
- box2(group, 4e-3, 0.035, 0.024, M.dark, 0.38, 0.05, 0);
5183
- cylinderY(group, 4e-3, 6e-3, M.lens, 0.38, 0.064, 0, 8);
5184
- parts.trigger = box2(group, 0.05, 0.016, 0.024, M.rubber, 0.5, 0.042, 0);
5185
- box2(group, 0.1, 0.01, 0.03, M.dark, 0.47, 0.036, 0);
5186
- box2(group, 0.02, 0.03, 0.01, M.dark, 0.66, -0.035, 0.03);
5187
- box2(group, 8e-3, 0.03, 8e-3, M.metal, 0.25, -0.04, 0.03);
5188
- parts.muzzle = kit.pivot(group, 0.881, 0, 0);
5189
- parts.rear = kit.pivot(group, 0, 0, 0);
5036
+ cylinderX(parent, 8e-3, 0.02, M.dark, x, y, 0, 12);
5037
+ for (const side of [-1, 1]) {
5038
+ const leg = kit.pivot(parent, x, y, side * 0.012);
5039
+ leg.rotation.set(side * 0.15, 0, -0.35);
5040
+ box2(leg, 6e-3, 0.2, 6e-3, M.dark, 0, -0.1, 0);
5041
+ box2(leg, 0.01, 6e-3, 0.01, M.rubber, 0, -0.2, 0);
5042
+ }
5190
5043
  }
5191
- function buildAt4(kit, group, parts) {
5192
- const { M, box: box2, cylinderX } = kit;
5193
- parts.kick = 1.3;
5194
- parts.disposable = true;
5195
- heatify(parts.glow, cylinderX(group, 0.05, 0.86, M.olive, 0.51, 0, 0, 24));
5196
- cylinderX(group, 0.056, 0.08, M.dark, 0.98, 0, 0, 24);
5197
- cylinderX(group, 0.058, 0.07, M.dark, 0.04, 0, 0, 24, 0.05);
5198
- cylinderX(group, 0.051, 0.015, M.yellow, 0.86, 0, 0, 24);
5199
- box2(group, 0.16, 0.026, 0.05, M.dark, 0.42, 0.058, 0);
5200
- box2(group, 0.03, 0.012, 0.016, M.red, 0.47, 0.075, 0);
5201
- parts.trigger = box2(group, 0.022, 0.012, 0.018, M.rubber, 0.5, 0.074, 0);
5202
- box2(group, 0.012, 0.07, 0.02, M.dark, 0.35, 0.09, -0.035);
5203
- box2(group, 0.012, 0.05, 0.02, M.dark, 0.71, 0.08, -0.035);
5204
- const grip = kit.pivot(group, 0.68, -0.05, 0);
5205
- grip.rotation.z = -0.15;
5206
- parts.grip = grip;
5207
- box2(grip, 0.035, 0.11, 0.032, M.dark, 0, -0.05, 0);
5208
- box2(group, 0.14, 0.035, 0.06, M.rubber, 0.32, -0.06, 0);
5209
- box2(group, 0.3, 0.012, 0.055, M.dark, 0.5, -0.05, 0);
5210
- parts.muzzle = kit.pivot(group, 1.02, 0, 0);
5211
- parts.rear = kit.pivot(group, 0, 0, 0);
5044
+ var buildBipodModel = ({ kit }) => {
5045
+ const group = kit.group();
5046
+ buildBipod(kit, group, 0, 0);
5047
+ return group;
5048
+ };
5049
+
5050
+ // src/models/rifle-fittings.ts
5051
+ var SCOPE_IDS = { 1: "red_dot", 2: "acog_4x", 3: "scope_3_9x", 4: "scope_6_24x" };
5052
+ function buildFittings(context, gripX) {
5053
+ const { kit, group, parts, options: o, furniture: furn, length: L, receiverStart: x0, receiverEnd: x1, receiverHeight: hR, receiverWidth: wR, barrelRadius: r, barrelY: by } = context;
5054
+ const { M, box: box2, cylinderX, cylinderZ, sphere } = kit;
5055
+ const middle = (x0 + x1) / 2;
5056
+ if (o.scope) recordFitting(parts, SCOPE_IDS[o.scope] ?? "scope", group, () => scope(kit, group, o.scope, middle + 0.03, hR / 2 + (o.rail ? 6e-3 : 0), o.scope === 4 ? -0.02 : 0));
5057
+ if (o.carry) {
5058
+ box2(group, 0.15, 0.026, 0.014, M.metal, middle, hR / 2 + 0.03, 0);
5059
+ box2(group, 0.012, 0.03, 0.014, M.metal, middle + 0.07, hR / 2 + 0.014, 0);
5060
+ box2(group, 0.012, 0.03, 0.014, M.metal, middle - 0.07, hR / 2 + 0.014, 0);
5061
+ }
5062
+ if (o.bipod) recordFitting(parts, "bipod", group, () => buildBipod(kit, group, x1 + (L - x1) * 0.45 + 0.04, by - 0.012));
5063
+ if (o.bolt) {
5064
+ const handle = kit.pivot(group, x1 - 0.045, by, 0);
5065
+ parts.boltHandle = handle;
5066
+ parts.boltHandleRest = handle.position.x;
5067
+ cylinderZ(handle, 42e-4, 0.05, M.steelL, 0, 0, wR / 2 + 0.022, 12);
5068
+ sphere(handle, 95e-4, M.dark, 0, -4e-3, wR / 2 + 0.048, 1);
5069
+ cylinderX(group, hR * 0.3, 0.03, M.steel, x0 + 5e-3, by, 0, 16);
5070
+ }
5071
+ if (o.lever) {
5072
+ const lever = kit.pivot(group, gripX - 0.02, -hR / 2 - 0.01, 0);
5073
+ parts.lever = lever;
5074
+ box2(lever, 0.11, 6e-3, 0.012, M.metal, 0.055, -0.052, 0);
5075
+ box2(lever, 6e-3, 0.05, 0.012, M.metal, 3e-3, -0.028, 0);
5076
+ box2(lever, 6e-3, 0.05, 0.012, M.metal, 0.108, -0.028, 0);
5077
+ box2(lever, 0.04, 6e-3, 0.012, M.metal, 0.08, -2e-3, 0);
5078
+ box2(group, 0.03, 0.012, 3e-3, M.dark, x1 - 0.04, by, wR / 2 + 1e-3);
5079
+ }
5080
+ if (o.tube) {
5081
+ const tubeLength = (L - x1) * (o.tubeLen || 0.8);
5082
+ cylinderX(group, 0.0105, tubeLength, M.metal, x1 + tubeLength / 2, by - 0.023, 0, 16);
5083
+ cylinderX(group, 0.0125, 0.02, M.dark, x1 + tubeLength + 6e-3, by - 0.023, 0, 16);
5084
+ }
5085
+ if (o.pump) {
5086
+ const pump = kit.pivot(group);
5087
+ parts.pump = pump;
5088
+ box2(pump, 0.17, 0.042, 0.046, furn, x1 + 0.14, by - 0.022, 0);
5089
+ for (let i = 0; i < 8; i++) box2(pump, 6e-3, 0.044, 0.048, M.dark, x1 + 0.07 + i * 0.02, by - 0.022, 0);
5090
+ }
5091
+ if (o.heat) {
5092
+ cylinderX(group, r * 1.75, (L - x1) * 0.7, M.dark, x1 + (L - x1) * 0.4, by, 0, 12);
5093
+ for (let i = 0; i < 10; i++) box2(group, 8e-3, r * 1.5, 4e-3, M.hole, x1 + (L - x1) * (0.1 + i * 0.06), by + r * 0.8, r * 1.5);
5094
+ }
5095
+ if (o.bayo) box2(group, 0.025, 8e-3, 0.01, M.metal, L - 0.07, by - 0.016, 0);
5096
+ if (o.rod) cylinderX(group, 3e-3, (L - x1) * 0.85, M.steel, x1 + (L - x1) * 0.45, by - 0.014, 0, 8);
5212
5097
  }
5213
- function buildCarlGustaf(kit, group, parts) {
5214
- const { M, box: box2, cylinderX } = kit;
5215
- parts.kick = 1.4;
5216
- heatify(parts.glow, cylinderX(group, 0.045, 0.72, M.dark, 0.59, 0, 0, 24));
5217
- cylinderX(group, 0.05, 0.04, M.rubber, 0.93, 0, 0, 24);
5218
- const breech = kit.pivot(group, 0.21, 0, 0.05);
5219
- parts.breech = [breech];
5220
- cylinderX(breech, 0.06, 0.16, M.dark, -0.1, 0, -0.05, 24, 0.046);
5221
- cylinderX(breech, 0.062, 0.015, M.metal, -0.18, 0, -0.05, 24);
5222
- box2(group, 0.025, 0.03, 0.02, M.metal, 0.22, 0, 0.052);
5223
- const foreGrip = kit.pivot(group, 0.62, -0.05, 0);
5224
- foreGrip.rotation.z = -0.2;
5225
- box2(foreGrip, 0.035, 0.11, 0.034, M.dark, 0, -0.05, 0);
5226
- parts.trigger = box2(group, 4e-3, 0.018, 4e-3, M.dark, 0.66, -0.06, 0, 0.15);
5227
- box2(group, 0.06, 4e-3, 0.012, M.metal, 0.66, -0.07, 0);
5228
- const rearGrip = kit.pivot(group, 0.4, -0.05, 0);
5229
- rearGrip.rotation.z = -0.25;
5230
- box2(rearGrip, 0.035, 0.1, 0.034, M.dark, 0, -0.05, 0);
5231
- box2(group, 0.13, 0.04, 0.07, M.rubber, 0.28, -0.065, 0);
5232
- box2(group, 0.16, 0.05, 0.045, M.dark, 0.56, 0.07, -0.035);
5233
- cylinderX(group, 0.016, 0.05, M.rubber, 0.46, 0.075, -0.035, 14);
5234
- cylinderX(group, 0.02, 4e-3, M.lens, 0.645, 0.075, -0.035, 14);
5235
- box2(group, 0.06, 0.02, 0.02, M.metal, 0.56, 0.04, -0.03);
5236
- box2(group, 0.22, 0.01, 0.02, M.dark, 0.6, 0.06, 0.03);
5237
- box2(group, 0.01, 0.03, 0.02, M.dark, 0.5, 0.05, 0.03);
5238
- box2(group, 0.01, 0.03, 0.02, M.dark, 0.7, 0.05, 0.03);
5239
- parts.makeRound = () => buildRecoillessRound(kit);
5240
- parts.muzzle = kit.pivot(group, 0.95, 0, 0);
5241
- parts.rear = kit.pivot(group, 0.05, 0, 0);
5098
+
5099
+ // src/models/rifle-forend.ts
5100
+ var PI3 = Math.PI;
5101
+ function buildForend(context) {
5102
+ const { kit, group, options: o, furniture: furn, length: L, receiverEnd: x1, receiverHeight: hR, receiverWidth: wR, barrelRadius: r, barrelY: by } = context;
5103
+ const { M, box: box2 } = kit;
5104
+ const hasLongScope = (o.scope ?? 0) >= 3;
5105
+ if (!context.isTraditional && o.hg !== "none") {
5106
+ const handguardLength = (L - x1) * (o.hgFrac || 0.5);
5107
+ const hx = x1 + handguardLength / 2;
5108
+ const hh = hR * 0.8;
5109
+ if (o.hg === "rail") {
5110
+ box2(group, handguardLength, hh, wR * 1.04, M.dark, hx, by - 2e-3, 0);
5111
+ rail(kit, group, x1, handguardLength, by + hh / 2, 0, wR * 0.66);
5112
+ rail(kit, group, x1 + 0.01, handguardLength - 0.02, by - 2e-3, wR * 0.54, wR * 0.4, PI3 / 2);
5113
+ rail(kit, group, x1 + 0.01, handguardLength - 0.02, by - 2e-3, -wR * 0.54, wR * 0.4, -PI3 / 2);
5114
+ rail(kit, group, x1 + 0.01, handguardLength - 0.02, by - hh / 2, 0, wR * 0.6, PI3);
5115
+ } else {
5116
+ const material = o.hg === "wood" ? M.wood : furn;
5117
+ if (o.ak) {
5118
+ box2(group, handguardLength, hh * 0.55, wR * 1.08, material, hx, by - hh * 0.22, 0);
5119
+ box2(group, handguardLength * 0.75, hh * 0.35, wR * 0.7, material, hx - handguardLength * 0.1, by + r * 2.2, 0);
5120
+ } else {
5121
+ box2(group, handguardLength, hh, wR * 1.08, material, hx, by - 2e-3, 0);
5122
+ vents(kit, group, x1 + 0.01, handguardLength - 0.02, by + hh * 0.15, wR * 0.54 + 1e-3, Math.round(handguardLength / 0.025), hh * 0.3);
5123
+ vents(kit, group, x1 + 0.01, handguardLength - 0.02, by + hh * 0.15, -wR * 0.54 - 1e-3, Math.round(handguardLength / 0.025), hh * 0.3);
5124
+ }
5125
+ }
5126
+ if (!hasLongScope && !o.rail && o.ak) {
5127
+ box2(group, 0.03, 0.016, wR * 0.6, M.metal, x1 + 0.02, by + r * 2.5 + 0.01, 0);
5128
+ box2(group, 0.012, 4e-3, wR * 0.5, M.dark, x1 + 0.03, by + r * 2.5 + 0.019, 0);
5129
+ }
5130
+ }
5131
+ if (hasLongScope) return;
5132
+ const sightX = L - (o.ak ? 0.06 : 0.08);
5133
+ if (o.ak) {
5134
+ box2(group, 0.022, 0.022, wR * 0.6, M.metal, sightX, by + 8e-3, 0);
5135
+ box2(group, 6e-3, 0.024, 4e-3, M.metal, sightX, by + 0.026, 8e-3);
5136
+ box2(group, 6e-3, 0.024, 4e-3, M.metal, sightX, by + 0.026, -8e-3);
5137
+ box2(group, 3e-3, 0.016, 3e-3, M.dark, sightX, by + 0.022, 0);
5138
+ } else if (!o.rail || o.carry) {
5139
+ box2(group, 0.018, 0.014, wR * 0.55, M.dark, sightX, by + 8e-3, 0);
5140
+ box2(group, 0.012, 0.034, 8e-3, M.dark, sightX, by + 0.03, 0, -0.25);
5141
+ } else box2(group, 0.012, 0.024, 0.012, M.dark, sightX - 0.04, by + hR * 0.4 + 0.012, 0);
5242
5142
  }
5243
- var ROCKET_BUILDERS = {
5244
- rpg: buildRpg,
5245
- m72: buildM72,
5246
- at4: buildAt4,
5247
- carl: buildCarlGustaf
5248
- };
5249
5143
 
5250
- // src/models/launcher.ts
5251
- function buildLauncher(kit, options) {
5252
- const group = kit.group();
5253
- const type = options.lt ?? "";
5254
- const isFlame = type === "flame" || type === "flameDIY";
5255
- const parts = { family: isFlame ? "flame" : "rocket", launcherType: type, glow: [] };
5256
- if (type === "flame") buildFlamethrower(kit, group, parts);
5257
- else if (type === "flameDIY") buildImprovisedFlamethrower(kit, group, parts);
5258
- else ROCKET_BUILDERS[type]?.(kit, group, parts);
5259
- parts.eject = parts.muzzle;
5260
- return { group, parts };
5144
+ // src/models/attachments/belt-box.ts
5145
+ function buildBeltBox(kit, belt) {
5146
+ const { M, box: box2, cylinderX } = kit;
5147
+ box2(belt, 0.11, 0.1, 0.08, M.olive, 0, 0, 0);
5148
+ box2(belt, 0.112, 0.012, 0.082, M.dark, 0, 0.046, 0);
5149
+ for (let i = 0; i < 7; i++) {
5150
+ cylinderX(belt, 42e-4, 0.034, M.brass, -0.04 + i * 0.012, 0.07, 0.035, 10);
5151
+ box2(belt, 6e-3, 6e-3, 0.012, M.metal, -0.04 + i * 0.012, 0.066, 0.035);
5152
+ }
5261
5153
  }
5262
5154
 
5263
- // src/models/m134.ts
5155
+ // src/models/attachments/drum.ts
5156
+ function buildDrum(kit, magazine) {
5157
+ const { M, box: box2, cylinderZ } = kit;
5158
+ cylinderZ(magazine, 0.075, 0.07, M.dark, 0.02, -0.085, 0, 22);
5159
+ cylinderZ(magazine, 0.05, 0.074, M.metal, 0.02, -0.085, 0, 18);
5160
+ box2(magazine, 0.05, 0.04, 0.03, M.dark, 0, -0.02, 0);
5161
+ }
5162
+
5163
+ // src/models/helical-magazine.ts
5264
5164
  var PI4 = Math.PI;
5265
- var BARRELS = 6;
5266
- var CLUSTER_RADIUS = 0.03;
5267
- var BARREL_RADIUS = 78e-4;
5268
- var BARREL_END = 0.8;
5269
- function buildBarrelCluster(kit, parts, group) {
5270
- const { M, cylinderX } = kit;
5271
- const barrels = kit.pivot(group, 0, 0, 0);
5272
- parts.barrels = barrels;
5273
- const length = BARREL_END - 0.3;
5274
- let heated;
5275
- for (let i = 0; i < BARRELS; i++) {
5276
- const angle = i * 2 * PI4 / BARRELS;
5277
- const y = Math.cos(angle) * CLUSTER_RADIUS;
5278
- const z = Math.sin(angle) * CLUSTER_RADIUS;
5279
- const barrel = cylinderX(barrels, BARREL_RADIUS, length, M.metal, 0.3 + length / 2, y, z, 14);
5280
- if (heated) barrel.material = heated;
5281
- else heated = heatify(parts.glow, barrel).material;
5282
- cylinderX(barrels, BARREL_RADIUS * 0.55, 4e-3, M.hole, BARREL_END + 5e-4, y, z, 10);
5165
+ var LENGTH = 0.26;
5166
+ var RADIUS = 0.026;
5167
+ var TURNS = 11;
5168
+ function buildHelicalMagazine(context) {
5169
+ const { kit, group, parts, receiverEnd, barrelY, barrelRadius } = context;
5170
+ const { M, cylinderX, box: box2 } = kit;
5171
+ const x = receiverEnd + LENGTH / 2 + 0.02;
5172
+ const y = barrelY - barrelRadius - RADIUS - 0.012;
5173
+ const magazine = kit.pivot(group, x, y, 0);
5174
+ parts.mag = magazine;
5175
+ cylinderX(magazine, RADIUS, LENGTH, M.dark, 0, 0, 0, 20);
5176
+ cylinderX(magazine, RADIUS * 1.06, 0.01, M.metal, -LENGTH / 2, 0, 0, 20);
5177
+ cylinderX(magazine, RADIUS * 1.06, 0.01, M.metal, LENGTH / 2, 0, 0, 20);
5178
+ for (let i = 0; i < TURNS; i++) box2(magazine, 0.012, RADIUS * 2.15, 0.014, M.metal, -LENGTH / 2 + (i + 0.5) * LENGTH / TURNS, 0, 0, 0, 0, i * PI4 / 4);
5179
+ box2(magazine, 0.05, 0.03, 0.03, M.dark, -LENGTH / 2 - 0.02, RADIUS * 0.4, 0);
5180
+ parts.magDrop = 0.16;
5181
+ return x;
5182
+ }
5183
+
5184
+ // src/models/rifle-controls.ts
5185
+ function buildGrip(context) {
5186
+ const { kit, group, parts, options: o, furniture: furn, receiverStart: x0, receiverHeight: hR, receiverWidth: wR } = context;
5187
+ const { M, box: box2 } = kit;
5188
+ const gx = x0 + (o.gripAt || 0.07);
5189
+ if (!context.isTraditional && o.grip !== "none") {
5190
+ const grip = kit.pivot(group, gx - 0.012, -hR / 2 - 0.046, 0);
5191
+ grip.rotation.z = -0.3;
5192
+ parts.grip = grip;
5193
+ box2(grip, 0.032, 0.105, 0.028, furn, 0, 0, 0);
5194
+ for (let i = 0; i < 3; i++) box2(grip, 6e-3, 0.012, 0.026, furn, 0.017, 0.022 - i * 0.02, 0);
5195
+ box2(grip, 0.034, 8e-3, 0.03, furn, 0, -0.052, 0);
5283
5196
  }
5284
- cylinderX(barrels, 0.012, length, M.steel, 0.3 + length / 2, 0, 0, 12);
5285
- for (const x of [0.42, 0.58, 0.74]) cylinderX(barrels, CLUSTER_RADIUS + BARREL_RADIUS * 1.6, 0.014, M.steel, x, 0, 0, 24);
5286
- cylinderX(barrels, CLUSTER_RADIUS + BARREL_RADIUS * 1.8, 0.03, M.dark, BARREL_END - 0.015, 0, 0, 24);
5197
+ box2(group, 0.06, 4e-3, 0.01, M.metal, gx + 0.03, -hR / 2 - 0.024, 0);
5198
+ box2(group, 4e-3, 0.022, 0.01, M.metal, gx + 0.06, -hR / 2 - 0.013, 0);
5199
+ parts.trigger = box2(group, 4e-3, 0.016, 4e-3, M.dark, gx + 0.024, -hR / 2 - 0.012, 0, 0.15);
5200
+ if (o.sel) parts.safety = box2(group, 0.016, 5e-3, 3e-3, M.dark, gx + 5e-3, -hR * 0.15, -wR / 2 - 2e-3, 0.4);
5201
+ return gx;
5287
5202
  }
5288
- function buildHousing(kit, parts, group) {
5289
- const { M, box: box2, cylinderX, cylinderZ } = kit;
5290
- box2(group, 0.3, 0.14, 0.15, M.metal, 0.18, 0, 0);
5291
- cylinderX(group, 0.052, 0.12, M.dark, 0.06, 0, 0, 20);
5292
- cylinderX(group, 0.062, 0.2, M.dark, 0.38, 0, 0, 22);
5293
- for (let i = 0; i < 4; i++) cylinderX(group, 0.064, 8e-3, M.steel, 0.3 + i * 0.05, 0, 0, 22);
5294
- box2(group, 0.04, 0.05, 0.2, M.dark, 0.1, 0.095, 0);
5295
- box2(group, 0.2, 0.012, 0.045, M.dark, 0.22, 0.158, 0);
5296
- box2(group, 0.012, 0.05, 0.045, M.dark, 0.13, 0.13, 0);
5297
- box2(group, 0.012, 0.05, 0.045, M.dark, 0.31, 0.13, 0);
5298
- for (const side of [-1, 1]) {
5299
- box2(group, 0.03, 0.03, 0.1, M.poly, 0.06, -0.01, side * 0.14);
5300
- cylinderZ(group, 0.016, 0.07, M.poly, 0.06, -0.01, side * 0.2, 14);
5203
+ function buildMagazine(context, gx) {
5204
+ const { kit, group, parts, options: o, receiverStart: x0, receiverHeight: hR, receiverWidth: wR, receiverLength: RL } = context;
5205
+ const { M, box: box2 } = kit;
5206
+ if (o.mag === "helical") return buildHelicalMagazine(context);
5207
+ const mx = o.magAt != null ? x0 + o.magAt : gx + 0.075;
5208
+ const scale = o.magLen || 1;
5209
+ const magazine = kit.pivot(group, mx, -hR / 2, 0);
5210
+ parts.mag = magazine;
5211
+ const finish = o.magMat === "bakelite" ? M.bakelite : o.magMat === "poly" ? M.polyMag : void 0;
5212
+ if (o.mag === "curved") {
5213
+ let px = 0;
5214
+ let py = 0;
5215
+ const material = finish ?? M.metal;
5216
+ for (const angle of [0.06, 0.22, 0.38]) {
5217
+ const length = 0.07 * scale;
5218
+ const cx = px + Math.sin(angle) * length / 2;
5219
+ const cy = py - Math.cos(angle) * length / 2;
5220
+ box2(magazine, 0.052, length + 4e-3, 0.024, material, cx, cy, 0, angle);
5221
+ box2(magazine, 0.04, length * 0.8, 0.026, M.dark, cx, cy, 0, angle);
5222
+ px += Math.sin(angle) * length;
5223
+ py -= Math.cos(angle) * length;
5224
+ }
5225
+ box2(magazine, 0.058, 8e-3, 0.028, material, px, py, 0, 0.38);
5226
+ parts.magDrop = 0.07 * scale * 3 + 0.04;
5227
+ } else if (o.mag === "box") {
5228
+ const length = 0.13 * scale;
5229
+ const curve = o.curve || 0;
5230
+ const segment = length / 3;
5231
+ const material = finish ?? M.dark;
5232
+ let px = 0;
5233
+ let py = 0;
5234
+ let angle = 0.08;
5235
+ for (let i = 0; i < 3; i++) {
5236
+ angle = 0.08 + curve * i;
5237
+ const cx = px + Math.sin(angle) * segment / 2;
5238
+ const cy = py - Math.cos(angle) * segment / 2;
5239
+ box2(magazine, 0.05, segment + 4e-3, 0.024, material, cx, cy, 0, angle);
5240
+ box2(magazine, 0.04, 3e-3, 0.026, M.metal, cx, cy, 0, angle);
5241
+ px += Math.sin(angle) * segment;
5242
+ py -= Math.cos(angle) * segment;
5243
+ }
5244
+ box2(magazine, 0.056, 7e-3, 0.028, material, px, py - 2e-3, 0, angle);
5245
+ parts.magDrop = length + 0.03;
5246
+ } else if (o.mag === "drum") {
5247
+ buildDrum(kit, magazine);
5248
+ parts.magDrop = 0.2;
5249
+ } else if (o.mag === "internal") {
5250
+ box2(group, 0.08, 0.025, 0.03, context.isTraditional ? context.furniture : M.metal, mx, -hR / 2 - 0.012, 0);
5251
+ parts.mag = null;
5252
+ } else parts.mag = null;
5253
+ if (o.mag === "belt") {
5254
+ const belt = kit.pivot(group, mx + 0.02, -hR / 2 - 0.05, -0.045);
5255
+ parts.belt = belt;
5256
+ buildBeltBox(kit, belt);
5257
+ const cover = kit.pivot(group, x0 + 0.01, hR / 2, 0);
5258
+ parts.cover = cover;
5259
+ box2(cover, RL - 0.03, 0.014, wR * 1.05, M.metal, (RL - 0.03) / 2, 7e-3, 0);
5260
+ box2(cover, 0.02, 0.012, wR * 0.6, M.dark, RL - 0.04, 0.016, 0);
5301
5261
  }
5302
- parts.trigger = box2(group, 0.012, 0.02, 0.016, M.dark, 0.1, 0.085, 0.14, 0.3);
5303
- parts.safety = box2(group, 0.014, 6e-3, 0.01, M.dark, 0.2, 0.073, -0.076, 0.4);
5262
+ return mx;
5304
5263
  }
5305
- function buildFeed(kit, parts, group) {
5264
+
5265
+ // src/models/rifle-stock.ts
5266
+ function buildStock(context, magazineX) {
5267
+ const { kit, group, options: o, furniture: furn, length: L, stockLength: S, receiverStart: x0, receiverEnd: x1, receiverHeight: hR } = context;
5306
5268
  const { M, box: box2, cylinderX } = kit;
5307
- const belt = kit.pivot(group, 0.2, -0.13, -0.16);
5308
- parts.belt = belt;
5309
- box2(belt, 0.16, 0.12, 0.1, M.olive, 0, 0, 0);
5310
- box2(belt, 0.162, 0.012, 0.102, M.dark, 0, 0.056, 0);
5311
- for (let i = 0; i < 8; i++) cylinderX(belt, 46e-4, 0.04, M.brass, -0.07 + i * 0.02, 0.075, 0.04, 10);
5312
- box2(group, 0.1, 0.03, 0.1, M.metal, 0.22, -0.06, -0.1, 0, 0, 0.5);
5313
- const cover = kit.pivot(group, 0.1, 0.075, 0);
5314
- parts.cover = cover;
5315
- box2(cover, 0.24, 0.012, 0.15, M.metal, 0.12, 6e-3, 0);
5316
- const bolt = kit.pivot(group);
5317
- parts.bolt = bolt;
5318
- parts.stroke = 0.03;
5319
- box2(bolt, 0.03, 0.012, 0.012, M.steelL, 0.28, 0.02, 0.078);
5320
- }
5321
- function buildM134(kit) {
5322
- const group = kit.group();
5323
- const parts = { family: "belt", kick: 0.9, glow: [] };
5324
- buildHousing(kit, parts, group);
5325
- buildBarrelCluster(kit, parts, group);
5326
- buildFeed(kit, parts, group);
5327
- parts.frames = {
5328
- length: BARREL_END,
5329
- barrelY: 0,
5330
- barrelRadius: BARREL_RADIUS,
5331
- receiverStart: 0.06,
5332
- receiverEnd: 0.48,
5333
- receiverHeight: 0.14,
5334
- receiverWidth: 0.15,
5335
- handguardEnd: 0.6,
5336
- magazineX: 0.2,
5337
- gripX: 0.06,
5338
- hasRail: false,
5339
- // the tripod head, where the gun balances (the tripod of the counters joins here too)
5340
- underbarrel: [0.4, -0.1]
5341
- };
5342
- parts.muzzle = kit.mark(group, BARREL_END + 0.01, 0, 0);
5343
- parts.eject = kit.mark(group, 0.3, -0.07, 0.08);
5344
- return { group, parts };
5269
+ if (context.isTraditional) {
5270
+ buttpad(kit, group, 6e-3, -0.04, 0.13, 0.038);
5271
+ box2(group, 0.03, 0.13, 0.038, furn, 0.024, -0.04, 0);
5272
+ box2(group, S, 0.055, 0.034, furn, S / 2, -0.012, 0, 0.06);
5273
+ box2(group, 0.09, 0.05, 0.032, furn, x0 - 0.02, -0.035, 0, 0.25);
5274
+ const forendLength = x1 - x0 + (L - x1) * (o.fore ?? 0.62);
5275
+ if (forendLength > 0) box2(group, forendLength, 0.045, 0.04, furn, x0 + forendLength / 2, -hR / 2 + 4e-3, 0);
5276
+ if (o.stock === "straight") box2(group, 0.15, 0.04, 0.035, furn, x1 + 0.08, -hR / 2, 0);
5277
+ box2(group, 0.04, 4e-3, 0.03, M.metal, magazineX, -hR / 2 - 0.022, 0);
5278
+ swivel(kit, group, 0.2, -0.08);
5279
+ swivel(kit, group, x1 + (L - x1) * 0.45, -hR / 2 - 0.03);
5280
+ if (o.bands) for (const fraction of [0.35, 0.75]) cylinderX(group, 0.024, 8e-3, M.metal, x1 + (L - x1) * fraction * (o.fore || 0.62), -4e-3, 0, 16);
5281
+ } else if (o.stock === "fixed" || o.stock === "wood") {
5282
+ box2(group, S, 0.05, 0.03, furn, S / 2, -8e-3, 0, 0.05);
5283
+ box2(group, 0.03, 0.13, 0.036, furn, 0.022, -0.035, 0);
5284
+ buttpad(kit, group, 4e-3, -0.035, 0.13, 0.036);
5285
+ if (o.stock === "wood") box2(group, S * 0.6, 0.03, 0.028, furn, S * 0.55, -0.045, 0, 0.2);
5286
+ swivel(kit, group, 0.08, -0.09);
5287
+ } else if (o.stock === "skeleton") {
5288
+ box2(group, S, 0.014, 0.026, furn, S / 2, 0.012, 0, 0.03);
5289
+ box2(group, S, 0.014, 0.026, furn, S / 2, -0.05, 0, -0.08);
5290
+ box2(group, 0.03, 0.12, 0.034, furn, 0.02, -0.025, 0);
5291
+ buttpad(kit, group, 4e-3, -0.025, 0.12, 0.034);
5292
+ } else if (o.stock === "retract") {
5293
+ cylinderX(group, 0.013, S, M.metal, S / 2, 0, 0, 16);
5294
+ for (let i = 0; i < 5; i++) box2(group, 3e-3, 3e-3, 8e-3, M.dark, S * 0.2 + i * 0.015, -0.013, 0);
5295
+ box2(group, 0.034, 0.11, 0.036, furn, 0.02, -0.025, 0);
5296
+ box2(group, S * 0.55, 0.032, 0.03, furn, S * 0.35, 0, 0);
5297
+ buttpad(kit, group, 3e-3, -0.025, 0.11, 0.036);
5298
+ } else if (o.stock === "fold") {
5299
+ box2(group, S, 6e-3, 6e-3, M.metal, S / 2, 0.01, 0.012);
5300
+ box2(group, S, 6e-3, 6e-3, M.metal, S / 2, -0.04, 0.012, -0.12);
5301
+ box2(group, 0.012, 0.1, 0.03, M.metal, 6e-3, -0.02, 0.012);
5302
+ }
5345
5303
  }
5346
5304
 
5347
- // src/models/m2.ts
5348
- function buildM2(kit) {
5349
- const { M, box: box2, cylinderX, cylinderZ } = kit;
5350
- const group = kit.group();
5351
- const parts = { family: "belt", kick: 2.2, stroke: 0.08, glow: [] };
5352
- box2(group, 0.52, 0.13, 0.12, M.metal, 0.36, 0, 0);
5353
- for (let i = 0; i < 5; i++) cylinderZ(group, 6e-3, 0.124, M.dark, 0.15 + i * 0.1, -0.03, 0, 10);
5354
- heatify(parts.glow, cylinderX(group, 0.016, 1.14, M.metal, 1.14, 0.01, 0, 16));
5355
- cylinderX(group, 0.034, 0.24, M.dark, 0.74, 0.01, 0, 16);
5356
- for (let i = 0; i < 6; i++) cylinderX(group, 0.036, 0.012, M.metal, 0.66 + i * 0.035, 0.01, 0, 16);
5357
- cylinderX(group, 0.022, 0.05, M.dark, 1.69, 0.01, 0, 16);
5358
- box2(group, 0.04, 0.09, 0.03, M.wood, 0.05, -0.02, 0.05);
5359
- box2(group, 0.04, 0.09, 0.03, M.wood, 0.05, -0.02, -0.05);
5360
- box2(group, 0.06, 0.02, 0.12, M.metal, 0.09, -0.02, 0);
5361
- const belt = kit.pivot(group, 0.38, -0.05, -0.12);
5362
- parts.belt = belt;
5363
- box2(belt, 0.16, 0.12, 0.09, M.olive, 0, 0, 0);
5364
- for (let i = 0; i < 8; i++) cylinderX(belt, 6e-3, 0.05, M.brass, -0.08 + i * 0.02, 0.08, 0.04, 8);
5365
- const cover = kit.pivot(group, 0.12, 0.065, 0);
5366
- parts.cover = cover;
5367
- box2(cover, 0.4, 0.012, 0.12, M.metal, 0.2, 6e-3, 0);
5368
- box2(group, 0.04, 0.05, 0.04, M.dark, 0.66, 0.1, 0);
5305
+ // src/models/rifle.ts
5306
+ var PI5 = Math.PI;
5307
+ function buildReceiver(context) {
5308
+ const { kit, group, parts, options: o, length: L, receiverStart: x0, receiverEnd: x1, receiverHeight: hR, receiverWidth: wR, receiverLength: RL, barrelY: by } = context;
5309
+ const { M, box: box2, cylinderX, cylinderY, cylinderZ, sphere } = kit;
5310
+ const receiverMaterial = o.tan ? M.tan : M.metal;
5311
+ const middle = (x0 + x1) / 2;
5312
+ if (o.round) {
5313
+ cylinderX(group, hR * 0.36, RL, M.steel, middle, by, 0, 20);
5314
+ cylinderX(group, hR * 0.32, 0.05, M.steel, x0 - 0.01, by, 0, 18);
5315
+ } else if (o.ar) {
5316
+ box2(group, RL, hR * 0.5, wR, receiverMaterial, middle, hR * 0.25, 0);
5317
+ box2(group, RL * 0.92, hR * 0.52, wR * 0.96, receiverMaterial, middle - RL * 0.02, -hR * 0.24, 0);
5318
+ box2(group, 0.05, 0.03, wR * 1.15, receiverMaterial, x0 + RL * 0.62, -hR * 0.5 - 0.01, 0);
5319
+ cylinderZ(group, 6e-3, 0.012, M.metal, x1 - 0.03, hR * 0.28, wR / 2 + 6e-3, 12);
5320
+ box2(group, 0.012, 0.01, 0.012, M.metal, x1 - 0.05, hR * 0.12, wR / 2 + 6e-3);
5321
+ box2(group, 0.03, 8e-3, 0.012, M.dark, x0 + 0.02, hR / 2 + 4e-3, 0);
5322
+ box2(group, 0.012, 6e-3, 4e-3, M.dark, x0 + RL * 0.55, -hR * 0.15, -wR / 2 - 2e-3);
5323
+ } else box2(group, RL, hR, wR, receiverMaterial, middle, 0, 0);
5324
+ if (o.ak) {
5325
+ box2(group, RL * 0.86, hR * 0.28, wR * 0.95, M.metal, middle - 8e-3, hR / 2 + 4e-3, 0);
5326
+ for (let i = 0; i < 3; i++) box2(group, 4e-3, hR * 0.3, wR * 0.97, M.dark, x0 + RL * 0.25 + i * 0.02, hR / 2 + 4e-3, 0);
5327
+ box2(group, RL * 0.5, 0.012, 3e-3, M.metal, x0 + RL * 0.55, -hR * 0.02, wR / 2 + 15e-4, -0.05);
5328
+ box2(group, 0.012, 0.014, 3e-3, M.dark, x0 + RL * 0.32, -hR * 0.05, wR / 2 + 2e-3);
5329
+ }
5330
+ if (o.g3) {
5331
+ cylinderX(group, 9e-3, (L - x1) * 0.6, M.metal, x1 + (L - x1) * 0.28, hR * 0.48, 0, 14);
5332
+ box2(group, 0.012, 0.024, 8e-3, M.dark, x1 + (L - x1) * 0.5, hR * 0.48, -0.012);
5333
+ cylinderY(group, 0.012, 0.016, M.dark, x0 + 0.03, hR / 2 + 8e-3, 0, 12);
5334
+ }
5335
+ const ejectX = x1 - (o.ejectBack || 0.07);
5336
+ const ejectY = o.ar ? hR * 0.2 : by;
5337
+ box2(group, 0.05, hR * 0.36, 4e-3, M.hole, ejectX, ejectY, wR / 2 - 4e-4);
5369
5338
  const bolt = kit.pivot(group);
5370
5339
  parts.bolt = bolt;
5371
- box2(bolt, 0.03, 0.03, 0.02, M.metal, 0.5, 0, 0.07);
5372
- const tripod = kit.group();
5373
- tripod.position.set(0.42, -0.1, 0);
5374
- group.add(tripod);
5375
- box2(tripod, 0.03, 0.06, 0.03, M.metal, 0, 0, 0);
5376
- box2(tripod, 0.012, 0.62, 0.012, M.metal, -0.2, -0.25, 0, 0.75);
5377
- box2(tripod, 0.012, 0.62, 0.012, M.metal, 0.22, -0.25, 0.16, -0.75, 0, 0.3);
5378
- box2(tripod, 0.012, 0.62, 0.012, M.metal, 0.22, -0.25, -0.16, -0.75, 0, -0.3);
5379
- parts.muzzle = kit.mark(group, 1.72, 0.01, 0);
5380
- parts.eject = kit.mark(group, 0.3, -0.07, 0);
5381
- return { group, parts };
5340
+ parts.stroke = Math.min(0.1, RL * 0.32);
5341
+ box2(bolt, 0.044, hR * 0.26, 6e-3, M.steelL, ejectX, ejectY, wR / 2 - 12e-4);
5342
+ if (o.charge) {
5343
+ box2(bolt, 0.012, 0.01, 0.03, M.metal, ejectX + 0.04, ejectY + 4e-3, wR / 2 + 0.012);
5344
+ sphere(bolt, 65e-4, M.metal, ejectX + 0.04, ejectY + 4e-3, wR / 2 + 0.028, 1);
5345
+ }
5346
+ if (o.ar) box2(group, 0.012, 0.016, 6e-3, receiverMaterial, ejectX - 0.03, ejectY + 4e-3, wR / 2 + 4e-3);
5347
+ if (o.rail) rail(kit, group, x0 - 5e-3, RL + 0.07, hR / 2 + (o.ar ? 0 : 3e-3), 0, wR * 0.68);
5382
5348
  }
5383
-
5384
- // src/models/p90.ts
5385
- function buildP90(kit) {
5349
+ function buildBarrel(context) {
5350
+ const { kit, group, parts, options: o, length: L, barrelLength: Bl, receiverEnd: x1, barrelRadius: r, barrelY: by } = context;
5386
5351
  const { M, box: box2, cylinderX } = kit;
5352
+ heatify(parts.glow, cylinderX(group, r, Bl, M.metal, L - Bl / 2, by, 0, 16));
5353
+ if (o.jacket) {
5354
+ cylinderX(group, r * 2, (L - x1) * 0.6, M.dark, x1 + (L - x1) * 0.3, by, 0, 16);
5355
+ for (let i = 0; i < 6; i++) cylinderX(group, r * 2.15, 4e-3, M.metal, x1 + (L - x1) * (0.05 + i * 0.09), by, 0, 16);
5356
+ }
5357
+ if (o.gas) cylinderX(group, r * 0.75, (L - x1) * 0.55, M.metal, x1 + (L - x1) * 0.3, by + r * 2.2, 0, 14);
5358
+ if (o.brake) {
5359
+ const kind = o.brake === 2 ? 2 : 1;
5360
+ recordFitting(parts, kind === 2 ? "muzzle_brake_heavy" : "muzzle_brake", group, () => buildBrake(kit, group, kind, L, by, r, (body) => void heatify(parts.glow, body)));
5361
+ } else if (!o.noFH) {
5362
+ recordFitting(parts, "muzzle_default", group, () => {
5363
+ heatify(parts.glow, cylinderX(group, r * 1.45, 0.042, M.dark, L - 0.021, by, 0, 16));
5364
+ for (let i = 0; i < 4; i++) {
5365
+ const angle = i * PI5 / 2 + PI5 / 4;
5366
+ box2(group, 0.026, 25e-4, 4e-3, M.hole, L - 0.02, by + Math.cos(angle) * r * 1.42, Math.sin(angle) * r * 1.42, 0, 0, angle);
5367
+ }
5368
+ });
5369
+ }
5370
+ cylinderX(group, r * 0.55, 4e-3, M.hole, L + 5e-4, by, 0, 12);
5371
+ }
5372
+ var familyOf = (o) => {
5373
+ if (o.mag === "belt") return "belt";
5374
+ if (o.pump) return "pump";
5375
+ if (o.lever) return "lever";
5376
+ if (o.tube) return "semi12";
5377
+ if (o.bolt) return o.mag === "box" ? "boltmag" : "bolt";
5378
+ return o.mag === "internal" ? "semiint" : "rifle";
5379
+ };
5380
+ function buildRifle(kit, options) {
5381
+ const { M } = kit;
5387
5382
  const group = kit.group();
5388
- const parts = { family: "p90", stroke: 0.04, kick: 0.6, glow: [] };
5389
- box2(group, 0.4, 0.07, 0.055, M.poly, 0.2, 0, 0);
5390
- box2(group, 0.12, 0.075, 0.05, M.poly, 0.2, -0.07, 0);
5391
- box2(group, 0.08, 0.04, 0.03, M.hole, 0.21, -0.06, 0);
5392
- box2(group, 0.06, 0.13, 0.056, M.poly, 0.03, -0.03, 0);
5393
- box2(group, 0.05, 0.03, 0.056, M.poly, 0.33, -0.04, 0);
5394
- heatify(parts.glow, cylinderX(group, 8e-3, 0.09, M.metal, 0.43, 0, 0, 16));
5395
- const magazine = kit.pivot(group, 0.22, 0.047, 0);
5396
- parts.mag = magazine;
5397
- parts.magDir = 1;
5398
- parts.magDrop = 0.08;
5399
- box2(magazine, 0.3, 0.022, 0.05, M.glass, 0, 0, 0);
5400
- for (let i = 0; i < 12; i++) cylinderX(magazine, 28e-4, 0.02, M.brass, -0.12 + i * 0.022, 0, 0, 6);
5401
- scope(kit, group, 1, 0.24, 0.06, 0);
5402
- const bolt = kit.pivot(group);
5403
- parts.bolt = bolt;
5404
- box2(bolt, 0.02, 0.012, 0.012, M.dark, 0.3, 0.02, 0.03);
5405
- parts.trigger = box2(group, 4e-3, 0.016, 4e-3, M.dark, 0.24, -0.045, 0);
5406
- parts.muzzle = kit.mark(group, 0.48, 0, 0);
5407
- parts.eject = kit.mark(group, 0.2, -0.07, 0);
5383
+ const parts = { family: "rifle", glow: [] };
5384
+ const length = (options.L ?? 0) / 1e3;
5385
+ const barrelLength = (options.B ?? 0) / 1e3;
5386
+ const isTraditional = options.stock === "trad" || options.stock === "straight";
5387
+ const stockLength = options.stock === "none" ? 0 : options.stockLen || Math.min(0.36, Math.max(0.2, 0.3 * length));
5388
+ const receiverStart = stockLength;
5389
+ const receiverEnd = Math.max(receiverStart + 0.12, length - barrelLength * 0.92);
5390
+ const receiverHeight = options.hR || 0.06;
5391
+ const context = {
5392
+ kit,
5393
+ group,
5394
+ parts,
5395
+ options,
5396
+ furniture: options.wood ? M.wood : options.olive ? M.olive : options.tan ? M.tan : M.poly,
5397
+ isTraditional,
5398
+ length,
5399
+ barrelLength,
5400
+ stockLength,
5401
+ receiverStart,
5402
+ receiverEnd,
5403
+ receiverHeight,
5404
+ receiverWidth: options.wR || 0.034,
5405
+ barrelRadius: options.br || 85e-4,
5406
+ barrelY: receiverHeight * 0.15,
5407
+ receiverLength: receiverEnd - receiverStart
5408
+ };
5409
+ buildReceiver(context);
5410
+ buildBarrel(context);
5411
+ buildForend(context);
5412
+ const gripX = buildGrip(context);
5413
+ const magazineX = buildMagazine(context, gripX);
5414
+ parts.stockMeshes = context.isTraditional ? [] : captured(group, () => buildStock(context, magazineX));
5415
+ if (context.isTraditional) buildStock(context, magazineX);
5416
+ buildFittings(context, gripX);
5417
+ const hasHandguard = !context.isTraditional && options.hg !== "none";
5418
+ parts.frames = {
5419
+ length,
5420
+ barrelY: context.barrelY,
5421
+ barrelRadius: context.barrelRadius,
5422
+ receiverStart,
5423
+ receiverEnd,
5424
+ receiverHeight,
5425
+ receiverWidth: context.receiverWidth,
5426
+ handguardEnd: receiverEnd + (length - receiverEnd) * (hasHandguard ? options.hgFrac || 0.5 : 0.4),
5427
+ magazineX,
5428
+ gripX,
5429
+ hasRail: Boolean(options.rail)
5430
+ };
5431
+ const { barrelY: by, receiverWidth: wR } = context;
5432
+ const ejectX = receiverEnd - (options.ejectBack || 0.07);
5433
+ const ejectY = options.ar ? receiverHeight * 0.2 : by;
5434
+ parts.muzzle = kit.mark(group, length + 0.01, by, 0);
5435
+ parts.eject = kit.mark(group, ejectX, ejectY, wR / 2 + 6e-3);
5436
+ parts.loadPort = kit.mark(group, receiverEnd - 0.05, -receiverHeight / 2 - 4e-3, 0);
5437
+ parts.topLoad = kit.mark(group, ejectX, receiverHeight / 2 + 4e-3, 0);
5438
+ parts.family = familyOf(options);
5408
5439
  return { group, parts };
5409
5440
  }
5410
5441
 
5411
- // src/models/part-registry.ts
5412
- var SINGLE_PARTS = ["slide", "bolt", "boltHandle", "mag", "trigger", "hammer", "lever", "pump", "belt", "cover", "crane", "cylinder", "barrels", "safety", "muzzle", "eject", "loadPort", "topLoad", "rear", "round", "gauge", "stock", "ugl"];
5413
- function partRegistry(parts) {
5414
- const registry = /* @__PURE__ */ new Map();
5415
- for (const key of SINGLE_PARTS) {
5416
- const part = parts[key];
5417
- if (part) registry.set(key, part);
5442
+ // src/models/break-launcher.ts
5443
+ function buildBreakLauncher(kit, options) {
5444
+ const built = buildRifle(kit, options);
5445
+ const { group, parts } = built;
5446
+ const frames = parts.frames;
5447
+ if (!frames) return built;
5448
+ const hingeY = frames.barrelY - frames.barrelRadius;
5449
+ const barrels = kit.pivot(group, frames.receiverEnd, hingeY, 0);
5450
+ group.updateMatrixWorld(true);
5451
+ const centre = new kit.THREE.Vector3();
5452
+ for (const child of [...group.children]) {
5453
+ if (child === barrels) continue;
5454
+ const mesh = child;
5455
+ const isAhead = mesh.isMesh ? new kit.THREE.Box3().setFromObject(mesh).getCenter(centre).x > frames.receiverEnd : child === parts.muzzle;
5456
+ if (isAhead) barrels.attach(child);
5418
5457
  }
5419
- parts.breech?.forEach((part, index) => registry.set(`breech_${index}`, part));
5420
- parts.chambers?.forEach((part, index) => registry.set(`chamber_${index}`, part));
5421
- return registry;
5458
+ parts.barrels = barrels;
5459
+ parts.breech = [kit.mark(barrels, 4e-3, frames.barrelY - hingeY, 0)];
5460
+ parts.eject = parts.breech[0];
5461
+ parts.family = "break";
5462
+ return built;
5422
5463
  }
5423
5464
 
5424
- // src/models/pistol-slide.ts
5425
- var PI5 = Math.PI;
5426
- function buildSlide(kit, slide2, parts, options, style, size, metal2) {
5427
- const { M, box: box2, cylinderX } = kit;
5428
- const { length: L, width: w } = size;
5429
- const slideHeight = style.inset ? size.height * 0.92 : size.height;
5430
- const slideWidth = style.inset ? w * 0.86 : w;
5431
- kit.roundness = style.rf;
5432
- if (style.tri) box2(slide2, L * 0.42, slideHeight, slideWidth, metal2, L * 0.21, 0, 0);
5433
- else if (style.open) {
5434
- box2(slide2, L * 0.45, slideHeight, slideWidth, metal2, L * 0.225, 0, 0);
5435
- box2(slide2, L * 0.55, slideHeight * 0.62, slideWidth, metal2, L * 0.72, -slideHeight * 0.19, 0);
5436
- } else box2(slide2, L, slideHeight, slideWidth, metal2, L / 2, 0, 0);
5437
- if (style.slope) box2(slide2, L * 0.25, slideHeight * 0.5, slideWidth * 0.98, metal2, L * 0.86, slideHeight * 0.35, 0, -0.22);
5438
- kit.roundness = null;
5439
- for (let i = 0; i < (style.flush ? 9 : 7); i++) box2(slide2, 22e-4, slideHeight * 0.72, slideWidth + 14e-4, M.dark, 7e-3 + i * 52e-4, -2e-3, 0, 0);
5440
- if (style.frontSer) for (let i = 0; i < 5; i++) box2(slide2, 22e-4, slideHeight * 0.6, slideWidth + 14e-4, M.dark, L * 0.76 + i * 52e-4, -3e-3, 0);
5441
- if (style.port) {
5442
- box2(slide2, L * style.port, 4e-3, slideWidth * 0.55, M.hole, L * 0.6, slideHeight / 2 - 1e-3, slideWidth * 0.12);
5443
- box2(slide2, L * style.port * 0.85, 3e-3, slideWidth * 0.4, M.steelL, L * 0.6, slideHeight / 2 - 4e-4, slideWidth * 0.1);
5444
- box2(slide2, 0.012, 4e-3, 2e-3, M.dark, L * 0.55, slideHeight * 0.15, slideWidth / 2 + 8e-4);
5445
- }
5446
- if (style.open) {
5447
- heatify(parts.glow, cylinderX(slide2, 62e-4, L * 0.56, M.steelL, L * 0.7, slideHeight * 0.12, 0, 18));
5448
- box2(slide2, 0.03, 4e-3, slideWidth * 0.6, M.hole, L * 0.48, slideHeight / 2 - 1e-3, 0);
5449
- }
5450
- if (style.tri) {
5451
- const tube = new kit.THREE.Mesh(new kit.THREE.CylinderGeometry(0.016, 0.016, L * 0.6, 3), metal2);
5452
- tube.castShadow = tube.receiveShadow = true;
5453
- tube.rotation.set(0, 0, -PI5 / 2);
5454
- tube.rotateY(PI5 / 6);
5455
- tube.position.set(L * 0.7, slideHeight * 0.08, 0);
5456
- slide2.add(tube);
5457
- heatify(parts.glow, tube);
5458
- box2(slide2, L * 0.6, 6e-3, 0.012, metal2, L * 0.7, slideHeight * 0.38, 0);
5459
- for (let i = 0; i < 6; i++) box2(slide2, 8e-3, 3e-3, 0.013, M.hole, L * 0.45 + i * 0.04, slideHeight * 0.41, 0);
5460
- }
5461
- if (style.bushing) {
5462
- heatify(parts.glow, cylinderX(slide2, 78e-4, 6e-3, M.steelL, L + 2e-3, 1e-3, 0, 18));
5463
- cylinderX(slide2, 45e-4, 4e-3, M.steel, L + 2e-3, -0.012, 0, 12);
5465
+ // src/models/attachments/mount-frames.ts
5466
+ var DEFAULT_BARREL_RADIUS = 85e-4;
5467
+ var DEFAULT_RECEIVER_WIDTH = 0.034;
5468
+ var RECEIVER_START_FRACTION = 0.25;
5469
+ var RECEIVER_END_FRACTION = 0.55;
5470
+ var RECEIVER_HEIGHT_FRACTION = 0.4;
5471
+ var DEFAULT_GRIP_OFFSET = 0.07;
5472
+ var HANDGUARD_FRACTION = 0.5;
5473
+ function deriveFrames(THREE, model) {
5474
+ const { root, inner, parts } = model;
5475
+ root.updateMatrixWorld(true);
5476
+ const box2 = new THREE.Box3().setFromObject(root);
5477
+ const span = box2.max.x - box2.min.x;
5478
+ const toInnerX = (x) => (x - inner.position.x) / inner.scale.x;
5479
+ const muzzle = parts.muzzle?.position;
5480
+ const length = muzzle ? muzzle.x - 0.01 : toInnerX(box2.max.x);
5481
+ const receiverStart = toInnerX(box2.min.x + span * RECEIVER_START_FRACTION);
5482
+ const receiverEnd = toInnerX(box2.min.x + span * RECEIVER_END_FRACTION);
5483
+ return {
5484
+ length,
5485
+ barrelY: muzzle?.y ?? (box2.max.y + box2.min.y) / 2,
5486
+ barrelRadius: DEFAULT_BARREL_RADIUS,
5487
+ receiverStart,
5488
+ receiverEnd,
5489
+ receiverHeight: (box2.max.y - box2.min.y) * RECEIVER_HEIGHT_FRACTION,
5490
+ receiverWidth: Math.min(DEFAULT_RECEIVER_WIDTH, box2.max.z - box2.min.z),
5491
+ handguardEnd: receiverEnd + (length - receiverEnd) * HANDGUARD_FRACTION,
5492
+ magazineX: parts.mag ? parts.mag.position.x : (receiverStart + receiverEnd) / 2,
5493
+ gripX: receiverStart + DEFAULT_GRIP_OFFSET,
5494
+ hasRail: false
5495
+ };
5496
+ }
5497
+ function mountFrame(model, mount) {
5498
+ const frames = model.parts.frames;
5499
+ if (!frames) return void 0;
5500
+ const { inner } = model;
5501
+ const toRoot = (x, y, z) => [inner.position.x + x * inner.scale.x, y, z];
5502
+ const handguardHeight = frames.receiverHeight * 0.8;
5503
+ const railTopY = frames.receiverHeight / 2 + (frames.hasRail ? 6e-3 : 0);
5504
+ const forendMiddle = (frames.receiverEnd + frames.handguardEnd) / 2;
5505
+ const base = { mount, barrelRadius: frames.barrelRadius, receiverWidth: frames.receiverWidth, receiverHeight: frames.receiverHeight, hasRail: frames.hasRail };
5506
+ switch (mount) {
5507
+ case "muzzle":
5508
+ return { ...base, position: toRoot(frames.length, frames.barrelY, 0), side: 0 };
5509
+ case "rail_top":
5510
+ return { ...base, position: toRoot((frames.receiverStart + frames.receiverEnd) / 2 + 0.03, railTopY, 0), side: 0 };
5511
+ case "rail_bottom":
5512
+ return { ...base, position: toRoot(forendMiddle, frames.barrelY - handguardHeight / 2, 0), side: 0 };
5513
+ case "rail_left":
5514
+ return { ...base, position: toRoot(forendMiddle, frames.barrelY, -(frames.receiverWidth * 0.54 + 4e-3)), side: -1 };
5515
+ case "rail_right":
5516
+ return { ...base, position: toRoot(forendMiddle, frames.barrelY, frames.receiverWidth * 0.54 + 4e-3), side: 1 };
5517
+ case "underbarrel": {
5518
+ const [x, y] = frames.underbarrel ?? [frames.receiverEnd + (frames.handguardEnd - frames.receiverEnd) * 0.25, frames.barrelY - handguardHeight / 2 - 0.02];
5519
+ return { ...base, position: toRoot(x, y, 0), side: 0 };
5520
+ }
5521
+ case "magazine":
5522
+ return { ...base, position: toRoot(frames.magazineX, -frames.receiverHeight / 2, 0), side: 0 };
5523
+ case "feed": {
5524
+ const belt = model.parts.belt?.position;
5525
+ return { ...base, position: belt ? toRoot(belt.x, belt.y, belt.z) : toRoot(frames.magazineX + 0.02, -frames.receiverHeight / 2 - 0.05, -0.045), side: -1 };
5526
+ }
5527
+ case "sling":
5528
+ return {
5529
+ ...base,
5530
+ position: toRoot(frames.receiverEnd + (frames.length - frames.receiverEnd) * 0.45, -frames.receiverHeight / 2 - 0.03, 0),
5531
+ secondary: toRoot(0.08, -0.09, 0),
5532
+ side: 0
5533
+ };
5534
+ default:
5535
+ return void 0;
5464
5536
  }
5465
- const muzzleY = style.tri ? slideHeight * 0.08 : 2e-3;
5466
- heatify(parts.glow, cylinderX(slide2, style.tri ? 72e-4 : 62e-4, 4e-3, M.dark, L + 15e-4, muzzleY, 0, 18));
5467
- cylinderX(slide2, 34e-4, 5e-3, M.hole, L + 25e-4, muzzleY, 0, 12);
5468
- const top = slideHeight / 2;
5469
- if (style.sight === "u" || style.sight === "3dot") {
5470
- box2(slide2, 4e-3, 7e-3, 4e-3, M.dark, L - 8e-3, top + 35e-4, 0);
5471
- box2(slide2, 22e-4, 22e-4, 46e-4, M.white, L - 8e-3, top + 58e-4, 0);
5472
- box2(slide2, 8e-3, 7e-3, 0.016, M.dark, 9e-3, top + 35e-4, 0);
5473
- if (style.sight === "u") box2(slide2, 4e-3, 2e-3, 0.0122, M.white, 9e-3, top + 6e-3, 0);
5474
- else box2(slide2, 22e-4, 22e-4, 0.0166, M.white, 9e-3, top + 58e-4, 0);
5537
+ }
5538
+
5539
+ // src/models/bullpup.ts
5540
+ function buildBullpup(kit, options) {
5541
+ const { M, box: box2, cylinderX } = kit;
5542
+ const group = kit.group();
5543
+ const L = (options.L ?? 0) / 1e3;
5544
+ const barrel = (options.B ?? 0) / 1e3;
5545
+ const bodyLength = Math.max(0.32, L - barrel * 0.72);
5546
+ const body = options.shot ? M.poly : M.olive;
5547
+ const parts = { family: options.shot ? "pump" : "rifle", stroke: 0.06, glow: [] };
5548
+ box2(group, bodyLength, 0.075, 0.042, body, bodyLength / 2, 0, 0);
5549
+ box2(group, bodyLength * 0.9, 0.02, 0.044, M.dark, bodyLength * 0.5, -0.02, 0);
5550
+ box2(group, 0.032, 0.13, 0.044, M.rubber, 0.016, -0.025, 0);
5551
+ const gx = bodyLength * 0.8;
5552
+ const grip = kit.pivot(group, gx, -0.085, 0);
5553
+ grip.rotation.z = -0.25;
5554
+ parts.grip = grip;
5555
+ box2(grip, 0.03, 0.1, 0.03, M.poly, 0, 0, 0);
5556
+ box2(group, 0.1, 6e-3, 0.03, body, gx + 0.01, -0.06, 0);
5557
+ parts.trigger = box2(group, 4e-3, 0.016, 4e-3, M.dark, gx + 0.02, -0.05, 0, 0.15);
5558
+ heatify(parts.glow, cylinderX(group, options.shot ? 0.0105 : 85e-4, barrel, M.metal, L - barrel / 2, 0.012, 0, 16));
5559
+ cylinderX(group, 6e-3, 4e-3, M.hole, L + 1e-3, 0.012, 0, 12);
5560
+ box2(group, 0.05, 0.024, 4e-3, M.hole, bodyLength * 0.25, 5e-3, 0.0212);
5561
+ const bolt = kit.pivot(group);
5562
+ parts.bolt = bolt;
5563
+ box2(bolt, 0.044, 0.02, 6e-3, M.steelL, bodyLength * 0.25, 5e-3, 0.02);
5564
+ if (options.shot) {
5565
+ cylinderX(group, 0.012, bodyLength * 0.75, M.metal, bodyLength * 0.55, -0.025, 0.014, 14);
5566
+ cylinderX(group, 0.012, bodyLength * 0.75, M.metal, bodyLength * 0.55, -0.025, -0.014, 14);
5567
+ const pump = kit.pivot(group);
5568
+ parts.pump = pump;
5569
+ box2(pump, 0.15, 0.045, 0.052, M.poly, bodyLength + 0.06, -0.015, 0);
5570
+ for (let i = 0; i < 6; i++) box2(pump, 6e-3, 0.047, 0.054, M.dark, bodyLength + 5e-3 + i * 0.022, -0.015, 0);
5571
+ rail(kit, group, bodyLength * 0.15, bodyLength * 0.8, 0.04, 0, 0.02);
5572
+ parts.loadPort = kit.mark(group, bodyLength * 0.2, -0.04, 0);
5475
5573
  } else {
5476
- box2(slide2, 6e-3, 4e-3, 3e-3, M.dark, L - 6e-3, top + 2e-3, 0);
5477
- box2(slide2, 6e-3, 4e-3, 0.012, M.dark, 8e-3, top + 2e-3, 0);
5478
- }
5479
- if (style.decock === "slide") {
5480
- box2(slide2, 0.012, 8e-3, 4e-3, M.dark, 0.02, slideHeight * 0.15, slideWidth / 2 + 2e-3, 0.3);
5481
- box2(slide2, 0.012, 8e-3, 4e-3, M.dark, 0.02, slideHeight * 0.15, -slideWidth / 2 - 2e-3, 0.3);
5482
- }
5483
- if (style.safety === "slide") parts.safety = box2(slide2, 0.012, 8e-3, 4e-3, M.dark, 0.018, slideHeight * 0.1, -slideWidth / 2 - 2e-3, 0.2);
5484
- if (options.sel) {
5485
- const selector = box2(slide2, 0.01, 8e-3, 3e-3, M.dark, 0.014, -slideHeight * 0.2, slideWidth / 2 + 14e-4);
5486
- parts.safety ?? (parts.safety = selector);
5574
+ const magazine = kit.pivot(group, gx - 0.11, -0.03, 0);
5575
+ magazine.rotation.z = 0.14;
5576
+ parts.mag = magazine;
5577
+ box2(magazine, 0.05, 0.14, 0.026, M.glass, 0, -0.07, 0);
5578
+ for (let i = 0; i < 6; i++) cylinderX(magazine, 3e-3, 0.04, M.brass, 0, -0.01 - i * 0.02, 0, 6);
5579
+ parts.magDrop = 0.18;
5580
+ scope(kit, group, 2, bodyLength * 0.5, 0.04, 0);
5581
+ box2(group, 0.2, 0.03, 0.02, body, bodyLength * 0.62, 0.048, 0);
5582
+ box2(group, 0.026, 0.075, 0.026, body, bodyLength + 0.05, -0.05, 0, 0.1);
5487
5583
  }
5584
+ parts.muzzle = kit.mark(group, L + 0.01, 0.012, 0);
5585
+ parts.eject = kit.mark(group, bodyLength * 0.25, 5e-3, 0.03);
5586
+ return { group, parts };
5488
5587
  }
5489
5588
 
5490
- // src/models/pistol-style.ts
5491
- var STYLES = {
5492
- glock: { rf: 0.07, ga: -0.36, grip: "finger", guard: "square", rail: 1, sight: "u", port: 0.2, slideM: "metal", frame: "poly" },
5493
- taurus: { rf: 0.16, ga: -0.3, grip: "panel", guard: "square", rail: 1, sight: "3dot", port: 0.18, frontSer: 1, frame: "poly" },
5494
- g2c: { rf: 0.2, ga: -0.3, grip: "panel", guard: "round", rail: 0, sight: "3dot", port: 0.17, frame: "poly", safety: "thumb" },
5495
- beretta: { rf: 0.28, ga: -0.27, grip: "wood", guard: "round", hammer: "spur", sight: "3dot", open: 1, decock: "slide", port: 0, frame: "metal" },
5496
- pt100: { rf: 0.25, ga: -0.27, grip: "rubber", guard: "round", hammer: "spur", sight: "3dot", port: 0.17, decock: "frame", frame: "metal" },
5497
- sig: { rf: 0.14, ga: -0.3, grip: "rubber", guard: "hook", hammer: "spur", sight: "3dot", port: 0.19, decock: "frame", rail: 1, tall: 1.12, frame: "metal" },
5498
- cz: { rf: 0.32, ga: -0.3, grip: "rubber", guard: "round", hammer: "spur", sight: "3dot", port: 0.15, inset: 1, safety: "thumb", frame: "metal" },
5499
- m1911: { rf: 0.24, ga: -0.2, grip: "wood", guard: "round", hammer: "ring", sight: "small", port: 0.12, bushing: 1, beaver: 1, safety: "thumb", slideStop: 1, frame: "metal" },
5500
- makarov: { rf: 0.48, ga: -0.24, grip: "star", guard: "round", hammer: "ring", sight: "small", port: 0.14, flush: 1, safety: "slide", lanyard: 1, frame: "metal" },
5501
- fiveseven: { rf: 0.2, ga: -0.4, grip: "chunky", guard: "square", rail: 1, sight: "3dot", port: 0.18, slope: 1, frame: "poly" },
5502
- deagle: { rf: 0.08, ga: -0.28, grip: "rubber", guard: "round", hammer: "big", sight: "small", port: 0.16, tri: 1, safety: "slide", frame: "metal" }
5589
+ // src/models/launcher-flame.ts
5590
+ var hose = (kit, group, points, radius, material) => {
5591
+ const { THREE } = kit;
5592
+ const curve = new THREE.CatmullRomCurve3(points.map((point) => new THREE.Vector3(...point)));
5593
+ const tube = new THREE.Mesh(new THREE.TubeGeometry(curve, 40, radius, 8, false), material);
5594
+ tube.castShadow = tube.receiveShadow = true;
5595
+ group.add(tube);
5503
5596
  };
5504
-
5505
- // src/models/pistol.ts
5506
- var PI6 = Math.PI;
5507
- function buildFrame(kit, group, parts, options, style, size, materials) {
5508
- const { M, box: box2, cylinderZ, sphere } = kit;
5509
- const { length: L, height: h, width: w } = size;
5510
- const fm = materials.frame;
5511
- if (style.inset) {
5512
- box2(group, L * 0.9, h * 0.35, w * 0.12, fm, L * 0.47, -h * 0.32, w * 0.47);
5513
- box2(group, L * 0.9, h * 0.35, w * 0.12, fm, L * 0.47, -h * 0.32, -w * 0.47);
5514
- }
5515
- const frameLength = style.tri ? L * 0.66 : L * 0.8;
5516
- kit.roundness = style.rf;
5517
- box2(group, frameLength, 0.016, w * 0.92, fm, frameLength / 2 + L * 0.06, -h / 2 - 8e-3, 0);
5518
- kit.roundness = null;
5519
- if (style.rail) for (let i = 0; i < (style.slope ? 5 : 3); i++) box2(group, 4e-3, 3e-3, w * 0.96, M.dark, L * 0.62 + i * 9e-3, -h / 2 - 0.017, 0);
5520
- const isGlock = options.st === "glock" || !options.st;
5521
- if (isGlock) {
5522
- box2(group, 8e-3, 4e-3, 3e-3, M.dark, L * 0.5, -h / 2 - 3e-3, -w / 2 - 12e-4);
5523
- box2(group, 8e-3, 4e-3, 3e-3, M.dark, L * 0.5, -h / 2 - 3e-3, w / 2 + 12e-4);
5524
- }
5525
- if (style.slideStop) {
5526
- box2(group, 0.03, 6e-3, 3e-3, M.dark, L * 0.42, -h / 2 - 2e-3, -w / 2 - 14e-4);
5527
- sphere(group, 28e-4, M.dark, L * 0.55, -h / 2 - 3e-3, w / 2 + 1e-3, 1);
5528
- } else box2(group, 0.012, 4e-3, 3e-3, M.dark, L * 0.45, -h / 2 - 3e-3, -w / 2 - 12e-4);
5529
- if (style.decock === "frame") box2(group, 0.01, 0.012, 3e-3, M.dark, L * 0.38, -h / 2 - 0.012, -w / 2 - 14e-4, 0.6);
5530
- if (style.safety === "thumb") parts.safety = box2(group, 0.014, 6e-3, 4e-3, M.dark, 0.02, -h / 2 - 2e-3, -w / 2 - 16e-4);
5531
- const gripLength = options.gl ?? 0.105;
5532
- const gx = 0.034 + (L - 0.2) * 0.15;
5533
- const gy = -h / 2 - gripLength / 2 + 6e-3;
5534
- const grip = kit.pivot(group, gx, gy, 0);
5535
- grip.rotation.z = style.ga;
5536
- parts.grip = grip;
5537
- const gripWidth = style.grip === "chunky" ? w * 1.18 : w * 1.06;
5538
- const gripDepth = style.grip === "chunky" ? 0.036 : 0.032;
5539
- kit.roundness = 0.3;
5540
- if (style.grip === "wood" || style.grip === "star") {
5541
- box2(grip, gripDepth * 0.86, gripLength, w * 0.9, fm, 0, 0, 0);
5542
- box2(grip, gripDepth * 0.8, gripLength * 0.86, 4e-3, materials.grip, 1e-3, -2e-3, w * 0.47);
5543
- box2(grip, gripDepth * 0.8, gripLength * 0.86, 4e-3, materials.grip, 1e-3, -2e-3, -w * 0.47);
5544
- if (style.grip === "wood") {
5545
- for (let i = 0; i < 6; i++) for (let j = 0; j < 2; j++) box2(grip, 3e-3, 3e-3, 12e-4, M.woodL, -6e-3 + j * 0.012, -gripLength * 0.3 + i * 0.012, w * 0.495, 0, 0, 0);
5546
- cylinderZ(grip, 35e-4, 15e-4, M.brass, 0, gripLength * 0.25, w * 0.5, 10);
5547
- cylinderZ(grip, 35e-4, 15e-4, M.brass, 0, -gripLength * 0.3, w * 0.5, 10);
5548
- } else cylinderZ(grip, 8e-3, 15e-4, M.dark, 0, gripLength * 0.1, w * 0.5, 5);
5549
- } else box2(grip, gripDepth, gripLength, gripWidth, materials.grip, 0, 0, 0);
5550
- kit.roundness = null;
5551
- if (style.grip === "finger") {
5552
- for (let i = 0; i < 3; i++) box2(grip, 6e-3, 0.012, gripWidth * 0.98, materials.grip, gripDepth / 2 + 2e-3, gripLength * 0.18 - i * 0.022, 0);
5553
- for (let i = 0; i < 5; i++) for (let j = 0; j < 3; j++) box2(grip, 3e-3, 3e-3, 12e-4, M.dark, -8e-3 + j * 8e-3, -gripLength * 0.25 + i * 0.012, gripWidth / 2 + 4e-4);
5554
- }
5555
- if (style.grip === "panel" || style.grip === "rubber" || style.grip === "chunky") for (let i = 0; i < 6; i++) box2(grip, gripDepth * 0.75, 25e-4, gripWidth + 1e-3, M.dark, 2e-3, -gripLength / 2 + 0.018 + i * 0.013, 0);
5556
- if (style.grip === "chunky") box2(grip, 8e-3, gripLength * 0.8, gripWidth * 0.9, materials.grip, gripDepth / 2 + 2e-3, -4e-3, 0);
5557
- if (style.beaver) {
5558
- box2(group, 0.026, 8e-3, w * 0.9, fm, -4e-3, -h / 2 - 4e-3, 0, 0.25);
5559
- box2(grip, 6e-3, gripLength * 0.55, w * 0.9, fm, -gripDepth * 0.5 - 2e-3, gripLength * 0.18, 0);
5560
- } else box2(group, 0.02, 9e-3, w * 0.9, fm, 4e-3, -h / 2 - 6e-3, 0);
5561
- if (style.lanyard) {
5562
- const ring = kit.torus(grip, new kit.THREE.TorusGeometry(5e-3, 13e-4, 6, 12), M.dark, -0.012, -gripLength / 2 + 4e-3, 0);
5563
- ring.position.set(-0.012, -gripLength / 2 + 4e-3, 0);
5597
+ var pilot = (kit, group, x, y) => {
5598
+ const { M, cylinderX } = kit;
5599
+ cylinderX(group, 6e-3, 0.03, M.fire2, x + 0.015, y, 0, 6, 0).userData.flame = 1;
5600
+ cylinderX(group, 4e-3, 0.022, M.fire, x + 0.012, y, 0, 6, 0).userData.flame = 1;
5601
+ };
5602
+ function buildFlamethrower(kit, group, parts) {
5603
+ const { M, box: box2, cylinderX, cylinderY, sphere } = kit;
5604
+ parts.kick = 0.15;
5605
+ heatify(parts.glow, cylinderX(group, 0.016, 0.62, M.steel, 0.42, 0, 0, 14));
5606
+ cylinderX(group, 0.022, 0.1, M.dark, 0.78, 0, 0, 14);
5607
+ heatify(parts.glow, cylinderX(group, 0.013, 0.1, M.steel, 0.88, 0, 0, 12, 9e-3));
5608
+ cylinderX(group, 0.018, 0.02, M.metal, 0.73, 0, 0, 14);
5609
+ cylinderX(group, 0.012, 0.06, M.metal, 0.84, -0.025, 0, 10);
5610
+ pilot(kit, group, 0.92, -0.012);
5611
+ const foreGrip = kit.pivot(group, 0.6, -0.02, 0);
5612
+ foreGrip.rotation.z = -0.1;
5613
+ box2(foreGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5614
+ const lever = kit.pivot(group, 0.62, -0.02, 0);
5615
+ parts.trigger = box2(lever, 8e-3, 0.05, 6e-3, M.metal, 0.02, -0.03, 0, 0.3);
5616
+ const rearGrip = kit.pivot(group, 0.17, -0.02, 0);
5617
+ rearGrip.rotation.z = -0.28;
5618
+ box2(rearGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5619
+ box2(group, 0.05, 4e-3, 0.012, M.metal, 0.2, -0.035, 0);
5620
+ cylinderX(group, 0.02, 0.04, M.metal, 0.1, 0, 0, 12);
5621
+ hose(kit, group, [[0.08, 0, 0], [0, -0.04, 0], [-0.08, -0.2, 0.05], [-0.18, -0.32, 0.08], [-0.25, -0.33, 0.08]], 0.011, M.rubber);
5622
+ for (const z of [-0.1, 0.1]) {
5623
+ cylinderY(group, 0.085, 0.5, M.olive, -0.3, -0.05, z, 20);
5624
+ sphere(group, 0.085, M.olive, -0.3, 0.2, z, 2).scale.y = 0.45;
5625
+ sphere(group, 0.085, M.olive, -0.3, -0.3, z, 2).scale.y = 0.45;
5626
+ cylinderY(group, 0.03, 0.03, M.metal, -0.3, 0.24, z, 12);
5564
5627
  }
5565
- const magazine = kit.pivot(grip);
5566
- parts.mag = magazine;
5567
- parts.magDrop = gripLength * 1.15;
5568
- box2(magazine, 0.022, gripLength * 0.94, w * 0.72, M.dark, 3e-3, -4e-3, 0);
5569
- if (style.grip === "chunky") box2(magazine, 0.04, 0.01, gripWidth * 1.02, M.poly, 3e-3, -gripLength / 2 - 4e-3, 0);
5570
- else if (style.flush || style.grip === "wood") box2(magazine, 0.028, 4e-3, w * 0.9, M.steel, 3e-3, -gripLength / 2 - 1e-3, 0);
5571
- else box2(magazine, 0.031, 6e-3, gripWidth * 1.02, M.dark, 3e-3, -gripLength / 2 - 2e-3, 0);
5572
- return { gx, ty: -h / 2 };
5628
+ sphere(group, 0.075, M.olive, -0.3, 0.1, 0, 2).position.x = -0.39;
5629
+ box2(group, 0.03, 0.6, 0.24, M.dark, -0.4, -0.05, 0);
5630
+ cylinderY(group, 0.03, 0.012, M.metal, -0.39, 0.18, 0, 14);
5631
+ const gauge = kit.pivot(group, -0.39, 0.18, 3e-3);
5632
+ parts.gauge = gauge;
5633
+ box2(gauge, 2e-3, 0.022, 2e-3, M.red, 0, 8e-3, 0.012);
5634
+ parts.muzzle = kit.pivot(group, 0.94, 0, 0);
5635
+ parts.rear = kit.pivot(group, 0.1, 0, 0);
5573
5636
  }
5574
- function buildPistol(kit, options) {
5575
- const { M, box: box2 } = kit;
5576
- const style = { ...STYLES[options.st ?? "glock"] };
5577
- const group = kit.group();
5578
- const parts = { family: "pistol", kick: options.deagle ? 1.8 : 0.6, glow: [] };
5579
- const size = { length: (options.L ?? 0) / 1e3 * 0.97, height: (options.h ?? 0.031) * (style.tall ?? 1), width: options.deagle ? 0.032 : style.flush ? 0.024 : 0.025 };
5580
- const { length: L, height: h, width: w } = size;
5581
- const slideMetal = options.steel ? M.steel : options.stain ? M.steelL : M.metal;
5582
- const frameMetal = style.frame === "poly" ? M.poly : slideMetal;
5583
- const gripMaterial = style.grip === "wood" ? M.wood : style.grip === "star" ? M.bake : style.grip === "rubber" ? M.rubber : M.poly;
5584
- const slide2 = kit.pivot(group);
5585
- parts.slide = slide2;
5586
- parts.stroke = L * 0.15;
5587
- buildSlide(kit, slide2, parts, options, style, size, slideMetal);
5588
- const { gx, ty } = buildFrame(kit, group, parts, options, style, size, { frame: frameMetal, grip: gripMaterial });
5589
- if (style.guard === "square") {
5590
- box2(group, 0.046, 4e-3, 9e-3, frameMetal, gx + 0.05, ty - 0.034, 0);
5591
- box2(group, 4e-3, 0.03, 9e-3, frameMetal, gx + 0.074, ty - 0.02, 0);
5592
- box2(group, 6e-3, 4e-3, 9e-3, frameMetal, gx + 0.075, ty - 0.034, 0);
5593
- } else if (style.guard === "hook") {
5594
- box2(group, 0.046, 4e-3, 9e-3, frameMetal, gx + 0.05, ty - 0.034, 0);
5595
- box2(group, 4e-3, 0.03, 9e-3, frameMetal, gx + 0.073, ty - 0.02, 0, 0.2);
5596
- box2(group, 8e-3, 6e-3, 9e-3, frameMetal, gx + 0.074, ty - 0.037, 0, 0.4);
5597
- } else {
5598
- const guard = kit.torus(group, new kit.THREE.TorusGeometry(0.019, 22e-4, 6, 16, PI6), frameMetal, gx + 0.05, ty - 0.014, 0);
5599
- guard.rotation.z = PI6;
5600
- guard.scale.set(1.2, 1, 1);
5601
- }
5602
- const isGlock = options.st === "glock" || !options.st;
5603
- parts.trigger = box2(group, 4e-3, 0.014, 4e-3, isGlock ? M.dark : M.steel, gx + 0.042, ty - 0.022, 0, 0.2);
5604
- if (isGlock) box2(parts.trigger, 15e-4, 8e-3, 45e-4, M.metal, 0, -1e-3, 0);
5605
- if (style.hammer) {
5606
- const hammer = kit.pivot(group, 3e-3, h / 2 - 8e-3, 0);
5607
- parts.hammer = hammer;
5608
- if (style.hammer === "ring") {
5609
- box2(hammer, 6e-3, 0.012, 6e-3, M.dark, -2e-3, 4e-3, 0, 0.3);
5610
- kit.torus(hammer, new kit.THREE.TorusGeometry(45e-4, 16e-4, 6, 12), M.dark, -4e-3, 0.012, 0);
5611
- } else if (style.hammer === "big") box2(hammer, 0.014, 0.02, 9e-3, M.dark, -4e-3, 7e-3, 0, 0.35);
5612
- else {
5613
- box2(hammer, 8e-3, 0.016, 7e-3, M.dark, -2e-3, 6e-3, 0, 0.3);
5614
- box2(hammer, 8e-3, 4e-3, 8e-3, M.dark, -6e-3, 0.013, 0, 0.3);
5615
- }
5616
- }
5617
- parts.muzzle = kit.mark(group, L + 6e-3, style.tri ? h * 0.08 : 2e-3, 0);
5618
- parts.eject = kit.mark(group, L * 0.6, h / 2, w * 0.25);
5619
- return { group, parts };
5637
+ function buildImprovisedFlamethrower(kit, group, parts) {
5638
+ const { M, box: box2, cylinderX, cylinderY, sphere } = kit;
5639
+ parts.kick = 0.12;
5640
+ heatify(parts.glow, cylinderX(group, 0.011, 0.66, M.pipe, 0.38, 0, 0, 12));
5641
+ heatify(parts.glow, cylinderX(group, 0.015, 0.06, M.steel, 0.74, 0, 0, 12, 0.01));
5642
+ cylinderX(group, 6e-3, 0.05, M.metal, 0.735, -0.02, 0, 8);
5643
+ pilot(kit, group, 0.77, -0.02);
5644
+ cylinderY(group, 0.012, 0.04, M.redPaint, 0.72, -0.055, 0, 10);
5645
+ box2(group, 0.02, 0.03, 0.012, M.dark, 0.72, -0.085, 0);
5646
+ cylinderX(group, 0.06, 0.42, M.redPaint, 0.3, -0.09, 0, 22);
5647
+ sphere(group, 0.06, M.redPaint, 0.51, -0.09, 0, 2).scale.x = 0.5;
5648
+ sphere(group, 0.06, M.redPaint, 0.09, -0.09, 0, 2).scale.x = 0.5;
5649
+ cylinderX(group, 0.02, 0.04, M.dark, 0.04, -0.09, 0, 12);
5650
+ box2(group, 0.02, 0.06, 5e-3, M.white, 0.3, -0.09, 0.06);
5651
+ for (const x of [0.15, 0.3, 0.45]) cylinderX(group, 0.063, 0.03, M.tape, x, -0.09, 0, 22);
5652
+ cylinderX(group, 0.016, 0.03, M.tape, 0.3, 0, 0, 12);
5653
+ hose(kit, group, [[0.04, -0.09, 0], [-0.02, -0.06, 0.02], [0, 0, 0.02], [0.05, 0, 0]], 7e-3, M.rubber);
5654
+ const rearGrip = kit.pivot(group, 0.12, -0.01, 0);
5655
+ rearGrip.rotation.z = -0.3;
5656
+ box2(rearGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5657
+ const lever = kit.pivot(group, 0.17, -0.02, 0);
5658
+ parts.trigger = box2(lever, 8e-3, 0.04, 6e-3, M.metal, 0, -0.02, 0, 0.2);
5659
+ parts.muzzle = kit.pivot(group, 0.78, 0, 0);
5660
+ parts.rear = kit.pivot(group, 0.05, 0, 0);
5620
5661
  }
5621
5662
 
5622
- // src/models/revolver.ts
5623
- var PI7 = Math.PI;
5624
- var DEFAULT_CHAMBERS = 6;
5625
- function buildRevolver(kit, options) {
5663
+ // src/models/launcher-rounds.ts
5664
+ var PI6 = Math.PI;
5665
+ function buildRocketGrenade(kit) {
5666
+ const { M, cylinderX } = kit;
5667
+ const round2 = kit.group();
5668
+ cylinderX(round2, 0.018, 0.15, M.olive, -0.05, 0, 0, 14);
5669
+ cylinderX(round2, 0.02, 0.07, M.olive, 0.035, 0, 0, 18, 0.0425);
5670
+ cylinderX(round2, 0.0425, 0.12, M.olive, 0.13, 0, 0, 20);
5671
+ cylinderX(round2, 0.0428, 0.012, M.dark, 0.09, 0, 0, 20);
5672
+ cylinderX(round2, 0.0425, 0.13, M.olive, 0.255, 0, 0, 20, 0.011);
5673
+ cylinderX(round2, 0.011, 0.03, M.metal, 0.335, 0, 0, 10, 4e-3);
5674
+ return round2;
5675
+ }
5676
+ function buildRecoillessRound(kit) {
5677
+ const { M, cylinderX } = kit;
5678
+ const round2 = kit.group();
5679
+ cylinderX(round2, 0.041, 0.32, M.green, -0.16, 0, 0, 20);
5680
+ cylinderX(round2, 0.0415, 0.02, M.yellow, -0.05, 0, 0, 20);
5681
+ cylinderX(round2, 0.041, 0.12, M.green, 0.06, 0, 0, 20, 0.012);
5682
+ cylinderX(round2, 0.043, 0.03, M.brass, -0.33, 0, 0, 20);
5683
+ return round2;
5684
+ }
5685
+ function buildFlyingRocket(kit, launcherType) {
5626
5686
  const { M, box: box2, cylinderX } = kit;
5627
- const group = kit.group();
5628
- const parts = { family: "revolver", kick: options.cal === ".44 Magnum" ? 1.5 : 0.9, glow: [] };
5629
- const barrel = (options.B ?? 0) / 1e3;
5630
- const chambers = options.chambers ?? DEFAULT_CHAMBERS;
5631
- const metal2 = options.steel ? M.steel : M.metal;
5632
- box2(group, 0.08, 0.044, 0.024, metal2, 0.07, 0, 0);
5633
- box2(group, 0.036, 0.012, 0.022, metal2, 0.096, 0.022, 0);
5634
- box2(group, 0.024, 0.012, 4e-3, M.dark, 0.055, 6e-3, -0.013);
5635
- const crane = kit.pivot(group, 0.077, -0.024, 0);
5636
- parts.crane = crane;
5637
- const cylinder = kit.pivot(crane, 0, 0.022, 0);
5638
- parts.cylinder = cylinder;
5639
- cylinderX(cylinder, 0.0215, 0.042, metal2, 0, 0, 0, 24);
5640
- for (let i = 0; i < chambers; i++) {
5641
- const angle = i * 2 * PI7 / chambers;
5642
- box2(cylinder, 0.026, 5e-3, 5e-3, M.dark, 0, Math.cos(angle) * 0.0205, Math.sin(angle) * 0.0205, 0, 0, angle);
5643
- }
5644
- parts.chambers = [];
5645
- for (let i = 0; i < chambers; i++) {
5646
- const angle = i * 2 * PI7 / chambers + PI7 / chambers;
5647
- parts.chambers.push(kit.mark(cylinder, -0.022, Math.cos(angle) * 0.013, Math.sin(angle) * 0.013));
5648
- cylinderX(cylinder, 46e-4, 2e-3, M.brass, -0.0215, Math.cos(angle) * 0.013, Math.sin(angle) * 0.013, 12);
5687
+ const rocket = kit.group();
5688
+ const diameter = launcherType === "at4" || launcherType === "carl" ? 0.041 : 0.032;
5689
+ cylinderX(rocket, diameter * 0.5, 0.2, M.dark, -0.1, 0, 0, 10);
5690
+ cylinderX(rocket, diameter, 0.3, M.olive, 0.15, 0, 0, 18);
5691
+ cylinderX(rocket, diameter, 0.12, M.olive, 0.36, 0, 0, 18, diameter * 0.25);
5692
+ for (let i = 0; i < 4; i++) {
5693
+ const fin = box2(rocket, 0.06, 2e-3, diameter * 1.4, M.metal, -0.17, 0, 0);
5694
+ fin.rotation.x = i * PI6 / 4;
5649
5695
  }
5650
- heatify(parts.glow, cylinderX(group, 88e-4, barrel, metal2, 0.112 + barrel / 2, 9e-3, 0, 18));
5651
- box2(group, barrel * 0.92, 0.015, 0.013, metal2, 0.112 + barrel * 0.46, -5e-3, 0);
5652
- cylinderX(group, 4e-3, 5e-3, M.hole, 0.112 + barrel + 15e-4, 9e-3, 0, 12);
5653
- box2(group, 0.012, 9e-3, 4e-3, M.dark, 0.112 + barrel - 8e-3, 0.021, 0, -0.3);
5654
- box2(group, 0.012, 6e-3, 0.012, M.dark, 0.105, 0.031, 0);
5655
- const hammer = kit.pivot(group, 0.036, 0.018, 0);
5656
- box2(hammer, 0.01, 0.02, 8e-3, M.dark, -4e-3, 8e-3, 0, 0.45);
5657
- parts.hammer = hammer;
5658
- const grip = kit.pivot(group, 0.024, -0.062, 0);
5659
- grip.rotation.z = -0.38;
5660
- parts.grip = grip;
5661
- box2(grip, 0.034, 0.1, 0.031, options.wood ? M.wood : M.rubber, 0, 0, 0);
5662
- if (!options.wood) for (let i = 0; i < 4; i++) box2(grip, 6e-3, 0.07, 0.033, M.dark, -0.012 + i * 8e-3, 0, 0);
5663
- box2(group, 0.038, 4e-3, 8e-3, metal2, 0.062, -0.036, 0);
5664
- box2(group, 4e-3, 0.022, 8e-3, metal2, 0.08, -0.026, 0, 0.2);
5665
- parts.trigger = box2(group, 4e-3, 0.015, 4e-3, M.dark, 0.06, -0.027, 0, 0.2);
5666
- parts.muzzle = kit.mark(group, 0.112 + barrel + 6e-3, 9e-3, 0);
5667
- parts.eject = kit.mark(group, 0.077, 0, 0);
5668
- return { group, parts };
5696
+ return rocket;
5669
5697
  }
5670
5698
 
5671
- // src/models/attachments/muzzle-brake.ts
5672
- function buildBrake(kit, parent, kind, x, y, barrelRadius, heat = () => void 0) {
5699
+ // src/models/launcher-rocket.ts
5700
+ function buildRpg(kit, group, parts) {
5673
5701
  const { M, box: box2, cylinderX } = kit;
5674
- const scale = kind === 2 ? 3.2 : 2;
5675
- const length = kind === 2 ? 0.12 : 0.06;
5676
- heat(cylinderX(parent, barrelRadius * scale, length, M.dark, x - length / 2, y, 0, kind === 2 ? 8 : 16));
5677
- for (let i = 0; i < 3; i++) box2(parent, length * 0.16, barrelRadius * scale * 1.3, barrelRadius * scale * 2.2, M.hole, x - length * 0.25 - i * length * 0.25, y, 0);
5678
- }
5679
-
5680
- // src/models/capture.ts
5681
- function captured(parent, draw) {
5682
- const before = parent.children.length;
5683
- draw();
5684
- return parent.children.slice(before);
5702
+ parts.kick = 1.6;
5703
+ cylinderX(group, 0.036, 0.13, M.dark, 0.065, 0, 0, 20, 0.021);
5704
+ cylinderX(group, 0.038, 0.012, M.metal, 0, 0, 0, 20);
5705
+ heatify(parts.glow, cylinderX(group, 0.021, 0.83, M.steel, 0.545, 0, 0, 18));
5706
+ cylinderX(group, 0.03, 0.3, M.wood, 0.47, 0, 0, 16);
5707
+ cylinderX(group, 0.031, 0.012, M.metal, 0.32, 0, 0, 16);
5708
+ cylinderX(group, 0.031, 0.012, M.metal, 0.62, 0, 0, 16);
5709
+ cylinderX(group, 0.024, 0.02, M.metal, 0.94, 0, 0, 16);
5710
+ box2(group, 0.09, 0.03, 0.032, M.metal, 0.46, -0.028, 0);
5711
+ const frontGrip = kit.pivot(group, 0.4, -0.04, 0);
5712
+ frontGrip.rotation.z = -0.22;
5713
+ box2(frontGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5714
+ const rearGrip = kit.pivot(group, 0.5, -0.04, 0);
5715
+ rearGrip.rotation.z = -0.18;
5716
+ box2(rearGrip, 0.03, 0.1, 0.03, M.wood, 0, -0.05, 0);
5717
+ box2(group, 0.06, 4e-3, 0.012, M.metal, 0.53, -0.05, 0);
5718
+ box2(group, 4e-3, 0.025, 0.012, M.metal, 0.56, -0.04, 0);
5719
+ parts.trigger = box2(group, 4e-3, 0.018, 4e-3, M.dark, 0.535, -0.045, 0, 0.15);
5720
+ const hammer = kit.pivot(group, 0.44, -0.012, 0);
5721
+ parts.hammer = hammer;
5722
+ box2(hammer, 0.012, 0.02, 0.01, M.dark, 0, 0, 0);
5723
+ box2(group, 6e-3, 0.03, 6e-3, M.dark, 0.78, 0.032, 0);
5724
+ box2(group, 0.02, 0.022, 0.014, M.dark, 0.46, 0.032, 0);
5725
+ box2(group, 0.12, 0.042, 0.032, M.dark, 0.5, 0.04, -0.04);
5726
+ cylinderX(group, 0.013, 0.05, M.rubber, 0.42, 0.048, -0.04, 14);
5727
+ cylinderX(group, 0.016, 4e-3, M.lens, 0.565, 0.048, -0.04, 14);
5728
+ box2(group, 0.04, 0.012, 0.012, M.metal, 0.5, 0.016, -0.03);
5729
+ const round2 = buildRocketGrenade(kit);
5730
+ round2.position.x = 0.95;
5731
+ group.add(round2);
5732
+ parts.round = round2;
5733
+ parts.roundRest = 0.95;
5734
+ parts.makeRound = () => buildRocketGrenade(kit);
5735
+ parts.muzzle = kit.pivot(group, 0.95, 0, 0);
5736
+ parts.rear = kit.pivot(group, 0, 0, 0);
5685
5737
  }
5686
- function recordFitting(parts, id, parent, draw) {
5687
- parts.fittings = { ...parts.fittings, [id]: captured(parent, draw) };
5738
+ function buildM72(kit, group, parts) {
5739
+ const { M, box: box2, cylinderX, cylinderY } = kit;
5740
+ parts.kick = 1.1;
5741
+ parts.disposable = true;
5742
+ heatify(parts.glow, cylinderX(group, 0.0365, 0.6, M.olive, 0.58, 0, 0, 20));
5743
+ cylinderX(group, 0.032, 0.32, M.olive, 0.16, 0, 0, 20);
5744
+ cylinderX(group, 0.039, 0.03, M.rubber, 0.866, 0, 0, 20);
5745
+ cylinderX(group, 0.035, 0.025, M.rubber, 0.012, 0, 0, 20);
5746
+ cylinderX(group, 0.037, 0.015, M.yellow, 0.8, 0, 0, 20);
5747
+ cylinderX(group, 0.037, 0.012, M.dark, 0.3, 0, 0, 20);
5748
+ box2(group, 4e-3, 0.05, 0.05, M.dark, 0.79, 0.055, 0);
5749
+ box2(group, 4e-3, 6e-3, 0.04, M.dark, 0.79, 0.078, 0);
5750
+ box2(group, 4e-3, 0.035, 0.024, M.dark, 0.38, 0.05, 0);
5751
+ cylinderY(group, 4e-3, 6e-3, M.lens, 0.38, 0.064, 0, 8);
5752
+ parts.trigger = box2(group, 0.05, 0.016, 0.024, M.rubber, 0.5, 0.042, 0);
5753
+ box2(group, 0.1, 0.01, 0.03, M.dark, 0.47, 0.036, 0);
5754
+ box2(group, 0.02, 0.03, 0.01, M.dark, 0.66, -0.035, 0.03);
5755
+ box2(group, 8e-3, 0.03, 8e-3, M.metal, 0.25, -0.04, 0.03);
5756
+ parts.muzzle = kit.pivot(group, 0.881, 0, 0);
5757
+ parts.rear = kit.pivot(group, 0, 0, 0);
5688
5758
  }
5689
-
5690
- // src/models/attachments/bipod.ts
5691
- function buildBipod(kit, parent, x, y) {
5759
+ function buildAt4(kit, group, parts) {
5692
5760
  const { M, box: box2, cylinderX } = kit;
5693
- cylinderX(parent, 8e-3, 0.02, M.dark, x, y, 0, 12);
5694
- for (const side of [-1, 1]) {
5695
- const leg = kit.pivot(parent, x, y, side * 0.012);
5696
- leg.rotation.set(side * 0.15, 0, -0.35);
5697
- box2(leg, 6e-3, 0.2, 6e-3, M.dark, 0, -0.1, 0);
5698
- box2(leg, 0.01, 6e-3, 0.01, M.rubber, 0, -0.2, 0);
5699
- }
5761
+ parts.kick = 1.3;
5762
+ parts.disposable = true;
5763
+ heatify(parts.glow, cylinderX(group, 0.05, 0.86, M.olive, 0.51, 0, 0, 24));
5764
+ cylinderX(group, 0.056, 0.08, M.dark, 0.98, 0, 0, 24);
5765
+ cylinderX(group, 0.058, 0.07, M.dark, 0.04, 0, 0, 24, 0.05);
5766
+ cylinderX(group, 0.051, 0.015, M.yellow, 0.86, 0, 0, 24);
5767
+ box2(group, 0.16, 0.026, 0.05, M.dark, 0.42, 0.058, 0);
5768
+ box2(group, 0.03, 0.012, 0.016, M.red, 0.47, 0.075, 0);
5769
+ parts.trigger = box2(group, 0.022, 0.012, 0.018, M.rubber, 0.5, 0.074, 0);
5770
+ box2(group, 0.012, 0.07, 0.02, M.dark, 0.35, 0.09, -0.035);
5771
+ box2(group, 0.012, 0.05, 0.02, M.dark, 0.71, 0.08, -0.035);
5772
+ const grip = kit.pivot(group, 0.68, -0.05, 0);
5773
+ grip.rotation.z = -0.15;
5774
+ parts.grip = grip;
5775
+ box2(grip, 0.035, 0.11, 0.032, M.dark, 0, -0.05, 0);
5776
+ box2(group, 0.14, 0.035, 0.06, M.rubber, 0.32, -0.06, 0);
5777
+ box2(group, 0.3, 0.012, 0.055, M.dark, 0.5, -0.05, 0);
5778
+ parts.muzzle = kit.pivot(group, 1.02, 0, 0);
5779
+ parts.rear = kit.pivot(group, 0, 0, 0);
5700
5780
  }
5701
- var buildBipodModel = ({ kit }) => {
5702
- const group = kit.group();
5703
- buildBipod(kit, group, 0, 0);
5704
- return group;
5705
- };
5706
-
5707
- // src/models/rifle-fittings.ts
5708
- var SCOPE_IDS = { 1: "red_dot", 2: "acog_4x", 3: "scope_3_9x", 4: "scope_6_24x" };
5709
- function buildFittings(context, gripX) {
5710
- const { kit, group, parts, options: o, furniture: furn, length: L, receiverStart: x0, receiverEnd: x1, receiverHeight: hR, receiverWidth: wR, barrelRadius: r, barrelY: by } = context;
5711
- const { M, box: box2, cylinderX, cylinderZ, sphere } = kit;
5712
- const middle = (x0 + x1) / 2;
5713
- if (o.scope) recordFitting(parts, SCOPE_IDS[o.scope] ?? "scope", group, () => scope(kit, group, o.scope, middle + 0.03, hR / 2 + (o.rail ? 6e-3 : 0), o.scope === 4 ? -0.02 : 0));
5714
- if (o.carry) {
5715
- box2(group, 0.15, 0.026, 0.014, M.metal, middle, hR / 2 + 0.03, 0);
5716
- box2(group, 0.012, 0.03, 0.014, M.metal, middle + 0.07, hR / 2 + 0.014, 0);
5717
- box2(group, 0.012, 0.03, 0.014, M.metal, middle - 0.07, hR / 2 + 0.014, 0);
5718
- }
5719
- if (o.bipod) recordFitting(parts, "bipod", group, () => buildBipod(kit, group, x1 + (L - x1) * 0.45 + 0.04, by - 0.012));
5720
- if (o.bolt) {
5721
- const handle = kit.pivot(group, x1 - 0.045, by, 0);
5722
- parts.boltHandle = handle;
5723
- parts.boltHandleRest = handle.position.x;
5724
- cylinderZ(handle, 42e-4, 0.05, M.steelL, 0, 0, wR / 2 + 0.022, 12);
5725
- sphere(handle, 95e-4, M.dark, 0, -4e-3, wR / 2 + 0.048, 1);
5726
- cylinderX(group, hR * 0.3, 0.03, M.steel, x0 + 5e-3, by, 0, 16);
5727
- }
5728
- if (o.lever) {
5729
- const lever = kit.pivot(group, gripX - 0.02, -hR / 2 - 0.01, 0);
5730
- parts.lever = lever;
5731
- box2(lever, 0.11, 6e-3, 0.012, M.metal, 0.055, -0.052, 0);
5732
- box2(lever, 6e-3, 0.05, 0.012, M.metal, 3e-3, -0.028, 0);
5733
- box2(lever, 6e-3, 0.05, 0.012, M.metal, 0.108, -0.028, 0);
5734
- box2(lever, 0.04, 6e-3, 0.012, M.metal, 0.08, -2e-3, 0);
5735
- box2(group, 0.03, 0.012, 3e-3, M.dark, x1 - 0.04, by, wR / 2 + 1e-3);
5736
- }
5737
- if (o.tube) {
5738
- const tubeLength = (L - x1) * (o.tubeLen || 0.8);
5739
- cylinderX(group, 0.0105, tubeLength, M.metal, x1 + tubeLength / 2, by - 0.023, 0, 16);
5740
- cylinderX(group, 0.0125, 0.02, M.dark, x1 + tubeLength + 6e-3, by - 0.023, 0, 16);
5741
- }
5742
- if (o.pump) {
5743
- const pump = kit.pivot(group);
5744
- parts.pump = pump;
5745
- box2(pump, 0.17, 0.042, 0.046, furn, x1 + 0.14, by - 0.022, 0);
5746
- for (let i = 0; i < 8; i++) box2(pump, 6e-3, 0.044, 0.048, M.dark, x1 + 0.07 + i * 0.02, by - 0.022, 0);
5747
- }
5748
- if (o.heat) {
5749
- cylinderX(group, r * 1.75, (L - x1) * 0.7, M.dark, x1 + (L - x1) * 0.4, by, 0, 12);
5750
- for (let i = 0; i < 10; i++) box2(group, 8e-3, r * 1.5, 4e-3, M.hole, x1 + (L - x1) * (0.1 + i * 0.06), by + r * 0.8, r * 1.5);
5751
- }
5752
- if (o.bayo) box2(group, 0.025, 8e-3, 0.01, M.metal, L - 0.07, by - 0.016, 0);
5753
- if (o.rod) cylinderX(group, 3e-3, (L - x1) * 0.85, M.steel, x1 + (L - x1) * 0.45, by - 0.014, 0, 8);
5781
+ function buildCarlGustaf(kit, group, parts) {
5782
+ const { M, box: box2, cylinderX } = kit;
5783
+ parts.kick = 1.4;
5784
+ heatify(parts.glow, cylinderX(group, 0.045, 0.72, M.dark, 0.59, 0, 0, 24));
5785
+ cylinderX(group, 0.05, 0.04, M.rubber, 0.93, 0, 0, 24);
5786
+ const breech = kit.pivot(group, 0.21, 0, 0.05);
5787
+ parts.breech = [breech];
5788
+ cylinderX(breech, 0.06, 0.16, M.dark, -0.1, 0, -0.05, 24, 0.046);
5789
+ cylinderX(breech, 0.062, 0.015, M.metal, -0.18, 0, -0.05, 24);
5790
+ box2(group, 0.025, 0.03, 0.02, M.metal, 0.22, 0, 0.052);
5791
+ const foreGrip = kit.pivot(group, 0.62, -0.05, 0);
5792
+ foreGrip.rotation.z = -0.2;
5793
+ box2(foreGrip, 0.035, 0.11, 0.034, M.dark, 0, -0.05, 0);
5794
+ parts.trigger = box2(group, 4e-3, 0.018, 4e-3, M.dark, 0.66, -0.06, 0, 0.15);
5795
+ box2(group, 0.06, 4e-3, 0.012, M.metal, 0.66, -0.07, 0);
5796
+ const rearGrip = kit.pivot(group, 0.4, -0.05, 0);
5797
+ rearGrip.rotation.z = -0.25;
5798
+ box2(rearGrip, 0.035, 0.1, 0.034, M.dark, 0, -0.05, 0);
5799
+ box2(group, 0.13, 0.04, 0.07, M.rubber, 0.28, -0.065, 0);
5800
+ box2(group, 0.16, 0.05, 0.045, M.dark, 0.56, 0.07, -0.035);
5801
+ cylinderX(group, 0.016, 0.05, M.rubber, 0.46, 0.075, -0.035, 14);
5802
+ cylinderX(group, 0.02, 4e-3, M.lens, 0.645, 0.075, -0.035, 14);
5803
+ box2(group, 0.06, 0.02, 0.02, M.metal, 0.56, 0.04, -0.03);
5804
+ box2(group, 0.22, 0.01, 0.02, M.dark, 0.6, 0.06, 0.03);
5805
+ box2(group, 0.01, 0.03, 0.02, M.dark, 0.5, 0.05, 0.03);
5806
+ box2(group, 0.01, 0.03, 0.02, M.dark, 0.7, 0.05, 0.03);
5807
+ parts.makeRound = () => buildRecoillessRound(kit);
5808
+ parts.muzzle = kit.pivot(group, 0.95, 0, 0);
5809
+ parts.rear = kit.pivot(group, 0.05, 0, 0);
5754
5810
  }
5811
+ var ROCKET_BUILDERS = {
5812
+ rpg: buildRpg,
5813
+ m72: buildM72,
5814
+ at4: buildAt4,
5815
+ carl: buildCarlGustaf
5816
+ };
5755
5817
 
5756
- // src/models/rifle-forend.ts
5757
- var PI8 = Math.PI;
5758
- function buildForend(context) {
5759
- const { kit, group, options: o, furniture: furn, length: L, receiverEnd: x1, receiverHeight: hR, receiverWidth: wR, barrelRadius: r, barrelY: by } = context;
5760
- const { M, box: box2 } = kit;
5761
- const hasLongScope = (o.scope ?? 0) >= 3;
5762
- if (!context.isTraditional && o.hg !== "none") {
5763
- const handguardLength = (L - x1) * (o.hgFrac || 0.5);
5764
- const hx = x1 + handguardLength / 2;
5765
- const hh = hR * 0.8;
5766
- if (o.hg === "rail") {
5767
- box2(group, handguardLength, hh, wR * 1.04, M.dark, hx, by - 2e-3, 0);
5768
- rail(kit, group, x1, handguardLength, by + hh / 2, 0, wR * 0.66);
5769
- rail(kit, group, x1 + 0.01, handguardLength - 0.02, by - 2e-3, wR * 0.54, wR * 0.4, PI8 / 2);
5770
- rail(kit, group, x1 + 0.01, handguardLength - 0.02, by - 2e-3, -wR * 0.54, wR * 0.4, -PI8 / 2);
5771
- rail(kit, group, x1 + 0.01, handguardLength - 0.02, by - hh / 2, 0, wR * 0.6, PI8);
5772
- } else {
5773
- const material = o.hg === "wood" ? M.wood : furn;
5774
- if (o.ak) {
5775
- box2(group, handguardLength, hh * 0.55, wR * 1.08, material, hx, by - hh * 0.22, 0);
5776
- box2(group, handguardLength * 0.75, hh * 0.35, wR * 0.7, material, hx - handguardLength * 0.1, by + r * 2.2, 0);
5777
- } else {
5778
- box2(group, handguardLength, hh, wR * 1.08, material, hx, by - 2e-3, 0);
5779
- vents(kit, group, x1 + 0.01, handguardLength - 0.02, by + hh * 0.15, wR * 0.54 + 1e-3, Math.round(handguardLength / 0.025), hh * 0.3);
5780
- vents(kit, group, x1 + 0.01, handguardLength - 0.02, by + hh * 0.15, -wR * 0.54 - 1e-3, Math.round(handguardLength / 0.025), hh * 0.3);
5781
- }
5782
- }
5783
- if (!hasLongScope && !o.rail && o.ak) {
5784
- box2(group, 0.03, 0.016, wR * 0.6, M.metal, x1 + 0.02, by + r * 2.5 + 0.01, 0);
5785
- box2(group, 0.012, 4e-3, wR * 0.5, M.dark, x1 + 0.03, by + r * 2.5 + 0.019, 0);
5786
- }
5787
- }
5788
- if (hasLongScope) return;
5789
- const sightX = L - (o.ak ? 0.06 : 0.08);
5790
- if (o.ak) {
5791
- box2(group, 0.022, 0.022, wR * 0.6, M.metal, sightX, by + 8e-3, 0);
5792
- box2(group, 6e-3, 0.024, 4e-3, M.metal, sightX, by + 0.026, 8e-3);
5793
- box2(group, 6e-3, 0.024, 4e-3, M.metal, sightX, by + 0.026, -8e-3);
5794
- box2(group, 3e-3, 0.016, 3e-3, M.dark, sightX, by + 0.022, 0);
5795
- } else if (!o.rail || o.carry) {
5796
- box2(group, 0.018, 0.014, wR * 0.55, M.dark, sightX, by + 8e-3, 0);
5797
- box2(group, 0.012, 0.034, 8e-3, M.dark, sightX, by + 0.03, 0, -0.25);
5798
- } else box2(group, 0.012, 0.024, 0.012, M.dark, sightX - 0.04, by + hR * 0.4 + 0.012, 0);
5818
+ // src/models/launcher.ts
5819
+ function buildLauncher(kit, options) {
5820
+ const group = kit.group();
5821
+ const type = options.lt ?? "";
5822
+ const isFlame = type === "flame" || type === "flameDIY";
5823
+ const parts = { family: isFlame ? "flame" : "rocket", launcherType: type, glow: [] };
5824
+ if (type === "flame") buildFlamethrower(kit, group, parts);
5825
+ else if (type === "flameDIY") buildImprovisedFlamethrower(kit, group, parts);
5826
+ else ROCKET_BUILDERS[type]?.(kit, group, parts);
5827
+ parts.eject = parts.muzzle;
5828
+ return { group, parts };
5799
5829
  }
5800
5830
 
5801
- // src/models/attachments/belt-box.ts
5802
- function buildBeltBox(kit, belt) {
5803
- const { M, box: box2, cylinderX } = kit;
5804
- box2(belt, 0.11, 0.1, 0.08, M.olive, 0, 0, 0);
5805
- box2(belt, 0.112, 0.012, 0.082, M.dark, 0, 0.046, 0);
5806
- for (let i = 0; i < 7; i++) {
5807
- cylinderX(belt, 42e-4, 0.034, M.brass, -0.04 + i * 0.012, 0.07, 0.035, 10);
5808
- box2(belt, 6e-3, 6e-3, 0.012, M.metal, -0.04 + i * 0.012, 0.066, 0.035);
5831
+ // src/models/m134.ts
5832
+ var PI7 = Math.PI;
5833
+ var BARRELS = 6;
5834
+ var CLUSTER_RADIUS = 0.03;
5835
+ var BARREL_RADIUS = 78e-4;
5836
+ var BARREL_END = 0.8;
5837
+ function buildBarrelCluster(kit, parts, group) {
5838
+ const { M, cylinderX } = kit;
5839
+ const barrels = kit.pivot(group, 0, 0, 0);
5840
+ parts.barrels = barrels;
5841
+ const length = BARREL_END - 0.3;
5842
+ let heated;
5843
+ for (let i = 0; i < BARRELS; i++) {
5844
+ const angle = i * 2 * PI7 / BARRELS;
5845
+ const y = Math.cos(angle) * CLUSTER_RADIUS;
5846
+ const z = Math.sin(angle) * CLUSTER_RADIUS;
5847
+ const barrel = cylinderX(barrels, BARREL_RADIUS, length, M.metal, 0.3 + length / 2, y, z, 14);
5848
+ if (heated) barrel.material = heated;
5849
+ else heated = heatify(parts.glow, barrel).material;
5850
+ cylinderX(barrels, BARREL_RADIUS * 0.55, 4e-3, M.hole, BARREL_END + 5e-4, y, z, 10);
5809
5851
  }
5852
+ cylinderX(barrels, 0.012, length, M.steel, 0.3 + length / 2, 0, 0, 12);
5853
+ for (const x of [0.42, 0.58, 0.74]) cylinderX(barrels, CLUSTER_RADIUS + BARREL_RADIUS * 1.6, 0.014, M.steel, x, 0, 0, 24);
5854
+ cylinderX(barrels, CLUSTER_RADIUS + BARREL_RADIUS * 1.8, 0.03, M.dark, BARREL_END - 0.015, 0, 0, 24);
5810
5855
  }
5811
-
5812
- // src/models/attachments/drum.ts
5813
- function buildDrum(kit, magazine) {
5814
- const { M, box: box2, cylinderZ } = kit;
5815
- cylinderZ(magazine, 0.075, 0.07, M.dark, 0.02, -0.085, 0, 22);
5816
- cylinderZ(magazine, 0.05, 0.074, M.metal, 0.02, -0.085, 0, 18);
5817
- box2(magazine, 0.05, 0.04, 0.03, M.dark, 0, -0.02, 0);
5856
+ function buildHousing(kit, parts, group) {
5857
+ const { M, box: box2, cylinderX, cylinderZ } = kit;
5858
+ box2(group, 0.3, 0.14, 0.15, M.metal, 0.18, 0, 0);
5859
+ cylinderX(group, 0.052, 0.12, M.dark, 0.06, 0, 0, 20);
5860
+ cylinderX(group, 0.062, 0.2, M.dark, 0.38, 0, 0, 22);
5861
+ for (let i = 0; i < 4; i++) cylinderX(group, 0.064, 8e-3, M.steel, 0.3 + i * 0.05, 0, 0, 22);
5862
+ box2(group, 0.04, 0.05, 0.2, M.dark, 0.1, 0.095, 0);
5863
+ box2(group, 0.2, 0.012, 0.045, M.dark, 0.22, 0.158, 0);
5864
+ box2(group, 0.012, 0.05, 0.045, M.dark, 0.13, 0.13, 0);
5865
+ box2(group, 0.012, 0.05, 0.045, M.dark, 0.31, 0.13, 0);
5866
+ for (const side of [-1, 1]) {
5867
+ box2(group, 0.03, 0.03, 0.1, M.poly, 0.06, -0.01, side * 0.14);
5868
+ cylinderZ(group, 0.016, 0.07, M.poly, 0.06, -0.01, side * 0.2, 14);
5869
+ }
5870
+ parts.trigger = box2(group, 0.012, 0.02, 0.016, M.dark, 0.1, 0.085, 0.14, 0.3);
5871
+ parts.safety = box2(group, 0.014, 6e-3, 0.01, M.dark, 0.2, 0.073, -0.076, 0.4);
5818
5872
  }
5819
-
5820
- // src/models/helical-magazine.ts
5821
- var PI9 = Math.PI;
5822
- var LENGTH = 0.26;
5823
- var RADIUS = 0.026;
5824
- var TURNS = 11;
5825
- function buildHelicalMagazine(context) {
5826
- const { kit, group, parts, receiverEnd, barrelY, barrelRadius } = context;
5827
- const { M, cylinderX, box: box2 } = kit;
5828
- const x = receiverEnd + LENGTH / 2 + 0.02;
5829
- const y = barrelY - barrelRadius - RADIUS - 0.012;
5830
- const magazine = kit.pivot(group, x, y, 0);
5831
- parts.mag = magazine;
5832
- cylinderX(magazine, RADIUS, LENGTH, M.dark, 0, 0, 0, 20);
5833
- cylinderX(magazine, RADIUS * 1.06, 0.01, M.metal, -LENGTH / 2, 0, 0, 20);
5834
- cylinderX(magazine, RADIUS * 1.06, 0.01, M.metal, LENGTH / 2, 0, 0, 20);
5835
- for (let i = 0; i < TURNS; i++) box2(magazine, 0.012, RADIUS * 2.15, 0.014, M.metal, -LENGTH / 2 + (i + 0.5) * LENGTH / TURNS, 0, 0, 0, 0, i * PI9 / 4);
5836
- box2(magazine, 0.05, 0.03, 0.03, M.dark, -LENGTH / 2 - 0.02, RADIUS * 0.4, 0);
5837
- parts.magDrop = 0.16;
5838
- return x;
5873
+ function buildFeed(kit, parts, group) {
5874
+ const { M, box: box2, cylinderX } = kit;
5875
+ const belt = kit.pivot(group, 0.2, -0.13, -0.16);
5876
+ parts.belt = belt;
5877
+ box2(belt, 0.16, 0.12, 0.1, M.olive, 0, 0, 0);
5878
+ box2(belt, 0.162, 0.012, 0.102, M.dark, 0, 0.056, 0);
5879
+ for (let i = 0; i < 8; i++) cylinderX(belt, 46e-4, 0.04, M.brass, -0.07 + i * 0.02, 0.075, 0.04, 10);
5880
+ box2(group, 0.1, 0.03, 0.1, M.metal, 0.22, -0.06, -0.1, 0, 0, 0.5);
5881
+ const cover = kit.pivot(group, 0.1, 0.075, 0);
5882
+ parts.cover = cover;
5883
+ box2(cover, 0.24, 0.012, 0.15, M.metal, 0.12, 6e-3, 0);
5884
+ const bolt = kit.pivot(group);
5885
+ parts.bolt = bolt;
5886
+ parts.stroke = 0.03;
5887
+ box2(bolt, 0.03, 0.012, 0.012, M.steelL, 0.28, 0.02, 0.078);
5888
+ }
5889
+ function buildM134(kit) {
5890
+ const group = kit.group();
5891
+ const parts = { family: "belt", kick: 0.9, glow: [] };
5892
+ buildHousing(kit, parts, group);
5893
+ buildBarrelCluster(kit, parts, group);
5894
+ buildFeed(kit, parts, group);
5895
+ parts.frames = {
5896
+ length: BARREL_END,
5897
+ barrelY: 0,
5898
+ barrelRadius: BARREL_RADIUS,
5899
+ receiverStart: 0.06,
5900
+ receiverEnd: 0.48,
5901
+ receiverHeight: 0.14,
5902
+ receiverWidth: 0.15,
5903
+ handguardEnd: 0.6,
5904
+ magazineX: 0.2,
5905
+ gripX: 0.06,
5906
+ hasRail: false,
5907
+ // the tripod head, where the gun balances (the tripod of the counters joins here too)
5908
+ underbarrel: [0.4, -0.1]
5909
+ };
5910
+ parts.muzzle = kit.mark(group, BARREL_END + 0.01, 0, 0);
5911
+ parts.eject = kit.mark(group, 0.3, -0.07, 0.08);
5912
+ return { group, parts };
5839
5913
  }
5840
5914
 
5841
- // src/models/rifle-controls.ts
5842
- function buildGrip(context) {
5843
- const { kit, group, parts, options: o, furniture: furn, receiverStart: x0, receiverHeight: hR, receiverWidth: wR } = context;
5844
- const { M, box: box2 } = kit;
5845
- const gx = x0 + (o.gripAt || 0.07);
5846
- if (!context.isTraditional && o.grip !== "none") {
5847
- const grip = kit.pivot(group, gx - 0.012, -hR / 2 - 0.046, 0);
5848
- grip.rotation.z = -0.3;
5849
- parts.grip = grip;
5850
- box2(grip, 0.032, 0.105, 0.028, furn, 0, 0, 0);
5851
- for (let i = 0; i < 3; i++) box2(grip, 6e-3, 0.012, 0.026, furn, 0.017, 0.022 - i * 0.02, 0);
5852
- box2(grip, 0.034, 8e-3, 0.03, furn, 0, -0.052, 0);
5853
- }
5854
- box2(group, 0.06, 4e-3, 0.01, M.metal, gx + 0.03, -hR / 2 - 0.024, 0);
5855
- box2(group, 4e-3, 0.022, 0.01, M.metal, gx + 0.06, -hR / 2 - 0.013, 0);
5856
- parts.trigger = box2(group, 4e-3, 0.016, 4e-3, M.dark, gx + 0.024, -hR / 2 - 0.012, 0, 0.15);
5857
- if (o.sel) parts.safety = box2(group, 0.016, 5e-3, 3e-3, M.dark, gx + 5e-3, -hR * 0.15, -wR / 2 - 2e-3, 0.4);
5858
- return gx;
5915
+ // src/models/m2.ts
5916
+ function buildM2(kit) {
5917
+ const { M, box: box2, cylinderX, cylinderZ } = kit;
5918
+ const group = kit.group();
5919
+ const parts = { family: "belt", kick: 2.2, stroke: 0.08, glow: [] };
5920
+ box2(group, 0.52, 0.13, 0.12, M.metal, 0.36, 0, 0);
5921
+ for (let i = 0; i < 5; i++) cylinderZ(group, 6e-3, 0.124, M.dark, 0.15 + i * 0.1, -0.03, 0, 10);
5922
+ heatify(parts.glow, cylinderX(group, 0.016, 1.14, M.metal, 1.14, 0.01, 0, 16));
5923
+ cylinderX(group, 0.034, 0.24, M.dark, 0.74, 0.01, 0, 16);
5924
+ for (let i = 0; i < 6; i++) cylinderX(group, 0.036, 0.012, M.metal, 0.66 + i * 0.035, 0.01, 0, 16);
5925
+ cylinderX(group, 0.022, 0.05, M.dark, 1.69, 0.01, 0, 16);
5926
+ box2(group, 0.04, 0.09, 0.03, M.wood, 0.05, -0.02, 0.05);
5927
+ box2(group, 0.04, 0.09, 0.03, M.wood, 0.05, -0.02, -0.05);
5928
+ box2(group, 0.06, 0.02, 0.12, M.metal, 0.09, -0.02, 0);
5929
+ const belt = kit.pivot(group, 0.38, -0.05, -0.12);
5930
+ parts.belt = belt;
5931
+ box2(belt, 0.16, 0.12, 0.09, M.olive, 0, 0, 0);
5932
+ for (let i = 0; i < 8; i++) cylinderX(belt, 6e-3, 0.05, M.brass, -0.08 + i * 0.02, 0.08, 0.04, 8);
5933
+ const cover = kit.pivot(group, 0.12, 0.065, 0);
5934
+ parts.cover = cover;
5935
+ box2(cover, 0.4, 0.012, 0.12, M.metal, 0.2, 6e-3, 0);
5936
+ box2(group, 0.04, 0.05, 0.04, M.dark, 0.66, 0.1, 0);
5937
+ const bolt = kit.pivot(group);
5938
+ parts.bolt = bolt;
5939
+ box2(bolt, 0.03, 0.03, 0.02, M.metal, 0.5, 0, 0.07);
5940
+ const tripod = kit.group();
5941
+ tripod.position.set(0.42, -0.1, 0);
5942
+ group.add(tripod);
5943
+ box2(tripod, 0.03, 0.06, 0.03, M.metal, 0, 0, 0);
5944
+ box2(tripod, 0.012, 0.62, 0.012, M.metal, -0.2, -0.25, 0, 0.75);
5945
+ box2(tripod, 0.012, 0.62, 0.012, M.metal, 0.22, -0.25, 0.16, -0.75, 0, 0.3);
5946
+ box2(tripod, 0.012, 0.62, 0.012, M.metal, 0.22, -0.25, -0.16, -0.75, 0, -0.3);
5947
+ parts.muzzle = kit.mark(group, 1.72, 0.01, 0);
5948
+ parts.eject = kit.mark(group, 0.3, -0.07, 0);
5949
+ return { group, parts };
5859
5950
  }
5860
- function buildMagazine(context, gx) {
5861
- const { kit, group, parts, options: o, receiverStart: x0, receiverHeight: hR, receiverWidth: wR, receiverLength: RL } = context;
5862
- const { M, box: box2 } = kit;
5863
- if (o.mag === "helical") return buildHelicalMagazine(context);
5864
- const mx = o.magAt != null ? x0 + o.magAt : gx + 0.075;
5865
- const scale = o.magLen || 1;
5866
- const magazine = kit.pivot(group, mx, -hR / 2, 0);
5951
+
5952
+ // src/models/p90.ts
5953
+ function buildP90(kit) {
5954
+ const { M, box: box2, cylinderX } = kit;
5955
+ const group = kit.group();
5956
+ const parts = { family: "p90", stroke: 0.04, kick: 0.6, glow: [] };
5957
+ box2(group, 0.4, 0.07, 0.055, M.poly, 0.2, 0, 0);
5958
+ box2(group, 0.12, 0.075, 0.05, M.poly, 0.2, -0.07, 0);
5959
+ box2(group, 0.08, 0.04, 0.03, M.hole, 0.21, -0.06, 0);
5960
+ box2(group, 0.06, 0.13, 0.056, M.poly, 0.03, -0.03, 0);
5961
+ box2(group, 0.05, 0.03, 0.056, M.poly, 0.33, -0.04, 0);
5962
+ heatify(parts.glow, cylinderX(group, 8e-3, 0.09, M.metal, 0.43, 0, 0, 16));
5963
+ const magazine = kit.pivot(group, 0.22, 0.047, 0);
5867
5964
  parts.mag = magazine;
5868
- const finish = o.magMat === "bakelite" ? M.bakelite : o.magMat === "poly" ? M.polyMag : void 0;
5869
- if (o.mag === "curved") {
5870
- let px = 0;
5871
- let py = 0;
5872
- const material = finish ?? M.metal;
5873
- for (const angle of [0.06, 0.22, 0.38]) {
5874
- const length = 0.07 * scale;
5875
- const cx = px + Math.sin(angle) * length / 2;
5876
- const cy = py - Math.cos(angle) * length / 2;
5877
- box2(magazine, 0.052, length + 4e-3, 0.024, material, cx, cy, 0, angle);
5878
- box2(magazine, 0.04, length * 0.8, 0.026, M.dark, cx, cy, 0, angle);
5879
- px += Math.sin(angle) * length;
5880
- py -= Math.cos(angle) * length;
5881
- }
5882
- box2(magazine, 0.058, 8e-3, 0.028, material, px, py, 0, 0.38);
5883
- parts.magDrop = 0.07 * scale * 3 + 0.04;
5884
- } else if (o.mag === "box") {
5885
- const length = 0.13 * scale;
5886
- const curve = o.curve || 0;
5887
- const segment = length / 3;
5888
- const material = finish ?? M.dark;
5889
- let px = 0;
5890
- let py = 0;
5891
- let angle = 0.08;
5892
- for (let i = 0; i < 3; i++) {
5893
- angle = 0.08 + curve * i;
5894
- const cx = px + Math.sin(angle) * segment / 2;
5895
- const cy = py - Math.cos(angle) * segment / 2;
5896
- box2(magazine, 0.05, segment + 4e-3, 0.024, material, cx, cy, 0, angle);
5897
- box2(magazine, 0.04, 3e-3, 0.026, M.metal, cx, cy, 0, angle);
5898
- px += Math.sin(angle) * segment;
5899
- py -= Math.cos(angle) * segment;
5900
- }
5901
- box2(magazine, 0.056, 7e-3, 0.028, material, px, py - 2e-3, 0, angle);
5902
- parts.magDrop = length + 0.03;
5903
- } else if (o.mag === "drum") {
5904
- buildDrum(kit, magazine);
5905
- parts.magDrop = 0.2;
5906
- } else if (o.mag === "internal") {
5907
- box2(group, 0.08, 0.025, 0.03, context.isTraditional ? context.furniture : M.metal, mx, -hR / 2 - 0.012, 0);
5908
- parts.mag = null;
5909
- } else parts.mag = null;
5910
- if (o.mag === "belt") {
5911
- const belt = kit.pivot(group, mx + 0.02, -hR / 2 - 0.05, -0.045);
5912
- parts.belt = belt;
5913
- buildBeltBox(kit, belt);
5914
- const cover = kit.pivot(group, x0 + 0.01, hR / 2, 0);
5915
- parts.cover = cover;
5916
- box2(cover, RL - 0.03, 0.014, wR * 1.05, M.metal, (RL - 0.03) / 2, 7e-3, 0);
5917
- box2(cover, 0.02, 0.012, wR * 0.6, M.dark, RL - 0.04, 0.016, 0);
5965
+ parts.magDir = 1;
5966
+ parts.magDrop = 0.08;
5967
+ box2(magazine, 0.3, 0.022, 0.05, M.glass, 0, 0, 0);
5968
+ for (let i = 0; i < 12; i++) cylinderX(magazine, 28e-4, 0.02, M.brass, -0.12 + i * 0.022, 0, 0, 6);
5969
+ scope(kit, group, 1, 0.24, 0.06, 0);
5970
+ const bolt = kit.pivot(group);
5971
+ parts.bolt = bolt;
5972
+ box2(bolt, 0.02, 0.012, 0.012, M.dark, 0.3, 0.02, 0.03);
5973
+ parts.trigger = box2(group, 4e-3, 0.016, 4e-3, M.dark, 0.24, -0.045, 0);
5974
+ parts.muzzle = kit.mark(group, 0.48, 0, 0);
5975
+ parts.eject = kit.mark(group, 0.2, -0.07, 0);
5976
+ return { group, parts };
5977
+ }
5978
+
5979
+ // src/models/part-registry.ts
5980
+ var SINGLE_PARTS = ["slide", "bolt", "boltHandle", "mag", "trigger", "hammer", "lever", "pump", "belt", "cover", "crane", "cylinder", "barrels", "safety", "muzzle", "eject", "loadPort", "topLoad", "rear", "round", "gauge", "stock", "ugl"];
5981
+ function partRegistry(parts) {
5982
+ const registry = /* @__PURE__ */ new Map();
5983
+ for (const key of SINGLE_PARTS) {
5984
+ const part = parts[key];
5985
+ if (part) registry.set(key, part);
5918
5986
  }
5919
- return mx;
5987
+ parts.breech?.forEach((part, index) => registry.set(`breech_${index}`, part));
5988
+ parts.chambers?.forEach((part, index) => registry.set(`chamber_${index}`, part));
5989
+ return registry;
5920
5990
  }
5921
5991
 
5922
- // src/models/rifle-stock.ts
5923
- function buildStock(context, magazineX) {
5924
- const { kit, group, options: o, furniture: furn, length: L, stockLength: S, receiverStart: x0, receiverEnd: x1, receiverHeight: hR } = context;
5992
+ // src/models/pistol-slide.ts
5993
+ var PI8 = Math.PI;
5994
+ function buildSlide(kit, slide2, parts, options, style, size, metal2) {
5925
5995
  const { M, box: box2, cylinderX } = kit;
5926
- if (context.isTraditional) {
5927
- buttpad(kit, group, 6e-3, -0.04, 0.13, 0.038);
5928
- box2(group, 0.03, 0.13, 0.038, furn, 0.024, -0.04, 0);
5929
- box2(group, S, 0.055, 0.034, furn, S / 2, -0.012, 0, 0.06);
5930
- box2(group, 0.09, 0.05, 0.032, furn, x0 - 0.02, -0.035, 0, 0.25);
5931
- const forendLength = x1 - x0 + (L - x1) * (o.fore ?? 0.62);
5932
- if (forendLength > 0) box2(group, forendLength, 0.045, 0.04, furn, x0 + forendLength / 2, -hR / 2 + 4e-3, 0);
5933
- if (o.stock === "straight") box2(group, 0.15, 0.04, 0.035, furn, x1 + 0.08, -hR / 2, 0);
5934
- box2(group, 0.04, 4e-3, 0.03, M.metal, magazineX, -hR / 2 - 0.022, 0);
5935
- swivel(kit, group, 0.2, -0.08);
5936
- swivel(kit, group, x1 + (L - x1) * 0.45, -hR / 2 - 0.03);
5937
- if (o.bands) for (const fraction of [0.35, 0.75]) cylinderX(group, 0.024, 8e-3, M.metal, x1 + (L - x1) * fraction * (o.fore || 0.62), -4e-3, 0, 16);
5938
- } else if (o.stock === "fixed" || o.stock === "wood") {
5939
- box2(group, S, 0.05, 0.03, furn, S / 2, -8e-3, 0, 0.05);
5940
- box2(group, 0.03, 0.13, 0.036, furn, 0.022, -0.035, 0);
5941
- buttpad(kit, group, 4e-3, -0.035, 0.13, 0.036);
5942
- if (o.stock === "wood") box2(group, S * 0.6, 0.03, 0.028, furn, S * 0.55, -0.045, 0, 0.2);
5943
- swivel(kit, group, 0.08, -0.09);
5944
- } else if (o.stock === "skeleton") {
5945
- box2(group, S, 0.014, 0.026, furn, S / 2, 0.012, 0, 0.03);
5946
- box2(group, S, 0.014, 0.026, furn, S / 2, -0.05, 0, -0.08);
5947
- box2(group, 0.03, 0.12, 0.034, furn, 0.02, -0.025, 0);
5948
- buttpad(kit, group, 4e-3, -0.025, 0.12, 0.034);
5949
- } else if (o.stock === "retract") {
5950
- cylinderX(group, 0.013, S, M.metal, S / 2, 0, 0, 16);
5951
- for (let i = 0; i < 5; i++) box2(group, 3e-3, 3e-3, 8e-3, M.dark, S * 0.2 + i * 0.015, -0.013, 0);
5952
- box2(group, 0.034, 0.11, 0.036, furn, 0.02, -0.025, 0);
5953
- box2(group, S * 0.55, 0.032, 0.03, furn, S * 0.35, 0, 0);
5954
- buttpad(kit, group, 3e-3, -0.025, 0.11, 0.036);
5955
- } else if (o.stock === "fold") {
5956
- box2(group, S, 6e-3, 6e-3, M.metal, S / 2, 0.01, 0.012);
5957
- box2(group, S, 6e-3, 6e-3, M.metal, S / 2, -0.04, 0.012, -0.12);
5958
- box2(group, 0.012, 0.1, 0.03, M.metal, 6e-3, -0.02, 0.012);
5996
+ const { length: L, width: w } = size;
5997
+ const slideHeight = style.inset ? size.height * 0.92 : size.height;
5998
+ const slideWidth = style.inset ? w * 0.86 : w;
5999
+ kit.roundness = style.rf;
6000
+ if (style.tri) box2(slide2, L * 0.42, slideHeight, slideWidth, metal2, L * 0.21, 0, 0);
6001
+ else if (style.open) {
6002
+ box2(slide2, L * 0.45, slideHeight, slideWidth, metal2, L * 0.225, 0, 0);
6003
+ box2(slide2, L * 0.55, slideHeight * 0.62, slideWidth, metal2, L * 0.72, -slideHeight * 0.19, 0);
6004
+ } else box2(slide2, L, slideHeight, slideWidth, metal2, L / 2, 0, 0);
6005
+ if (style.slope) box2(slide2, L * 0.25, slideHeight * 0.5, slideWidth * 0.98, metal2, L * 0.86, slideHeight * 0.35, 0, -0.22);
6006
+ kit.roundness = null;
6007
+ for (let i = 0; i < (style.flush ? 9 : 7); i++) box2(slide2, 22e-4, slideHeight * 0.72, slideWidth + 14e-4, M.dark, 7e-3 + i * 52e-4, -2e-3, 0, 0);
6008
+ if (style.frontSer) for (let i = 0; i < 5; i++) box2(slide2, 22e-4, slideHeight * 0.6, slideWidth + 14e-4, M.dark, L * 0.76 + i * 52e-4, -3e-3, 0);
6009
+ if (style.port) {
6010
+ box2(slide2, L * style.port, 4e-3, slideWidth * 0.55, M.hole, L * 0.6, slideHeight / 2 - 1e-3, slideWidth * 0.12);
6011
+ box2(slide2, L * style.port * 0.85, 3e-3, slideWidth * 0.4, M.steelL, L * 0.6, slideHeight / 2 - 4e-4, slideWidth * 0.1);
6012
+ box2(slide2, 0.012, 4e-3, 2e-3, M.dark, L * 0.55, slideHeight * 0.15, slideWidth / 2 + 8e-4);
6013
+ }
6014
+ if (style.open) {
6015
+ heatify(parts.glow, cylinderX(slide2, 62e-4, L * 0.56, M.steelL, L * 0.7, slideHeight * 0.12, 0, 18));
6016
+ box2(slide2, 0.03, 4e-3, slideWidth * 0.6, M.hole, L * 0.48, slideHeight / 2 - 1e-3, 0);
6017
+ }
6018
+ if (style.tri) {
6019
+ const tube = new kit.THREE.Mesh(new kit.THREE.CylinderGeometry(0.016, 0.016, L * 0.6, 3), metal2);
6020
+ tube.castShadow = tube.receiveShadow = true;
6021
+ tube.rotation.set(0, 0, -PI8 / 2);
6022
+ tube.rotateY(PI8 / 6);
6023
+ tube.position.set(L * 0.7, slideHeight * 0.08, 0);
6024
+ slide2.add(tube);
6025
+ heatify(parts.glow, tube);
6026
+ box2(slide2, L * 0.6, 6e-3, 0.012, metal2, L * 0.7, slideHeight * 0.38, 0);
6027
+ for (let i = 0; i < 6; i++) box2(slide2, 8e-3, 3e-3, 0.013, M.hole, L * 0.45 + i * 0.04, slideHeight * 0.41, 0);
6028
+ }
6029
+ if (style.bushing) {
6030
+ heatify(parts.glow, cylinderX(slide2, 78e-4, 6e-3, M.steelL, L + 2e-3, 1e-3, 0, 18));
6031
+ cylinderX(slide2, 45e-4, 4e-3, M.steel, L + 2e-3, -0.012, 0, 12);
6032
+ }
6033
+ const muzzleY = style.tri ? slideHeight * 0.08 : 2e-3;
6034
+ heatify(parts.glow, cylinderX(slide2, style.tri ? 72e-4 : 62e-4, 4e-3, M.dark, L + 15e-4, muzzleY, 0, 18));
6035
+ cylinderX(slide2, 34e-4, 5e-3, M.hole, L + 25e-4, muzzleY, 0, 12);
6036
+ const top = slideHeight / 2;
6037
+ if (style.sight === "u" || style.sight === "3dot") {
6038
+ box2(slide2, 4e-3, 7e-3, 4e-3, M.dark, L - 8e-3, top + 35e-4, 0);
6039
+ box2(slide2, 22e-4, 22e-4, 46e-4, M.white, L - 8e-3, top + 58e-4, 0);
6040
+ box2(slide2, 8e-3, 7e-3, 0.016, M.dark, 9e-3, top + 35e-4, 0);
6041
+ if (style.sight === "u") box2(slide2, 4e-3, 2e-3, 0.0122, M.white, 9e-3, top + 6e-3, 0);
6042
+ else box2(slide2, 22e-4, 22e-4, 0.0166, M.white, 9e-3, top + 58e-4, 0);
6043
+ } else {
6044
+ box2(slide2, 6e-3, 4e-3, 3e-3, M.dark, L - 6e-3, top + 2e-3, 0);
6045
+ box2(slide2, 6e-3, 4e-3, 0.012, M.dark, 8e-3, top + 2e-3, 0);
6046
+ }
6047
+ if (style.decock === "slide") {
6048
+ box2(slide2, 0.012, 8e-3, 4e-3, M.dark, 0.02, slideHeight * 0.15, slideWidth / 2 + 2e-3, 0.3);
6049
+ box2(slide2, 0.012, 8e-3, 4e-3, M.dark, 0.02, slideHeight * 0.15, -slideWidth / 2 - 2e-3, 0.3);
6050
+ }
6051
+ if (style.safety === "slide") parts.safety = box2(slide2, 0.012, 8e-3, 4e-3, M.dark, 0.018, slideHeight * 0.1, -slideWidth / 2 - 2e-3, 0.2);
6052
+ if (options.sel) {
6053
+ const selector = box2(slide2, 0.01, 8e-3, 3e-3, M.dark, 0.014, -slideHeight * 0.2, slideWidth / 2 + 14e-4);
6054
+ parts.safety ?? (parts.safety = selector);
5959
6055
  }
5960
6056
  }
5961
6057
 
5962
- // src/models/rifle.ts
5963
- var PI10 = Math.PI;
5964
- function buildReceiver(context) {
5965
- const { kit, group, parts, options: o, length: L, receiverStart: x0, receiverEnd: x1, receiverHeight: hR, receiverWidth: wR, receiverLength: RL, barrelY: by } = context;
5966
- const { M, box: box2, cylinderX, cylinderY, cylinderZ, sphere } = kit;
5967
- const receiverMaterial = o.tan ? M.tan : M.metal;
5968
- const middle = (x0 + x1) / 2;
5969
- if (o.round) {
5970
- cylinderX(group, hR * 0.36, RL, M.steel, middle, by, 0, 20);
5971
- cylinderX(group, hR * 0.32, 0.05, M.steel, x0 - 0.01, by, 0, 18);
5972
- } else if (o.ar) {
5973
- box2(group, RL, hR * 0.5, wR, receiverMaterial, middle, hR * 0.25, 0);
5974
- box2(group, RL * 0.92, hR * 0.52, wR * 0.96, receiverMaterial, middle - RL * 0.02, -hR * 0.24, 0);
5975
- box2(group, 0.05, 0.03, wR * 1.15, receiverMaterial, x0 + RL * 0.62, -hR * 0.5 - 0.01, 0);
5976
- cylinderZ(group, 6e-3, 0.012, M.metal, x1 - 0.03, hR * 0.28, wR / 2 + 6e-3, 12);
5977
- box2(group, 0.012, 0.01, 0.012, M.metal, x1 - 0.05, hR * 0.12, wR / 2 + 6e-3);
5978
- box2(group, 0.03, 8e-3, 0.012, M.dark, x0 + 0.02, hR / 2 + 4e-3, 0);
5979
- box2(group, 0.012, 6e-3, 4e-3, M.dark, x0 + RL * 0.55, -hR * 0.15, -wR / 2 - 2e-3);
5980
- } else box2(group, RL, hR, wR, receiverMaterial, middle, 0, 0);
5981
- if (o.ak) {
5982
- box2(group, RL * 0.86, hR * 0.28, wR * 0.95, M.metal, middle - 8e-3, hR / 2 + 4e-3, 0);
5983
- for (let i = 0; i < 3; i++) box2(group, 4e-3, hR * 0.3, wR * 0.97, M.dark, x0 + RL * 0.25 + i * 0.02, hR / 2 + 4e-3, 0);
5984
- box2(group, RL * 0.5, 0.012, 3e-3, M.metal, x0 + RL * 0.55, -hR * 0.02, wR / 2 + 15e-4, -0.05);
5985
- box2(group, 0.012, 0.014, 3e-3, M.dark, x0 + RL * 0.32, -hR * 0.05, wR / 2 + 2e-3);
6058
+ // src/models/pistol-style.ts
6059
+ var STYLES = {
6060
+ glock: { rf: 0.07, ga: -0.36, grip: "finger", guard: "square", rail: 1, sight: "u", port: 0.2, slideM: "metal", frame: "poly" },
6061
+ taurus: { rf: 0.16, ga: -0.3, grip: "panel", guard: "square", rail: 1, sight: "3dot", port: 0.18, frontSer: 1, frame: "poly" },
6062
+ g2c: { rf: 0.2, ga: -0.3, grip: "panel", guard: "round", rail: 0, sight: "3dot", port: 0.17, frame: "poly", safety: "thumb" },
6063
+ beretta: { rf: 0.28, ga: -0.27, grip: "wood", guard: "round", hammer: "spur", sight: "3dot", open: 1, decock: "slide", port: 0, frame: "metal" },
6064
+ pt100: { rf: 0.25, ga: -0.27, grip: "rubber", guard: "round", hammer: "spur", sight: "3dot", port: 0.17, decock: "frame", frame: "metal" },
6065
+ sig: { rf: 0.14, ga: -0.3, grip: "rubber", guard: "hook", hammer: "spur", sight: "3dot", port: 0.19, decock: "frame", rail: 1, tall: 1.12, frame: "metal" },
6066
+ cz: { rf: 0.32, ga: -0.3, grip: "rubber", guard: "round", hammer: "spur", sight: "3dot", port: 0.15, inset: 1, safety: "thumb", frame: "metal" },
6067
+ m1911: { rf: 0.24, ga: -0.2, grip: "wood", guard: "round", hammer: "ring", sight: "small", port: 0.12, bushing: 1, beaver: 1, safety: "thumb", slideStop: 1, frame: "metal" },
6068
+ makarov: { rf: 0.48, ga: -0.24, grip: "star", guard: "round", hammer: "ring", sight: "small", port: 0.14, flush: 1, safety: "slide", lanyard: 1, frame: "metal" },
6069
+ fiveseven: { rf: 0.2, ga: -0.4, grip: "chunky", guard: "square", rail: 1, sight: "3dot", port: 0.18, slope: 1, frame: "poly" },
6070
+ deagle: { rf: 0.08, ga: -0.28, grip: "rubber", guard: "round", hammer: "big", sight: "small", port: 0.16, tri: 1, safety: "slide", frame: "metal" }
6071
+ };
6072
+
6073
+ // src/models/pistol.ts
6074
+ var PI9 = Math.PI;
6075
+ function buildFrame(kit, group, parts, options, style, size, materials) {
6076
+ const { M, box: box2, cylinderZ, sphere } = kit;
6077
+ const { length: L, height: h, width: w } = size;
6078
+ const fm = materials.frame;
6079
+ if (style.inset) {
6080
+ box2(group, L * 0.9, h * 0.35, w * 0.12, fm, L * 0.47, -h * 0.32, w * 0.47);
6081
+ box2(group, L * 0.9, h * 0.35, w * 0.12, fm, L * 0.47, -h * 0.32, -w * 0.47);
5986
6082
  }
5987
- if (o.g3) {
5988
- cylinderX(group, 9e-3, (L - x1) * 0.6, M.metal, x1 + (L - x1) * 0.28, hR * 0.48, 0, 14);
5989
- box2(group, 0.012, 0.024, 8e-3, M.dark, x1 + (L - x1) * 0.5, hR * 0.48, -0.012);
5990
- cylinderY(group, 0.012, 0.016, M.dark, x0 + 0.03, hR / 2 + 8e-3, 0, 12);
6083
+ const frameLength = style.tri ? L * 0.66 : L * 0.8;
6084
+ kit.roundness = style.rf;
6085
+ box2(group, frameLength, 0.016, w * 0.92, fm, frameLength / 2 + L * 0.06, -h / 2 - 8e-3, 0);
6086
+ kit.roundness = null;
6087
+ if (style.rail) for (let i = 0; i < (style.slope ? 5 : 3); i++) box2(group, 4e-3, 3e-3, w * 0.96, M.dark, L * 0.62 + i * 9e-3, -h / 2 - 0.017, 0);
6088
+ const isGlock = options.st === "glock" || !options.st;
6089
+ if (isGlock) {
6090
+ box2(group, 8e-3, 4e-3, 3e-3, M.dark, L * 0.5, -h / 2 - 3e-3, -w / 2 - 12e-4);
6091
+ box2(group, 8e-3, 4e-3, 3e-3, M.dark, L * 0.5, -h / 2 - 3e-3, w / 2 + 12e-4);
6092
+ }
6093
+ if (style.slideStop) {
6094
+ box2(group, 0.03, 6e-3, 3e-3, M.dark, L * 0.42, -h / 2 - 2e-3, -w / 2 - 14e-4);
6095
+ sphere(group, 28e-4, M.dark, L * 0.55, -h / 2 - 3e-3, w / 2 + 1e-3, 1);
6096
+ } else box2(group, 0.012, 4e-3, 3e-3, M.dark, L * 0.45, -h / 2 - 3e-3, -w / 2 - 12e-4);
6097
+ if (style.decock === "frame") box2(group, 0.01, 0.012, 3e-3, M.dark, L * 0.38, -h / 2 - 0.012, -w / 2 - 14e-4, 0.6);
6098
+ if (style.safety === "thumb") parts.safety = box2(group, 0.014, 6e-3, 4e-3, M.dark, 0.02, -h / 2 - 2e-3, -w / 2 - 16e-4);
6099
+ const gripLength = options.gl ?? 0.105;
6100
+ const gx = 0.034 + (L - 0.2) * 0.15;
6101
+ const gy = -h / 2 - gripLength / 2 + 6e-3;
6102
+ const grip = kit.pivot(group, gx, gy, 0);
6103
+ grip.rotation.z = style.ga;
6104
+ parts.grip = grip;
6105
+ const gripWidth = style.grip === "chunky" ? w * 1.18 : w * 1.06;
6106
+ const gripDepth = style.grip === "chunky" ? 0.036 : 0.032;
6107
+ kit.roundness = 0.3;
6108
+ if (style.grip === "wood" || style.grip === "star") {
6109
+ box2(grip, gripDepth * 0.86, gripLength, w * 0.9, fm, 0, 0, 0);
6110
+ box2(grip, gripDepth * 0.8, gripLength * 0.86, 4e-3, materials.grip, 1e-3, -2e-3, w * 0.47);
6111
+ box2(grip, gripDepth * 0.8, gripLength * 0.86, 4e-3, materials.grip, 1e-3, -2e-3, -w * 0.47);
6112
+ if (style.grip === "wood") {
6113
+ for (let i = 0; i < 6; i++) for (let j = 0; j < 2; j++) box2(grip, 3e-3, 3e-3, 12e-4, M.woodL, -6e-3 + j * 0.012, -gripLength * 0.3 + i * 0.012, w * 0.495, 0, 0, 0);
6114
+ cylinderZ(grip, 35e-4, 15e-4, M.brass, 0, gripLength * 0.25, w * 0.5, 10);
6115
+ cylinderZ(grip, 35e-4, 15e-4, M.brass, 0, -gripLength * 0.3, w * 0.5, 10);
6116
+ } else cylinderZ(grip, 8e-3, 15e-4, M.dark, 0, gripLength * 0.1, w * 0.5, 5);
6117
+ } else box2(grip, gripDepth, gripLength, gripWidth, materials.grip, 0, 0, 0);
6118
+ kit.roundness = null;
6119
+ if (style.grip === "finger") {
6120
+ for (let i = 0; i < 3; i++) box2(grip, 6e-3, 0.012, gripWidth * 0.98, materials.grip, gripDepth / 2 + 2e-3, gripLength * 0.18 - i * 0.022, 0);
6121
+ for (let i = 0; i < 5; i++) for (let j = 0; j < 3; j++) box2(grip, 3e-3, 3e-3, 12e-4, M.dark, -8e-3 + j * 8e-3, -gripLength * 0.25 + i * 0.012, gripWidth / 2 + 4e-4);
5991
6122
  }
5992
- const ejectX = x1 - (o.ejectBack || 0.07);
5993
- const ejectY = o.ar ? hR * 0.2 : by;
5994
- box2(group, 0.05, hR * 0.36, 4e-3, M.hole, ejectX, ejectY, wR / 2 - 4e-4);
5995
- const bolt = kit.pivot(group);
5996
- parts.bolt = bolt;
5997
- parts.stroke = Math.min(0.1, RL * 0.32);
5998
- box2(bolt, 0.044, hR * 0.26, 6e-3, M.steelL, ejectX, ejectY, wR / 2 - 12e-4);
5999
- if (o.charge) {
6000
- box2(bolt, 0.012, 0.01, 0.03, M.metal, ejectX + 0.04, ejectY + 4e-3, wR / 2 + 0.012);
6001
- sphere(bolt, 65e-4, M.metal, ejectX + 0.04, ejectY + 4e-3, wR / 2 + 0.028, 1);
6123
+ if (style.grip === "panel" || style.grip === "rubber" || style.grip === "chunky") for (let i = 0; i < 6; i++) box2(grip, gripDepth * 0.75, 25e-4, gripWidth + 1e-3, M.dark, 2e-3, -gripLength / 2 + 0.018 + i * 0.013, 0);
6124
+ if (style.grip === "chunky") box2(grip, 8e-3, gripLength * 0.8, gripWidth * 0.9, materials.grip, gripDepth / 2 + 2e-3, -4e-3, 0);
6125
+ if (style.beaver) {
6126
+ box2(group, 0.026, 8e-3, w * 0.9, fm, -4e-3, -h / 2 - 4e-3, 0, 0.25);
6127
+ box2(grip, 6e-3, gripLength * 0.55, w * 0.9, fm, -gripDepth * 0.5 - 2e-3, gripLength * 0.18, 0);
6128
+ } else box2(group, 0.02, 9e-3, w * 0.9, fm, 4e-3, -h / 2 - 6e-3, 0);
6129
+ if (style.lanyard) {
6130
+ const ring = kit.torus(grip, new kit.THREE.TorusGeometry(5e-3, 13e-4, 6, 12), M.dark, -0.012, -gripLength / 2 + 4e-3, 0);
6131
+ ring.position.set(-0.012, -gripLength / 2 + 4e-3, 0);
6002
6132
  }
6003
- if (o.ar) box2(group, 0.012, 0.016, 6e-3, receiverMaterial, ejectX - 0.03, ejectY + 4e-3, wR / 2 + 4e-3);
6004
- if (o.rail) rail(kit, group, x0 - 5e-3, RL + 0.07, hR / 2 + (o.ar ? 0 : 3e-3), 0, wR * 0.68);
6133
+ const magazine = kit.pivot(grip);
6134
+ parts.mag = magazine;
6135
+ parts.magDrop = gripLength * 1.15;
6136
+ box2(magazine, 0.022, gripLength * 0.94, w * 0.72, M.dark, 3e-3, -4e-3, 0);
6137
+ if (style.grip === "chunky") box2(magazine, 0.04, 0.01, gripWidth * 1.02, M.poly, 3e-3, -gripLength / 2 - 4e-3, 0);
6138
+ else if (style.flush || style.grip === "wood") box2(magazine, 0.028, 4e-3, w * 0.9, M.steel, 3e-3, -gripLength / 2 - 1e-3, 0);
6139
+ else box2(magazine, 0.031, 6e-3, gripWidth * 1.02, M.dark, 3e-3, -gripLength / 2 - 2e-3, 0);
6140
+ return { gx, ty: -h / 2 };
6005
6141
  }
6006
- function buildBarrel(context) {
6007
- const { kit, group, parts, options: o, length: L, barrelLength: Bl, receiverEnd: x1, barrelRadius: r, barrelY: by } = context;
6008
- const { M, box: box2, cylinderX } = kit;
6009
- heatify(parts.glow, cylinderX(group, r, Bl, M.metal, L - Bl / 2, by, 0, 16));
6010
- if (o.jacket) {
6011
- cylinderX(group, r * 2, (L - x1) * 0.6, M.dark, x1 + (L - x1) * 0.3, by, 0, 16);
6012
- for (let i = 0; i < 6; i++) cylinderX(group, r * 2.15, 4e-3, M.metal, x1 + (L - x1) * (0.05 + i * 0.09), by, 0, 16);
6142
+ function buildPistol(kit, options) {
6143
+ const { M, box: box2 } = kit;
6144
+ const style = { ...STYLES[options.st ?? "glock"] };
6145
+ const group = kit.group();
6146
+ const parts = { family: "pistol", kick: options.deagle ? 1.8 : 0.6, glow: [] };
6147
+ const size = { length: (options.L ?? 0) / 1e3 * 0.97, height: (options.h ?? 0.031) * (style.tall ?? 1), width: options.deagle ? 0.032 : style.flush ? 0.024 : 0.025 };
6148
+ const { length: L, height: h, width: w } = size;
6149
+ const slideMetal = options.steel ? M.steel : options.stain ? M.steelL : M.metal;
6150
+ const frameMetal = style.frame === "poly" ? M.poly : slideMetal;
6151
+ const gripMaterial = style.grip === "wood" ? M.wood : style.grip === "star" ? M.bake : style.grip === "rubber" ? M.rubber : M.poly;
6152
+ const slide2 = kit.pivot(group);
6153
+ parts.slide = slide2;
6154
+ parts.stroke = L * 0.15;
6155
+ buildSlide(kit, slide2, parts, options, style, size, slideMetal);
6156
+ const { gx, ty } = buildFrame(kit, group, parts, options, style, size, { frame: frameMetal, grip: gripMaterial });
6157
+ if (style.guard === "square") {
6158
+ box2(group, 0.046, 4e-3, 9e-3, frameMetal, gx + 0.05, ty - 0.034, 0);
6159
+ box2(group, 4e-3, 0.03, 9e-3, frameMetal, gx + 0.074, ty - 0.02, 0);
6160
+ box2(group, 6e-3, 4e-3, 9e-3, frameMetal, gx + 0.075, ty - 0.034, 0);
6161
+ } else if (style.guard === "hook") {
6162
+ box2(group, 0.046, 4e-3, 9e-3, frameMetal, gx + 0.05, ty - 0.034, 0);
6163
+ box2(group, 4e-3, 0.03, 9e-3, frameMetal, gx + 0.073, ty - 0.02, 0, 0.2);
6164
+ box2(group, 8e-3, 6e-3, 9e-3, frameMetal, gx + 0.074, ty - 0.037, 0, 0.4);
6165
+ } else {
6166
+ const guard = kit.torus(group, new kit.THREE.TorusGeometry(0.019, 22e-4, 6, 16, PI9), frameMetal, gx + 0.05, ty - 0.014, 0);
6167
+ guard.rotation.z = PI9;
6168
+ guard.scale.set(1.2, 1, 1);
6013
6169
  }
6014
- if (o.gas) cylinderX(group, r * 0.75, (L - x1) * 0.55, M.metal, x1 + (L - x1) * 0.3, by + r * 2.2, 0, 14);
6015
- if (o.brake) {
6016
- const kind = o.brake === 2 ? 2 : 1;
6017
- recordFitting(parts, kind === 2 ? "muzzle_brake_heavy" : "muzzle_brake", group, () => buildBrake(kit, group, kind, L, by, r, (body) => void heatify(parts.glow, body)));
6018
- } else if (!o.noFH) {
6019
- recordFitting(parts, "muzzle_default", group, () => {
6020
- heatify(parts.glow, cylinderX(group, r * 1.45, 0.042, M.dark, L - 0.021, by, 0, 16));
6021
- for (let i = 0; i < 4; i++) {
6022
- const angle = i * PI10 / 2 + PI10 / 4;
6023
- box2(group, 0.026, 25e-4, 4e-3, M.hole, L - 0.02, by + Math.cos(angle) * r * 1.42, Math.sin(angle) * r * 1.42, 0, 0, angle);
6024
- }
6025
- });
6170
+ const isGlock = options.st === "glock" || !options.st;
6171
+ parts.trigger = box2(group, 4e-3, 0.014, 4e-3, isGlock ? M.dark : M.steel, gx + 0.042, ty - 0.022, 0, 0.2);
6172
+ if (isGlock) box2(parts.trigger, 15e-4, 8e-3, 45e-4, M.metal, 0, -1e-3, 0);
6173
+ if (style.hammer) {
6174
+ const hammer = kit.pivot(group, 3e-3, h / 2 - 8e-3, 0);
6175
+ parts.hammer = hammer;
6176
+ if (style.hammer === "ring") {
6177
+ box2(hammer, 6e-3, 0.012, 6e-3, M.dark, -2e-3, 4e-3, 0, 0.3);
6178
+ kit.torus(hammer, new kit.THREE.TorusGeometry(45e-4, 16e-4, 6, 12), M.dark, -4e-3, 0.012, 0);
6179
+ } else if (style.hammer === "big") box2(hammer, 0.014, 0.02, 9e-3, M.dark, -4e-3, 7e-3, 0, 0.35);
6180
+ else {
6181
+ box2(hammer, 8e-3, 0.016, 7e-3, M.dark, -2e-3, 6e-3, 0, 0.3);
6182
+ box2(hammer, 8e-3, 4e-3, 8e-3, M.dark, -6e-3, 0.013, 0, 0.3);
6183
+ }
6026
6184
  }
6027
- cylinderX(group, r * 0.55, 4e-3, M.hole, L + 5e-4, by, 0, 12);
6185
+ parts.muzzle = kit.mark(group, L + 6e-3, style.tri ? h * 0.08 : 2e-3, 0);
6186
+ parts.eject = kit.mark(group, L * 0.6, h / 2, w * 0.25);
6187
+ return { group, parts };
6028
6188
  }
6029
- var familyOf = (o) => {
6030
- if (o.mag === "belt") return "belt";
6031
- if (o.pump) return "pump";
6032
- if (o.lever) return "lever";
6033
- if (o.tube) return "semi12";
6034
- if (o.bolt) return o.mag === "box" ? "boltmag" : "bolt";
6035
- return o.mag === "internal" ? "semiint" : "rifle";
6036
- };
6037
- function buildRifle(kit, options) {
6038
- const { M } = kit;
6189
+
6190
+ // src/models/revolver.ts
6191
+ var PI10 = Math.PI;
6192
+ var DEFAULT_CHAMBERS = 6;
6193
+ function buildRevolver(kit, options) {
6194
+ const { M, box: box2, cylinderX } = kit;
6039
6195
  const group = kit.group();
6040
- const parts = { family: "rifle", glow: [] };
6041
- const length = (options.L ?? 0) / 1e3;
6042
- const barrelLength = (options.B ?? 0) / 1e3;
6043
- const isTraditional = options.stock === "trad" || options.stock === "straight";
6044
- const stockLength = options.stock === "none" ? 0 : options.stockLen || Math.min(0.36, Math.max(0.2, 0.3 * length));
6045
- const receiverStart = stockLength;
6046
- const receiverEnd = Math.max(receiverStart + 0.12, length - barrelLength * 0.92);
6047
- const receiverHeight = options.hR || 0.06;
6048
- const context = {
6049
- kit,
6050
- group,
6051
- parts,
6052
- options,
6053
- furniture: options.wood ? M.wood : options.olive ? M.olive : options.tan ? M.tan : M.poly,
6054
- isTraditional,
6055
- length,
6056
- barrelLength,
6057
- stockLength,
6058
- receiverStart,
6059
- receiverEnd,
6060
- receiverHeight,
6061
- receiverWidth: options.wR || 0.034,
6062
- barrelRadius: options.br || 85e-4,
6063
- barrelY: receiverHeight * 0.15,
6064
- receiverLength: receiverEnd - receiverStart
6065
- };
6066
- buildReceiver(context);
6067
- buildBarrel(context);
6068
- buildForend(context);
6069
- const gripX = buildGrip(context);
6070
- const magazineX = buildMagazine(context, gripX);
6071
- parts.stockMeshes = context.isTraditional ? [] : captured(group, () => buildStock(context, magazineX));
6072
- if (context.isTraditional) buildStock(context, magazineX);
6073
- buildFittings(context, gripX);
6074
- const hasHandguard = !context.isTraditional && options.hg !== "none";
6075
- parts.frames = {
6076
- length,
6077
- barrelY: context.barrelY,
6078
- barrelRadius: context.barrelRadius,
6079
- receiverStart,
6080
- receiverEnd,
6081
- receiverHeight,
6082
- receiverWidth: context.receiverWidth,
6083
- handguardEnd: receiverEnd + (length - receiverEnd) * (hasHandguard ? options.hgFrac || 0.5 : 0.4),
6084
- magazineX,
6085
- gripX,
6086
- hasRail: Boolean(options.rail)
6087
- };
6088
- const { barrelY: by, receiverWidth: wR } = context;
6089
- const ejectX = receiverEnd - (options.ejectBack || 0.07);
6090
- const ejectY = options.ar ? receiverHeight * 0.2 : by;
6091
- parts.muzzle = kit.mark(group, length + 0.01, by, 0);
6092
- parts.eject = kit.mark(group, ejectX, ejectY, wR / 2 + 6e-3);
6093
- parts.loadPort = kit.mark(group, receiverEnd - 0.05, -receiverHeight / 2 - 4e-3, 0);
6094
- parts.topLoad = kit.mark(group, ejectX, receiverHeight / 2 + 4e-3, 0);
6095
- parts.family = familyOf(options);
6196
+ const parts = { family: "revolver", kick: options.cal === ".44 Magnum" ? 1.5 : 0.9, glow: [] };
6197
+ const barrel = (options.B ?? 0) / 1e3;
6198
+ const chambers = options.chambers ?? DEFAULT_CHAMBERS;
6199
+ const metal2 = options.steel ? M.steel : M.metal;
6200
+ box2(group, 0.08, 0.044, 0.024, metal2, 0.07, 0, 0);
6201
+ box2(group, 0.036, 0.012, 0.022, metal2, 0.096, 0.022, 0);
6202
+ box2(group, 0.024, 0.012, 4e-3, M.dark, 0.055, 6e-3, -0.013);
6203
+ const crane = kit.pivot(group, 0.077, -0.024, 0);
6204
+ parts.crane = crane;
6205
+ const cylinder = kit.pivot(crane, 0, 0.022, 0);
6206
+ parts.cylinder = cylinder;
6207
+ cylinderX(cylinder, 0.0215, 0.042, metal2, 0, 0, 0, 24);
6208
+ for (let i = 0; i < chambers; i++) {
6209
+ const angle = i * 2 * PI10 / chambers;
6210
+ box2(cylinder, 0.026, 5e-3, 5e-3, M.dark, 0, Math.cos(angle) * 0.0205, Math.sin(angle) * 0.0205, 0, 0, angle);
6211
+ }
6212
+ parts.chambers = [];
6213
+ for (let i = 0; i < chambers; i++) {
6214
+ const angle = i * 2 * PI10 / chambers + PI10 / chambers;
6215
+ parts.chambers.push(kit.mark(cylinder, -0.022, Math.cos(angle) * 0.013, Math.sin(angle) * 0.013));
6216
+ cylinderX(cylinder, 46e-4, 2e-3, M.brass, -0.0215, Math.cos(angle) * 0.013, Math.sin(angle) * 0.013, 12);
6217
+ }
6218
+ heatify(parts.glow, cylinderX(group, 88e-4, barrel, metal2, 0.112 + barrel / 2, 9e-3, 0, 18));
6219
+ box2(group, barrel * 0.92, 0.015, 0.013, metal2, 0.112 + barrel * 0.46, -5e-3, 0);
6220
+ cylinderX(group, 4e-3, 5e-3, M.hole, 0.112 + barrel + 15e-4, 9e-3, 0, 12);
6221
+ box2(group, 0.012, 9e-3, 4e-3, M.dark, 0.112 + barrel - 8e-3, 0.021, 0, -0.3);
6222
+ box2(group, 0.012, 6e-3, 0.012, M.dark, 0.105, 0.031, 0);
6223
+ const hammer = kit.pivot(group, 0.036, 0.018, 0);
6224
+ box2(hammer, 0.01, 0.02, 8e-3, M.dark, -4e-3, 8e-3, 0, 0.45);
6225
+ parts.hammer = hammer;
6226
+ const grip = kit.pivot(group, 0.024, -0.062, 0);
6227
+ grip.rotation.z = -0.38;
6228
+ parts.grip = grip;
6229
+ box2(grip, 0.034, 0.1, 0.031, options.wood ? M.wood : M.rubber, 0, 0, 0);
6230
+ if (!options.wood) for (let i = 0; i < 4; i++) box2(grip, 6e-3, 0.07, 0.033, M.dark, -0.012 + i * 8e-3, 0, 0);
6231
+ box2(group, 0.038, 4e-3, 8e-3, metal2, 0.062, -0.036, 0);
6232
+ box2(group, 4e-3, 0.022, 8e-3, metal2, 0.08, -0.026, 0, 0.2);
6233
+ parts.trigger = box2(group, 4e-3, 0.015, 4e-3, M.dark, 0.06, -0.027, 0, 0.2);
6234
+ parts.muzzle = kit.mark(group, 0.112 + barrel + 6e-3, 9e-3, 0);
6235
+ parts.eject = kit.mark(group, 0.077, 0, 0);
6096
6236
  return { group, parts };
6097
6237
  }
6098
6238
 
@@ -6108,13 +6248,14 @@ var ARCHETYPE_MODELS = (() => {
6108
6248
  }
6109
6249
  return chosen;
6110
6250
  })();
6111
- var BUILDERS = {
6251
+ var BUILDERS2 = {
6112
6252
  pistol: buildPistol,
6113
6253
  revolver: buildRevolver,
6114
6254
  rifle: buildRifle,
6115
6255
  bullpup: buildBullpup,
6116
6256
  p90: (kit) => buildP90(kit),
6117
6257
  break: buildBreakAction,
6258
+ breakLauncher: buildBreakLauncher,
6118
6259
  m2: (kit) => buildM2(kit),
6119
6260
  m134: (kit) => buildM134(kit),
6120
6261
  launcher: buildLauncher
@@ -6138,7 +6279,7 @@ function buildWeaponModelById(THREE, id) {
6138
6279
  const kit = modelKit(THREE);
6139
6280
  const options = { L: entry.length, B: entry.barrel, cal: entry.caliber, ...entry.options };
6140
6281
  if (id in ARCHETYPE_MODELS) delete options.chambers;
6141
- const built = BUILDERS[entry.builder](kit, options);
6282
+ const built = BUILDERS2[entry.builder](kit, options);
6142
6283
  nameParts(built.parts);
6143
6284
  let root;
6144
6285
  if (entry.builder === "launcher") {
@@ -6147,7 +6288,7 @@ function buildWeaponModelById(THREE, id) {
6147
6288
  built.group.position.x = -new THREE.Box3().setFromObject(built.group).min.x;
6148
6289
  } else root = fitLength(kit, built.group, entry.length / 1e3);
6149
6290
  root.name = id;
6150
- markKitOrigin2(root, { owner: "game-weapons-three", kind: "weapon", id });
6291
+ markKitOrigin3(root, { owner: "game-weapons-three", kind: "weapon", id });
6151
6292
  const model = { id, root, inner: built.group, parts: built.parts };
6152
6293
  if (!built.parts.frames) built.parts.frames = deriveFrames(THREE, model);
6153
6294
  return model;
@@ -6380,9 +6521,17 @@ function revolverReload(kind) {
6380
6521
  if (kind !== "one_by_one") events.push(cue(0.7, "move_in", { from: "chambers" }), sound2(0.7, "speedloader"));
6381
6522
  return { tweens: [turn("crane", "x", 0, 0.22, 0, -1.25), turn("crane", "x", 1.05, 0.2, -1.25, 0)], events, end: 1.3 };
6382
6523
  }
6383
- var breakReload = () => ({
6524
+ var breakReload = (chambers) => ({
6384
6525
  tweens: [turn("barrels", "z", 0, 0.2, 0, -0.5), turn("barrels", "z", 1, 0.15, -0.5, 0)],
6385
- events: [sound2(0, "barrels_open"), cue(0.25, "eject_casing", { from: "breech", down: false }), sound2(0.25, "casings_eject"), cue(0.65, "move_in", { from: "breech" }), sound2(0.65, "shell_insert"), sound2(0.8, "shell_insert"), sound2(1.13, "barrels_close")],
6526
+ events: [
6527
+ sound2(0, "barrels_open"),
6528
+ cue(0.25, "eject_casing", { from: "breech", down: false }),
6529
+ sound2(0.25, "casings_eject"),
6530
+ cue(0.65, "move_in", { from: "breech" }),
6531
+ sound2(0.65, "shell_insert"),
6532
+ ...chambers > 1 ? [sound2(0.8, "shell_insert")] : [],
6533
+ sound2(1.13, "barrels_close")
6534
+ ],
6386
6535
  end: 1.2
6387
6536
  });
6388
6537
  var beltReload = (context) => ({
@@ -6455,7 +6604,7 @@ function draftFor(context, kind) {
6455
6604
  if (family === "rocket" || family === "flame") return heavyReload(context);
6456
6605
  if (parts.mag) return magazineReload(context, kind);
6457
6606
  if (family === "revolver" && parts.crane) return revolverReload(kind);
6458
- if (family === "break" && parts.barrels) return breakReload();
6607
+ if (family === "break" && parts.barrels) return breakReload(parts.breech?.length ?? 2);
6459
6608
  if (family === "belt" && parts.cover && parts.belt) return beltReload(context);
6460
6609
  if (isTube(context)) return tubeReload(context, kind);
6461
6610
  if (family === "bolt") return boltReload(context);
@@ -7155,7 +7304,7 @@ function applyCondition(kit, model, condition) {
7155
7304
  }
7156
7305
 
7157
7306
  // src/models/cartridge.ts
7158
- import { markKitOrigin as markKitOrigin3 } from "@adatechnology/game-contracts";
7307
+ import { markKitOrigin as markKitOrigin4 } from "@adatechnology/game-contracts";
7159
7308
  var BOTTLENECK_RATIO = 0.8;
7160
7309
  function cartridgeShape(caliber, dims) {
7161
7310
  if (caliber.caseType === "shell" || caliber.rim === "shell") return "shell";
@@ -7167,7 +7316,7 @@ function cartridgeShape(caliber, dims) {
7167
7316
  }
7168
7317
  function buildCartridge(kit, dims, shape) {
7169
7318
  const { M, cylinderY } = kit;
7170
- const group = markKitOrigin3(kit.group(), { owner: "game-weapons-three", kind: "ammunition", id: shape });
7319
+ const group = markKitOrigin4(kit.group(), { owner: "game-weapons-three", kind: "ammunition", id: shape });
7171
7320
  const radius = dims.caseDiameter / 2;
7172
7321
  const bulletRadius = dims.projectileDiameter / 2;
7173
7322
  const caseLength = dims.caseLength;
@@ -7199,7 +7348,7 @@ function buildCartridge(kit, dims, shape) {
7199
7348
  }
7200
7349
  function buildCasing(kit, dims, shape) {
7201
7350
  const { M, cylinderX } = kit;
7202
- const group = markKitOrigin3(kit.group(), { owner: "game-weapons-three", kind: "ammunition", id: shape });
7351
+ const group = markKitOrigin4(kit.group(), { owner: "game-weapons-three", kind: "ammunition", id: shape });
7203
7352
  const radius = dims.caseDiameter / 2e3;
7204
7353
  const length = dims.caseLength / 1e3;
7205
7354
  if (shape === "shell") {
@@ -7215,7 +7364,7 @@ function buildCasing(kit, dims, shape) {
7215
7364
  return { group, radius, length };
7216
7365
  }
7217
7366
  function buildRound(kit, dims, shape) {
7218
- const group = markKitOrigin3(kit.group(), { owner: "game-weapons-three", kind: "ammunition", id: shape });
7367
+ const group = markKitOrigin4(kit.group(), { owner: "game-weapons-three", kind: "ammunition", id: shape });
7219
7368
  const cartridge = buildCartridge(kit, dims, shape);
7220
7369
  cartridge.scale.setScalar(1e-3);
7221
7370
  cartridge.rotation.z = -Math.PI / 2;
@@ -7252,6 +7401,38 @@ function createEffects(THREE, sink, random) {
7252
7401
  };
7253
7402
  }
7254
7403
 
7404
+ // src/fx/view-hold.ts
7405
+ var VIEW_HOLD = {
7406
+ /** The right hand's grip, from the eye. */
7407
+ grip: [0.2, -0.2, -0.3],
7408
+ /** A handgun's grip: higher and nearer the middle, the arms out in front. */
7409
+ handgunGrip: [0.13, -0.15, -0.33],
7410
+ /** A rocket launcher's grip: its tube rides on the shoulder, so the hands are higher. */
7411
+ launcherGrip: [0.2, -0.14, -0.3],
7412
+ /** A gun with no grip anchor: the rear of its bore, from the eye. */
7413
+ rear: [0.2, -0.24, -0.12]
7414
+ };
7415
+ var scratch2 = { grip: null };
7416
+ function placeInView(placement) {
7417
+ const { THREE, hand, holder, camera, right } = placement;
7418
+ holder.scale.setScalar(1);
7419
+ hand.position.copy(camera.position);
7420
+ hand.quaternion.copy(camera.quaternion);
7421
+ hand.scale.setScalar(1);
7422
+ holder.position.set(0, 0, 0);
7423
+ hand.updateMatrixWorld(true);
7424
+ if (!right) {
7425
+ holder.position.set(VIEW_HOLD.rear[0], VIEW_HOLD.rear[1], VIEW_HOLD.rear[2] + placement.rear);
7426
+ hand.updateMatrixWorld(true);
7427
+ return;
7428
+ }
7429
+ scratch2.grip ?? (scratch2.grip = new THREE.Vector3());
7430
+ const grip = hand.worldToLocal(right.part.localToWorld(scratch2.grip.copy(right.offset)));
7431
+ const [x, y, z] = placement.kind === "handgun" ? VIEW_HOLD.handgunGrip : placement.kind === "launcher" ? VIEW_HOLD.launcherGrip : VIEW_HOLD.grip;
7432
+ holder.position.set(x - grip.x, y - grip.y, z - grip.z);
7433
+ hand.updateMatrixWorld(true);
7434
+ }
7435
+
7255
7436
  // src/fx/heavy.ts
7256
7437
  import { LIGHT_PRIORITY as LIGHT_PRIORITY2, geometryKey, materialKey } from "@adatechnology/game-render-three";
7257
7438
  import { discardObject3D } from "@adatechnology/game-shapes-three";
@@ -8244,6 +8425,8 @@ function createViewmodel(options) {
8244
8425
  let triggerRest = 0;
8245
8426
  let feedCued = false;
8246
8427
  const rootQuaternion = new THREE.Quaternion();
8428
+ const muzzleAt = new THREE.Vector3();
8429
+ const muzzleAlong = new THREE.Vector3();
8247
8430
  const world = {
8248
8431
  kit,
8249
8432
  scene,
@@ -8343,6 +8526,7 @@ function createViewmodel(options) {
8343
8526
  if (model.parts.stockMove && onTool?.stock === "collapsed") rig.setPartPose("stock", collapsedStockPose(model.parts.stockMove));
8344
8527
  applyCondition(kit, model, condition);
8345
8528
  lastAmmo = options.tool?.().ammo ?? 0;
8529
+ void audio?.loadSamples();
8346
8530
  hand.updateMatrixWorld(true);
8347
8531
  hold = holdOfModel(THREE, model, 1);
8348
8532
  hold = { ...hold, works: handWorkOf(THREE, model, weapon, hold.right) };
@@ -8395,12 +8579,22 @@ function createViewmodel(options) {
8395
8579
  if (!session) return;
8396
8580
  const family = session.model.parts.family;
8397
8581
  session.holderScale = 1;
8582
+ if (options.firstPerson?.()) {
8583
+ placeInView({ THREE, hand, holder, camera, right: hold?.right ?? null, rear: modelRear, kind: family === "pistol" || family === "revolver" ? "handgun" : family === "rocket" ? "launcher" : "long" });
8584
+ lift = 0;
8585
+ pullFromWall(dt);
8586
+ return;
8587
+ }
8588
+ hand.scale.set(1, 1, 1);
8398
8589
  placeOnBody({ THREE, hand, holder, frame: options.bodyHold?.() ?? eyeFrame(), rear: modelRear, lengthMetres: session.lengthMetres, handgun: family === "pistol" || family === "revolver", right: hold?.right ?? null, left: hold?.left ?? null });
8399
8590
  hand.updateMatrixWorld(true);
8400
8591
  lift += (liftToKeepInView() - lift) * Math.min(1, dt * HAND_IN_VIEW.rate);
8401
8592
  if (lift > 1e-4) hand.position.addScaledVector(up.set(0, 1, 0).applyQuaternion(camera.quaternion), lift);
8402
8593
  hand.updateMatrixWorld(true);
8403
- if (!options.contact) return;
8594
+ pullFromWall(dt);
8595
+ };
8596
+ const pullFromWall = (dt) => {
8597
+ if (!options.contact || !session) return;
8404
8598
  ahead.set(0, 0, -1).applyQuaternion(camera.quaternion);
8405
8599
  if (reach === 0) reach = Math.max(0.01, measureReach(session));
8406
8600
  const free = options.contact(camera.position, ahead, WALL_PULL.probe);
@@ -8431,7 +8625,7 @@ function createViewmodel(options) {
8431
8625
  }
8432
8626
  const action = current.def.action;
8433
8627
  if (action.kind === "launcher") {
8434
- launchRocket(heavy(current), options.flyRocket !== false);
8628
+ if (current.model.parts.family === "rocket") launchRocket(heavy(current), options.flyRocket !== false);
8435
8629
  audio?.play("explosion", LAUNCH_VOLUME, distance());
8436
8630
  }
8437
8631
  if (action.kind !== "firearm") return;
@@ -8508,6 +8702,15 @@ function createViewmodel(options) {
8508
8702
  condition = next;
8509
8703
  if (session) applyCondition(kit, session.model, condition);
8510
8704
  },
8705
+ muzzle() {
8706
+ const mark = session?.model.parts.muzzle;
8707
+ if (!session || !mark) return null;
8708
+ hand.updateMatrixWorld(true);
8709
+ const at = worldPositionOf(session, mark, muzzleAt);
8710
+ const along = muzzleAlong.set(1, 0, 0).applyQuaternion(session.model.root.getWorldQuaternion(rootQuaternion)).normalize();
8711
+ const kind = session.def.action.kind;
8712
+ return { position: [at.x, at.y, at.z], direction: [along.x, along.y, along.z], drawsFlash: kind === "firearm" || kind === "launcher" };
8713
+ },
8511
8714
  handTargets() {
8512
8715
  if (!session || !hold) return null;
8513
8716
  hand.updateMatrixWorld(true);
@@ -8562,6 +8765,7 @@ function createWeaponFx(options) {
8562
8765
  ...options.raycast ? { raycast: options.raycast } : {},
8563
8766
  ...options.contact ? { contact: options.contact } : {},
8564
8767
  ...options.bodyHold ? { bodyHold: options.bodyHold } : {},
8768
+ ...options.firstPerson ? { firstPerson: options.firstPerson } : {},
8565
8769
  ...options.effects ? { effects: options.effects } : {},
8566
8770
  ...options.flyRocket === void 0 ? {} : { flyRocket: options.flyRocket },
8567
8771
  ...options.particleLimit ? { particleLimit: options.particleLimit } : {},
@@ -8584,6 +8788,7 @@ function createWeaponFx(options) {
8584
8788
  },
8585
8789
  setCondition: (condition) => model.setCondition(condition),
8586
8790
  handTargets: () => model.handTargets() ?? box2.handTargets(),
8791
+ muzzle: () => model.inHand !== null ? model.muzzle() : box2.muzzle(),
8587
8792
  layers,
8588
8793
  get inHand() {
8589
8794
  return model.inHand ?? box2.inHand;
@@ -8605,17 +8810,17 @@ function createWeaponFx(options) {
8605
8810
  }
8606
8811
 
8607
8812
  // src/modeled-prop.ts
8608
- import { markKitOrigin as markKitOrigin4 } from "@adatechnology/game-contracts";
8813
+ import { markKitOrigin as markKitOrigin5 } from "@adatechnology/game-contracts";
8609
8814
  import { mergeByMaterial } from "@adatechnology/game-shapes-three";
8610
8815
  var MODEL_BODY = "body";
8611
8816
  function buildModeledProp(THREE, def, name, decorate, items, props = defaultWeaponProps()) {
8612
8817
  const model = buildWeaponModelById(THREE, def.id) ?? (() => {
8613
- const root = props.buildItemModel(THREE, def, items);
8818
+ const root = props.buildItemModel(THREE, def, items) ?? buildThrowableModel(THREE, def);
8614
8819
  return root ? { root } : void 0;
8615
8820
  })();
8616
8821
  if (!model) return void 0;
8617
8822
  decorate?.(model.root);
8618
- const group = markKitOrigin4(new THREE.Group(), { owner: "game-weapons-three", kind: "weapon", id: def.id });
8823
+ const group = markKitOrigin5(new THREE.Group(), { owner: "game-weapons-three", kind: "weapon", id: def.id });
8619
8824
  group.name = name;
8620
8825
  const box2 = new THREE.Box3().setFromObject(model.root);
8621
8826
  const center = box2.getCenter(new THREE.Vector3());
@@ -9753,7 +9958,7 @@ var targetProp = (id, name) => {
9753
9958
  };
9754
9959
  var weaponItem = (def, name, items, props = defaultWeaponProps()) => {
9755
9960
  const id = `${WEAPON_ITEM_PREFIX}${def.id}`;
9756
- if (!hasWeaponModel(def.id) && !props.itemModelOf(def.id, items)) return asItem(id, name, weaponProp(def, name), { weapon: def.id, movable: true });
9961
+ if (!hasWeaponModel(def.id) && !props.itemModelOf(def.id, items) && !hasThrowableModel(def)) return asItem(id, name, weaponProp(def, name), { weapon: def.id, movable: true });
9757
9962
  return {
9758
9963
  id,
9759
9964
  name,
@@ -9773,7 +9978,7 @@ var mountedM134Item = (def, name, items, props) => ({
9773
9978
  });
9774
9979
  var targetItem = (id, name) => {
9775
9980
  const prop = targetProp(id, name);
9776
- return { id, name, build: (THREE) => markKitOrigin5(buildProp(THREE, prop), { owner: "game-weapons-three", kind: "target", id }), spec: prop.spec, movable: false };
9981
+ return { id, name, build: (THREE) => markKitOrigin6(buildProp(THREE, prop), { owner: "game-weapons-three", kind: "target", id }), spec: prop.spec, movable: false };
9777
9982
  };
9778
9983
  var asItem = (id, name, prop, extra = {}) => ({
9779
9984
  id,
@@ -9813,7 +10018,8 @@ function createWeaponsTestModule(options = {}) {
9813
10018
  const seedOption = options.seed === void 0 ? {} : { seed: options.seed };
9814
10019
  let audio;
9815
10020
  const ownAudio = () => audio === void 0 ? audio = options.sound === false ? null : createWeaponAudio(void 0, { ...seedOption, ...options.samples ? { samples: options.samples } : {} }) : audio;
9816
- const weaponDefs = [...arsenal.weapons.values()].filter((def) => !BODY_ACTIONS.has(def.id));
10021
+ const generics = genericWeapons(arsenal);
10022
+ const weaponDefs = [...arsenal.weapons.values()].filter((def) => !BODY_ACTIONS.has(def.id) && !generics.has(def.id));
9817
10023
  const thumbnailBase = options.thumbnailBase ?? DEFAULT_THUMBNAIL_BASE;
9818
10024
  const withThumbnail = (item, key) => {
9819
10025
  const thumbnail = thumbnailBase === false ? void 0 : weaponThumbnailUrl(key, thumbnailBase);
@@ -9930,6 +10136,7 @@ function createWeaponsTestModule(options = {}) {
9930
10136
  // the weapon against a wall asks as often as the laser, on the clock of the simulation (so a fast-forwarded room measures as often per frame)
9931
10137
  contact: createLaserRaycast(lent.THREE, lent.scene, lent.camera, { now: () => simulatedMs }),
9932
10138
  ...lent.holdFrame ? { bodyHold: () => lent.holdFrame?.() ?? null } : {},
10139
+ firstPerson: () => lent.player().cameraMode === "first",
9933
10140
  ...lent.effects ? { effects: lent.effects } : {},
9934
10141
  ...lent.particleLimit ? { particleLimit: lent.particleLimit } : {},
9935
10142
  ...isLightBudget(lent.lights) ? { lights: lent.lights } : {},
@@ -9980,6 +10187,7 @@ function createWeaponsTestModule(options = {}) {
9980
10187
  });
9981
10188
  const offFrame = lent.onFrame((dt) => created.update(dt));
9982
10189
  const offHands = lent.setHandTargets?.(() => created.handTargets());
10190
+ const offMuzzle = lent.setMuzzleSource?.(() => created.muzzle());
9983
10191
  created.handle({ type: "tool", weapon: lent.tool().weapon }, lent.tool());
9984
10192
  return () => {
9985
10193
  accessories.dispose();
@@ -9988,6 +10196,7 @@ function createWeaponsTestModule(options = {}) {
9988
10196
  offFrame();
9989
10197
  offClock();
9990
10198
  offHands?.();
10199
+ offMuzzle?.();
9991
10200
  created.dispose();
9992
10201
  audio?.dispose();
9993
10202
  fx = null;
@@ -10003,7 +10212,7 @@ import { createArsenal as createArsenal2 } from "@adatechnology/game-weapons";
10003
10212
 
10004
10213
  // src/test-area/explosives-bench.ts
10005
10214
  import { discardObject3D as discardObject3D4 } from "@adatechnology/game-shapes-three";
10006
- import { markKitOrigin as markKitOrigin6 } from "@adatechnology/game-contracts";
10215
+ import { markKitOrigin as markKitOrigin7 } from "@adatechnology/game-contracts";
10007
10216
 
10008
10217
  // src/test-area/explosives-text.ts
10009
10218
  var EXPLOSIVES_TEXT = {
@@ -10266,7 +10475,7 @@ function createExplosivesBench(options) {
10266
10475
  return new (T()).LineLoop(geometry, new (T()).LineBasicMaterial({ color: colour }));
10267
10476
  };
10268
10477
  const drawRings = (vessel) => {
10269
- const group = markKitOrigin6(new (T()).Group(), { owner: "game-weapons-three", kind: "aid" });
10478
+ const group = markKitOrigin7(new (T()).Group(), { owner: "game-weapons-three", kind: "aid" });
10270
10479
  group.name = "blast_rings";
10271
10480
  const lethal = lethalOf(vessel);
10272
10481
  const injury = injuryOf(vessel);
@@ -10726,6 +10935,7 @@ var SHELTERED = 0.5;
10726
10935
  var SHELTERED_MOST = 1;
10727
10936
  var CHEST = 1.1;
10728
10937
  var MIDDLE = 0.3;
10938
+ var AIM_SEARCH = 1.5;
10729
10939
  var HEAVY_ITEMS = ["camp_table"];
10730
10940
  var PERSON_MASS = 70;
10731
10941
  var GROUND_FRICTION = 7;
@@ -10785,6 +10995,27 @@ function pushScenario(all, bench, mode) {
10785
10995
  }
10786
10996
  const partsBefore = new Map(placed.map((body) => [body.objectId, partsOf(body.objectId)]));
10787
10997
  expect(placed.length > 0, fillText2(text.push_found, { n: placed.length, kinds: [...new Set(placed.map((body) => body.kind))].join(", ") }), { placed: placed.map((body) => body.itemId) });
10998
+ const aimOf = new Map(
10999
+ placed.map((body) => {
11000
+ const from = origin.get(body.objectId) ?? body.from;
11001
+ if (body.kind === "zombie" || body.kind === "survivor") return [body.objectId, [from[0], from[1] + CHEST, from[2]]];
11002
+ const boxes = room.renderer.solidsNear?.(from[0], from[2], AIM_SEARCH).filter((box2) => box2.objectId === body.objectId) ?? [];
11003
+ if (boxes.length === 0) return [body.objectId, [from[0], from[1] + MIDDLE, from[2]]];
11004
+ const low = [0, 1, 2].map((axis) => Math.min(...boxes.map((box2) => box2.min[axis])));
11005
+ const high = [0, 1, 2].map((axis) => Math.max(...boxes.map((box2) => box2.max[axis])));
11006
+ return [body.objectId, [0, 1, 2].map((axis) => (low[axis] + high[axis]) / 2)];
11007
+ })
11008
+ );
11009
+ const coverAt = (point) => new Map(
11010
+ placed.map((body) => {
11011
+ const ignore = source ? [source, body.objectId] : [body.objectId];
11012
+ return [body.objectId, room.world.blastReach?.(point, aimOf.get(body.objectId) ?? body.from, { ignore }) ?? 1];
11013
+ })
11014
+ );
11015
+ let coverOf;
11016
+ const stopListening = room.world.on("*", (event) => {
11017
+ if ((event.type === "explode" || event.type === "blast") && !coverOf) coverOf = coverAt(event.point);
11018
+ });
10788
11019
  if (mode === "p13") bench.actions.shoot();
10789
11020
  else {
10790
11021
  room.setTool("frag_grenade");
@@ -10797,17 +11028,11 @@ function pushScenario(all, bench, mode) {
10797
11028
  step(0.05);
10798
11029
  waited += 0.05;
10799
11030
  }
11031
+ stopListening();
10800
11032
  const blast = bench.lastBlast();
10801
11033
  expect(!!blast, text.push_told, { waited });
10802
11034
  if (!blast) return;
10803
- const coverOf = new Map(
10804
- placed.map((body) => {
10805
- const from = origin.get(body.objectId) ?? body.from;
10806
- const person = body.kind === "zombie" || body.kind === "survivor";
10807
- const ignore = source ? [source, body.objectId] : [body.objectId];
10808
- return [body.objectId, room.world.blastReach?.(blast.point, [from[0], from[1] + (person ? CHEST : MIDDLE), from[2]], { ignore }) ?? 1];
10809
- })
10810
- );
11035
+ const cover = coverOf ?? coverAt(blast.point);
10811
11036
  step(PUSH_READ_AT);
10812
11037
  const rows = placed.map((body) => {
10813
11038
  const from = origin.get(body.objectId) ?? body.from;
@@ -10815,16 +11040,18 @@ function pushScenario(all, bench, mode) {
10815
11040
  const distance = flat(from, blast.point);
10816
11041
  const away = Math.max(1e-6, distance);
10817
11042
  const outward = ((now[0] - from[0]) * (from[0] - blast.point[0]) + (now[2] - from[2]) * (from[2] - blast.point[2])) / away;
10818
- return { ...body, distance, outward, moved: flat(now, from), cover: coverOf.get(body.objectId) ?? 1 };
11043
+ return { ...body, distance, outward, moved: flat(now, from), cover: cover.get(body.objectId) ?? 1 };
10819
11044
  });
10820
11045
  const torn = (row) => (PUSH_KINDS.some((entry) => entry.kind === row.kind) || HEAVY_ITEMS.includes(row.kind)) && partsOf(row.objectId) < (partsBefore.get(row.objectId) ?? 0);
10821
11046
  const inReach = rows.filter((row) => row.distance < blast.radius * 0.85 && !torn(row));
11047
+ const isPerson = (row) => row.kind === "survivor" || row.kind === "zombie";
11048
+ const reached = inReach.filter((row) => row.cover >= SHELTERED && !isPerson(row));
10822
11049
  const sheltered = inReach.filter((row) => row.cover < SHELTERED);
10823
11050
  for (const row of sheltered) {
10824
- expect(row.outward < SHELTERED_MOST, fillText2(text.push_sheltered, { what: things[row.kind] ?? row.kind, distance: row.distance.toFixed(1), cover: (row.cover * 100).toFixed(0), moved: Math.max(0, row.outward).toFixed(2) }), { row: { kind: row.kind, distance: row.distance, outward: row.outward, cover: row.cover } });
11051
+ const open = reached.filter((other) => other.kind === row.kind && other.distance <= row.distance).map((other) => other.outward);
11052
+ const most = Math.max(SHELTERED_MOST, row.cover * Math.max(0, ...open));
11053
+ expect(row.outward < most, fillText2(text.push_sheltered, { what: things[row.kind] ?? row.kind, distance: row.distance.toFixed(1), cover: (row.cover * 100).toFixed(0), moved: Math.max(0, row.outward).toFixed(2) }), { row: { kind: row.kind, distance: row.distance, outward: row.outward, cover: row.cover, most } });
10825
11054
  }
10826
- const isPerson = (row) => row.kind === "survivor" || row.kind === "zombie";
10827
- const reached = inReach.filter((row) => row.cover >= SHELTERED && !isPerson(row));
10828
11055
  const movedAway = reached.filter((row) => row.outward >= MOVED);
10829
11056
  const least = reached.length === 0 ? 0 : Math.min(...reached.map((row) => row.outward));
10830
11057
  expect(reached.length > 0 && movedAway.length === reached.length, fillText2(text.push_outward, { moved: movedAway.length, n: reached.length, least: least.toFixed(2) }), { rows: reached.map((row) => ({ kind: row.kind, distance: row.distance, outward: row.outward })), torn: rows.filter(torn).map((row) => row.objectId) });
@@ -11014,14 +11241,19 @@ export {
11014
11241
  BODY_ACTIONS,
11015
11242
  weaponProp,
11016
11243
  buildProp,
11244
+ modelKit,
11245
+ EXPLOSIVE_MODEL_IDS,
11246
+ hasExplosiveModel,
11247
+ buildExplosiveModel,
11017
11248
  SHIPPED_WEAPON_ITEM_MODELS,
11249
+ hasThrowableModel,
11250
+ buildThrowableModel,
11018
11251
  weaponPropsOver,
11019
11252
  defaultWeaponProps,
11020
11253
  registerWeaponItemModel,
11021
11254
  itemModelOfWeapon,
11022
11255
  buildItemWeaponModel,
11023
11256
  buildWeaponModel,
11024
- modelKit,
11025
11257
  deriveFrames,
11026
11258
  mountFrame,
11027
11259
  buildFlyingRocket,
@@ -11067,4 +11299,4 @@ export {
11067
11299
  DEFAULT_EXPLOSIVES_SCENE,
11068
11300
  createExplosivesTestModule
11069
11301
  };
11070
- //# sourceMappingURL=chunk-XMVSA7UT.js.map
11302
+ //# sourceMappingURL=chunk-GBO6L7CQ.js.map