sa2kit 1.6.30 → 1.6.32

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 (251) hide show
  1. package/dist/AliyunOSSProvider-4W47OFEK.mjs +6 -0
  2. package/dist/{AliyunOSSProvider-KJYRIZES.mjs.map → AliyunOSSProvider-4W47OFEK.mjs.map} +1 -1
  3. package/dist/AliyunOSSProvider-HCNGDJL7.js +15 -0
  4. package/dist/{AliyunOSSProvider-FWAKUB2T.js.map → AliyunOSSProvider-HCNGDJL7.js.map} +1 -1
  5. package/dist/ConfigService-3DIC6C3Q.js +21 -0
  6. package/dist/{ConfigService-7MEZXKJ5.js.map → ConfigService-3DIC6C3Q.js.map} +1 -1
  7. package/dist/ConfigService-V6ZK273Z.mjs +4 -0
  8. package/dist/{ConfigService-BV57YYFW.mjs.map → ConfigService-V6ZK273Z.mjs.map} +1 -1
  9. package/dist/LocalStorageProvider-3RVPCQB3.mjs +6 -0
  10. package/dist/{LocalStorageProvider-RTPMUOZ2.mjs.map → LocalStorageProvider-3RVPCQB3.mjs.map} +1 -1
  11. package/dist/LocalStorageProvider-PP7MA5OT.js +15 -0
  12. package/dist/{LocalStorageProvider-XSRCUXOU.js.map → LocalStorageProvider-PP7MA5OT.js.map} +1 -1
  13. package/dist/PMXParser-2VTA737I.js +13 -0
  14. package/dist/{PMXParser-YBS3B6HM.js.map → PMXParser-2VTA737I.js.map} +1 -1
  15. package/dist/PMXParser-RNVQL76A.mjs +4 -0
  16. package/dist/{PMXParser-L6IWHL4I.mjs.map → PMXParser-RNVQL76A.mjs.map} +1 -1
  17. package/dist/analytics/index.js +46 -45
  18. package/dist/analytics/index.js.map +1 -1
  19. package/dist/analytics/index.mjs +45 -44
  20. package/dist/analytics/index.mjs.map +1 -1
  21. package/dist/analytics/server/index.js +4 -4
  22. package/dist/analytics/server/index.js.map +1 -1
  23. package/dist/analytics/server/index.mjs +4 -4
  24. package/dist/analytics/server/index.mjs.map +1 -1
  25. package/dist/api/index.js +5 -5
  26. package/dist/api/index.js.map +1 -1
  27. package/dist/api/index.mjs +5 -5
  28. package/dist/api/index.mjs.map +1 -1
  29. package/dist/audioDetection/index.js +17 -16
  30. package/dist/audioDetection/index.js.map +1 -1
  31. package/dist/audioDetection/index.mjs +17 -16
  32. package/dist/audioDetection/index.mjs.map +1 -1
  33. package/dist/auth/client/index.js +4 -4
  34. package/dist/auth/client/index.mjs +1 -1
  35. package/dist/auth/components/index.js +3 -3
  36. package/dist/auth/components/index.js.map +1 -1
  37. package/dist/auth/components/index.mjs +3 -3
  38. package/dist/auth/components/index.mjs.map +1 -1
  39. package/dist/auth/index.js +29 -29
  40. package/dist/auth/index.mjs +5 -5
  41. package/dist/auth/middleware/index.js +3 -3
  42. package/dist/auth/middleware/index.mjs +2 -2
  43. package/dist/auth/routes/index.js +14 -14
  44. package/dist/auth/routes/index.mjs +2 -2
  45. package/dist/auth/services/index.js +7 -7
  46. package/dist/auth/services/index.mjs +1 -1
  47. package/dist/calendar/index.js +146 -182
  48. package/dist/calendar/index.js.map +1 -1
  49. package/dist/calendar/index.mjs +139 -175
  50. package/dist/calendar/index.mjs.map +1 -1
  51. package/dist/calendar/routes/index.js +1 -1
  52. package/dist/calendar/routes/index.js.map +1 -1
  53. package/dist/calendar/routes/index.mjs +1 -1
  54. package/dist/calendar/routes/index.mjs.map +1 -1
  55. package/dist/{chunk-5YQ5B7IZ.js → chunk-24HGREE6.js} +5 -5
  56. package/dist/{chunk-5YQ5B7IZ.js.map → chunk-24HGREE6.js.map} +1 -1
  57. package/dist/{chunk-6PRFP5EG.js → chunk-25OFOKNF.js} +6 -6
  58. package/dist/chunk-25OFOKNF.js.map +1 -0
  59. package/dist/{chunk-KQGP6BTS.mjs → chunk-3DXPQ4YV.mjs} +6 -6
  60. package/dist/chunk-3DXPQ4YV.mjs.map +1 -0
  61. package/dist/{chunk-3BGPZN4X.mjs → chunk-3NHAT7D4.mjs} +12 -12
  62. package/dist/chunk-3NHAT7D4.mjs.map +1 -0
  63. package/dist/{chunk-MW4BCIZC.mjs → chunk-4HC6M7FK.mjs} +3 -3
  64. package/dist/chunk-4HC6M7FK.mjs.map +1 -0
  65. package/dist/{chunk-ESRCX5TQ.mjs → chunk-52TN2QSS.mjs} +3 -3
  66. package/dist/{chunk-ESRCX5TQ.mjs.map → chunk-52TN2QSS.mjs.map} +1 -1
  67. package/dist/{chunk-CNTILN5J.mjs → chunk-5YQ62BKX.mjs} +20 -19
  68. package/dist/chunk-5YQ62BKX.mjs.map +1 -0
  69. package/dist/{chunk-6W5BMXJG.js → chunk-6OWNMJKG.js} +4 -4
  70. package/dist/{chunk-6W5BMXJG.js.map → chunk-6OWNMJKG.js.map} +1 -1
  71. package/dist/{chunk-DUHZ7VZP.js → chunk-7VRT55ZD.js} +3 -3
  72. package/dist/chunk-7VRT55ZD.js.map +1 -0
  73. package/dist/{chunk-3WOAPLEG.mjs → chunk-EB4NR623.mjs} +27 -26
  74. package/dist/chunk-EB4NR623.mjs.map +1 -0
  75. package/dist/chunk-EI27JKND.mjs +1988 -0
  76. package/dist/chunk-EI27JKND.mjs.map +1 -0
  77. package/dist/{chunk-CD77U7LZ.js → chunk-GBPLX42J.js} +9 -9
  78. package/dist/chunk-GBPLX42J.js.map +1 -0
  79. package/dist/{chunk-TFQF2HDO.mjs → chunk-HDEOCX2L.mjs} +12 -12
  80. package/dist/chunk-HDEOCX2L.mjs.map +1 -0
  81. package/dist/{chunk-TV3VKRJK.mjs → chunk-HDMIOOZY.mjs} +38 -68
  82. package/dist/chunk-HDMIOOZY.mjs.map +1 -0
  83. package/dist/{chunk-OPPF3326.js → chunk-HJ6MH7J7.js} +39 -69
  84. package/dist/chunk-HJ6MH7J7.js.map +1 -0
  85. package/dist/chunk-KO73EBUT.js +80 -0
  86. package/dist/chunk-KO73EBUT.js.map +1 -0
  87. package/dist/{chunk-6YKMCPQI.mjs → chunk-KZKIH4AS.mjs} +4 -4
  88. package/dist/chunk-KZKIH4AS.mjs.map +1 -0
  89. package/dist/{chunk-LX4XX6W7.js → chunk-L47ZOYHL.js} +15 -89
  90. package/dist/chunk-L47ZOYHL.js.map +1 -0
  91. package/dist/{chunk-6MQUBPKB.mjs → chunk-LJ4CCSSY.mjs} +3 -3
  92. package/dist/{chunk-6MQUBPKB.mjs.map → chunk-LJ4CCSSY.mjs.map} +1 -1
  93. package/dist/{chunk-TOC5FSHP.js → chunk-NJ2SNXBJ.js} +12 -12
  94. package/dist/chunk-NJ2SNXBJ.js.map +1 -0
  95. package/dist/{chunk-OCR5DS4C.mjs → chunk-PE5EAHZK.mjs} +3 -3
  96. package/dist/chunk-PE5EAHZK.mjs.map +1 -0
  97. package/dist/{chunk-LZHMNOED.js → chunk-Q5EDCKQA.js} +26 -26
  98. package/dist/chunk-Q5EDCKQA.js.map +1 -0
  99. package/dist/{chunk-CLKKZSPZ.js → chunk-RBKGYWME.js} +20 -19
  100. package/dist/chunk-RBKGYWME.js.map +1 -0
  101. package/dist/{chunk-VRTRSEEH.mjs → chunk-RSJSZ7QH.mjs} +11 -11
  102. package/dist/chunk-RSJSZ7QH.mjs.map +1 -0
  103. package/dist/{chunk-E7RGBAYJ.js → chunk-TDCDEBGP.js} +30 -29
  104. package/dist/chunk-TDCDEBGP.js.map +1 -0
  105. package/dist/{chunk-JZXJQMVE.js → chunk-UIFFDRTE.js} +11 -11
  106. package/dist/chunk-UIFFDRTE.js.map +1 -0
  107. package/dist/{chunk-T5OZHYVM.mjs → chunk-UKT3PLON.mjs} +13 -85
  108. package/dist/chunk-UKT3PLON.mjs.map +1 -0
  109. package/dist/{chunk-UOFTHYIH.js → chunk-UL6XJGUZ.js} +4 -4
  110. package/dist/chunk-UL6XJGUZ.js.map +1 -0
  111. package/dist/chunk-VVWQTO4Y.mjs +77 -0
  112. package/dist/chunk-VVWQTO4Y.mjs.map +1 -0
  113. package/dist/{chunk-A3UP56MS.js → chunk-WA67GZSZ.js} +3 -3
  114. package/dist/chunk-WA67GZSZ.js.map +1 -0
  115. package/dist/{chunk-OLHGZXN3.mjs → chunk-WEEXCPSE.mjs} +5 -5
  116. package/dist/chunk-WEEXCPSE.mjs.map +1 -0
  117. package/dist/chunk-XGBE4SUV.js +2093 -0
  118. package/dist/chunk-XGBE4SUV.js.map +1 -0
  119. package/dist/{chunk-QU5OT4DF.js → chunk-XJ7ZAGC5.js} +5 -5
  120. package/dist/chunk-XJ7ZAGC5.js.map +1 -0
  121. package/dist/{chunk-ZI25QCHD.mjs → chunk-YOTQG4NP.mjs} +25 -25
  122. package/dist/chunk-YOTQG4NP.mjs.map +1 -0
  123. package/dist/{chunk-QAT2RWAO.mjs → chunk-Z36R3P62.mjs} +7 -7
  124. package/dist/chunk-Z36R3P62.mjs.map +1 -0
  125. package/dist/{chunk-7Z5LLJ3A.js → chunk-ZWQJSZEY.js} +13 -13
  126. package/dist/chunk-ZWQJSZEY.js.map +1 -0
  127. package/dist/config/index.js +6 -6
  128. package/dist/config/index.js.map +1 -1
  129. package/dist/config/index.mjs +6 -6
  130. package/dist/config/index.mjs.map +1 -1
  131. package/dist/config/server/index.js +37 -37
  132. package/dist/config/server/index.js.map +1 -1
  133. package/dist/config/server/index.mjs +37 -37
  134. package/dist/config/server/index.mjs.map +1 -1
  135. package/dist/i18n/index.d.mts +2 -2
  136. package/dist/i18n/index.d.ts +2 -2
  137. package/dist/i18n/index.js +16 -17
  138. package/dist/i18n/index.js.map +1 -1
  139. package/dist/i18n/index.mjs +16 -17
  140. package/dist/i18n/index.mjs.map +1 -1
  141. package/dist/imageCrop/index.js +11 -10
  142. package/dist/imageCrop/index.js.map +1 -1
  143. package/dist/imageCrop/index.mjs +11 -10
  144. package/dist/imageCrop/index.mjs.map +1 -1
  145. package/dist/index.d.mts +185 -100
  146. package/dist/index.d.ts +185 -100
  147. package/dist/index.js +225 -249
  148. package/dist/index.js.map +1 -1
  149. package/dist/index.mjs +80 -104
  150. package/dist/index.mjs.map +1 -1
  151. package/dist/logger/index.js +6 -6
  152. package/dist/logger/index.mjs +1 -1
  153. package/dist/mikuFusionGame/index.d.mts +112 -0
  154. package/dist/mikuFusionGame/index.d.ts +112 -0
  155. package/dist/mikuFusionGame/index.js +680 -0
  156. package/dist/mikuFusionGame/index.js.map +1 -0
  157. package/dist/mikuFusionGame/index.mjs +667 -0
  158. package/dist/mikuFusionGame/index.mjs.map +1 -0
  159. package/dist/mmd/admin/index.js +11 -10
  160. package/dist/mmd/admin/index.js.map +1 -1
  161. package/dist/mmd/admin/index.mjs +11 -10
  162. package/dist/mmd/admin/index.mjs.map +1 -1
  163. package/dist/mmd/index.js +223 -241
  164. package/dist/mmd/index.js.map +1 -1
  165. package/dist/mmd/index.mjs +220 -238
  166. package/dist/mmd/index.mjs.map +1 -1
  167. package/dist/mmd/server/index.js +6 -6
  168. package/dist/mmd/server/index.js.map +1 -1
  169. package/dist/mmd/server/index.mjs +6 -6
  170. package/dist/mmd/server/index.mjs.map +1 -1
  171. package/dist/music/index.js +16 -16
  172. package/dist/music/index.mjs +2 -2
  173. package/dist/music/server/index.js +8 -8
  174. package/dist/music/server/index.mjs +1 -1
  175. package/dist/request/index.js +2 -2
  176. package/dist/request/index.js.map +1 -1
  177. package/dist/request/index.mjs +2 -2
  178. package/dist/request/index.mjs.map +1 -1
  179. package/dist/storage/index.js +15 -14
  180. package/dist/storage/index.mjs +3 -2
  181. package/dist/testYourself/admin/index.js +3 -3
  182. package/dist/testYourself/admin/index.mjs +1 -1
  183. package/dist/testYourself/index.js +22 -22
  184. package/dist/testYourself/index.js.map +1 -1
  185. package/dist/testYourself/index.mjs +14 -14
  186. package/dist/testYourself/index.mjs.map +1 -1
  187. package/dist/testYourself/server/index.js +4 -4
  188. package/dist/testYourself/server/index.mjs +1 -1
  189. package/dist/universalExport/index.d.mts +3 -3
  190. package/dist/universalExport/index.d.ts +3 -3
  191. package/dist/universalExport/index.js +48 -47
  192. package/dist/universalExport/index.js.map +1 -1
  193. package/dist/universalExport/index.mjs +48 -47
  194. package/dist/universalExport/index.mjs.map +1 -1
  195. package/dist/universalExport/server/index.js +29 -29
  196. package/dist/universalExport/server/index.js.map +1 -1
  197. package/dist/universalExport/server/index.mjs +28 -28
  198. package/dist/universalExport/server/index.mjs.map +1 -1
  199. package/dist/universalFile/index.d.mts +3 -3
  200. package/dist/universalFile/index.d.ts +3 -3
  201. package/dist/universalFile/index.js +73 -72
  202. package/dist/universalFile/index.js.map +1 -1
  203. package/dist/universalFile/index.mjs +73 -72
  204. package/dist/universalFile/index.mjs.map +1 -1
  205. package/dist/universalFile/server/index.js +258 -260
  206. package/dist/universalFile/server/index.js.map +1 -1
  207. package/dist/universalFile/server/index.mjs +244 -246
  208. package/dist/universalFile/server/index.mjs.map +1 -1
  209. package/dist/utils/index.js +11 -11
  210. package/dist/utils/index.mjs +2 -2
  211. package/package.json +25 -31
  212. package/dist/AliyunOSSProvider-FWAKUB2T.js +0 -15
  213. package/dist/AliyunOSSProvider-KJYRIZES.mjs +0 -6
  214. package/dist/ConfigService-7MEZXKJ5.js +0 -21
  215. package/dist/ConfigService-BV57YYFW.mjs +0 -4
  216. package/dist/LocalStorageProvider-RTPMUOZ2.mjs +0 -6
  217. package/dist/LocalStorageProvider-XSRCUXOU.js +0 -15
  218. package/dist/PMXParser-L6IWHL4I.mjs +0 -4
  219. package/dist/PMXParser-YBS3B6HM.js +0 -13
  220. package/dist/chunk-3BGPZN4X.mjs.map +0 -1
  221. package/dist/chunk-3WOAPLEG.mjs.map +0 -1
  222. package/dist/chunk-6PRFP5EG.js.map +0 -1
  223. package/dist/chunk-6YKMCPQI.mjs.map +0 -1
  224. package/dist/chunk-7Z5LLJ3A.js.map +0 -1
  225. package/dist/chunk-A3UP56MS.js.map +0 -1
  226. package/dist/chunk-CD77U7LZ.js.map +0 -1
  227. package/dist/chunk-CLKKZSPZ.js.map +0 -1
  228. package/dist/chunk-CNTILN5J.mjs.map +0 -1
  229. package/dist/chunk-DUHZ7VZP.js.map +0 -1
  230. package/dist/chunk-DW2ZTOCV.js +0 -1727
  231. package/dist/chunk-DW2ZTOCV.js.map +0 -1
  232. package/dist/chunk-E7RGBAYJ.js.map +0 -1
  233. package/dist/chunk-JZXJQMVE.js.map +0 -1
  234. package/dist/chunk-KQGP6BTS.mjs.map +0 -1
  235. package/dist/chunk-LFG6FPM5.mjs +0 -1597
  236. package/dist/chunk-LFG6FPM5.mjs.map +0 -1
  237. package/dist/chunk-LX4XX6W7.js.map +0 -1
  238. package/dist/chunk-LZHMNOED.js.map +0 -1
  239. package/dist/chunk-MW4BCIZC.mjs.map +0 -1
  240. package/dist/chunk-OCR5DS4C.mjs.map +0 -1
  241. package/dist/chunk-OLHGZXN3.mjs.map +0 -1
  242. package/dist/chunk-OPPF3326.js.map +0 -1
  243. package/dist/chunk-QAT2RWAO.mjs.map +0 -1
  244. package/dist/chunk-QU5OT4DF.js.map +0 -1
  245. package/dist/chunk-T5OZHYVM.mjs.map +0 -1
  246. package/dist/chunk-TFQF2HDO.mjs.map +0 -1
  247. package/dist/chunk-TOC5FSHP.js.map +0 -1
  248. package/dist/chunk-TV3VKRJK.mjs.map +0 -1
  249. package/dist/chunk-UOFTHYIH.js.map +0 -1
  250. package/dist/chunk-VRTRSEEH.mjs.map +0 -1
  251. package/dist/chunk-ZI25QCHD.mjs.map +0 -1
