Files
novafarma/src/systems/SmartZombieSystem.js

601 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* SMART ZOMBIE SYSTEM
* Manages zombie intelligence levels (Lv1-10), independent AI, follower commands, and team construction.
*
* Features:
* - Lv1-4: Helpers (resource finding, carry materials, warn danger)
* - Lv5-7: Assistants (repair assistance +25% speed, material delivery AI)
* - Lv8-10: INDEPENDENT AI (build/repair alone, auto-detect damage, teach others)
* - Follower System: 10 zombie followers with commands (Stop, Help, Attack, Home)
* - Team Construction: Lv10 leader + multi-zombie mega-projects
*/
class SmartZombieSystem {
constructor(scene) {
this.scene = scene;
// Zombie intelligence data
this.zombies = new Map(); // zombieId -> zombie data
this.followers = []; // Active followers (max 10)
this.independentWorkers = []; // Lv8+ zombies working autonomously
this.teams = []; // Construction teams (Lv10 leader + team)
// Commands
this.followerCommands = ['STOP', 'HELP', 'ATTACK', 'HOME'];
this.currentCommand = 'HELP'; // Default command
// AI Task Queue
this.taskQueue = []; // {type, position, priority, assignedZombie}
// Intelligence Levels
this.intelligenceLevels = {
1: { name: 'Curious', abilities: ['ping_resources', 'carry_light'] },
2: { name: 'Aware', abilities: ['carry_medium', 'warn_danger'] },
3: { name: 'Helper', abilities: ['gather_resources', 'follow_player'] },
4: { name: 'Assistant', abilities: ['detect_ores', 'organize_storage'] },
5: { name: 'Skilled', abilities: ['repair_assist', 'deliver_materials'] },
6: { name: 'Expert', abilities: ['craft_assist', 'defend_farm'] },
7: { name: 'Advanced', abilities: ['build_assist', 'teach_lv1_3'] },
8: { name: 'INDEPENDENT', abilities: ['build_alone', 'repair_alone', 'auto_detect_damage'] },
9: { name: 'MASTER', abilities: ['teach_all', 'lead_team', 'optimize_tasks'] },
10: { name: 'GENIUS', abilities: ['mega_projects', 'invent_blueprints', 'immortal_knowledge'] }
};
console.log('🧠 Smart Zombie System initialized!');
}
/**
* Create or upgrade a zombie
*/
createZombie(name, level = 1, position = { x: 0, y: 0 }) {
const zombieId = `zombie_${Date.now()}_${Math.random()}`;
const zombie = {
id: zombieId,
name: name,
level: Math.min(level, 10),
position: position,
status: 'idle', // idle, working, following, patrolling
currentTask: null,
experience: 0,
experienceToNext: this.getExpRequired(level),
abilities: this.getAbilities(level),
inventory: [],
loyalty: 50, // 0-100
energy: 100, // 0-100
sprite: null // Will be set when rendered
};
this.zombies.set(zombieId, zombie);
console.log(`🧟 Created ${name} (Lv${level}) - ${this.intelligenceLevels[level].name}`);
// Auto-assign if Lv8+
if (level >= 8) {
this.makeIndependent(zombieId);
}
return zombieId;
}
/**
* Get required XP for next level
*/
getExpRequired(level) {
return 100 * level * level; // 100, 400, 900, 1600, etc.
}
/**
* Get abilities for a level
*/
getAbilities(level) {
const abilities = [];
for (let i = 1; i <= level; i++) {
abilities.push(...this.intelligenceLevels[i].abilities);
}
return [...new Set(abilities)]; // Remove duplicates
}
/**
* Add experience to a zombie
*/
addExperience(zombieId, amount) {
const zombie = this.zombies.get(zombieId);
if (!zombie) return;
zombie.experience += amount;
// Level up?
while (zombie.experience >= zombie.experienceToNext && zombie.level < 10) {
zombie.level++;
zombie.experience -= zombie.experienceToNext;
zombie.experienceToNext = this.getExpRequired(zombie.level + 1);
zombie.abilities = this.getAbilities(zombie.level);
console.log(`⬆️ ${zombie.name} leveled up to Lv${zombie.level}!`);
this.scene.events.emit('show-floating-text', {
x: zombie.position.x,
y: zombie.position.y - 50,
text: `LEVEL UP! Lv${zombie.level}`,
color: '#FFD700'
});
// Auto-assign if reached Lv8
if (zombie.level === 8) {
this.makeIndependent(zombieId);
}
}
}
/**
* Make a zombie independent (Lv8+)
*/
makeIndependent(zombieId) {
const zombie = this.zombies.get(zombieId);
if (!zombie || zombie.level < 8) return false;
if (!this.independentWorkers.includes(zombieId)) {
this.independentWorkers.push(zombieId);
zombie.status = 'independent';
console.log(`🤖 ${zombie.name} is now INDEPENDENT!`);
this.scene.events.emit('notification', {
title: 'Independent Worker!',
message: `${zombie.name} can now work autonomously!`,
icon: '🤖'
});
}
return true;
}
/**
* Add zombie to follower group (max 10)
*/
addFollower(zombieId) {
if (this.followers.length >= 10) {
console.log('❌ Maximum 10 followers!');
return false;
}
const zombie = this.zombies.get(zombieId);
if (!zombie) return false;
if (!this.followers.includes(zombieId)) {
this.followers.push(zombieId);
zombie.status = 'following';
console.log(` ${zombie.name} joined your followers!`);
return true;
}
return false;
}
/**
* Remove zombie from followers
*/
removeFollower(zombieId) {
const index = this.followers.indexOf(zombieId);
if (index > -1) {
this.followers.splice(index, 1);
const zombie = this.zombies.get(zombieId);
if (zombie) {
zombie.status = 'idle';
console.log(` ${zombie.name} left your followers.`);
}
return true;
}
return false;
}
/**
* Send command to all followers
*/
commandFollowers(command) {
if (!this.followerCommands.includes(command)) {
console.log(`❌ Invalid command: ${command}`);
return;
}
this.currentCommand = command;
this.followers.forEach(zombieId => {
const zombie = this.zombies.get(zombieId);
if (!zombie) return;
switch (command) {
case 'STOP':
zombie.status = 'idle';
zombie.currentTask = null;
break;
case 'HELP':
zombie.status = 'helping';
this.assignHelpTask(zombieId);
break;
case 'ATTACK':
zombie.status = 'combat';
this.assignCombatTask(zombieId);
break;
case 'HOME':
zombie.status = 'returning';
this.sendHome(zombieId);
break;
}
});
console.log(`📢 Commanded ${this.followers.length} followers: ${command}`);
this.scene.events.emit('notification', {
title: 'Follower Command',
message: `${this.followers.length} zombies: ${command}`,
icon: '📢'
});
}
/**
* Assign help task (gather, repair, build)
*/
assignHelpTask(zombieId) {
const zombie = this.zombies.get(zombieId);
if (!zombie) return;
// Find nearest task from queue
const task = this.findNearestTask(zombie.position);
if (task) {
zombie.currentTask = task;
task.assignedZombie = zombieId;
console.log(`🛠️ ${zombie.name} assigned to ${task.type}`);
} else {
// Follow player and gather resources
zombie.currentTask = { type: 'gather', target: 'player' };
}
}
/**
* Assign combat task
*/
assignCombatTask(zombieId) {
const zombie = this.zombies.get(zombieId);
if (!zombie) return;
zombie.currentTask = { type: 'defend', target: 'player', radius: 200 };
console.log(`⚔️ ${zombie.name} defending!`);
}
/**
* Send zombie home
*/
sendHome(zombieId) {
const zombie = this.zombies.get(zombieId);
if (!zombie) return;
zombie.currentTask = { type: 'return_home', target: { x: 0, y: 0 } };
console.log(`🏠 ${zombie.name} returning home.`);
}
/**
* Find nearest task for zombie
*/
findNearestTask(position) {
if (this.taskQueue.length === 0) return null;
let nearest = null;
let minDist = Infinity;
this.taskQueue.forEach(task => {
if (task.assignedZombie) return; // Already assigned
const dist = Phaser.Math.Distance.Between(
position.x, position.y,
task.position.x, task.position.y
);
if (dist < minDist) {
minDist = dist;
nearest = task;
}
});
return nearest;
}
/**
* Add task to queue
*/
addTask(type, position, priority = 1) {
const task = {
id: `task_${Date.now()}`,
type: type, // 'build', 'repair', 'gather', 'mine'
position: position,
priority: priority,
assignedZombie: null,
progress: 0,
required: 100
};
this.taskQueue.push(task);
// Auto-assign to independent workers
this.autoAssignIndependentWorkers();
return task.id;
}
/**
* Auto-assign tasks to independent workers (Lv8+)
*/
autoAssignIndependentWorkers() {
this.independentWorkers.forEach(zombieId => {
const zombie = this.zombies.get(zombieId);
if (!zombie || zombie.currentTask) return;
const task = this.findNearestTask(zombie.position);
if (task && zombie.abilities.includes('build_alone') || zombie.abilities.includes('repair_alone')) {
zombie.currentTask = task;
task.assignedZombie = zombieId;
console.log(`🤖 Independent: ${zombie.name} auto-assigned to ${task.type}`);
}
});
}
/**
* Create construction team (Lv10 leader required)
*/
createTeam(leaderZombieId, memberZombieIds) {
const leader = this.zombies.get(leaderZombieId);
if (!leader || leader.level < 10) {
console.log('❌ Team leader must be Lv10!');
return null;
}
const members = memberZombieIds
.map(id => this.zombies.get(id))
.filter(z => z && z.level >= 5);
if (members.length === 0) {
console.log('❌ Need at least 1 Lv5+ member!');
return null;
}
const teamId = `team_${Date.now()}`;
const team = {
id: teamId,
leader: leaderZombieId,
members: memberZombieIds,
status: 'ready',
currentProject: null,
efficiency: 1.0 + (members.length * 0.15) // +15% per member
};
this.teams.push(team);
console.log(`👥 Team created! Leader: ${leader.name}, Members: ${members.length}, Efficiency: ${team.efficiency}x`);
this.scene.events.emit('notification', {
title: 'Team Created!',
message: `${leader.name}'s team (${members.length} members) is ready!`,
icon: '👥'
});
return teamId;
}
/**
* Assign mega-project to team
*/
assignMegaProject(teamId, projectType, position) {
const team = this.teams.find(t => t.id === teamId);
if (!team) return false;
const project = {
type: projectType, // 'mega_barn', 'fortress', 'factory'
position: position,
progress: 0,
required: 1000, // Mega projects take time
efficiency: team.efficiency
};
team.currentProject = project;
team.status = 'working';
console.log(`🏗️ Team assigned to ${projectType}! Efficiency: ${team.efficiency}x`);
return true;
}
/**
* Update all zombies (called every frame)
*/
update(delta) {
// Update followers
this.followers.forEach(zombieId => {
const zombie = this.zombies.get(zombieId);
if (!zombie) return;
this.updateZombie(zombie, delta);
});
// Update independent workers
this.independentWorkers.forEach(zombieId => {
const zombie = this.zombies.get(zombieId);
if (!zombie) return;
this.updateZombie(zombie, delta);
});
// Update teams
this.teams.forEach(team => {
if (team.status === 'working' && team.currentProject) {
team.currentProject.progress += delta * 0.01 * team.efficiency;
if (team.currentProject.progress >= team.currentProject.required) {
this.completeMegaProject(team);
}
}
});
}
/**
* Update individual zombie
*/
updateZombie(zombie, delta) {
if (!zombie.currentTask) return;
const task = zombie.currentTask;
switch (task.type) {
case 'build':
case 'repair':
this.progressTask(zombie, task, delta);
break;
case 'gather':
this.gatherResources(zombie, delta);
break;
case 'defend':
this.defendArea(zombie);
break;
case 'return_home':
this.moveToHome(zombie);
break;
}
// Drain energy
zombie.energy = Math.max(0, zombie.energy - delta * 0.001);
}
/**
* Progress on a task
*/
progressTask(zombie, task, delta) {
const speedBonus = zombie.level >= 5 ? 1.25 : 1.0;
task.progress += delta * 0.02 * speedBonus;
if (task.progress >= task.required) {
this.completeTask(zombie, task);
}
}
/**
* Complete a task
*/
completeTask(zombie, task) {
console.log(`${zombie.name} completed ${task.type}!`);
// Award XP
this.addExperience(zombie.id, 50 + (task.priority * 10));
// Remove from queue
const index = this.taskQueue.indexOf(task);
if (index > -1) {
this.taskQueue.splice(index, 1);
}
zombie.currentTask = null;
// Find next task if independent
if (zombie.status === 'independent') {
this.autoAssignIndependentWorkers();
}
}
/**
* Gather resources
*/
gatherResources(zombie, delta) {
// Simple resource gathering simulation
if (Math.random() < 0.01) {
const resources = ['wood', 'stone', 'iron_ore'];
const resource = resources[Math.floor(Math.random() * resources.length)];
zombie.inventory.push(resource);
console.log(`📦 ${zombie.name} gathered ${resource}`);
}
}
/**
* Defend area around player/target
*/
defendArea(zombie) {
// Detect nearby enemies (placeholder)
// In real game, would check for hostile entities
}
/**
* Move zombie to home position
*/
moveToHome(zombie) {
const target = zombie.currentTask.target;
const dist = Phaser.Math.Distance.Between(
zombie.position.x, zombie.position.y,
target.x, target.y
);
if (dist < 10) {
zombie.status = 'idle';
zombie.currentTask = null;
console.log(`🏠 ${zombie.name} arrived home.`);
} else {
// Move toward home (simplified)
const angle = Phaser.Math.Angle.Between(
zombie.position.x, zombie.position.y,
target.x, target.y
);
zombie.position.x += Math.cos(angle) * 2;
zombie.position.y += Math.sin(angle) * 2;
}
}
/**
* Complete mega-project
*/
completeMegaProject(team) {
console.log(`🏆 Mega-project ${team.currentProject.type} COMPLETE!`);
const leader = this.zombies.get(team.leader);
if (leader) {
this.addExperience(leader.id, 500);
}
team.members.forEach(memberId => {
this.addExperience(memberId, 200);
});
this.scene.events.emit('notification', {
title: 'Mega-Project Complete!',
message: `${team.currentProject.type} is finished!`,
icon: '🏆'
});
team.currentProject = null;
team.status = 'ready';
}
/**
* Get zombie stats
*/
getZombieStats(zombieId) {
const zombie = this.zombies.get(zombieId);
if (!zombie) return null;
return {
name: zombie.name,
level: zombie.level,
intelligence: this.intelligenceLevels[zombie.level].name,
abilities: zombie.abilities,
status: zombie.status,
energy: Math.round(zombie.energy),
loyalty: zombie.loyalty,
experience: `${zombie.experience}/${zombie.experienceToNext}`,
inventory: zombie.inventory.length
};
}
}