diff --git a/VERSION b/VERSION index 8f0916f..1d0ba9e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.0 +0.4.0 diff --git a/eslint.config.js b/eslint.config.js index 4165506..87f9419 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,12 +1,10 @@ import js from '@eslint/js'; import globals from 'globals'; import tseslint from 'typescript-eslint'; -import prettier from 'eslint-config-prettier'; export default [ js.configs.recommended, ...tseslint.configs.recommended, - prettier, { languageOptions: { ecmaVersion: 2022, @@ -29,6 +27,7 @@ export default [ ], '@typescript-eslint/no-explicit-any': 'warn', 'no-console': 'off', + indent: ['error', 2], '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-non-null-assertion': 'warn', }, diff --git a/package-lock.json b/package-lock.json index c895d29..c187459 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,12 +7,12 @@ "": { "name": "slime-genesis-poc", "version": "0.1.0", + "hasInstallScript": true, "devDependencies": { "@eslint/js": "^9.39.2", "@typescript-eslint/eslint-plugin": "^8.52.0", "@typescript-eslint/parser": "^8.52.0", "eslint": "^9.39.2", - "eslint-config-prettier": "^10.1.8", "globals": "^17.0.0", "husky": "^9.1.7", "lint-staged": "^16.2.7", @@ -1753,22 +1753,6 @@ } } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -3136,6 +3120,7 @@ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index 514ccca..4c6fc04 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@typescript-eslint/eslint-plugin": "^8.52.0", "@typescript-eslint/parser": "^8.52.0", "eslint": "^9.39.2", - "eslint-config-prettier": "^10.1.8", "globals": "^17.0.0", "husky": "^9.1.7", "lint-staged": "^16.2.7", diff --git a/portainer.yml b/portainer.yml index ace98e9..d2a3e11 100644 --- a/portainer.yml +++ b/portainer.yml @@ -2,7 +2,7 @@ name: slime services: app: - image: git.jusemon.com/jusemon/slime:0.5.0 + image: git.jusemon.com/jusemon/slime:0.4.0 restart: unless-stopped networks: diff --git a/src/components/Camera.ts b/src/components/Camera.ts deleted file mode 100644 index e8afa88..0000000 --- a/src/components/Camera.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Component } from '../core/Component.ts'; -import { ComponentType } from '../core/Constants.ts'; - -/** - * Component for camera/viewport management. - */ -export class Camera extends Component { - x: number; - y: number; - targetX: number; - targetY: number; - smoothness: number; - bounds: { - minX: number; - maxX: number; - minY: number; - maxY: number; - }; - viewportWidth: number; - viewportHeight: number; - - constructor(viewportWidth: number, viewportHeight: number, smoothness = 0.15) { - super(ComponentType.CAMERA); - this.x = 0; - this.y = 0; - this.targetX = 0; - this.targetY = 0; - this.smoothness = smoothness; - this.viewportWidth = viewportWidth; - this.viewportHeight = viewportHeight; - this.bounds = { - minX: 0, - maxX: 0, - minY: 0, - maxY: 0, - }; - } - - /** - * Set camera bounds based on map size. - * @param mapWidth - Total map width in pixels - * @param mapHeight - Total map height in pixels - */ - setBounds(mapWidth: number, mapHeight: number): void { - this.bounds.minX = this.viewportWidth / 2; - this.bounds.maxX = mapWidth - this.viewportWidth / 2; - this.bounds.minY = this.viewportHeight / 2; - this.bounds.maxY = mapHeight - this.viewportHeight / 2; - } - - /** - * Clamp camera position to bounds. - */ - clampToBounds(): void { - this.x = Math.max(this.bounds.minX, Math.min(this.bounds.maxX, this.x)); - this.y = Math.max(this.bounds.minY, Math.min(this.bounds.maxY, this.y)); - } -} diff --git a/src/components/Stealth.ts b/src/components/Stealth.ts index 490db21..40f6655 100644 --- a/src/components/Stealth.ts +++ b/src/components/Stealth.ts @@ -10,10 +10,6 @@ export class Stealth extends Component { isStealthed: boolean; stealthLevel: number; detectionRadius: number; - camouflageColor: string | null; - baseColor: string | null; - sizeMultiplier: number; - formAppearance: string | null; constructor() { super(ComponentType.STEALTH); @@ -22,29 +18,16 @@ export class Stealth extends Component { this.isStealthed = false; this.stealthLevel = 0; this.detectionRadius = 100; - this.camouflageColor = null; - this.baseColor = null; - this.sizeMultiplier = 1.0; - this.formAppearance = null; } /** * Enter stealth mode. * @param type - The type of stealth (e.g., 'slime', 'human') - * @param baseColor - Original entity color to restore later */ - enterStealth(type: string, baseColor?: string): void { + enterStealth(type: string): void { this.stealthType = type; this.isStealthed = true; this.visibility = 0.3; - if (baseColor) { - this.baseColor = baseColor; - } - if (type === 'slime') { - this.sizeMultiplier = 0.6; - } else { - this.sizeMultiplier = 1.0; - } } /** @@ -53,9 +36,6 @@ export class Stealth extends Component { exitStealth(): void { this.isStealthed = false; this.visibility = 1.0; - this.camouflageColor = null; - this.sizeMultiplier = 1.0; - this.formAppearance = null; } /** diff --git a/src/core/ColorSampler.ts b/src/core/ColorSampler.ts deleted file mode 100644 index 43081ab..0000000 --- a/src/core/ColorSampler.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { TileMap } from './TileMap.ts'; -import { Palette } from './Palette.ts'; - -/** - * Utility for sampling colors from the background and tile map. - */ -export class ColorSampler { - private static cache: Map = new Map(); - private static cacheFrame: number = 0; - - /** - * Sample the dominant color from a region around a position based on tile map and background. - * @param tileMap - The tile map to sample from - * @param x - Center X coordinate in world space - * @param y - Center Y coordinate in world space - * @param radius - Sampling radius in pixels - * @returns Dominant color as hex string (e.g., '#1a1a2e') - */ - static sampleDominantColor( - tileMap: TileMap | null, - x: number, - y: number, - radius: number - ): string { - const cacheKey = `${Math.floor(x / 20)}_${Math.floor(y / 20)}`; - const currentFrame = Math.floor(Date.now() / 200); - - if (currentFrame !== this.cacheFrame) { - this.cache.clear(); - this.cacheFrame = currentFrame; - } - - if (this.cache.has(cacheKey)) { - return this.cache.get(cacheKey) || Palette.VOID; - } - - if (!tileMap) { - return Palette.VOID; - } - - const tileSize = tileMap.tileSize; - const startCol = Math.max(0, Math.floor((x - radius) / tileSize)); - const endCol = Math.min(tileMap.cols, Math.ceil((x + radius) / tileSize)); - const startRow = Math.max(0, Math.floor((y - radius) / tileSize)); - const endRow = Math.min(tileMap.rows, Math.ceil((y + radius) / tileSize)); - - const colorCounts: Map = new Map(); - let totalTiles = 0; - - for (let r = startRow; r < endRow; r++) { - for (let c = startCol; c < endCol; c++) { - const tileType = tileMap.getTile(c, r); - let color: string; - - if (tileType === 1) { - color = Palette.DARK_BLUE; - } else { - const distFromCenter = Math.sqrt( - Math.pow(c * tileSize - x, 2) + Math.pow(r * tileSize - y, 2) - ); - if (distFromCenter < radius) { - const noise = Math.sin(c * 0.1 + r * 0.1) * 0.1; - if (Math.random() < 0.3 + noise) { - color = Palette.DARKER_BLUE; - } else { - color = Palette.VOID; - } - } else { - continue; - } - } - - colorCounts.set(color, (colorCounts.get(color) || 0) + 1); - totalTiles++; - } - } - - if (totalTiles === 0) { - return Palette.VOID; - } - - let dominantColor = Palette.VOID; - let maxCount = 0; - - colorCounts.forEach((count, color) => { - if (count > maxCount) { - maxCount = count; - dominantColor = color; - } - }); - - this.cache.set(cacheKey, dominantColor); - return dominantColor; - } - - /** - * Clear the color sampling cache. - */ - static clearCache(): void { - this.cache.clear(); - } -} diff --git a/src/core/Constants.ts b/src/core/Constants.ts index d64e312..c12a14f 100644 --- a/src/core/Constants.ts +++ b/src/core/Constants.ts @@ -32,7 +32,6 @@ export enum ComponentType { INVENTORY = 'Inventory', MUSIC = 'Music', SOUND_EFFECTS = 'SoundEffects', - CAMERA = 'Camera', } /** @@ -84,5 +83,4 @@ export enum SystemName { HEALTH_REGEN = 'HealthRegenerationSystem', MUSIC = 'MusicSystem', SOUND_EFFECTS = 'SoundEffectsSystem', - CAMERA = 'CameraSystem', } diff --git a/src/core/Engine.ts b/src/core/Engine.ts index 50b07f7..d795cb3 100644 --- a/src/core/Engine.ts +++ b/src/core/Engine.ts @@ -44,7 +44,7 @@ export class Engine { this.ctx.imageSmoothingEnabled = false; this.deltaTime = 0; - this.tileMap = LevelLoader.loadDesignedLevel(200, 150, 16); + this.tileMap = LevelLoader.loadSimpleLevel(20, 15, 16); } /** diff --git a/src/core/LevelLoader.ts b/src/core/LevelLoader.ts index fe11fef..1b211e3 100644 --- a/src/core/LevelLoader.ts +++ b/src/core/LevelLoader.ts @@ -27,99 +27,4 @@ export class LevelLoader { } return map; } - - /** - * Generates a larger designed map with rooms, corridors, and interesting layout. - * @param cols - Map width in tiles (default 200) - * @param rows - Map height in tiles (default 150) - * @param tileSize - Tile size in pixels (default 16) - * @returns The generated tile map - */ - static loadDesignedLevel(cols = 200, rows = 150, tileSize = 16): TileMap { - const map = new TileMap(cols, rows, tileSize); - - for (let r = 0; r < rows; r++) { - for (let c = 0; c < cols; c++) { - if (r === 0 || r === rows - 1 || c === 0 || c === cols - 1) { - map.setTile(c, r, 1); - } else { - map.setTile(c, r, 0); - } - } - } - - const roomCount = 15; - const rooms: Array<{ x: number; y: number; w: number; h: number }> = []; - - for (let i = 0; i < roomCount; i++) { - const roomW = 8 + Math.floor(Math.random() * 12); - const roomH = 8 + Math.floor(Math.random() * 12); - const roomX = 2 + Math.floor(Math.random() * (cols - roomW - 4)); - const roomY = 2 + Math.floor(Math.random() * (rows - roomH - 4)); - - let overlaps = false; - for (const existingRoom of rooms) { - if ( - roomX < existingRoom.x + existingRoom.w + 2 && - roomX + roomW + 2 > existingRoom.x && - roomY < existingRoom.y + existingRoom.h + 2 && - roomY + roomH + 2 > existingRoom.y - ) { - overlaps = true; - break; - } - } - - if (!overlaps) { - rooms.push({ x: roomX, y: roomY, w: roomW, h: roomH }); - - for (let ry = roomY; ry < roomY + roomH; ry++) { - for (let rx = roomX; rx < roomX + roomW; rx++) { - if (rx > 0 && rx < cols - 1 && ry > 0 && ry < rows - 1) { - map.setTile(rx, ry, 0); - } - } - } - } - } - - for (let i = 1; i < rooms.length; i++) { - const prevRoom = rooms[i - 1]; - const currRoom = rooms[i]; - - const startX = Math.floor(prevRoom.x + prevRoom.w / 2); - const startY = Math.floor(prevRoom.y + prevRoom.h / 2); - const endX = Math.floor(currRoom.x + currRoom.w / 2); - const endY = Math.floor(currRoom.y + currRoom.h / 2); - - let x = startX; - let y = startY; - - while (x !== endX || y !== endY) { - if (x > 0 && x < cols - 1 && y > 0 && y < rows - 1) { - map.setTile(x, y, 0); - } - - if (x < endX) x++; - else if (x > endX) x--; - - if (y < endY) y++; - else if (y > endY) y--; - } - - if (x > 0 && x < cols - 1 && y > 0 && y < rows - 1) { - map.setTile(x, y, 0); - } - } - - for (let r = 1; r < rows - 1; r++) { - for (let c = 1; c < cols - 1; c++) { - if (map.getTile(c, r) === 0 && Math.random() < 0.03) { - map.setTile(c, r, 1); - } - } - } - - return map; - } } diff --git a/src/main.ts b/src/main.ts index dc16e33..9971c67 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,7 +17,6 @@ import { UISystem } from './systems/UISystem.ts'; import { VFXSystem } from './systems/VFXSystem.ts'; import { MusicSystem } from './systems/MusicSystem.ts'; import { SoundEffectsSystem } from './systems/SoundEffectsSystem.ts'; -import { CameraSystem } from './systems/CameraSystem.ts'; import { Position } from './components/Position.ts'; import { Velocity } from './components/Velocity.ts'; @@ -35,7 +34,6 @@ import { SkillProgress } from './components/SkillProgress.ts'; import { Intent } from './components/Intent.ts'; import { Music } from './components/Music.ts'; import { SoundEffects } from './components/SoundEffects.ts'; -import { Camera } from './components/Camera.ts'; import { EntityType, ComponentType } from './core/Constants.ts'; import type { Entity } from './core/Entity.ts'; @@ -52,7 +50,6 @@ if (!canvas) { engine.addSystem(new InputSystem()); engine.addSystem(new MusicSystem()); engine.addSystem(new SoundEffectsSystem()); - engine.addSystem(new CameraSystem()); engine.addSystem(new PlayerControllerSystem()); engine.addSystem(new StealthSystem()); engine.addSystem(new AISystem()); @@ -69,9 +66,7 @@ if (!canvas) { engine.addSystem(new UISystem(engine)); const player = engine.createEntity(); - const startX = engine.tileMap ? (engine.tileMap.cols * engine.tileMap.tileSize) / 2 : 160; - const startY = engine.tileMap ? (engine.tileMap.rows * engine.tileMap.tileSize) / 2 : 120; - player.addComponent(new Position(startX, startY)); + player.addComponent(new Position(160, 120)); player.addComponent(new Velocity(0, 0)); player.addComponent(new Sprite('#00ff96', 14, 14, EntityType.SLIME)); player.addComponent(new Health(100)); @@ -88,17 +83,6 @@ if (!canvas) { player.addComponent(new SkillProgress()); player.addComponent(new Intent()); - const cameraEntity = engine.createEntity(); - const camera = new Camera(canvas.width, canvas.height, 0.15); - if (engine.tileMap) { - const mapWidth = engine.tileMap.cols * engine.tileMap.tileSize; - const mapHeight = engine.tileMap.rows * engine.tileMap.tileSize; - camera.setBounds(mapWidth, mapHeight); - camera.x = startX; - camera.y = startY; - } - cameraEntity.addComponent(camera); - function createCreature(engine: Engine, x: number, y: number, type: EntityType): Entity { const creature = engine.createEntity(); creature.addComponent(new Position(x, y)); @@ -145,36 +129,12 @@ if (!canvas) { return creature; } - const mapWidth = engine.tileMap ? engine.tileMap.cols * engine.tileMap.tileSize : 320; - const mapHeight = engine.tileMap ? engine.tileMap.rows * engine.tileMap.tileSize : 240; - - function spawnEnemyNearPlayer(): void { - const playerPos = player.getComponent(ComponentType.POSITION); - if (!playerPos) return; - - const spawnRadius = 150; - const minDistance = 80; - const maxAttempts = 10; - - for (let attempt = 0; attempt < maxAttempts; attempt++) { - const angle = Math.random() * Math.PI * 2; - const distance = minDistance + Math.random() * (spawnRadius - minDistance); - const x = playerPos.x + Math.cos(angle) * distance; - const y = playerPos.y + Math.sin(angle) * distance; - - if (x >= 50 && x <= mapWidth - 50 && y >= 50 && y <= mapHeight - 50) { - const types = [EntityType.HUMANOID, EntityType.BEAST, EntityType.ELEMENTAL]; - const type = types[Math.floor(Math.random() * types.length)]; - createCreature(engine, x, y, type); - return; - } - } - } - - const numberOfEnemies = 20; - - for (let i = 0; i < numberOfEnemies / 2; i++) { - spawnEnemyNearPlayer(); + for (let i = 0; i < 8; i++) { + const x = 20 + Math.random() * 280; + const y = 20 + Math.random() * 200; + const types = [EntityType.HUMANOID, EntityType.BEAST, EntityType.ELEMENTAL]; + const type = types[Math.floor(Math.random() * types.length)]; + createCreature(engine, x, y, type); } setInterval(() => { @@ -182,8 +142,12 @@ if (!canvas) { .getEntities() .filter((e) => e.hasComponent(ComponentType.AI) && e !== player); - if (existingCreatures.length < numberOfEnemies) { - spawnEnemyNearPlayer(); + if (existingCreatures.length < 10) { + const x = 20 + Math.random() * 280; + const y = 20 + Math.random() * 200; + const types = [EntityType.HUMANOID, EntityType.BEAST, EntityType.ELEMENTAL]; + const type = types[Math.floor(Math.random() * types.length)]; + createCreature(engine, x, y, type); } }, 5000); diff --git a/src/systems/CameraSystem.ts b/src/systems/CameraSystem.ts deleted file mode 100644 index 316a3e9..0000000 --- a/src/systems/CameraSystem.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { System } from '../core/System.ts'; -import { SystemName, ComponentType } from '../core/Constants.ts'; -import type { Entity } from '../core/Entity.ts'; -import type { Camera } from '../components/Camera.ts'; -import type { Position } from '../components/Position.ts'; -import type { PlayerControllerSystem } from './PlayerControllerSystem.ts'; - -/** - * System responsible for camera movement and following the player. - */ -export class CameraSystem extends System { - constructor() { - super(SystemName.CAMERA); - this.requiredComponents = [ComponentType.CAMERA]; - this.priority = 0; - } - - /** - * Update camera position to smoothly follow the player. - * @param deltaTime - Time elapsed since last frame in seconds - * @param entities - Filtered entities with Camera component - */ - process(deltaTime: number, entities: Entity[]): void { - const playerController = this.engine.systems.find( - (s) => s.name === SystemName.PLAYER_CONTROLLER - ) as PlayerControllerSystem | undefined; - const player = playerController ? playerController.getPlayerEntity() : null; - - if (!player) return; - - const playerPos = player.getComponent(ComponentType.POSITION); - if (!playerPos) return; - - entities.forEach((entity) => { - const camera = entity.getComponent(ComponentType.CAMERA); - if (!camera) return; - - camera.targetX = playerPos.x; - camera.targetY = playerPos.y; - - const dx = camera.targetX - camera.x; - const dy = camera.targetY - camera.y; - - camera.x += dx * camera.smoothness; - camera.y += dy * camera.smoothness; - - camera.clampToBounds(); - }); - } -} diff --git a/src/systems/InputSystem.ts b/src/systems/InputSystem.ts index e9f069c..9c046b1 100644 --- a/src/systems/InputSystem.ts +++ b/src/systems/InputSystem.ts @@ -1,8 +1,7 @@ import { System } from '../core/System.ts'; -import { SystemName, ComponentType } from '../core/Constants.ts'; +import { SystemName } from '../core/Constants.ts'; import type { Engine } from '../core/Engine.ts'; import type { Entity } from '../core/Entity.ts'; -import type { Camera } from '../components/Camera.ts'; interface MouseState { x: number; @@ -156,26 +155,10 @@ export class InputSystem extends System { /** * Get the current mouse position in world coordinates. - * @returns The mouse coordinates in world space + * @returns The mouse coordinates */ getMousePosition(): { x: number; y: number } { - if (!this.engine) { - return { x: this.mouse.x, y: this.mouse.y }; - } - - const cameraEntity = this.engine.entities.find((e) => e.hasComponent(ComponentType.CAMERA)); - if (!cameraEntity) { - return { x: this.mouse.x, y: this.mouse.y }; - } - - const camera = cameraEntity.getComponent(ComponentType.CAMERA); - if (!camera) { - return { x: this.mouse.x, y: this.mouse.y }; - } - - const worldX = this.mouse.x + camera.x - camera.viewportWidth / 2; - const worldY = this.mouse.y + camera.y - camera.viewportHeight / 2; - return { x: worldX, y: worldY }; + return { x: this.mouse.x, y: this.mouse.y }; } /** diff --git a/src/systems/MovementSystem.ts b/src/systems/MovementSystem.ts index 6008ee8..6d8b193 100644 --- a/src/systems/MovementSystem.ts +++ b/src/systems/MovementSystem.ts @@ -77,42 +77,21 @@ export class MovementSystem extends System { velocity.vy *= Math.pow(friction, deltaTime * 60); } - if (tileMap) { - const mapWidth = tileMap.cols * tileMap.tileSize; - const mapHeight = tileMap.rows * tileMap.tileSize; + const canvas = this.engine.canvas; + if (position.x < 0) { + position.x = 0; + velocity.vx = 0; + } else if (position.x > canvas.width) { + position.x = canvas.width; + velocity.vx = 0; + } - if (position.x < 0) { - position.x = 0; - velocity.vx = 0; - } else if (position.x > mapWidth) { - position.x = mapWidth; - velocity.vx = 0; - } - - if (position.y < 0) { - position.y = 0; - velocity.vy = 0; - } else if (position.y > mapHeight) { - position.y = mapHeight; - velocity.vy = 0; - } - } else { - const canvas = this.engine.canvas; - if (position.x < 0) { - position.x = 0; - velocity.vx = 0; - } else if (position.x > canvas.width) { - position.x = canvas.width; - velocity.vx = 0; - } - - if (position.y < 0) { - position.y = 0; - velocity.vy = 0; - } else if (position.y > canvas.height) { - position.y = canvas.height; - velocity.vy = 0; - } + if (position.y < 0) { + position.y = 0; + velocity.vy = 0; + } else if (position.y > canvas.height) { + position.y = canvas.height; + velocity.vy = 0; } }); } diff --git a/src/systems/ProjectileSystem.ts b/src/systems/ProjectileSystem.ts index 44aa4bf..c9d91f4 100644 --- a/src/systems/ProjectileSystem.ts +++ b/src/systems/ProjectileSystem.ts @@ -99,23 +99,14 @@ export class ProjectileSystem extends System { } }); - const tileMap = this.engine.tileMap; - if (tileMap) { - const mapWidth = tileMap.cols * tileMap.tileSize; - const mapHeight = tileMap.rows * tileMap.tileSize; - if (position.x < 0 || position.x > mapWidth || position.y < 0 || position.y > mapHeight) { - this.engine.removeEntity(entity); - } - } else { - const canvas = this.engine.canvas; - if ( - position.x < 0 || - position.x > canvas.width || - position.y < 0 || - position.y > canvas.height - ) { - this.engine.removeEntity(entity); - } + const canvas = this.engine.canvas; + if ( + position.x < 0 || + position.x > canvas.width || + position.y < 0 || + position.y > canvas.height + ) { + this.engine.removeEntity(entity); } }); } diff --git a/src/systems/RenderSystem.ts b/src/systems/RenderSystem.ts index e73820a..b989c22 100644 --- a/src/systems/RenderSystem.ts +++ b/src/systems/RenderSystem.ts @@ -18,7 +18,6 @@ import type { Combat } from '../components/Combat.ts'; import type { Stealth } from '../components/Stealth.ts'; import type { Evolution } from '../components/Evolution.ts'; import type { Absorbable } from '../components/Absorbable.ts'; -import type { Camera } from '../components/Camera.ts'; import type { VFXSystem } from './VFXSystem.ts'; import type { SkillEffectSystem, SkillEffect } from './SkillEffectSystem.ts'; @@ -27,7 +26,6 @@ import type { SkillEffectSystem, SkillEffect } from './SkillEffectSystem.ts'; */ export class RenderSystem extends System { ctx: CanvasRenderingContext2D; - private camera: Camera | null; /** * @param engine - The game engine instance @@ -38,37 +36,6 @@ export class RenderSystem extends System { this.priority = 100; this.engine = engine; this.ctx = engine.ctx; - this.camera = null; - } - - /** - * Get the active camera from the engine. - */ - private getCamera(): Camera | null { - if (this.camera) return this.camera; - - const cameraEntity = this.engine.entities.find((e) => e.hasComponent(ComponentType.CAMERA)); - if (cameraEntity) { - this.camera = cameraEntity.getComponent(ComponentType.CAMERA); - } - return this.camera; - } - - /** - * Transform world coordinates to screen coordinates using camera. - * @param worldX - World X coordinate - * @param worldY - World Y coordinate - * @returns Screen coordinates {x, y} - */ - private worldToScreen(worldX: number, worldY: number): { x: number; y: number } { - const camera = this.getCamera(); - if (!camera) { - return { x: worldX, y: worldY }; - } - - const screenX = worldX - camera.x + camera.viewportWidth / 2; - const screenY = worldY - camera.y + camera.viewportHeight / 2; - return { x: screenX, y: screenY }; } /** @@ -123,14 +90,11 @@ export class RenderSystem extends System { ctx.fillStyle = Palette.DARKER_BLUE; for (let i = 0; i < 20; i++) { - const worldX = (i * 70 + Math.sin(i) * 30) % 2000; - const worldY = (i * 50 + Math.cos(i) * 40) % 1500; - const screen = this.worldToScreen(worldX, worldY); + const x = Math.floor((i * 70 + Math.sin(i) * 30) % width); + const y = Math.floor((i * 50 + Math.cos(i) * 40) % height); const size = Math.floor(25 + (i % 4) * 15); - if (screen.x + size > 0 && screen.x < width && screen.y + size > 0 && screen.y < height) { - ctx.fillRect(screen.x, screen.y, size, size); - } + ctx.fillRect(x, y, size, size); } } @@ -141,35 +105,18 @@ export class RenderSystem extends System { const tileMap = this.engine.tileMap; if (!tileMap) return; - const camera = this.getCamera(); const ctx = this.ctx; const tileSize = tileMap.tileSize; - const viewportLeft = camera ? camera.x - camera.viewportWidth / 2 : 0; - const viewportRight = camera ? camera.x + camera.viewportWidth / 2 : this.engine.canvas.width; - const viewportTop = camera ? camera.y - camera.viewportHeight / 2 : 0; - const viewportBottom = camera - ? camera.y + camera.viewportHeight / 2 - : this.engine.canvas.height; - - const startCol = Math.max(0, Math.floor(viewportLeft / tileSize) - 1); - const endCol = Math.min(tileMap.cols, Math.ceil(viewportRight / tileSize) + 1); - const startRow = Math.max(0, Math.floor(viewportTop / tileSize) - 1); - const endRow = Math.min(tileMap.rows, Math.ceil(viewportBottom / tileSize) + 1); - ctx.fillStyle = Palette.DARK_BLUE; - for (let r = startRow; r < endRow; r++) { - for (let c = startCol; c < endCol; c++) { + for (let r = 0; r < tileMap.rows; r++) { + for (let c = 0; c < tileMap.cols; c++) { if (tileMap.getTile(c, r) === 1) { - const worldX = c * tileSize; - const worldY = r * tileSize; - const screen = this.worldToScreen(worldX, worldY); - - ctx.fillRect(screen.x, screen.y, tileSize, tileSize); + ctx.fillRect(c * tileSize, r * tileSize, tileSize, tileSize); ctx.fillStyle = Palette.ROYAL_BLUE; - ctx.fillRect(screen.x, screen.y, tileSize, 2); + ctx.fillRect(c * tileSize, r * tileSize, tileSize, 2); ctx.fillStyle = Palette.DARK_BLUE; } } @@ -191,9 +138,8 @@ export class RenderSystem extends System { this.ctx.save(); - const screen = this.worldToScreen(position.x, position.y); - const drawX = Math.floor(screen.x); - const drawY = Math.floor(screen.y); + const drawX = Math.floor(position.x); + const drawY = Math.floor(position.y); let alpha = sprite.alpha; if (isDeadFade && health && health.isDead()) { @@ -209,21 +155,13 @@ export class RenderSystem extends System { this.ctx.translate(drawX, drawY + (sprite.yOffset || 0)); this.ctx.scale(sprite.scale, sprite.scale); - const stealth = entity.getComponent(ComponentType.STEALTH); - let effectiveShape = sprite.shape; - if (stealth && stealth.isStealthed && stealth.formAppearance) { - effectiveShape = stealth.formAppearance; - } - - if (effectiveShape === EntityType.SLIME) { + if (sprite.shape === EntityType.SLIME) { sprite.animationTime += deltaTime; sprite.morphAmount = Math.sin(sprite.animationTime * 3) * 0.2 + 0.8; } let drawColor = sprite.color; - if (effectiveShape === EntityType.SLIME && (!stealth || !stealth.isStealthed)) { - drawColor = Palette.CYAN; - } + if (sprite.shape === EntityType.SLIME) drawColor = Palette.CYAN; this.ctx.fillStyle = drawColor; @@ -233,7 +171,7 @@ export class RenderSystem extends System { sprite.animationState = isMoving ? AnimationState.WALK : AnimationState.IDLE; } - let spriteData = SpriteLibrary[effectiveShape as string]; + let spriteData = SpriteLibrary[sprite.shape as string]; if (!spriteData) { spriteData = SpriteLibrary[EntityType.SLIME]; } @@ -307,6 +245,7 @@ export class RenderSystem extends System { this.ctx.restore(); } + const stealth = entity.getComponent(ComponentType.STEALTH); if (stealth && stealth.isStealthed) { this.drawStealthIndicator(stealth, sprite); } @@ -345,9 +284,8 @@ export class RenderSystem extends System { ctx.fillStyle = p.color; ctx.globalAlpha = p.type === VFXType.IMPACT ? Math.min(1, p.lifetime / 0.3) : 0.8; - const screen = this.worldToScreen(p.x, p.y); - const x = Math.floor(screen.x); - const y = Math.floor(screen.y); + const x = Math.floor(p.x); + const y = Math.floor(p.y); const size = Math.floor(p.size); ctx.fillRect(x - size / 2, y - size / 2, size, size); @@ -399,13 +337,7 @@ export class RenderSystem extends System { ctx.save(); - const stealth = entity.getComponent(ComponentType.STEALTH); - let effectiveShape = sprite.shape; - if (stealth && stealth.isStealthed && stealth.formAppearance) { - effectiveShape = stealth.formAppearance; - } - - if (effectiveShape === EntityType.SLIME) { + if (sprite.shape === EntityType.SLIME) { ctx.strokeStyle = Palette.CYAN; ctx.lineWidth = 3; ctx.lineCap = 'round'; @@ -421,7 +353,7 @@ export class RenderSystem extends System { ctx.beginPath(); ctx.arc(length, 0, 2, 0, Math.PI * 2); ctx.fill(); - } else if (effectiveShape === EntityType.BEAST) { + } else if (sprite.shape === EntityType.BEAST) { ctx.strokeStyle = Palette.WHITE; ctx.lineWidth = 2; ctx.globalAlpha = alpha; @@ -433,7 +365,7 @@ export class RenderSystem extends System { ctx.beginPath(); ctx.arc(0, 0, radius, start - 0.5, start + 0.5); ctx.stroke(); - } else if (effectiveShape === EntityType.HUMANOID) { + } else if (sprite.shape === EntityType.HUMANOID) { ctx.strokeStyle = `rgba(255, 255, 255, ${alpha})`; ctx.lineWidth = 4; @@ -549,8 +481,7 @@ export class RenderSystem extends System { const progress = Math.min(1.0, effect.time / effect.lifetime); const alpha = Math.max(0, 1.0 - progress); - const screen = this.worldToScreen(effect.x, effect.y); - ctx.translate(screen.x, screen.y); + ctx.translate(effect.x, effect.y); ctx.rotate(effect.angle); const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, effect.range); @@ -600,13 +531,10 @@ export class RenderSystem extends System { currentY = effect.startY + Math.sin(effect.angle) * (effect.speed || 400) * effect.time; } - const startScreen = this.worldToScreen(effect.startX, effect.startY); - const currentScreen = this.worldToScreen(currentX, currentY); - ctx.globalAlpha = Math.max(0, 0.3 * (1 - progress)); ctx.fillStyle = Palette.VOID; ctx.beginPath(); - ctx.ellipse(startScreen.x, startScreen.y, 10, 5, 0, 0, Math.PI * 2); + ctx.ellipse(effect.startX, effect.startY, 10, 5, 0, 0, Math.PI * 2); ctx.fill(); const alpha = Math.max(0, 0.8 * (1.0 - progress)); @@ -614,15 +542,15 @@ export class RenderSystem extends System { ctx.lineWidth = 2; ctx.lineCap = 'round'; ctx.beginPath(); - ctx.moveTo(startScreen.x, startScreen.y); - ctx.lineTo(currentScreen.x, currentScreen.y); + ctx.moveTo(effect.startX, effect.startY); + ctx.lineTo(currentX, currentY); ctx.stroke(); const ringSize = progress * 40; ctx.strokeStyle = `rgba(255, 255, 255, ${0.4 * (1 - progress)})`; ctx.lineWidth = 1; ctx.beginPath(); - ctx.arc(startScreen.x, startScreen.y, ringSize, 0, Math.PI * 2); + ctx.arc(effect.startX, effect.startY, ringSize, 0, Math.PI * 2); ctx.stroke(); } @@ -635,20 +563,18 @@ export class RenderSystem extends System { const alpha = Math.max(0, 1.0 - progress); const size = Math.max(0, 30 * (1 - progress)); - const screen = this.worldToScreen(effect.x, effect.y); - if (size > 0 && alpha > 0) { ctx.strokeStyle = `rgba(255, 200, 0, ${alpha})`; ctx.lineWidth = 3; ctx.beginPath(); - ctx.arc(screen.x, screen.y, size, 0, Math.PI * 2); + ctx.arc(effect.x, effect.y, size, 0, Math.PI * 2); ctx.stroke(); for (let i = 0; i < 8; i++) { const angle = (i / 8) * Math.PI * 2; const dist = size * 0.7; - const x = screen.x + Math.cos(angle) * dist; - const y = screen.y + Math.sin(angle) * dist; + const x = effect.x + Math.cos(angle) * dist; + const y = effect.y + Math.sin(angle) * dist; ctx.fillStyle = `rgba(255, 150, 0, ${alpha})`; ctx.beginPath(); diff --git a/src/systems/StealthSystem.ts b/src/systems/StealthSystem.ts index 51a5e0b..8d91067 100644 --- a/src/systems/StealthSystem.ts +++ b/src/systems/StealthSystem.ts @@ -1,13 +1,10 @@ import { System } from '../core/System.ts'; -import { SystemName, ComponentType, EntityType } from '../core/Constants.ts'; -import { ColorSampler } from '../core/ColorSampler.ts'; +import { SystemName, ComponentType } from '../core/Constants.ts'; import type { Entity } from '../core/Entity.ts'; import type { Stealth } from '../components/Stealth.ts'; import type { Velocity } from '../components/Velocity.ts'; import type { Combat } from '../components/Combat.ts'; import type { Evolution } from '../components/Evolution.ts'; -import type { Sprite } from '../components/Sprite.ts'; -import type { Position } from '../components/Position.ts'; import type { InputSystem } from './InputSystem.ts'; import type { PlayerControllerSystem } from './PlayerControllerSystem.ts'; @@ -48,23 +45,13 @@ export class StealthSystem extends System { stealth.stealthType = form; } - const sprite = entity.getComponent(ComponentType.SPRITE); - const position = entity.getComponent(ComponentType.POSITION); - if (entity === player && inputSystem) { const shiftPress = inputSystem.isKeyJustPressed('shift'); if (shiftPress) { if (stealth.isStealthed) { stealth.exitStealth(); - if (sprite && stealth.baseColor) { - sprite.color = stealth.baseColor; - } } else { - if (sprite) { - stealth.enterStealth(stealth.stealthType, sprite.color); - } else { - stealth.enterStealth(stealth.stealthType); - } + stealth.enterStealth(stealth.stealthType); } } } @@ -74,51 +61,24 @@ export class StealthSystem extends System { stealth.updateStealth(isMoving || false, isInCombat || false); - if (stealth.isStealthed && sprite && position) { + if (stealth.isStealthed) { switch (stealth.stealthType) { - case 'slime': { + case 'slime': if (!isMoving) { stealth.visibility = Math.max(0.05, stealth.visibility - deltaTime * 0.2); } - - const sampledColor = ColorSampler.sampleDominantColor( - this.engine.tileMap, - position.x, - position.y, - 30 - ); - - if (stealth.camouflageColor !== sampledColor) { - stealth.camouflageColor = sampledColor; - sprite.color = sampledColor; - } - - sprite.scale = stealth.sizeMultiplier; break; - } - case 'beast': { + case 'beast': if (isMoving && velocity) { const speed = Math.sqrt(velocity.vx * velocity.vx + velocity.vy * velocity.vy); if (speed < 50) { stealth.visibility = Math.max(0.1, stealth.visibility - deltaTime * 0.1); } } - - stealth.formAppearance = EntityType.BEAST; - sprite.scale = 1.0; break; - } - case 'human': { + case 'human': stealth.visibility = Math.max(0.2, stealth.visibility - deltaTime * 0.05); - stealth.formAppearance = EntityType.HUMANOID; - sprite.scale = 1.0; break; - } - } - } else if (!stealth.isStealthed && sprite) { - sprite.scale = 1.0; - if (stealth.baseColor) { - sprite.color = stealth.baseColor; } } });