@adatechnology/game-items-three 0.4.1 → 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 +666 -64
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +325 -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
|
});
|
|
@@ -2812,6 +2819,55 @@ var GameItemsThree = (() => {
|
|
|
2812
2819
|
ends: 22e-5
|
|
2813
2820
|
}
|
|
2814
2821
|
}
|
|
2822
|
+
},
|
|
2823
|
+
plastic_bottle: {
|
|
2824
|
+
models: [
|
|
2825
|
+
"water_bottle",
|
|
2826
|
+
"water_jug_5l",
|
|
2827
|
+
"dirty_water",
|
|
2828
|
+
"sports_drink",
|
|
2829
|
+
"coconut_water",
|
|
2830
|
+
"cooking_oil",
|
|
2831
|
+
"alcohol_70",
|
|
2832
|
+
"fuel_jerrycan"
|
|
2833
|
+
],
|
|
2834
|
+
rule: {
|
|
2835
|
+
hollow: true,
|
|
2836
|
+
welded: true,
|
|
2837
|
+
skinTags: [
|
|
2838
|
+
"paper",
|
|
2839
|
+
"food"
|
|
2840
|
+
],
|
|
2841
|
+
materials: {
|
|
2842
|
+
glass: "plastic"
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
},
|
|
2846
|
+
glass_bottle: {
|
|
2847
|
+
models: [
|
|
2848
|
+
"cachaca",
|
|
2849
|
+
"communion_wine",
|
|
2850
|
+
"honey_jar",
|
|
2851
|
+
"home_preserves"
|
|
2852
|
+
],
|
|
2853
|
+
rule: {
|
|
2854
|
+
hollow: true,
|
|
2855
|
+
welded: true,
|
|
2856
|
+
skinTags: [
|
|
2857
|
+
"paper",
|
|
2858
|
+
"food"
|
|
2859
|
+
]
|
|
2860
|
+
}
|
|
2861
|
+
},
|
|
2862
|
+
drink_carton: {
|
|
2863
|
+
models: [
|
|
2864
|
+
"juice_box",
|
|
2865
|
+
"milk_carton"
|
|
2866
|
+
],
|
|
2867
|
+
rule: {
|
|
2868
|
+
hollow: true,
|
|
2869
|
+
welded: true
|
|
2870
|
+
}
|
|
2815
2871
|
}
|
|
2816
2872
|
},
|
|
2817
2873
|
contents: {
|
|
@@ -2827,6 +2883,67 @@ var GameItemsThree = (() => {
|
|
|
2827
2883
|
liquid: "soda",
|
|
2828
2884
|
fill: 0.95
|
|
2829
2885
|
},
|
|
2886
|
+
water_bottle: {
|
|
2887
|
+
liquid: "water",
|
|
2888
|
+
fill: 0.95
|
|
2889
|
+
},
|
|
2890
|
+
water_jug_5l: {
|
|
2891
|
+
liquid: "water",
|
|
2892
|
+
fill: 0.95,
|
|
2893
|
+
box: true
|
|
2894
|
+
},
|
|
2895
|
+
dirty_water: {
|
|
2896
|
+
liquid: "water",
|
|
2897
|
+
fill: 0.9
|
|
2898
|
+
},
|
|
2899
|
+
sports_drink: {
|
|
2900
|
+
liquid: "juice",
|
|
2901
|
+
fill: 0.95
|
|
2902
|
+
},
|
|
2903
|
+
coconut_water: {
|
|
2904
|
+
liquid: "water",
|
|
2905
|
+
fill: 0.9,
|
|
2906
|
+
box: true
|
|
2907
|
+
},
|
|
2908
|
+
juice_box: {
|
|
2909
|
+
liquid: "juice",
|
|
2910
|
+
fill: 0.95,
|
|
2911
|
+
box: true
|
|
2912
|
+
},
|
|
2913
|
+
milk_carton: {
|
|
2914
|
+
liquid: "milk",
|
|
2915
|
+
fill: 0.95,
|
|
2916
|
+
box: true
|
|
2917
|
+
},
|
|
2918
|
+
cachaca: {
|
|
2919
|
+
liquid: "spirits",
|
|
2920
|
+
fill: 0.9
|
|
2921
|
+
},
|
|
2922
|
+
communion_wine: {
|
|
2923
|
+
liquid: "wine",
|
|
2924
|
+
fill: 0.9
|
|
2925
|
+
},
|
|
2926
|
+
cooking_oil: {
|
|
2927
|
+
liquid: "cooking_oil",
|
|
2928
|
+
fill: 0.9
|
|
2929
|
+
},
|
|
2930
|
+
alcohol_70: {
|
|
2931
|
+
liquid: "spirits",
|
|
2932
|
+
fill: 0.9
|
|
2933
|
+
},
|
|
2934
|
+
honey_jar: {
|
|
2935
|
+
liquid: "honey",
|
|
2936
|
+
fill: 0.85
|
|
2937
|
+
},
|
|
2938
|
+
home_preserves: {
|
|
2939
|
+
liquid: "water",
|
|
2940
|
+
fill: 0.5
|
|
2941
|
+
},
|
|
2942
|
+
fuel_jerrycan: {
|
|
2943
|
+
liquid: "fuel",
|
|
2944
|
+
fill: 0.85,
|
|
2945
|
+
box: true
|
|
2946
|
+
},
|
|
2830
2947
|
canned_beans: {
|
|
2831
2948
|
liquid: "juice",
|
|
2832
2949
|
fill: 0.4
|
|
@@ -2848,6 +2965,12 @@ var GameItemsThree = (() => {
|
|
|
2848
2965
|
liquid: "honey",
|
|
2849
2966
|
fill: 0.9
|
|
2850
2967
|
}
|
|
2968
|
+
},
|
|
2969
|
+
shapes: {
|
|
2970
|
+
box: {
|
|
2971
|
+
hollow: true,
|
|
2972
|
+
welded: true
|
|
2973
|
+
}
|
|
2851
2974
|
}
|
|
2852
2975
|
};
|
|
2853
2976
|
|
|
@@ -2863,6 +2986,8 @@ var GameItemsThree = (() => {
|
|
|
2863
2986
|
};
|
|
2864
2987
|
var FAMILIES = destruction_default.families;
|
|
2865
2988
|
var CONTENTS = destruction_default.contents;
|
|
2989
|
+
var SHAPES2 = destruction_default.shapes;
|
|
2990
|
+
var shapeDestruction = (shapeId) => SHAPES2[shapeId];
|
|
2866
2991
|
var FAMILY_OF_MODEL = new Map(Object.entries(FAMILIES).flatMap(([family, entry2]) => entry2.models.map((model) => [model, family])));
|
|
2867
2992
|
var itemFamily = (modelId) => FAMILY_OF_MODEL.get(modelId);
|
|
2868
2993
|
var itemDestruction = (modelId) => {
|
|
@@ -2978,9 +3103,9 @@ var GameItemsThree = (() => {
|
|
|
2978
3103
|
root.userData.tessellationTriangles = added;
|
|
2979
3104
|
}
|
|
2980
3105
|
function tagForDestruction(root, options = {}) {
|
|
2981
|
-
const { modelId, weight, size, three } = options;
|
|
3106
|
+
const { modelId, shapeId, weight, size, powder, three } = options;
|
|
2982
3107
|
if (weight !== void 0 && weight > 0) root.userData.destructionMass = weight;
|
|
2983
|
-
const rule = modelId
|
|
3108
|
+
const rule = modelId !== void 0 ? itemDestruction(modelId) : shapeId !== void 0 ? shapeDestruction(shapeId) : void 0;
|
|
2984
3109
|
if (rule?.tessellate) tessellateShapes(root, rule.tessellate, three);
|
|
2985
3110
|
const solid = [];
|
|
2986
3111
|
root.traverse((object2) => {
|
|
@@ -2995,6 +3120,7 @@ var GameItemsThree = (() => {
|
|
|
2995
3120
|
return;
|
|
2996
3121
|
}
|
|
2997
3122
|
if (rule?.hollow) mesh2.userData.hollow = true;
|
|
3123
|
+
if (powder) mesh2.userData.destructionPowder = { color: powder.color };
|
|
2998
3124
|
solid.push(mesh2);
|
|
2999
3125
|
});
|
|
3000
3126
|
if (!rule?.welded || solid.length < 2) return;
|
|
@@ -5477,8 +5603,168 @@ var GameItemsThree = (() => {
|
|
|
5477
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]));
|
|
5478
5604
|
};
|
|
5479
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
|
+
|
|
5480
5766
|
// src/models/water-bottle.ts
|
|
5481
|
-
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]));
|
|
5482
5768
|
|
|
5483
5769
|
// src/models/rice.ts
|
|
5484
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]));
|
|
@@ -5487,7 +5773,7 @@ var GameItemsThree = (() => {
|
|
|
5487
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]));
|
|
5488
5774
|
|
|
5489
5775
|
// src/models/cachaca.ts
|
|
5490
|
-
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]));
|
|
5491
5777
|
|
|
5492
5778
|
// src/models/first-aid-kit.ts
|
|
5493
5779
|
var firstAidKit = ({ mesh: mesh2, bx, torus: torus2, G, plastic }) => {
|
|
@@ -5703,7 +5989,7 @@ var GameItemsThree = (() => {
|
|
|
5703
5989
|
mesh2(cyl(0.41, 0.41, 0.86, 12), food(9314106), [0, -0.2, 0]),
|
|
5704
5990
|
mesh2(cyl(0.34, 0.445, 0.12, 12), glass(13625070, 0.5), [0, 0.39, 0]),
|
|
5705
5991
|
mesh2(cyl(0.34, 0.34, 0.05, 12), glass(13625070, 0.5), [0, 0.475, 0]),
|
|
5706
|
-
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])),
|
|
5707
5993
|
mesh2(cyl(0.45, 0.45, 0.4, 12), paper2(15327172), [0, -0.2, 0]),
|
|
5708
5994
|
mesh2(cyl(0.452, 0.452, 0.08, 12), cloth2(10172986), [0, -0.2, 0])
|
|
5709
5995
|
);
|
|
@@ -5908,7 +6194,7 @@ var GameItemsThree = (() => {
|
|
|
5908
6194
|
mesh2(cyl(0.395, 0.395, 0.68, 12), glass(14925936, 0.55), [0, -0.16, 0]),
|
|
5909
6195
|
mesh2(cyl(0.37, 0.37, 0.6, 12), food(14260764), [0, -0.2, 0]),
|
|
5910
6196
|
mesh2(cyl(0.3, 0.395, 0.14, 12), glass(14925936, 0.55), [0, 0.25, 0]),
|
|
5911
|
-
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])),
|
|
5912
6198
|
mesh2(cyl(0.4, 0.4, 0.34, 12), paper2(15853512), [0, -0.2, 0]),
|
|
5913
6199
|
mesh2(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.1, 0]),
|
|
5914
6200
|
mesh2(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.3, 0])
|
|
@@ -6012,7 +6298,7 @@ var GameItemsThree = (() => {
|
|
|
6012
6298
|
mesh2(cyl(0.345, 0.345, 1.7, 12), glass(14923824, 0.65), [0, -0.45, 0]),
|
|
6013
6299
|
mesh2(cyl(0.12, 0.345, 0.5, 12), glass(14923824, 0.65), [0, 0.65, 0]),
|
|
6014
6300
|
mesh2(cyl(0.12, 0.12, 0.25, 12), glass(14923824, 0.65), [0, 1.025, 0]),
|
|
6015
|
-
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])),
|
|
6016
6302
|
mesh2(cyl(0.35, 0.35, 0.8, 12), paper2(15853512), [0, -0.5, 0]),
|
|
6017
6303
|
mesh2(cyl(0.352, 0.352, 0.2, 12), paper2(4160042), [0, -0.5, 0])
|
|
6018
6304
|
);
|
|
@@ -6050,7 +6336,7 @@ var GameItemsThree = (() => {
|
|
|
6050
6336
|
const body = mesh2(bx(1.5, 2.3, 1.5), glass(10473710, 0.75), [0, -0.5, 0]);
|
|
6051
6337
|
const shoulder = mesh2(bx(1.2, 0.45, 1.2), glass(10473710, 0.75), [0, 0.875, 0]);
|
|
6052
6338
|
const neck = mesh2(cyl(0.34, 0.34, 0.3, 12), dark, [0, 1.25, 0]);
|
|
6053
|
-
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]));
|
|
6054
6340
|
const grip = mesh2(bx(0.16, 0.9, 0.3), dark, [1.07, 0.6, 0]);
|
|
6055
6341
|
const upperBar = mesh2(bx(0.45, 0.16, 0.3), dark, [0.925, 0.97, 0]);
|
|
6056
6342
|
const lowerBar = mesh2(bx(0.45, 0.16, 0.3), dark, [0.925, 0.23, 0]);
|
|
@@ -6065,7 +6351,7 @@ var GameItemsThree = (() => {
|
|
|
6065
6351
|
const label = mesh2(cyl(0.332, 0.332, 0.7, 12), paper2(1448738), [0, -0.12, 0]);
|
|
6066
6352
|
const stripe = mesh2(cyl(0.332, 0.332, 0.1, 12), paper2(15753232), [0, 0.28, 0]);
|
|
6067
6353
|
const collar = mesh2(cyl(0.2, 0.2, 0.04, 12), plastic(15263976), [0, 0.705, 0]);
|
|
6068
|
-
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]));
|
|
6069
6355
|
return G(bottle2, label, stripe, collar, cap);
|
|
6070
6356
|
};
|
|
6071
6357
|
|
|
@@ -6112,7 +6398,7 @@ var GameItemsThree = (() => {
|
|
|
6112
6398
|
const goldBand = mesh2(cyl(0.382, 0.382, 0.05, 12), metal(11896880), [0, 0.075, 0]);
|
|
6113
6399
|
const crossUpright = mesh2(bx(0.06, 0.26, 0.02), paper2(8003371), [0, -0.35, 0.375]);
|
|
6114
6400
|
const crossArm = mesh2(bx(0.18, 0.06, 0.02), paper2(8003371), [0, -0.3, 0.375]);
|
|
6115
|
-
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]));
|
|
6116
6402
|
return G(bottle2, label, goldBand, crossUpright, crossArm, foil);
|
|
6117
6403
|
};
|
|
6118
6404
|
|
|
@@ -6123,7 +6409,7 @@ var GameItemsThree = (() => {
|
|
|
6123
6409
|
const nut = mesh2(cyl(0.11, 0.11, 0.03, 10), paper2(7031339), [0, -0.12, 0.285], [Math.PI / 2, 0, 0]);
|
|
6124
6410
|
const leafLeft = mesh2(bx(0.2, 0.05, 0.01), paper2(15135448), [-0.12, 0.08, 0.292], [0, 0, 0.5]);
|
|
6125
6411
|
const leafRight = mesh2(bx(0.2, 0.05, 0.01), paper2(15135448), [0.12, 0.08, 0.292], [0, 0, -0.5]);
|
|
6126
|
-
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]));
|
|
6127
6413
|
return G(bottle2, label, nut, leafLeft, leafRight, cap);
|
|
6128
6414
|
};
|
|
6129
6415
|
|
|
@@ -6133,7 +6419,7 @@ var GameItemsThree = (() => {
|
|
|
6133
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));
|
|
6134
6420
|
const sediment = mesh2(cyl(0.31, 0.27, 0.12, 8), food(4865314), [0, -0.82, 0]);
|
|
6135
6421
|
const label = mesh2(cyl(0.332, 0.332, 0.22, 12), paper2(13617588), [0, 0.38, 0]);
|
|
6136
|
-
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]));
|
|
6137
6423
|
return G(bottle2, murk, sediment, label, cap);
|
|
6138
6424
|
};
|
|
6139
6425
|
|
|
@@ -9122,7 +9408,7 @@ var GameItemsThree = (() => {
|
|
|
9122
9408
|
const materials = kit.materials.slice(first);
|
|
9123
9409
|
if (options.wear) setWear(materials, options.wear);
|
|
9124
9410
|
root.userData.wear = options.wear ?? 0;
|
|
9125
|
-
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 });
|
|
9126
9412
|
markKitOrigin(root, { owner: "game-items-three", kind: "item", id: item.id, ...kind === "shape" ? { standIn: true } : {} });
|
|
9127
9413
|
return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
|
|
9128
9414
|
}
|
|
@@ -15037,6 +15323,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15037
15323
|
var FLAMMABLE_FLUIDS = ["gas", "fuel"];
|
|
15038
15324
|
var VENT_DIAMETER = 8e-3;
|
|
15039
15325
|
var FLAME_LIGHTS_LEAK_C = 470;
|
|
15326
|
+
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
15040
15327
|
var BREACH_RADIUS = 0.55;
|
|
15041
15328
|
var BREACH_BLAST_FACTOR = 6;
|
|
15042
15329
|
var BREACH_AREA = 0.25;
|
|
@@ -15052,6 +15339,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15052
15339
|
var BREACH_GRID_CAP = 4096;
|
|
15053
15340
|
var CHIP_ENERGY = 200;
|
|
15054
15341
|
var CHIP_SPLASH = 0.5;
|
|
15342
|
+
var CRATER_SPENT = 0.6;
|
|
15055
15343
|
var BAR_BOW = 0.08;
|
|
15056
15344
|
var BULLET_JOINT_SPAN = 0.3;
|
|
15057
15345
|
var CUT_SLICE = 1;
|
|
@@ -15072,12 +15360,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15072
15360
|
const cells = p.st.chipped;
|
|
15073
15361
|
if (!cells?.length) return 0;
|
|
15074
15362
|
const [u, v] = planeAxes(p.thin);
|
|
15075
|
-
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;
|
|
15076
15367
|
const across2 = along === u ? v : u;
|
|
15077
15368
|
let worst = 0;
|
|
15078
15369
|
for (const c of cells) {
|
|
15079
|
-
|
|
15080
|
-
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
|
+
}
|
|
15081
15379
|
worst = Math.max(worst, gone / Math.max(p.dims[across2], 1e-6));
|
|
15082
15380
|
}
|
|
15083
15381
|
return Math.min(1, worst);
|
|
@@ -15373,7 +15671,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15373
15671
|
if (k < 0 || j.spec.kind !== "tie") return k;
|
|
15374
15672
|
return partXf(o, o.parts[k]).c[1] > partXf(o, o.parts[r]).c[1] + 1e-6 ? k : -1;
|
|
15375
15673
|
};
|
|
15674
|
+
let poseVersion = 0;
|
|
15675
|
+
const contactMemo = /* @__PURE__ */ new WeakMap();
|
|
15376
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) => {
|
|
15377
15686
|
const A = aabbOf(p.spec.shape, p.pos, p.q), B = aabbOf(support.spec.shape, support.pos, support.q);
|
|
15378
15687
|
const lo = [0, 0, 0], hi = [0, 0, 0];
|
|
15379
15688
|
for (let k = 0; k < 3; k++) {
|
|
@@ -16168,7 +16477,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16168
16477
|
p.st.holes.push(hole);
|
|
16169
16478
|
if (p.st.holes.length > maxHoles) p.st.holes.shift();
|
|
16170
16479
|
markDamaged(p);
|
|
16171
|
-
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 } } : {} });
|
|
16172
16481
|
setStage(o, p, 1, {}, src);
|
|
16173
16482
|
return hole;
|
|
16174
16483
|
};
|
|
@@ -16185,6 +16494,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16185
16494
|
const detachEvents = (o, opened, by, src) => {
|
|
16186
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]) });
|
|
16187
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
|
+
};
|
|
16188
16513
|
const checkDestroyed = (o, src) => {
|
|
16189
16514
|
if (!o.st.destroyed && o.parts.length > 0 && o.parts.every(isLost)) {
|
|
16190
16515
|
o.st.destroyed = true;
|
|
@@ -16285,7 +16610,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16285
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 }));
|
|
16286
16611
|
const unitsOf2 = (p) => p.spec.units ?? { size: [p.mat.fragmentSize?.[1] ?? 0.2, p.mat.fragmentSize?.[1] ?? 0.2, p.thickness], bond: "stack" };
|
|
16287
16612
|
const FRAGMENT_SHAPE = { shatter: "shard", splinter: "splinter", tear: "scrap" };
|
|
16288
|
-
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag) => {
|
|
16613
|
+
const cellFragments = (o, p, cells, hitW, dir, speed, mass, tag, inner = false) => {
|
|
16289
16614
|
const { c, q } = partXf(o, p);
|
|
16290
16615
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
16291
16616
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -16309,7 +16634,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16309
16634
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd(), dv[2] * k],
|
|
16310
16635
|
angularVelocity: [(rnd() - 0.5) * 8, (rnd() - 0.5) * 8, (rnd() - 0.5) * 8],
|
|
16311
16636
|
mass: mass * faceArea(cell) / total,
|
|
16312
|
-
...marks.length > 0 ? { marks } : {}
|
|
16637
|
+
...marks.length > 0 ? { marks } : {},
|
|
16638
|
+
...inner ? { inner } : {}
|
|
16313
16639
|
};
|
|
16314
16640
|
});
|
|
16315
16641
|
};
|
|
@@ -16350,7 +16676,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16350
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);
|
|
16351
16677
|
};
|
|
16352
16678
|
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
16353
|
-
var _a, _b;
|
|
16679
|
+
var _a, _b, _c;
|
|
16354
16680
|
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
16355
16681
|
const rule = p.mat.hits?.[kind];
|
|
16356
16682
|
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
@@ -16360,7 +16686,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16360
16686
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
16361
16687
|
const grid = gridOf(p);
|
|
16362
16688
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
16689
|
+
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
16363
16690
|
const out = [];
|
|
16691
|
+
const shot = kind === "ballistic";
|
|
16364
16692
|
for (let i = 0; i < grid.length; i++) {
|
|
16365
16693
|
const cell = grid[i];
|
|
16366
16694
|
if (worn[i] === -1) continue;
|
|
@@ -16373,18 +16701,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16373
16701
|
worn[i] = total;
|
|
16374
16702
|
continue;
|
|
16375
16703
|
}
|
|
16376
|
-
|
|
16377
|
-
|
|
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 });
|
|
16378
16726
|
}
|
|
16379
16727
|
if (out.length === 0) return;
|
|
16380
16728
|
const { c, q } = partXf(o, p);
|
|
16381
|
-
const chipped = (
|
|
16729
|
+
const chipped = (_c = p.st).chipped ?? (_c.chipped = []);
|
|
16382
16730
|
const holes = localHolesOf(p);
|
|
16383
16731
|
for (const cell of out) {
|
|
16384
16732
|
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
16385
16733
|
const center = toWorld(c, q, cell.center);
|
|
16386
16734
|
const speed = clamp2(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
16387
|
-
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);
|
|
16388
16737
|
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
16389
16738
|
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
16390
16739
|
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
@@ -16401,6 +16750,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16401
16750
|
collapseCheck(o, src);
|
|
16402
16751
|
}
|
|
16403
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
|
+
};
|
|
16404
16791
|
const exposeRebar = (o, p, src) => {
|
|
16405
16792
|
const r = p.spec.reinforcement;
|
|
16406
16793
|
if (!r) return;
|
|
@@ -16688,7 +17075,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16688
17075
|
});
|
|
16689
17076
|
}
|
|
16690
17077
|
const patch = [scale * 4, scale * 4, scale * 4];
|
|
16691
|
-
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 });
|
|
16692
17079
|
};
|
|
16693
17080
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round3 = {}) => {
|
|
16694
17081
|
const mat = p.mat;
|
|
@@ -16747,12 +17134,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16747
17134
|
} else if (mat.ductility >= 0.5) {
|
|
16748
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);
|
|
16749
17136
|
} else if (brittle) {
|
|
16750
|
-
addCrack(o, p, entry2, n, crackLen, src);
|
|
17137
|
+
if (!p.spec.units) addCrack(o, p, entry2, n, crackLen, src);
|
|
16751
17138
|
if (mat.fracture !== "shatter") addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
16752
17139
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
16753
17140
|
debris(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
16754
17141
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
16755
|
-
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;
|
|
16756
17144
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
16757
17145
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
16758
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);
|
|
@@ -16837,10 +17225,25 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16837
17225
|
if (dist <= BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3)) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
16838
17226
|
return reach;
|
|
16839
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
|
+
};
|
|
16840
17243
|
const blastObject = (o, point, energy, radius, src) => {
|
|
16841
17244
|
const start = cur.length;
|
|
16842
17245
|
const res = { object: o.id, damage: 0, penetrated: false, exitEnergy: 0, events: [] };
|
|
16843
|
-
for (const p of o
|
|
17246
|
+
for (const p of blastCandidates(o, point, radius)) blastPart(o, p, point, energy, radius, src, res);
|
|
16844
17247
|
res.events = cur.slice(start);
|
|
16845
17248
|
return res;
|
|
16846
17249
|
};
|
|
@@ -16964,7 +17367,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16964
17367
|
let order = 0;
|
|
16965
17368
|
for (const o of inRange) {
|
|
16966
17369
|
if (only && only !== o) continue;
|
|
16967
|
-
for (const p of o
|
|
17370
|
+
for (const p of blastCandidates(o, point, radius)) {
|
|
16968
17371
|
if (isLost(p)) continue;
|
|
16969
17372
|
const dist = distToPart(o, p, point);
|
|
16970
17373
|
if (dist < radius) items.push({ dist, order: order++, o, p });
|
|
@@ -17511,6 +17914,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17511
17914
|
const p = getPart(o, partId);
|
|
17512
17915
|
p.pos = [position[0], position[1], position[2]];
|
|
17513
17916
|
p.q = q;
|
|
17917
|
+
poseVersion++;
|
|
17514
17918
|
updateBound(o);
|
|
17515
17919
|
},
|
|
17516
17920
|
remove(id) {
|
|
@@ -17529,6 +17933,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17529
17933
|
const o = getObj(id);
|
|
17530
17934
|
o.base = { pos: [position[0], position[1], position[2]], rot: [rotation[0], rotation[1], rotation[2], rotation[3]] };
|
|
17531
17935
|
applySoilPose(o);
|
|
17936
|
+
slideOffResting(o, rotation);
|
|
17532
17937
|
},
|
|
17533
17938
|
splitGroup(id, group, newId) {
|
|
17534
17939
|
if (impls.has(id)) throw new Error(`object "${id}" is a registered DestructibleImpl and cannot be split; its module owns its pieces`);
|
|
@@ -17552,7 +17957,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17552
17957
|
if (o.spec.name !== void 0) s.name = o.spec.name;
|
|
17553
17958
|
if (o.spec.tags) s.tags = [...o.spec.tags];
|
|
17554
17959
|
if (o.spec.soil !== void 0) s.soil = o.spec.soil;
|
|
17555
|
-
return
|
|
17960
|
+
return s;
|
|
17556
17961
|
};
|
|
17557
17962
|
const inNew = (pid) => moved.has(pid);
|
|
17558
17963
|
const inOld = (pid) => !moved.has(pid);
|
|
@@ -17579,7 +17984,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17579
17984
|
const p = getPart(o, partId);
|
|
17580
17985
|
return whole(() => doHit(o, h, p));
|
|
17581
17986
|
},
|
|
17987
|
+
blastProbe() {
|
|
17988
|
+
return { ...blastProbe };
|
|
17989
|
+
},
|
|
17582
17990
|
explode(point, energy, radius, source, blast) {
|
|
17991
|
+
blastProbe.partsTested = 0;
|
|
17992
|
+
blastProbe.partsNear = 0;
|
|
17583
17993
|
return withCollapses(run(() => blastWorld(point, energy, radius, source, blast)));
|
|
17584
17994
|
},
|
|
17585
17995
|
raycast(origin, direction, maxDistance = Infinity) {
|
|
@@ -17589,7 +17999,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17589
17999
|
sweepBox(box4, direction, distance2, options2 = {}) {
|
|
17590
18000
|
const d = vec.normalize(direction);
|
|
17591
18001
|
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
17592
|
-
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;
|
|
17593
18004
|
let best = null;
|
|
17594
18005
|
for (const origin of sweepOrigins(box4, d, options2.spacing ?? 0.2)) {
|
|
17595
18006
|
const r = raycastInternal(origin, d, distance2 + SWEEP_BACK, ignore);
|
|
@@ -27192,6 +27603,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27192
27603
|
function collapseRange(mesh2, v0, v1) {
|
|
27193
27604
|
const pos = mesh2.geometry.attributes.position;
|
|
27194
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]);
|
|
27195
27610
|
const saved = a.slice(v0 * 3, v1 * 3);
|
|
27196
27611
|
const x = a[v0 * 3], y = a[v0 * 3 + 1], z = a[v0 * 3 + 2];
|
|
27197
27612
|
for (let k = v0; k < v1; k++) {
|
|
@@ -27206,6 +27621,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27206
27621
|
const pos = mesh2.geometry.attributes.position;
|
|
27207
27622
|
pos.array.set(saved, v0 * 3);
|
|
27208
27623
|
pos.needsUpdate = true;
|
|
27624
|
+
const list2 = mesh2.userData.collapsedRanges;
|
|
27625
|
+
if (list2) mesh2.userData.collapsedRanges = list2.filter((r) => r[0] !== v0);
|
|
27209
27626
|
}
|
|
27210
27627
|
function noise2(x, y, cell) {
|
|
27211
27628
|
const h = (i2, j2) => {
|
|
@@ -27220,13 +27637,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27220
27637
|
var SPALL_MIN = 0.025;
|
|
27221
27638
|
var SPALL_RAGGED = 0.045;
|
|
27222
27639
|
var COVER2 = 0.015;
|
|
27640
|
+
var MAX_FLAKES = 64;
|
|
27223
27641
|
function installDestructionBreach(K, _shared) {
|
|
27224
27642
|
const { THREE, fragmentMaterial, invalidate, invalidateShadow } = K;
|
|
27225
27643
|
function createBreachView(opts) {
|
|
27226
27644
|
const { house, structure } = opts;
|
|
27227
27645
|
const views = /* @__PURE__ */ new Map();
|
|
27646
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
27647
|
+
const paintBudgetMs = opts.paintBudgetMs ?? Infinity;
|
|
27648
|
+
const dropped = [];
|
|
27228
27649
|
let lod = 0;
|
|
27229
|
-
const unitMaterial = new THREE.MeshLambertMaterial({ color: 16777215 });
|
|
27230
27650
|
const unitGeometry = new THREE.BoxGeometry(1, 1, 1);
|
|
27231
27651
|
const tmp = { m: new THREE.Matrix4(), q: new THREE.Quaternion(), p: new THREE.Vector3(), s: new THREE.Vector3(), c: new THREE.Color() };
|
|
27232
27652
|
const local = (p) => {
|
|
@@ -27295,6 +27715,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27295
27715
|
const mesh2 = new THREE.Mesh(g, material);
|
|
27296
27716
|
mesh2.name = "breach-shell";
|
|
27297
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
|
+
};
|
|
27298
27723
|
mesh2.position.copy(it.mesh.position);
|
|
27299
27724
|
mesh2.quaternion.copy(it.mesh.quaternion);
|
|
27300
27725
|
mesh2.scale.copy(it.mesh.scale);
|
|
@@ -27310,6 +27735,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27310
27735
|
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh2.matrixWorld);
|
|
27311
27736
|
return mesh2;
|
|
27312
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
|
+
}
|
|
27313
27751
|
function maskOf(hostId, parts) {
|
|
27314
27752
|
const host = structure.byId.get(hostId);
|
|
27315
27753
|
const [u0, v0, u1, v1] = host.aabb;
|
|
@@ -27325,41 +27763,73 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27325
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) };
|
|
27326
27764
|
}
|
|
27327
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
|
+
}
|
|
27328
27779
|
function paint(view) {
|
|
27329
|
-
const { mask } = view;
|
|
27780
|
+
const { mask, core } = view;
|
|
27330
27781
|
const along = (p) => (mask.alongU ? p[0] : p[2]) - mask.start;
|
|
27331
27782
|
const holes = [];
|
|
27783
|
+
const pits = [];
|
|
27332
27784
|
const discs = [];
|
|
27785
|
+
const flakes = [];
|
|
27333
27786
|
const shown = [];
|
|
27334
27787
|
for (const id of view.parts) {
|
|
27335
|
-
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);
|
|
27336
27789
|
const all = cells(part, units2);
|
|
27337
27790
|
const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
|
|
27338
27791
|
const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
|
|
27339
27792
|
if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
|
|
27340
|
-
|
|
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;
|
|
27341
27796
|
const kept = [];
|
|
27342
27797
|
for (const c of all) (gone(c) ? holes : kept).push(c);
|
|
27343
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
|
+
}
|
|
27344
27803
|
}
|
|
27345
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);
|
|
27346
27806
|
mask.data.fill(0);
|
|
27347
|
-
|
|
27348
|
-
|
|
27349
|
-
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));
|
|
27350
27811
|
for (let j = j0; j <= j1; j++) {
|
|
27351
|
-
const y =
|
|
27812
|
+
const y = m.y0 + (j + 0.5) / m.h * m.height;
|
|
27352
27813
|
for (let i = i0; i <= i1; i++) {
|
|
27353
|
-
const s = (i + 0.5) /
|
|
27354
|
-
|
|
27355
|
-
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;
|
|
27356
27816
|
}
|
|
27357
27817
|
}
|
|
27358
27818
|
};
|
|
27359
|
-
|
|
27360
|
-
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
|
+
}
|
|
27361
27830
|
mask.texture.needsUpdate = true;
|
|
27362
|
-
|
|
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);
|
|
27363
27833
|
const list2 = [];
|
|
27364
27834
|
const tone = (hex3, k) => tmp.c.setHex(hex3).multiplyScalar(k).getHex();
|
|
27365
27835
|
for (const { c, part, units: units2, thick, mid } of shown) {
|
|
@@ -27369,7 +27839,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27369
27839
|
const base = fragmentMaterial(part.material).color.getHex();
|
|
27370
27840
|
const n = noise2(centre * 3.1, y * 2.7, 0.07);
|
|
27371
27841
|
if (units2.mortar === "concrete") {
|
|
27372
|
-
at(len, h, thick - 2 * COVER2
|
|
27842
|
+
at(len, h, thick - 2 * COVER2, tone(base, 0.9 + 0.04 * n));
|
|
27373
27843
|
} else {
|
|
27374
27844
|
at(len, h, thick - 2 * COVER2 - 0.012, 10327948);
|
|
27375
27845
|
at(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
|
|
@@ -27379,7 +27849,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27379
27849
|
view.units.removeFromParent();
|
|
27380
27850
|
view.units.dispose();
|
|
27381
27851
|
}
|
|
27382
|
-
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));
|
|
27383
27853
|
units.name = "breach:" + view.host;
|
|
27384
27854
|
list2.forEach((u, i) => {
|
|
27385
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));
|
|
@@ -27395,6 +27865,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27395
27865
|
house.add(units);
|
|
27396
27866
|
view.units = units;
|
|
27397
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 };
|
|
27398
27881
|
}
|
|
27399
27882
|
function draw2(partId, change) {
|
|
27400
27883
|
if (lod !== 0) return false;
|
|
@@ -27406,23 +27889,49 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27406
27889
|
if (!items.length) return false;
|
|
27407
27890
|
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
27408
27891
|
if (!parts.includes(partId)) return false;
|
|
27409
|
-
const mask = maskOf(hostId, parts);
|
|
27410
|
-
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
|
+
};
|
|
27411
27910
|
}
|
|
27412
27911
|
if (!view.chips.has(partId)) return false;
|
|
27413
27912
|
if (change.opening) {
|
|
27414
27913
|
if (view.openings.has(partId)) return false;
|
|
27415
27914
|
view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
|
|
27416
27915
|
}
|
|
27417
|
-
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
|
+
}
|
|
27418
27922
|
if (!view.saved) {
|
|
27419
27923
|
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
27924
|
+
for (const m of view.shells) m.userData.behind = () => view.units;
|
|
27420
27925
|
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
27421
27926
|
}
|
|
27422
|
-
paint(view);
|
|
27423
27927
|
views.set(hostId, view);
|
|
27424
|
-
|
|
27425
|
-
|
|
27928
|
+
if (opts.deferPaint && change.spall) dirty.add(view);
|
|
27929
|
+
else {
|
|
27930
|
+
dirty.delete(view);
|
|
27931
|
+
paint(view);
|
|
27932
|
+
invalidate();
|
|
27933
|
+
invalidateShadow();
|
|
27934
|
+
}
|
|
27426
27935
|
return true;
|
|
27427
27936
|
}
|
|
27428
27937
|
function plain2(view, shown) {
|
|
@@ -27431,24 +27940,70 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27431
27940
|
else s.data = collapseRange(s.it.mesh, s.v0, s.it.v1);
|
|
27432
27941
|
}
|
|
27433
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
|
+
}
|
|
27434
27955
|
function dispose(view) {
|
|
27435
|
-
|
|
27436
|
-
view.units
|
|
27956
|
+
dirty.delete(view);
|
|
27957
|
+
if (view.units) {
|
|
27958
|
+
view.units.removeFromParent();
|
|
27959
|
+
view.units.dispose();
|
|
27960
|
+
}
|
|
27961
|
+
view.unitMaterial.dispose();
|
|
27437
27962
|
for (const m of view.shells) {
|
|
27438
27963
|
m.removeFromParent();
|
|
27439
27964
|
m.geometry.dispose();
|
|
27440
27965
|
m.material.dispose();
|
|
27441
27966
|
}
|
|
27442
27967
|
view.mask.texture.dispose();
|
|
27968
|
+
view.core.texture.dispose();
|
|
27443
27969
|
}
|
|
27444
27970
|
return {
|
|
27445
27971
|
/** Handles a `stage` event of the house's object. Returns true when the wall was cut. */
|
|
27446
27972
|
stage(event) {
|
|
27447
27973
|
return event.stage === 2 && !!event.opening ? draw2(event.part, { opening: event.opening }) : false;
|
|
27448
27974
|
},
|
|
27449
|
-
/**
|
|
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
|
+
*/
|
|
27450
28004
|
chip(event) {
|
|
27451
|
-
|
|
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] } });
|
|
27452
28007
|
},
|
|
27453
28008
|
/** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
|
|
27454
28009
|
drop(partId) {
|
|
@@ -27456,9 +28011,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27456
28011
|
if (!v) return;
|
|
27457
28012
|
dispose(v);
|
|
27458
28013
|
views.delete(partId);
|
|
28014
|
+
if (v.saved) dropped.push(v);
|
|
27459
28015
|
},
|
|
27460
28016
|
/** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
|
|
27461
28017
|
setLod(level) {
|
|
28018
|
+
flush(Infinity);
|
|
27462
28019
|
const was = lod;
|
|
27463
28020
|
lod = level;
|
|
27464
28021
|
if (was === 0 === (level === 0)) return;
|
|
@@ -27477,21 +28034,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27477
28034
|
if (lod === 0) plain2(v, true);
|
|
27478
28035
|
}
|
|
27479
28036
|
views.clear();
|
|
28037
|
+
for (const v of dropped) plain2(v, true);
|
|
28038
|
+
dropped.length = 0;
|
|
27480
28039
|
},
|
|
28040
|
+
flush,
|
|
27481
28041
|
/** The wall has damage drawn. */
|
|
27482
28042
|
has(partId) {
|
|
27483
28043
|
return views.has(partId);
|
|
27484
28044
|
},
|
|
27485
|
-
/** Walls with damage drawn, units out of them, and units laid bare at the edge of the cuts. */
|
|
27486
|
-
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), {
|
|
27487
28047
|
walls: views.size,
|
|
27488
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),
|
|
27489
28051
|
units: [...views.values()].reduce((n, v) => n + v.units.count, 0)
|
|
27490
28052
|
})
|
|
27491
28053
|
};
|
|
27492
28054
|
}
|
|
27493
28055
|
Object.assign(K, { createBreachView });
|
|
27494
28056
|
}
|
|
28057
|
+
var FOLLOW_FLUSH_FALLBACK_MS = 100;
|
|
27495
28058
|
function installDestructionWorld(K, shared) {
|
|
27496
28059
|
const { THREE, cfg, invalidateShadow, invalidate, rngString, FRAGMENT_GEO, fragmentMaterial, createBreachView } = K;
|
|
27497
28060
|
function createHouseDestruction(root, house, structure, parts, colliders, water) {
|
|
@@ -27500,7 +28063,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27500
28063
|
const R = cfg.destructionRenderer || null, listeners = /* @__PURE__ */ new Set();
|
|
27501
28064
|
D.renderer = R;
|
|
27502
28065
|
D.spec = null;
|
|
27503
|
-
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 } : {} });
|
|
27504
28067
|
D.breach = breach;
|
|
27505
28068
|
D.onEvent = (fn) => {
|
|
27506
28069
|
listeners.add(fn);
|
|
@@ -27685,6 +28248,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27685
28248
|
case "chip":
|
|
27686
28249
|
breach.chip(e);
|
|
27687
28250
|
break;
|
|
28251
|
+
case "spall":
|
|
28252
|
+
breach.spall(e);
|
|
28253
|
+
break;
|
|
27688
28254
|
case "collapse":
|
|
27689
28255
|
for (const id of e.parts) {
|
|
27690
28256
|
breach.drop(id);
|
|
@@ -27726,7 +28292,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27726
28292
|
D.specOptions = specOptions || null;
|
|
27727
28293
|
D.spec = structure.spec(specOptions);
|
|
27728
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] });
|
|
27729
|
-
|
|
28295
|
+
breach.claim();
|
|
28296
|
+
if (R) R.bind(D.id, house, { parts: rangesOf(), onFrame: () => breach.flush() });
|
|
27730
28297
|
off = D.world.on("*", handleEvent);
|
|
27731
28298
|
D.active = true;
|
|
27732
28299
|
return D;
|
|
@@ -27741,6 +28308,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27741
28308
|
};
|
|
27742
28309
|
D.follow = (world, id, spec) => {
|
|
27743
28310
|
D.spec = spec;
|
|
28311
|
+
breach.claim();
|
|
28312
|
+
let fallback = null;
|
|
27744
28313
|
const offFollow = world.on("*", (e) => {
|
|
27745
28314
|
if (e.object !== id) return;
|
|
27746
28315
|
switch (e.type) {
|
|
@@ -27750,6 +28319,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27750
28319
|
case "chip":
|
|
27751
28320
|
breach.chip(e);
|
|
27752
28321
|
break;
|
|
28322
|
+
case "spall":
|
|
28323
|
+
breach.spall(e);
|
|
28324
|
+
break;
|
|
27753
28325
|
case "fracture":
|
|
27754
28326
|
breach.drop(e.part);
|
|
27755
28327
|
break;
|
|
@@ -27759,10 +28331,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27759
28331
|
default:
|
|
27760
28332
|
break;
|
|
27761
28333
|
}
|
|
28334
|
+
if (!fallback && typeof setTimeout === "function") fallback = setTimeout(() => {
|
|
28335
|
+
fallback = null;
|
|
28336
|
+
breach.flush(Infinity);
|
|
28337
|
+
}, FOLLOW_FLUSH_FALLBACK_MS);
|
|
27762
28338
|
});
|
|
27763
28339
|
return { restore: () => breach.restore(), dispose: () => {
|
|
27764
28340
|
offFollow();
|
|
27765
28341
|
breach.restore();
|
|
28342
|
+
}, update: () => {
|
|
28343
|
+
breach.flush();
|
|
27766
28344
|
} };
|
|
27767
28345
|
};
|
|
27768
28346
|
D.piece = (id) => D.active ? D.world.part(D.id, id) : null;
|
|
@@ -27795,6 +28373,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27795
28373
|
return r;
|
|
27796
28374
|
};
|
|
27797
28375
|
D.animate = (dt) => {
|
|
28376
|
+
breach.flush();
|
|
27798
28377
|
if (R) return;
|
|
27799
28378
|
let moved = false;
|
|
27800
28379
|
for (const [id, list2] of bodies) {
|
|
@@ -29290,7 +29869,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
29290
29869
|
const provider = options.provider ?? createFindsProvider(catalog);
|
|
29291
29870
|
const items = catalog.list();
|
|
29292
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));
|
|
29293
|
-
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);
|
|
29294
29881
|
const showcase = listItemModels().map((id) => ({ id, size: sizeOfModel(id), def: catalog.has(id) ? catalog.get(id) : void 0 }));
|
|
29295
29882
|
const state = {
|
|
29296
29883
|
category: CATEGORY_ALL,
|
|
@@ -29341,12 +29928,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
29341
29928
|
const contents = itemContents(subject);
|
|
29342
29929
|
return contents ? { fluid: { container: contents.container, part: SHELL_PART_ID } } : {};
|
|
29343
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
|
+
};
|
|
29344
29946
|
const itemOf = (def) => ({
|
|
29345
29947
|
id: `${ITEM_PREFIX}${def.id}`,
|
|
29346
29948
|
name: nameOf(def),
|
|
29347
29949
|
movable: true,
|
|
29348
29950
|
thumbnail: itemThumbnailSvg(drawable(def)),
|
|
29349
|
-
...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 })),
|
|
29350
29952
|
build: (THREE) => {
|
|
29351
29953
|
const T2 = THREE;
|
|
29352
29954
|
latestTHREE = T2;
|
|
@@ -29710,7 +30312,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
29710
30312
|
id: "modeled",
|
|
29711
30313
|
name: text.metrics.modeled,
|
|
29712
30314
|
read: () => {
|
|
29713
|
-
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()];
|
|
29714
30316
|
return `${shown.filter((entry2) => entry2.kind === "model").length}/${shown.length}`;
|
|
29715
30317
|
}
|
|
29716
30318
|
},
|