sekai-calculator 0.0.7 → 0.1.0
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/README.md +3 -2
- package/dist/index.cjs +409 -0
- package/dist/index.d.ts +93 -93
- package/dist/index.mjs +403 -0
- package/package.json +5 -5
- package/dist/index.es.js +0 -75
- package/dist/index.js +0 -77
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# sekai-calculator
|
|
2
2
|
Project SEKAI Calculator for deck power, live score, event point and more.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
This project is fully made up in TypeScript, while reducing `any` as possible.
|
|
5
|
+
|
|
6
|
+
Both ECMAScript Module `index.mjs` and CommonJS `index.cjs` are provided with types `index.d.ts` for TypeScript.
|
|
6
7
|
|
|
7
8
|
## Quick Start
|
|
8
9
|
W.I.P.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function findOrThrow(arr, p) {
|
|
4
|
+
const result = arr.find(p);
|
|
5
|
+
if (result === undefined)
|
|
6
|
+
throw new Error('object not found');
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
function getOrThrow(map, key) {
|
|
10
|
+
const value = map.get(key);
|
|
11
|
+
if (value === undefined)
|
|
12
|
+
throw new Error('key not found');
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
function getOrDefault(map, key, defaultValue) {
|
|
16
|
+
const value = map.get(key);
|
|
17
|
+
if (value === undefined)
|
|
18
|
+
return defaultValue;
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
function computeWithDefault(map, key, defaultValue, action) {
|
|
22
|
+
map.set(key, action(getOrDefault(map, key, defaultValue)));
|
|
23
|
+
}
|
|
24
|
+
function duplicateObj(obj, times) {
|
|
25
|
+
const ret = [];
|
|
26
|
+
for (let i = 0; i < times; ++i)
|
|
27
|
+
ret.push(obj);
|
|
28
|
+
return ret;
|
|
29
|
+
}
|
|
30
|
+
function mapOrUndefined(arr, fun) {
|
|
31
|
+
if (arr === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
return arr.map(fun);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class CardPowerCalculator {
|
|
37
|
+
dataProvider;
|
|
38
|
+
constructor(dataProvider) {
|
|
39
|
+
this.dataProvider = dataProvider;
|
|
40
|
+
}
|
|
41
|
+
async getCardPower(userCard, card, cardUnits, userAreaItemLevels) {
|
|
42
|
+
const ret = new CardDetailMap();
|
|
43
|
+
const basePower = await this.getCardBasePowers(userCard, card);
|
|
44
|
+
const characterBonus = await this.getCharacterBonusPower(basePower, card.characterId);
|
|
45
|
+
for (const unit of cardUnits) {
|
|
46
|
+
ret.set(unit, 5, 5, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, true, true));
|
|
47
|
+
ret.set(unit, 5, 1, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, true, false));
|
|
48
|
+
ret.set(unit, 1, 5, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, false, true));
|
|
49
|
+
ret.set(unit, 1, 1, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, false, false));
|
|
50
|
+
}
|
|
51
|
+
return ret;
|
|
52
|
+
}
|
|
53
|
+
async getPower(card, basePower, characterBonus, userAreaItemLevels, unit, sameUnit, sameAttr) {
|
|
54
|
+
return basePower.reduce((v, it) => v + it, 0) + characterBonus + await this.getAreaItemBonusPower(userAreaItemLevels, basePower, card.characterId, unit, sameUnit, card.attr, sameAttr);
|
|
55
|
+
}
|
|
56
|
+
async getCardBasePowers(userCard, card) {
|
|
57
|
+
const cardEpisodes = await this.dataProvider.getMasterData('cardEpisodes');
|
|
58
|
+
const masterLessons = await this.dataProvider.getMasterData('masterLessons');
|
|
59
|
+
const ret = [0, 0, 0];
|
|
60
|
+
const cardParameters = card.cardParameters.filter(it => it.cardLevel === userCard.level);
|
|
61
|
+
const params = ['param1', 'param2', 'param3'];
|
|
62
|
+
params.forEach((param, i) => {
|
|
63
|
+
ret[i] = findOrThrow(cardParameters, it => it.cardParameterType === param).power;
|
|
64
|
+
});
|
|
65
|
+
if (userCard.specialTrainingStatus === 'done') {
|
|
66
|
+
ret[0] += card.specialTrainingPower1BonusFixed;
|
|
67
|
+
ret[1] += card.specialTrainingPower2BonusFixed;
|
|
68
|
+
ret[2] += card.specialTrainingPower3BonusFixed;
|
|
69
|
+
}
|
|
70
|
+
const episodes = userCard.episodes.filter(it => it.scenarioStatus === 'already_read')
|
|
71
|
+
.map(it => findOrThrow(cardEpisodes, e => e.id === it.cardEpisodeId));
|
|
72
|
+
for (const episode of episodes) {
|
|
73
|
+
ret[0] += episode.power1BonusFixed;
|
|
74
|
+
ret[1] += episode.power2BonusFixed;
|
|
75
|
+
ret[2] += episode.power3BonusFixed;
|
|
76
|
+
}
|
|
77
|
+
const usedMasterLessons = masterLessons
|
|
78
|
+
.filter((it) => it.cardRarityType === card.cardRarityType && it.masterRank <= userCard.masterRank);
|
|
79
|
+
for (const masterLesson of usedMasterLessons) {
|
|
80
|
+
ret[0] += masterLesson.power1BonusFixed;
|
|
81
|
+
ret[1] += masterLesson.power2BonusFixed;
|
|
82
|
+
ret[2] += masterLesson.power3BonusFixed;
|
|
83
|
+
}
|
|
84
|
+
return ret;
|
|
85
|
+
}
|
|
86
|
+
async getAreaItemBonusPower(userAreaItemLevels, basePower, characterId, unit, sameUnit, attr, sameAttr) {
|
|
87
|
+
const usedAreaItems = userAreaItemLevels.filter(it => (it.targetUnit === 'any' || it.targetUnit === unit) &&
|
|
88
|
+
(it.targetCardAttr === 'any' || it.targetCardAttr === attr) &&
|
|
89
|
+
(it.targetGameCharacterId === undefined || it.targetGameCharacterId === characterId));
|
|
90
|
+
const areaItemBonus = [0, 0, 0];
|
|
91
|
+
for (const areaItem of usedAreaItems) {
|
|
92
|
+
const allMatch = (areaItem.targetUnit !== 'any' && sameUnit) || (areaItem.targetCardAttr !== 'any' && sameAttr);
|
|
93
|
+
areaItemBonus[0] += allMatch ? areaItem.power1AllMatchBonusRate : areaItem.power1BonusRate;
|
|
94
|
+
areaItemBonus[1] += allMatch ? areaItem.power2AllMatchBonusRate : areaItem.power2BonusRate;
|
|
95
|
+
areaItemBonus[2] += allMatch ? areaItem.power3AllMatchBonusRate : areaItem.power3BonusRate;
|
|
96
|
+
}
|
|
97
|
+
return basePower.reduce((v, it, i) => v + Math.floor(it * areaItemBonus[i] / 100), 0);
|
|
98
|
+
}
|
|
99
|
+
async getCharacterBonusPower(basePower, characterId) {
|
|
100
|
+
const characterRanks = await this.dataProvider.getMasterData('characterRanks');
|
|
101
|
+
const userCharacters = await this.dataProvider.getUserData('userCharacters');
|
|
102
|
+
const userCharacter = findOrThrow(userCharacters, it => it.characterId === characterId);
|
|
103
|
+
const characterRank = findOrThrow(characterRanks, it => it.characterId === userCharacter.characterId && it.characterRank === userCharacter.characterRank);
|
|
104
|
+
const rates = [characterRank.power1BonusRate, characterRank.power2BonusRate, characterRank.power3BonusRate];
|
|
105
|
+
return rates.reduce((v, it, i) => v + Math.floor(basePower[i] * it / 100), 0);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class CardSkillCalculator {
|
|
110
|
+
dataProvider;
|
|
111
|
+
constructor(dataProvider) {
|
|
112
|
+
this.dataProvider = dataProvider;
|
|
113
|
+
}
|
|
114
|
+
async getCardSkill(userCard, card) {
|
|
115
|
+
const scoreUpMap = new CardDetailMap();
|
|
116
|
+
const detail = await this.getSkillDetail(userCard, card);
|
|
117
|
+
if (detail.scoreUpEnhance !== undefined) {
|
|
118
|
+
for (let i = 1; i <= 5; ++i) {
|
|
119
|
+
const scoreUp = detail.scoreUp + (i === 5 ? 5 : (i - 1)) * detail.scoreUpEnhance.value;
|
|
120
|
+
scoreUpMap.set(detail.scoreUpEnhance.unit, i, 1, scoreUp);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
scoreUpMap.set('any', 1, 1, detail.scoreUp);
|
|
124
|
+
return { scoreUp: scoreUpMap, lifeRecovery: detail.lifeRecovery };
|
|
125
|
+
}
|
|
126
|
+
async getSkillDetail(userCard, card) {
|
|
127
|
+
const skills = await this.dataProvider.getMasterData('skills');
|
|
128
|
+
const skill = findOrThrow(skills, it => it.id === card.skillId);
|
|
129
|
+
const ret = { scoreUp: 0, lifeRecovery: 0 };
|
|
130
|
+
for (const skillEffect of skill.skillEffects) {
|
|
131
|
+
const skillEffectDetail = findOrThrow(skillEffect.skillEffectDetails, it => it.level === userCard.skillLevel);
|
|
132
|
+
if (skillEffect.skillEffectType === 'score_up' ||
|
|
133
|
+
skillEffect.skillEffectType === 'score_up_condition_life' ||
|
|
134
|
+
skillEffect.skillEffectType === 'score_up_keep') {
|
|
135
|
+
const current = skillEffectDetail.activateEffectValue;
|
|
136
|
+
if (skillEffect.skillEnhance !== undefined) {
|
|
137
|
+
ret.scoreUpEnhance = {
|
|
138
|
+
unit: skillEffect.skillEnhance.skillEnhanceCondition.unit,
|
|
139
|
+
value: skillEffect.skillEnhance.activateEffectValue
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
ret.scoreUp = Math.max(ret.scoreUp, current);
|
|
143
|
+
}
|
|
144
|
+
else if (skillEffect.skillEffectType === 'life_recovery') {
|
|
145
|
+
ret.lifeRecovery += skillEffectDetail.activateEffectValue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return ret;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class CardCalculator {
|
|
153
|
+
dataProvider;
|
|
154
|
+
powerCalculator;
|
|
155
|
+
skillCalculator;
|
|
156
|
+
constructor(dataProvider) {
|
|
157
|
+
this.dataProvider = dataProvider;
|
|
158
|
+
this.powerCalculator = new CardPowerCalculator(dataProvider);
|
|
159
|
+
this.skillCalculator = new CardSkillCalculator(dataProvider);
|
|
160
|
+
}
|
|
161
|
+
async getCardUnits(card) {
|
|
162
|
+
const gameCharacters = await this.dataProvider.getMasterData('gameCharacters');
|
|
163
|
+
const units = [];
|
|
164
|
+
if (card.supportUnit !== 'none')
|
|
165
|
+
units.push(card.supportUnit);
|
|
166
|
+
units.push(findOrThrow(gameCharacters, it => it.id === card.characterId).unit);
|
|
167
|
+
return units;
|
|
168
|
+
}
|
|
169
|
+
async getCardDetail(userCard, userAreaItemLevels) {
|
|
170
|
+
const cards = await this.dataProvider.getMasterData('cards');
|
|
171
|
+
const card = findOrThrow(cards, it => it.id === userCard.cardId);
|
|
172
|
+
const units = await this.getCardUnits(card);
|
|
173
|
+
const skill = await this.skillCalculator.getCardSkill(userCard, card);
|
|
174
|
+
const power = await this.powerCalculator.getCardPower(userCard, card, units, userAreaItemLevels);
|
|
175
|
+
return {
|
|
176
|
+
cardId: card.id,
|
|
177
|
+
units,
|
|
178
|
+
attr: card.attr,
|
|
179
|
+
power,
|
|
180
|
+
scoreSkill: skill.scoreUp,
|
|
181
|
+
lifeSkill: skill.lifeRecovery
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
class CardDetailMap {
|
|
186
|
+
min = Number.MAX_SAFE_INTEGER;
|
|
187
|
+
max = Number.MIN_SAFE_INTEGER;
|
|
188
|
+
values = new Map();
|
|
189
|
+
set(unit, unitMember, attrMember, value) {
|
|
190
|
+
this.min = Math.min(this.min, value);
|
|
191
|
+
this.max = Math.max(this.max, value);
|
|
192
|
+
this.values.set(CardDetailMap.getKey(unit, unitMember, attrMember), value);
|
|
193
|
+
}
|
|
194
|
+
get(unit, unitMember, attrMember) {
|
|
195
|
+
const attrMember0 = attrMember === 5 ? 5 : 1;
|
|
196
|
+
let best = this.getInternal(unit, unitMember, attrMember0);
|
|
197
|
+
if (best !== undefined)
|
|
198
|
+
return best;
|
|
199
|
+
best = this.getInternal(unit, unitMember === 5 ? 5 : 1, attrMember0);
|
|
200
|
+
if (best !== undefined)
|
|
201
|
+
return best;
|
|
202
|
+
best = this.getInternal('any', 1, 1);
|
|
203
|
+
if (best !== undefined)
|
|
204
|
+
return best;
|
|
205
|
+
throw new Error('case not found');
|
|
206
|
+
}
|
|
207
|
+
getInternal(unit, unitMember, attrMember) {
|
|
208
|
+
return this.values.get(CardDetailMap.getKey(unit, unitMember, attrMember));
|
|
209
|
+
}
|
|
210
|
+
static getKey(unit, unitMember, attrMember) {
|
|
211
|
+
return `${unit}-${unitMember}-${attrMember}`;
|
|
212
|
+
}
|
|
213
|
+
isCertainlyLessThen(another) {
|
|
214
|
+
return this.max < another.min;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
class DeckCalculator {
|
|
219
|
+
dataProvider;
|
|
220
|
+
cardCalculator;
|
|
221
|
+
constructor(dataProvider) {
|
|
222
|
+
this.dataProvider = dataProvider;
|
|
223
|
+
this.cardCalculator = new CardCalculator(dataProvider);
|
|
224
|
+
}
|
|
225
|
+
async getDeck(deckId) {
|
|
226
|
+
const userDecks = await this.dataProvider.getUserData('userDecks');
|
|
227
|
+
return findOrThrow(userDecks, it => it.deckId === deckId);
|
|
228
|
+
}
|
|
229
|
+
async getDeckCards(userDeck) {
|
|
230
|
+
const userCards = await this.dataProvider.getUserData('userCards');
|
|
231
|
+
const cardIds = [userDeck.member1, userDeck.member2, userDeck.member3, userDeck.member4, userDeck.member5];
|
|
232
|
+
return cardIds.map(id => findOrThrow(userCards, it => it.cardId === id));
|
|
233
|
+
}
|
|
234
|
+
async getDeckCardsById(deckId) {
|
|
235
|
+
return await this.getDeckCards(await this.getDeck(deckId));
|
|
236
|
+
}
|
|
237
|
+
async getHonorBonusPower() {
|
|
238
|
+
const honors = await this.dataProvider.getMasterData('honors');
|
|
239
|
+
const userHonors = await this.dataProvider.getUserData('userHonors');
|
|
240
|
+
return userHonors
|
|
241
|
+
.map(userHonor => {
|
|
242
|
+
const honor = findOrThrow(honors, it => it.id === userHonor.honorId);
|
|
243
|
+
return findOrThrow(honor.levels, it => it.level === userHonor.level);
|
|
244
|
+
})
|
|
245
|
+
.reduce((v, it) => v + it.bonus, 0);
|
|
246
|
+
}
|
|
247
|
+
async getDeckDetailByCards(cardDetails, honorBonus) {
|
|
248
|
+
const map = new Map();
|
|
249
|
+
for (const cardDetail of cardDetails) {
|
|
250
|
+
computeWithDefault(map, cardDetail.attr, 0, it => it + 1);
|
|
251
|
+
cardDetail.units.forEach(key => { computeWithDefault(map, key, 0, it => it + 1); });
|
|
252
|
+
}
|
|
253
|
+
const power = cardDetails.reduce((v, cardDetail) => v + cardDetail.units.reduce((vv, unit) => Math.max(vv, cardDetail.power.get(unit, getOrThrow(map, unit), getOrThrow(map, cardDetail.attr))), 0), 0) + honorBonus;
|
|
254
|
+
const skill = cardDetails.map(cardDetail => {
|
|
255
|
+
const scoreUp = cardDetail.units.reduce((vv, unit) => Math.max(vv, cardDetail.scoreSkill.get(unit, getOrThrow(map, unit), 1)), 0);
|
|
256
|
+
return {
|
|
257
|
+
cardId: cardDetail.cardId,
|
|
258
|
+
scoreUp,
|
|
259
|
+
lifeRecovery: cardDetail.lifeSkill
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
return { power, skill };
|
|
263
|
+
}
|
|
264
|
+
async getDeckDetail(deckCards) {
|
|
265
|
+
const areaItemLevels = await this.dataProvider.getMasterData('areaItemLevels');
|
|
266
|
+
const userAreas = await this.dataProvider.getUserData('userAreas');
|
|
267
|
+
const userItemLevels = userAreas.flatMap(it => it.areaItems).map(areaItem => findOrThrow(areaItemLevels, it => it.areaItemId === areaItem.areaItemId && it.level === areaItem.level));
|
|
268
|
+
const cardDetails = await Promise.all(deckCards.map(async (it) => await this.cardCalculator.getCardDetail(it, userItemLevels)));
|
|
269
|
+
return await this.getDeckDetailByCards(cardDetails, await this.getHonorBonusPower());
|
|
270
|
+
}
|
|
271
|
+
async getDeckDetailById(deckId) {
|
|
272
|
+
return await this.getDeckDetail(await this.getDeckCardsById(deckId));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
class CardEventCalculator {
|
|
277
|
+
dataProvider;
|
|
278
|
+
constructor(dataProvider) {
|
|
279
|
+
this.dataProvider = dataProvider;
|
|
280
|
+
}
|
|
281
|
+
async getEventDeckBonus(eventId, card) {
|
|
282
|
+
const eventDeckBonuses = await this.dataProvider.getMasterData('eventDeckBonuses');
|
|
283
|
+
const gameCharacterUnits = await this.dataProvider.getMasterData('gameCharacterUnits');
|
|
284
|
+
return eventDeckBonuses.filter(it => it.eventId === eventId &&
|
|
285
|
+
(it.cardAttr === undefined || it.cardAttr === card.attr))
|
|
286
|
+
.reduce((v, eventDeckBonus) => {
|
|
287
|
+
if (eventDeckBonus.gameCharacterUnitId === undefined)
|
|
288
|
+
return Math.max(v, eventDeckBonus.bonusRate);
|
|
289
|
+
const gameCharacterUnit = findOrThrow(gameCharacterUnits, unit => unit.id === eventDeckBonus.gameCharacterUnitId);
|
|
290
|
+
if (gameCharacterUnit.gameCharacterId !== card.characterId)
|
|
291
|
+
return v;
|
|
292
|
+
if (card.characterId < 21 || card.supportUnit === gameCharacterUnit.unit) {
|
|
293
|
+
return Math.max(v, eventDeckBonus.bonusRate);
|
|
294
|
+
}
|
|
295
|
+
return Math.max(v, card.supportUnit === 'none' ? eventDeckBonus.bonusRate - 10 : 0);
|
|
296
|
+
}, 0);
|
|
297
|
+
}
|
|
298
|
+
async getCardEventBonus(userCard, eventId) {
|
|
299
|
+
const cards = await this.dataProvider.getMasterData('cards');
|
|
300
|
+
const eventCards = await this.dataProvider.getMasterData('eventCards');
|
|
301
|
+
const eventRarityBonusRates = await this.dataProvider.getMasterData('eventRarityBonusRates');
|
|
302
|
+
let eventBonus = 0;
|
|
303
|
+
const card = findOrThrow(cards, it => it.id === userCard.cardId);
|
|
304
|
+
eventBonus += await this.getEventDeckBonus(eventId, card);
|
|
305
|
+
const cardBonus = eventCards.find((it) => it.eventId === eventId && it.cardId === card.id);
|
|
306
|
+
if (cardBonus != null) {
|
|
307
|
+
eventBonus += cardBonus.bonusRate;
|
|
308
|
+
}
|
|
309
|
+
const masterRankBonus = findOrThrow(eventRarityBonusRates, it => it.cardRarityType === card.cardRarityType && it.masterRank === userCard.masterRank);
|
|
310
|
+
eventBonus += masterRankBonus.bonusRate;
|
|
311
|
+
return eventBonus;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
class EventCalculator {
|
|
316
|
+
dataProvider;
|
|
317
|
+
deckCalculator;
|
|
318
|
+
cardEventCalculator;
|
|
319
|
+
constructor(dataProvider) {
|
|
320
|
+
this.dataProvider = dataProvider;
|
|
321
|
+
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
322
|
+
this.cardEventCalculator = new CardEventCalculator(dataProvider);
|
|
323
|
+
}
|
|
324
|
+
async getDeckEventBonus(userDeck, eventId) {
|
|
325
|
+
const deckCards = await this.deckCalculator.getDeckCards(userDeck);
|
|
326
|
+
return await deckCards.reduce(async (v, it) => await v + await this.cardEventCalculator.getCardEventBonus(it, eventId), Promise.resolve(0));
|
|
327
|
+
}
|
|
328
|
+
async getDeckEventBonusById(deckId, eventId) {
|
|
329
|
+
return await this.getDeckEventBonus(await this.deckCalculator.getDeck(deckId), eventId);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
class LiveCalculator {
|
|
334
|
+
dataProvider;
|
|
335
|
+
deckCalculator;
|
|
336
|
+
constructor(dataProvider) {
|
|
337
|
+
this.dataProvider = dataProvider;
|
|
338
|
+
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
339
|
+
}
|
|
340
|
+
getBaseScore(musicMeta, liveType) {
|
|
341
|
+
switch (liveType) {
|
|
342
|
+
case exports.LiveType.SOLO:
|
|
343
|
+
return musicMeta.base_score;
|
|
344
|
+
case exports.LiveType.MULTI:
|
|
345
|
+
return musicMeta.base_score + musicMeta.fever_score;
|
|
346
|
+
case exports.LiveType.AUTO:
|
|
347
|
+
return musicMeta.base_score_auto;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
getSkillScore(musicMeta, liveType) {
|
|
351
|
+
switch (liveType) {
|
|
352
|
+
case exports.LiveType.SOLO:
|
|
353
|
+
return musicMeta.skill_score_solo;
|
|
354
|
+
case exports.LiveType.MULTI:
|
|
355
|
+
return musicMeta.skill_score_multi;
|
|
356
|
+
case exports.LiveType.AUTO:
|
|
357
|
+
return musicMeta.skill_score_auto;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async getLiveDetailByDeck(deckDetail, musicMeta, liveType, skillDetails = undefined, multiPowerSum = 0) {
|
|
361
|
+
const bestSkill = skillDetails === undefined;
|
|
362
|
+
const skills = bestSkill
|
|
363
|
+
? [...deckDetail.skill, deckDetail.skill[0]].sort((a, b) => a.scoreUp - b.scoreUp)
|
|
364
|
+
: skillDetails;
|
|
365
|
+
const baseRate = this.getBaseScore(musicMeta, liveType);
|
|
366
|
+
const skillRate = bestSkill
|
|
367
|
+
? [...this.getSkillScore(musicMeta, liveType)].sort((a, b) => a - b)
|
|
368
|
+
: this.getSkillScore(musicMeta, liveType);
|
|
369
|
+
const rate = baseRate + skills.reduce((v, it, i) => v + it.scoreUp * skillRate[i] / 100, 0);
|
|
370
|
+
const life = skills.reduce((v, it) => v + it.lifeRecovery, 0);
|
|
371
|
+
const powerSum = multiPowerSum === 0 ? 5 * deckDetail.power : multiPowerSum;
|
|
372
|
+
const activeBonus = liveType === exports.LiveType.MULTI ? 5 * 0.015 * powerSum : 0;
|
|
373
|
+
return {
|
|
374
|
+
score: Math.floor(rate * deckDetail.power * 4 + activeBonus),
|
|
375
|
+
time: musicMeta.music_time,
|
|
376
|
+
life: Math.min(2000, life + 1000),
|
|
377
|
+
tap: musicMeta.tap_count
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
getMultiLiveSkill(deckDetail) {
|
|
381
|
+
const scoreUp = deckDetail.skill.reduce((v, it, i) => v + (i === 0 ? it.scoreUp : (it.scoreUp / 5)), 0);
|
|
382
|
+
const lifeRecovery = deckDetail.skill[0].lifeRecovery;
|
|
383
|
+
return { scoreUp, lifeRecovery };
|
|
384
|
+
}
|
|
385
|
+
async getLiveDetail(deckCards, musicId, musicDiff, liveType, liveSkills = undefined) {
|
|
386
|
+
const musicMetas = await this.dataProvider.getMusicMeta();
|
|
387
|
+
const musicMeta = findOrThrow(musicMetas, it => it.music_id === musicId && it.difficulty === musicDiff);
|
|
388
|
+
const deckDetail = await this.deckCalculator.getDeckDetail(deckCards);
|
|
389
|
+
const skills = liveType === exports.LiveType.MULTI
|
|
390
|
+
? duplicateObj(this.getMultiLiveSkill(deckDetail), 6)
|
|
391
|
+
: (mapOrUndefined(liveSkills, liveSkill => findOrThrow(deckDetail.skill, it => it.cardId === liveSkill.cardId)));
|
|
392
|
+
return await this.getLiveDetailByDeck(deckDetail, musicMeta, liveType, skills);
|
|
393
|
+
}
|
|
394
|
+
async getLiveDetailById(deckId, musicId, musicDiff, liveType, liveSkills = undefined) {
|
|
395
|
+
return await this.getLiveDetail(await this.deckCalculator.getDeckCardsById(deckId), musicId, musicDiff, liveType, liveSkills);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
exports.LiveType = void 0;
|
|
399
|
+
(function (LiveType) {
|
|
400
|
+
LiveType["SOLO"] = "solo";
|
|
401
|
+
LiveType["MULTI"] = "multi";
|
|
402
|
+
LiveType["AUTO"] = "auto";
|
|
403
|
+
})(exports.LiveType || (exports.LiveType = {}));
|
|
404
|
+
|
|
405
|
+
exports.CardCalculator = CardCalculator;
|
|
406
|
+
exports.CardDetailMap = CardDetailMap;
|
|
407
|
+
exports.DeckCalculator = DeckCalculator;
|
|
408
|
+
exports.EventCalculator = EventCalculator;
|
|
409
|
+
exports.LiveCalculator = LiveCalculator;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,18 +4,6 @@ interface DataProvider {
|
|
|
4
4
|
getMusicMeta: () => Promise<any>;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
interface UserCard {
|
|
8
|
-
cardId: number;
|
|
9
|
-
level?: number;
|
|
10
|
-
skillLevel?: number;
|
|
11
|
-
masterRank: number;
|
|
12
|
-
specialTrainingStatus?: string;
|
|
13
|
-
episodes?: Array<{
|
|
14
|
-
cardEpisodeId: number;
|
|
15
|
-
scenarioStatus: string;
|
|
16
|
-
}>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
interface UserDeck {
|
|
20
8
|
deckId: number;
|
|
21
9
|
member1: number;
|
|
@@ -28,19 +16,30 @@ interface UserDeck {
|
|
|
28
16
|
declare class EventCalculator {
|
|
29
17
|
private readonly dataProvider;
|
|
30
18
|
private readonly deckCalculator;
|
|
19
|
+
private readonly cardEventCalculator;
|
|
31
20
|
constructor(dataProvider: DataProvider);
|
|
32
|
-
private getEventDeckBonus;
|
|
33
|
-
getCardEventBonus(userCard: UserCard, eventId: number): Promise<number>;
|
|
34
21
|
getDeckEventBonus(userDeck: UserDeck, eventId: number): Promise<number>;
|
|
35
22
|
getDeckEventBonusById(deckId: number, eventId: number): Promise<number>;
|
|
36
23
|
}
|
|
37
24
|
|
|
25
|
+
interface UserCard {
|
|
26
|
+
cardId: number;
|
|
27
|
+
level: number;
|
|
28
|
+
skillLevel: number;
|
|
29
|
+
masterRank: number;
|
|
30
|
+
specialTrainingStatus?: string;
|
|
31
|
+
episodes: Array<{
|
|
32
|
+
cardEpisodeId: number;
|
|
33
|
+
scenarioStatus: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
|
|
38
37
|
interface AreaItemLevel {
|
|
39
38
|
areaItemId: number;
|
|
40
39
|
level: number;
|
|
41
40
|
targetUnit: string;
|
|
42
41
|
targetCardAttr: string;
|
|
43
|
-
targetGameCharacterId
|
|
42
|
+
targetGameCharacterId?: number;
|
|
44
43
|
power1BonusRate: number;
|
|
45
44
|
power1AllMatchBonusRate: number;
|
|
46
45
|
power2BonusRate: number;
|
|
@@ -50,95 +49,96 @@ interface AreaItemLevel {
|
|
|
50
49
|
sentence: string;
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
declare class CardCalculator {
|
|
53
|
+
private readonly dataProvider;
|
|
54
|
+
private readonly powerCalculator;
|
|
55
|
+
private readonly skillCalculator;
|
|
56
|
+
constructor(dataProvider: DataProvider);
|
|
57
|
+
private getCardUnits;
|
|
58
|
+
getCardDetail(userCard: UserCard, userAreaItemLevels: AreaItemLevel[]): Promise<CardDetail>;
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
seq: number;
|
|
63
|
-
characterId: number;
|
|
64
|
-
cardRarityType: string;
|
|
65
|
-
specialTrainingPower1BonusFixed: number;
|
|
66
|
-
specialTrainingPower2BonusFixed: number;
|
|
67
|
-
specialTrainingPower3BonusFixed: number;
|
|
60
|
+
interface CardDetail {
|
|
61
|
+
cardId: number;
|
|
62
|
+
units: string[];
|
|
68
63
|
attr: string;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
assetbundleName: string;
|
|
74
|
-
gachaPhrase: string;
|
|
75
|
-
flavorText: string;
|
|
76
|
-
releaseAt: number;
|
|
77
|
-
cardParameters: Array<{
|
|
78
|
-
id: number;
|
|
79
|
-
cardId: number;
|
|
80
|
-
cardLevel: number;
|
|
81
|
-
cardParameterType: string;
|
|
82
|
-
power: number;
|
|
83
|
-
}>;
|
|
84
|
-
specialTrainingCosts: CommonResource[];
|
|
85
|
-
masterLessonAchieveResources: {
|
|
86
|
-
releaseConditionId: number;
|
|
87
|
-
cardId: number;
|
|
88
|
-
masterRank: number;
|
|
89
|
-
resources: CommonResource[];
|
|
90
|
-
};
|
|
64
|
+
power: CardDetailMap;
|
|
65
|
+
scoreSkill: CardDetailMap;
|
|
66
|
+
lifeSkill: number;
|
|
67
|
+
eventBonus?: number;
|
|
91
68
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
69
|
+
declare class CardDetailMap {
|
|
70
|
+
min: number;
|
|
71
|
+
max: number;
|
|
72
|
+
values: Map<string, number>;
|
|
73
|
+
set(unit: string, unitMember: number, attrMember: number, value: number): void;
|
|
74
|
+
get(unit: string, unitMember: number, attrMember: number): number;
|
|
75
|
+
private getInternal;
|
|
76
|
+
static getKey(unit: string, unitMember: number, attrMember: number): string;
|
|
77
|
+
isCertainlyLessThen(another: CardDetailMap): boolean;
|
|
98
78
|
}
|
|
99
79
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
80
|
+
declare class DeckCalculator {
|
|
81
|
+
private readonly dataProvider;
|
|
82
|
+
private readonly cardCalculator;
|
|
83
|
+
constructor(dataProvider: DataProvider);
|
|
84
|
+
getDeck(deckId: number): Promise<UserDeck>;
|
|
85
|
+
getDeckCards(userDeck: UserDeck): Promise<UserCard[]>;
|
|
86
|
+
getDeckCardsById(deckId: number): Promise<UserCard[]>;
|
|
87
|
+
private getHonorBonusPower;
|
|
88
|
+
getDeckDetailByCards(cardDetails: CardDetail[], honorBonus: number): Promise<DeckDetail>;
|
|
89
|
+
getDeckDetail(deckCards: UserCard[]): Promise<DeckDetail>;
|
|
90
|
+
getDeckDetailById(deckId: number): Promise<DeckDetail>;
|
|
106
91
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
cardRarityType: string;
|
|
111
|
-
masterRank: number;
|
|
112
|
-
bonusRate: number;
|
|
92
|
+
interface DeckDetail {
|
|
93
|
+
power: number;
|
|
94
|
+
skill: SkillDetail[];
|
|
113
95
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
unit: string;
|
|
119
|
-
colorCode: string;
|
|
120
|
-
skinColorCode: string;
|
|
121
|
-
skinShadowColorCode1: string;
|
|
122
|
-
skinShadowColorCode2: string;
|
|
96
|
+
interface SkillDetail {
|
|
97
|
+
cardId?: number;
|
|
98
|
+
scoreUp: number;
|
|
99
|
+
lifeRecovery: number;
|
|
123
100
|
}
|
|
124
101
|
|
|
125
|
-
interface
|
|
126
|
-
|
|
127
|
-
|
|
102
|
+
interface MusicMeta {
|
|
103
|
+
music_id: number;
|
|
104
|
+
difficulty: string;
|
|
105
|
+
music_time: number;
|
|
106
|
+
event_rate: number;
|
|
107
|
+
base_score: number;
|
|
108
|
+
base_score_auto: number;
|
|
109
|
+
skill_score_solo: number[];
|
|
110
|
+
skill_score_auto: number[];
|
|
111
|
+
skill_score_multi: number[];
|
|
112
|
+
fever_score: number;
|
|
113
|
+
fever_end_time: number;
|
|
114
|
+
tap_count: number;
|
|
128
115
|
}
|
|
129
116
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
117
|
+
declare class LiveCalculator {
|
|
118
|
+
private readonly dataProvider;
|
|
119
|
+
private readonly deckCalculator;
|
|
120
|
+
constructor(dataProvider: DataProvider);
|
|
121
|
+
private getBaseScore;
|
|
122
|
+
private getSkillScore;
|
|
123
|
+
getLiveDetailByDeck(deckDetail: DeckDetail, musicMeta: MusicMeta, liveType: LiveType, skillDetails?: SkillDetail[] | undefined, multiPowerSum?: number): Promise<LiveDetail>;
|
|
124
|
+
private getMultiLiveSkill;
|
|
125
|
+
getLiveDetail(deckCards: UserCard[], musicId: number, musicDiff: string, liveType: LiveType, liveSkills?: LiveSkill[] | undefined): Promise<LiveDetail>;
|
|
126
|
+
getLiveDetailById(deckId: number, musicId: number, musicDiff: string, liveType: LiveType, liveSkills?: LiveSkill[] | undefined): Promise<LiveDetail>;
|
|
137
127
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
128
|
+
interface LiveDetail {
|
|
129
|
+
score: number;
|
|
130
|
+
time: number;
|
|
131
|
+
life: number;
|
|
132
|
+
tap: number;
|
|
133
|
+
}
|
|
134
|
+
interface LiveSkill {
|
|
135
|
+
seq?: number;
|
|
136
|
+
cardId: number;
|
|
137
|
+
}
|
|
138
|
+
declare enum LiveType {
|
|
139
|
+
SOLO = "solo",
|
|
140
|
+
MULTI = "multi",
|
|
141
|
+
AUTO = "auto"
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
export {
|
|
144
|
+
export { CardCalculator, CardDetail, CardDetailMap, DataProvider, DeckCalculator, DeckDetail, EventCalculator, LiveCalculator, LiveDetail, LiveSkill, LiveType, SkillDetail };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
function findOrThrow(arr, p) {
|
|
2
|
+
const result = arr.find(p);
|
|
3
|
+
if (result === undefined)
|
|
4
|
+
throw new Error('object not found');
|
|
5
|
+
return result;
|
|
6
|
+
}
|
|
7
|
+
function getOrThrow(map, key) {
|
|
8
|
+
const value = map.get(key);
|
|
9
|
+
if (value === undefined)
|
|
10
|
+
throw new Error('key not found');
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function getOrDefault(map, key, defaultValue) {
|
|
14
|
+
const value = map.get(key);
|
|
15
|
+
if (value === undefined)
|
|
16
|
+
return defaultValue;
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function computeWithDefault(map, key, defaultValue, action) {
|
|
20
|
+
map.set(key, action(getOrDefault(map, key, defaultValue)));
|
|
21
|
+
}
|
|
22
|
+
function duplicateObj(obj, times) {
|
|
23
|
+
const ret = [];
|
|
24
|
+
for (let i = 0; i < times; ++i)
|
|
25
|
+
ret.push(obj);
|
|
26
|
+
return ret;
|
|
27
|
+
}
|
|
28
|
+
function mapOrUndefined(arr, fun) {
|
|
29
|
+
if (arr === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
return arr.map(fun);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class CardPowerCalculator {
|
|
35
|
+
dataProvider;
|
|
36
|
+
constructor(dataProvider) {
|
|
37
|
+
this.dataProvider = dataProvider;
|
|
38
|
+
}
|
|
39
|
+
async getCardPower(userCard, card, cardUnits, userAreaItemLevels) {
|
|
40
|
+
const ret = new CardDetailMap();
|
|
41
|
+
const basePower = await this.getCardBasePowers(userCard, card);
|
|
42
|
+
const characterBonus = await this.getCharacterBonusPower(basePower, card.characterId);
|
|
43
|
+
for (const unit of cardUnits) {
|
|
44
|
+
ret.set(unit, 5, 5, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, true, true));
|
|
45
|
+
ret.set(unit, 5, 1, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, true, false));
|
|
46
|
+
ret.set(unit, 1, 5, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, false, true));
|
|
47
|
+
ret.set(unit, 1, 1, await this.getPower(card, basePower, characterBonus, userAreaItemLevels, unit, false, false));
|
|
48
|
+
}
|
|
49
|
+
return ret;
|
|
50
|
+
}
|
|
51
|
+
async getPower(card, basePower, characterBonus, userAreaItemLevels, unit, sameUnit, sameAttr) {
|
|
52
|
+
return basePower.reduce((v, it) => v + it, 0) + characterBonus + await this.getAreaItemBonusPower(userAreaItemLevels, basePower, card.characterId, unit, sameUnit, card.attr, sameAttr);
|
|
53
|
+
}
|
|
54
|
+
async getCardBasePowers(userCard, card) {
|
|
55
|
+
const cardEpisodes = await this.dataProvider.getMasterData('cardEpisodes');
|
|
56
|
+
const masterLessons = await this.dataProvider.getMasterData('masterLessons');
|
|
57
|
+
const ret = [0, 0, 0];
|
|
58
|
+
const cardParameters = card.cardParameters.filter(it => it.cardLevel === userCard.level);
|
|
59
|
+
const params = ['param1', 'param2', 'param3'];
|
|
60
|
+
params.forEach((param, i) => {
|
|
61
|
+
ret[i] = findOrThrow(cardParameters, it => it.cardParameterType === param).power;
|
|
62
|
+
});
|
|
63
|
+
if (userCard.specialTrainingStatus === 'done') {
|
|
64
|
+
ret[0] += card.specialTrainingPower1BonusFixed;
|
|
65
|
+
ret[1] += card.specialTrainingPower2BonusFixed;
|
|
66
|
+
ret[2] += card.specialTrainingPower3BonusFixed;
|
|
67
|
+
}
|
|
68
|
+
const episodes = userCard.episodes.filter(it => it.scenarioStatus === 'already_read')
|
|
69
|
+
.map(it => findOrThrow(cardEpisodes, e => e.id === it.cardEpisodeId));
|
|
70
|
+
for (const episode of episodes) {
|
|
71
|
+
ret[0] += episode.power1BonusFixed;
|
|
72
|
+
ret[1] += episode.power2BonusFixed;
|
|
73
|
+
ret[2] += episode.power3BonusFixed;
|
|
74
|
+
}
|
|
75
|
+
const usedMasterLessons = masterLessons
|
|
76
|
+
.filter((it) => it.cardRarityType === card.cardRarityType && it.masterRank <= userCard.masterRank);
|
|
77
|
+
for (const masterLesson of usedMasterLessons) {
|
|
78
|
+
ret[0] += masterLesson.power1BonusFixed;
|
|
79
|
+
ret[1] += masterLesson.power2BonusFixed;
|
|
80
|
+
ret[2] += masterLesson.power3BonusFixed;
|
|
81
|
+
}
|
|
82
|
+
return ret;
|
|
83
|
+
}
|
|
84
|
+
async getAreaItemBonusPower(userAreaItemLevels, basePower, characterId, unit, sameUnit, attr, sameAttr) {
|
|
85
|
+
const usedAreaItems = userAreaItemLevels.filter(it => (it.targetUnit === 'any' || it.targetUnit === unit) &&
|
|
86
|
+
(it.targetCardAttr === 'any' || it.targetCardAttr === attr) &&
|
|
87
|
+
(it.targetGameCharacterId === undefined || it.targetGameCharacterId === characterId));
|
|
88
|
+
const areaItemBonus = [0, 0, 0];
|
|
89
|
+
for (const areaItem of usedAreaItems) {
|
|
90
|
+
const allMatch = (areaItem.targetUnit !== 'any' && sameUnit) || (areaItem.targetCardAttr !== 'any' && sameAttr);
|
|
91
|
+
areaItemBonus[0] += allMatch ? areaItem.power1AllMatchBonusRate : areaItem.power1BonusRate;
|
|
92
|
+
areaItemBonus[1] += allMatch ? areaItem.power2AllMatchBonusRate : areaItem.power2BonusRate;
|
|
93
|
+
areaItemBonus[2] += allMatch ? areaItem.power3AllMatchBonusRate : areaItem.power3BonusRate;
|
|
94
|
+
}
|
|
95
|
+
return basePower.reduce((v, it, i) => v + Math.floor(it * areaItemBonus[i] / 100), 0);
|
|
96
|
+
}
|
|
97
|
+
async getCharacterBonusPower(basePower, characterId) {
|
|
98
|
+
const characterRanks = await this.dataProvider.getMasterData('characterRanks');
|
|
99
|
+
const userCharacters = await this.dataProvider.getUserData('userCharacters');
|
|
100
|
+
const userCharacter = findOrThrow(userCharacters, it => it.characterId === characterId);
|
|
101
|
+
const characterRank = findOrThrow(characterRanks, it => it.characterId === userCharacter.characterId && it.characterRank === userCharacter.characterRank);
|
|
102
|
+
const rates = [characterRank.power1BonusRate, characterRank.power2BonusRate, characterRank.power3BonusRate];
|
|
103
|
+
return rates.reduce((v, it, i) => v + Math.floor(basePower[i] * it / 100), 0);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
class CardSkillCalculator {
|
|
108
|
+
dataProvider;
|
|
109
|
+
constructor(dataProvider) {
|
|
110
|
+
this.dataProvider = dataProvider;
|
|
111
|
+
}
|
|
112
|
+
async getCardSkill(userCard, card) {
|
|
113
|
+
const scoreUpMap = new CardDetailMap();
|
|
114
|
+
const detail = await this.getSkillDetail(userCard, card);
|
|
115
|
+
if (detail.scoreUpEnhance !== undefined) {
|
|
116
|
+
for (let i = 1; i <= 5; ++i) {
|
|
117
|
+
const scoreUp = detail.scoreUp + (i === 5 ? 5 : (i - 1)) * detail.scoreUpEnhance.value;
|
|
118
|
+
scoreUpMap.set(detail.scoreUpEnhance.unit, i, 1, scoreUp);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
scoreUpMap.set('any', 1, 1, detail.scoreUp);
|
|
122
|
+
return { scoreUp: scoreUpMap, lifeRecovery: detail.lifeRecovery };
|
|
123
|
+
}
|
|
124
|
+
async getSkillDetail(userCard, card) {
|
|
125
|
+
const skills = await this.dataProvider.getMasterData('skills');
|
|
126
|
+
const skill = findOrThrow(skills, it => it.id === card.skillId);
|
|
127
|
+
const ret = { scoreUp: 0, lifeRecovery: 0 };
|
|
128
|
+
for (const skillEffect of skill.skillEffects) {
|
|
129
|
+
const skillEffectDetail = findOrThrow(skillEffect.skillEffectDetails, it => it.level === userCard.skillLevel);
|
|
130
|
+
if (skillEffect.skillEffectType === 'score_up' ||
|
|
131
|
+
skillEffect.skillEffectType === 'score_up_condition_life' ||
|
|
132
|
+
skillEffect.skillEffectType === 'score_up_keep') {
|
|
133
|
+
const current = skillEffectDetail.activateEffectValue;
|
|
134
|
+
if (skillEffect.skillEnhance !== undefined) {
|
|
135
|
+
ret.scoreUpEnhance = {
|
|
136
|
+
unit: skillEffect.skillEnhance.skillEnhanceCondition.unit,
|
|
137
|
+
value: skillEffect.skillEnhance.activateEffectValue
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
ret.scoreUp = Math.max(ret.scoreUp, current);
|
|
141
|
+
}
|
|
142
|
+
else if (skillEffect.skillEffectType === 'life_recovery') {
|
|
143
|
+
ret.lifeRecovery += skillEffectDetail.activateEffectValue;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return ret;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
class CardCalculator {
|
|
151
|
+
dataProvider;
|
|
152
|
+
powerCalculator;
|
|
153
|
+
skillCalculator;
|
|
154
|
+
constructor(dataProvider) {
|
|
155
|
+
this.dataProvider = dataProvider;
|
|
156
|
+
this.powerCalculator = new CardPowerCalculator(dataProvider);
|
|
157
|
+
this.skillCalculator = new CardSkillCalculator(dataProvider);
|
|
158
|
+
}
|
|
159
|
+
async getCardUnits(card) {
|
|
160
|
+
const gameCharacters = await this.dataProvider.getMasterData('gameCharacters');
|
|
161
|
+
const units = [];
|
|
162
|
+
if (card.supportUnit !== 'none')
|
|
163
|
+
units.push(card.supportUnit);
|
|
164
|
+
units.push(findOrThrow(gameCharacters, it => it.id === card.characterId).unit);
|
|
165
|
+
return units;
|
|
166
|
+
}
|
|
167
|
+
async getCardDetail(userCard, userAreaItemLevels) {
|
|
168
|
+
const cards = await this.dataProvider.getMasterData('cards');
|
|
169
|
+
const card = findOrThrow(cards, it => it.id === userCard.cardId);
|
|
170
|
+
const units = await this.getCardUnits(card);
|
|
171
|
+
const skill = await this.skillCalculator.getCardSkill(userCard, card);
|
|
172
|
+
const power = await this.powerCalculator.getCardPower(userCard, card, units, userAreaItemLevels);
|
|
173
|
+
return {
|
|
174
|
+
cardId: card.id,
|
|
175
|
+
units,
|
|
176
|
+
attr: card.attr,
|
|
177
|
+
power,
|
|
178
|
+
scoreSkill: skill.scoreUp,
|
|
179
|
+
lifeSkill: skill.lifeRecovery
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
class CardDetailMap {
|
|
184
|
+
min = Number.MAX_SAFE_INTEGER;
|
|
185
|
+
max = Number.MIN_SAFE_INTEGER;
|
|
186
|
+
values = new Map();
|
|
187
|
+
set(unit, unitMember, attrMember, value) {
|
|
188
|
+
this.min = Math.min(this.min, value);
|
|
189
|
+
this.max = Math.max(this.max, value);
|
|
190
|
+
this.values.set(CardDetailMap.getKey(unit, unitMember, attrMember), value);
|
|
191
|
+
}
|
|
192
|
+
get(unit, unitMember, attrMember) {
|
|
193
|
+
const attrMember0 = attrMember === 5 ? 5 : 1;
|
|
194
|
+
let best = this.getInternal(unit, unitMember, attrMember0);
|
|
195
|
+
if (best !== undefined)
|
|
196
|
+
return best;
|
|
197
|
+
best = this.getInternal(unit, unitMember === 5 ? 5 : 1, attrMember0);
|
|
198
|
+
if (best !== undefined)
|
|
199
|
+
return best;
|
|
200
|
+
best = this.getInternal('any', 1, 1);
|
|
201
|
+
if (best !== undefined)
|
|
202
|
+
return best;
|
|
203
|
+
throw new Error('case not found');
|
|
204
|
+
}
|
|
205
|
+
getInternal(unit, unitMember, attrMember) {
|
|
206
|
+
return this.values.get(CardDetailMap.getKey(unit, unitMember, attrMember));
|
|
207
|
+
}
|
|
208
|
+
static getKey(unit, unitMember, attrMember) {
|
|
209
|
+
return `${unit}-${unitMember}-${attrMember}`;
|
|
210
|
+
}
|
|
211
|
+
isCertainlyLessThen(another) {
|
|
212
|
+
return this.max < another.min;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
class DeckCalculator {
|
|
217
|
+
dataProvider;
|
|
218
|
+
cardCalculator;
|
|
219
|
+
constructor(dataProvider) {
|
|
220
|
+
this.dataProvider = dataProvider;
|
|
221
|
+
this.cardCalculator = new CardCalculator(dataProvider);
|
|
222
|
+
}
|
|
223
|
+
async getDeck(deckId) {
|
|
224
|
+
const userDecks = await this.dataProvider.getUserData('userDecks');
|
|
225
|
+
return findOrThrow(userDecks, it => it.deckId === deckId);
|
|
226
|
+
}
|
|
227
|
+
async getDeckCards(userDeck) {
|
|
228
|
+
const userCards = await this.dataProvider.getUserData('userCards');
|
|
229
|
+
const cardIds = [userDeck.member1, userDeck.member2, userDeck.member3, userDeck.member4, userDeck.member5];
|
|
230
|
+
return cardIds.map(id => findOrThrow(userCards, it => it.cardId === id));
|
|
231
|
+
}
|
|
232
|
+
async getDeckCardsById(deckId) {
|
|
233
|
+
return await this.getDeckCards(await this.getDeck(deckId));
|
|
234
|
+
}
|
|
235
|
+
async getHonorBonusPower() {
|
|
236
|
+
const honors = await this.dataProvider.getMasterData('honors');
|
|
237
|
+
const userHonors = await this.dataProvider.getUserData('userHonors');
|
|
238
|
+
return userHonors
|
|
239
|
+
.map(userHonor => {
|
|
240
|
+
const honor = findOrThrow(honors, it => it.id === userHonor.honorId);
|
|
241
|
+
return findOrThrow(honor.levels, it => it.level === userHonor.level);
|
|
242
|
+
})
|
|
243
|
+
.reduce((v, it) => v + it.bonus, 0);
|
|
244
|
+
}
|
|
245
|
+
async getDeckDetailByCards(cardDetails, honorBonus) {
|
|
246
|
+
const map = new Map();
|
|
247
|
+
for (const cardDetail of cardDetails) {
|
|
248
|
+
computeWithDefault(map, cardDetail.attr, 0, it => it + 1);
|
|
249
|
+
cardDetail.units.forEach(key => { computeWithDefault(map, key, 0, it => it + 1); });
|
|
250
|
+
}
|
|
251
|
+
const power = cardDetails.reduce((v, cardDetail) => v + cardDetail.units.reduce((vv, unit) => Math.max(vv, cardDetail.power.get(unit, getOrThrow(map, unit), getOrThrow(map, cardDetail.attr))), 0), 0) + honorBonus;
|
|
252
|
+
const skill = cardDetails.map(cardDetail => {
|
|
253
|
+
const scoreUp = cardDetail.units.reduce((vv, unit) => Math.max(vv, cardDetail.scoreSkill.get(unit, getOrThrow(map, unit), 1)), 0);
|
|
254
|
+
return {
|
|
255
|
+
cardId: cardDetail.cardId,
|
|
256
|
+
scoreUp,
|
|
257
|
+
lifeRecovery: cardDetail.lifeSkill
|
|
258
|
+
};
|
|
259
|
+
});
|
|
260
|
+
return { power, skill };
|
|
261
|
+
}
|
|
262
|
+
async getDeckDetail(deckCards) {
|
|
263
|
+
const areaItemLevels = await this.dataProvider.getMasterData('areaItemLevels');
|
|
264
|
+
const userAreas = await this.dataProvider.getUserData('userAreas');
|
|
265
|
+
const userItemLevels = userAreas.flatMap(it => it.areaItems).map(areaItem => findOrThrow(areaItemLevels, it => it.areaItemId === areaItem.areaItemId && it.level === areaItem.level));
|
|
266
|
+
const cardDetails = await Promise.all(deckCards.map(async (it) => await this.cardCalculator.getCardDetail(it, userItemLevels)));
|
|
267
|
+
return await this.getDeckDetailByCards(cardDetails, await this.getHonorBonusPower());
|
|
268
|
+
}
|
|
269
|
+
async getDeckDetailById(deckId) {
|
|
270
|
+
return await this.getDeckDetail(await this.getDeckCardsById(deckId));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
class CardEventCalculator {
|
|
275
|
+
dataProvider;
|
|
276
|
+
constructor(dataProvider) {
|
|
277
|
+
this.dataProvider = dataProvider;
|
|
278
|
+
}
|
|
279
|
+
async getEventDeckBonus(eventId, card) {
|
|
280
|
+
const eventDeckBonuses = await this.dataProvider.getMasterData('eventDeckBonuses');
|
|
281
|
+
const gameCharacterUnits = await this.dataProvider.getMasterData('gameCharacterUnits');
|
|
282
|
+
return eventDeckBonuses.filter(it => it.eventId === eventId &&
|
|
283
|
+
(it.cardAttr === undefined || it.cardAttr === card.attr))
|
|
284
|
+
.reduce((v, eventDeckBonus) => {
|
|
285
|
+
if (eventDeckBonus.gameCharacterUnitId === undefined)
|
|
286
|
+
return Math.max(v, eventDeckBonus.bonusRate);
|
|
287
|
+
const gameCharacterUnit = findOrThrow(gameCharacterUnits, unit => unit.id === eventDeckBonus.gameCharacterUnitId);
|
|
288
|
+
if (gameCharacterUnit.gameCharacterId !== card.characterId)
|
|
289
|
+
return v;
|
|
290
|
+
if (card.characterId < 21 || card.supportUnit === gameCharacterUnit.unit) {
|
|
291
|
+
return Math.max(v, eventDeckBonus.bonusRate);
|
|
292
|
+
}
|
|
293
|
+
return Math.max(v, card.supportUnit === 'none' ? eventDeckBonus.bonusRate - 10 : 0);
|
|
294
|
+
}, 0);
|
|
295
|
+
}
|
|
296
|
+
async getCardEventBonus(userCard, eventId) {
|
|
297
|
+
const cards = await this.dataProvider.getMasterData('cards');
|
|
298
|
+
const eventCards = await this.dataProvider.getMasterData('eventCards');
|
|
299
|
+
const eventRarityBonusRates = await this.dataProvider.getMasterData('eventRarityBonusRates');
|
|
300
|
+
let eventBonus = 0;
|
|
301
|
+
const card = findOrThrow(cards, it => it.id === userCard.cardId);
|
|
302
|
+
eventBonus += await this.getEventDeckBonus(eventId, card);
|
|
303
|
+
const cardBonus = eventCards.find((it) => it.eventId === eventId && it.cardId === card.id);
|
|
304
|
+
if (cardBonus != null) {
|
|
305
|
+
eventBonus += cardBonus.bonusRate;
|
|
306
|
+
}
|
|
307
|
+
const masterRankBonus = findOrThrow(eventRarityBonusRates, it => it.cardRarityType === card.cardRarityType && it.masterRank === userCard.masterRank);
|
|
308
|
+
eventBonus += masterRankBonus.bonusRate;
|
|
309
|
+
return eventBonus;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
class EventCalculator {
|
|
314
|
+
dataProvider;
|
|
315
|
+
deckCalculator;
|
|
316
|
+
cardEventCalculator;
|
|
317
|
+
constructor(dataProvider) {
|
|
318
|
+
this.dataProvider = dataProvider;
|
|
319
|
+
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
320
|
+
this.cardEventCalculator = new CardEventCalculator(dataProvider);
|
|
321
|
+
}
|
|
322
|
+
async getDeckEventBonus(userDeck, eventId) {
|
|
323
|
+
const deckCards = await this.deckCalculator.getDeckCards(userDeck);
|
|
324
|
+
return await deckCards.reduce(async (v, it) => await v + await this.cardEventCalculator.getCardEventBonus(it, eventId), Promise.resolve(0));
|
|
325
|
+
}
|
|
326
|
+
async getDeckEventBonusById(deckId, eventId) {
|
|
327
|
+
return await this.getDeckEventBonus(await this.deckCalculator.getDeck(deckId), eventId);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
class LiveCalculator {
|
|
332
|
+
dataProvider;
|
|
333
|
+
deckCalculator;
|
|
334
|
+
constructor(dataProvider) {
|
|
335
|
+
this.dataProvider = dataProvider;
|
|
336
|
+
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
337
|
+
}
|
|
338
|
+
getBaseScore(musicMeta, liveType) {
|
|
339
|
+
switch (liveType) {
|
|
340
|
+
case LiveType.SOLO:
|
|
341
|
+
return musicMeta.base_score;
|
|
342
|
+
case LiveType.MULTI:
|
|
343
|
+
return musicMeta.base_score + musicMeta.fever_score;
|
|
344
|
+
case LiveType.AUTO:
|
|
345
|
+
return musicMeta.base_score_auto;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
getSkillScore(musicMeta, liveType) {
|
|
349
|
+
switch (liveType) {
|
|
350
|
+
case LiveType.SOLO:
|
|
351
|
+
return musicMeta.skill_score_solo;
|
|
352
|
+
case LiveType.MULTI:
|
|
353
|
+
return musicMeta.skill_score_multi;
|
|
354
|
+
case LiveType.AUTO:
|
|
355
|
+
return musicMeta.skill_score_auto;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async getLiveDetailByDeck(deckDetail, musicMeta, liveType, skillDetails = undefined, multiPowerSum = 0) {
|
|
359
|
+
const bestSkill = skillDetails === undefined;
|
|
360
|
+
const skills = bestSkill
|
|
361
|
+
? [...deckDetail.skill, deckDetail.skill[0]].sort((a, b) => a.scoreUp - b.scoreUp)
|
|
362
|
+
: skillDetails;
|
|
363
|
+
const baseRate = this.getBaseScore(musicMeta, liveType);
|
|
364
|
+
const skillRate = bestSkill
|
|
365
|
+
? [...this.getSkillScore(musicMeta, liveType)].sort((a, b) => a - b)
|
|
366
|
+
: this.getSkillScore(musicMeta, liveType);
|
|
367
|
+
const rate = baseRate + skills.reduce((v, it, i) => v + it.scoreUp * skillRate[i] / 100, 0);
|
|
368
|
+
const life = skills.reduce((v, it) => v + it.lifeRecovery, 0);
|
|
369
|
+
const powerSum = multiPowerSum === 0 ? 5 * deckDetail.power : multiPowerSum;
|
|
370
|
+
const activeBonus = liveType === LiveType.MULTI ? 5 * 0.015 * powerSum : 0;
|
|
371
|
+
return {
|
|
372
|
+
score: Math.floor(rate * deckDetail.power * 4 + activeBonus),
|
|
373
|
+
time: musicMeta.music_time,
|
|
374
|
+
life: Math.min(2000, life + 1000),
|
|
375
|
+
tap: musicMeta.tap_count
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
getMultiLiveSkill(deckDetail) {
|
|
379
|
+
const scoreUp = deckDetail.skill.reduce((v, it, i) => v + (i === 0 ? it.scoreUp : (it.scoreUp / 5)), 0);
|
|
380
|
+
const lifeRecovery = deckDetail.skill[0].lifeRecovery;
|
|
381
|
+
return { scoreUp, lifeRecovery };
|
|
382
|
+
}
|
|
383
|
+
async getLiveDetail(deckCards, musicId, musicDiff, liveType, liveSkills = undefined) {
|
|
384
|
+
const musicMetas = await this.dataProvider.getMusicMeta();
|
|
385
|
+
const musicMeta = findOrThrow(musicMetas, it => it.music_id === musicId && it.difficulty === musicDiff);
|
|
386
|
+
const deckDetail = await this.deckCalculator.getDeckDetail(deckCards);
|
|
387
|
+
const skills = liveType === LiveType.MULTI
|
|
388
|
+
? duplicateObj(this.getMultiLiveSkill(deckDetail), 6)
|
|
389
|
+
: (mapOrUndefined(liveSkills, liveSkill => findOrThrow(deckDetail.skill, it => it.cardId === liveSkill.cardId)));
|
|
390
|
+
return await this.getLiveDetailByDeck(deckDetail, musicMeta, liveType, skills);
|
|
391
|
+
}
|
|
392
|
+
async getLiveDetailById(deckId, musicId, musicDiff, liveType, liveSkills = undefined) {
|
|
393
|
+
return await this.getLiveDetail(await this.deckCalculator.getDeckCardsById(deckId), musicId, musicDiff, liveType, liveSkills);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
var LiveType;
|
|
397
|
+
(function (LiveType) {
|
|
398
|
+
LiveType["SOLO"] = "solo";
|
|
399
|
+
LiveType["MULTI"] = "multi";
|
|
400
|
+
LiveType["AUTO"] = "auto";
|
|
401
|
+
})(LiveType || (LiveType = {}));
|
|
402
|
+
|
|
403
|
+
export { CardCalculator, CardDetailMap, DeckCalculator, EventCalculator, LiveCalculator, LiveType };
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sekai-calculator",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Project SEKAI Calculator for deck power, live score, event point and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
-
"main": "dist/index.
|
|
8
|
-
"module": "dist/index.
|
|
7
|
+
"main": "dist/index.cjs",
|
|
8
|
+
"module": "dist/index.mjs",
|
|
9
9
|
"files": [
|
|
10
|
-
"dist/index.
|
|
11
|
-
"dist/index.
|
|
10
|
+
"dist/index.cjs",
|
|
11
|
+
"dist/index.mjs",
|
|
12
12
|
"dist/index.d.ts"
|
|
13
13
|
],
|
|
14
14
|
"keywords": [
|
package/dist/index.es.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
function findOrThrow(arr, p) {
|
|
2
|
-
const result = arr.find(p);
|
|
3
|
-
if (result === undefined)
|
|
4
|
-
throw new Error('object not found');
|
|
5
|
-
return result;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
class DeckCalculator {
|
|
9
|
-
dataProvider;
|
|
10
|
-
constructor(dataProvider) {
|
|
11
|
-
this.dataProvider = dataProvider;
|
|
12
|
-
}
|
|
13
|
-
async getDeck(deckId) {
|
|
14
|
-
const userDecks = await this.dataProvider.getUserData('userDecks');
|
|
15
|
-
return findOrThrow(userDecks, it => it.deckId === deckId);
|
|
16
|
-
}
|
|
17
|
-
async getDeckCards(userDeck) {
|
|
18
|
-
const userCards = await this.dataProvider.getUserData('userCards');
|
|
19
|
-
const cardIds = [userDeck.member1, userDeck.member2, userDeck.member3, userDeck.member4, userDeck.member5];
|
|
20
|
-
return cardIds.map(id => findOrThrow(userCards, it => it.cardId === id));
|
|
21
|
-
}
|
|
22
|
-
async getDeckCardsById(deckId) {
|
|
23
|
-
return await this.getDeckCards(await this.getDeck(deckId));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
class EventCalculator {
|
|
28
|
-
dataProvider;
|
|
29
|
-
deckCalculator;
|
|
30
|
-
constructor(dataProvider) {
|
|
31
|
-
this.dataProvider = dataProvider;
|
|
32
|
-
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
33
|
-
}
|
|
34
|
-
async getEventDeckBonus(eventId, card) {
|
|
35
|
-
const eventDeckBonuses = await this.dataProvider.getMasterData('eventDeckBonuses');
|
|
36
|
-
const gameCharacterUnits = await this.dataProvider.getMasterData('gameCharacterUnits');
|
|
37
|
-
return eventDeckBonuses.filter(it => it.eventId === eventId &&
|
|
38
|
-
(it.cardAttr === undefined || it.cardAttr === card.attr))
|
|
39
|
-
.reduce((v, eventDeckBonus) => {
|
|
40
|
-
if (eventDeckBonus.gameCharacterUnitId === undefined)
|
|
41
|
-
return Math.max(v, eventDeckBonus.bonusRate);
|
|
42
|
-
const gameCharacterUnit = findOrThrow(gameCharacterUnits, unit => unit.id === eventDeckBonus.gameCharacterUnitId);
|
|
43
|
-
if (gameCharacterUnit.gameCharacterId !== card.characterId)
|
|
44
|
-
return v;
|
|
45
|
-
if (card.characterId < 21 || card.supportUnit === gameCharacterUnit.unit) {
|
|
46
|
-
return Math.max(v, eventDeckBonus.bonusRate);
|
|
47
|
-
}
|
|
48
|
-
return Math.max(v, card.supportUnit === 'none' ? eventDeckBonus.bonusRate - 10 : 0);
|
|
49
|
-
}, 0);
|
|
50
|
-
}
|
|
51
|
-
async getCardEventBonus(userCard, eventId) {
|
|
52
|
-
const cards = await this.dataProvider.getMasterData('cards');
|
|
53
|
-
const eventCards = await this.dataProvider.getMasterData('eventCards');
|
|
54
|
-
const eventRarityBonusRates = await this.dataProvider.getMasterData('eventRarityBonusRates');
|
|
55
|
-
let eventBonus = 0;
|
|
56
|
-
const card = findOrThrow(cards, it => it.id === userCard.cardId);
|
|
57
|
-
eventBonus += await this.getEventDeckBonus(eventId, card);
|
|
58
|
-
const cardBonus = eventCards.find((it) => it.eventId === eventId && it.cardId === card.id);
|
|
59
|
-
if (cardBonus != null) {
|
|
60
|
-
eventBonus += cardBonus.bonusRate;
|
|
61
|
-
}
|
|
62
|
-
const masterRankBonus = findOrThrow(eventRarityBonusRates, it => it.cardRarityType === card.cardRarityType && it.masterRank === userCard.masterRank);
|
|
63
|
-
eventBonus += masterRankBonus.bonusRate;
|
|
64
|
-
return eventBonus;
|
|
65
|
-
}
|
|
66
|
-
async getDeckEventBonus(userDeck, eventId) {
|
|
67
|
-
const deckCards = await this.deckCalculator.getDeckCards(userDeck);
|
|
68
|
-
return await deckCards.reduce(async (v, it) => await this.getCardEventBonus(it, eventId) + await v, Promise.resolve(0));
|
|
69
|
-
}
|
|
70
|
-
async getDeckEventBonusById(deckId, eventId) {
|
|
71
|
-
return await this.getDeckEventBonus(await this.deckCalculator.getDeck(deckId), eventId);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export { EventCalculator };
|
package/dist/index.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function findOrThrow(arr, p) {
|
|
4
|
-
const result = arr.find(p);
|
|
5
|
-
if (result === undefined)
|
|
6
|
-
throw new Error('object not found');
|
|
7
|
-
return result;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
class DeckCalculator {
|
|
11
|
-
dataProvider;
|
|
12
|
-
constructor(dataProvider) {
|
|
13
|
-
this.dataProvider = dataProvider;
|
|
14
|
-
}
|
|
15
|
-
async getDeck(deckId) {
|
|
16
|
-
const userDecks = await this.dataProvider.getUserData('userDecks');
|
|
17
|
-
return findOrThrow(userDecks, it => it.deckId === deckId);
|
|
18
|
-
}
|
|
19
|
-
async getDeckCards(userDeck) {
|
|
20
|
-
const userCards = await this.dataProvider.getUserData('userCards');
|
|
21
|
-
const cardIds = [userDeck.member1, userDeck.member2, userDeck.member3, userDeck.member4, userDeck.member5];
|
|
22
|
-
return cardIds.map(id => findOrThrow(userCards, it => it.cardId === id));
|
|
23
|
-
}
|
|
24
|
-
async getDeckCardsById(deckId) {
|
|
25
|
-
return await this.getDeckCards(await this.getDeck(deckId));
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
class EventCalculator {
|
|
30
|
-
dataProvider;
|
|
31
|
-
deckCalculator;
|
|
32
|
-
constructor(dataProvider) {
|
|
33
|
-
this.dataProvider = dataProvider;
|
|
34
|
-
this.deckCalculator = new DeckCalculator(dataProvider);
|
|
35
|
-
}
|
|
36
|
-
async getEventDeckBonus(eventId, card) {
|
|
37
|
-
const eventDeckBonuses = await this.dataProvider.getMasterData('eventDeckBonuses');
|
|
38
|
-
const gameCharacterUnits = await this.dataProvider.getMasterData('gameCharacterUnits');
|
|
39
|
-
return eventDeckBonuses.filter(it => it.eventId === eventId &&
|
|
40
|
-
(it.cardAttr === undefined || it.cardAttr === card.attr))
|
|
41
|
-
.reduce((v, eventDeckBonus) => {
|
|
42
|
-
if (eventDeckBonus.gameCharacterUnitId === undefined)
|
|
43
|
-
return Math.max(v, eventDeckBonus.bonusRate);
|
|
44
|
-
const gameCharacterUnit = findOrThrow(gameCharacterUnits, unit => unit.id === eventDeckBonus.gameCharacterUnitId);
|
|
45
|
-
if (gameCharacterUnit.gameCharacterId !== card.characterId)
|
|
46
|
-
return v;
|
|
47
|
-
if (card.characterId < 21 || card.supportUnit === gameCharacterUnit.unit) {
|
|
48
|
-
return Math.max(v, eventDeckBonus.bonusRate);
|
|
49
|
-
}
|
|
50
|
-
return Math.max(v, card.supportUnit === 'none' ? eventDeckBonus.bonusRate - 10 : 0);
|
|
51
|
-
}, 0);
|
|
52
|
-
}
|
|
53
|
-
async getCardEventBonus(userCard, eventId) {
|
|
54
|
-
const cards = await this.dataProvider.getMasterData('cards');
|
|
55
|
-
const eventCards = await this.dataProvider.getMasterData('eventCards');
|
|
56
|
-
const eventRarityBonusRates = await this.dataProvider.getMasterData('eventRarityBonusRates');
|
|
57
|
-
let eventBonus = 0;
|
|
58
|
-
const card = findOrThrow(cards, it => it.id === userCard.cardId);
|
|
59
|
-
eventBonus += await this.getEventDeckBonus(eventId, card);
|
|
60
|
-
const cardBonus = eventCards.find((it) => it.eventId === eventId && it.cardId === card.id);
|
|
61
|
-
if (cardBonus != null) {
|
|
62
|
-
eventBonus += cardBonus.bonusRate;
|
|
63
|
-
}
|
|
64
|
-
const masterRankBonus = findOrThrow(eventRarityBonusRates, it => it.cardRarityType === card.cardRarityType && it.masterRank === userCard.masterRank);
|
|
65
|
-
eventBonus += masterRankBonus.bonusRate;
|
|
66
|
-
return eventBonus;
|
|
67
|
-
}
|
|
68
|
-
async getDeckEventBonus(userDeck, eventId) {
|
|
69
|
-
const deckCards = await this.deckCalculator.getDeckCards(userDeck);
|
|
70
|
-
return await deckCards.reduce(async (v, it) => await this.getCardEventBonus(it, eventId) + await v, Promise.resolve(0));
|
|
71
|
-
}
|
|
72
|
-
async getDeckEventBonusById(deckId, eventId) {
|
|
73
|
-
return await this.getDeckEventBonus(await this.deckCalculator.getDeck(deckId), eventId);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
exports.EventCalculator = EventCalculator;
|