📚 DOCUMENTATION COMPLETE: AUDIO_ASSET_MANIFEST.md (61 files detailed) and VFX_IMPLEMENTATION_GUIDE.md (6 systems with code examples). Ready for asset production phase.
This commit is contained in:
341
src/systems/NPCSettlementSystem.js
Normal file
341
src/systems/NPCSettlementSystem.js
Normal file
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user