| """Featured characters and living world showcase.""" |
|
|
| from __future__ import annotations |
|
|
| from ui import assets |
| from world.entities import get_all_entities |
| from world.locations import get_location_by_id |
| from ui.avatars import render_avatar |
|
|
|
|
| def _location_slug(entity: dict) -> str | None: |
| loc = get_location_by_id(entity["location_id"]) |
| return loc["slug"] if loc else None |
|
|
|
|
| def render_featured_characters(limit: int = 6) -> str: |
| entities = get_all_entities(limit=50) |
| if not entities: |
| return "" |
|
|
| |
| def score(e: dict) -> int: |
| s = 0 |
| if e["type"] in ("character", "creature"): |
| s += 3 |
| if e.get("memory_summary"): |
| s += 2 |
| if e["status"] == "legendary": |
| s += 4 |
| if e.get("tags"): |
| s += 1 |
| s += min(e["days_in_realm"], 10) |
| return s |
|
|
| featured = sorted(entities, key=score, reverse=True)[:limit] |
|
|
| cards = [] |
| for entity in featured: |
| loc = get_location_by_id(entity["location_id"]) |
| loc_name = loc["short_description"][:60] if loc else "" |
| loc_short = loc["name"].replace("The ", "") if loc else "Unknown" |
| slug = loc["slug"] if loc else None |
| avatar = render_avatar(entity, size=72, location_slug=slug) |
| status_class = f"featured-{entity['status']}" |
| tag = entity["tags"][0] if entity.get("tags") else entity["type"] |
| img = assets.location_image_url(slug) |
| bg_style = f"background-image: url('{img}');" if img else "" |
|
|
| cards.append(f""" |
| <div class="featured-card {status_class}" data-entity-id="{entity['id']}" style="{bg_style}"> |
| <div class="featured-card-scrim"></div> |
| <div class="featured-portrait">{avatar}</div> |
| <div class="featured-info"> |
| <span class="featured-type">{tag}</span> |
| <h3 class="featured-name">{entity['name']}</h3> |
| <p class="featured-location">{loc_short}</p> |
| <p class="featured-appearance">{entity['appearance'][:140]}β¦</p> |
| <blockquote class="featured-greeting">"{entity['greeting'][:90]}{'β¦' if len(entity['greeting']) > 90 else ''}"</blockquote> |
| {f'<p class="featured-memory">{entity["memory_summary"][:100]}β¦</p>' if entity.get("memory_summary") else ''} |
| </div> |
| </div> |
| """) |
|
|
| return f""" |
| <div class="featured-section"> |
| <div class="section-header"> |
| <h2 class="section-title">Souls of the Garden</h2> |
| <p class="section-desc">Real inhabitants β each with their own wants, fears, and memories</p> |
| </div> |
| <div class="featured-grid">{"".join(cards)}</div> |
| </div> |
| """ |
|
|
|
|
| def render_world_vignette() -> str: |
| from world.book_of_ages import get_entries |
| from world.events import get_active_world_event |
|
|
| event = get_active_world_event() |
| if event: |
| effect = (event.get("entity_effect") or "").strip() |
| mystery = (event.get("mystery_hook") or "").strip() |
| chronicle = (event.get("book_of_ages_entry") or "").strip() |
| parts = [] |
| if effect: |
| parts.append(f"<p class=\"vignette-text\"><b>On the souls:</b> {effect}</p>") |
| if mystery: |
| parts.append(f"<p class=\"vignette-text vignette-mystery\"><b>Whispered omen:</b> <em>{mystery}</em></p>") |
| if not parts and chronicle: |
| parts.append(f"<p class=\"vignette-text\">{chronicle}</p>") |
| body = "".join(parts) if parts else ( |
| "<p class=\"vignette-text\">The hour shifts β watch the map and chronicle for what stirs.</p>" |
| ) |
| return f""" |
| <div class="world-vignette"> |
| <p class="vignette-label">Ripples through the Garden</p> |
| {body} |
| </div> |
| """ |
|
|
| entries = get_entries(limit=1, entry_type="interaction") |
| if entries: |
| return f""" |
| <div class="world-vignette"> |
| <p class="vignette-label">Recently, in the Garden</p> |
| <p class="vignette-text">{entries[0]['content']}</p> |
| </div> |
| """ |
|
|
| return """ |
| <div class="world-vignette"> |
| <p class="vignette-label">You stand at the threshold</p> |
| <p class="vignette-text">The lanterns are lit. Someone left footprints in the moss. |
| The world has been waiting. Add something β and it will remember you.</p> |
| </div> |
| """ |
|
|