maidraw 0.4.5 → 0.4.7

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