refactor: комплексный рефакторинг + Isaac-like геймплей
Баги и гигиена (Wave 1):
- Player.addWeapon использует MODE_MELEE вместо литерала 1
- Фильтрация мёртвых врагов из room.enemies (раньше массив рос в долгих боях)
- Кап миньёнов босса (BOSS.maxMinions=4) — иначе комната могла не зачиститься
- aliveCount корректно считает живых в knockback-фазе
- equipSlot валидирует диапазон слота
- Магические числа range=70/spread=0.15 вынесены в WeaponDef (beamRange/spread)
- Регрессия cleared-flag: после фильтрации массива длина 0, но cleared должен стать true
Распил Game.ts 683→465 строк (Wave 2):
- systems/movement.ts: moveEntity(e, dx, dy, room) — убрал 5 копий коллизионного паттерна
- systems/projectiles.ts: applyWeaponProjectileStats, explodeBomb, projectileHitWall
- systems/ai.ts: runAI через диспетчер-таблицу (вместо if/else-if каскада)
- Переходы/этажи оставлены в Game.ts (тесно завязаны на cc/cr/player)
Тесты 33→72 (Wave 3):
- movement.test.ts, projectiles.test.ts, combat.test.ts, regressions.test.ts, items.test.ts
- Покрытие: ближний/дальний бой, огнемёт, бомба, лазер, splitter, сундук→пикап,
переход комнат, фазы босса + кап миньёнов, фильтрация мёртвых, секретка
Isaac-like геймплей (Wave 4):
- Статы игрока: damageMul/fireRateMul/rangeMul/shotSpeedMul (мультипликативно
поверх WeaponDef), effectiveDamage/Cooldown, отображение в HUD
- 8-направленный прицел: aimDir: Dir → aimVec: {x,y}, стрелки дают диагонали
- Пассивные предметы (items.ts, 6 штук): сундук дропает 50/50 оружие/предмет
- Новый тип врага splitter: при смерти распадается на двух fast
- Новый тип комнаты secret: +1 max HP один раз при первом входе
This commit is contained in:
+3
-3
@@ -39,7 +39,7 @@ describe('Game', () => {
|
||||
|
||||
it('стрельба создаёт снаряд, который потом исчезает', () => {
|
||||
const game = new Game(DEFAULT_RULES, new Rng(4));
|
||||
game.step(input({ aimDir: 'right' }));
|
||||
game.step(input({ aimVec: { x: 1, y: 0 } }));
|
||||
expect(game.curRoom.tears.length).toBe(1);
|
||||
// Снаряд летит вправо и со временем гаснет (стена/время жизни).
|
||||
for (let i = 0; i < 200; i++) game.step(input());
|
||||
@@ -158,7 +158,7 @@ describe('Game', () => {
|
||||
const game = new Game(DEFAULT_RULES, new Rng(12));
|
||||
game.player.addWeapon('boomerang');
|
||||
|
||||
game.step(input({ aimDir: 'right' }));
|
||||
game.step(input({ aimVec: { x: 1, y: 0 } }));
|
||||
|
||||
expect(game.curRoom.tears[0].type).toBe('boomerang');
|
||||
expect(game.curRoom.tears[0].damage).toBe(2);
|
||||
@@ -171,7 +171,7 @@ describe('Game', () => {
|
||||
const enemy = new Enemy(game.player.x + 7, game.player.y, 'tank');
|
||||
game.curRoom.enemies = [enemy];
|
||||
|
||||
game.step(input({ aimDir: 'right' }));
|
||||
game.step(input({ aimVec: { x: 1, y: 0 } }));
|
||||
expect(enemy.burnTimer).toBeGreaterThan(0);
|
||||
|
||||
const hpAfterHit = enemy.hp;
|
||||
|
||||
Reference in New Issue
Block a user