@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/dist/index.js CHANGED
@@ -416,6 +416,55 @@ var destruction_default = {
416
416
  ends: 22e-5
417
417
  }
418
418
  }
419
+ },
420
+ plastic_bottle: {
421
+ models: [
422
+ "water_bottle",
423
+ "water_jug_5l",
424
+ "dirty_water",
425
+ "sports_drink",
426
+ "coconut_water",
427
+ "cooking_oil",
428
+ "alcohol_70",
429
+ "fuel_jerrycan"
430
+ ],
431
+ rule: {
432
+ hollow: true,
433
+ welded: true,
434
+ skinTags: [
435
+ "paper",
436
+ "food"
437
+ ],
438
+ materials: {
439
+ glass: "plastic"
440
+ }
441
+ }
442
+ },
443
+ glass_bottle: {
444
+ models: [
445
+ "cachaca",
446
+ "communion_wine",
447
+ "honey_jar",
448
+ "home_preserves"
449
+ ],
450
+ rule: {
451
+ hollow: true,
452
+ welded: true,
453
+ skinTags: [
454
+ "paper",
455
+ "food"
456
+ ]
457
+ }
458
+ },
459
+ drink_carton: {
460
+ models: [
461
+ "juice_box",
462
+ "milk_carton"
463
+ ],
464
+ rule: {
465
+ hollow: true,
466
+ welded: true
467
+ }
419
468
  }
420
469
  },
421
470
  contents: {
@@ -431,6 +480,67 @@ var destruction_default = {
431
480
  liquid: "soda",
432
481
  fill: 0.95
433
482
  },
483
+ water_bottle: {
484
+ liquid: "water",
485
+ fill: 0.95
486
+ },
487
+ water_jug_5l: {
488
+ liquid: "water",
489
+ fill: 0.95,
490
+ box: true
491
+ },
492
+ dirty_water: {
493
+ liquid: "water",
494
+ fill: 0.9
495
+ },
496
+ sports_drink: {
497
+ liquid: "juice",
498
+ fill: 0.95
499
+ },
500
+ coconut_water: {
501
+ liquid: "water",
502
+ fill: 0.9,
503
+ box: true
504
+ },
505
+ juice_box: {
506
+ liquid: "juice",
507
+ fill: 0.95,
508
+ box: true
509
+ },
510
+ milk_carton: {
511
+ liquid: "milk",
512
+ fill: 0.95,
513
+ box: true
514
+ },
515
+ cachaca: {
516
+ liquid: "spirits",
517
+ fill: 0.9
518
+ },
519
+ communion_wine: {
520
+ liquid: "wine",
521
+ fill: 0.9
522
+ },
523
+ cooking_oil: {
524
+ liquid: "cooking_oil",
525
+ fill: 0.9
526
+ },
527
+ alcohol_70: {
528
+ liquid: "spirits",
529
+ fill: 0.9
530
+ },
531
+ honey_jar: {
532
+ liquid: "honey",
533
+ fill: 0.85
534
+ },
535
+ home_preserves: {
536
+ liquid: "water",
537
+ fill: 0.5
538
+ },
539
+ fuel_jerrycan: {
540
+ liquid: "fuel",
541
+ fill: 0.85,
542
+ box: true
543
+ },
434
544
  canned_beans: {
435
545
  liquid: "juice",
436
546
  fill: 0.4
@@ -452,6 +562,12 @@ var destruction_default = {
452
562
  liquid: "honey",
453
563
  fill: 0.9
454
564
  }
565
+ },
566
+ shapes: {
567
+ box: {
568
+ hollow: true,
569
+ welded: true
570
+ }
455
571
  }
456
572
  };
457
573
 
@@ -467,6 +583,8 @@ var DESTRUCTION_MATERIAL_BY_TAG = {
467
583
  };
468
584
  var FAMILIES = destruction_default.families;
469
585
  var CONTENTS = destruction_default.contents;
586
+ var SHAPES2 = destruction_default.shapes;
587
+ var shapeDestruction = (shapeId) => SHAPES2[shapeId];
470
588
  var FAMILY_OF_MODEL = new Map(Object.entries(FAMILIES).flatMap(([family, entry]) => entry.models.map((model) => [model, family])));
