@adatechnology/game-floorplan-structure 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -1
- package/dist/index.global.js +569 -65
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.global.js
CHANGED
|
@@ -32,6 +32,7 @@ var GameFloorplanStructure = (() => {
|
|
|
32
32
|
FLAT_ROOF_DEFAULTS: () => FLAT_ROOF_DEFAULTS,
|
|
33
33
|
GROUND: () => GROUND,
|
|
34
34
|
MECHANICAL_DEFAULTS: () => MECHANICAL_DEFAULTS,
|
|
35
|
+
MEMBER_CAGE: () => MEMBER_CAGE,
|
|
35
36
|
PILE_AREA: () => PILE_AREA,
|
|
36
37
|
PILE_DEFAULTS: () => PILE_DEFAULTS,
|
|
37
38
|
PLATE_DEFAULTS: () => PLATE_DEFAULTS,
|
|
@@ -2755,6 +2756,47 @@ var GameFloorplanStructure = (() => {
|
|
|
2755
2756
|
return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle));
|
|
2756
2757
|
};
|
|
2757
2758
|
var clamp3 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
2759
|
+
var splinterBox = (inp, box2, limit = inp.maxFragments) => {
|
|
2760
|
+
const { rnd } = inp;
|
|
2761
|
+
const dims = box2.s;
|
|
2762
|
+
const [fmin, fmax] = inp.fragmentSize;
|
|
2763
|
+
const g = inp.grain ?? [0, 1, 2].sort((a2, b2) => dims[b2] - dims[a2])[0];
|
|
2764
|
+
const [a, b] = [0, 1, 2].filter((i) => i !== g);
|
|
2765
|
+
const w = Math.max(fmin * 1.5, 0.01);
|
|
2766
|
+
const c = clamp3(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(limit / 2)));
|
|
2767
|
+
const maxLen = fmax * 4;
|
|
2768
|
+
const cells = bsp(box2, c, [a, b], rnd);
|
|
2769
|
+
const pieces = [];
|
|
2770
|
+
for (const cell of cells) {
|
|
2771
|
+
const extra = rnd() < 0.4 ? 1 : 0;
|
|
2772
|
+
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
2773
|
+
for (const p of bsp(cell, k, [g], rnd)) pieces.push(p);
|
|
2774
|
+
}
|
|
2775
|
+
return pieces.slice(0, limit).map((p) => ({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: "splinter", weight: p.s[0] * p.s[1] * p.s[2] }));
|
|
2776
|
+
};
|
|
2777
|
+
var severAt = (inp, root, at2, gap) => {
|
|
2778
|
+
const dims = root.s;
|
|
2779
|
+
const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
|
|
2780
|
+
const half = dims[L] / 2;
|
|
2781
|
+
const lo = clamp3(at2 - gap / 2, -half, half), hi = clamp3(at2 + gap / 2, -half, half);
|
|
2782
|
+
const out = [];
|
|
2783
|
+
const stub = (from, to) => {
|
|
2784
|
+
if (to - from < 0.02) return;
|
|
2785
|
+
const c = [root.c[0], root.c[1], root.c[2]], sz = [dims[0], dims[1], dims[2]];
|
|
2786
|
+
c[L] = (from + to) / 2;
|
|
2787
|
+
sz[L] = to - from;
|
|
2788
|
+
out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight: sz[0] * sz[1] * sz[2] });
|
|
2789
|
+
};
|
|
2790
|
+
stub(-half, lo);
|
|
2791
|
+
stub(hi, half);
|
|
2792
|
+
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
2793
|
+
band.c[L] = (lo + hi) / 2;
|
|
2794
|
+
band.s[L] = Math.max(hi - lo, 0.01);
|
|
2795
|
+
if (inp.style === "snap") {
|
|
2796
|
+
for (const b of bsp(band, Math.max(3, Math.min(6, inp.maxFragments - out.length)), [0, 1, 2], inp.rnd)) out.push({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] });
|
|
2797
|
+
} else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
2798
|
+
return out;
|
|
2799
|
+
};
|
|
2758
2800
|
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
2759
2801
|
const { dims, rnd, maxFragments } = inp;
|
|
2760
2802
|
const layers = clamp3(Math.ceil(dims[t] / STACK_LAYER), 2, 4);
|
|
@@ -2783,6 +2825,7 @@ var GameFloorplanStructure = (() => {
|
|
|
2783
2825
|
const t = sorted[0];
|
|
2784
2826
|
const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
|
|
2785
2827
|
if (style === "snap") {
|
|
2828
|
+
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
2786
2829
|
const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
|
|
2787
2830
|
for (const b of bsp(root, n2, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: "slab", weight: b.s[0] * b.s[1] * b.s[2] });
|
|
2788
2831
|
return out;
|
|
@@ -2793,23 +2836,8 @@ var GameFloorplanStructure = (() => {
|
|
|
2793
2836
|
return out;
|
|
2794
2837
|
}
|
|
2795
2838
|
if (style === "splinter") {
|
|
2796
|
-
|
|
2797
|
-
const
|
|
2798
|
-
const w = Math.max(fmin * 1.5, 0.01);
|
|
2799
|
-
const c = clamp3(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(maxFragments / 2)));
|
|
2800
|
-
const maxLen = fmax * 4;
|
|
2801
|
-
const cells = bsp(root, c, [a, b], rnd);
|
|
2802
|
-
const pieces = [];
|
|
2803
|
-
for (const cell of cells) {
|
|
2804
|
-
let list = [cell];
|
|
2805
|
-
const extra = rnd() < 0.4 ? 1 : 0;
|
|
2806
|
-
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
2807
|
-
list = bsp(cell, k, [g], rnd);
|
|
2808
|
-
for (const p of list) pieces.push(p);
|
|
2809
|
-
}
|
|
2810
|
-
for (const p of pieces.slice(0, maxFragments)) {
|
|
2811
|
-
out.push({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: "splinter", weight: p.s[0] * p.s[1] * p.s[2] });
|
|
2812
|
-
}
|
|
2839
|
+
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
2840
|
+
for (const p of splinterBox(inp, root)) out.push(p);
|
|
2813
2841
|
return out;
|
|
2814
2842
|
}
|
|
2815
2843
|
if (style === "tear") {
|
|
@@ -2971,6 +2999,27 @@ var GameFloorplanStructure = (() => {
|
|
|
2971
2999
|
for (const x of spread(dims[u], COVER, spacing, MAX_BARS_PER_DIRECTION)) out.push(bar(v, dims[v] / 2 - 0.02, [[u, x], [thin, 0]]));
|
|
2972
3000
|
return out;
|
|
2973
3001
|
};
|
|
3002
|
+
var clipBarsToBox = (bars, u, v, center, size) => {
|
|
3003
|
+
const out = [];
|
|
3004
|
+
for (const [a, b] of bars) {
|
|
3005
|
+
const axis = a[u] !== b[u] ? u : v;
|
|
3006
|
+
const other = axis === u ? v : u;
|
|
3007
|
+
if (Math.abs(a[other] - center[other]) > size[other] / 2) continue;
|
|
3008
|
+
const lo = Math.max(Math.min(a[axis], b[axis]), center[axis] - size[axis] / 2), hi = Math.min(Math.max(a[axis], b[axis]), center[axis] + size[axis] / 2);
|
|
3009
|
+
if (hi - lo < 1e-6) continue;
|
|
3010
|
+
const p = [a[0], a[1], a[2]], q = [b[0], b[1], b[2]];
|
|
3011
|
+
p[axis] = lo;
|
|
3012
|
+
q[axis] = hi;
|
|
3013
|
+
out.push([p, q]);
|
|
3014
|
+
}
|
|
3015
|
+
return out;
|
|
3016
|
+
};
|
|
3017
|
+
var bendBar = ([a, b], thin, bow, i) => {
|
|
3018
|
+
const length = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
|
3019
|
+
const m = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2];
|
|
3020
|
+
m[thin] += (i % 2 === 0 ? 1 : -1) * bow * length;
|
|
3021
|
+
return [[a, m], [m, b]];
|
|
3022
|
+
};
|
|
2974
3023
|
var clipBarsToDisc = (bars, u, v, center, radius) => {
|
|
2975
3024
|
const out = [];
|
|
2976
3025
|
for (const [a, b] of bars) {
|
|
@@ -3891,14 +3940,21 @@ var GameFloorplanStructure = (() => {
|
|
|
3891
3940
|
smoke: "grey"
|
|
3892
3941
|
},
|
|
3893
3942
|
visual: {
|
|
3894
|
-
charColor: 1709072
|
|
3943
|
+
charColor: 1709072,
|
|
3944
|
+
innerColor: 14730131
|
|
3895
3945
|
},
|
|
3896
3946
|
tags: [
|
|
3897
3947
|
"wood",
|
|
3898
3948
|
"organic",
|
|
3899
3949
|
"flammable"
|
|
3900
3950
|
],
|
|
3901
|
-
compressive: 1e5
|
|
3951
|
+
compressive: 1e5,
|
|
3952
|
+
hits: {
|
|
3953
|
+
blunt: {
|
|
3954
|
+
tear: "splinters",
|
|
3955
|
+
tearJoules: 60
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3902
3958
|
},
|
|
3903
3959
|
{
|
|
3904
3960
|
id: "wood_hard",
|
|
@@ -3928,14 +3984,21 @@ var GameFloorplanStructure = (() => {
|
|
|
3928
3984
|
smoke: "grey"
|
|
3929
3985
|
},
|
|
3930
3986
|
visual: {
|
|
3931
|
-
charColor: 1709072
|
|
3987
|
+
charColor: 1709072,
|
|
3988
|
+
innerColor: 13213802
|
|
3932
3989
|
},
|
|
3933
3990
|
tags: [
|
|
3934
3991
|
"wood",
|
|
3935
3992
|
"organic",
|
|
3936
3993
|
"flammable"
|
|
3937
3994
|
],
|
|
3938
|
-
compressive: 15e4
|
|
3995
|
+
compressive: 15e4,
|
|
3996
|
+
hits: {
|
|
3997
|
+
blunt: {
|
|
3998
|
+
tear: "splinters",
|
|
3999
|
+
tearJoules: 150
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
3939
4002
|
},
|
|
3940
4003
|
{
|
|
3941
4004
|
id: "plywood",
|
|
@@ -3965,14 +4028,24 @@ var GameFloorplanStructure = (() => {
|
|
|
3965
4028
|
smoke: "grey"
|
|
3966
4029
|
},
|
|
3967
4030
|
visual: {
|
|
3968
|
-
charColor: 1709072
|
|
4031
|
+
charColor: 1709072,
|
|
4032
|
+
innerColor: 14862234
|
|
3969
4033
|
},
|
|
3970
4034
|
tags: [
|
|
3971
4035
|
"wood",
|
|
3972
4036
|
"organic",
|
|
3973
4037
|
"flammable"
|
|
3974
4038
|
],
|
|
3975
|
-
compressive: 6e4
|
|
4039
|
+
compressive: 6e4,
|
|
4040
|
+
hits: {
|
|
4041
|
+
ballistic: {
|
|
4042
|
+
tear: "splinters"
|
|
4043
|
+
},
|
|
4044
|
+
blunt: {
|
|
4045
|
+
tear: "splinters",
|
|
4046
|
+
tearJoules: 80
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
3976
4049
|
},
|
|
3977
4050
|
{
|
|
3978
4051
|
id: "mdf",
|
|
@@ -3999,14 +4072,25 @@ var GameFloorplanStructure = (() => {
|
|
|
3999
4072
|
smoke: "grey"
|
|
4000
4073
|
},
|
|
4001
4074
|
visual: {
|
|
4002
|
-
charColor: 1709072
|
|
4075
|
+
charColor: 1709072,
|
|
4076
|
+
innerColor: 12096362
|
|
4003
4077
|
},
|
|
4004
4078
|
tags: [
|
|
4005
4079
|
"wood",
|
|
4006
4080
|
"organic",
|
|
4007
4081
|
"flammable"
|
|
4008
4082
|
],
|
|
4009
|
-
compressive: 3e4
|
|
4083
|
+
compressive: 3e4,
|
|
4084
|
+
hits: {
|
|
4085
|
+
ballistic: {
|
|
4086
|
+
tear: "lumps",
|
|
4087
|
+
tearJoules: 120
|
|
4088
|
+
},
|
|
4089
|
+
blunt: {
|
|
4090
|
+
tear: "lumps",
|
|
4091
|
+
tearJoules: 20
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4010
4094
|
},
|
|
4011
4095
|
{
|
|
4012
4096
|
id: "glass",
|
|
@@ -4120,6 +4204,17 @@ var GameFloorplanStructure = (() => {
|
|
|
4120
4204
|
damageScale: {
|
|
4121
4205
|
explosive: 1.2
|
|
4122
4206
|
},
|
|
4207
|
+
hits: {
|
|
4208
|
+
ballistic: {
|
|
4209
|
+
unitJoules: 500,
|
|
4210
|
+
floor: 900,
|
|
4211
|
+
crater: true
|
|
4212
|
+
},
|
|
4213
|
+
blunt: {
|
|
4214
|
+
unitJoules: 1500,
|
|
4215
|
+
floor: 60
|
|
4216
|
+
}
|
|
4217
|
+
},
|
|
4123
4218
|
hitDebris: "chips",
|
|
4124
4219
|
breakDebris: "dust",
|
|
4125
4220
|
thermal: {
|
|
@@ -4488,7 +4583,8 @@ var GameFloorplanStructure = (() => {
|
|
|
4488
4583
|
smoke: "white"
|
|
4489
4584
|
},
|
|
4490
4585
|
visual: {
|
|
4491
|
-
charColor: 1709072
|
|
4586
|
+
charColor: 1709072,
|
|
4587
|
+
innerColor: 14207126
|
|
4492
4588
|
},
|
|
4493
4589
|
tags: [
|
|
4494
4590
|
"plant",
|
|
@@ -4496,7 +4592,12 @@ var GameFloorplanStructure = (() => {
|
|
|
4496
4592
|
"organic",
|
|
4497
4593
|
"flammable"
|
|
4498
4594
|
],
|
|
4499
|
-
compressive: 6e4
|
|
4595
|
+
compressive: 6e4,
|
|
4596
|
+
hits: {
|
|
4597
|
+
blunt: {
|
|
4598
|
+
tear: "none"
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4500
4601
|
},
|
|
4501
4602
|
{
|
|
4502
4603
|
id: "cactus",
|
|
@@ -4611,6 +4712,130 @@ var GameFloorplanStructure = (() => {
|
|
|
4611
4712
|
"masonry"
|
|
4612
4713
|
],
|
|
4613
4714
|
compressive: 2e4
|
|
4715
|
+
},
|
|
4716
|
+
{
|
|
4717
|
+
id: "asphalt",
|
|
4718
|
+
density: 2350,
|
|
4719
|
+
hpPerKg: 10,
|
|
4720
|
+
penetration: 14,
|
|
4721
|
+
penetrationClass: "concrete",
|
|
4722
|
+
penetrationScale: 0.8,
|
|
4723
|
+
ductility: 0.1,
|
|
4724
|
+
fracture: "crumble",
|
|
4725
|
+
fragmentSize: [
|
|
4726
|
+
0.03,
|
|
4727
|
+
0.15
|
|
4728
|
+
],
|
|
4729
|
+
hitDebris: "chips",
|
|
4730
|
+
breakDebris: "dust",
|
|
4731
|
+
thermal: {
|
|
4732
|
+
conductivity: 0.1,
|
|
4733
|
+
heatCapacity: 2,
|
|
4734
|
+
ignitionC: 400,
|
|
4735
|
+
burnRate: 2e-3,
|
|
4736
|
+
smoke: "black"
|
|
4737
|
+
},
|
|
4738
|
+
tags: [
|
|
4739
|
+
"masonry",
|
|
4740
|
+
"brittle"
|
|
4741
|
+
],
|
|
4742
|
+
compressive: 3e4
|
|
4743
|
+
},
|
|
4744
|
+
{
|
|
4745
|
+
id: "sand",
|
|
4746
|
+
density: 1600,
|
|
4747
|
+
hpPerKg: 2,
|
|
4748
|
+
penetration: 6,
|
|
4749
|
+
ductility: 0.4,
|
|
4750
|
+
fracture: "crumble",
|
|
4751
|
+
fragmentSize: [
|
|
4752
|
+
0.01,
|
|
4753
|
+
0.04
|
|
4754
|
+
],
|
|
4755
|
+
hitDebris: "dust",
|
|
4756
|
+
breakDebris: "dust",
|
|
4757
|
+
thermal: {
|
|
4758
|
+
conductivity: 0.1,
|
|
4759
|
+
heatCapacity: 2
|
|
4760
|
+
},
|
|
4761
|
+
tags: [
|
|
4762
|
+
"soil"
|
|
4763
|
+
],
|
|
4764
|
+
compressive: 1e4
|
|
4765
|
+
},
|
|
4766
|
+
{
|
|
4767
|
+
id: "gravel",
|
|
4768
|
+
density: 1800,
|
|
4769
|
+
hpPerKg: 3,
|
|
4770
|
+
penetration: 8,
|
|
4771
|
+
ductility: 0.3,
|
|
4772
|
+
fracture: "crumble",
|
|
4773
|
+
fragmentSize: [
|
|
4774
|
+
0.02,
|
|
4775
|
+
0.06
|
|
4776
|
+
],
|
|
4777
|
+
hitDebris: "chips",
|
|
4778
|
+
breakDebris: "dust",
|
|
4779
|
+
thermal: {
|
|
4780
|
+
conductivity: 0.1,
|
|
4781
|
+
heatCapacity: 2
|
|
4782
|
+
},
|
|
4783
|
+
tags: [
|
|
4784
|
+
"soil",
|
|
4785
|
+
"masonry"
|
|
4786
|
+
],
|
|
4787
|
+
compressive: 2e4
|
|
4788
|
+
},
|
|
4789
|
+
{
|
|
4790
|
+
id: "turf",
|
|
4791
|
+
density: 900,
|
|
4792
|
+
hpPerKg: 3,
|
|
4793
|
+
penetration: 4,
|
|
4794
|
+
ductility: 0.5,
|
|
4795
|
+
fracture: "crumble",
|
|
4796
|
+
fragmentSize: [
|
|
4797
|
+
0.03,
|
|
4798
|
+
0.1
|
|
4799
|
+
],
|
|
4800
|
+
hitDebris: "dust",
|
|
4801
|
+
breakDebris: "dust",
|
|
4802
|
+
thermal: {
|
|
4803
|
+
conductivity: 0.1,
|
|
4804
|
+
heatCapacity: 1.5,
|
|
4805
|
+
ignitionC: 250,
|
|
4806
|
+
burnRate: 0.03,
|
|
4807
|
+
smoke: "white"
|
|
4808
|
+
},
|
|
4809
|
+
tags: [
|
|
4810
|
+
"soil",
|
|
4811
|
+
"organic"
|
|
4812
|
+
],
|
|
4813
|
+
compressive: 15e3
|
|
4814
|
+
},
|
|
4815
|
+
{
|
|
4816
|
+
id: "mortar",
|
|
4817
|
+
density: 1900,
|
|
4818
|
+
hpPerKg: 10,
|
|
4819
|
+
penetration: 12,
|
|
4820
|
+
penetrationClass: "concrete",
|
|
4821
|
+
penetrationScale: 1.2,
|
|
4822
|
+
ductility: 0.03,
|
|
4823
|
+
fracture: "crumble",
|
|
4824
|
+
fragmentSize: [
|
|
4825
|
+
0.02,
|
|
4826
|
+
0.08
|
|
4827
|
+
],
|
|
4828
|
+
hitDebris: "chips",
|
|
4829
|
+
breakDebris: "dust",
|
|
4830
|
+
thermal: {
|
|
4831
|
+
conductivity: 0.15,
|
|
4832
|
+
heatCapacity: 2.5
|
|
4833
|
+
},
|
|
4834
|
+
tags: [
|
|
4835
|
+
"masonry",
|
|
4836
|
+
"brittle"
|
|
4837
|
+
],
|
|
4838
|
+
compressive: 1e5
|
|
4614
4839
|
}
|
|
4615
4840
|
];
|
|
4616
4841
|
var material_hints_default = {
|
|
@@ -4784,6 +5009,32 @@ var GameFloorplanStructure = (() => {
|
|
|
4784
5009
|
return (1 + 4 * (0.3 - ductility) / 0.3) * Math.min(25, Math.max(1, (0.02 / Math.max(thickness, 1e-4)) ** 2));
|
|
4785
5010
|
};
|
|
4786
5011
|
var THIN_SHEET_DISHING = 5e-3;
|
|
5012
|
+
var SWEEP_BACK = 0.01;
|
|
5013
|
+
function sweepOrigins(box2, d, spacing) {
|
|
5014
|
+
const origins = [];
|
|
5015
|
+
const line = (from, to) => {
|
|
5016
|
+
const n = Math.max(1, Math.ceil((to - from) / spacing));
|
|
5017
|
+
return Array.from({ length: n + 1 }, (_, i) => from + (to - from) * i / n);
|
|
5018
|
+
};
|
|
5019
|
+
for (const axis of [0, 1, 2]) {
|
|
5020
|
+
if (Math.abs(d[axis]) < 1e-3) continue;
|
|
5021
|
+
const lead = d[axis] > 0 ? box2.max[axis] - SWEEP_BACK : box2.min[axis] + SWEEP_BACK;
|
|
5022
|
+
const u = (axis + 1) % 3;
|
|
5023
|
+
const v = (axis + 2) % 3;
|
|
5024
|
+
for (const a of line(box2.min[u], box2.max[u])) {
|
|
5025
|
+
for (const b of line(box2.min[v], box2.max[v])) {
|
|
5026
|
+
const point = [0, 0, 0];
|
|
5027
|
+
point[axis] = lead;
|
|
5028
|
+
point[u] = a;
|
|
5029
|
+
point[v] = b;
|
|
5030
|
+
origins.push(point);
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
5034
|
+
return origins;
|
|
5035
|
+
}
|
|
5036
|
+
var THIN_SHEET_CRUSH = 5e-4;
|
|
5037
|
+
var THIN_SHEET_CONTACT = 0.025;
|
|
4787
5038
|
var DMG_PER_J = 0.02;
|
|
4788
5039
|
var BLAST_COUPLING = 0.1;
|
|
4789
5040
|
var SHELTER_DEFAULTS = { k: 2800, brokenTransmission: 0.5, minPath: 5e-3 };
|
|
@@ -4797,9 +5048,17 @@ var GameFloorplanStructure = (() => {
|
|
|
4797
5048
|
var BREACH_AREA = 0.25;
|
|
4798
5049
|
var BREACH_BLAST_REACH = 0.12;
|
|
4799
5050
|
var RESIDUAL_CAPACITY = 0.15;
|
|
5051
|
+
var SEVER_BAND = 0.1;
|
|
5052
|
+
var SEVER_ASPECT = 3;
|
|
5053
|
+
var SEVER_BALLISTIC = 2;
|
|
5054
|
+
var TEAR_JOULES = 150;
|
|
5055
|
+
var tears = 0;
|
|
4800
5056
|
var SUPPORT_TOLERANCE = 0.05;
|
|
4801
5057
|
var UNIT_FRAGMENT_FACTOR = 4;
|
|
4802
5058
|
var BREACH_GRID_CAP = 4096;
|
|
5059
|
+
var CHIP_ENERGY = 200;
|
|
5060
|
+
var CHIP_SPLASH = 0.5;
|
|
5061
|
+
var BAR_BOW = 0.08;
|
|
4803
5062
|
var CUT_SLICE = 1;
|
|
4804
5063
|
var cutFactorOf = (shape) => clamp22(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
4805
5064
|
var clamp22 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -4809,8 +5068,24 @@ var GameFloorplanStructure = (() => {
|
|
|
4809
5068
|
return v === void 0 ? void 0 : v * (mat.penetrationScale ?? 1);
|
|
4810
5069
|
};
|
|
4811
5070
|
var capacityScale = (p) => {
|
|
4812
|
-
|
|
4813
|
-
|
|
5071
|
+
const residual = p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY;
|
|
5072
|
+
if (p.st.stripped) return residual;
|
|
5073
|
+
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, 1 - sectionLoss(p));
|
|
5074
|
+
return Math.max(p.spec.reinforcement ? residual : 0.05, kept);
|
|
5075
|
+
};
|
|
5076
|
+
var sectionLoss = (p) => {
|
|
5077
|
+
const cells = p.st.chipped;
|
|
5078
|
+
if (!cells?.length) return 0;
|
|
5079
|
+
const [u, v] = planeAxes(p.thin);
|
|
5080
|
+
const along = p.dims[u] >= p.dims[v] ? u : v;
|
|
5081
|
+
const across = along === u ? v : u;
|
|
5082
|
+
let worst = 0;
|
|
5083
|
+
for (const c of cells) {
|
|
5084
|
+
let gone = 0;
|
|
5085
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) gone += d.size[across];
|
|
5086
|
+
worst = Math.max(worst, gone / Math.max(p.dims[across], 1e-6));
|
|
5087
|
+
}
|
|
5088
|
+
return Math.min(1, worst);
|
|
4814
5089
|
};
|
|
4815
5090
|
var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
|
|
4816
5091
|
var clone = (v) => structuredClone(v);
|
|
@@ -4843,6 +5118,7 @@ var GameFloorplanStructure = (() => {
|
|
|
4843
5118
|
let windCfg = windOpts ? resolveWind(windOpts) : null;
|
|
4844
5119
|
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
4845
5120
|
const breachBlastFactor = options.breach?.blastFactor ?? BREACH_BLAST_FACTOR;
|
|
5121
|
+
const chipEnergy = options.breach?.chipEnergy ?? CHIP_ENERGY;
|
|
4846
5122
|
const rng = createRng(options.seed ?? 1);
|
|
4847
5123
|
const rnd = () => rng.next();
|
|
4848
5124
|
let time = 0;
|
|
@@ -5216,41 +5492,68 @@ var GameFloorplanStructure = (() => {
|
|
|
5216
5492
|
markDamaged(p);
|
|
5217
5493
|
if (p.rt.hinge >= flexure.failRotationDeg * Math.PI / 180) hingeDrop(o, p, src);
|
|
5218
5494
|
};
|
|
5495
|
+
const freeSpan = (p, supports) => {
|
|
5496
|
+
if (supports.length === 0) return Infinity;
|
|
5497
|
+
const d = p.dims;
|
|
5498
|
+
const L = [0, 1, 2].sort((a, b) => d[b] - d[a])[0];
|
|
5499
|
+
const spans = supports.map((k) => contactBox(p, k)).map(([lo, hi]) => [lo[L], hi[L]]);
|
|
5500
|
+
let far = 0;
|
|
5501
|
+
for (let i = 0; i <= 16; i++) {
|
|
5502
|
+
const x = (i / 16 - 0.5) * d[L];
|
|
5503
|
+
let near = Infinity;
|
|
5504
|
+
for (const [lo, hi] of spans) near = Math.min(near, Math.max(lo - x, x - hi, 0));
|
|
5505
|
+
far = Math.max(far, near);
|
|
5506
|
+
}
|
|
5507
|
+
return 2 * far;
|
|
5508
|
+
};
|
|
5219
5509
|
const solveStructure = (o) => {
|
|
5220
5510
|
o.maxStress = 0;
|
|
5221
5511
|
if (!o.hasStructure) return;
|
|
5222
5512
|
const parts = o.parts;
|
|
5223
5513
|
const n = parts.length;
|
|
5224
5514
|
const dist = new Array(n).fill(-1);
|
|
5225
|
-
const queue = [];
|
|
5226
5515
|
const ok = (i) => parts[i].structural && !isLost(parts[i]);
|
|
5227
5516
|
const isPartition = (i) => parts[i].spec.structure?.role === "partition";
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
if (
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
const r = j.ia === s ? j.ib : j.ia;
|
|
5238
|
-
if (dist[r] >= 0 || !ok(r) || restsOn(o, j, r) !== s) continue;
|
|
5239
|
-
dist[r] = dist[s] + 1;
|
|
5240
|
-
queue.push(r);
|
|
5517
|
+
const out = /* @__PURE__ */ new Set();
|
|
5518
|
+
let queue = [];
|
|
5519
|
+
let lowerOf = /* @__PURE__ */ new Map();
|
|
5520
|
+
for (; ; ) {
|
|
5521
|
+
dist.fill(-1);
|
|
5522
|
+
queue = [];
|
|
5523
|
+
for (let i = 0; i < n; i++) if (ok(i) && !out.has(i) && parts[i].anchored) {
|
|
5524
|
+
dist[i] = 0;
|
|
5525
|
+
queue.push(i);
|
|
5241
5526
|
}
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5527
|
+
for (let h = 0; h < queue.length; h++) {
|
|
5528
|
+
const s = queue[h];
|
|
5529
|
+
if (isPartition(s)) continue;
|
|
5530
|
+
for (const j of o.byPart.get(parts[s].spec.id)) {
|
|
5531
|
+
if (j.st.open || !j.bears) continue;
|
|
5532
|
+
const r = j.ia === s ? j.ib : j.ia;
|
|
5533
|
+
if (dist[r] >= 0 || !ok(r) || out.has(r) || restsOn(o, j, r) !== s) continue;
|
|
5534
|
+
dist[r] = dist[s] + 1;
|
|
5535
|
+
queue.push(r);
|
|
5536
|
+
}
|
|
5251
5537
|
}
|
|
5252
|
-
lowerOf
|
|
5538
|
+
lowerOf = /* @__PURE__ */ new Map();
|
|
5539
|
+
let dropped = false;
|
|
5540
|
+
for (const i of queue) {
|
|
5541
|
+
const lower = [];
|
|
5542
|
+
for (const j of o.byPart.get(parts[i].spec.id)) {
|
|
5543
|
+
if (j.st.open || !j.bears) continue;
|
|
5544
|
+
const k = restsOn(o, j, i);
|
|
5545
|
+
if (k >= 0 && dist[k] >= 0 && dist[k] < dist[i] && !isPartition(k)) lower.push(k);
|
|
5546
|
+
}
|
|
5547
|
+
lowerOf.set(i, lower);
|
|
5548
|
+
const max = parts[i].spec.structure?.maxSpan;
|
|
5549
|
+
if (max !== void 0 && !parts[i].anchored && freeSpan(parts[i], lower.map((k) => parts[k])) > max) {
|
|
5550
|
+
out.add(i);
|
|
5551
|
+
dropped = true;
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
if (!dropped) break;
|
|
5253
5555
|
}
|
|
5556
|
+
const incoming = new Array(n).fill(0);
|
|
5254
5557
|
const bending = new Array(n).fill(0);
|
|
5255
5558
|
const through = parts.map(() => []);
|
|
5256
5559
|
const pass = (k, at2, mass) => {
|
|
@@ -5762,6 +6065,7 @@ var GameFloorplanStructure = (() => {
|
|
|
5762
6065
|
const r = rayPart(o, p, origin, d);
|
|
5763
6066
|
if (!r || r.t0 < 0 || r.t0 > maxDist) continue;
|
|
5764
6067
|
if (p.st.opening && insideOpening(o, p, vec.madd(origin, d, r.t0))) continue;
|
|
6068
|
+
if (p.st.chipped && insideChip(p, objLocal(o, vec.madd(origin, d, r.t0)))) continue;
|
|
5765
6069
|
if (!best || r.t0 < best.distance) best = { object: o.id, part: p.spec.id, point: vec.madd(origin, d, r.t0), normal: r.n, distance: r.t0, pathLen: r.t1 - r.t0 };
|
|
5766
6070
|
}
|
|
5767
6071
|
}
|
|
@@ -5962,6 +6266,12 @@ var GameFloorplanStructure = (() => {
|
|
|
5962
6266
|
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src);
|
|
5963
6267
|
setStage(o, p, 1, {}, src);
|
|
5964
6268
|
};
|
|
6269
|
+
const crater = (o, p, wp, normal, radius, src) => {
|
|
6270
|
+
markDamaged(p);
|
|
6271
|
+
emit({ ...base(o, src), type: "spall", part: p.spec.id, layer: -1, point: wp, area: Math.PI * radius * radius });
|
|
6272
|
+
debris(o, p, p.mat.breakDebris, wp, normal, 6, 1.5, src);
|
|
6273
|
+
setStage(o, p, 1, {}, src);
|
|
6274
|
+
};
|
|
5965
6275
|
const insideOpening = (o, p, objectPoint) => {
|
|
5966
6276
|
const op = p.st.opening;
|
|
5967
6277
|
if (!op) return false;
|
|
@@ -6025,6 +6335,91 @@ var GameFloorplanStructure = (() => {
|
|
|
6025
6335
|
if (total < (p.spec.breach?.area ?? BREACH_AREA)) return;
|
|
6026
6336
|
openBreach(o, p, [sum[0] / total, sum[1] / total, sum[2] / total], total, energy, dir, src);
|
|
6027
6337
|
};
|
|
6338
|
+
const unitGrids = /* @__PURE__ */ new WeakMap();
|
|
6339
|
+
const gridOf = (p) => {
|
|
6340
|
+
let grid = unitGrids.get(p);
|
|
6341
|
+
if (!grid) unitGrids.set(p, grid = unitGrid(p.dims, p.thin, unitsOf2(p), BREACH_GRID_CAP));
|
|
6342
|
+
return grid;
|
|
6343
|
+
};
|
|
6344
|
+
const insideChip = (p, objectPoint) => {
|
|
6345
|
+
const cells = p.st.chipped;
|
|
6346
|
+
if (!cells?.length) return false;
|
|
6347
|
+
const [u, v] = planeAxes(p.thin);
|
|
6348
|
+
const lp = toLocal(p.pos, p.q, objectPoint);
|
|
6349
|
+
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);
|
|
6350
|
+
};
|
|
6351
|
+
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
6352
|
+
var _a, _b;
|
|
6353
|
+
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
6354
|
+
const rule = p.mat.hits?.[kind];
|
|
6355
|
+
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
6356
|
+
const energy = hitEnergy - (rule?.floor ?? 0);
|
|
6357
|
+
if (perKg <= 0 || energy <= 0) return;
|
|
6358
|
+
const [u, v] = planeAxes(p.thin);
|
|
6359
|
+
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
6360
|
+
const grid = gridOf(p);
|
|
6361
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
6362
|
+
const out = [];
|
|
6363
|
+
for (let i = 0; i < grid.length; i++) {
|
|
6364
|
+
const cell = grid[i];
|
|
6365
|
+
if (worn[i] === -1) continue;
|
|
6366
|
+
const gap = Math.hypot(Math.max(0, Math.abs(lp[u] - cell.center[u]) - cell.size[u] / 2), Math.max(0, Math.abs(lp[v] - cell.center[v]) - cell.size[v] / 2));
|
|
6367
|
+
if (gap > radius) continue;
|
|
6368
|
+
const share = gap === 0 ? 1 : CHIP_SPLASH * (1 - gap / radius);
|
|
6369
|
+
const total = (worn[i] ?? 0) + energy * share;
|
|
6370
|
+
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
6371
|
+
if (total < perKg * mass) {
|
|
6372
|
+
worn[i] = total;
|
|
6373
|
+
continue;
|
|
6374
|
+
}
|
|
6375
|
+
worn[i] = -1;
|
|
6376
|
+
out.push(cell);
|
|
6377
|
+
}
|
|
6378
|
+
if (out.length === 0) return;
|
|
6379
|
+
const { c, q } = partXf(o, p);
|
|
6380
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
6381
|
+
const holes = localHolesOf(p);
|
|
6382
|
+
for (const cell of out) {
|
|
6383
|
+
const mass = Math.min(p.st.mass, p.mat.density * cell.size[0] * cell.size[1] * cell.size[2]);
|
|
6384
|
+
const center = toWorld(c, q, cell.center);
|
|
6385
|
+
const speed = clamp22(Math.sqrt(2 * energy * 0.05 / Math.max(mass, 0.05)), 0.5, 6);
|
|
6386
|
+
const fragments = cellFragments(o, p, [cell], wp, dir, speed, mass, `c${chipped.length}_`);
|
|
6387
|
+
p.st.holes = p.st.holes.filter((_, k) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length === 0);
|
|
6388
|
+
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
6389
|
+
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
6390
|
+
p.rt.removed = (p.rt.removed ?? 0) + mass;
|
|
6391
|
+
markDamaged(p);
|
|
6392
|
+
setStage(o, p, 1, {}, src);
|
|
6393
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
6394
|
+
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
6395
|
+
}
|
|
6396
|
+
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
6397
|
+
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
6398
|
+
if (p.structural && !isLost(p)) {
|
|
6399
|
+
solveStructure(o);
|
|
6400
|
+
collapseCheck(o, src);
|
|
6401
|
+
}
|
|
6402
|
+
};
|
|
6403
|
+
const exposeRebar = (o, p, src) => {
|
|
6404
|
+
const r = p.spec.reinforcement;
|
|
6405
|
+
if (!r) return;
|
|
6406
|
+
const [u, v] = planeAxes(p.thin);
|
|
6407
|
+
const all = rebarBars(p.dims, p.thin, r.spacing);
|
|
6408
|
+
let bars;
|
|
6409
|
+
if (p.st.stripped) bars = all;
|
|
6410
|
+
else {
|
|
6411
|
+
bars = [];
|
|
6412
|
+
for (const cell of p.st.chipped ?? []) bars.push(...clipBarsToBox(all, u, v, cell.center, cell.size));
|
|
6413
|
+
const op = p.st.opening;
|
|
6414
|
+
if (op) {
|
|
6415
|
+
const lc = toLocal(p.pos, p.q, op.center);
|
|
6416
|
+
bars.push(...clipBarsToDisc(all, u, v, [lc[u], lc[v]], op.radius + 0.05));
|
|
6417
|
+
}
|
|
6418
|
+
}
|
|
6419
|
+
const bow = clamp22(((p.st.stress ?? 0) - 1) / 0.5, 0, 1);
|
|
6420
|
+
if (bow > 0) bars = bars.flatMap((bar2, i) => bendBar(bar2, p.thin, bow * BAR_BOW, i));
|
|
6421
|
+
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
6422
|
+
};
|
|
6028
6423
|
const openBreach = (o, p, center, total, energy, dir, src) => {
|
|
6029
6424
|
const [u, v] = planeAxes(p.thin);
|
|
6030
6425
|
const radius = clamp22(Math.sqrt(total / Math.PI), Math.sqrt((p.spec.breach?.area ?? BREACH_AREA) / Math.PI), breachRadius);
|
|
@@ -6036,7 +6431,8 @@ var GameFloorplanStructure = (() => {
|
|
|
6036
6431
|
failPart(o, p, worldCenter, dir, energy, 12, src);
|
|
6037
6432
|
return;
|
|
6038
6433
|
}
|
|
6039
|
-
const
|
|
6434
|
+
const worn = p.rt.chips;
|
|
6435
|
+
const cells = cellsWithin(gridOf(p).filter((_, i) => worn?.[i] !== -1), p.thin, lc, radius);
|
|
6040
6436
|
const removed = p.st.mass * cells.reduce((sum, cell) => sum + cell.size[u] * cell.size[v], 0) / (p.dims[u] * p.dims[v]);
|
|
6041
6437
|
const speed = clamp22(Math.sqrt(2 * energy * 0.1 / Math.max(removed, 0.05)), 0.5, 8);
|
|
6042
6438
|
const fragments = cellFragments(o, p, cells, worldCenter, dir, speed, removed, "b");
|
|
@@ -6050,10 +6446,7 @@ var GameFloorplanStructure = (() => {
|
|
|
6050
6446
|
setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
|
|
6051
6447
|
debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
|
|
6052
6448
|
const reinforcement = p.spec.reinforcement;
|
|
6053
|
-
if (reinforcement)
|
|
6054
|
-
const bars = worldBars(o, p, clipBarsToDisc(rebarBars(p.dims, p.thin, reinforcement.spacing), u, v, [lc[u], lc[v]], radius + 0.05));
|
|
6055
|
-
if (bars.length > 0) emit({ ...base(o, src), type: "expose", part: p.spec.id, bars });
|
|
6056
|
-
}
|
|
6449
|
+
if (reinforcement) exposeRebar(o, p, src);
|
|
6057
6450
|
if (p.structural) solveStructure(o);
|
|
6058
6451
|
};
|
|
6059
6452
|
const stripPart = (o, p, reinforcement, src) => {
|
|
@@ -6065,8 +6458,8 @@ var GameFloorplanStructure = (() => {
|
|
|
6065
6458
|
p.st.burning = false;
|
|
6066
6459
|
p.rt.jet = false;
|
|
6067
6460
|
markDamaged(p);
|
|
6068
|
-
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
6069
6461
|
solveStructure(o);
|
|
6462
|
+
exposeRebar(o, p, src);
|
|
6070
6463
|
collapseCheck(o, src);
|
|
6071
6464
|
};
|
|
6072
6465
|
const unitFragmentPlan = (p, cap) => {
|
|
@@ -6120,6 +6513,8 @@ var GameFloorplanStructure = (() => {
|
|
|
6120
6513
|
const grain = p.spec.grain ? AXIS[p.spec.grain] : void 0;
|
|
6121
6514
|
const hitLocal = toLocal(c, q, hitW);
|
|
6122
6515
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
6516
|
+
const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
|
|
6517
|
+
const cutAt2 = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
6123
6518
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
6124
6519
|
style,
|
|
6125
6520
|
dims: p.dims,
|
|
@@ -6129,7 +6524,8 @@ var GameFloorplanStructure = (() => {
|
|
|
6129
6524
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
6130
6525
|
maxFragments,
|
|
6131
6526
|
rnd,
|
|
6132
|
-
sheet: p.mat.sheet
|
|
6527
|
+
sheet: p.mat.sheet,
|
|
6528
|
+
...cutAt2 !== void 0 ? { cut: { at: cutAt2, gap: SEVER_BAND } } : {}
|
|
6133
6529
|
});
|
|
6134
6530
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
6135
6531
|
const mass = p.st.mass;
|
|
@@ -6188,10 +6584,33 @@ var GameFloorplanStructure = (() => {
|
|
|
6188
6584
|
emit({ ...base(o, src), type: "primed", part: p.spec.id, point: partXf(o, p).c, trigger, delay });
|
|
6189
6585
|
};
|
|
6190
6586
|
const CHAIN_TRIGGER_OF = { ballistic: "shot", explosive: "explosion" };
|
|
6587
|
+
const severAxis = (p) => {
|
|
6588
|
+
if (p.mat.fracture !== "splinter" && p.mat.fracture !== "snap" || p.spec.shape.kind !== "box") return -1;
|
|
6589
|
+
const d = p.dims;
|
|
6590
|
+
const order = [0, 1, 2].sort((a, b) => d[b] - d[a]);
|
|
6591
|
+
return d[order[0]] >= SEVER_ASPECT * d[order[1]] && d[order[0]] >= 3 * SEVER_BAND ? order[0] : -1;
|
|
6592
|
+
};
|
|
6593
|
+
const bandOf = (o, p, axis, origin) => {
|
|
6594
|
+
const { c, q } = partXf(o, p);
|
|
6595
|
+
const along = toLocal(c, q, origin)[axis];
|
|
6596
|
+
const L = p.dims[axis];
|
|
6597
|
+
return clamp22(Math.floor((along + L / 2) / SEVER_BAND), 0, Math.ceil(L / SEVER_BAND) - 1);
|
|
6598
|
+
};
|
|
6191
6599
|
const applyDamage = (o, p, amount, type, origin, dir, fragEnergy, cap, src) => {
|
|
6600
|
+
var _a;
|
|
6192
6601
|
if (isLost(p) || amount <= 0) return 0;
|
|
6193
6602
|
const prev = p.st.hp;
|
|
6194
6603
|
p.st.hp -= amount;
|
|
6604
|
+
const axis = origin && type !== "fire" && type !== "heat" ? severAxis(p) : -1;
|
|
6605
|
+
if (axis >= 0 && p.st.hp > 0) {
|
|
6606
|
+
const band = bandOf(o, p, axis, origin);
|
|
6607
|
+
const cuts = (_a = p.rt).cuts ?? (_a.cuts = {});
|
|
6608
|
+
cuts[band] = (cuts[band] ?? 0) + amount * (type === "ballistic" ? SEVER_BALLISTIC : 1);
|
|
6609
|
+
if (cuts[band] >= p.st.maxHp * SEVER_BAND / p.dims[axis]) {
|
|
6610
|
+
p.rt.cutAt = -p.dims[axis] / 2 + (band + 0.5) * SEVER_BAND;
|
|
6611
|
+
p.st.hp = 0;
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6195
6614
|
markDamaged(p);
|
|
6196
6615
|
const trigger = CHAIN_TRIGGER_OF[type];
|
|
6197
6616
|
const chained = trigger !== void 0 && chainDelayOf(p.spec, trigger) !== void 0;
|
|
@@ -6227,10 +6646,53 @@ var GameFloorplanStructure = (() => {
|
|
|
6227
6646
|
}
|
|
6228
6647
|
}
|
|
6229
6648
|
};
|
|
6649
|
+
const tearOf = (mat, kind) => {
|
|
6650
|
+
const h = mat.hits?.[kind];
|
|
6651
|
+
const tear = h?.tear ?? (kind === "ballistic" && mat.penetrationClass === "wood" ? "splinters" : "none");
|
|
6652
|
+
return { tear, joules: h?.tearJoules ?? TEAR_JOULES };
|
|
6653
|
+
};
|
|
6654
|
+
const tearAt = (o, p, kind, entry2, exit, d, E, scale, src) => {
|
|
6655
|
+
const { tear, joules } = tearOf(p.mat, kind);
|
|
6656
|
+
if (tear === "none") return;
|
|
6657
|
+
const shot = kind === "ballistic";
|
|
6658
|
+
const n = shot ? exit ? clamp22(Math.round(E / joules), 1, 7) : 1 : clamp22(Math.floor(E / joules), 0, 5);
|
|
6659
|
+
if (n === 0) return;
|
|
6660
|
+
const fibre = tear === "lumps";
|
|
6661
|
+
const { q } = partXf(o, p);
|
|
6662
|
+
const along = (p.spec.grain ? AXIS[p.spec.grain] : void 0) ?? [0, 1, 2].sort((a, b) => p.dims[b] - p.dims[a])[0];
|
|
6663
|
+
const fragments = [];
|
|
6664
|
+
for (let i = 0; i < n; i++) {
|
|
6665
|
+
const back = !exit || i % 3 === 2;
|
|
6666
|
+
const len = scale * (fibre ? 1.2 + 1.2 * rnd() : 3 + 4 * rnd()), w = scale * (0.8 + 0.8 * rnd()), t = scale * (fibre ? 0.7 + 0.6 * rnd() : 0.4 + 0.4 * rnd());
|
|
6667
|
+
const size = [w, w, w];
|
|
6668
|
+
size[along] = len;
|
|
6669
|
+
size[(along + 1) % 3] = w;
|
|
6670
|
+
size[(along + 2) % 3] = t;
|
|
6671
|
+
const spread3 = [(rnd() - 0.5) * 0.8, (rnd() - 0.2) * 0.8, (rnd() - 0.5) * 0.8];
|
|
6672
|
+
const speed = shot ? back ? 1.5 + 2 * rnd() : 2 + 4 * rnd() : 0.8 + 1.2 * rnd();
|
|
6673
|
+
const v = vec.scale(vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3)), speed);
|
|
6674
|
+
const mass = size[0] * size[1] * size[2] * p.mat.density;
|
|
6675
|
+
fragments.push({
|
|
6676
|
+
id: `${p.spec.id}#t${tears++}-${i}`,
|
|
6677
|
+
part: p.spec.id,
|
|
6678
|
+
material: p.spec.material,
|
|
6679
|
+
shape: fibre ? "chunk" : "splinter",
|
|
6680
|
+
center: back || !exit ? vec.madd(entry2, d, -0.01) : vec.madd(exit, d, 0.01),
|
|
6681
|
+
size,
|
|
6682
|
+
rotation: q,
|
|
6683
|
+
velocity: v,
|
|
6684
|
+
inner: true,
|
|
6685
|
+
angularVelocity: [(rnd() - 0.5) * 30, (rnd() - 0.5) * 30, (rnd() - 0.5) * 30],
|
|
6686
|
+
mass
|
|
6687
|
+
});
|
|
6688
|
+
}
|
|
6689
|
+
const patch = [scale * 4, scale * 4, scale * 4];
|
|
6690
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments });
|
|
6691
|
+
};
|
|
6230
6692
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round3 = {}) => {
|
|
6231
6693
|
const mat = p.mat;
|
|
6232
6694
|
const piercing = round3.piercing ?? 1;
|
|
6233
|
-
if (p.st.stripped || p.st.opening && insideOpening(o, p, objLocal(o, entry2))) {
|
|
6695
|
+
if (p.st.stripped || p.st.opening && insideOpening(o, p, objLocal(o, entry2)) || insideChip(p, objLocal(o, entry2))) {
|
|
6234
6696
|
return { penetrated: true, exitEnergy: E, exitPoint: vec.madd(entry2, d, Math.min(pathLen, p.thickness)), damage: 0 };
|
|
6235
6697
|
}
|
|
6236
6698
|
const sin = Math.min(1, Math.abs(vec.dot(d, n)));
|
|
@@ -6255,6 +6717,7 @@ var GameFloorplanStructure = (() => {
|
|
|
6255
6717
|
return { penetrated: false, exitEnergy: 0, damage: dmg2 };
|
|
6256
6718
|
}
|
|
6257
6719
|
if (p.layers.length > 0) spallOuter(o, p, entry2, face, n, Math.max(0.03, caliber / 1e3 * 4), src);
|
|
6720
|
+
else if (mat.hits?.ballistic?.crater) crater(o, p, entry2, n, clamp22(0.02 + Math.sqrt(E) * 15e-4, 0.02, 0.15), src);
|
|
6258
6721
|
const brittle = mat.ductility < 0.3;
|
|
6259
6722
|
const ratio = Ep / Math.max(thr, 1e-6);
|
|
6260
6723
|
const crackLen = clamp22(0.03 + ratio * 0.2, 0.03, 0.8);
|
|
@@ -6288,7 +6751,10 @@ var GameFloorplanStructure = (() => {
|
|
|
6288
6751
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
6289
6752
|
debris(o, p, mat.hitDebris, entry2, n, clamp22(Math.round(E / 60) + 2, 2, 14), clamp22(Math.sqrt(E) / 6, 1, 8), src);
|
|
6290
6753
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
6291
|
-
const
|
|
6754
|
+
const absorbed = penetrated && mat.ductility >= 0.5 && mat.fracture !== "tear" ? Math.min(E, thr) : E;
|
|
6755
|
+
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
6756
|
+
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
6757
|
+
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);
|
|
6292
6758
|
return { penetrated, exitEnergy: penetrated ? penMm !== void 0 ? E * (1 - thr / Ep) : E - thr / (hard ? piercing : 1) : 0, exitPoint, damage: dmg };
|
|
6293
6759
|
};
|
|
6294
6760
|
const melee = (o, p, kind, E, point, n, d, contactR, src) => {
|
|
@@ -6296,14 +6762,20 @@ var GameFloorplanStructure = (() => {
|
|
|
6296
6762
|
const thrMm = mat.penetration * p.thickness * 1e3;
|
|
6297
6763
|
const fragile = kind === "blunt" ? fragility(mat.ductility, p.thickness) : 1;
|
|
6298
6764
|
const Ec = E * fragile;
|
|
6765
|
+
const sheet = mat.ductility >= 0.5 && p.thickness <= THIN_SHEET_CRUSH;
|
|
6766
|
+
const fold = sheet ? Math.min(4, Math.sqrt(1e-3 / p.thickness)) : 1;
|
|
6767
|
+
const patch = sheet ? Math.min(contactR, THIN_SHEET_CONTACT) : contactR;
|
|
6299
6768
|
if (E > 0) {
|
|
6300
6769
|
if (kind === "blunt") {
|
|
6301
|
-
if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(
|
|
6770
|
+
if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(patch, 0.015), clamp22(E * 1e-4 * fold, 1e-3, sheet ? Math.min(0.06, Math.min(...p.dims) * 0.45) : 0.06), src);
|
|
6302
6771
|
else if (mat.ductility < 0.3) {
|
|
6303
6772
|
if (Ec >= 5) addCrack(o, p, point, n, clamp22(0.04 + Math.min(Ec, E * 6) * 8e-4, 0.04, 0.6), src);
|
|
6304
6773
|
} else if (E >= thrMm * 0.2) addDent(o, p, point, n, Math.max(contactR, 0.015), clamp22(E * 6e-5, 1e-3, 0.04), src);
|
|
6305
6774
|
} else if (mat.fracture === "tear") {
|
|
6306
6775
|
addHole(o, p, point, n, clamp22(0.02 + E * 5e-4, 0.02, 0.3), true, void 0, src);
|
|
6776
|
+
} else if (mat.ductility >= 0.3 && E >= thrMm) {
|
|
6777
|
+
addHole(o, p, point, n, clamp22(contactR, 0.01, 0.08), true, void 0, src);
|
|
6778
|
+
if (sheet) addDent(o, p, point, n, Math.max(patch * 0.6, 0.01), clamp22(E * 4e-5 * fold, 5e-4, 0.02), src);
|
|
6307
6779
|
} else if (mat.ductility >= 0.5) {
|
|
6308
6780
|
addDent(o, p, point, n, Math.max(contactR, 0.01), clamp22(E * 4e-5, 5e-4, 0.02), src);
|
|
6309
6781
|
}
|
|
@@ -6316,6 +6788,8 @@ var GameFloorplanStructure = (() => {
|
|
|
6316
6788
|
const scale = mat.damageScale?.[kind] ?? 1;
|
|
6317
6789
|
const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === "slash" ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src);
|
|
6318
6790
|
addBreach(o, p, point, breachAreaOf(p, done, false), E, d, src);
|
|
6791
|
+
if (kind === "blunt") chipUnits(o, p, "blunt", point, E, d, Math.max(contactR, 0.03), src);
|
|
6792
|
+
if (E > 0 && !isLost(p)) tearAt(o, p, kind, point, void 0, d, E, clamp22(contactR * 0.2, 6e-3, 0.02), src);
|
|
6319
6793
|
return done;
|
|
6320
6794
|
};
|
|
6321
6795
|
const blastPart = (o, p, point, energy, radius, src, res, reachAt) => {
|
|
@@ -6894,6 +7368,7 @@ var GameFloorplanStructure = (() => {
|
|
|
6894
7368
|
if (p.rt.stressT <= 0) {
|
|
6895
7369
|
p.rt.stressT = 0.5;
|
|
6896
7370
|
emit({ ...base(o), type: "stress", part: p.spec.id, stress, load: p.st.load ?? 0 });
|
|
7371
|
+
if (p.spec.reinforcement && (p.st.stripped || p.st.chipped?.length || p.st.opening)) exposeRebar(o, p);
|
|
6897
7372
|
}
|
|
6898
7373
|
applyDamage(o, p, overloadRate(stress) * p.st.maxHp * dt, "blunt", null, null, 5, 3);
|
|
6899
7374
|
} else if (stress > 0.8 && p.mat.ductility < 0.3 && p.rt.stressT <= 0 && p.st.cracks.length < 6) {
|
|
@@ -7110,6 +7585,18 @@ var GameFloorplanStructure = (() => {
|
|
|
7110
7585
|
const r = raycastInternal(origin, direction, maxDistance);
|
|
7111
7586
|
return r ? { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: r.distance } : null;
|
|
7112
7587
|
},
|
|
7588
|
+
sweepBox(box2, direction, distance2, options2 = {}) {
|
|
7589
|
+
const d = vec.normalize(direction);
|
|
7590
|
+
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
7591
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : void 0;
|
|
7592
|
+
let best = null;
|
|
7593
|
+
for (const origin of sweepOrigins(box2, d, options2.spacing ?? 0.2)) {
|
|
7594
|
+
const r = raycastInternal(origin, d, distance2 + SWEEP_BACK, ignore);
|
|
7595
|
+
if (!r || best && r.distance >= best.distance) continue;
|
|
7596
|
+
best = { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: Math.max(0, r.distance - SWEEP_BACK) };
|
|
7597
|
+
}
|
|
7598
|
+
return best;
|
|
7599
|
+
},
|
|
7113
7600
|
shoot(origin, direction, energy, caliber, source, round3) {
|
|
7114
7601
|
const extras = typeof round3 === "number" ? { piercing: round3 } : round3 ?? {};
|
|
7115
7602
|
return withCollapses(run(() => {
|
|
@@ -8534,7 +9021,24 @@ var GameFloorplanStructure = (() => {
|
|
|
8534
9021
|
}
|
|
8535
9022
|
return spec;
|
|
8536
9023
|
};
|
|
8537
|
-
var
|
|
9024
|
+
var MEMBER_CAGE = { spacing: 0.1, diameter: 8e-3, unit: 0.1 };
|
|
9025
|
+
var CAGED_ROLES = /* @__PURE__ */ new Set(["beam", "column"]);
|
|
9026
|
+
var caged = (spec, part, rebar) => {
|
|
9027
|
+
if (!rebar || part.material !== "concrete" || !CAGED_ROLES.has(part.role) || spec.reinforcement) return spec;
|
|
9028
|
+
const size = spec.shape.kind === "box" ? spec.shape.size : null;
|
|
9029
|
+
if (!size) return spec;
|
|
9030
|
+
const thin = Math.min(...size);
|
|
9031
|
+
return {
|
|
9032
|
+
...spec,
|
|
9033
|
+
reinforcement: { material: rebar, spacing: MEMBER_CAGE.spacing, diameter: MEMBER_CAGE.diameter },
|
|
9034
|
+
units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, thin], bond: "stack", mortar: "concrete" }
|
|
9035
|
+
};
|
|
9036
|
+
};
|
|
9037
|
+
var partSpecOf = (part, capacity, detailed, rebar) => {
|
|
9038
|
+
if (detailed && part.assembly) return assemblySpec(part, capacity);
|
|
9039
|
+
const spec = boxSpec(part, capacity);
|
|
9040
|
+
return detailed ? caged(spec, part, rebar) : spec;
|
|
9041
|
+
};
|
|
8538
9042
|
function toDestructibleSpec(structure, options = {}) {
|
|
8539
9043
|
const { safety = 0.65, redundancy = 0.97, safetyOthers = 0.45 } = options;
|
|
8540
9044
|
const kept = structure.parts.filter((part) => !options.filter || options.filter(part));
|
|
@@ -8551,7 +9055,7 @@ var GameFloorplanStructure = (() => {
|
|
|
8551
9055
|
const sizing = { safety, redundancy, safetyOthers };
|
|
8552
9056
|
const sized = { ...panels, capacities: options.sized === false ? /* @__PURE__ */ new Map() : sizeCapacities(panels.parts, panels.supports, sizing) };
|
|
8553
9057
|
const { parts, supports, capacities } = detail === "units" ? applyUnits(sized, structure) : sized;
|
|
8554
|
-
const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse"));
|
|
9058
|
+
const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse", structure.construction.frame.rebar));
|
|
8555
9059
|
const margin = options.terrainMargin ?? TERRAIN_MARGIN;
|
|
8556
9060
|
const x0 = Math.min(...parts.map((part) => part.aabb[0]));
|
|
8557
9061
|
const z0 = Math.min(...parts.map((part) => part.aabb[1]));
|