ok
This commit is contained in:
371
EMERGENCY_SYSTEMS_RECOVERY/StructureInteractionSystem.js
Normal file
371
EMERGENCY_SYSTEMS_RECOVERY/StructureInteractionSystem.js
Normal file
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* 🏛️ 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user