471
589
  var itemFamily = (modelId) => FAMILY_OF_MODEL.get(modelId);
472
590
  var itemDestruction = (modelId) => {
@@ -582,9 +700,9 @@ function tessellateShapes(root, rule, T) {
582
700
  root.userData.tessellationTriangles = added;
583
701
  }
584
702
  function tagForDestruction(root, options = {}) {
585
- const { modelId, weight, size, three } = options;
703
+ const { modelId, shapeId, weight, size, powder, three } = options;
586
704
  if (weight !== void 0 && weight > 0) root.userData.destructionMass = weight;
587
- const rule = modelId === void 0 ? void 0 : itemDestruction(modelId);
705
+ const rule = modelId !== void 0 ? itemDestruction(modelId) : shapeId !== void 0 ? shapeDestruction(shapeId) : void 0;
588
706
  if (rule?.tessellate) tessellateShapes(root, rule.tessellate, three);
589
707
  const solid = [];
590
708
  root.traverse((object) => {
@@ -599,6 +717,7 @@ function tagForDestruction(root, options = {}) {
599
717
  return;
600
718
  }
601
719
  if (rule?.hollow) mesh.userData.hollow = true;
720
+ if (powder) mesh.userData.destructionPowder = { color: powder.color };
602
721
  solid.push(mesh);
603
722
  });
604
723
  if (!rule?.welded || solid.length < 2) return;
@@ -3081,8 +3200,168 @@ var cannedBeans = ({ mesh, cyl, torus, G, metal, paper: paper2, food }) => {
3081
3200
  return G(mesh(cyl(0.32, 0.32, 0.52), m), mesh(cyl(0.326, 0.326, 0.34, 14), food(10172964)), mesh(cyl(0.33, 0.33, 0.06), paper2(14861162), [0, 0.02, 0]), mesh(torus(0.31, 0.025), m, [0, 0.26, 0], [Math.PI / 2, 0, 0]), mesh(torus(0.31, 0.025), m, [0, -0.26, 0], [Math.PI / 2, 0, 0]));
3082
3201
  };
3083
3202
 
3203
+ // src/data/opening.json
3204
+ var opening_default = {
3205
+ families: {
3206
+ drink_can: { style: "pull_tab" },
3207
+ food_tin: { style: "cut_lid" }
3208
+ },
3209
+ models: {
3210
+ empty_can: { style: "none" },
3211
+ canned_beans: { food: "#7a2e1c" },
3212
+ canned_corn_and_peas: { food: "#c7b03c" },
3213
+ canned_sausages: { food: "#c98d5c" },
3214
+ canned_sardines: { style: "peel_lid", food: "#8e979c" },
3215
+ condensed_milk: { food: "#e3cf9c" },
3216
+ energy_drink: { style: "pull_tab" },
3217
+ honey_jar: { style: "screw_cap", food: "#d99a1c" },
3218
+ home_preserves: { style: "screw_cap", food: "#8e1f3a" },
3219
+ cooking_oil: { style: "screw_cap", food: "#e3b830" },
3220
+ water_bottle: { style: "screw_cap" },
3221
+ water_jug_5l: { style: "screw_cap" },
3222
+ dirty_water: { style: "screw_cap" },
3223
+ sports_drink: { style: "screw_cap" },
3224
+ coconut_water: { style: "screw_cap" },
3225
+ cachaca: { style: "screw_cap" },
3226
+ communion_wine: { style: "screw_cap" }
3227
+ }
3228
+ };
3229
+
3230
+ // src/opening.ts
3231
+ var FAMILIES2 = opening_default.families;
3232
+ var MODELS = opening_default.models;
3233
+ var itemOpening = (modelId) => {
3234
+ const family = itemFamily(modelId);
3235
+ const row = { ...family === void 0 ? {} : FAMILIES2[family], ...MODELS[modelId] };
3236
+ if (row.style === void 0 || row.style === "none") return void 0;
3237
+ return { style: row.style, ...row.food === void 0 ? {} : { food: row.food } };
3238
+ };
3239
+ var OPENING_NAME = "opening";
3240
+ var INSIDE = 1315086;
3241
+ var CUT_EDGE = 14278114;
3242
+ var LID = 12172996;
3243
+ var isLid = (o) => o.userData.itemLid === true;
3244
+ var asLid = (mesh) => {
3245
+ mesh.userData.itemLid = true;
3246
+ return mesh;
3247
+ };
3248
+ var localBox = (T, root, only) => {
3249
+ root.updateMatrixWorld(true);
3250
+ const toRoot = new T.Matrix4().copy(root.matrixWorld).invert();
3251
+ const box2 = new T.Box3();
3252
+ const part = new T.Box3();
3253
+ const m = new T.Matrix4();
3254
+ root.traverse((o) => {
3255
+ const mesh = o;
3256
+ if (!mesh.isMesh || mesh.userData.destructionDebris || mesh.name === OPENING_NAME || mesh.parent?.name === OPENING_NAME) return;
3257
+ if (only && !only(mesh)) return;
3258
+ if (!mesh.geometry.boundingBox) mesh.geometry.computeBoundingBox();
3259
+ part.copy(mesh.geometry.boundingBox).applyMatrix4(m.multiplyMatrices(toRoot, mesh.matrixWorld));
3260
+ box2.union(part);
3261
+ });
3262
+ return box2;
3263
+ };
3264
+ function openItem(T, root, modelId) {
3265
+ const look = itemOpening(modelId);
3266
+ if (!look) return false;
3267
+ if (root.userData.itemOpened) return true;
3268
+ const box2 = look.style === "screw_cap" ? localBox(T, root, isLid) : localBox(T, root);
3269
+ if (box2.isEmpty()) return false;
3270
+ const size = box2.getSize(new T.Vector3());
3271
+ const centre = box2.getCenter(new T.Vector3());
3272
+ const top = look.style === "screw_cap" ? box2.min.y : box2.max.y;
3273
+ const lift = size.y * 3e-3;
3274
+ const sheet = Math.max(size.y * 6e-3, 1e-4);
3275
+ const group = new T.Group();
3276
+ group.name = OPENING_NAME;
3277
+ group.position.set(centre.x, top, centre.z);
3278
+ const material = (color, metal = 0) => new T.MeshStandardMaterial({ color, metalness: metal, roughness: metal ? 0.45 : 0.85, polygonOffset: true, polygonOffsetFactor: -2, polygonOffsetUnits: -2 });
3279
+ const add = (geometry, mat, y, parent = group) => {
3280
+ const mesh = new T.Mesh(geometry, mat);
3281
+ mesh.position.y = y;
3282
+ mesh.renderOrder = 2;
3283
+ mesh.userData.destructionDebris = true;
3284
+ mesh.userData.itemOpening = true;
3285
+ parent.add(mesh);
3286
+ return mesh;
3287
+ };
3288
+ const flat = (geometry) => geometry.rotateX(-Math.PI / 2);
3289
+ const food = look.food ?? INSIDE;
3290
+ if (look.style === "screw_cap") {
3291
+ const r = Math.min(size.x, size.z) / 2;
3292
+ root.traverse((o) => isLid(o) && (o.visible = false));
3293
+ add(flat(new T.CircleGeometry(r * 0.85, 24)), material(INSIDE), lift);
3294
+ if (look.food !== void 0) add(flat(new T.CircleGeometry(r * 0.7, 24)), material(look.food), lift * 2).userData.itemFood = true;
3295
+ } else if (look.style === "pull_tab") {
3296
+ const r = Math.min(size.x, size.z) / 2;
3297
+ const mouth = add(flat(new T.CircleGeometry(r * 0.2, 24)), material(INSIDE), lift);
3298
+ mouth.scale.set(1.15, 1, 0.85);
3299
+ mouth.position.z = r * 0.52;
3300
+ } else if (look.style === "cut_lid") {
3301
+ const r = Math.min(size.x, size.z) / 2;
3302
+ add(flat(new T.CircleGeometry(r * 0.88, 40)), material(INSIDE), lift);
3303
+ add(flat(new T.CircleGeometry(r * 0.76, 40)), material(food), lift * 2).userData.itemFood = true;
3304
+ add(flat(new T.RingGeometry(r * 0.86, r * 0.9, 40)), material(CUT_EDGE, 0.9), lift * 3);
3305
+ const hinge = new T.Group();
3306
+ hinge.position.set(0, lift, -r * 0.86);
3307
+ hinge.rotation.x = -1.95;
3308
+ group.add(hinge);
3309
+ const lid = add(new T.CylinderGeometry(r * 0.86, r * 0.86, sheet, 40), material(LID, 0.4), 0, hinge);
3310
+ lid.position.z = r * 0.86;
3311
+ } else {
3312
+ const long = size.x >= size.z ? "x" : "z";
3313
+ const length = Math.max(size.x, size.z);
3314
+ const width = Math.min(size.x, size.z);
3315
+ const plane = (a, b) => flat(long === "x" ? new T.PlaneGeometry(a, b) : new T.PlaneGeometry(b, a));
3316
+ add(plane(length * 0.92, width * 0.86), material(INSIDE), lift);
3317
+ add(plane(length * 0.84, width * 0.76), material(food), lift * 2).userData.itemFood = true;
3318
+ const hinge = new T.Group();
3319
+ const end = length * 0.46;
3320
+ if (long === "x") {
3321
+ hinge.position.set(end, lift, 0);
3322
+ hinge.rotation.z = -1.7;
3323
+ } else {
3324
+ hinge.position.set(0, lift, end);
3325
+ hinge.rotation.x = 1.7;
3326
+ }
3327
+ group.add(hinge);
3328
+ const lid = add(long === "x" ? new T.BoxGeometry(length * 0.9, sheet, width * 0.84) : new T.BoxGeometry(width * 0.84, sheet, length * 0.9), material(LID, 0.4), 0, hinge);
3329
+ if (long === "x") lid.position.x = -length * 0.45;
3330
+ else lid.position.z = -length * 0.45;
3331
+ }
3332
+ root.add(group);
3333
+ root.userData.itemOpened = true;
3334
+ return true;
3335
+ }
3336
+ var CAP_OFF = 1.6;
3337
+ function turnItem(root, modelId, state) {
3338
+ if (itemOpening(modelId)?.style !== "screw_cap") return false;
3339
+ root.traverse((o) => {
3340
+ var _a;
3341
+ if (!isLid(o)) return;
3342
+ const mesh = o;
3343
+ const base = (_a = mesh.userData).lidBase ?? (_a.lidBase = { y: mesh.position.y, turn: mesh.rotation.y });
3344
+ if (!mesh.geometry.boundingBox) mesh.geometry.computeBoundingBox();
3345
+ const height = (mesh.geometry.boundingBox.max.y - mesh.geometry.boundingBox.min.y) * mesh.scale.y;
3346
+ mesh.rotation.y = base.turn + state.turned;
3347
+ mesh.position.y = base.y + Math.min(1, Math.max(0, state.lift)) * height * CAP_OFF;
3348
+ });
3349
+ return true;
3350
+ }
3351
+ function setOpeningContents(root, share) {
3352
+ const opening = root.getObjectByName(OPENING_NAME);
3353
+ if (!opening) return;
3354
+ const left = Math.max(0, Math.min(1, share));
3355
+ opening.traverse((o) => {
3356
+ if (!o.userData.itemFood) return;
3357
+ o.visible = left > 0.02;
3358
+ const k = 0.35 + 0.65 * left;
3359
+ o.scale.set(k, 1, k);
3360
+ });
3361
+ }
3362
+
3084
3363
  // src/models/water-bottle.ts
3085
- var waterBottle = ({ mesh, cyl, lathe, G, plastic, paper: paper2, glass }) => G(mesh(lathe([[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)), mesh(cyl(0.085, 0.085, 0.1, 12), plastic(2907530), [0, 0.6, 0]), mesh(cyl(0.225, 0.225, 0.22, 16), paper2(15330543), [0, -0.15, 0]), mesh(cyl(0.226, 0.226, 0.05, 16), plastic(3831976), [0, -0.12, 0]));
3364
+ var waterBottle = ({ mesh, cyl, lathe, G, plastic, paper: paper2, glass }) => G(mesh(lathe([[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(mesh(cyl(0.085, 0.085, 0.1, 12), plastic(2907530), [0, 0.6, 0])), mesh(cyl(0.225, 0.225, 0.22, 16), paper2(15330543), [0, -0.15, 0]), mesh(cyl(0.226, 0.226, 0.05, 16), plastic(3831976), [0, -0.12, 0]));
3086
3365
 
3087
3366
  // src/models/rice.ts
3088
3367
  var rice = ({ mesh, bx, G, paper: paper2, food }) => G(mesh(bx(0.7, 0.95, 0.28), paper2(15129792)), mesh(bx(0.72, 0.12, 0.1), paper2(14010792), [0, 0.52, 0]), mesh(bx(0.5, 0.3, 0.02), food(11883039), [0, 0.05, 0.15]), mesh(bx(0.3, 0.1, 0.02), paper2(4160069), [0, -0.25, 0.15]));
@@ -3091,7 +3370,7 @@ var rice = ({ mesh, bx, G, paper: paper2, food }) => G(mesh(bx(0.7, 0.95, 0.28),
3091
3370
  var militaryRation = ({ mesh, bx, G, plastic, paper: paper2 }) => G(mesh(bx(0.75, 1, 0.22), plastic(5923142)), mesh(bx(0.77, 0.1, 0.23), plastic(4672823), [0, 0.48, 0]), mesh(bx(0.5, 0.14, 0.01), paper2(2237727), [0, 0.18, 0.115]), mesh(bx(0.36, 0.06, 0.01), paper2(2237727), [0, -0.02, 0.115]));
3092
3371
 
3093
3372
  // src/models/cachaca.ts
3094
- var cachaca = ({ mesh, cyl, lathe, G, wood, paper: paper2, food, glass }) => G(mesh(lathe([[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)), mesh(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0]), mesh(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
3373
+ var cachaca = ({ mesh, cyl, lathe, G, wood, paper: paper2, food, glass }) => G(mesh(lathe([[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(mesh(cyl(0.08, 0.07, 0.14, 10), wood(9071172), [0, 0.64, 0])), mesh(cyl(0.275, 0.275, 0.34, 16), paper2(15327692), [0, -0.25, 0]), mesh(cyl(0.276, 0.276, 0.06, 16), food(8007455), [0, -0.15, 0]));
3095
3374
 
3096
3375
  // src/models/first-aid-kit.ts
3097
3376
  var firstAidKit = ({ mesh, bx, torus, G, plastic }) => {
@@ -3307,7 +3586,7 @@ var homePreserves = ({ mesh, cyl, G, glass, metal, paper: paper2, food, cloth: c
3307
3586
  mesh(cyl(0.41, 0.41, 0.86, 12), food(9314106), [0, -0.2, 0]),
3308
3587
  mesh(cyl(0.34, 0.445, 0.12, 12), glass(13625070, 0.5), [0, 0.39, 0]),
3309
3588
  mesh(cyl(0.34, 0.34, 0.05, 12), glass(13625070, 0.5), [0, 0.475, 0]),
3310
- mesh(cyl(0.38, 0.38, 0.15, 12), metal(12163644), [0, 0.575, 0]),
3589
+ asLid(mesh(cyl(0.38, 0.38, 0.15, 12), metal(12163644), [0, 0.575, 0])),
3311
3590
  mesh(cyl(0.45, 0.45, 0.4, 12), paper2(15327172), [0, -0.2, 0]),
3312
3591
  mesh(cyl(0.452, 0.452, 0.08, 12), cloth2(10172986), [0, -0.2, 0])
3313
3592
  );
@@ -3512,7 +3791,7 @@ var honeyJar = ({ mesh, cyl, G, glass, metal, paper: paper2, food }) => G(
3512
3791
  mesh(cyl(0.395, 0.395, 0.68, 12), glass(14925936, 0.55), [0, -0.16, 0]),
3513
3792
  mesh(cyl(0.37, 0.37, 0.6, 12), food(14260764), [0, -0.2, 0]),
3514
3793
  mesh(cyl(0.3, 0.395, 0.14, 12), glass(14925936, 0.55), [0, 0.25, 0]),
3515
- mesh(cyl(0.3, 0.3, 0.18, 12), metal(7031324), [0, 0.41, 0]),
3794
+ asLid(mesh(cyl(0.3, 0.3, 0.18, 12), metal(7031324), [0, 0.41, 0])),
3516
3795
  mesh(cyl(0.4, 0.4, 0.34, 12), paper2(15853512), [0, -0.2, 0]),
3517
3796
  mesh(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.1, 0]),
3518
3797
  mesh(cyl(0.402, 0.402, 0.05, 12), paper2(9067028), [0, -0.3, 0])
@@ -3616,7 +3895,7 @@ var cookingOil = ({ mesh, cyl, G, glass, plastic, paper: paper2 }) => G(
3616
3895
  mesh(cyl(0.345, 0.345, 1.7, 12), glass(14923824, 0.65), [0, -0.45, 0]),
3617
3896
  mesh(cyl(0.12, 0.345, 0.5, 12), glass(14923824, 0.65), [0, 0.65, 0]),
3618
3897
  mesh(cyl(0.12, 0.12, 0.25, 12), glass(14923824, 0.65), [0, 1.025, 0]),
3619
- mesh(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0]),
3898
+ asLid(mesh(cyl(0.14, 0.14, 0.15, 12), plastic(4160042), [0, 1.225, 0])),
3620
3899
  mesh(cyl(0.35, 0.35, 0.8, 12), paper2(15853512), [0, -0.5, 0]),
3621
3900
  mesh(cyl(0.352, 0.352, 0.2, 12), paper2(4160042), [0, -0.5, 0])
3622
3901
  );
@@ -3654,7 +3933,7 @@ var waterJug5l = ({ mesh, bx, cyl, G, plastic, paper: paper2, glass }) => {
3654
3933
  const body = mesh(bx(1.5, 2.3, 1.5), glass(10473710, 0.75), [0, -0.5, 0]);
3655
3934
  const shoulder = mesh(bx(1.2, 0.45, 1.2), glass(10473710, 0.75), [0, 0.875, 0]);
3656
3935
  const neck = mesh(cyl(0.34, 0.34, 0.3, 12), dark, [0, 1.25, 0]);
3657
- const cap = mesh(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]);
3936
+ const cap = asLid(mesh(cyl(0.4, 0.4, 0.25, 12), dark, [0, 1.525, 0]));
3658
3937
  const grip = mesh(bx(0.16, 0.9, 0.3), dark, [1.07, 0.6, 0]);
3659
3938
  const upperBar = mesh(bx(0.45, 0.16, 0.3), dark, [0.925, 0.97, 0]);
3660
3939
  const lowerBar = mesh(bx(0.45, 0.16, 0.3), dark, [0.925, 0.23, 0]);
@@ -3669,7 +3948,7 @@ var sportsDrink = ({ mesh, cyl, lathe, G, plastic, paper: paper2 }) => {
3669
3948
  const label = mesh(cyl(0.332, 0.332, 0.7, 12), paper2(1448738), [0, -0.12, 0]);
3670
3949
  const stripe = mesh(cyl(0.332, 0.332, 0.1, 12), paper2(15753232), [0, 0.28, 0]);
3671
3950
  const collar = mesh(cyl(0.2, 0.2, 0.04, 12), plastic(15263976), [0, 0.705, 0]);
3672
- const cap = mesh(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]);
3951
+ const cap = asLid(mesh(cyl(0.19, 0.19, 0.2, 12), plastic(15753232), [0, 0.815, 0]));
3673
3952
  return G(bottle2, label, stripe, collar, cap);
3674
3953
  };
3675
3954
 
@@ -3716,7 +3995,7 @@ var communionWine = ({ mesh, bx, cyl, lathe, G, glass, paper: paper2, metal }) =
3716
3995
  const goldBand = mesh(cyl(0.382, 0.382, 0.05, 12), metal(11896880), [0, 0.075, 0]);
3717
3996
  const crossUpright = mesh(bx(0.06, 0.26, 0.02), paper2(8003371), [0, -0.35, 0.375]);
3718
3997
  const crossArm = mesh(bx(0.18, 0.06, 0.02), paper2(8003371), [0, -0.3, 0.375]);
3719
- const foil = mesh(cyl(0.15, 0.15, 0.46, 12), metal(11896880), [0, 1.27, 0]);
3998
+ const foil = asLid(mesh(cyl(0.15, 0.15, 0.46, 12), metal(11896880), [0, 1.27, 0]));
3720
3999
  return G(bottle2, label, goldBand, crossUpright, crossArm, foil);
3721
4000
  };
3722
4001
 
@@ -3727,7 +4006,7 @@ var coconutWater = ({ mesh, bx, cyl, lathe, G, plastic, paper: paper2 }) => {
3727
4006
  const nut = mesh(cyl(0.11, 0.11, 0.03, 10), paper2(7031339), [0, -0.12, 0.285], [Math.PI / 2, 0, 0]);
3728
4007
  const leafLeft = mesh(bx(0.2, 0.05, 0.01), paper2(15135448), [-0.12, 0.08, 0.292], [0, 0, 0.5]);
3729
4008
  const leafRight = mesh(bx(0.2, 0.05, 0.01), paper2(15135448), [0.12, 0.08, 0.292], [0, 0, -0.5]);
3730
- const cap = mesh(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]);
4009
+ const cap = asLid(mesh(cyl(0.14, 0.14, 0.2, 12), plastic(4165450), [0, 0.75, 0]));
3731
4010
  return G(bottle2, label, nut, leafLeft, leafRight, cap);
3732
4011
  };
3733
4012
 
@@ -3737,7 +4016,7 @@ var dirtyWater = ({ mesh, cyl, lathe, G, plastic, paper: paper2, glass, food })
3737
4016
  const murk = mesh(lathe([[0, -0.89], [0.27, -0.89], [0.315, -0.84], [0.315, 0.1], [0, 0.1]], 12), food(7035444));
3738
4017
  const sediment = mesh(cyl(0.31, 0.27, 0.12, 8), food(4865314), [0, -0.82, 0]);
3739
4018
  const label = mesh(cyl(0.332, 0.332, 0.22, 12), paper2(13617588), [0, 0.38, 0]);
3740
- const cap = mesh(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]);
4019
+ const cap = asLid(mesh(cyl(0.19, 0.19, 0.2, 8), plastic(5922920), [0, 0.815, 0]));
3741
4020
  return G(bottle2, murk, sediment, label, cap);
3742
4021
  };
3743
4022
 
@@ -6726,7 +7005,7 @@ function buildItem(T, item, options = {}) {
6726
7005
  const materials = kit.materials.slice(first);
6727
7006
  if (options.wear) setWear(materials, options.wear);
6728
7007
  root.userData.wear = options.wear ?? 0;
6729
- 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: T });
7008
+ 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: T });
6730
7009
  markKitOrigin(root, { owner: "game-items-three", kind: "item", id: item.id, ...kind === "shape" ? { standIn: true } : {} });
6731
7010
  return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
6732
7011
  }
@@ -7109,7 +7388,15 @@ function createItemsTestModule(options) {
7109
7388
  const provider = options.provider ?? createFindsProvider(catalog);
7110
7389
  const items = catalog.list();
7111
7390
  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));
7112
- const shelves = shelvesLayout(items, categories);
7391
+ const openerOf = (item) => {
7392
+ const id = item.seal?.openWith?.find((tool2) => catalog.has(tool2));
7393
+ return id === void 0 ? void 0 : catalog.get(id);
7394
+ };
7395
+ const laid = items.flatMap((item) => {
7396
+ const opener = openerOf(item);
7397
+ return opener ? [item, { id: opener.id, category: item.category, size: opener.size }] : [item];
7398
+ });
7399
+ const shelves = shelvesLayout(laid, categories);
7113
7400
  const showcase = listItemModels().map((id) => ({ id, size: sizeOfModel(id), def: catalog.has(id) ? catalog.get(id) : void 0 }));
7114
7401
  const state = {
7115
7402
  category: CATEGORY_ALL,
@@ -7160,12 +7447,27 @@ function createItemsTestModule(options) {
7160
7447
  const contents = itemContents(subject);
7161
7448
  return contents ? { fluid: { container: contents.container, part: SHELL_PART_ID } } : {};
7162
7449
  };
7450
+ const sealOf = (def, fluid) => {
7451
+ const model = def.model;
7452
+ if (!def.seal || model === void 0 || !itemOpening(model)) return fluid;
7453
+ const openWith = def.seal.openWith ? [...def.seal.openWith, ...def.seal.openWith.map((id) => `${ITEM_PREFIX}${id}`)] : void 0;
7454
+ const seal = {
7455
+ ...def.seal,
7456
+ ...openWith ? { openWith } : {},
7457
+ open: (root) => void (latestTHREE && openItem(latestTHREE, root, model)),
7458
+ contents: (root, share) => setOpeningContents(root, share),
7459
+ // a screw cap is turned off by the other hand before the first bite or sip
7460
+ ...itemOpening(model)?.style === "screw_cap" ? { openUse: "unscrew", turn: (root, state2) => void turnItem(root, model, state2) } : {}
7461
+ };
7462
+ const eaten = def.category === "food" && fluid.fluid ? { fluid: { ...fluid.fluid, uses: ["eat", "pour_out"] } } : fluid;
7463
+ return { ...eaten, seal };
7464
+ };
7163
7465
  const itemOf = (def) => ({
7164
7466
  id: `${ITEM_PREFIX}${def.id}`,
7165
7467
  name: nameOf(def),
7166
7468
  movable: true,
7167
7469
  thumbnail: itemThumbnailSvg(drawable(def)),
7168
- ...fluidOf({ ...def.model === void 0 ? {} : { model: def.model }, size: def.size, weight: def.weight }),
7470
+ ...sealOf(def, fluidOf({ ...def.model === void 0 ? {} : { model: def.model }, size: def.size, weight: def.weight })),
7169
7471
  build: (THREE) => {
7170
7472
  const T = THREE;
7171
7473
  latestTHREE = T;
@@ -7529,7 +7831,7 @@ function createItemsTestModule(options) {
7529
7831
  id: "modeled",
7530
7832
  name: text.metrics.modeled,
7531
7833
  read: () => {
7532
- const shown = visibleEntries().filter((entry) => entry.group === "shelf");
7834
+ const shown = [...new Map(visibleEntries().filter((entry) => entry.group === "shelf").map((entry) => [entry.itemId, entry])).values()];
7533
7835
  return `${shown.filter((entry) => entry.kind === "model").length}/${shown.length}`;
7534
7836
  }
7535
7837
  },
@@ -7656,6 +7958,7 @@ export {
7656
7958
  ITEM_FAMILIES,
7657
7959
  ITEM_PREFIX,
7658
7960
  MODEL_BUDGET_DEVIATIONS,
7961
+ OPENING_NAME,
7659
7962
  ROW_LIMIT,
7660
7963
  RULER_HEIGHT,
7661
7964
  RULER_ID,
@@ -7669,6 +7972,7 @@ export {
7669
7972
  WEAR_AMOUNT,
7670
7973
  WEAR_TAGS,
7671
7974
  WEAR_TARGET,
7975
+ asLid,
7672
7976
  buildItem,
7673
7977
  buildItemLod,
7674
7978
  canonicalFrame,
@@ -7687,14 +7991,18 @@ export {
7687
7991
  itemFamily,
7688
7992
  itemModelBuilder,
7689
7993
  itemModelEntry,
7994
+ itemOpening,
7690
7995
  itemThumbnail,
7691
7996
  itemThumbnailSvg,
7692
7997
  listItemModels,
7693
7998
  mergeItem,
7999
+ openItem,
7694
8000
  registerItemModel,
7695
8001
  renderItemThumbnail,
8002
+ setOpeningContents,
7696
8003
  setWear,
7697
8004
  shade,
8005
+ shapeDestruction,
7698
8006
  shapeOf,
7699
8007
  shapeThumbnailSvg,
7700
8008
  shelvesLayout,
@@ -7702,6 +8010,7 @@ export {
7702
8010
  tagForDestruction,
7703
8011
  thumbnailPatch,
7704
8012
  triangleBudgetOf,
8013
+ turnItem,
7705
8014
  unregisterItemModel,
7706
8015
  wearableMaterials
7707
8016
  };