/** * TwinBondSystem.js * ================= * KRVAVA Ε½ETEV - Twin Bond Mechanic (Kai ↔ Ana Connection) * * Core Concept: * Kai and Ana share a psychic bond through the Alfa virus * As twins, they can: * - Feel each other's emotions * - Sense each other's location (vaguely) * - Communicate telepathically (limited) * - Share HP/stamina in emergencies * * Features: * - Bond Strength meter (0-100) * - Telepathic messages from Ana * - Direction to Ana indicator * - Twin abilities (heal twin, boost twin, swap positions) * - Bond events (visions, flashbacks) * - Ana's status tracking (health, danger level) * * @author NovaFarma Team * @date 2025-12-23 */ export default class TwinBondSystem { constructor(scene) { this.scene = scene; // Bond state this.bondStrength = 75; // Starts strong (0-100) this.maxBondStrength = 100; // Ana's state (unknown location) this.anaState = { alive: true, health: 100, dangerLevel: 0, // 0 = safe, 100 = critical distance: 5000, // pixels from Kai (initially far) direction: { x: 1000, y: 1000 }, // General direction lastMessage: null, messageTime: null }; // Bond abilities this.abilities = { telepathy: { unlocked: true, cooldown: 0, maxCooldown: 30000 }, // 30s sensePulse: { unlocked: true, cooldown: 0, maxCooldown: 60000 }, // 1min emergencyLink: { unlocked: false, cooldown: 0, maxCooldown: 300000 }, // 5min twinRecall: { unlocked: false, cooldown: 0, maxCooldown: 600000 } // 10min }; // Messages from Ana (telepathic) this.messageQueue = []; this.lastMessageTime = 0; // UI elements this.bondUI = null; // Events this.bondEvents = this.defineBondEvents(); this.nextEventTime = Date.now() + 60000; // First event in 1 minute console.log('πŸ’ž TwinBondSystem initialized - Bond Strength:', this.bondStrength); } /** * Update bond strength based on actions */ update(delta) { const deltaSeconds = delta / 1000; // Passive bond decay (small) this.bondStrength = Math.max(0, this.bondStrength - 0.01 * deltaSeconds); // Update ability cooldowns for (const ability in this.abilities) { if (this.abilities[ability].cooldown > 0) { this.abilities[ability].cooldown -= delta; } } // Check for random bond events if (Date.now() > this.nextEventTime) { this.triggerRandomBondEvent(); this.nextEventTime = Date.now() + Phaser.Math.Between(60000, 180000); // 1-3 min } // Update Ana's danger level based on story progress this.updateAnaDanger(deltaSeconds); } /** * Define bond events (telepathic visions) */ defineBondEvents() { return [ { id: 'first_contact', trigger: 'auto', condition: null, message: 'Kai... can you hear me? I\'m... somewhere dark...', emotion: 'worried', bondChange: +5 }, { id: 'danger_warning', trigger: 'danger_high', condition: () => this.anaState.dangerLevel > 70, message: 'Brother! They\'re coming for me! Please hurry!', emotion: 'fear', bondChange: -10 }, { id: 'memory_flash', trigger: 'random', condition: null, message: 'Remember when we first discovered the Alfa strain? We were so hopeful...', emotion: 'sad', bondChange: +3 }, { id: 'location_hint', trigger: 'sense_pulse', condition: null, message: 'I can feel concrete walls... cold metal... some kind of facility?', emotion: 'neutral', bondChange: +5 }, { id: 'encouragement', trigger: 'random', condition: () => this.scene.player?.hp < 50, message: 'Stay strong, Kai! I believe in you!', emotion: 'determined', bondChange: +10 } ]; } /** * Trigger a random bond event */ triggerRandomBondEvent() { const randomEvents = this.bondEvents.filter(e => e.trigger === 'random'); if (randomEvents.length === 0) return; const event = Phaser.Utils.Array.GetRandom(randomEvents); // Check condition if (event.condition && !event.condition()) { return; } this.showTelepathicMessage(event.message, event.emotion); this.changeBondStrength(event.bondChange); } /** * Show telepathic message from Ana */ showTelepathicMessage(message, emotion = 'neutral') { console.log(`πŸ’­ Twin Bond Message: ${message}`); // Update Ana's last message this.anaState.lastMessage = message; this.anaState.messageTime = Date.now(); // Show in dialogue system if (this.scene.dialogueSystem) { const anaData = { name: 'Ana (Twin Bond)', id: 'ana_telepathy' }; // Create temporary dialogue const telepathyDialogue = { root: 'message', nodes: { 'message': { speaker: 'Ana (Twin Bond)', emotion: emotion, text: message, next: null // Auto-close } } }; this.scene.dialogueSystem.registerDialogue('telepathy_' + Date.now(), telepathyDialogue); this.scene.dialogueSystem.startDialogue('telepathy_' + Date.now(), anaData); } // Visual effect (bond pulse) this.scene.cameras.main.flash(500, 150, 100, 255, false); // Bond strength change this.showBondPulse(); } /** * Change bond strength */ changeBondStrength(amount) { this.bondStrength = Phaser.Math.Clamp( this.bondStrength + amount, 0, this.maxBondStrength ); console.log(`πŸ’ž Bond Strength: ${this.bondStrength.toFixed(1)}% (${amount > 0 ? '+' : ''}${amount})`); // Notify player if (amount > 0) { this.scene.events.emit('bondStrengthened', { strength: this.bondStrength }); } else { this.scene.events.emit('bondWeakened', { strength: this.bondStrength }); } } /** * Visual bond pulse effect */ showBondPulse() { // TODO: Create particle effect at player position console.log('πŸ’« Bond pulse visualization'); } /** * Ability: Telepathy (send message to Ana) */ useTelepathy(message) { if (!this.abilities.telepathy.unlocked) { console.log('❌ Telepathy not unlocked'); return false; } if (this.abilities.telepathy.cooldown > 0) { console.log('⏸️ Telepathy on cooldown'); return false; } console.log(`πŸ“‘ Sending to Ana: ${message}`); // Ana responds after delay this.scene.time.delayedCall(2000, () => { const responses = [ "I heard you! Keep searching!", "Kai... I'm trying to stay strong...", "They don't know about our bond. Use that!", "I can feel you getting closer!" ]; const response = Phaser.Utils.Array.GetRandom(responses); this.showTelepathicMessage(response, 'determined'); }); // Set cooldown this.abilities.telepathy.cooldown = this.abilities.telepathy.maxCooldown; this.changeBondStrength(+2); // Strengthen bond return true; } /** * Ability: Sense Pulse (detect Ana's direction) */ useSensePulse() { if (!this.abilities.sensePulse.unlocked) { console.log('❌ Sense Pulse not unlocked'); return null; } if (this.abilities.sensePulse.cooldown > 0) { console.log('⏸️ Sense Pulse on cooldown'); return null; } console.log('πŸ“ Sensing Ana\'s location...'); // Calculate general direction const playerX = this.scene.player?.x || 0; const playerY = this.scene.player?.y || 0; const angle = Phaser.Math.Angle.Between( playerX, playerY, this.anaState.direction.x, this.anaState.direction.y ); const distance = this.anaState.distance; // Show visual indicator this.showDirectionIndicator(angle, distance); // Set cooldown this.abilities.sensePulse.cooldown = this.abilities.sensePulse.maxCooldown; this.changeBondStrength(+5); return { angle: angle, distance: distance, distanceCategory: this.getDistanceCategory(distance) }; } /** * Get distance category (for vague communication) */ getDistanceCategory(distance) { if (distance < 500) return 'very_close'; if (distance < 1500) return 'close'; if (distance < 3000) return 'far'; return 'very_far'; } /** * Show direction indicator */ showDirectionIndicator(angle, distance) { const category = this.getDistanceCategory(distance); const messages = { 'very_close': 'Ana is VERY CLOSE! ⬆️', 'close': 'Ana is nearby πŸ“', 'far': 'Ana is far away πŸ”­', 'very_far': 'Ana is very far 🌌' }; console.log(`πŸ“ ${messages[category]} (${Math.round(distance)}px)`); // TODO: Show UI arrow pointing in direction } /** * Update Ana's danger level */ updateAnaDanger(deltaSeconds) { // Danger level increases over time (captors getting desperate) if (this.anaState.alive) { this.anaState.dangerLevel = Math.min( 100, this.anaState.dangerLevel + 0.1 * deltaSeconds ); // Trigger danger events if (this.anaState.dangerLevel > 70 && Math.random() < 0.01) { const dangerEvent = this.bondEvents.find(e => e.id === 'danger_warning'); if (dangerEvent) { this.showTelepathicMessage(dangerEvent.message, dangerEvent.emotion); } } } } /** * Update Ana's position (for story progression) */ updateAnaLocation(x, y, distance) { this.anaState.direction.x = x; this.anaState.direction.y = y; this.anaState.distance = distance; console.log(`πŸ“ Ana's location updated: (${x}, ${y}), distance: ${distance}px`); } /** * Create Twin Bond UI */ createBondUI() { const width = this.scene.cameras.main.width; // Bond meter (top-left) const x = 20; const y = 120; // Background const bg = this.scene.add.rectangle(x, y, 200, 40, 0x2d1b00, 0.8); bg.setOrigin(0, 0); bg.setScrollFactor(0); bg.setDepth(100); // Title const title = this.scene.add.text(x + 10, y + 5, 'πŸ’ž Twin Bond', { fontSize: '14px', fontFamily: 'Georgia, serif', color: '#FFD700', fontStyle: 'bold' }); title.setScrollFactor(0); title.setDepth(100); // Bond bar const barBg = this.scene.add.rectangle(x + 10, y + 25, 180, 8, 0x000000, 0.8); barBg.setOrigin(0, 0); barBg.setScrollFactor(0); barBg.setDepth(100); const barFill = this.scene.add.rectangle( x + 10, y + 25, 180 * (this.bondStrength / 100), 8, 0xFF69B4, 1 ); barFill.setOrigin(0, 0); barFill.setScrollFactor(0); barFill.setDepth(100); this.bondUI = { bg, title, barBg, barFill }; // Update bar every frame this.scene.events.on('update', () => { if (this.bondUI && this.bondUI.barFill) { this.bondUI.barFill.width = 180 * (this.bondStrength / 100); } }); } /** * Getters */ getBondStrength() { return this.bondStrength; } getAnaStatus() { return this.anaState; } isAnaSafe() { return this.anaState.dangerLevel < 50; } }