Android build v1.0.39

This commit is contained in:
Warren H
2026-06-20 20:09:57 -04:00
parent f8a1fbc5e2
commit 8f5a957963
11 changed files with 4311 additions and 2696 deletions
+61 -5
View File
@@ -341,6 +341,7 @@ export function CombatScreen({
difficulty,
dungeon,
hardMode = false,
marathonMode = false,
profile,
startPart = 1,
roguelikeMode,
@@ -353,6 +354,7 @@ export function CombatScreen({
difficulty: Difficulty
dungeon: Dungeon
hardMode?: boolean
marathonMode?: boolean
profile: CharacterProfile
startPart?: number
roguelikeMode?: RoguelikeMode
@@ -416,7 +418,7 @@ export function CombatScreen({
const [combatState, setCombatState] = useState<SinglePlayerCombatState>(() => initialCombatState)
const [selectedId, setSelectedId] = useState(partyTemplate[0].id)
const [encounterIndex, setEncounterIndex] = useState(initialEncounterIndex)
const [status, setStatus] = useState<'playing' | 'won' | 'lost' | 'part-complete' | 'upgrade-choice'>('playing')
const [status, setStatus] = useState<'playing' | 'won' | 'lost' | 'part-complete' | 'marathon-choice' | 'upgrade-choice'>('playing')
const [paused, setPaused] = useState(false)
const [speedMultiplier, setSpeedMultiplier] = useState<1 | 2>(1)
const [targetGroup, setTargetGroup] = useState<0 | 1 | 2>(0)
@@ -430,6 +432,7 @@ export function CombatScreen({
const [floatingTexts, setFloatingTexts] = useState<FloatingCombatText[]>([])
const [roguelikeUpgrades, setRoguelikeUpgrades] = useState<RoguelikeUpgrade[]>([])
const [upgradeChoices, setUpgradeChoices] = useState<RoguelikeUpgrade[]>([])
const [marathonBossesDefeated, setMarathonBossesDefeated] = useState(0)
const rewardClaimedRef = useRef(false)
const profileRefreshedRef = useRef(false)
const rolledEncounterIdsRef = useRef(new Set<string>())
@@ -439,6 +442,7 @@ export function CombatScreen({
const partStartTimesRef = useRef<Record<number, number>>({})
const nextLogId = useRef(2)
const nextFloatingTextId = useRef(1)
const marathonBossesDefeatedRef = useRef(0)
const combatRef = useRef(initialCombatState)
const selectedIdRef = useRef(partyTemplate[0].id)
const runCombatTickRef = useRef<() => void>(() => {})
@@ -553,10 +557,10 @@ export function CombatScreen({
const requestLootRoll = useCallback(
(encounterId: number, rollIndex = 0) => {
const rollKey = `${encounterId}:${rollIndex}`
const rollKey = `${encounterId}:${rollIndex}:${marathonBossesDefeatedRef.current}`
if (rolledEncounterIdsRef.current.has(rollKey)) return
rolledEncounterIdsRef.current.add(rollKey)
const runToken = rollIndex === 0 ? runTokenRef.current : `${runTokenRef.current}-hard-${rollIndex}`
const runToken = `${runTokenRef.current}-${marathonBossesDefeatedRef.current}-${rollIndex}`
rollEncounterLoot(encounterId, difficulty.id, runToken)
.then((result) => {
setLootRolls((current) => [...current, result])
@@ -609,10 +613,12 @@ export function CombatScreen({
setFloatingTexts([])
setRoguelikeUpgrades([])
setUpgradeChoices([])
setMarathonBossesDefeated(0)
rewardClaimedRef.current = false
profileRefreshedRef.current = false
rolledEncounterIdsRef.current = new Set()
runTokenRef.current = crypto.randomUUID()
marathonBossesDefeatedRef.current = 0
resourceSpentRef.current = 0
runStartedAtRef.current = Date.now()
partStartTimesRef.current = { [startPart]: runStartedAtRef.current }
@@ -1201,6 +1207,9 @@ export function CombatScreen({
}
if (isPartBoss && !isFinalBoss) {
const nextMarathonKills = marathonBossesDefeatedRef.current + 1
marathonBossesDefeatedRef.current = nextMarathonKills
setMarathonBossesDefeated(nextMarathonKills)
setCombat({
...current,
party: nextParty,
@@ -1209,12 +1218,28 @@ export function CombatScreen({
elapsedTicks: nextElapsedTicks,
enemyHealth: 0,
})
setStatus('part-complete')
setStatus(marathonMode && encounter.isBoss ? 'marathon-choice' : 'part-complete')
addLog(`${encounter.enemyName} is defeated.`, 'loot')
return
}
if (encounterIndex === encounters.length - 1) {
if (marathonMode && encounter.isBoss) {
const nextMarathonKills = marathonBossesDefeatedRef.current + 1
marathonBossesDefeatedRef.current = nextMarathonKills
setMarathonBossesDefeated(nextMarathonKills)
setCombat({
...current,
party: nextParty,
resource: nextResource,
cooldowns: nextCooldowns,
elapsedTicks: nextElapsedTicks,
enemyHealth: 0,
})
setStatus('marathon-choice')
addLog(`${encounter.enemyName} is defeated. Continue marathon or end the hunt.`, 'loot')
return
}
setCombat({
...current,
party: nextParty,
@@ -1267,6 +1292,7 @@ export function CombatScreen({
isPartBoss,
isFinalBoss,
isRoguelike,
marathonMode,
upgradesEveryEncounter,
roguelikeUpgradeCatalog,
roguelikeUpgrades,
@@ -1620,7 +1646,7 @@ export function CombatScreen({
</div>
)}
{status !== 'playing' && status !== 'part-complete' && status !== 'upgrade-choice' && (
{status !== 'playing' && status !== 'part-complete' && status !== 'marathon-choice' && status !== 'upgrade-choice' && (
<div className="result-screen">
<div>
<p className="eyebrow">{status === 'won' ? `${contentName} Complete` : 'Party Defeated'}</p>
@@ -1735,6 +1761,36 @@ export function CombatScreen({
</div>
</div>
)}
{status === 'marathon-choice' && (
<div className="result-screen">
<div>
<p className="eyebrow">Marathon</p>
<h2>{encounter.enemyName} Defeated</h2>
<p>
{marathonBossesDefeated} boss{marathonBossesDefeated === 1 ? '' : 'es'} defeated.
Continue with current health and {gameClass.resourceName}, or end the hunt.
</p>
<button
onClick={() => {
const current = combatRef.current
setCombat({
...current,
enemyHealth: encounter.maxHealth * enemyCount,
elapsedTicks: 0,
})
setStatus('playing')
addLog(`Marathon continues. Another ${encounter.enemyName} appears.`, 'danger')
}}
type="button"
>
Continue Marathon
</button>
<button className="secondary-result-button" onClick={() => finishRun(currentPart, startPart)} type="button">
End
</button>
</div>
</div>
)}
{status === 'part-complete' && (
<div className="result-screen">
<div>