generated from nhcarrigan/template
feat: add equipment, achievements, and visual polish
- Equipment system: 12 items across weapon/armour/trinket slots with common/rare/epic/legendary rarities; starter commons auto-equipped, higher tiers drop from boss victories - Achievement system: 15 milestones with typed conditions; checked each tick and crystal rewards applied automatically - Achievement toast: slide-in notification, auto-dismisses after 4s - Floating click text: +X gold floats on each manual click - Expanded quests (9 total) and upgrades (12 total) - Upgrade panel now shows locked upgrades so players can see their progression path - formatNumber utility (K/M/B/T) used consistently across all panels - Backfill logic for existing saves to add new content gracefully - types package now emits .d.ts declarations
This commit is contained in:
@@ -291,6 +291,22 @@ body {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.upgrade-card.locked {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.upgrade-locked-label {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.upgrade-progress {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.upgrade-info {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -611,6 +627,475 @@ body {
|
||||
background: var(--colour-accent-light);
|
||||
}
|
||||
|
||||
/* ===================== BATTLE MODAL ===================== */
|
||||
.battle-modal {
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.battle-stats {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.battle-stat {
|
||||
background: var(--colour-bg);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
min-width: 120px;
|
||||
padding: 0.5rem 0.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.battle-stat .stat-label {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.battle-stat .stat-value {
|
||||
color: var(--colour-accent-light);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.battle-stat-divider {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.battle-bars {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.battle-bar-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.battle-bar-row .bar-label {
|
||||
font-size: 0.85rem;
|
||||
min-width: 100px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.hp-bar-container {
|
||||
background: var(--colour-bg);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: 4px;
|
||||
flex: 1;
|
||||
height: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hp-bar-fill {
|
||||
border-radius: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.battle-bar-row .bar-hp {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.75rem;
|
||||
min-width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.vs-divider {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.battle-in-progress {
|
||||
color: var(--colour-text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.battle-outcome {
|
||||
border-radius: var(--radius);
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.battle-outcome.victory {
|
||||
background: rgba(39, 174, 96, 0.1);
|
||||
border: 1px solid #27ae60;
|
||||
}
|
||||
|
||||
.battle-outcome.defeat {
|
||||
background: rgba(231, 76, 60, 0.1);
|
||||
border: 1px solid #e74c3c;
|
||||
}
|
||||
|
||||
.battle-outcome h3 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.battle-rewards,
|
||||
.battle-casualties {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 0.9rem;
|
||||
gap: 0.2rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.dismiss-button {
|
||||
background: var(--colour-accent);
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
margin-top: 0.75rem;
|
||||
padding: 0.5rem 2rem;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.dismiss-button:hover {
|
||||
background: var(--colour-accent-light);
|
||||
}
|
||||
|
||||
/* Party combat stat bar in BossPanel */
|
||||
.party-combat-stats {
|
||||
background: var(--colour-bg);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.25rem;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.combat-stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.combat-stat .stat-label {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.combat-stat .stat-value {
|
||||
color: var(--colour-accent-light);
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.boss-meta {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* ===================== CLICK FLOAT ===================== */
|
||||
@keyframes float-up {
|
||||
0% { opacity: 1; transform: translate(-50%, 0); }
|
||||
100% { opacity: 0; transform: translate(-50%, -70px); }
|
||||
}
|
||||
|
||||
.click-button-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.click-float {
|
||||
animation: float-up 0.9s ease-out forwards;
|
||||
color: var(--colour-gold);
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
text-shadow: 0 1px 4px rgba(0,0,0,0.5);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* ===================== EQUIPMENT ===================== */
|
||||
.equipment-intro {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.equipment-slot-section {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.slot-heading {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.5rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.equipment-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.equipment-card {
|
||||
align-items: center;
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-left: 3px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
|
||||
.equipment-card.equipped {
|
||||
border-color: var(--colour-success);
|
||||
border-left-color: var(--colour-success);
|
||||
box-shadow: 0 0 8px rgba(16, 185, 129, 0.15);
|
||||
}
|
||||
|
||||
.equipment-card.not-owned {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
/* Rarity border-left colours */
|
||||
.equipment-card.rarity-common { border-left-color: #9ca3af; }
|
||||
.equipment-card.rarity-rare { border-left-color: #3b82f6; }
|
||||
.equipment-card.rarity-epic { border-left-color: #a855f7; }
|
||||
.equipment-card.rarity-legendary { border-left-color: #f59e0b; }
|
||||
|
||||
.equipment-icon {
|
||||
font-size: 1.5rem;
|
||||
min-width: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.equipment-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.equipment-name-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.equipment-name-row h3 {
|
||||
font-size: 0.95rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rarity-badge {
|
||||
border-radius: 999px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
padding: 0.1rem 0.45rem;
|
||||
}
|
||||
|
||||
.rarity-badge.rarity-common { background: rgba(156, 163, 175, 0.2); color: #9ca3af; }
|
||||
.rarity-badge.rarity-rare { background: rgba(59, 130, 246, 0.2); color: #60a5fa; }
|
||||
.rarity-badge.rarity-epic { background: rgba(168, 85, 247, 0.2); color: #c084fc; }
|
||||
.rarity-badge.rarity-legendary { background: rgba(245, 158, 11, 0.2); color: #fbbf24; }
|
||||
|
||||
.equipment-description {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.equipment-bonus {
|
||||
color: var(--colour-gold);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.equipment-action {
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.equipment-locked {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.equipment-equipped-badge {
|
||||
color: var(--colour-success);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.equip-button {
|
||||
background: var(--colour-accent);
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
padding: 0.3rem 0.8rem;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.equip-button:hover {
|
||||
background: var(--colour-accent-light);
|
||||
}
|
||||
|
||||
/* ===================== ACHIEVEMENTS ===================== */
|
||||
.achievement-progress {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.achievement-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.achievement-card {
|
||||
align-items: center;
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-border);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
|
||||
.achievement-card.unlocked {
|
||||
border-color: var(--colour-gold);
|
||||
box-shadow: 0 0 8px rgba(245, 158, 11, 0.15);
|
||||
}
|
||||
|
||||
.achievement-card.locked {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.achievement-icon {
|
||||
font-size: 1.5rem;
|
||||
min-width: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.achievement-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.achievement-info h3 {
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.achievement-info p {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.achievement-condition {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.achievement-reward {
|
||||
color: var(--colour-crystal) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.achievement-status {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.achievement-unlocked-badge {
|
||||
color: var(--colour-gold);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.achievement-locked-badge {
|
||||
color: var(--colour-text-muted);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* ===================== ACHIEVEMENT TOAST ===================== */
|
||||
@keyframes slide-in-right {
|
||||
from { opacity: 0; transform: translateX(120%); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
.achievement-toast-container {
|
||||
bottom: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
position: fixed;
|
||||
right: 1.5rem;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
.achievement-toast {
|
||||
align-items: center;
|
||||
animation: slide-in-right 0.35s ease-out;
|
||||
background: var(--colour-surface);
|
||||
border: 1px solid var(--colour-gold);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
max-width: 280px;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.toast-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1rem;
|
||||
}
|
||||
|
||||
.toast-label {
|
||||
color: var(--colour-gold);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.toast-name {
|
||||
color: var(--colour-text);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toast-reward {
|
||||
color: var(--colour-crystal);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* ===================== UTILITY ===================== */
|
||||
.error {
|
||||
color: var(--colour-error);
|
||||
|
||||
Reference in New Issue
Block a user