refactor: centralize system names, component types, entity types, and animation states into a new Constants module.
This commit is contained in:
parent
e9db84abd1
commit
3db2bb9160
20 changed files with 385 additions and 313 deletions
|
|
@ -1,24 +1,25 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { GameConfig } from '../GameConfig.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class AISystem extends System {
|
||||
constructor() {
|
||||
super('AISystem');
|
||||
this.requiredComponents = ['Position', 'Velocity', 'AI'];
|
||||
super(SystemName.AI);
|
||||
this.requiredComponents = [ComponentType.POSITION, ComponentType.VELOCITY, ComponentType.AI];
|
||||
this.priority = 15;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
|
||||
const playerController = this.engine.systems.find(s => s.name === SystemName.PLAYER_CONTROLLER);
|
||||
const player = playerController ? playerController.getPlayerEntity() : null;
|
||||
const playerPos = player?.getComponent('Position');
|
||||
const playerPos = player?.getComponent(ComponentType.POSITION);
|
||||
const config = GameConfig.AI;
|
||||
|
||||
entities.forEach(entity => {
|
||||
const health = entity.getComponent('Health');
|
||||
const ai = entity.getComponent('AI');
|
||||
const position = entity.getComponent('Position');
|
||||
const velocity = entity.getComponent('Velocity');
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
const ai = entity.getComponent(ComponentType.AI);
|
||||
const position = entity.getComponent(ComponentType.POSITION);
|
||||
const velocity = entity.getComponent(ComponentType.VELOCITY);
|
||||
|
||||
if (!ai || !position || !velocity) return;
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ export class AISystem extends System {
|
|||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Update awareness based on distance and player stealth
|
||||
const playerStealth = player?.getComponent('Stealth');
|
||||
const playerStealth = player?.getComponent(ComponentType.STEALTH);
|
||||
const playerVisibility = playerStealth ? playerStealth.visibility : 1.0;
|
||||
|
||||
if (distance < ai.alertRadius) {
|
||||
|
|
@ -50,10 +51,10 @@ export class AISystem extends System {
|
|||
}
|
||||
|
||||
// Biological Reputation Logic
|
||||
const playerEvolution = player?.getComponent('Evolution');
|
||||
const playerEvolution = player?.getComponent(ComponentType.EVOLUTION);
|
||||
const playerForm = playerEvolution ? playerEvolution.getDominantForm() : 'slime';
|
||||
const entityType = entity.getComponent('Sprite')?.color === '#ffaa00' ? 'beast' :
|
||||
entity.getComponent('Sprite')?.color === '#ff5555' ? 'humanoid' : 'other';
|
||||
const entityType = entity.getComponent(ComponentType.SPRITE)?.color === '#ffaa00' ? 'beast' :
|
||||
entity.getComponent(ComponentType.SPRITE)?.color === '#ff5555' ? 'humanoid' : 'other';
|
||||
|
||||
// Check if player is "one of us" or "too scary"
|
||||
let isPassive = false;
|
||||
|
|
@ -64,8 +65,8 @@ export class AISystem extends System {
|
|||
if (ai.awareness < config.passiveAwarenessThreshold) isPassive = true;
|
||||
} else if (entityType === 'beast' && playerForm === 'beast') {
|
||||
// Beasts might flee from a dominant beast player
|
||||
const playerStats = player?.getComponent('Stats');
|
||||
const entityStats = entity.getComponent('Stats');
|
||||
const playerStats = player?.getComponent(ComponentType.STATS);
|
||||
const entityStats = entity.getComponent(ComponentType.STATS);
|
||||
if (playerStats && entityStats && playerStats.level > entityStats.level) {
|
||||
shouldFlee = true;
|
||||
}
|
||||
|
|
@ -85,7 +86,7 @@ export class AISystem extends System {
|
|||
} else if (ai.awareness > config.detectionAwarenessThreshold && distance < ai.chaseRadius) {
|
||||
if (ai.behaviorType !== 'flee') {
|
||||
// Check if in attack range - if so, use combat behavior
|
||||
const combat = entity.getComponent('Combat');
|
||||
const combat = entity.getComponent(ComponentType.COMBAT);
|
||||
if (combat && distance <= combat.attackRange) {
|
||||
ai.setBehavior('combat');
|
||||
ai.state = 'combat';
|
||||
|
|
@ -103,7 +104,7 @@ export class AISystem extends System {
|
|||
}
|
||||
} else if (ai.behaviorType === 'chase') {
|
||||
// Update from chase to combat if in range
|
||||
const combat = entity.getComponent('Combat');
|
||||
const combat = entity.getComponent(ComponentType.COMBAT);
|
||||
if (combat && distance <= combat.attackRange) {
|
||||
ai.setBehavior('combat');
|
||||
ai.state = 'combat';
|
||||
|
|
@ -151,7 +152,7 @@ export class AISystem extends System {
|
|||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Check if we should switch to combat
|
||||
const combat = entity.getComponent('Combat');
|
||||
const combat = entity.getComponent(ComponentType.COMBAT);
|
||||
if (combat && distance <= combat.attackRange) {
|
||||
ai.setBehavior('combat');
|
||||
ai.state = 'combat';
|
||||
|
|
@ -193,7 +194,7 @@ export class AISystem extends System {
|
|||
const dy = targetPos.y - position.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
const combat = entity.getComponent('Combat');
|
||||
const combat = entity.getComponent(ComponentType.COMBAT);
|
||||
if (combat && distance > combat.attackRange) {
|
||||
// Move closer if out of range
|
||||
const speed = ai.wanderSpeed;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { GameConfig } from '../GameConfig.js';
|
||||
import { Events } from '../core/EventBus.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class AbsorptionSystem extends System {
|
||||
constructor() {
|
||||
super('AbsorptionSystem');
|
||||
this.requiredComponents = ['Position', 'Absorbable'];
|
||||
super(SystemName.ABSORPTION);
|
||||
this.requiredComponents = [ComponentType.POSITION, ComponentType.ABSORBABLE];
|
||||
this.priority = 25;
|
||||
}
|
||||
|
||||
process(_deltaTime, _entities) {
|
||||
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
|
||||
const playerController = this.engine.systems.find(s => s.name === SystemName.PLAYER_CONTROLLER);
|
||||
const player = playerController ? playerController.getPlayerEntity() : null;
|
||||
|
||||
if (!player) return;
|
||||
|
||||
const playerPos = player.getComponent('Position');
|
||||
const playerEvolution = player.getComponent('Evolution');
|
||||
const playerSkills = player.getComponent('Skills');
|
||||
const playerStats = player.getComponent('Stats');
|
||||
const skillProgress = player.getComponent('SkillProgress');
|
||||
const playerPos = player.getComponent(ComponentType.POSITION);
|
||||
const playerEvolution = player.getComponent(ComponentType.EVOLUTION);
|
||||
const playerSkills = player.getComponent(ComponentType.SKILLS);
|
||||
const playerStats = player.getComponent(ComponentType.STATS);
|
||||
const skillProgress = player.getComponent(ComponentType.SKILL_PROGRESS);
|
||||
|
||||
if (!playerPos || !playerEvolution) return;
|
||||
|
||||
|
|
@ -33,19 +34,19 @@ export class AbsorptionSystem extends System {
|
|||
if (entity === player) return;
|
||||
// Allow inactive entities if they're dead and absorbable
|
||||
if (!entity.active) {
|
||||
const health = entity.getComponent('Health');
|
||||
const absorbable = entity.getComponent('Absorbable');
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
const absorbable = entity.getComponent(ComponentType.ABSORBABLE);
|
||||
// Only process inactive entities if they're dead and not yet absorbed
|
||||
if (!health || !health.isDead() || !absorbable || absorbable.absorbed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!entity.hasComponent('Absorbable')) return;
|
||||
if (!entity.hasComponent('Health')) return;
|
||||
if (!entity.hasComponent(ComponentType.ABSORBABLE)) return;
|
||||
if (!entity.hasComponent(ComponentType.HEALTH)) return;
|
||||
|
||||
const absorbable = entity.getComponent('Absorbable');
|
||||
const health = entity.getComponent('Health');
|
||||
const entityPos = entity.getComponent('Position');
|
||||
const absorbable = entity.getComponent(ComponentType.ABSORBABLE);
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
const entityPos = entity.getComponent(ComponentType.POSITION);
|
||||
|
||||
if (!entityPos) return;
|
||||
|
||||
|
|
@ -68,8 +69,8 @@ export class AbsorptionSystem extends System {
|
|||
if (absorbable.absorbed) return;
|
||||
|
||||
absorbable.absorbed = true;
|
||||
const entityPos = entity.getComponent('Position');
|
||||
const health = player.getComponent('Health');
|
||||
const entityPos = entity.getComponent(ComponentType.POSITION);
|
||||
const health = player.getComponent(ComponentType.HEALTH);
|
||||
const config = GameConfig.Absorption;
|
||||
|
||||
// Add evolution points
|
||||
|
|
@ -111,7 +112,7 @@ export class AbsorptionSystem extends System {
|
|||
|
||||
// Visual effect
|
||||
if (entityPos) {
|
||||
const vfxSystem = this.engine.systems.find(s => s.name === 'VFXSystem');
|
||||
const vfxSystem = this.engine.systems.find(s => s.name === SystemName.VFX);
|
||||
if (vfxSystem) {
|
||||
vfxSystem.createAbsorption(entityPos.x, entityPos.y);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,36 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { GameConfig } from '../GameConfig.js';
|
||||
import { Events } from '../core/EventBus.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class CombatSystem extends System {
|
||||
constructor() {
|
||||
super('CombatSystem');
|
||||
this.requiredComponents = ['Position', 'Combat', 'Health'];
|
||||
super(SystemName.COMBAT);
|
||||
this.requiredComponents = [ComponentType.POSITION, ComponentType.COMBAT, ComponentType.HEALTH];
|
||||
this.priority = 20;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
// Update combat cooldowns
|
||||
entities.forEach(entity => {
|
||||
const combat = entity.getComponent('Combat');
|
||||
const combat = entity.getComponent(ComponentType.COMBAT);
|
||||
if (combat) {
|
||||
combat.update(deltaTime);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle player attacks
|
||||
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
|
||||
const playerController = this.engine.systems.find(s => s.name === SystemName.PLAYER_CONTROLLER);
|
||||
const player = playerController ? playerController.getPlayerEntity() : null;
|
||||
|
||||
if (player && player.hasComponent('Combat')) {
|
||||
if (player && player.hasComponent(ComponentType.COMBAT)) {
|
||||
this.handlePlayerCombat(player, deltaTime);
|
||||
}
|
||||
|
||||
// Handle creature attacks
|
||||
const creatures = entities.filter(e =>
|
||||
e.hasComponent('AI') &&
|
||||
e.hasComponent('Combat') &&
|
||||
e.hasComponent(ComponentType.AI) &&
|
||||
e.hasComponent(ComponentType.COMBAT) &&
|
||||
e !== player
|
||||
);
|
||||
|
||||
|
|
@ -42,9 +43,9 @@ export class CombatSystem extends System {
|
|||
}
|
||||
|
||||
handlePlayerCombat(player, _deltaTime) {
|
||||
const inputSystem = this.engine.systems.find(s => s.name === 'InputSystem');
|
||||
const combat = player.getComponent('Combat');
|
||||
const position = player.getComponent('Position');
|
||||
const inputSystem = this.engine.systems.find(s => s.name === SystemName.INPUT);
|
||||
const combat = player.getComponent(ComponentType.COMBAT);
|
||||
const position = player.getComponent(ComponentType.POSITION);
|
||||
|
||||
if (!inputSystem || !combat || !position) return;
|
||||
|
||||
|
|
@ -72,10 +73,10 @@ export class CombatSystem extends System {
|
|||
}
|
||||
|
||||
handleCreatureCombat(creature, player, _deltaTime) {
|
||||
const ai = creature.getComponent('AI');
|
||||
const combat = creature.getComponent('Combat');
|
||||
const position = creature.getComponent('Position');
|
||||
const playerPos = player?.getComponent('Position');
|
||||
const ai = creature.getComponent(ComponentType.AI);
|
||||
const combat = creature.getComponent(ComponentType.COMBAT);
|
||||
const position = creature.getComponent(ComponentType.POSITION);
|
||||
const playerPos = player?.getComponent(ComponentType.POSITION);
|
||||
|
||||
if (!ai || !combat || !position) return;
|
||||
|
||||
|
|
@ -98,16 +99,12 @@ export class CombatSystem extends System {
|
|||
|
||||
performAttack(attacker, combat, attackerPos) {
|
||||
const entities = this.engine.getEntities();
|
||||
const stats = attacker.getComponent('Stats');
|
||||
const _baseDamage = stats ?
|
||||
(combat.attackDamage + stats.strength * 0.5) :
|
||||
combat.attackDamage;
|
||||
|
||||
entities.forEach(target => {
|
||||
if (target === attacker) return;
|
||||
if (!target.hasComponent('Health')) return;
|
||||
if (!target.hasComponent(ComponentType.HEALTH)) return;
|
||||
|
||||
const targetPos = target.getComponent('Position');
|
||||
const targetPos = target.getComponent(ComponentType.POSITION);
|
||||
if (!targetPos) return;
|
||||
|
||||
// Check if in attack range and angle
|
||||
|
|
@ -124,14 +121,14 @@ export class CombatSystem extends System {
|
|||
// Attack arc
|
||||
const attackArc = GameConfig.Combat.defaultAttackArc;
|
||||
if (minDiff < attackArc) {
|
||||
const health = target.getComponent('Health');
|
||||
const health = target.getComponent(ComponentType.HEALTH);
|
||||
const config = GameConfig.Combat;
|
||||
const stats = attacker.getComponent('Stats');
|
||||
const stats = attacker.getComponent(ComponentType.STATS);
|
||||
const baseDamage = stats ? (combat.attackDamage + stats.strength * 0.5) : combat.attackDamage;
|
||||
|
||||
// Defense bonus from Hardened Shell
|
||||
let finalDamage = baseDamage;
|
||||
const targetEvolution = target.getComponent('Evolution');
|
||||
const targetEvolution = target.getComponent(ComponentType.EVOLUTION);
|
||||
|
||||
if (targetEvolution && targetEvolution.mutationEffects.hardenedShell) {
|
||||
finalDamage *= config.hardenedShellReduction;
|
||||
|
|
@ -149,7 +146,7 @@ export class CombatSystem extends System {
|
|||
|
||||
// Damage reflection from Electric Skin
|
||||
if (targetEvolution && targetEvolution.mutationEffects.electricSkin) {
|
||||
const attackerHealth = attacker.getComponent('Health');
|
||||
const attackerHealth = attacker.getComponent(ComponentType.HEALTH);
|
||||
if (attackerHealth) {
|
||||
const reflectedDamage = actualDamage * config.damageReflectionPercent;
|
||||
attackerHealth.takeDamage(reflectedDamage);
|
||||
|
|
@ -169,7 +166,7 @@ export class CombatSystem extends System {
|
|||
}
|
||||
|
||||
// Apply knockback
|
||||
const velocity = target.getComponent('Velocity');
|
||||
const velocity = target.getComponent(ComponentType.VELOCITY);
|
||||
if (velocity) {
|
||||
const knockbackPower = config.knockbackPower;
|
||||
const kx = Math.cos(angle) * knockbackPower;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
/**
|
||||
* System to handle entity death - removes dead entities immediately
|
||||
*/
|
||||
export class DeathSystem extends System {
|
||||
constructor() {
|
||||
super('DeathSystem');
|
||||
this.requiredComponents = ['Health'];
|
||||
super(SystemName.DEATH);
|
||||
this.requiredComponents = [ComponentType.HEALTH];
|
||||
this.priority = 50; // Run after absorption (absorption is priority 25)
|
||||
}
|
||||
|
||||
|
|
@ -19,15 +20,15 @@ export class DeathSystem extends System {
|
|||
|
||||
process(deltaTime, allEntities) {
|
||||
allEntities.forEach(entity => {
|
||||
const health = entity.getComponent('Health');
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
if (!health) return;
|
||||
|
||||
// Check if entity is dead
|
||||
if (health.isDead()) {
|
||||
// Check if player died
|
||||
const evolution = entity.getComponent('Evolution');
|
||||
const evolution = entity.getComponent(ComponentType.EVOLUTION);
|
||||
if (evolution) {
|
||||
const menuSystem = this.engine.systems.find(s => s.name === 'MenuSystem');
|
||||
const menuSystem = this.engine.systems.find(s => s.name === SystemName.MENU);
|
||||
if (menuSystem) {
|
||||
menuSystem.showGameOver();
|
||||
}
|
||||
|
|
@ -45,7 +46,7 @@ export class DeathSystem extends System {
|
|||
}
|
||||
|
||||
// Check if it's absorbable - if so, give a short window for absorption
|
||||
const absorbable = entity.getComponent('Absorbable');
|
||||
const absorbable = entity.getComponent(ComponentType.ABSORBABLE);
|
||||
if (absorbable && !absorbable.absorbed) {
|
||||
// Give 3 seconds for player to absorb, then remove
|
||||
const timeSinceDeath = (Date.now() - entity.deathTime) / 1000;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
/**
|
||||
* System to handle health regeneration
|
||||
*/
|
||||
* System to handle health regeneration
|
||||
*/
|
||||
export class HealthRegenerationSystem extends System {
|
||||
constructor() {
|
||||
super('HealthRegenerationSystem');
|
||||
this.requiredComponents = ['Health'];
|
||||
super(SystemName.HEALTH_REGEN);
|
||||
this.requiredComponents = [ComponentType.HEALTH];
|
||||
this.priority = 35;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
entities.forEach(entity => {
|
||||
const health = entity.getComponent('Health');
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
if (!health || health.regeneration <= 0) return;
|
||||
|
||||
// Regenerate health over time
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SystemName } from '../core/Constants.js';
|
||||
|
||||
export class InputSystem extends System {
|
||||
constructor() {
|
||||
super('InputSystem');
|
||||
super(SystemName.INPUT);
|
||||
this.requiredComponents = []; // No required components - handles input globally
|
||||
this.priority = 0; // Run first
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { PixelFont } from '../core/PixelFont.js';
|
||||
import { Palette } from '../core/Palette.js';
|
||||
import { GameState, ComponentType, SystemName } from '../core/Constants.js';
|
||||
|
||||
/**
|
||||
* System to handle game menus (start, pause)
|
||||
*/
|
||||
export class MenuSystem extends System {
|
||||
constructor(engine) {
|
||||
super('MenuSystem');
|
||||
super(SystemName.MENU);
|
||||
this.requiredComponents = []; // No required components
|
||||
this.priority = 1; // Run early
|
||||
this.engine = engine;
|
||||
this.ctx = engine.ctx;
|
||||
this.gameState = 'start'; // 'start', 'playing', 'paused'
|
||||
this.gameState = GameState.START;
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
|
|
@ -24,16 +25,16 @@ export class MenuSystem extends System {
|
|||
setupInput() {
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' || e.key === 'p' || e.key === 'P') {
|
||||
if (this.gameState === 'playing') {
|
||||
if (this.gameState === GameState.PLAYING) {
|
||||
this.togglePause();
|
||||
}
|
||||
}
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
if (this.gameState === 'start') {
|
||||
if (this.gameState === GameState.START) {
|
||||
this.startGame();
|
||||
} else if (this.gameState === 'paused') {
|
||||
} else if (this.gameState === GameState.PAUSED) {
|
||||
this.resumeGame();
|
||||
} else if (this.gameState === 'gameOver') {
|
||||
} else if (this.gameState === GameState.GAME_OVER) {
|
||||
this.restartGame();
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +42,7 @@ export class MenuSystem extends System {
|
|||
}
|
||||
|
||||
showGameOver() {
|
||||
this.gameState = 'gameOver';
|
||||
this.gameState = GameState.GAME_OVER;
|
||||
this.paused = true;
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ export class MenuSystem extends System {
|
|||
}
|
||||
|
||||
startGame() {
|
||||
this.gameState = 'playing';
|
||||
this.gameState = GameState.PLAYING;
|
||||
this.paused = false;
|
||||
if (!this.engine.running) {
|
||||
this.engine.start();
|
||||
|
|
@ -58,28 +59,23 @@ export class MenuSystem extends System {
|
|||
}
|
||||
|
||||
togglePause() {
|
||||
if (this.gameState === 'playing') {
|
||||
this.gameState = 'paused';
|
||||
if (this.gameState === GameState.PLAYING) {
|
||||
this.gameState = GameState.PAUSED;
|
||||
this.paused = true;
|
||||
} else if (this.gameState === 'paused') {
|
||||
} else if (this.gameState === GameState.PAUSED) {
|
||||
this.resumeGame();
|
||||
}
|
||||
}
|
||||
|
||||
resumeGame() {
|
||||
this.gameState = 'playing';
|
||||
this.gameState = GameState.PLAYING;
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
process(_deltaTime, _entities) {
|
||||
// Don't update game systems if paused or at start menu
|
||||
if (this.gameState === 'paused' || this.gameState === 'start') {
|
||||
// Pause all other systems
|
||||
this.engine.systems.forEach(system => {
|
||||
if (system !== this && system.name !== 'MenuSystem' && system.name !== 'UISystem') {
|
||||
// Systems will check game state themselves
|
||||
}
|
||||
});
|
||||
if (this.gameState === GameState.PAUSED || this.gameState === GameState.START) {
|
||||
// Logic for system handling is moved to the update loop in Engine
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +88,7 @@ export class MenuSystem extends System {
|
|||
ctx.fillStyle = 'rgba(32, 21, 51, 0.8)'; // Semi-transparent VOID
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
if (this.gameState === 'start') {
|
||||
if (this.gameState === GameState.START) {
|
||||
const title = 'SLIME GENESIS';
|
||||
const titleW = PixelFont.getTextWidth(title, 2);
|
||||
PixelFont.drawText(ctx, title, (width - titleW) / 2, height / 2 - 40, Palette.CYAN, 2);
|
||||
|
|
@ -112,7 +108,7 @@ export class MenuSystem extends System {
|
|||
PixelFont.drawText(ctx, line, (width - lineW) / 2, height / 2 + 25 + i * 10, Palette.ROYAL_BLUE, 1);
|
||||
});
|
||||
|
||||
} else if (this.gameState === 'paused') {
|
||||
} else if (this.gameState === GameState.PAUSED) {
|
||||
const paused = 'PAUSED';
|
||||
const pausedW = PixelFont.getTextWidth(paused, 2);
|
||||
PixelFont.drawText(ctx, paused, (width - pausedW) / 2, 20, Palette.SKY_BLUE, 2);
|
||||
|
|
@ -122,8 +118,8 @@ export class MenuSystem extends System {
|
|||
PixelFont.drawText(ctx, resume, (width - resumeW) / 2, 45, Palette.WHITE, 1);
|
||||
|
||||
// Draw Stats and Knowledge (Moved from HUD)
|
||||
const player = this.engine.getEntities().find(e => e.hasComponent('Evolution'));
|
||||
const uiSystem = this.engine.systems.find(s => s.name === 'UISystem');
|
||||
const player = this.engine.getEntities().find(e => e.hasComponent(ComponentType.EVOLUTION));
|
||||
const uiSystem = this.engine.systems.find(s => s.name === SystemName.UI);
|
||||
|
||||
if (player && uiSystem) {
|
||||
// Draw Stats on the left
|
||||
|
|
@ -132,7 +128,7 @@ export class MenuSystem extends System {
|
|||
// Draw Learning Progress on the right
|
||||
uiSystem.drawSkillProgress(player, width - 110, 80);
|
||||
}
|
||||
} else if (this.gameState === 'gameOver') {
|
||||
} else if (this.gameState === GameState.GAME_OVER) {
|
||||
const dead = 'YOU PERISHED';
|
||||
const deadW = PixelFont.getTextWidth(dead, 2);
|
||||
PixelFont.drawText(ctx, dead, (width - deadW) / 2, height / 2 - 30, Palette.WHITE, 2);
|
||||
|
|
@ -152,7 +148,7 @@ export class MenuSystem extends System {
|
|||
}
|
||||
|
||||
isPaused() {
|
||||
return this.paused || this.gameState === 'start';
|
||||
return this.paused || this.gameState === GameState.START;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class MovementSystem extends System {
|
||||
constructor() {
|
||||
super('MovementSystem');
|
||||
this.requiredComponents = ['Position', 'Velocity'];
|
||||
super(SystemName.MOVEMENT);
|
||||
this.requiredComponents = [ComponentType.POSITION, ComponentType.VELOCITY];
|
||||
this.priority = 10;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
entities.forEach(entity => {
|
||||
const position = entity.getComponent('Position');
|
||||
const velocity = entity.getComponent('Velocity');
|
||||
const health = entity.getComponent('Health');
|
||||
const position = entity.getComponent(ComponentType.POSITION);
|
||||
const velocity = entity.getComponent(ComponentType.VELOCITY);
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
|
||||
if (!position || !velocity) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,27 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class PlayerControllerSystem extends System {
|
||||
constructor() {
|
||||
super('PlayerControllerSystem');
|
||||
this.requiredComponents = ['Position', 'Velocity'];
|
||||
super(SystemName.PLAYER_CONTROLLER);
|
||||
this.requiredComponents = [ComponentType.POSITION, ComponentType.VELOCITY];
|
||||
this.priority = 5;
|
||||
this.playerEntity = null;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
// Find player entity (first entity with player tag or specific component)
|
||||
// Find player entity (entity with Evolution component)
|
||||
if (!this.playerEntity) {
|
||||
this.playerEntity = entities.find(e => e.hasComponent('Evolution'));
|
||||
this.playerEntity = entities.find(e => e.hasComponent(ComponentType.EVOLUTION));
|
||||
}
|
||||
|
||||
if (!this.playerEntity) return;
|
||||
|
||||
const inputSystem = this.engine.systems.find(s => s.name === 'InputSystem');
|
||||
const inputSystem = this.engine.systems.find(s => s.name === SystemName.INPUT);
|
||||
if (!inputSystem) return;
|
||||
|
||||
const velocity = this.playerEntity.getComponent('Velocity');
|
||||
const position = this.playerEntity.getComponent('Position');
|
||||
const velocity = this.playerEntity.getComponent(ComponentType.VELOCITY);
|
||||
const position = this.playerEntity.getComponent(ComponentType.POSITION);
|
||||
if (!velocity || !position) return;
|
||||
|
||||
// Movement input
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { Events } from '../core/EventBus.js';
|
||||
import { Palette } from '../core/Palette.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class ProjectileSystem extends System {
|
||||
constructor() {
|
||||
super('ProjectileSystem');
|
||||
this.requiredComponents = ['Position', 'Velocity'];
|
||||
super(SystemName.PROJECTILE);
|
||||
this.requiredComponents = [ComponentType.POSITION, ComponentType.VELOCITY];
|
||||
this.priority = 18;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
|
||||
const playerController = this.engine.systems.find(s => s.name === SystemName.PLAYER_CONTROLLER);
|
||||
const _player = playerController ? playerController.getPlayerEntity() : null;
|
||||
|
||||
entities.forEach(entity => {
|
||||
const health = entity.getComponent('Health');
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
if (!health || !health.isProjectile) return;
|
||||
|
||||
const position = entity.getComponent('Position');
|
||||
const position = entity.getComponent(ComponentType.POSITION);
|
||||
if (!position) return;
|
||||
|
||||
// Check range - remove if traveled beyond max range
|
||||
|
|
@ -46,10 +47,10 @@ export class ProjectileSystem extends System {
|
|||
allEntities.forEach(target => {
|
||||
if (target.id === entity.owner) return;
|
||||
if (target.id === entity.id) return;
|
||||
if (!target.hasComponent('Health')) return;
|
||||
if (target.getComponent('Health').isProjectile) return;
|
||||
if (!target.hasComponent(ComponentType.HEALTH)) return;
|
||||
if (target.getComponent(ComponentType.HEALTH).isProjectile) return;
|
||||
|
||||
const targetPos = target.getComponent('Position');
|
||||
const targetPos = target.getComponent(ComponentType.POSITION);
|
||||
if (!targetPos) return;
|
||||
|
||||
const dx = targetPos.x - position.x;
|
||||
|
|
@ -58,13 +59,13 @@ export class ProjectileSystem extends System {
|
|||
|
||||
if (distance < 8) {
|
||||
// Hit!
|
||||
const targetHealth = target.getComponent('Health');
|
||||
const targetHealth = target.getComponent(ComponentType.HEALTH);
|
||||
const damage = entity.damage || 10;
|
||||
targetHealth.takeDamage(damage);
|
||||
|
||||
// Impact animation
|
||||
const vfxSystem = this.engine.systems.find(s => s.name === 'VFXSystem');
|
||||
const velocity = entity.getComponent('Velocity');
|
||||
const vfxSystem = this.engine.systems.find(s => s.name === SystemName.VFX);
|
||||
const velocity = entity.getComponent(ComponentType.VELOCITY);
|
||||
if (vfxSystem) {
|
||||
const angle = velocity ? Math.atan2(velocity.vy, velocity.vx) : null;
|
||||
vfxSystem.createImpact(position.x, position.y, Palette.CYAN, angle);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { Palette } from '../core/Palette.js';
|
||||
import { SpriteLibrary } from '../core/SpriteLibrary.js';
|
||||
import { ComponentType, SystemName, AnimationState, VFXType, EntityType } from '../core/Constants.js';
|
||||
|
||||
export class RenderSystem extends System {
|
||||
constructor(engine) {
|
||||
super('RenderSystem');
|
||||
this.requiredComponents = ['Position', 'Sprite'];
|
||||
super(SystemName.RENDER);
|
||||
this.requiredComponents = [ComponentType.POSITION, ComponentType.SPRITE];
|
||||
this.priority = 100; // Render last
|
||||
this.engine = engine;
|
||||
this.ctx = engine.ctx;
|
||||
|
|
@ -25,12 +26,12 @@ export class RenderSystem extends System {
|
|||
// Get all entities including inactive ones for rendering dead absorbable entities
|
||||
const allEntities = this.engine.entities;
|
||||
allEntities.forEach(entity => {
|
||||
const health = entity.getComponent('Health');
|
||||
const evolution = entity.getComponent('Evolution');
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
const evolution = entity.getComponent(ComponentType.EVOLUTION);
|
||||
|
||||
// Skip inactive entities UNLESS they're dead and absorbable (for absorption window)
|
||||
if (!entity.active) {
|
||||
const absorbable = entity.getComponent('Absorbable');
|
||||
const absorbable = entity.getComponent(ComponentType.ABSORBABLE);
|
||||
if (health && health.isDead() && absorbable && !absorbable.absorbed) {
|
||||
// Render dead absorbable entities even if inactive (fade them out)
|
||||
this.drawEntity(entity, deltaTime, true); // Pass fade flag
|
||||
|
|
@ -41,7 +42,7 @@ export class RenderSystem extends System {
|
|||
|
||||
// Don't render dead non-player entities (unless they're absorbable, handled above)
|
||||
if (health && health.isDead() && !evolution) {
|
||||
const absorbable = entity.getComponent('Absorbable');
|
||||
const absorbable = entity.getComponent(ComponentType.ABSORBABLE);
|
||||
if (!absorbable || absorbable.absorbed) {
|
||||
return; // Skip dead non-absorbable entities
|
||||
}
|
||||
|
|
@ -105,9 +106,9 @@ export class RenderSystem extends System {
|
|||
}
|
||||
|
||||
drawEntity(entity, deltaTime, isDeadFade = false) {
|
||||
const position = entity.getComponent('Position');
|
||||
const sprite = entity.getComponent('Sprite');
|
||||
const health = entity.getComponent('Health');
|
||||
const position = entity.getComponent(ComponentType.POSITION);
|
||||
const sprite = entity.getComponent(ComponentType.SPRITE);
|
||||
const health = entity.getComponent(ComponentType.HEALTH);
|
||||
|
||||
if (!position || !sprite) return;
|
||||
|
||||
|
|
@ -120,7 +121,7 @@ export class RenderSystem extends System {
|
|||
// Fade out dead entities
|
||||
let alpha = sprite.alpha;
|
||||
if (isDeadFade && health && health.isDead()) {
|
||||
const absorbable = entity.getComponent('Absorbable');
|
||||
const absorbable = entity.getComponent(ComponentType.ABSORBABLE);
|
||||
if (absorbable && !absorbable.absorbed) {
|
||||
// Calculate fade based on time since death
|
||||
const deathTime = entity.deathTime || Date.now();
|
||||
|
|
@ -135,24 +136,22 @@ export class RenderSystem extends System {
|
|||
this.ctx.scale(sprite.scale, sprite.scale);
|
||||
|
||||
// Update animation time for slime morphing
|
||||
if (sprite.shape === 'slime') {
|
||||
if (sprite.shape === EntityType.SLIME) {
|
||||
sprite.animationTime += deltaTime;
|
||||
sprite.morphAmount = Math.sin(sprite.animationTime * 3) * 0.2 + 0.8;
|
||||
}
|
||||
|
||||
// Map legacy colors to new Palette if necessary
|
||||
let drawColor = sprite.color;
|
||||
if (sprite.shape === 'slime') drawColor = Palette.CYAN;
|
||||
// Map other colors? For now keep them if they match, but we should enforce palette eventually.
|
||||
// The previous code had specific hardcoded colors.
|
||||
if (sprite.shape === EntityType.SLIME) drawColor = Palette.CYAN;
|
||||
|
||||
this.ctx.fillStyle = drawColor;
|
||||
|
||||
// Select appropriate animation state based on velocity
|
||||
const velocity = entity.getComponent('Velocity');
|
||||
const velocity = entity.getComponent(ComponentType.VELOCITY);
|
||||
if (velocity) {
|
||||
const isMoving = Math.abs(velocity.vx) > 1 || Math.abs(velocity.vy) > 1;
|
||||
sprite.animationState = isMoving ? 'walk' : 'idle';
|
||||
sprite.animationState = isMoving ? AnimationState.WALK : AnimationState.IDLE;
|
||||
}
|
||||
|
||||
// Lookup animation data
|
||||
|
|
@ -162,7 +161,7 @@ export class RenderSystem extends System {
|
|||
}
|
||||
|
||||
// Get animation frames for the current state
|
||||
let frames = spriteData[sprite.animationState] || spriteData['idle'];
|
||||
let frames = spriteData[sprite.animationState] || spriteData[AnimationState.IDLE];
|
||||
|
||||
// If frames is still not an array (fallback for simple grids or missing states)
|
||||
if (!frames || !Array.isArray(frames)) {
|
||||
|
|
@ -238,7 +237,7 @@ export class RenderSystem extends System {
|
|||
}
|
||||
|
||||
// Draw combat indicator if attacking (This DOES rotate)
|
||||
const combat = entity.getComponent('Combat');
|
||||
const combat = entity.getComponent(ComponentType.COMBAT);
|
||||
if (combat && combat.isAttacking) {
|
||||
this.ctx.save();
|
||||
this.ctx.rotate(position.rotation);
|
||||
|
|
@ -247,13 +246,13 @@ export class RenderSystem extends System {
|
|||
}
|
||||
|
||||
// Draw stealth indicator
|
||||
const stealth = entity.getComponent('Stealth');
|
||||
const stealth = entity.getComponent(ComponentType.STEALTH);
|
||||
if (stealth && stealth.isStealthed) {
|
||||
this.drawStealthIndicator(stealth, sprite);
|
||||
}
|
||||
|
||||
// Mutation Visual Effects - Simplified for pixel art
|
||||
const evolution = entity.getComponent('Evolution');
|
||||
const evolution = entity.getComponent(ComponentType.EVOLUTION);
|
||||
if (evolution) {
|
||||
if (evolution.mutationEffects.glowingBody) {
|
||||
// Simple outline (square)
|
||||
|
|
@ -275,10 +274,8 @@ export class RenderSystem extends System {
|
|||
this.ctx.restore();
|
||||
}
|
||||
|
||||
|
||||
|
||||
drawVFX() {
|
||||
const vfxSystem = this.engine.systems.find(s => s.name === 'VFXSystem');
|
||||
const vfxSystem = this.engine.systems.find(s => s.name === SystemName.VFX);
|
||||
if (!vfxSystem) return;
|
||||
|
||||
const ctx = this.ctx;
|
||||
|
|
@ -287,7 +284,7 @@ export class RenderSystem extends System {
|
|||
particles.forEach(p => {
|
||||
ctx.fillStyle = p.color;
|
||||
// Fade based on lifetime for impact, or keep solid/flicker for absorption
|
||||
ctx.globalAlpha = p.type === 'impact' ? Math.min(1, p.lifetime / 0.3) : 0.8;
|
||||
ctx.globalAlpha = p.type === VFXType.IMPACT ? Math.min(1, p.lifetime / 0.3) : 0.8;
|
||||
|
||||
// Snap to integers for pixel crispness
|
||||
const x = Math.floor(p.x);
|
||||
|
|
@ -333,7 +330,6 @@ export class RenderSystem extends System {
|
|||
ctx.fillRect(startX, startY, fillWidth, barHeight);
|
||||
}
|
||||
|
||||
|
||||
drawAttackIndicator(combat, _position) {
|
||||
const ctx = this.ctx;
|
||||
const length = 25; // Scaled down
|
||||
|
|
@ -395,7 +391,8 @@ export class RenderSystem extends System {
|
|||
}
|
||||
|
||||
drawSkillEffects() {
|
||||
const skillEffectSystem = this.engine.systems.find(s => s.name === 'SkillEffectSystem');
|
||||
const skillEffectSystem = this.engine.systems.find(s => s.name === SystemName.SKILL_EFFECT);
|
||||
// NOTE: SKILL_EFFECT was missing in my constants. I should add it.
|
||||
if (!skillEffectSystem) return;
|
||||
|
||||
const effects = skillEffectSystem.getEffects();
|
||||
|
|
@ -405,15 +402,15 @@ export class RenderSystem extends System {
|
|||
ctx.save();
|
||||
|
||||
switch (effect.type) {
|
||||
case 'fire_breath':
|
||||
this.drawFireBreath(ctx, effect);
|
||||
break;
|
||||
case 'pounce':
|
||||
this.drawPounce(ctx, effect);
|
||||
break;
|
||||
case 'pounce_impact':
|
||||
this.drawPounceImpact(ctx, effect);
|
||||
break;
|
||||
case 'fire_breath':
|
||||
this.drawFireBreath(ctx, effect);
|
||||
break;
|
||||
case 'pounce':
|
||||
this.drawPounce(ctx, effect);
|
||||
break;
|
||||
case 'pounce_impact':
|
||||
this.drawPounceImpact(ctx, effect);
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SystemName } from '../core/Constants.js';
|
||||
|
||||
/**
|
||||
* System to track and render skill effects (Fire Breath, Pounce, etc.)
|
||||
*/
|
||||
export class SkillEffectSystem extends System {
|
||||
constructor() {
|
||||
super('SkillEffectSystem');
|
||||
super(SystemName.SKILL_EFFECT);
|
||||
this.requiredComponents = []; // No required components
|
||||
this.priority = 50; // Run after skills but before rendering
|
||||
this.activeEffects = [];
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SkillRegistry } from '../skills/SkillRegistry.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class SkillSystem extends System {
|
||||
constructor() {
|
||||
super('SkillSystem');
|
||||
this.requiredComponents = ['Skills'];
|
||||
super(SystemName.SKILL);
|
||||
this.requiredComponents = [ComponentType.SKILLS];
|
||||
this.priority = 30;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
const inputSystem = this.engine.systems.find(s => s.name === 'InputSystem');
|
||||
const inputSystem = this.engine.systems.find(s => s.name === SystemName.INPUT);
|
||||
if (!inputSystem) return;
|
||||
|
||||
entities.forEach(entity => {
|
||||
const skills = entity.getComponent('Skills');
|
||||
const skills = entity.getComponent(ComponentType.SKILLS);
|
||||
if (!skills) return;
|
||||
|
||||
// Update cooldowns
|
||||
|
|
@ -43,7 +44,7 @@ export class SkillSystem extends System {
|
|||
}
|
||||
|
||||
if (skill.activate(entity, this.engine)) {
|
||||
const skills = entity.getComponent('Skills');
|
||||
const skills = entity.getComponent(ComponentType.SKILLS);
|
||||
if (skills) {
|
||||
skills.setCooldown(skillId, skill.cooldown);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { SystemName, ComponentType } from '../core/Constants.js';
|
||||
|
||||
export class StealthSystem extends System {
|
||||
constructor() {
|
||||
super('StealthSystem');
|
||||
this.requiredComponents = ['Stealth'];
|
||||
super(SystemName.STEALTH);
|
||||
this.requiredComponents = [ComponentType.STEALTH];
|
||||
this.priority = 12;
|
||||
}
|
||||
|
||||
process(deltaTime, entities) {
|
||||
const inputSystem = this.engine.systems.find(s => s.name === 'InputSystem');
|
||||
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
|
||||
const inputSystem = this.engine.systems.find(s => s.name === SystemName.INPUT);
|
||||
const playerController = this.engine.systems.find(s => s.name === SystemName.PLAYER_CONTROLLER);
|
||||
const player = playerController ? playerController.getPlayerEntity() : null;
|
||||
|
||||
entities.forEach(entity => {
|
||||
const stealth = entity.getComponent('Stealth');
|
||||
const velocity = entity.getComponent('Velocity');
|
||||
const combat = entity.getComponent('Combat');
|
||||
const evolution = entity.getComponent('Evolution');
|
||||
const stealth = entity.getComponent(ComponentType.STEALTH);
|
||||
const velocity = entity.getComponent(ComponentType.VELOCITY);
|
||||
const combat = entity.getComponent(ComponentType.COMBAT);
|
||||
const evolution = entity.getComponent(ComponentType.EVOLUTION);
|
||||
|
||||
if (!stealth) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ import { SkillRegistry } from '../skills/SkillRegistry.js';
|
|||
import { Events } from '../core/EventBus.js';
|
||||
import { PixelFont } from '../core/PixelFont.js';
|
||||
import { Palette } from '../core/Palette.js';
|
||||
import { GameState, ComponentType, SystemName } from '../core/Constants.js';
|
||||
|
||||
export class UISystem extends System {
|
||||
constructor(engine) {
|
||||
super('UISystem');
|
||||
super(SystemName.UI);
|
||||
this.requiredComponents = []; // No required components - renders UI
|
||||
this.priority = 200; // Render after everything else
|
||||
this.engine = engine;
|
||||
|
|
@ -44,11 +45,11 @@ export class UISystem extends System {
|
|||
this.updateDamageNumbers(deltaTime);
|
||||
this.updateNotifications(deltaTime);
|
||||
|
||||
const menuSystem = this.engine.systems.find(s => s.name === 'MenuSystem');
|
||||
const gameState = menuSystem ? menuSystem.getGameState() : 'playing';
|
||||
const menuSystem = this.engine.systems.find(s => s.name === SystemName.MENU);
|
||||
const gameState = menuSystem ? menuSystem.getGameState() : GameState.PLAYING;
|
||||
|
||||
// Only draw menu overlay if in start, paused, or gameOver state
|
||||
if (gameState === 'start' || gameState === 'paused' || gameState === 'gameOver') {
|
||||
if (gameState === GameState.START || gameState === GameState.PAUSED || gameState === GameState.GAME_OVER) {
|
||||
if (menuSystem) {
|
||||
menuSystem.drawMenu();
|
||||
}
|
||||
|
|
@ -56,7 +57,7 @@ export class UISystem extends System {
|
|||
return;
|
||||
}
|
||||
|
||||
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
|
||||
const playerController = this.engine.systems.find(s => s.name === SystemName.PLAYER_CONTROLLER);
|
||||
const player = playerController ? playerController.getPlayerEntity() : null;
|
||||
|
||||
if (!player) return;
|
||||
|
|
@ -70,9 +71,9 @@ export class UISystem extends System {
|
|||
}
|
||||
|
||||
drawHUD(player) {
|
||||
const health = player.getComponent('Health');
|
||||
const stats = player.getComponent('Stats');
|
||||
const evolution = player.getComponent('Evolution');
|
||||
const health = player.getComponent(ComponentType.HEALTH);
|
||||
const stats = player.getComponent(ComponentType.STATS);
|
||||
const evolution = player.getComponent(ComponentType.EVOLUTION);
|
||||
|
||||
if (!health || !stats || !evolution) return;
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ export class UISystem extends System {
|
|||
}
|
||||
|
||||
drawSkills(player) {
|
||||
const skills = player.getComponent('Skills');
|
||||
const skills = player.getComponent(ComponentType.SKILLS);
|
||||
if (!skills) return;
|
||||
|
||||
const ctx = this.ctx;
|
||||
|
|
@ -132,8 +133,8 @@ export class UISystem extends System {
|
|||
}
|
||||
|
||||
drawStats(player, x, y) {
|
||||
const stats = player.getComponent('Stats');
|
||||
const evolution = player.getComponent('Evolution');
|
||||
const stats = player.getComponent(ComponentType.STATS);
|
||||
const evolution = player.getComponent(ComponentType.EVOLUTION);
|
||||
if (!stats || !evolution) return;
|
||||
|
||||
const ctx = this.ctx;
|
||||
|
|
@ -150,7 +151,7 @@ export class UISystem extends System {
|
|||
}
|
||||
|
||||
drawSkillProgress(player, x, y) {
|
||||
const skillProgress = player.getComponent('SkillProgress');
|
||||
const skillProgress = player.getComponent(ComponentType.SKILL_PROGRESS);
|
||||
if (!skillProgress) return;
|
||||
|
||||
const ctx = this.ctx;
|
||||
|
|
|
|||
|
|
@ -1,104 +1,105 @@
|
|||
import { System } from '../core/System.js';
|
||||
import { Palette } from '../core/Palette.js';
|
||||
import { SystemName, ComponentType, VFXType } from '../core/Constants.js';
|
||||
|
||||
export class VFXSystem extends System {
|
||||
constructor() {
|
||||
super('VFXSystem');
|
||||
this.requiredComponents = [];
|
||||
this.priority = 40;
|
||||
this.particles = [];
|
||||
}
|
||||
constructor() {
|
||||
super(SystemName.VFX);
|
||||
this.requiredComponents = [];
|
||||
this.priority = 40;
|
||||
this.particles = [];
|
||||
}
|
||||
|
||||
process(deltaTime, _entities) {
|
||||
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
|
||||
const player = playerController ? playerController.getPlayerEntity() : null;
|
||||
const playerPos = player ? player.getComponent('Position') : null;
|
||||
process(deltaTime, _entities) {
|
||||
const playerController = this.engine.systems.find(s => s.name === SystemName.PLAYER_CONTROLLER);
|
||||
const player = playerController ? playerController.getPlayerEntity() : null;
|
||||
const playerPos = player ? player.getComponent(ComponentType.POSITION) : null;
|
||||
|
||||
for (let i = this.particles.length - 1; i >= 0; i--) {
|
||||
const p = this.particles[i];
|
||||
for (let i = this.particles.length - 1; i >= 0; i--) {
|
||||
const p = this.particles[i];
|
||||
|
||||
// Update lifetime
|
||||
p.lifetime -= deltaTime;
|
||||
if (p.lifetime <= 0) {
|
||||
this.particles.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
// Update lifetime
|
||||
p.lifetime -= deltaTime;
|
||||
if (p.lifetime <= 0) {
|
||||
this.particles.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Behavior logic
|
||||
if (p.type === 'absorption' && playerPos) {
|
||||
// Attract to player
|
||||
const dx = playerPos.x - p.x;
|
||||
const dy = playerPos.y - p.y;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
// Behavior logic
|
||||
if (p.type === VFXType.ABSORPTION && playerPos) {
|
||||
// Attract to player
|
||||
const dx = playerPos.x - p.x;
|
||||
const dy = playerPos.y - p.y;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (dist > 5) {
|
||||
p.vx += (dx / dist) * 800 * deltaTime;
|
||||
p.vy += (dy / dist) * 800 * deltaTime;
|
||||
// Add some drag to make it smooth
|
||||
p.vx *= 0.95;
|
||||
p.vy *= 0.95;
|
||||
} else {
|
||||
// Arrived
|
||||
this.particles.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Basic drag for impact particles
|
||||
p.vx *= 0.9;
|
||||
p.vy *= 0.9;
|
||||
}
|
||||
|
||||
// Update position
|
||||
p.x += p.vx * deltaTime;
|
||||
p.y += p.vy * deltaTime;
|
||||
if (dist > 5) {
|
||||
p.vx += (dx / dist) * 800 * deltaTime;
|
||||
p.vy += (dy / dist) * 800 * deltaTime;
|
||||
// Add some drag to make it smooth
|
||||
p.vx *= 0.95;
|
||||
p.vy *= 0.95;
|
||||
} else {
|
||||
// Arrived
|
||||
this.particles.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Basic drag for impact particles
|
||||
p.vx *= 0.9;
|
||||
p.vy *= 0.9;
|
||||
}
|
||||
|
||||
createImpact(x, y, color = Palette.WHITE, angle = null) {
|
||||
const count = 8;
|
||||
for (let i = 0; i < count; i++) {
|
||||
let vx, vy;
|
||||
if (angle !== null) {
|
||||
// Splash in the direction of hit + some spread
|
||||
const spread = (Math.random() - 0.5) * 2;
|
||||
const speed = 50 + Math.random() * 150;
|
||||
vx = Math.cos(angle + spread) * speed;
|
||||
vy = Math.sin(angle + spread) * speed;
|
||||
} else {
|
||||
vx = (Math.random() - 0.5) * 150;
|
||||
vy = (Math.random() - 0.5) * 150;
|
||||
}
|
||||
|
||||
this.particles.push({
|
||||
x,
|
||||
y,
|
||||
vx,
|
||||
vy,
|
||||
lifetime: 0.2 + Math.random() * 0.3,
|
||||
size: 1 + Math.random() * 2,
|
||||
color: color,
|
||||
type: 'impact'
|
||||
});
|
||||
}
|
||||
// Update position
|
||||
p.x += p.vx * deltaTime;
|
||||
p.y += p.vy * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
createAbsorption(x, y, color = Palette.CYAN) {
|
||||
for (let i = 0; i < 12; i++) {
|
||||
// Start with a small explosion then attract
|
||||
this.particles.push({
|
||||
x,
|
||||
y,
|
||||
vx: (Math.random() - 0.5) * 100,
|
||||
vy: (Math.random() - 0.5) * 100,
|
||||
lifetime: 1.5,
|
||||
size: 2,
|
||||
color: color,
|
||||
type: 'absorption'
|
||||
});
|
||||
}
|
||||
}
|
||||
createImpact(x, y, color = Palette.WHITE, angle = null) {
|
||||
const count = 8;
|
||||
for (let i = 0; i < count; i++) {
|
||||
let vx, vy;
|
||||
if (angle !== null) {
|
||||
// Splash in the direction of hit + some spread
|
||||
const spread = (Math.random() - 0.5) * 2;
|
||||
const speed = 50 + Math.random() * 150;
|
||||
vx = Math.cos(angle + spread) * speed;
|
||||
vy = Math.sin(angle + spread) * speed;
|
||||
} else {
|
||||
vx = (Math.random() - 0.5) * 150;
|
||||
vy = (Math.random() - 0.5) * 150;
|
||||
}
|
||||
|
||||
getParticles() {
|
||||
return this.particles;
|
||||
this.particles.push({
|
||||
x,
|
||||
y,
|
||||
vx,
|
||||
vy,
|
||||
lifetime: 0.2 + Math.random() * 0.3,
|
||||
size: 1 + Math.random() * 2,
|
||||
color: color,
|
||||
type: VFXType.IMPACT
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createAbsorption(x, y, color = Palette.CYAN) {
|
||||
for (let i = 0; i < 12; i++) {
|
||||
// Start with a small explosion then attract
|
||||
this.particles.push({
|
||||
x,
|
||||
y,
|
||||
vx: (Math.random() - 0.5) * 100,
|
||||
vy: (Math.random() - 0.5) * 100,
|
||||
lifetime: 1.5,
|
||||
size: 2,
|
||||
color: color,
|
||||
type: VFXType.ABSORPTION
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getParticles() {
|
||||
return this.particles;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue