feat: enhance evolution guide to display cumulative icon counts and adjust styling for clarity

This commit is contained in:
sha
2026-02-21 23:46:44 +02:00
parent 39e4b2d3cf
commit e0dba18315
3 changed files with 55 additions and 3 deletions
+46
View File
@@ -30,6 +30,8 @@ const EVOLUTION_STORAGE_KEY = 'devpage:evolution';
export class EvolutionController {
/** @type {Entity[]} */ #entities = [];
/** Cumulative counts of every icon ever created (since last clear or load). */
/** @type {Record<string,number>} */ #totalCounts = {};
/** @type {HTMLElement|null} */ #container = null;
/** @type {number} */ #moveSpeed = DEFAULTS.MOVE_SPEED;
/** @type {number} */ #spawnRate = 5;
@@ -131,6 +133,8 @@ export class EvolutionController {
const iconMeta = this.#iconsData.icons[iconName];
if (!iconMeta) return;
const typeMeta = this.#iconsData.types[iconMeta.type];
// track total count
this.#incrementTotal(iconName);
const margin = DEFAULTS.ICON_SIZE * 2;
const vw = window.innerWidth;
@@ -155,6 +159,8 @@ export class EvolutionController {
this.#entities.forEach(e => e.destroy());
this.#entities = [];
this.#startTime = Date.now(); // reset lifetime counter
// also clear total counts
this.#totalCounts = {};
// also tell the logo word to forget its bump counts; the visual labels
// should go back to `0` immediately
@@ -195,8 +201,21 @@ export class EvolutionController {
/** Read-only access to current entity count (useful for debugging). */
get entityCount() { return this.#entities.length; }
/** Cumulative totals for each icon (spawned/mutated) since last clear or load. */
get totalCounts() { return { ...this.#totalCounts }; }
// ── Spawning ────────────────────────────────────────────────
/**
* Private helper: increment the alltime count for an icon.
*
* @param {string} name
*/
#incrementTotal(name) {
if (!name) return;
this.#totalCounts[name] = (this.#totalCounts[name] || 0) + 1;
}
/**
* Schedule the next spawn after a random delay within the configured range.
* Self-resets after each spawn so the process continues indefinitely.
@@ -251,6 +270,8 @@ export class EvolutionController {
if (entity.alive) {
this.#entities.push(entity);
// record spawn for totals
this.#incrementTotal(name);
}
}
@@ -447,6 +468,7 @@ export class EvolutionController {
hueShift: e.hueShift,
})),
logo: this.#logo?.serialise() ?? null,
totalCounts: this.#totalCounts,
};
localStorage.setItem(EVOLUTION_STORAGE_KEY, JSON.stringify(snapshot));
} catch { /* quota exceeded — ignore */ }
@@ -473,6 +495,20 @@ export class EvolutionController {
this.#savedLogoState = saved.logo;
}
// determine whether we'll need to rebuild totals from the entity list
let buildTotalsFromEntities = false;
if (
saved.totalCounts &&
typeof saved.totalCounts === 'object' &&
Object.keys(saved.totalCounts).length > 0
) {
// existing totals present
this.#totalCounts = { ...saved.totalCounts };
} else {
// no meaningful totals stored; rebuild from entity list below
buildTotalsFromEntities = true;
}
const entities = saved.entities;
if (!Array.isArray(entities) || entities.length === 0) return;
@@ -504,8 +540,18 @@ export class EvolutionController {
`hue-rotate(${s.hueShift}deg) drop-shadow(0 1px 6px rgba(0,0,0,0.35))`;
}
this.#entities.push(entity);
// if the snapshot didn't already include totals, count this entity now
if (buildTotalsFromEntities) {
this.#incrementTotal(s.name);
}
}
}));
// make sure totals are at least as large as the current restored population
const restored = this.getCounts();
for (const [key, num] of Object.entries(restored)) {
this.#totalCounts[key] = Math.max(this.#totalCounts[key] || 0, num);
}
} catch { /* corrupt save — start fresh */ }
}
+8 -2
View File
@@ -124,10 +124,16 @@ export class GuideController {
const counts = this.#evolution.getCounts();
const lifetime = this.#evolution.lifetime;
// Update per-entity count badges
// Update per-entity count badges (current + alltime total in parentheses)
const totals = this.#evolution.totalCounts || {};
this.#panel.querySelectorAll('[data-count-key]').forEach(item => {
const el = item.querySelector('.guide-entity__count');
if (el) el.textContent = String(counts[item.dataset.countKey] ?? 0);
if (el) {
const key = item.dataset.countKey;
const cur = counts[key] ?? 0;
const tot = totals[key] ?? 0;
el.textContent = tot > 0 ? `${cur} (${tot})` : String(cur);
}
});
// Update uptime display
+1 -1
View File
@@ -137,7 +137,7 @@
font-family: var(--font-mono);
font-size: 0.62rem;
color: rgba(255, 255, 255, 0.22);
min-width: 20px;
min-width: 40px; /* allow space for “cur (total)” */
text-align: right;
flex-shrink: 0;
font-variant-numeric: tabular-nums;