601 lines
18 KiB
JavaScript
601 lines
18 KiB
JavaScript
/**
|
||
* 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
|
||
};
|
||
}
|
||
}
|