Android build v1.0.36

This commit is contained in:
Warren H
2026-06-20 16:57:03 -04:00
parent 2300973164
commit 753bba581a
6 changed files with 139 additions and 44 deletions
+55 -14
View File
@@ -3465,14 +3465,13 @@ h2 {
.spell-effect-layout {
display: grid;
gap: 14px;
grid-template-columns: 260px minmax(0, 1fr) 260px;
grid-template-columns: 220px minmax(0, 1fr);
margin-top: 17px;
min-height: 0;
}
.effect-slots-panel,
.effect-pool-panel,
.effect-detail-panel {
.effect-pool-panel {
background: #191b25;
border: 2px solid #090a0d;
min-height: 0;
@@ -3544,10 +3543,47 @@ h2 {
font-size: 9px;
}
.selected-effect-strip {
align-items: center;
background: #20222d;
border: 2px solid #090a0d;
display: grid;
gap: 12px;
grid-template-columns: minmax(0, 1fr) auto;
margin-top: 12px;
outline: 2px solid #3a3944;
padding: 10px;
}
.selected-effect-strip strong,
.selected-effect-strip small {
display: block;
}
.selected-effect-strip strong {
color: var(--gold);
font-family: 'Press Start 2P', monospace;
font-size: 9px;
line-height: 1.35;
margin-top: 5px;
}
.selected-effect-strip small {
color: var(--muted);
font-size: 15px;
line-height: 1;
margin-top: 5px;
}
.selected-effect-strip .primary-button {
min-width: 120px;
padding: 9px 12px;
}
.effect-pool {
display: grid;
gap: 10px;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-columns: repeat(3, minmax(0, 1fr));
margin-top: 12px;
}
@@ -3609,17 +3645,22 @@ h2 {
margin-top: 5px;
}
.effect-detail-panel h2 {
color: var(--gold);
font-size: 22px;
margin-top: 10px;
}
@media (max-width: 800px) {
.spell-effect-layout {
grid-template-columns: 1fr;
}
.effect-detail-panel p {
color: var(--muted);
font-size: 17px;
line-height: 1.1;
margin: 12px 0;
.effect-slots-panel {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.effect-pool {
grid-template-columns: 1fr;
}
.selected-effect-strip {
grid-template-columns: 1fr;
}
}
.talent-tier {
+42 -24
View File
@@ -16,6 +16,18 @@ type Props = {
const EFFECT_SLOT_LEVELS = [5, 10, 15, 20] as const
const EFFECT_CLASS_ID = 1
const EFFECT_SOURCE_LABELS: Record<string, string> = {
mend: 'Mend',
radiance: 'Radiance',
shield: 'Shield',
}
function effectSource(effectType: string) {
if (effectType.startsWith('mend_')) return 'mend'
if (effectType.startsWith('radiance_')) return 'radiance'
if (effectType.startsWith('shield_') || effectType.startsWith('shielded_')) return 'shield'
return effectType
}
function effectCapacity(level: number) {
return EFFECT_SLOT_LEVELS.filter((slotLevel) => level >= slotLevel).length
@@ -59,6 +71,9 @@ export function TalentScreen({ profile, onBack, onUpdated, embedded = false }: P
function lockReason(talent: Talent) {
if (!isEffectClass) return 'Coming soon'
if (talent.rank > 0) return ''
const source = effectSource(talent.effectType)
const sourceConflict = selectedEffects.find((effect) => effectSource(effect.effectType) === source)
if (sourceConflict) return `${EFFECT_SOURCE_LABELS[source] ?? source} already selected`
if (capacity <= 0) return 'Unlocks at level 5'
if (selectedEffects.length >= capacity) {
return `Active slots full (${capacity}/${capacity})`
@@ -182,6 +197,33 @@ export function TalentScreen({ profile, onBack, onUpdated, embedded = false }: P
</div>
<span>{selectedEffects.length}/{capacity} active</span>
</div>
<div className="selected-effect-strip">
<div>
<p className="eyebrow">Selected Effect</p>
{selectedTalent ? (
<>
<strong>{selectedTalent.name}</strong>
<small>{selectedTalent.description}</small>
</>
) : (
<small>No effect selected.</small>
)}
</div>
{selectedTalent && (
<button
className="primary-button"
disabled={Boolean(lockReason(selectedTalent)) || busyTalentId === selectedTalent.id}
onClick={() => toggleEffect(selectedTalent)}
type="button"
>
{busyTalentId === selectedTalent.id
? 'Saving...'
: selectedTalent.rank > 0
? 'Remove'
: 'Activate'}
</button>
)}
</div>
<div className="effect-pool">
{gameClass.talents.map((talent) => {
const reason = lockReason(talent)
@@ -210,30 +252,6 @@ export function TalentScreen({ profile, onBack, onUpdated, embedded = false }: P
})}
</div>
</section>
<aside className="effect-detail-panel">
<p className="eyebrow">Selected Effect</p>
{selectedTalent ? (
<>
<h2>{selectedTalent.name}</h2>
<p>{selectedTalent.description}</p>
<button
className="primary-button"
disabled={Boolean(lockReason(selectedTalent)) || busyTalentId === selectedTalent.id}
onClick={() => toggleEffect(selectedTalent)}
type="button"
>
{busyTalentId === selectedTalent.id
? 'Saving...'
: selectedTalent.rank > 0
? 'Remove Effect'
: 'Activate Effect'}
</button>
</>
) : (
<p>No effect selected.</p>
)}
</aside>
</div>
)}
+17
View File
@@ -432,6 +432,13 @@ function talentEffectCapacity(level: number) {
return Math.min(4, Math.max(0, Math.floor(level / 5)))
}
function talentEffectSource(effectType: string) {
if (effectType.startsWith('mend_')) return 'Mend'
if (effectType.startsWith('radiance_')) return 'Radiance'
if (effectType.startsWith('shield_') || effectType.startsWith('shielded_')) return 'Shield'
return effectType
}
type ComponentTemplate = { id: number; slug: string; name: string; itemLevel: number; glyph: string; description: string }
const COMPONENT_ITEMS: Record<number, ComponentTemplate> = {
1: { id: 600, slug: 'minor-component', name: 'Minor Component', itemLevel: 1, glyph: '◆', description: 'A basic crafting component.' },
@@ -1112,6 +1119,16 @@ function createLocalRepository(store: LocalSaveStore): GameRepository {
} else {
const capacity = talentEffectCapacity(cd.level)
if (capacity <= 0) throw new Error('Spell effects unlock at level 5.')
const source = talentEffectSource(talent.effectType)
const sourceConflict = gameClass.talents.find(
(candidate) =>
candidate.id !== talentId
&& (cd.talentRanks[String(candidate.id)] ?? 0) > 0
&& talentEffectSource(candidate.effectType) === source,
)
if (sourceConflict) {
throw new Error(`Only one ${source} spell effect can be active.`)
}
const activeCount = gameClass.talents.reduce(
(total, candidate) => total + ((cd.talentRanks[String(candidate.id)] ?? 0) > 0 ? 1 : 0),
0,