Files
dmtools/templates/enemy/_statblock.html.twig
T
FrankandClaude Sonnet 5 2ed169fc78 Tracker: unfold a combatant's stat block inline
- extract the 5e stat-block markup into enemy/_statblock.html.twig
  (sb.block(enemy) macro); the enemy detail page now uses it too
- each tracker combatant gets a "stat block" toggle that expands the
  block in place (Bootstrap collapse), so you don't have to leave the
  encounter to check AC / attacks
- open blocks are remembered in localStorage and re-opened after each
  damage/heal reload, and the caret rotates when expanded

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:29:52 +02:00

67 lines
2.7 KiB
Twig

{#
# Shared 5e stat-block rendering. Used on the enemy detail page and, folded
# up, inside each tracker combatant.
# {% import 'enemy/_statblock.html.twig' as sb %}
# {{ sb.block(enemy) }}
#}
{% macro mod(n) %}{{ n >= 0 ? '+' ~ n : n }}{% endmacro %}
{% macro _stat(label, score, m) %}
<div class="col text-center">
<div class="text-uppercase small text-body-secondary">{{ label }}</div>
<div class="fw-semibold">{{ score }} ({{ m }})</div>
</div>
{% endmacro %}
{% macro _line(label, value) %}
{% if value %}
<div><span class="fw-semibold">{{ label }}.</span> {{ value }}</div>
{% endif %}
{% endmacro %}
{% macro _section(title, body) %}
{% if body %}
<h3 class="h6 border-bottom pb-1 mt-3">{{ title }}</h3>
<div>{{ body|nl2br }}</div>
{% endif %}
{% endmacro %}
{% macro block(e) %}
{% import _self as sb %}
{{ sb._line('Armor Class', e.armorClass ~ (e.armorClassNote ? ' (' ~ e.armorClassNote ~ ')' : '')) }}
{{ sb._line('Hit Points', e.hitPoints ~ (e.hitDice ? ' (' ~ e.hitDice ~ ')' : '')) }}
{{ sb._line('Speed', e.speed) }}
<div class="row row-cols-6 my-3 g-1">
{{ sb._stat('Str', e.strength, sb.mod(e.abilityModifier(e.strength))) }}
{{ sb._stat('Dex', e.dexterity, sb.mod(e.abilityModifier(e.dexterity))) }}
{{ sb._stat('Con', e.constitution, sb.mod(e.abilityModifier(e.constitution))) }}
{{ sb._stat('Int', e.intelligence, sb.mod(e.abilityModifier(e.intelligence))) }}
{{ sb._stat('Wis', e.wisdom, sb.mod(e.abilityModifier(e.wisdom))) }}
{{ sb._stat('Cha', e.charisma, sb.mod(e.abilityModifier(e.charisma))) }}
</div>
{{ sb._line('Saving Throws', e.savingThrows) }}
{{ sb._line('Skills', e.skills) }}
{{ sb._line('Damage Vulnerabilities', e.damageVulnerabilities) }}
{{ sb._line('Damage Resistances', e.damageResistances) }}
{{ sb._line('Damage Immunities', e.damageImmunities) }}
{{ sb._line('Condition Immunities', e.conditionImmunities) }}
{{ sb._line('Senses', e.senses) }}
{{ sb._line('Languages', e.languages ?? '—') }}
{{ sb._line('Challenge', e.challengeRating ? e.challengeRating ~ (e.experiencePoints ? ' (' ~ e.experiencePoints ~ ' XP)' : '') : null) }}
{{ sb._line('Proficiency Bonus', e.proficiencyBonus ? sb.mod(e.proficiencyBonus) : null) }}
{{ sb._section('Traits', e.traits) }}
{{ sb._section('Actions', e.actions) }}
{{ sb._section('Bonus Actions', e.bonusActions) }}
{{ sb._section('Reactions', e.reactions) }}
{{ sb._section('Legendary Actions', e.legendaryActions) }}
{{ sb._section('Description', e.description) }}
{% if e.source %}
<p class="text-body-secondary small mt-3 mb-0">Source: {{ e.source }}</p>
{% endif %}
{% endmacro %}