first commmit
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import { CameraScroller, PARALLAX_LAYERS, PARALLAX_RATIOS, cameraFromLevel } from '@logic/CameraScroller';
|
||||
import { LevelMgr } from '@logic/LevelMgr';
|
||||
import { EnemyType, ILevelConfig } from '@data/Interfaces';
|
||||
|
||||
const HORIZONTAL_LEVEL: ILevelConfig = {
|
||||
id: '1-1',
|
||||
chapter: 1,
|
||||
displayName: '初始森林',
|
||||
sceneTheme: 'forest',
|
||||
scrollDirection: 'horizontal',
|
||||
timeLimitSec: 75,
|
||||
objective: { kind: 'kill_count', enemy: EnemyType.YaoFang, count: 3 },
|
||||
levelLengthPx: 3840,
|
||||
bgm: 'bgm_forest',
|
||||
enemySpawns: [],
|
||||
};
|
||||
|
||||
describe('CameraScroller — horizontal (req 8.1)', () => {
|
||||
it('camera scrolls forward as player advances', () => {
|
||||
const cam = cameraFromLevel(HORIZONTAL_LEVEL, 960, 540);
|
||||
cam.followPlayer(480, 270);
|
||||
expect(cam.offsetX).toBe(0);
|
||||
cam.followPlayer(900, 270);
|
||||
expect(cam.offsetX).toBe(420);
|
||||
});
|
||||
|
||||
it('camera never rewinds on horizontal scroll (req 8.1)', () => {
|
||||
const cam = cameraFromLevel(HORIZONTAL_LEVEL);
|
||||
cam.followPlayer(1500, 270);
|
||||
const forward = cam.offsetX;
|
||||
cam.followPlayer(100, 270);
|
||||
expect(cam.offsetX).toBe(forward); // did not rewind
|
||||
});
|
||||
|
||||
it('camera stops at level end', () => {
|
||||
const cam = cameraFromLevel(HORIZONTAL_LEVEL);
|
||||
cam.followPlayer(10_000, 270);
|
||||
expect(cam.offsetX).toBe(HORIZONTAL_LEVEL.levelLengthPx - 960);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CameraScroller — parallax layers (req 8.8 — 1:2:4:4)', () => {
|
||||
it('exposes 4 layers with ratios 1,2,4,4', () => {
|
||||
expect(PARALLAX_LAYERS).toEqual(['far', 'mid', 'near', 'fx']);
|
||||
expect([...PARALLAX_RATIOS]).toEqual([1, 2, 4, 4]);
|
||||
});
|
||||
|
||||
it('far/mid/near produce progressively smaller offsets', () => {
|
||||
const cam = cameraFromLevel(HORIZONTAL_LEVEL);
|
||||
cam.followPlayer(1200, 270);
|
||||
const far = cam.offsetForLayer('far');
|
||||
const mid = cam.offsetForLayer('mid');
|
||||
const near = cam.offsetForLayer('near');
|
||||
expect(far.x).toBeGreaterThan(mid.x);
|
||||
expect(mid.x).toBeGreaterThan(near.x);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CameraScroller — bi-directional + vertical', () => {
|
||||
it('horizontal_bi rewinds when the player walks backward', () => {
|
||||
const cam = new CameraScroller({
|
||||
direction: 'horizontal_bi',
|
||||
lengthX: 4800,
|
||||
viewportW: 960,
|
||||
viewportH: 540,
|
||||
});
|
||||
cam.followPlayer(2000, 0);
|
||||
cam.followPlayer(400, 0);
|
||||
expect(cam.offsetX).toBe(0);
|
||||
});
|
||||
|
||||
it('vertical rising clamps at top', () => {
|
||||
const cam = new CameraScroller({
|
||||
direction: 'vertical',
|
||||
lengthX: 960,
|
||||
lengthY: 3240,
|
||||
viewportW: 960,
|
||||
viewportH: 540,
|
||||
});
|
||||
cam.followPlayer(480, 6000);
|
||||
expect(cam.offsetY).toBe(3240 - 540);
|
||||
});
|
||||
});
|
||||
|
||||
describe('LevelMgr — objective / timer / result', () => {
|
||||
it('reports victory when kill objective is met', () => {
|
||||
const lv = new LevelMgr(HORIZONTAL_LEVEL);
|
||||
lv.onEnemyKilled(EnemyType.YaoFang);
|
||||
lv.onEnemyKilled(EnemyType.YaoFang);
|
||||
lv.onEnemyKilled(EnemyType.YaoFang);
|
||||
expect(lv.tick(0.016)).toBe('victory');
|
||||
});
|
||||
|
||||
it('reports timeout when time-limit expires', () => {
|
||||
const lv = new LevelMgr(HORIZONTAL_LEVEL);
|
||||
expect(lv.tick(HORIZONTAL_LEVEL.timeLimitSec + 0.1)).toBe('timeout');
|
||||
});
|
||||
|
||||
it('player_dead is terminal and outranks victory', () => {
|
||||
const lv = new LevelMgr(HORIZONTAL_LEVEL);
|
||||
lv.onPlayerDied();
|
||||
expect(lv.tick(0.1)).toBe('player_dead');
|
||||
});
|
||||
|
||||
it('reach_top objective', () => {
|
||||
const cfg: ILevelConfig = { ...HORIZONTAL_LEVEL, objective: { kind: 'reach_top' } };
|
||||
const lv = new LevelMgr(cfg);
|
||||
lv.onReachedTop();
|
||||
expect(lv.tick(0.016)).toBe('victory');
|
||||
});
|
||||
|
||||
it('defeat_boss objective', () => {
|
||||
const cfg: ILevelConfig = {
|
||||
...HORIZONTAL_LEVEL,
|
||||
objective: { kind: 'defeat_boss', bossId: 'shuang_huan_fang' },
|
||||
};
|
||||
const lv = new LevelMgr(cfg);
|
||||
lv.onBossKilled();
|
||||
expect(lv.tick(0.016)).toBe('victory');
|
||||
});
|
||||
|
||||
it('result() returns kills and remaining seconds', () => {
|
||||
const lv = new LevelMgr(HORIZONTAL_LEVEL);
|
||||
lv.onEnemyKilled(EnemyType.QingRen);
|
||||
lv.tick(10);
|
||||
const r = lv.result();
|
||||
expect(r.kills[EnemyType.QingRen]).toBe(1);
|
||||
expect(r.remainingSec).toBeCloseTo(65, 1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user