diff --git a/index.html b/index.html index 40a0ade..33e118a 100644 --- a/index.html +++ b/index.html @@ -89,34 +89,43 @@

interactions

diff --git a/src/js/evolution.js b/src/js/evolution.js index f0ae755..9259cf1 100644 --- a/src/js/evolution.js +++ b/src/js/evolution.js @@ -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 ──────────────────────────────────────── diff --git a/src/styles/guide.css b/src/styles/guide.css index 7372d32..0bac353 100644 --- a/src/styles/guide.css +++ b/src/styles/guide.css @@ -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 ───────────────────────── */