Android build v1.1.29
This commit is contained in:
Binary file not shown.
@@ -7,8 +7,8 @@ android {
|
|||||||
applicationId "com.warren.iwanttoheal"
|
applicationId "com.warren.iwanttoheal"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 108
|
versionCode 109
|
||||||
versionName "1.1.28"
|
versionName "1.1.29"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ import {
|
|||||||
type Iwt2RoguelikeSelfBuffId,
|
type Iwt2RoguelikeSelfBuffId,
|
||||||
type Iwt2RoguelikeVariant,
|
type Iwt2RoguelikeVariant,
|
||||||
} from './content/roguelike'
|
} from './content/roguelike'
|
||||||
|
import {
|
||||||
|
createIwt2PveRoguelikeBossPair,
|
||||||
|
createUniformIwt2RoguelikeBossPair,
|
||||||
|
IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED,
|
||||||
|
} from './content/roguelikeBossProgression'
|
||||||
|
|
||||||
type Iwt2Screen =
|
type Iwt2Screen =
|
||||||
| 'menu'
|
| 'menu'
|
||||||
@@ -522,7 +527,7 @@ function createRoguelikeRun(
|
|||||||
contentType: Iwt2RoguelikeContentType,
|
contentType: Iwt2RoguelikeContentType,
|
||||||
): Iwt2RoguelikeRunState {
|
): Iwt2RoguelikeRunState {
|
||||||
return {
|
return {
|
||||||
bossIds: createRandomRoguelikeBossPair(),
|
bossIds: createRoguelikeBossPair(variant, 1),
|
||||||
buffs: [],
|
buffs: [],
|
||||||
contentType,
|
contentType,
|
||||||
debuffs: [],
|
debuffs: [],
|
||||||
@@ -590,15 +595,17 @@ function applyRoguelikeChoice(
|
|||||||
return {
|
return {
|
||||||
...run,
|
...run,
|
||||||
...nextBase,
|
...nextBase,
|
||||||
bossIds: createRandomRoguelikeBossPair(),
|
bossIds: createRoguelikeBossPair(run.variant, run.stage + 1),
|
||||||
...buildRoguelikeChoices(save, run.variant),
|
...buildRoguelikeChoices(save, run.variant),
|
||||||
stage: run.stage + 1,
|
stage: run.stage + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRandomRoguelikeBossPair(): Iwt2BossId[] {
|
function createRoguelikeBossPair(variant: Iwt2RoguelikeVariant, stage: number): Iwt2BossId[] {
|
||||||
const pool = Object.keys(IWT2_BOSS_METADATA) as Iwt2BossId[]
|
if (variant === 'pve' && IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED) {
|
||||||
return chooseRunChoices(pool, 2)
|
return createIwt2PveRoguelikeBossPair(stage)
|
||||||
|
}
|
||||||
|
return createUniformIwt2RoguelikeBossPair()
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseRunChoices<T>(items: readonly T[], count: number): T[] {
|
function chooseRunChoices<T>(items: readonly T[], count: number): T[] {
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
import { IWT2_BOSS_METADATA, type Iwt2BossId } from './bosses'
|
||||||
|
|
||||||
|
export const IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED = true
|
||||||
|
|
||||||
|
type Iwt2RoguelikeBossTier = 'early' | 'mid' | 'late'
|
||||||
|
|
||||||
|
type TierWeight = {
|
||||||
|
tier: Iwt2RoguelikeBossTier
|
||||||
|
weight: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const IWT2_ROGUELIKE_BOSS_TIERS: Record<Iwt2RoguelikeBossTier, readonly Iwt2BossId[]> = {
|
||||||
|
early: ['bulldrome', 'yian-kut-ku', 'great-jaggi', 'rathian', 'stormcoil-wyrm'],
|
||||||
|
mid: ['khezu', 'barroth', 'tobi-kadachi', 'ember-mantis-duelist', 'crystal-bat-matriarch', 'hollowcrown-revenant'],
|
||||||
|
late: ['rimebastion', 'cinderback-ricochet', 'obsidian-ram-golem', 'venom-orchid-hydra', 'sandglass-scorpion'],
|
||||||
|
}
|
||||||
|
|
||||||
|
const IWT2_ROGUELIKE_BOSS_THREAT: Record<Iwt2RoguelikeBossTier, number> = {
|
||||||
|
early: 1,
|
||||||
|
mid: 2,
|
||||||
|
late: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
const IWT2_ROGUELIKE_BOSS_TIER_BY_ID: Record<Iwt2BossId, Iwt2RoguelikeBossTier> = {
|
||||||
|
bulldrome: 'early',
|
||||||
|
'yian-kut-ku': 'early',
|
||||||
|
'great-jaggi': 'early',
|
||||||
|
rathian: 'early',
|
||||||
|
'stormcoil-wyrm': 'early',
|
||||||
|
khezu: 'mid',
|
||||||
|
barroth: 'mid',
|
||||||
|
'tobi-kadachi': 'mid',
|
||||||
|
'ember-mantis-duelist': 'mid',
|
||||||
|
'crystal-bat-matriarch': 'mid',
|
||||||
|
'hollowcrown-revenant': 'mid',
|
||||||
|
rimebastion: 'late',
|
||||||
|
'cinderback-ricochet': 'late',
|
||||||
|
'obsidian-ram-golem': 'late',
|
||||||
|
'venom-orchid-hydra': 'late',
|
||||||
|
'sandglass-scorpion': 'late',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createIwt2PveRoguelikeBossPair(
|
||||||
|
stage: number,
|
||||||
|
random: () => number = Math.random,
|
||||||
|
): Iwt2BossId[] {
|
||||||
|
const choices: Iwt2BossId[] = []
|
||||||
|
const maxThreat = maxThreatForStage(stage)
|
||||||
|
|
||||||
|
while (choices.length < 2) {
|
||||||
|
const next = chooseWeightedBossForStage(stage, choices, maxThreat, random)
|
||||||
|
if (!next) break
|
||||||
|
choices.push(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
return choices.length === 2
|
||||||
|
? choices
|
||||||
|
: createUniformIwt2RoguelikeBossPair(random)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUniformIwt2RoguelikeBossPair(random: () => number = Math.random): Iwt2BossId[] {
|
||||||
|
const pool = Object.keys(IWT2_BOSS_METADATA) as Iwt2BossId[]
|
||||||
|
const choices: Iwt2BossId[] = []
|
||||||
|
while (pool.length > 0 && choices.length < 2) {
|
||||||
|
const index = randomIndex(pool.length, random)
|
||||||
|
const [choice] = pool.splice(index, 1)
|
||||||
|
if (choice) choices.push(choice)
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseWeightedBossForStage(
|
||||||
|
stage: number,
|
||||||
|
selected: readonly Iwt2BossId[],
|
||||||
|
maxThreat: number,
|
||||||
|
random: () => number,
|
||||||
|
): Iwt2BossId | undefined {
|
||||||
|
const selectedSet = new Set(selected)
|
||||||
|
const selectedThreat = selected.reduce((total, bossId) => total + threatForBoss(bossId), 0)
|
||||||
|
const weightedTiers = tierWeightsForStage(stage)
|
||||||
|
.map((entry) => ({
|
||||||
|
...entry,
|
||||||
|
bosses: IWT2_ROGUELIKE_BOSS_TIERS[entry.tier].filter((bossId) => (
|
||||||
|
!selectedSet.has(bossId) && selectedThreat + threatForBoss(bossId) <= maxThreat
|
||||||
|
)),
|
||||||
|
}))
|
||||||
|
.filter((entry) => entry.weight > 0 && entry.bosses.length > 0)
|
||||||
|
|
||||||
|
const totalWeight = weightedTiers.reduce((total, entry) => total + entry.weight, 0)
|
||||||
|
if (totalWeight <= 0) return undefined
|
||||||
|
|
||||||
|
let roll = safeRandom(random) * totalWeight
|
||||||
|
for (const entry of weightedTiers) {
|
||||||
|
roll -= entry.weight
|
||||||
|
if (roll <= 0) {
|
||||||
|
return entry.bosses[randomIndex(entry.bosses.length, random)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastEntry = weightedTiers[weightedTiers.length - 1]
|
||||||
|
return lastEntry?.bosses[randomIndex(lastEntry.bosses.length, random)]
|
||||||
|
}
|
||||||
|
|
||||||
|
function tierWeightsForStage(stage: number): TierWeight[] {
|
||||||
|
const safeStage = Math.max(1, Math.floor(stage))
|
||||||
|
if (safeStage <= 2) {
|
||||||
|
return [
|
||||||
|
{ tier: 'early', weight: 85 },
|
||||||
|
{ tier: 'mid', weight: 15 },
|
||||||
|
{ tier: 'late', weight: 0 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (safeStage === 3) {
|
||||||
|
return [
|
||||||
|
{ tier: 'early', weight: 60 },
|
||||||
|
{ tier: 'mid', weight: 35 },
|
||||||
|
{ tier: 'late', weight: 5 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (safeStage <= 5) {
|
||||||
|
return [
|
||||||
|
{ tier: 'early', weight: 25 },
|
||||||
|
{ tier: 'mid', weight: 60 },
|
||||||
|
{ tier: 'late', weight: 15 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (safeStage === 6) {
|
||||||
|
return [
|
||||||
|
{ tier: 'early', weight: 10 },
|
||||||
|
{ tier: 'mid', weight: 50 },
|
||||||
|
{ tier: 'late', weight: 40 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{ tier: 'early', weight: 5 },
|
||||||
|
{ tier: 'mid', weight: 30 },
|
||||||
|
{ tier: 'late', weight: 65 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function maxThreatForStage(stage: number): number {
|
||||||
|
const safeStage = Math.max(1, Math.floor(stage))
|
||||||
|
if (safeStage <= 2) return 3
|
||||||
|
if (safeStage === 3) return 4
|
||||||
|
if (safeStage <= 5) return 5
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
|
||||||
|
function threatForBoss(bossId: Iwt2BossId): number {
|
||||||
|
return IWT2_ROGUELIKE_BOSS_THREAT[IWT2_ROGUELIKE_BOSS_TIER_BY_ID[bossId]]
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomIndex(length: number, random: () => number): number {
|
||||||
|
return Math.min(length - 1, Math.floor(safeRandom(random) * length))
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeRandom(random: () => number): number {
|
||||||
|
const value = random()
|
||||||
|
return Number.isFinite(value) ? Math.min(0.999999999, Math.max(0, value)) : 0
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user