maidraw 0.7.0 → 0.7.1

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 (214) hide show
  1. package/assets/themes/maimai/chart/common/prism/jp/background.png +0 -0
  2. package/assets/themes/maimai/chart/common/versionless/logo/cn/200.png +0 -0
  3. package/assets/themes/maimai/chart/common/versionless/logo/cn/210.png +0 -0
  4. package/assets/themes/maimai/chart/common/versionless/logo/cn/220.png +0 -0
  5. package/assets/themes/maimai/chart/common/versionless/logo/cn/230.png +0 -0
  6. package/assets/themes/maimai/chart/common/versionless/logo/cn/240.png +0 -0
  7. package/assets/themes/maimai/chart/common/versionless/logo/intl/210.png +0 -0
  8. package/assets/themes/maimai/chart/common/versionless/logo/intl/215.png +0 -0
  9. package/assets/themes/maimai/chart/common/versionless/logo/jp/100.png +0 -0
  10. package/assets/themes/maimai/chart/common/versionless/logo/jp/110.png +0 -0
  11. package/assets/themes/maimai/chart/common/versionless/logo/jp/120.png +0 -0
  12. package/assets/themes/maimai/chart/common/versionless/logo/jp/130.png +0 -0
  13. package/assets/themes/maimai/chart/common/versionless/logo/jp/140.png +0 -0
  14. package/assets/themes/maimai/chart/common/versionless/logo/jp/150.png +0 -0
  15. package/assets/themes/maimai/chart/common/versionless/logo/jp/160.png +0 -0
  16. package/assets/themes/maimai/chart/common/versionless/logo/jp/170.png +0 -0
  17. package/assets/themes/maimai/chart/common/versionless/logo/jp/180.png +0 -0
  18. package/assets/themes/maimai/chart/common/versionless/logo/jp/185.png +0 -0
  19. package/assets/themes/maimai/chart/common/versionless/logo/jp/190.png +0 -0
  20. package/assets/themes/maimai/chart/common/versionless/logo/jp/195.png +0 -0
  21. package/assets/themes/maimai/chart/common/versionless/logo/jp/199.png +0 -0
  22. package/assets/themes/maimai/chart/common/versionless/logo/jp/200.png +0 -0
  23. package/assets/themes/maimai/chart/common/versionless/logo/jp/205.png +0 -0
  24. package/assets/themes/maimai/chart/common/versionless/logo/jp/210.png +0 -0
  25. package/assets/themes/maimai/chart/common/versionless/logo/jp/215.png +0 -0
  26. package/assets/themes/maimai/chart/common/versionless/logo/jp/220.png +0 -0
  27. package/assets/themes/maimai/chart/common/versionless/logo/jp/225.png +0 -0
  28. package/assets/themes/maimai/chart/common/versionless/logo/jp/230.png +0 -0
  29. package/assets/themes/maimai/chart/common/versionless/logo/jp/235.png +0 -0
  30. package/assets/themes/maimai/chart/common/versionless/logo/jp/240.png +0 -0
  31. package/assets/themes/maimai/chart/common/versionless/logo/jp/245.png +0 -0
  32. package/assets/themes/maimai/chart/common/versionless/logo/jp/250.png +0 -0
  33. package/assets/themes/maimai/chart/common/versionless/logo/jp/255.png +0 -0
  34. package/assets/themes/maimai/chart/jp/prism/manifest.json +167 -0
  35. package/dist/chu/bests/best50.d.ts +0 -2
  36. package/dist/chu/bests/best50.js +14 -61
  37. package/dist/chu/bests/best50.js.map +1 -1
  38. package/dist/chu/bests/type.d.ts +1 -1
  39. package/dist/geki/bests/best50.d.ts +0 -2
  40. package/dist/geki/bests/best50.js +14 -61
  41. package/dist/geki/bests/best50.js.map +1 -1
  42. package/dist/geki/bests/type.d.ts +1 -1
  43. package/dist/geki/chart/database.js +0 -1
  44. package/dist/geki/chart/database.js.map +1 -1
  45. package/dist/lib/util.d.ts +32 -0
  46. package/dist/lib/util.js +119 -0
  47. package/dist/lib/util.js.map +1 -0
  48. package/dist/mai/best50/index.d.ts +231 -38
  49. package/dist/mai/best50/index.js +115 -161
  50. package/dist/mai/best50/index.js.map +1 -1
  51. package/dist/mai/chart/index.d.ts +224 -0
  52. package/dist/mai/chart/index.js +1152 -1
  53. package/dist/mai/chart/index.js.map +1 -1
  54. package/dist/mai/{best50/lib → lib}/divingFish/index.d.ts +14 -6
  55. package/dist/mai/{best50/lib → lib}/divingFish/index.js +38 -27
  56. package/dist/mai/lib/divingFish/index.js.map +1 -0
  57. package/dist/mai/{best50/lib → lib}/index.d.ts +12 -4
  58. package/dist/mai/{best50/lib → lib}/index.js +2 -2
  59. package/dist/mai/lib/index.js.map +1 -0
  60. package/dist/mai/{best50/lib → lib}/kamaiTachi/index.d.ts +37 -29
  61. package/dist/mai/{best50/lib → lib}/kamaiTachi/index.js +74 -21
  62. package/dist/mai/lib/kamaiTachi/index.js.map +1 -0
  63. package/dist/mai/{best50/lib → lib}/lxns/index.d.ts +14 -6
  64. package/dist/mai/{best50/lib → lib}/lxns/index.js +37 -27
  65. package/dist/mai/lib/lxns/index.js.map +1 -0
  66. package/dist/mai/type.d.ts +0 -51
  67. package/dist/mai/type.js +1 -35
  68. package/dist/mai/type.js.map +1 -1
  69. package/package.json +5 -2
  70. package/dist/mai/best50/lib/divingFish/index.js.map +0 -1
  71. package/dist/mai/best50/lib/index.js.map +0 -1
  72. package/dist/mai/best50/lib/kamaiTachi/index.js.map +0 -1
  73. package/dist/mai/best50/lib/lxns/index.js.map +0 -1
  74. package/dist/mai/best50/type.d.ts +0 -145
  75. package/dist/mai/best50/type.js +0 -3
  76. package/dist/mai/best50/type.js.map +0 -1
  77. /package/assets/themes/maimai/{cn → best50/cn}/2024Landscape/manifest.json +0 -0
  78. /package/assets/themes/maimai/{cn → best50/cn}/2024Portrait/manifest.json +0 -0
  79. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/a.png +0 -0
  80. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/aa.png +0 -0
  81. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/aaa.png +0 -0
  82. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/b.png +0 -0
  83. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/bb.png +0 -0
  84. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/bbb.png +0 -0
  85. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/c.png +0 -0
  86. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/d.png +0 -0
  87. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/s.png +0 -0
  88. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/sp.png +0 -0
  89. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/ss.png +0 -0
  90. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/ssp.png +0 -0
  91. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/sss.png +0 -0
  92. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/sssp.png +0 -0
  93. /package/assets/themes/maimai/{common → best50/common}/buddies/cn/logo.png +0 -0
  94. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/background.png +0 -0
  95. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/chara.png +0 -0
  96. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/laundry.png +0 -0
  97. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/logo.png +0 -0
  98. /package/assets/themes/maimai/{common → best50/common}/buddies/plus/chara.png +0 -0
  99. /package/assets/themes/maimai/{common → best50/common}/buddies/plus/laundry.png +0 -0
  100. /package/assets/themes/maimai/{common → best50/common}/buddies/plus/logo.png +0 -0
  101. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/a.png +0 -0
  102. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/aa.png +0 -0
  103. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/aaa.png +0 -0
  104. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/b.png +0 -0
  105. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/c.png +0 -0
  106. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/d.png +0 -0
  107. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/e.png +0 -0
  108. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/f.png +0 -0
  109. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/s.png +0 -0
  110. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/sp.png +0 -0
  111. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/ss.png +0 -0
  112. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/ssp.png +0 -0
  113. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/sss.png +0 -0
  114. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/sssp.png +0 -0
  115. /package/assets/themes/maimai/{common → best50/common}/finale/background.png +0 -0
  116. /package/assets/themes/maimai/{common → best50/common}/finale/background2.png +0 -0
  117. /package/assets/themes/maimai/{common → best50/common}/finale/logo.png +0 -0
  118. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/ap.png +0 -0
  119. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/app.png +0 -0
  120. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fc.png +0 -0
  121. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fcp.png +0 -0
  122. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fdx.png +0 -0
  123. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fdxp.png +0 -0
  124. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fsp.png +0 -0
  125. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/sync.png +0 -0
  126. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/a.png +0 -0
  127. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/aa.png +0 -0
  128. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/aaa.png +0 -0
  129. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/b.png +0 -0
  130. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/bb.png +0 -0
  131. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/bbb.png +0 -0
  132. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/c.png +0 -0
  133. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/d.png +0 -0
  134. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/s.png +0 -0
  135. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/sp.png +0 -0
  136. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/ss.png +0 -0
  137. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/ssp.png +0 -0
  138. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/sss.png +0 -0
  139. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/sssp.png +0 -0
  140. /package/assets/themes/maimai/{common → best50/common}/prism/jp/background.png +0 -0
  141. /package/assets/themes/maimai/{common → best50/common}/prism/jp/chara.png +0 -0
  142. /package/assets/themes/maimai/{common → best50/common}/prism/jp/laundry.png +0 -0
  143. /package/assets/themes/maimai/{common → best50/common}/prism/jp/logo.png +0 -0
  144. /package/assets/themes/maimai/{common → best50/common}/prism/plus/chara.png +0 -0
  145. /package/assets/themes/maimai/{common → best50/common}/prism/plus/laundry.png +0 -0
  146. /package/assets/themes/maimai/{common → best50/common}/prism/plus/logo.png +0 -0
  147. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/blue.png +0 -0
  148. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/bronze.png +0 -0
  149. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/gold.png +0 -0
  150. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/green.png +0 -0
  151. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/platinum.png +0 -0
  152. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/purple.png +0 -0
  153. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/rainbow.png +0 -0
  154. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/red.png +0 -0
  155. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/silver.png +0 -0
  156. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/white.png +0 -0
  157. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/yellow.png +0 -0
  158. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/blue.png +0 -0
  159. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/bronze.png +0 -0
  160. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/gold.png +0 -0
  161. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/green.png +0 -0
  162. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/platinum.png +0 -0
  163. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/purple.png +0 -0
  164. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/rainbow.png +0 -0
  165. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/red.png +0 -0
  166. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/silver.png +0 -0
  167. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/white.png +0 -0
  168. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/yellow.png +0 -0
  169. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/numberMap.png +0 -0
  170. /package/assets/themes/maimai/{common → best50/common}/versionless/icon.png +0 -0
  171. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/ap.png +0 -0
  172. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/app.png +0 -0
  173. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fc.png +0 -0
  174. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fcp.png +0 -0
  175. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fdx.png +0 -0
  176. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fdxp.png +0 -0
  177. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fs.png +0 -0
  178. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fsp.png +0 -0
  179. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/none.png +0 -0
  180. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/sync.png +0 -0
  181. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/cn/dx.png +0 -0
  182. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/cn/standard.png +0 -0
  183. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/jp/dx.png +0 -0
  184. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/jp/standard.png +0 -0
  185. /package/assets/themes/maimai/{common → best50/common}/versionless/nameplate.png +0 -0
  186. /package/assets/themes/maimai/{common → best50/common}/versionless/void.png +0 -0
  187. /package/assets/themes/maimai/{jp → best50/jp}/buddiesLandscape/manifest.json +0 -0
  188. /package/assets/themes/maimai/{jp → best50/jp}/buddiesPlusLandscape/manifest.json +0 -0
  189. /package/assets/themes/maimai/{jp → best50/jp}/buddiesPlusPortrait/manifest.json +0 -0
  190. /package/assets/themes/maimai/{jp → best50/jp}/buddiesPortrait/manifest.json +0 -0
  191. /package/assets/themes/maimai/{jp → best50/jp}/finaleLandscape/manifest.json +0 -0
  192. /package/assets/themes/maimai/{jp → best50/jp}/finalePortrait/manifest.json +0 -0
  193. /package/assets/themes/maimai/{jp → best50/jp}/prismLandscape/manifest.json +0 -0
  194. /package/assets/themes/maimai/{jp → best50/jp}/prismPlusLandscape/manifest.json +0 -0
  195. /package/assets/themes/maimai/{jp → best50/jp}/prismPlusPortrait/manifest.json +0 -0
  196. /package/assets/themes/maimai/{jp → best50/jp}/prismPortrait/manifest.json +0 -0
  197. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/a.png +0 -0
  198. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/aa.png +0 -0
  199. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/aaa.png +0 -0
  200. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/b.png +0 -0
  201. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/bb.png +0 -0
  202. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/bbb.png +0 -0
  203. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/c.png +0 -0
  204. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/d.png +0 -0
  205. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/s.png +0 -0
  206. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/sp.png +0 -0
  207. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/ss.png +0 -0
  208. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/ssp.png +0 -0
  209. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/sss.png +0 -0
  210. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/sssp.png +0 -0
  211. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/background.png +0 -0
  212. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/chara.png +0 -0
  213. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/logo.png +0 -0
  214. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/manifest.json +0 -0
