feat: implement Camera system and component for improved viewport management and player tracking
All checks were successful
Build and Publish Docker Image / Build and Validate (pull_request) Successful in 14s
All checks were successful
Build and Publish Docker Image / Build and Validate (pull_request) Successful in 14s
This commit is contained in:
parent
62e58f77ae
commit
c859e20ffc
13 changed files with 596 additions and 72 deletions
50
src/systems/CameraSystem.ts
Normal file
50
src/systems/CameraSystem.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
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<Position>(ComponentType.POSITION);
|
||||
if (!playerPos) return;
|
||||
|
||||
entities.forEach((entity) => {
|
||||
const camera = entity.getComponent<Camera>(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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { System } from '../core/System.ts';
|
||||
import { SystemName } from '../core/Constants.ts';
|
||||
import { SystemName, ComponentType } 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;
|
||||
|
|
@ -155,10 +156,26 @@ export class InputSystem extends System {
|
|||
|
||||
/**
|
||||
* Get the current mouse position in world coordinates.
|
||||
* @returns The mouse coordinates
|
||||
* @returns The mouse coordinates in world space
|
||||
*/
|
||||
getMousePosition(): { x: number; y: number } {
|
||||
return { x: this.mouse.x, y: this.mouse.y };
|
||||
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<Camera>(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 };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -77,21 +77,42 @@ export class MovementSystem extends System {
|
|||
velocity.vy *= Math.pow(friction, deltaTime * 60);
|
||||
}
|
||||
|
||||
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 (tileMap) {
|
||||
const mapWidth = tileMap.cols * tileMap.tileSize;
|
||||
const mapHeight = tileMap.rows * tileMap.tileSize;
|
||||
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,14 +99,23 @@ export class ProjectileSystem extends System {
|
|||
}
|
||||
});
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ 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';
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ import type { SkillEffectSystem, SkillEffect } from './SkillEffectSystem.ts';
|
|||
*/
|
||||
export class RenderSystem extends System {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
private camera: Camera | null;
|
||||
|
||||
/**
|
||||
* @param engine - The game engine instance
|
||||
|
|
@ -36,6 +38,37 @@ 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<Camera>(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 };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -90,11 +123,14 @@ export class RenderSystem extends System {
|
|||
|
||||
ctx.fillStyle = Palette.DARKER_BLUE;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const x = Math.floor((i * 70 + Math.sin(i) * 30) % width);
|
||||
const y = Math.floor((i * 50 + Math.cos(i) * 40) % height);
|
||||
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 size = Math.floor(25 + (i % 4) * 15);
|
||||
|
||||
ctx.fillRect(x, y, size, size);
|
||||
if (screen.x + size > 0 && screen.x < width && screen.y + size > 0 && screen.y < height) {
|
||||
ctx.fillRect(screen.x, screen.y, size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,18 +141,35 @@ 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 = 0; r < tileMap.rows; r++) {
|
||||
for (let c = 0; c < tileMap.cols; c++) {
|
||||
for (let r = startRow; r < endRow; r++) {
|
||||
for (let c = startCol; c < endCol; c++) {
|
||||
if (tileMap.getTile(c, r) === 1) {
|
||||
ctx.fillRect(c * tileSize, r * tileSize, tileSize, tileSize);
|
||||
const worldX = c * tileSize;
|
||||
const worldY = r * tileSize;
|
||||
const screen = this.worldToScreen(worldX, worldY);
|
||||
|
||||
ctx.fillRect(screen.x, screen.y, tileSize, tileSize);
|
||||
|
||||
ctx.fillStyle = Palette.ROYAL_BLUE;
|
||||
ctx.fillRect(c * tileSize, r * tileSize, tileSize, 2);
|
||||
ctx.fillRect(screen.x, screen.y, tileSize, 2);
|
||||
ctx.fillStyle = Palette.DARK_BLUE;
|
||||
}
|
||||
}
|
||||
|
|
@ -138,8 +191,9 @@ export class RenderSystem extends System {
|
|||
|
||||
this.ctx.save();
|
||||
|
||||
const drawX = Math.floor(position.x);
|
||||
const drawY = Math.floor(position.y);
|
||||
const screen = this.worldToScreen(position.x, position.y);
|
||||
const drawX = Math.floor(screen.x);
|
||||
const drawY = Math.floor(screen.y);
|
||||
|
||||
let alpha = sprite.alpha;
|
||||
if (isDeadFade && health && health.isDead()) {
|
||||
|
|
@ -155,13 +209,21 @@ export class RenderSystem extends System {
|
|||
this.ctx.translate(drawX, drawY + (sprite.yOffset || 0));
|
||||
this.ctx.scale(sprite.scale, sprite.scale);
|
||||
|
||||
if (sprite.shape === EntityType.SLIME) {
|
||||
const stealth = entity.getComponent<Stealth>(ComponentType.STEALTH);
|
||||
let effectiveShape = sprite.shape;
|
||||
if (stealth && stealth.isStealthed && stealth.formAppearance) {
|
||||
effectiveShape = stealth.formAppearance;
|
||||
}
|
||||
|
||||
if (effectiveShape === EntityType.SLIME) {
|
||||
sprite.animationTime += deltaTime;
|
||||
sprite.morphAmount = Math.sin(sprite.animationTime * 3) * 0.2 + 0.8;
|
||||
}
|
||||
|
||||
let drawColor = sprite.color;
|
||||
if (sprite.shape === EntityType.SLIME) drawColor = Palette.CYAN;
|
||||
if (effectiveShape === EntityType.SLIME && (!stealth || !stealth.isStealthed)) {
|
||||
drawColor = Palette.CYAN;
|
||||
}
|
||||
|
||||
this.ctx.fillStyle = drawColor;
|
||||
|
||||
|
|
@ -171,7 +233,7 @@ export class RenderSystem extends System {
|
|||
sprite.animationState = isMoving ? AnimationState.WALK : AnimationState.IDLE;
|
||||
}
|
||||
|
||||
let spriteData = SpriteLibrary[sprite.shape as string];
|
||||
let spriteData = SpriteLibrary[effectiveShape as string];
|
||||
if (!spriteData) {
|
||||
spriteData = SpriteLibrary[EntityType.SLIME];
|
||||
}
|
||||
|
|
@ -245,7 +307,6 @@ export class RenderSystem extends System {
|
|||
this.ctx.restore();
|
||||
}
|
||||
|
||||
const stealth = entity.getComponent<Stealth>(ComponentType.STEALTH);
|
||||
if (stealth && stealth.isStealthed) {
|
||||
this.drawStealthIndicator(stealth, sprite);
|
||||
}
|
||||
|
|
@ -284,8 +345,9 @@ 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 x = Math.floor(p.x);
|
||||
const y = Math.floor(p.y);
|
||||
const screen = this.worldToScreen(p.x, p.y);
|
||||
const x = Math.floor(screen.x);
|
||||
const y = Math.floor(screen.y);
|
||||
const size = Math.floor(p.size);
|
||||
|
||||
ctx.fillRect(x - size / 2, y - size / 2, size, size);
|
||||
|
|
@ -337,7 +399,13 @@ export class RenderSystem extends System {
|
|||
|
||||
ctx.save();
|
||||
|
||||
if (sprite.shape === EntityType.SLIME) {
|
||||
const stealth = entity.getComponent<Stealth>(ComponentType.STEALTH);
|
||||
let effectiveShape = sprite.shape;
|
||||
if (stealth && stealth.isStealthed && stealth.formAppearance) {
|
||||
effectiveShape = stealth.formAppearance;
|
||||
}
|
||||
|
||||
if (effectiveShape === EntityType.SLIME) {
|
||||
ctx.strokeStyle = Palette.CYAN;
|
||||
ctx.lineWidth = 3;
|
||||
ctx.lineCap = 'round';
|
||||
|
|
@ -353,7 +421,7 @@ export class RenderSystem extends System {
|
|||
ctx.beginPath();
|
||||
ctx.arc(length, 0, 2, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
} else if (sprite.shape === EntityType.BEAST) {
|
||||
} else if (effectiveShape === EntityType.BEAST) {
|
||||
ctx.strokeStyle = Palette.WHITE;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.globalAlpha = alpha;
|
||||
|
|
@ -365,7 +433,7 @@ export class RenderSystem extends System {
|
|||
ctx.beginPath();
|
||||
ctx.arc(0, 0, radius, start - 0.5, start + 0.5);
|
||||
ctx.stroke();
|
||||
} else if (sprite.shape === EntityType.HUMANOID) {
|
||||
} else if (effectiveShape === EntityType.HUMANOID) {
|
||||
ctx.strokeStyle = `rgba(255, 255, 255, ${alpha})`;
|
||||
ctx.lineWidth = 4;
|
||||
|
||||
|
|
@ -481,7 +549,8 @@ export class RenderSystem extends System {
|
|||
const progress = Math.min(1.0, effect.time / effect.lifetime);
|
||||
const alpha = Math.max(0, 1.0 - progress);
|
||||
|
||||
ctx.translate(effect.x, effect.y);
|
||||
const screen = this.worldToScreen(effect.x, effect.y);
|
||||
ctx.translate(screen.x, screen.y);
|
||||
ctx.rotate(effect.angle);
|
||||
|
||||
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, effect.range);
|
||||
|
|
@ -531,10 +600,13 @@ 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(effect.startX, effect.startY, 10, 5, 0, 0, Math.PI * 2);
|
||||
ctx.ellipse(startScreen.x, startScreen.y, 10, 5, 0, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
const alpha = Math.max(0, 0.8 * (1.0 - progress));
|
||||
|
|
@ -542,15 +614,15 @@ export class RenderSystem extends System {
|
|||
ctx.lineWidth = 2;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(effect.startX, effect.startY);
|
||||
ctx.lineTo(currentX, currentY);
|
||||
ctx.moveTo(startScreen.x, startScreen.y);
|
||||
ctx.lineTo(currentScreen.x, currentScreen.y);
|
||||
ctx.stroke();
|
||||
|
||||
const ringSize = progress * 40;
|
||||
ctx.strokeStyle = `rgba(255, 255, 255, ${0.4 * (1 - progress)})`;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.arc(effect.startX, effect.startY, ringSize, 0, Math.PI * 2);
|
||||
ctx.arc(startScreen.x, startScreen.y, ringSize, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
|
|
@ -563,18 +635,20 @@ 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(effect.x, effect.y, size, 0, Math.PI * 2);
|
||||
ctx.arc(screen.x, screen.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 = effect.x + Math.cos(angle) * dist;
|
||||
const y = effect.y + Math.sin(angle) * dist;
|
||||
const x = screen.x + Math.cos(angle) * dist;
|
||||
const y = screen.y + Math.sin(angle) * dist;
|
||||
|
||||
ctx.fillStyle = `rgba(255, 150, 0, ${alpha})`;
|
||||
ctx.beginPath();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { System } from '../core/System.ts';
|
||||
import { SystemName, ComponentType } from '../core/Constants.ts';
|
||||
import { SystemName, ComponentType, EntityType } from '../core/Constants.ts';
|
||||
import { ColorSampler } from '../core/ColorSampler.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';
|
||||
|
||||
|
|
@ -45,13 +48,23 @@ export class StealthSystem extends System {
|
|||
stealth.stealthType = form;
|
||||
}
|
||||
|
||||
const sprite = entity.getComponent<Sprite>(ComponentType.SPRITE);
|
||||
const position = entity.getComponent<Position>(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 {
|
||||
stealth.enterStealth(stealth.stealthType);
|
||||
if (sprite) {
|
||||
stealth.enterStealth(stealth.stealthType, sprite.color);
|
||||
} else {
|
||||
stealth.enterStealth(stealth.stealthType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,24 +74,51 @@ export class StealthSystem extends System {
|
|||
|
||||
stealth.updateStealth(isMoving || false, isInCombat || false);
|
||||
|
||||
if (stealth.isStealthed) {
|
||||
if (stealth.isStealthed && sprite && position) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue