feat: update evolution guide with new interaction rules and enhance styling for clarity

This commit is contained in:
sha
2026-02-21 23:33:26 +02:00
parent b6379a0bd3
commit 39e4b2d3cf
3 changed files with 57 additions and 30 deletions
+15 -5
View File
@@ -385,7 +385,10 @@ export class EvolutionController {
}
// ── Virus kill / mutation ──────────────────────────────
// virus-filled hitting a non-immune entity: 90% kills it, 10% triggers mutation.
// virus-filled hitting a non-immune entity: the target may die or mutate.
// On a failed kill roll the contact will convert the target into bacteria
// (10% shown in the guide) if that icon exists. Bacteria are immune to
// subsequent virus or bug interactions.
const virusImmune = new Set(['bug', 'virus-filled', 'bacteria']);
const aIsVirus = a.entityKey === 'virus-filled';
const bIsVirus = b.entityKey === 'virus-filled';
@@ -403,10 +406,11 @@ export class EvolutionController {
/**
* Handle a collision between a virus-filled entity and a vulnerable target.
* Bug is the sole mutation/transformation trigger — virus only kills.
*
* - virusKillChance: target begins slow-death sequence.
* - else: target survives unaffected (virus bounces away normally).
* 50%100% of the time (controlled by virusKillChance) the target is killed.
* On the remaining rolls the virus fails to kill and the contact triggers a
* rare mutation: the target becomes a "bacteria" entity if that icon is
* defined. Bacteria are immune to further virus attacks and bugs.
*
* @param {import('./entity.js').Entity} _virus the virus-filled entity (unused)
* @param {import('./entity.js').Entity} target the entity being contacted
@@ -414,8 +418,14 @@ export class EvolutionController {
#resolveVirusContact(_virus, target) {
if (Math.random() < this.#virusKillChance) {
target.die();
} else {
// mutation path only if bacteria is registered
if (this.#iconsData && this.#iconsData.icons['bacteria']) {
const bacMeta = this.#iconsData.icons['bacteria'];
const bacColor = this.#iconsData.types[bacMeta.type]?.color || '#80ffee';
target.infectWith('bacteria', bacColor, { force: true });
}
}
// else: target survives — virus bounces, no transformation
}
// ── State persistence ────────────────────────────────────────
+17 -9
View File
@@ -106,10 +106,10 @@
/* Icon container — SVG injected here by guide.js */
.guide-entity__icon {
width: 16px;
height: 16px;
width: 18px;
height: 18px;
flex-shrink: 0;
color: var(--c, rgba(255, 255, 255, 0.6));
color: var(--c, rgba(255, 255, 255, 0.75));
display: flex;
align-items: center;
justify-content: center;
@@ -154,10 +154,11 @@
}
.guide-rule {
display: grid;
grid-template-columns: 9px 9px 9px 1fr;
display: flex;
align-items: center;
gap: 0.28rem;
gap: 0.4rem;
flex-wrap: nowrap; /* absolutely no wrapping */
white-space: nowrap; /* safeguard for inline elements */
}
/* Coloured circle representing an entity type */
@@ -172,9 +173,14 @@
.guide-rule__op {
font-family: var(--font-mono);
font-size: 0.55rem;
color: rgba(255, 255, 255, 0.22);
display: inline-block;
/* 1.2rem ≈ 19px on a 16px root, sized to match the 18px icons */
font-size: 1.2rem;
line-height: 1;
font-weight: 500;
color: rgba(255, 255, 255, 0.75);
text-align: center;
flex-shrink: 0;
}
.guide-rule__desc {
@@ -182,11 +188,13 @@
font-size: 0.62rem;
color: rgba(255, 255, 255, 0.45);
line-height: 1.3;
margin-left: 0.4rem;
}
.guide-rule__pct {
color: rgba(255, 255, 255, 0.22);
font-size: 0.58rem;
font-size: 0.7rem;
flex-shrink: 0;
}
/* ── Guide footer + restart button ───────────────────────── */