UNPKG

koishi-plugin-beatsaber-bot

Version:

一个 用于 BeatSaber 的 koishi Bot 插件

296 lines (295 loc) 12.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.formatNumber = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const bsorReplayAcc_1 = require("../utils/bl/bsorReplayAcc"); const beatleader_1 = require("../utils/bl/beatleader"); const stastic_1 = __importDefault(require("../utils/bl/stastic")); const auto_1 = __importDefault(require("chart.js/auto")); const canvas_1 = require("../utils/canvas"); function formatNumber(num, digits = 2, addSign = false, notANumber = null) { if (!Number.isFinite(num)) { return notANumber; } return ((addSign && num > 0 ? '+' : '') + num.toLocaleString(navigator.language, { minimumFractionDigits: digits, maximumFractionDigits: digits, })); } exports.formatNumber = formatNumber; function timeToLabel(time) { const minutes = Math.floor(time / 60); const seconds = Math.floor(time % 60); return minutes + ':' + seconds.toString().padStart(2, '0'); } function processChartData(chartData, resolution, smoothPeriodPercentage, weightFunctionSteepness) { var data = []; if (chartData.length === 0 || resolution === 0) return data; var songDuration = chartData[chartData.length - 1][1]; const distanceWeightFunction = (0, beatleader_1.createDistanceWeightFunction)(songDuration * smoothPeriodPercentage, weightFunctionSteepness); for (let i = 0.0; i < resolution; i += 1.0) { const songTime = (songDuration * i) / (resolution - 1); var sum = 0; var divider = 0; for (let j = 0.0; j < chartData.length; j += 1.0) { const item = chartData[j]; const weight = distanceWeightFunction.getWeight(item[1] - songTime); sum += item[0] * weight; divider += weight; } if (divider === 0) continue; const value = 100 + (sum / divider) * 15; data.push(value); } return data; } // get score Statics function ScoreGraph({ scoreId, scoreInfo, statistic, bsor }) { const canvas = (0, canvas_1.createCanvas)(400 * 4, 192 * 4); const setupChart = (canvas, chartData, underswingsData, beatSavior) => { if (!canvas || !chartData || !Object.keys(chartData).length) return; const title = underswingsData?.noUnderswingsScore > underswingsData?.score ? `Lost by underswings: ${formatNumber(underswingsData.noUnderswingsScore - underswingsData.score, 0)}pts, ${formatNumber(underswingsData.noUnderswingsAcc - underswingsData.acc, 2)}% acc` + (underswingsData?.noUnderswingsPp && underswingsData?.noUnderswingsPp > underswingsData?.pp ? `, ${formatNumber(underswingsData.noUnderswingsPp - underswingsData.pp, 2)}pp` : '') : null; let labels = chartData.times.map(timeToLabel); const minMaxCounter = (0, beatleader_1.createMinMaxCounter)(0, 115, 1.0); var hands = []; if (beatSavior?.stats?.accLeft) { hands.push('red'); } if (beatSavior?.stats?.accRight) { hands.push('blue'); } if (hands.length == 2) { hands.push('total'); } for (let i = 0; i < chartData.times.length; i++) { hands.forEach((saberType) => { minMaxCounter.update(chartData.realScoreBySaber[saberType][i]); minMaxCounter.update(chartData.fullSwingBySaber[saberType][i]); }); } var datasets = []; if (beatSavior?.stats?.accLeft) { datasets.push({ yAxisID: 'score', label: 'Accuracy (left)', data: chartData.fullSwingBySaber.red, type: 'line', borderColor: '#ee5555', backgroundColor: '#ee5555', borderWidth: 8, pointRadius: 0, cubicInterpolationMode: 'monotone', tension: 0.4, spanGaps: true, order: 3, }); datasets.push({ yAxisID: 'score', label: 'Underswing (left)', data: chartData.realScoreBySaber.red, type: 'line', fill: '-1', borderColor: '#ee555555', backgroundColor: '#ee555555', borderWidth: 2, pointRadius: 0, cubicInterpolationMode: 'monotone', tension: 0.4, spanGaps: true, order: 4, }); } if (beatSavior?.stats?.accRight) { datasets.push({ yAxisID: 'score', label: 'Accuracy (right)', data: chartData.fullSwingBySaber.blue, type: 'line', borderColor: '#5555ee', backgroundColor: '#5555ee', borderWidth: 8, pointRadius: 0, cubicInterpolationMode: 'monotone', tension: 0.4, spanGaps: true, order: 5, }); datasets.push({ yAxisID: 'score', label: 'Underswing (right)', data: chartData.realScoreBySaber.blue, type: 'line', fill: '-1', borderColor: '#5555ee55', backgroundColor: '#5555ee55', borderWidth: 2, pointRadius: 0, cubicInterpolationMode: 'monotone', tension: 0.4, spanGaps: true, order: 6, }); } datasets = datasets.concat([ { yAxisID: 'score', label: 'Accuracy', data: chartData.fullSwingBySaber.total, type: 'line', borderColor: 'white', backgroundColor: 'white', borderWidth: 8, pointRadius: 0, cubicInterpolationMode: 'monotone', tension: 0.4, spanGaps: true, order: 1, }, { yAxisID: 'score', label: 'Underswing', data: chartData.realScoreBySaber.total, type: 'line', fill: '-1', borderColor: '#aaaaaa55', backgroundColor: '#aaaaaa55', borderWidth: 2, pointRadius: 0, cubicInterpolationMode: 'monotone', tension: 0.4, spanGaps: true, order: 2, }, { yAxisID: 'score', label: 'Score', data: chartData.realScoreBySaber.total, type: 'line', borderColor: '#aaaaaa', backgroundColor: '#aaaaaa', borderWidth: 8, pointRadius: 0, cubicInterpolationMode: 'monotone', tension: 0.4, spanGaps: true, order: 0, }, ]); new auto_1.default(canvas, { data: { labels, datasets }, options: { responsive: false, devicePixelRatio: 1, maintainAspectRatio: false, plugins: { legend: { display: false, }, title: { display: !!title?.length, text: title, color: 'white', font: { weight: 'normal', size: 60 }, position: 'bottom', padding: { top: 5, bottom: 0 }, }, tooltip: { callbacks: { title(tooltipItems) { const item = tooltipItems[0]; const labels = item.chart.data.labels; return labels[item.dataIndex] + ' (10 seconds average)'; }, label(ctx) { const datasetLabel = ctx.dataset.label; let percentage; let value; switch (datasetLabel) { case 'Underswing (left)': value = ctx.raw - chartData.fullSwingBySaber.red[ctx.dataIndex]; percentage = value / 115.0; return `${datasetLabel}: ${formatNumber(value, 1)} (${formatNumber(percentage * 100, 2)}%)`; case 'Underswing (right)': value = ctx.raw - chartData.fullSwingBySaber.blue[ctx.dataIndex]; percentage = value / 115.0; return `${datasetLabel}: ${formatNumber(value, 1)} (${formatNumber(percentage * 100, 2)}%)`; case 'Underswing': value = ctx.raw - chartData.fullSwingBySaber.total[ctx.dataIndex]; percentage = value / 115.0; return `${datasetLabel}: ${formatNumber(value, 1)} (${formatNumber(percentage * 100, 2)}%)`; case 'Score': value = ctx.raw; percentage = value / 115.0; return `${datasetLabel}: ${formatNumber(value, 1)} (${formatNumber(percentage * 100, 2)}%)`; default: value = ctx.raw; percentage = value / 115.0; return `${datasetLabel}: ${formatNumber(value - 100.0, 1)} (${formatNumber(percentage * 100, 2)}%)`; } }, }, }, }, scales: { x: { ticks: { autoSkip: true, autoSkipPadding: 4, color: 'white', font: { weight: 'normal', size: 60 } }, }, y: { display: true, min: minMaxCounter.minValue, max: minMaxCounter.maxValue, ticks: { autoSkip: true, autoSkipPadding: 4, color: 'white', font: { weight: 'normal', size: 60 } }, }, score: { display: false, min: minMaxCounter.minValue, max: minMaxCounter.maxValue, ticks: { autoSkip: true, autoSkipPadding: 4, color: 'white', }, } }, }, }); }; const beatSavior = (0, stastic_1.default)(scoreInfo, scoreId, statistic); const replayAccGraphs = (0, bsorReplayAcc_1.processAccGraphs)(bsor); const underswingsData = (0, bsorReplayAcc_1.processUnderswings)(bsor); setupChart(canvas, replayAccGraphs, underswingsData, beatSavior); const dataURL = (0, canvas_1.canvasToDataURL)(canvas); return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)("div", { style: { height: '12em' }, children: (0, jsx_runtime_1.jsx)("img", { src: dataURL, width: 400, height: 192 }) }) })); } exports.default = ScoreGraph;