Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 787e2bbae9 |
Binary file not shown.
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.warren.iwanttoheal"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 72
|
||||
versionName "1.0.53"
|
||||
versionCode 73
|
||||
versionName "1.0.54"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
+96
-25
@@ -6087,27 +6087,17 @@ h2 {
|
||||
.pvp-board {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(210px, 0.68fr) minmax(0, 1fr);
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.pvp-side,
|
||||
.pvp-middle-panel {
|
||||
.pvp-side {
|
||||
gap: 8px;
|
||||
min-height: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.pvp-vertical-spell-bar,
|
||||
.pvp-vertical-spell-bar.six-slots {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.pvp-vertical-spell-bar .spell {
|
||||
min-height: 58px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.pvp-screen-tools {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -6118,18 +6108,41 @@ h2 {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.pvp-resource-wrap {
|
||||
color: #82bfff;
|
||||
min-width: 150px;
|
||||
text-align: right;
|
||||
width: min(170px, 100%);
|
||||
.pvp-side-bars {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
min-width: min(320px, 45%);
|
||||
width: min(360px, 48%);
|
||||
}
|
||||
|
||||
.pvp-clear-wrap,
|
||||
.pvp-resource-wrap {
|
||||
color: var(--muted);
|
||||
font-family: 'Press Start 2P', monospace;
|
||||
font-size: 7px;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pvp-clear-wrap > span,
|
||||
.pvp-resource-wrap > span {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.pvp-clear-wrap .bar,
|
||||
.pvp-resource-wrap .bar {
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.pvp-clear-wrap {
|
||||
color: #ff8d9a;
|
||||
}
|
||||
|
||||
.pvp-resource-wrap {
|
||||
color: #82bfff;
|
||||
}
|
||||
|
||||
.pvp-side .party-member,
|
||||
.pvp-side .party-member > div,
|
||||
.pvp-side .party-member > small {
|
||||
@@ -6147,7 +6160,7 @@ h2 {
|
||||
}
|
||||
|
||||
.pvp-side .pvp-party-grid.raid .party-member {
|
||||
min-height: 62px;
|
||||
min-height: 96px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
@@ -6184,6 +6197,29 @@ h2 {
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.pvp-side .member-health {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pvp-side .member-health .health-text {
|
||||
align-items: center;
|
||||
color: #fff3c7;
|
||||
display: flex;
|
||||
font-family: 'Press Start 2P', monospace;
|
||||
font-size: 7px;
|
||||
font-style: normal;
|
||||
inset: 0;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
text-shadow: 1px 1px 0 #08090c, -1px -1px 0 #08090c;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.pvp-side .party-member .member-header small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pvp-side .member-effects {
|
||||
margin-top: 4px;
|
||||
}
|
||||
@@ -6202,22 +6238,57 @@ h2 {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pvp-middle-panel .encounter-header h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.pvp-middle-panel .encounter-header small,
|
||||
.pvp-enemy-race small {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pvp-middle-panel .roguelike-upgrade-list,
|
||||
.pvp-side .roguelike-upgrade-list {
|
||||
font-size: 12px;
|
||||
line-height: 1.1;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.pvp-bottom-spell-bar {
|
||||
background: var(--panel);
|
||||
border: 3px solid #0c0d11;
|
||||
box-shadow: 4px 4px 0 #08090c;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-column: 1 / -1;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
outline: 2px solid var(--edge);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.pvp-bottom-spell-bar .spell {
|
||||
align-items: center;
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
grid-template-columns: auto auto minmax(0, 1fr) auto;
|
||||
min-height: 58px;
|
||||
padding: 7px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pvp-bottom-spell-bar .spell-icon {
|
||||
height: 34px;
|
||||
margin: 0;
|
||||
width: 34px;
|
||||
}
|
||||
|
||||
.pvp-bottom-spell-bar .spell strong {
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pvp-bottom-spell-bar .spell small {
|
||||
font-size: 13px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pvp-choice-columns {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
|
||||
@@ -1618,8 +1618,15 @@ export function PvPRoguelikeScreen({
|
||||
<div>
|
||||
<p className="eyebrow">You</p>
|
||||
<h2>{profile.character.name}</h2>
|
||||
<small>{encounter.enemyName} | Stage {stage}{encounter.isBoss ? ' Boss' : ''}</small>
|
||||
</div>
|
||||
<div className="resource-row pvp-resource-row">
|
||||
<div className="pvp-side-bars">
|
||||
<div className="pvp-clear-wrap">
|
||||
<span>Your clear {Math.max(0, Math.floor(playerSide.enemyHealth))} / {encounter.maxHealth}</span>
|
||||
<div className="bar enemy-health boss-bar">
|
||||
<span style={{ width: `${(playerSide.enemyHealth / encounter.maxHealth) * 100}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="pvp-resource-wrap">
|
||||
<span>{gameClass.resourceName} {Math.floor(playerSide.resource)} / {maxResource}</span>
|
||||
{speedMultiplier === 2 && <strong className="speed-badge">2x speed</strong>}
|
||||
@@ -1643,6 +1650,7 @@ export function PvPRoguelikeScreen({
|
||||
<div className="bar member-health">
|
||||
<span style={{ width: `${(member.health / effectiveMaxHealth(member)) * 100}%` }} />
|
||||
{member.shield > 0 && <i style={{ width: `${(member.shield / effectiveMaxHealth(member)) * 100}%` }} />}
|
||||
<em className="health-text">{Math.floor(member.health)} / {effectiveMaxHealth(member)}</em>
|
||||
</div>
|
||||
<div className="floating-combat-texts" aria-hidden="true">
|
||||
{floatingTexts
|
||||
@@ -1665,69 +1673,20 @@ export function PvPRoguelikeScreen({
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="combat-panel pvp-middle-panel">
|
||||
<div className="encounter-header">
|
||||
<div>
|
||||
<p className="eyebrow">Encounter {encounterIndex + 1}</p>
|
||||
<h2>{encounter.enemyName}</h2>
|
||||
<small>Stage {stage}{encounter.isBoss ? ' Boss' : ''}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pvp-enemy-race">
|
||||
<div>
|
||||
<strong>Your clear</strong>
|
||||
<div className="bar enemy-health boss-bar">
|
||||
<span style={{ width: `${(playerSide.enemyHealth / encounter.maxHealth) * 100}%` }} />
|
||||
</div>
|
||||
<small>{Math.max(0, Math.floor(playerSide.enemyHealth))} / {encounter.maxHealth}</small>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{liveMatch ? `${liveMatch.opponentName} clear` : 'CPU clear'}</strong>
|
||||
<div className="bar enemy-health boss-bar">
|
||||
<span style={{ width: `${(cpuSide.enemyHealth / encounter.maxHealth) * 100}%` }} />
|
||||
</div>
|
||||
<small>{Math.max(0, Math.floor(cpuSide.enemyHealth))} / {encounter.maxHealth}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="spell-bar six-slots vertical-spell-bar pvp-vertical-spell-bar">
|
||||
{starterSpells.map((spell) => {
|
||||
const remaining = playerSide.cooldowns[spell.id] ?? 0
|
||||
const cost = spellResourceCost(spell, playerSide.buffs, playerSide.debuffs, playerSide.freeCastReady)
|
||||
return (
|
||||
<button
|
||||
className="spell"
|
||||
disabled={status !== 'playing' || playerDone || !playerAlive || remaining > 0 || playerSide.resource < cost}
|
||||
key={`middle-${spell.id}`}
|
||||
onClick={() => castPlayerSpell(spell)}
|
||||
type="button"
|
||||
>
|
||||
<kbd>
|
||||
<ControllerBindingLabel
|
||||
binding={bindings[lastDevice][`ability${Number(spell.key)}` as InputAction]}
|
||||
compact
|
||||
iconStyle={controllerIconStyle}
|
||||
/>
|
||||
</kbd>
|
||||
<span className={`spell-icon spell-${spell.kind}`}>{spell.glyph}</span>
|
||||
<strong>{spell.name}</strong>
|
||||
<small>{cost} {gameClass.resourceName}</small>
|
||||
{remaining > 0 && <i>{remaining.toFixed(1)}</i>}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<p className="roguelike-upgrade-list">
|
||||
{liveMatch ? `${liveMatch.opponentName} (${liveMatch.opponentClassName})` : `CPU ${cpuDifficulty}`} | Encounters cleared: {encountersCleared}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="combat-panel pvp-side">
|
||||
<div className="encounter-header">
|
||||
<div>
|
||||
<p className="eyebrow">Opponent</p>
|
||||
<h2>{opponentLabel}</h2>
|
||||
<small>{liveMatch ? liveMatch.opponentClassName : `CPU ${cpuDifficulty}`} | Encounters cleared: {encountersCleared}</small>
|
||||
</div>
|
||||
<div className="resource-row pvp-resource-row">
|
||||
<div className="pvp-side-bars">
|
||||
<div className="pvp-clear-wrap">
|
||||
<span>{liveMatch ? `${liveMatch.opponentName} clear` : 'CPU clear'} {Math.max(0, Math.floor(cpuSide.enemyHealth))} / {encounter.maxHealth}</span>
|
||||
<div className="bar enemy-health boss-bar">
|
||||
<span style={{ width: `${(cpuSide.enemyHealth / encounter.maxHealth) * 100}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="pvp-resource-wrap">
|
||||
<span>{gameClass.resourceName} {Math.floor(cpuSide.resource)} / {maxResource}</span>
|
||||
<div className="bar mana-bar"><span style={{ width: `${(cpuSide.resource / maxResource) * 100}%` }} /></div>
|
||||
@@ -1745,6 +1704,7 @@ export function PvPRoguelikeScreen({
|
||||
<div className="bar member-health">
|
||||
<span style={{ width: `${(member.health / effectiveMaxHealth(member)) * 100}%` }} />
|
||||
{member.shield > 0 && <i style={{ width: `${(member.shield / effectiveMaxHealth(member)) * 100}%` }} />}
|
||||
<em className="health-text">{Math.floor(member.health)} / {effectiveMaxHealth(member)}</em>
|
||||
</div>
|
||||
<div className="floating-combat-texts" aria-hidden="true">
|
||||
{floatingTexts
|
||||
@@ -1766,6 +1726,34 @@ export function PvPRoguelikeScreen({
|
||||
Buffs: {cpuSide.buffs.length > 0 ? summarizeStacks(cpuSide.buffs, selfBuffChoicesCatalog) : 'none'} | Debuffs: {cpuSide.debuffs.length > 0 ? summarizeStacks(cpuSide.debuffs, opponentDebuffChoicesCatalog) : 'none'}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="pvp-bottom-spell-bar" aria-label="Player abilities">
|
||||
{starterSpells.map((spell) => {
|
||||
const remaining = playerSide.cooldowns[spell.id] ?? 0
|
||||
const cost = spellResourceCost(spell, playerSide.buffs, playerSide.debuffs, playerSide.freeCastReady)
|
||||
return (
|
||||
<button
|
||||
className="spell"
|
||||
disabled={status !== 'playing' || playerDone || !playerAlive || remaining > 0 || playerSide.resource < cost}
|
||||
key={`bottom-${spell.id}`}
|
||||
onClick={() => castPlayerSpell(spell)}
|
||||
type="button"
|
||||
>
|
||||
<kbd>
|
||||
<ControllerBindingLabel
|
||||
binding={bindings[lastDevice][`ability${Number(spell.key)}` as InputAction]}
|
||||
compact
|
||||
iconStyle={controllerIconStyle}
|
||||
/>
|
||||
</kbd>
|
||||
<span className={`spell-icon spell-${spell.kind}`}>{spell.glyph}</span>
|
||||
<strong>{spell.name}</strong>
|
||||
<small>{cost} {gameClass.resourceName}</small>
|
||||
{remaining > 0 && <i>{remaining.toFixed(1)}</i>}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user