Files
2026-09-24 17:57:54 +02:00

130 lines
3.7 KiB
JavaScript

import { describe, expect, it } from 'vitest';
import { WEAPONS } from '../../app/static/js/config.js';
import { degToRad } from '../../app/static/js/logic/math.js';
import { createRng } from '../../app/static/js/logic/rng.js';
import { pelletOffsets, WeaponState } from '../../app/static/js/logic/weapon.js';
const def = {
id: 'test',
name: 'Test',
magazineSize: 3,
reserveStart: 4,
reserveMax: 10,
fireIntervalMs: 100,
reloadMs: 500,
damage: 1,
pellets: 1,
spreadDeg: 0,
bulletSpeed: 1,
rangePx: 1,
automatic: true,
};
const rng = createRng(1);
describe('WeaponState', () => {
it('startet mit vollem Magazin und Startreserve', () => {
const w = new WeaponState(def);
expect(w.magazine).toBe(3);
expect(w.reserve).toBe(4);
expect(w.isReloading).toBe(false);
});
it('respektiert die Feuerrate', () => {
const w = new WeaponState(def);
expect(w.fire(0, rng)).toHaveLength(1);
expect(w.fire(50, rng)).toBeNull();
expect(w.fire(100, rng)).toHaveLength(1);
expect(w.magazine).toBe(1);
});
it('lädt bei leerem Magazin automatisch nach und kann währenddessen nicht feuern', () => {
const w = new WeaponState(def);
w.fire(0, rng);
w.fire(100, rng);
w.fire(200, rng);
expect(w.magazine).toBe(0);
expect(w.isReloading).toBe(true);
expect(w.fire(300, rng)).toBeNull();
expect(w.update(699)).toBe(false);
expect(w.update(700)).toBe(true);
expect(w.magazine).toBe(3);
expect(w.reserve).toBe(1);
});
it('lädt bei knapper Reserve nur teilweise nach und ist danach leer', () => {
const w = new WeaponState({ ...def, reserveStart: 1 });
w.fire(0, rng);
w.fire(100, rng);
w.fire(200, rng);
w.update(10_000);
expect(w.magazine).toBe(1);
expect(w.reserve).toBe(0);
w.fire(10_100, rng);
expect(w.isEmpty).toBe(true);
expect(w.startReload(10_200)).toBe(false);
});
it('lädt nicht nach, wenn das Magazin voll ist', () => {
const w = new WeaponState(def);
expect(w.startReload(0)).toBe(false);
});
it('bricht das Nachladen beim Waffenwechsel ohne Verlust ab', () => {
const w = new WeaponState(def);
w.fire(0, rng);
expect(w.startReload(100)).toBe(true);
w.cancelReload();
w.update(10_000);
expect(w.magazine).toBe(2);
expect(w.reserve).toBe(4);
});
it('zeigt den Nachladefortschritt', () => {
const w = new WeaponState(def);
w.fire(0, rng);
w.startReload(1000);
expect(w.reloadProgress(1250)).toBeCloseTo(0.5);
w.update(1500);
expect(w.reloadProgress(1600)).toBe(1);
});
it('füllt die Reserve per Nachschub auf', () => {
const w = new WeaponState(def);
w.refill();
expect(w.reserve).toBe(10);
});
it('hat bei der Dienstpistole unbegrenzte Reserve', () => {
const pistol = new WeaponState(WEAPONS.pistol);
expect(pistol.hasInfiniteReserve).toBe(true);
let t = 0;
for (let i = 0; i < 100; i += 1) {
t += 10_000;
pistol.update(t);
pistol.fire(t, rng);
}
expect(pistol.isEmpty).toBe(false);
pistol.refill();
expect(pistol.reserve).toBe(Infinity);
});
});
describe('pelletOffsets', () => {
it('streut ein Einzelprojektil innerhalb des Fächers', () => {
for (let i = 0; i < 50; i += 1) {
const [offset] = pelletOffsets(1, 10, rng);
expect(Math.abs(offset)).toBeLessThanOrEqual(degToRad(5));
}
});
it('verteilt Schrot über den ganzen Fächer', () => {
const offsets = pelletOffsets(8, 24, rng);
expect(offsets).toHaveLength(8);
const spread = degToRad(24);
expect(Math.min(...offsets)).toBeLessThan(-spread / 3);
expect(Math.max(...offsets)).toBeGreaterThan(spread / 3);
for (const o of offsets) expect(Math.abs(o)).toBeLessThanOrEqual(spread / 2 + spread / 7);
});
});