maidraw 0.9.5 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/assets/themes/chunithm/best/luminousLandscape/new/manifest.json +58 -32
  2. package/assets/themes/chunithm/best/luminousLandscape/recents/manifest.json +57 -31
  3. package/assets/themes/chunithm/best/luminousPlusLandscape/new/manifest.json +57 -31
  4. package/assets/themes/chunithm/best/luminousPlusLandscape/recents/manifest.json +57 -31
  5. package/assets/themes/chunithm/best/verseLandscape/new/manifest.json +57 -31
  6. package/assets/themes/chunithm/best/verseLandscape/recents/manifest.json +57 -31
  7. package/assets/themes/chunithm/chart/jp/verse/manifest.json +73 -71
  8. package/assets/themes/maimai/best50/cn/2024Landscape/manifest.json +90 -54
  9. package/assets/themes/maimai/best50/cn/2024Portrait/manifest.json +89 -53
  10. package/assets/themes/maimai/best50/cn/2025Landscape/manifest.json +90 -54
  11. package/assets/themes/maimai/best50/cn/2025Portrait/manifest.json +89 -53
  12. package/assets/themes/maimai/best50/common/prism/plus/background.png +0 -0
  13. package/assets/themes/maimai/best50/jp/buddiesLandscape/manifest.json +89 -53
  14. package/assets/themes/maimai/best50/jp/buddiesPlusLandscape/manifest.json +89 -53
  15. package/assets/themes/maimai/best50/jp/buddiesPlusPortrait/manifest.json +89 -53
  16. package/assets/themes/maimai/best50/jp/buddiesPortrait/manifest.json +89 -53
  17. package/assets/themes/maimai/best50/jp/finaleLandscape/manifest.json +89 -53
  18. package/assets/themes/maimai/best50/jp/finalePortrait/manifest.json +89 -53
  19. package/assets/themes/maimai/best50/jp/prismLandscape/manifest.json +90 -54
  20. package/assets/themes/maimai/best50/jp/prismPlusLandscape/manifest.json +90 -54
  21. package/assets/themes/maimai/best50/jp/prismPlusPortrait/manifest.json +92 -55
  22. package/assets/themes/maimai/best50/jp/prismPortrait/manifest.json +89 -53
  23. package/assets/themes/maimai/best50/salt/2026landscape/manifest.json +89 -53
  24. package/assets/themes/maimai/chart/jp/prism/manifest.json +100 -96
  25. package/assets/themes/ongeki/jp/brightMemoryLandscape/classic/manifest.json +101 -30
  26. package/assets/themes/ongeki/jp/brightMemoryLandscape/refresh/manifest.json +78 -30
  27. package/assets/themes/ongeki/jp/refreshLandscape/classic/manifest.json +101 -2
  28. package/assets/themes/ongeki/jp/refreshLandscape/refresh/manifest.json +78 -30
  29. package/dist/chu/index.d.ts +14 -4
  30. package/dist/chu/index.js +16 -4
  31. package/dist/chu/index.js.map +1 -1
  32. package/dist/chu/lib/{index.d.ts → adapter/index.d.ts} +1 -1
  33. package/dist/chu/lib/{index.js → adapter/index.js} +1 -1
  34. package/dist/chu/lib/adapter/index.js.map +1 -0
  35. package/dist/chu/lib/{kamaiTachi → adapter/kamaiTachi}/index.d.ts +1 -1
  36. package/dist/chu/lib/{kamaiTachi → adapter/kamaiTachi}/index.js +5 -5
  37. package/dist/chu/lib/adapter/kamaiTachi/index.js.map +1 -0
  38. package/dist/chu/lib/{lxns → adapter/lxns}/index.d.ts +1 -1
  39. package/dist/chu/lib/{lxns → adapter/lxns}/index.js +4 -4
  40. package/dist/chu/lib/adapter/lxns/index.js.map +1 -0
  41. package/dist/chu/{chart → lib}/database.d.ts +3 -3
  42. package/dist/chu/{chart → lib}/database.js +2 -2
  43. package/dist/chu/lib/database.js.map +1 -0
  44. package/dist/chu/lib/util.d.ts +149 -0
  45. package/dist/chu/lib/util.js +270 -0
  46. package/dist/chu/lib/util.js.map +1 -0
  47. package/dist/chu/painter/best50/index.d.ts +120 -0
  48. package/dist/chu/painter/best50/index.js +158 -0
  49. package/dist/chu/painter/best50/index.js.map +1 -0
  50. package/dist/chu/painter/chart/index.d.ts +170 -0
  51. package/dist/chu/painter/chart/index.js +116 -0
  52. package/dist/chu/painter/chart/index.js.map +1 -0
  53. package/dist/chu/painter/index.d.ts +194 -0
  54. package/dist/chu/painter/index.js +1105 -0
  55. package/dist/chu/painter/index.js.map +1 -0
  56. package/dist/geki/index.d.ts +10 -4
  57. package/dist/geki/index.js +12 -4
  58. package/dist/geki/index.js.map +1 -1
  59. package/dist/geki/lib/adapter/index.js.map +1 -0
  60. package/dist/geki/lib/adapter/kamaiTachi/index.js.map +1 -0
  61. package/dist/geki/{chart → lib}/database.d.ts +1 -1
  62. package/dist/geki/{chart → lib}/database.js +2 -2
  63. package/dist/geki/lib/database.js.map +1 -0
  64. package/dist/geki/painter/best50/index.d.ts +122 -0
  65. package/dist/geki/painter/best50/index.js +197 -0
  66. package/dist/geki/painter/best50/index.js.map +1 -0
  67. package/dist/geki/painter/index.d.ts +88 -0
  68. package/dist/geki/painter/index.js +448 -0
  69. package/dist/geki/painter/index.js.map +1 -0
  70. package/dist/lib/cache.d.ts +1 -0
  71. package/dist/lib/cache.js +6 -1
  72. package/dist/lib/cache.js.map +1 -1
  73. package/dist/lib/painter.d.ts +100 -0
  74. package/dist/lib/painter.js +216 -0
  75. package/dist/lib/painter.js.map +1 -0
  76. package/dist/lib/util.d.ts +6 -282
  77. package/dist/lib/util.js +50 -540
  78. package/dist/lib/util.js.map +1 -1
  79. package/dist/mai/index.d.ts +22 -6
  80. package/dist/mai/index.js +24 -6
  81. package/dist/mai/index.js.map +1 -1
  82. package/dist/mai/lib/{divingFish → adapter/divingFish}/index.d.ts +6 -6
  83. package/dist/mai/lib/{divingFish → adapter/divingFish}/index.js +29 -30
  84. package/dist/mai/lib/adapter/divingFish/index.js.map +1 -0
  85. package/dist/mai/lib/{index.d.ts → adapter/index.d.ts} +11 -11
  86. package/dist/mai/lib/{index.js → adapter/index.js} +2 -2
  87. package/dist/{geki/bests/lib → mai/lib/adapter}/index.js.map +1 -1
  88. package/dist/mai/lib/{kamaiTachi → adapter/kamaiTachi}/index.d.ts +84 -42
  89. package/dist/mai/lib/{kamaiTachi → adapter/kamaiTachi}/index.js +36 -29
  90. package/dist/mai/lib/adapter/kamaiTachi/index.js.map +1 -0
  91. package/dist/mai/lib/{lxns → adapter/lxns}/index.d.ts +12 -12
  92. package/dist/mai/lib/{lxns → adapter/lxns}/index.js +29 -30
  93. package/dist/mai/lib/adapter/lxns/index.js.map +1 -0
  94. package/dist/mai/lib/{maishift → adapter/maishift}/index.d.ts +4 -4
  95. package/dist/mai/lib/{maishift → adapter/maishift}/index.js +20 -20
  96. package/dist/mai/lib/adapter/maishift/index.js.map +1 -0
  97. package/dist/mai/{chart → lib}/database.d.ts +3 -3
  98. package/dist/mai/{chart → lib}/database.js +2 -2
  99. package/dist/mai/lib/database.js.map +1 -0
  100. package/dist/mai/lib/util.d.ts +134 -0
  101. package/dist/mai/lib/util.js +264 -0
  102. package/dist/mai/lib/util.js.map +1 -0
  103. package/dist/mai/painter/best50/index.d.ts +140 -0
  104. package/dist/mai/painter/best50/index.js +176 -0
  105. package/dist/mai/painter/best50/index.js.map +1 -0
  106. package/dist/mai/painter/chart/index.d.ts +195 -0
  107. package/dist/mai/painter/chart/index.js +115 -0
  108. package/dist/mai/painter/chart/index.js.map +1 -0
  109. package/dist/mai/painter/index.d.ts +237 -0
  110. package/dist/mai/painter/index.js +1431 -0
  111. package/dist/mai/painter/index.js.map +1 -0
  112. package/dist/mai/painter/level50/index.d.ts +28 -0
  113. package/dist/mai/painter/level50/index.js +129 -0
  114. package/dist/mai/painter/level50/index.js.map +1 -0
  115. package/dist/mai/type.d.ts +110 -0
  116. package/dist/mai/type.js +73 -1
  117. package/dist/mai/type.js.map +1 -1
  118. package/package.json +56 -55
  119. package/dist/chu/bests/best50.d.ts +0 -37
  120. package/dist/chu/bests/best50.js +0 -742
  121. package/dist/chu/bests/best50.js.map +0 -1
  122. package/dist/chu/bests/index.d.ts +0 -1
  123. package/dist/chu/bests/index.js +0 -6
  124. package/dist/chu/bests/index.js.map +0 -1
  125. package/dist/chu/bests/type.d.ts +0 -122
  126. package/dist/chu/bests/type.js +0 -3
  127. package/dist/chu/bests/type.js.map +0 -1
  128. package/dist/chu/chart/database.js.map +0 -1
  129. package/dist/chu/chart/index.d.ts +0 -212
  130. package/dist/chu/chart/index.js +0 -1064
  131. package/dist/chu/chart/index.js.map +0 -1
  132. package/dist/chu/lib/index.js.map +0 -1
  133. package/dist/chu/lib/kamaiTachi/index.js.map +0 -1
  134. package/dist/chu/lib/lxns/index.js.map +0 -1
  135. package/dist/geki/bests/best50.d.ts +0 -35
  136. package/dist/geki/bests/best50.js +0 -821
  137. package/dist/geki/bests/best50.js.map +0 -1
  138. package/dist/geki/bests/index.d.ts +0 -1
  139. package/dist/geki/bests/index.js +0 -6
  140. package/dist/geki/bests/index.js.map +0 -1
  141. package/dist/geki/bests/lib/kamaiTachi/index.js.map +0 -1
  142. package/dist/geki/bests/type.d.ts +0 -122
  143. package/dist/geki/bests/type.js +0 -3
  144. package/dist/geki/bests/type.js.map +0 -1
  145. package/dist/geki/chart/database.js.map +0 -1
  146. package/dist/geki/chart/index.d.ts +0 -4
  147. package/dist/geki/chart/index.js +0 -32
  148. package/dist/geki/chart/index.js.map +0 -1
  149. package/dist/mai/best50/index.d.ts +0 -271
  150. package/dist/mai/best50/index.js +0 -915
  151. package/dist/mai/best50/index.js.map +0 -1
  152. package/dist/mai/chart/database.js.map +0 -1
  153. package/dist/mai/chart/index.d.ts +0 -238
  154. package/dist/mai/chart/index.js +0 -1281
  155. package/dist/mai/chart/index.js.map +0 -1
  156. package/dist/mai/level50/index.d.ts +0 -28
  157. package/dist/mai/level50/index.js +0 -774
  158. package/dist/mai/level50/index.js.map +0 -1
  159. package/dist/mai/lib/divingFish/index.js.map +0 -1
  160. package/dist/mai/lib/index.js.map +0 -1
  161. package/dist/mai/lib/kamaiTachi/index.js.map +0 -1
  162. package/dist/mai/lib/lxns/index.js.map +0 -1
  163. package/dist/mai/lib/maishift/index.js.map +0 -1
  164. /package/dist/geki/{bests/lib → lib/adapter}/index.d.ts +0 -0
  165. /package/dist/geki/{bests/lib → lib/adapter}/index.js +0 -0
  166. /package/dist/geki/{bests/lib → lib/adapter}/kamaiTachi/index.d.ts +0 -0
  167. /package/dist/geki/{bests/lib → lib/adapter}/kamaiTachi/index.js +0 -0
