Files
novafarma/src/systems/StructureInteractionSystem.js

372 lines
12 KiB
JavaScript

/**
* 🏛️ STRUCTURE INTERACTION SYSTEM
* Enables player interaction with structures in the world
* - Enter buildings
* - Loot chests
* - Landmark treasures
* - Lock/unlock mechanics
*/
class StructureInteractionSystem {
constructor(scene) {
this.scene = scene;
// Interaction markers
this.interactionMarkers = [];
// Loot chests (generated per structure)
this.chests = new Map(); // key: "x,y" → chest data
// Active interactions
this.nearbyStructures = [];
this.currentInteraction = null;
// Loot tables per biome
this.lootTables = {
'Grassland': [
{ item: 'wheat_seeds', min: 5, max: 15, chance: 0.7 },
{ item: 'wood', min: 10, max: 30, chance: 0.8 },
{ item: 'gold', min: 20, max: 50, chance: 0.5 },
{ item: 'iron_ore', min: 1, max: 5, chance: 0.3 }
],
'Forest': [
{ item: 'wood', min: 20, max: 50, chance: 0.9 },
{ item: 'apple', min: 3, max: 10, chance: 0.6 },
{ item: 'berry', min: 5, max: 15, chance: 0.7 },
{ item: 'mushroom', min: 2, max: 8, chance: 0.4 }
],
'Desert': [
{ item: 'gold', min: 50, max: 150, chance: 0.6 },
{ item: 'ruby', min: 1, max: 3, chance: 0.2 },
{ item: 'ancient_scroll', min: 1, max: 1, chance: 0.1 },
{ item: 'cactus_fruit', min: 3, max: 10, chance: 0.5 }
],
'Mountain': [
{ item: 'iron_ore', min: 10, max: 30, chance: 0.8 },
{ item: 'gold_ore', min: 5, max: 15, chance: 0.6 },
{ item: 'diamond', min: 1, max: 3, chance: 0.15 },
{ item: 'stone', min: 20, max: 50, chance: 0.9 }
],
'Swamp': [
{ item: 'herbs', min: 5, max: 20, chance: 0.7 },
{ item: 'mushroom', min: 10, max: 30, chance: 0.8 },
{ item: 'slime', min: 5, max: 15, chance: 0.6 },
{ item: 'ancient_bone', min: 1, max: 5, chance: 0.3 }
]
};
// Landmark treasure (special rare items)
this.landmarkTreasures = {
'ancient_temple': [
{ item: 'legendary_sword', min: 1, max: 1, chance: 1.0 },
{ item: 'gold', min: 500, max: 1000, chance: 1.0 },
{ item: 'ancient_artifact', min: 1, max: 1, chance: 1.0 }
],
'great_pyramid': [
{ item: 'pharaoh_staff', min: 1, max: 1, chance: 1.0 },
{ item: 'ruby', min: 10, max: 20, chance: 1.0 },
{ item: 'mummy_wraps', min: 5, max: 10, chance: 1.0 }
],
'mountain_peak': [
{ item: 'titan_hammer', min: 1, max: 1, chance: 1.0 },
{ item: 'diamond', min: 10, max: 20, chance: 1.0 },
{ item: 'eagle_feather', min: 1, max: 1, chance: 1.0 }
],
'abandoned_city': [
{ item: 'ancient_key', min: 1, max: 1, chance: 1.0 },
{ item: 'gold', min: 1000, max: 2000, chance: 1.0 },
{ item: 'city_map', min: 1, max: 1, chance: 1.0 }
],
'dragon_skeleton': [
{ item: 'dragon_scale', min: 5, max: 10, chance: 1.0 },
{ item: 'dragon_tooth', min: 1, max: 3, chance: 1.0 },
{ item: 'dragon_heart', min: 1, max: 1, chance: 1.0 }
]
};
console.log('🏛️ StructureInteractionSystem initialized');
}
// Generate chests for all structures
generateChestsForStructures(structureSystem) {
if (!structureSystem) return;
let chestsGenerated = 0;
// Regular structures
for (const structure of structureSystem.structures) {
// 70% chance to have a chest
if (Math.random() < 0.7) {
const chestKey = `${structure.x},${structure.y}`;
this.chests.set(chestKey, {
x: structure.x,
y: structure.y,
biome: structure.biome,
opened: false,
loot: this.generateLoot(structure.biome)
});
chestsGenerated++;
}
}
// Landmarks (always have treasure)
for (const landmark of structureSystem.landmarks) {
const chestKey = `${landmark.x},${landmark.y}`;
this.chests.set(chestKey, {
x: landmark.x,
y: landmark.y,
type: 'landmark',
landmarkType: landmark.type,
opened: false,
loot: this.generateLandmarkTreasure(landmark.type)
});
chestsGenerated++;
}
console.log(`✅ Generated ${chestsGenerated} chests (${this.chests.size} total)`);
}
// Generate loot based on biome
generateLoot(biome) {
const lootTable = this.lootTables[biome] || this.lootTables['Grassland'];
const loot = [];
for (const entry of lootTable) {
if (Math.random() < entry.chance) {
const amount = Math.floor(Math.random() * (entry.max - entry.min + 1)) + entry.min;
loot.push({
item: entry.item,
amount: amount
});
}
}
return loot;
}
// Generate landmark treasure (always guaranteed)
generateLandmarkTreasure(landmarkType) {
const treasureTable = this.landmarkTreasures[landmarkType];
if (!treasureTable) return [];
const loot = [];
for (const entry of treasureTable) {
const amount = Math.floor(Math.random() * (entry.max - entry.min + 1)) + entry.min;
loot.push({
item: entry.item,
amount: amount,
legendary: true // Mark as legendary loot
});
}
return loot;
}
// Check for nearby structures
update(playerX, playerY) {
this.nearbyStructures = [];
// Check all chests
for (const [key, chest] of this.chests) {
const dist = Math.sqrt((chest.x - playerX) ** 2 + (chest.y - playerY) ** 2);
if (dist < 3 && !chest.opened) {
this.nearbyStructures.push({
type: chest.type === 'landmark' ? 'landmark' : 'structure',
x: chest.x,
y: chest.y,
key: key,
data: chest
});
}
}
// Show interaction prompt if nearby
if (this.nearbyStructures.length > 0 && !this.currentInteraction) {
this.showInteractionPrompt();
} else if (this.nearbyStructures.length === 0) {
this.hideInteractionPrompt();
}
}
// Show UI prompt to interact
showInteractionPrompt() {
if (this.interactionPrompt) return;
const nearest = this.nearbyStructures[0];
const promptText = nearest.type === 'landmark'
? '⭐ Press E to open LEGENDARY TREASURE'
: '📦 Press E to open chest';
this.interactionPrompt = this.scene.add.text(
this.scene.cameras.main.centerX,
this.scene.cameras.main.height - 100,
promptText,
{
fontSize: '24px',
fontFamily: 'Arial',
color: nearest.type === 'landmark' ? '#FFD700' : '#ffffff',
backgroundColor: '#000000',
padding: { x: 20, y: 10 }
}
);
this.interactionPrompt.setOrigin(0.5);
this.interactionPrompt.setScrollFactor(0);
this.interactionPrompt.setDepth(10000);
}
// Hide interaction prompt
hideInteractionPrompt() {
if (this.interactionPrompt) {
this.interactionPrompt.destroy();
this.interactionPrompt = null;
}
}
// Player presses E to interact
interact() {
if (this.nearbyStructures.length === 0) return;
const nearest = this.nearbyStructures[0];
this.openChest(nearest.key, nearest.data);
}
// Open chest and give loot to player
openChest(chestKey, chestData) {
if (chestData.opened) return;
// Mark as opened
chestData.opened = true;
this.chests.set(chestKey, chestData);
// Give loot to player
const inventory = this.scene.inventorySystem;
if (!inventory) {
console.warn('⚠️ InventorySystem not found');
return;
}
console.log(`📦 Opening chest at (${chestData.x}, ${chestData.y})`);
let totalValue = 0;
for (const lootItem of chestData.loot) {
if (lootItem.item === 'gold') {
inventory.gold += lootItem.amount;
totalValue += lootItem.amount;
} else {
inventory.addItem(lootItem.item, lootItem.amount);
totalValue += lootItem.amount * 10; // Estimate value
}
console.log(` + ${lootItem.amount}x ${lootItem.item}${lootItem.legendary ? ' (LEGENDARY!)' : ''}`);
}
// Show loot notification
this.showLootNotification(chestData, totalValue);
// Play sound
if (this.scene.soundManager) {
this.scene.soundManager.beepPickup();
}
// Hide prompt
this.hideInteractionPrompt();
}
// Show loot notification UI
showLootNotification(chestData, totalValue) {
const isLandmark = chestData.type === 'landmark';
const notification = this.scene.add.text(
this.scene.cameras.main.centerX,
this.scene.cameras.main.centerY - 100,
isLandmark
? `⭐ LEGENDARY TREASURE FOUND! ⭐\n${chestData.landmarkType}\nValue: ${totalValue} gold`
: `📦 Chest Opened!\nValue: ${totalValue} gold`,
{
fontSize: isLandmark ? '32px' : '24px',
fontFamily: 'Arial',
color: isLandmark ? '#FFD700' : '#ffffff',
backgroundColor: '#000000',
padding: { x: 30, y: 20 },
align: 'center'
}
);
notification.setOrigin(0.5);
notification.setScrollFactor(0);
notification.setDepth(10001);
// Fade out after 3 seconds
this.scene.tweens.add({
targets: notification,
alpha: 0,
duration: 1000,
delay: 2000,
onComplete: () => notification.destroy()
});
// Particle effect
if (this.scene.particleEffects && isLandmark) {
// Golden particles for landmarks
for (let i = 0; i < 20; i++) {
const particle = this.scene.add.circle(
this.scene.cameras.main.centerX + (Math.random() - 0.5) * 100,
this.scene.cameras.main.centerY - 100,
5,
0xFFD700
);
particle.setScrollFactor(0);
particle.setDepth(10000);
this.scene.tweens.add({
targets: particle,
y: particle.y - 100,
alpha: 0,
duration: 1500,
onComplete: () => particle.destroy()
});
}
}
}
// Get statistics
getStats() {
const opened = Array.from(this.chests.values()).filter(c => c.opened).length;
return {
totalChests: this.chests.size,
chestsOpened: opened,
chestsRemaining: this.chests.size - opened
};
}
// Export/Import for save system
exportData() {
const chestsArray = [];
for (const [key, chest] of this.chests) {
chestsArray.push({
key,
opened: chest.opened
});
}
return { chests: chestsArray };
}
importData(data) {
if (!data || !data.chests) return;
for (const savedChest of data.chests) {
if (this.chests.has(savedChest.key)) {
const chest = this.chests.get(savedChest.key);
chest.opened = savedChest.opened;
this.chests.set(savedChest.key, chest);
}
}
}
destroy() {
this.hideInteractionPrompt();
this.interactionMarkers.forEach(m => m.destroy());
this.chests.clear();
}
}