@adatechnology/game-contracts 0.13.0 → 0.15.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 +16 -1
- package/dist/index.d.ts +552 -3
- package/dist/index.global.js +259 -2
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +259 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.global.js
CHANGED
|
@@ -21,14 +21,19 @@ var GameContracts = (() => {
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
DEFAULT_BLAST_SHOVE: () => DEFAULT_BLAST_SHOVE,
|
|
24
25
|
DEFAULT_ENVIRONMENT: () => DEFAULT_ENVIRONMENT,
|
|
26
|
+
DEFAULT_IGNITION_RULES: () => DEFAULT_IGNITION_RULES,
|
|
25
27
|
IMPACT_CONTRACT: () => IMPACT_CONTRACT,
|
|
28
|
+
KIT_ORIGIN_KEY: () => KIT_ORIGIN_KEY,
|
|
26
29
|
MISSING_BITS: () => MISSING_BITS,
|
|
27
30
|
STAGE_EVENT: () => STAGE_EVENT,
|
|
28
31
|
TOOL_CLIP: () => TOOL_CLIP,
|
|
29
32
|
applyPatches: () => applyPatches,
|
|
33
|
+
blastShove: () => blastShove,
|
|
30
34
|
checkAnimation: () => checkAnimation,
|
|
31
35
|
checkBench: () => checkBench,
|
|
36
|
+
checkCategorized: () => checkCategorized,
|
|
32
37
|
checkDestructible: () => checkDestructible,
|
|
33
38
|
checkHit: () => checkHit,
|
|
34
39
|
checkSpec: () => checkSpec,
|
|
@@ -36,20 +41,49 @@ var GameContracts = (() => {
|
|
|
36
41
|
createEnvironmentBus: () => createEnvironmentBus,
|
|
37
42
|
createToolClipPlayer: () => createToolClipPlayer,
|
|
38
43
|
deepMerge: () => deepMerge,
|
|
44
|
+
defineCategories: () => defineCategories,
|
|
45
|
+
fillFactor: () => fillFactor,
|
|
46
|
+
ignitionChance: () => ignitionChance,
|
|
47
|
+
kitOriginOf: () => kitOriginOf,
|
|
48
|
+
markKitOrigin: () => markKitOrigin,
|
|
39
49
|
mergeById: () => mergeById,
|
|
40
50
|
removeIds: () => removeIds,
|
|
51
|
+
resolveEntry: () => resolveEntry,
|
|
52
|
+
resolveIgnitionRules: () => resolveIgnitionRules,
|
|
41
53
|
sampleClip: () => sampleClip,
|
|
42
54
|
scaleClip: () => scaleClip,
|
|
55
|
+
shotExtras: () => shotExtras,
|
|
43
56
|
shotToHit: () => shotToHit,
|
|
44
|
-
|
|
57
|
+
sparkTargetFactor: () => sparkTargetFactor,
|
|
58
|
+
toolAnimationStep: () => toolAnimationStep,
|
|
59
|
+
unmarkedDrawings: () => unmarkedDrawings,
|
|
60
|
+
useToHit: () => useToHit
|
|
45
61
|
});
|
|
46
62
|
|
|
63
|
+
// src/blast-shove.ts
|
|
64
|
+
var DEFAULT_BLAST_SHOVE = { coupling: 0.02, maxSpeed: 20, lift: 0.3, minSpeed: 0.3, falloffPower: 2 };
|
|
65
|
+
function blastShove(blast, target, options = {}) {
|
|
66
|
+
const o = { ...DEFAULT_BLAST_SHOVE, ...options };
|
|
67
|
+
const dx = target.position[0] - blast.point[0];
|
|
68
|
+
const dy = target.position[1] - blast.point[1];
|
|
69
|
+
const dz = target.position[2] - blast.point[2];
|
|
70
|
+
const distance = Math.hypot(dx, dy, dz);
|
|
71
|
+
if (!(blast.radius > 0) || distance > blast.radius) return void 0;
|
|
72
|
+
const falloff = (1 - distance / blast.radius) ** o.falloffPower;
|
|
73
|
+
const mass = Math.max(target.mass, 0.05);
|
|
74
|
+
const speed = Math.min(o.maxSpeed, Math.sqrt(2 * blast.energy * (blast.reach ?? 1) * o.coupling / mass)) * falloff;
|
|
75
|
+
if (!(speed >= o.minSpeed)) return void 0;
|
|
76
|
+
const direction = distance > 1e-4 ? [dx / distance, dy / distance + o.lift, dz / distance] : [0, 1, 0];
|
|
77
|
+
const length = Math.hypot(direction[0], direction[1], direction[2]);
|
|
78
|
+
return { speed, direction: [direction[0] / length, direction[1] / length, direction[2] / length], distance, falloff };
|
|
79
|
+
}
|
|
80
|
+
|
|
47
81
|
// src/impact.ts
|
|
48
82
|
var IMPACT_CONTRACT = {
|
|
49
83
|
ballistic: { required: ["type", "point", "direction", "energy", "caliber"], optional: ["space", "source"], note: "bullets and pellets: joules and diameter in millimetres" },
|
|
50
84
|
blunt: { required: ["type", "point", "energy"], optional: ["direction", "radius", "space", "source"], note: "contact radius in metres (default 3 cm)" },
|
|
51
85
|
slash: { required: ["type", "point", "energy"], optional: ["direction", "radius", "space", "source"], note: "axes, knives, machetes" },
|
|
52
|
-
explosive: { required: ["type", "point", "energy", "radius"], optional: ["space", "source"], note: "point is the centre of the blast, radius the range in metres" },
|
|
86
|
+
explosive: { required: ["type", "point", "energy", "radius"], optional: ["lethalRadius", "space", "source"], note: "point is the centre of the blast, radius the range in metres" },
|
|
53
87
|
fire: { required: ["type", "point", "temperature", "radius"], optional: ["duration", "space", "source"], note: "flame temperature in \xB0C; duration in seconds (call every frame with `dt`)" },
|
|
54
88
|
heat: { required: ["type", "point", "temperature", "radius"], optional: ["duration", "space", "source"], note: "heat without flame (cutting torch, hot plate)" }
|
|
55
89
|
};
|
|
@@ -272,6 +306,19 @@ var GameContracts = (() => {
|
|
|
272
306
|
};
|
|
273
307
|
|
|
274
308
|
// src/weapons.ts
|
|
309
|
+
var shotExtras = (shot, distance = 0) => {
|
|
310
|
+
const out = {};
|
|
311
|
+
if (shot.penetration) {
|
|
312
|
+
const k = shot.energy > 0 ? Math.min(1, Math.max(0, shot.energyAt(distance) / shot.energy)) : 1;
|
|
313
|
+
const scaled = {};
|
|
314
|
+
for (const key of Object.keys(shot.penetration)) scaled[key] = shot.penetration[key] * k;
|
|
315
|
+
out.penetration = scaled;
|
|
316
|
+
}
|
|
317
|
+
if (shot.incendiary) out.incendiary = true;
|
|
318
|
+
if (shot.tracer) out.tracer = true;
|
|
319
|
+
if (shot.ignition) out.ignition = { spark: shot.ignition.spark, flame: shot.ignition.flame };
|
|
320
|
+
return out;
|
|
321
|
+
};
|
|
275
322
|
var shotToHit = (shot, point, normal) => {
|
|
276
323
|
void normal;
|
|
277
324
|
const dx = point[0] - shot.origin[0];
|
|
@@ -285,9 +332,79 @@ var GameContracts = (() => {
|
|
|
285
332
|
energy: shot.energyAt(distance),
|
|
286
333
|
caliber: shot.caliber,
|
|
287
334
|
...shot.mass === void 0 ? {} : { mass: shot.mass },
|
|
335
|
+
...shotExtras(shot, distance),
|
|
288
336
|
source: shot.source ?? shot.weapon
|
|
289
337
|
};
|
|
290
338
|
};
|
|
339
|
+
var useToHit = (use, point, normal) => {
|
|
340
|
+
void normal;
|
|
341
|
+
const p = [point[0], point[1], point[2]];
|
|
342
|
+
switch (use.kind) {
|
|
343
|
+
case "melee":
|
|
344
|
+
return { type: use.damage, point: p, direction: [use.direction[0], use.direction[1], use.direction[2]], energy: use.energy, radius: use.radius };
|
|
345
|
+
case "flame":
|
|
346
|
+
case "heat":
|
|
347
|
+
return {
|
|
348
|
+
type: use.kind === "flame" ? "fire" : "heat",
|
|
349
|
+
point: p,
|
|
350
|
+
direction: [use.direction[0], use.direction[1], use.direction[2]],
|
|
351
|
+
temperature: use.temperature,
|
|
352
|
+
radius: Math.min(use.radius, 0.3),
|
|
353
|
+
duration: use.duration
|
|
354
|
+
};
|
|
355
|
+
case "throw":
|
|
356
|
+
if (use.energy > 0) return { type: "explosive", point: p, energy: use.energy, radius: use.radius };
|
|
357
|
+
if (use.fire) return { type: "fire", point: p, temperature: use.fire.temperature, radius: use.fire.radius, duration: use.fire.seconds };
|
|
358
|
+
return null;
|
|
359
|
+
default:
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/ignition.ts
|
|
365
|
+
var DEFAULT_IGNITION_RULES = {
|
|
366
|
+
fillCurve: [
|
|
367
|
+
[0, 0.6],
|
|
368
|
+
[0.1, 1],
|
|
369
|
+
[0.35, 0.6],
|
|
370
|
+
[0.65, 0.3],
|
|
371
|
+
[0.9, 0.15],
|
|
372
|
+
[1, 0.12]
|
|
373
|
+
],
|
|
374
|
+
flameFillInfluence: 0.5,
|
|
375
|
+
flameDefaults: { incendiary: 0.7, tracer: 0.2 },
|
|
376
|
+
sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },
|
|
377
|
+
sparkMetalDefault: 0.5,
|
|
378
|
+
sparkFluid: { diesel: 0.15 },
|
|
379
|
+
fireNear: { radius: 1.5, chancePerSecond: 0.5 }
|
|
380
|
+
};
|
|
381
|
+
var resolveIgnitionRules = (rules) => ({
|
|
382
|
+
...DEFAULT_IGNITION_RULES,
|
|
383
|
+
...rules,
|
|
384
|
+
flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },
|
|
385
|
+
sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },
|
|
386
|
+
sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },
|
|
387
|
+
fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear }
|
|
388
|
+
});
|
|
389
|
+
var fillFactor = (fill, curve = DEFAULT_IGNITION_RULES.fillCurve) => {
|
|
390
|
+
if (curve.length === 0) return 1;
|
|
391
|
+
const f = Math.min(1, Math.max(0, fill));
|
|
392
|
+
if (f <= curve[0][0]) return curve[0][1];
|
|
393
|
+
for (let i = 1; i < curve.length; i++) {
|
|
394
|
+
const [x1, y1] = curve[i];
|
|
395
|
+
const [x0, y0] = curve[i - 1];
|
|
396
|
+
if (f <= x1) return x1 === x0 ? y1 : y0 + (y1 - y0) * (f - x0) / (x1 - x0);
|
|
397
|
+
}
|
|
398
|
+
return curve[curve.length - 1][1];
|
|
399
|
+
};
|
|
400
|
+
var sparkTargetFactor = (materialId, metal, rules = DEFAULT_IGNITION_RULES) => !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault;
|
|
401
|
+
var ignitionChance = (hit, target, rules = DEFAULT_IGNITION_RULES) => {
|
|
402
|
+
const k = fillFactor(target.fill, rules.fillCurve);
|
|
403
|
+
const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === void 0 ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k;
|
|
404
|
+
const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0);
|
|
405
|
+
const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
|
|
406
|
+
return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
|
|
407
|
+
};
|
|
291
408
|
|
|
292
409
|
// src/animation.ts
|
|
293
410
|
var finite3 = (v) => Array.isArray(v) && v.length === 3 && v.every((n) => typeof n === "number" && Number.isFinite(n));
|
|
@@ -503,6 +620,9 @@ var GameContracts = (() => {
|
|
|
503
620
|
},
|
|
504
621
|
get playing() {
|
|
505
622
|
return instances.map((instance) => instance.name);
|
|
623
|
+
},
|
|
624
|
+
get running() {
|
|
625
|
+
return instances.filter((instance) => instance.delay <= 0).map((instance) => ({ name: instance.name, time: instance.time, clip: instance.clip }));
|
|
506
626
|
}
|
|
507
627
|
};
|
|
508
628
|
};
|
|
@@ -616,6 +736,143 @@ var GameContracts = (() => {
|
|
|
616
736
|
}
|
|
617
737
|
};
|
|
618
738
|
}
|
|
739
|
+
|
|
740
|
+
// src/categories.ts
|
|
741
|
+
var defineCategories = (defs, options = {}) => {
|
|
742
|
+
const problems = [];
|
|
743
|
+
const duplicates = (list) => {
|
|
744
|
+
const ids = /* @__PURE__ */ new Set();
|
|
745
|
+
for (const def of list) {
|
|
746
|
+
if (ids.has(def.id)) problems.push(`duplicate category "${def.id}"`);
|
|
747
|
+
ids.add(def.id);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
duplicates(defs);
|
|
751
|
+
duplicates(options.extra ?? []);
|
|
752
|
+
const entries = mergeById(defs, options.extra);
|
|
753
|
+
applyPatches(entries, options.patches);
|
|
754
|
+
removeIds(entries, options.remove);
|
|
755
|
+
const chain = (id) => {
|
|
756
|
+
const path = [];
|
|
757
|
+
let current = id;
|
|
758
|
+
while (current !== void 0) {
|
|
759
|
+
const def = entries.get(current);
|
|
760
|
+
if (!def) throw new Error(path.length === 0 ? `unknown category "${current}"` : `category "${path[0]}" has unknown parent "${current}"`);
|
|
761
|
+
if (path.includes(current)) throw new Error(`category cycle: ${[...path, current].reverse().join(" -> ")}`);
|
|
762
|
+
path.push(current);
|
|
763
|
+
current = def.extends;
|
|
764
|
+
}
|
|
765
|
+
return path.reverse();
|
|
766
|
+
};
|
|
767
|
+
const registry = {
|
|
768
|
+
get: (id) => entries.get(id),
|
|
769
|
+
has: (id) => entries.has(id),
|
|
770
|
+
ids: () => [...entries.keys()],
|
|
771
|
+
chain,
|
|
772
|
+
defaults: (id) => chain(id).reduce((acc, ancestor) => deepMerge(acc, entries.get(ancestor)?.defaults), {}),
|
|
773
|
+
requires: (id) => {
|
|
774
|
+
const out = [];
|
|
775
|
+
for (const ancestor of chain(id)) for (const contract of entries.get(ancestor)?.requires ?? []) if (!out.includes(contract)) out.push(contract);
|
|
776
|
+
return out;
|
|
777
|
+
},
|
|
778
|
+
isA: (id, ancestorId) => {
|
|
779
|
+
try {
|
|
780
|
+
return chain(id).includes(ancestorId);
|
|
781
|
+
} catch {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
check: () => {
|
|
786
|
+
const found = [...problems];
|
|
787
|
+
for (const [id, def] of entries) {
|
|
788
|
+
if (def.extends !== void 0 && !entries.has(def.extends)) found.push(`category "${id}" extends unknown "${def.extends}"`);
|
|
789
|
+
else {
|
|
790
|
+
try {
|
|
791
|
+
chain(id);
|
|
792
|
+
} catch (error) {
|
|
793
|
+
const message = error.message;
|
|
794
|
+
if (message.startsWith("category cycle") && !found.includes(message)) found.push(message);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return found;
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
return registry;
|
|
802
|
+
};
|
|
803
|
+
var resolveEntry = (registry, entry, kindField = "kind") => deepMerge(registry.defaults(String(entry[kindField])), entry);
|
|
804
|
+
var checkCategorized = (entries, registry, checkers, options = {}) => {
|
|
805
|
+
const kindField = options.kindField ?? "kind";
|
|
806
|
+
const byId = new Map(checkers.map((checker) => [checker.id, checker]));
|
|
807
|
+
const problems = [...registry.check()];
|
|
808
|
+
const allowed = [];
|
|
809
|
+
for (const entry of entries) {
|
|
810
|
+
const kind = String(entry[kindField]);
|
|
811
|
+
if (!registry.has(kind)) {
|
|
812
|
+
problems.push(`"${entry.id}" has unknown kind "${kind}"`);
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (registry.get(kind)?.abstract) {
|
|
816
|
+
problems.push(`"${entry.id}" has abstract kind "${kind}"`);
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
let path;
|
|
820
|
+
try {
|
|
821
|
+
path = registry.chain(kind);
|
|
822
|
+
} catch (error) {
|
|
823
|
+
problems.push(`"${entry.id}" (${kind}): ${error.message}`);
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
const resolved = resolveEntry(registry, entry, kindField);
|
|
827
|
+
for (const contract of registry.requires(kind)) {
|
|
828
|
+
const owner = path.find((ancestor) => registry.get(ancestor)?.requires?.includes(contract)) ?? kind;
|
|
829
|
+
const checker = byId.get(contract);
|
|
830
|
+
if (!checker) {
|
|
831
|
+
problems.push(`kind "${kind}" requires contract "${contract}" but no checker is registered`);
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
const prefix = `"${entry.id}" (${kind}): `;
|
|
835
|
+
for (const problem of checker.check(resolved, { kind, chain: path })) {
|
|
836
|
+
const text = `${prefix}${problem}, required by ${owner}`;
|
|
837
|
+
if ((options.allow?.[entry.id] ?? []).some((start) => problem.startsWith(start))) allowed.push(text);
|
|
838
|
+
else problems.push(text);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return { ok: problems.length === 0, problems: [...new Set(problems)], allowed };
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// src/origin.ts
|
|
846
|
+
var KIT_ORIGIN_KEY = "kitOrigin";
|
|
847
|
+
function markKitOrigin(object, origin) {
|
|
848
|
+
const node = object;
|
|
849
|
+
node.userData = node.userData ?? {};
|
|
850
|
+
node.userData[KIT_ORIGIN_KEY] = { ...origin };
|
|
851
|
+
return object;
|
|
852
|
+
}
|
|
853
|
+
function kitOriginOf(object) {
|
|
854
|
+
for (let node = object; node; node = node.parent) {
|
|
855
|
+
const origin = node.userData?.[KIT_ORIGIN_KEY];
|
|
856
|
+
if (origin && typeof origin.owner === "string") return origin;
|
|
857
|
+
}
|
|
858
|
+
return null;
|
|
859
|
+
}
|
|
860
|
+
function unmarkedDrawings(root) {
|
|
861
|
+
const out = [];
|
|
862
|
+
const pathOf = (node) => {
|
|
863
|
+
const names = [];
|
|
864
|
+
for (let n = node; n && n !== root; n = n.parent) if (n.name) names.push(n.name);
|
|
865
|
+
return names.reverse().join(" > ") || "(no name)";
|
|
866
|
+
};
|
|
867
|
+
const walk = (node, marked) => {
|
|
868
|
+
if (node.visible === false) return;
|
|
869
|
+
const here = marked || node !== root && Boolean(node.userData?.[KIT_ORIGIN_KEY]?.owner);
|
|
870
|
+
if (!here && (node.isMesh || node.isPoints || node.isLine || node.isSprite)) out.push({ path: pathOf(node), object: node });
|
|
871
|
+
for (const child of node.children ?? []) walk(child, here);
|
|
872
|
+
};
|
|
873
|
+
walk(root, false);
|
|
874
|
+
return out;
|
|
875
|
+
}
|
|
619
876
|
return __toCommonJS(index_exports);
|
|
620
877
|
})();
|
|
621
878
|
//# sourceMappingURL=index.global.js.map
|