@@ -0,0 +1,680 @@
1
+ 'use strict';
2
+
3
+ var chunkKO73EBUT_js = require('../chunk-KO73EBUT.js');
4
+ require('../chunk-XJ7ZAGC5.js');
5
+ require('../chunk-DGUM43GV.js');
6
+ var React3 = require('react');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
11
+
12
+ // src/mikuFusionGame/constants.ts
13
+ var BASE_RADIUS = 22;
14
+ var RADIUS_STEP = 4;
15
+ var LEVEL_LABEL_PREFIX = "M";
16
+ var DEFAULT_MIKU_FUSION_CONFIG = {
17
+ width: 390,
18
+ height: 700,
19
+ gravity: 1450,
20
+ damping: 0.995,
21
+ collisionDamping: 0.92,
22
+ spawnY: 82,
23
+ lossLineY: 132,
24
+ maxLevel: 10,
25
+ maxOrbs: 90,
26
+ maxMergesPerTick: 6,
27
+ gameOverAgeThreshold: 1.1,
28
+ spawnWeights: [0.62, 0.28, 0.1],
29
+ theme: {
30
+ backgroundTop: "#dafaff",
31
+ backgroundBottom: "#86e5ef",
32
+ aimLine: "#14b8a6",
33
+ lossLine: "#ef4444",
34
+ orbColors: [
35
+ "#67e8f9",
36
+ "#22d3ee",
37
+ "#2dd4bf",
38
+ "#5eead4",
39
+ "#34d399",
40
+ "#a7f3d0",
41
+ "#99f6e4",
42
+ "#0ea5e9",
43
+ "#06b6d4",
44
+ "#14b8a6"
45
+ ]
46
+ }
47
+ };
48
+
49
+ // src/mikuFusionGame/engine/collision.ts
50
+ function resolveCircleCollisions(orbs, config) {
51
+ const next = orbs.map((orb) => ({ ...orb }));
52
+ for (let i = 0; i < next.length; i += 1) {
53
+ for (let j = i + 1; j < next.length; j += 1) {
54
+ const a = next[i];
55
+ const b = next[j];
56
+ const dx = b.x - a.x;
57
+ const dy = b.y - a.y;
58
+ const distance = Math.sqrt(dx * dx + dy * dy) || 1e-4;
59
+ const minDistance = a.radius + b.radius;
60
+ if (distance >= minDistance) {
61
+ continue;
62
+ }
63
+ const nx = dx / distance;
64
+ const ny = dy / distance;
65
+ const overlap = minDistance - distance;
66
+ a.x -= nx * overlap * 0.5;
67
+ a.y -= ny * overlap * 0.5;
68
+ b.x += nx * overlap * 0.5;
69
+ b.y += ny * overlap * 0.5;
70
+ const rvx = b.vx - a.vx;
71
+ const rvy = b.vy - a.vy;
72
+ const velocityAlongNormal = rvx * nx + rvy * ny;
73
+ if (velocityAlongNormal > 0) {
74
+ continue;
75
+ }
76
+ const restitution = config.collisionDamping;
77
+ const impulse = -(1 + restitution) * velocityAlongNormal / 2;
78
+ const impulseX = impulse * nx;
79
+ const impulseY = impulse * ny;
80
+ a.vx -= impulseX;
81
+ a.vy -= impulseY;
82
+ b.vx += impulseX;
83
+ b.vy += impulseY;
84
+ }
85
+ }
86
+ return next;
87
+ }
88
+
89
+ // src/mikuFusionGame/engine/scoring.ts
90
+ function getMergeScore(newLevel, chainIndex) {
91
+ const base = 10 * Math.max(1, newLevel) * Math.max(1, newLevel);
92
+ const chainMultiplier = 1 + Math.max(0, chainIndex - 1) * 0.15;
93
+ return Math.round(base * chainMultiplier);
94
+ }
95
+
96
+ // src/mikuFusionGame/engine/physics.ts
97
+ function getRadiusByLevel(level) {
98
+ return BASE_RADIUS + (Math.max(1, level) - 1) * RADIUS_STEP;
99
+ }
100
+ function stepPhysics(orbs, config, dt) {
101
+ return orbs.map((orb) => {
102
+ const next = { ...orb };
103
+ next.vy += config.gravity * dt;
104
+ next.vx *= config.damping;
105
+ next.vy *= config.damping;
106
+ next.x += next.vx * dt;
107
+ next.y += next.vy * dt;
108
+ next.age += dt;
109
+ if (next.x - next.radius < 0) {
110
+ next.x = next.radius;
111
+ next.vx = Math.abs(next.vx) * config.collisionDamping;
112
+ } else if (next.x + next.radius > config.width) {
113
+ next.x = config.width - next.radius;
114
+ next.vx = -Math.abs(next.vx) * config.collisionDamping;
115
+ }
116
+ if (next.y + next.radius > config.height) {
117
+ next.y = config.height - next.radius;
118
+ next.vy = -Math.abs(next.vy) * config.collisionDamping;
119
+ }
120
+ return next;
121
+ });
122
+ }
123
+
124
+ // src/mikuFusionGame/engine/merge.ts
125
+ function canMerge(a, b, config) {
126
+ if (a.level !== b.level) {
127
+ return false;
128
+ }
129
+ if (a.level >= config.maxLevel) {
130
+ return false;
131
+ }
132
+ const dx = b.x - a.x;
133
+ const dy = b.y - a.y;
134
+ const distance = Math.sqrt(dx * dx + dy * dy);
135
+ const threshold = (a.radius + b.radius) * 0.92;
136
+ return distance <= threshold;
137
+ }
138
+ function mergeSameLevelOrbs(orbs, config) {
139
+ const mergedIds = /* @__PURE__ */ new Set();
140
+ const spawned = [];
141
+ let scoreGain = 0;
142
+ let chainIndex = 0;
143
+ for (let i = 0; i < orbs.length; i += 1) {
144
+ if (mergedIds.has(orbs[i].id)) {
145
+ continue;
146
+ }
147
+ for (let j = i + 1; j < orbs.length; j += 1) {
148
+ if (mergedIds.has(orbs[j].id)) {
149
+ continue;
150
+ }
151
+ const a = orbs[i];
152
+ const b = orbs[j];
153
+ if (!canMerge(a, b, config)) {
154
+ continue;
155
+ }
156
+ mergedIds.add(a.id);
157
+ mergedIds.add(b.id);
158
+ chainIndex += 1;
159
+ const nextLevel = Math.min(config.maxLevel, a.level + 1);
160
+ const mergedOrb = {
161
+ id: `orb-${a.id}-${b.id}-${chainIndex}`,
162
+ x: (a.x + b.x) / 2,
163
+ y: (a.y + b.y) / 2,
164
+ vx: (a.vx + b.vx) / 2 * 0.4,
165
+ vy: (a.vy + b.vy) / 2 * 0.4,
166
+ radius: getRadiusByLevel(nextLevel),
167
+ level: nextLevel,
168
+ age: Math.min(a.age, b.age)
169
+ };
170
+ spawned.push(mergedOrb);
171
+ scoreGain += getMergeScore(nextLevel, chainIndex);
172
+ if (chainIndex >= config.maxMergesPerTick) {
173
+ break;
174
+ }
175
+ }
176
+ if (chainIndex >= config.maxMergesPerTick) {
177
+ break;
178
+ }
179
+ }
180
+ if (mergedIds.size === 0) {
181
+ return {
182
+ orbs,
183
+ scoreGain: 0,
184
+ mergeCount: 0
185
+ };
186
+ }
187
+ const survivors = orbs.filter((orb) => !mergedIds.has(orb.id));
188
+ const mergedOrbs = survivors.concat(spawned);
189
+ return {
190
+ orbs: mergedOrbs,
191
+ scoreGain,
192
+ mergeCount: chainIndex
193
+ };
194
+ }
195
+
196
+ // src/mikuFusionGame/engine/stateMachine.ts
197
+ var ALLOWED_TRANSITIONS = {
198
+ ready: ["playing"],
199
+ playing: ["paused", "gameOver", "ready"],
200
+ paused: ["playing", "ready"],
201
+ gameOver: ["ready", "playing"]
202
+ };
203
+ function canTransition(from, to) {
204
+ return ALLOWED_TRANSITIONS[from].includes(to);
205
+ }
206
+
207
+ // src/mikuFusionGame/hooks/useMikuFusionGame.ts
208
+ function clamp(value, min, max) {
209
+ return Math.max(min, Math.min(max, value));
210
+ }
211
+ function nextOrbLevel(weights) {
212
+ const random = Math.random();
213
+ let cumulative = 0;
214
+ for (let i = 0; i < weights.length; i += 1) {
215
+ cumulative += weights[i] ?? 0;
216
+ if (random <= cumulative) {
217
+ return i + 1;
218
+ }
219
+ }
220
+ return Math.max(1, weights.length);
221
+ }
222
+ function getOrbColor(level, config) {
223
+ const index = Math.max(0, Math.min(config.theme.orbColors.length - 1, level - 1));
224
+ return config.theme.orbColors[index] || "#22d3ee";
225
+ }
226
+ function drawScene(context, width, height, config, orbs, aimX, nextLevel, status) {
227
+ const gradient = context.createLinearGradient(0, 0, 0, height);
228
+ gradient.addColorStop(0, config.theme.backgroundTop);
229
+ gradient.addColorStop(1, config.theme.backgroundBottom);
230
+ context.fillStyle = gradient;
231
+ context.fillRect(0, 0, width, height);
232
+ context.lineWidth = 2;
233
+ context.strokeStyle = config.theme.lossLine;
234
+ context.setLineDash([6, 6]);
235
+ context.beginPath();
236
+ context.moveTo(0, config.lossLineY);
237
+ context.lineTo(width, config.lossLineY);
238
+ context.stroke();
239
+ context.setLineDash([]);
240
+ context.strokeStyle = config.theme.aimLine;
241
+ context.lineWidth = 1.5;
242
+ context.beginPath();
243
+ context.moveTo(aimX, 0);
244
+ context.lineTo(aimX, config.spawnY);
245
+ context.stroke();
246
+ const previewRadius = getRadiusByLevel(nextLevel);
247
+ context.fillStyle = getOrbColor(nextLevel, config);
248
+ context.beginPath();
249
+ context.arc(aimX, config.spawnY - previewRadius - 4, previewRadius, 0, Math.PI * 2);
250
+ context.fill();
251
+ orbs.forEach((orb) => {
252
+ context.beginPath();
253
+ context.fillStyle = getOrbColor(orb.level, config);
254
+ context.arc(orb.x, orb.y, orb.radius, 0, Math.PI * 2);
255
+ context.fill();
256
+ context.fillStyle = "#0f172a";
257
+ context.font = "bold 12px system-ui";
258
+ context.textAlign = "center";
259
+ context.textBaseline = "middle";
260
+ context.fillText(`${LEVEL_LABEL_PREFIX}${orb.level}`, orb.x, orb.y);
261
+ });
262
+ if (status === "paused") {
263
+ context.fillStyle = "rgba(15, 23, 42, 0.5)";
264
+ context.fillRect(0, 0, width, height);
265
+ context.fillStyle = "#ffffff";
266
+ context.font = "bold 30px system-ui";
267
+ context.textAlign = "center";
268
+ context.fillText("Paused", width / 2, height / 2);
269
+ }
270
+ }
271
+ function normalizeConfig(config) {
272
+ const mergedTheme = {
273
+ ...DEFAULT_MIKU_FUSION_CONFIG.theme,
274
+ ...config?.theme ?? {}
275
+ };
276
+ const merged = {
277
+ ...DEFAULT_MIKU_FUSION_CONFIG,
278
+ ...config ?? {},
279
+ theme: mergedTheme
280
+ };
281
+ const totalWeight = merged.spawnWeights.reduce((sum, current) => sum + current, 0);
282
+ if (totalWeight <= 0) {
283
+ merged.spawnWeights = [...DEFAULT_MIKU_FUSION_CONFIG.spawnWeights];
284
+ } else if (Math.abs(totalWeight - 1) > 1e-3) {
285
+ merged.spawnWeights = merged.spawnWeights.map((weight) => weight / totalWeight);
286
+ }
287
+ return merged;
288
+ }
289
+ function useMikuFusionGame(options = {}) {
290
+ const configRef = React3.useRef(normalizeConfig(options));
291
+ const config = configRef.current;
292
+ const onScoreChangeRef = React3.useRef(options.onScoreChange);
293
+ const onGameOverRef = React3.useRef(options.onGameOver);
294
+ React3.useEffect(() => {
295
+ onScoreChangeRef.current = options.onScoreChange;
296
+ onGameOverRef.current = options.onGameOver;
297
+ }, [options.onGameOver, options.onScoreChange]);
298
+ const [bestScore, setBestScore] = chunkKO73EBUT_js.useLocalStorage(
299
+ options.storageKey || "sa2kit:mikuFusionGame:bestScore",
300
+ 0
301
+ );
302
+ const canvasRef = React3.useRef(null);
303
+ const rafRef = React3.useRef(null);
304
+ const lastTsRef = React3.useRef(0);
305
+ const idRef = React3.useRef(0);
306
+ const orbsRef = React3.useRef([]);
307
+ const aimXRef = React3.useRef(config.width / 2);
308
+ const scoreRef = React3.useRef(0);
309
+ const [status, setStatus] = React3.useState("ready");
310
+ const [score, setScore] = React3.useState(0);
311
+ const [nextLevel, setNextLevel] = React3.useState(() => nextOrbLevel(config.spawnWeights));
312
+ const draw = React3.useCallback(
313
+ (nextStatus = status) => {
314
+ const canvas = canvasRef.current;
315
+ if (!canvas) {
316
+ return;
317
+ }
318
+ const context = canvas.getContext("2d");
319
+ if (!context) {
320
+ return;
321
+ }
322
+ drawScene(
323
+ context,
324
+ config.width,
325
+ config.height,
326
+ config,
327
+ orbsRef.current,
328
+ aimXRef.current,
329
+ nextLevel,
330
+ nextStatus
331
+ );
332
+ },
333
+ [config, nextLevel, status]
334
+ );
335
+ const transitionStatus = React3.useCallback((nextStatus) => {
336
+ setStatus((previous) => canTransition(previous, nextStatus) ? nextStatus : previous);
337
+ }, []);
338
+ const spawnOrb = React3.useCallback(() => {
339
+ if (status === "paused" || status === "gameOver") {
340
+ return;
341
+ }
342
+ if (status === "ready") {
343
+ transitionStatus("playing");
344
+ }
345
+ idRef.current += 1;
346
+ const radius = getRadiusByLevel(nextLevel);
347
+ const x = clamp(aimXRef.current, radius, config.width - radius);
348
+ const created = {
349
+ id: `${idRef.current}`,
350
+ x,
351
+ y: config.spawnY,
352
+ vx: 0,
353
+ vy: 0,
354
+ radius,
355
+ level: nextLevel,
356
+ age: 0
357
+ };
358
+ const nextOrbs = orbsRef.current.concat(created);
359
+ orbsRef.current = nextOrbs.slice(-config.maxOrbs);
360
+ setNextLevel(nextOrbLevel(config.spawnWeights));
361
+ }, [config, nextLevel, status, transitionStatus]);
362
+ const restart = React3.useCallback(() => {
363
+ orbsRef.current = [];
364
+ scoreRef.current = 0;
365
+ setScore(0);
366
+ setNextLevel(nextOrbLevel(config.spawnWeights));
367
+ transitionStatus("ready");
368
+ draw("ready");
369
+ }, [config.spawnWeights, draw, transitionStatus]);
370
+ const togglePause = React3.useCallback(() => {
371
+ setStatus((previous) => {
372
+ if (previous === "playing" && canTransition(previous, "paused")) {
373
+ return "paused";
374
+ }
375
+ if (previous === "paused" && canTransition(previous, "playing")) {
376
+ return "playing";
377
+ }
378
+ return previous;
379
+ });
380
+ }, []);
381
+ const setAimFromClientX = React3.useCallback((clientX, rect) => {
382
+ const ratio = config.width / rect.width;
383
+ const x = (clientX - rect.left) * ratio;
384
+ aimXRef.current = clamp(x, 0, config.width);
385
+ }, [config.width]);
386
+ const handleDrop = React3.useCallback(
387
+ (clientX, rect) => {
388
+ setAimFromClientX(clientX, rect);
389
+ spawnOrb();
390
+ },
391
+ [setAimFromClientX, spawnOrb]
392
+ );
393
+ React3.useEffect(() => {
394
+ draw(status);
395
+ }, [draw, status]);
396
+ React3.useEffect(() => {
397
+ if (status !== "playing") {
398
+ if (rafRef.current) {
399
+ window.cancelAnimationFrame(rafRef.current);
400
+ }
401
+ lastTsRef.current = 0;
402
+ draw(status);
403
+ return;
404
+ }
405
+ const frame = (timestamp) => {
406
+ if (lastTsRef.current === 0) {
407
+ lastTsRef.current = timestamp;
408
+ }
409
+ const dt = Math.min((timestamp - lastTsRef.current) / 1e3, 0.033);
410
+ lastTsRef.current = timestamp;
411
+ let orbs = stepPhysics(orbsRef.current, config, dt);
412
+ orbs = resolveCircleCollisions(orbs, config);
413
+ const merged = mergeSameLevelOrbs(orbs, config);
414
+ orbs = merged.orbs;
415
+ orbsRef.current = orbs.slice(-config.maxOrbs);
416
+ if (merged.scoreGain > 0) {
417
+ scoreRef.current += merged.scoreGain;
418
+ setScore(scoreRef.current);
419
+ onScoreChangeRef.current?.(scoreRef.current);
420
+ }
421
+ const gameOver = orbs.some(
422
+ (orb) => orb.age > config.gameOverAgeThreshold && orb.y - orb.radius <= config.lossLineY && Math.abs(orb.vy) < 90
423
+ );
424
+ if (gameOver) {
425
+ transitionStatus("gameOver");
426
+ const latestBest = Math.max(bestScore, scoreRef.current);
427
+ if (latestBest !== bestScore) {
428
+ setBestScore(latestBest);
429
+ }
430
+ onGameOverRef.current?.(scoreRef.current, latestBest);
431
+ draw("gameOver");
432
+ return;
433
+ }
434
+ draw("playing");
435
+ rafRef.current = window.requestAnimationFrame(frame);
436
+ };
437
+ rafRef.current = window.requestAnimationFrame(frame);
438
+ return () => {
439
+ if (rafRef.current) {
440
+ window.cancelAnimationFrame(rafRef.current);
441
+ }
442
+ };
443
+ }, [
444
+ bestScore,
445
+ config,
446
+ draw,
447
+ setBestScore,
448
+ status,
449
+ transitionStatus
450
+ ]);
451
+ React3.useEffect(() => {
452
+ if (score > bestScore) {
453
+ setBestScore(score);
454
+ }
455
+ }, [bestScore, score, setBestScore]);
456
+ return {
457
+ canvasRef,
458
+ status,
459
+ score,
460
+ bestScore: Math.max(bestScore, score),
461
+ nextLevel,
462
+ config,
463
+ setAimFromClientX,
464
+ handleDrop,
465
+ togglePause,
466
+ restart
467
+ };
468
+ }
469
+ function useResponsiveCanvas(logicalWidth, logicalHeight, containerRef) {
470
+ const [size, setSize] = React3.useState({
471
+ displayWidth: logicalWidth,
472
+ displayHeight: logicalHeight,
473
+ scale: 1
474
+ });
475
+ React3.useEffect(() => {
476
+ const updateSize = () => {
477
+ const container2 = containerRef.current;
478
+ if (!container2 || typeof window === "undefined") {
479
+ return;
480
+ }
481
+ const rect = container2.getBoundingClientRect();
482
+ const maxWidth = Math.max(280, rect.width);
483
+ const maxHeight = Math.max(420, Math.floor(window.innerHeight * 0.78));
484
+ const scale = Math.min(maxWidth / logicalWidth, maxHeight / logicalHeight, 1);
485
+ setSize({
486
+ displayWidth: Math.floor(logicalWidth * scale),
487
+ displayHeight: Math.floor(logicalHeight * scale),
488
+ scale
489
+ });
490
+ };
491
+ updateSize();
492
+ const container = containerRef.current;
493
+ const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(updateSize) : null;
494
+ if (container && observer) {
495
+ observer.observe(container);
496
+ }
497
+ window.addEventListener("resize", updateSize);
498
+ return () => {
499
+ observer?.disconnect();
500
+ window.removeEventListener("resize", updateSize);
501
+ };
502
+ }, [containerRef, logicalWidth, logicalHeight]);
503
+ return size;
504
+ }
505
+ function GameCanvas({
506
+ canvasRef,
507
+ width,
508
+ height,
509
+ displayWidth,
510
+ displayHeight,
511
+ onPointerMove,
512
+ onPointerDown,
513
+ onPointerUp
514
+ }) {
515
+ return /* @__PURE__ */ React3__default.default.createElement(
516
+ "canvas",
517
+ {
518
+ ref: canvasRef,
519
+ width,
520
+ height,
521
+ className: "rounded-xl border border-cyan-200 bg-cyan-50 shadow-sm",
522
+ style: {
523
+ width: `${displayWidth}px`,
524
+ height: `${displayHeight}px`,
525
+ maxWidth: "100%",
526
+ touchAction: "none"
527
+ },
528
+ onPointerMove,
529
+ onPointerDown,
530
+ onPointerUp,
531
+ onContextMenu: (event) => event.preventDefault()
532
+ }
533
+ );
534
+ }
535
+ function GameControls({ isPaused, isPlaying, onTogglePause, onRestart }) {
536
+ return /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ React3__default.default.createElement(
537
+ "button",
538
+ {
539
+ type: "button",
540
+ className: "rounded-lg bg-cyan-600 px-4 py-3 text-sm font-semibold text-white disabled:cursor-not-allowed disabled:opacity-50",
541
+ onClick: onTogglePause,
542
+ disabled: !isPlaying
543
+ },
544
+ isPaused ? "\u7EE7\u7EED" : "\u6682\u505C"
545
+ ), /* @__PURE__ */ React3__default.default.createElement(
546
+ "button",
547
+ {
548
+ type: "button",
549
+ className: "rounded-lg bg-emerald-600 px-4 py-3 text-sm font-semibold text-white",
550
+ onClick: onRestart
551
+ },
552
+ "\u91CD\u5F00"
553
+ ));
554
+ }
555
+ function GameHUD({ score, bestScore, nextLevel, statusText }) {
556
+ return /* @__PURE__ */ React3__default.default.createElement("div", { className: "flex items-center justify-between gap-2 rounded-lg border border-cyan-200 bg-white/80 p-3" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "text-xs text-slate-500" }, "\u5F53\u524D\u5206\u6570"), /* @__PURE__ */ React3__default.default.createElement("div", { className: "text-xl font-bold text-cyan-700" }, score), /* @__PURE__ */ React3__default.default.createElement("div", { className: "text-xs text-slate-500" }, "\u6700\u9AD8\u5206 ", bestScore)), /* @__PURE__ */ React3__default.default.createElement("div", { className: "text-right" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "text-xs text-slate-500" }, "\u4E0B\u4E00\u4E2A"), /* @__PURE__ */ React3__default.default.createElement("div", { className: "text-base font-semibold text-emerald-700" }, "M", nextLevel), /* @__PURE__ */ React3__default.default.createElement("div", { className: "text-xs font-medium text-slate-600" }, statusText)));
557
+ }
558
+ function GameResultModal({ open, score, bestScore, onRestart }) {
559
+ if (!open) {
560
+ return null;
561
+ }
562
+ return /* @__PURE__ */ React3__default.default.createElement("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center bg-slate-900/50 p-4" }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "pointer-events-auto w-full max-w-xs rounded-xl bg-white p-5 shadow-2xl" }, /* @__PURE__ */ React3__default.default.createElement("h3", { className: "text-lg font-semibold text-slate-900" }, "\u6E38\u620F\u7ED3\u675F"), /* @__PURE__ */ React3__default.default.createElement("p", { className: "mt-2 text-sm text-slate-600" }, "\u5F53\u524D\u5206\u6570\uFF1A", score), /* @__PURE__ */ React3__default.default.createElement("p", { className: "text-sm text-slate-600" }, "\u6700\u9AD8\u5206\uFF1A", bestScore), /* @__PURE__ */ React3__default.default.createElement(
563
+ "button",
564
+ {
565
+ type: "button",
566
+ className: "mt-4 w-full rounded-lg bg-cyan-600 px-4 py-2 text-sm font-semibold text-white",
567
+ onClick: onRestart
568
+ },
569
+ "\u518D\u6765\u4E00\u5C40"
570
+ )));
571
+ }
572
+
573
+ // src/mikuFusionGame/components/MikuFusionGame.tsx
574
+ function MikuFusionGame({ className, storageKey, ...options }) {
575
+ const containerRef = React3.useRef(null);
576
+ const {
577
+ canvasRef,
578
+ status,
579
+ score,
580
+ bestScore,
581
+ nextLevel,
582
+ config,
583
+ setAimFromClientX,
584
+ handleDrop,
585
+ togglePause,
586
+ restart
587
+ } = useMikuFusionGame({ ...options, storageKey });
588
+ const { displayWidth, displayHeight } = useResponsiveCanvas(
589
+ config.width,
590
+ config.height,
591
+ containerRef
592
+ );
593
+ const statusText = React3.useMemo(() => {
594
+ if (status === "ready") {
595
+ return "\u5F85\u5F00\u59CB";
596
+ }
597
+ if (status === "playing") {
598
+ return "\u8FDB\u884C\u4E2D";
599
+ }
600
+ if (status === "paused") {
601
+ return "\u5DF2\u6682\u505C";
602
+ }
603
+ return "\u5DF2\u7ED3\u675F";
604
+ }, [status]);
605
+ const handlePointerMove = (event) => {
606
+ const rect = event.currentTarget.getBoundingClientRect();
607
+ setAimFromClientX(event.clientX, rect);
608
+ };
609
+ const handlePointerDown = (event) => {
610
+ event.currentTarget.setPointerCapture(event.pointerId);
611
+ const rect = event.currentTarget.getBoundingClientRect();
612
+ setAimFromClientX(event.clientX, rect);
613
+ };
614
+ const handlePointerUp = (event) => {
615
+ const rect = event.currentTarget.getBoundingClientRect();
616
+ handleDrop(event.clientX, rect);
617
+ event.currentTarget.releasePointerCapture(event.pointerId);
618
+ };
619
+ const handleKeyDown = (event) => {
620
+ if (event.key.toLowerCase() === "r") {
621
+ restart();
622
+ }
623
+ if (event.key.toLowerCase() === "p") {
624
+ togglePause();
625
+ }
626
+ };
627
+ return /* @__PURE__ */ React3__default.default.createElement(
628
+ "div",
629
+ {
630
+ ref: containerRef,
631
+ tabIndex: 0,
632
+ onKeyDown: handleKeyDown,
633
+ className: `relative mx-auto flex w-full max-w-xl flex-col gap-3 p-3 md:p-4 ${className || ""}`,
634
+ style: {
635
+ paddingBottom: "max(0.75rem, env(safe-area-inset-bottom))"
636
+ }
637
+ },
638
+ /* @__PURE__ */ React3__default.default.createElement(GameHUD, { score, bestScore, nextLevel, statusText }),
639
+ /* @__PURE__ */ React3__default.default.createElement("div", { className: "relative mx-auto" }, /* @__PURE__ */ React3__default.default.createElement(
640
+ GameCanvas,
641
+ {
642
+ canvasRef,
643
+ width: config.width,
644
+ height: config.height,
645
+ displayWidth,
646
+ displayHeight,
647
+ onPointerMove: handlePointerMove,
648
+ onPointerDown: handlePointerDown,
649
+ onPointerUp: handlePointerUp
650
+ }
651
+ ), /* @__PURE__ */ React3__default.default.createElement(GameResultModal, { open: status === "gameOver", score, bestScore, onRestart: restart })),
652
+ /* @__PURE__ */ React3__default.default.createElement(
653
+ GameControls,
654
+ {
655
+ isPaused: status === "paused",
656
+ isPlaying: status === "playing" || status === "paused",
657
+ onTogglePause: togglePause,
658
+ onRestart: restart
659
+ }
660
+ )
661
+ );
662
+ }
663
+
664
+ // src/mikuFusionGame/content/index.ts
665
+ var defaultContentRegistry = {
666
+ producers: [],
667
+ songs: [],
668
+ themes: []
669
+ };
670
+
671
+ exports.BASE_RADIUS = BASE_RADIUS;
672
+ exports.DEFAULT_MIKU_FUSION_CONFIG = DEFAULT_MIKU_FUSION_CONFIG;
673
+ exports.LEVEL_LABEL_PREFIX = LEVEL_LABEL_PREFIX;
674
+ exports.MikuFusionGame = MikuFusionGame;
675
+ exports.RADIUS_STEP = RADIUS_STEP;
676
+ exports.defaultContentRegistry = defaultContentRegistry;
677
+ exports.useMikuFusionGame = useMikuFusionGame;
678
+ exports.useResponsiveCanvas = useResponsiveCanvas;
679
+ //# sourceMappingURL=index.js.map
680
+ //# sourceMappingURL=index.js.map