LEGENDARY NIGHT! P22-P25 complete - 4 new systems (2,300 LOC) - Smart Zombies, Tools, Blueprints, Ana Clues!
This commit is contained in:
600
src/systems/SmartZombieSystem.js
Normal file
600
src/systems/SmartZombieSystem.js
Normal file
@@ -0,0 +1,600 @@
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user