acesesibiliti
This commit is contained in:
@@ -7,26 +7,51 @@ class VisualSoundCueSystem {
|
||||
this.scene = scene;
|
||||
this.enabled = true;
|
||||
|
||||
// Visual elements
|
||||
this.heartbeatIndicator = null;
|
||||
this.damageIndicators = [];
|
||||
this.screenFlash = null;
|
||||
this.subtitleBackground = null;
|
||||
this.subtitleText = null;
|
||||
this.subtitleSpeaker = null;
|
||||
this.subtitleArrows = { left: null, right: null };
|
||||
this.heartbeatTween = null;
|
||||
this.fishingBobberIndicator = null;
|
||||
|
||||
// Speaker color mapping
|
||||
this.speakerColors = {
|
||||
'Player': '#00ff00',
|
||||
'NPC': '#ffff00',
|
||||
'Enemy': '#ff0000',
|
||||
'System': '#00ffff',
|
||||
'Narrator': '#ffffff'
|
||||
};
|
||||
|
||||
// Subtitle size presets
|
||||
this.subtitleSizes = {
|
||||
'small': { main: 16, speaker: 12, arrow: 24 },
|
||||
'medium': { main: 20, speaker: 16, arrow: 32 },
|
||||
'large': { main: 28, speaker: 20, arrow: 40 },
|
||||
'very-large': { main: 36, speaker: 24, arrow: 48 }
|
||||
};
|
||||
|
||||
// Settings
|
||||
this.settings = {
|
||||
heartbeatEnabled: true,
|
||||
damageIndicatorEnabled: true,
|
||||
screenFlashEnabled: true,
|
||||
subtitlesEnabled: true
|
||||
subtitlesEnabled: true,
|
||||
directionalArrowsEnabled: true,
|
||||
speakerNamesEnabled: true,
|
||||
subtitleOpacity: 0.8,
|
||||
fishingBobberEnabled: true,
|
||||
subtitleSize: 'medium' // 'small', 'medium', 'large', 'very-large'
|
||||
};
|
||||
|
||||
// Visual elements
|
||||
this.heartbeatSprite = null;
|
||||
this.damageIndicators = [];
|
||||
this.subtitleText = null;
|
||||
this.subtitleBackground = null;
|
||||
|
||||
// Heartbeat state
|
||||
this.heartbeatActive = false;
|
||||
this.heartbeatTween = null;
|
||||
|
||||
this.loadSettings();
|
||||
this.init();
|
||||
console.log('✅ VisualSoundCueSystem initialized');
|
||||
|
||||
console.log('✅ Visual Sound Cue System initialized');
|
||||
}
|
||||
|
||||
init() {
|
||||
@@ -37,7 +62,7 @@ class VisualSoundCueSystem {
|
||||
this.createSubtitleContainer();
|
||||
|
||||
// Load settings from localStorage
|
||||
this.loadSettings();
|
||||
// this.loadSettings(); // Moved to constructor
|
||||
}
|
||||
|
||||
createHeartbeatIndicator() {
|
||||
@@ -45,13 +70,13 @@ class VisualSoundCueSystem {
|
||||
const y = 30;
|
||||
|
||||
// Heart emoji as sprite
|
||||
this.heartbeatSprite = this.scene.add.text(x, y, '❤️', {
|
||||
this.heartbeatIndicator = this.scene.add.text(x, y, '❤️', {
|
||||
fontSize: '48px'
|
||||
});
|
||||
this.heartbeatSprite.setOrigin(0.5);
|
||||
this.heartbeatSprite.setDepth(10000);
|
||||
this.heartbeatSprite.setScrollFactor(0);
|
||||
this.heartbeatSprite.setVisible(false);
|
||||
this.heartbeatIndicator.setOrigin(0.5);
|
||||
this.heartbeatIndicator.setDepth(10000);
|
||||
this.heartbeatIndicator.setScrollFactor(0);
|
||||
this.heartbeatIndicator.setVisible(false);
|
||||
}
|
||||
|
||||
createSubtitleContainer() {
|
||||
@@ -63,16 +88,34 @@ class VisualSoundCueSystem {
|
||||
width / 2,
|
||||
height - 100,
|
||||
width - 100,
|
||||
80,
|
||||
100,
|
||||
0x000000,
|
||||
0.8
|
||||
this.settings.subtitleOpacity
|
||||
);
|
||||
this.subtitleBackground.setOrigin(0.5);
|
||||
this.subtitleBackground.setDepth(9999);
|
||||
this.subtitleBackground.setScrollFactor(0);
|
||||
this.subtitleBackground.setVisible(false);
|
||||
|
||||
// Text
|
||||
// Speaker name text
|
||||
this.subtitleSpeaker = this.scene.add.text(
|
||||
width / 2,
|
||||
height - 130,
|
||||
'',
|
||||
{
|
||||
fontSize: '16px',
|
||||
fontFamily: 'Arial',
|
||||
fontStyle: 'bold',
|
||||
color: '#ffffff',
|
||||
align: 'center'
|
||||
}
|
||||
);
|
||||
this.subtitleSpeaker.setOrigin(0.5);
|
||||
this.subtitleSpeaker.setDepth(10001);
|
||||
this.subtitleSpeaker.setScrollFactor(0);
|
||||
this.subtitleSpeaker.setVisible(false);
|
||||
|
||||
// Main subtitle text
|
||||
this.subtitleText = this.scene.add.text(
|
||||
width / 2,
|
||||
height - 100,
|
||||
@@ -82,13 +125,44 @@ class VisualSoundCueSystem {
|
||||
fontFamily: 'Arial',
|
||||
color: '#ffffff',
|
||||
align: 'center',
|
||||
wordWrap: { width: width - 120 }
|
||||
wordWrap: { width: width - 160 }
|
||||
}
|
||||
);
|
||||
this.subtitleText.setOrigin(0.5);
|
||||
this.subtitleText.setDepth(10000);
|
||||
this.subtitleText.setScrollFactor(0);
|
||||
this.subtitleText.setVisible(false);
|
||||
|
||||
// Directional arrows
|
||||
this.subtitleArrows.left = this.scene.add.text(
|
||||
50,
|
||||
height - 100,
|
||||
'◄',
|
||||
{
|
||||
fontSize: '32px',
|
||||
fontFamily: 'Arial',
|
||||
color: '#ffff00'
|
||||
}
|
||||
);
|
||||
this.subtitleArrows.left.setOrigin(0.5);
|
||||
this.subtitleArrows.left.setDepth(10001);
|
||||
this.subtitleArrows.left.setScrollFactor(0);
|
||||
this.subtitleArrows.left.setVisible(false);
|
||||
|
||||
this.subtitleArrows.right = this.scene.add.text(
|
||||
width - 50,
|
||||
height - 100,
|
||||
'►',
|
||||
{
|
||||
fontSize: '32px',
|
||||
fontFamily: 'Arial',
|
||||
color: '#ffff00'
|
||||
}
|
||||
);
|
||||
this.subtitleArrows.right.setOrigin(0.5);
|
||||
this.subtitleArrows.right.setDepth(10001);
|
||||
this.subtitleArrows.right.setScrollFactor(0);
|
||||
this.subtitleArrows.right.setVisible(false);
|
||||
}
|
||||
|
||||
// ========== VISUAL HEARTBEAT (LOW HEALTH) ==========
|
||||
@@ -110,13 +184,13 @@ class VisualSoundCueSystem {
|
||||
|
||||
startHeartbeat(healthPercent) {
|
||||
this.heartbeatActive = true;
|
||||
this.heartbeatSprite.setVisible(true);
|
||||
this.heartbeatIndicator.setVisible(true);
|
||||
|
||||
// Calculate speed based on health (lower health = faster beat)
|
||||
const speed = Phaser.Math.Clamp(1000 - (healthPercent * 20), 300, 1000);
|
||||
|
||||
this.heartbeatTween = this.scene.tweens.add({
|
||||
targets: this.heartbeatSprite,
|
||||
targets: this.heartbeatIndicator,
|
||||
scale: 1.3,
|
||||
alpha: 0.6,
|
||||
duration: speed / 2,
|
||||
@@ -139,15 +213,15 @@ class VisualSoundCueSystem {
|
||||
if (!this.heartbeatActive) return;
|
||||
|
||||
this.heartbeatActive = false;
|
||||
this.heartbeatSprite.setVisible(false);
|
||||
this.heartbeatIndicator.setVisible(false);
|
||||
|
||||
if (this.heartbeatTween) {
|
||||
this.heartbeatTween.stop();
|
||||
this.heartbeatTween = null;
|
||||
}
|
||||
|
||||
this.heartbeatSprite.setScale(1);
|
||||
this.heartbeatSprite.setAlpha(1);
|
||||
this.heartbeatIndicator.setScale(1);
|
||||
this.heartbeatIndicator.setAlpha(1);
|
||||
}
|
||||
|
||||
// ========== DAMAGE DIRECTION INDICATOR ==========
|
||||
@@ -279,30 +353,90 @@ class VisualSoundCueSystem {
|
||||
|
||||
// ========== SUBTITLES ==========
|
||||
|
||||
showSubtitle(text, duration = 3000, speaker = null) {
|
||||
showSubtitle(text, duration = 3000, speaker = null, direction = null) {
|
||||
if (!this.settings.subtitlesEnabled) return;
|
||||
|
||||
// Format text with speaker name if provided
|
||||
let displayText = text;
|
||||
if (speaker) {
|
||||
displayText = `[${speaker}]: ${text}`;
|
||||
}
|
||||
|
||||
this.subtitleText.setText(displayText);
|
||||
// Set subtitle text
|
||||
this.subtitleText.setText(text);
|
||||
this.subtitleText.setVisible(true);
|
||||
this.subtitleBackground.setVisible(true);
|
||||
|
||||
// Show speaker name with color if enabled
|
||||
if (speaker && this.settings.speakerNamesEnabled) {
|
||||
const color = this.speakerColors[speaker] || '#ffffff';
|
||||
this.subtitleSpeaker.setText(speaker);
|
||||
this.subtitleSpeaker.setColor(color);
|
||||
this.subtitleSpeaker.setVisible(true);
|
||||
} else {
|
||||
this.subtitleSpeaker.setVisible(false);
|
||||
}
|
||||
|
||||
// Show directional arrows if enabled and direction provided
|
||||
if (direction && this.settings.directionalArrowsEnabled) {
|
||||
this.showDirectionalArrows(direction);
|
||||
} else {
|
||||
this.hideDirectionalArrows();
|
||||
}
|
||||
|
||||
// Auto-hide after duration
|
||||
this.scene.time.delayedCall(duration, () => {
|
||||
this.hideSubtitle();
|
||||
});
|
||||
|
||||
console.log('💬 Subtitle shown:', displayText);
|
||||
console.log('💬 Subtitle shown:', speaker ? `[${speaker}] ${text}` : text);
|
||||
}
|
||||
|
||||
showDirectionalArrows(direction) {
|
||||
this.hideDirectionalArrows();
|
||||
|
||||
if (direction === 'left' || direction === 'west') {
|
||||
this.subtitleArrows.left.setVisible(true);
|
||||
// Pulse animation
|
||||
this.scene.tweens.add({
|
||||
targets: this.subtitleArrows.left,
|
||||
alpha: { from: 1, to: 0.3 },
|
||||
duration: 500,
|
||||
yoyo: true,
|
||||
repeat: -1
|
||||
});
|
||||
} else if (direction === 'right' || direction === 'east') {
|
||||
this.subtitleArrows.right.setVisible(true);
|
||||
// Pulse animation
|
||||
this.scene.tweens.add({
|
||||
targets: this.subtitleArrows.right,
|
||||
alpha: { from: 1, to: 0.3 },
|
||||
duration: 500,
|
||||
yoyo: true,
|
||||
repeat: -1
|
||||
});
|
||||
} else if (direction === 'both') {
|
||||
this.subtitleArrows.left.setVisible(true);
|
||||
this.subtitleArrows.right.setVisible(true);
|
||||
// Pulse animation for both
|
||||
this.scene.tweens.add({
|
||||
targets: [this.subtitleArrows.left, this.subtitleArrows.right],
|
||||
alpha: { from: 1, to: 0.3 },
|
||||
duration: 500,
|
||||
yoyo: true,
|
||||
repeat: -1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
hideDirectionalArrows() {
|
||||
this.scene.tweens.killTweensOf(this.subtitleArrows.left);
|
||||
this.scene.tweens.killTweensOf(this.subtitleArrows.right);
|
||||
this.subtitleArrows.left.setVisible(false);
|
||||
this.subtitleArrows.right.setVisible(false);
|
||||
this.subtitleArrows.left.setAlpha(1);
|
||||
this.subtitleArrows.right.setAlpha(1);
|
||||
}
|
||||
|
||||
hideSubtitle() {
|
||||
this.subtitleText.setVisible(false);
|
||||
this.subtitleBackground.setVisible(false);
|
||||
this.subtitleSpeaker.setVisible(false);
|
||||
this.hideDirectionalArrows();
|
||||
}
|
||||
|
||||
// ========== SOUND EVENT HANDLERS ==========
|
||||
@@ -313,35 +447,192 @@ class VisualSoundCueSystem {
|
||||
switch (soundType) {
|
||||
case 'damage':
|
||||
this.showDamageIndicator(data.direction || 'down', data.amount || 10);
|
||||
this.showSubtitle('[DAMAGE TAKEN]', 1500);
|
||||
this.showSubtitle('[DAMAGE TAKEN]', 1500, 'System', data.direction);
|
||||
break;
|
||||
|
||||
case 'pickup':
|
||||
this.showSubtitle(`[PICKED UP: ${data.item || 'Item'}]`, 1500);
|
||||
this.showSubtitle(`[PICKED UP: ${data.item || 'Item'}]`, 1500, 'System');
|
||||
break;
|
||||
|
||||
case 'harvest':
|
||||
this.showSubtitle('[CROP HARVESTED]', 1500);
|
||||
this.showSubtitle('[CROP HARVESTED]', 1500, 'System');
|
||||
break;
|
||||
|
||||
case 'build':
|
||||
this.showSubtitle('[BUILDING PLACED]', 1500);
|
||||
this.showSubtitle('[BUILDING PLACED]', 1500, 'System');
|
||||
break;
|
||||
|
||||
case 'dig':
|
||||
this.showSubtitle('[DIGGING SOUND]', 1000, 'System');
|
||||
break;
|
||||
|
||||
case 'plant':
|
||||
this.showSubtitle('[PLANTING SOUND]', 1000, 'System');
|
||||
break;
|
||||
|
||||
case 'footsteps':
|
||||
this.showSubtitle('[FOOTSTEPS]', 500, null, data.direction);
|
||||
break;
|
||||
|
||||
case 'door':
|
||||
this.showSubtitle('[DOOR OPENS]', 1000, 'System');
|
||||
break;
|
||||
|
||||
case 'chest':
|
||||
this.showSubtitle('[CHEST OPENS]', 1000, 'System');
|
||||
break;
|
||||
|
||||
case 'water':
|
||||
this.showSubtitle('[WATER SPLASH]', 1000, 'System');
|
||||
break;
|
||||
|
||||
case 'fire':
|
||||
this.showSubtitle('[FIRE CRACKLING]', 2000, 'System');
|
||||
break;
|
||||
|
||||
case 'explosion':
|
||||
this.showSubtitle('[EXPLOSION!]', 1500, 'System');
|
||||
this.showScreenFlash('danger', '[EXPLOSION!]');
|
||||
break;
|
||||
|
||||
case 'npc_talk':
|
||||
this.showSubtitle(data.text || '[NPC TALKING]', 3000, data.speaker || 'NPC', data.direction);
|
||||
break;
|
||||
|
||||
case 'enemy_growl':
|
||||
this.showSubtitle('[ENEMY GROWL]', 1500, 'Enemy', data.direction);
|
||||
break;
|
||||
|
||||
case 'fishing_cast':
|
||||
this.showSubtitle('[FISHING LINE CAST]', 1000, 'System');
|
||||
break;
|
||||
|
||||
case 'fishing_bite':
|
||||
this.showSubtitle('[FISH BITING!]', 1500, 'System');
|
||||
this.showFishingBobberCue();
|
||||
break;
|
||||
|
||||
case 'danger':
|
||||
this.showScreenFlash('danger', '[DANGER!]');
|
||||
this.showSubtitle('[DANGER NEARBY]', 2000, 'System');
|
||||
break;
|
||||
|
||||
case 'night':
|
||||
this.showScreenFlash('warning', '[NIGHT FALLING]');
|
||||
this.showSubtitle('[NIGHT IS FALLING]', 2000, 'System');
|
||||
break;
|
||||
|
||||
case 'achievement':
|
||||
this.showScreenFlash('success', data.message || '[ACHIEVEMENT UNLOCKED]');
|
||||
this.showSubtitle(data.message || '[ACHIEVEMENT UNLOCKED]', 3000, 'System');
|
||||
break;
|
||||
|
||||
case 'ui_click':
|
||||
this.showSubtitle('[CLICK]', 300, null);
|
||||
break;
|
||||
|
||||
case 'ui_hover':
|
||||
this.showSubtitle('[HOVER]', 200, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show fishing bobber visual cue
|
||||
*/
|
||||
showFishingBobberCue() {
|
||||
if (!this.settings.fishingBobberEnabled) return;
|
||||
|
||||
const width = this.scene.cameras.main.width;
|
||||
const height = this.scene.cameras.main.height;
|
||||
|
||||
// Create bobber indicator if it doesn't exist
|
||||
if (!this.fishingBobberIndicator) {
|
||||
this.fishingBobberIndicator = this.scene.add.container(width / 2, height / 2);
|
||||
this.fishingBobberIndicator.setDepth(10002);
|
||||
this.fishingBobberIndicator.setScrollFactor(0);
|
||||
|
||||
// Circle background
|
||||
const circle = this.scene.add.circle(0, 0, 60, 0xff6600, 0.8);
|
||||
this.fishingBobberIndicator.add(circle);
|
||||
|
||||
// Exclamation mark
|
||||
const exclamation = this.scene.add.text(0, 0, '!', {
|
||||
fontSize: '48px',
|
||||
fontFamily: 'Arial',
|
||||
fontStyle: 'bold',
|
||||
color: '#ffffff'
|
||||
});
|
||||
exclamation.setOrigin(0.5);
|
||||
this.fishingBobberIndicator.add(exclamation);
|
||||
|
||||
// Text below
|
||||
const text = this.scene.add.text(0, 80, 'FISH BITING!\nPress E', {
|
||||
fontSize: '20px',
|
||||
fontFamily: 'Arial',
|
||||
fontStyle: 'bold',
|
||||
color: '#ffffff',
|
||||
align: 'center'
|
||||
});
|
||||
text.setOrigin(0.5);
|
||||
this.fishingBobberIndicator.add(text);
|
||||
}
|
||||
|
||||
// Show and animate
|
||||
this.fishingBobberIndicator.setVisible(true);
|
||||
this.fishingBobberIndicator.setAlpha(0);
|
||||
|
||||
// Fade in and pulse
|
||||
this.scene.tweens.add({
|
||||
targets: this.fishingBobberIndicator,
|
||||
alpha: 1,
|
||||
duration: 200,
|
||||
onComplete: () => {
|
||||
// Pulse animation
|
||||
this.scene.tweens.add({
|
||||
targets: this.fishingBobberIndicator,
|
||||
scale: { from: 1, to: 1.2 },
|
||||
duration: 300,
|
||||
yoyo: true,
|
||||
repeat: 5,
|
||||
onComplete: () => {
|
||||
// Fade out
|
||||
this.scene.tweens.add({
|
||||
targets: this.fishingBobberIndicator,
|
||||
alpha: 0,
|
||||
duration: 300,
|
||||
onComplete: () => {
|
||||
this.fishingBobberIndicator.setVisible(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log('🎣 Fishing bobber cue shown');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set subtitle background opacity
|
||||
*/
|
||||
setSubtitleOpacity(opacity) {
|
||||
this.settings.subtitleOpacity = Phaser.Math.Clamp(opacity, 0, 1);
|
||||
if (this.subtitleBackground) {
|
||||
this.subtitleBackground.setAlpha(this.settings.subtitleOpacity);
|
||||
}
|
||||
this.saveSettings();
|
||||
console.log('📊 Subtitle opacity set to:', this.settings.subtitleOpacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom speaker color
|
||||
*/
|
||||
addSpeakerColor(speaker, color) {
|
||||
this.speakerColors[speaker] = color;
|
||||
console.log(`🎨 Added speaker color: ${speaker} = ${color}`);
|
||||
}
|
||||
|
||||
// ========== SETTINGS ==========
|
||||
|
||||
toggleHeartbeat(enabled) {
|
||||
@@ -364,6 +655,65 @@ class VisualSoundCueSystem {
|
||||
this.settings.subtitlesEnabled = enabled;
|
||||
if (!enabled) this.hideSubtitle();
|
||||
this.saveSettings();
|
||||
console.log('💬 Subtitles:', enabled ? 'ENABLED' : 'DISABLED');
|
||||
}
|
||||
|
||||
toggleDirectionalArrows(enabled) {
|
||||
this.settings.directionalArrowsEnabled = enabled;
|
||||
if (!enabled) {
|
||||
this.hideDirectionalArrows();
|
||||
}
|
||||
this.saveSettings();
|
||||
console.log('➡️ Directional Arrows:', enabled ? 'ENABLED' : 'DISABLED');
|
||||
}
|
||||
|
||||
toggleSpeakerNames(enabled) {
|
||||
this.settings.speakerNamesEnabled = enabled;
|
||||
this.saveSettings();
|
||||
console.log('👤 Speaker Names:', enabled ? 'ENABLED' : 'DISABLED');
|
||||
}
|
||||
|
||||
toggleFishingBobber(enabled) {
|
||||
this.settings.fishingBobberEnabled = enabled;
|
||||
this.saveSettings();
|
||||
console.log('🎣 Fishing Bobber Cue:', enabled ? 'ENABLED' : 'DISABLED');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set subtitle text size
|
||||
* @param {string} size - 'small', 'medium', 'large', 'very-large'
|
||||
*/
|
||||
setSubtitleSize(size) {
|
||||
if (!this.subtitleSizes[size]) {
|
||||
console.error(`Invalid subtitle size: ${size}. Valid options: small, medium, large, very-large`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.settings.subtitleSize = size;
|
||||
const sizes = this.subtitleSizes[size];
|
||||
|
||||
// Update text sizes
|
||||
if (this.subtitleText) {
|
||||
this.subtitleText.setFontSize(sizes.main);
|
||||
}
|
||||
if (this.subtitleSpeaker) {
|
||||
this.subtitleSpeaker.setFontSize(sizes.speaker);
|
||||
}
|
||||
if (this.subtitleArrows.left) {
|
||||
this.subtitleArrows.left.setFontSize(sizes.arrow);
|
||||
}
|
||||
if (this.subtitleArrows.right) {
|
||||
this.subtitleArrows.right.setFontSize(sizes.arrow);
|
||||
}
|
||||
|
||||
// Adjust background height based on text size
|
||||
if (this.subtitleBackground) {
|
||||
const bgHeight = sizes.main * 4; // 4x font size for padding
|
||||
this.subtitleBackground.setSize(this.subtitleBackground.width, bgHeight);
|
||||
}
|
||||
|
||||
this.saveSettings();
|
||||
console.log(`📏 Subtitle size set to: ${size.toUpperCase()} (${sizes.main}px)`);
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
|
||||
Reference in New Issue
Block a user