@adatechnology/game-items-three 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.d.ts +48 -3
- package/dist/index.global.js +556 -64
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +215 -16
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.global.js
CHANGED
|
@@ -29,6 +29,7 @@ var GameItemsThree = (() => {
|
|
|
29
29
|
ITEM_FAMILIES: () => ITEM_FAMILIES,
|
|
30
30
|
ITEM_PREFIX: () => ITEM_PREFIX,
|
|
31
31
|
MODEL_BUDGET_DEVIATIONS: () => MODEL_BUDGET_DEVIATIONS,
|
|
32
|
+
OPENING_NAME: () => OPENING_NAME,
|
|
32
33
|
ROW_LIMIT: () => ROW_LIMIT,
|
|
33
34
|
RULER_HEIGHT: () => RULER_HEIGHT,
|
|
34
35
|
RULER_ID: () => RULER_ID,
|
|
@@ -42,6 +43,7 @@ var GameItemsThree = (() => {
|
|
|
42
43
|
WEAR_AMOUNT: () => WEAR_AMOUNT,
|
|
43
44
|
WEAR_TAGS: () => WEAR_TAGS,
|
|
44
45
|
WEAR_TARGET: () => WEAR_TARGET,
|
|
46
|
+
asLid: () => asLid,
|
|
45
47
|
buildItem: () => buildItem,
|
|
46
48
|
buildItemLod: () => buildItemLod,
|
|
47
49
|
canonicalFrame: () => canonicalFrame,
|
|
@@ -60,14 +62,18 @@ var GameItemsThree = (() => {
|
|
|
60
62
|
itemFamily: () => itemFamily,
|
|
61
63
|
itemModelBuilder: () => itemModelBuilder,
|
|
62
64
|
itemModelEntry: () => itemModelEntry,
|
|
65
|
+
itemOpening: () => itemOpening,
|
|
63
66
|
itemThumbnail: () => itemThumbnail,
|
|
64
67
|
itemThumbnailSvg: () => itemThumbnailSvg,
|
|
65
68
|
listItemModels: () => listItemModels,
|
|
66
69
|
mergeItem: () => mergeItem,
|
|
70
|
+
openItem: () => openItem,
|
|
67
71
|
registerItemModel: () => registerItemModel,
|
|
68
72
|
renderItemThumbnail: () => renderItemThumbnail,
|
|
73
|
+
setOpeningContents: () => setOpeningContents,
|
|
69
74
|
setWear: () => setWear,
|
|
70
75
|
shade: () => shade,
|
|
76
|
+
shapeDestruction: () => shapeDestruction,
|
|
71
77
|
shapeOf: () => shapeOf,
|
|
72
78
|
shapeThumbnailSvg: () => shapeThumbnailSvg,
|
|
73
79
|
shelvesLayout: () => shelvesLayout,
|
|
@@ -75,6 +81,7 @@ var GameItemsThree = (() => {
|
|
|
75
81
|
tagForDestruction: () => tagForDestruction,
|
|
76
82
|
thumbnailPatch: () => thumbnailPatch,
|
|
77
83
|
triangleBudgetOf: () => triangleBudgetOf,
|
|
84
|
+
turnItem: () => turnItem,
|
|
78
85
|
unregisterItemModel: () => unregisterItemModel,
|
|
79
86
|
wearableMaterials: () => wearableMaterials
|
|
80
87
|
});
|
|
@@ -2958,6 +2965,12 @@ var GameItemsThree = (() => {
|
|
|
2958
2965
|
liquid: "honey",
|
|
2959
2966
|
fill: 0.9
|
|
2960
2967
|
}
|
|
2968
|
+
},
|
|
2969
|
+
shapes: {
|
|
2970
|
+
box: {
|
|
2971
|
+
hollow: true,
|
|
2972
|
+
welded: true
|
|
2973
|
+
}
|
|
2961
2974
|
}
|
|
2962
2975
|
};
|
|
2963
2976
|
|
|
@@ -2973,6 +2986,8 @@ var GameItemsThree = (() => {
|
|
|
2973
2986
|
};
|
|
2974
2987
|
var FAMILIES = destruction_default.families;
|
|
2975
2988
|
var CONTENTS = destruction_default.contents;
|
|
2989
|
+
var SHAPES2 = destruction_default.shapes;
|
|
2990
|
+
var shapeDestruction = (shapeId) => SHAPES2[shapeId];
|
|
2976
2991
|
var FAMILY_OF_MODEL = new Map(Object.entries(FAMILIES).flatMap(([family, entry2]) => entry2.models.map((model) => [model, family])));
|
|
2977
2992
|
var itemFamily = (modelId) => FAMILY_OF_MODEL.get(modelId);
|
|
2978
2993
|
var itemDestruction = (modelId) => {
|
|
@@ -3088,9 +3103,9 @@ var GameItemsThree = (() => {
|
|
|
3088
3103
|
root.userData.tessellationTriangles = added;
|
|
3089
3104
|
}
|
|
3090
3105
|
function tagForDestruction(root, options = {}) {
|
|
3091
|
-
const { modelId, weight, size, three } = options;
|
|
3106
|
+
const { modelId, shapeId, weight, size, powder, three } = options;
|
|
3092
3107
|
if (weight !== void 0 && weight > 0) root.userData.destructionMass = weight;
|
|
3093
|
-
const rule = modelId
|
|
3108
|
+
const rule = modelId !== void 0 ? itemDestruction(modelId) : shapeId !== void 0 ? shapeDestruction(shapeId) : void 0;
|
|
3094
3109
|
if (rule?.tessellate) tessellateShapes(root, rule.tessellate, three);
|
|
3095
3110
|
const solid = [];
|
|
3096
3111
|
root.traverse((object2) => {
|
|
@@ -3105,6 +3120,7 @@ var GameItemsThree = (() => {
|
|
|
3105
3120
|
return;
|
|
3106
3121
|
}
|
|
3107
3122
|
if (rule?.hollow) mesh2.userData.hollow = true;
|
|
3123
|
+
if (powder) mesh2.userData.destructionPowder = { color: powder.color };
|
|
3108
3124
|
solid.push(mesh2);
|
|
3109
3125
|
});
|
|
3110
3126
|
if (!rule?.welded || solid.length < 2) return;
|
|
@@ -5587,8 +5603,168 @@ var GameItemsThree = (() => {
|
|
|
5587
5603
|
return G(mesh2(cyl(0.32, 0.32, 0.52), m), mesh2(cyl(0.326, 0.326, 0.34, 14), food(10172964)), mesh2(cyl(0.33, 0.33, 0.06), paper2(14861162), [0, 0.02, 0]), mesh2(torus2(0.31, 0.025), m, [0, 0.26, 0], [Math.PI / 2, 0, 0]), mesh2(torus2(0.31, 0.025), m, [0, -0.26, 0], [Math.PI / 2, 0, 0]));
|
|
5588
5604
|
};
|
|
5589
5605
|
|
|
5606
|
+
// src/data/opening.json
|
|
5607
|
+
var opening_default = {
|
|
5608
|
+
families: {
|
|
5609
|
+
drink_can: { style: "pull_tab" },
|
|
5610
|
+
food_tin: { style: "cut_lid" }
|
|
5611
|
+
},
|
|
5612
|
+
models: {
|
|
5613
|
+
empty_can: { style: "none" },
|
|
5614
|
+
canned_beans: { food: "#7a2e1c" },
|
|
5615
|
+
canned_corn_and_peas: { food: "#c7b03c" },
|
|
5616
|
+
canned_sausages: { food: "#c98d5c" },
|
|
5617
|
+
canned_sardines: { style: "peel_lid", food: "#8e979c" },
|
|
5618
|
+
condensed_milk: { food: "#e3cf9c" },
|
|
5619
|
+
energy_drink: { style: "pull_tab" },
|
|
5620
|
+
honey_jar: { style: "screw_cap", food: "#d99a1c" },
|
|
5621
|
+
home_preserves: { style: "screw_cap", food: "#8e1f3a" },
|
|
5622
|
+
cooking_oil: { style: "screw_cap", food: "#e3b830" },
|
|
5623
|
+
water_bottle: { style: "screw_cap" },
|
|
5624
|
+
water_jug_5l: { style: "screw_cap" },
|
|
5625
|
+
dirty_water: { style: "screw_cap" },
|
|
5626
|
+
sports_drink: { style: "screw_cap" },
|
|
5627
|
+
coconut_water: { style: "screw_cap" },
|
|
5628
|
+
cachaca: { style: "screw_cap" },
|
|
5629
|
+
communion_wine: { style: "screw_cap" }
|
|
5630
|
+
}
|
|
5631
|
+
};
|
|
5632
|
+
|
|
5633
|
+
// src/opening.ts
|
|
5634
|
+
var FAMILIES2 = opening_default.families;
|
|
5635
|
+
var MODELS = opening_default.models;
|
|
5636
|
+
var itemOpening = (modelId) => {
|
|
5637
|
+
const family = itemFamily(modelId);
|
|
5638
|
+
const row = { ...family === void 0 ? {} : FAMILIES2[family], ...MODELS[modelId] };
|
|
5639
|
+
if (row.style === void 0 || row.style === "none") return void 0;
|
|
5640
|
+
return { style: row.style, ...row.food === void 0 ? {} : { food: row.food } };
|
|
5641
|
+
};
|
|
5642
|
+
var OPENING_NAME = "opening";
|
|
5643
|
+
var INSIDE = 1315086;
|
|
5644
|
+
var CUT_EDGE = 14278114;
|
|
5645
|
+
var LID = 12172996;
|
|
5646
|
+
var isLid = (o) => o.userData.itemLid === true;
|
|
5647
|
+
var asLid = (mesh2) => {
|
|
5648
|
+
mesh2.userData.itemLid = true;
|
|
5649
|
+
return mesh2;
|
|
5650
|
+
};
|
|
5651
|
+
var localBox = (T2, root, only) => {
|
|
5652
|
+
root.updateMatrixWorld(true);
|
|
5653
|
+
const toRoot = new T2.Matrix4().copy(root.matrixWorld).invert();
|
|
5654
|
+
const box4 = new T2.Box3();
|
|
5655
|
+
const part = new T2.Box3();
|
|
5656
|
+
const m = new T2.Matrix4();
|
|
5657
|
+
root.traverse((o) => {
|
|
5658
|
+
const mesh2 = o;
|
|
5659
|
+
if (!mesh2.isMesh || mesh2.userData.destructionDebris || mesh2.name === OPENING_NAME || mesh2.parent?.name === OPENING_NAME) return;
|
|
5660
|
+
if (only && !only(mesh2)) return;
|
|
5661
|
+
if (!mesh2.geometry.boundingBox) mesh2.geometry.computeBoundingBox();
|
|
5662
|
+
part.copy(mesh2.geometry.boundingBox).applyMatrix4(m.multiplyMatrices(toRoot, mesh2.matrixWorld));
|
|
5663
|
+
box4.union(part);
|
|
5664
|
+
});
|
|
5665
|
+
return box4;
|
|
5666
|
+
};
|
|
5667
|
+
function openItem(T2, root, modelId) {
|
|
5668
|
+
const look = itemOpening(modelId);
|
|
5669
|
+
if (!look) return false;
|
|
5670
|
+
if (root.userData.itemOpened) return true;
|
|
5671
|
+
const box4 = look.style === "screw_cap" ? localBox(T2, root, isLid) : localBox(T2, root);
|
|
5672
|
+
if (box4.isEmpty()) return false;
|
|
5673
|
+
const size = box4.getSize(new T2.Vector3());
|
|
5674
|
+
const centre = box4.getCenter(new T2.Vector3());
|
|
5675
|
+
const top = look.style === "screw_cap" ? box4.min.y : box4.max.y;
|
|
5676
|
+
const lift = size.y * 3e-3;
|
|
5677
|
+
const sheet = Math.max(size.y * 6e-3, 1e-4);
|
|
5678
|
+
const group = new T2.Group();
|
|
5679
|
+
group.name = OPENING_NAME;
|
|
5680
|
+
group.position.set(centre.x, top, centre.z);
|
|
5681
|
+
const material = (color2, metal = 0) => new T2.MeshStandardMaterial({ color: color2, metalness: metal, roughness: metal ? 0.45 : 0.85, polygonOffset: true, polygonOffsetFactor: -2, polygonOffsetUnits: -2 });
|
|
5682
|
+
const add = (geometry, mat, y, parent = group) => {
|
|
5683
|
+
const mesh2 = new T2.Mesh(geometry, mat);
|
|
5684
|
+
mesh2.position.y = y;
|
|
5685
|
+
mesh2.renderOrder = 2;
|
|
5686
|
+
mesh2.userData.destructionDebris = true;
|
|
5687
|
+
mesh2.userData.itemOpening = true;
|
|
5688
|
+
parent.add(mesh2);
|
|
5689
|
+
return mesh2;
|
|
5690
|
+
};
|
|
5691
|
+
const flat = (geometry) => geometry.rotateX(-Math.PI / 2);
|
|
5692
|
+
const food = look.food ?? INSIDE;
|
|
5693
|
+
if (look.style === "screw_cap") {
|
|
5694
|
+
const r = Math.min(size.x, size.z) / 2;
|
|
5695
|
+
root.traverse((o) => isLid(o) && (o.visible = false));
|
|
5696
|
+
add(flat(new T2.CircleGeometry(r * 0.85, 24)), material(INSIDE), lift);
|
|
5697
|
+
if (look.food !== void 0) add(flat(new T2.CircleGeometry(r * 0.7, 24)), material(look.food), lift * 2).userData.itemFood = true;
|
|
5698
|
+
} else if (look.style === "pull_tab") {
|
|
5699
|
+
const r = Math.min(size.x, size.z) / 2;
|
|
5700
|
+
const mouth = add(flat(new T2.CircleGeometry(r * 0.2, 24)), material(INSIDE), lift);
|
|
5701
|
+
mouth.scale.set(1.15, 1, 0.85);
|
|
5702
|
+
mouth.position.z = r * 0.52;
|
|
5703
|
+
} else if (look.style === "cut_lid") {
|
|
5704
|
+
const r = Math.min(size.x, size.z) / 2;
|
|
5705
|
+
add(flat(new T2.CircleGeometry(r * 0.88, 40)), material(INSIDE), lift);
|
|
5706
|
+
add(flat(new T2.CircleGeometry(r * 0.76, 40)), material(food), lift * 2).userData.itemFood = true;
|
|
5707
|
+
add(flat(new T2.RingGeometry(r * 0.86, r * 0.9, 40)), material(CUT_EDGE, 0.9), lift * 3);
|
|
5708
|
+
const hinge = new T2.Group();
|
|
5709
|
+
hinge.position.set(0, lift, -r * 0.86);
|
|
5710
|
+
hinge.rotation.x = -1.95;
|
|
5711
|
+
group.add(hinge);
|
|
5712
|
+
const lid = add(new T2.CylinderGeometry(r * 0.86, r * 0.86, sheet, 40), material(LID, 0.4), 0, hinge);
|
|
5713
|
+
lid.position.z = r * 0.86;
|
|
5714
|
+
} else {
|
|
5715
|
+
const long = size.x >= size.z ? "x" : "z";
|
|
5716
|
+
const length = Math.max(size.x, size.z);
|
|
5717
|
+
const width = Math.min(size.x, size.z);
|
|
5718
|
+
const plane2 = (a, b) => flat(long === "x" ? new T2.PlaneGeometry(a, b) : new T2.PlaneGeometry(b, a));
|
|
5719
|
+
add(plane2(length * 0.92, width * 0.86), material(INSIDE), lift);
|
|
5720
|
+
add(plane2(length * 0.84, width * 0.76), material(food), lift * 2).userData.itemFood = true;
|
|
5721
|
+
const hinge = new T2.Group();
|
|
5722
|
+
const end = length * 0.46;
|
|
5723
|
+
if (long === "x") {
|
|
5724
|
+
hinge.position.set(end, lift, 0);
|
|
5725
|
+
hinge.rotation.z = -1.7;
|
|
5726
|
+
} else {
|
|
5727
|
+
hinge.position.set(0, lift, end);
|
|
5728
|
+
hinge.rotation.x = 1.7;
|
|
5729
|
+
}
|
|
5730
|
+
group.add(hinge);
|
|
5731
|
+
const lid = add(long === "x" ? new T2.BoxGeometry(length * 0.9, sheet, width * 0.84) : new T2.BoxGeometry(width * 0.84, sheet, length * 0.9), material(LID, 0.4), 0, hinge);
|
|
5732
|
+
if (long === "x") lid.position.x = -length * 0.45;
|
|
5733
|
+
else lid.position.z = -length * 0.45;
|
|
5734
|
+
}
|
|
5735
|
+
root.add(group);
|
|
5736
|
+
root.userData.itemOpened = true;
|
|
5737
|
+
return true;
|
|
5738
|
+
}
|
|
5739
|
+
var CAP_OFF = 1.6;
|
|
5740
|
+
function turnItem(root, modelId, state) {
|
|
5741
|
+
if (itemOpening(modelId)?.style !== "screw_cap") return false;
|
|
5742
|
+
root.traverse((o) => {
|
|
5743
|
+
var _a;
|
|
5744
|
+
if (!isLid(o)) return;
|
|
5745
|
+
const mesh2 = o;
|
|
5746
|
+
const base = (_a = mesh2.userData).lidBase ?? (_a.lidBase = { y: mesh2.position.y, turn: mesh2.rotation.y });
|
|
5747
|
+
if (!mesh2.geometry.boundingBox) mesh2.geometry.computeBoundingBox();
|
|
5748
|
+
const height = (mesh2.geometry.boundingBox.max.y - mesh2.geometry.boundingBox.min.y) * mesh2.scale.y;
|
|
5749
|
+
mesh2.rotation.y = base.turn + state.turned;
|
|
5750
|
+
mesh2.position.y = base.y + Math.min(1, Math.max(0, state.lift)) * height * CAP_OFF;
|
|
5751
|
+
});
|
|
5752
|
+
return true;
|
|
5753
|
+
}
|
|
5754
|
+
function setOpeningContents(root, share) {
|
|
5755
|
+
const opening = root.getObjectByName(OPENING_NAME);
|
|
5756
|
+
if (!opening) return;
|
|
5757
|
+
const left = Math.max(0, Math.min(1, share));
|
|
5758
|
+
opening.traverse((o) => {
|
|
5759
|
+
if (!o.userData.itemFood) return;
|
|
5760
|
+
o.visible = left > 0.02;
|
|
5761
|
+
const k = 0.35 + 0.65 * left;
|
|
5762
|
+
o.scale.set(k, 1, k);
|
|
5763
|
+
});
|
|
5764
|
+
}
|
|
5765
|
+
|
|
5590
5766
|
// src/models/water-bottle.ts
|
|
5591
|
-
var waterBottle = ({ mesh: mesh2, cyl, lathe: lathe2, G, plastic, paper: paper2, glass }) => G(mesh2(lathe2([[0, -0.6], [0.2, -0.6], [0.22, -0.5], [0.22, 0.2], [0.19, 0.32], [0.09, 0.45], [0.08, 0.55], [0, 0.55]]), glass(9421788, 0.55)), mesh2(cyl(0.085, 0.085, 0.1, 12), plastic(2907530), [0, 0.6, 0]), mesh2(cyl(0.225, 0.225, 0.22, 16), paper2(15330543), [0, -0.15, 0]), mesh2(cyl(0.226, 0.226, 0.05, 16), plastic(3831976), [0, -0.12, 0]));
|
|
5767
|
+
var waterBottle = ({ mesh: mesh2, cyl, lathe: lathe2, G, plastic, paper: paper2, glass }) => G(mesh2(lathe2([[0, -0.6], [0.2, -0.6], [0.22, -0.5], [0.22, 0.2], [0.19, 0.32], [0.09, 0.45], [0.08, 0.55], [0, 0.55]]), glass(9421788, 0.55)), asLid(mesh2(cyl(0.085, 0.085, 0.1, 12), plastic(2907530), [0, 0.6, 0])), mesh2(cyl(0.225, 0.225, 0.22, 16), paper2(15330543), [0, -0.15, 0]), mesh2(cyl(0.226, 0.226, 0.05, 16), plastic(3831976), [0, -0.12, 0]));
|
|
5592
5768
|
|
|
5593
5769
|
// src/models/rice.ts
|
|
5594
5770
|
var rice = ({ mesh: mesh2, bx, G, paper: paper2, food }) => G(mesh2(bx(0.7, 0.95, 0.28), paper2(15129792)), mesh2(bx(0.72, 0.12, 0.1), paper2(14010792), [0, 0.52, 0]), mesh2(bx(0.5, 0.3, 0.02), food(11883039), [0, 0.05, 0.15]), mesh2(bx(0.3, 0.1, 0.02), paper2(4160069), [0, -0.25, 0.15]));
|
|
@@ -5597,7 +5773,7 @@ var GameItemsThree = (() => {
|
|
|
5597
5773
|
var militaryRation = ({ mesh: mesh2, bx, G, plastic, paper: paper2 }) => G(mesh2(bx(0.75, 1, 0.22), plastic(5923142)), mesh2(bx(0.77, 0.1, 0.23), plastic(4672823), [0, 0.48, 0]), mesh2(bx(0.5, 0.14, 0.01), paper2(2237727), [0, 0.18, 0.115]), mesh2(bx(0.36, 0.06, 0.01), paper2(2237727), [0, -0.02, 0.115]));
|
|
5598
5774
|
|
|
5599
5775
|
// src/models/cachaca.ts
|
|
5600
|
-
var cachaca = ({ mesh: mesh2, cyl, lathe: lathe2, G, wood, paper: paper2, food, glass }) => G(mesh2(lathe2([[0, -0.6], [0.25, -0.6], [0.27, -0.52], [0.27, 0.1], [0.2, 0.25], [0.09, 0.35], [0.09, 0.58], [0, 0.58]]), glass(12749354, 0.78)), mesh2(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0]), mesh2(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh2(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
|
|
5776
|
+
var cachaca = ({ mesh: mesh2, cyl, lathe: lathe2, G, wood, paper: paper2, food, glass }) => G(mesh2(lathe2([[0, -0.6], [0.25, -0.6], [0.27, -0.52], [0.27, 0.1], [0.2, 0.25], [0.09, 0.35], [0.09, 0.58], [0, 0.58]]), glass(12749354, 0.78)), asLid(mesh2(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0])), mesh2(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh2(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
|
|
5601
5777
|
|
|
5602
5778
|
// src/models/first-aid-kit.ts
|
|
5603
5779
|
var firstAidKit = ({ mesh: mesh2, bx, torus: torus2, G, plastic }) => {
|
|
@@ -5813,7 +5989,7 @@ var GameItemsThree = (() => {
|
|
|
5813
5989
|
mesh2(cyl(0.41, 0.41, 0.86, 12), food(9314106), [0, -0.2, 0]),
|
|
5814
5990
|
mesh2(cyl(0.34, 0.445, 0.12, 12), glass(13625070, 0.5), [0, 0.39, 0]),
|
|
5815
5991
|
mesh2(cyl(0.34, 0.34, 0.05, 12), glass(13625070, 0.5), [0, 0.475, 0]),
|
|
5816
|
-
mesh2(cyl(0.38, 0.38, 0.15, 12), metal(12163644), [0, 0.575, 0]),
|
|
5992
|
+
asLid(mesh2(cyl(0.38, 0.38, 0.15, 12), metal(12163644), [0, 0.575, 0])),
|
|
5817
5993
|
mesh2(cyl(0.45, 0.45, 0.4, 12), paper2(15327172), [0, -0.2, 0]),
|
|
5818
5994
|
mesh2(cyl(0.452, 0.452, 0.08, 12), cloth2(10172986), [0, -0.2, 0])
|
|
5819
5995
|
);
|
|
@@ -6018,7 +6194,7 @@ var GameItemsThree = (() => {
|
|
|
6018
6194
|
mesh2(cyl(0.395, 0.395, 0.68, 12), glass(14925936, 0.55), [0, -0.16, 0]),
|
|
6019
6195
|
mesh2(cyl(0.37, 0.37, 0.6, 12), food(14260764), [0, -0.2, 0]),
|
|
6020
6196
|
mesh2(cyl(0.3, 0.395, 0.14, 12), glass(14925936, 0.55), [0, 0.25, 0]),
|
|
6021
|
-
mesh2(cyl(0.3, 0.3, 0.18, 12), metal(7031324), [0, 0.41, 0]),
|
|
6197
|
+
asLid(mesh2(cyl(0.3, 0.3, 0.18, 12), metal(7031324), [0, 0.41, 0])),
|
|
6022
6198
|
mesh2(cyl(0.4, 0.4, 0.34, 12), paper2(15853512), [0, -0.2, 0]),
|
|
6023
6199
|
mesh2(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.1, 0]),
|
|
6024
6200
|
mesh2(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.3, 0])
|
|
@@ -6122,7 +6298,7 @@ var GameItemsThree = (() => {
|
|
|
6122
6298
|
mesh2(cyl(0.345, 0.345, 1.7, 12), glass(14923824, 0.65), [0, -0.45, 0]),
|
|
6123
6299
|
mesh2(cyl(0.12, 0.345, 0.5, 12), glass(14923824, 0.65), [0, 0.65, 0]),
|
|
6124
6300
|
mesh2(cyl(0.12, 0.12, 0.25, 12), glass(14923824, 0.65), [0, 1.025, 0]),
|
|
6125
|
-
mesh2(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0]),
|
|
6301
|
+
asLid(mesh2(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0])),
|
|
6126
6302
|
mesh2(cyl(0.35, 0.35, 0.8, 12), paper2(15853512), [0, -0.5, 0]),
|
|
6127
6303
|
mesh2(cyl(0.352, 0.352, 0.2, 12), paper2(4160042), [0, -0.5, 0])
|
|
6128
6304
|
);
|
|
@@ -6160,7 +6336,7 @@ var GameItemsThree = (() => {
|
|
|
6160
6336
|
const body = mesh2(bx(1.5, 2.3, 1.5), glass(10473710, 0.75), [0, -0.5, 0]);
|
|
6161
6337
|
const shoulder = mesh2(bx(1.2, 0.45, 1.2), glass(10473710, 0.75), [0, 0.875, 0]);
|
|
6162
6338
|
const neck = mesh2(cyl(0.34, 0.34, 0.3, 12), dark, [0, 1.25, 0]);
|
|
6163
|
-
const cap = mesh2(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]);
|
|
6339
|
+
const cap = asLid(mesh2(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]));
|
|
6164
6340
|
const grip = mesh2(bx(0.16, 0.9, 0.3), dark, [1.07, 0.6, 0]);
|
|
6165
6341
|
const upperBar = mesh2(bx(0.45, 0.16, 0.3), dark, [0.925, 0.97, 0]);
|
|
6166
6342
|
const lowerBar = mesh2(bx(0.45, 0.16, 0.3), dark, [0.925, 0.23, 0]);
|
|
@@ -6175,7 +6351,7 @@ var GameItemsThree = (() => {
|
|
|
6175
6351
|
const label = mesh2(cyl(0.332, 0.332, 0.7, 12), paper2(1448738), [0, -0.12, 0]);
|
|
6176
6352
|
const stripe = mesh2(cyl(0.332, 0.332, 0.1, 12), paper2(15753232), [0, 0.28, 0]);
|
|
6177
6353
|
const collar = mesh2(cyl(0.2, 0.2, 0.04, 12), plastic(15263976), [0, 0.705, 0]);
|
|
6178
|
-
const cap = mesh2(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]);
|
|
6354
|
+
const cap = asLid(mesh2(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]));
|
|
6179
6355
|
return G(bottle2, label, stripe, collar, cap);
|
|
6180
6356
|
};
|
|
6181
6357
|
|
|
@@ -6222,7 +6398,7 @@ var GameItemsThree = (() => {
|
|
|
6222
6398
|
const goldBand = mesh2(cyl(0.382, 0.382, 0.05, 12), metal(11896880), [0, 0.075, 0]);
|
|
6223
6399
|
const crossUpright = mesh2(bx(0.06, 0.26, 0.02), paper2(8003371), [0, -0.35, 0.375]);
|
|
6224
6400
|
const crossArm = mesh2(bx(0.18, 0.06, 0.02), paper2(8003371), [0, -0.3, 0.375]);
|
|
6225
|
-
const foil = mesh2(cyl(0.15, 0.15, 0.46, 12), metal(11896880), [0, 1.27, 0]);
|
|
6401
|
+
const foil = asLid(mesh2(cyl(0.15, 0.15, 0.46, 12), metal(11896880), [0, 1.27, 0]));
|
|
6226
6402
|
return G(bottle2, label, goldBand, crossUpright, crossArm, foil);
|
|
6227
6403
|
};
|
|
6228
6404
|
|
|
@@ -6233,7 +6409,7 @@ var GameItemsThree = (() => {
|
|
|
6233
6409
|
const nut = mesh2(cyl(0.11, 0.11, 0.03, 10), paper2(7031339), [0, -0.12, 0.285], [Math.PI / 2, 0, 0]);
|
|
6234
6410
|
const leafLeft = mesh2(bx(0.2, 0.05, 0.01), paper2(15135448), [-0.12, 0.08, 0.292], [0, 0, 0.5]);
|
|
6235
6411
|
const leafRight = mesh2(bx(0.2, 0.05, 0.01), paper2(15135448), [0.12, 0.08, 0.292], [0, 0, -0.5]);
|
|
6236
|
-
const cap = mesh2(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]);
|
|
6412
|
+
const cap = asLid(mesh2(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]));
|
|
6237
6413
|
return G(bottle2, label, nut, leafLeft, leafRight, cap);
|
|
6238
6414
|
};
|
|
6239
6415
|
|
|
@@ -6243,7 +6419,7 @@ var GameItemsThree = (() => {
|
|
|
6243
6419
|
const murk = mesh2(lathe2([[0, -0.89], [0.27, -0.89], [0.315, -0.84], [0.315, 0.1], [0, 0.1]], 12), food(7035444));
|
|
6244
6420
|
const sediment = mesh2(cyl(0.31, 0.27, 0.12, 8), food(4865314), [0, -0.82, 0]);
|
|
6245
6421
|
const label = mesh2(cyl(0.332, 0.332, 0.22, 12), paper2(13617588), [0, 0.38, 0]);
|
|
6246
|
-
const cap = mesh2(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]);
|
|
6422
|
+
const cap = asLid(mesh2(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]));
|
|
6247
6423
|
return G(bottle2, murk, sediment, label, cap);
|
|
6248
6424
|
};
|
|
6249
6425
|
|
|
@@ -9232,7 +9408,7 @@ var GameItemsThree = (() => {
|
|
|
9232
9408
|
const materials = kit.materials.slice(first);
|
|
9233
9409
|
if (options.wear) setWear(materials, options.wear);
|
|
9234
9410
|
root.userData.wear = options.wear ?? 0;
|
|
9235
|
-
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size, three: T2 });
|
|
9411
|
+
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...shape === void 0 ? {} : { shapeId: shape }, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size, ...item.powder ? { powder: item.powder } : {}, three: T2 });
|
|
9236
9412
|
markKitOrigin(root, { owner: "game-items-three", kind: "item", id: item.id, ...kind === "shape" ? { standIn: true } : {} });
|
|
9237
9413
|
return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
|
|
9238
9414
|
}
|
|
@@ -15147,6 +15323,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15147
15323
|
var FLAMMABLE_FLUIDS = ["gas", "fuel"];
|
|
15148
15324
|
var VENT_DIAMETER = 8e-3;
|
|
15149
15325
|
var FLAME_LIGHTS_LEAK_C = 470;
|
|
15326
|
+
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
15150
15327
|
var BREACH_RADIUS = 0.55;
|
|
15151
15328
|
var BREACH_BLAST_FACTOR = 6;
|
|
15152
15329
|
var BREACH_AREA = 0.25;
|
|
@@ -15162,6 +15339,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15162
15339
|
var BREACH_GRID_CAP = 4096;
|
|
15163
15340
|
var CHIP_ENERGY = 200;
|
|
15164
15341
|
var CHIP_SPLASH = 0.5;
|
|
15342
|
+
var CRATER_SPENT = 0.6;
|
|
15165
15343
|
var BAR_BOW = 0.08;
|
|
15166
15344
|
var BULLET_JOINT_SPAN = 0.3;
|
|
15167
15345
|
var CUT_SLICE = 1;
|
|
@@ -15182,12 +15360,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15182
15360
|
const cells = p.st.chipped;
|
|
15183
15361
|
if (!cells?.length) return 0;
|
|
15184
15362
|
const [u, v] = planeAxes(p.thin);
|
|
15185
|
-
const
|
|
15363
|
+
const role = p.spec.structure?.role;
|
|
15364
|
+
const up = rotateVec(p.q, [0, 0, 0].map((_, k) => k === v ? 1 : 0));
|
|
15365
|
+
const upright = Math.abs(up[1]) > 0.7 ? v : Math.abs(rotateVec(p.q, [0, 0, 0].map((_, k) => k === u ? 1 : 0))[1]) > 0.7 ? u : -1;
|
|
15366
|
+
const along = (role === "wall" || role === "partition" || role === "column") && upright >= 0 ? upright : p.dims[u] >= p.dims[v] ? u : v;
|
|
15186
15367
|
const across2 = along === u ? v : u;
|
|
15187
15368
|
let worst = 0;
|
|
15188
15369
|
for (const c of cells) {
|
|
15189
|
-
|
|
15190
|
-
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6)
|
|
15370
|
+
const spans = [];
|
|
15371
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) spans.push([d.center[across2] - d.size[across2] / 2, d.center[across2] + d.size[across2] / 2]);
|
|
15372
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
15373
|
+
let gone = 0, end = -Infinity;
|
|
15374
|
+
for (const [a, b] of spans) {
|
|
15375
|
+
if (b <= end) continue;
|
|
15376
|
+
gone += b - Math.max(a, end);
|
|
15377
|
+
end = b;
|
|
15378
|
+
}
|
|
15191
15379
|
worst = Math.max(worst, gone / Math.max(p.dims[across2], 1e-6));
|
|
15192
15380
|
}
|
|
15193
15381
|
return Math.min(1, worst);
|
|
@@ -15483,7 +15671,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15483
15671
|
if (k < 0 || j.spec.kind !== "tie") return k;
|
|
15484
15672
|
return partXf(o, o.parts[k]).c[1] > partXf(o, o.parts[r]).c[1] + 1e-6 ? k : -1;
|
|
15485
15673
|
};
|
|
15674
|
+
let poseVersion = 0;
|
|
15675
|
+
const contactMemo = /* @__PURE__ */ new WeakMap();
|
|
15486
15676
|
const contactBox = (p, support) => {
|
|
15677
|
+
let byTouching = contactMemo.get(p);
|
|
15678
|
+
if (!byTouching) contactMemo.set(p, byTouching = /* @__PURE__ */ new Map());
|
|
15679
|
+
const known = byTouching.get(support);
|
|
15680
|
+
if (known && known.version === poseVersion) return known.box;
|
|
15681
|
+
const box4 = measureContact(p, support);
|
|
15682
|
+
byTouching.set(support, { version: poseVersion, box: box4 });
|
|
15683
|
+
return box4;
|
|
15684
|
+
};
|
|
15685
|
+
const measureContact = (p, support) => {
|
|
15487
15686
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
15488
15687
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
15489
15688
|
for (let k = 0; k < 3; k++) {
|
|
@@ -16278,7 +16477,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16278
16477
|
p.st.holes.push(hole);
|
|
16279
16478
|
if (p.st.holes.length > maxHoles) p.st.holes.shift();
|
|
16280
16479
|
markDamaged(p);
|
|
16281
|
-
emit({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {} });
|
|
16480
|
+
emit({ ...base(o, src), type: "hole", part: p.spec.id, hole, worldPoint: wp, ...exit ? { exitPoint: exit } : {}, ...p.spec.powder ? { powder: { ...p.spec.powder } } : {} });
|
|
16282
16481
|
setStage(o, p, 1, {}, src);
|
|
16283
16482
|
return hole;
|
|
16284
16483
|
};
|
|
@@ -16295,6 +16494,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16295
16494
|
const detachEvents = (o, opened, by, src) => {
|
|
16296
16495
|
for (const j of opened) emit({ ...base(o, src), type: "detach", joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) });
|
|
16297
16496
|
};
|
|
16497
|
+
const slideOffResting = (o, rotation) => {
|
|
16498
|
+
const upY = 1 - 2 * (rotation[0] * rotation[0] + rotation[2] * rotation[2]);
|
|
16499
|
+
if (upY >= RESTING_HOLD_COS) return;
|
|
16500
|
+
const opened = o.joints.filter((j) => j.spec.kind === "resting" && !j.st.open);
|
|
16501
|
+
if (opened.length === 0) return;
|
|
16502
|
+
run(() => {
|
|
16503
|
+
for (const j of opened) {
|
|
16504
|
+
j.st.open = true;
|
|
16505
|
+
j.st.openedBy = "break";
|
|
16506
|
+
}
|
|
16507
|
+
solveStructure(o);
|
|
16508
|
+
recomputeGroups(o);
|
|
16509
|
+
detachEvents(o, opened, "break");
|
|
16510
|
+
collapseCheck(o);
|
|
16511
|
+
});
|
|
16512
|
+
};
|
|
16298
16513
|
const checkDestroyed = (o, src) => {
|
|
16299
16514
|
if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {
|
|
16300
16515
|
o.st.destroyed = true;
|
|
@@ -16395,7 +16610,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16395
16610
|
const localHolesOf = (p) => p.st.holes.map((h) => ({ point: toLocal(p.pos, p.q, h.point), normal: rotateVec(quat.conjugate(p.q), h.normal), diameter: h.diameter }));
|
|
16396
16611
|
const unitsOf2 = (p) => p.spec.units ?? { size: [p.mat.fragmentSize?.[1] ?? 0.2, p.mat.fragmentSize?.[1] ?? 0.2, p.thickness], bond: "stack" };
|
|
16397
16612
|
const FRAGMENT_SHAPE = { shatter: "shard", splinter: "splinter", tear: "scrap" };
|
|
16398
|
-
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag) => {
|
|
16613
|
+
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag, inner = false) => {
|
|
16399
16614
|
const { c, q } = partXf(o, p);
|
|
16400
16615
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
16401
16616
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -16419,7 +16634,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16419
16634
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k],
|
|
16420
16635
|
angularVelocity: [(rnd() - 0.5) * 8, (rnd() - 0.5) * 8, (rnd() - 0.5) * 8],
|
|
16421
16636
|
mass: mass * faceArea(cell) / total,
|
|
16422
|
-
...marks.length > 0 ? { marks } : {}
|
|
16637
|
+
...marks.length > 0 ? { marks } : {},
|
|
16638
|
+
...inner ? { inner } : {}
|
|
16423
16639
|
};
|
|
16424
16640
|
});
|
|
16425
16641
|
};
|
|
@@ -16460,7 +16676,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16460
16676
|
return cells.some((c) => Math.abs(lp[u] - c.center[u]) <= c.size[u] / 2 && Math.abs(lp[v] - c.center[v]) <= c.size[v] / 2);
|
|
16461
16677
|
};
|
|
16462
16678
|
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
16463
|
-
var _a, _b;
|
|
16679
|
+
var _a, _b, _c;
|
|
16464
16680
|
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
16465
16681
|
const rule = p.mat.hits?.[kind];
|
|
16466
16682
|
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
@@ -16470,7 +16686,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16470
16686
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
16471
16687
|
const grid = gridOf(p);
|
|
16472
16688
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
16689
|
+
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
16473
16690
|
const out = [];
|
|
16691
|
+
const shot = kind === "ballistic";
|
|
16474
16692
|
for (let i = 0; i < grid.length; i++) {
|
|
16475
16693
|
const cell = grid[i];
|
|
16476
16694
|
if (worn[i] === -1) continue;
|
|
@@ -16483,18 +16701,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16483
16701
|
worn[i] = total;
|
|
16484
16702
|
continue;
|
|
16485
16703
|
}
|
|
16486
|
-
|
|
16487
|
-
|
|
16704
|
+
if (!shot) {
|
|
16705
|
+
worn[i] = -1;
|
|
16706
|
+
out.push(cell);
|
|
16707
|
+
continue;
|
|
16708
|
+
}
|
|
16709
|
+
const size = [cell.size[0], cell.size[1], cell.size[2]];
|
|
16710
|
+
const center = [cell.center[0], cell.center[1], cell.center[2]];
|
|
16711
|
+
for (const a of [u, v]) {
|
|
16712
|
+
const w = Math.min(cell.size[a], craterAcross(hitEnergy) * (0.85 + 0.3 * rnd()));
|
|
16713
|
+
const lo = cell.center[a] - cell.size[a] / 2 + w / 2, hi = cell.center[a] + cell.size[a] / 2 - w / 2;
|
|
16714
|
+
size[a] = w;
|
|
16715
|
+
center[a] = clamp2(lp[a], lo, hi);
|
|
16716
|
+
}
|
|
16717
|
+
const taken2 = (cut[i] ?? 0) + size[u] * size[v];
|
|
16718
|
+
if (taken2 >= cell.size[u] * cell.size[v] * CRATER_SPENT) {
|
|
16719
|
+
worn[i] = -1;
|
|
16720
|
+
out.push({ ...cell, crater: true });
|
|
16721
|
+
continue;
|
|
16722
|
+
}
|
|
16723
|
+
worn[i] = 0;
|
|
16724
|
+
cut[i] = taken2;
|
|
16725
|
+
out.push({ center, size, crater: true });
|
|
16488
16726
|
}
|
|
16489
16727
|
if (out.length === 0) return;
|
|
16490
16728
|
const { c, q } = partXf(o, p);
|
|
16491
|
-
const chipped = (
|
|
16729
|
+
const chipped = (_c = p.st).chipped ?? (_c.chipped = []);
|
|
16492
16730
|
const holes = localHolesOf(p);
|
|
16493
16731
|
for (const cell of out) {
|
|
16494
16732
|
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
16495
16733
|
const center = toWorld(c, q, cell.center);
|
|
16496
16734
|
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
16497
|
-
const
|
|
16735
|
+
const tag = `c${chipped.length}_`;
|
|
16736
|
+
const fragments = cell.crater ? craterFragments(o, p, cell, dir, speed, mass, tag) : cellFragments(o, p, [cell], wp, dir, speed, mass, tag, true);
|
|
16498
16737
|
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
16499
16738
|
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
16500
16739
|
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
@@ -16511,6 +16750,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16511
16750
|
collapseCheck(o, src);
|
|
16512
16751
|
}
|
|
16513
16752
|
};
|
|
16753
|
+
const craterAcross = (energy) => clamp2(0.03 + 12e-4 * Math.sqrt(Math.max(0, energy)), 0.03, 0.14);
|
|
16754
|
+
const craterFragments = (o, p, cell, dir, speed, mass, tag) => {
|
|
16755
|
+
const { c, q } = partXf(o, p);
|
|
16756
|
+
const [u, v] = planeAxes(p.thin);
|
|
16757
|
+
const d = dir ?? rotateVec(q, [p.thin === 0 ? 1 : 0, p.thin === 1 ? 1 : 0, p.thin === 2 ? 1 : 0]);
|
|
16758
|
+
const exitSign = Math.sign(rotateVec(quat.conjugate(q), d)[p.thin]) || 1;
|
|
16759
|
+
const bit = Math.max(0.01, Math.min(cell.size[u], cell.size[v]) / 3);
|
|
16760
|
+
const n = clamp2(Math.round(cell.size[u] * cell.size[v] / (bit * bit * 3)), 4, 10) + Math.floor(rnd() * 3);
|
|
16761
|
+
const out = [];
|
|
16762
|
+
for (let i = 0; i < n; i++) {
|
|
16763
|
+
const back = i % 3 === 2;
|
|
16764
|
+
const lc = [cell.center[0], cell.center[1], cell.center[2]];
|
|
16765
|
+
lc[u] += (rnd() - 0.5) * cell.size[u] * 0.7;
|
|
16766
|
+
lc[v] += (rnd() - 0.5) * cell.size[v] * 0.7;
|
|
16767
|
+
lc[p.thin] += (back ? -exitSign : exitSign) * (p.thickness / 2 + 0.01);
|
|
16768
|
+
const size = [0, 0, 0];
|
|
16769
|
+
size[u] = bit * (0.6 + 0.8 * rnd());
|
|
16770
|
+
size[v] = bit * (0.5 + 0.7 * rnd());
|
|
16771
|
+
size[p.thin] = bit * (0.4 + 0.6 * rnd());
|
|
16772
|
+
const spread3 = [(rnd() - 0.5) * 0.9, (rnd() - 0.3) * 0.9, (rnd() - 0.5) * 0.9];
|
|
16773
|
+
const k = speed * (back ? 0.4 + 0.4 * rnd() : 0.7 + 0.8 * rnd());
|
|
16774
|
+
const vdir = vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3));
|
|
16775
|
+
out.push({
|
|
16776
|
+
id: `${p.spec.id}#${tag}${i}`,
|
|
16777
|
+
part: p.spec.id,
|
|
16778
|
+
material: p.spec.material,
|
|
16779
|
+
shape: "chunk",
|
|
16780
|
+
center: toWorld(c, q, lc),
|
|
16781
|
+
size,
|
|
16782
|
+
rotation: q,
|
|
16783
|
+
velocity: [vdir[0] * k, vdir[1] * k + 0.5 * rnd(), vdir[2] * k],
|
|
16784
|
+
angularVelocity: [(rnd() - 0.5) * 20, (rnd() - 0.5) * 20, (rnd() - 0.5) * 20],
|
|
16785
|
+
mass: mass / n,
|
|
16786
|
+
inner: true
|
|
16787
|
+
});
|
|
16788
|
+
}
|
|
16789
|
+
return out;
|
|
16790
|
+
};
|
|
16514
16791
|
const exposeRebar = (o, p, src) => {
|
|
16515
16792
|
const r = p.spec.reinforcement;
|
|
16516
16793
|
if (!r) return;
|
|
@@ -16798,7 +17075,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16798
17075
|
});
|
|
16799
17076
|
}
|
|
16800
17077
|
const patch = [scale * 4, scale * 4, scale * 4];
|
|
16801
|
-
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments });
|
|
17078
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments, torn: true });
|
|
16802
17079
|
};
|
|
16803
17080
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round3 = {}) => {
|
|
16804
17081
|
const mat = p.mat;
|
|
@@ -16857,12 +17134,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16857
17134
|
} else if (mat.ductility >= 0.5) {
|
|
16858
17135
|
addDent(o, p, entry2, n, Math.max(caliber * 3 / 1e3, 0.012), clamp2(Math.min(Math.max(p.thickness * 3, 3e-3), ratio * 0.01), 1e-3, 0.05), src);
|
|
16859
17136
|
} else if (brittle) {
|
|
16860
|
-
addCrack(o, p, entry2, n, crackLen, src);
|
|
17137
|
+
if (!p.spec.units) addCrack(o, p, entry2, n, crackLen, src);
|
|
16861
17138
|
if (mat.fracture !== "shatter") addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
16862
17139
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
16863
17140
|
debris(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
16864
17141
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
16865
|
-
const
|
|
17142
|
+
const hollowTear = mat.fracture === "tear" && p.spec.fill !== void 0 && p.spec.fill < 0.5;
|
|
17143
|
+
const absorbed = penetrated && (mat.ductility >= 0.5 && mat.fracture !== "tear" || hollowTear) ? Math.min(E, thr) : E;
|
|
16866
17144
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
16867
17145
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
16868
17146
|
chipUnits(o, p, "ballistic", entry2, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
@@ -16947,10 +17225,25 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16947
17225
|
if (dist <= BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3)) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
16948
17226
|
return reach;
|
|
16949
17227
|
};
|
|
17228
|
+
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
17229
|
+
const blastCandidates = (o, point, radius) => {
|
|
17230
|
+
const local = toLocal(o.st.position, o.st.rotation, point);
|
|
17231
|
+
const near = [];
|
|
17232
|
+
for (const p of o.parts) {
|
|
17233
|
+
const reach = radius + p.bound;
|
|
17234
|
+
const dx = local[0] - p.pos[0];
|
|
17235
|
+
const dy = local[1] - p.pos[1];
|
|
17236
|
+
const dz = local[2] - p.pos[2];
|
|
17237
|
+
if (dx * dx + dy * dy + dz * dz < reach * reach) near.push(p);
|
|
17238
|
+
}
|
|
17239
|
+
blastProbe.partsTested += o.parts.length;
|
|
17240
|
+
blastProbe.partsNear += near.length;
|
|
17241
|
+
return near;
|
|
17242
|
+
};
|
|
16950
17243
|
const blastObject = (o, point, energy, radius, src) => {
|
|
16951
17244
|
const start = cur.length;
|
|
16952
17245
|
const res = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] };
|
|
16953
|
-
for (const p of o
|
|
17246
|
+
for (const p of blastCandidates(o, point, radius)) blastPart(o, p, point, energy, radius, src, res);
|
|
16954
17247
|
res.events = cur.slice(start);
|
|
16955
17248
|
return res;
|
|
16956
17249
|
};
|
|
@@ -17074,7 +17367,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17074
17367
|
let order = 0;
|
|
17075
17368
|
for (const o of inRange) {
|
|
17076
17369
|
if (only && only !== o) continue;
|
|
17077
|
-
for (const p of o
|
|
17370
|
+
for (const p of blastCandidates(o, point, radius)) {
|
|
17078
17371
|
if (isLost(p)) continue;
|
|
17079
17372
|
const dist = distToPart(o, p, point);
|
|
17080
17373
|
if (dist < radius) items.push({ dist, order: order++, o, p });
|
|
@@ -17621,6 +17914,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17621
17914
|
const p = getPart(o, partId);
|
|
17622
17915
|
p.pos = [position[0], position[1], position[2]];
|
|
17623
17916
|
p.q = q;
|
|
17917
|
+
poseVersion++;
|
|
17624
17918
|
updateBound(o);
|
|
17625
17919
|
},
|
|
17626
17920
|
remove(id) {
|
|
@@ -17639,6 +17933,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17639
17933
|
const o = getObj(id);
|
|
17640
17934
|
o.base = { pos: [position[0], position[1], position[2]], rot: [rotation[0], rotation[1], rotation[2], rotation[3]] };
|
|
17641
17935
|
applySoilPose(o);
|
|
17936
|
+
slideOffResting(o, rotation);
|
|
17642
17937
|
},
|
|
17643
17938
|
splitGroup(id, group, newId) {
|
|
17644
17939
|
if (impls.has(id)) throw new Error(`object "${id}" is a registered DestructibleImpl and cannot be split; its module owns its pieces`);
|
|
@@ -17662,7 +17957,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17662
17957
|
if (o.spec.name !== void 0) s.name = o.spec.name;
|
|
17663
17958
|
if (o.spec.tags) s.tags = [...o.spec.tags];
|
|
17664
17959
|
if (o.spec.soil !== void 0) s.soil = o.spec.soil;
|
|
17665
|
-
return
|
|
17960
|
+
return s;
|
|
17666
17961
|
};
|
|
17667
17962
|
const inNew = (pid) => moved.has(pid);
|
|
17668
17963
|
const inOld = (pid) => !moved.has(pid);
|
|
@@ -17689,7 +17984,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17689
17984
|
const p = getPart(o, partId);
|
|
17690
17985
|
return whole(() => doHit(o, h, p));
|
|
17691
17986
|
},
|
|
17987
|
+
blastProbe() {
|
|
17988
|
+
return { ...blastProbe };
|
|
17989
|
+
},
|
|
17692
17990
|
explode(point, energy, radius, source, blast) {
|
|
17991
|
+
blastProbe.partsTested = 0;
|
|
17992
|
+
blastProbe.partsNear = 0;
|
|
17693
17993
|
return withCollapses(run(() => blastWorld(point, energy, radius, source, blast)));
|
|
17694
17994
|
},
|
|
17695
17995
|
raycast(origin, direction, maxDistance = Infinity) {
|
|
@@ -17699,7 +17999,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17699
17999
|
sweepBox(box4, direction, distance2, options2 = {}) {
|
|
17700
18000
|
const d = vec.normalize(direction);
|
|
17701
18001
|
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
17702
|
-
const
|
|
18002
|
+
const hosted = options2.ignore === void 0 || ignored ? void 0 : impls.get(options2.ignore);
|
|
18003
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : hosted ? new Set(hosted.impl.describe().parts.map((p) => `${hosted.impl.id}\0${p.id}`)) : void 0;
|
|
17703
18004
|
let best = null;
|
|
17704
18005
|
for (const origin of sweepOrigins(box4, d, options2.spacing ?? 0.2)) {
|
|
17705
18006
|
const r = raycastInternal(origin, d, distance2 + SWEEP_BACK, ignore);
|
|
@@ -27302,6 +27603,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27302
27603
|
function collapseRange(mesh2, v0, v1) {
|
|
27303
27604
|
const pos = mesh2.geometry.attributes.position;
|
|
27304
27605
|
const a = pos.array;
|
|
27606
|
+
const u = mesh2.userData;
|
|
27607
|
+
if (!u._p0) u._p0 = a.slice();
|
|
27608
|
+
const list2 = u.collapsedRanges ?? (u.collapsedRanges = []);
|
|
27609
|
+
if (!list2.some((r) => r[0] === v0 && r[1] === v1)) list2.push([v0, v1]);
|
|
27305
27610
|
const saved = a.slice(v0 * 3, v1 * 3);
|
|
27306
27611
|
const x = a[v0 * 3], y = a[v0 * 3 + 1], z = a[v0 * 3 + 2];
|
|
27307
27612
|
for (let k = v0; k < v1; k++) {
|
|
@@ -27316,6 +27621,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27316
27621
|
const pos = mesh2.geometry.attributes.position;
|
|
27317
27622
|
pos.array.set(saved, v0 * 3);
|
|
27318
27623
|
pos.needsUpdate = true;
|
|
27624
|
+
const list2 = mesh2.userData.collapsedRanges;
|
|
27625
|
+
if (list2) mesh2.userData.collapsedRanges = list2.filter((r) => r[0] !== v0);
|
|
27319
27626
|
}
|
|
27320
27627
|
function noise2(x, y, cell) {
|
|
27321
27628
|
const h = (i2, j2) => {
|
|
@@ -27330,13 +27637,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27330
27637
|
var SPALL_MIN = 0.025;
|
|
27331
27638
|
var SPALL_RAGGED = 0.045;
|
|
27332
27639
|
var COVER2 = 0.015;
|
|
27640
|
+
var MAX_FLAKES = 64;
|
|
27333
27641
|
function installDestructionBreach(K, _shared) {
|
|
27334
27642
|
const { THREE, fragmentMaterial, invalidate, invalidateShadow } = K;
|
|
27335
27643
|
function createBreachView(opts) {
|
|
27336
27644
|
const { house, structure } = opts;
|
|
27337
27645
|
const views = /* @__PURE__ */ new Map();
|
|
27646
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
27647
|
+
const paintBudgetMs = opts.paintBudgetMs ?? Infinity;
|
|
27648
|
+
const dropped = [];
|
|
27338
27649
|
let lod = 0;
|
|
27339
|
-
const unitMaterial = new THREE.MeshLambertMaterial({ color: 16777215 });
|
|
27340
27650
|
const unitGeometry = new THREE.BoxGeometry(1, 1, 1);
|
|
27341
27651
|
const tmp = { m: new THREE.Matrix4(), q: new THREE.Quaternion(), p: new THREE.Vector3(), s: new THREE.Vector3(), c: new THREE.Color() };
|
|
27342
27652
|
const local = (p) => {
|
|
@@ -27405,6 +27715,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27405
27715
|
const mesh2 = new THREE.Mesh(g, material);
|
|
27406
27716
|
mesh2.name = "breach-shell";
|
|
27407
27717
|
mesh2.userData.breachMask = mask;
|
|
27718
|
+
mesh2.userData.cutAt = (p) => {
|
|
27719
|
+
const v = local([p.x, p.y, p.z]);
|
|
27720
|
+
const i = Math.floor(((mask.alongU ? v[0] : v[2]) - mask.start) / mask.len * mask.w), j = Math.floor((v[1] - mask.y0) / mask.height * mask.h);
|
|
27721
|
+
return i >= 0 && i < mask.w && j >= 0 && j < mask.h && mask.data[j * mask.w + i] > 127;
|
|
27722
|
+
};
|
|
27408
27723
|
mesh2.position.copy(it.mesh.position);
|
|
27409
27724
|
mesh2.quaternion.copy(it.mesh.quaternion);
|
|
27410
27725
|
mesh2.scale.copy(it.mesh.scale);
|
|
@@ -27420,6 +27735,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27420
27735
|
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh2.matrixWorld);
|
|
27421
27736
|
return mesh2;
|
|
27422
27737
|
}
|
|
27738
|
+
function unitsMaterial(core) {
|
|
27739
|
+
const material = new THREE.MeshLambertMaterial({ color: 16777215, side: THREE.DoubleSide });
|
|
27740
|
+
material.onBeforeCompile = (sh) => {
|
|
27741
|
+
sh.uniforms.uCore = { value: core.texture };
|
|
27742
|
+
sh.uniforms.uCoreRect = { value: core.rect };
|
|
27743
|
+
sh.uniforms.uCoreAlongX = { value: core.alongU ? 1 : 0 };
|
|
27744
|
+
sh.vertexShader = sh.vertexShader.replace("void main() {", "varying vec3 vHousePos;\nvoid main() {").replace("#include <begin_vertex>", "#include <begin_vertex>\n#ifdef USE_INSTANCING\nvHousePos = (instanceMatrix * vec4(transformed, 1.0)).xyz;\n#else\nvHousePos = transformed;\n#endif");
|
|
27745
|
+
sh.fragmentShader = sh.fragmentShader.replace("void main() {", "uniform sampler2D uCore;\nuniform vec4 uCoreRect;\nuniform float uCoreAlongX;\nvarying vec3 vHousePos;\nvoid main() {\n vec2 cUv = vec2(((uCoreAlongX > 0.5 ? vHousePos.x : vHousePos.z) - uCoreRect.x) / uCoreRect.z, (vHousePos.y - uCoreRect.y) / uCoreRect.w);\n if (cUv.x >= 0.0 && cUv.x <= 1.0 && cUv.y >= 0.0 && cUv.y <= 1.0 && texture2D(uCore, cUv).r > 0.5) discard;").replace("#include <color_fragment>", "#include <color_fragment>\nif (!gl_FrontFacing) diffuseColor.rgb *= 0.55;");
|
|
27746
|
+
};
|
|
27747
|
+
material.customProgramCacheKey = () => "breachCore";
|
|
27748
|
+
material.userData.breachCore = core;
|
|
27749
|
+
return material;
|
|
27750
|
+
}
|
|
27423
27751
|
function maskOf(hostId, parts) {
|
|
27424
27752
|
const host = structure.byId.get(hostId);
|
|
27425
27753
|
const [u0, v0, u1, v1] = host.aabb;
|
|
@@ -27435,41 +27763,73 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27435
27763
|
return { data, texture, w, h, alongU, start: alongU ? u0 : v0, len, y0, height: y1 - y0, rect: new THREE.Vector4(alongU ? u0 : v0, y0, len, y1 - y0) };
|
|
27436
27764
|
}
|
|
27437
27765
|
const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
|
|
27766
|
+
const ellipseDistance = (s, y, cs, cy, rs, ry) => (Math.hypot((s - cs) / Math.max(rs, 1e-4), (y - cy) / Math.max(ry, 1e-4)) - 1) * Math.min(rs, ry);
|
|
27767
|
+
function covered(c, boxes) {
|
|
27768
|
+
const n = 8;
|
|
27769
|
+
let hit = 0;
|
|
27770
|
+
for (let j = 0; j < n; j++) {
|
|
27771
|
+
const y = c.y0 + (j + 0.5) / n * (c.y1 - c.y0);
|
|
27772
|
+
for (let i = 0; i < n; i++) {
|
|
27773
|
+
const s = c.a + (i + 0.5) / n * (c.b - c.a);
|
|
27774
|
+
if (boxes.some((k) => s >= k.s0 && s <= k.s1 && y >= k.y0 && y <= k.y1)) hit++;
|
|
27775
|
+
}
|
|
27776
|
+
}
|
|
27777
|
+
return hit / (n * n);
|
|
27778
|
+
}
|
|
27438
27779
|
function paint(view) {
|
|
27439
|
-
const { mask } = view;
|
|
27780
|
+
const { mask, core } = view;
|
|
27440
27781
|
const along = (p) => (mask.alongU ? p[0] : p[2]) - mask.start;
|
|
27441
27782
|
const holes = [];
|
|
27783
|
+
const pits = [];
|
|
27442
27784
|
const discs = [];
|
|
27785
|
+
const flakes = [];
|
|
27443
27786
|
const shown = [];
|
|
27444
27787
|
for (const id of view.parts) {
|
|
27445
|
-
const part = structure.byId.get(id), units2 = specOf(id).units, chips = view.chips.get(id), opening = view.openings.get(id);
|
|
27788
|
+
const part = structure.byId.get(id), units2 = specOf(id).units, chips = view.chips.get(id), opening = view.openings.get(id), spalls = view.spalls.get(id);
|
|
27446
27789
|
const all = cells(part, units2);
|
|
27447
27790
|
const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
|
|
27448
27791
|
const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
|
|
27449
27792
|
if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
|
|
27450
|
-
|
|
27793
|
+
for (const k of spalls) flakes.push({ s: along(k.center), y: k.center[1], r: k.radius });
|
|
27794
|
+
const boxes = chips.map((k) => ({ s0: along(k.center) - k.along, s1: along(k.center) + k.along, y0: k.center[1] - k.up, y1: k.center[1] + k.up }));
|
|
27795
|
+
const gone = (c) => covered(c, boxes.filter((k) => k.s1 > c.a && k.s0 < c.b && k.y1 > c.y0 && k.y0 < c.y1)) >= 0.9 || opening && Math.hypot((c.a + c.b) / 2 - along(opening.center), (c.y0 + c.y1) / 2 - opening.center[1]) < opening.radius;
|
|
27451
27796
|
const kept = [];
|
|
27452
27797
|
for (const c of all) (gone(c) ? holes : kept).push(c);
|
|
27453
27798
|
for (const c of kept) shown.push({ c, part, units: units2, thick, mid });
|
|
27799
|
+
for (const k of boxes) {
|
|
27800
|
+
if (holes.some((h) => k.s0 >= h.a - 1e-4 && k.s1 <= h.b + 1e-4 && k.y0 >= h.y0 - 1e-4 && k.y1 <= h.y1 + 1e-4)) continue;
|
|
27801
|
+
pits.push({ s: (k.s0 + k.s1) / 2, y: (k.y0 + k.y1) / 2, rs: (k.s1 - k.s0) / 2, ry: (k.y1 - k.y0) / 2 });
|
|
27802
|
+
}
|
|
27454
27803
|
}
|
|
27455
27804
|
const reach = SPALL_MIN + SPALL_RAGGED;
|
|
27805
|
+
const ragged = (s, y) => 0.6 * noise2(s, y, 0.03) + 0.4 * noise2(s + 7.3, y - 3.1, 0.011);
|
|
27456
27806
|
mask.data.fill(0);
|
|
27457
|
-
|
|
27458
|
-
|
|
27459
|
-
const
|
|
27807
|
+
core.data.fill(0);
|
|
27808
|
+
const stamp = (m, s0, s1, y0, y1, inside2) => {
|
|
27809
|
+
const i0 = Math.max(0, Math.floor((s0 - reach) / m.len * m.w)), i1 = Math.min(m.w - 1, Math.ceil((s1 + reach) / m.len * m.w));
|
|
27810
|
+
const j0 = Math.max(0, Math.floor((y0 - reach - m.y0) / m.height * m.h)), j1 = Math.min(m.h - 1, Math.ceil((y1 + reach - m.y0) / m.height * m.h));
|
|
27460
27811
|
for (let j = j0; j <= j1; j++) {
|
|
27461
|
-
const y =
|
|
27812
|
+
const y = m.y0 + (j + 0.5) / m.h * m.height;
|
|
27462
27813
|
for (let i = i0; i <= i1; i++) {
|
|
27463
|
-
const s = (i + 0.5) /
|
|
27464
|
-
|
|
27465
|
-
if (inside2(s, y) <= edge) mask.data[j * mask.w + i] = 255;
|
|
27814
|
+
const s = (i + 0.5) / m.w * m.len;
|
|
27815
|
+
if (inside2(s, y)) m.data[j * m.w + i] = 255;
|
|
27466
27816
|
}
|
|
27467
27817
|
}
|
|
27468
27818
|
};
|
|
27469
|
-
|
|
27470
|
-
for (const
|
|
27819
|
+
const plaster = (d, s, y) => d <= SPALL_MIN + SPALL_RAGGED * ragged(s, y);
|
|
27820
|
+
for (const c of holes) stamp(mask, c.a, c.b, c.y0, c.y1, (s, y) => plaster(boxDistance(s, y, c.a, c.b, c.y0, c.y1), s, y));
|
|
27821
|
+
for (const d of discs) stamp(mask, d.s - d.r, d.s + d.r, d.y - d.r, d.y + d.r, (s, y) => plaster(Math.max(0, Math.hypot(s - d.s, y - d.y) - d.r), s, y));
|
|
27822
|
+
for (const f of flakes) {
|
|
27823
|
+
const rough = (s, y) => Math.hypot(s - f.s, y - f.y) - f.r * (0.65 + 0.7 * ragged(s * 2.3, y * 2.3));
|
|
27824
|
+
stamp(mask, f.s - 2 * f.r, f.s + 2 * f.r, f.y - 2 * f.r, f.y + 2 * f.r, (s, y) => rough(s, y) <= 0);
|
|
27825
|
+
}
|
|
27826
|
+
for (const p of pits) {
|
|
27827
|
+
stamp(mask, p.s - p.rs, p.s + p.rs, p.y - p.ry, p.y + p.ry, (s, y) => plaster(Math.max(0, ellipseDistance(s, y, p.s, p.y, p.rs, p.ry)), s, y));
|
|
27828
|
+
stamp(core, p.s - p.rs, p.s + p.rs, p.y - p.ry, p.y + p.ry, (s, y) => ellipseDistance(s, y, p.s, p.y, p.rs, p.ry) <= -0.35 * Math.min(p.rs, p.ry) * noise2(s * 1.7, y * 1.3, 0.012));
|
|
27829
|
+
}
|
|
27471
27830
|
mask.texture.needsUpdate = true;
|
|
27472
|
-
|
|
27831
|
+
core.texture.needsUpdate = true;
|
|
27832
|
+
const near = (c) => holes.some((h) => boxDistance((c.a + c.b) / 2, (c.y0 + c.y1) / 2, h.a - (c.b - c.a) / 2, h.b + (c.b - c.a) / 2, h.y0 - (c.y1 - c.y0) / 2, h.y1 + (c.y1 - c.y0) / 2) <= reach) || discs.some((d) => boxDistance(d.s, d.y, c.a, c.b, c.y0, c.y1) <= d.r + reach) || pits.some((p) => boxDistance(p.s, p.y, c.a, c.b, c.y0, c.y1) <= Math.max(p.rs, p.ry) + reach) || flakes.some((f) => boxDistance(f.s, f.y, c.a, c.b, c.y0, c.y1) <= 2 * f.r);
|
|
27473
27833
|
const list2 = [];
|
|
27474
27834
|
const tone = (hex3, k) => tmp.c.setHex(hex3).multiplyScalar(k).getHex();
|
|
27475
27835
|
for (const { c, part, units: units2, thick, mid } of shown) {
|
|
@@ -27479,7 +27839,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27479
27839
|
const base = fragmentMaterial(part.material).color.getHex();
|
|
27480
27840
|
const n = noise2(centre * 3.1, y * 2.7, 0.07);
|
|
27481
27841
|
if (units2.mortar === "concrete") {
|
|
27482
|
-
at(len, h, thick - 2 * COVER2
|
|
27842
|
+
at(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
|
|
27483
27843
|
} else {
|
|
27484
27844
|
at(len, h, thick - 2 * COVER2 - 0.012, 10327948);
|
|
27485
27845
|
at(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
|
|
@@ -27489,7 +27849,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27489
27849
|
view.units.removeFromParent();
|
|
27490
27850
|
view.units.dispose();
|
|
27491
27851
|
}
|
|
27492
|
-
const units = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
27852
|
+
const units = new THREE.InstancedMesh(unitGeometry, view.unitMaterial, Math.max(1, list2.length));
|
|
27493
27853
|
units.name = "breach:" + view.host;
|
|
27494
27854
|
list2.forEach((u, i) => {
|
|
27495
27855
|
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
@@ -27505,6 +27865,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27505
27865
|
house.add(units);
|
|
27506
27866
|
view.units = units;
|
|
27507
27867
|
view.gone = holes.length;
|
|
27868
|
+
view.pits = pits.length;
|
|
27869
|
+
view.flakes = flakes.length;
|
|
27870
|
+
}
|
|
27871
|
+
function chipBox(chip, alongU) {
|
|
27872
|
+
house.updateMatrixWorld(true);
|
|
27873
|
+
const q = tmp.q.set(chip.rotation[0], chip.rotation[1], chip.rotation[2], chip.rotation[3]).premultiply(house.getWorldQuaternion(new THREE.Quaternion()).invert());
|
|
27874
|
+
let a = 0, u = 0;
|
|
27875
|
+
for (let k = 0; k < 3; k++) {
|
|
27876
|
+
const axis = tmp.p.set(k === 0 ? 1 : 0, k === 1 ? 1 : 0, k === 2 ? 1 : 0).applyQuaternion(q);
|
|
27877
|
+
a += Math.abs(alongU ? axis.x : axis.z) * chip.size[k] / 2;
|
|
27878
|
+
u += Math.abs(axis.y) * chip.size[k] / 2;
|
|
27879
|
+
}
|
|
27880
|
+
return { center: local(chip.center), along: a, up: u };
|
|
27508
27881
|
}
|
|
27509
27882
|
function draw2(partId, change) {
|
|
27510
27883
|
if (lod !== 0) return false;
|
|
@@ -27516,23 +27889,49 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27516
27889
|
if (!items.length) return false;
|
|
27517
27890
|
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
27518
27891
|
if (!parts.includes(partId)) return false;
|
|
27519
|
-
const mask = maskOf(hostId, parts);
|
|
27520
|
-
view = {
|
|
27892
|
+
const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
|
|
27893
|
+
view = {
|
|
27894
|
+
host: hostId,
|
|
27895
|
+
parts,
|
|
27896
|
+
items,
|
|
27897
|
+
mask,
|
|
27898
|
+
core,
|
|
27899
|
+
unitMaterial: unitsMaterial(core),
|
|
27900
|
+
shells: [],
|
|
27901
|
+
saved: null,
|
|
27902
|
+
units: null,
|
|
27903
|
+
gone: 0,
|
|
27904
|
+
pits: 0,
|
|
27905
|
+
chips: new Map(parts.map((id) => [id, []])),
|
|
27906
|
+
spalls: new Map(parts.map((id) => [id, []])),
|
|
27907
|
+
openings: /* @__PURE__ */ new Map(),
|
|
27908
|
+
flakes: 0
|
|
27909
|
+
};
|
|
27521
27910
|
}
|
|
27522
27911
|
if (!view.chips.has(partId)) return false;
|
|
27523
27912
|
if (change.opening) {
|
|
27524
27913
|
if (view.openings.has(partId)) return false;
|
|
27525
27914
|
view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
|
|
27526
27915
|
}
|
|
27527
|
-
if (change.chip) view.chips.get(partId).push(
|
|
27916
|
+
if (change.chip) view.chips.get(partId).push(chipBox(change.chip, view.mask.alongU));
|
|
27917
|
+
if (change.spall) {
|
|
27918
|
+
const list2 = view.spalls.get(partId);
|
|
27919
|
+
list2.push({ center: local(change.spall.center), radius: change.spall.radius });
|
|
27920
|
+
if (list2.length > MAX_FLAKES) list2.shift();
|
|
27921
|
+
}
|
|
27528
27922
|
if (!view.saved) {
|
|
27529
27923
|
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
27924
|
+
for (const m of view.shells) m.userData.behind = () => view.units;
|
|
27530
27925
|
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
27531
27926
|
}
|
|
27532
|
-
paint(view);
|
|
27533
27927
|
views.set(hostId, view);
|
|
27534
|
-
|
|
27535
|
-
|
|
27928
|
+
if (opts.deferPaint && change.spall) dirty.add(view);
|
|
27929
|
+
else {
|
|
27930
|
+
dirty.delete(view);
|
|
27931
|
+
paint(view);
|
|
27932
|
+
invalidate();
|
|
27933
|
+
invalidateShadow();
|
|
27934
|
+
}
|
|
27536
27935
|
return true;
|
|
27537
27936
|
}
|
|
27538
27937
|
function plain2(view, shown) {
|
|
@@ -27541,24 +27940,70 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27541
27940
|
else s.data = collapseRange(s.it.mesh, s.v0, s.it.v1);
|
|
27542
27941
|
}
|
|
27543
27942
|
}
|
|
27943
|
+
function flush(budgetMs = paintBudgetMs) {
|
|
27944
|
+
if (dirty.size === 0) return 0;
|
|
27945
|
+
const deadline = performance.now() + budgetMs;
|
|
27946
|
+
for (const view of dirty) {
|
|
27947
|
+
dirty.delete(view);
|
|
27948
|
+
paint(view);
|
|
27949
|
+
if (performance.now() >= deadline) break;
|
|
27950
|
+
}
|
|
27951
|
+
invalidate();
|
|
27952
|
+
invalidateShadow();
|
|
27953
|
+
return dirty.size;
|
|
27954
|
+
}
|
|
27544
27955
|
function dispose(view) {
|
|
27545
|
-
|
|
27546
|
-
view.units
|
|
27956
|
+
dirty.delete(view);
|
|
27957
|
+
if (view.units) {
|
|
27958
|
+
view.units.removeFromParent();
|
|
27959
|
+
view.units.dispose();
|
|
27960
|
+
}
|
|
27961
|
+
view.unitMaterial.dispose();
|
|
27547
27962
|
for (const m of view.shells) {
|
|
27548
27963
|
m.removeFromParent();
|
|
27549
27964
|
m.geometry.dispose();
|
|
27550
27965
|
m.material.dispose();
|
|
27551
27966
|
}
|
|
27552
27967
|
view.mask.texture.dispose();
|
|
27968
|
+
view.core.texture.dispose();
|
|
27553
27969
|
}
|
|
27554
27970
|
return {
|
|
27555
27971
|
/** Handles a `stage` event of the house's object. Returns true when the wall was cut. */
|
|
27556
27972
|
stage(event) {
|
|
27557
27973
|
return event.stage === 2 && !!event.opening ? draw2(event.part, { opening: event.opening }) : false;
|
|
27558
27974
|
},
|
|
27559
|
-
/**
|
|
27975
|
+
/**
|
|
27976
|
+
* Handles a `spall` of a wall of units: the coat comes off in a ragged patch and the units show under it, drawn by the house (the destruction
|
|
27977
|
+
* renderer leaves its flat patch out on the meshes `claim` marked). Returns true when the wall was cut.
|
|
27978
|
+
*/
|
|
27979
|
+
spall(event) {
|
|
27980
|
+
const r = Math.sqrt(Math.max(0, event.area) / Math.PI);
|
|
27981
|
+
return r > 0 ? draw2(event.part, { spall: { center: event.point, radius: r } }) : false;
|
|
27982
|
+
},
|
|
27983
|
+
/**
|
|
27984
|
+
* Marks the meshes that draw a wall of units as drawing its spalls itself (`userData.drawnSpalls`, the part ids): the destruction renderer then
|
|
27985
|
+
* throws the bits and leaves its patch out. Call it once the spec is known.
|
|
27986
|
+
*/
|
|
27987
|
+
claim() {
|
|
27988
|
+
var _a;
|
|
27989
|
+
for (const q of opts.spec()?.parts ?? []) {
|
|
27990
|
+
if (!q.units) continue;
|
|
27991
|
+
const host = hostOf(q.id);
|
|
27992
|
+
if (!host) continue;
|
|
27993
|
+
for (const it of structure.meshesOf(host)) {
|
|
27994
|
+
if (!it.mesh) continue;
|
|
27995
|
+
const list2 = (_a = it.mesh.userData).drawnSpalls ?? (_a.drawnSpalls = []);
|
|
27996
|
+
if (!list2.includes(q.id)) list2.push(q.id);
|
|
27997
|
+
}
|
|
27998
|
+
}
|
|
27999
|
+
},
|
|
28000
|
+
/**
|
|
28001
|
+
* Handles a `chip` event: what came out (a unit, or a crater a shot broke through one) leaves a hole in the wall where it was; a `torn` chip (what a blow
|
|
28002
|
+
* tore off a face) leaves the marks the renderer drew. Returns true when the wall was cut.
|
|
28003
|
+
*/
|
|
27560
28004
|
chip(event) {
|
|
27561
|
-
|
|
28005
|
+
if (event.torn) return false;
|
|
28006
|
+
return draw2(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation: event.rotation ?? [0, 0, 0, 1] } });
|
|
27562
28007
|
},
|
|
27563
28008
|
/** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
|
|
27564
28009
|
drop(partId) {
|
|
@@ -27566,9 +28011,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27566
28011
|
if (!v) return;
|
|
27567
28012
|
dispose(v);
|
|
27568
28013
|
views.delete(partId);
|
|
28014
|
+
if (v.saved) dropped.push(v);
|
|
27569
28015
|
},
|
|
27570
28016
|
/** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
|
|
27571
28017
|
setLod(level) {
|
|
28018
|
+
flush(Infinity);
|
|
27572
28019
|
const was = lod;
|
|
27573
28020
|
lod = level;
|
|
27574
28021
|
if (was === 0 === (level === 0)) return;
|
|
@@ -27587,21 +28034,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27587
28034
|
if (lod === 0) plain2(v, true);
|
|
27588
28035
|
}
|
|
27589
28036
|
views.clear();
|
|
28037
|
+
for (const v of dropped) plain2(v, true);
|
|
28038
|
+
dropped.length = 0;
|
|
27590
28039
|
},
|
|
28040
|
+
flush,
|
|
27591
28041
|
/** The wall has damage drawn. */
|
|
27592
28042
|
has(partId) {
|
|
27593
28043
|
return views.has(partId);
|
|
27594
28044
|
},
|
|
27595
|
-
/** Walls with damage drawn, units out of them, and units laid bare at the edge of the cuts. */
|
|
27596
|
-
statistics: () => ({
|
|
28045
|
+
/** Walls with damage drawn, units out of them, craters shots broke through units that stay, and units laid bare at the edge of the cuts. */
|
|
28046
|
+
statistics: () => (flush(Infinity), {
|
|
27597
28047
|
walls: views.size,
|
|
27598
28048
|
gone: [...views.values()].reduce((n, v) => n + v.gone, 0),
|
|
28049
|
+
craters: [...views.values()].reduce((n, v) => n + v.pits, 0),
|
|
28050
|
+
flakes: [...views.values()].reduce((n, v) => n + v.flakes, 0),
|
|
27599
28051
|
units: [...views.values()].reduce((n, v) => n + v.units.count, 0)
|
|
27600
28052
|
})
|
|
27601
28053
|
};
|
|
27602
28054
|
}
|
|
27603
28055
|
Object.assign(K, { createBreachView });
|
|
27604
28056
|
}
|
|
28057
|
+
var FOLLOW_FLUSH_FALLBACK_MS = 100;
|
|
27605
28058
|
function installDestructionWorld(K, shared) {
|
|
27606
28059
|
const { THREE, cfg, invalidateShadow, invalidate, rngString, FRAGMENT_GEO, fragmentMaterial, createBreachView } = K;
|
|
27607
28060
|
function createHouseDestruction(root, house, structure, parts, colliders, water) {
|
|
@@ -27610,7 +28063,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27610
28063
|
const R = cfg.destructionRenderer || null, listeners = /* @__PURE__ */ new Set();
|
|
27611
28064
|
D.renderer = R;
|
|
27612
28065
|
D.spec = null;
|
|
27613
|
-
const breach = createBreachView({ house, structure, spec: () => D.spec });
|
|
28066
|
+
const breach = createBreachView({ house, structure, spec: () => D.spec, deferPaint: true, ...cfg.breachPaintBudgetMs !== void 0 ? { paintBudgetMs: cfg.breachPaintBudgetMs } : {} });
|
|
27614
28067
|
D.breach = breach;
|
|
27615
28068
|
D.onEvent = (fn) => {
|
|
27616
28069
|
listeners.add(fn);
|
|
@@ -27795,6 +28248,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27795
28248
|
case "chip":
|
|
27796
28249
|
breach.chip(e);
|
|
27797
28250
|
break;
|
|
28251
|
+
case "spall":
|
|
28252
|
+
breach.spall(e);
|
|
28253
|
+
break;
|
|
27798
28254
|
case "collapse":
|
|
27799
28255
|
for (const id of e.parts) {
|
|
27800
28256
|
breach.drop(id);
|
|
@@ -27836,7 +28292,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27836
28292
|
D.specOptions = specOptions || null;
|
|
27837
28293
|
D.spec = structure.spec(specOptions);
|
|
27838
28294
|
D.id = D.world.add(D.spec, { id: "house3d:" + ++shared.destructionSeq, position: [p.x, p.y, p.z], rotation: [q.x, q.y, q.z, q.w] });
|
|
27839
|
-
|
|
28295
|
+
breach.claim();
|
|
28296
|
+
if (R) R.bind(D.id, house, { parts: rangesOf(), onFrame: () => breach.flush() });
|
|
27840
28297
|
off = D.world.on("*", handleEvent);
|
|
27841
28298
|
D.active = true;
|
|
27842
28299
|
return D;
|
|
@@ -27851,6 +28308,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27851
28308
|
};
|
|
27852
28309
|
D.follow = (world, id, spec) => {
|
|
27853
28310
|
D.spec = spec;
|
|
28311
|
+
breach.claim();
|
|
28312
|
+
let fallback = null;
|
|
27854
28313
|
const offFollow = world.on("*", (e) => {
|
|
27855
28314
|
if (e.object !== id) return;
|
|
27856
28315
|
switch (e.type) {
|
|
@@ -27860,6 +28319,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27860
28319
|
case "chip":
|
|
27861
28320
|
breach.chip(e);
|
|
27862
28321
|
break;
|
|
28322
|
+
case "spall":
|
|
28323
|
+
breach.spall(e);
|
|
28324
|
+
break;
|
|
27863
28325
|
case "fracture":
|
|
27864
28326
|
breach.drop(e.part);
|
|
27865
28327
|
break;
|
|
@@ -27869,10 +28331,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27869
28331
|
default:
|
|
27870
28332
|
break;
|
|
27871
28333
|
}
|
|
28334
|
+
if (!fallback && typeof setTimeout === "function") fallback = setTimeout(() => {
|
|
28335
|
+
fallback = null;
|
|
28336
|
+
breach.flush(Infinity);
|
|
28337
|
+
}, FOLLOW_FLUSH_FALLBACK_MS);
|
|
27872
28338
|
});
|
|
27873
28339
|
return { restore: () => breach.restore(), dispose: () => {
|
|
27874
28340
|
offFollow();
|
|
27875
28341
|
breach.restore();
|
|
28342
|
+
}, update: () => {
|
|
28343
|
+
breach.flush();
|
|
27876
28344
|
} };
|
|
27877
28345
|
};
|
|
27878
28346
|
D.piece = (id) => D.active ? D.world.part(D.id, id) : null;
|
|
@@ -27905,6 +28373,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27905
28373
|
return r;
|
|
27906
28374
|
};
|
|
27907
28375
|
D.animate = (dt) => {
|
|
28376
|
+
breach.flush();
|
|
27908
28377
|
if (R) return;
|
|
27909
28378
|
let moved = false;
|
|
27910
28379
|
for (const [id, list2] of bodies) {
|
|
@@ -29400,7 +29869,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
29400
29869
|
const provider = options.provider ?? createFindsProvider(catalog);
|
|
29401
29870
|
const items = catalog.list();
|
|
29402
29871
|
const categories = [...ITEM_CATEGORIES.map((c) => c.id), ...[...catalog.categories.keys()].filter((id) => !ITEM_CATEGORIES.some((c) => c.id === id))].filter((id) => items.some((item) => item.category === id));
|
|
29403
|
-
const
|
|
29872
|
+
const openerOf = (item) => {
|
|
29873
|
+
const id = item.seal?.openWith?.find((tool2) => catalog.has(tool2));
|
|
29874
|
+
return id === void 0 ? void 0 : catalog.get(id);
|
|
29875
|
+
};
|
|
29876
|
+
const laid = items.flatMap((item) => {
|
|
29877
|
+
const opener = openerOf(item);
|
|
29878
|
+
return opener ? [item, { id: opener.id, category: item.category, size: opener.size }] : [item];
|
|
29879
|
+
});
|
|
29880
|
+
const shelves = shelvesLayout(laid, categories);
|
|
29404
29881
|
const showcase = listItemModels().map((id) => ({ id, size: sizeOfModel(id), def: catalog.has(id) ? catalog.get(id) : void 0 }));
|
|
29405
29882
|
const state = {
|
|
29406
29883
|
category: CATEGORY_ALL,
|
|
@@ -29451,12 +29928,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
29451
29928
|
const contents = itemContents(subject);
|
|
29452
29929
|
return contents ? { fluid: { container: contents.container, part: SHELL_PART_ID } } : {};
|
|
29453
29930
|
};
|
|
29931
|
+
const sealOf = (def, fluid) => {
|
|
29932
|
+
const model = def.model;
|
|
29933
|
+
if (!def.seal || model === void 0 || !itemOpening(model)) return fluid;
|
|
29934
|
+
const openWith = def.seal.openWith ? [...def.seal.openWith, ...def.seal.openWith.map((id) => `${ITEM_PREFIX}${id}`)] : void 0;
|
|
29935
|
+
const seal = {
|
|
29936
|
+
...def.seal,
|
|
29937
|
+
...openWith ? { openWith } : {},
|
|
29938
|
+
open: (root) => void (latestTHREE && openItem(latestTHREE, root, model)),
|
|
29939
|
+
contents: (root, share) => setOpeningContents(root, share),
|
|
29940
|
+
// a screw cap is turned off by the other hand before the first bite or sip
|
|
29941
|
+
...itemOpening(model)?.style === "screw_cap" ? { openUse: "unscrew", turn: (root, state2) => void turnItem(root, model, state2) } : {}
|
|
29942
|
+
};
|
|
29943
|
+
const eaten = def.category === "food" && fluid.fluid ? { fluid: { ...fluid.fluid, uses: ["eat", "pour_out"] } } : fluid;
|
|
29944
|
+
return { ...eaten, seal };
|
|
29945
|
+
};
|
|
29454
29946
|
const itemOf = (def) => ({
|
|
29455
29947
|
id: `${ITEM_PREFIX}${def.id}`,
|
|
29456
29948
|
name: nameOf(def),
|
|
29457
29949
|
movable: true,
|
|
29458
29950
|
thumbnail: itemThumbnailSvg(drawable(def)),
|
|
29459
|
-
...fluidOf({ ...def.model === void 0 ? {} : { model: def.model }, size: def.size, weight: def.weight }),
|
|
29951
|
+
...sealOf(def, fluidOf({ ...def.model === void 0 ? {} : { model: def.model }, size: def.size, weight: def.weight })),
|
|
29460
29952
|
build: (THREE) => {
|
|
29461
29953
|
const T2 = THREE;
|
|
29462
29954
|
latestTHREE = T2;
|
|
@@ -29820,7 +30312,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
29820
30312
|
id: "modeled",
|
|
29821
30313
|
name: text.metrics.modeled,
|
|
29822
30314
|
read: () => {
|
|
29823
|
-
const shown = visibleEntries().filter((entry2) => entry2.group === "shelf");
|
|
30315
|
+
const shown = [...new Map(visibleEntries().filter((entry2) => entry2.group === "shelf").map((entry2) => [entry2.itemId, entry2])).values()];
|
|
29824
30316
|
return `${shown.filter((entry2) => entry2.kind === "model").length}/${shown.length}`;
|
|
29825
30317
|
}
|
|
29826
30318
|
},
|