@combos-fun/engine 0.0.38 → 0.0.39

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.
@@ -1001,7 +1001,7 @@ class Scene extends GameObject {
1001
1001
  }
1002
1002
 
1003
1003
  /** Generated at build from package.json */
1004
- const version = "0.0.38";
1004
+ const version = "0.0.39";
1005
1005
 
1006
1006
  /**
1007
1007
  * Sent to `window.parent` after each `System.init` completes during `Game.addSystem`
@@ -1642,6 +1642,105 @@ class Progress extends EventEmitter__default.default {
1642
1642
  }
1643
1643
  }
1644
1644
 
1645
+ const SLOT_DEFAULT_TYPE = {
1646
+ image: 'png',
1647
+ json: 'json',
1648
+ tex: 'tex',
1649
+ ske: 'ske',
1650
+ audio: 'audio',
1651
+ video: 'video',
1652
+ };
1653
+ const EXT_TO_TYPE = {
1654
+ png: 'png',
1655
+ jpg: 'jpg',
1656
+ jpeg: 'jpeg',
1657
+ webp: 'webp',
1658
+ json: 'json',
1659
+ tex: 'tex',
1660
+ ske: 'ske',
1661
+ mp3: 'audio',
1662
+ wav: 'audio',
1663
+ aac: 'audio',
1664
+ ogg: 'audio',
1665
+ mp4: 'video',
1666
+ webm: 'video',
1667
+ glb: 'glb',
1668
+ gltf: 'gltf',
1669
+ };
1670
+ function extensionOf(url) {
1671
+ const path = url.split('?')[0].split('#')[0];
1672
+ const dot = path.lastIndexOf('.');
1673
+ if (dot < 0)
1674
+ return '';
1675
+ return path.slice(dot + 1).toLowerCase();
1676
+ }
1677
+ function typeFromUrl(url, slot) {
1678
+ return EXT_TO_TYPE[extensionOf(url)] || SLOT_DEFAULT_TYPE[slot] || 'png';
1679
+ }
1680
+ function defaultSlotForResourceType(resourceType) {
1681
+ if (resourceType === 'AUDIO')
1682
+ return 'audio';
1683
+ if (resourceType === 'VIDEO')
1684
+ return 'video';
1685
+ return 'image';
1686
+ }
1687
+ function wrapSlot(slot, value, resourceName) {
1688
+ if (typeof value === 'string') {
1689
+ const url = value.trim();
1690
+ if (!url) {
1691
+ throw missingUrlError(resourceName, slot);
1692
+ }
1693
+ return { type: typeFromUrl(url, slot), url };
1694
+ }
1695
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
1696
+ throw new Error(`Resource "${resourceName}" src.${slot} must be a url string or { type, url }.`);
1697
+ }
1698
+ const slotValue = value;
1699
+ if (slotValue.type === 'data') {
1700
+ return slotValue;
1701
+ }
1702
+ const url = typeof slotValue.url === 'string' ? slotValue.url.trim() : '';
1703
+ if (!url) {
1704
+ throw missingUrlError(resourceName, slot);
1705
+ }
1706
+ return {
1707
+ ...slotValue,
1708
+ type: slotValue.type || typeFromUrl(url, slot),
1709
+ url,
1710
+ };
1711
+ }
1712
+ function missingUrlError(resourceName, slot) {
1713
+ return new Error(`Resource "${resourceName}" src.${slot}.url is required. ` +
1714
+ `Use { type, url } (e.g. { type: "webp", url: "https://..." }). ` +
1715
+ `A flat path string is also accepted and will be normalized.`);
1716
+ }
1717
+ /**
1718
+ * Accept the documented `{ type, url }` slots, plus the common guesses
1719
+ * `src: "file.png"` and `src: { image: "a.webp", json: "a.json" }`.
1720
+ */
1721
+ function normalizeResourceSrc(src, resourceType, resourceName = '(unnamed)') {
1722
+ if (src == null) {
1723
+ throw new Error(`Resource "${resourceName}" src is required.`);
1724
+ }
1725
+ if (typeof src === 'string') {
1726
+ const slot = defaultSlotForResourceType(resourceType);
1727
+ return { [slot]: wrapSlot(slot, src, resourceName) };
1728
+ }
1729
+ if (typeof src !== 'object' || Array.isArray(src)) {
1730
+ throw new Error(`Resource "${resourceName}" src must be a url string or a slot object.`);
1731
+ }
1732
+ const out = {};
1733
+ for (const [slot, value] of Object.entries(src)) {
1734
+ if (value == null)
1735
+ continue;
1736
+ out[slot] = wrapSlot(slot, value, resourceName);
1737
+ }
1738
+ if (Object.keys(out).length === 0) {
1739
+ throw new Error(`Resource "${resourceName}" src has no loadable slots.`);
1740
+ }
1741
+ return out;
1742
+ }
1743
+
1645
1744
  const resourceLoader = {
1646
1745
  AbstractLoadStrategy: resourceLoader$1.AbstractLoadStrategy,
1647
1746
  AudioLoadStrategy: resourceLoader$1.AudioLoadStrategy,
@@ -1656,6 +1755,102 @@ const resourceLoader = {
1656
1755
  ResourceState: resourceLoader$1.ResourceState
1657
1756
  };
1658
1757
 
1758
+ function isFrameMap(frames) {
1759
+ return !!frames && typeof frames === 'object' && !Array.isArray(frames);
1760
+ }
1761
+ function frameFromPackedItem(item) {
1762
+ const frame = item.frame;
1763
+ if (!frame || typeof frame !== 'object')
1764
+ return null;
1765
+ return {
1766
+ ...item,
1767
+ frame: frame,
1768
+ rotated: Boolean(item.rotated),
1769
+ trimmed: Boolean(item.trimmed),
1770
+ spriteSourceSize: item.spriteSourceSize,
1771
+ sourceSize: item.sourceSize,
1772
+ };
1773
+ }
1774
+ function collectArrayFrames(list, frames, animations) {
1775
+ for (const raw of list) {
1776
+ if (!raw || typeof raw !== 'object')
1777
+ continue;
1778
+ const item = raw;
1779
+ const name = String(item.filename || item.name || '');
1780
+ if (!name)
1781
+ continue;
1782
+ const packed = frameFromPackedItem(item);
1783
+ if (!packed)
1784
+ continue;
1785
+ frames[name] = packed;
1786
+ const slash = name.lastIndexOf('/');
1787
+ if (slash > 0) {
1788
+ const anim = name.slice(0, slash);
1789
+ if (!animations[anim])
1790
+ animations[anim] = [];
1791
+ if (!animations[anim].includes(name))
1792
+ animations[anim].push(name);
1793
+ }
1794
+ }
1795
+ }
1796
+ /**
1797
+ * Pixi `Spritesheet` reads `data.meta.scale` and a name→frame `frames` map.
1798
+ * TexturePacker multi-atlas (`textures[].frames` as an array) is accepted and
1799
+ * rewritten to that hash. Always returns a `meta.scale` so construction cannot
1800
+ * throw `Cannot read properties of undefined (reading 'scale')`.
1801
+ */
1802
+ function normalizeSpritesheetData(raw) {
1803
+ if (!raw || typeof raw !== 'object') {
1804
+ return { frames: {}, animations: {}, meta: { scale: 1 } };
1805
+ }
1806
+ const json = raw;
1807
+ if (isFrameMap(json.frames)) {
1808
+ return {
1809
+ ...json,
1810
+ frames: json.frames,
1811
+ animations: { ...(json.animations || {}) },
1812
+ meta: { scale: 1, ...(json.meta || {}) },
1813
+ };
1814
+ }
1815
+ const frames = {};
1816
+ const animations = { ...(json.animations || {}) };
1817
+ if (Array.isArray(json.textures)) {
1818
+ for (const sheet of json.textures) {
1819
+ if (!sheet || typeof sheet !== 'object')
1820
+ continue;
1821
+ if (Array.isArray(sheet.frames)) {
1822
+ collectArrayFrames(sheet.frames, frames, animations);
1823
+ }
1824
+ }
1825
+ const first = json.textures[0] || {};
1826
+ const scale = first.scale ?? json.meta?.scale ?? 1;
1827
+ return {
1828
+ frames,
1829
+ animations,
1830
+ meta: {
1831
+ scale: typeof scale === 'number' || typeof scale === 'string' ? scale : 1,
1832
+ image: typeof first.image === 'string' ? first.image : undefined,
1833
+ size: first.size,
1834
+ format: first.format,
1835
+ ...(json.meta || {}),
1836
+ },
1837
+ };
1838
+ }
1839
+ if (Array.isArray(json.frames)) {
1840
+ collectArrayFrames(json.frames, frames, animations);
1841
+ return {
1842
+ frames,
1843
+ animations,
1844
+ meta: { scale: 1, ...(json.meta || {}) },
1845
+ };
1846
+ }
1847
+ return {
1848
+ frames: {},
1849
+ animations,
1850
+ meta: { scale: 1, ...(json.meta || {}) },
1851
+ };
1852
+ }
1853
+
1659
1854
  /** Resource type */
1660
1855
  exports.RESOURCE_TYPE = void 0;
1661
1856
  (function (RESOURCE_TYPE) {
@@ -1733,8 +1928,11 @@ class Resource extends EventEmitter__default.default {
1733
1928
  console.warn(res.name + ' was already added');
1734
1929
  continue;
1735
1930
  }
1736
- this.resourcesMap[res.name] = res;
1737
- this.resourcesMap[res.name].data = {};
1931
+ this.resourcesMap[res.name] = {
1932
+ ...res,
1933
+ src: normalizeResourceSrc(res.src, String(res.type), res.name),
1934
+ data: {},
1935
+ };
1738
1936
  }
1739
1937
  }
1740
1938
  /** dd resource preprocesser*/
@@ -1823,6 +2021,7 @@ class Resource extends EventEmitter__default.default {
1823
2021
  for (const handler of this.preProcessResourceHandlers) {
1824
2022
  handler(res);
1825
2023
  }
2024
+ res.src = normalizeResourceSrc(res.src, String(res.type), res.name);
1826
2025
  for (const key in res.src) {
1827
2026
  const resourceType = res.src[key].type;
1828
2027
  if (resourceType === 'data') {
@@ -1949,6 +2148,8 @@ exports.componentObserver = componentObserver;
1949
2148
  exports.decorators = decorators;
1950
2149
  exports.isAllowedMessageOrigin = isAllowedMessageOrigin;
1951
2150
  exports.mergeAllowedMessageOrigins = mergeAllowedMessageOrigins;
2151
+ exports.normalizeResourceSrc = normalizeResourceSrc;
2152
+ exports.normalizeSpritesheetData = normalizeSpritesheetData;
1952
2153
  exports.parseSetPlayingMessage = parseSetPlayingMessage;
1953
2154
  exports.postParentGameReady = postParentGameReady;
1954
2155
  exports.postParentGameState = postParentGameState;