@adatechnology/game-destruction 0.16.0 → 0.18.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 +13 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.global.js +441 -16
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +441 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -340,6 +340,22 @@ var frame = (ax, ay, az, angle = 0) => {
|
|
|
340
340
|
return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle));
|
|
341
341
|
};
|
|
342
342
|
var clamp = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
343
|
+
var splitAlong = (box, count, axis, rnd) => {
|
|
344
|
+
const shares = Array.from({ length: count }, () => 0.25 + 1.5 * rnd());
|
|
345
|
+
const total = shares.reduce((sum, share) => sum + share, 0);
|
|
346
|
+
const out = [];
|
|
347
|
+
let from = box.c[axis] - box.s[axis] / 2;
|
|
348
|
+
for (const share of shares) {
|
|
349
|
+
const length = box.s[axis] * share / total;
|
|
350
|
+
const c = [box.c[0], box.c[1], box.c[2]];
|
|
351
|
+
const size = [box.s[0], box.s[1], box.s[2]];
|
|
352
|
+
c[axis] = from + length / 2;
|
|
353
|
+
size[axis] = length;
|
|
354
|
+
out.push({ c, s: size });
|
|
355
|
+
from += length;
|
|
356
|
+
}
|
|
357
|
+
return out;
|
|
358
|
+
};
|
|
343
359
|
var splinterBox = (inp, box, limit = inp.maxFragments) => {
|
|
344
360
|
const { rnd } = inp;
|
|
345
361
|
const dims = box.s;
|
|
@@ -349,35 +365,81 @@ var splinterBox = (inp, box, limit = inp.maxFragments) => {
|
|
|
349
365
|
const w = Math.max(fmin * 1.5, 0.01);
|
|
350
366
|
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(limit / 2)));
|
|
351
367
|
const maxLen = fmax * 4;
|
|
352
|
-
const cells =
|
|
368
|
+
const cells = [];
|
|
369
|
+
for (const cell of bsp(box, c, [a, b], rnd)) {
|
|
370
|
+
const wide = cell.s[a] >= cell.s[b] ? a : b;
|
|
371
|
+
if (rnd() < 0.4 && cell.s[wide] > fmin * 0.5) for (const part of splitAlong(cell, 2, wide, rnd)) cells.push(part);
|
|
372
|
+
else cells.push(cell);
|
|
373
|
+
}
|
|
353
374
|
const pieces = [];
|
|
354
375
|
for (const cell of cells) {
|
|
355
|
-
const extra = rnd()
|
|
376
|
+
const extra = Math.floor(rnd() * 3);
|
|
356
377
|
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
357
|
-
for (const p of
|
|
378
|
+
for (const p of splitAlong(cell, k, g, rnd)) pieces.push(p);
|
|
358
379
|
}
|
|
359
380
|
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] }));
|
|
360
381
|
};
|
|
382
|
+
var PLY = 3e-3;
|
|
383
|
+
var PLY_GAP = 0.6;
|
|
384
|
+
var PLY_SHEET = 0.012;
|
|
385
|
+
var plyBreak = (inp, root, limit) => {
|
|
386
|
+
const { rnd } = inp;
|
|
387
|
+
const dims = root.s;
|
|
388
|
+
const sorted = [0, 1, 2].sort((x, y) => dims[x] - dims[y]);
|
|
389
|
+
const t = sorted[0];
|
|
390
|
+
const [u, v] = [sorted[1], sorted[2]].sort((x, y) => x - y);
|
|
391
|
+
const along = dims[u] >= dims[v] ? u : v;
|
|
392
|
+
const across = along === u ? v : u;
|
|
393
|
+
const layers = clamp(Math.round(dims[t] / PLY / 2), 2, 3);
|
|
394
|
+
const pitch = dims[t] / layers;
|
|
395
|
+
const th = Math.min(pitch, PLY_SHEET);
|
|
396
|
+
const per = Math.max(2, Math.floor(limit / layers));
|
|
397
|
+
const out = [];
|
|
398
|
+
for (let k = 0; k < layers; k++) {
|
|
399
|
+
const layer = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
400
|
+
layer.c[t] = root.c[t] + (-dims[t] / 2 + (k + 0.5) * pitch) * (pitch > PLY_SHEET ? 1 : 1 + PLY_GAP);
|
|
401
|
+
layer.s[t] = th;
|
|
402
|
+
for (const cell of bsp(layer, per, [u, v], rnd)) {
|
|
403
|
+
if (out.length >= limit) return out;
|
|
404
|
+
if (rnd() < 0.35) {
|
|
405
|
+
const strand = cell.s[across] * (0.15 + 0.2 * rnd());
|
|
406
|
+
out.push({ center: cell.c, size: [cell.s[along], strand, th * 0.7], q: frame(along, across, t), shape: "splinter", weight: cell.s[along] * strand * th });
|
|
407
|
+
} else {
|
|
408
|
+
out.push({ center: cell.c, size: [cell.s[u], cell.s[v], th], q: frame(u, v, t, (rnd() - 0.5) * 0.5), shape: "scrap", weight: cell.s[u] * cell.s[v] * th });
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return out;
|
|
413
|
+
};
|
|
414
|
+
var granules = (inp, box, count) => bsp(box, count, [0, 1, 2], inp.rnd).map((b) => ({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] }));
|
|
415
|
+
var CRUMBS = 6;
|
|
361
416
|
var severAt = (inp, root, at, gap) => {
|
|
362
417
|
const dims = root.s;
|
|
363
418
|
const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
|
|
364
419
|
const half = dims[L] / 2;
|
|
365
420
|
const lo = clamp(at - gap / 2, -half, half), hi = clamp(at + gap / 2, -half, half);
|
|
366
421
|
const out = [];
|
|
367
|
-
const
|
|
422
|
+
const fibrous = inp.form === "slivers" || inp.form === "plies";
|
|
423
|
+
const [a, b] = [0, 1, 2].filter((i) => i !== L);
|
|
424
|
+
const stub = (from, to, breakAtEnd) => {
|
|
368
425
|
if (to - from < 0.02) return;
|
|
369
426
|
const c = [root.c[0], root.c[1], root.c[2]], sz = [dims[0], dims[1], dims[2]];
|
|
370
427
|
c[L] = (from + to) / 2;
|
|
371
428
|
sz[L] = to - from;
|
|
372
|
-
|
|
429
|
+
const weight = sz[0] * sz[1] * sz[2];
|
|
430
|
+
if (fibrous) out.push({ center: c, size: [sz[L], sz[a], sz[b]], q: frame(L, a, b, breakAtEnd ? 0 : Math.PI), shape: "slab", weight });
|
|
431
|
+
else out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight });
|
|
373
432
|
};
|
|
374
|
-
stub(-half, lo);
|
|
375
|
-
stub(hi, half);
|
|
433
|
+
stub(-half, lo, true);
|
|
434
|
+
stub(hi, half, false);
|
|
376
435
|
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
377
436
|
band.c[L] = (lo + hi) / 2;
|
|
378
437
|
band.s[L] = Math.max(hi - lo, 0.01);
|
|
379
|
-
|
|
380
|
-
|
|
438
|
+
const room = inp.maxFragments - out.length;
|
|
439
|
+
if (inp.style === "snap" && inp.form === "plies") for (const p of plyBreak(inp, band, Math.max(3, room))) out.push(p);
|
|
440
|
+
else if (inp.style === "snap" && inp.form === "granules") for (const p of granules(inp, band, clamp(room, 3, 12))) out.push(p);
|
|
441
|
+
else if (inp.style === "snap") {
|
|
442
|
+
for (const b2 of bsp(band, Math.max(3, Math.min(6, room)), [0, 1, 2], inp.rnd)) out.push({ center: b2.c, size: b2.s, q: quat.identity(), shape: "chunk", weight: b2.s[0] * b2.s[1] * b2.s[2] });
|
|
381
443
|
} else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
382
444
|
return out;
|
|
383
445
|
};
|
|
@@ -410,8 +472,18 @@ var planFragments = (inp) => {
|
|
|
410
472
|
const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
|
|
411
473
|
if (style === "snap") {
|
|
412
474
|
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
475
|
+
if (inp.form === "plies") return plyBreak(inp, root, maxFragments);
|
|
413
476
|
const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
|
|
414
477
|
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] });
|
|
478
|
+
if (inp.form !== "granules") return out;
|
|
479
|
+
const crumbs = Math.min(CRUMBS, maxFragments - out.length);
|
|
480
|
+
if (crumbs <= 0) return out;
|
|
481
|
+
const band = { c: [0, 0, 0], s: [0, 0, 0] };
|
|
482
|
+
for (let i = 0; i < 3; i++) {
|
|
483
|
+
band.s[i] = Math.min(dims[i], Math.max(fmin, dims[i] * 0.2));
|
|
484
|
+
band.c[i] = clamp(inp.hit[i], -dims[i] / 2 + band.s[i] / 2, dims[i] / 2 - band.s[i] / 2);
|
|
485
|
+
}
|
|
486
|
+
for (const p of granules(inp, band, crumbs)) out.push({ ...p, weight: p.weight * 0.5 });
|
|
415
487
|
return out;
|
|
416
488
|
}
|
|
417
489
|
if (style === "crumble" || style === "none") {
|
|
@@ -520,6 +592,88 @@ var marksInBox = (center, size, rotation, holes) => {
|
|
|
520
592
|
}
|
|
521
593
|
return marks;
|
|
522
594
|
};
|
|
595
|
+
var JOINT_GAP = 1e-4;
|
|
596
|
+
var MIN_BEARING_SHARE = 0.25;
|
|
597
|
+
var unsupportedCells = (cells, thin, dims, standing) => {
|
|
598
|
+
if (thin === 1) return [];
|
|
599
|
+
const [u, v] = planeAxes(thin);
|
|
600
|
+
const bottom = -dims[v] / 2;
|
|
601
|
+
const rowOf = (y) => Math.round((y - bottom) * 1e4);
|
|
602
|
+
const rows = /* @__PURE__ */ new Map();
|
|
603
|
+
for (let i = 0; i < cells.length; i++) {
|
|
604
|
+
if (!standing(i)) continue;
|
|
605
|
+
const cell = cells[i];
|
|
606
|
+
const key = rowOf(cell.center[v] - cell.size[v] / 2);
|
|
607
|
+
const row = rows.get(key);
|
|
608
|
+
if (row) row.push(i);
|
|
609
|
+
else rows.set(key, [i]);
|
|
610
|
+
}
|
|
611
|
+
const held = /* @__PURE__ */ new Set();
|
|
612
|
+
const queue = [];
|
|
613
|
+
for (const i of rows.get(0) ?? []) {
|
|
614
|
+
held.add(i);
|
|
615
|
+
queue.push(i);
|
|
616
|
+
}
|
|
617
|
+
const overlap = (a, b) => Math.min(a.center[u] + a.size[u] / 2, b.center[u] + b.size[u] / 2) - Math.max(a.center[u] - a.size[u] / 2, b.center[u] - b.size[u] / 2);
|
|
618
|
+
const climb = () => {
|
|
619
|
+
while (queue.length > 0) {
|
|
620
|
+
const cell = cells[queue.pop()];
|
|
621
|
+
for (const j of rows.get(rowOf(cell.center[v] + cell.size[v] / 2)) ?? []) {
|
|
622
|
+
const other = cells[j];
|
|
623
|
+
if (held.has(j) || overlap(cell, other) <= Math.max(JOINT_GAP, MIN_BEARING_SHARE * Math.min(cell.size[u], other.size[u]))) continue;
|
|
624
|
+
held.add(j);
|
|
625
|
+
queue.push(j);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
for (const row of rows.values()) row.sort((a, b) => cells[a].center[u] - cells[b].center[u]);
|
|
630
|
+
const touching = (a, b) => Math.abs(a.center[u] + a.size[u] / 2 - (b.center[u] - b.size[u] / 2)) <= 1e-3;
|
|
631
|
+
for (let wedged = true; wedged; ) {
|
|
632
|
+
climb();
|
|
633
|
+
wedged = false;
|
|
634
|
+
for (const row of rows.values()) {
|
|
635
|
+
for (let k = 1; k < row.length - 1; k++) {
|
|
636
|
+
const i = row[k];
|
|
637
|
+
if (held.has(i) || !held.has(row[k - 1]) || !held.has(row[k + 1])) continue;
|
|
638
|
+
if (!touching(cells[row[k - 1]], cells[i]) || !touching(cells[i], cells[row[k + 1]])) continue;
|
|
639
|
+
held.add(i);
|
|
640
|
+
queue.push(i);
|
|
641
|
+
wedged = true;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const loose = [];
|
|
646
|
+
for (const row of rows.values()) for (const i of row) if (!held.has(i)) loose.push(i);
|
|
647
|
+
return loose;
|
|
648
|
+
};
|
|
649
|
+
var clumpCells = (cells, thin, cap) => {
|
|
650
|
+
if (cells.length <= cap) return [...cells];
|
|
651
|
+
const [u, v] = planeAxes(thin);
|
|
652
|
+
let lo0 = Infinity, lo1 = Infinity, hi0 = -Infinity, hi1 = -Infinity;
|
|
653
|
+
for (const cell of cells) {
|
|
654
|
+
lo0 = Math.min(lo0, cell.center[u] - cell.size[u] / 2);
|
|
655
|
+
hi0 = Math.max(hi0, cell.center[u] + cell.size[u] / 2);
|
|
656
|
+
lo1 = Math.min(lo1, cell.center[v] - cell.size[v] / 2);
|
|
657
|
+
hi1 = Math.max(hi1, cell.center[v] + cell.size[v] / 2);
|
|
658
|
+
}
|
|
659
|
+
let side = Math.sqrt((hi0 - lo0) * (hi1 - lo1) / cap);
|
|
660
|
+
for (; ; ) {
|
|
661
|
+
const bins = /* @__PURE__ */ new Map();
|
|
662
|
+
for (const cell of cells) {
|
|
663
|
+
const key = `${Math.floor((cell.center[u] - lo0) / side)}:${Math.floor((cell.center[v] - lo1) / side)}`;
|
|
664
|
+
const bin = bins.get(key) ?? { lo: [Infinity, Infinity, Infinity], hi: [-Infinity, -Infinity, -Infinity] };
|
|
665
|
+
for (let k = 0; k < 3; k++) {
|
|
666
|
+
bin.lo[k] = Math.min(bin.lo[k], cell.center[k] - cell.size[k] / 2);
|
|
667
|
+
bin.hi[k] = Math.max(bin.hi[k], cell.center[k] + cell.size[k] / 2);
|
|
668
|
+
}
|
|
669
|
+
bins.set(key, bin);
|
|
670
|
+
}
|
|
671
|
+
if (bins.size <= cap) {
|
|
672
|
+
return [...bins.values()].map(({ lo, hi }) => ({ center: [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2], size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]] }));
|
|
673
|
+
}
|
|
674
|
+
side *= 1.25;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
523
677
|
|
|
524
678
|
// src/layers.ts
|
|
525
679
|
var MAX_SPALLS = 64;
|
|
@@ -1560,6 +1714,7 @@ var materials_default = [
|
|
|
1560
1714
|
},
|
|
1561
1715
|
{
|
|
1562
1716
|
id: "wood_soft",
|
|
1717
|
+
splitForm: "slivers",
|
|
1563
1718
|
density: 500,
|
|
1564
1719
|
hpPerKg: 90,
|
|
1565
1720
|
penetration: 9,
|
|
@@ -1605,6 +1760,7 @@ var materials_default = [
|
|
|
1605
1760
|
},
|
|
1606
1761
|
{
|
|
1607
1762
|
id: "wood_hard",
|
|
1763
|
+
splitForm: "slivers",
|
|
1608
1764
|
density: 750,
|
|
1609
1765
|
hpPerKg: 140,
|
|
1610
1766
|
penetration: 14,
|
|
@@ -1649,6 +1805,7 @@ var materials_default = [
|
|
|
1649
1805
|
},
|
|
1650
1806
|
{
|
|
1651
1807
|
id: "plywood",
|
|
1808
|
+
splitForm: "plies",
|
|
1652
1809
|
density: 600,
|
|
1653
1810
|
hpPerKg: 70,
|
|
1654
1811
|
penetration: 7,
|
|
@@ -1696,6 +1853,7 @@ var materials_default = [
|
|
|
1696
1853
|
},
|
|
1697
1854
|
{
|
|
1698
1855
|
id: "mdf",
|
|
1856
|
+
splitForm: "granules",
|
|
1699
1857
|
density: 750,
|
|
1700
1858
|
hpPerKg: 45,
|
|
1701
1859
|
penetration: 6,
|
|
@@ -1862,6 +2020,7 @@ var materials_default = [
|
|
|
1862
2020
|
floor: 60
|
|
1863
2021
|
}
|
|
1864
2022
|
},
|
|
2023
|
+
ricochetDeg: 12,
|
|
1865
2024
|
hitDebris: "chips",
|
|
1866
2025
|
breakDebris: "dust",
|
|
1867
2026
|
visual: { innerColor: 10197652 },
|
|
@@ -1893,6 +2052,7 @@ var materials_default = [
|
|
|
1893
2052
|
0.04,
|
|
1894
2053
|
0.12
|
|
1895
2054
|
],
|
|
2055
|
+
ricochetDeg: 10,
|
|
1896
2056
|
hitDebris: "chips",
|
|
1897
2057
|
breakDebris: "dust",
|
|
1898
2058
|
visual: { innerColor: 12085567 },
|
|
@@ -1950,6 +2110,7 @@ var materials_default = [
|
|
|
1950
2110
|
0.06,
|
|
1951
2111
|
0.3
|
|
1952
2112
|
],
|
|
2113
|
+
ricochetDeg: 15,
|
|
1953
2114
|
hitDebris: "chips",
|
|
1954
2115
|
breakDebris: "dust",
|
|
1955
2116
|
visual: { innerColor: 9407364 },
|
|
@@ -2207,6 +2368,7 @@ var materials_default = [
|
|
|
2207
2368
|
},
|
|
2208
2369
|
{
|
|
2209
2370
|
id: "green_wood",
|
|
2371
|
+
splitForm: "slivers",
|
|
2210
2372
|
density: 650,
|
|
2211
2373
|
hpPerKg: 5,
|
|
2212
2374
|
penetration: 10,
|
|
@@ -2377,6 +2539,7 @@ var materials_default = [
|
|
|
2377
2539
|
0.03,
|
|
2378
2540
|
0.15
|
|
2379
2541
|
],
|
|
2542
|
+
ricochetDeg: 8,
|
|
2380
2543
|
hitDebris: "chips",
|
|
2381
2544
|
breakDebris: "dust",
|
|
2382
2545
|
thermal: {
|
|
@@ -2476,6 +2639,7 @@ var materials_default = [
|
|
|
2476
2639
|
0.02,
|
|
2477
2640
|
0.08
|
|
2478
2641
|
],
|
|
2642
|
+
ricochetDeg: 8,
|
|
2479
2643
|
hitDebris: "chips",
|
|
2480
2644
|
breakDebris: "dust",
|
|
2481
2645
|
thermal: {
|
|
@@ -2487,6 +2651,127 @@ var materials_default = [
|
|
|
2487
2651
|
"brittle"
|
|
2488
2652
|
],
|
|
2489
2653
|
compressive: 1e5
|
|
2654
|
+
},
|
|
2655
|
+
{
|
|
2656
|
+
id: "mud",
|
|
2657
|
+
density: 1700,
|
|
2658
|
+
hpPerKg: 1,
|
|
2659
|
+
penetration: 5,
|
|
2660
|
+
ductility: 0.8,
|
|
2661
|
+
fracture: "crumble",
|
|
2662
|
+
fragmentSize: [
|
|
2663
|
+
0.02,
|
|
2664
|
+
0.08
|
|
2665
|
+
],
|
|
2666
|
+
hitDebris: "dust",
|
|
2667
|
+
breakDebris: "dust",
|
|
2668
|
+
thermal: {
|
|
2669
|
+
conductivity: 0.5,
|
|
2670
|
+
heatCapacity: 3.2
|
|
2671
|
+
},
|
|
2672
|
+
tags: [
|
|
2673
|
+
"soil",
|
|
2674
|
+
"wet"
|
|
2675
|
+
],
|
|
2676
|
+
compressive: 5e3
|
|
2677
|
+
},
|
|
2678
|
+
{
|
|
2679
|
+
id: "clay",
|
|
2680
|
+
density: 1800,
|
|
2681
|
+
hpPerKg: 5,
|
|
2682
|
+
penetration: 7,
|
|
2683
|
+
ductility: 0.05,
|
|
2684
|
+
fracture: "crumble",
|
|
2685
|
+
fragmentSize: [
|
|
2686
|
+
0.02,
|
|
2687
|
+
0.1
|
|
2688
|
+
],
|
|
2689
|
+
hitDebris: "chips",
|
|
2690
|
+
breakDebris: "dust",
|
|
2691
|
+
thermal: {
|
|
2692
|
+
conductivity: 0.15,
|
|
2693
|
+
heatCapacity: 2.2
|
|
2694
|
+
},
|
|
2695
|
+
tags: [
|
|
2696
|
+
"soil",
|
|
2697
|
+
"brittle"
|
|
2698
|
+
],
|
|
2699
|
+
compressive: 6e4
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
id: "snow",
|
|
2703
|
+
density: 300,
|
|
2704
|
+
hpPerKg: 1,
|
|
2705
|
+
penetration: 3,
|
|
2706
|
+
ductility: 0.6,
|
|
2707
|
+
fracture: "crumble",
|
|
2708
|
+
fragmentSize: [
|
|
2709
|
+
0.01,
|
|
2710
|
+
0.05
|
|
2711
|
+
],
|
|
2712
|
+
hitDebris: "dust",
|
|
2713
|
+
breakDebris: "dust",
|
|
2714
|
+
thermal: {
|
|
2715
|
+
conductivity: 0.1,
|
|
2716
|
+
heatCapacity: 2.1
|
|
2717
|
+
},
|
|
2718
|
+
tags: [
|
|
2719
|
+
"soil",
|
|
2720
|
+
"frozen"
|
|
2721
|
+
],
|
|
2722
|
+
compressive: 1e3
|
|
2723
|
+
},
|
|
2724
|
+
{
|
|
2725
|
+
id: "cobble",
|
|
2726
|
+
density: 2650,
|
|
2727
|
+
hpPerKg: 25,
|
|
2728
|
+
penetration: 25,
|
|
2729
|
+
penetrationClass: "concrete",
|
|
2730
|
+
penetrationScale: 0.9,
|
|
2731
|
+
ductility: 0.03,
|
|
2732
|
+
fracture: "crumble",
|
|
2733
|
+
fragmentSize: [
|
|
2734
|
+
0.05,
|
|
2735
|
+
0.15
|
|
2736
|
+
],
|
|
2737
|
+
ricochetDeg: 15,
|
|
2738
|
+
hitDebris: "chips",
|
|
2739
|
+
breakDebris: "chips",
|
|
2740
|
+
thermal: {
|
|
2741
|
+
conductivity: 0.2,
|
|
2742
|
+
heatCapacity: 2.5
|
|
2743
|
+
},
|
|
2744
|
+
tags: [
|
|
2745
|
+
"masonry",
|
|
2746
|
+
"brittle"
|
|
2747
|
+
],
|
|
2748
|
+
compressive: 3e5
|
|
2749
|
+
},
|
|
2750
|
+
{
|
|
2751
|
+
id: "paver",
|
|
2752
|
+
density: 2300,
|
|
2753
|
+
hpPerKg: 14,
|
|
2754
|
+
penetration: 18,
|
|
2755
|
+
penetrationClass: "concrete",
|
|
2756
|
+
penetrationScale: 1,
|
|
2757
|
+
ductility: 0.03,
|
|
2758
|
+
fracture: "crumble",
|
|
2759
|
+
fragmentSize: [
|
|
2760
|
+
0.04,
|
|
2761
|
+
0.12
|
|
2762
|
+
],
|
|
2763
|
+
ricochetDeg: 12,
|
|
2764
|
+
hitDebris: "chips",
|
|
2765
|
+
breakDebris: "chips",
|
|
2766
|
+
thermal: {
|
|
2767
|
+
conductivity: 0.15,
|
|
2768
|
+
heatCapacity: 2.5
|
|
2769
|
+
},
|
|
2770
|
+
tags: [
|
|
2771
|
+
"masonry",
|
|
2772
|
+
"brittle"
|
|
2773
|
+
],
|
|
2774
|
+
compressive: 35e4
|
|
2490
2775
|
}
|
|
2491
2776
|
];
|
|
2492
2777
|
|
|
@@ -2711,6 +2996,11 @@ var BREACH_RADIUS = 0.55;
|
|
|
2711
2996
|
var BREACH_BLAST_FACTOR = 6;
|
|
2712
2997
|
var BREACH_AREA = 0.25;
|
|
2713
2998
|
var BREACH_BLAST_REACH = 0.12;
|
|
2999
|
+
var MEMBER_CUT_REACH = 0.5;
|
|
3000
|
+
var BLAST_REFERENCE_RESISTANCE = 200 * 1900 * 0.13;
|
|
3001
|
+
var BLAST_EDGE_RAGGED = 0.3;
|
|
3002
|
+
var BLAST_EDGE_LOOSE = 1.35;
|
|
3003
|
+
var BLAST_EDGE_WEAR = 0.6;
|
|
2714
3004
|
var RESIDUAL_CAPACITY = 0.15;
|
|
2715
3005
|
var SEVER_BAND = 0.1;
|
|
2716
3006
|
var SEVER_ASPECT = 3;
|
|
@@ -3963,7 +4253,7 @@ var createDestruction = (options = {}) => {
|
|
|
3963
4253
|
if (j.st.open) continue;
|
|
3964
4254
|
if (j.spec.kind === "rebar" && (isReinforced(o.parts[j.ia].spec) || isReinforced(o.parts[j.ib].spec))) continue;
|
|
3965
4255
|
if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
|
|
3966
|
-
j.st.hp -= span === void 0 ? amount : amount * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
4256
|
+
j.st.hp -= span === void 0 ? amount : Math.min(amount, j.st.maxHp) * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
3967
4257
|
if (j.st.hp <= 0) {
|
|
3968
4258
|
j.st.hp = 0;
|
|
3969
4259
|
j.st.open = true;
|
|
@@ -4091,6 +4381,7 @@ var createDestruction = (options = {}) => {
|
|
|
4091
4381
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4092
4382
|
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
4093
4383
|
const out = [];
|
|
4384
|
+
const wholeBefore = Object.values(worn).filter((w) => w === -1).length;
|
|
4094
4385
|
const shot = kind === "ballistic";
|
|
4095
4386
|
for (let i = 0; i < grid.length; i++) {
|
|
4096
4387
|
const cell = grid[i];
|
|
@@ -4149,6 +4440,7 @@ var createDestruction = (options = {}) => {
|
|
|
4149
4440
|
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
4150
4441
|
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
4151
4442
|
}
|
|
4443
|
+
if (Object.values(worn).filter((w) => w === -1).length > wholeBefore) dropUnsupported(o, p, src);
|
|
4152
4444
|
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
4153
4445
|
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
4154
4446
|
if (p.structural && !isLost(p)) {
|
|
@@ -4252,10 +4544,122 @@ var createDestruction = (options = {}) => {
|
|
|
4252
4544
|
setStage(o, p, 1, {}, src);
|
|
4253
4545
|
setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
|
|
4254
4546
|
debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
|
|
4547
|
+
dropUnsupported(o, p, src);
|
|
4255
4548
|
const reinforcement = p.spec.reinforcement;
|
|
4256
4549
|
if (reinforcement) exposeRebar(o, p, src);
|
|
4257
4550
|
if (p.structural) solveStructure(o);
|
|
4258
4551
|
};
|
|
4552
|
+
const blastReachOf = (p, energy) => {
|
|
4553
|
+
const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
|
|
4554
|
+
const resistance = Math.max(1, perKg * p.mat.density * p.thickness);
|
|
4555
|
+
return BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3) * clamp2(Math.cbrt(BLAST_REFERENCE_RESISTANCE / resistance), 0.3, 2);
|
|
4556
|
+
};
|
|
4557
|
+
const blastUnits = (o, p, wp, energy, dist, src) => {
|
|
4558
|
+
var _a, _b;
|
|
4559
|
+
if (!p.staged || p.st.stripped || isLost(p) || chipEnergy <= 0) return false;
|
|
4560
|
+
const reach = blastReachOf(p, energy);
|
|
4561
|
+
if (dist >= reach) return false;
|
|
4562
|
+
const radius = Math.sqrt(reach * reach - dist * dist);
|
|
4563
|
+
const [u, v] = planeAxes(p.thin);
|
|
4564
|
+
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
4565
|
+
const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
|
|
4566
|
+
const grid = gridOf(p);
|
|
4567
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4568
|
+
const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
|
|
4569
|
+
const lobes = [rnd() * 2 * Math.PI, rnd() * 2 * Math.PI];
|
|
4570
|
+
const out = [];
|
|
4571
|
+
for (let i = 0; i < grid.length; i++) {
|
|
4572
|
+
const cell = grid[i];
|
|
4573
|
+
if (worn[i] === -1) continue;
|
|
4574
|
+
if (op && p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius) continue;
|
|
4575
|
+
const du = cell.center[u] - lp[u], dv = cell.center[v] - lp[v];
|
|
4576
|
+
const angle = Math.atan2(dv, du);
|
|
4577
|
+
const edge = radius * (1 + BLAST_EDGE_RAGGED * (0.5 * Math.sin(3 * angle + lobes[0]) + 0.3 * Math.sin(5 * angle + lobes[1]) + 0.4 * (rnd() - 0.5)));
|
|
4578
|
+
const d = Math.hypot(du, dv);
|
|
4579
|
+
if (d <= edge) {
|
|
4580
|
+
worn[i] = -1;
|
|
4581
|
+
out.push(i);
|
|
4582
|
+
} else if (d <= edge * BLAST_EDGE_LOOSE) {
|
|
4583
|
+
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
4584
|
+
worn[i] = Math.max(worn[i] ?? 0, perKg * mass * BLAST_EDGE_WEAR);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
if (out.length === 0) return false;
|
|
4588
|
+
const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !(op && p.st.opening && Math.hypot(grid[i].center[u] - op[u], grid[i].center[v] - op[v]) <= p.st.opening.radius));
|
|
4589
|
+
for (const i of loose) worn[i] = -1;
|
|
4590
|
+
const blown = out.map((i) => grid[i]), fallen = loose.map((i) => grid[i]);
|
|
4591
|
+
const volume = (cells2) => cells2.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0);
|
|
4592
|
+
const blownMass = Math.min(p.st.mass, p.mat.density * volume(blown));
|
|
4593
|
+
const fallenMass = Math.min(p.st.mass - blownMass, p.mat.density * volume(fallen));
|
|
4594
|
+
const speed = clamp2(Math.sqrt(2 * energy * 0.02 / Math.max(blownMass, 0.05)), 1, 12);
|
|
4595
|
+
const { c, q } = partXf(o, p);
|
|
4596
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
4597
|
+
const tag = `x${chipped.length}_`;
|
|
4598
|
+
const fragments = [
|
|
4599
|
+
...cellFragments(o, p, clumpCells(blown, p.thin, maxFragments), wp, null, speed, blownMass, tag, true),
|
|
4600
|
+
...fallen.length > 0 ? cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), wp, null, 0.3, fallenMass, `${tag}f`, true) : []
|
|
4601
|
+
];
|
|
4602
|
+
const cells = [...blown, ...fallen];
|
|
4603
|
+
const holes = localHolesOf(p);
|
|
4604
|
+
p.st.holes = p.st.holes.filter((_, k) => !cells.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
|
|
4605
|
+
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
|
|
4606
|
+
for (const cell of cells) {
|
|
4607
|
+
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
4608
|
+
for (let k = 0; k < 3; k++) {
|
|
4609
|
+
lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
|
|
4610
|
+
hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
p.st.mass = Math.max(0, p.st.mass - blownMass - fallenMass);
|
|
4614
|
+
p.rt.removed = (p.rt.removed ?? 0) + blownMass + fallenMass;
|
|
4615
|
+
markDamaged(p);
|
|
4616
|
+
setStage(o, p, 1, {}, src);
|
|
4617
|
+
const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
|
|
4618
|
+
const units = cells.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
|
|
4619
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
|
|
4620
|
+
debris(o, p, p.mat.breakDebris, center, vec.normalize(vec.sub(center, wp), [0, 1, 0]), 14, 3, src);
|
|
4621
|
+
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
4622
|
+
if (p.structural && !isLost(p)) {
|
|
4623
|
+
solveStructure(o);
|
|
4624
|
+
collapseCheck(o, src);
|
|
4625
|
+
}
|
|
4626
|
+
return true;
|
|
4627
|
+
};
|
|
4628
|
+
const dropUnsupported = (o, p, src) => {
|
|
4629
|
+
var _a, _b;
|
|
4630
|
+
if (!p.spec.units || p.spec.units.mortar === p.spec.material || p.st.stripped || isLost(p)) return 0;
|
|
4631
|
+
const [u, v] = planeAxes(p.thin);
|
|
4632
|
+
const grid = gridOf(p);
|
|
4633
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
4634
|
+
const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
|
|
4635
|
+
const opened = (cell) => !!op && !!p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius;
|
|
4636
|
+
const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !opened(grid[i]));
|
|
4637
|
+
if (loose.length === 0) return 0;
|
|
4638
|
+
for (const i of loose) worn[i] = -1;
|
|
4639
|
+
const fallen = loose.map((i) => grid[i]);
|
|
4640
|
+
const mass = Math.min(p.st.mass, p.mat.density * fallen.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0));
|
|
4641
|
+
const { c, q } = partXf(o, p);
|
|
4642
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
4643
|
+
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
|
|
4644
|
+
for (const cell of fallen) {
|
|
4645
|
+
for (let k = 0; k < 3; k++) {
|
|
4646
|
+
lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
|
|
4647
|
+
hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
|
|
4651
|
+
const fragments = cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), vec.add(center, [0, 1, 0]), null, 0.3, mass, `u${chipped.length}_`, true);
|
|
4652
|
+
const holes = localHolesOf(p);
|
|
4653
|
+
p.st.holes = p.st.holes.filter((_, k) => !fallen.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
|
|
4654
|
+
for (const cell of fallen) chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
4655
|
+
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
4656
|
+
p.rt.removed = (p.rt.removed ?? 0) + mass;
|
|
4657
|
+
markDamaged(p);
|
|
4658
|
+
const units = fallen.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
|
|
4659
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
|
|
4660
|
+
debris(o, p, p.mat.breakDebris, center, [0, -1, 0], 6, 1, src);
|
|
4661
|
+
return loose.length;
|
|
4662
|
+
};
|
|
4259
4663
|
const stripPart = (o, p, reinforcement, src) => {
|
|
4260
4664
|
const bars = rebarBars(p.dims, p.thin, reinforcement.spacing);
|
|
4261
4665
|
p.st.stripped = true;
|
|
@@ -4322,6 +4726,7 @@ var createDestruction = (options = {}) => {
|
|
|
4322
4726
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
4323
4727
|
const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
|
|
4324
4728
|
const cutAt = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
4729
|
+
const planRnd = p.mat.splitForm ? splitStream(o.id, p.spec.id, hitLocal) : rnd;
|
|
4325
4730
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
4326
4731
|
style,
|
|
4327
4732
|
dims: p.dims,
|
|
@@ -4330,8 +4735,9 @@ var createDestruction = (options = {}) => {
|
|
|
4330
4735
|
hit: hitLocal,
|
|
4331
4736
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
4332
4737
|
maxFragments,
|
|
4333
|
-
rnd,
|
|
4738
|
+
rnd: planRnd,
|
|
4334
4739
|
sheet: p.mat.sheet,
|
|
4740
|
+
...p.mat.splitForm ? { form: p.mat.splitForm } : {},
|
|
4335
4741
|
...cutAt !== void 0 ? { cut: { at: cutAt, gap: SEVER_BAND } } : {}
|
|
4336
4742
|
});
|
|
4337
4743
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
@@ -4470,7 +4876,8 @@ var createDestruction = (options = {}) => {
|
|
|
4470
4876
|
const fragments = [];
|
|
4471
4877
|
for (let i = 0; i < n; i++) {
|
|
4472
4878
|
const back = !exit || i % 3 === 2;
|
|
4473
|
-
const
|
|
4879
|
+
const plies = p.mat.splitForm === "plies";
|
|
4880
|
+
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() : plies ? 0.15 + 0.15 * rnd() : 0.4 + 0.4 * rnd());
|
|
4474
4881
|
const size = [w, w, w];
|
|
4475
4882
|
size[along] = len;
|
|
4476
4883
|
size[(along + 1) % 3] = w;
|
|
@@ -4631,7 +5038,7 @@ var createDestruction = (options = {}) => {
|
|
|
4631
5038
|
checkIgnite(o, p, src);
|
|
4632
5039
|
}
|
|
4633
5040
|
}
|
|
4634
|
-
wearJoints(o, p, Ein * 0.5, void 0, 0, src);
|
|
5041
|
+
wearJoints(o, p, Ein * 0.5, void 0, 0, src, false, 2 * BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3));
|
|
4635
5042
|
const amount = Ein * DMG_PER_J * (mat.damageScale?.explosive ?? 1);
|
|
4636
5043
|
if (blastFrontSpeed > 0 && dist > 0 && mat.tags?.includes("glass")) {
|
|
4637
5044
|
frontQueue.push({ o, p, due: time + dist / blastFrontSpeed, amount, fragEnergy: Ein * 0.3, point, ...src !== void 0 ? { src } : {} });
|
|
@@ -4641,7 +5048,10 @@ var createDestruction = (options = {}) => {
|
|
|
4641
5048
|
const dealt = applyDamage(o, p, amount, "explosive", point, null, Ein * 0.3, 40, src);
|
|
4642
5049
|
res.damage += dealt;
|
|
4643
5050
|
if (p.bend && flexure.enabled) bendByBlast(o, p, point, energy, radius, reach, src);
|
|
4644
|
-
|
|
5051
|
+
const breachReach = BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3);
|
|
5052
|
+
if (p.spec.units && p.spec.units.mortar !== p.spec.material) blastUnits(o, p, wp, energy, dist, src);
|
|
5053
|
+
else if (p.spec.reinforcement && p.spec.units && dist > MEMBER_CUT_REACH * breachReach) blastUnits(o, p, wp, energy, dist, src);
|
|
5054
|
+
else if (dist <= breachReach) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
4645
5055
|
return reach;
|
|
4646
5056
|
};
|
|
4647
5057
|
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
@@ -4887,6 +5297,11 @@ var createDestruction = (options = {}) => {
|
|
|
4887
5297
|
}
|
|
4888
5298
|
return h | 0;
|
|
4889
5299
|
};
|
|
5300
|
+
const splitStream = (objectId, partId, hit) => {
|
|
5301
|
+
const codes = Array.from(`${objectId}|${partId}`, (char) => char.charCodeAt(0) / 1e3);
|
|
5302
|
+
const stream = createRng(seedOf(rng.state() / 1e3, ...codes, hit[0], hit[1], hit[2]));
|
|
5303
|
+
return () => stream.next();
|
|
5304
|
+
};
|
|
4890
5305
|
const throwFragment = (frag, objectId, from, g, centre, index, src, shields, results, reports, mult) => {
|
|
4891
5306
|
const v = frag.velocity;
|
|
4892
5307
|
const speed = Math.hypot(v[0], v[1], v[2]);
|
|
@@ -6151,7 +6566,12 @@ var locales = {
|
|
|
6151
6566
|
sand: "sand",
|
|
6152
6567
|
gravel: "gravel",
|
|
6153
6568
|
turf: "turf",
|
|
6154
|
-
mortar: "mortar"
|
|
6569
|
+
mortar: "mortar",
|
|
6570
|
+
mud: "mud",
|
|
6571
|
+
clay: "dry clay",
|
|
6572
|
+
snow: "snow",
|
|
6573
|
+
cobble: "cobblestone",
|
|
6574
|
+
paver: "concrete paver"
|
|
6155
6575
|
},
|
|
6156
6576
|
partStatus: { intact: "intact", damaged: "damaged", collapsed: "collapsed", broken: "broken", melted: "melted", ashes: "ashes" },
|
|
6157
6577
|
jointKinds: {
|
|
@@ -6247,7 +6667,12 @@ var locales = {
|
|
|
6247
6667
|
sand: "areia",
|
|
6248
6668
|
gravel: "brita",
|
|
6249
6669
|
turf: "grama",
|
|
6250
|
-
mortar: "argamassa"
|
|
6670
|
+
mortar: "argamassa",
|
|
6671
|
+
mud: "lama",
|
|
6672
|
+
clay: "barro seco",
|
|
6673
|
+
snow: "neve",
|
|
6674
|
+
cobble: "paralelep\xEDpedo",
|
|
6675
|
+
paver: "bloquete"
|
|
6251
6676
|
},
|
|
6252
6677
|
partStatus: { intact: "intacto", damaged: "danificado", collapsed: "desabado", broken: "quebrado", melted: "derretido", ashes: "cinzas" },
|
|
6253
6678
|
jointKinds: {
|