Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 224249e372 | |||
| 66f5af4484 |
Binary file not shown.
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 59
|
versionCode 61
|
||||||
versionName "1.0.39"
|
versionName "1.0.41"
|
||||||
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.
|
||||||
|
|||||||
+29
-33
@@ -1795,6 +1795,23 @@ h2 {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.run-title-row {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-title-row h2 {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-back-button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.run-setup-heading small {
|
.run-setup-heading small {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -1952,16 +1969,6 @@ h2 {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.part-start-row {
|
|
||||||
display: grid;
|
|
||||||
gap: 8px;
|
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(82px, 0.38fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hard-mode-button {
|
|
||||||
border-color: #c25b4b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-setup-panel .primary-button {
|
.part-setup-panel .primary-button {
|
||||||
min-height: 54px;
|
min-height: 54px;
|
||||||
}
|
}
|
||||||
@@ -2054,14 +2061,6 @@ h2 {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dungeon-run-screen .screen-heading {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dungeon-run-screen .screen-heading h1 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dungeon-run-board,
|
.dungeon-run-board,
|
||||||
.dungeon-run-main {
|
.dungeon-run-main {
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@@ -2098,6 +2097,12 @@ h2 {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline-back-button {
|
||||||
|
font-size: 12px;
|
||||||
|
min-height: 28px;
|
||||||
|
padding: 4px 7px;
|
||||||
|
}
|
||||||
|
|
||||||
.activity-pager button {
|
.activity-pager button {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
@@ -2246,21 +2251,6 @@ h2 {
|
|||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dungeon-run-screen .screen-heading {
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dungeon-run-screen .screen-heading h1 {
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.15;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dungeon-run-screen .back-button {
|
|
||||||
font-size: 14px;
|
|
||||||
min-height: 34px;
|
|
||||||
padding: 5px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dungeon-run-board,
|
.dungeon-run-board,
|
||||||
.dungeon-run-main,
|
.dungeon-run-main,
|
||||||
.dungeon-setup-rail {
|
.dungeon-setup-rail {
|
||||||
@@ -2297,6 +2287,12 @@ h2 {
|
|||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline-back-button {
|
||||||
|
font-size: 10px;
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 3px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.dungeon-run-screen .eyebrow,
|
.dungeon-run-screen .eyebrow,
|
||||||
.dungeon-run-screen .tier-grid strong,
|
.dungeon-run-screen .tier-grid strong,
|
||||||
.dungeon-choice-grid .activity-card strong,
|
.dungeon-choice-grid .activity-card strong,
|
||||||
|
|||||||
+18
-25
@@ -88,7 +88,6 @@ function App() {
|
|||||||
const [roguelikeUpgradeTiming, setRoguelikeUpgradeTiming] = useState<RoguelikeUpgradeTiming>('encounter')
|
const [roguelikeUpgradeTiming, setRoguelikeUpgradeTiming] = useState<RoguelikeUpgradeTiming>('encounter')
|
||||||
const [roguelikeAbilityLabelMode, setRoguelikeAbilityLabelMode] = useState<RoguelikeAbilityLabelMode>('ability')
|
const [roguelikeAbilityLabelMode, setRoguelikeAbilityLabelMode] = useState<RoguelikeAbilityLabelMode>('ability')
|
||||||
const [pvpContentType, setPvpContentType] = useState<PvpContentType>('dungeon')
|
const [pvpContentType, setPvpContentType] = useState<PvpContentType>('dungeon')
|
||||||
const [selectedPart, setSelectedPart] = useState(1)
|
|
||||||
const [selectedMarathonMode, setSelectedMarathonMode] = useState(false)
|
const [selectedMarathonMode, setSelectedMarathonMode] = useState(false)
|
||||||
const [activityPage, setActivityPage] = useState(0)
|
const [activityPage, setActivityPage] = useState(0)
|
||||||
const [combatContentId, setCombatContentId] = useState(1)
|
const [combatContentId, setCombatContentId] = useState(1)
|
||||||
@@ -233,7 +232,6 @@ function App() {
|
|||||||
const roguelikePool = profile.dungeons
|
const roguelikePool = profile.dungeons
|
||||||
.filter((candidate) => candidate.contentType === roguelikeKind)
|
.filter((candidate) => candidate.contentType === roguelikeKind)
|
||||||
.flatMap((candidate) => candidate.encounters)
|
.flatMap((candidate) => candidate.encounters)
|
||||||
const startPart = selectedPart
|
|
||||||
return (
|
return (
|
||||||
<CombatScreen
|
<CombatScreen
|
||||||
difficulty={difficulty}
|
difficulty={difficulty}
|
||||||
@@ -245,7 +243,7 @@ function App() {
|
|||||||
roguelikeUpgradeTiming={combatContentId < 0 ? roguelikeUpgradeTiming : undefined}
|
roguelikeUpgradeTiming={combatContentId < 0 ? roguelikeUpgradeTiming : undefined}
|
||||||
roguelikeAbilityLabelMode={combatContentId < 0 ? roguelikeAbilityLabelMode : undefined}
|
roguelikeAbilityLabelMode={combatContentId < 0 ? roguelikeAbilityLabelMode : undefined}
|
||||||
roguelikeEncounterPool={combatContentId < 0 ? roguelikePool : undefined}
|
roguelikeEncounterPool={combatContentId < 0 ? roguelikePool : undefined}
|
||||||
startPart={startPart}
|
startPart={1}
|
||||||
onExit={() => {
|
onExit={() => {
|
||||||
setScreen(combatContentId < 0 ? 'roguelike' : dungeon.contentType === 'raid' ? 'raids' : 'dungeons')
|
setScreen(combatContentId < 0 ? 'roguelike' : dungeon.contentType === 'raid' ? 'raids' : 'dungeons')
|
||||||
}}
|
}}
|
||||||
@@ -298,7 +296,6 @@ function App() {
|
|||||||
setCombatContentId(-1)
|
setCombatContentId(-1)
|
||||||
setSelectedDifficultyId(baseDungeon?.difficulties[0]?.id ?? 1)
|
setSelectedDifficultyId(baseDungeon?.difficulties[0]?.id ?? 1)
|
||||||
}
|
}
|
||||||
setSelectedPart(1)
|
|
||||||
setSelectedMarathonMode(false)
|
setSelectedMarathonMode(false)
|
||||||
setScreen('combat')
|
setScreen('combat')
|
||||||
}
|
}
|
||||||
@@ -318,24 +315,24 @@ function App() {
|
|||||||
?? tierOptions.slice().reverse().find((candidate) => profile.character.level >= candidate.unlockLevel)
|
?? tierOptions.slice().reverse().find((candidate) => profile.character.level >= candidate.unlockLevel)
|
||||||
?? tierOptions[0]
|
?? tierOptions[0]
|
||||||
const selectedTierItemLevel = selectedTier?.droppedItemLevel ?? 0
|
const selectedTierItemLevel = selectedTier?.droppedItemLevel ?? 0
|
||||||
const tierActivityOptions = activityOptions.filter((option) =>
|
const activityPageCount = Math.max(1, Math.ceil(activityOptions.length / ACTIVITY_PAGE_SIZE))
|
||||||
option.difficulties.some((difficulty) => difficulty.droppedItemLevel === selectedTierItemLevel),
|
|
||||||
)
|
|
||||||
const activityPageCount = Math.max(1, Math.ceil(tierActivityOptions.length / ACTIVITY_PAGE_SIZE))
|
|
||||||
const currentActivityPage = Math.min(activityPage, activityPageCount - 1)
|
const currentActivityPage = Math.min(activityPage, activityPageCount - 1)
|
||||||
const pagedActivityOptions = tierActivityOptions.slice(
|
const pagedActivityOptions = activityOptions.slice(
|
||||||
currentActivityPage * ACTIVITY_PAGE_SIZE,
|
currentActivityPage * ACTIVITY_PAGE_SIZE,
|
||||||
currentActivityPage * ACTIVITY_PAGE_SIZE + ACTIVITY_PAGE_SIZE,
|
currentActivityPage * ACTIVITY_PAGE_SIZE + ACTIVITY_PAGE_SIZE,
|
||||||
)
|
)
|
||||||
|
const activityPageStart = activityOptions.length === 0
|
||||||
|
? 0
|
||||||
|
: currentActivityPage * ACTIVITY_PAGE_SIZE + 1
|
||||||
|
const activityPageEnd = Math.min(activityOptions.length, (currentActivityPage + 1) * ACTIVITY_PAGE_SIZE)
|
||||||
const selectedActivityId = screen === 'raids' && raid ? raid.id : dungeon.id
|
const selectedActivityId = screen === 'raids' && raid ? raid.id : dungeon.id
|
||||||
const activity = tierActivityOptions.find((candidate) => candidate.id === selectedActivityId)
|
const activity = activityOptions.find((candidate) => candidate.id === selectedActivityId)
|
||||||
?? tierActivityOptions[0]
|
?? activityOptions[0]
|
||||||
?? (screen === 'raids' && raid ? raid : dungeon)
|
?? (screen === 'raids' && raid ? raid : dungeon)
|
||||||
const selectedDifficulty = activity.difficulties.find(
|
const selectedDifficulty = activity.difficulties.find(
|
||||||
(candidate) => candidate.droppedItemLevel === selectedTierItemLevel,
|
(candidate) => candidate.droppedItemLevel === selectedTierItemLevel,
|
||||||
) ?? activity.difficulties[0]
|
) ?? activity.difficulties[0]
|
||||||
const difficultyLocked = profile.character.level < selectedDifficulty.unlockLevel
|
const difficultyLocked = profile.character.level < selectedDifficulty.unlockLevel
|
||||||
const sectionName = activity.contentType === 'raid' ? 'Phase' : 'Part'
|
|
||||||
const activityCompletionCount = activity.completionCount ?? 0
|
const activityCompletionCount = activity.completionCount ?? 0
|
||||||
const marathonUnlocked = activityCompletionCount >= 10
|
const marathonUnlocked = activityCompletionCount >= 10
|
||||||
const cloudSync = getCloudSyncStatus()
|
const cloudSync = getCloudSyncStatus()
|
||||||
@@ -612,11 +609,6 @@ function App() {
|
|||||||
|
|
||||||
{(screen === 'dungeons' || screen === 'raids') && (
|
{(screen === 'dungeons' || screen === 'raids') && (
|
||||||
<section className="content-screen dungeon-run-screen">
|
<section className="content-screen dungeon-run-screen">
|
||||||
<ScreenHeading
|
|
||||||
eyebrow="Adventure"
|
|
||||||
title={activity.contentType === 'raid' ? 'Raids' : 'Dungeons'}
|
|
||||||
onBack={() => setScreen('menu')}
|
|
||||||
/>
|
|
||||||
<div className="dungeon-run-board">
|
<div className="dungeon-run-board">
|
||||||
<div className="dungeon-run-main">
|
<div className="dungeon-run-main">
|
||||||
<article className="run-summary-card dungeon-focus-card">
|
<article className="run-summary-card dungeon-focus-card">
|
||||||
@@ -625,7 +617,10 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="run-summary-copy">
|
<div className="run-summary-copy">
|
||||||
<p className="eyebrow">Selected Run</p>
|
<p className="eyebrow">Selected Run</p>
|
||||||
<h2>{activity.name}</h2>
|
<div className="run-title-row">
|
||||||
|
<h2>{activity.name}</h2>
|
||||||
|
<button className="back-button inline-back-button" onClick={() => setScreen('menu')} type="button">Back</button>
|
||||||
|
</div>
|
||||||
<p>{activity.description}</p>
|
<p>{activity.description}</p>
|
||||||
<div className="tag-row">
|
<div className="tag-row">
|
||||||
<span>Level {activity.recommendedLevel}</span>
|
<span>Level {activity.recommendedLevel}</span>
|
||||||
@@ -652,7 +647,7 @@ function App() {
|
|||||||
>
|
>
|
||||||
Prev
|
Prev
|
||||||
</button>
|
</button>
|
||||||
<span>{currentActivityPage + 1}/{activityPageCount}</span>
|
<span>{activityPageStart}-{activityPageEnd} of {activityOptions.length}</span>
|
||||||
<button
|
<button
|
||||||
disabled={currentActivityPage >= activityPageCount - 1}
|
disabled={currentActivityPage >= activityPageCount - 1}
|
||||||
onClick={() => setActivityPage((page) => Math.min(activityPageCount - 1, page + 1))}
|
onClick={() => setActivityPage((page) => Math.min(activityPageCount - 1, page + 1))}
|
||||||
@@ -764,7 +759,6 @@ function App() {
|
|||||||
className="primary-button selected-part"
|
className="primary-button selected-part"
|
||||||
disabled={difficultyLocked}
|
disabled={difficultyLocked}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedPart(1)
|
|
||||||
setSelectedMarathonMode(false)
|
setSelectedMarathonMode(false)
|
||||||
setCombatContentId(activity.id)
|
setCombatContentId(activity.id)
|
||||||
setSelectedDifficultyId(selectedDifficulty.id)
|
setSelectedDifficultyId(selectedDifficulty.id)
|
||||||
@@ -778,7 +772,6 @@ function App() {
|
|||||||
className={`primary-button ${selectedMarathonMode ? 'selected-part' : ''} ${!marathonUnlocked ? 'locked' : ''}`}
|
className={`primary-button ${selectedMarathonMode ? 'selected-part' : ''} ${!marathonUnlocked ? 'locked' : ''}`}
|
||||||
disabled={difficultyLocked || !marathonUnlocked}
|
disabled={difficultyLocked || !marathonUnlocked}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedPart(1)
|
|
||||||
setSelectedMarathonMode(true)
|
setSelectedMarathonMode(true)
|
||||||
setCombatContentId(activity.id)
|
setCombatContentId(activity.id)
|
||||||
setSelectedDifficultyId(selectedDifficulty.id)
|
setSelectedDifficultyId(selectedDifficulty.id)
|
||||||
@@ -900,10 +893,10 @@ function App() {
|
|||||||
</p>
|
</p>
|
||||||
<div className="leaderboard-tabs">
|
<div className="leaderboard-tabs">
|
||||||
{([
|
{([
|
||||||
{ key: 'part_1', label: `${sectionName} 1` },
|
{ key: 'part_1', label: 'Run' },
|
||||||
{ key: 'part_2', label: `${sectionName} 2` },
|
{ key: 'part_2', label: 'Legacy 2' },
|
||||||
{ key: 'part_3', label: `${sectionName} 3` },
|
{ key: 'part_3', label: 'Legacy 3' },
|
||||||
{ key: 'full_run', label: 'Full Run' },
|
{ key: 'full_run', label: 'Legacy Full' },
|
||||||
] as const).map((tab) => (
|
] as const).map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab.key}
|
key={tab.key}
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ export function CombatScreen({
|
|||||||
})),
|
})),
|
||||||
[dungeon.partySize, profile.character.name],
|
[dungeon.partySize, profile.character.name],
|
||||||
)
|
)
|
||||||
const sectionName = isRoguelike ? 'Stage' : dungeon.contentType === 'raid' ? 'Phase' : 'Part'
|
const sectionName = isRoguelike ? 'Stage' : 'Run'
|
||||||
const contentName = isRoguelike ? 'Roguelike' : dungeon.contentType === 'raid' ? 'Raid' : 'Dungeon'
|
const contentName = isRoguelike ? 'Roguelike' : dungeon.contentType === 'raid' ? 'Raid' : 'Dungeon'
|
||||||
const initialEncounterIndex = (startPart - 1) * 3
|
const initialEncounterIndex = (startPart - 1) * 3
|
||||||
const enemyCount = hardMode ? 2 : 1
|
const enemyCount = hardMode ? 2 : 1
|
||||||
@@ -1365,7 +1365,7 @@ export function CombatScreen({
|
|||||||
}).reverse()
|
}).reverse()
|
||||||
const dualScreenState = useMemo<DualScreenCombatState>(() => ({
|
const dualScreenState = useMemo<DualScreenCombatState>(() => ({
|
||||||
difficultyName: difficulty.name,
|
difficultyName: difficulty.name,
|
||||||
dungeonName: hardMode ? `${dungeon.name} Hard` : dungeon.name,
|
dungeonName: dungeon.name,
|
||||||
contentName,
|
contentName,
|
||||||
encounterName: encounter.enemyName,
|
encounterName: encounter.enemyName,
|
||||||
encounterDescription: encounter.description,
|
encounterDescription: encounter.description,
|
||||||
@@ -1444,7 +1444,7 @@ export function CombatScreen({
|
|||||||
>
|
>
|
||||||
{!dualScreenEnabled && <header className="topbar">
|
{!dualScreenEnabled && <header className="topbar">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">{difficulty.name}{hardMode ? ' Hard' : ''} - Item Level {difficulty.droppedItemLevel}</p>
|
<p className="eyebrow">{difficulty.name} - Item Level {difficulty.droppedItemLevel}</p>
|
||||||
<h1>{dungeon.name}</h1>
|
<h1>{dungeon.name}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="combat-header-actions">
|
<div className="combat-header-actions">
|
||||||
@@ -1467,7 +1467,7 @@ export function CombatScreen({
|
|||||||
</div>
|
</div>
|
||||||
<div className="enemy-info">
|
<div className="enemy-info">
|
||||||
<div className="bar-label">
|
<div className="bar-label">
|
||||||
<strong>{hardMode ? `${encounter.enemyName} x2` : encounter.enemyName}</strong>
|
<strong>{encounter.enemyName}</strong>
|
||||||
<span>{Math.ceil(enemyHealth)} / {encounterMaxHealth}</span>
|
<span>{Math.ceil(enemyHealth)} / {encounterMaxHealth}</span>
|
||||||
</div>
|
</div>
|
||||||
{hardMode ? (
|
{hardMode ? (
|
||||||
@@ -1796,7 +1796,7 @@ export function CombatScreen({
|
|||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">{sectionName} Complete</p>
|
<p className="eyebrow">{sectionName} Complete</p>
|
||||||
<h2>{encounter.enemyName} Defeated</h2>
|
<h2>{encounter.enemyName} Defeated</h2>
|
||||||
<p>{canContinueAfterPart ? `Proceed to ${sectionName} ${currentPart + 1} or end the run?` : 'Hard mode for this section is complete.'}</p>
|
<p>{canContinueAfterPart ? `Proceed to ${sectionName} ${currentPart + 1} or end the run?` : 'Run checkpoint complete.'}</p>
|
||||||
{canContinueAfterPart && (
|
{canContinueAfterPart && (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user