first commmit
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { AttackController, IJumpStateProvider } from '@logic/AttackController';
|
||||
import { WeaponType } from '@data/Interfaces';
|
||||
import { PlayerColorState } from '@common/Constants';
|
||||
|
||||
function makeJumpState(ts?: number): IJumpStateProvider {
|
||||
return {
|
||||
lastJumpPressTs: () => ts,
|
||||
isGrounded: () => ts === undefined,
|
||||
};
|
||||
}
|
||||
|
||||
describe('AttackController — mutual exclusion (req 3.1-3.3)', () => {
|
||||
it('first-pressed weapon wins when both buttons go down together', () => {
|
||||
const ac = new AttackController();
|
||||
ac.press(WeaponType.Shuriken, 10);
|
||||
ac.press(WeaponType.NinjaSword, 11);
|
||||
expect(ac.getActive()).toBe(WeaponType.Shuriken);
|
||||
});
|
||||
|
||||
it('releasing the active weapon transfers activation to the still-held one', () => {
|
||||
const ac = new AttackController();
|
||||
ac.press(WeaponType.Shuriken, 0);
|
||||
ac.press(WeaponType.NinjaSword, 10);
|
||||
ac.release(WeaponType.Shuriken);
|
||||
expect(ac.getActive()).toBe(WeaponType.NinjaSword);
|
||||
});
|
||||
|
||||
it('releasing the only pressed weapon deactivates everything', () => {
|
||||
const ac = new AttackController();
|
||||
ac.press(WeaponType.Shuriken, 0);
|
||||
ac.release(WeaponType.Shuriken);
|
||||
expect(ac.getActive()).toBe('none');
|
||||
});
|
||||
});
|
||||
|
||||
describe('AttackController — firing intervals (req 3.4, 3.6)', () => {
|
||||
it('shuriken fires every 300ms for red/green, 250ms for yellow', () => {
|
||||
const ac = new AttackController();
|
||||
ac.press(WeaponType.Shuriken, 0);
|
||||
expect(ac.tick(0, PlayerColorState.Red).length).toBe(1);
|
||||
expect(ac.tick(299, PlayerColorState.Red).length).toBe(0);
|
||||
expect(ac.tick(300, PlayerColorState.Red).length).toBe(1);
|
||||
|
||||
const fast = new AttackController();
|
||||
fast.press(WeaponType.Shuriken, 0);
|
||||
fast.tick(0, PlayerColorState.Yellow);
|
||||
expect(fast.tick(249, PlayerColorState.Yellow).length).toBe(0);
|
||||
expect(fast.tick(250, PlayerColorState.Yellow).length).toBe(1);
|
||||
});
|
||||
|
||||
it('sword fires every 500ms', () => {
|
||||
const ac = new AttackController();
|
||||
ac.press(WeaponType.NinjaSword, 0);
|
||||
ac.tick(0, PlayerColorState.Red);
|
||||
expect(ac.tick(499, PlayerColorState.Red).length).toBe(0);
|
||||
expect(ac.tick(500, PlayerColorState.Red).length).toBe(1);
|
||||
});
|
||||
|
||||
it('shuriken burst index caps at SHURIKEN_BURST_MAX', () => {
|
||||
const ac = new AttackController();
|
||||
ac.press(WeaponType.Shuriken, 0);
|
||||
const indexes: number[] = [];
|
||||
for (let i = 0; i <= 600; i += 300) {
|
||||
const fires = ac.tick(i, PlayerColorState.Red);
|
||||
if (fires.length) indexes.push(fires[0].burstIndex);
|
||||
}
|
||||
expect(indexes).toEqual([1, 2, 3]);
|
||||
// One more attempt must still cap at 3 not 4.
|
||||
const more = ac.tick(900, PlayerColorState.Red);
|
||||
expect(more[0].burstIndex).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AttackController — combo window (req 4.1)', () => {
|
||||
it('comboWithJump is true when jump timestamp is within 100ms', () => {
|
||||
const ac = new AttackController(makeJumpState(95));
|
||||
ac.press(WeaponType.Shuriken, 100);
|
||||
const fires = ac.tick(100, PlayerColorState.Red);
|
||||
expect(fires[0].comboWithJump).toBe(true);
|
||||
});
|
||||
|
||||
it('comboWithJump is false when jump was pressed >100ms ago', () => {
|
||||
const ac = new AttackController(makeJumpState(0));
|
||||
ac.press(WeaponType.Shuriken, 200);
|
||||
const fires = ac.tick(200, PlayerColorState.Red);
|
||||
expect(fires[0].comboWithJump).toBe(false);
|
||||
});
|
||||
|
||||
it('comboWithJump is false when the player has not jumped yet', () => {
|
||||
const ac = new AttackController(makeJumpState(undefined));
|
||||
ac.press(WeaponType.Shuriken, 0);
|
||||
const fires = ac.tick(0, PlayerColorState.Red);
|
||||
expect(fires[0].comboWithJump).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AttackController — reset()', () => {
|
||||
it('clears active / pressed / cooldowns', () => {
|
||||
const ac = new AttackController();
|
||||
ac.press(WeaponType.Shuriken, 0);
|
||||
ac.tick(0, PlayerColorState.Red);
|
||||
ac.reset();
|
||||
expect(ac.getActive()).toBe('none');
|
||||
expect(ac.isPressed(WeaponType.Shuriken)).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user