Files
novafarma/EMERGENCY_SYSTEMS_RECOVERY/NPCPopulationSystem.js
2026-01-16 02:43:46 +01:00

338 lines
10 KiB
JavaScript

/**
* 👥 NPC POPULATION SYSTEM
* Spawns and manages NPCs in structures across the world
* - Biome-specific NPCs
* - Dialog system
* - Trading functionality
* - Quest giving
*/
class NPCPopulationSystem {
constructor(scene) {
this.scene = scene;
// All NPCs in the world
this.npcs = [];
// NPC types per biome
this.npcTypes = {
'Grassland': ['farmer', 'blacksmith', 'merchant', 'guard'],
'Forest': ['hunter', 'herbalist', 'ranger', 'druid'],
'Desert': ['nomad', 'treasure_hunter', 'merchant', 'archaeologist'],
'Mountain': ['miner', 'dwarf', 'geologist', 'mountaineer'],
'Swamp': ['witch', 'alchemist', 'hermit', 'shaman']
};
// Dialog templates
this.dialogs = {
'farmer': [
"Welcome to my farm! Need any seeds?",
"The harvest this year is bountiful!",
"I sell the best wheat in the land!"
],
'merchant': [
"Looking to buy or sell? I've got the best deals!",
"Welcome, traveler! Check out my wares!",
"Gold for goods, goods for gold!"
],
'hunter': [
"The forest is full of game. Happy hunting!",
"Watch out for the wolves at night.",
"I can sell you some arrows if you need them."
],
'nomad': [
"The desert holds many secrets...",
"Water is more valuable than gold here.",
"I've traveled far and wide, seen many things."
],
'miner': [
"These mountains are rich with ore!",
"I can sell you some iron if you need it.",
"Watch your step in those caves!"
],
'witch': [
"Potions and hexes, what do you need?",
"The swamp holds ancient magic...",
"I can brew you something special."
]
};
// Trade goods per NPC type
this.tradeGoods = {
'farmer': [
{ item: 'wheat_seeds', price: 10, stock: 50 },
{ item: 'wheat', price: 5, stock: 100 },
{ item: 'bread', price: 15, stock: 20 }
],
'merchant': [
{ item: 'wood', price: 8, stock: 200 },
{ item: 'stone', price: 6, stock: 150 },
{ item: 'iron_ore', price: 20, stock: 50 }
],
'blacksmith': [
{ item: 'iron_sword', price: 150, stock: 5 },
{ item: 'iron_pickaxe', price: 100, stock: 10 },
{ item: 'iron_axe', price: 120, stock: 8 }
],
'hunter': [
{ item: 'arrow', price: 5, stock: 100 },
{ item: 'bow', price: 80, stock: 3 },
{ item: 'meat', price: 20, stock: 30 }
]
};
console.log('👥 NPCPopulationSystem initialized');
}
// Populate structures with NPCs
populateStructures(structureSystem) {
if (!structureSystem) return;
let npcsSpawned = 0;
// Spawn NPCs in structures (30% chance)
for (const structure of structureSystem.structures) {
if (Math.random() < 0.3) {
const npcType = this.selectNPCType(structure.biome);
this.spawnNPC(structure.x, structure.y, npcType, structure.biome);
npcsSpawned++;
}
}
// Always spawn special NPCs at landmarks
for (const landmark of structureSystem.landmarks) {
this.spawnNPC(landmark.x, landmark.y, 'quest_giver', landmark.type, true);
npcsSpawned++;
}
console.log(`✅ Spawned ${npcsSpawned} NPCs in structures`);
}
// Select NPC type for biome
selectNPCType(biome) {
const types = this.npcTypes[biome] || this.npcTypes['Grassland'];
return types[Math.floor(Math.random() * types.length)];
}
// Spawn single NPC
spawnNPC(x, y, type, biome, isQuestGiver = false) {
const npc = {
x,
y,
type,
biome,
isQuestGiver,
dialogIndex: 0,
hasQuest: isQuestGiver,
questCompleted: false,
sprite: null
};
this.npcs.push(npc);
return npc;
}
// Create NPC sprite (called when chunk loads)
createNPCSprite(npc, chunk) {
if (npc.sprite) return; // Already has sprite
const worldX = npc.x * 48 + 24;
const worldY = npc.y * 48 + 24;
// Create simple circle sprite for NPC
const color = this.getNPCColor(npc.type);
const sprite = this.scene.add.circle(worldX, worldY, 12, color);
sprite.setDepth(10 + worldY);
// Add name label
const label = this.scene.add.text(worldX, worldY - 20, npc.type, {
fontSize: '12px',
color: '#ffffff',
backgroundColor: '#000000',
padding: { x: 4, y: 2 }
});
label.setOrigin(0.5);
label.setDepth(10 + worldY);
// Quest marker for quest givers
if (npc.isQuestGiver && !npc.questCompleted) {
const questMarker = this.scene.add.text(worldX, worldY - 35, '!', {
fontSize: '24px',
color: '#FFD700',
fontStyle: 'bold'
});
questMarker.setOrigin(0.5);
questMarker.setDepth(10 + worldY);
npc.questMarker = questMarker;
if (chunk) chunk.sprites.push(questMarker);
}
npc.sprite = sprite;
npc.label = label;
if (chunk) {
chunk.sprites.push(sprite);
chunk.sprites.push(label);
}
}
// Get NPC color
getNPCColor(type) {
const colors = {
'farmer': 0x8B4513,
'merchant': 0xDAA520,
'blacksmith': 0x696969,
'hunter': 0x228B22,
'nomad': 0xD2691E,
'miner': 0x708090,
'witch': 0x9370DB,
'quest_giver': 0xFFD700
};
return colors[type] || 0x808080;
}
// Check for nearby NPCs
update(playerX, playerY) {
let nearestNPC = null;
let minDist = 3;
for (const npc of this.npcs) {
const dist = Math.sqrt((npc.x - playerX) ** 2 + (npc.y - playerY) ** 2);
if (dist < minDist) {
minDist = dist;
nearestNPC = npc;
}
}
if (nearestNPC && !this.currentNPC) {
this.showTalkPrompt(nearestNPC);
this.currentNPC = nearestNPC;
} else if (!nearestNPC && this.currentNPC) {
this.hideTalkPrompt();
this.currentNPC = null;
}
}
// Show prompt to talk
showTalkPrompt(npc) {
if (this.talkPrompt) return;
const promptText = npc.isQuestGiver
? '💬 Press T to talk (QUEST AVAILABLE)'
: `💬 Press T to talk to ${npc.type}`;
this.talkPrompt = this.scene.add.text(
this.scene.cameras.main.centerX,
this.scene.cameras.main.height - 100,
promptText,
{
fontSize: '24px',
color: npc.isQuestGiver ? '#FFD700' : '#ffffff',
backgroundColor: '#000000',
padding: { x: 20, y: 10 }
}
);
this.talkPrompt.setOrigin(0.5);
this.talkPrompt.setScrollFactor(0);
this.talkPrompt.setDepth(10000);
}
// Hide talk prompt
hideTalkPrompt() {
if (this.talkPrompt) {
this.talkPrompt.destroy();
this.talkPrompt = null;
}
}
// Talk to NPC (T key)
talkToNPC() {
if (!this.currentNPC) return;
const npc = this.currentNPC;
// Get dialog
const dialogs = this.dialogs[npc.type] || ["Hello, traveler!"];
const dialog = dialogs[npc.dialogIndex % dialogs.length];
npc.dialogIndex++;
// Show dialog box
this.showDialog(npc, dialog);
}
// Show dialog UI
showDialog(npc, text) {
// Close existing dialog
if (this.dialogBox) {
this.dialogBox.destroy();
this.dialogText.destroy();
this.dialogBox = null;
}
// Create dialog box
const centerX = this.scene.cameras.main.centerX;
const centerY = this.scene.cameras.main.height - 150;
this.dialogBox = this.scene.add.rectangle(
centerX, centerY,
600, 120,
0x000000, 0.8
);
this.dialogBox.setStrokeStyle(3, 0xFFFFFF);
this.dialogBox.setScrollFactor(0);
this.dialogBox.setDepth(10001);
this.dialogText = this.scene.add.text(
centerX, centerY - 30,
`${npc.type}:\n${text}`,
{
fontSize: '20px',
color: '#ffffff',
align: 'center',
wordWrap: { width: 550 }
}
);
this.dialogText.setOrigin(0.5, 0);
this.dialogText.setScrollFactor(0);
this.dialogText.setDepth(10002);
// Auto-close after 4 seconds
this.scene.time.delayedCall(4000, () => {
if (this.dialogBox) {
this.dialogBox.destroy();
this.dialogText.destroy();
this.dialogBox = null;
}
});
}
// Get statistics
getStats() {
const byBiome = {};
for (const npc of this.npcs) {
byBiome[npc.biome] = (byBiome[npc.biome] || 0) + 1;
}
return {
totalNPCs: this.npcs.length,
questGivers: this.npcs.filter(n => n.isQuestGiver).length,
byBiome
};
}
destroy() {
this.hideTalkPrompt();
if (this.dialogBox) {
this.dialogBox.destroy();
this.dialogText.destroy();
}
this.npcs.forEach(npc => {
if (npc.sprite) npc.sprite.destroy();
if (npc.label) npc.label.destroy();
if (npc.questMarker) npc.questMarker.destroy();
});
this.npcs = [];
}
}