130 lines
3.7 KiB
JavaScript
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);
|
|
});
|
|
});
|