refine ranged combat

This commit is contained in:
2026-05-16 21:35:35 +02:00
parent 866c0a9f2d
commit 97ba4bf478
7 changed files with 183 additions and 91 deletions
+35 -6
View File
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
import { computeRangedTarget } from '$lib/engine/ranged';
import { fkBasis } from '$lib/engine/derived';
import { newCharacter } from '$lib/characters/default';
import type { ExtraModifierId } from '$lib/rules/modifiers-ranged';
@@ -26,8 +27,8 @@ const baseSel = {
describe('ranged', () => {
it('computes FK target with zero modifiers', () => {
const c = charWithBogen(4);
const fkBase = Math.round((11 + 11 + 11) / 4);
const r = computeRangedTarget(c, 'shortbow', { ...baseSel, reloadState: undefined }, 'none');
const fkBase = fkBasis(c);
const r = computeRangedTarget(c, 'shortbow', { ...baseSel, reloadState: undefined });
expect(r.fkBase).toBe(fkBase);
expect(r.baseTarget).toBe(fkBase + 4);
expect(r.finalTarget).toBe(fkBase + 4);
@@ -38,8 +39,7 @@ describe('ranged', () => {
const r = computeRangedTarget(
c,
'shortbow',
{ ...baseSel, range: 'medium', reloadState: 'regular_shot' },
'none'
{ ...baseSel, range: 'medium', reloadState: 'regular_shot' }
);
// +4 Entfernung, +1 normaler Schuss, +0 Zielgröße „groß“
expect(r.totalModifier).toBe(5);
@@ -51,10 +51,39 @@ describe('ranged', () => {
const r = computeRangedTarget(
c,
'shortbow',
{ ...baseSel, reloadState: 'aimed_shot' },
'none'
{ ...baseSel, reloadState: 'aimed_shot' }
);
expect(r.effectiveTaW).toBe(5);
expect(r.baseTarget).toBe(r.fkBase + 5);
});
it('applies matching weapon specialization (+2 FK)', () => {
const c = charWithBogen(0);
c.abilities = [{ id: 'sf1', defId: 'specialize_shortbow' }];
const base = computeRangedTarget(c, 'shortbow', baseSel);
const plain = computeRangedTarget(charWithBogen(0), 'shortbow', baseSel);
expect(base.finalTarget).toBe(plain.finalTarget + 2);
});
it('ignores specialization for a different weapon', () => {
const c = charWithBogen(0);
c.abilities = [{ id: 'sf1', defId: 'specialize_longbow' }];
const r = computeRangedTarget(c, 'shortbow', baseSel);
const plain = computeRangedTarget(charWithBogen(0), 'shortbow', baseSel);
expect(r.finalTarget).toBe(plain.finalTarget);
});
it('derives expert from Scharfschütze ability (TaW/2 -2 for aimed shot)', () => {
const c = charWithBogen(10);
c.abilities = [{ id: 'sf1', defId: 'sniper' }];
const r = computeRangedTarget(c, 'shortbow', { ...baseSel, reloadState: 'aimed_shot' });
expect(r.effectiveTaW).toBe(3);
});
it('derives master from Meisterschütze ability (full TaW for aimed shot)', () => {
const c = charWithBogen(10);
c.abilities = [{ id: 'sf1', defId: 'marksman' }];
const r = computeRangedTarget(c, 'shortbow', { ...baseSel, reloadState: 'aimed_shot' });
expect(r.effectiveTaW).toBe(10);
});
});