maidraw 0.4.5 → 0.4.6

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 (203) hide show
  1. package/assets/themes/chunithm/common/verse/achievement/A.png +0 -0
  2. package/assets/themes/chunithm/common/verse/achievement/AA.png +0 -0
  3. package/assets/themes/chunithm/common/verse/achievement/AAA.png +0 -0
  4. package/assets/themes/chunithm/common/verse/achievement/B.png +0 -0
  5. package/assets/themes/chunithm/common/verse/achievement/BB.png +0 -0
  6. package/assets/themes/chunithm/common/verse/achievement/BBB.png +0 -0
  7. package/assets/themes/chunithm/common/verse/achievement/C.png +0 -0
  8. package/assets/themes/chunithm/common/verse/achievement/D.png +0 -0
  9. package/assets/themes/chunithm/common/verse/achievement/S.png +0 -0
  10. package/assets/themes/chunithm/common/verse/achievement/SS.png +0 -0
  11. package/assets/themes/chunithm/common/verse/achievement/SSP.png +0 -0
  12. package/assets/themes/chunithm/common/verse/achievement/SSS.png +0 -0
  13. package/assets/themes/chunithm/common/verse/achievement/SSSP.png +0 -0
  14. package/assets/themes/chunithm/common/verse/jp/background.png +0 -0
  15. package/assets/themes/chunithm/common/verse/jp/logo.png +0 -0
  16. package/assets/themes/chunithm/common/versionless/icon.png +0 -0
  17. package/assets/themes/chunithm/common/versionless/milestone/aj.png +0 -0
  18. package/assets/themes/chunithm/common/versionless/milestone/ajc.png +0 -0
  19. package/assets/themes/chunithm/common/versionless/milestone/fc.png +0 -0
  20. package/assets/themes/chunithm/common/versionless/nameplate.png +0 -0
  21. package/assets/themes/chunithm/jp/verseLandscape/manifest.json +131 -0
  22. package/assets/themes/maimai/common/versionless/dxRating/numberMap.png +0 -0
  23. package/assets/themes/maimai/common/versionless/void.png +0 -0
  24. package/dist/chu/bests/best50.d.ts +32 -0
  25. package/dist/chu/bests/best50.js +752 -0
  26. package/dist/chu/bests/best50.js.map +1 -0
  27. package/dist/chu/bests/index.d.ts +1 -0
  28. package/dist/chu/bests/index.js +6 -0
  29. package/dist/chu/bests/index.js.map +1 -0
  30. package/dist/chu/bests/lib/index.d.ts +26 -0
  31. package/dist/chu/bests/lib/index.js +60 -0
  32. package/dist/chu/bests/lib/index.js.map +1 -0
  33. package/dist/chu/bests/lib/kamaiTachi/index.d.ts +154 -0
  34. package/dist/chu/bests/lib/kamaiTachi/index.js +315 -0
  35. package/dist/chu/bests/lib/kamaiTachi/index.js.map +1 -0
  36. package/dist/chu/bests/type.d.ts +121 -0
  37. package/dist/chu/bests/type.js +3 -0
  38. package/dist/chu/bests/type.js.map +1 -0
  39. package/dist/chu/chart/database.d.ts +9 -0
  40. package/dist/chu/chart/database.js +64 -0
  41. package/dist/chu/chart/database.js.map +1 -0
  42. package/dist/chu/chart/index.d.ts +4 -0
  43. package/dist/chu/chart/index.js +9 -0
  44. package/dist/chu/chart/index.js.map +1 -0
  45. package/dist/chu/index.d.ts +8 -0
  46. package/dist/chu/index.js +45 -0
  47. package/dist/chu/index.js.map +1 -0
  48. package/dist/chu/type.d.ts +51 -0
  49. package/dist/chu/type.js +42 -0
  50. package/dist/chu/type.js.map +1 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.js +2 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/lib/cache.d.ts +8 -0
  55. package/dist/lib/cache.js +52 -0
  56. package/dist/lib/cache.js.map +1 -0
  57. package/dist/mai/best50/index.d.ts +1 -1
  58. package/dist/mai/best50/index.js +6 -4
  59. package/dist/mai/best50/index.js.map +1 -1
  60. package/dist/mai/best50/lib/divingFish/index.d.ts +1 -1
  61. package/dist/mai/best50/lib/divingFish/index.js +1 -1
  62. package/dist/mai/best50/lib/divingFish/index.js.map +1 -1
  63. package/dist/mai/best50/lib/index.d.ts +2 -12
  64. package/dist/mai/best50/lib/index.js +6 -82
  65. package/dist/mai/best50/lib/index.js.map +1 -1
  66. package/dist/mai/best50/lib/kamaiTachi/index.d.ts +1 -1
  67. package/dist/mai/best50/lib/kamaiTachi/index.js +1 -1
  68. package/dist/mai/best50/lib/kamaiTachi/index.js.map +1 -1
  69. package/dist/mai/best50/lib/lxns/index.d.ts +1 -1
  70. package/dist/mai/best50/lib/lxns/index.js +1 -1
  71. package/dist/mai/best50/lib/lxns/index.js.map +1 -1
  72. package/dist/mai/best50/type.d.ts +1 -1
  73. package/dist/mai/chart/database.d.ts +1 -1
  74. package/dist/mai/chart/database.js +19 -12
  75. package/dist/mai/chart/database.js.map +1 -1
  76. package/dist/mai/index.d.ts +2 -0
  77. package/dist/mai/index.js +34 -0
  78. package/dist/mai/index.js.map +1 -1
  79. package/dist/mai/type.js.map +1 -0
  80. package/package.json +2 -2
  81. package/dist/type.js.map +0 -1
  82. /package/assets/themes/{common/versionless/dxRating → chunithm/common/versionless}/numberMap.png +0 -0
  83. /package/assets/themes/{common → chunithm/common}/versionless/void.png +0 -0
  84. /package/assets/themes/{cn → maimai/cn}/2024Landscape/manifest.json +0 -0
  85. /package/assets/themes/{cn → maimai/cn}/2024Portrait/manifest.json +0 -0
  86. /package/assets/themes/{common → maimai/common}/buddies/achievement/a.png +0 -0
  87. /package/assets/themes/{common → maimai/common}/buddies/achievement/aa.png +0 -0
  88. /package/assets/themes/{common → maimai/common}/buddies/achievement/aaa.png +0 -0
  89. /package/assets/themes/{common → maimai/common}/buddies/achievement/b.png +0 -0
  90. /package/assets/themes/{common → maimai/common}/buddies/achievement/bb.png +0 -0
  91. /package/assets/themes/{common → maimai/common}/buddies/achievement/bbb.png +0 -0
  92. /package/assets/themes/{common → maimai/common}/buddies/achievement/c.png +0 -0
  93. /package/assets/themes/{common → maimai/common}/buddies/achievement/d.png +0 -0
  94. /package/assets/themes/{common → maimai/common}/buddies/achievement/s.png +0 -0
  95. /package/assets/themes/{common → maimai/common}/buddies/achievement/sp.png +0 -0
  96. /package/assets/themes/{common → maimai/common}/buddies/achievement/ss.png +0 -0
  97. /package/assets/themes/{common → maimai/common}/buddies/achievement/ssp.png +0 -0
  98. /package/assets/themes/{common → maimai/common}/buddies/achievement/sss.png +0 -0
  99. /package/assets/themes/{common → maimai/common}/buddies/achievement/sssp.png +0 -0
  100. /package/assets/themes/{common → maimai/common}/buddies/cn/logo.png +0 -0
  101. /package/assets/themes/{common → maimai/common}/buddies/jp/background.png +0 -0
  102. /package/assets/themes/{common → maimai/common}/buddies/jp/chara.png +0 -0
  103. /package/assets/themes/{common → maimai/common}/buddies/jp/laundry.png +0 -0
  104. /package/assets/themes/{common → maimai/common}/buddies/jp/logo.png +0 -0
  105. /package/assets/themes/{common → maimai/common}/buddies/plus/chara.png +0 -0
  106. /package/assets/themes/{common → maimai/common}/buddies/plus/laundry.png +0 -0
  107. /package/assets/themes/{common → maimai/common}/buddies/plus/logo.png +0 -0
  108. /package/assets/themes/{common → maimai/common}/finale/achievement/a.png +0 -0
  109. /package/assets/themes/{common → maimai/common}/finale/achievement/aa.png +0 -0
  110. /package/assets/themes/{common → maimai/common}/finale/achievement/aaa.png +0 -0
  111. /package/assets/themes/{common → maimai/common}/finale/achievement/b.png +0 -0
  112. /package/assets/themes/{common → maimai/common}/finale/achievement/c.png +0 -0
  113. /package/assets/themes/{common → maimai/common}/finale/achievement/d.png +0 -0
  114. /package/assets/themes/{common → maimai/common}/finale/achievement/e.png +0 -0
  115. /package/assets/themes/{common → maimai/common}/finale/achievement/f.png +0 -0
  116. /package/assets/themes/{common → maimai/common}/finale/achievement/s.png +0 -0
  117. /package/assets/themes/{common → maimai/common}/finale/achievement/sp.png +0 -0
  118. /package/assets/themes/{common → maimai/common}/finale/achievement/ss.png +0 -0
  119. /package/assets/themes/{common → maimai/common}/finale/achievement/ssp.png +0 -0
  120. /package/assets/themes/{common → maimai/common}/finale/achievement/sss.png +0 -0
  121. /package/assets/themes/{common → maimai/common}/finale/achievement/sssp.png +0 -0
  122. /package/assets/themes/{common → maimai/common}/finale/background.png +0 -0
  123. /package/assets/themes/{common → maimai/common}/finale/background2.png +0 -0
  124. /package/assets/themes/{common → maimai/common}/finale/logo.png +0 -0
  125. /package/assets/themes/{common → maimai/common}/finale/milestone/ap.png +0 -0
  126. /package/assets/themes/{common → maimai/common}/finale/milestone/app.png +0 -0
  127. /package/assets/themes/{common → maimai/common}/finale/milestone/fc.png +0 -0
  128. /package/assets/themes/{common → maimai/common}/finale/milestone/fcp.png +0 -0
  129. /package/assets/themes/{common → maimai/common}/finale/milestone/fdx.png +0 -0
  130. /package/assets/themes/{common → maimai/common}/finale/milestone/fdxp.png +0 -0
  131. /package/assets/themes/{common → maimai/common}/finale/milestone/fsp.png +0 -0
  132. /package/assets/themes/{common → maimai/common}/finale/milestone/sync.png +0 -0
  133. /package/assets/themes/{common → maimai/common}/prism/achievement/a.png +0 -0
  134. /package/assets/themes/{common → maimai/common}/prism/achievement/aa.png +0 -0
  135. /package/assets/themes/{common → maimai/common}/prism/achievement/aaa.png +0 -0
  136. /package/assets/themes/{common → maimai/common}/prism/achievement/b.png +0 -0
  137. /package/assets/themes/{common → maimai/common}/prism/achievement/bb.png +0 -0
  138. /package/assets/themes/{common → maimai/common}/prism/achievement/bbb.png +0 -0
  139. /package/assets/themes/{common → maimai/common}/prism/achievement/c.png +0 -0
  140. /package/assets/themes/{common → maimai/common}/prism/achievement/d.png +0 -0
  141. /package/assets/themes/{common → maimai/common}/prism/achievement/s.png +0 -0
  142. /package/assets/themes/{common → maimai/common}/prism/achievement/sp.png +0 -0
  143. /package/assets/themes/{common → maimai/common}/prism/achievement/ss.png +0 -0
  144. /package/assets/themes/{common → maimai/common}/prism/achievement/ssp.png +0 -0
  145. /package/assets/themes/{common → maimai/common}/prism/achievement/sss.png +0 -0
  146. /package/assets/themes/{common → maimai/common}/prism/achievement/sssp.png +0 -0
  147. /package/assets/themes/{common → maimai/common}/prism/jp/background.png +0 -0
  148. /package/assets/themes/{common → maimai/common}/prism/jp/chara.png +0 -0
  149. /package/assets/themes/{common → maimai/common}/prism/jp/laundry.png +0 -0
  150. /package/assets/themes/{common → maimai/common}/prism/jp/logo.png +0 -0
  151. /package/assets/themes/{common → maimai/common}/prism/plus/chara.png +0 -0
  152. /package/assets/themes/{common → maimai/common}/prism/plus/laundry.png +0 -0
  153. /package/assets/themes/{common → maimai/common}/prism/plus/logo.png +0 -0
  154. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/blue.png +0 -0
  155. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/bronze.png +0 -0
  156. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/gold.png +0 -0
  157. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/green.png +0 -0
  158. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/platinum.png +0 -0
  159. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/purple.png +0 -0
  160. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/rainbow.png +0 -0
  161. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/red.png +0 -0
  162. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/silver.png +0 -0
  163. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/white.png +0 -0
  164. /package/assets/themes/{common → maimai/common}/versionless/dxRating/cn/yellow.png +0 -0
  165. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/blue.png +0 -0
  166. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/bronze.png +0 -0
  167. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/gold.png +0 -0
  168. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/green.png +0 -0
  169. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/platinum.png +0 -0
  170. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/purple.png +0 -0
  171. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/rainbow.png +0 -0
  172. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/red.png +0 -0
  173. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/silver.png +0 -0
  174. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/white.png +0 -0
  175. /package/assets/themes/{common → maimai/common}/versionless/dxRating/jp/yellow.png +0 -0
  176. /package/assets/themes/{common → maimai/common}/versionless/icon.png +0 -0
  177. /package/assets/themes/{common → maimai/common}/versionless/milestone/ap.png +0 -0
  178. /package/assets/themes/{common → maimai/common}/versionless/milestone/app.png +0 -0
  179. /package/assets/themes/{common → maimai/common}/versionless/milestone/fc.png +0 -0
  180. /package/assets/themes/{common → maimai/common}/versionless/milestone/fcp.png +0 -0
  181. /package/assets/themes/{common → maimai/common}/versionless/milestone/fdx.png +0 -0
  182. /package/assets/themes/{common → maimai/common}/versionless/milestone/fdxp.png +0 -0
  183. /package/assets/themes/{common → maimai/common}/versionless/milestone/fs.png +0 -0
  184. /package/assets/themes/{common → maimai/common}/versionless/milestone/fsp.png +0 -0
  185. /package/assets/themes/{common → maimai/common}/versionless/milestone/none.png +0 -0
  186. /package/assets/themes/{common → maimai/common}/versionless/milestone/sync.png +0 -0
  187. /package/assets/themes/{common → maimai/common}/versionless/mode/cn/dx.png +0 -0
  188. /package/assets/themes/{common → maimai/common}/versionless/mode/cn/standard.png +0 -0
  189. /package/assets/themes/{common → maimai/common}/versionless/mode/jp/dx.png +0 -0
  190. /package/assets/themes/{common → maimai/common}/versionless/mode/jp/standard.png +0 -0
  191. /package/assets/themes/{common → maimai/common}/versionless/nameplate.png +0 -0
  192. /package/assets/themes/{jp → maimai/jp}/buddiesLandscape/manifest.json +0 -0
  193. /package/assets/themes/{jp → maimai/jp}/buddiesPlusLandscape/manifest.json +0 -0
  194. /package/assets/themes/{jp → maimai/jp}/buddiesPlusPortrait/manifest.json +0 -0
  195. /package/assets/themes/{jp → maimai/jp}/buddiesPortrait/manifest.json +0 -0
  196. /package/assets/themes/{jp → maimai/jp}/finaleLandscape/manifest.json +0 -0
  197. /package/assets/themes/{jp → maimai/jp}/finalePortrait/manifest.json +0 -0
  198. /package/assets/themes/{jp → maimai/jp}/prismLandscape/manifest.json +0 -0
  199. /package/assets/themes/{jp → maimai/jp}/prismPlusLandscape/manifest.json +0 -0
  200. /package/assets/themes/{jp → maimai/jp}/prismPlusPortrait/manifest.json +0 -0
  201. /package/assets/themes/{jp → maimai/jp}/prismPortrait/manifest.json +0 -0
  202. /package/dist/{type.d.ts → mai/type.d.ts} +0 -0
  203. /package/dist/{type.js → mai/type.js} +0 -0
