342 lines
11 KiB
JavaScript
342 lines
11 KiB
JavaScript
/**
|
|
* NPC SETTLEMENT SYSTEM (Magic Helpers)
|
|
* NPCs auto-assist with tasks
|
|
* Worker efficiency, happiness, housing
|
|
*/
|
|
|
|
export class NPCSettlementSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
|
|
// Settled NPCs
|
|
this.settledNPCs = new Map();
|
|
|
|
// Worker assignments
|
|
this.assignments = new Map(); // npcId → task
|
|
|
|
// NPC stats
|
|
this.npcHappiness = new Map(); // npcId → happiness (0-100)
|
|
this.npcEfficiency = new Map(); // npcId → efficiency (0.5-2.0)
|
|
|
|
// Housing
|
|
this.npcHomes = new Map(); // npcId → buildingId
|
|
this.housingCapacity = 0;
|
|
|
|
// Task types
|
|
this.taskTypes = ['farming', 'building', 'crafting', 'defense', 'gathering'];
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
// Update worker tasks periodically
|
|
this.scene.time.addEvent({
|
|
delay: 5000, // Every 5s
|
|
callback: () => this.updateWorkers(),
|
|
loop: true
|
|
});
|
|
|
|
// Update happiness hourly
|
|
this.scene.time.addEvent({
|
|
delay: 3600000, // 1 hour
|
|
callback: () => this.updateHappiness(),
|
|
loop: true
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Settle an NPC (they join the town)
|
|
*/
|
|
settleNPC(npcId, npcData) {
|
|
if (this.settledNPCs.has(npcId)) return false;
|
|
|
|
//Check housing availability
|
|
if (this.settledNPCs.size >= this.housingCapacity) {
|
|
console.warn('No housing available');
|
|
return false;
|
|
}
|
|
|
|
this.settledNPCs.set(npcId, {
|
|
id: npcId,
|
|
name: npcData.name,
|
|
skills: npcData.skills || [],
|
|
assignedTask: null,
|
|
...npcData
|
|
});
|
|
|
|
// Initial stats
|
|
this.npcHappiness.set(npcId, 75); // Start at 75% happiness
|
|
this.npcEfficiency.set(npcId, 1.0); // Base efficiency
|
|
|
|
// Notification
|
|
this.scene.uiSystem?.showNotification(
|
|
`${npcData.name} has joined the settlement!`,
|
|
'success',
|
|
{ icon: 'npc_join' }
|
|
);
|
|
|
|
console.log(`🏘️ ${npcData.name} settled in town`);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Assign NPC to task
|
|
*/
|
|
assignTask(npcId, taskType, taskData) {
|
|
const npc = this.settledNPCs.get(npcId);
|
|
if (!npc) return false;
|
|
|
|
// Check if NPC has skill for task
|
|
if (!npc.skills.includes(taskType)) {
|
|
console.warn(`${npc.name} lacks skill: ${taskType}`);
|
|
return false;
|
|
}
|
|
|
|
// Assign
|
|
npc.assignedTask = taskType;
|
|
this.assignments.set(npcId, {
|
|
type: taskType,
|
|
data: taskData,
|
|
startTime: Date.now()
|
|
});
|
|
|
|
console.log(`👷 ${npc.name} assigned to ${taskType}`);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unassign NPC from task
|
|
*/
|
|
unassignTask(npcId) {
|
|
const npc = this.settledNPCs.get(npcId);
|
|
if (!npc) return false;
|
|
|
|
npc.assignedTask = null;
|
|
this.assignments.delete(npcId);
|
|
|
|
console.log(`${npc.name} unassigned`);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Update all worker tasks
|
|
*/
|
|
updateWorkers() {
|
|
this.assignments.forEach((assignment, npcId) => {
|
|
const npc = this.settledNPCs.get(npcId);
|
|
const efficiency = this.npcEfficiency.get(npcId) || 1.0;
|
|
|
|
switch (assignment.type) {
|
|
case 'farming':
|
|
this.performFarming(npc, efficiency, assignment.data);
|
|
break;
|
|
case 'building':
|
|
this.performBuilding(npc, efficiency, assignment.data);
|
|
break;
|
|
case 'crafting':
|
|
this.performCrafting(npc, efficiency, assignment.data);
|
|
break;
|
|
case 'defense':
|
|
this.performDefense(npc, efficiency);
|
|
break;
|
|
case 'gathering':
|
|
this.performGathering(npc, efficiency);
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* WORKER TASKS
|
|
*/
|
|
|
|
performFarming(npc, efficiency, data) {
|
|
// Auto-tend crops
|
|
const crops = this.scene.crops || [];
|
|
const tendsPerCycle = Math.floor(2 * efficiency);
|
|
|
|
for (let i = 0; i < tendsPerCycle && i < crops.length; i++) {
|
|
const crop = crops[i];
|
|
crop.water?.();
|
|
crop.fertilize?.();
|
|
}
|
|
|
|
// Chance to auto-harvest
|
|
if (Math.random() < 0.1 * efficiency) {
|
|
const harvestable = crops.find(c => c.isHarvestable);
|
|
if (harvestable) {
|
|
this.scene.farmingSystem?.harvestCrop(harvestable);
|
|
console.log(`${npc.name} harvested crop`);
|
|
}
|
|
}
|
|
}
|
|
|
|
performBuilding(npc, efficiency, data) {
|
|
// Speed up construction
|
|
const building = data.buildingId ? this.scene.buildingSystem?.getBuilding(data.buildingId) : null;
|
|
|
|
if (building && building.isUnderConstruction) {
|
|
const progressBonus = 5 * efficiency;
|
|
building.constructionProgress += progressBonus;
|
|
|
|
if (building.constructionProgress >= 100) {
|
|
this.scene.buildingSystem?.completeConstruction(building.id);
|
|
console.log(`${npc.name} completed building: ${building.name}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
performCrafting(npc, efficiency, data) {
|
|
// Auto-craft items
|
|
if (data.recipe) {
|
|
const canCraft = this.scene.craftingSystem?.canCraft(data.recipe);
|
|
if (canCraft && Math.random() < 0.2 * efficiency) {
|
|
this.scene.craftingSystem?.craft(data.recipe);
|
|
console.log(`${npc.name} crafted ${data.recipe}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
performDefense(npc, efficiency) {
|
|
// Patrol and defend
|
|
// Increased detection range for raids
|
|
this.scene.gameState.buffs.raid_detection_range = (this.scene.gameState.buffs.raid_detection_range || 1.0) + (0.1 * efficiency);
|
|
|
|
// Auto-repair walls/defenses
|
|
const defenses = this.scene.defenseSystem?.getAllDefenses() || [];
|
|
defenses.forEach(defense => {
|
|
if (defense.health < defense.maxHealth && Math.random() < 0.05 * efficiency) {
|
|
defense.health = Math.min(defense.maxHealth, defense.health + 10);
|
|
console.log(`${npc.name} repaired ${defense.name}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
performGathering(npc, efficiency) {
|
|
// Auto-gather resources
|
|
const gatherChance = 0.15 * efficiency;
|
|
|
|
if (Math.random() < gatherChance) {
|
|
const resources = ['wood', 'stone', 'berries', 'herbs'];
|
|
const resource = Phaser.Utils.Array.GetRandom(resources);
|
|
const amount = Math.floor(Phaser.Math.Between(1, 3) * efficiency);
|
|
|
|
this.scene.inventorySystem?.addItem(resource, amount);
|
|
console.log(`${npc.name} gathered ${amount} ${resource}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update NPC happiness
|
|
*/
|
|
updateHappiness() {
|
|
this.settledNPCs.forEach((npc, npcId) => {
|
|
let happiness = this.npcHappiness.get(npcId) || 50;
|
|
|
|
// Factors affecting happiness
|
|
const hasHome = this.npcHomes.has(npcId);
|
|
const hasTask = npc.assignedTask !== null;
|
|
const isOverworked = hasTask && this.assignments.get(npcId)?.overworked;
|
|
|
|
// Adjustments
|
|
if (hasHome) happiness += 5;
|
|
if (hasTask) happiness += 2;
|
|
if (isOverworked) happiness -= 10;
|
|
if (!hasHome && this.settledNPCs.size > this.housingCapacity) happiness -= 15;
|
|
|
|
// Natural decay
|
|
happiness -= 1;
|
|
|
|
// Clamp
|
|
happiness = Math.max(0, Math.min(100, happiness));
|
|
this.npcHappiness.set(npcId, happiness);
|
|
|
|
// Update efficiency based on happiness
|
|
const efficiency = 0.5 + (happiness / 100) * 1.5; // 0.5-2.0
|
|
this.npcEfficiency.set(npcId, efficiency);
|
|
|
|
// Low happiness warning
|
|
if (happiness < 30) {
|
|
this.scene.uiSystem?.showNotification(
|
|
`${npc.name} is unhappy!`,
|
|
'warning'
|
|
);
|
|
console.warn(`${npc.name} happiness: ${happiness}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Assign NPC to home
|
|
*/
|
|
assignHome(npcId, buildingId) {
|
|
this.npcHomes.set(npcId, buildingId);
|
|
|
|
// Happiness boost
|
|
const happiness = this.npcHappiness.get(npcId) || 50;
|
|
this.npcHappiness.set(npcId, Math.min(100, happiness + 20));
|
|
|
|
console.log(`${this.settledNPCs.get(npcId).name} moved into home`);
|
|
}
|
|
|
|
/**
|
|
* Increase housing capacity
|
|
*/
|
|
addHousing(capacity) {
|
|
this.housingCapacity += capacity;
|
|
console.log(`Housing capacity: ${this.housingCapacity}`);
|
|
}
|
|
|
|
/**
|
|
* Get settlement statistics
|
|
*/
|
|
getSettlementStats() {
|
|
const npcs = Array.from(this.settledNPCs.values());
|
|
const avgHappiness = npcs.reduce((sum, npc) => sum + (this.npcHappiness.get(npc.id) || 0), 0) / npcs.length || 0;
|
|
const avgEfficiency = npcs.reduce((sum, npc) => sum + (this.npcEfficiency.get(npc.id) || 1), 0) / npcs.length || 1;
|
|
|
|
return {
|
|
population: npcs.length,
|
|
housingCapacity: this.housingCapacity,
|
|
averageHappiness: Math.round(avgHappiness),
|
|
averageEfficiency: avgEfficiency.toFixed(2),
|
|
assignedWorkers: this.assignments.size,
|
|
unemployed: npcs.length - this.assignments.size
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get NPCs by skill
|
|
*/
|
|
getNPCsBySkill(skill) {
|
|
return Array.from(this.settledNPCs.values()).filter(npc => npc.skills.includes(skill));
|
|
}
|
|
|
|
/**
|
|
* Save/Load
|
|
*/
|
|
getSaveData() {
|
|
return {
|
|
settledNPCs: Array.from(this.settledNPCs.entries()),
|
|
assignments: Array.from(this.assignments.entries()),
|
|
npcHappiness: Array.from(this.npcHappiness.entries()),
|
|
npcHomes: Array.from(this.npcHomes.entries()),
|
|
housingCapacity: this.housingCapacity
|
|
};
|
|
}
|
|
|
|
loadSaveData(data) {
|
|
this.settledNPCs = new Map(data.settledNPCs || []);
|
|
this.assignments = new Map(data.assignments || []);
|
|
this.npcHappiness = new Map(data.npcHappiness || []);
|
|
this.npcHomes = new Map(data.npcHomes || []);
|
|
this.housingCapacity = data.housingCapacity || 0;
|
|
|
|
// Recalculate efficiency
|
|
this.npcHappiness.forEach((happiness, npcId) => {
|
|
const efficiency = 0.5 + (happiness / 100) * 1.5;
|
|
this.npcEfficiency.set(npcId, efficiency);
|
|
});
|
|
}
|
|
}
|