576 lines
18 KiB
JavaScript
576 lines
18 KiB
JavaScript
/**
|
||
* PYRAMID SYSTEM
|
||
* Manages the 3 Great Pyramids, Sphinx, player-buildable pyramids, and pyramid benefits.
|
||
*
|
||
* Features:
|
||
* - 3 Great Pyramids: Dungeons with chambers, traps, puzzles, Mummy Pharaoh boss
|
||
* - The Sphinx: 3 riddles system, boss fight on wrong answer, blueprint reward
|
||
* - Player-Buildable Pyramids: 3 sizes (Small, Medium, Great), 2,000-15,000 Sandstone cost
|
||
* - Pyramid Benefits: Tourist income (+500 Zł/day), healing buff, respawn point
|
||
*/
|
||
class PyramidSystem {
|
||
constructor(scene) {
|
||
this.scene = scene;
|
||
|
||
// Great Pyramids (world landmarks)
|
||
this.greatPyramids = [
|
||
{
|
||
id: 'pyramid_of_khufu',
|
||
name: 'Pyramid of Khufu',
|
||
position: { x: 6000, y: 4000 },
|
||
biome: 'desert',
|
||
floors: 5,
|
||
chambers: ['Grand Gallery', 'King\'s Chamber', 'Queen\'s Chamber', 'Subterranean Chamber'],
|
||
boss: 'pharaoh_khufu',
|
||
explored: false,
|
||
treasures: ['golden_mask', 'pharaoh_staff', 'blueprint_time_machine']
|
||
},
|
||
{
|
||
id: 'pyramid_of_khafre',
|
||
name: 'Pyramid of Khafre',
|
||
position: { x: 6200, y: 4100 },
|
||
biome: 'desert',
|
||
floors: 4,
|
||
chambers: ['Entrance Corridor', 'Burial Chamber', 'Treasure Room'],
|
||
boss: 'pharaoh_khafre',
|
||
explored: false,
|
||
treasures: ['ancient_scroll', 'scarab_amulet', 'blueprint_cloning_vat']
|
||
},
|
||
{
|
||
id: 'pyramid_of_menkaure',
|
||
name: 'Pyramid of Menkaure',
|
||
position: { x: 6100, y: 4200 },
|
||
biome: 'desert',
|
||
floors: 3,
|
||
chambers: ['Main Chamber', 'Sarcophagus Hall'],
|
||
boss: 'pharaoh_menkaure',
|
||
explored: false,
|
||
treasures: ['royal_scepter', 'papyrus_collection', 'blueprint_helicopter']
|
||
}
|
||
];
|
||
|
||
// The Sphinx
|
||
this.sphinx = {
|
||
position: { x: 6150, y: 4050 },
|
||
riddlesAsked: [],
|
||
defeated: false,
|
||
riddles: [
|
||
{
|
||
id: 'riddle_1',
|
||
question: 'What walks on four legs in the morning, two legs at noon, and three legs in the evening?',
|
||
answer: 'human',
|
||
alternatives: ['man', 'person', 'humanity']
|
||
},
|
||
{
|
||
id: 'riddle_2',
|
||
question: 'I have cities but no houses, forests but no trees, and water but no fish. What am I?',
|
||
answer: 'map',
|
||
alternatives: ['a map', 'the map']
|
||
},
|
||
{
|
||
id: 'riddle_3',
|
||
question: 'The more you take, the more you leave behind. What am I?',
|
||
answer: 'footsteps',
|
||
alternatives: ['steps', 'footprints', 'tracks']
|
||
},
|
||
{
|
||
id: 'riddle_4',
|
||
question: 'What has keys but no locks, space but no room, and you can enter but can\'t go inside?',
|
||
answer: 'keyboard',
|
||
alternatives: ['a keyboard']
|
||
},
|
||
{
|
||
id: 'riddle_5',
|
||
question: 'I speak without a mouth and hear without ears. I have no body, but come alive with wind. What am I?',
|
||
answer: 'echo',
|
||
alternatives: ['an echo']
|
||
}
|
||
],
|
||
reward: 'blueprint_sphinx_statue'
|
||
};
|
||
|
||
// Player-buildable pyramids
|
||
this.playerPyramids = new Map(); // pyramidId -> pyramid data
|
||
|
||
// Pyramid sizes
|
||
this.pyramidSizes = {
|
||
small: {
|
||
name: 'Small Pyramid',
|
||
cost: { sandstone: 2000, gold: 500 },
|
||
size: { width: 10, height: 8 },
|
||
buildTime: 3, // days
|
||
benefits: { tourism: 100, healing: 0.05, respawn: false }
|
||
},
|
||
medium: {
|
||
name: 'Medium Pyramid',
|
||
cost: { sandstone: 7000, gold: 2000 },
|
||
size: { width: 20, height: 16 },
|
||
buildTime: 7,
|
||
benefits: { tourism: 300, healing: 0.10, respawn: true }
|
||
},
|
||
great: {
|
||
name: 'Great Pyramid',
|
||
cost: { sandstone: 15000, gold: 5000, limestone: 3000 },
|
||
size: { width: 40, height: 32 },
|
||
buildTime: 14,
|
||
benefits: { tourism: 500, healing: 0.20, respawn: true }
|
||
}
|
||
};
|
||
|
||
// Dungeon traps
|
||
this.trapTypes = ['arrow_trap', 'spike_floor', 'falling_ceiling', 'poison_dart', 'sand_trap', 'cursed_scarab'];
|
||
|
||
// Puzzles
|
||
this.puzzleTypes = ['weight_puzzle', 'hieroglyph_puzzle', 'mirror_puzzle', 'torch_puzzle', 'statue_puzzle'];
|
||
|
||
console.log('🏜️ Pyramid System initialized!');
|
||
}
|
||
|
||
/**
|
||
* Enter a Great Pyramid dungeon
|
||
*/
|
||
enterPyramid(pyramidId) {
|
||
const pyramid = this.greatPyramids.find(p => p.id === pyramidId);
|
||
if (!pyramid) {
|
||
console.log(`❌ Pyramid not found: ${pyramidId}`);
|
||
return false;
|
||
}
|
||
|
||
console.log(`🏜️ Entering ${pyramid.name}...`);
|
||
|
||
// Generate dungeon layout
|
||
const dungeon = this.generateDungeon(pyramid);
|
||
|
||
// Load dungeon scene
|
||
this.scene.events.emit('enter-dungeon', {
|
||
pyramid: pyramid,
|
||
dungeon: dungeon
|
||
});
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Generate dungeon layout
|
||
*/
|
||
generateDungeon(pyramid) {
|
||
const dungeon = {
|
||
floors: [],
|
||
totalChambers: pyramid.chambers.length,
|
||
totalTraps: pyramid.floors * 3,
|
||
totalPuzzles: pyramid.floors
|
||
};
|
||
|
||
for (let floor = 0; floor < pyramid.floors; floor++) {
|
||
const floorData = {
|
||
level: floor + 1,
|
||
chambers: this.generateFloorChambers(floor, pyramid),
|
||
traps: this.generateTraps(3),
|
||
puzzle: this.generatePuzzle()
|
||
};
|
||
|
||
dungeon.floors.push(floorData);
|
||
}
|
||
|
||
// Final floor has boss
|
||
dungeon.floors[pyramid.floors - 1].boss = pyramid.boss;
|
||
dungeon.floors[pyramid.floors - 1].treasures = pyramid.treasures;
|
||
|
||
return dungeon;
|
||
}
|
||
|
||
/**
|
||
* Generate chambers for a floor
|
||
*/
|
||
generateFloorChambers(floorLevel, pyramid) {
|
||
const chambersPerFloor = Math.ceil(pyramid.chambers.length / pyramid.floors);
|
||
const startIndex = floorLevel * chambersPerFloor;
|
||
const endIndex = Math.min(startIndex + chambersPerFloor, pyramid.chambers.length);
|
||
|
||
return pyramid.chambers.slice(startIndex, endIndex);
|
||
}
|
||
|
||
/**
|
||
* Generate traps
|
||
*/
|
||
generateTraps(count) {
|
||
const traps = [];
|
||
for (let i = 0; i < count; i++) {
|
||
const trapType = this.trapTypes[Math.floor(Math.random() * this.trapTypes.length)];
|
||
traps.push({
|
||
type: trapType,
|
||
damage: 20 + (i * 10),
|
||
disarmable: Math.random() > 0.3,
|
||
position: { x: Math.random() * 100, y: Math.random() * 100 }
|
||
});
|
||
}
|
||
return traps;
|
||
}
|
||
|
||
/**
|
||
* Generate puzzle
|
||
*/
|
||
generatePuzzle() {
|
||
const puzzleType = this.puzzleTypes[Math.floor(Math.random() * this.puzzleTypes.length)];
|
||
|
||
const puzzles = {
|
||
weight_puzzle: {
|
||
type: 'weight_puzzle',
|
||
description: 'Balance the scales to open the door.',
|
||
solution: 'Place 3 golden weights on left, 2 silver on right'
|
||
},
|
||
hieroglyph_puzzle: {
|
||
type: 'hieroglyph_puzzle',
|
||
description: 'Match the hieroglyphs to their meanings.',
|
||
solution: 'Sun-Life, Ankh-Eternal, Eye-Protection, Scarab-Rebirth'
|
||
},
|
||
mirror_puzzle: {
|
||
type: 'mirror_puzzle',
|
||
description: 'Reflect the light beam to hit all 4 crystals.',
|
||
solution: 'Rotate mirrors at 45° angles'
|
||
},
|
||
torch_puzzle: {
|
||
type: 'torch_puzzle',
|
||
description: 'Light the torches in the correct order.',
|
||
solution: 'North, East, South, West (clockwise from North)'
|
||
},
|
||
statue_puzzle: {
|
||
type: 'statue_puzzle',
|
||
description: 'Turn the statues to face the center altar.',
|
||
solution: 'All 4 statues facing inward'
|
||
}
|
||
};
|
||
|
||
return puzzles[puzzleType];
|
||
}
|
||
|
||
/**
|
||
* Fight Mummy Pharaoh boss
|
||
*/
|
||
fightPharaoh(pharaohId) {
|
||
const pharaohs = {
|
||
pharaoh_khufu: {
|
||
name: 'Mummy Pharaoh Khufu',
|
||
health: 5000,
|
||
attacks: ['curse_of_ages', 'sandstorm', 'royal_smite', 'summon_mummies'],
|
||
weaknesses: ['fire_magic', 'holy_water'],
|
||
phases: 3
|
||
},
|
||
pharaoh_khafre: {
|
||
name: 'Mummy Pharaoh Khafre',
|
||
health: 4500,
|
||
attacks: ['plague_swarm', 'quicksand', 'pharaoh_wrath', 'scarab_army'],
|
||
weaknesses: ['ice_magic', 'silver_weapons'],
|
||
phases: 2
|
||
},
|
||
pharaoh_menkaure: {
|
||
name: 'Mummy Pharaoh Menkaure',
|
||
health: 4000,
|
||
attacks: ['tomb_collapse', 'cursed_touch', 'soul_drain', 'guardian_spirits'],
|
||
weaknesses: ['lightning_magic', 'blessed_blade'],
|
||
phases: 2
|
||
}
|
||
};
|
||
|
||
const pharaoh = pharaohs[pharaohId];
|
||
if (!pharaoh) return null;
|
||
|
||
console.log(`⚔️ Boss Fight: ${pharaoh.name}!`);
|
||
|
||
this.scene.events.emit('start-boss-fight', {
|
||
boss: pharaoh,
|
||
arena: 'pyramid_throne_room',
|
||
music: 'epic_egyptian'
|
||
});
|
||
|
||
return pharaoh;
|
||
}
|
||
|
||
/**
|
||
* Challenge the Sphinx
|
||
*/
|
||
challengeSphinx() {
|
||
if (this.sphinx.defeated) {
|
||
console.log('ℹ️ The Sphinx has already been defeated!');
|
||
return { success: false, message: 'Already defeated' };
|
||
}
|
||
|
||
// Select 3 random riddles
|
||
const selectedRiddles = this.getRandomRiddles(3);
|
||
this.sphinx.riddlesAsked = selectedRiddles;
|
||
|
||
console.log('🦁 The Sphinx speaks...');
|
||
|
||
this.scene.events.emit('sphinx-challenge', {
|
||
riddles: selectedRiddles,
|
||
onAnswer: (riddleId, answer) => this.answerSphinxRiddle(riddleId, answer)
|
||
});
|
||
|
||
return { success: true, riddles: selectedRiddles };
|
||
}
|
||
|
||
/**
|
||
* Get random riddles
|
||
*/
|
||
getRandomRiddles(count) {
|
||
const shuffled = [...this.sphinx.riddles].sort(() => 0.5 - Math.random());
|
||
return shuffled.slice(0, count);
|
||
}
|
||
|
||
/**
|
||
* Answer Sphinx riddle
|
||
*/
|
||
answerSphinxRiddle(riddleId, playerAnswer) {
|
||
const riddle = this.sphinx.riddles.find(r => r.id === riddleId);
|
||
if (!riddle) return { correct: false, fight: false };
|
||
|
||
const normalizedAnswer = playerAnswer.toLowerCase().trim();
|
||
const isCorrect = normalizedAnswer === riddle.answer ||
|
||
riddle.alternatives.some(alt => normalizedAnswer === alt.toLowerCase());
|
||
|
||
if (isCorrect) {
|
||
console.log('✅ Correct answer!');
|
||
|
||
// Check if all 3 riddles answered
|
||
const riddlesAnswered = this.sphinx.riddlesAsked.filter(r => r.answered).length + 1;
|
||
|
||
if (riddlesAnswered >= 3) {
|
||
return this.defeatSphinxPeacefully();
|
||
}
|
||
|
||
return { correct: true, fight: false, remaining: 3 - riddlesAnswered };
|
||
} else {
|
||
console.log('❌ Wrong answer! The Sphinx attacks!');
|
||
return this.fightSphinx();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Defeat Sphinx peacefully (all riddles correct)
|
||
*/
|
||
defeatSphinxPeacefully() {
|
||
this.sphinx.defeated = true;
|
||
|
||
console.log('🎉 All riddles correct! The Sphinx grants you a reward!');
|
||
|
||
this.scene.events.emit('notification', {
|
||
title: 'Sphinx Impressed!',
|
||
message: `Received ${this.sphinx.reward}!`,
|
||
icon: '🦁'
|
||
});
|
||
|
||
// Award blueprint
|
||
if (this.scene.blueprintSystem) {
|
||
this.scene.blueprintSystem.unlockRecipe('sphinx_statue');
|
||
}
|
||
|
||
return { correct: true, fight: false, reward: this.sphinx.reward, peaceful: true };
|
||
}
|
||
|
||
/**
|
||
* Fight the Sphinx (wrong answer)
|
||
*/
|
||
fightSphinx() {
|
||
const sphinxBoss = {
|
||
name: 'The Great Sphinx',
|
||
health: 6000,
|
||
attacks: ['riddle_blast', 'sand_claw', 'roar_of_ages', 'ancient_curse'],
|
||
weaknesses: ['wisdom_spell', 'truth_serum'],
|
||
phases: 3
|
||
};
|
||
|
||
console.log('⚔️ Boss Fight: The Great Sphinx!');
|
||
|
||
this.scene.events.emit('start-boss-fight', {
|
||
boss: sphinxBoss,
|
||
arena: 'sphinx_platform',
|
||
music: 'epic_sphinx'
|
||
});
|
||
|
||
return { correct: false, fight: true, boss: sphinxBoss };
|
||
}
|
||
|
||
/**
|
||
* Build a player pyramid
|
||
*/
|
||
buildPyramid(size, position) {
|
||
if (!['small', 'medium', 'great'].includes(size)) {
|
||
console.log(`❌ Invalid pyramid size: ${size}`);
|
||
return { success: false, message: 'Invalid size' };
|
||
}
|
||
|
||
const pyramidData = this.pyramidSizes[size];
|
||
|
||
// Check resources
|
||
const hasResources = this.checkResources(pyramidData.cost);
|
||
if (!hasResources) {
|
||
return { success: false, message: 'Not enough resources!' };
|
||
}
|
||
|
||
// Consume resources
|
||
this.consumeResources(pyramidData.cost);
|
||
|
||
// Create pyramid
|
||
const pyramidId = `player_pyramid_${Date.now()}`;
|
||
const pyramid = {
|
||
id: pyramidId,
|
||
size: size,
|
||
position: position,
|
||
buildProgress: 0,
|
||
buildTime: pyramidData.buildTime,
|
||
complete: false,
|
||
benefits: pyramidData.benefits
|
||
};
|
||
|
||
this.playerPyramids.set(pyramidId, pyramid);
|
||
|
||
console.log(`🏗️ Building ${pyramidData.name}! ${pyramidData.buildTime} days to complete.`);
|
||
|
||
this.scene.events.emit('notification', {
|
||
title: 'Pyramid Construction Started!',
|
||
message: `${pyramidData.name} will be ready in ${pyramidData.buildTime} days!`,
|
||
icon: '🏗️'
|
||
});
|
||
|
||
return { success: true, pyramidId: pyramidId, pyramid: pyramid };
|
||
}
|
||
|
||
/**
|
||
* Check if player has resources
|
||
*/
|
||
checkResources(cost) {
|
||
// Placeholder - in real game would check inventory
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Consume resources
|
||
*/
|
||
consumeResources(cost) {
|
||
// Placeholder - in real game would remove from inventory
|
||
Object.keys(cost).forEach(resource => {
|
||
console.log(`Consumed ${cost[resource]} ${resource}`);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Update pyramid construction (called daily)
|
||
*/
|
||
updateConstruction(delta) {
|
||
this.playerPyramids.forEach((pyramid, id) => {
|
||
if (!pyramid.complete) {
|
||
pyramid.buildProgress += delta;
|
||
|
||
if (pyramid.buildProgress >= pyramid.buildTime) {
|
||
this.completePyramid(id);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Complete pyramid construction
|
||
*/
|
||
completePyramid(pyramidId) {
|
||
const pyramid = this.playerPyramids.get(pyramidId);
|
||
if (!pyramid) return;
|
||
|
||
pyramid.complete = true;
|
||
pyramid.buildProgress = pyramid.buildTime;
|
||
|
||
const pyramidData = this.pyramidSizes[pyramid.size];
|
||
|
||
console.log(`🎉 ${pyramidData.name} construction complete!`);
|
||
|
||
this.scene.events.emit('notification', {
|
||
title: 'Pyramid Complete!',
|
||
message: `Your ${pyramidData.name} is finished! Enjoy +${pyramid.benefits.tourism} Zł/day!`,
|
||
icon: '🏜️'
|
||
});
|
||
|
||
// Apply benefits
|
||
this.applyPyramidBenefits(pyramidId);
|
||
}
|
||
|
||
/**
|
||
* Apply pyramid benefits
|
||
*/
|
||
applyPyramidBenefits(pyramidId) {
|
||
const pyramid = this.playerPyramids.get(pyramidId);
|
||
if (!pyramid || !pyramid.complete) return;
|
||
|
||
const benefits = pyramid.benefits;
|
||
|
||
// Tourism income (passive gold)
|
||
if (this.scene.economySystem) {
|
||
this.scene.economySystem.addPassiveIncome('pyramid_' + pyramidId, benefits.tourism);
|
||
}
|
||
|
||
// Set as respawn point if applicable
|
||
if (benefits.respawn) {
|
||
this.scene.respawnPoint = pyramid.position;
|
||
console.log(`🏠 Pyramid set as respawn point!`);
|
||
}
|
||
|
||
console.log(`✨ Pyramid benefits active: +${benefits.tourism} Zł/day, ${benefits.healing * 100}% healing buff`);
|
||
}
|
||
|
||
/**
|
||
* Get healing buff from nearest pyramid
|
||
*/
|
||
getHealingBuff(playerPosition) {
|
||
let maxHealing = 0;
|
||
|
||
this.playerPyramids.forEach(pyramid => {
|
||
if (!pyramid.complete) return;
|
||
|
||
const distance = Phaser.Math.Distance.Between(
|
||
playerPosition.x, playerPosition.y,
|
||
pyramid.position.x, pyramid.position.y
|
||
);
|
||
|
||
// Healing decreases with distance (max 500 tiles)
|
||
if (distance < 500) {
|
||
const healingAmount = pyramid.benefits.healing * (1 - (distance / 500));
|
||
maxHealing = Math.max(maxHealing, healingAmount);
|
||
}
|
||
});
|
||
|
||
return maxHealing;
|
||
}
|
||
|
||
/**
|
||
* Get total daily tourism income
|
||
*/
|
||
getTotalTourismIncome() {
|
||
let total = 0;
|
||
|
||
this.playerPyramids.forEach(pyramid => {
|
||
if (pyramid.complete) {
|
||
total += pyramid.benefits.tourism;
|
||
}
|
||
});
|
||
|
||
return total;
|
||
}
|
||
|
||
/**
|
||
* Get pyramid stats
|
||
*/
|
||
getPyramidStats(pyramidId) {
|
||
const pyramid = this.playerPyramids.get(pyramidId);
|
||
if (!pyramid) return null;
|
||
|
||
const pyramidData = this.pyramidSizes[pyramid.size];
|
||
|
||
return {
|
||
name: pyramidData.name,
|
||
size: pyramid.size,
|
||
progress: pyramid.complete ? '100%' : `${Math.round((pyramid.buildProgress / pyramid.buildTime) * 100)}%`,
|
||
daysRemaining: pyramid.complete ? 0 : Math.ceil(pyramid.buildTime - pyramid.buildProgress),
|
||
benefits: pyramid.benefits,
|
||
position: pyramid.position
|
||
};
|
||
}
|
||
}
|