@@ -0,0 +1,1431 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MaimaiPainterModule = exports.MaimaiPainter = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const sharp_1 = __importDefault(require("sharp"));
9
+ const color_1 = __importDefault(require("color"));
10
+ const v4_1 = require("zod/v4");
11
+ const canvas_1 = require("canvas");
12
+ const type_1 = require("../type");
13
+ const util_1 = require("../../lib/util");
14
+ const painter_1 = require("../../lib/painter");
15
+ const database_1 = require("../lib/database");
16
+ const util_2 = require("../lib/util");
17
+ class MaimaiPainter extends painter_1.Painter {
18
+ constructor({ theme: { schema, searchPaths, defaultTheme }, }) {
19
+ super({ theme: { schema, searchPaths, defaultTheme } });
20
+ }
21
+ }
22
+ exports.MaimaiPainter = MaimaiPainter;
23
+ var MaimaiPainterModule;
24
+ (function (MaimaiPainterModule) {
25
+ let Profile;
26
+ (function (Profile) {
27
+ Profile.schema = painter_1.ThemeManager.Element.extend({
28
+ type: v4_1.z.literal("profile"),
29
+ height: v4_1.z.number().min(1),
30
+ sprites: v4_1.z.object({
31
+ dxRating: v4_1.z.object({
32
+ white: v4_1.z.string(),
33
+ blue: v4_1.z.string(),
34
+ green: v4_1.z.string(),
35
+ yellow: v4_1.z.string(),
36
+ red: v4_1.z.string(),
37
+ purple: v4_1.z.string(),
38
+ bronze: v4_1.z.string(),
39
+ silver: v4_1.z.string(),
40
+ gold: v4_1.z.string(),
41
+ platinum: v4_1.z.string(),
42
+ rainbow: v4_1.z.string(),
43
+ }),
44
+ dxRatingNumberMap: v4_1.z.string(),
45
+ profile: v4_1.z.object({
46
+ nameplate: v4_1.z.string(),
47
+ icon: v4_1.z.string(),
48
+ }),
49
+ }),
50
+ });
51
+ async function draw(ctx, theme, element, username, rating, profilePicture) {
52
+ const nameplate = new canvas_1.Image();
53
+ nameplate.src = theme.getFile(element.sprites.profile.nameplate);
54
+ ctx.drawImage(nameplate, element.x, element.y, element.height * 6.207, element.height);
55
+ /* Begin Profile Picture Draw */
56
+ {
57
+ ctx.save();
58
+ ctx.beginPath();
59
+ ctx.roundRect(element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872, (element.height * 0.872) / 16);
60
+ ctx.clip();
61
+ ctx.fillStyle = "white";
62
+ ctx.fill();
63
+ const icon = new canvas_1.Image();
64
+ try {
65
+ (0, sharp_1.default)(profilePicture);
66
+ }
67
+ catch {
68
+ // Unknown profile picture binary
69
+ profilePicture = undefined;
70
+ }
71
+ const pfp = profilePicture ||
72
+ theme.getFile(element.sprites.profile.icon);
73
+ const { dominant } = await (0, sharp_1.default)(pfp).stats();
74
+ icon.src = await (0, sharp_1.default)(pfp).png().toBuffer();
75
+ const cropSize = Math.min(icon.width, icon.height);
76
+ ctx.drawImage(icon, (icon.width - cropSize) / 2, (icon.height - cropSize) / 2, cropSize, cropSize, element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872);
77
+ if (profilePicture) {
78
+ ctx.beginPath();
79
+ ctx.roundRect(element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872, (element.height * 0.872) / 16);
80
+ ctx.strokeStyle = color_1.default.rgb(dominant).darken(0.3).hex();
81
+ ctx.lineWidth = element.height / 30;
82
+ ctx.stroke();
83
+ }
84
+ ctx.restore();
85
+ }
86
+ /* End Profile Picture Draw */
87
+ /* Begin DX Rating Draw */
88
+ {
89
+ const dxRating = new canvas_1.Image();
90
+ let dxRatingImg;
91
+ switch (true) {
92
+ case rating >= 15000: {
93
+ dxRatingImg = theme.getFile(element.sprites.dxRating.rainbow);
94
+ break;
95
+ }
96
+ case rating >= 14500: {
97
+ dxRatingImg = theme.getFile(element.sprites.dxRating.platinum);
98
+ break;
99
+ }
100
+ case rating >= 14000: {
101
+ dxRatingImg = theme.getFile(element.sprites.dxRating.gold);
102
+ break;
103
+ }
104
+ case rating >= 13000: {
105
+ dxRatingImg = theme.getFile(element.sprites.dxRating.silver);
106
+ break;
107
+ }
108
+ case rating >= 12000: {
109
+ dxRatingImg = theme.getFile(element.sprites.dxRating.bronze);
110
+ break;
111
+ }
112
+ case rating >= 10000: {
113
+ dxRatingImg = theme.getFile(element.sprites.dxRating.purple);
114
+ break;
115
+ }
116
+ case rating >= 8000: {
117
+ dxRatingImg = theme.getFile(element.sprites.dxRating.red);
118
+ break;
119
+ }
120
+ case rating >= 6000: {
121
+ dxRatingImg = theme.getFile(element.sprites.dxRating.yellow);
122
+ break;
123
+ }
124
+ case rating >= 4000: {
125
+ dxRatingImg = theme.getFile(element.sprites.dxRating.green);
126
+ break;
127
+ }
128
+ case rating >= 2000: {
129
+ dxRatingImg = theme.getFile(element.sprites.dxRating.blue);
130
+ break;
131
+ }
132
+ default: {
133
+ dxRatingImg = theme.getFile(element.sprites.dxRating.white);
134
+ break;
135
+ }
136
+ }
137
+ dxRating.src = dxRatingImg;
138
+ ctx.drawImage(dxRating, element.x + element.height, element.y + element.height * 0.064, (element.height / 3) * 5.108, element.height / 3);
139
+ }
140
+ /* End DX Rating Draw */
141
+ /* Begin Username Draw */
142
+ {
143
+ ctx.beginPath();
144
+ ctx.roundRect(element.x + element.height * (1 + 1 / 32), element.y + element.height * (0.064 + 0.333 + 1 / 32), ((element.height / 3) * 5.108 * 6) / 5, (element.height * 7) / 24, element.height / 20);
145
+ ctx.fillStyle = "white";
146
+ ctx.strokeStyle = color_1.default.rgb(180, 180, 180).hex();
147
+ ctx.lineWidth = element.height / 32;
148
+ ctx.stroke();
149
+ ctx.fill();
150
+ const ratingImgBuffer = await getRatingNumber(rating, theme, element);
151
+ if (ratingImgBuffer) {
152
+ const { width, height } = await (0, sharp_1.default)(ratingImgBuffer).metadata();
153
+ if (width && height) {
154
+ const aspectRatio = width / height;
155
+ const image = new canvas_1.Image();
156
+ image.src = ratingImgBuffer;
157
+ const drawHeight = (element.height * 7) / 32;
158
+ ctx.drawImage(image, element.x + element.height * 1.785, element.y + element.height * 0.12, drawHeight * aspectRatio * 0.8, drawHeight);
159
+ }
160
+ }
161
+ util_1.Util.drawText(ctx, util_1.Util.HalfFullWidthConvert.toFullWidth(username), element.x + element.height * (1 + 1 / 16), element.y + element.height * (0.064 + 0.333 + 1 / 4), (element.height * 1) / 6, 0, ((element.height / 3) * 5.108 * 6) / 5, "left", "black", "black", "standard-font-username");
162
+ }
163
+ /* End Username Draw*/
164
+ }
165
+ Profile.draw = draw;
166
+ async function getRatingNumber(num, theme, element) {
167
+ async function getRatingDigit(map, digit, unitWidth, unitHeight) {
168
+ digit = Math.trunc(digit % 10);
169
+ return await (0, sharp_1.default)(map)
170
+ .extract({
171
+ left: (digit % 4) * unitWidth,
172
+ top: Math.trunc(digit / 4) * unitHeight,
173
+ width: unitWidth,
174
+ height: unitHeight,
175
+ })
176
+ .toBuffer();
177
+ }
178
+ const map = theme.getFile(element.sprites.dxRatingNumberMap);
179
+ const { width, height } = await (0, sharp_1.default)(map).metadata();
180
+ if (!(width && height))
181
+ return null;
182
+ const unitWidth = width / 4, unitHeight = height / 4;
183
+ let digits = [];
184
+ while (num > 0) {
185
+ digits.push(await getRatingDigit(map, num % 10, unitWidth, unitHeight));
186
+ num = Math.trunc(num / 10);
187
+ }
188
+ while (digits.length < 5)
189
+ digits.push(null);
190
+ digits = digits.reverse();
191
+ const canvas = new canvas_1.Canvas(unitWidth * digits.length, unitHeight);
192
+ const ctx = canvas.getContext("2d");
193
+ for (let i = 0; i < digits.length; ++i) {
194
+ const curDigit = digits[i];
195
+ if (!curDigit)
196
+ continue;
197
+ const img = new canvas_1.Image();
198
+ img.src = curDigit;
199
+ ctx.drawImage(img, unitWidth * i * 0.88, 0);
200
+ }
201
+ return canvas.toBuffer();
202
+ }
203
+ })(Profile = MaimaiPainterModule.Profile || (MaimaiPainterModule.Profile = {}));
204
+ let Best50;
205
+ (function (Best50) {
206
+ let ScoreGrid;
207
+ (function (ScoreGrid) {
208
+ ScoreGrid.schema = painter_1.ThemeManager.Element.extend({
209
+ type: v4_1.z.literal("score-grid"),
210
+ horizontalSize: v4_1.z.number().min(1),
211
+ verticalSize: v4_1.z.number().min(1),
212
+ region: v4_1.z.enum(["new", "old"]),
213
+ index: v4_1.z.number().min(0),
214
+ scoreBubble: v4_1.z.object({
215
+ width: v4_1.z.number().min(1),
216
+ height: v4_1.z.number().min(1),
217
+ margin: v4_1.z.number().min(0),
218
+ gap: v4_1.z.number().min(0),
219
+ color: v4_1.z.object({
220
+ basic: util_1.Util.z.color(),
221
+ advanced: util_1.Util.z.color(),
222
+ expert: util_1.Util.z.color(),
223
+ master: util_1.Util.z.color(),
224
+ remaster: util_1.Util.z.color(),
225
+ utage: util_1.Util.z.color(),
226
+ }),
227
+ }),
228
+ sprites: v4_1.z.object({
229
+ achievement: v4_1.z.object({
230
+ d: v4_1.z.string(),
231
+ c: v4_1.z.string(),
232
+ b: v4_1.z.string(),
233
+ bb: v4_1.z.string(),
234
+ bbb: v4_1.z.string(),
235
+ a: v4_1.z.string(),
236
+ aa: v4_1.z.string(),
237
+ aaa: v4_1.z.string(),
238
+ s: v4_1.z.string(),
239
+ sp: v4_1.z.string(),
240
+ ss: v4_1.z.string(),
241
+ ssp: v4_1.z.string(),
242
+ sss: v4_1.z.string(),
243
+ sssp: v4_1.z.string(),
244
+ }),
245
+ mode: v4_1.z.object({
246
+ standard: v4_1.z.string(),
247
+ dx: v4_1.z.string(),
248
+ }),
249
+ milestone: v4_1.z.object({
250
+ ap: v4_1.z.string(),
251
+ app: v4_1.z.string(),
252
+ fc: v4_1.z.string(),
253
+ fcp: v4_1.z.string(),
254
+ fdx: v4_1.z.string(),
255
+ fdxp: v4_1.z.string(),
256
+ fs: v4_1.z.string(),
257
+ fsp: v4_1.z.string(),
258
+ sync: v4_1.z.string(),
259
+ none: v4_1.z.string(),
260
+ }),
261
+ }),
262
+ });
263
+ async function draw(ctx, theme, element, { x, y, index, score, scale, }) {
264
+ let curColor = "#FFFFFF";
265
+ switch (score.chart.difficulty) {
266
+ case type_1.EDifficulty.BASIC:
267
+ curColor = element.scoreBubble.color.basic;
268
+ break;
269
+ case type_1.EDifficulty.ADVANCED:
270
+ curColor = element.scoreBubble.color.advanced;
271
+ break;
272
+ case type_1.EDifficulty.EXPERT:
273
+ curColor = element.scoreBubble.color.expert;
274
+ break;
275
+ case type_1.EDifficulty.MASTER:
276
+ curColor = element.scoreBubble.color.master;
277
+ break;
278
+ case type_1.EDifficulty.REMASTER:
279
+ curColor = element.scoreBubble.color.remaster;
280
+ break;
281
+ case type_1.EDifficulty.UTAGE:
282
+ curColor = element.scoreBubble.color.utage;
283
+ break;
284
+ }
285
+ /** Begin Card Draw */
286
+ ctx.save();
287
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
288
+ ctx.beginPath();
289
+ ctx.roundRect(x, y, element.scoreBubble.width, element.scoreBubble.height, (element.scoreBubble.height * 0.806) / 7);
290
+ ctx.strokeStyle = new color_1.default(curColor).darken(0.3).hexa();
291
+ ctx.lineWidth = element.scoreBubble.margin / 4;
292
+ ctx.stroke();
293
+ ctx.fill();
294
+ ctx.beginPath();
295
+ ctx.roundRect(x, y, element.scoreBubble.width, element.scoreBubble.height, (element.scoreBubble.height * 0.806) / 7);
296
+ ctx.clip();
297
+ /** Begin Main Content Draw */
298
+ {
299
+ const cardRoundCornerRadius = (element.scoreBubble.height * 0.806) / 7;
300
+ ctx.save();
301
+ ctx.beginPath();
302
+ ctx.roundRect(x, y, element.scoreBubble.width, element.scoreBubble.height * 0.742, cardRoundCornerRadius);
303
+ ctx.clip();
304
+ ctx.fillStyle = curColor;
305
+ ctx.fill();
306
+ /** Begin score "Freshness" draw */
307
+ if (scale !== undefined) {
308
+ ctx.save();
309
+ ctx.beginPath();
310
+ /**
311
+ * Get a color representing freshness.
312
+ *
313
+ * @param status From 0 to 1, 0 means most fresh, 1 means most dead.
314
+ * @returns
315
+ */
316
+ function getStatusColor(status) {
317
+ const FRESHNESS_COLOUR = "#94E436";
318
+ const DEAD_COLOUR = "#F54932";
319
+ const NEUTRAL_COLOUR = "#c2c2c2";
320
+ if (status < 0.5) {
321
+ return new color_1.default(FRESHNESS_COLOUR).mix(new color_1.default(NEUTRAL_COLOUR), status / 0.5);
322
+ }
323
+ else {
324
+ return new color_1.default(NEUTRAL_COLOUR).mix(new color_1.default(DEAD_COLOUR), status / 0.5 - 1);
325
+ }
326
+ }
327
+ ctx.fillStyle = getStatusColor(scale).hexa();
328
+ ctx.roundRect(x +
329
+ element.scoreBubble.width -
330
+ cardRoundCornerRadius, y - cardRoundCornerRadius, cardRoundCornerRadius * 2, cardRoundCornerRadius * 2, cardRoundCornerRadius / 4);
331
+ ctx.closePath();
332
+ ctx.fill();
333
+ ctx.restore();
334
+ }
335
+ /** End score "Freshness" draw */
336
+ const jacketSize = Math.min(element.scoreBubble.width, element.scoreBubble.height * 0.742);
337
+ const jacketMaskGrad = ctx.createLinearGradient(x + jacketSize / 2, y + jacketSize / 2, x + jacketSize, y + jacketSize / 2);
338
+ jacketMaskGrad.addColorStop(0, new color_1.default(curColor).alpha(0).hexa());
339
+ jacketMaskGrad.addColorStop(0.25, new color_1.default(curColor).alpha(0.2).hexa());
340
+ jacketMaskGrad.addColorStop(1, new color_1.default(curColor).alpha(1).hexa());
341
+ const jacketMaskGradDark = ctx.createLinearGradient(x + jacketSize / 2, y + jacketSize / 2, x + jacketSize, y + jacketSize / 2);
342
+ jacketMaskGradDark.addColorStop(0, new color_1.default(curColor).darken(0.3).alpha(0).hexa());
343
+ jacketMaskGradDark.addColorStop(0.25, new color_1.default(curColor).darken(0.3).alpha(0.2).hexa());
344
+ jacketMaskGradDark.addColorStop(1, new color_1.default(curColor).darken(0.3).alpha(1).hexa());
345
+ /** Begin Jacket Draw*/
346
+ let jacket = await database_1.Database.fetchJacket(score.chart.id);
347
+ if (!jacket)
348
+ jacket = await database_1.Database.fetchJacket(0);
349
+ if (jacket) {
350
+ const img = new canvas_1.Image();
351
+ img.src = jacket;
352
+ ctx.drawImage(img, x, y, jacketSize, jacketSize);
353
+ }
354
+ else {
355
+ ctx.fillStyle = "#b6ffab";
356
+ ctx.fillRect(x, y, jacketSize, jacketSize);
357
+ }
358
+ /** End Jacket Draw*/
359
+ /** Begin Jacket Gradient Mask Draw*/ {
360
+ ctx.fillStyle = jacketMaskGrad;
361
+ ctx.fillRect(x + jacketSize / 2, y, (jacketSize * 3) / 4, jacketSize);
362
+ } /** End Jacket Gradient Mask Draw*/
363
+ ctx.beginPath();
364
+ ctx.roundRect(x + element.scoreBubble.margin, y + element.scoreBubble.margin, element.scoreBubble.width -
365
+ element.scoreBubble.margin * 2, element.scoreBubble.height * 0.806 -
366
+ element.scoreBubble.margin * 2, (element.scoreBubble.height * 0.806 -
367
+ element.scoreBubble.margin * 2) /
368
+ 7);
369
+ /** Begin Title Draw */ {
370
+ util_1.Util.drawText(ctx, score.chart.name, x + (jacketSize * 7) / 8, y +
371
+ element.scoreBubble.margin +
372
+ element.scoreBubble.height * 0.806 * 0.144, element.scoreBubble.height * 0.806 * 0.144, element.scoreBubble.height * 0.806 * 0.04, element.scoreBubble.width -
373
+ (jacketSize * 7) / 8 -
374
+ element.scoreBubble.margin, "left", "white", jacketMaskGradDark);
375
+ } /** End Title Draw */
376
+ /** Begin Separation Line Draw */ {
377
+ ctx.beginPath();
378
+ ctx.roundRect(x + (jacketSize * 13) / 16, y +
379
+ element.scoreBubble.margin +
380
+ element.scoreBubble.height *
381
+ 0.806 *
382
+ (0.144 + 0.072), element.scoreBubble.width -
383
+ (jacketSize * 13) / 16 -
384
+ element.scoreBubble.margin * 2, element.scoreBubble.height * 0.806 * 0.02, element.scoreBubble.height * 0.806 * 0.16);
385
+ ctx.fillStyle = jacketMaskGradDark;
386
+ ctx.fill();
387
+ } /** End Separation Line Draw */
388
+ /** Begin Achievement Rate Draw */
389
+ util_1.Util.drawText(ctx, `${util_1.Util.truncate(score.achievement, 4)}%`, x -
390
+ element.scoreBubble.margin -
391
+ element.scoreBubble.height * 0.806 * 0.02 +
392
+ element.scoreBubble.width, y +
393
+ element.scoreBubble.margin +
394
+ element.scoreBubble.height *
395
+ 0.806 *
396
+ (0.144 + 0.144 + 0.208 - 0.04), element.scoreBubble.height * 0.806 * 0.208, element.scoreBubble.height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
397
+ /** End Achievement Rate Draw */
398
+ /** Begin Achievement Rank Draw */
399
+ {
400
+ let rankImg;
401
+ switch (score.achievementRank) {
402
+ case type_1.EAchievementTypes.D:
403
+ rankImg = theme.getFile(element.sprites.achievement.d);
404
+ break;
405
+ case type_1.EAchievementTypes.C:
406
+ rankImg = theme.getFile(element.sprites.achievement.c);
407
+ break;
408
+ case type_1.EAchievementTypes.B:
409
+ rankImg = theme.getFile(element.sprites.achievement.b);
410
+ break;
411
+ case type_1.EAchievementTypes.BB:
412
+ rankImg = theme.getFile(element.sprites.achievement.bb);
413
+ break;
414
+ case type_1.EAchievementTypes.BBB:
415
+ rankImg = theme.getFile(element.sprites.achievement.bbb);
416
+ break;
417
+ case type_1.EAchievementTypes.A:
418
+ rankImg = theme.getFile(element.sprites.achievement.a);
419
+ break;
420
+ case type_1.EAchievementTypes.AA:
421
+ rankImg = theme.getFile(element.sprites.achievement.aa);
422
+ break;
423
+ case type_1.EAchievementTypes.AAA:
424
+ rankImg = theme.getFile(element.sprites.achievement.aaa);
425
+ break;
426
+ case type_1.EAchievementTypes.S:
427
+ rankImg = theme.getFile(element.sprites.achievement.s);
428
+ break;
429
+ case type_1.EAchievementTypes.SP:
430
+ rankImg = theme.getFile(element.sprites.achievement.sp);
431
+ break;
432
+ case type_1.EAchievementTypes.SS:
433
+ rankImg = theme.getFile(element.sprites.achievement.ss);
434
+ break;
435
+ case type_1.EAchievementTypes.SSP:
436
+ rankImg = theme.getFile(element.sprites.achievement.ssp);
437
+ break;
438
+ case type_1.EAchievementTypes.SSS:
439
+ rankImg = theme.getFile(element.sprites.achievement.sss);
440
+ break;
441
+ default:
442
+ rankImg = theme.getFile(element.sprites.achievement.sssp);
443
+ }
444
+ const img = new canvas_1.Image();
445
+ img.src = rankImg;
446
+ ctx.drawImage(img, x + jacketSize, y +
447
+ element.scoreBubble.margin +
448
+ element.scoreBubble.height *
449
+ 0.806 *
450
+ (0.144 + 0.144 + 0.208 + 0.02), element.scoreBubble.height * 0.806 * 0.3 * 2.133, element.scoreBubble.height * 0.806 * 0.3);
451
+ }
452
+ /** End Achievement Rank Draw */
453
+ /** Begin Milestone Draw */
454
+ {
455
+ let comboImg, syncImg;
456
+ switch (score.combo) {
457
+ case type_1.EComboTypes.NONE:
458
+ comboImg = theme.getFile(element.sprites.milestone.none);
459
+ break;
460
+ case type_1.EComboTypes.FULL_COMBO:
461
+ comboImg = theme.getFile(element.sprites.milestone.fc);
462
+ break;
463
+ case type_1.EComboTypes.FULL_COMBO_PLUS:
464
+ comboImg = theme.getFile(element.sprites.milestone.fcp);
465
+ break;
466
+ case type_1.EComboTypes.ALL_PERFECT:
467
+ comboImg = theme.getFile(element.sprites.milestone.ap);
468
+ break;
469
+ case type_1.EComboTypes.ALL_PERFECT_PLUS:
470
+ comboImg = theme.getFile(element.sprites.milestone.app);
471
+ break;
472
+ }
473
+ switch (score.sync) {
474
+ case type_1.ESyncTypes.NONE:
475
+ syncImg = theme.getFile(element.sprites.milestone.none);
476
+ break;
477
+ case type_1.ESyncTypes.SYNC_PLAY:
478
+ syncImg = theme.getFile(element.sprites.milestone.sync);
479
+ break;
480
+ case type_1.ESyncTypes.FULL_SYNC:
481
+ syncImg = theme.getFile(element.sprites.milestone.fs);
482
+ break;
483
+ case type_1.ESyncTypes.FULL_SYNC_PLUS:
484
+ syncImg = theme.getFile(element.sprites.milestone.fsp);
485
+ break;
486
+ case type_1.ESyncTypes.FULL_SYNC_DX:
487
+ syncImg = theme.getFile(element.sprites.milestone.fdx);
488
+ break;
489
+ case type_1.ESyncTypes.FULL_SYNC_DX_PLUS:
490
+ syncImg = theme.getFile(element.sprites.milestone.fdxp);
491
+ break;
492
+ }
493
+ const combo = new canvas_1.Image();
494
+ combo.src = comboImg;
495
+ ctx.drawImage(combo, x +
496
+ (jacketSize * 7) / 8 +
497
+ element.scoreBubble.height *
498
+ 0.806 *
499
+ (0.32 * 2.133 + 0.06), y +
500
+ element.scoreBubble.margin +
501
+ element.scoreBubble.height *
502
+ 0.806 *
503
+ (0.144 + 0.144 + 0.208 + 0.01), element.scoreBubble.height * 0.806 * 0.32, element.scoreBubble.height * 0.806 * 0.32);
504
+ const sync = new canvas_1.Image();
505
+ sync.src = syncImg;
506
+ ctx.drawImage(sync, x +
507
+ (jacketSize * 7) / 8 +
508
+ element.scoreBubble.height *
509
+ 0.806 *
510
+ (0.32 * 2.133 + 0.04 + 0.32), y +
511
+ element.scoreBubble.margin +
512
+ element.scoreBubble.height *
513
+ 0.806 *
514
+ (0.144 + 0.144 + 0.208 + 0.01), element.scoreBubble.height * 0.806 * 0.32, element.scoreBubble.height * 0.806 * 0.32);
515
+ }
516
+ /** End Milestone Draw */
517
+ /** Begin Chart Mode Draw */
518
+ {
519
+ const mode = new canvas_1.Image();
520
+ const chartModeBadgeImg = theme.getFile(score.chart.id > 10000
521
+ ? element.sprites.mode.dx
522
+ : element.sprites.mode.standard);
523
+ const { width, height } = await (0, sharp_1.default)(chartModeBadgeImg).metadata();
524
+ const aspectRatio = (width ?? 0) / (height ?? 1) || 3;
525
+ mode.src = chartModeBadgeImg;
526
+ const drawHeight = (jacketSize * 6) / 8;
527
+ ctx.drawImage(mode, x + ((jacketSize * 7) / 8 - drawHeight) / 2, y +
528
+ element.scoreBubble.margin +
529
+ element.scoreBubble.height * 0.806 * 0.02, drawHeight, drawHeight / aspectRatio);
530
+ }
531
+ /** End Chart Mode Draw */
532
+ /** Begin Bests Index Draw */
533
+ {
534
+ util_1.Util.drawText(ctx, `#${index + 1}`, x + element.scoreBubble.margin * 2, y + jacketSize - element.scoreBubble.margin * 2, element.scoreBubble.height * 0.806 * 0.128, element.scoreBubble.height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
535
+ }
536
+ /** End Bests Index Draw */
537
+ ctx.restore();
538
+ }
539
+ /** End Main Content Draw */
540
+ /** Begin Difficulty & DX Rating Draw */
541
+ {
542
+ util_1.Util.drawText(ctx, `${util_1.Util.truncate(score.chart.level, 1)} ↑${util_1.Util.truncate(score.dxRating, 0)}`, x + element.scoreBubble.margin * 2, y +
543
+ element.scoreBubble.height *
544
+ (0.806 + (1 - 0.806) / 2), element.scoreBubble.height * 0.806 * 0.128, element.scoreBubble.height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
545
+ if (score.dxScore >= 0 && score.chart.maxDxScore > 0) {
546
+ util_1.Util.drawText(ctx, `${score.dxScore}/${score.chart.maxDxScore}`, x +
547
+ element.scoreBubble.width -
548
+ element.scoreBubble.margin * 2, y +
549
+ element.scoreBubble.height *
550
+ (0.806 + (1 - 0.806) / 2), element.scoreBubble.height * 0.806 * 0.128, element.scoreBubble.height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
551
+ }
552
+ }
553
+ /** End Difficulty & DX Rating Draw */
554
+ ctx.restore();
555
+ /** End Card Draw */
556
+ }
557
+ ScoreGrid.draw = draw;
558
+ })(ScoreGrid = Best50.ScoreGrid || (Best50.ScoreGrid = {}));
559
+ })(Best50 = MaimaiPainterModule.Best50 || (MaimaiPainterModule.Best50 = {}));
560
+ let Chart;
561
+ (function (Chart) {
562
+ const DX_LATEST = 55;
563
+ const EX_LATEST = 50;
564
+ const CN_LATEST = 50;
565
+ const MAIMAI_VERSIONS = [
566
+ 99, 95, 90, 85, 80, 70, 60, 50, 40, 30, 20, 10, 0,
567
+ ];
568
+ const MAIMAIDX_VERSIONS = [
569
+ 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0,
570
+ ];
571
+ const WUMENGDX_VERSIONS = [50, 40, 30, 20, 10, 0];
572
+ function findVersion(v, isDX, isCN) {
573
+ const target = isDX
574
+ ? isCN
575
+ ? WUMENGDX_VERSIONS
576
+ : MAIMAIDX_VERSIONS
577
+ : MAIMAI_VERSIONS;
578
+ for (const version of target) {
579
+ if (v >= version)
580
+ return version;
581
+ }
582
+ return null;
583
+ }
584
+ let ChartGrid;
585
+ (function (ChartGrid) {
586
+ ChartGrid.schema = painter_1.ThemeManager.Element.extend({
587
+ type: v4_1.z.literal("chart-grid"),
588
+ width: v4_1.z.number().min(1),
589
+ height: v4_1.z.number().min(1),
590
+ margin: v4_1.z.number().min(0),
591
+ gap: v4_1.z.number().min(0),
592
+ bubble: v4_1.z.object({
593
+ margin: v4_1.z.number().min(0),
594
+ color: v4_1.z.object({
595
+ basic: util_1.Util.z.color(),
596
+ advanced: util_1.Util.z.color(),
597
+ expert: util_1.Util.z.color(),
598
+ master: util_1.Util.z.color(),
599
+ remaster: util_1.Util.z.color(),
600
+ utage: util_1.Util.z.color(),
601
+ }),
602
+ }),
603
+ color: v4_1.z.object({
604
+ card: util_1.Util.z.color(),
605
+ }),
606
+ sprites: v4_1.z.object({
607
+ achievement: v4_1.z.object({
608
+ d: v4_1.z.string(),
609
+ c: v4_1.z.string(),
610
+ b: v4_1.z.string(),
611
+ bb: v4_1.z.string(),
612
+ bbb: v4_1.z.string(),
613
+ a: v4_1.z.string(),
614
+ aa: v4_1.z.string(),
615
+ aaa: v4_1.z.string(),
616
+ s: v4_1.z.string(),
617
+ sp: v4_1.z.string(),
618
+ ss: v4_1.z.string(),
619
+ ssp: v4_1.z.string(),
620
+ sss: v4_1.z.string(),
621
+ sssp: v4_1.z.string(),
622
+ }),
623
+ milestone: v4_1.z.object({
624
+ ap: v4_1.z.string(),
625
+ app: v4_1.z.string(),
626
+ fc: v4_1.z.string(),
627
+ fcp: v4_1.z.string(),
628
+ fdx: v4_1.z.string(),
629
+ fdxp: v4_1.z.string(),
630
+ fs: v4_1.z.string(),
631
+ fsp: v4_1.z.string(),
632
+ sync: v4_1.z.string(),
633
+ none: v4_1.z.string(),
634
+ }),
635
+ versions: v4_1.z.object({
636
+ OLD: v4_1.z.object({
637
+ "0": v4_1.z.string(),
638
+ "10": v4_1.z.string(),
639
+ "20": v4_1.z.string(),
640
+ "30": v4_1.z.string(),
641
+ "40": v4_1.z.string(),
642
+ "50": v4_1.z.string(),
643
+ "60": v4_1.z.string(),
644
+ "70": v4_1.z.string(),
645
+ "80": v4_1.z.string(),
646
+ "85": v4_1.z.string(),
647
+ "90": v4_1.z.string(),
648
+ "95": v4_1.z.string(),
649
+ "99": v4_1.z.string(),
650
+ }),
651
+ DX: v4_1.z.object({
652
+ "0": v4_1.z.string(),
653
+ "5": v4_1.z.string(),
654
+ "10": v4_1.z.string(),
655
+ "15": v4_1.z.string(),
656
+ "20": v4_1.z.string(),
657
+ "25": v4_1.z.string(),
658
+ "30": v4_1.z.string(),
659
+ "35": v4_1.z.string(),
660
+ "40": v4_1.z.string(),
661
+ "45": v4_1.z.string(),
662
+ "50": v4_1.z.string(),
663
+ "55": v4_1.z.string(),
664
+ }),
665
+ EX: v4_1.z.object({
666
+ "10": v4_1.z.string(),
667
+ "15": v4_1.z.string(),
668
+ }),
669
+ CN: v4_1.z.object({
670
+ "0": v4_1.z.string(),
671
+ "10": v4_1.z.string(),
672
+ "20": v4_1.z.string(),
673
+ "30": v4_1.z.string(),
674
+ "40": v4_1.z.string(),
675
+ "50": v4_1.z.string(),
676
+ }),
677
+ }),
678
+ }),
679
+ });
680
+ async function draw(ctx, theme, element, chartId, scores, region = "DX") {
681
+ /* Begin Background Draw */
682
+ ctx.roundRect(element.x, element.y, element.width, element.height, Math.min(theme.content.width, theme.content.height) *
683
+ (3 / 128));
684
+ ctx.fillStyle = element.color.card;
685
+ ctx.strokeStyle = new color_1.default(element.color.card)
686
+ .darken(0.6)
687
+ .hex();
688
+ ctx.lineWidth =
689
+ Math.min(theme.content.width, theme.content.height) *
690
+ (3 / 512);
691
+ ctx.stroke();
692
+ ctx.fill();
693
+ /* End Background Draw */
694
+ const difficulties = [];
695
+ for (let i = type_1.EDifficulty.BASIC; i <= type_1.EDifficulty.REMASTER; ++i) {
696
+ const chart = database_1.Database.getLocalChart(chartId, i);
697
+ if (chart)
698
+ difficulties.push(chart);
699
+ }
700
+ const cardWidth = element.width - element.margin * 2;
701
+ const cardHeight = (element.height - element.margin * 2 - element.gap * 3) / 4;
702
+ for (let y = element.y + element.margin, i = 0; i < difficulties.length; ++i, y += cardHeight + element.gap) {
703
+ const chart = difficulties[i];
704
+ if (chart)
705
+ if (difficulties.length > 4 && i == 0) {
706
+ await drawChartGridCard(ctx, theme, element, chart, i, element.x + element.margin, y, (cardWidth - element.margin) / 2, cardHeight, true, region, scores[i]);
707
+ i++;
708
+ const chartA = difficulties[i];
709
+ if (chartA)
710
+ await drawChartGridCard(ctx, theme, element, chartA, i, element.x +
711
+ element.margin +
712
+ (cardWidth + element.margin) / 2, y, (cardWidth - element.margin) / 2, cardHeight, true, region, scores[i]);
713
+ }
714
+ else {
715
+ await drawChartGridCard(ctx, theme, element, chart, i, element.x + element.margin, y, cardWidth, cardHeight, false, region, scores[i]);
716
+ }
717
+ }
718
+ }
719
+ ChartGrid.draw = draw;
720
+ async function drawChartGridCard(ctx, theme, element, chart, difficulty, x, y, width, height, isShort, targetRegion = "DX", score) {
721
+ let curColor = "#FFFFFF";
722
+ switch (difficulty) {
723
+ case type_1.EDifficulty.BASIC:
724
+ curColor = element.bubble.color.basic;
725
+ break;
726
+ case type_1.EDifficulty.ADVANCED:
727
+ curColor = element.bubble.color.advanced;
728
+ break;
729
+ case type_1.EDifficulty.EXPERT:
730
+ curColor = element.bubble.color.expert;
731
+ break;
732
+ case type_1.EDifficulty.MASTER:
733
+ curColor = element.bubble.color.master;
734
+ break;
735
+ case type_1.EDifficulty.REMASTER:
736
+ curColor = element.bubble.color.remaster;
737
+ break;
738
+ case type_1.EDifficulty.UTAGE:
739
+ curColor = element.bubble.color.utage;
740
+ break;
741
+ }
742
+ /** Begin Card Draw */
743
+ ctx.save();
744
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
745
+ ctx.beginPath();
746
+ ctx.roundRect(x, y, width, height, (height * 0.806) / 7);
747
+ ctx.strokeStyle = new color_1.default(curColor).darken(0.3).hexa();
748
+ ctx.lineWidth = element.bubble.margin / 4;
749
+ ctx.stroke();
750
+ ctx.fill();
751
+ ctx.beginPath();
752
+ ctx.roundRect(x, y, width, height, (height * 0.806) / 7);
753
+ ctx.clip();
754
+ /** Begin Main Content Draw */
755
+ {
756
+ ctx.save();
757
+ ctx.beginPath();
758
+ ctx.rect(x, y, width, height);
759
+ ctx.clip();
760
+ ctx.fillStyle = curColor;
761
+ ctx.fill();
762
+ const titleSize = height * (47 / 256);
763
+ /** Begin Difficulty Text & Separation Line Draw */
764
+ {
765
+ let difficultiy = "";
766
+ switch (chart.difficulty) {
767
+ case type_1.EDifficulty.BASIC:
768
+ difficultiy = "BASIC";
769
+ break;
770
+ case type_1.EDifficulty.ADVANCED:
771
+ difficultiy = "ADVANCED";
772
+ break;
773
+ case type_1.EDifficulty.EXPERT:
774
+ difficultiy = "EXPERT";
775
+ break;
776
+ case type_1.EDifficulty.MASTER:
777
+ difficultiy = "MASTER";
778
+ break;
779
+ case type_1.EDifficulty.REMASTER:
780
+ difficultiy = "Re:MASTER";
781
+ break;
782
+ case type_1.EDifficulty.UTAGE:
783
+ difficultiy = "U·TA·GE";
784
+ break;
785
+ }
786
+ const levelTextSize = titleSize * (5 / 8);
787
+ util_1.Util.drawText(ctx, difficultiy, x + element.bubble.margin, y +
788
+ element.bubble.margin +
789
+ titleSize -
790
+ element.bubble.margin * (1 / 4), titleSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
791
+ const difficultyTextWidth = util_1.Util.measureText(ctx, difficultiy, titleSize, Infinity).width;
792
+ util_1.Util.drawText(ctx, `Lv. ${util_1.Util.truncate(chart.level, 1)}${score ? ` ↑${util_1.Util.truncate(score.dxRating, 0)}` : ""}`, x + element.bubble.margin * 2 + difficultyTextWidth, y +
793
+ element.bubble.margin +
794
+ titleSize -
795
+ element.bubble.margin * (1 / 4), levelTextSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
796
+ ctx.beginPath();
797
+ ctx.roundRect(x + element.bubble.margin, y +
798
+ element.bubble.margin +
799
+ titleSize +
800
+ element.bubble.margin * (1 / 4), height * 2 - element.bubble.margin * 2, height * 0.806 * 0.02, height * 0.806 * 0.16);
801
+ ctx.fillStyle = new color_1.default(curColor).darken(0.3).hex();
802
+ ctx.fill();
803
+ }
804
+ /** End Difficulty Text & Separation Line Draw */
805
+ /** Begin Achievement Rate Draw */
806
+ {
807
+ const scoreSize = height * 0.806 * 0.208;
808
+ util_1.Util.drawText(ctx, score
809
+ ? `${util_1.Util.truncate(score.achievement, 4)}%`
810
+ : "NO RECORD", x +
811
+ height * 2 -
812
+ element.bubble.margin -
813
+ height * 0.806 * 0.02, y +
814
+ element.bubble.margin +
815
+ titleSize +
816
+ element.bubble.margin * (5 / 8) +
817
+ scoreSize, scoreSize, height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
818
+ }
819
+ /** End Achievement Rate Draw */
820
+ /** Begin Achievement Rank Draw */
821
+ {
822
+ if (score) {
823
+ let rankImg;
824
+ switch (score.achievementRank) {
825
+ case type_1.EAchievementTypes.D:
826
+ rankImg = theme.getFile(element.sprites.achievement.d);
827
+ break;
828
+ case type_1.EAchievementTypes.C:
829
+ rankImg = theme.getFile(element.sprites.achievement.c);
830
+ break;
831
+ case type_1.EAchievementTypes.B:
832
+ rankImg = theme.getFile(element.sprites.achievement.b);
833
+ break;
834
+ case type_1.EAchievementTypes.BB:
835
+ rankImg = theme.getFile(element.sprites.achievement.bb);
836
+ break;
837
+ case type_1.EAchievementTypes.BBB:
838
+ rankImg = theme.getFile(element.sprites.achievement.bbb);
839
+ break;
840
+ case type_1.EAchievementTypes.A:
841
+ rankImg = theme.getFile(element.sprites.achievement.a);
842
+ break;
843
+ case type_1.EAchievementTypes.AA:
844
+ rankImg = theme.getFile(element.sprites.achievement.aa);
845
+ break;
846
+ case type_1.EAchievementTypes.AAA:
847
+ rankImg = theme.getFile(element.sprites.achievement.aaa);
848
+ break;
849
+ case type_1.EAchievementTypes.S:
850
+ rankImg = theme.getFile(element.sprites.achievement.s);
851
+ break;
852
+ case type_1.EAchievementTypes.SP:
853
+ rankImg = theme.getFile(element.sprites.achievement.sp);
854
+ break;
855
+ case type_1.EAchievementTypes.SS:
856
+ rankImg = theme.getFile(element.sprites.achievement.ss);
857
+ break;
858
+ case type_1.EAchievementTypes.SSP:
859
+ rankImg = theme.getFile(element.sprites.achievement.ssp);
860
+ break;
861
+ case type_1.EAchievementTypes.SSS:
862
+ rankImg = theme.getFile(element.sprites.achievement.sss);
863
+ break;
864
+ default:
865
+ rankImg = theme.getFile(element.sprites.achievement.sssp);
866
+ }
867
+ const img = new canvas_1.Image();
868
+ img.src = rankImg;
869
+ ctx.drawImage(img, x + element.bubble.margin * (1 / 4), y +
870
+ element.bubble.margin +
871
+ titleSize +
872
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.3 * 2.133, height * 0.806 * 0.3);
873
+ }
874
+ }
875
+ /** End Achievement Rank Draw */
876
+ /** Begin Milestone Draw */
877
+ {
878
+ if (score) {
879
+ let comboImg, syncImg;
880
+ switch (score.combo) {
881
+ case type_1.EComboTypes.NONE:
882
+ comboImg = theme.getFile(element.sprites.milestone.none);
883
+ break;
884
+ case type_1.EComboTypes.FULL_COMBO:
885
+ comboImg = theme.getFile(element.sprites.milestone.fc);
886
+ break;
887
+ case type_1.EComboTypes.FULL_COMBO_PLUS:
888
+ comboImg = theme.getFile(element.sprites.milestone.fcp);
889
+ break;
890
+ case type_1.EComboTypes.ALL_PERFECT:
891
+ comboImg = theme.getFile(element.sprites.milestone.ap);
892
+ break;
893
+ case type_1.EComboTypes.ALL_PERFECT_PLUS:
894
+ comboImg = theme.getFile(element.sprites.milestone.app);
895
+ break;
896
+ }
897
+ switch (score.sync) {
898
+ case type_1.ESyncTypes.NONE:
899
+ syncImg = theme.getFile(element.sprites.milestone.none);
900
+ break;
901
+ case type_1.ESyncTypes.SYNC_PLAY:
902
+ syncImg = theme.getFile(element.sprites.milestone.sync);
903
+ break;
904
+ case type_1.ESyncTypes.FULL_SYNC:
905
+ syncImg = theme.getFile(element.sprites.milestone.fs);
906
+ break;
907
+ case type_1.ESyncTypes.FULL_SYNC_PLUS:
908
+ syncImg = theme.getFile(element.sprites.milestone.fsp);
909
+ break;
910
+ case type_1.ESyncTypes.FULL_SYNC_DX:
911
+ syncImg = theme.getFile(element.sprites.milestone.fdx);
912
+ break;
913
+ case type_1.ESyncTypes.FULL_SYNC_DX_PLUS:
914
+ syncImg = theme.getFile(element.sprites.milestone.fdxp);
915
+ break;
916
+ }
917
+ const combo = new canvas_1.Image();
918
+ combo.src = comboImg;
919
+ ctx.drawImage(combo, x +
920
+ height *
921
+ 0.806 *
922
+ (0.32 * 2.133 + 0.06 - 0.1), y +
923
+ element.bubble.margin +
924
+ titleSize +
925
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.32, height * 0.806 * 0.32);
926
+ const sync = new canvas_1.Image();
927
+ sync.src = syncImg;
928
+ ctx.drawImage(sync, x +
929
+ height *
930
+ 0.806 *
931
+ (0.32 * 2.133 + 0.04 + 0.32 - 0.1), y +
932
+ element.bubble.margin +
933
+ titleSize +
934
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.32, height * 0.806 * 0.32);
935
+ }
936
+ }
937
+ /** End Milestone Draw */
938
+ /** Begin Version Draw */
939
+ {
940
+ let versions = [];
941
+ for (let i = 0; i < 3; ++i) {
942
+ versions[i] = {
943
+ region: "DX",
944
+ version: undefined,
945
+ EXAppend: false,
946
+ };
947
+ }
948
+ const VER_DX = chart.difficulty == type_1.EDifficulty.REMASTER
949
+ ? chart.events.find((v) => v.type == "existence" &&
950
+ v.version.region == "DX")?.version
951
+ : chart.addVersion.DX;
952
+ const VER_EX = chart.difficulty == type_1.EDifficulty.REMASTER
953
+ ? chart.events.find((v) => v.type == "existence" &&
954
+ v.version.region == "EX")?.version
955
+ : chart.addVersion.EX;
956
+ const VER_CN = chart.difficulty == type_1.EDifficulty.REMASTER
957
+ ? chart.events.find((v) => v.type == "existence" &&
958
+ v.version.region == "CN")?.version
959
+ : chart.addVersion.CN;
960
+ if (VER_DX) {
961
+ const version = versions[0];
962
+ version.version = VER_DX;
963
+ version.region = "DX";
964
+ }
965
+ if (VER_EX) {
966
+ for (let i = 0; i < 3; ++i) {
967
+ const version = versions[i];
968
+ if (version.version) {
969
+ if (!(version.version.gameVersion.isDX &&
970
+ version.version.gameVersion.minor >=
971
+ 10 &&
972
+ version.version.gameVersion.minor <
973
+ 20) &&
974
+ lodash_1.default.isEqual(versions[i].version?.gameVersion, VER_EX.gameVersion)) {
975
+ versions[i].EXAppend = true;
976
+ break;
977
+ }
978
+ }
979
+ else {
980
+ version.version = VER_EX;
981
+ version.region = "EX";
982
+ break;
983
+ }
984
+ }
985
+ }
986
+ if (VER_CN) {
987
+ for (let i = 0; i < 3; ++i) {
988
+ const version = versions[i];
989
+ if (version.version) {
990
+ if (!version.version.gameVersion.isDX &&
991
+ lodash_1.default.isEqual(versions[i].version?.gameVersion, VER_CN.gameVersion)) {
992
+ break;
993
+ }
994
+ }
995
+ else {
996
+ version.version = VER_CN;
997
+ version.region = "CN";
998
+ break;
999
+ }
1000
+ }
1001
+ }
1002
+ let counter = 0;
1003
+ for (const version of versions) {
1004
+ if (version.version) {
1005
+ counter++;
1006
+ if (!version.version.gameVersion.isDX)
1007
+ version.region = "OLD";
1008
+ }
1009
+ }
1010
+ const versionImageHeight = (height - element.bubble.margin * 2) *
1011
+ (isShort ? 1 / 3 : 3 / 8);
1012
+ const versionImageWidth = (versionImageHeight / 160) * 332;
1013
+ const regionTextSize = versionImageHeight * (5 / 8);
1014
+ for (let i = 0, curx = x + width - element.bubble.margin, cury = y + element.bubble.margin; i < versions.length; ++i,
1015
+ cury =
1016
+ isShort ||
1017
+ curx - versionImageWidth - regionTextSize <
1018
+ x + height * 2
1019
+ ? cury + versionImageHeight
1020
+ : cury,
1021
+ curx =
1022
+ isShort ||
1023
+ curx - versionImageWidth - regionTextSize <
1024
+ x + height * 2
1025
+ ? x + width - element.bubble.margin
1026
+ : curx) {
1027
+ const version = versions[i];
1028
+ if (version.version) {
1029
+ let region = version.region;
1030
+ if (version.region == "EX" &&
1031
+ !(version.version.gameVersion.minor >=
1032
+ 10 &&
1033
+ version.version.gameVersion.minor < 20)) {
1034
+ region = "DX";
1035
+ }
1036
+ const rawVersion = findVersion(version.version.gameVersion.minor, version.version.gameVersion.isDX, region == "CN");
1037
+ if (rawVersion) {
1038
+ const versionImage = theme.getFile(element.sprites.versions[region][rawVersion]);
1039
+ try {
1040
+ (0, sharp_1.default)(versionImage);
1041
+ if (versionImage) {
1042
+ const versionImg = new canvas_1.Image();
1043
+ versionImg.src = versionImage;
1044
+ let text;
1045
+ switch (version.region) {
1046
+ case "DX":
1047
+ if (version.EXAppend)
1048
+ text = "🇯🇵🌏";
1049
+ else
1050
+ text = "🇯🇵";
1051
+ break;
1052
+ case "EX":
1053
+ text = "🌏";
1054
+ break;
1055
+ case "CN":
1056
+ text = "🇨🇳";
1057
+ break;
1058
+ default:
1059
+ text = "";
1060
+ }
1061
+ ctx.drawImage(versionImg, (curx -= versionImageWidth), cury, versionImageWidth, versionImageHeight);
1062
+ if (version.region != "OLD") {
1063
+ await util_1.Util.drawEmojiOrGlyph(ctx, text, curx, cury +
1064
+ regionTextSize +
1065
+ (versionImageHeight -
1066
+ regionTextSize) /
1067
+ 2, regionTextSize, Infinity, "right");
1068
+ curx -=
1069
+ util_1.Util.visibleLength(text) *
1070
+ regionTextSize +
1071
+ element.bubble.margin;
1072
+ }
1073
+ }
1074
+ }
1075
+ catch { }
1076
+ }
1077
+ }
1078
+ }
1079
+ /** End Version Draw */
1080
+ /** Begin Note Count Draw */
1081
+ const noteCountTexts = Object.entries(chart.meta.notes).map(([k, v]) => `${util_1.Util.capitalize(k)}: ${v}`);
1082
+ const noteCountTextSize = (height - element.bubble.margin * 4) /
1083
+ noteCountTexts.length;
1084
+ let noteCountLength = 0;
1085
+ noteCountTexts.forEach((v, i) => {
1086
+ util_1.Util.drawText(ctx, v, x +
1087
+ element.bubble.margin * (3 / 2) +
1088
+ height * 2, y +
1089
+ element.bubble.margin +
1090
+ noteCountTextSize +
1091
+ (noteCountTextSize +
1092
+ element.bubble.margin / 2) *
1093
+ i, noteCountTextSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
1094
+ const length = util_1.Util.measureText(ctx, v, noteCountTextSize, Infinity).width;
1095
+ if (length > noteCountLength)
1096
+ noteCountLength = length;
1097
+ });
1098
+ /** End Note Count Draw */
1099
+ /** Begin Internal Level Trend Draw */
1100
+ if (!isShort) {
1101
+ const CURRENT_MINOR = (() => {
1102
+ switch (targetRegion) {
1103
+ case "EX":
1104
+ return EX_LATEST;
1105
+ case "CN":
1106
+ return CN_LATEST;
1107
+ case "DX":
1108
+ default:
1109
+ return DX_LATEST;
1110
+ }
1111
+ })();
1112
+ const maxWidth = width -
1113
+ height * 2 -
1114
+ element.bubble.margin * 4 -
1115
+ noteCountLength;
1116
+ const maxFitTrendCount = Math.trunc(maxWidth / versionImageWidth);
1117
+ const trendEvents = chart.events.filter((v) => v.type == "existence" &&
1118
+ v.version.region == targetRegion);
1119
+ let actualEvents = lodash_1.default.uniqWith(trendEvents, (a, b) => {
1120
+ return lodash_1.default.isEqual(a.data.level, b.data.level);
1121
+ });
1122
+ if (actualEvents.length == maxFitTrendCount) {
1123
+ }
1124
+ else if (actualEvents.length > maxFitTrendCount) {
1125
+ while (actualEvents.length > maxFitTrendCount)
1126
+ actualEvents.shift();
1127
+ actualEvents.shift();
1128
+ actualEvents.shift();
1129
+ actualEvents.unshift(trendEvents[0]);
1130
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
1131
+ }
1132
+ else if (trendEvents.length > maxFitTrendCount) {
1133
+ actualEvents = lodash_1.default.filter(actualEvents, (v) => !(lodash_1.default.isEqual(v.version.gameVersion, trendEvents[0].version
1134
+ .gameVersion) ||
1135
+ lodash_1.default.isEqual(v.version.gameVersion, trendEvents[trendEvents.length - 1].version.gameVersion)));
1136
+ for (let i = trendEvents.length - 2; i > 0 &&
1137
+ actualEvents.length < maxFitTrendCount - 2; --i) {
1138
+ const event = trendEvents[i];
1139
+ if (event)
1140
+ actualEvents.push(event);
1141
+ actualEvents = lodash_1.default.uniqWith(actualEvents, (a, b) => {
1142
+ return (lodash_1.default.isEqual(a.version.gameVersion.major, b.version.gameVersion.major) &&
1143
+ lodash_1.default.isEqual(a.version.gameVersion.minor, b.version.gameVersion.minor));
1144
+ });
1145
+ }
1146
+ actualEvents.unshift(trendEvents[0]);
1147
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
1148
+ actualEvents = lodash_1.default.uniqWith(actualEvents, (a, b) => {
1149
+ return (lodash_1.default.isEqual(a.version.gameVersion.major, b.version.gameVersion.major) &&
1150
+ lodash_1.default.isEqual(a.version.gameVersion.minor, b.version.gameVersion.minor));
1151
+ });
1152
+ actualEvents = lodash_1.default.sortBy(actualEvents, (v) => v.version.gameVersion.minor);
1153
+ if (trendEvents.length > 1) {
1154
+ if (actualEvents.length >= maxFitTrendCount)
1155
+ actualEvents.pop();
1156
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
1157
+ }
1158
+ const removalEvent = chart.events.find((v) => v.type == "removal" &&
1159
+ v.version.region == targetRegion);
1160
+ if (removalEvent) {
1161
+ actualEvents.pop();
1162
+ actualEvents.push(removalEvent);
1163
+ }
1164
+ }
1165
+ else {
1166
+ actualEvents = [...trendEvents];
1167
+ }
1168
+ if (actualEvents[actualEvents.length - 1]?.version
1169
+ .gameVersion.minor < CURRENT_MINOR) {
1170
+ while (actualEvents.length >= maxFitTrendCount)
1171
+ actualEvents.pop();
1172
+ actualEvents.push({
1173
+ type: "removal",
1174
+ version: util_2.MaimaiUtil.Version.toEventVersion(util_2.MaimaiUtil.Version.getNextVersion(trendEvents[trendEvents.length - 1]
1175
+ .version)),
1176
+ });
1177
+ }
1178
+ actualEvents = lodash_1.default.uniqWith(actualEvents, (a, b) => {
1179
+ return (lodash_1.default.isEqual(a.version.gameVersion.major, b.version.gameVersion.major) &&
1180
+ lodash_1.default.isEqual(a.version.gameVersion.minor, b.version.gameVersion.minor) &&
1181
+ lodash_1.default.isEqual(a.type, b.type));
1182
+ });
1183
+ let positionAdjustment = 0;
1184
+ let addGap = (maxWidth -
1185
+ actualEvents.length * versionImageWidth) /
1186
+ (actualEvents.length - 1);
1187
+ if (addGap > maxWidth / 5) {
1188
+ addGap = maxWidth / 5;
1189
+ positionAdjustment =
1190
+ (maxWidth -
1191
+ (addGap * (actualEvents.length - 1) +
1192
+ versionImageWidth *
1193
+ actualEvents.length)) /
1194
+ 2;
1195
+ }
1196
+ for (let i = 0, curx = x +
1197
+ positionAdjustment +
1198
+ height * 2 +
1199
+ element.bubble.margin * (5 / 2) +
1200
+ noteCountLength, cury = y +
1201
+ element.bubble.margin * (3 / 2) +
1202
+ versionImageHeight; i < actualEvents.length; ++i) {
1203
+ const event = actualEvents[i];
1204
+ if (!event)
1205
+ continue;
1206
+ let logoRegion = event.version.gameVersion.isDX
1207
+ ? targetRegion
1208
+ : "OLD";
1209
+ if (logoRegion == "EX") {
1210
+ if (!(10 <=
1211
+ event.version.gameVersion
1212
+ .minor &&
1213
+ event.version.gameVersion.minor < 20)) {
1214
+ logoRegion = "DX";
1215
+ }
1216
+ }
1217
+ const rawVersion = findVersion(event.version.gameVersion.minor, event.version.gameVersion.isDX, logoRegion == "CN");
1218
+ if (rawVersion) {
1219
+ const versionImage = theme.getFile(element.sprites.versions[logoRegion][rawVersion]);
1220
+ try {
1221
+ (0, sharp_1.default)(versionImage);
1222
+ if (versionImage) {
1223
+ const versionImg = new canvas_1.Image();
1224
+ versionImg.src = versionImage;
1225
+ ctx.drawImage(versionImg, curx, cury, versionImageWidth, versionImageHeight);
1226
+ if (event.type == "existence") {
1227
+ let symbol = "";
1228
+ if (i != 0) {
1229
+ const lastEvent = actualEvents[i - 1];
1230
+ if (lastEvent.type ==
1231
+ "existence") {
1232
+ if (lastEvent.data
1233
+ .level <
1234
+ event.data.level)
1235
+ symbol = "↑";
1236
+ else if (lastEvent.data
1237
+ .level >
1238
+ event.data.level)
1239
+ symbol = "↓";
1240
+ else if (lastEvent.data
1241
+ .level ==
1242
+ event.data.level)
1243
+ symbol = "→";
1244
+ }
1245
+ }
1246
+ util_1.Util.drawText(ctx, `${symbol}${util_1.Util.truncate(event.data.level, 1)}`, curx +
1247
+ versionImageWidth / 2, cury +
1248
+ versionImageHeight +
1249
+ noteCountTextSize, noteCountTextSize, height * 0.806 * 0.04, Infinity, "center", "white", new color_1.default(curColor)
1250
+ .darken(0.3)
1251
+ .hexa());
1252
+ }
1253
+ else if (event.type == "removal") {
1254
+ util_1.Util.drawText(ctx, `❌`, curx +
1255
+ versionImageWidth / 2, cury +
1256
+ versionImageHeight +
1257
+ noteCountTextSize, noteCountTextSize, height * 0.806 * 0.04, Infinity, "center", "white", new color_1.default(curColor)
1258
+ .darken(0.3)
1259
+ .hexa());
1260
+ }
1261
+ curx += versionImageWidth + addGap;
1262
+ }
1263
+ }
1264
+ catch { }
1265
+ }
1266
+ }
1267
+ }
1268
+ }
1269
+ /** End Internal Level Trend Draw */
1270
+ ctx.restore();
1271
+ }
1272
+ /** End Main Content Draw */
1273
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
1274
+ ctx.beginPath();
1275
+ ctx.roundRect(x, y + height * 0.742, height * 2, height * (1 - 0.742), [0, (height * 0.806) / 7, 0, (height * 0.806) / 7]);
1276
+ ctx.fill();
1277
+ /** Begin Difficulty & DX Rating Draw */
1278
+ {
1279
+ ctx.save();
1280
+ ctx.clip();
1281
+ util_1.Util.drawText(ctx, chart.designer.name || "-", x + element.bubble.margin, y + height * (0.806 + (1 - 0.806) / 2), height * 0.806 * 0.128, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
1282
+ ctx.restore();
1283
+ util_1.Util.drawText(ctx, `${score ? `${score.dxScore}/` : "MAX DX SCR: "}${chart.meta.maxDXScore}`, x + height * 2 - element.bubble.margin, y + height - element.bubble.margin * 3.1, height * 0.806 * 0.128, height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
1284
+ }
1285
+ /** End Difficulty & DX Rating Draw */
1286
+ ctx.restore();
1287
+ /** End Card Draw */
1288
+ }
1289
+ })(ChartGrid = Chart.ChartGrid || (Chart.ChartGrid = {}));
1290
+ let DetailInfo;
1291
+ (function (DetailInfo) {
1292
+ DetailInfo.schema = painter_1.ThemeManager.Element.extend({
1293
+ type: v4_1.z.literal("detail-info"),
1294
+ width: v4_1.z.number().min(1),
1295
+ height: v4_1.z.number().min(1),
1296
+ margin: v4_1.z.number().min(0),
1297
+ color: v4_1.z.object({
1298
+ card: util_1.Util.z.color(),
1299
+ }),
1300
+ sprites: v4_1.z.object({
1301
+ mode: v4_1.z.object({
1302
+ standard: v4_1.z.string(),
1303
+ dx: v4_1.z.string(),
1304
+ }),
1305
+ }),
1306
+ });
1307
+ async function draw(ctx, theme, element, chartId) {
1308
+ const jacketMargin = element.margin;
1309
+ const textMargin = element.margin;
1310
+ const chart = database_1.Database.getLocalChart(chartId, type_1.EDifficulty.BASIC);
1311
+ const jacket = await database_1.Database.fetchJacket(chartId);
1312
+ /* Begin Background Draw */
1313
+ ctx.beginPath();
1314
+ ctx.roundRect(element.x, element.y, element.width, element.height, Math.min(theme.content.width, theme.content.height) *
1315
+ (3 / 128));
1316
+ ctx.fillStyle = element.color.card;
1317
+ ctx.strokeStyle = new color_1.default(element.color.card)
1318
+ .darken(0.6)
1319
+ .hex();
1320
+ ctx.lineWidth =
1321
+ Math.min(theme.content.width, theme.content.height) *
1322
+ (3 / 512);
1323
+ ctx.stroke();
1324
+ ctx.fill();
1325
+ /* End Background Draw */
1326
+ /* Begin jacket draw */
1327
+ if (jacket) {
1328
+ const jacketImage = new canvas_1.Image();
1329
+ const roundRadius = Math.min(theme.content.width, theme.content.height) *
1330
+ (3 / 128);
1331
+ jacketImage.src = jacket;
1332
+ ctx.beginPath();
1333
+ ctx.roundRect(element.x + jacketMargin, element.y + jacketMargin, element.width - jacketMargin * 2, element.width - jacketMargin * 2, [roundRadius, roundRadius, 0, 0]);
1334
+ ctx.save();
1335
+ ctx.clip();
1336
+ ctx.drawImage(jacketImage, element.x + jacketMargin, element.y + jacketMargin, element.width - jacketMargin * 2, element.width - jacketMargin * 2);
1337
+ ctx.restore();
1338
+ }
1339
+ /* End jacket draw */
1340
+ /* Begin Detail Draw */
1341
+ if (chart) {
1342
+ const textSizeTitle = element.width * (1 / 16);
1343
+ const textSizeSecondary = element.width * (1 / 24);
1344
+ const { actualBoundingBoxAscent: ascent, actualBoundingBoxDescent: decent, } = util_1.Util.measureText(ctx, chart.name, textSizeTitle, Infinity);
1345
+ const titleActualHeight = Math.abs(ascent - decent);
1346
+ const mode = new canvas_1.Image();
1347
+ const chartModeBadgeImg = theme.getFile(chart.id > 10000
1348
+ ? element.sprites.mode.dx
1349
+ : element.sprites.mode.standard);
1350
+ const { width: modeWidth, height: modeHeight } = await (0, sharp_1.default)(chartModeBadgeImg).metadata();
1351
+ const aspectRatio = (modeWidth ?? 0) / (modeHeight ?? 1) || 3;
1352
+ const textLineWidth = element.width * (7 / 512);
1353
+ const textColor = new color_1.default(element.color.card)
1354
+ .darken(0.5)
1355
+ .hex();
1356
+ const textTitleMaxWidth = element.width -
1357
+ textMargin * 2 -
1358
+ textSizeTitle * aspectRatio;
1359
+ const titleMetrics = util_1.Util.measureText(ctx, chart.name, textSizeTitle, textTitleMaxWidth);
1360
+ util_1.Util.drawText(ctx, chart.name, element.x + textMargin, element.y +
1361
+ jacketMargin +
1362
+ textMargin * (1 / 2) +
1363
+ (element.width - jacketMargin * 2) +
1364
+ textSizeTitle, textSizeTitle, textLineWidth, textTitleMaxWidth, "left", "white", textColor);
1365
+ util_1.Util.drawText(ctx, chart.artist, element.x + textMargin, element.y +
1366
+ jacketMargin +
1367
+ textMargin * (1 / 2) +
1368
+ (element.width - jacketMargin * 2) +
1369
+ textSizeTitle * 2, textSizeSecondary, textLineWidth, element.width - textMargin * 2, "left", "white", textColor);
1370
+ util_1.Util.drawText(ctx, `#${chart.id} BPM: ${chart.bpm}`, element.x + textMargin, element.y +
1371
+ jacketMargin +
1372
+ textMargin * (1 / 2) +
1373
+ (element.width - jacketMargin * 2) +
1374
+ textSizeTitle * 3, textSizeSecondary, textLineWidth, element.width - textMargin * 2, "left", "white", textColor);
1375
+ const EVENT_DX = chart.events
1376
+ .filter((v) => v.version.region == "DX" &&
1377
+ v.version.gameVersion.minor >= DX_LATEST)
1378
+ .map((v) => v.type);
1379
+ const EVENT_EX = chart.events
1380
+ .filter((v) => v.version.region == "EX" &&
1381
+ v.version.gameVersion.minor >= EX_LATEST)
1382
+ .map((v) => v.type);
1383
+ const EVENT_CN = chart.events
1384
+ .filter((v) => v.version.region == "CN" &&
1385
+ v.version.gameVersion.minor >= CN_LATEST)
1386
+ .map((v) => v.type);
1387
+ const EXIST_DX = EVENT_DX.includes("existence") &&
1388
+ !EVENT_DX.includes("removal")
1389
+ ? "🇯🇵"
1390
+ : "";
1391
+ const EXIST_EX = EVENT_EX.includes("existence") &&
1392
+ !EVENT_EX.includes("removal")
1393
+ ? "🌏"
1394
+ : "";
1395
+ const EXIST_CN = EVENT_CN.includes("existence") &&
1396
+ !EVENT_CN.includes("removal")
1397
+ ? "🇨🇳"
1398
+ : "";
1399
+ const title = [];
1400
+ if (EXIST_DX)
1401
+ title.push(EXIST_DX);
1402
+ if (EXIST_EX)
1403
+ title.push(EXIST_EX);
1404
+ if (EXIST_CN)
1405
+ title.push(EXIST_CN);
1406
+ await util_1.Util.drawEmojiOrGlyph(ctx, title.join(" "), element.x + element.width - textMargin, element.y +
1407
+ jacketMargin +
1408
+ textMargin * (1 / 2) +
1409
+ (element.width - jacketMargin * 2) +
1410
+ textSizeTitle * 3, textSizeSecondary * (9 / 8), element.width - textMargin * 2, "right", "white", textColor);
1411
+ /** Begin Chart Mode Draw */
1412
+ {
1413
+ mode.src = chartModeBadgeImg;
1414
+ ctx.drawImage(mode, element.x +
1415
+ textMargin * (3 / 2) +
1416
+ titleMetrics.width, element.y +
1417
+ jacketMargin +
1418
+ textMargin * (1 / 2) +
1419
+ (element.width - jacketMargin * 2) -
1420
+ titleActualHeight / 2 +
1421
+ textSizeTitle / 2, textSizeTitle * aspectRatio, textSizeTitle);
1422
+ }
1423
+ /** End Chart Mode Draw */
1424
+ }
1425
+ /* End Detail Draw */
1426
+ }
1427
+ DetailInfo.draw = draw;
1428
+ })(DetailInfo = Chart.DetailInfo || (Chart.DetailInfo = {}));
1429
+ })(Chart = MaimaiPainterModule.Chart || (MaimaiPainterModule.Chart = {}));
1430
+ })(MaimaiPainterModule || (exports.MaimaiPainterModule = MaimaiPainterModule = {}));
1431
+ //# sourceMappingURL=index.js.map