421 lines
11 KiB
JavaScript
421 lines
11 KiB
JavaScript
/**
|
|
* ZOMBIE SCOUT SKILLS SYSTEM
|
|
* Skill tree with active/passive abilities
|
|
* Integrates with ZombieScoutLevelingSystem
|
|
*/
|
|
|
|
export class ZombieScoutSkills {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
|
|
// Skill trees
|
|
this.skills = new Map();
|
|
this.unlockedSkills = new Set();
|
|
this.activeSkills = new Map(); // Currently equipped active skills
|
|
|
|
// Skill categories
|
|
this.categories = ['digging', 'combat', 'utility'];
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.initializeSkillTree();
|
|
}
|
|
|
|
/**
|
|
* Initialize all available skills
|
|
*/
|
|
initializeSkillTree() {
|
|
// DIGGING SKILLS
|
|
this.registerSkill({
|
|
id: 'speed_dig',
|
|
name: 'Speed Dig',
|
|
category: 'digging',
|
|
type: 'active',
|
|
unlockLevel: 10,
|
|
cooldown: 30000, // 30 seconds
|
|
duration: 10000, // 10 seconds
|
|
description: 'Dig 50% faster for 10 seconds',
|
|
effect: () => this.activateSpeedDig()
|
|
});
|
|
|
|
this.registerSkill({
|
|
id: 'deep_scan',
|
|
name: 'Deep Scan',
|
|
category: 'digging',
|
|
type: 'passive',
|
|
unlockLevel: 5,
|
|
description: 'Reveal buried items within 5 tiles',
|
|
effect: () => this.enableDeepScan()
|
|
});
|
|
|
|
this.registerSkill({
|
|
id: 'treasure_sense',
|
|
name: 'Treasure Sense',
|
|
category: 'digging',
|
|
type: 'active',
|
|
unlockLevel: 15,
|
|
cooldown: 60000, // 60 seconds
|
|
description: 'Reveal all buried treasure on screen',
|
|
effect: () => this.activateTreasureSense()
|
|
});
|
|
|
|
// COMBAT SKILLS
|
|
this.registerSkill({
|
|
id: 'basic_attack',
|
|
name: 'Claw Swipe',
|
|
category: 'combat',
|
|
type: 'active',
|
|
unlockLevel: 5,
|
|
cooldown: 2000, // 2 seconds
|
|
damage: 10,
|
|
description: 'Basic melee attack dealing 10 damage',
|
|
effect: () => this.performBasicAttack()
|
|
});
|
|
|
|
this.registerSkill({
|
|
id: 'leap_attack',
|
|
name: 'Leap Attack',
|
|
category: 'combat',
|
|
type: 'active',
|
|
unlockLevel: 12,
|
|
cooldown: 8000, // 8 seconds
|
|
damage: 25,
|
|
description: 'Leap at enemy dealing 25 damage',
|
|
effect: () => this.performLeapAttack()
|
|
});
|
|
|
|
this.registerSkill({
|
|
id: 'tank_stance',
|
|
name: 'Undead Resilience',
|
|
category: 'combat',
|
|
type: 'passive',
|
|
unlockLevel: 8,
|
|
description: '+20% damage resistance',
|
|
effect: () => this.enableTankStance()
|
|
});
|
|
|
|
// UTILITY SKILLS
|
|
this.registerSkill({
|
|
id: 'scout_speed',
|
|
name: 'Scout Sprint',
|
|
category: 'utility',
|
|
type: 'active',
|
|
unlockLevel: 7,
|
|
cooldown: 20000, // 20 seconds
|
|
duration: 5000, // 5 seconds
|
|
description: 'Move 100% faster for 5 seconds',
|
|
effect: () => this.activateScoutSpeed()
|
|
});
|
|
|
|
this.registerSkill({
|
|
id: 'pack_mule',
|
|
name: 'Pack Mule',
|
|
category: 'utility',
|
|
type: 'passive',
|
|
unlockLevel: 6,
|
|
description: '+10 inventory slots',
|
|
effect: () => this.enablePackMule()
|
|
});
|
|
|
|
this.registerSkill({
|
|
id: 'night_vision',
|
|
name: 'Night Vision',
|
|
category: 'utility',
|
|
type: 'passive',
|
|
unlockLevel: 10,
|
|
description: 'See in darkness without penalty',
|
|
effect: () => this.enableNightVision()
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Register a skill in the system
|
|
*/
|
|
registerSkill(skillData) {
|
|
this.skills.set(skillData.id, {
|
|
...skillData,
|
|
unlocked: false,
|
|
active: false,
|
|
lastUsed: 0
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Unlock skill (called when level requirement met)
|
|
*/
|
|
unlockSkill(skillId) {
|
|
const skill = this.skills.get(skillId);
|
|
if (!skill) return false;
|
|
|
|
const scoutLevel = this.scene.zombieScoutLeveling?.currentLevel || 1;
|
|
|
|
// Check level requirement
|
|
if (scoutLevel < skill.unlockLevel) {
|
|
console.warn(`Scout level ${scoutLevel} too low for ${skill.name} (requires ${skill.unlockLevel})`);
|
|
return false;
|
|
}
|
|
|
|
skill.unlocked = true;
|
|
this.unlockedSkills.add(skillId);
|
|
|
|
// Auto-activate passive skills
|
|
if (skill.type === 'passive') {
|
|
skill.effect();
|
|
}
|
|
|
|
// Notification
|
|
this.scene.uiSystem?.showNotification(
|
|
`Skill Unlocked: ${skill.name}!`,
|
|
'skill_unlock',
|
|
{ description: skill.description }
|
|
);
|
|
|
|
console.log(`✨ Unlocked skill: ${skill.name}`);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Use active skill
|
|
*/
|
|
useSkill(skillId) {
|
|
const skill = this.skills.get(skillId);
|
|
if (!skill || !skill.unlocked || skill.type !== 'active') return false;
|
|
|
|
// Check cooldown
|
|
const now = Date.now();
|
|
const timeSinceLastUse = now - skill.lastUsed;
|
|
|
|
if (timeSinceLastUse < skill.cooldown) {
|
|
const remainingCooldown = Math.ceil((skill.cooldown - timeSinceLastUse) / 1000);
|
|
console.log(`Skill on cooldown: ${remainingCooldown}s remaining`);
|
|
return false;
|
|
}
|
|
|
|
// Activate skill
|
|
skill.lastUsed = now;
|
|
skill.effect();
|
|
|
|
// UI feedback
|
|
this.scene.uiSystem?.showSkillActivation(skill.name, skill.cooldown);
|
|
|
|
console.log(`🔥 Activated: ${skill.name}`);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* SKILL EFFECTS
|
|
*/
|
|
|
|
activateSpeedDig() {
|
|
const scout = this.scene.zombieScout;
|
|
if (!scout) return;
|
|
|
|
scout.digSpeed *= 1.5; // 50% faster
|
|
|
|
// VFX
|
|
this.scene.vfxSystem?.playEffect('speed_buff', scout.x, scout.y);
|
|
|
|
// Restore after duration
|
|
this.scene.time.delayedCall(10000, () => {
|
|
scout.digSpeed /= 1.5;
|
|
console.log('Speed Dig expired');
|
|
});
|
|
}
|
|
|
|
enableDeepScan() {
|
|
// Enable passive treasure detection
|
|
this.scene.gameState.buffs.treasure_detection_range = 5;
|
|
console.log('Deep Scan enabled: 5 tile range');
|
|
}
|
|
|
|
activateTreasureSense() {
|
|
// Reveal all buried items on screen
|
|
this.scene.treasureSystem?.revealAllTreasures();
|
|
|
|
// VFX: Screen pulse
|
|
this.scene.cameras.main.flash(500, 255, 215, 0, true);
|
|
|
|
console.log('Treasure Sense activated: All treasures revealed');
|
|
}
|
|
|
|
performBasicAttack() {
|
|
const scout = this.scene.zombieScout;
|
|
if (!scout) return;
|
|
|
|
// Find nearest enemy
|
|
const enemy = this.findNearestEnemy(scout, 50);
|
|
if (!enemy) {
|
|
console.log('No enemy in range');
|
|
return;
|
|
}
|
|
|
|
// Deal damage
|
|
enemy.takeDamage(10);
|
|
|
|
// VFX
|
|
this.scene.vfxSystem?.playEffect('claw_swipe', enemy.x, enemy.y);
|
|
this.scene.soundSystem?.play('zombie_attack');
|
|
|
|
console.log('Basic attack: 10 damage');
|
|
}
|
|
|
|
performLeapAttack() {
|
|
const scout = this.scene.zombieScout;
|
|
if (!scout) return;
|
|
|
|
// Find nearest enemy
|
|
const enemy = this.findNearestEnemy(scout, 150);
|
|
if (!enemy) return;
|
|
|
|
// Leap animation
|
|
this.scene.tweens.add({
|
|
targets: scout,
|
|
x: enemy.x,
|
|
y: enemy.y - 20,
|
|
duration: 300,
|
|
ease: 'Quad.easeOut',
|
|
onComplete: () => {
|
|
// Deal damage on landing
|
|
enemy.takeDamage(25);
|
|
this.scene.vfxSystem?.playEffect('impact', enemy.x, enemy.y);
|
|
this.scene.cameras.main.shake(200, 0.008);
|
|
}
|
|
});
|
|
|
|
console.log('Leap attack: 25 damage');
|
|
}
|
|
|
|
enableTankStance() {
|
|
const scout = this.scene.zombieScout;
|
|
if (scout) {
|
|
scout.damageResistance = (scout.damageResistance || 0) + 0.2; // +20%
|
|
console.log('Undead Resilience enabled: +20% damage resistance');
|
|
}
|
|
}
|
|
|
|
activateScoutSpeed() {
|
|
const scout = this.scene.zombieScout;
|
|
if (!scout) return;
|
|
|
|
scout.moveSpeed *= 2; // 100% faster
|
|
|
|
// VFX: Speed trails
|
|
this.scene.vfxSystem?.playEffect('speed_trail', scout.x, scout.y, { duration: 5000 });
|
|
|
|
// Restore after duration
|
|
this.scene.time.delayedCall(5000, () => {
|
|
scout.moveSpeed /= 2;
|
|
console.log('Scout Sprint expired');
|
|
});
|
|
}
|
|
|
|
enablePackMule() {
|
|
this.scene.inventorySystem?.addInventorySlots(10);
|
|
console.log('Pack Mule enabled: +10 inventory slots');
|
|
}
|
|
|
|
enableNightVision() {
|
|
this.scene.gameState.buffs.night_vision = true;
|
|
console.log('Night Vision enabled: Full visibility at night');
|
|
}
|
|
|
|
/**
|
|
* Helper: Find nearest enemy
|
|
*/
|
|
findNearestEnemy(from, maxDistance) {
|
|
let nearest = null;
|
|
let minDist = maxDistance;
|
|
|
|
// Search for enemies in scene
|
|
const enemies = this.scene.enemies || [];
|
|
|
|
enemies.forEach(enemy => {
|
|
const dist = Phaser.Math.Distance.Between(from.x, from.y, enemy.x, enemy.y);
|
|
if (dist < minDist) {
|
|
minDist = dist;
|
|
nearest = enemy;
|
|
}
|
|
});
|
|
|
|
return nearest;
|
|
}
|
|
|
|
/**
|
|
* Get unlocked skills by category
|
|
*/
|
|
getSkillsByCategory(category) {
|
|
return Array.from(this.skills.values())
|
|
.filter(s => s.category === category && s.unlocked);
|
|
}
|
|
|
|
/**
|
|
* Get all unlocked skills
|
|
*/
|
|
getUnlockedSkills() {
|
|
return Array.from(this.skills.values()).filter(s => s.unlocked);
|
|
}
|
|
|
|
/**
|
|
* Check skill availability
|
|
*/
|
|
isSkillAvailable(skillId) {
|
|
const skill = this.skills.get(skillId);
|
|
if (!skill || !skill.unlocked) return false;
|
|
|
|
if (skill.type === 'passive') return true;
|
|
|
|
const now = Date.now();
|
|
return (now - skill.lastUsed) >= skill.cooldown;
|
|
}
|
|
|
|
/**
|
|
* Get skill cooldown remaining
|
|
*/
|
|
getCooldownRemaining(skillId) {
|
|
const skill = this.skills.get(skillId);
|
|
if (!skill) return 0;
|
|
|
|
const now = Date.now();
|
|
const elapsed = now - skill.lastUsed;
|
|
const remaining = Math.max(0, skill.cooldown - elapsed);
|
|
|
|
return Math.ceil(remaining / 1000); // Return in seconds
|
|
}
|
|
|
|
/**
|
|
* Save/Load
|
|
*/
|
|
getSaveData() {
|
|
return {
|
|
unlockedSkills: Array.from(this.unlockedSkills),
|
|
skillCooldowns: Array.from(this.skills.entries()).map(([id, skill]) => ({
|
|
id,
|
|
lastUsed: skill.lastUsed
|
|
}))
|
|
};
|
|
}
|
|
|
|
loadSaveData(data) {
|
|
this.unlockedSkills = new Set(data.unlockedSkills || []);
|
|
|
|
// Restore cooldowns
|
|
data.skillCooldowns?.forEach(({ id, lastUsed }) => {
|
|
const skill = this.skills.get(id);
|
|
if (skill) {
|
|
skill.lastUsed = lastUsed;
|
|
skill.unlocked = this.unlockedSkills.has(id);
|
|
}
|
|
});
|
|
|
|
// Re-apply passive skills
|
|
this.unlockedSkills.forEach(skillId => {
|
|
const skill = this.skills.get(skillId);
|
|
if (skill && skill.type === 'passive') {
|
|
skill.effect();
|
|
}
|
|
});
|
|
}
|
|
}
|