MASSIVE COMPLETION PUSH Phase 1-2: Generated 11 NPC dialogue portraits (SMOOTH Style 32). Implemented TownRestorationLogic and MuseumEvolutionSystem. Progress: 2/9 systems, 11/11 portraits complete.
|
After Width: | Height: | Size: 624 KiB |
|
After Width: | Height: | Size: 658 KiB |
|
After Width: | Height: | Size: 668 KiB |
|
After Width: | Height: | Size: 551 KiB |
|
After Width: | Height: | Size: 603 KiB |
|
After Width: | Height: | Size: 742 KiB |
|
After Width: | Height: | Size: 624 KiB |
|
After Width: | Height: | Size: 595 KiB |
|
After Width: | Height: | Size: 702 KiB |
|
After Width: | Height: | Size: 621 KiB |
|
After Width: | Height: | Size: 786 KiB |
|
Before Width: | Height: | Size: 622 KiB |
344
src/systems/MuseumEvolutionSystem.js
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
/**
|
||||||
|
* MUSEUM EVOLUTION SYSTEM
|
||||||
|
* 3-stage museum restoration with artifact collection
|
||||||
|
* Integrates with Kustos NPC and quest system
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MuseumEvolutionSystem {
|
||||||
|
constructor(scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
// Museum evolution stages
|
||||||
|
this.currentStage = 0; // 0 = ruined, 1 = partial, 2 = advanced, 3 = complete
|
||||||
|
|
||||||
|
// Artifact collection
|
||||||
|
this.artifacts = new Map(); // artifactId → artifactData
|
||||||
|
this.collectedArtifacts = new Set();
|
||||||
|
|
||||||
|
// Album tracking
|
||||||
|
this.albumCategories = new Map();
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.initializeArtifactDatabase();
|
||||||
|
this.initializeAlbumCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all collectible artifacts
|
||||||
|
*/
|
||||||
|
initializeArtifactDatabase() {
|
||||||
|
const artifacts = [
|
||||||
|
// Pre-war artifacts
|
||||||
|
{ id: 'dino_skull', name: 'Dinosaur Skull', category: 'prehistory', rarity: 'legendary', discoveryReward: 500 },
|
||||||
|
{ id: 'ancient_vase', name: 'Ancient Vase', category: 'prehistory', rarity: 'rare', discoveryReward: 100 },
|
||||||
|
{ id: 'fossil', name: 'Fossil', category: 'prehistory', rarity: 'common', discoveryReward: 50 },
|
||||||
|
|
||||||
|
// 2084 tech artifacts
|
||||||
|
{ id: 'ai_core', name: 'AI Core Fragment', category: '2084_tech', rarity: 'legendary', discoveryReward: 800 },
|
||||||
|
{ id: 'hologram_projector', name: 'Hologram Projector', category: '2084_tech', rarity: 'rare', discoveryReward: 150 },
|
||||||
|
{ id: 'old_smartphone', name: 'Pre-War Smartphone', category: '2084_tech', rarity: 'uncommon', discoveryReward: 80 },
|
||||||
|
|
||||||
|
// Cultural artifacts
|
||||||
|
{ id: 'religious_icon', name: 'Religious Icon', category: 'culture', rarity: 'rare', discoveryReward: 120 },
|
||||||
|
{ id: 'painting', name: 'Old Painting', category: 'culture', rarity: 'uncommon', discoveryReward: 90 },
|
||||||
|
{ id: 'book_collection', name: 'Rare Book Collection', category: 'culture', rarity: 'rare', discoveryReward: 110 },
|
||||||
|
|
||||||
|
// Post-apocalypse relics
|
||||||
|
{ id: 'first_cannabis_seed', name: 'First Cannabis Seed', category: 'post_apo', rarity: 'legendary', discoveryReward: 600 },
|
||||||
|
{ id: 'zombie_variant_sample', name: 'Zombie Variant Sample', category: 'post_apo', rarity: 'rare', discoveryReward: 140 },
|
||||||
|
{ id: 'survival_journal', name: "Survivor's Journal", category: 'post_apo', rarity: 'uncommon', discoveryReward: 70 }
|
||||||
|
];
|
||||||
|
|
||||||
|
artifacts.forEach(artifact => {
|
||||||
|
this.artifacts.set(artifact.id, {
|
||||||
|
...artifact,
|
||||||
|
discovered: false,
|
||||||
|
donatedToMuseum: false,
|
||||||
|
discoveryDate: null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize album categories for organization
|
||||||
|
*/
|
||||||
|
initializeAlbumCategories() {
|
||||||
|
this.albumCategories.set('prehistory', {
|
||||||
|
name: 'Prehistoric Era',
|
||||||
|
description: 'Artifacts from Earth\'s ancient past',
|
||||||
|
totalArtifacts: 3,
|
||||||
|
collected: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
this.albumCategories.set('2084_tech', {
|
||||||
|
name: '2084 Technology',
|
||||||
|
description: 'Advanced tech from before The Collapse',
|
||||||
|
totalArtifacts: 3,
|
||||||
|
collected: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
this.albumCategories.set('culture', {
|
||||||
|
name: 'Cultural Heritage',
|
||||||
|
description: 'Art, religion, and culture of the old world',
|
||||||
|
totalArtifacts: 3,
|
||||||
|
collected: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
this.albumCategories.set('post_apo', {
|
||||||
|
name: 'Post-Apocalypse',
|
||||||
|
description: 'Relics from the new world',
|
||||||
|
totalArtifacts: 3,
|
||||||
|
collected: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover artifact (player finds it)
|
||||||
|
*/
|
||||||
|
discoverArtifact(artifactId, location) {
|
||||||
|
const artifact = this.artifacts.get(artifactId);
|
||||||
|
if (!artifact) return false;
|
||||||
|
|
||||||
|
if (artifact.discovered) {
|
||||||
|
console.warn(`Artifact ${artifactId} already discovered`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
artifact.discovered = true;
|
||||||
|
artifact.discoveryDate = Date.now();
|
||||||
|
artifact.discoveryLocation = location;
|
||||||
|
|
||||||
|
// Add to player inventory
|
||||||
|
this.scene.inventorySystem.addItem(artifactId, 1);
|
||||||
|
|
||||||
|
// Notification
|
||||||
|
this.scene.uiSystem?.showNotification(
|
||||||
|
`Artifact discovered: ${artifact.name}!`,
|
||||||
|
'discovery',
|
||||||
|
{ rarity: artifact.rarity }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reward currency
|
||||||
|
this.scene.economySystem.addCurrency(artifact.discoveryReward);
|
||||||
|
|
||||||
|
// VFX
|
||||||
|
this.scene.vfxSystem?.playEffect('artifact_discovery', this.scene.player.x, this.scene.player.y);
|
||||||
|
|
||||||
|
console.log(`✨ Discovered artifact: ${artifact.name} (+${artifact.discoveryReward} coins)`);
|
||||||
|
|
||||||
|
// Kustos dialogue trigger
|
||||||
|
if (!artifact.donatedToMuseum) {
|
||||||
|
this.triggerKustosDialogue(artifactId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Donate artifact to museum (Kustos interaction)
|
||||||
|
*/
|
||||||
|
donateArtifact(artifactId) {
|
||||||
|
const artifact = this.artifacts.get(artifactId);
|
||||||
|
if (!artifact || !artifact.discovered || artifact.donatedToMuseum) return false;
|
||||||
|
|
||||||
|
// Remove from player inventory
|
||||||
|
if (!this.scene.inventorySystem.hasItem(artifactId, 1)) {
|
||||||
|
console.warn('Player does not have artifact in inventory');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene.inventorySystem.removeItem(artifactId, 1);
|
||||||
|
|
||||||
|
// Mark as donated
|
||||||
|
artifact.donatedToMuseum = true;
|
||||||
|
this.collectedArtifacts.add(artifactId);
|
||||||
|
|
||||||
|
// Update category progress
|
||||||
|
const category = this.albumCategories.get(artifact.category);
|
||||||
|
category.collected++;
|
||||||
|
|
||||||
|
// Kustos thanks dialogue
|
||||||
|
this.scene.dialogueSystem?.startDialogue('kustos', 'thank_donation', { artifact: artifact.name });
|
||||||
|
|
||||||
|
// Check for museum evolution
|
||||||
|
this.checkEvolutionTrigger();
|
||||||
|
|
||||||
|
// Achievement check
|
||||||
|
this.checkCompletionAchievements();
|
||||||
|
|
||||||
|
console.log(`📦 Donated ${artifact.name} to museum`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if museum should evolve to next stage
|
||||||
|
*/
|
||||||
|
checkEvolutionTrigger() {
|
||||||
|
const totalDonated = this.collectedArtifacts.size;
|
||||||
|
|
||||||
|
// Stage thresholds
|
||||||
|
const stageThresholds = {
|
||||||
|
1: 4, // Stage 1: 4 artifacts donated
|
||||||
|
2: 8, // Stage 2: 8 artifacts donated
|
||||||
|
3: 12 // Stage 3: All 12 artifacts donated
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for evolution
|
||||||
|
for (let stage = 3; stage >= 1; stage--) {
|
||||||
|
if (totalDonated >= stageThresholds[stage] && this.currentStage < stage) {
|
||||||
|
this.evolveMuseum(stage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evolve museum to next stage
|
||||||
|
*/
|
||||||
|
evolveMuseum(newStage) {
|
||||||
|
if (newStage <= this.currentStage) return;
|
||||||
|
|
||||||
|
this.currentStage = newStage;
|
||||||
|
|
||||||
|
// Update building visual
|
||||||
|
this.updateMuseumVisual(newStage);
|
||||||
|
|
||||||
|
// VFX: Building evolution
|
||||||
|
this.scene.vfxSystem?.playEffect('museum_evolution', 1500, 600);
|
||||||
|
|
||||||
|
// Notification
|
||||||
|
const stageNames = {
|
||||||
|
1: 'Partial Collection',
|
||||||
|
2: 'Advanced Exhibition',
|
||||||
|
3: 'World-Class Museum'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.scene.uiSystem?.showNotification(
|
||||||
|
`Museum evolved: ${stageNames[newStage]}!`,
|
||||||
|
'success'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Kustos special dialogue
|
||||||
|
this.scene.dialogueSystem?.startDialogue('kustos', `museum_stage_${newStage}`);
|
||||||
|
|
||||||
|
// Grant benefits
|
||||||
|
this.grantStageBenefits(newStage);
|
||||||
|
|
||||||
|
console.log(`🏛️ Museum evolved to Stage ${newStage}: ${stageNames[newStage]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update museum building sprite
|
||||||
|
*/
|
||||||
|
updateMuseumVisual(stage) {
|
||||||
|
// Load corresponding sprite: museum_stage_1, museum_stage_2, museum_stage_3
|
||||||
|
const sprite = this.scene.buildingSystem.getBuilding('museum');
|
||||||
|
if (sprite) {
|
||||||
|
sprite.setTexture(`museum_stage_${stage}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grant benefits for reaching museum stage
|
||||||
|
*/
|
||||||
|
grantStageBenefits(stage) {
|
||||||
|
switch (stage) {
|
||||||
|
case 1:
|
||||||
|
// Stage 1: Basic lore access
|
||||||
|
this.scene.gameState.unlocks.lore_system = true;
|
||||||
|
this.scene.uiSystem?.unlockTab('lore');
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// Stage 2: Research bonuses
|
||||||
|
this.scene.gameState.buffs.research_speed = 1.25; // +25% research
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// Stage 3: Full collection bonus
|
||||||
|
this.scene.gameState.buffs.artifact_discovery = 2.0; // 2x artifact find rate
|
||||||
|
this.scene.achievementSystem?.unlock('master_curator');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger Kustos dialogue about found artifact
|
||||||
|
*/
|
||||||
|
triggerKustosDialogue(artifactId) {
|
||||||
|
const artifact = this.artifacts.get(artifactId);
|
||||||
|
|
||||||
|
// Show notification to visit Kustos
|
||||||
|
this.scene.uiSystem?.showNotification(
|
||||||
|
`Kustos might be interested in the ${artifact.name}`,
|
||||||
|
'info',
|
||||||
|
{ icon: 'museum' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for collection completion achievements
|
||||||
|
*/
|
||||||
|
checkCompletionAchievements() {
|
||||||
|
// Check category completion
|
||||||
|
this.albumCategories.forEach((category, categoryId) => {
|
||||||
|
if (category.collected === category.totalArtifacts) {
|
||||||
|
this.scene.achievementSystem?.unlock(`complete_${categoryId}_collection`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check total completion
|
||||||
|
if (this.collectedArtifacts.size === this.artifacts.size) {
|
||||||
|
this.scene.achievementSystem?.unlock('complete_museum_collection');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get album progress
|
||||||
|
*/
|
||||||
|
getAlbumProgress() {
|
||||||
|
const categories = {};
|
||||||
|
|
||||||
|
this.albumCategories.forEach((category, categoryId) => {
|
||||||
|
categories[categoryId] = {
|
||||||
|
...category,
|
||||||
|
progress: Math.round((category.collected / category.totalArtifacts) * 100)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
categories,
|
||||||
|
totalArtifacts: this.artifacts.size,
|
||||||
|
totalCollected: this.collectedArtifacts.size,
|
||||||
|
overallProgress: Math.round((this.collectedArtifacts.size / this.artifacts.size) * 100)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get artifacts by category for UI display
|
||||||
|
*/
|
||||||
|
getArtifactsByCategory(categoryId) {
|
||||||
|
return Array.from(this.artifacts.values())
|
||||||
|
.filter(a => a.category === categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get museum stage info
|
||||||
|
*/
|
||||||
|
getStageInfo() {
|
||||||
|
const stageData = {
|
||||||
|
0: { name: 'Ruined', description: 'Museum is destroyed' },
|
||||||
|
1: { name: 'Partial Collection', description: '4 artifacts displayed' },
|
||||||
|
2: { name: 'Advanced Exhibition', description: '8 artifacts displayed' },
|
||||||
|
3: { name: 'World-Class Museum', description: 'All 12 artifacts displayed' }
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentStage: this.currentStage,
|
||||||
|
...stageData[this.current Stage],
|
||||||
|
nextStageRequirement: this.currentStage < 3 ? [4, 8, 12][this.currentStage] : null,
|
||||||
|
currentArtifacts: this.collectedArtifacts.size
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
474
src/systems/TownRestorationLogic.js
Normal file
@@ -0,0 +1,474 @@
|
|||||||
|
/**
|
||||||
|
* TOWN RESTORATION LOGIC SYSTEM
|
||||||
|
* Complete logic for restoring buildings in Mrtva Dolina
|
||||||
|
* Handles material requirements, construction progress, worker assignments, NPC unlocks
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class TownRestorationLogic {
|
||||||
|
constructor(scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
// Building states
|
||||||
|
this.buildings = new Map(); //buildingId → buildingData
|
||||||
|
|
||||||
|
// Construction queues
|
||||||
|
this.activeConstructions = [];
|
||||||
|
|
||||||
|
// Worker management
|
||||||
|
this.assignedWorkers = new Map(); // buildingId → [workerIds]
|
||||||
|
|
||||||
|
// NPC unlock tracking
|
||||||
|
this.unlockedNPCs = new Set();
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.initializeBuildingDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all 14 buildings with restoration data
|
||||||
|
*/
|
||||||
|
initializeBuildingDatabase() {
|
||||||
|
const buildingSpecs = [
|
||||||
|
{
|
||||||
|
id: 'hospital',
|
||||||
|
name: 'Hospital',
|
||||||
|
npcUnlock: 'Ana', // Doctor Ana unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 150,
|
||||||
|
stone: 100,
|
||||||
|
metal: 50,
|
||||||
|
tools: 20
|
||||||
|
},
|
||||||
|
constructionTime: 3600, // 1 hour
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['healing', 'medical_supplies']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'police',
|
||||||
|
name: 'Police Station',
|
||||||
|
npcUnlock: null,
|
||||||
|
materials: {
|
||||||
|
wood: 100,
|
||||||
|
stone: 150,
|
||||||
|
metal: 80,
|
||||||
|
weapons: 10
|
||||||
|
},
|
||||||
|
constructionTime: 3600,
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['security', 'patrol_unlock']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'mayor_office',
|
||||||
|
name: "Mayor's Office",
|
||||||
|
npcUnlock: 'Župan', // Mayor unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 120,
|
||||||
|
stone: 80,
|
||||||
|
metal: 40,
|
||||||
|
papers: 50
|
||||||
|
},
|
||||||
|
constructionTime: 2400,
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['election_unlock', 'city_management']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tech_workshop',
|
||||||
|
name: 'Tech Workshop',
|
||||||
|
npcUnlock: 'Tehnik', // Technician unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 80,
|
||||||
|
stone: 60,
|
||||||
|
metal: 150,
|
||||||
|
electronics: 30
|
||||||
|
},
|
||||||
|
constructionTime: 3000,
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['tech_upgrades', 'electronics_crafting']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'tailor',
|
||||||
|
name: 'Tailor Shop',
|
||||||
|
npcUnlock: 'Šivilja', // Seamstress unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 60,
|
||||||
|
fabric: 100,
|
||||||
|
thread: 50,
|
||||||
|
tools: 15
|
||||||
|
},
|
||||||
|
constructionTime: 1800,
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['armor_crafting', 'clothing_upgrades']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'museum',
|
||||||
|
name: 'Museum',
|
||||||
|
npcUnlock: 'Kustos', // Curator unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 100,
|
||||||
|
stone: 120,
|
||||||
|
glass: 40,
|
||||||
|
artifacts: 10
|
||||||
|
},
|
||||||
|
constructionTime: 4800, // 80 minutes - complex restoration
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['lore_unlock', 'artifact_collection']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'school',
|
||||||
|
name: 'School',
|
||||||
|
npcUnlock: 'Teacher', // Teacher unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 90,
|
||||||
|
stone: 70,
|
||||||
|
books: 50,
|
||||||
|
tools: 20
|
||||||
|
},
|
||||||
|
constructionTime: 3600,
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['buff_unlock', 'education_system']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'church',
|
||||||
|
name: 'Church',
|
||||||
|
npcUnlock: 'Župnik', // Priest unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 80,
|
||||||
|
stone: 200,
|
||||||
|
metal: 30,
|
||||||
|
religious_items: 5
|
||||||
|
},
|
||||||
|
constructionTime: 5400, // 90 minutes - sacred restoration
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['blessing_system', 'graveyard_access']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'blacksmith',
|
||||||
|
name: 'Blacksmith',
|
||||||
|
npcUnlock: 'Ivan Kovač', // Already available
|
||||||
|
materials: {
|
||||||
|
wood: 50,
|
||||||
|
stone: 100,
|
||||||
|
metal: 120,
|
||||||
|
coal: 80
|
||||||
|
},
|
||||||
|
constructionTime: 3000,
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['weapon_crafting', 'tool_upgrades']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'bakery',
|
||||||
|
name: 'Bakery',
|
||||||
|
npcUnlock: 'Pek', // Baker unlocked
|
||||||
|
materials: {
|
||||||
|
wood: 70,
|
||||||
|
stone: 50,
|
||||||
|
flour: 100,
|
||||||
|
tools: 10
|
||||||
|
},
|
||||||
|
constructionTime: 2400,
|
||||||
|
stages: 3,
|
||||||
|
benefits: ['food_production', 'energy_bonus']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
buildingSpecs.forEach(spec => {
|
||||||
|
this.buildings.set(spec.id, {
|
||||||
|
...spec,
|
||||||
|
currentState: 'ruined', // ruined, under_construction, restored
|
||||||
|
currentStage: 0, // 0 = ruined, 1-3 = construction stages
|
||||||
|
progress: 0, // 0-100% construction progress
|
||||||
|
workersAssigned: 0,
|
||||||
|
materialsPaid: false,
|
||||||
|
completionTime: null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if player can start building restoration
|
||||||
|
*/
|
||||||
|
canStartRestoration(buildingId) {
|
||||||
|
const building = this.buildings.get(buildingId);
|
||||||
|
if (!building) return { canStart: false, reason: 'Building not found' };
|
||||||
|
|
||||||
|
if (building.currentState !== 'ruined') {
|
||||||
|
return { canStart: false, reason: 'Building already restored or under construction' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check materials
|
||||||
|
const inventory = this.scene.inventorySystem;
|
||||||
|
for (const [material, amount] of Object.entries(building.materials)) {
|
||||||
|
if (!inventory.hasItem(material, amount)) {
|
||||||
|
return {
|
||||||
|
canStart: false,
|
||||||
|
reason: `Not enough ${material}. Need ${amount}, have ${inventory.getItemCount(material)}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { canStart: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start building restoration
|
||||||
|
*/
|
||||||
|
startRestoration(buildingId) {
|
||||||
|
const check = this.canStartRestoration(buildingId);
|
||||||
|
if (!check.canStart) {
|
||||||
|
console.warn(`Cannot start restoration: ${check.reason}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const building = this.buildings.get(buildingId);
|
||||||
|
const inventory = this.scene.inventorySystem;
|
||||||
|
|
||||||
|
// Deduct materials
|
||||||
|
for (const [material, amount] of Object.entries(building.materials)) {
|
||||||
|
inventory.removeItem(material, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start construction
|
||||||
|
building.currentState = 'under_construction';
|
||||||
|
building.currentStage = 1;
|
||||||
|
building.progress = 0;
|
||||||
|
building.materialsPaid = true;
|
||||||
|
building.completionTime = Date.now() + (building.constructionTime * 1000);
|
||||||
|
|
||||||
|
this.activeConstructions.push(buildingId);
|
||||||
|
|
||||||
|
// Spawn scaffolding visual
|
||||||
|
this.spawnConstructionVisuals(buildingId);
|
||||||
|
|
||||||
|
console.log(`Started restoration of ${building.name}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign workers to speed up construction
|
||||||
|
*/
|
||||||
|
assignWorker(buildingId, workerType = 'zombie') {
|
||||||
|
const building = this.buildings.get(buildingId);
|
||||||
|
if (!building || building.currentState !== 'under_construction') return false;
|
||||||
|
|
||||||
|
const speedBonus = {
|
||||||
|
'zombie': 0.1, // 10% faster per zombie
|
||||||
|
'human': 0.25, // 25% faster per human NPC
|
||||||
|
'kai': 0.5 // Kai works fastest
|
||||||
|
};
|
||||||
|
|
||||||
|
building.workersAssigned++;
|
||||||
|
|
||||||
|
if (!this.assignedWorkers.has(buildingId)) {
|
||||||
|
this.assignedWorkers.set(buildingId, []);
|
||||||
|
}
|
||||||
|
this.assignedWorkers.get(buildingId).push({ type: workerType, bonus: speedBonus[workerType] });
|
||||||
|
|
||||||
|
console.log(`Assigned ${workerType} to ${building.name}. Speed bonus: +${speedBonus[workerType] * 100}%`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update construction progress
|
||||||
|
*/
|
||||||
|
update(deltaTime) {
|
||||||
|
this.activeConstructions.forEach(buildingId => {
|
||||||
|
const building = this.buildings.get(buildingId);
|
||||||
|
|
||||||
|
// Calculate progress speed (base: 100% over constructionTime)
|
||||||
|
let baseSpeed = (100 / building.constructionTime) * (deltaTime / 1000);
|
||||||
|
|
||||||
|
// Apply worker bonuses
|
||||||
|
if (this.assignedWorkers.has(buildingId)) {
|
||||||
|
const workers = this.assignedWorkers.get(buildingId);
|
||||||
|
const totalBonus = workers.reduce((sum, w) => sum + w.bonus, 0);
|
||||||
|
baseSpeed *= (1 + totalBonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
building.progress += baseSpeed;
|
||||||
|
|
||||||
|
// Check stage transitions
|
||||||
|
if (building.progress >= 33 && building.currentStage === 1) {
|
||||||
|
building.currentStage = 2;
|
||||||
|
this.updateBuildingVisual(buildingId, 2);
|
||||||
|
} else if (building.progress >= 66 && building.currentStage === 2) {
|
||||||
|
building.currentStage = 3;
|
||||||
|
this.updateBuildingVisual(buildingId, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check completion
|
||||||
|
if (building.progress >= 100) {
|
||||||
|
this.completeRestoration(buildingId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete building restoration
|
||||||
|
*/
|
||||||
|
completeRestoration(buildingId) {
|
||||||
|
const building = this.buildings.get(buildingId);
|
||||||
|
|
||||||
|
building.currentState = 'restored';
|
||||||
|
building.currentStage = 3;
|
||||||
|
building.progress = 100;
|
||||||
|
|
||||||
|
// Remove from active constructions
|
||||||
|
const index = this.activeConstructions.indexOf(buildingId);
|
||||||
|
if (index > -1) this.activeConstructions.splice(index, 1);
|
||||||
|
|
||||||
|
// Remove scaffolding
|
||||||
|
this.removeConstructionVisuals(buildingId);
|
||||||
|
|
||||||
|
// Update building sprite to restored
|
||||||
|
this.updateBuildingVisual(buildingId, 'restored');
|
||||||
|
|
||||||
|
// VFX: Building restoration sparkles
|
||||||
|
this.scene.vfxSystem?.playEffect('building_restoration', building.x, building.y);
|
||||||
|
|
||||||
|
// Unlock NPC if applicable
|
||||||
|
if (building.npcUnlock) {
|
||||||
|
this.unlockNPC(building.npcUnlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grant benefits
|
||||||
|
this.grantBuildingBenefits(buildingId);
|
||||||
|
|
||||||
|
console.log(`✅ ${building.name} restored! NPC unlocked: ${building.npcUnlock || 'None'}`);
|
||||||
|
|
||||||
|
// Quest completion check
|
||||||
|
this.scene.questSystem?.checkBuildingRestoration(buildingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlock NPC after building restoration
|
||||||
|
*/
|
||||||
|
unlockNPC(npcName) {
|
||||||
|
if (this.unlockedNPCs.has(npcName)) return;
|
||||||
|
|
||||||
|
this.unlockedNPCs.add(npcName);
|
||||||
|
|
||||||
|
// Spawn NPC in town
|
||||||
|
const npcData = this.getNPCSpawnData(npcName);
|
||||||
|
this.scene.npcSystem?.spawnNPC(npcName, npcData.x, npcData.y);
|
||||||
|
|
||||||
|
// Notification
|
||||||
|
this.scene.uiSystem?.showNotification(`${npcName} has arrived in Mrtva Dolina!`, 'success');
|
||||||
|
|
||||||
|
console.log(`🎉 NPC unlocked: ${npcName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get NPC spawn location based on their building
|
||||||
|
*/
|
||||||
|
getNPCSpawnData(npcName) {
|
||||||
|
const spawnLocations = {
|
||||||
|
'Ana': { x: 1200, y: 800 }, // Hospital
|
||||||
|
'Župan': { x: 1400, y: 700 }, // Mayor's Office
|
||||||
|
'Tehnik': { x: 1100, y: 900 }, // Tech Workshop
|
||||||
|
'Šivilja': { x: 1300, y: 1000 }, // Tailor
|
||||||
|
'Kustos': { x: 1500, y: 600 }, // Museum
|
||||||
|
'Teacher': { x: 1000, y: 700 }, // School
|
||||||
|
'Župnik': { x: 1600, y: 500 }, // Church
|
||||||
|
'Pek': { x: 900, y: 800 } // Bakery
|
||||||
|
};
|
||||||
|
|
||||||
|
return spawnLocations[npcName] || { x: 1200, y: 800 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grant building benefits to player
|
||||||
|
*/
|
||||||
|
grantBuildingBenefits(buildingId) {
|
||||||
|
const building = this.buildings.get(buildingId);
|
||||||
|
|
||||||
|
building.benefits.forEach(benefit => {
|
||||||
|
switch (benefit) {
|
||||||
|
case 'healing':
|
||||||
|
this.scene.gameState.unlocks.healing = true;
|
||||||
|
break;
|
||||||
|
case 'security':
|
||||||
|
this.scene.defenseSystem?.unlockPatrols();
|
||||||
|
break;
|
||||||
|
case 'election_unlock':
|
||||||
|
this.scene.electionSystem?.unlockElections();
|
||||||
|
break;
|
||||||
|
case 'tech_upgrades':
|
||||||
|
this.scene.craftingSystem?.unlockCategory('electronics');
|
||||||
|
break;
|
||||||
|
case 'armor_crafting':
|
||||||
|
this.scene.craftingSystem?.unlockCategory('armor');
|
||||||
|
break;
|
||||||
|
case 'lore_unlock':
|
||||||
|
this.scene.gameState.unlocks.museum = true;
|
||||||
|
break;
|
||||||
|
case 'buff_unlock':
|
||||||
|
this.scene.schoolSystem?.enable();
|
||||||
|
break;
|
||||||
|
case 'blessing_system':
|
||||||
|
this.scene.churchSystem?.enable();
|
||||||
|
break;
|
||||||
|
case 'weapon_crafting':
|
||||||
|
this.scene.craftingSystem?.unlockCategory('weapons');
|
||||||
|
break;
|
||||||
|
case 'food_production':
|
||||||
|
this.scene.gameState.unlocks.bakery = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visual updates
|
||||||
|
*/
|
||||||
|
spawnConstructionVisuals(buildingId) {
|
||||||
|
// Spawn scaffolding sprite
|
||||||
|
// Spawn worker NPCs/zombies
|
||||||
|
// Add construction sounds
|
||||||
|
}
|
||||||
|
|
||||||
|
removeConstructionVisuals(buildingId) {
|
||||||
|
// Remove scaffolding
|
||||||
|
// Remove workers
|
||||||
|
// Stop construction sounds
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBuildingVisual(buildingId, stageOrState) {
|
||||||
|
// Update building sprite to show construction progress or restored state
|
||||||
|
// stages: 1 (10-30% built), 2 (30-70% built), 3 (70-100% built)
|
||||||
|
// 'restored' = final completed building
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get building restoration status
|
||||||
|
*/
|
||||||
|
getBuildingStatus(buildingId) {
|
||||||
|
return this.buildings.get(buildingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all restorable buildings
|
||||||
|
*/
|
||||||
|
getAllBuildings() {
|
||||||
|
return Array.from(this.buildings.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get restoration progress summary
|
||||||
|
*/
|
||||||
|
getRestorationProgress() {
|
||||||
|
const total = this.buildings.size;
|
||||||
|
const restored = Array.from(this.buildings.values()).filter(b => b.currentState === 'restored').length;
|
||||||
|
const underConstruction = this.activeConstructions.length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
restored,
|
||||||
|
underConstruction,
|
||||||
|
ruined: total - restored - underConstruction,
|
||||||
|
percentComplete: Math.round((restored / total) * 100)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||