feat: add poc

This commit is contained in:
Juan Sebastián Montoya 2026-01-06 14:02:09 -05:00
parent 43d27b04d9
commit 4a4fa05ce4
53 changed files with 6191 additions and 0 deletions

View file

@ -0,0 +1,176 @@
import { System } from '../core/System.js';
import { GameConfig } from '../GameConfig.js';
import { Events } from '../core/EventBus.js';
export class AbsorptionSystem extends System {
constructor() {
super('AbsorptionSystem');
this.requiredComponents = ['Position', 'Absorbable'];
this.priority = 25;
this.absorptionEffects = []; // Visual effects
}
process(deltaTime, _entities) {
const playerController = this.engine.systems.find(s => s.name === 'PlayerControllerSystem');
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');
if (!playerPos || !playerEvolution) return;
// Get ALL entities (including inactive ones) for absorption check
const allEntities = this.engine.entities; // Get raw entities array, not filtered
const config = GameConfig.Absorption;
// Check for absorbable entities near player
allEntities.forEach(entity => {
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');
// 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;
const absorbable = entity.getComponent('Absorbable');
const health = entity.getComponent('Health');
const entityPos = entity.getComponent('Position');
if (!entityPos) return;
// Check if creature is dead and in absorption range
if (health.isDead() && !absorbable.absorbed) {
const dx = playerPos.x - entityPos.x;
const dy = playerPos.y - entityPos.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= config.range) {
this.absorbEntity(player, entity, absorbable, playerEvolution, playerSkills, playerStats, skillProgress);
}
}
});
// Update visual effects
this.updateEffects(deltaTime);
}
absorbEntity(player, entity, absorbable, evolution, skills, stats, skillProgress) {
if (absorbable.absorbed) return;
absorbable.absorbed = true;
const entityPos = entity.getComponent('Position');
const health = player.getComponent('Health');
const config = GameConfig.Absorption;
// Add evolution points
evolution.addEvolution(
absorbable.evolutionData.human,
absorbable.evolutionData.beast,
absorbable.evolutionData.slime
);
// Track skill progress (need to absorb multiple times to learn)
// Always track progress for ALL skills the enemy has, regardless of roll
if (skillProgress && absorbable.skillsGranted && absorbable.skillsGranted.length > 0) {
absorbable.skillsGranted.forEach(skill => {
// Always add progress when absorbing an enemy with this skill
const currentProgress = skillProgress.addSkillProgress(skill.id);
const required = skillProgress.requiredAbsorptions;
// If we've absorbed enough, learn the skill
if (currentProgress >= required && !skills.hasSkill(skill.id)) {
skills.addSkill(skill.id, false);
this.engine.emit(Events.SKILL_LEARNED, { id: skill.id });
console.log(`Learned skill: ${skill.id}!`);
}
});
}
// Heal from absorption (slime recovers by consuming)
if (health) {
const healPercent = config.healPercentMin + Math.random() * (config.healPercentMax - config.healPercentMin);
const healAmount = health.maxHp * healPercent;
health.heal(healAmount);
}
// Check for mutation
if (absorbable.shouldMutate() && stats) {
this.applyMutation(stats);
evolution.checkMutations(stats, this.engine);
}
// Visual effect
if (entityPos) {
this.addAbsorptionEffect(entityPos.x, entityPos.y);
}
// Mark as absorbed - DeathSystem will handle removal after absorption window
// Don't remove immediately, let DeathSystem handle it
}
applyMutation(stats) {
// Random stat mutation
const mutations = [
{ stat: 'strength', amount: 5 },
{ stat: 'agility', amount: 5 },
{ stat: 'intelligence', amount: 5 },
{ stat: 'constitution', amount: 5 },
{ stat: 'perception', amount: 5 },
];
const mutation = mutations[Math.floor(Math.random() * mutations.length)];
stats[mutation.stat] += mutation.amount;
// Could also add negative mutations
if (Math.random() < 0.3) {
const negativeStat = mutations[Math.floor(Math.random() * mutations.length)];
stats[negativeStat.stat] = Math.max(1, stats[negativeStat.stat] - 2);
}
}
addAbsorptionEffect(x, y) {
for (let i = 0; i < 20; i++) {
this.absorptionEffects.push({
x,
y,
vx: (Math.random() - 0.5) * 200,
vy: (Math.random() - 0.5) * 200,
lifetime: 0.5 + Math.random() * 0.5,
size: 3 + Math.random() * 5,
color: `hsl(${120 + Math.random() * 60}, 100%, 50%)`
});
}
}
updateEffects(deltaTime) {
for (let i = this.absorptionEffects.length - 1; i >= 0; i--) {
const effect = this.absorptionEffects[i];
effect.x += effect.vx * deltaTime;
effect.y += effect.vy * deltaTime;
effect.lifetime -= deltaTime;
effect.vx *= 0.95;
effect.vy *= 0.95;
if (effect.lifetime <= 0) {
this.absorptionEffects.splice(i, 1);
}
}
}
getEffects() {
return this.absorptionEffects;
}
}