Pabloler21 Claude Fable 5 commited on
Commit
d207901
Β·
1 Parent(s): a50d7dd

feat: dissolve entity mode + CSS for both ending visuals

Browse files

rage mode (asset-dependent) lands when hollow_rage.png arrives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Files changed (3) hide show
  1. render.py +12 -1
  2. styles.css +31 -0
  3. tests/test_render.py +8 -0
render.py CHANGED
@@ -48,7 +48,8 @@ def render_entity(affinity: int, mode: str = "idle", seq: int = 0) -> str:
48
  """The entity panel: Hollow materializes as affinity rises.
49
 
50
  mode: "idle" | "flash" (memory captured) | "flash_strong" (recall turn)
51
- | "end" (finale β€” dissolving portrait)
 
52
  seq: turn counter β€” alternates one-shot animation names (flash-0/flash-1)
53
  so the flash restarts even when Gradio morphs the DOM in place.
54
  """
@@ -81,6 +82,16 @@ def render_entity(affinity: int, mode: str = "idle", seq: int = 0) -> str:
81
  f"style=\"background-image:url('{_src('terror')}')\"></div>"
82
  )
83
  fog_op, grain_op, vig_op = 0.15, 0.05, 0.4
 
 
 
 
 
 
 
 
 
 
84
  else:
85
  sway = "" if almost_on else " entity-sway"
86
  almost_op = opacity if almost_on else 0
 
48
  """The entity panel: Hollow materializes as affinity rises.
49
 
50
  mode: "idle" | "flash" (memory captured) | "flash_strong" (recall turn)
51
+ | "end" (good finale) | "rage" (bad finale, needs hollow_rage asset)
52
+ | "dissolve" (neutral finale β€” materialization in reverse)
53
  seq: turn counter β€” alternates one-shot animation names (flash-0/flash-1)
54
  so the flash restarts even when Gradio morphs the DOM in place.
55
  """
 
82
  f"style=\"background-image:url('{_src('terror')}')\"></div>"
83
  )
84
  fog_op, grain_op, vig_op = 0.15, 0.05, 0.4
85
+ elif mode == "dissolve":
86
+ figure = (
87
+ '<div class="entity-figure">'
88
+ f'<img class="entity-img entity-dissolve" src="{_src("base")}" '
89
+ f'style="{look}">'
90
+ "</div>"
91
+ )
92
+ flash = ""
93
+ # the curtain falls back: fog and vignette swallow the child again
94
+ fog_op, grain_op, vig_op = 0.9, 0.13, 0.9
95
  else:
96
  sway = "" if almost_on else " entity-sway"
97
  almost_op = opacity if almost_on else 0
styles.css CHANGED
@@ -651,3 +651,34 @@ footer { display: none !important; }
651
 
652
  /* Title β€” no underline rule */
653
  .gradio-container h2 { border-bottom: none !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651
 
652
  /* Title β€” no underline rule */
653
  .gradio-container h2 { border-bottom: none !important; }
654
+
655
+ /* ── 18. Three endings β€” rage portrait & fog dissolve ── */
656
+
657
+ /* bad ending: a low red breath pulsing over the rage portrait β€” the only
658
+ color in the entire game, reserved for this moment */
659
+ .entity-rage-tint {
660
+ position: absolute;
661
+ inset: 0;
662
+ pointer-events: none;
663
+ background: radial-gradient(ellipse at 50% 35%, rgba(120, 20, 20, 0.16), transparent 70%);
664
+ mix-blend-mode: screen;
665
+ animation: rage-pulse 2.6s ease-in-out infinite;
666
+ }
667
+
668
+ @keyframes rage-pulse {
669
+ 0%, 100% { opacity: 0.55; }
670
+ 50% { opacity: 1; }
671
+ }
672
+
673
+ /* neutral ending: materialization in reverse β€” the child sinks back into
674
+ the fog. One-shot on mount; the animation overrides the inline idle style
675
+ and `forwards` holds the dissolved frame. Opacity/blur are the beat itself β€”
676
+ do NOT add these to the prefers-reduced-motion kill lists. */
677
+ .entity-dissolve {
678
+ animation: entity-dissolve 9s ease-in forwards;
679
+ }
680
+
681
+ @keyframes entity-dissolve {
682
+ 0% { filter: blur(0px); opacity: 1; transform: scale(1); }
683
+ 100% { filter: blur(10px); opacity: 0.18; transform: scale(0.62); }
684
+ }
tests/test_render.py CHANGED
@@ -189,3 +189,11 @@ class TestTreasureWounds:
189
  def test_wounds_alone_suppress_empty_placeholder(self):
190
  html_out = render_treasure([], wounds=["w1"])
191
  assert "nothing yet" not in html_out
 
 
 
 
 
 
 
 
 
189
  def test_wounds_alone_suppress_empty_placeholder(self):
190
  html_out = render_treasure([], wounds=["w1"])
191
  assert "nothing yet" not in html_out
192
+
193
+
194
+ class TestEntityDissolveMode:
195
+ def test_dissolve_mode_has_dissolve_class(self):
196
+ assert "entity-dissolve" in render_entity(89, "dissolve")
197
+
198
+ def test_idle_has_no_dissolve(self):
199
+ assert "entity-dissolve" not in render_entity(50, "idle")