kiwiengine 0.0.1-alpha

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.
Files changed (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -0
  3. package/assets/logo.png +0 -0
  4. package/examples/package.json +13 -0
  5. package/examples/test-dom/index.html +24 -0
  6. package/examples/test-dom/index.ts +21 -0
  7. package/examples/tsconfig.json +22 -0
  8. package/examples/webpack.config.js +31 -0
  9. package/lib/asset/audio.js +158 -0
  10. package/lib/asset/audio.js.map +1 -0
  11. package/lib/asset/loaders/audio.js +35 -0
  12. package/lib/asset/loaders/audio.js.map +1 -0
  13. package/lib/asset/loaders/binary.js +28 -0
  14. package/lib/asset/loaders/binary.js.map +1 -0
  15. package/lib/asset/loaders/font.js +27 -0
  16. package/lib/asset/loaders/font.js.map +1 -0
  17. package/lib/asset/loaders/loader.js +37 -0
  18. package/lib/asset/loaders/loader.js.map +1 -0
  19. package/lib/asset/loaders/spritesheet.js +56 -0
  20. package/lib/asset/loaders/spritesheet.js.map +1 -0
  21. package/lib/asset/loaders/text.js +27 -0
  22. package/lib/asset/loaders/text.js.map +1 -0
  23. package/lib/asset/loaders/texture.js +38 -0
  24. package/lib/asset/loaders/texture.js.map +1 -0
  25. package/lib/asset/preload.js +69 -0
  26. package/lib/asset/preload.js.map +1 -0
  27. package/lib/game-object/game-object-physics.js +188 -0
  28. package/lib/game-object/game-object-physics.js.map +1 -0
  29. package/lib/game-object/game-object-rendering.js +35 -0
  30. package/lib/game-object/game-object-rendering.js.map +1 -0
  31. package/lib/game-object/game-object.js +162 -0
  32. package/lib/game-object/game-object.js.map +1 -0
  33. package/lib/game-object/transform.js +118 -0
  34. package/lib/game-object/transform.js.map +1 -0
  35. package/lib/game-object-ext/animated-sprite.js +117 -0
  36. package/lib/game-object-ext/animated-sprite.js.map +1 -0
  37. package/lib/game-object-ext/dom-container.js +56 -0
  38. package/lib/game-object-ext/dom-container.js.map +1 -0
  39. package/lib/game-object-ext/rect.js +30 -0
  40. package/lib/game-object-ext/rect.js.map +1 -0
  41. package/lib/game-object-ext/spine.js +206 -0
  42. package/lib/game-object-ext/spine.js.map +1 -0
  43. package/lib/game-object-ext/sprite.js +46 -0
  44. package/lib/game-object-ext/sprite.js.map +1 -0
  45. package/lib/game-object-ext/text.js +68 -0
  46. package/lib/game-object-ext/text.js.map +1 -0
  47. package/lib/game-object-ext/tiling-sprite.js +64 -0
  48. package/lib/game-object-ext/tiling-sprite.js.map +1 -0
  49. package/lib/index.js +13 -0
  50. package/lib/index.js.map +1 -0
  51. package/lib/types/asset/audio.d.ts +21 -0
  52. package/lib/types/asset/audio.d.ts.map +1 -0
  53. package/lib/types/asset/loaders/audio.d.ts +7 -0
  54. package/lib/types/asset/loaders/audio.d.ts.map +1 -0
  55. package/lib/types/asset/loaders/binary.d.ts +7 -0
  56. package/lib/types/asset/loaders/binary.d.ts.map +1 -0
  57. package/lib/types/asset/loaders/font.d.ts +7 -0
  58. package/lib/types/asset/loaders/font.d.ts.map +1 -0
  59. package/lib/types/asset/loaders/loader.d.ts +13 -0
  60. package/lib/types/asset/loaders/loader.d.ts.map +1 -0
  61. package/lib/types/asset/loaders/spritesheet.d.ts +11 -0
  62. package/lib/types/asset/loaders/spritesheet.d.ts.map +1 -0
  63. package/lib/types/asset/loaders/text.d.ts +7 -0
  64. package/lib/types/asset/loaders/text.d.ts.map +1 -0
  65. package/lib/types/asset/loaders/texture.d.ts +9 -0
  66. package/lib/types/asset/loaders/texture.d.ts.map +1 -0
  67. package/lib/types/asset/preload.d.ts +8 -0
  68. package/lib/types/asset/preload.d.ts.map +1 -0
  69. package/lib/types/game-object/game-object-physics.d.ts +42 -0
  70. package/lib/types/game-object/game-object-physics.d.ts.map +1 -0
  71. package/lib/types/game-object/game-object-rendering.d.ts +15 -0
  72. package/lib/types/game-object/game-object-rendering.d.ts.map +1 -0
  73. package/lib/types/game-object/game-object.d.ts +81 -0
  74. package/lib/types/game-object/game-object.d.ts.map +1 -0
  75. package/lib/types/game-object/transform.d.ts +43 -0
  76. package/lib/types/game-object/transform.d.ts.map +1 -0
  77. package/lib/types/game-object-ext/animated-sprite.d.ts +29 -0
  78. package/lib/types/game-object-ext/animated-sprite.d.ts.map +1 -0
  79. package/lib/types/game-object-ext/dom-container.d.ts +16 -0
  80. package/lib/types/game-object-ext/dom-container.d.ts.map +1 -0
  81. package/lib/types/game-object-ext/rect.d.ts +17 -0
  82. package/lib/types/game-object-ext/rect.d.ts.map +1 -0
  83. package/lib/types/game-object-ext/spine.d.ts +35 -0
  84. package/lib/types/game-object-ext/spine.d.ts.map +1 -0
  85. package/lib/types/game-object-ext/sprite.d.ts +14 -0
  86. package/lib/types/game-object-ext/sprite.d.ts.map +1 -0
  87. package/lib/types/game-object-ext/text.d.ts +26 -0
  88. package/lib/types/game-object-ext/text.d.ts.map +1 -0
  89. package/lib/types/game-object-ext/tiling-sprite.d.ts +20 -0
  90. package/lib/types/game-object-ext/tiling-sprite.d.ts.map +1 -0
  91. package/lib/types/index.d.ts +14 -0
  92. package/lib/types/index.d.ts.map +1 -0
  93. package/lib/types/utils/debug.d.ts +3 -0
  94. package/lib/types/utils/debug.d.ts.map +1 -0
  95. package/lib/types/utils/go.d.ts +26 -0
  96. package/lib/types/utils/go.d.ts.map +1 -0
  97. package/lib/types/world/world-debug.d.ts +11 -0
  98. package/lib/types/world/world-debug.d.ts.map +1 -0
  99. package/lib/types/world/world-physics.d.ts +16 -0
  100. package/lib/types/world/world-physics.d.ts.map +1 -0
  101. package/lib/types/world/world-rendering.d.ts +28 -0
  102. package/lib/types/world/world-rendering.d.ts.map +1 -0
  103. package/lib/types/world/world.d.ts +38 -0
  104. package/lib/types/world/world.d.ts.map +1 -0
  105. package/lib/utils/debug.js +5 -0
  106. package/lib/utils/debug.js.map +1 -0
  107. package/lib/utils/go.js +33 -0
  108. package/lib/utils/go.js.map +1 -0
  109. package/lib/world/world-debug.js +89 -0
  110. package/lib/world/world-debug.js.map +1 -0
  111. package/lib/world/world-physics.js +45 -0
  112. package/lib/world/world-physics.js.map +1 -0
  113. package/lib/world/world-rendering.js +123 -0
  114. package/lib/world/world-rendering.js.map +1 -0
  115. package/lib/world/world.js +147 -0
  116. package/lib/world/world.js.map +1 -0
  117. package/package.json +23 -0
  118. package/src/asset/audio.ts +176 -0
  119. package/src/asset/loaders/audio.ts +39 -0
  120. package/src/asset/loaders/binary.ts +32 -0
  121. package/src/asset/loaders/font.ts +27 -0
  122. package/src/asset/loaders/loader.ts +39 -0
  123. package/src/asset/loaders/spritesheet.ts +67 -0
  124. package/src/asset/loaders/text.ts +31 -0
  125. package/src/asset/loaders/texture.ts +46 -0
  126. package/src/asset/preload.ts +76 -0
  127. package/src/game-object/game-object-physics.ts +191 -0
  128. package/src/game-object/game-object-rendering.ts +27 -0
  129. package/src/game-object/game-object.ts +190 -0
  130. package/src/game-object/transform.ts +164 -0
  131. package/src/game-object-ext/animated-sprite.ts +140 -0
  132. package/src/game-object-ext/dom-container.ts +67 -0
  133. package/src/game-object-ext/rect.ts +40 -0
  134. package/src/game-object-ext/spine.ts +235 -0
  135. package/src/game-object-ext/sprite.ts +55 -0
  136. package/src/game-object-ext/text.ts +83 -0
  137. package/src/game-object-ext/tiling-sprite.ts +73 -0
  138. package/src/index.ts +14 -0
  139. package/src/utils/debug.ts +5 -0
  140. package/src/utils/go.ts +53 -0
  141. package/src/world/world-debug.ts +114 -0
  142. package/src/world/world-physics.ts +52 -0
  143. package/src/world/world-rendering.ts +145 -0
  144. package/src/world/world.ts +171 -0
  145. package/tsconfig.json +33 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 jason026386
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # Kiwi Engine
2
+ TypeScript 기반 2D 웹 게임 엔진입니다.
3
+
4
+ ## 왜 키위인가?
5
+ 개발자가 키위를 좋아합니다.
6
+
7
+ ## 라이센스
8
+ MIT
Binary file
@@ -0,0 +1,13 @@
1
+ {
2
+ "scripts": {
3
+ "dev": "webpack serve --static=. --no-client-overlay --watch --mode=development",
4
+ "build": "NODE_ENV=production webpack --mode=production"
5
+ },
6
+ "devDependencies": {
7
+ "ts-loader": "^9.5.2",
8
+ "typescript": "^5.8.3",
9
+ "webpack": "^5.99.8",
10
+ "webpack-cli": "^6.0.1",
11
+ "webpack-dev-server": "^5.2.1"
12
+ }
13
+ }
@@ -0,0 +1,24 @@
1
+ <!doctype html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1">
7
+ <style>
8
+ html,
9
+ body {
10
+ width: 100%;
11
+ height: 100%;
12
+ margin: 0;
13
+ padding: 0;
14
+ overflow: hidden;
15
+ background-color: #000;
16
+ }
17
+ </style>
18
+ </head>
19
+
20
+ <body>
21
+ <script src="dist/game.js"></script>
22
+ </body>
23
+
24
+ </html>
@@ -0,0 +1,21 @@
1
+ import { enableDebug, World, DomContainerObject } from '../../src';
2
+
3
+ enableDebug();
4
+
5
+ const world = new World({ width: 800, height: 600 });
6
+ world.container.style.width = '100%';
7
+ world.container.style.height = '100%';
8
+ document.body.appendChild(world.container);
9
+
10
+ const testEl = document.createElement('div');
11
+ testEl.textContent = 'Hello World';
12
+ testEl.style.color = 'red';
13
+ testEl.onclick = () => alert('click');
14
+
15
+ const go = new DomContainerObject({ el: testEl });
16
+ go.alpha = 0.5;
17
+ world.add(go);
18
+
19
+ world.on('update', (dt) => {
20
+ go.rotation += dt;
21
+ });
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "noImplicitAny": true,
6
+ "preserveConstEnums": true,
7
+ "inlineSources": true,
8
+ "inlineSourceMap": true,
9
+ "esModuleInterop": true,
10
+ "lib": [
11
+ "DOM",
12
+ "ESNext",
13
+ "ScriptHost"
14
+ ],
15
+ "declaration": false,
16
+ "declarationMap": false,
17
+ "composite": false,
18
+ "moduleResolution": "node",
19
+ "skipLibCheck": true,
20
+ "strict": true
21
+ }
22
+ }
@@ -0,0 +1,31 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ entry: {
5
+ 'test-dom': './test-dom/index.ts',
6
+ },
7
+ output: {
8
+ filename: '[name]/dist/game.js',
9
+ path: path.resolve(__dirname, './'),
10
+ chunkFormat: false
11
+ },
12
+ module: {
13
+ rules: [
14
+ {
15
+ test: /\.tsx?$/,
16
+ use: 'ts-loader',
17
+ exclude: /node_modules/
18
+ }
19
+ ]
20
+ },
21
+ resolve: {
22
+ extensions: ['.ts', '.js']
23
+ },
24
+ devServer: {
25
+ client: {
26
+ overlay: false,
27
+ logging: 'none'
28
+ },
29
+ open: true
30
+ }
31
+ };
@@ -0,0 +1,158 @@
1
+ import { audioLoader } from './loaders/audio';
2
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
3
+ window.addEventListener('mousedown', () => audioContext.resume());
4
+ window.addEventListener('touchend', () => audioContext.resume());
5
+ async function getAvailableContext() {
6
+ if (audioContext.state === 'suspended')
7
+ await audioContext.resume();
8
+ return audioContext;
9
+ }
10
+ const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
11
+ let isPageVisible = !document.hidden;
12
+ window.addEventListener('visibilitychange', () => isPageVisible = !document.hidden);
13
+ class Audio {
14
+ src;
15
+ #volume;
16
+ #loop;
17
+ #audioBuffer;
18
+ #audioContext;
19
+ #gainNode;
20
+ #source;
21
+ #isPlaying = false;
22
+ #isPaused = false;
23
+ #startTime = 0;
24
+ #pauseTime = 0;
25
+ #offset = 0;
26
+ constructor(src, volume, loop) {
27
+ this.src = src;
28
+ this.#volume = volume;
29
+ this.#loop = loop;
30
+ this.play();
31
+ }
32
+ get volume() { return this.#volume; }
33
+ set volume(volume) {
34
+ this.#volume = volume;
35
+ if (this.#gainNode)
36
+ this.#gainNode.gain.value = Math.max(0, Math.min(1, volume));
37
+ }
38
+ async play() {
39
+ if (isMobile && !isPageVisible)
40
+ return;
41
+ if (!this.#audioBuffer) {
42
+ if (!audioLoader.checkLoaded(this.src)) {
43
+ console.info(`Audio not preloaded. Loading now: ${this.src}`);
44
+ }
45
+ this.#audioBuffer = await audioLoader.load(this.src);
46
+ }
47
+ if (!this.#audioBuffer)
48
+ return;
49
+ if (this.#isPlaying)
50
+ this.stop();
51
+ if (!this.#isPaused)
52
+ this.#offset = 0;
53
+ this.#isPlaying = true;
54
+ this.#isPaused = false;
55
+ if (!this.#audioContext)
56
+ this.#audioContext = await getAvailableContext();
57
+ if (!this.#isPlaying)
58
+ return;
59
+ if (!this.#gainNode) {
60
+ this.#gainNode = this.#audioContext.createGain();
61
+ this.#gainNode.gain.value = this.#volume;
62
+ this.#gainNode.connect(this.#audioContext.destination);
63
+ }
64
+ this.#source = this.#audioContext.createBufferSource();
65
+ this.#source.buffer = this.#audioBuffer;
66
+ this.#source.loop = this.#loop;
67
+ this.#source.connect(this.#gainNode);
68
+ this.#source.start(0, this.#offset);
69
+ this.#startTime = this.#audioContext.currentTime;
70
+ this.#source.onended = () => { if (!this.#isPaused && !this.#loop)
71
+ this.stop(); };
72
+ }
73
+ #clear() {
74
+ if (this.#source) {
75
+ this.#source.stop();
76
+ this.#source.disconnect();
77
+ this.#source = undefined;
78
+ }
79
+ }
80
+ pause() {
81
+ if (this.#isPlaying && !this.#isPaused) {
82
+ if (this.#audioContext) {
83
+ this.#pauseTime = this.#audioContext.currentTime;
84
+ this.#offset += this.#pauseTime - this.#startTime;
85
+ }
86
+ this.#isPaused = true;
87
+ this.#isPlaying = false;
88
+ this.#clear();
89
+ }
90
+ }
91
+ stop() {
92
+ this.#isPlaying = false;
93
+ this.#isPaused = false;
94
+ this.#offset = 0;
95
+ this.#clear();
96
+ }
97
+ }
98
+ class MusicPlayer {
99
+ #volume = 0.7;
100
+ #currentAudio;
101
+ constructor() {
102
+ const stored = parseFloat(localStorage.getItem('musicVolume') || '');
103
+ this.#volume = Number.isNaN(stored) ? this.#volume : stored;
104
+ if (isMobile) {
105
+ document.addEventListener('visibilitychange', () => {
106
+ if (document.hidden)
107
+ this.pause();
108
+ else {
109
+ isPageVisible = true;
110
+ this.#currentAudio?.play();
111
+ }
112
+ });
113
+ }
114
+ }
115
+ get volume() {
116
+ return this.#volume;
117
+ }
118
+ set volume(volume) {
119
+ this.#volume = volume;
120
+ localStorage.setItem('musicVolume', volume.toString());
121
+ if (this.#currentAudio)
122
+ this.#currentAudio.volume = volume;
123
+ }
124
+ play(src) {
125
+ if (this.#currentAudio?.src === src)
126
+ return;
127
+ this.#currentAudio?.stop();
128
+ this.#currentAudio = new Audio(src, this.#volume, true);
129
+ }
130
+ pause() {
131
+ this.#currentAudio?.pause();
132
+ }
133
+ stop() {
134
+ this.#currentAudio?.stop();
135
+ this.#currentAudio = undefined;
136
+ }
137
+ }
138
+ class SfxPlayer {
139
+ #volume = 1;
140
+ constructor() {
141
+ const stored = parseFloat(localStorage.getItem('sfxVolume') || '');
142
+ this.#volume = Number.isNaN(stored) ? this.#volume : stored;
143
+ }
144
+ get volume() {
145
+ return this.#volume;
146
+ }
147
+ set volume(volume) {
148
+ this.#volume = volume;
149
+ localStorage.setItem('sfxVolume', volume.toString());
150
+ }
151
+ play(src) {
152
+ new Audio(src, this.#volume, false);
153
+ }
154
+ }
155
+ const musicPlayer = new MusicPlayer();
156
+ const sfxPlayer = new SfxPlayer();
157
+ export { audioContext, musicPlayer, sfxPlayer };
158
+ //# sourceMappingURL=audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.js","sourceRoot":"","sources":["../../src/asset/audio.ts"],"names":[],"mappings":"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","sourcesContent":["import { audioLoader } from './loaders/audio';\n\nconst audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();\nwindow.addEventListener('mousedown', () => audioContext.resume());\nwindow.addEventListener('touchend', () => audioContext.resume());\n\nasync function getAvailableContext(): Promise<AudioContext> {\n if (audioContext.state === 'suspended') await audioContext.resume();\n return audioContext;\n}\n\nconst isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);\nlet isPageVisible = !document.hidden;\nwindow.addEventListener('visibilitychange', () => isPageVisible = !document.hidden);\n\nclass Audio {\n src: string;\n #volume: number;\n #loop: boolean;\n\n #audioBuffer?: AudioBuffer;\n #audioContext?: AudioContext;\n #gainNode?: GainNode;\n #source?: AudioBufferSourceNode;\n\n #isPlaying = false;\n #isPaused = false;\n #startTime = 0;\n #pauseTime = 0;\n #offset = 0;\n\n constructor(src: string, volume: number, loop: boolean) {\n this.src = src;\n this.#volume = volume;\n this.#loop = loop;\n this.play();\n }\n\n get volume() { return this.#volume; }\n set volume(volume: number) {\n this.#volume = volume;\n if (this.#gainNode) this.#gainNode.gain.value = Math.max(0, Math.min(1, volume));\n }\n\n async play() {\n if (isMobile && !isPageVisible) return;\n\n if (!this.#audioBuffer) {\n if (!audioLoader.checkLoaded(this.src)) {\n console.info(`Audio not preloaded. Loading now: ${this.src}`);\n }\n this.#audioBuffer = await audioLoader.load(this.src);\n }\n if (!this.#audioBuffer) return;\n\n if (this.#isPlaying) this.stop();\n if (!this.#isPaused) this.#offset = 0;\n this.#isPlaying = true;\n this.#isPaused = false;\n if (!this.#audioContext) this.#audioContext = await getAvailableContext();\n if (!this.#isPlaying) return;\n\n if (!this.#gainNode) {\n this.#gainNode = this.#audioContext.createGain();\n this.#gainNode.gain.value = this.#volume;\n this.#gainNode.connect(this.#audioContext.destination);\n }\n\n this.#source = this.#audioContext.createBufferSource();\n this.#source.buffer = this.#audioBuffer;\n this.#source.loop = this.#loop;\n this.#source.connect(this.#gainNode);\n this.#source.start(0, this.#offset);\n this.#startTime = this.#audioContext.currentTime;\n this.#source.onended = () => { if (!this.#isPaused && !this.#loop) this.stop(); };\n }\n\n #clear(): void {\n if (this.#source) {\n this.#source.stop();\n this.#source.disconnect();\n this.#source = undefined;\n }\n }\n\n pause() {\n if (this.#isPlaying && !this.#isPaused) {\n if (this.#audioContext) {\n this.#pauseTime = this.#audioContext.currentTime;\n this.#offset += this.#pauseTime - this.#startTime;\n }\n this.#isPaused = true;\n this.#isPlaying = false;\n this.#clear();\n }\n }\n\n stop() {\n this.#isPlaying = false;\n this.#isPaused = false;\n this.#offset = 0;\n this.#clear();\n }\n}\n\nclass MusicPlayer {\n #volume = 0.7;\n #currentAudio?: Audio;\n\n constructor() {\n const stored = parseFloat(localStorage.getItem('musicVolume') || '');\n this.#volume = Number.isNaN(stored) ? this.#volume : stored;\n\n if (isMobile) {\n document.addEventListener('visibilitychange', () => {\n if (document.hidden) this.pause();\n else {\n isPageVisible = true;\n this.#currentAudio?.play();\n }\n });\n }\n }\n\n get volume() {\n return this.#volume;\n }\n\n set volume(volume: number) {\n this.#volume = volume;\n localStorage.setItem('musicVolume', volume.toString());\n if (this.#currentAudio) this.#currentAudio.volume = volume;\n }\n\n play(src: string) {\n if (this.#currentAudio?.src === src) return;\n this.#currentAudio?.stop();\n this.#currentAudio = new Audio(src, this.#volume, true);\n }\n\n pause() {\n this.#currentAudio?.pause();\n }\n\n stop() {\n this.#currentAudio?.stop();\n this.#currentAudio = undefined;\n }\n}\n\nclass SfxPlayer {\n #volume = 1;\n\n constructor() {\n const stored = parseFloat(localStorage.getItem('sfxVolume') || '');\n this.#volume = Number.isNaN(stored) ? this.#volume : stored;\n }\n\n get volume() {\n return this.#volume;\n }\n\n set volume(volume: number) {\n this.#volume = volume;\n localStorage.setItem('sfxVolume', volume.toString());\n }\n\n play(src: string) {\n new Audio(src, this.#volume, false);\n }\n}\n\nconst musicPlayer = new MusicPlayer();\nconst sfxPlayer = new SfxPlayer();\n\nexport { audioContext, musicPlayer, sfxPlayer };\n"]}
@@ -0,0 +1,35 @@
1
+ import { audioContext } from '../audio';
2
+ import { Loader } from './loader';
3
+ class AudioLoader extends Loader {
4
+ async _load(src) {
5
+ const loadingPromise = (async () => {
6
+ const response = await fetch(src);
7
+ if (!response.ok) {
8
+ console.error(`Failed to load audio data: ${src}`);
9
+ return;
10
+ }
11
+ const arrayBuffer = await response.arrayBuffer();
12
+ try {
13
+ const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
14
+ this.loadingPromises.delete(src);
15
+ if (this.hasActiveRef(src)) {
16
+ if (this.loadedAssets.has(src)) {
17
+ console.error(`Audio buffer already exists: ${src}`);
18
+ }
19
+ else {
20
+ this.loadedAssets.set(src, audioBuffer);
21
+ return audioBuffer;
22
+ }
23
+ }
24
+ }
25
+ catch (error) {
26
+ console.error(`Failed to decode audio data: ${src}`, error);
27
+ this.loadingPromises.delete(src);
28
+ }
29
+ })();
30
+ this.loadingPromises.set(src, loadingPromise);
31
+ return await loadingPromise;
32
+ }
33
+ }
34
+ export const audioLoader = new AudioLoader();
35
+ //# sourceMappingURL=audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.js","sourceRoot":"","sources":["../../../src/asset/loaders/audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAY,SAAQ,MAAmB;IACxB,KAAK,CAAC,KAAK,CAAC,GAAW;QACxC,MAAM,cAAc,GAAG,CAAC,KAAK,IAAI,EAAE;YACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;gBACnD,OAAO;YACT,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;YAEjD,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;gBAEpE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAEjC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC3B,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC/B,OAAO,CAAC,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;oBACvD,CAAC;yBAAM,CAAC;wBACN,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;wBACxC,OAAO,WAAW,CAAC;oBACrB,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;gBAC5D,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACnC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9C,OAAO,MAAM,cAAc,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC","sourcesContent":["import { audioContext } from '../audio';\nimport { Loader } from './loader';\n\nclass AudioLoader extends Loader<AudioBuffer> {\n protected override async _load(src: string) {\n const loadingPromise = (async () => {\n const response = await fetch(src);\n if (!response.ok) {\n console.error(`Failed to load audio data: ${src}`);\n return;\n }\n\n const arrayBuffer = await response.arrayBuffer();\n\n try {\n const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n\n this.loadingPromises.delete(src);\n\n if (this.hasActiveRef(src)) {\n if (this.loadedAssets.has(src)) {\n console.error(`Audio buffer already exists: ${src}`);\n } else {\n this.loadedAssets.set(src, audioBuffer);\n return audioBuffer;\n }\n }\n } catch (error) {\n console.error(`Failed to decode audio data: ${src}`, error);\n this.loadingPromises.delete(src);\n }\n })();\n\n this.loadingPromises.set(src, loadingPromise);\n return await loadingPromise;\n }\n}\n\nexport const audioLoader = new AudioLoader();\n"]}
@@ -0,0 +1,28 @@
1
+ import { Loader } from './loader';
2
+ class BinaryLoader extends Loader {
3
+ async _load(src) {
4
+ const loadingPromise = (async () => {
5
+ const response = await fetch(src);
6
+ if (!response.ok) {
7
+ console.error(`Failed to load binary data: ${src}`);
8
+ return;
9
+ }
10
+ const arrayBuffer = await response.arrayBuffer();
11
+ const data = new Uint8Array(arrayBuffer);
12
+ this.loadingPromises.delete(src);
13
+ if (this.hasActiveRef(src)) {
14
+ if (this.loadedAssets.has(src)) {
15
+ console.error(`Binary data already exists: ${src}`);
16
+ }
17
+ else {
18
+ this.loadedAssets.set(src, data);
19
+ return data;
20
+ }
21
+ }
22
+ })();
23
+ this.loadingPromises.set(src, loadingPromise);
24
+ return await loadingPromise;
25
+ }
26
+ }
27
+ export const binaryLoader = new BinaryLoader();
28
+ //# sourceMappingURL=binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary.js","sourceRoot":"","sources":["../../../src/asset/loaders/binary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,YAAa,SAAQ,MAAkB;IACxB,KAAK,CAAC,KAAK,CAAC,GAAW;QACxC,MAAM,cAAc,GAAG,CAAC,KAAK,IAAI,EAAE;YACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;gBACpD,OAAO;YACT,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;YAEzC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAEjC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3B,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC/B,OAAO,CAAC,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;gBACtD,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBACjC,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9C,OAAO,MAAM,cAAc,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC","sourcesContent":["import { Loader } from './loader';\n\nclass BinaryLoader extends Loader<Uint8Array> {\n protected override async _load(src: string) {\n const loadingPromise = (async () => {\n const response = await fetch(src);\n if (!response.ok) {\n console.error(`Failed to load binary data: ${src}`);\n return;\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = new Uint8Array(arrayBuffer);\n\n this.loadingPromises.delete(src);\n\n if (this.hasActiveRef(src)) {\n if (this.loadedAssets.has(src)) {\n console.error(`Binary data already exists: ${src}`);\n } else {\n this.loadedAssets.set(src, data);\n return data;\n }\n }\n })();\n\n this.loadingPromises.set(src, loadingPromise);\n return await loadingPromise;\n }\n}\n\nexport const binaryLoader = new BinaryLoader();\n"]}
@@ -0,0 +1,27 @@
1
+ import { Loader } from './loader';
2
+ class FontFamilyLoader extends Loader {
3
+ async _load(fontName) {
4
+ const loadingPromise = (async () => {
5
+ if ('fonts' in document) {
6
+ try {
7
+ await document.fonts.load(`1em ${fontName}`);
8
+ await document.fonts.ready;
9
+ this.loadingPromises.delete(fontName);
10
+ return true;
11
+ }
12
+ catch (error) {
13
+ console.error(`Failed to load font: ${fontName}`, error);
14
+ this.loadingPromises.delete(fontName);
15
+ }
16
+ }
17
+ else {
18
+ console.warn(`This browser does not support the Font Loading API`);
19
+ this.loadingPromises.delete(fontName);
20
+ }
21
+ })();
22
+ this.loadingPromises.set(fontName, loadingPromise);
23
+ return await loadingPromise;
24
+ }
25
+ }
26
+ export const fontFamilyLoader = new FontFamilyLoader();
27
+ //# sourceMappingURL=font.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font.js","sourceRoot":"","sources":["../../../src/asset/loaders/font.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,gBAAiB,SAAQ,MAAe;IACzB,KAAK,CAAC,KAAK,CAAC,QAAgB;QAC7C,MAAM,cAAc,GAAG,CAAC,KAAK,IAAI,EAAE;YACjC,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;gBACxB,IAAI,CAAC;oBACH,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC;oBAC7C,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;oBAC3B,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACtC,OAAO,IAAI,CAAC;gBACd,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;oBACzD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;gBACnE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACnD,OAAO,MAAM,cAAc,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,EAAE,CAAC","sourcesContent":["import { Loader } from './loader';\n\nclass FontFamilyLoader extends Loader<boolean> {\n protected override async _load(fontName: string) {\n const loadingPromise = (async () => {\n if ('fonts' in document) {\n try {\n await document.fonts.load(`1em ${fontName}`);\n await document.fonts.ready;\n this.loadingPromises.delete(fontName);\n return true;\n } catch (error) {\n console.error(`Failed to load font: ${fontName}`, error);\n this.loadingPromises.delete(fontName);\n }\n } else {\n console.warn(`This browser does not support the Font Loading API`);\n this.loadingPromises.delete(fontName);\n }\n })();\n\n this.loadingPromises.set(fontName, loadingPromise);\n return await loadingPromise;\n }\n}\n\nexport const fontFamilyLoader = new FontFamilyLoader();\n"]}
@@ -0,0 +1,37 @@
1
+ class Loader {
2
+ loadedAssets = new Map();
3
+ loadingPromises = new Map();
4
+ #refCount = new Map();
5
+ #incRefCount(id) { this.#refCount.set(id, (this.#refCount.get(id) || 0) + 1); }
6
+ hasActiveRef(id) { return this.#refCount.get(id) > 0; }
7
+ _dispose(id, asset) { }
8
+ checkLoaded(id) {
9
+ return this.loadedAssets.has(id);
10
+ }
11
+ async load(id, ...args) {
12
+ this.#incRefCount(id);
13
+ if (this.checkLoaded(id))
14
+ return this.loadedAssets.get(id);
15
+ if (this.loadingPromises.has(id))
16
+ return await this.loadingPromises.get(id);
17
+ return await this._load(id, ...args);
18
+ }
19
+ release(id) {
20
+ const refCount = this.#refCount.get(id);
21
+ if (refCount === undefined)
22
+ throw new Error(`Asset not found: ${id}`);
23
+ if (refCount === 1) {
24
+ this.#refCount.delete(id);
25
+ const asset = this.loadedAssets.get(id);
26
+ if (asset) {
27
+ this._dispose(id, asset);
28
+ this.loadedAssets.delete(id);
29
+ }
30
+ }
31
+ else {
32
+ this.#refCount.set(id, refCount - 1);
33
+ }
34
+ }
35
+ }
36
+ export { Loader };
37
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/asset/loaders/loader.ts"],"names":[],"mappings":"AAAA,MAAe,MAAM;IACT,YAAY,GAAmB,IAAI,GAAG,EAAE,CAAC;IACzC,eAAe,GAAwC,IAAI,GAAG,EAAE,CAAC;IAE3E,SAAS,GAAwB,IAAI,GAAG,EAAE,CAAC;IAC3C,YAAY,CAAC,EAAU,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7E,YAAY,CAAC,EAAU,IAAI,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAEhE,QAAQ,CAAC,EAAU,EAAE,KAAQ,IAAqC,CAAC;IAE7E,WAAW,CAAC,EAAU;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAU,EAAE,GAAG,IAAW;QACnC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5E,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,EAAU;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;QACtE,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,KAAK,EAAE,CAAC;gBACV,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;gBACzB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;CACF;AAED,OAAO,EAAE,MAAM,EAAE,CAAC","sourcesContent":["abstract class Loader<T> {\n protected loadedAssets: Map<string, T> = new Map();\n protected loadingPromises: Map<string, Promise<T | undefined>> = new Map();\n\n #refCount: Map<string, number> = new Map();\n #incRefCount(id: string) { this.#refCount.set(id, (this.#refCount.get(id) || 0) + 1); }\n\n protected hasActiveRef(id: string) { return this.#refCount.get(id)! > 0; }\n protected abstract _load(id: string, ...args: any[]): Promise<T | undefined>;\n protected _dispose(id: string, asset: T): void { /* override to clean up */ }\n\n checkLoaded(id: string) {\n return this.loadedAssets.has(id);\n }\n\n async load(id: string, ...args: any[]) {\n this.#incRefCount(id);\n if (this.checkLoaded(id)) return this.loadedAssets.get(id);\n if (this.loadingPromises.has(id)) return await this.loadingPromises.get(id);\n return await this._load(id, ...args);\n }\n\n release(id: string) {\n const refCount = this.#refCount.get(id);\n if (refCount === undefined) throw new Error(`Asset not found: ${id}`);\n if (refCount === 1) {\n this.#refCount.delete(id);\n const asset = this.loadedAssets.get(id);\n if (asset) {\n this._dispose(id, asset);\n this.loadedAssets.delete(id);\n }\n } else {\n this.#refCount.set(id, refCount - 1);\n }\n }\n}\n\nexport { Loader };\n"]}
@@ -0,0 +1,56 @@
1
+ import { Spritesheet } from 'pixi.js';
2
+ import { Loader } from './loader';
3
+ import { textureLoader } from './texture';
4
+ const atlasIdCache = new WeakMap();
5
+ let idCounter = 0;
6
+ function getCachedId(src, atlas) {
7
+ let innerMap = atlasIdCache.get(atlas);
8
+ if (!innerMap) {
9
+ innerMap = new Map();
10
+ atlasIdCache.set(atlas, innerMap);
11
+ }
12
+ if (!innerMap.has(src)) {
13
+ innerMap.set(src, `${src}#${idCounter++}`);
14
+ }
15
+ return innerMap.get(src);
16
+ }
17
+ class SpritesheetLoader extends Loader {
18
+ #idToSrc = new Map();
19
+ async _load(id, src, atlas) {
20
+ this.#idToSrc.set(id, src);
21
+ const loadingPromise = (async () => {
22
+ const texture = await textureLoader.load(src);
23
+ if (!texture) {
24
+ console.error(`Failed to load texture: ${src}`);
25
+ return;
26
+ }
27
+ const spritesheet = new Spritesheet(texture, atlas);
28
+ await spritesheet.parse();
29
+ this.loadingPromises.delete(id);
30
+ if (this.hasActiveRef(id)) {
31
+ if (this.loadedAssets.has(id)) {
32
+ textureLoader.release(src);
33
+ console.error(`Spritesheet already exists: ${src}`);
34
+ }
35
+ else {
36
+ this.loadedAssets.set(id, spritesheet);
37
+ return spritesheet;
38
+ }
39
+ }
40
+ else {
41
+ textureLoader.release(src);
42
+ }
43
+ })();
44
+ this.loadingPromises.set(id, loadingPromise);
45
+ return await loadingPromise;
46
+ }
47
+ _dispose(id, spritesheet) {
48
+ spritesheet.destroy();
49
+ const src = this.#idToSrc.get(id);
50
+ if (src)
51
+ textureLoader.release(src);
52
+ }
53
+ }
54
+ const spritesheetLoader = new SpritesheetLoader();
55
+ export { getCachedId, spritesheetLoader };
56
+ //# sourceMappingURL=spritesheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spritesheet.js","sourceRoot":"","sources":["../../../src/asset/loaders/spritesheet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAmB,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,YAAY,GAAG,IAAI,OAAO,EAA+B,CAAC;AAChE,IAAI,SAAS,GAAG,CAAC,CAAC;AAElB,SAAS,WAAW,CAAC,GAAW,EAAE,KAAa;IAC7C,IAAI,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QACrC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,SAAS,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;AAC5B,CAAC;AAED,MAAM,iBAAkB,SAAQ,MAAmB;IACjD,QAAQ,GAAwB,IAAI,GAAG,EAAE,CAAC;IAEvB,KAAK,CAAC,KAAK,CAAC,EAAU,EAAE,GAAW,EAAE,KAAsB;QAC5E,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAE3B,MAAM,cAAc,GAAG,CAAC,KAAK,IAAI,EAAE;YACjC,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YAED,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACpD,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;YAE1B,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAEhC,IAAI,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC1B,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC9B,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBAC3B,OAAO,CAAC,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;gBACtD,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;oBACvC,OAAO,WAAW,CAAC;gBACrB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;QAC7C,OAAO,MAAM,cAAc,CAAC;IAC9B,CAAC;IAEkB,QAAQ,CAAC,EAAU,EAAE,WAAwB;QAC9D,WAAW,CAAC,OAAO,EAAE,CAAC;QAEtB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,GAAG;YAAE,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;CACF;AAED,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC","sourcesContent":["import { Spritesheet, SpritesheetData } from 'pixi.js';\nimport { Loader } from './loader';\nimport { textureLoader } from './texture';\n\nconst atlasIdCache = new WeakMap<object, Map<string, string>>();\nlet idCounter = 0;\n\nfunction getCachedId(src: string, atlas: object): string {\n let innerMap = atlasIdCache.get(atlas);\n if (!innerMap) {\n innerMap = new Map<string, string>();\n atlasIdCache.set(atlas, innerMap);\n }\n\n if (!innerMap.has(src)) {\n innerMap.set(src, `${src}#${idCounter++}`);\n }\n\n return innerMap.get(src)!;\n}\n\nclass SpritesheetLoader extends Loader<Spritesheet> {\n #idToSrc: Map<string, string> = new Map();\n\n protected override async _load(id: string, src: string, atlas: SpritesheetData) {\n this.#idToSrc.set(id, src);\n\n const loadingPromise = (async () => {\n const texture = await textureLoader.load(src);\n if (!texture) {\n console.error(`Failed to load texture: ${src}`);\n return;\n }\n\n const spritesheet = new Spritesheet(texture, atlas);\n await spritesheet.parse();\n\n this.loadingPromises.delete(id);\n\n if (this.hasActiveRef(id)) {\n if (this.loadedAssets.has(id)) {\n textureLoader.release(src);\n console.error(`Spritesheet already exists: ${src}`);\n } else {\n this.loadedAssets.set(id, spritesheet);\n return spritesheet;\n }\n } else {\n textureLoader.release(src);\n }\n })();\n\n this.loadingPromises.set(id, loadingPromise);\n return await loadingPromise;\n }\n\n protected override _dispose(id: string, spritesheet: Spritesheet) {\n spritesheet.destroy();\n\n const src = this.#idToSrc.get(id);\n if (src) textureLoader.release(src);\n }\n}\n\nconst spritesheetLoader = new SpritesheetLoader();\n\nexport { getCachedId, spritesheetLoader };\n"]}
@@ -0,0 +1,27 @@
1
+ import { Loader } from './loader';
2
+ class TextLoader extends Loader {
3
+ async _load(src) {
4
+ const loadingPromise = (async () => {
5
+ const response = await fetch(src);
6
+ if (!response.ok) {
7
+ console.error(`Failed to load text: ${src}`);
8
+ return;
9
+ }
10
+ const text = await response.text();
11
+ this.loadingPromises.delete(src);
12
+ if (this.hasActiveRef(src)) {
13
+ if (this.loadedAssets.has(src)) {
14
+ console.error(`Text already exists: ${src}`);
15
+ }
16
+ else {
17
+ this.loadedAssets.set(src, text);
18
+ return text;
19
+ }
20
+ }
21
+ })();
22
+ this.loadingPromises.set(src, loadingPromise);
23
+ return await loadingPromise;
24
+ }
25
+ }
26
+ export const textLoader = new TextLoader();
27
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/asset/loaders/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,UAAW,SAAQ,MAAc;IAClB,KAAK,CAAC,KAAK,CAAC,GAAW;QACxC,MAAM,cAAc,GAAG,CAAC,KAAK,IAAI,EAAE;YACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEnC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAEjC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3B,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC/B,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;gBAC/C,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBACjC,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9C,OAAO,MAAM,cAAc,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC","sourcesContent":["import { Loader } from './loader';\n\nclass TextLoader extends Loader<string> {\n protected override async _load(src: string) {\n const loadingPromise = (async () => {\n const response = await fetch(src);\n if (!response.ok) {\n console.error(`Failed to load text: ${src}`);\n return;\n }\n\n const text = await response.text();\n\n this.loadingPromises.delete(src);\n\n if (this.hasActiveRef(src)) {\n if (this.loadedAssets.has(src)) {\n console.error(`Text already exists: ${src}`);\n } else {\n this.loadedAssets.set(src, text);\n return text;\n }\n }\n })();\n\n this.loadingPromises.set(src, loadingPromise);\n return await loadingPromise;\n }\n}\n\nexport const textLoader = new TextLoader();\n"]}
@@ -0,0 +1,38 @@
1
+ import { Texture } from 'pixi.js';
2
+ import { Loader } from './loader';
3
+ class TextureLoader extends Loader {
4
+ async _load(src) {
5
+ const loadingPromise = new Promise((resolve) => {
6
+ const image = new Image();
7
+ image.crossOrigin = 'anonymous';
8
+ image.src = src;
9
+ image.onload = () => {
10
+ this.loadingPromises.delete(src);
11
+ if (!this.hasActiveRef(src)) {
12
+ resolve(undefined);
13
+ return;
14
+ }
15
+ if (this.loadedAssets.has(src)) {
16
+ console.error(`Texture already loaded: ${src}`);
17
+ resolve(undefined);
18
+ return;
19
+ }
20
+ const texture = Texture.from(image);
21
+ this.loadedAssets.set(src, texture);
22
+ resolve(texture);
23
+ };
24
+ image.onerror = (error) => {
25
+ this.loadingPromises.delete(src);
26
+ console.error(`Failed to load texture: ${src}`, error);
27
+ resolve(undefined);
28
+ };
29
+ });
30
+ this.loadingPromises.set(src, loadingPromise);
31
+ return await loadingPromise;
32
+ }
33
+ _dispose(src, texture) {
34
+ texture.destroy(true);
35
+ }
36
+ }
37
+ export const textureLoader = new TextureLoader();
38
+ //# sourceMappingURL=texture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"texture.js","sourceRoot":"","sources":["../../../src/asset/loaders/texture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,aAAc,SAAQ,MAAe;IACtB,KAAK,CAAC,KAAK,CAAC,GAAW;QACxC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAsB,CAAC,OAAO,EAAE,EAAE;YAClE,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;YAC1B,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;YAChC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;YAEhB,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE;gBAClB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAEjC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC5B,OAAO,CAAC,SAAS,CAAC,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC/B,OAAO,CAAC,KAAK,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;oBAChD,OAAO,CAAC,SAAS,CAAC,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBACpC,OAAO,CAAC,OAAO,CAAC,CAAC;YACnB,CAAC,CAAC;YAEF,KAAK,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;gBACxB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjC,OAAO,CAAC,KAAK,CAAC,2BAA2B,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;gBACvD,OAAO,CAAC,SAAS,CAAC,CAAC;YACrB,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9C,OAAO,MAAM,cAAc,CAAC;IAC9B,CAAC;IAEkB,QAAQ,CAAC,GAAW,EAAE,OAAgB;QACvD,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC","sourcesContent":["import { Texture } from 'pixi.js';\nimport { Loader } from './loader';\n\nclass TextureLoader extends Loader<Texture> {\n protected override async _load(src: string) {\n const loadingPromise = new Promise<Texture | undefined>((resolve) => {\n const image = new Image();\n image.crossOrigin = 'anonymous';\n image.src = src;\n\n image.onload = () => {\n this.loadingPromises.delete(src);\n\n if (!this.hasActiveRef(src)) {\n resolve(undefined);\n return;\n }\n\n if (this.loadedAssets.has(src)) {\n console.error(`Texture already loaded: ${src}`);\n resolve(undefined);\n return;\n }\n\n const texture = Texture.from(image);\n this.loadedAssets.set(src, texture);\n resolve(texture);\n };\n\n image.onerror = (error) => {\n this.loadingPromises.delete(src);\n console.error(`Failed to load texture: ${src}`, error);\n resolve(undefined);\n };\n });\n\n this.loadingPromises.set(src, loadingPromise);\n return await loadingPromise;\n }\n\n protected override _dispose(src: string, texture: Texture) {\n texture.destroy(true);\n }\n}\n\nexport const textureLoader = new TextureLoader();\n"]}