@@ -22,10 +22,1161 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
29
  exports.Chart = void 0;
30
+ const sharp_1 = __importDefault(require("sharp"));
31
+ const type_1 = require("../type");
27
32
  const database = __importStar(require("./database"));
28
- var Chart;
33
+ const util_1 = require("../../lib/util");
34
+ const canvas_1 = require("canvas");
35
+ const color_1 = __importDefault(require("color"));
36
+ const glob_1 = require("glob");
37
+ const upath_1 = __importDefault(require("upath"));
38
+ const fs_1 = __importDefault(require("fs"));
39
+ const best50_1 = require("../best50");
40
+ const string_template_1 = __importDefault(require("string-template"));
41
+ const lodash_1 = __importDefault(require("lodash"));
42
+ class Chart {
43
+ static DEFAULT_THEME = "jp-prism";
44
+ static primaryTheme = null;
45
+ static get assetsPath() {
46
+ return upath_1.default.join(__dirname, "..", "..", "..", "assets");
47
+ }
48
+ static themes = {};
49
+ static hasTheme(name) {
50
+ return !!this.themes[name];
51
+ }
52
+ static {
53
+ const manifests = (0, glob_1.globSync)(upath_1.default.join(this.assetsPath, "themes", "maimai", "**", "manifest.json"));
54
+ for (const manifestPath of manifests) {
55
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, "utf-8"));
56
+ if (this.validateManifest(manifest, upath_1.default.dirname(manifestPath))) {
57
+ this.themes[manifest.name] = upath_1.default.dirname(manifestPath);
58
+ }
59
+ }
60
+ const loadThemeResult = this.loadTheme(this.DEFAULT_THEME);
61
+ if (!loadThemeResult) {
62
+ console.error("Failed to load default theme.");
63
+ }
64
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "gen-jyuu-gothic", "GenJyuuGothic-Bold.ttf"), {
65
+ family: "standard-font-title-jp",
66
+ });
67
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "comfortaa", "Comfortaa-Bold.ttf"), {
68
+ family: "standard-font-title-latin",
69
+ weight: "regular",
70
+ });
71
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "seurat-db", "FOT-Seurat Pro DB.otf"), {
72
+ family: "standard-font-username",
73
+ weight: "regular",
74
+ });
75
+ }
76
+ static validateManifest(payload, path) {
77
+ function isFileExist(p) {
78
+ if (isString(p))
79
+ return fs_1.default.existsSync(upath_1.default.join(path, p));
80
+ else
81
+ return false;
82
+ }
83
+ function isOneOf(p, ...args) {
84
+ return args.includes(p);
85
+ }
86
+ function isString(p) {
87
+ return typeof p == "string";
88
+ }
89
+ function isNumber(p) {
90
+ return typeof p == "number";
91
+ }
92
+ function isArray(p) {
93
+ return Array.isArray(p);
94
+ }
95
+ function isObject(p) {
96
+ return typeof p == "object";
97
+ }
98
+ function isHexColor(p) {
99
+ if (isString(p))
100
+ return /^(?:#[0-9A-F]{6}|#[0-9A-F]{8})$/i.test(p);
101
+ else
102
+ return false;
103
+ }
104
+ function isUndefined(p) {
105
+ return typeof p == "undefined";
106
+ }
107
+ function isBoolean(p) {
108
+ return typeof p == "boolean";
109
+ }
110
+ if (isString(payload.displayName) &&
111
+ isString(payload.name) &&
112
+ isNumber(payload.width) &&
113
+ isNumber(payload.height) &&
114
+ isObject(payload.sprites) &&
115
+ isObject(payload.sprites.achievement) &&
116
+ isFileExist(payload.sprites.achievement.d) &&
117
+ isFileExist(payload.sprites.achievement.c) &&
118
+ isFileExist(payload.sprites.achievement.b) &&
119
+ isFileExist(payload.sprites.achievement.bb) &&
120
+ isFileExist(payload.sprites.achievement.bbb) &&
121
+ isFileExist(payload.sprites.achievement.a) &&
122
+ isFileExist(payload.sprites.achievement.aa) &&
123
+ isFileExist(payload.sprites.achievement.aaa) &&
124
+ isFileExist(payload.sprites.achievement.s) &&
125
+ isFileExist(payload.sprites.achievement.sp) &&
126
+ isFileExist(payload.sprites.achievement.ss) &&
127
+ isFileExist(payload.sprites.achievement.ssp) &&
128
+ isFileExist(payload.sprites.achievement.sss) &&
129
+ isFileExist(payload.sprites.achievement.sssp) &&
130
+ isObject(payload.sprites.mode) &&
131
+ isFileExist(payload.sprites.mode.standard) &&
132
+ isFileExist(payload.sprites.mode.dx) &&
133
+ isObject(payload.sprites.milestone) &&
134
+ isFileExist(payload.sprites.milestone.ap) &&
135
+ isFileExist(payload.sprites.milestone.app) &&
136
+ isFileExist(payload.sprites.milestone.fc) &&
137
+ isFileExist(payload.sprites.milestone.fcp) &&
138
+ isFileExist(payload.sprites.milestone.fdx) &&
139
+ isFileExist(payload.sprites.milestone.fdxp) &&
140
+ isFileExist(payload.sprites.milestone.fs) &&
141
+ isFileExist(payload.sprites.milestone.fsp) &&
142
+ isFileExist(payload.sprites.milestone.sync) &&
143
+ isFileExist(payload.sprites.milestone.none) &&
144
+ isObject(payload.sprites.dxRating) &&
145
+ isFileExist(payload.sprites.dxRating.white) &&
146
+ isFileExist(payload.sprites.dxRating.blue) &&
147
+ isFileExist(payload.sprites.dxRating.green) &&
148
+ isFileExist(payload.sprites.dxRating.yellow) &&
149
+ isFileExist(payload.sprites.dxRating.red) &&
150
+ isFileExist(payload.sprites.dxRating.purple) &&
151
+ isFileExist(payload.sprites.dxRating.bronze) &&
152
+ isFileExist(payload.sprites.dxRating.silver) &&
153
+ isFileExist(payload.sprites.dxRating.gold) &&
154
+ isFileExist(payload.sprites.dxRating.platinum) &&
155
+ isFileExist(payload.sprites.dxRating.rainbow) &&
156
+ isObject(payload.sprites.profile) &&
157
+ isFileExist(payload.sprites.profile.icon) &&
158
+ isFileExist(payload.sprites.profile.nameplate) &&
159
+ isFileExist(payload.sprites.dxRatingNumberMap) &&
160
+ isObject(payload.sprites.versions) &&
161
+ isObject(payload.sprites.versions.OLD) &&
162
+ isFileExist(payload.sprites.versions.OLD[0]) &&
163
+ isFileExist(payload.sprites.versions.OLD[10]) &&
164
+ isFileExist(payload.sprites.versions.OLD[20]) &&
165
+ isFileExist(payload.sprites.versions.OLD[30]) &&
166
+ isFileExist(payload.sprites.versions.OLD[40]) &&
167
+ isFileExist(payload.sprites.versions.OLD[50]) &&
168
+ isFileExist(payload.sprites.versions.OLD[60]) &&
169
+ isFileExist(payload.sprites.versions.OLD[70]) &&
170
+ isFileExist(payload.sprites.versions.OLD[80]) &&
171
+ isFileExist(payload.sprites.versions.OLD[85]) &&
172
+ isFileExist(payload.sprites.versions.OLD[90]) &&
173
+ isFileExist(payload.sprites.versions.OLD[95]) &&
174
+ isFileExist(payload.sprites.versions.OLD[99]) &&
175
+ isObject(payload.sprites.versions.DX) &&
176
+ isFileExist(payload.sprites.versions.DX[0]) &&
177
+ isFileExist(payload.sprites.versions.DX[5]) &&
178
+ isFileExist(payload.sprites.versions.DX[10]) &&
179
+ isFileExist(payload.sprites.versions.DX[15]) &&
180
+ isFileExist(payload.sprites.versions.DX[20]) &&
181
+ isFileExist(payload.sprites.versions.DX[25]) &&
182
+ isFileExist(payload.sprites.versions.DX[30]) &&
183
+ isFileExist(payload.sprites.versions.DX[35]) &&
184
+ isFileExist(payload.sprites.versions.DX[40]) &&
185
+ isFileExist(payload.sprites.versions.DX[45]) &&
186
+ isFileExist(payload.sprites.versions.DX[50]) &&
187
+ isFileExist(payload.sprites.versions.DX[55]) &&
188
+ isObject(payload.sprites.versions.EX) &&
189
+ isFileExist(payload.sprites.versions.DX[10]) &&
190
+ isFileExist(payload.sprites.versions.DX[15]) &&
191
+ isObject(payload.sprites.versions.CN) &&
192
+ isFileExist(payload.sprites.versions.DX[0]) &&
193
+ isFileExist(payload.sprites.versions.DX[10]) &&
194
+ isFileExist(payload.sprites.versions.DX[20]) &&
195
+ isFileExist(payload.sprites.versions.DX[30]) &&
196
+ isFileExist(payload.sprites.versions.DX[40]) &&
197
+ isArray(payload.elements)) {
198
+ for (const element of payload.elements) {
199
+ if (isNumber(element.x) && isNumber(element.y)) {
200
+ switch (element.type) {
201
+ case "image": {
202
+ if (isNumber(element.width) &&
203
+ isNumber(element.height) &&
204
+ isFileExist(element.path)) {
205
+ continue;
206
+ }
207
+ else
208
+ return false;
209
+ }
210
+ case "chart-grid": {
211
+ if (isNumber(element.width) &&
212
+ isNumber(element.height) &&
213
+ isNumber(element.margin) &&
214
+ isNumber(element.gap) &&
215
+ isObject(element.bubble) &&
216
+ isHexColor(element.bubble.color.basic) &&
217
+ isHexColor(element.bubble.color.advanced) &&
218
+ isHexColor(element.bubble.color.expert) &&
219
+ isHexColor(element.bubble.color.master) &&
220
+ isHexColor(element.bubble.color.remaster) &&
221
+ isHexColor(element.bubble.color.utage) &&
222
+ isObject(element.color) &&
223
+ isHexColor(element.color.card)) {
224
+ continue;
225
+ }
226
+ else
227
+ return false;
228
+ }
229
+ case "profile": {
230
+ if (isNumber(element.height)) {
231
+ continue;
232
+ }
233
+ else
234
+ return false;
235
+ }
236
+ case "detail-info": {
237
+ if (isNumber(element.width) &&
238
+ isNumber(element.height) &&
239
+ isNumber(element.margin) &&
240
+ isObject(element.color) &&
241
+ isHexColor(element.color.card) &&
242
+ (isUndefined(element.font) ||
243
+ isString(element.font))) {
244
+ continue;
245
+ }
246
+ else
247
+ return false;
248
+ }
249
+ case "text": {
250
+ if (isNumber(element.size) &&
251
+ isString(element.content) &&
252
+ (isUndefined(element.width) ||
253
+ isNumber(element.width)) &&
254
+ (isUndefined(element.height) ||
255
+ isNumber(element.height)) &&
256
+ (isUndefined(element.linebreak) ||
257
+ isBoolean(element.linebreak)) &&
258
+ (isUndefined(element.align) ||
259
+ isOneOf(element.align, "left", "center", "right")) &&
260
+ (isUndefined(element.color) ||
261
+ isString(element.color)) &&
262
+ (isUndefined(element.borderColor) ||
263
+ isString(element.borderColor)) &&
264
+ (isUndefined(element.font) ||
265
+ isString(element.font))) {
266
+ continue;
267
+ }
268
+ else
269
+ return false;
270
+ }
271
+ default:
272
+ return false;
273
+ }
274
+ }
275
+ }
276
+ return true;
277
+ }
278
+ else
279
+ return false;
280
+ }
281
+ static loadTheme(path) {
282
+ const theme = this.getTheme(path);
283
+ if (theme) {
284
+ this.primaryTheme = theme;
285
+ return true;
286
+ }
287
+ else
288
+ return false;
289
+ }
290
+ static getTheme(path) {
291
+ if (!fs_1.default.existsSync(upath_1.default.join(path, "manifest.json"))) {
292
+ path = this.themes[path] ?? "";
293
+ }
294
+ else
295
+ path = upath_1.default.join(this.assetsPath, path);
296
+ if (fs_1.default.existsSync(upath_1.default.join(path, "manifest.json"))) {
297
+ const manifest = JSON.parse(fs_1.default.readFileSync(upath_1.default.join(path, "manifest.json"), "utf-8"));
298
+ if (this.validateManifest(manifest, path)) {
299
+ return { manifest, path };
300
+ }
301
+ }
302
+ return null;
303
+ }
304
+ static getThemeFile(path, themePath) {
305
+ if (typeof path == "string" &&
306
+ fs_1.default.existsSync(upath_1.default.join(themePath ?? this.primaryTheme?.path ?? "", path)))
307
+ return fs_1.default.readFileSync(upath_1.default.join(themePath ?? this.primaryTheme?.path, path));
308
+ else
309
+ return Buffer.from([]);
310
+ }
311
+ /* Begin Draw Tools*/
312
+ static async drawImageModule(ctx, theme, element) {
313
+ const img = new canvas_1.Image();
314
+ img.src = this.getThemeFile(element.path, theme.path);
315
+ ctx.drawImage(img, element.x, element.y, element.width, element.height);
316
+ }
317
+ static async drawChartGridCard(ctx, theme, element, chart, difficulty, x, y, width, height, isShort, score) {
318
+ let curColor = "#FFFFFF";
319
+ switch (difficulty) {
320
+ case type_1.EDifficulty.BASIC:
321
+ curColor = element.bubble.color.basic;
322
+ break;
323
+ case type_1.EDifficulty.ADVANCED:
324
+ curColor = element.bubble.color.advanced;
325
+ break;
326
+ case type_1.EDifficulty.EXPERT:
327
+ curColor = element.bubble.color.expert;
328
+ break;
329
+ case type_1.EDifficulty.MASTER:
330
+ curColor = element.bubble.color.master;
331
+ break;
332
+ case type_1.EDifficulty.REMASTER:
333
+ curColor = element.bubble.color.remaster;
334
+ break;
335
+ case type_1.EDifficulty.UTAGE:
336
+ curColor = element.bubble.color.utage;
337
+ break;
338
+ }
339
+ /** Begin Card Draw */
340
+ ctx.save();
341
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
342
+ ctx.beginPath();
343
+ ctx.roundRect(x, y, width, height, (height * 0.806) / 7);
344
+ ctx.strokeStyle = new color_1.default(curColor).darken(0.3).hexa();
345
+ ctx.lineWidth = element.bubble.margin / 4;
346
+ ctx.stroke();
347
+ ctx.fill();
348
+ ctx.beginPath();
349
+ ctx.roundRect(x, y, width, height, (height * 0.806) / 7);
350
+ ctx.clip();
351
+ /** Begin Main Content Draw */
352
+ {
353
+ ctx.save();
354
+ ctx.beginPath();
355
+ ctx.rect(x, y, width, height);
356
+ ctx.clip();
357
+ ctx.fillStyle = curColor;
358
+ ctx.fill();
359
+ const titleSize = height * (47 / 256);
360
+ /** Begin Difficulty Text & Separation Line Draw */
361
+ {
362
+ let difficultiy = "";
363
+ switch (chart.difficulty) {
364
+ case type_1.EDifficulty.BASIC:
365
+ difficultiy = "BASIC";
366
+ break;
367
+ case type_1.EDifficulty.ADVANCED:
368
+ difficultiy = "ADVANCED";
369
+ break;
370
+ case type_1.EDifficulty.EXPERT:
371
+ difficultiy = "EXPERT";
372
+ break;
373
+ case type_1.EDifficulty.MASTER:
374
+ difficultiy = "MASTER";
375
+ break;
376
+ case type_1.EDifficulty.REMASTER:
377
+ difficultiy = "Re:MASTER";
378
+ break;
379
+ case type_1.EDifficulty.UTAGE:
380
+ difficultiy = "U·TA·GE";
381
+ break;
382
+ }
383
+ const levelTextSize = titleSize * (5 / 8);
384
+ util_1.Util.drawText(ctx, difficultiy, x + element.bubble.margin, y +
385
+ element.bubble.margin +
386
+ titleSize -
387
+ element.bubble.margin * (1 / 4), titleSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
388
+ const difficultyTextWidth = util_1.Util.measureText(ctx, difficultiy, titleSize, Infinity).width;
389
+ util_1.Util.drawText(ctx, `Lv. ${chart.level.toFixed(1)}`, x + element.bubble.margin * 2 + difficultyTextWidth, y +
390
+ element.bubble.margin +
391
+ titleSize -
392
+ element.bubble.margin * (1 / 4), levelTextSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
393
+ ctx.beginPath();
394
+ ctx.roundRect(x + element.bubble.margin, y +
395
+ element.bubble.margin +
396
+ titleSize +
397
+ element.bubble.margin * (1 / 4), height * 2 - element.bubble.margin * 2, height * 0.806 * 0.02, height * 0.806 * 0.16);
398
+ ctx.fillStyle = new color_1.default(curColor).darken(0.3).hex();
399
+ ctx.fill();
400
+ }
401
+ /** End Difficulty Text & Separation Line Draw */
402
+ /** Begin Achievement Rate Draw */
403
+ {
404
+ const scoreSize = height * 0.806 * 0.208;
405
+ util_1.Util.drawText(ctx, score ? `${score.achievement.toFixed(4)}%` : "NO RECORD", x +
406
+ height * 2 -
407
+ element.bubble.margin -
408
+ height * 0.806 * 0.02, y +
409
+ element.bubble.margin +
410
+ titleSize +
411
+ element.bubble.margin * (5 / 8) +
412
+ scoreSize, scoreSize, height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
413
+ }
414
+ /** End Achievement Rate Draw */
415
+ /** Begin Achievement Rank Draw */
416
+ {
417
+ if (score) {
418
+ let rankImg;
419
+ switch (score.achievementRank) {
420
+ case best50_1.Best50.EAchievementTypes.D:
421
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.d, theme.path);
422
+ break;
423
+ case best50_1.Best50.EAchievementTypes.C:
424
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.c, theme.path);
425
+ break;
426
+ case best50_1.Best50.EAchievementTypes.B:
427
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.b, theme.path);
428
+ break;
429
+ case best50_1.Best50.EAchievementTypes.BB:
430
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.bb, theme.path);
431
+ break;
432
+ case best50_1.Best50.EAchievementTypes.BBB:
433
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.bbb, theme.path);
434
+ break;
435
+ case best50_1.Best50.EAchievementTypes.A:
436
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.a, theme.path);
437
+ break;
438
+ case best50_1.Best50.EAchievementTypes.AA:
439
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.aa, theme.path);
440
+ break;
441
+ case best50_1.Best50.EAchievementTypes.AAA:
442
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.aaa, theme.path);
443
+ break;
444
+ case best50_1.Best50.EAchievementTypes.S:
445
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.s, theme.path);
446
+ break;
447
+ case best50_1.Best50.EAchievementTypes.SP:
448
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sp, theme.path);
449
+ break;
450
+ case best50_1.Best50.EAchievementTypes.SS:
451
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.ss, theme.path);
452
+ break;
453
+ case best50_1.Best50.EAchievementTypes.SSP:
454
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.ssp, theme.path);
455
+ break;
456
+ case best50_1.Best50.EAchievementTypes.SSS:
457
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sss, theme.path);
458
+ break;
459
+ default:
460
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sssp, theme.path);
461
+ }
462
+ const img = new canvas_1.Image();
463
+ img.src = rankImg;
464
+ ctx.drawImage(img, x + element.bubble.margin * (1 / 4), y +
465
+ element.bubble.margin +
466
+ titleSize +
467
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.3 * 2.133, height * 0.806 * 0.3);
468
+ }
469
+ }
470
+ /** End Achievement Rank Draw */
471
+ /** Begin Milestone Draw */
472
+ {
473
+ if (score) {
474
+ let comboImg, syncImg;
475
+ switch (score.combo) {
476
+ case best50_1.Best50.EComboTypes.NONE:
477
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.none, theme.path);
478
+ break;
479
+ case best50_1.Best50.EComboTypes.FULL_COMBO:
480
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.fc, theme.path);
481
+ break;
482
+ case best50_1.Best50.EComboTypes.FULL_COMBO_PLUS:
483
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.fcp, theme.path);
484
+ break;
485
+ case best50_1.Best50.EComboTypes.ALL_PERFECT:
486
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.ap, theme.path);
487
+ break;
488
+ case best50_1.Best50.EComboTypes.ALL_PERFECT_PLUS:
489
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.app, theme.path);
490
+ break;
491
+ }
492
+ switch (score.sync) {
493
+ case best50_1.Best50.ESyncTypes.NONE:
494
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.none, theme.path);
495
+ break;
496
+ case best50_1.Best50.ESyncTypes.SYNC_PLAY:
497
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.sync, theme.path);
498
+ break;
499
+ case best50_1.Best50.ESyncTypes.FULL_SYNC:
500
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fs, theme.path);
501
+ break;
502
+ case best50_1.Best50.ESyncTypes.FULL_SYNC_PLUS:
503
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fsp, theme.path);
504
+ break;
505
+ case best50_1.Best50.ESyncTypes.FULL_SYNC_DX:
506
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fdx, theme.path);
507
+ break;
508
+ case best50_1.Best50.ESyncTypes.FULL_SYNC_DX_PLUS:
509
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fdxp, theme.path);
510
+ break;
511
+ }
512
+ const combo = new canvas_1.Image();
513
+ combo.src = comboImg;
514
+ ctx.drawImage(combo, x + height * 0.806 * (0.32 * 2.133 + 0.06 - 0.1), y +
515
+ element.bubble.margin +
516
+ titleSize +
517
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.32, height * 0.806 * 0.32);
518
+ const sync = new canvas_1.Image();
519
+ sync.src = syncImg;
520
+ ctx.drawImage(sync, x + height * 0.806 * (0.32 * 2.133 + 0.04 + 0.32 - 0.1), y +
521
+ element.bubble.margin +
522
+ titleSize +
523
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.32, height * 0.806 * 0.32);
524
+ }
525
+ }
526
+ /** End Milestone Draw */
527
+ /** Begin Version Draw */
528
+ {
529
+ let versions = [];
530
+ for (let i = 0; i < 3; ++i) {
531
+ versions[i] = {
532
+ region: "DX",
533
+ version: undefined,
534
+ EXAppend: false,
535
+ };
536
+ }
537
+ const VER_DX = chart.difficulty == type_1.EDifficulty.REMASTER
538
+ ? chart.events.find((v) => v.type == "existence" &&
539
+ v.version.region == "DX")?.version
540
+ : chart.addVersion.DX;
541
+ const VER_EX = chart.difficulty == type_1.EDifficulty.REMASTER
542
+ ? chart.events.find((v) => v.type == "existence" &&
543
+ v.version.region == "EX")?.version
544
+ : chart.addVersion.EX;
545
+ const VER_CN = chart.difficulty == type_1.EDifficulty.REMASTER
546
+ ? chart.events.find((v) => v.type == "existence" &&
547
+ v.version.region == "CN")?.version
548
+ : chart.addVersion.CN;
549
+ if (VER_DX) {
550
+ const version = versions[0];
551
+ version.version = VER_DX;
552
+ version.region = "DX";
553
+ }
554
+ if (VER_EX) {
555
+ for (let i = 0; i < 3; ++i) {
556
+ const version = versions[i];
557
+ if (version.version) {
558
+ if (!(version.version.gameVersion.isDX &&
559
+ version.version.gameVersion.minor >= 10 &&
560
+ version.version.gameVersion.minor < 20) &&
561
+ lodash_1.default.isEqual(versions[i].version?.gameVersion, VER_EX.gameVersion)) {
562
+ versions[i].EXAppend = true;
563
+ break;
564
+ }
565
+ }
566
+ else {
567
+ version.version = VER_EX;
568
+ version.region = "EX";
569
+ break;
570
+ }
571
+ }
572
+ }
573
+ if (VER_CN) {
574
+ for (let i = 0; i < 3; ++i) {
575
+ const version = versions[i];
576
+ if (version.version) {
577
+ if (!version.version.gameVersion.isDX &&
578
+ lodash_1.default.isEqual(versions[i].version?.gameVersion, VER_CN.gameVersion)) {
579
+ break;
580
+ }
581
+ }
582
+ else {
583
+ version.version = VER_CN;
584
+ version.region = "CN";
585
+ break;
586
+ }
587
+ }
588
+ }
589
+ let counter = 0;
590
+ for (const version of versions) {
591
+ if (version.version) {
592
+ counter++;
593
+ if (!version.version.gameVersion.isDX)
594
+ version.region = "OLD";
595
+ }
596
+ }
597
+ const versionImageHeight = (height - element.bubble.margin * 2) *
598
+ (isShort ? 1 / 4 : 3 / 8);
599
+ const versionImageWidth = (versionImageHeight / 160) * 332;
600
+ const regionTextSize = versionImageHeight * (5 / 8);
601
+ for (let i = 0, curx = x + width - element.bubble.margin, cury = y + element.bubble.margin; i < versions.length; ++i,
602
+ cury =
603
+ curx - versionImageWidth - regionTextSize <
604
+ x + height * 2
605
+ ? cury + versionImageHeight
606
+ : cury,
607
+ curx =
608
+ curx - versionImageWidth - regionTextSize <
609
+ x + height * 2
610
+ ? x + width - element.bubble.margin
611
+ : curx) {
612
+ const version = versions[i];
613
+ if (version.version) {
614
+ let region = version.region;
615
+ if (version.region == "EX" &&
616
+ !(version.version.gameVersion.minor >= 10 &&
617
+ version.version.gameVersion.minor < 20)) {
618
+ region = "DX";
619
+ }
620
+ const versionImage = this.getThemeFile(theme.manifest.sprites.versions[region][version.version.gameVersion
621
+ .minor]);
622
+ if (versionImage) {
623
+ const versionImg = new canvas_1.Image();
624
+ versionImg.src = versionImage;
625
+ let text;
626
+ switch (version.region) {
627
+ case "DX":
628
+ if (version.EXAppend)
629
+ text = "🇯🇵🌏";
630
+ else
631
+ text = "🇯🇵";
632
+ break;
633
+ case "EX":
634
+ text = "🌏";
635
+ break;
636
+ case "CN":
637
+ text = "🇨🇳";
638
+ break;
639
+ default:
640
+ text = "";
641
+ }
642
+ ctx.drawImage(versionImg, (curx -= versionImageWidth), cury, versionImageWidth, versionImageHeight);
643
+ if (version.region != "OLD") {
644
+ await util_1.Util.drawEmojiOrGlyph(ctx, text, curx, cury +
645
+ regionTextSize +
646
+ (versionImageHeight - regionTextSize) /
647
+ 2, regionTextSize, Infinity, "right");
648
+ curx -=
649
+ util_1.Util.visibleLength(text) * regionTextSize +
650
+ element.bubble.margin;
651
+ }
652
+ }
653
+ }
654
+ }
655
+ /** End Version Draw */
656
+ /** Begin Note Count Draw */
657
+ const noteCountTexts = Object.entries(chart.meta.notes).map(([k, v]) => `${util_1.Util.capitalize(k)}: ${v}`);
658
+ const shortCompensation = isShort
659
+ ? versionImageHeight + element.bubble.margin / 2
660
+ : 0;
661
+ const noteCountTextSize = (height - shortCompensation - element.bubble.margin * 4) /
662
+ noteCountTexts.length;
663
+ let noteCountLength = 0;
664
+ noteCountTexts.forEach((v, i) => {
665
+ util_1.Util.drawText(ctx, v, x + element.bubble.margin * (3 / 2) + height * 2, y +
666
+ shortCompensation +
667
+ element.bubble.margin +
668
+ noteCountTextSize +
669
+ (noteCountTextSize + element.bubble.margin / 2) * i, noteCountTextSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
670
+ const length = util_1.Util.measureText(ctx, v, noteCountTextSize, Infinity).width;
671
+ if (length > noteCountLength)
672
+ noteCountLength = length;
673
+ });
674
+ /** End Note Count Draw */
675
+ /** Begin Internal Level Trend Draw */
676
+ if (!isShort) {
677
+ const CURRENT_DX_MINOR = 55;
678
+ const maxWidth = width -
679
+ height * 2 -
680
+ element.bubble.margin * 4 -
681
+ noteCountLength;
682
+ const maxFitTrendCount = Math.floor(maxWidth / versionImageWidth);
683
+ const trendEvents = chart.events.filter((v) => v.type == "existence" && v.version.region == "DX");
684
+ let actualEvents = lodash_1.default.uniqWith(trendEvents, (a, b) => {
685
+ return lodash_1.default.isEqual(a.data.level, b.data.level);
686
+ });
687
+ console.log(actualEvents.length);
688
+ if (actualEvents.length == maxFitTrendCount) {
689
+ }
690
+ else if (actualEvents.length > maxFitTrendCount) {
691
+ while (actualEvents.length > maxFitTrendCount)
692
+ actualEvents.shift();
693
+ actualEvents.shift();
694
+ actualEvents.shift();
695
+ actualEvents.unshift(trendEvents[0]);
696
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
697
+ }
698
+ else if (trendEvents.length > maxFitTrendCount) {
699
+ actualEvents = lodash_1.default.filter(actualEvents, (v) => !(lodash_1.default.isEqual(v.version.gameVersion, trendEvents[0].version.gameVersion) ||
700
+ lodash_1.default.isEqual(v.version.gameVersion, trendEvents[trendEvents.length - 1]
701
+ .version.gameVersion)));
702
+ for (let i = trendEvents.length - 2; i > 0 && actualEvents.length < maxFitTrendCount - 2; --i) {
703
+ const event = trendEvents[i];
704
+ if (event)
705
+ actualEvents.push(event);
706
+ actualEvents = lodash_1.default.uniqWith(actualEvents, (a, b) => {
707
+ return lodash_1.default.isEqual(a.version.gameVersion, b.version.gameVersion);
708
+ });
709
+ }
710
+ actualEvents.unshift(trendEvents[0]);
711
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
712
+ actualEvents = lodash_1.default.uniqWith(actualEvents, (a, b) => {
713
+ return lodash_1.default.isEqual(a.version.gameVersion, b.version.gameVersion);
714
+ });
715
+ actualEvents = lodash_1.default.sortBy(actualEvents, (v) => v.version.gameVersion.minor);
716
+ if (trendEvents.length > 1) {
717
+ if (actualEvents.length >= maxFitTrendCount)
718
+ actualEvents.pop();
719
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
720
+ }
721
+ }
722
+ else {
723
+ actualEvents = [...trendEvents];
724
+ }
725
+ const addGap = (maxWidth - actualEvents.length * versionImageWidth) /
726
+ (actualEvents.length - 1);
727
+ console.log(chart.difficulty, maxFitTrendCount, trendEvents.length, actualEvents.length, versionImageWidth, maxWidth, addGap);
728
+ for (let i = 0, curx = x +
729
+ height * 2 +
730
+ element.bubble.margin * (5 / 2) +
731
+ noteCountLength, cury = y +
732
+ element.bubble.margin * (3 / 2) +
733
+ versionImageHeight; i < maxFitTrendCount; ++i) {
734
+ const event = actualEvents[i];
735
+ if (!event)
736
+ continue;
737
+ const region = event.version.gameVersion.isDX
738
+ ? "DX"
739
+ : "OLD";
740
+ const versionImage = this.getThemeFile(theme.manifest.sprites.versions[region][event.version.gameVersion
741
+ .minor]);
742
+ if (versionImage) {
743
+ const versionImg = new canvas_1.Image();
744
+ versionImg.src = versionImage;
745
+ ctx.drawImage(versionImg, curx, cury, versionImageWidth, versionImageHeight);
746
+ let symbol = "";
747
+ if (i != 0) {
748
+ const lastEvent = actualEvents[i - 1];
749
+ if (lastEvent) {
750
+ if (lastEvent.data.level < event.data.level)
751
+ symbol = "↑";
752
+ else if (lastEvent.data.level > event.data.level)
753
+ symbol = "↓";
754
+ else if (lastEvent.data.level == event.data.level)
755
+ symbol = "→";
756
+ }
757
+ }
758
+ util_1.Util.drawText(ctx, `${symbol}${event.data.level.toFixed(1)}`, curx + versionImageWidth / 2, cury + versionImageHeight + noteCountTextSize, noteCountTextSize, height * 0.806 * 0.04, Infinity, "center", "white", new color_1.default(curColor).darken(0.3).hexa());
759
+ curx += versionImageWidth + addGap;
760
+ }
761
+ }
762
+ }
763
+ }
764
+ /** End Internal Level Trend Draw */
765
+ ctx.restore();
766
+ }
767
+ /** End Main Content Draw */
768
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
769
+ ctx.beginPath();
770
+ ctx.roundRect(x, y + height * 0.742, height * 2, height * (1 - 0.742), [
771
+ 0,
772
+ (height * 0.806) / 7,
773
+ 0,
774
+ (height * 0.806) / 7,
775
+ ]);
776
+ ctx.fill();
777
+ /** Begin Difficulty & DX Rating Draw */
778
+ {
779
+ util_1.Util.drawText(ctx, chart.designer.name || "-", x + element.bubble.margin, y + height * (0.806 + (1 - 0.806) / 2), height * 0.806 * 0.128, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
780
+ util_1.Util.drawText(ctx, `${score?.dxScore || "MAX DX SCR"}/${chart.meta.maxDXScore}`, x + height * 2 - element.bubble.margin, y + height * (0.806 + (1 - 0.806) / 2), height * 0.806 * 0.128, height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
781
+ }
782
+ /** End Difficulty & DX Rating Draw */
783
+ ctx.restore();
784
+ /** End Card Draw */
785
+ }
786
+ static async drawDetailInfoModule(ctx, theme, element, chartId) {
787
+ const jacketMargin = element.margin;
788
+ const textMargin = element.margin;
789
+ const chart = Chart.Database.getLocalChart(chartId, type_1.EDifficulty.BASIC);
790
+ const jacket = await Chart.Database.fecthJacket(chartId);
791
+ /* Begin Background Draw */
792
+ ctx.beginPath();
793
+ ctx.roundRect(element.x, element.y, element.width, element.height, Math.min(theme.manifest.width, theme.manifest.height) * (3 / 128));
794
+ ctx.fillStyle = element.color.card;
795
+ ctx.strokeStyle = new color_1.default(element.color.card).darken(0.6).hex();
796
+ ctx.lineWidth =
797
+ Math.min(theme.manifest.width, theme.manifest.height) * (3 / 512);
798
+ ctx.stroke();
799
+ ctx.fill();
800
+ /* End Background Draw */
801
+ /* Begin jacket draw */
802
+ if (jacket) {
803
+ const jacketImage = new canvas_1.Image();
804
+ const roundRadius = Math.min(theme.manifest.width, theme.manifest.height) *
805
+ (3 / 128);
806
+ jacketImage.src = jacket;
807
+ ctx.beginPath();
808
+ ctx.roundRect(element.x + jacketMargin, element.y + jacketMargin, element.width - jacketMargin * 2, element.width - jacketMargin * 2, [roundRadius, roundRadius, 0, 0]);
809
+ ctx.save();
810
+ ctx.clip();
811
+ ctx.drawImage(jacketImage, element.x + jacketMargin, element.y + jacketMargin, element.width - jacketMargin * 2, element.width - jacketMargin * 2);
812
+ ctx.restore();
813
+ }
814
+ /* End jacket draw */
815
+ /* Begin Detail Draw */
816
+ if (chart) {
817
+ const textSizeTitle = element.width * (1 / 16);
818
+ const textSizeSecondary = element.width * (1 / 24);
819
+ const { actualBoundingBoxAscent: ascent, actualBoundingBoxDescent: decent, } = util_1.Util.measureText(ctx, chart.name, textSizeTitle, Infinity);
820
+ const titleActualHeight = Math.abs(ascent - decent);
821
+ const mode = new canvas_1.Image();
822
+ const chartModeBadgeImg = this.getThemeFile(chart.id > 10000
823
+ ? theme.manifest.sprites.mode.dx
824
+ : theme.manifest.sprites.mode.standard, theme.path);
825
+ const { width: modeWidth, height: modeHeight } = await (0, sharp_1.default)(chartModeBadgeImg).metadata();
826
+ const aspectRatio = (modeWidth ?? 0) / (modeHeight ?? 1) || 3;
827
+ const textLineWidth = element.width * (7 / 512);
828
+ const textColor = new color_1.default(element.color.card).darken(0.5).hex();
829
+ const textTitleMaxWidth = element.width - textMargin * 2 - textSizeTitle * aspectRatio;
830
+ const titleMetrics = util_1.Util.measureText(ctx, chart.name, textSizeTitle, textTitleMaxWidth);
831
+ util_1.Util.drawText(ctx, chart.name, element.x + textMargin, element.y +
832
+ jacketMargin +
833
+ textMargin * (1 / 2) +
834
+ (element.width - jacketMargin * 2) +
835
+ textSizeTitle, textSizeTitle, textLineWidth, textTitleMaxWidth, "left", "white", textColor);
836
+ util_1.Util.drawText(ctx, chart.artist, element.x + textMargin, element.y +
837
+ jacketMargin +
838
+ textMargin * (1 / 2) +
839
+ (element.width - jacketMargin * 2) +
840
+ textSizeTitle * 2, textSizeSecondary, textLineWidth, element.width - textMargin * 2, "left", "white", textColor);
841
+ util_1.Util.drawText(ctx, `BPM: ${chart.bpm}`, element.x + textMargin, element.y +
842
+ jacketMargin +
843
+ textMargin * (1 / 2) +
844
+ (element.width - jacketMargin * 2) +
845
+ textSizeTitle * 3, textSizeSecondary, textLineWidth, element.width - textMargin * 2, "left", "white", textColor);
846
+ const DX_LATEST = 55, EX_LATEST = 50, CN_LATEST = 40;
847
+ const EVENT_DX = chart.events
848
+ .filter((v) => v.version.region == "DX" &&
849
+ v.version.gameVersion.minor >= DX_LATEST)
850
+ .map((v) => v.type);
851
+ const EVENT_EX = chart.events
852
+ .filter((v) => v.version.region == "EX" &&
853
+ v.version.gameVersion.minor >= EX_LATEST)
854
+ .map((v) => v.type);
855
+ const EVENT_CN = chart.events
856
+ .filter((v) => v.version.region == "CN" &&
857
+ v.version.gameVersion.minor >= CN_LATEST)
858
+ .map((v) => v.type);
859
+ const EXIST_DX = EVENT_DX.includes("existence") && !EVENT_DX.includes("removal")
860
+ ? "🇯🇵"
861
+ : "";
862
+ const EXIST_EX = EVENT_EX.includes("existence") && !EVENT_EX.includes("removal")
863
+ ? "🌏"
864
+ : "";
865
+ const EXIST_CN = EVENT_CN.includes("existence") && !EVENT_CN.includes("removal")
866
+ ? "🇨🇳"
867
+ : "";
868
+ const title = [];
869
+ if (EXIST_DX)
870
+ title.push(EXIST_DX);
871
+ if (EXIST_EX)
872
+ title.push(EXIST_EX);
873
+ if (EXIST_CN)
874
+ title.push(EXIST_CN);
875
+ await util_1.Util.drawEmojiOrGlyph(ctx, title.join(" "), element.x + element.width - textMargin, element.y +
876
+ jacketMargin +
877
+ textMargin * (1 / 2) +
878
+ (element.width - jacketMargin * 2) +
879
+ textSizeTitle * 3, textSizeSecondary * (9 / 8), element.width - textMargin * 2, "right", "white", textColor);
880
+ /** Begin Chart Mode Draw */
881
+ {
882
+ mode.src = chartModeBadgeImg;
883
+ ctx.drawImage(mode, element.x + textMargin * (3 / 2) + titleMetrics.width, element.y +
884
+ jacketMargin +
885
+ textMargin * (1 / 2) +
886
+ (element.width - jacketMargin * 2) -
887
+ titleActualHeight / 2 +
888
+ textSizeTitle / 2, textSizeTitle * aspectRatio, textSizeTitle);
889
+ }
890
+ /** End Chart Mode Draw */
891
+ }
892
+ /* End Detail Draw */
893
+ }
894
+ static async drawChartGridModule(ctx, theme, element, chartId, scores) {
895
+ /* Begin Background Draw */
896
+ ctx.roundRect(element.x, element.y, element.width, element.height, Math.min(theme.manifest.width, theme.manifest.height) * (3 / 128));
897
+ ctx.fillStyle = element.color.card;
898
+ ctx.strokeStyle = new color_1.default(element.color.card).darken(0.6).hex();
899
+ ctx.lineWidth =
900
+ Math.min(theme.manifest.width, theme.manifest.height) * (3 / 512);
901
+ ctx.stroke();
902
+ ctx.fill();
903
+ /* End Background Draw */
904
+ const difficulties = [];
905
+ for (let i = type_1.EDifficulty.BASIC; i <= type_1.EDifficulty.REMASTER; ++i) {
906
+ const chart = Chart.Database.getLocalChart(chartId, i);
907
+ if (chart)
908
+ difficulties.push(chart);
909
+ }
910
+ const cardWidth = element.width - element.margin * 2;
911
+ const cardHeight = (element.height - element.margin * 2 - element.gap * 3) / 4;
912
+ for (let y = element.y + element.margin, i = 0; i < difficulties.length; ++i, y += cardHeight + element.gap) {
913
+ const chart = difficulties[i];
914
+ if (chart)
915
+ if (difficulties.length > 4 && i == 0) {
916
+ await this.drawChartGridCard(ctx, theme, element, chart, i, element.x + element.margin, y, (cardWidth - element.margin) / 2, cardHeight, true, scores[i]);
917
+ i++;
918
+ const chartA = difficulties[i];
919
+ if (chartA)
920
+ await this.drawChartGridCard(ctx, theme, element, chartA, i, element.x +
921
+ element.margin +
922
+ (cardWidth + element.margin) / 2, y, (cardWidth - element.margin) / 2, cardHeight, true, scores[i]);
923
+ }
924
+ else {
925
+ await this.drawChartGridCard(ctx, theme, element, chart, i, element.x + element.margin, y, cardWidth, cardHeight, false, scores[i]);
926
+ }
927
+ }
928
+ }
929
+ static async drawProfileModule(ctx, theme, element, username, rating, profilePicture) {
930
+ const nameplate = new canvas_1.Image();
931
+ nameplate.src = this.getThemeFile(theme.manifest.sprites.profile.nameplate, theme.path);
932
+ ctx.drawImage(nameplate, element.x, element.y, element.height * 6.207, element.height);
933
+ /* Begin Profile Picture Draw */
934
+ {
935
+ ctx.save();
936
+ ctx.beginPath();
937
+ ctx.roundRect(element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872, (element.height * 0.872) / 16);
938
+ ctx.clip();
939
+ ctx.fillStyle = "white";
940
+ ctx.fill();
941
+ const icon = new canvas_1.Image();
942
+ try {
943
+ (0, sharp_1.default)(profilePicture);
944
+ }
945
+ catch {
946
+ // Unknown profile picture binary
947
+ profilePicture = undefined;
948
+ }
949
+ const pfp = profilePicture ||
950
+ this.getThemeFile(theme.manifest.sprites.profile.icon, theme.path);
951
+ const { dominant } = await (0, sharp_1.default)(pfp).stats();
952
+ icon.src = await (0, sharp_1.default)(pfp).png().toBuffer();
953
+ const cropSize = Math.min(icon.width, icon.height);
954
+ ctx.drawImage(icon, (icon.width - cropSize) / 2, (icon.height - cropSize) / 2, cropSize, cropSize, element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872);
955
+ if (profilePicture) {
956
+ ctx.beginPath();
957
+ ctx.roundRect(element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872, (element.height * 0.872) / 16);
958
+ ctx.strokeStyle = color_1.default.rgb(dominant).darken(0.3).hex();
959
+ ctx.lineWidth = element.height / 30;
960
+ ctx.stroke();
961
+ }
962
+ ctx.restore();
963
+ }
964
+ /* End Profile Picture Draw */
965
+ /* Begin DX Rating Draw */
966
+ {
967
+ const dxRating = new canvas_1.Image();
968
+ let dxRatingImg;
969
+ switch (true) {
970
+ case rating >= 15000: {
971
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.rainbow, theme.path);
972
+ break;
973
+ }
974
+ case rating >= 14500: {
975
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.platinum, theme.path);
976
+ break;
977
+ }
978
+ case rating >= 14000: {
979
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.gold, theme.path);
980
+ break;
981
+ }
982
+ case rating >= 13000: {
983
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.silver, theme.path);
984
+ break;
985
+ }
986
+ case rating >= 12000: {
987
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.bronze, theme.path);
988
+ break;
989
+ }
990
+ case rating >= 10000: {
991
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.purple, theme.path);
992
+ break;
993
+ }
994
+ case rating >= 8000: {
995
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.red, theme.path);
996
+ break;
997
+ }
998
+ case rating >= 6000: {
999
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.yellow, theme.path);
1000
+ break;
1001
+ }
1002
+ case rating >= 4000: {
1003
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.green, theme.path);
1004
+ break;
1005
+ }
1006
+ case rating >= 2000: {
1007
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.blue, theme.path);
1008
+ break;
1009
+ }
1010
+ default: {
1011
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.white, theme.path);
1012
+ break;
1013
+ }
1014
+ }
1015
+ dxRating.src = dxRatingImg;
1016
+ ctx.drawImage(dxRating, element.x + element.height, element.y + element.height * 0.064, (element.height / 3) * 5.108, element.height / 3);
1017
+ }
1018
+ /* End DX Rating Draw */
1019
+ /* Begin Username Draw */
1020
+ {
1021
+ ctx.beginPath();
1022
+ ctx.roundRect(element.x + element.height * (1 + 1 / 32), element.y + element.height * (0.064 + 0.333 + 1 / 32), ((element.height / 3) * 5.108 * 6) / 5, (element.height * 7) / 24, element.height / 20);
1023
+ ctx.fillStyle = "white";
1024
+ ctx.strokeStyle = color_1.default.rgb(180, 180, 180).hex();
1025
+ ctx.lineWidth = element.height / 32;
1026
+ ctx.stroke();
1027
+ ctx.fill();
1028
+ const ratingImgBuffer = await this.getRatingNumber(rating, theme);
1029
+ if (ratingImgBuffer) {
1030
+ const { width, height } = await (0, sharp_1.default)(ratingImgBuffer).metadata();
1031
+ if (width && height) {
1032
+ const aspectRatio = width / height;
1033
+ const image = new canvas_1.Image();
1034
+ image.src = ratingImgBuffer;
1035
+ const drawHeight = (element.height * 7) / 32;
1036
+ ctx.drawImage(image, element.x + element.height * 1.785, element.y + element.height * 0.12, drawHeight * aspectRatio * 0.8, drawHeight);
1037
+ }
1038
+ }
1039
+ util_1.Util.drawText(ctx, util_1.Util.HalfFullWidthConvert.toFullWidth(username), element.x + element.height * (1 + 1 / 16), element.y + element.height * (0.064 + 0.333 + 1 / 4), (element.height * 1) / 6, 0, ((element.height / 3) * 5.108 * 6) / 5, "left", "black", "black", "standard-font-username");
1040
+ }
1041
+ /* End Username Draw*/
1042
+ }
1043
+ static async drawTextModule(ctx, theme, element, variables = {}) {
1044
+ let naiveLines = (0, string_template_1.default)(element.content, variables).split("\n");
1045
+ let lines = [];
1046
+ if (element.linebreak) {
1047
+ for (let originalContent of naiveLines) {
1048
+ while (originalContent.length) {
1049
+ const line = util_1.Util.findMaxFitString(ctx, originalContent, element.width || Infinity, "");
1050
+ originalContent = originalContent.replace(line, "").trim();
1051
+ lines.push(line.trim());
1052
+ }
1053
+ }
1054
+ }
1055
+ else {
1056
+ for (const originalContent of naiveLines) {
1057
+ lines.push(util_1.Util.findMaxFitString(ctx, originalContent, element.width || Infinity));
1058
+ }
1059
+ }
1060
+ for (let i = 0; i < lines.length; ++i) {
1061
+ const line = lines[i];
1062
+ util_1.Util.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
1063
+ ? element.borderColor
1064
+ : color_1.default.rgb(element.color || "#FFFFFF")
1065
+ .darken(0.3)
1066
+ .hex(), element.font, element.linebreak ? "" : "...");
1067
+ }
1068
+ }
1069
+ /* End Draw Tools*/
1070
+ static async draw(name, rating, chartId, scores, options) {
1071
+ let currentTheme = this.primaryTheme;
1072
+ if (options?.theme) {
1073
+ const theme = this.getTheme(options.theme);
1074
+ if (theme) {
1075
+ currentTheme = theme;
1076
+ }
1077
+ }
1078
+ if (currentTheme) {
1079
+ await Chart.Database.cacheJackets([chartId]);
1080
+ const canvas = new canvas_1.Canvas(currentTheme.manifest.width * (options?.scale ?? 1), currentTheme.manifest.height * (options?.scale ?? 1));
1081
+ const ctx = canvas.getContext("2d");
1082
+ if (options?.scale)
1083
+ ctx.scale(options.scale, options.scale);
1084
+ ctx.imageSmoothingEnabled = true;
1085
+ for (const element of currentTheme.manifest.elements) {
1086
+ switch (element.type) {
1087
+ case "image": {
1088
+ await this.drawImageModule(ctx, currentTheme, element);
1089
+ break;
1090
+ }
1091
+ case "chart-grid": {
1092
+ await this.drawChartGridModule(ctx, currentTheme, element, chartId, scores);
1093
+ break;
1094
+ }
1095
+ case "detail-info": {
1096
+ await this.drawDetailInfoModule(ctx, currentTheme, element, chartId);
1097
+ break;
1098
+ }
1099
+ case "profile": {
1100
+ await this.drawProfileModule(ctx, currentTheme, element, name, rating, options?.profilePicture);
1101
+ break;
1102
+ }
1103
+ case "text": {
1104
+ await this.drawTextModule(ctx, currentTheme, element, {
1105
+ username: util_1.Util.HalfFullWidthConvert.toFullWidth(name),
1106
+ rating: rating.toFixed(0),
1107
+ });
1108
+ break;
1109
+ }
1110
+ }
1111
+ }
1112
+ return canvas.toBuffer();
1113
+ }
1114
+ else
1115
+ return null;
1116
+ }
1117
+ static async drawWithScoreSource(source, username, chartId, options) {
1118
+ const profile = await source.getPlayerInfo(username);
1119
+ const score = await source.getPlayerScore(username, chartId);
1120
+ if (!profile || !score)
1121
+ return null;
1122
+ return this.draw(profile.name, profile.rating, chartId, [
1123
+ score.basic,
1124
+ score.advanced,
1125
+ score.expert,
1126
+ score.master,
1127
+ score.remaster,
1128
+ score.utage,
1129
+ ], {
1130
+ ...options,
1131
+ profilePicture: options?.profilePicture === null
1132
+ ? undefined
1133
+ : options?.profilePicture ||
1134
+ (await source.getPlayerProfilePicture(username)) ||
1135
+ undefined,
1136
+ });
1137
+ }
1138
+ static async getRatingNumber(num, theme) {
1139
+ async function getRaingDigit(map, digit, unitWidth, unitHeight) {
1140
+ digit = Math.floor(digit % 10);
1141
+ return await (0, sharp_1.default)(map)
1142
+ .extract({
1143
+ left: (digit % 4) * unitWidth,
1144
+ top: Math.floor(digit / 4) * unitHeight,
1145
+ width: unitWidth,
1146
+ height: unitHeight,
1147
+ })
1148
+ .toBuffer();
1149
+ }
1150
+ if (theme.manifest) {
1151
+ const map = this.getThemeFile(theme.manifest.sprites.dxRatingNumberMap, theme.path);
1152
+ const { width, height } = await (0, sharp_1.default)(map).metadata();
1153
+ if (!(width && height))
1154
+ return null;
1155
+ const unitWidth = width / 4, unitHeight = height / 4;
1156
+ let digits = [];
1157
+ while (num > 0) {
1158
+ digits.push(await getRaingDigit(map, num % 10, unitWidth, unitHeight));
1159
+ num = Math.floor(num / 10);
1160
+ }
1161
+ while (digits.length < 5)
1162
+ digits.push(null);
1163
+ digits = digits.reverse();
1164
+ const canvas = new canvas_1.Canvas(unitWidth * digits.length, unitHeight);
1165
+ const ctx = canvas.getContext("2d");
1166
+ for (let i = 0; i < digits.length; ++i) {
1167
+ const curDigit = digits[i];
1168
+ if (!curDigit)
1169
+ continue;
1170
+ const img = new canvas_1.Image();
1171
+ img.src = curDigit;
1172
+ ctx.drawImage(img, unitWidth * i * 0.88, 0);
1173
+ }
1174
+ return canvas.toBuffer();
1175
+ }
1176
+ return null;
1177
+ }
1178
+ }
1179
+ exports.Chart = Chart;
29
1180
  (function (Chart) {
30
1181
  Chart.Database = database.Database;
31
1182
  })(Chart || (exports.Chart = Chart = {}));