feat: 移植 B50 成绩图,并把项目名从 purarine 改回 ichino
海报从 Empurple 的 Best50ImageRenderer/ImageBuilder 移植:按原始像素坐标 (1762x1850 画布、300x100 卡片、5 列、行距 120) 叠加在 base.png 上,底图里 已画好的面板/药丸标签/页脚不重复绘制;页面外壳走 Ichino 的 Material 3。 RatingCalculator 的达成率分档改成单张有序表,避免偏移与评级错位; RA 与评级由 test/rating_calculator_test.dart 的边界用例锁定。 GetUserRatingApi 只需要 userId(Cookie 可选),所以 Preview API 模式、未登录 游戏服务器时也能出图,只是没有 FC/AP/FS 标记。导出用 RepaintBoundary 抓 全尺寸 PNG:原生走 share_plus(不支持文件分享的平台回退为落盘并提示路径), web 走下载。 BEST35 与 BEST15 改为各自独立排版。原先沿用 Empurple 的跨列表全局序号, BEST35 没打满时 BEST15 会整体上移混进上半区。 path_provider 会经 path_provider_android 2.3.x 拖入 jni 插件,而 jni 把 windows/linux 也声明成 ffiPlugin,桌面构建因此去链接 jvm.lib 并失败, 故用 dependency_overrides 钉住 path_provider_android <2.3.0。
This commit is contained in:
1 parent
24ccd628d1
commit
79fa0d7a20
67 files changed
+1563
-62
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ichino/services/rating_calculator.dart';
|
||||
|
||||
/// achievementRaw 接口原值(百分比 * 10000)。
|
||||
double _pct(int raw) => raw / 10000.0;
|
||||
|
||||
void main() {
|
||||
group('评级区间边界', () {
|
||||
const cases = <int, String>{
|
||||
0: 'D',
|
||||
99999: 'D',
|
||||
100000: 'D',
|
||||
499999: 'D',
|
||||
500000: 'C',
|
||||
599999: 'C',
|
||||
600000: 'B',
|
||||
699999: 'B',
|
||||
700000: 'BB',
|
||||
749999: 'BB',
|
||||
750000: 'BBB',
|
||||
799999: 'BBB',
|
||||
800000: 'A',
|
||||
899999: 'A',
|
||||
900000: 'AA',
|
||||
939999: 'AA',
|
||||
940000: 'AAA',
|
||||
969999: 'AAA',
|
||||
970000: 'S',
|
||||
979999: 'S',
|
||||
980000: 'Sp',
|
||||
989999: 'Sp',
|
||||
990000: 'SS',
|
||||
994999: 'SS',
|
||||
995000: 'SSp',
|
||||
999999: 'SSp',
|
||||
1000000: 'SSS',
|
||||
1004999: 'SSS',
|
||||
1005000: 'SSSp',
|
||||
};
|
||||
|
||||
cases.forEach((raw, expected) {
|
||||
test('$raw => $expected', () {
|
||||
expect(RatingCalculator.compute(10.0, _pct(raw)).rate, expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('RA 计算', () {
|
||||
test('低于 10% 时 RA 为 0', () {
|
||||
expect(RatingCalculator.computeRa(10.0, _pct(99999)), 0);
|
||||
});
|
||||
|
||||
test('ds 10.0 / 100.1234% => 216', () {
|
||||
expect(RatingCalculator.compute(10.0, _pct(1001234)).ra, 216);
|
||||
});
|
||||
|
||||
test('ds 10.0 / 99.0000% => 205 (整数截断)', () {
|
||||
expect(RatingCalculator.compute(10.0, _pct(990000)).ra, 205);
|
||||
});
|
||||
|
||||
test('ds 10.0 / 100.5% 封顶 SSSp => 225', () {
|
||||
expect(RatingCalculator.compute(10.0, _pct(1005000)).ra, 225);
|
||||
});
|
||||
|
||||
test('超出 100.5% 的达成率被夹到上限', () {
|
||||
final clamped = RatingCalculator.compute(10.0, _pct(1200000));
|
||||
final capped = RatingCalculator.compute(10.0, _pct(1005000));
|
||||
expect(clamped.ra, capped.ra);
|
||||
expect(clamped.rate, capped.rate);
|
||||
});
|
||||
|
||||
test('定数 13.0 / 100% => 280', () {
|
||||
expect(RatingCalculator.compute(13.0, _pct(1000000)).ra, 280);
|
||||
});
|
||||
|
||||
test('缺失定数 (ds=0) 时 RA 为 0', () {
|
||||
expect(RatingCalculator.computeRa(0.0, _pct(1005000)), 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user