Android build v1.0.32
This commit is contained in:
+54
-17
@@ -26,6 +26,7 @@ export interface GameRepository {
|
||||
completedPart?: number,
|
||||
startPart?: number,
|
||||
partDurationSeconds?: [number, number, number],
|
||||
hardMode?: boolean,
|
||||
): Promise<DungeonReward>
|
||||
completeRoguelike(
|
||||
dungeonId: number,
|
||||
@@ -359,11 +360,33 @@ function experienceForLevel(level: number) {
|
||||
return (level - 1) * (level - 1) * 100
|
||||
}
|
||||
|
||||
function catchUpExperienceReward(
|
||||
baseReward: number,
|
||||
currentExperience: number,
|
||||
currentLevel: number,
|
||||
targetLevel: number,
|
||||
) {
|
||||
if (targetLevel <= currentLevel) return baseReward
|
||||
const targetExperience = experienceForLevel(targetLevel)
|
||||
const gap = Math.max(0, targetExperience - currentExperience)
|
||||
if (gap <= 0) return baseReward
|
||||
const doubledBase = Math.min(baseReward, Math.ceil(gap / 2))
|
||||
return doubledBase * 2 + (baseReward - doubledBase)
|
||||
}
|
||||
|
||||
function highestOtherClassLevel(save: OfflineSave) {
|
||||
const activeClass = save.activeClassId
|
||||
return Object.entries(save.characters)
|
||||
.filter(([classId]) => Number(classId) !== activeClass)
|
||||
.reduce((highest, [, character]) => Math.max(highest, character.level), 0)
|
||||
}
|
||||
|
||||
function scaledPvpBossExperience(
|
||||
startingExperience: number,
|
||||
startingLevel: number,
|
||||
bossesCleared: number,
|
||||
maxLevel: number,
|
||||
targetLevel = startingLevel,
|
||||
) {
|
||||
let experience = startingExperience
|
||||
let level = startingLevel
|
||||
@@ -374,7 +397,8 @@ function scaledPvpBossExperience(
|
||||
? maxExperience
|
||||
: experienceForLevel(level + 1)
|
||||
const levelBand = Math.max(1, nextLevelExperience - currentLevelFloor)
|
||||
experience = Math.min(maxExperience, experience + Math.round(levelBand * 0.25))
|
||||
const rewardRate = targetLevel > level ? 0.5 : 0.25
|
||||
experience = Math.min(maxExperience, experience + Math.round(levelBand * rewardRate))
|
||||
while (level < maxLevel && experienceForLevel(level + 1) <= experience) {
|
||||
level += 1
|
||||
}
|
||||
@@ -389,6 +413,7 @@ const COMPONENT_ITEMS: Record<number, ComponentTemplate> = {
|
||||
20: { id: 604, slug: 'superior-component', name: 'Superior Component', itemLevel: 20, glyph: '◎', description: 'A superior crafting component.' },
|
||||
25: { id: 605, slug: 'primal-component', name: 'Primal Component', itemLevel: 25, glyph: '✦', description: 'A primal crafting component.' },
|
||||
}
|
||||
const DIRECT_CRAFT_ITEM_LEVELS = new Set([1, 10, 20, 25])
|
||||
|
||||
type WindowWithApiBase = Window & {
|
||||
CAPACITOR_API_BASE_URL?: string
|
||||
@@ -716,7 +741,7 @@ const serverRepository: GameRepository = {
|
||||
),
|
||||
saveProfile: (classId, abilitySlots) =>
|
||||
cachedOnlineLocalRepository.saveProfile(classId, abilitySlots),
|
||||
completeDungeon: (dungeonId, difficultyId, resourceSpent, durationSeconds, completedPart, startPart, partDurationSeconds) =>
|
||||
completeDungeon: (dungeonId, difficultyId, resourceSpent, durationSeconds, completedPart, startPart, partDurationSeconds, hardMode) =>
|
||||
cachedOnlineLocalRepository.completeDungeon(
|
||||
dungeonId,
|
||||
difficultyId,
|
||||
@@ -725,6 +750,7 @@ const serverRepository: GameRepository = {
|
||||
completedPart,
|
||||
startPart,
|
||||
partDurationSeconds,
|
||||
hardMode,
|
||||
),
|
||||
completeRoguelike: (dungeonId, difficultyId, encountersCleared, resourceSpent, durationSeconds, options) =>
|
||||
cachedOnlineLocalRepository.completeRoguelike(
|
||||
@@ -831,7 +857,7 @@ function createLocalRepository(store: LocalSaveStore): GameRepository {
|
||||
store.writeSave(save)
|
||||
return buildProfile(save)
|
||||
},
|
||||
async completeDungeon(dungeonId, difficultyId, resourceSpent, durationSeconds, completedPart, startPart, partDurationSeconds) {
|
||||
async completeDungeon(dungeonId, difficultyId, resourceSpent, durationSeconds, completedPart, startPart, partDurationSeconds, hardMode) {
|
||||
void startPart
|
||||
void partDurationSeconds
|
||||
if (!Number.isInteger(resourceSpent) || resourceSpent < 0) {
|
||||
@@ -857,8 +883,15 @@ function createLocalRepository(store: LocalSaveStore): GameRepository {
|
||||
const previousLevel = cd.level
|
||||
const previousExperience = cd.experience
|
||||
const partCount = completedPart ?? 1
|
||||
const experienceReward = Math.round(
|
||||
dungeon.experienceReward * difficulty.experienceMultiplier * partCount,
|
||||
const rewardMultiplier = hardMode ? 2 : 1
|
||||
const baseExperienceReward = Math.round(
|
||||
dungeon.experienceReward * difficulty.experienceMultiplier * partCount * rewardMultiplier,
|
||||
)
|
||||
const experienceReward = catchUpExperienceReward(
|
||||
baseExperienceReward,
|
||||
previousExperience,
|
||||
previousLevel,
|
||||
highestOtherClassLevel(save),
|
||||
)
|
||||
const maxExperience = experienceForLevel(profile.maxLevel)
|
||||
const newExperience = Math.min(previousExperience + experienceReward, maxExperience)
|
||||
@@ -906,19 +939,20 @@ function createLocalRepository(store: LocalSaveStore): GameRepository {
|
||||
const selected = rewardPool[Math.floor(Math.random() * rewardPool.length)]
|
||||
const existing = profile.inventory.find((item) => item.id === selected.id)
|
||||
const duplicate = Boolean(existing)
|
||||
let quantityAfter = 1
|
||||
const rewardQuantity = rewardMultiplier
|
||||
let quantityAfter = rewardQuantity
|
||||
if (existing) {
|
||||
existing.quantity += 1
|
||||
existing.quantity += rewardQuantity
|
||||
quantityAfter = existing.quantity
|
||||
} else {
|
||||
profile.inventory.push({
|
||||
...selected,
|
||||
quantity: 1,
|
||||
quantity: rewardQuantity,
|
||||
equipped: false,
|
||||
})
|
||||
}
|
||||
cd.inventory = profile.inventory
|
||||
bonusItem = { ...selected, quantity: 1, duplicate, quantityAfter }
|
||||
bonusItem = { ...selected, quantity: rewardQuantity, duplicate, quantityAfter }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -971,13 +1005,19 @@ function createLocalRepository(store: LocalSaveStore): GameRepository {
|
||||
const maxExperience = experienceForLevel(profile.maxLevel)
|
||||
const bossesCleared = Math.max(0, Math.floor(options?.bossesCleared ?? encountersCleared / 3))
|
||||
const scaledReward = options?.experienceMode === 'pvp-boss-quarter-level'
|
||||
? scaledPvpBossExperience(previousExperience, previousLevel, bossesCleared, profile.maxLevel)
|
||||
? scaledPvpBossExperience(previousExperience, previousLevel, bossesCleared, profile.maxLevel, highestOtherClassLevel(save))
|
||||
: null
|
||||
const baseRoguelikeReward = Math.round(dungeon.experienceReward * difficulty.experienceMultiplier * (encountersCleared / 3))
|
||||
const newExperience = scaledReward
|
||||
? scaledReward.experience
|
||||
: Math.min(
|
||||
previousExperience
|
||||
+ Math.round(dungeon.experienceReward * difficulty.experienceMultiplier * (encountersCleared / 3)),
|
||||
+ catchUpExperienceReward(
|
||||
baseRoguelikeReward,
|
||||
previousExperience,
|
||||
previousLevel,
|
||||
highestOtherClassLevel(save),
|
||||
),
|
||||
maxExperience,
|
||||
)
|
||||
let newLevel = scaledReward?.level ?? previousLevel
|
||||
@@ -1161,11 +1201,7 @@ function createLocalRepository(store: LocalSaveStore): GameRepository {
|
||||
const profile = buildProfile(save)
|
||||
const recipe = profile.craftingRecipes.find((candidate) => candidate.id === recipeId)
|
||||
if (!recipe) throw new Error('That crafting recipe does not exist.')
|
||||
const requiresUpgrade = profile.craftingRecipes.some((candidate) =>
|
||||
candidate.sourceEncounterId === recipe.sourceEncounterId
|
||||
&& candidate.item.slot === recipe.item.slot
|
||||
&& candidate.item.itemLevel < recipe.item.itemLevel,
|
||||
)
|
||||
const requiresUpgrade = !DIRECT_CRAFT_ITEM_LEVELS.has(recipe.item.itemLevel)
|
||||
if (requiresUpgrade) throw new Error('Upgrade the previous item tier instead.')
|
||||
const missing = recipe.components.find((component) => component.owned < component.quantity)
|
||||
if (missing) {
|
||||
@@ -1361,7 +1397,7 @@ const cachedOnlineRepository: GameRepository = {
|
||||
},
|
||||
loadProfile: () => cachedOnlineLocalRepository.loadProfile(),
|
||||
saveProfile: (classId, abilitySlots) => cachedOnlineLocalRepository.saveProfile(classId, abilitySlots),
|
||||
completeDungeon: (dungeonId, difficultyId, resourceSpent, durationSeconds, completedPart, startPart, partDurationSeconds) =>
|
||||
completeDungeon: (dungeonId, difficultyId, resourceSpent, durationSeconds, completedPart, startPart, partDurationSeconds, hardMode) =>
|
||||
cachedOnlineLocalRepository.completeDungeon(
|
||||
dungeonId,
|
||||
difficultyId,
|
||||
@@ -1370,6 +1406,7 @@ const cachedOnlineRepository: GameRepository = {
|
||||
completedPart,
|
||||
startPart,
|
||||
partDurationSeconds,
|
||||
hardMode,
|
||||
),
|
||||
completeRoguelike: (dungeonId, difficultyId, encountersCleared, resourceSpent, durationSeconds, options) =>
|
||||
cachedOnlineLocalRepository.completeRoguelike(
|
||||
|
||||
Reference in New Issue
Block a user