Compare commits

..
1 Commits
Author SHA1 Message Date
Warren H 1797154726 Android build v1.1.4 2026-06-29 23:06:37 -04:00
6 changed files with 99 additions and 35 deletions
Binary file not shown.
+2 -2
View File
@@ -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 82 versionCode 83
versionName "1.1.3" versionName "1.1.4"
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.
+33
View File
@@ -787,6 +787,10 @@ textarea:focus-visible,
min-height: 0; min-height: 0;
} }
.dual-top-main.pvp-roguelike-dual-top {
grid-template-rows: auto minmax(0, 1fr);
}
.dual-top-main.stadium-dual-top { .dual-top-main.stadium-dual-top {
grid-template-rows: auto auto minmax(0, 1fr) auto; grid-template-rows: auto auto minmax(0, 1fr) auto;
} }
@@ -798,6 +802,12 @@ textarea:focus-visible,
width: 100%; width: 100%;
} }
.dual-top-main.pvp-roguelike-dual-top .dual-top-enemy {
align-items: center;
display: grid;
grid-template-columns: 84px minmax(0, 1fr) minmax(96px, 108px);
}
.dual-top-main .dual-top-party-grid { .dual-top-main .dual-top-party-grid {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
} }
@@ -829,6 +839,14 @@ textarea:focus-visible,
padding: 8px 10px; padding: 8px 10px;
} }
.dual-top-cooldowns {
align-content: center;
display: grid;
gap: 6px;
grid-template-columns: repeat(2, 48px);
justify-content: end;
}
.dual-top-spell { .dual-top-spell {
align-items: center; align-items: center;
background: #20232c; background: #20232c;
@@ -843,6 +861,11 @@ textarea:focus-visible,
position: relative; position: relative;
} }
.dual-top-cooldowns .dual-top-spell {
height: 38px;
width: 48px;
}
.dual-top-spell:not(:disabled) { .dual-top-spell:not(:disabled) {
cursor: pointer; cursor: pointer;
} }
@@ -858,6 +881,12 @@ textarea:focus-visible,
width: 32px; width: 32px;
} }
.dual-top-cooldowns .dual-top-spell .spell-icon {
font-size: 16px;
height: 26px;
width: 26px;
}
.dual-top-spell > i { .dual-top-spell > i {
background: rgba(0, 0, 0, 0.58); background: rgba(0, 0, 0, 0.58);
bottom: 0; bottom: 0;
@@ -875,6 +904,10 @@ textarea:focus-visible,
position: absolute; position: absolute;
} }
.dual-top-cooldowns .dual-top-spell > small {
font-size: 8px;
}
.dual-top-resource { .dual-top-resource {
align-self: center; align-self: center;
color: #82bfff; color: #82bfff;
+4
View File
@@ -1339,6 +1339,9 @@ export function PvPRoguelikeScreen({
opponentClassName: liveMatch?.opponentClassName ?? (cpuDifficulty ? `CPU ${cpuDifficulty}` : 'CPU'), opponentClassName: liveMatch?.opponentClassName ?? (cpuDifficulty ? `CPU ${cpuDifficulty}` : 'CPU'),
opponentParty: cpuSide.party, opponentParty: cpuSide.party,
opponentEnemyHealth: cpuSide.enemyHealth, opponentEnemyHealth: cpuSide.enemyHealth,
opponentResource: cpuSide.resource,
opponentMaxResource: maxResource,
opponentResourceName: gameClass.resourceName,
opponentBuffSummary, opponentBuffSummary,
opponentDebuffSummary, opponentDebuffSummary,
floatingTexts: dualScreenFloatingTexts, floatingTexts: dualScreenFloatingTexts,
@@ -1362,6 +1365,7 @@ export function PvPRoguelikeScreen({
cpuDifficulty, cpuDifficulty,
cpuSide.enemyHealth, cpuSide.enemyHealth,
cpuSide.party, cpuSide.party,
cpuSide.resource,
directPartyTargeting, directPartyTargeting,
encounter.description, encounter.description,
encounter.enemyName, encounter.enemyName,
+4
View File
@@ -1055,6 +1055,9 @@ export function PvpStadiumScreen({
opponentClassName: liveMatch?.opponentClassName ?? (cpuDifficulty ? `CPU ${cpuDifficulty}` : 'CPU'), opponentClassName: liveMatch?.opponentClassName ?? (cpuDifficulty ? `CPU ${cpuDifficulty}` : 'CPU'),
opponentParty: cpuSide.party, opponentParty: cpuSide.party,
opponentEnemyHealth: 0, opponentEnemyHealth: 0,
opponentResource: cpuSide.resource,
opponentMaxResource: MAX_RESOURCE,
opponentResourceName: gameClass.resourceName,
opponentBuffSummary, opponentBuffSummary,
opponentDebuffSummary, opponentDebuffSummary,
floatingTexts: dualScreenFloatingTexts, floatingTexts: dualScreenFloatingTexts,
@@ -1083,6 +1086,7 @@ export function PvpStadiumScreen({
controllerIconStyle, controllerIconStyle,
cpuDifficulty, cpuDifficulty,
cpuSide.party, cpuSide.party,
cpuSide.resource,
cpuSide.survivalSeconds, cpuSide.survivalSeconds,
directPartyTargeting, directPartyTargeting,
dualScreenFloatingTexts, dualScreenFloatingTexts,
+49 -26
View File
@@ -47,6 +47,9 @@ export type DualScreenCombatState = {
opponentClassName?: string opponentClassName?: string
opponentParty?: PartyMember[] opponentParty?: PartyMember[]
opponentEnemyHealth?: number opponentEnemyHealth?: number
opponentResource?: number
opponentMaxResource?: number
opponentResourceName?: string
opponentBuffSummary?: string opponentBuffSummary?: string
opponentDebuffSummary?: string opponentDebuffSummary?: string
floatingTexts: Array<{ floatingTexts: Array<{
@@ -479,6 +482,17 @@ export function DualScreenBottomDisplay() {
<div className="bar enemy-health boss-bar"> <div className="bar enemy-health boss-bar">
<span style={{ width: `${Math.max(0, ((state.opponentEnemyHealth ?? 0) / state.encounterMaxHealth) * 100)}%` }} /> <span style={{ width: `${Math.max(0, ((state.opponentEnemyHealth ?? 0) / state.encounterMaxHealth) * 100)}%` }} />
</div> </div>
{typeof state.opponentResource === 'number' && state.opponentMaxResource ? (
<>
<div>
<p className="eyebrow">{state.opponentResourceName ?? state.resourceName}</p>
<strong>{Math.floor(state.opponentResource)} / {state.opponentMaxResource}</strong>
</div>
<div className="bar mana-bar">
<span style={{ width: `${Math.max(0, (state.opponentResource / state.opponentMaxResource) * 100)}%` }} />
</div>
</>
) : null}
</section> </section>
)} )}
@@ -626,8 +640,34 @@ export function DualScreenTopCombat({
[state.floatingTexts], [state.floatingTexts],
) )
const spellButtons = state.spells.map((spell, slotIndex) => {
if (!spell) return <div className="dual-top-spell empty" key={`empty-${slotIndex}`} />
const percent = spell.remaining > 0
? Math.min(100, (spell.remaining / Math.max(1, spell.cooldown)) * 100)
: 0
return ( return (
<div className="dual-top-main"> <button
className="dual-top-spell"
disabled={
!state.playerIsAlive
|| state.resource < spell.cost
|| spell.remaining > 0
|| state.status !== 'playing'
|| state.paused
}
key={spell.id}
onClick={() => onCastSpell?.(spell)}
type="button"
>
<span className={`spell-icon spell-${spell.kind}`}>{spell.glyph}</span>
{spell.remaining > 0 && <i style={{ height: `${percent}%` }} />}
{spell.remaining > 0 && <small>{spell.remaining.toFixed(0)}</small>}
</button>
)
})
return (
<div className={`dual-top-main ${state.opponentParty ? 'pvp-roguelike-dual-top' : ''}`}>
<section className="dual-top-enemy"> <section className="dual-top-enemy">
<div className="enemy-portrait" aria-hidden="true"> <div className="enemy-portrait" aria-hidden="true">
{state.encounterIsBoss ? 'B' : 'M'} {state.encounterIsBoss ? 'B' : 'M'}
@@ -642,6 +682,11 @@ export function DualScreenTopCombat({
</div> </div>
<p>{state.encounterDescription}</p> <p>{state.encounterDescription}</p>
</div> </div>
{state.opponentParty && (
<div className="dual-top-cooldowns" aria-label="Cooldowns">
{spellButtons}
</div>
)}
</section> </section>
<section className="dual-top-party"> <section className="dual-top-party">
@@ -669,32 +714,9 @@ export function DualScreenTopCombat({
</div> </div>
</section> </section>
{!state.opponentParty && (
<section className="dual-top-spell-strip"> <section className="dual-top-spell-strip">
{state.spells.map((spell, slotIndex) => { {spellButtons}
if (!spell) return <div className="dual-top-spell empty" key={`empty-${slotIndex}`} />
const percent = spell.remaining > 0
? Math.min(100, (spell.remaining / Math.max(1, spell.cooldown)) * 100)
: 0
return (
<button
className="dual-top-spell"
disabled={
!state.playerIsAlive
|| state.resource < spell.cost
|| spell.remaining > 0
|| state.status !== 'playing'
|| state.paused
}
key={spell.id}
onClick={() => onCastSpell?.(spell)}
type="button"
>
<span className={`spell-icon spell-${spell.kind}`}>{spell.glyph}</span>
{spell.remaining > 0 && <i style={{ height: `${percent}%` }} />}
{spell.remaining > 0 && <small>{spell.remaining.toFixed(0)}</small>}
</button>
)
})}
<div className="dual-top-resource"> <div className="dual-top-resource">
<strong>{state.resourceName} {Math.floor(state.resource)} / {state.maxResource}</strong> <strong>{state.resourceName} {Math.floor(state.resource)} / {state.maxResource}</strong>
<div className="bar mana-bar"> <div className="bar mana-bar">
@@ -702,6 +724,7 @@ export function DualScreenTopCombat({
</div> </div>
</div> </div>
</section> </section>
)}
</div> </div>
) )