sekai-calculator 0.8.0 → 0.8.3
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.
- package/dist/index.cjs +144 -23
- package/dist/index.d.ts +115 -48
- package/dist/index.mjs +143 -24
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -77,6 +77,37 @@ class CachedDataProvider {
|
|
|
77
77
|
async getUserDataAll() {
|
|
78
78
|
return await this.getData(this.instanceCache, 'userData', async () => await this.dataProvider.getUserDataAll());
|
|
79
79
|
}
|
|
80
|
+
async preloadMasterData(keys) {
|
|
81
|
+
return await Promise.all(keys.map(async (it) => await this.getMasterData(it)));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class AreaItemService {
|
|
86
|
+
dataProvider;
|
|
87
|
+
constructor(dataProvider) {
|
|
88
|
+
this.dataProvider = dataProvider;
|
|
89
|
+
}
|
|
90
|
+
async getAreaItemLevels() {
|
|
91
|
+
const userAreas = await this.dataProvider.getUserData('userAreas');
|
|
92
|
+
return await Promise.all(userAreas.flatMap(it => it.areaItems)
|
|
93
|
+
.map(async (it) => await this.getAreaItemLevel(it.areaItemId, it.level)));
|
|
94
|
+
}
|
|
95
|
+
async getAreaItemLevel(areaItemId, level) {
|
|
96
|
+
const areaItemLevels = await this.dataProvider.getMasterData('areaItemLevels');
|
|
97
|
+
return findOrThrow(areaItemLevels, it => it.areaItemId === areaItemId && it.level === level);
|
|
98
|
+
}
|
|
99
|
+
async getAreaItemNextLevel(areaItem, areaItemLevel) {
|
|
100
|
+
const level = areaItemLevel === undefined ? 1 : (areaItemLevel.level === 15 ? 15 : areaItemLevel.level + 1);
|
|
101
|
+
return await this.getAreaItemLevel(areaItem.id, level);
|
|
102
|
+
}
|
|
103
|
+
async getShopItem(areaItemLevel) {
|
|
104
|
+
const shopItems = await this.dataProvider.getMasterData('shopItems');
|
|
105
|
+
const idOffset = areaItemLevel.level <= 10
|
|
106
|
+
? (1000 + (areaItemLevel.areaItemId - 1) * 10)
|
|
107
|
+
: (1550 - 10 + (areaItemLevel.areaItemId - 1) * 5);
|
|
108
|
+
const id = idOffset + areaItemLevel.level;
|
|
109
|
+
return findOrThrow(shopItems, it => it.id === id);
|
|
110
|
+
}
|
|
80
111
|
}
|
|
81
112
|
|
|
82
113
|
class DeckService {
|
|
@@ -189,8 +220,12 @@ class CardPowerCalculator {
|
|
|
189
220
|
return basePower.reduce((v, it) => v + it, 0) + characterBonus + await this.getAreaItemBonusPower(userAreaItemLevels, basePower, card.characterId, unit, sameUnit, card.attr, sameAttr);
|
|
190
221
|
}
|
|
191
222
|
async getCardBasePowers(userCard, card) {
|
|
192
|
-
const
|
|
193
|
-
|
|
223
|
+
const p = await Promise.all([
|
|
224
|
+
await this.dataProvider.getMasterData('cardEpisodes'),
|
|
225
|
+
await this.dataProvider.getMasterData('masterLessons')
|
|
226
|
+
]);
|
|
227
|
+
const cardEpisodes = p[0];
|
|
228
|
+
const masterLessons = p[1];
|
|
194
229
|
const ret = [0, 0, 0];
|
|
195
230
|
const cardParameters = card.cardParameters.filter(it => it.cardLevel === userCard.level);
|
|
196
231
|
const params = ['param1', 'param2', 'param3'];
|
|
@@ -330,11 +365,13 @@ class CardCalculator {
|
|
|
330
365
|
powerCalculator;
|
|
331
366
|
skillCalculator;
|
|
332
367
|
eventCalculator;
|
|
368
|
+
areaItemService;
|
|
333
369
|
constructor(dataProvider) {
|
|
334
370
|
this.dataProvider = dataProvider;
|
|
335
371
|
this.powerCalculator = new CardPowerCalculator(dataProvider);
|
|
336
372
|
this.skillCalculator = new CardSkillCalculator(dataProvider);
|
|
337
373
|
this.eventCalculator = new CardEventCalculator(dataProvider);
|
|
374
|
+
this.areaItemService = new AreaItemService(dataProvider);
|
|
338
375
|
}
|
|
339
376
|
async getCardUnits(card) {
|
|
340
377
|
const gameCharacters = await this.dataProvider.getMasterData('gameCharacters');
|
|
@@ -360,7 +397,9 @@ class CardCalculator {
|
|
|
360
397
|
}
|
|
361
398
|
}
|
|
362
399
|
if (episodeRead && ret.episodes !== undefined) {
|
|
363
|
-
ret.episodes.forEach(it => {
|
|
400
|
+
ret.episodes.forEach(it => {
|
|
401
|
+
it.scenarioStatus = 'already_read';
|
|
402
|
+
});
|
|
364
403
|
}
|
|
365
404
|
if (masterMax) {
|
|
366
405
|
ret.masterRank = 5;
|
|
@@ -393,11 +432,11 @@ class CardCalculator {
|
|
|
393
432
|
eventBonus
|
|
394
433
|
};
|
|
395
434
|
}
|
|
396
|
-
async batchGetCardDetail(userCards, config = {}, eventId = 0) {
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return await Promise.all(userCards.map(async (it) => await this.getCardDetail(it,
|
|
435
|
+
async batchGetCardDetail(userCards, config = {}, eventId = 0, areaItemLevels) {
|
|
436
|
+
const areaItemLevels0 = areaItemLevels === undefined
|
|
437
|
+
? await this.areaItemService.getAreaItemLevels()
|
|
438
|
+
: areaItemLevels;
|
|
439
|
+
return await Promise.all(userCards.map(async (it) => await this.getCardDetail(it, areaItemLevels0, config, eventId)));
|
|
401
440
|
}
|
|
402
441
|
static isCertainlyLessThan(cardDetail0, cardDetail1) {
|
|
403
442
|
return cardDetail0.power.isCertainlyLessThen(cardDetail1.power) &&
|
|
@@ -442,21 +481,47 @@ class LiveCalculator {
|
|
|
442
481
|
return musicMeta.skill_score_auto;
|
|
443
482
|
}
|
|
444
483
|
}
|
|
484
|
+
static getSortedSkillDetails(deckDetail, liveType, skillDetails = undefined) {
|
|
485
|
+
if (skillDetails !== undefined && skillDetails.length === 6 && skillDetails[5].scoreUp > 0) {
|
|
486
|
+
return {
|
|
487
|
+
details: skillDetails,
|
|
488
|
+
sorted: false
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
if (liveType === exports.LiveType.MULTI) {
|
|
492
|
+
return {
|
|
493
|
+
details: duplicateObj(LiveCalculator.getMultiLiveSkill(deckDetail), 6),
|
|
494
|
+
sorted: false
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
const sortedSkill = [...deckDetail.cards].map(it => it.skill)
|
|
498
|
+
.sort((a, b) => a.scoreUp - b.scoreUp);
|
|
499
|
+
const emptySkill = duplicateObj({
|
|
500
|
+
scoreUp: 0,
|
|
501
|
+
lifeRecovery: 0
|
|
502
|
+
}, 5 - sortedSkill.length);
|
|
503
|
+
return {
|
|
504
|
+
details: [...sortedSkill, ...emptySkill, deckDetail.cards[0].skill],
|
|
505
|
+
sorted: true
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
static getSortedSkillRate(sorted, cardLength, skillScores) {
|
|
509
|
+
if (!sorted) {
|
|
510
|
+
return skillScores;
|
|
511
|
+
}
|
|
512
|
+
return [
|
|
513
|
+
...skillScores.slice(0, cardLength).sort((a, b) => a - b),
|
|
514
|
+
...skillScores.slice(cardLength)
|
|
515
|
+
];
|
|
516
|
+
}
|
|
445
517
|
static getLiveDetailByDeck(deckDetail, musicMeta, liveType, skillDetails = undefined, multiPowerSum = 0) {
|
|
446
|
-
const skills =
|
|
447
|
-
? skillDetails
|
|
448
|
-
: (liveType === exports.LiveType.MULTI
|
|
449
|
-
? duplicateObj(LiveCalculator.getMultiLiveSkill(deckDetail), 6)
|
|
450
|
-
: [...[...deckDetail.cards].map(it => it.skill)
|
|
451
|
-
.sort((a, b) => a.scoreUp - b.scoreUp),
|
|
452
|
-
deckDetail.cards[0].skill]);
|
|
518
|
+
const skills = this.getSortedSkillDetails(deckDetail, liveType, skillDetails);
|
|
453
519
|
const baseRate = LiveCalculator.getBaseScore(musicMeta, liveType);
|
|
454
520
|
const skillScores = [...LiveCalculator.getSkillScore(musicMeta, liveType)];
|
|
455
|
-
const skillRate =
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const
|
|
459
|
-
const life = skills.reduce((v, it) => v + it.lifeRecovery, 0);
|
|
521
|
+
const skillRate = LiveCalculator.getSortedSkillRate(skills.sorted, deckDetail.cards.length, skillScores);
|
|
522
|
+
const rate = baseRate + skills.details
|
|
523
|
+
.reduce((v, it, i) => v + it.scoreUp * skillRate[i] / 100, 0);
|
|
524
|
+
const life = skills.details.reduce((v, it) => v + it.lifeRecovery, 0);
|
|
460
525
|
const powerSum = multiPowerSum === 0 ? 5 * deckDetail.power : multiPowerSum;
|
|
461
526
|
const activeBonus = liveType === exports.LiveType.MULTI ? 5 * 0.015 * powerSum : 0;
|
|
462
527
|
return {
|
|
@@ -612,8 +677,56 @@ class DeckCalculator {
|
|
|
612
677
|
cards
|
|
613
678
|
};
|
|
614
679
|
}
|
|
615
|
-
async getDeckDetail(deckCards) {
|
|
616
|
-
return DeckCalculator.getDeckDetailByCards(await this.cardCalculator.batchGetCardDetail(deckCards), await this.getHonorBonusPower());
|
|
680
|
+
async getDeckDetail(deckCards, eventId = 0, areaItemLevels) {
|
|
681
|
+
return DeckCalculator.getDeckDetailByCards(await this.cardCalculator.batchGetCardDetail(deckCards, {}, eventId, areaItemLevels), await this.getHonorBonusPower());
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
class AreaItemRecommend {
|
|
686
|
+
dataProvider;
|
|
687
|
+
areaItemService;
|
|
688
|
+
deckCalculator;
|
|
689
|
+
constructor(dataProvider) {
|
|
690
|
+
this.dataProvider = dataProvider;
|
|
691
|
+
this.areaItemService = new AreaItemService(dataProvider);
|
|
692
|
+
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
693
|
+
}
|
|
694
|
+
static findCost(shopItem, resourceType, resourceId) {
|
|
695
|
+
const cost = shopItem.costs.map(it => it.cost)
|
|
696
|
+
.find(it => it.resourceType === resourceType && it.resourceId === resourceId);
|
|
697
|
+
return cost === undefined ? 0 : cost.quantity;
|
|
698
|
+
}
|
|
699
|
+
async getRecommendAreaItem(areaItem, areaItemLevel, power) {
|
|
700
|
+
const areas = await this.dataProvider.getMasterData('areas');
|
|
701
|
+
const area = findOrThrow(areas, it => it.id === areaItem.areaId);
|
|
702
|
+
const shopItem = await this.areaItemService.getShopItem(areaItemLevel);
|
|
703
|
+
return {
|
|
704
|
+
area,
|
|
705
|
+
areaItem,
|
|
706
|
+
areaItemLevel,
|
|
707
|
+
shopItem,
|
|
708
|
+
cost: {
|
|
709
|
+
coin: AreaItemRecommend.findCost(shopItem, 'coin', 0),
|
|
710
|
+
seed: AreaItemRecommend.findCost(shopItem, 'material', 17),
|
|
711
|
+
szk: AreaItemRecommend.findCost(shopItem, 'material', 57)
|
|
712
|
+
},
|
|
713
|
+
power
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
async recommendAreaItem(userCards) {
|
|
717
|
+
const areaItems = await this.dataProvider.getMasterData('areaItems');
|
|
718
|
+
const currentAreaItemLevels = await this.areaItemService.getAreaItemLevels();
|
|
719
|
+
const { power: currentPower } = await this.deckCalculator.getDeckDetail(userCards, 0, currentAreaItemLevels);
|
|
720
|
+
const recommend = await Promise.all(areaItems.map(async (areaItem) => {
|
|
721
|
+
const newAreaItemLevel = await this.areaItemService.getAreaItemNextLevel(areaItem, currentAreaItemLevels.find(it => it.areaItemId === areaItem.id));
|
|
722
|
+
const newAreaItemLevels = [
|
|
723
|
+
...currentAreaItemLevels.filter(it => it.areaItemId !== areaItem.id), newAreaItemLevel
|
|
724
|
+
];
|
|
725
|
+
const { power: newPower } = await this.deckCalculator.getDeckDetail(userCards, 0, newAreaItemLevels);
|
|
726
|
+
return await this.getRecommendAreaItem(areaItem, newAreaItemLevel, newPower - currentPower);
|
|
727
|
+
}));
|
|
728
|
+
return recommend.filter(it => it.power > 0)
|
|
729
|
+
.sort((a, b) => b.power / b.cost.coin - a.power / a.cost.coin);
|
|
617
730
|
}
|
|
618
731
|
}
|
|
619
732
|
|
|
@@ -763,12 +876,17 @@ class BaseDeckRecommend {
|
|
|
763
876
|
dataProvider;
|
|
764
877
|
cardCalculator;
|
|
765
878
|
deckCalculator;
|
|
879
|
+
areaItemService;
|
|
766
880
|
constructor(dataProvider) {
|
|
767
881
|
this.dataProvider = dataProvider;
|
|
768
882
|
this.cardCalculator = new CardCalculator(dataProvider);
|
|
769
883
|
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
884
|
+
this.areaItemService = new AreaItemService(dataProvider);
|
|
770
885
|
}
|
|
771
886
|
static findBestCards(cardDetails, scoreFunc, limit = 1, isChallengeLive = false, member = 5, honorBonus = 0, deckCards = [], deckCharacters = []) {
|
|
887
|
+
if (isChallengeLive) {
|
|
888
|
+
member = Math.min(member, cardDetails.length);
|
|
889
|
+
}
|
|
772
890
|
if (deckCards.length === member) {
|
|
773
891
|
const score = scoreFunc(deckCards);
|
|
774
892
|
const deckDetail = DeckCalculator.getDeckDetailByCards(deckCards, honorBonus);
|
|
@@ -823,7 +941,8 @@ class BaseDeckRecommend {
|
|
|
823
941
|
return ans;
|
|
824
942
|
}
|
|
825
943
|
async recommendHighScoreDeck(userCards, scoreFunc, { musicMeta, limit = 1, member = 5, cardConfig = {} }, eventId = 0, isChallengeLive = false) {
|
|
826
|
-
const
|
|
944
|
+
const areaItemLevels = await this.areaItemService.getAreaItemLevels();
|
|
945
|
+
const cards = await this.cardCalculator.batchGetCardDetail(userCards, cardConfig, eventId, areaItemLevels);
|
|
827
946
|
const honorBonus = await this.deckCalculator.getHonorBonusPower();
|
|
828
947
|
let priority = (isChallengeLive || eventId === 0) ? 10 : 0;
|
|
829
948
|
while (priority < 10) {
|
|
@@ -909,6 +1028,8 @@ class MusicRecommend {
|
|
|
909
1028
|
}
|
|
910
1029
|
}
|
|
911
1030
|
|
|
1031
|
+
exports.AreaItemRecommend = AreaItemRecommend;
|
|
1032
|
+
exports.AreaItemService = AreaItemService;
|
|
912
1033
|
exports.BaseDeckRecommend = BaseDeckRecommend;
|
|
913
1034
|
exports.CachedDataProvider = CachedDataProvider;
|
|
914
1035
|
exports.CardCalculator = CardCalculator;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
interface MusicMeta {
|
|
2
|
+
music_id: number;
|
|
3
|
+
difficulty: string;
|
|
4
|
+
music_time: number;
|
|
5
|
+
event_rate: number;
|
|
6
|
+
base_score: number;
|
|
7
|
+
base_score_auto: number;
|
|
8
|
+
skill_score_solo: number[];
|
|
9
|
+
skill_score_auto: number[];
|
|
10
|
+
skill_score_multi: number[];
|
|
11
|
+
fever_score: number;
|
|
12
|
+
fever_end_time: number;
|
|
13
|
+
tap_count: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
interface DataProvider {
|
|
2
|
-
getMasterData: (key: string) => Promise<
|
|
3
|
-
getUserDataAll: () => Promise<any
|
|
4
|
-
getUserData: (key: string) => Promise<
|
|
5
|
-
getMusicMeta: () => Promise<
|
|
17
|
+
getMasterData: <T>(key: string) => Promise<T[]>;
|
|
18
|
+
getUserDataAll: () => Promise<Record<string, any>>;
|
|
19
|
+
getUserData: <T>(key: string) => Promise<T>;
|
|
20
|
+
getMusicMeta: () => Promise<MusicMeta[]>;
|
|
6
21
|
}
|
|
7
22
|
|
|
8
23
|
declare class CachedDataProvider implements DataProvider {
|
|
@@ -12,10 +27,64 @@ declare class CachedDataProvider implements DataProvider {
|
|
|
12
27
|
private readonly instanceCache;
|
|
13
28
|
private static readonly runningPromise;
|
|
14
29
|
private getData;
|
|
15
|
-
getMasterData(key: string): Promise<
|
|
16
|
-
getMusicMeta(): Promise<
|
|
17
|
-
getUserData(key: string): Promise<
|
|
18
|
-
getUserDataAll(): Promise<any
|
|
30
|
+
getMasterData<T>(key: string): Promise<T[]>;
|
|
31
|
+
getMusicMeta(): Promise<MusicMeta[]>;
|
|
32
|
+
getUserData<T>(key: string): Promise<T>;
|
|
33
|
+
getUserDataAll(): Promise<Record<string, any>>;
|
|
34
|
+
preloadMasterData(keys: string[]): Promise<any[]>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface AreaItemLevel {
|
|
38
|
+
areaItemId: number;
|
|
39
|
+
level: number;
|
|
40
|
+
targetUnit: string;
|
|
41
|
+
targetCardAttr: string;
|
|
42
|
+
targetGameCharacterId?: number;
|
|
43
|
+
power1BonusRate: number;
|
|
44
|
+
power1AllMatchBonusRate: number;
|
|
45
|
+
power2BonusRate: number;
|
|
46
|
+
power2AllMatchBonusRate: number;
|
|
47
|
+
power3BonusRate: number;
|
|
48
|
+
power3AllMatchBonusRate: number;
|
|
49
|
+
sentence: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface AreaItem {
|
|
53
|
+
id: number;
|
|
54
|
+
areaId: number;
|
|
55
|
+
name: string;
|
|
56
|
+
flavorText: string;
|
|
57
|
+
spawnPoint: string;
|
|
58
|
+
assetbundleName: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface CommonResource {
|
|
62
|
+
resourceId?: number;
|
|
63
|
+
resourceType: string;
|
|
64
|
+
resourceLevel?: number;
|
|
65
|
+
quantity: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface ShopItem {
|
|
69
|
+
id: number;
|
|
70
|
+
shopId: number;
|
|
71
|
+
seq: number;
|
|
72
|
+
releaseConditionId: number;
|
|
73
|
+
resourceBoxId: number;
|
|
74
|
+
costs: Array<{
|
|
75
|
+
shopItemId: number;
|
|
76
|
+
seq: number;
|
|
77
|
+
cost: CommonResource;
|
|
78
|
+
}>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
declare class AreaItemService {
|
|
82
|
+
private readonly dataProvider;
|
|
83
|
+
constructor(dataProvider: DataProvider);
|
|
84
|
+
getAreaItemLevels(): Promise<AreaItemLevel[]>;
|
|
85
|
+
getAreaItemLevel(areaItemId: number, level: number): Promise<AreaItemLevel>;
|
|
86
|
+
getAreaItemNextLevel(areaItem: AreaItem, areaItemLevel?: AreaItemLevel): Promise<AreaItemLevel>;
|
|
87
|
+
getShopItem(areaItemLevel: AreaItemLevel): Promise<ShopItem>;
|
|
19
88
|
}
|
|
20
89
|
|
|
21
90
|
interface UserDeck {
|
|
@@ -62,21 +131,6 @@ interface UserChallengeLiveSoloDeck {
|
|
|
62
131
|
support4: number | null;
|
|
63
132
|
}
|
|
64
133
|
|
|
65
|
-
interface AreaItemLevel {
|
|
66
|
-
areaItemId: number;
|
|
67
|
-
level: number;
|
|
68
|
-
targetUnit: string;
|
|
69
|
-
targetCardAttr: string;
|
|
70
|
-
targetGameCharacterId?: number;
|
|
71
|
-
power1BonusRate: number;
|
|
72
|
-
power1AllMatchBonusRate: number;
|
|
73
|
-
power2BonusRate: number;
|
|
74
|
-
power2AllMatchBonusRate: number;
|
|
75
|
-
power3BonusRate: number;
|
|
76
|
-
power3AllMatchBonusRate: number;
|
|
77
|
-
sentence: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
134
|
declare class CardDetailMap {
|
|
81
135
|
min: number;
|
|
82
136
|
max: number;
|
|
@@ -93,11 +147,12 @@ declare class CardCalculator {
|
|
|
93
147
|
private readonly powerCalculator;
|
|
94
148
|
private readonly skillCalculator;
|
|
95
149
|
private readonly eventCalculator;
|
|
150
|
+
private readonly areaItemService;
|
|
96
151
|
constructor(dataProvider: DataProvider);
|
|
97
152
|
private getCardUnits;
|
|
98
153
|
private applyCardConfig;
|
|
99
154
|
getCardDetail(userCard: UserCard, userAreaItemLevels: AreaItemLevel[], config?: Record<string, CardConfig>, eventId?: number): Promise<CardDetail>;
|
|
100
|
-
batchGetCardDetail(userCards: UserCard[], config?: Record<string, CardConfig>, eventId?: number): Promise<CardDetail[]>;
|
|
155
|
+
batchGetCardDetail(userCards: UserCard[], config?: Record<string, CardConfig>, eventId?: number, areaItemLevels?: AreaItemLevel[]): Promise<CardDetail[]>;
|
|
101
156
|
static isCertainlyLessThan(cardDetail0: CardDetail, cardDetail1: CardDetail): boolean;
|
|
102
157
|
}
|
|
103
158
|
interface CardDetail {
|
|
@@ -127,7 +182,7 @@ declare class DeckCalculator {
|
|
|
127
182
|
constructor(dataProvider: DataProvider);
|
|
128
183
|
getHonorBonusPower(): Promise<number>;
|
|
129
184
|
static getDeckDetailByCards(cardDetails: CardDetail[], honorBonus: number): DeckDetail;
|
|
130
|
-
getDeckDetail(deckCards: UserCard[]): Promise<DeckDetail>;
|
|
185
|
+
getDeckDetail(deckCards: UserCard[], eventId?: number, areaItemLevels?: AreaItemLevel[]): Promise<DeckDetail>;
|
|
131
186
|
}
|
|
132
187
|
interface DeckDetail {
|
|
133
188
|
power: number;
|
|
@@ -160,13 +215,6 @@ declare class DeckService {
|
|
|
160
215
|
static toUserChallengeLiveSoloDeck(userCards: DeckCardDetail[], characterId: number): UserChallengeLiveSoloDeck;
|
|
161
216
|
}
|
|
162
217
|
|
|
163
|
-
interface CommonResource {
|
|
164
|
-
resourceId?: number;
|
|
165
|
-
resourceType: string;
|
|
166
|
-
resourceLevel?: number;
|
|
167
|
-
quantity: number;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
218
|
interface Card {
|
|
171
219
|
id: number;
|
|
172
220
|
seq: number;
|
|
@@ -227,21 +275,6 @@ declare class CardEventCalculator {
|
|
|
227
275
|
getCardEventBonus(userCard: UserCard, eventId: number): Promise<number>;
|
|
228
276
|
}
|
|
229
277
|
|
|
230
|
-
interface MusicMeta {
|
|
231
|
-
music_id: number;
|
|
232
|
-
difficulty: string;
|
|
233
|
-
music_time: number;
|
|
234
|
-
event_rate: number;
|
|
235
|
-
base_score: number;
|
|
236
|
-
base_score_auto: number;
|
|
237
|
-
skill_score_solo: number[];
|
|
238
|
-
skill_score_auto: number[];
|
|
239
|
-
skill_score_multi: number[];
|
|
240
|
-
fever_score: number;
|
|
241
|
-
fever_end_time: number;
|
|
242
|
-
tap_count: number;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
278
|
declare class LiveCalculator {
|
|
246
279
|
private readonly dataProvider;
|
|
247
280
|
private readonly deckCalculator;
|
|
@@ -249,6 +282,8 @@ declare class LiveCalculator {
|
|
|
249
282
|
getMusicMeta(musicId: number, musicDiff: string): Promise<MusicMeta>;
|
|
250
283
|
private static getBaseScore;
|
|
251
284
|
private static getSkillScore;
|
|
285
|
+
private static getSortedSkillDetails;
|
|
286
|
+
private static getSortedSkillRate;
|
|
252
287
|
static getLiveDetailByDeck(deckDetail: DeckDetail, musicMeta: MusicMeta, liveType: LiveType, skillDetails?: DeckCardSkillDetail[] | undefined, multiPowerSum?: number): LiveDetail;
|
|
253
288
|
private static getMultiLiveSkill;
|
|
254
289
|
private static getSoloLiveSkill;
|
|
@@ -283,10 +318,42 @@ declare class EventCalculator {
|
|
|
283
318
|
static getDeckEventPoint(deckCards: CardDetail[], honorBonus: number, musicMeta: MusicMeta, liveType: LiveType): number;
|
|
284
319
|
}
|
|
285
320
|
|
|
321
|
+
interface Area {
|
|
322
|
+
id: number;
|
|
323
|
+
assetbundleName: string;
|
|
324
|
+
areaType: string;
|
|
325
|
+
viewType: string;
|
|
326
|
+
name: string;
|
|
327
|
+
releaseConditionId: number;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
declare class AreaItemRecommend {
|
|
331
|
+
private readonly dataProvider;
|
|
332
|
+
private readonly areaItemService;
|
|
333
|
+
private readonly deckCalculator;
|
|
334
|
+
constructor(dataProvider: DataProvider);
|
|
335
|
+
private static findCost;
|
|
336
|
+
private getRecommendAreaItem;
|
|
337
|
+
recommendAreaItem(userCards: UserCard[]): Promise<RecommendAreaItem[]>;
|
|
338
|
+
}
|
|
339
|
+
interface RecommendAreaItem {
|
|
340
|
+
area: Area;
|
|
341
|
+
areaItem: AreaItem;
|
|
342
|
+
areaItemLevel: AreaItemLevel;
|
|
343
|
+
shopItem: ShopItem;
|
|
344
|
+
cost: {
|
|
345
|
+
coin: number;
|
|
346
|
+
seed: number;
|
|
347
|
+
szk: number;
|
|
348
|
+
};
|
|
349
|
+
power: number;
|
|
350
|
+
}
|
|
351
|
+
|
|
286
352
|
declare class BaseDeckRecommend {
|
|
287
353
|
private readonly dataProvider;
|
|
288
354
|
private readonly cardCalculator;
|
|
289
355
|
private readonly deckCalculator;
|
|
356
|
+
private readonly areaItemService;
|
|
290
357
|
constructor(dataProvider: DataProvider);
|
|
291
358
|
private static findBestCards;
|
|
292
359
|
recommendHighScoreDeck(userCards: UserCard[], scoreFunc: ScoreFunction, { musicMeta, limit, member, cardConfig }: DeckRecommendConfig, eventId?: number, isChallengeLive?: boolean): Promise<RecommendDeck[]>;
|
|
@@ -635,4 +702,4 @@ interface UserHonor {
|
|
|
635
702
|
level: number;
|
|
636
703
|
}
|
|
637
704
|
|
|
638
|
-
export { AreaItemLevel, BaseDeckRecommend, CachedDataProvider, Card, CardCalculator, CardConfig, CardDetail, CardEpisode, CardEventCalculator, CardPowerCalculator, CardRarity, CardSkillCalculator, ChallengeLiveDeckRecommend, CharacterRank, DataProvider, DeckCalculator, DeckCardDetail, DeckCardSkillDetail, DeckDetail, DeckRecommendConfig, DeckService, Event, EventCalculator, EventCard, EventDeckBonus, EventDeckRecommend, EventExchange, EventRarityBonusRate, GameCharacter, GameCharacterUnit, Honor, LiveCalculator, LiveDetail, LiveSkill, LiveType, MasterLesson, Music, MusicDifficulty, MusicMeta, MusicRecommend, MusicVocal, RecommendDeck, ScoreFunction, Skill, User, UserArea, UserCard, UserChallengeLiveSoloDeck, UserCharacter, UserDeck, UserHonor };
|
|
705
|
+
export { Area, AreaItem, AreaItemLevel, AreaItemRecommend, AreaItemService, BaseDeckRecommend, CachedDataProvider, Card, CardCalculator, CardConfig, CardDetail, CardEpisode, CardEventCalculator, CardPowerCalculator, CardRarity, CardSkillCalculator, ChallengeLiveDeckRecommend, CharacterRank, DataProvider, DeckCalculator, DeckCardDetail, DeckCardSkillDetail, DeckDetail, DeckRecommendConfig, DeckService, Event, EventCalculator, EventCard, EventDeckBonus, EventDeckRecommend, EventExchange, EventRarityBonusRate, GameCharacter, GameCharacterUnit, Honor, LiveCalculator, LiveDetail, LiveSkill, LiveType, MasterLesson, Music, MusicDifficulty, MusicMeta, MusicRecommend, MusicVocal, RecommendAreaItem, RecommendDeck, ScoreFunction, ShopItem, Skill, User, UserArea, UserCard, UserChallengeLiveSoloDeck, UserCharacter, UserDeck, UserHonor };
|
package/dist/index.mjs
CHANGED
|
@@ -75,6 +75,37 @@ class CachedDataProvider {
|
|
|
75
75
|
async getUserDataAll() {
|
|
76
76
|
return await this.getData(this.instanceCache, 'userData', async () => await this.dataProvider.getUserDataAll());
|
|
77
77
|
}
|
|
78
|
+
async preloadMasterData(keys) {
|
|
79
|
+
return await Promise.all(keys.map(async (it) => await this.getMasterData(it)));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class AreaItemService {
|
|
84
|
+
dataProvider;
|
|
85
|
+
constructor(dataProvider) {
|
|
86
|
+
this.dataProvider = dataProvider;
|
|
87
|
+
}
|
|
88
|
+
async getAreaItemLevels() {
|
|
89
|
+
const userAreas = await this.dataProvider.getUserData('userAreas');
|
|
90
|
+
return await Promise.all(userAreas.flatMap(it => it.areaItems)
|
|
91
|
+
.map(async (it) => await this.getAreaItemLevel(it.areaItemId, it.level)));
|
|
92
|
+
}
|
|
93
|
+
async getAreaItemLevel(areaItemId, level) {
|
|
94
|
+
const areaItemLevels = await this.dataProvider.getMasterData('areaItemLevels');
|
|
95
|
+
return findOrThrow(areaItemLevels, it => it.areaItemId === areaItemId && it.level === level);
|
|
96
|
+
}
|
|
97
|
+
async getAreaItemNextLevel(areaItem, areaItemLevel) {
|
|
98
|
+
const level = areaItemLevel === undefined ? 1 : (areaItemLevel.level === 15 ? 15 : areaItemLevel.level + 1);
|
|
99
|
+
return await this.getAreaItemLevel(areaItem.id, level);
|
|
100
|
+
}
|
|
101
|
+
async getShopItem(areaItemLevel) {
|
|
102
|
+
const shopItems = await this.dataProvider.getMasterData('shopItems');
|
|
103
|
+
const idOffset = areaItemLevel.level <= 10
|
|
104
|
+
? (1000 + (areaItemLevel.areaItemId - 1) * 10)
|
|
105
|
+
: (1550 - 10 + (areaItemLevel.areaItemId - 1) * 5);
|
|
106
|
+
const id = idOffset + areaItemLevel.level;
|
|
107
|
+
return findOrThrow(shopItems, it => it.id === id);
|
|
108
|
+
}
|
|
78
109
|
}
|
|
79
110
|
|
|
80
111
|
class DeckService {
|
|
@@ -187,8 +218,12 @@ class CardPowerCalculator {
|
|
|
187
218
|
return basePower.reduce((v, it) => v + it, 0) + characterBonus + await this.getAreaItemBonusPower(userAreaItemLevels, basePower, card.characterId, unit, sameUnit, card.attr, sameAttr);
|
|
188
219
|
}
|
|
189
220
|
async getCardBasePowers(userCard, card) {
|
|
190
|
-
const
|
|
191
|
-
|
|
221
|
+
const p = await Promise.all([
|
|
222
|
+
await this.dataProvider.getMasterData('cardEpisodes'),
|
|
223
|
+
await this.dataProvider.getMasterData('masterLessons')
|
|
224
|
+
]);
|
|
225
|
+
const cardEpisodes = p[0];
|
|
226
|
+
const masterLessons = p[1];
|
|
192
227
|
const ret = [0, 0, 0];
|
|
193
228
|
const cardParameters = card.cardParameters.filter(it => it.cardLevel === userCard.level);
|
|
194
229
|
const params = ['param1', 'param2', 'param3'];
|
|
@@ -328,11 +363,13 @@ class CardCalculator {
|
|
|
328
363
|
powerCalculator;
|
|
329
364
|
skillCalculator;
|
|
330
365
|
eventCalculator;
|
|
366
|
+
areaItemService;
|
|
331
367
|
constructor(dataProvider) {
|
|
332
368
|
this.dataProvider = dataProvider;
|
|
333
369
|
this.powerCalculator = new CardPowerCalculator(dataProvider);
|
|
334
370
|
this.skillCalculator = new CardSkillCalculator(dataProvider);
|
|
335
371
|
this.eventCalculator = new CardEventCalculator(dataProvider);
|
|
372
|
+
this.areaItemService = new AreaItemService(dataProvider);
|
|
336
373
|
}
|
|
337
374
|
async getCardUnits(card) {
|
|
338
375
|
const gameCharacters = await this.dataProvider.getMasterData('gameCharacters');
|
|
@@ -358,7 +395,9 @@ class CardCalculator {
|
|
|
358
395
|
}
|
|
359
396
|
}
|
|
360
397
|
if (episodeRead && ret.episodes !== undefined) {
|
|
361
|
-
ret.episodes.forEach(it => {
|
|
398
|
+
ret.episodes.forEach(it => {
|
|
399
|
+
it.scenarioStatus = 'already_read';
|
|
400
|
+
});
|
|
362
401
|
}
|
|
363
402
|
if (masterMax) {
|
|
364
403
|
ret.masterRank = 5;
|
|
@@ -391,11 +430,11 @@ class CardCalculator {
|
|
|
391
430
|
eventBonus
|
|
392
431
|
};
|
|
393
432
|
}
|
|
394
|
-
async batchGetCardDetail(userCards, config = {}, eventId = 0) {
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return await Promise.all(userCards.map(async (it) => await this.getCardDetail(it,
|
|
433
|
+
async batchGetCardDetail(userCards, config = {}, eventId = 0, areaItemLevels) {
|
|
434
|
+
const areaItemLevels0 = areaItemLevels === undefined
|
|
435
|
+
? await this.areaItemService.getAreaItemLevels()
|
|
436
|
+
: areaItemLevels;
|
|
437
|
+
return await Promise.all(userCards.map(async (it) => await this.getCardDetail(it, areaItemLevels0, config, eventId)));
|
|
399
438
|
}
|
|
400
439
|
static isCertainlyLessThan(cardDetail0, cardDetail1) {
|
|
401
440
|
return cardDetail0.power.isCertainlyLessThen(cardDetail1.power) &&
|
|
@@ -440,21 +479,47 @@ class LiveCalculator {
|
|
|
440
479
|
return musicMeta.skill_score_auto;
|
|
441
480
|
}
|
|
442
481
|
}
|
|
482
|
+
static getSortedSkillDetails(deckDetail, liveType, skillDetails = undefined) {
|
|
483
|
+
if (skillDetails !== undefined && skillDetails.length === 6 && skillDetails[5].scoreUp > 0) {
|
|
484
|
+
return {
|
|
485
|
+
details: skillDetails,
|
|
486
|
+
sorted: false
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
if (liveType === LiveType.MULTI) {
|
|
490
|
+
return {
|
|
491
|
+
details: duplicateObj(LiveCalculator.getMultiLiveSkill(deckDetail), 6),
|
|
492
|
+
sorted: false
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
const sortedSkill = [...deckDetail.cards].map(it => it.skill)
|
|
496
|
+
.sort((a, b) => a.scoreUp - b.scoreUp);
|
|
497
|
+
const emptySkill = duplicateObj({
|
|
498
|
+
scoreUp: 0,
|
|
499
|
+
lifeRecovery: 0
|
|
500
|
+
}, 5 - sortedSkill.length);
|
|
501
|
+
return {
|
|
502
|
+
details: [...sortedSkill, ...emptySkill, deckDetail.cards[0].skill],
|
|
503
|
+
sorted: true
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
static getSortedSkillRate(sorted, cardLength, skillScores) {
|
|
507
|
+
if (!sorted) {
|
|
508
|
+
return skillScores;
|
|
509
|
+
}
|
|
510
|
+
return [
|
|
511
|
+
...skillScores.slice(0, cardLength).sort((a, b) => a - b),
|
|
512
|
+
...skillScores.slice(cardLength)
|
|
513
|
+
];
|
|
514
|
+
}
|
|
443
515
|
static getLiveDetailByDeck(deckDetail, musicMeta, liveType, skillDetails = undefined, multiPowerSum = 0) {
|
|
444
|
-
const skills =
|
|
445
|
-
? skillDetails
|
|
446
|
-
: (liveType === LiveType.MULTI
|
|
447
|
-
? duplicateObj(LiveCalculator.getMultiLiveSkill(deckDetail), 6)
|
|
448
|
-
: [...[...deckDetail.cards].map(it => it.skill)
|
|
449
|
-
.sort((a, b) => a.scoreUp - b.scoreUp),
|
|
450
|
-
deckDetail.cards[0].skill]);
|
|
516
|
+
const skills = this.getSortedSkillDetails(deckDetail, liveType, skillDetails);
|
|
451
517
|
const baseRate = LiveCalculator.getBaseScore(musicMeta, liveType);
|
|
452
518
|
const skillScores = [...LiveCalculator.getSkillScore(musicMeta, liveType)];
|
|
453
|
-
const skillRate =
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const
|
|
457
|
-
const life = skills.reduce((v, it) => v + it.lifeRecovery, 0);
|
|
519
|
+
const skillRate = LiveCalculator.getSortedSkillRate(skills.sorted, deckDetail.cards.length, skillScores);
|
|
520
|
+
const rate = baseRate + skills.details
|
|
521
|
+
.reduce((v, it, i) => v + it.scoreUp * skillRate[i] / 100, 0);
|
|
522
|
+
const life = skills.details.reduce((v, it) => v + it.lifeRecovery, 0);
|
|
458
523
|
const powerSum = multiPowerSum === 0 ? 5 * deckDetail.power : multiPowerSum;
|
|
459
524
|
const activeBonus = liveType === LiveType.MULTI ? 5 * 0.015 * powerSum : 0;
|
|
460
525
|
return {
|
|
@@ -610,8 +675,56 @@ class DeckCalculator {
|
|
|
610
675
|
cards
|
|
611
676
|
};
|
|
612
677
|
}
|
|
613
|
-
async getDeckDetail(deckCards) {
|
|
614
|
-
return DeckCalculator.getDeckDetailByCards(await this.cardCalculator.batchGetCardDetail(deckCards), await this.getHonorBonusPower());
|
|
678
|
+
async getDeckDetail(deckCards, eventId = 0, areaItemLevels) {
|
|
679
|
+
return DeckCalculator.getDeckDetailByCards(await this.cardCalculator.batchGetCardDetail(deckCards, {}, eventId, areaItemLevels), await this.getHonorBonusPower());
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
class AreaItemRecommend {
|
|
684
|
+
dataProvider;
|
|
685
|
+
areaItemService;
|
|
686
|
+
deckCalculator;
|
|
687
|
+
constructor(dataProvider) {
|
|
688
|
+
this.dataProvider = dataProvider;
|
|
689
|
+
this.areaItemService = new AreaItemService(dataProvider);
|
|
690
|
+
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
691
|
+
}
|
|
692
|
+
static findCost(shopItem, resourceType, resourceId) {
|
|
693
|
+
const cost = shopItem.costs.map(it => it.cost)
|
|
694
|
+
.find(it => it.resourceType === resourceType && it.resourceId === resourceId);
|
|
695
|
+
return cost === undefined ? 0 : cost.quantity;
|
|
696
|
+
}
|
|
697
|
+
async getRecommendAreaItem(areaItem, areaItemLevel, power) {
|
|
698
|
+
const areas = await this.dataProvider.getMasterData('areas');
|
|
699
|
+
const area = findOrThrow(areas, it => it.id === areaItem.areaId);
|
|
700
|
+
const shopItem = await this.areaItemService.getShopItem(areaItemLevel);
|
|
701
|
+
return {
|
|
702
|
+
area,
|
|
703
|
+
areaItem,
|
|
704
|
+
areaItemLevel,
|
|
705
|
+
shopItem,
|
|
706
|
+
cost: {
|
|
707
|
+
coin: AreaItemRecommend.findCost(shopItem, 'coin', 0),
|
|
708
|
+
seed: AreaItemRecommend.findCost(shopItem, 'material', 17),
|
|
709
|
+
szk: AreaItemRecommend.findCost(shopItem, 'material', 57)
|
|
710
|
+
},
|
|
711
|
+
power
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
async recommendAreaItem(userCards) {
|
|
715
|
+
const areaItems = await this.dataProvider.getMasterData('areaItems');
|
|
716
|
+
const currentAreaItemLevels = await this.areaItemService.getAreaItemLevels();
|
|
717
|
+
const { power: currentPower } = await this.deckCalculator.getDeckDetail(userCards, 0, currentAreaItemLevels);
|
|
718
|
+
const recommend = await Promise.all(areaItems.map(async (areaItem) => {
|
|
719
|
+
const newAreaItemLevel = await this.areaItemService.getAreaItemNextLevel(areaItem, currentAreaItemLevels.find(it => it.areaItemId === areaItem.id));
|
|
720
|
+
const newAreaItemLevels = [
|
|
721
|
+
...currentAreaItemLevels.filter(it => it.areaItemId !== areaItem.id), newAreaItemLevel
|
|
722
|
+
];
|
|
723
|
+
const { power: newPower } = await this.deckCalculator.getDeckDetail(userCards, 0, newAreaItemLevels);
|
|
724
|
+
return await this.getRecommendAreaItem(areaItem, newAreaItemLevel, newPower - currentPower);
|
|
725
|
+
}));
|
|
726
|
+
return recommend.filter(it => it.power > 0)
|
|
727
|
+
.sort((a, b) => b.power / b.cost.coin - a.power / a.cost.coin);
|
|
615
728
|
}
|
|
616
729
|
}
|
|
617
730
|
|
|
@@ -761,12 +874,17 @@ class BaseDeckRecommend {
|
|
|
761
874
|
dataProvider;
|
|
762
875
|
cardCalculator;
|
|
763
876
|
deckCalculator;
|
|
877
|
+
areaItemService;
|
|
764
878
|
constructor(dataProvider) {
|
|
765
879
|
this.dataProvider = dataProvider;
|
|
766
880
|
this.cardCalculator = new CardCalculator(dataProvider);
|
|
767
881
|
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
882
|
+
this.areaItemService = new AreaItemService(dataProvider);
|
|
768
883
|
}
|
|
769
884
|
static findBestCards(cardDetails, scoreFunc, limit = 1, isChallengeLive = false, member = 5, honorBonus = 0, deckCards = [], deckCharacters = []) {
|
|
885
|
+
if (isChallengeLive) {
|
|
886
|
+
member = Math.min(member, cardDetails.length);
|
|
887
|
+
}
|
|
770
888
|
if (deckCards.length === member) {
|
|
771
889
|
const score = scoreFunc(deckCards);
|
|
772
890
|
const deckDetail = DeckCalculator.getDeckDetailByCards(deckCards, honorBonus);
|
|
@@ -821,7 +939,8 @@ class BaseDeckRecommend {
|
|
|
821
939
|
return ans;
|
|
822
940
|
}
|
|
823
941
|
async recommendHighScoreDeck(userCards, scoreFunc, { musicMeta, limit = 1, member = 5, cardConfig = {} }, eventId = 0, isChallengeLive = false) {
|
|
824
|
-
const
|
|
942
|
+
const areaItemLevels = await this.areaItemService.getAreaItemLevels();
|
|
943
|
+
const cards = await this.cardCalculator.batchGetCardDetail(userCards, cardConfig, eventId, areaItemLevels);
|
|
825
944
|
const honorBonus = await this.deckCalculator.getHonorBonusPower();
|
|
826
945
|
let priority = (isChallengeLive || eventId === 0) ? 10 : 0;
|
|
827
946
|
while (priority < 10) {
|
|
@@ -907,4 +1026,4 @@ class MusicRecommend {
|
|
|
907
1026
|
}
|
|
908
1027
|
}
|
|
909
1028
|
|
|
910
|
-
export { BaseDeckRecommend, CachedDataProvider, CardCalculator, CardEventCalculator, CardPowerCalculator, CardSkillCalculator, ChallengeLiveDeckRecommend, DeckCalculator, DeckService, EventCalculator, EventDeckRecommend, LiveCalculator, LiveType, MusicRecommend };
|
|
1029
|
+
export { AreaItemRecommend, AreaItemService, BaseDeckRecommend, CachedDataProvider, CardCalculator, CardEventCalculator, CardPowerCalculator, CardSkillCalculator, ChallengeLiveDeckRecommend, DeckCalculator, DeckService, EventCalculator, EventDeckRecommend, LiveCalculator, LiveType, MusicRecommend };
|