update spirit

This commit is contained in:
jakciehan
2026-06-07 22:10:03 +08:00
parent 427a33c55b
commit 9c57deff6d
82 changed files with 5465 additions and 149 deletions
+2 -2
View File
@@ -10,8 +10,8 @@ const bossCfg: IBossConfig = {
princessCutsceneAtHpRatio: 0.5,
phases: [
{ hpThreshold: 1.0, mode: 'pair_pincer', actionIntervalSec: 2.2 },
{ hpThreshold: 0.66, mode: 'fireball_spread', actionIntervalSec: 1.8 },
{ hpThreshold: 0.33, mode: 'clone_confuse', actionIntervalSec: 1.4 },
{ hpThreshold: 2 / 3, mode: 'fireball_spread', actionIntervalSec: 1.8 },
{ hpThreshold: 1 / 3, mode: 'clone_confuse', actionIntervalSec: 1.4 },
],
};
+1
View File
@@ -13,6 +13,7 @@ function newPair(color: PlayerColorState = PlayerColorState.Red) {
aabb: { x: 0, y: 16, w: 16, h: 32 },
platforms: [{ topY: 0, leftX: -500, rightX: 500 }],
initialColorState: color,
levelLengthPx: 2000,
});
motion.update(0.016); // settle on ground
const jump = new JumpController(motion);
+3 -2
View File
@@ -7,9 +7,10 @@ function makeGroundPlatform(): IPlatform {
function makeModel(color: PlayerColorState = PlayerColorState.Red) {
return new PlayerMotionModel({
aabb: { x: 0, y: 16, w: 16, h: 32 }, // character 16x32 resting on y=0 ground
aabb: { x: 100, y: 16, w: 16, h: 32 }, // character 16x32 resting on y=0 ground; x=100 avoids level-edge clamp
platforms: [makeGroundPlatform()],
initialColorState: color,
levelLengthPx: 2000,
});
}
@@ -27,7 +28,7 @@ describe('PlayerMotionModel — horizontal movement (req 2.1, 5.1-5.2)', () => {
m.setHorizontalInput(1);
m.update(1);
expect(m.vx).toBe(MOVE_SPEED[PlayerColorState.Red]);
expect(m.aabb.x).toBeCloseTo(100, 1);
expect(m.aabb.x).toBeCloseTo(200, 1);
});
it('moves at 150 px/s in yellow state', () => {
+6 -5
View File
@@ -44,21 +44,22 @@ describe('PlayerStateMachine — damage (req 5.3-5.6, 10.4-10.5)', () => {
expect(sm.color).toBe(PlayerColorState.Red);
});
it('Red + shuriken → death, consumes one life', () => {
it('Red + shuriken → life loss (downgraded) when lives > 1', () => {
const sm = new PlayerStateMachine(2);
const out = sm.takeHit('shuriken');
expect(out.kind).toBe('died');
expect(out.kind).toBe('downgraded');
expect(sm.lives).toBe(1);
expect(sm.isDead).toBe(false);
});
it('fireball is always lethal regardless of color', () => {
it('fireball reduces one life regardless of color', () => {
const sm = new PlayerStateMachine(2);
sm.pickupCrystalJade();
sm.pickupCrystalJade();
const out = sm.takeHit('fireball');
expect(out).toEqual({ kind: 'died', cause: 'fireball' });
expect(sm.color).toBe(PlayerColorState.Red);
expect(out.kind).toBe('downgraded');
expect(sm.lives).toBe(1);
expect(sm.isDead).toBe(false);
});
it('smoke bomb is always lethal', () => {