Files
dsa-ranger/src/routes/characters/[id]/combat/melee/+page.svelte
T
2026-05-11 22:07:17 +02:00

155 lines
3.5 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/stores';
import type { Character } from '$lib/schema/character';
import { getCharacter } from '$lib/storage/repo';
import { computeMeleeTargets } from '$lib/engine/melee';
import { TALENTS } from '$lib/rules/talents';
let char: Character | undefined;
let err = '';
const id = $page.params.id ?? '';
let talentId = 'dolche';
let weaponIndex = 0;
let situation = '0';
let atExtra = 0;
let paExtra = 0;
let ansage = 0;
onMount(async () => {
if (!id) {
err = 'Ungültige URL.';
return;
}
char = await getCharacter(id);
if (!char) err = 'Held nicht gefunden.';
else if (char.waffen.nahkampf.length) {
talentId = char.waffen.nahkampf[0].talentId;
}
});
$: wIdx = Number(weaponIndex);
$: weapon = char?.waffen.nahkampf[wIdx];
$: result =
char &&
weapon &&
computeMeleeTargets(
char,
talentId,
weapon.atMod,
weapon.paMod,
{
situationModifiers: situation
.split(/[,;]+/)
.map((s) => parseInt(s.trim(), 10))
.filter((n) => Number.isFinite(n)),
atExtra,
paExtra,
ansageAtAnteil: ansage
}
);
</script>
{#if err}
<p class="err">{err}</p>
{:else if !char}
<p>Lade …</p>
{:else}
<h1>Nahkampf · {char.meta.name}</h1>
<p class="muted">
Wähle Waffe und Erschwernisse (Zahlen). Manöver-Details am Tisch hier nur Summen.
</p>
<section class="card stack">
<h2>Waffe</h2>
{#if char.waffen.nahkampf.length === 0}
<p>Keine Nahkampfwaffe im Bogen. Unter „Bogen“ anlegen.</p>
{:else}
<select bind:value={weaponIndex}>
{#each char.waffen.nahkampf as w, i}
<option value={i}>{w.name}</option>
{/each}
</select>
<div class="field">
<label>Kampftalent für diese Attacke</label>
<select bind:value={talentId}>
{#each TALENTS.filter((t) => t.category === 'kampf') as t}
<option value={t.id}>{t.name}</option>
{/each}
</select>
</div>
{/if}
</section>
<section class="card stack">
<h2>Situation</h2>
<div class="field">
<label>Zusätzliche Erschwernisse (Komma-getrennt, z.B. 3,2)</label>
<input bind:value={situation} placeholder="0" />
</div>
<div class="grid-2">
<div class="field">
<label>AT-Zusatz</label>
<input type="number" bind:value={atExtra} />
</div>
<div class="field">
<label>PA-Zusatz</label>
<input type="number" bind:value={paExtra} />
</div>
</div>
<div class="field">
<label>Ansage (AT-Anteil, vereinfacht)</label>
<input type="number" min="0" bind:value={ansage} />
</div>
</section>
{#if result}
<section class="card stack highlight">
<h2>Zielzahlen</h2>
<div class="grid-2">
<div>
<p class="muted">Attacke</p>
<p class="big-number">{result.atTarget}</p>
</div>
<div>
<p class="muted">Parade</p>
<p class="big-number">{result.paTarget}</p>
</div>
</div>
<ul class="kv">
<li><span>AT (inkl. Waffe)</span><strong>{result.at}</strong></li>
<li><span>PA (inkl. Waffe)</span><strong>{result.pa}</strong></li>
<li><span>Summe Modifikatoren</span><strong>{result.modifierSum}</strong></li>
</ul>
{#each result.notes as n}
<p class="muted">{n}</p>
{/each}
</section>
{/if}
<p><a href="/characters/{char.id}">Zurück</a></p>
{/if}
<style>
.highlight {
border-color: var(--accent-dim);
}
.kv {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.kv li {
display: flex;
justify-content: space-between;
gap: 1rem;
}
.err {
color: var(--danger);
}
</style>