Half changes to switch to exit level, Ran out of credits, re added enemies
This commit is contained in:
BIN
public/assets/sprites/items/track_switch.png
Normal file
BIN
public/assets/sprites/items/track_switch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 896 B |
@@ -73,8 +73,8 @@ export const GAME_CONFIG = {
|
||||
},
|
||||
|
||||
enemyScaling: {
|
||||
baseCount: 0,
|
||||
baseCountPerFloor: 0,
|
||||
baseCount: 15,
|
||||
baseCountPerFloor: 5,
|
||||
hpPerFloor: 5,
|
||||
attackPerTwoFloors: 1,
|
||||
expMultiplier: 1.2
|
||||
@@ -190,7 +190,8 @@ export const GAME_CONFIG = {
|
||||
{ key: "mine_cart", path: "assets/sprites/items/mine_cart.png" },
|
||||
{ key: "track_straight", path: "assets/sprites/items/track_straight.png" },
|
||||
{ key: "track_corner", path: "assets/sprites/items/track_corner.png" },
|
||||
{ key: "track_vertical", path: "assets/sprites/items/track_vertical.png" }
|
||||
{ key: "track_vertical", path: "assets/sprites/items/track_vertical.png" },
|
||||
{ key: "track_switch", path: "assets/sprites/items/track_switch.png" }
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ function createMockWorld(): World {
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ const createTestWorld = (): World => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(TileType.EMPTY),
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ describe("CombatLogic - getClosestVisibleEnemy", () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
@@ -70,7 +71,8 @@ describe("CombatLogic - getClosestVisibleEnemy", () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
@@ -123,7 +125,8 @@ describe("CombatLogic - getClosestVisibleEnemy", () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
|
||||
const actors = new Map<EntityId, Actor>();
|
||||
|
||||
@@ -11,7 +11,8 @@ describe('Pathfinding', () => {
|
||||
width,
|
||||
height,
|
||||
tiles: new Array(width * height).fill(tileType),
|
||||
exit: { x: 0, y: 0 }
|
||||
exit: { x: 0, y: 0 },
|
||||
trackPath: []
|
||||
});
|
||||
|
||||
it('should find a path between two reachable points', () => {
|
||||
|
||||
@@ -10,7 +10,8 @@ const createTestWorld = (): World => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ const createTestWorld = (): World => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0), // 0 = Floor
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { idx, inBounds, isWall, isBlocked, tryDestructTile, isPlayerOnExit } from '../world/world-logic';
|
||||
import { idx, inBounds, isWall, isBlocked, tryDestructTile } from '../world/world-logic';
|
||||
import { type World, type Tile } from '../../core/types';
|
||||
import { TileType } from '../../core/terrain';
|
||||
|
||||
@@ -9,7 +9,8 @@ describe('World Utilities', () => {
|
||||
width,
|
||||
height,
|
||||
tiles,
|
||||
exit: { x: 0, y: 0 }
|
||||
exit: { x: 0, y: 0 },
|
||||
trackPath: []
|
||||
});
|
||||
|
||||
describe('idx', () => {
|
||||
@@ -141,39 +142,4 @@ describe('World Utilities', () => {
|
||||
expect(tryDestructTile(world, -1, 0)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPlayerOnExit', () => {
|
||||
it('should return true when player is on exit', () => {
|
||||
const world = createTestWorld(10, 10, new Array(100).fill(TileType.EMPTY));
|
||||
world.exit = { x: 5, y: 5 };
|
||||
|
||||
const mockAccessor = {
|
||||
getPlayer: () => ({ pos: { x: 5, y: 5 } })
|
||||
} as any;
|
||||
|
||||
expect(isPlayerOnExit(world, mockAccessor)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when player is not on exit', () => {
|
||||
const world = createTestWorld(10, 10, new Array(100).fill(TileType.EMPTY));
|
||||
world.exit = { x: 5, y: 5 };
|
||||
|
||||
const mockAccessor = {
|
||||
getPlayer: () => ({ pos: { x: 4, y: 4 } })
|
||||
} as any;
|
||||
|
||||
expect(isPlayerOnExit(world, mockAccessor)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when player does not exist', () => {
|
||||
const world = createTestWorld(10, 10, new Array(100).fill(TileType.EMPTY));
|
||||
world.exit = { x: 5, y: 5 };
|
||||
|
||||
const mockAccessor = {
|
||||
getPlayer: () => null
|
||||
} as any;
|
||||
|
||||
expect(isPlayerOnExit(world, mockAccessor)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -174,7 +174,7 @@ export class EntityBuilder {
|
||||
effectDuration?: number;
|
||||
}): this {
|
||||
this.components.trigger = {
|
||||
onEnter: options.onEnter ?? true,
|
||||
onEnter: options.onEnter ?? false,
|
||||
onExit: options.onExit,
|
||||
onInteract: options.onInteract,
|
||||
oneShot: options.oneShot,
|
||||
|
||||
@@ -243,9 +243,11 @@ export const Prefabs = {
|
||||
return EntityBuilder.create(world)
|
||||
.withPosition(x, y)
|
||||
.withName("Track Switch")
|
||||
.withSprite("dungeon", 31) // TileType.SWITCH_OFF
|
||||
.withSprite("track_switch", 0)
|
||||
.asTrigger({
|
||||
onEnter: false,
|
||||
onInteract: true,
|
||||
oneShot: true,
|
||||
targetId: cartId
|
||||
})
|
||||
.build();
|
||||
|
||||
@@ -12,7 +12,8 @@ describe('ECS Removal and Accessor', () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 0, y: 0 }
|
||||
exit: { x: 0, y: 0 },
|
||||
trackPath: []
|
||||
};
|
||||
const accessor = new EntityAccessor(gameWorld, 999 as EntityId, ecsWorld);
|
||||
|
||||
|
||||
@@ -105,9 +105,9 @@ export class TriggerSystem extends System {
|
||||
if (mineCart) {
|
||||
mineCart.isMoving = true;
|
||||
|
||||
// Change switch sprite to "on" (using dungeon sprite 32)
|
||||
// Change switch sprite if applicable (optional for now as we only have one frame)
|
||||
const sprite = world.getComponent(triggerId, "sprite");
|
||||
if (sprite) {
|
||||
if (sprite && sprite.texture === "dungeon") {
|
||||
sprite.index = 32;
|
||||
}
|
||||
}
|
||||
@@ -149,9 +149,9 @@ export class TriggerSystem extends System {
|
||||
if (trigger.oneShot) {
|
||||
trigger.triggered = true;
|
||||
|
||||
// Change sprite to triggered appearance (dungeon sprite 23)
|
||||
// Change sprite to triggered appearance if it's a dungeon sprite
|
||||
const sprite = world.getComponent(triggerId, "sprite");
|
||||
if (sprite) {
|
||||
if (sprite && sprite.texture === "dungeon") {
|
||||
sprite.index = 23; // Triggered/spent trap appearance
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ describe('CombatLogic', () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(TileType.EMPTY),
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
ecsWorld = new ECSWorld();
|
||||
// Shooter ID 1
|
||||
|
||||
@@ -18,7 +18,8 @@ describe("Fireable Weapons & Ammo System", () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
ecsWorld = new ECSWorld();
|
||||
accessor = new EntityAccessor(world, 1 as EntityId, ecsWorld);
|
||||
|
||||
@@ -19,7 +19,8 @@ describe('Movement Blocking Behavior', () => {
|
||||
width: 3,
|
||||
height: 3,
|
||||
tiles: new Array(9).fill(TileType.GRASS),
|
||||
exit: { x: 2, y: 2 }
|
||||
exit: { x: 2, y: 2 },
|
||||
trackPath: []
|
||||
};
|
||||
|
||||
// Blocking wall at (1, 0)
|
||||
|
||||
@@ -99,8 +99,25 @@ export function generateWorld(floor: number, runState: RunState): { world: World
|
||||
|
||||
const exit = { ...trackPath[trackPath.length - 1] };
|
||||
|
||||
// Place Switch at the end of the track
|
||||
Prefabs.trackSwitch(ecsWorld, exit.x, exit.y, cartId);
|
||||
// Place Switch adjacent to the end of the track
|
||||
let switchPos = { x: exit.x, y: exit.y };
|
||||
const neighbors = [
|
||||
{ x: exit.x + 1, y: exit.y },
|
||||
{ x: exit.x - 1, y: exit.y },
|
||||
{ x: exit.x, y: exit.y + 1 },
|
||||
{ x: exit.x, y: exit.y - 1 },
|
||||
];
|
||||
for (const n of neighbors) {
|
||||
if (n.x >= 1 && n.x < width - 1 && n.y >= 1 && n.y < height - 1) {
|
||||
const t = tiles[n.y * width + n.x];
|
||||
if (t === TileType.EMPTY || t === TileType.EMPTY_DECO || t === TileType.GRASS || t === TileType.TRACK) {
|
||||
switchPos = n;
|
||||
// Don't break if it's track, try to find a real empty spot first
|
||||
if (t !== TileType.TRACK) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Prefabs.trackSwitch(ecsWorld, switchPos.x, switchPos.y, cartId);
|
||||
|
||||
// Mark all track and room tiles as occupied for objects
|
||||
const occupiedPositions = new Set<string>();
|
||||
@@ -366,7 +383,7 @@ function placeEnemies(
|
||||
const room = rooms[roomIdx];
|
||||
|
||||
// Try to find an empty spot in the room
|
||||
for (let attempts = 0; attempts < 5; attempts++) {
|
||||
for (let attempts = 0; attempts < 20; attempts++) {
|
||||
|
||||
const ex = room.x + 1 + Math.floor(random() * (room.width - 2));
|
||||
const ey = room.y + 1 + Math.floor(random() * (room.height - 2));
|
||||
@@ -389,6 +406,9 @@ function placeEnemies(
|
||||
EntityBuilder.create(ecsWorld)
|
||||
.asEnemy(type)
|
||||
.withPosition(ex, ey)
|
||||
.withSprite(type, 0)
|
||||
.withName(type.charAt(0).toUpperCase() + type.slice(1))
|
||||
.withCombat()
|
||||
.withStats({
|
||||
maxHp: scaledHp + Math.floor(random() * 4),
|
||||
hp: scaledHp + Math.floor(random() * 4),
|
||||
@@ -396,7 +416,6 @@ function placeEnemies(
|
||||
defense: enemyDef.baseDefense,
|
||||
})
|
||||
.withEnergy(speed) // Configured speed
|
||||
// Note: Other stats like crit/evasion are defaults from EntityBuilder or BaseStats
|
||||
.build();
|
||||
|
||||
occupiedPositions.add(k);
|
||||
|
||||
@@ -43,7 +43,20 @@ export function isBlocked(w: World, x: number, y: number, accessor: EntityAccess
|
||||
|
||||
if (!accessor) return false;
|
||||
const actors = accessor.getActorsAt(x, y);
|
||||
return actors.some(a => a.category === "combatant");
|
||||
if (actors.some(a => a.category === "combatant")) return true;
|
||||
|
||||
// Check for interactable entities (switches, etc.) that should block movement
|
||||
if (accessor.context) {
|
||||
const ecs = accessor.context;
|
||||
const isInteractable = ecs.getEntitiesWith("position", "trigger").some(id => {
|
||||
const p = ecs.getComponent(id, "position");
|
||||
const t = ecs.getComponent(id, "trigger");
|
||||
return p?.x === x && p?.y === y && t?.onInteract;
|
||||
});
|
||||
if (isInteractable) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ export class DungeonRenderer {
|
||||
const spriteData = this.ecsWorld.getComponent(entId, "sprite");
|
||||
if (pos && spriteData) {
|
||||
try {
|
||||
const isStandalone = spriteData.texture === "mine_cart" || spriteData.texture === "ceramic_dragon_head";
|
||||
const isStandalone = spriteData.texture === "mine_cart" || spriteData.texture === "ceramic_dragon_head" || spriteData.texture === "track_switch";
|
||||
const sprite = this.scene.add.sprite(
|
||||
pos.x * TILE_SIZE + TILE_SIZE / 2,
|
||||
pos.y * TILE_SIZE + TILE_SIZE / 2,
|
||||
|
||||
@@ -152,6 +152,7 @@ describe('DungeonRenderer', () => {
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(0),
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
ecsWorld = new ECSWorld();
|
||||
accessor = new EntityAccessor(mockWorld, 1 as EntityId, ecsWorld);
|
||||
|
||||
@@ -13,7 +13,8 @@ describe('ItemManager', () => {
|
||||
width: 10,
|
||||
height: 10,
|
||||
tiles: new Array(100).fill(1), // Floor
|
||||
exit: { x: 9, y: 9 }
|
||||
exit: { x: 9, y: 9 },
|
||||
trackPath: []
|
||||
};
|
||||
|
||||
entityAccessor = {
|
||||
|
||||
Reference in New Issue
Block a user