feat(@projects/@magic-civilization): add combat preview hp tracking visuals

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-04-26 15:58:03 -07:00
parent 7644090e9c
commit 649178d745

View file

@ -325,18 +325,33 @@
margin-top: 2px;
}
/* Expected table */
.expected-grid {
display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: center;
}
.exp-block {
/* HP-after bars */
.hp-after-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.hp-after-block {
background: rgba(31,23,51,0.6); border: 1px solid var(--border);
border-radius: 3px; padding: 8px 12px; text-align: center;
border-radius: 3px; padding: 10px 12px;
}
.exp-label { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 3px; }
.exp-val { font-size: 20px; font-weight: bold; font-family: monospace; }
.exp-sub { font-size: 10px; color: var(--muted); margin-top: 2px; }
.exp-arrow { text-align: center; color: var(--muted); font-size: 18px; }
.hp-after-label { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px; display: flex; justify-content: space-between; align-items: center; }
.hp-after-label .death-badge { background: #3d0a08; border: 1px solid var(--neg); border-radius: 2px; padding: 1px 5px; font-size: 9px; color: var(--neg); letter-spacing: 0.06em; }
.hp-track-outer {
height: 20px; background: #ffffff08; border-radius: 2px; position: relative; overflow: hidden;
border: 1px solid #73591f33;
}
/* green: guaranteed surviving HP */
.hp-sure { position: absolute; top: 0; bottom: 0; left: 0; background: #66b86666; }
/* yellow: possible HP zone */
.hp-maybe { position: absolute; top: 0; bottom: 0; background: #e6993344; }
/* red: death zone (below 0) capped at left edge */
.hp-dead { position: absolute; top: 0; bottom: 0; left: 0; background: #d9594044; border-right: 2px solid var(--neg); }
/* current HP marker */
.hp-cur { position: absolute; top: 0; bottom: 0; width: 2px; background: #ffffff66; }
.hp-zero { position: absolute; top: 0; bottom: 0; left: 0; width: 2px; background: var(--neg); }
.hp-nums { display: flex; justify-content: space-between; font-size: 10px; font-family: monospace; margin-top: 4px; }
.hp-n-min { color: var(--neg); }
.hp-n-avg { color: var(--warn); }
.hp-n-max { color: var(--sage); }
.hp-n-dead { color: var(--neg); font-weight: bold; }
.death-prob { font-size: 11px; color: var(--neg); margin-top: 5px; font-family: monospace; }
/* Action row */
.action-row {
@ -612,17 +627,38 @@
</div>
</div>
<div class="expected-grid">
<div class="exp-block">
<div class="exp-label">Warrior HP after</div>
<div class="exp-val" style="color:var(--sage)">5660</div>
<div class="exp-sub">likely survives</div>
<!-- HP-after: Warrior 68hp, takes 816. Pikeman 100hp, takes 1425. Neither dies. -->
<div class="hp-after-grid">
<div class="hp-after-block">
<div class="hp-after-label"><span>Warrior HP after (68 now)</span></div>
<div class="hp-track-outer">
<!-- sure zone: 68-16=52 → 65% of 80 -->
<div class="hp-sure" style="width:65%"></div>
<!-- maybe zone: 52→60 of 80 = 65%75% -->
<div class="hp-maybe" style="left:65%;width:10%"></div>
<!-- current HP marker at 68/80 = 85% -->
<div class="hp-cur" style="left:85%"></div>
</div>
<div class="hp-nums">
<span class="hp-n-min">min 52</span>
<span class="hp-n-avg">avg 56</span>
<span class="hp-n-max">max 60 · no death risk</span>
</div>
</div>
<div class="exp-arrow"></div>
<div class="exp-block">
<div class="exp-label">Pikeman HP after</div>
<div class="exp-val" style="color:var(--warn)">7586</div>
<div class="exp-sub">damaged, survives</div>
<div class="hp-after-block">
<div class="hp-after-label"><span>Pikeman HP after (100 now)</span></div>
<div class="hp-track-outer">
<!-- sure zone: 100-25=75 → 75% -->
<div class="hp-sure" style="width:75%"></div>
<!-- maybe zone: 75→86 of 100 = 75%86% -->
<div class="hp-maybe" style="left:75%;width:11%"></div>
<div class="hp-cur" style="left:100%"></div>
</div>
<div class="hp-nums">
<span class="hp-n-min">min 75</span>
<span class="hp-n-avg">avg 80</span>
<span class="hp-n-max">max 86 · no death risk</span>
</div>
</div>
</div>
</div>
@ -752,17 +788,33 @@
<div class="range-label-row"><span style="color:var(--sci)">min 10</span><span style="color:var(--sci)">avg 17</span><span style="color:var(--sci)">max 22</span></div>
</div>
</div>
<div class="expected-grid">
<div class="exp-block">
<div class="exp-label">Berserker HP after</div>
<div class="exp-val" style="color:var(--warn)">6880</div>
<div class="exp-sub">survives (DEF 6 is fragile)</div>
<!-- Berserker 90hp takes 1022 counter. Warrior 80hp takes 2538. Neither can die. -->
<div class="hp-after-grid">
<div class="hp-after-block">
<div class="hp-after-label"><span>Berserker HP after (90 now)</span></div>
<div class="hp-track-outer">
<div class="hp-sure" style="width:76%"></div><!-- 68/90 -->
<div class="hp-maybe" style="left:76%;width:13%"></div><!-- 68→80 -->
<div class="hp-cur" style="left:100%"></div>
</div>
<div class="hp-nums">
<span class="hp-n-min">min 68</span>
<span class="hp-n-avg">avg 73</span>
<span class="hp-n-max">max 80 · cannot die</span>
</div>
</div>
<div class="exp-arrow"></div>
<div class="exp-block">
<div class="exp-label">Warrior HP after</div>
<div class="exp-val" style="color:var(--neg)">4255</div>
<div class="exp-sub">likely survives, heavily wounded</div>
<div class="hp-after-block">
<div class="hp-after-label"><span>Warrior HP after (80 now)</span></div>
<div class="hp-track-outer">
<div class="hp-sure" style="width:53%"></div><!-- 42/80 -->
<div class="hp-maybe" style="left:53%;width:16%"></div><!-- 42→55 -->
<div class="hp-cur" style="left:100%"></div>
</div>
<div class="hp-nums">
<span class="hp-n-min">min 42</span>
<span class="hp-n-avg">avg 49</span>
<span class="hp-n-max">max 55 · cannot die</span>
</div>
</div>
</div>
</div>
@ -900,17 +952,54 @@
<div class="range-label-row"><span style="color:var(--sci)">min 10</span><span style="color:var(--sci)">avg 15</span><span style="color:var(--sci)">max 20</span></div>
</div>
</div>
<div class="expected-grid">
<div class="exp-block">
<div class="exp-label">Ironwarden HP after</div>
<div class="exp-val" style="color:var(--sage)">90100</div>
<div class="exp-sub">barely scratched</div>
<!--
Ironwarden 110hp takes 1020 counter.
Berserker WOUNDED 45/90hp takes 3657.
45-57 = -12 → death possible. 45-36 = 9 min surviving HP.
Death range: damage 4557 out of range 3657 = 12/21 = ~57% chance.
-->
<div class="hp-after-grid">
<div class="hp-after-block">
<div class="hp-after-label"><span>Ironwarden HP after (110 now)</span></div>
<div class="hp-track-outer">
<div class="hp-sure" style="width:82%"></div><!-- 90/110 -->
<div class="hp-maybe" style="left:82%;width:9%"></div><!-- 90→100 -->
<div class="hp-cur" style="left:100%"></div>
</div>
<div class="hp-nums">
<span class="hp-n-min">min 90</span>
<span class="hp-n-avg">avg 95</span>
<span class="hp-n-max">max 100 · cannot die</span>
</div>
</div>
<div class="exp-arrow"></div>
<div class="exp-block">
<div class="exp-label">Berserker HP after</div>
<div class="exp-val" style="color:var(--neg)">3354</div>
<div class="exp-sub">critical / likely dead</div>
<div class="hp-after-block">
<div class="hp-after-label">
<span>Berserker HP after (<strong style="color:var(--warn)">wounded 45</strong>/90)</span>
<span class="death-badge">⚠ DEATH POSSIBLE</span>
</div>
<div class="hp-track-outer">
<!-- scale is /90. Current HP 45 = 50%. Min remaining 9 = 10%. Dead zone 00. -->
<div class="hp-zero"></div>
<!-- sure surviving: 9/90 = 10% -->
<div class="hp-sure" style="left:2px;width:8%"></div>
<!-- maybe zone: 9→45 impossible — damage min is 36 so hp ranges 9→45-36=9... wait -->
<!-- damage: 3657. hp=45. remaining: 45-57=-12 (dead) to 45-36=9. -->
<!-- surviving range: 09 hp (if damage 4536 = under 45) -->
<!-- so: sure dead zone if damage>45 -->
<!-- dead portion: (57-45)/(57-36) = 12/21 = 57%. surviving 43%. -->
<!-- On bar (scale 090): 09 surviving (10%), 945 is the current HP marker -->
<div class="hp-maybe" style="left:2px;width:8%"></div><!-- 09 possible surviving -->
<div class="hp-dead" style="left:2px;right:0;width:0%;background:transparent"></div>
<!-- red overlay on dead portion of the bar to the left of 0 — show as left stripe -->
<div style="position:absolute;top:0;bottom:0;left:0;width:10%;background:#d9594044;border-right:2px solid var(--neg)"></div>
<div class="hp-cur" style="left:50%"></div><!-- 45/90 -->
</div>
<div class="hp-nums">
<span class="hp-n-dead">💀 min 12 (dead)</span>
<span class="hp-n-avg">avg 1 (dead)</span>
<span class="hp-n-max">max 9 HP</span>
</div>
<div class="death-prob">death probability: (5745)÷(5736) = 12/21 ≈ <strong>57%</strong></div>
</div>
</div>
</div>
@ -1046,17 +1135,44 @@
<div class="range-label-row"><span style="color:var(--neg)">min 18</span><span style="color:var(--neg)">avg 25</span><span style="color:var(--neg)">max 32 💀</span></div>
</div>
</div>
<div class="expected-grid">
<div class="exp-block">
<div class="exp-label">Cavalry HP after</div>
<div class="exp-val" style="color:var(--neg)">3852</div>
<div class="exp-sub">wounded / dead next hit</div>
<!--
Cavalry WOUNDED 28/70hp, takes 1832 from Spearmen.
28-18=10 min surviving. 28-32=-4 dead. Death when damage>=28.
Death range: 2832 out of 1832 = 4/14 = ~29% chance.
Spearmen 60/60hp takes 1223. 60-23=37 min. Cannot die.
-->
<div class="hp-after-grid">
<div class="hp-after-block">
<div class="hp-after-label">
<span>Cavalry HP after (<strong style="color:var(--warn)">wounded 28</strong>/70)</span>
<span class="death-badge">⚠ DEATH POSSIBLE</span>
</div>
<div class="hp-track-outer">
<!-- scale /70. Current 28=40%. min surviving 10=14%. dead if dmg>=28. -->
<div style="position:absolute;top:0;bottom:0;left:0;width:14%;background:#d9594033;border-right:2px solid var(--neg)"></div>
<div class="hp-sure" style="left:2px;width:12%"></div><!-- 10/70 sure surviving -->
<div class="hp-maybe" style="left:14%;width:12%"></div><!-- 10→18 surviving -->
<div class="hp-cur" style="left:40%"></div><!-- 28/70 -->
</div>
<div class="hp-nums">
<span class="hp-n-dead">💀 min 4 (dead)</span>
<span class="hp-n-avg">avg 3</span>
<span class="hp-n-max">max 10 HP</span>
</div>
<div class="death-prob">death probability: (3228)÷(3218) = 4/14 ≈ <strong>29%</strong></div>
</div>
<div class="exp-arrow"></div>
<div class="exp-block">
<div class="exp-label">Spearmen HP after</div>
<div class="exp-val" style="color:var(--warn)">3748</div>
<div class="exp-sub">damaged but survives</div>
<div class="hp-after-block">
<div class="hp-after-label"><span>Spearmen HP after (60 now)</span></div>
<div class="hp-track-outer">
<div class="hp-sure" style="width:62%"></div><!-- 37/60 -->
<div class="hp-maybe" style="left:62%;width:20%"></div><!-- 37→48 -->
<div class="hp-cur" style="left:100%"></div>
</div>
<div class="hp-nums">
<span class="hp-n-min">min 37</span>
<span class="hp-n-avg">avg 42</span>
<span class="hp-n-max">max 48 · cannot die</span>
</div>
</div>
</div>
</div>