@@ -0,0 +1,752 @@
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.Best50 = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const upath_1 = __importDefault(require("upath"));
9
+ const type_1 = require("../type");
10
+ const canvas_1 = require("canvas");
11
+ const color_1 = __importDefault(require("color"));
12
+ const sharp_1 = __importDefault(require("sharp"));
13
+ const glob_1 = require("glob");
14
+ const kamaiTachi_1 = require("./lib/kamaiTachi");
15
+ const string_template_1 = __importDefault(require("string-template"));
16
+ const chart_1 = require("../chart");
17
+ class HalfFullWidthConvert {
18
+ static charsets = {
19
+ latin: { halfRE: /[!-~]/g, fullRE: /[!-~]/g, delta: 0xfee0 },
20
+ hangul1: { halfRE: /[ᄀ-ᄒ]/g, fullRE: /[ᆨ-ᇂ]/g, delta: -0xedf9 },
21
+ hangul2: { halfRE: /[ᅡ-ᅵ]/g, fullRE: /[ᅡ-ᅵ]/g, delta: -0xee61 },
22
+ kana: {
23
+ delta: 0,
24
+ half: "。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚",
25
+ full: "。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシ" +
26
+ "スセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゛゜",
27
+ },
28
+ extras: {
29
+ delta: 0,
30
+ half: "¢£¬¯¦¥₩\u0020|←↑→↓■°",
31
+ full: "¢£¬ ̄¦¥₩\u3000│←↑→↓■○",
32
+ },
33
+ };
34
+ // @ts-ignore
35
+ static toFull = (set) => (c) => set.delta
36
+ ? String.fromCharCode(c.charCodeAt(0) + set.delta)
37
+ : [...set.full][[...set.half].indexOf(c)];
38
+ // @ts-ignore
39
+ static toHalf = (set) => (c) => set.delta
40
+ ? String.fromCharCode(c.charCodeAt(0) - set.delta)
41
+ : [...set.half][[...set.full].indexOf(c)];
42
+ // @ts-ignore
43
+ static re = (set, way) => set[way + "RE"] || new RegExp("[" + set[way] + "]", "g");
44
+ static sets = Object.values(this.charsets);
45
+ // @ts-ignore
46
+ static toFullWidth = (str0) => this.sets.reduce((str, set) => str.replace(this.re(set, "half"), this.toFull(set)), str0);
47
+ // @ts-ignore
48
+ static toHalfWidth = (str0) => this.sets.reduce((str, set) => str.replace(this.re(set, "full"), this.toHalf(set)), str0);
49
+ }
50
+ class Best50 {
51
+ static KamaiTachi = kamaiTachi_1.KamaiTachi;
52
+ static DEFAULT_THEME = "jp-verse-landscape";
53
+ static get assetsPath() {
54
+ return upath_1.default.join(__dirname, "..", "..", "..", "assets");
55
+ }
56
+ static themes = {};
57
+ static hasTheme(name) {
58
+ return !!this.themes[name];
59
+ }
60
+ static {
61
+ const manifests = (0, glob_1.globSync)(upath_1.default.join(this.assetsPath, "themes", "chunithm", "**", "manifest.json"));
62
+ for (const manifestPath of manifests) {
63
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, "utf-8"));
64
+ if (this.validateManifest(manifest, upath_1.default.dirname(manifestPath))) {
65
+ this.themes[manifest.name] = upath_1.default.dirname(manifestPath);
66
+ }
67
+ }
68
+ const loadThemeResult = this.loadTheme(this.DEFAULT_THEME);
69
+ if (!loadThemeResult) {
70
+ console.error("Failed to load default theme.");
71
+ }
72
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "gen-jyuu-gothic", "GenJyuuGothic-Bold.ttf"), {
73
+ family: "standard-font-title-jp",
74
+ });
75
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "comfortaa", "Comfortaa-Bold.ttf"), {
76
+ family: "standard-font-title-latin",
77
+ weight: "regular",
78
+ });
79
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "seurat-db", "FOT-Seurat Pro DB.otf"), {
80
+ family: "standard-font-username",
81
+ weight: "regular",
82
+ });
83
+ }
84
+ static validateManifest(payload, path) {
85
+ function isFileExist(p) {
86
+ if (isString(p))
87
+ return fs_1.default.existsSync(upath_1.default.join(path, p));
88
+ else
89
+ return false;
90
+ }
91
+ function isOneOf(p, ...args) {
92
+ return args.includes(p);
93
+ }
94
+ function isString(p) {
95
+ return typeof p == "string";
96
+ }
97
+ function isNumber(p) {
98
+ return typeof p == "number";
99
+ }
100
+ function isArray(p) {
101
+ return Array.isArray(p);
102
+ }
103
+ function isObject(p) {
104
+ return typeof p == "object";
105
+ }
106
+ function isHexColor(p) {
107
+ if (isString(p))
108
+ return /^(?:#[0-9A-F]{6}|#[0-9A-F]{8})$/i.test(p);
109
+ else
110
+ return false;
111
+ }
112
+ function isUndefined(p) {
113
+ return typeof p == "undefined";
114
+ }
115
+ function isBoolean(p) {
116
+ return typeof p == "boolean";
117
+ }
118
+ if (isString(payload.displayName) &&
119
+ isString(payload.name) &&
120
+ isNumber(payload.width) &&
121
+ isNumber(payload.height) &&
122
+ isObject(payload.sprites) &&
123
+ isObject(payload.sprites.achievement) &&
124
+ isFileExist(payload.sprites.achievement.d) &&
125
+ isFileExist(payload.sprites.achievement.c) &&
126
+ isFileExist(payload.sprites.achievement.b) &&
127
+ isFileExist(payload.sprites.achievement.bb) &&
128
+ isFileExist(payload.sprites.achievement.bbb) &&
129
+ isFileExist(payload.sprites.achievement.a) &&
130
+ isFileExist(payload.sprites.achievement.aa) &&
131
+ isFileExist(payload.sprites.achievement.aaa) &&
132
+ isFileExist(payload.sprites.achievement.s) &&
133
+ isFileExist(payload.sprites.achievement.ss) &&
134
+ isFileExist(payload.sprites.achievement.ssp) &&
135
+ isFileExist(payload.sprites.achievement.sss) &&
136
+ isFileExist(payload.sprites.achievement.sssp) &&
137
+ isObject(payload.sprites.milestone) &&
138
+ isFileExist(payload.sprites.milestone.aj) &&
139
+ isFileExist(payload.sprites.milestone.ajc) &&
140
+ isFileExist(payload.sprites.milestone.fc) &&
141
+ isFileExist(payload.sprites.milestone.none) &&
142
+ isObject(payload.sprites.profile) &&
143
+ isFileExist(payload.sprites.profile.icon) &&
144
+ isFileExist(payload.sprites.profile.nameplate) &&
145
+ isFileExist(payload.sprites.ratingNumberMap) &&
146
+ isArray(payload.elements)) {
147
+ for (const element of payload.elements) {
148
+ if (isNumber(element.x) && isNumber(element.y)) {
149
+ switch (element.type) {
150
+ case "image": {
151
+ if (isNumber(element.width) &&
152
+ isNumber(element.height) &&
153
+ isFileExist(element.path)) {
154
+ continue;
155
+ }
156
+ else
157
+ return false;
158
+ }
159
+ case "score-grid": {
160
+ if (isOneOf(element.region, "old", "new") &&
161
+ isNumber(element.horizontalSize) &&
162
+ isNumber(element.verticalSize) &&
163
+ isNumber(element.index) &&
164
+ isObject(element.scoreBubble) &&
165
+ isNumber(element.scoreBubble.width) &&
166
+ isNumber(element.scoreBubble.height) &&
167
+ isNumber(element.scoreBubble.margin) &&
168
+ isNumber(element.scoreBubble.gap) &&
169
+ isObject(element.scoreBubble.color) &&
170
+ isHexColor(element.scoreBubble.color.basic) &&
171
+ isHexColor(element.scoreBubble.color.advanced) &&
172
+ isHexColor(element.scoreBubble.color.expert) &&
173
+ isHexColor(element.scoreBubble.color.master) &&
174
+ isHexColor(element.scoreBubble.color.ultima) &&
175
+ isHexColor(element.scoreBubble.color.worldsEnd)) {
176
+ continue;
177
+ }
178
+ else
179
+ return false;
180
+ }
181
+ case "profile": {
182
+ if (isNumber(element.height)) {
183
+ continue;
184
+ }
185
+ else
186
+ return false;
187
+ }
188
+ case "text": {
189
+ if (isNumber(element.size) &&
190
+ isString(element.content) &&
191
+ (isUndefined(element.width) ||
192
+ isNumber(element.width)) &&
193
+ (isUndefined(element.height) ||
194
+ isNumber(element.height)) &&
195
+ (isUndefined(element.linebreak) ||
196
+ isBoolean(element.linebreak)) &&
197
+ (isUndefined(element.align) ||
198
+ isOneOf(element.align, "left", "center", "right")) &&
199
+ (isUndefined(element.color) ||
200
+ isString(element.color)) &&
201
+ (isUndefined(element.borderColor) ||
202
+ isString(element.borderColor)) &&
203
+ (isUndefined(element.font) ||
204
+ isString(element.font))) {
205
+ continue;
206
+ }
207
+ else
208
+ return false;
209
+ }
210
+ default:
211
+ return false;
212
+ }
213
+ }
214
+ }
215
+ return true;
216
+ }
217
+ else
218
+ return false;
219
+ }
220
+ static primaryTheme = null;
221
+ static loadTheme(path) {
222
+ const theme = this.getTheme(path);
223
+ if (theme) {
224
+ this.primaryTheme = theme;
225
+ return true;
226
+ }
227
+ else
228
+ return false;
229
+ }
230
+ static getTheme(path) {
231
+ if (!fs_1.default.existsSync(upath_1.default.join(this.assetsPath, path, "manifest.json"))) {
232
+ path = this.themes[path] ?? "";
233
+ }
234
+ else
235
+ path = upath_1.default.join(this.assetsPath, path);
236
+ if (fs_1.default.existsSync(upath_1.default.join(path, "manifest.json"))) {
237
+ const manifest = JSON.parse(fs_1.default.readFileSync(upath_1.default.join(path, "manifest.json"), "utf-8"));
238
+ if (this.validateManifest(manifest, path)) {
239
+ return { manifest, path };
240
+ }
241
+ }
242
+ return null;
243
+ }
244
+ static getThemeFile(path, themePath) {
245
+ if (typeof path == "string" &&
246
+ fs_1.default.existsSync(upath_1.default.join(themePath ?? this.primaryTheme?.path, path)))
247
+ return fs_1.default.readFileSync(upath_1.default.join(themePath ?? this.primaryTheme?.path, path));
248
+ else
249
+ return Buffer.from([]);
250
+ }
251
+ /* Begin Draw Tools*/
252
+ static findMaxFitString(ctx, original, maxWidth, lineBreakSuffix = "...") {
253
+ const metrics = ctx.measureText(original);
254
+ if (metrics.width <= maxWidth)
255
+ return original;
256
+ for (let i = 1; i < original.length; ++i) {
257
+ let cur = original.slice(0, original.length - i);
258
+ if (ctx.measureText(cur + lineBreakSuffix).width <= maxWidth) {
259
+ while (cur[cur.length - 1] == " ") {
260
+ cur = cur.substring(0, cur.length - 1);
261
+ }
262
+ return cur.trim() + lineBreakSuffix;
263
+ }
264
+ }
265
+ return original;
266
+ }
267
+ static drawText(ctx, str, x, y, fontSize,
268
+ /**
269
+ * Line width of the text stroke.
270
+ */
271
+ linewidth,
272
+ /**
273
+ * Max width of the text block.
274
+ */
275
+ maxWidth, textAlign = "left", mainColor = "white", borderColor = "black", font = `"standard-font-title-latin", "standard-font-title-jp"`, lineBreakSuffix = "...") {
276
+ ctx.font = `${fontSize}px ${font}`;
277
+ str = this.findMaxFitString(ctx, str, maxWidth, lineBreakSuffix);
278
+ if (linewidth > 0) {
279
+ ctx.strokeStyle = borderColor;
280
+ ctx.lineWidth = linewidth;
281
+ ctx.lineCap = "round";
282
+ ctx.lineJoin = "round";
283
+ ctx.textAlign = textAlign;
284
+ ctx.strokeText(str, x, y);
285
+ }
286
+ ctx.fillStyle = mainColor;
287
+ ctx.font = `${fontSize}px ${font}`;
288
+ ctx.textAlign = textAlign;
289
+ ctx.fillText(str, x, y);
290
+ if (linewidth > 0) {
291
+ ctx.strokeStyle = mainColor;
292
+ ctx.lineWidth = linewidth / 8;
293
+ ctx.lineCap = "round";
294
+ ctx.lineJoin = "round";
295
+ ctx.font = `${fontSize}px ${font}`;
296
+ ctx.textAlign = textAlign;
297
+ ctx.strokeText(str, x, y);
298
+ }
299
+ }
300
+ static async drawImageModule(ctx, theme, element) {
301
+ const img = new canvas_1.Image();
302
+ img.src = this.getThemeFile(element.path, theme.path);
303
+ ctx.drawImage(img, element.x, element.y, element.width, element.height);
304
+ }
305
+ static async drawScoreGridModule(ctx, theme, element, score, index, x, y) {
306
+ let curColor = "#FFFFFF";
307
+ switch (score.chart.difficulty) {
308
+ case type_1.EDifficulty.BASIC:
309
+ curColor = element.scoreBubble.color.basic;
310
+ break;
311
+ case type_1.EDifficulty.ADVANCED:
312
+ curColor = element.scoreBubble.color.advanced;
313
+ break;
314
+ case type_1.EDifficulty.EXPERT:
315
+ curColor = element.scoreBubble.color.expert;
316
+ break;
317
+ case type_1.EDifficulty.MASTER:
318
+ curColor = element.scoreBubble.color.master;
319
+ break;
320
+ case type_1.EDifficulty.ULTIMA:
321
+ curColor = element.scoreBubble.color.ultima;
322
+ break;
323
+ case type_1.EDifficulty.WORLDS_END:
324
+ curColor = element.scoreBubble.color.worldsEnd;
325
+ break;
326
+ }
327
+ /** Begin Card Draw */
328
+ ctx.save();
329
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
330
+ ctx.beginPath();
331
+ ctx.roundRect(x, y, element.scoreBubble.width, element.scoreBubble.height, (element.scoreBubble.height * 0.806) / 7);
332
+ ctx.strokeStyle = new color_1.default(curColor).darken(0.3).hexa();
333
+ ctx.lineWidth = element.scoreBubble.margin / 4;
334
+ ctx.stroke();
335
+ ctx.fill();
336
+ ctx.beginPath();
337
+ ctx.roundRect(x, y, element.scoreBubble.width, element.scoreBubble.height, (element.scoreBubble.height * 0.806) / 7);
338
+ ctx.clip();
339
+ /** Begin Main Content Draw */
340
+ {
341
+ ctx.save();
342
+ ctx.beginPath();
343
+ ctx.roundRect(x, y, element.scoreBubble.width, element.scoreBubble.height * 0.742, (element.scoreBubble.height * 0.806) / 7);
344
+ ctx.clip();
345
+ ctx.fillStyle = curColor;
346
+ ctx.fill();
347
+ const jacketSize = Math.min(element.scoreBubble.width, element.scoreBubble.height * 0.742);
348
+ const jacketMaskGrad = ctx.createLinearGradient(x + jacketSize / 2, y + jacketSize / 2, x + jacketSize, y + jacketSize / 2);
349
+ jacketMaskGrad.addColorStop(0, new color_1.default(curColor).alpha(0).hexa());
350
+ jacketMaskGrad.addColorStop(0.25, new color_1.default(curColor).alpha(0.2).hexa());
351
+ jacketMaskGrad.addColorStop(1, new color_1.default(curColor).alpha(1).hexa());
352
+ const jacketMaskGradDark = ctx.createLinearGradient(x + jacketSize / 2, y + jacketSize / 2, x + jacketSize, y + jacketSize / 2);
353
+ jacketMaskGradDark.addColorStop(0, new color_1.default(curColor).darken(0.3).alpha(0).hexa());
354
+ jacketMaskGradDark.addColorStop(0.25, new color_1.default(curColor).darken(0.3).alpha(0.2).hexa());
355
+ jacketMaskGradDark.addColorStop(1, new color_1.default(curColor).darken(0.3).alpha(1).hexa());
356
+ /** Begin Jacket Draw*/
357
+ let jacket = await chart_1.Chart.Database.fecthJacket(score.chart.id);
358
+ if (!jacket)
359
+ jacket = await chart_1.Chart.Database.fecthJacket(0);
360
+ if (jacket) {
361
+ const img = new canvas_1.Image();
362
+ img.src = jacket;
363
+ ctx.drawImage(img, x, y, jacketSize, jacketSize);
364
+ }
365
+ else {
366
+ ctx.fillStyle = "#b6ffab";
367
+ ctx.fillRect(x, y, jacketSize, jacketSize);
368
+ }
369
+ /** End Jacket Draw*/
370
+ /** Begin Jacket Gradient Mask Draw*/ {
371
+ ctx.fillStyle = jacketMaskGrad;
372
+ ctx.fillRect(x + jacketSize / 2, y, (jacketSize * 3) / 4, jacketSize);
373
+ } /** End Jacket Gradient Mask Draw*/
374
+ ctx.beginPath();
375
+ ctx.roundRect(x + element.scoreBubble.margin, y + element.scoreBubble.margin, element.scoreBubble.width - element.scoreBubble.margin * 2, element.scoreBubble.height * 0.806 -
376
+ element.scoreBubble.margin * 2, (element.scoreBubble.height * 0.806 -
377
+ element.scoreBubble.margin * 2) /
378
+ 7);
379
+ /** Begin Title Draw */ {
380
+ this.drawText(ctx, score.chart.name, x + (jacketSize * 7) / 8, y +
381
+ element.scoreBubble.margin +
382
+ element.scoreBubble.height * 0.806 * 0.144, element.scoreBubble.height * 0.806 * 0.144, element.scoreBubble.height * 0.806 * 0.04, element.scoreBubble.width -
383
+ (jacketSize * 7) / 8 -
384
+ element.scoreBubble.margin, "left", "white", jacketMaskGradDark);
385
+ } /** End Title Draw */
386
+ /** Begin Separation Line Draw */ {
387
+ ctx.beginPath();
388
+ ctx.roundRect(x + (jacketSize * 13) / 16, y +
389
+ element.scoreBubble.margin +
390
+ element.scoreBubble.height * 0.806 * (0.144 + 0.072), element.scoreBubble.width -
391
+ (jacketSize * 13) / 16 -
392
+ element.scoreBubble.margin * 2, element.scoreBubble.height * 0.806 * 0.02, element.scoreBubble.height * 0.806 * 0.16);
393
+ ctx.fillStyle = jacketMaskGradDark;
394
+ ctx.fill();
395
+ } /** End Separation Line Draw */
396
+ /** Begin Achievement Rate Draw */
397
+ this.drawText(ctx, score.score.toFixed(0), x -
398
+ element.scoreBubble.margin -
399
+ element.scoreBubble.height * 0.806 * 0.02 +
400
+ element.scoreBubble.width, y +
401
+ element.scoreBubble.margin +
402
+ element.scoreBubble.height *
403
+ 0.806 *
404
+ (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());
405
+ /** End Achievement Rate Draw */
406
+ /** Begin Achievement Rank Draw */
407
+ {
408
+ let rankImg;
409
+ switch (score.rank) {
410
+ case type_1.EAchievementTypes.D:
411
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.d, theme.path);
412
+ break;
413
+ case type_1.EAchievementTypes.C:
414
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.c, theme.path);
415
+ break;
416
+ case type_1.EAchievementTypes.B:
417
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.b, theme.path);
418
+ break;
419
+ case type_1.EAchievementTypes.BB:
420
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.bb, theme.path);
421
+ break;
422
+ case type_1.EAchievementTypes.BBB:
423
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.bbb, theme.path);
424
+ break;
425
+ case type_1.EAchievementTypes.A:
426
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.a, theme.path);
427
+ break;
428
+ case type_1.EAchievementTypes.AA:
429
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.aa, theme.path);
430
+ break;
431
+ case type_1.EAchievementTypes.AAA:
432
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.aaa, theme.path);
433
+ break;
434
+ case type_1.EAchievementTypes.S:
435
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.s, theme.path);
436
+ break;
437
+ case type_1.EAchievementTypes.SS:
438
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.ss, theme.path);
439
+ break;
440
+ case type_1.EAchievementTypes.SSP:
441
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.ssp, theme.path);
442
+ break;
443
+ case type_1.EAchievementTypes.SSS:
444
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sss, theme.path);
445
+ break;
446
+ default:
447
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sssp, theme.path);
448
+ }
449
+ const img = new canvas_1.Image();
450
+ img.src = rankImg;
451
+ ctx.drawImage(img, x + jacketSize * (13 / 16), y +
452
+ element.scoreBubble.margin +
453
+ element.scoreBubble.height *
454
+ 0.806 *
455
+ (0.144 + 0.144 - 0.01), element.scoreBubble.height * 0.806 * 0.2 * 3, element.scoreBubble.height * 0.806 * 0.2);
456
+ }
457
+ /** End Achievement Rank Draw */
458
+ /** Begin Milestone Draw */
459
+ {
460
+ let comboImg;
461
+ switch (score.combo) {
462
+ case type_1.EComboTypes.NONE:
463
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.none, theme.path);
464
+ break;
465
+ case type_1.EComboTypes.FULL_COMBO:
466
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.fc, theme.path);
467
+ break;
468
+ case type_1.EComboTypes.ALL_JUSTICE:
469
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.aj, theme.path);
470
+ break;
471
+ case type_1.EComboTypes.ALL_JUSTICE_CRITICAL:
472
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.ajc, theme.path);
473
+ break;
474
+ }
475
+ ctx.fillStyle = "#e8eaec";
476
+ ctx.roundRect(x -
477
+ element.scoreBubble.height * 0.806 * 0.32 * 3 -
478
+ element.scoreBubble.margin -
479
+ element.scoreBubble.height * 0.806 * 0.02 +
480
+ element.scoreBubble.width, y +
481
+ element.scoreBubble.margin +
482
+ element.scoreBubble.height *
483
+ 0.806 *
484
+ (0.144 + 0.144 + 0.208 + 0.1), element.scoreBubble.height * 0.806 * 0.32 * 3, (element.scoreBubble.height * 0.806 * 0.32 * 3) / 6.7, (element.scoreBubble.height * 0.806 * 0.32 * 3) / 56);
485
+ ctx.fill();
486
+ const combo = new canvas_1.Image();
487
+ combo.src = comboImg;
488
+ ctx.drawImage(combo, x -
489
+ element.scoreBubble.height * 0.806 * 0.32 * 3 -
490
+ element.scoreBubble.margin -
491
+ element.scoreBubble.height * 0.806 * 0.02 +
492
+ element.scoreBubble.width, y +
493
+ element.scoreBubble.margin +
494
+ element.scoreBubble.height *
495
+ 0.806 *
496
+ (0.144 + 0.144 + 0.208 + 0.1), element.scoreBubble.height * 0.806 * 0.32 * 3, (element.scoreBubble.height * 0.806 * 0.32 * 3) / 6.7);
497
+ }
498
+ /** End Milestone Draw */
499
+ /** Begin Bests Index Draw */
500
+ {
501
+ this.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());
502
+ }
503
+ /** End Bests Index Draw */
504
+ ctx.restore();
505
+ }
506
+ /** End Main Content Draw */
507
+ /** Begin Difficulty & Rating Draw */
508
+ {
509
+ this.drawText(ctx, `lv. ${score.chart.level.toFixed(1)}`, x + element.scoreBubble.margin * 2, y + element.scoreBubble.height * (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());
510
+ this.drawText(ctx, `+${score.rating.toFixed(2)}`, x + element.scoreBubble.width - element.scoreBubble.margin * 2, y + element.scoreBubble.height * (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());
511
+ }
512
+ /** End Difficulty & Rating Draw */
513
+ ctx.restore();
514
+ /** End Card Draw */
515
+ }
516
+ static async drawProfileModule(ctx, theme, element, username, rating, profilePicture) {
517
+ const nameplate = new canvas_1.Image();
518
+ nameplate.src = this.getThemeFile(theme.manifest.sprites.profile.nameplate, theme.path);
519
+ ctx.drawImage(nameplate, element.x, element.y, element.height * 2.526, element.height);
520
+ /* Begin Profile Picture Draw */
521
+ {
522
+ ctx.save();
523
+ ctx.beginPath();
524
+ ctx.roundRect(element.x + element.height * 2.0, element.y + element.height * 0.3, element.height * 0.45, element.height * 0.45, 0);
525
+ ctx.clip();
526
+ ctx.fillStyle = "white";
527
+ ctx.fill();
528
+ const icon = new canvas_1.Image();
529
+ try {
530
+ (0, sharp_1.default)(profilePicture);
531
+ }
532
+ catch {
533
+ // Unknown profile picture binary
534
+ profilePicture = undefined;
535
+ }
536
+ const pfp = profilePicture ||
537
+ this.getThemeFile(theme.manifest.sprites.profile.icon, theme.path);
538
+ const { dominant } = await (0, sharp_1.default)(pfp).stats();
539
+ icon.src = await (0, sharp_1.default)(pfp).png().toBuffer();
540
+ const cropSize = Math.min(icon.width, icon.height);
541
+ ctx.drawImage(icon, (icon.width - cropSize) / 2, (icon.height - cropSize) / 2, cropSize, cropSize, element.x + element.height * 2.0, element.y + element.height * 0.3, element.height * 0.45, element.height * 0.45);
542
+ if (profilePicture) {
543
+ ctx.beginPath();
544
+ ctx.roundRect(element.x + element.height * 2.0, element.y + element.height * 0.3, element.height * 0.45, element.height * 0.45, 0);
545
+ ctx.strokeStyle = color_1.default.rgb(dominant).darken(0.3).hex();
546
+ ctx.lineWidth = element.height / 128;
547
+ ctx.stroke();
548
+ }
549
+ ctx.restore();
550
+ }
551
+ /* End Profile Picture Draw */
552
+ /* Begin Username Draw */
553
+ {
554
+ ctx.beginPath();
555
+ ctx.roundRect(element.x + element.height * (21 / 32), element.y + element.height * 0.3, element.x + element.height * (65 / 64), element.height * 0.45, 0);
556
+ ctx.fillStyle = "rgba(255, 255, 255, 0.45)";
557
+ ctx.lineWidth = element.height / 32;
558
+ ctx.fill();
559
+ // const ratingImgBuffer = await this.getRatingNumber(rating, theme);
560
+ // if (ratingImgBuffer) {
561
+ // const { width, height } =
562
+ // await sharp(ratingImgBuffer).metadata();
563
+ // if (width && height) {
564
+ // const aspectRatio = width / height;
565
+ // const image = new Image();
566
+ // image.src = ratingImgBuffer;
567
+ // const drawHeight = (element.height * 7) / 32;
568
+ // ctx.drawImage(
569
+ // image,
570
+ // element.x + element.height * 2.0,
571
+ // element.y + element.height * 0.3,
572
+ // drawHeight * aspectRatio * 0.8,
573
+ // drawHeight
574
+ // );
575
+ // }
576
+ // }
577
+ this.drawText(ctx, "Lv.", element.x + element.height * (43 / 64), element.y + element.height * (0.3 + 1 / 4), (element.height * 1) / 16, 0, ((element.height / 3) * 5.108 * 3.1) / 5, "left", "black", "black", "standard-font-username");
578
+ this.drawText(ctx, "99", element.x + element.height * (49 / 64), element.y + element.height * (0.3 + 1 / 4), (element.height * 1) / 11, 0, ((element.height / 3) * 5.108 * 3.1) / 5, "left", "black", "black", "standard-font-username");
579
+ this.drawText(ctx, HalfFullWidthConvert.toFullWidth(username), element.x + element.height * (56 / 64), element.y + element.height * (0.3 + 1 / 4), (element.height * 1) / 6, 0, element.height * (65 / 64), "left", "black", "black", "standard-font-username");
580
+ this.drawText(ctx, "RATING", element.x + element.height * (43 / 64), element.y + element.height * (47 / 64), (element.height * 4) / 44, 0, ((element.height / 3) * 5.108 * 3.1) / 5, "left", "black", "black", "standard-font-username");
581
+ this.drawText(ctx, rating.toFixed(2), element.x + element.height * (67 / 64), element.y + element.height * (47 / 64), (element.height * 3) / 22, 0, ((element.height / 3) * 5.108 * 3.1) / 5, "left", "black", "black", "standard-font-username");
582
+ }
583
+ /* End Username Draw*/
584
+ }
585
+ static async drawTextModule(ctx, theme, element, variables = {}) {
586
+ let naiveLines = (0, string_template_1.default)(element.content, variables).split("\n");
587
+ let lines = [];
588
+ if (element.linebreak) {
589
+ for (let originalContent of naiveLines) {
590
+ while (originalContent.length) {
591
+ const line = this.findMaxFitString(ctx, originalContent, element.width || Infinity, "");
592
+ originalContent = originalContent.replace(line, "").trim();
593
+ lines.push(line.trim());
594
+ }
595
+ }
596
+ }
597
+ else {
598
+ for (const originalContent of naiveLines) {
599
+ lines.push(this.findMaxFitString(ctx, originalContent, element.width || Infinity));
600
+ }
601
+ }
602
+ for (let i = 0; i < lines.length; ++i) {
603
+ const line = lines[i];
604
+ this.drawText(ctx, line, element.x, element.y + i * element.size * 1.3, element.size, element.size / 3.5, element.width || Infinity, element.align, element.color || "#FFFFFF", element.borderColor
605
+ ? element.borderColor
606
+ : color_1.default.rgb(element.color || "#FFFFFF")
607
+ .darken(0.3)
608
+ .hex(), element.font, element.linebreak ? "" : "...");
609
+ }
610
+ }
611
+ /* End Draw Tools*/
612
+ static async draw(name, rating, newScores, oldScores, options) {
613
+ await chart_1.Chart.Database.cacheJackets([
614
+ ...newScores.map((v) => v.chart.id),
615
+ ...oldScores.map((v) => v.chart.id),
616
+ ]);
617
+ let currentTheme = this.primaryTheme;
618
+ if (options?.theme) {
619
+ const theme = this.getTheme(options.theme);
620
+ if (theme) {
621
+ currentTheme = theme;
622
+ }
623
+ }
624
+ if (currentTheme) {
625
+ const canvas = new canvas_1.Canvas(currentTheme.manifest.width * (options?.scale ?? 1), currentTheme.manifest.height * (options?.scale ?? 1));
626
+ const ctx = canvas.getContext("2d");
627
+ if (options?.scale)
628
+ ctx.scale(options.scale, options.scale);
629
+ ctx.imageSmoothingEnabled = true;
630
+ for (const element of currentTheme.manifest.elements) {
631
+ switch (element.type) {
632
+ case "image": {
633
+ await this.drawImageModule(ctx, currentTheme, element);
634
+ break;
635
+ }
636
+ case "score-grid": {
637
+ for (let y = element.y, index = element.index, i = 0; i < element.verticalSize; ++i,
638
+ y +=
639
+ element.scoreBubble.height +
640
+ element.scoreBubble.gap) {
641
+ for (let x = element.x, j = 0; j < element.horizontalSize; ++j,
642
+ ++index,
643
+ x +=
644
+ element.scoreBubble.width +
645
+ element.scoreBubble.gap) {
646
+ let curScore;
647
+ if (element.region == "new")
648
+ curScore = newScores[index];
649
+ else
650
+ curScore = oldScores[index];
651
+ if (curScore) {
652
+ await this.drawScoreGridModule(ctx, currentTheme, element, curScore, index, x, y);
653
+ }
654
+ }
655
+ }
656
+ break;
657
+ }
658
+ case "profile": {
659
+ await this.drawProfileModule(ctx, currentTheme, element, name, rating, options?.profilePicture);
660
+ break;
661
+ }
662
+ case "text": {
663
+ function getRatingBase(scores) {
664
+ return (scores
665
+ .map((v) => v.rating)
666
+ .sort((a, b) => a - b)[0] || 0);
667
+ }
668
+ function getRatingAvg(scores) {
669
+ if (scores.length <= 0)
670
+ return 0;
671
+ return (scores
672
+ .map((v) => v.rating)
673
+ .reduce((sum, v) => (sum += v)) /
674
+ scores.length);
675
+ }
676
+ await this.drawTextModule(ctx, currentTheme, element, {
677
+ username: HalfFullWidthConvert.toFullWidth(name),
678
+ rating: rating.toFixed(0),
679
+ newScoreRatingAvg: getRatingAvg(newScores).toFixed(2),
680
+ oldScoreRatingAvg: getRatingAvg(oldScores).toFixed(2),
681
+ newScoreRatingBase: getRatingBase(newScores).toFixed(2),
682
+ oldScoreRatingBase: getRatingBase(oldScores).toFixed(2),
683
+ });
684
+ break;
685
+ }
686
+ }
687
+ }
688
+ return canvas.toBuffer();
689
+ }
690
+ else
691
+ return null;
692
+ }
693
+ static async drawWithScoreSource(source, username, options) {
694
+ const profile = await source.getPlayerInfo(username);
695
+ const score = await source.getPlayerBest50(username);
696
+ if (!profile || !score)
697
+ return null;
698
+ return this.draw(profile.name, profile.rating, score.new, score.old, {
699
+ ...options,
700
+ profilePicture: options?.profilePicture === null
701
+ ? undefined
702
+ : options?.profilePicture ||
703
+ (await source.getPlayerProfilePicture(username)) ||
704
+ undefined,
705
+ });
706
+ }
707
+ static async getRatingNumber(num, theme) {
708
+ async function getRaingDigit(map, digit, unitWidth, unitHeight) {
709
+ digit = Math.floor(digit % 10);
710
+ return await (0, sharp_1.default)(map)
711
+ .extract({
712
+ left: (digit % 4) * unitWidth,
713
+ top: Math.floor(digit / 4) * unitHeight,
714
+ width: unitWidth,
715
+ height: unitHeight,
716
+ })
717
+ .toBuffer();
718
+ }
719
+ if (theme.manifest) {
720
+ const map = this.getThemeFile(theme.manifest.sprites.ratingNumberMap, theme.path);
721
+ const { width, height } = await (0, sharp_1.default)(map).metadata();
722
+ if (!(width && height))
723
+ return null;
724
+ const unitWidth = width / 4, unitHeight = height / 4;
725
+ let digits = [];
726
+ while (num != Math.floor(num)) {
727
+ num *= 10;
728
+ }
729
+ while (num > 0) {
730
+ digits.push(await getRaingDigit(map, num % 10, unitWidth, unitHeight));
731
+ num = Math.floor(num / 10);
732
+ }
733
+ while (digits.length < 5)
734
+ digits.push(null);
735
+ digits = digits.reverse();
736
+ const canvas = new canvas_1.Canvas(unitWidth * digits.length, unitHeight);
737
+ const ctx = canvas.getContext("2d");
738
+ for (let i = 0; i < digits.length; ++i) {
739
+ const curDigit = digits[i];
740
+ if (!curDigit)
741
+ continue;
742
+ const img = new canvas_1.Image();
743
+ img.src = curDigit;
744
+ ctx.drawImage(img, unitWidth * i * 0.88, 0);
745
+ }
746
+ return canvas.toBuffer();
747
+ }
748
+ return null;
749
+ }
750
+ }
751
+ exports.Best50 = Best50;
752
+ //# sourceMappingURL=best50.js.map