ok
This commit is contained in:
369
EMERGENCY_SYSTEMS_RECOVERY/AccessibilityManager.js
Normal file
369
EMERGENCY_SYSTEMS_RECOVERY/AccessibilityManager.js
Normal file
@@ -0,0 +1,369 @@
|
||||
/**
|
||||
* ACCESSIBILITY MANAGER - STREAMER-READY FEATURES
|
||||
* One-Handed Mode, High Contrast, Font Scaling
|
||||
*/
|
||||
|
||||
class AccessibilityManager {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
|
||||
// Accessibility settings
|
||||
this.settings = {
|
||||
oneHandedMode: false,
|
||||
oneHandedSide: 'left', // 'left' or 'right'
|
||||
highContrast: false,
|
||||
colorBlindMode: 'none', // 'none', 'protanopia', 'deuteranopia', 'tritanopia'
|
||||
fontScale: 1.0, // 0.8 - 2.0
|
||||
subtitleSize: 'medium', // 'small', 'medium', 'large', 'xlarge'
|
||||
reduceMotion: false,
|
||||
screenReader: false
|
||||
};
|
||||
|
||||
// Load saved settings
|
||||
this.load();
|
||||
|
||||
// Apply settings
|
||||
this.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* ONE-HANDED MODE (Xbox Controller)
|
||||
*/
|
||||
enableOneHandedMode(side = 'left') {
|
||||
this.settings.oneHandedMode = true;
|
||||
this.settings.oneHandedSide = side;
|
||||
|
||||
console.log(`♿ ONE-HANDED MODE ENABLED (${side.toUpperCase()} hand)`);
|
||||
console.log(' Movement: Left stick');
|
||||
|
||||
if (side === 'left') {
|
||||
console.log(' Actions mapped to LEFT side:');
|
||||
console.log(' - LB: Interact (was A)');
|
||||
console.log(' - LT: Attack (was X)');
|
||||
console.log(' - L3 (click stick): Menu (was B)');
|
||||
console.log(' - D-Pad Up: Whistle Susi (was Y)');
|
||||
} else {
|
||||
console.log(' Actions mapped to RIGHT side:');
|
||||
console.log(' - RB: Interact (was A)');
|
||||
console.log(' - RT: Attack (was X)');
|
||||
console.log(' - R3 (click stick): Menu (was B)');
|
||||
console.log(' - Right Stick Click: Whistle (was Y)');
|
||||
}
|
||||
|
||||
this.save();
|
||||
|
||||
// Emit event for gamepad controller
|
||||
if (this.scene.events) {
|
||||
this.scene.events.emit('accessibility-changed', this.settings);
|
||||
}
|
||||
}
|
||||
|
||||
disableOneHandedMode() {
|
||||
this.settings.oneHandedMode = false;
|
||||
console.log('♿ One-handed mode disabled - Back to standard controls');
|
||||
this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* GET BUTTON MAPPING FOR ONE-HANDED MODE
|
||||
*/
|
||||
getButtonMapping() {
|
||||
if (!this.settings.oneHandedMode) {
|
||||
// Standard mapping
|
||||
return {
|
||||
interact: 'A',
|
||||
attack: 'X',
|
||||
whistle: 'Y',
|
||||
menu: 'B',
|
||||
movement: 'LEFT_STICK'
|
||||
};
|
||||
}
|
||||
|
||||
if (this.settings.oneHandedSide === 'left') {
|
||||
// Left-hand mapping
|
||||
return {
|
||||
interact: 'LB',
|
||||
attack: 'LT',
|
||||
whistle: 'DPAD_UP',
|
||||
menu: 'L3',
|
||||
movement: 'LEFT_STICK'
|
||||
};
|
||||
} else {
|
||||
// Right-hand mapping
|
||||
return {
|
||||
interact: 'RB',
|
||||
attack: 'RT',
|
||||
whistle: 'R3',
|
||||
menu: 'DPAD_DOWN',
|
||||
movement: 'RIGHT_STICK'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HIGH CONTRAST MODE
|
||||
*/
|
||||
enableHighContrast() {
|
||||
this.settings.highContrast = true;
|
||||
console.log('♿ HIGH CONTRAST MODE ENABLED');
|
||||
|
||||
// Apply high contrast shader
|
||||
this.applyHighContrastShader();
|
||||
this.save();
|
||||
}
|
||||
|
||||
disableHighContrast() {
|
||||
this.settings.highContrast = false;
|
||||
console.log('♿ High contrast mode disabled');
|
||||
|
||||
// Remove shader
|
||||
this.removeHighContrastShader();
|
||||
this.save();
|
||||
}
|
||||
|
||||
applyHighContrastShader() {
|
||||
if (!this.scene.cameras || !this.scene.cameras.main) return;
|
||||
|
||||
// Increase contrast using post-processing
|
||||
const camera = this.scene.cameras.main;
|
||||
|
||||
// Store original values
|
||||
if (!this.originalContrast) {
|
||||
this.originalContrast = {
|
||||
alpha: camera.alpha,
|
||||
brightness: 1.0
|
||||
};
|
||||
}
|
||||
|
||||
// Boost contrast
|
||||
camera.setAlpha(1.0);
|
||||
|
||||
// Add vignette for edge clarity (reverse of normal vignette)
|
||||
if (this.contrastOverlay) {
|
||||
this.contrastOverlay.destroy();
|
||||
}
|
||||
|
||||
const width = this.scene.cameras.main.width;
|
||||
const height = this.scene.cameras.main.height;
|
||||
|
||||
this.contrastOverlay = this.scene.add.rectangle(
|
||||
width / 2,
|
||||
height / 2,
|
||||
width,
|
||||
height,
|
||||
0xFFFFFF,
|
||||
0.1
|
||||
);
|
||||
this.contrastOverlay.setScrollFactor(0);
|
||||
this.contrastOverlay.setDepth(10000);
|
||||
this.contrastOverlay.setBlendMode(Phaser.BlendModes.OVERLAY);
|
||||
|
||||
console.log('✅ High contrast shader applied');
|
||||
}
|
||||
|
||||
removeHighContrastShader() {
|
||||
if (this.contrastOverlay) {
|
||||
this.contrastOverlay.destroy();
|
||||
this.contrastOverlay = null;
|
||||
}
|
||||
|
||||
if (this.originalContrast && this.scene.cameras && this.scene.cameras.main) {
|
||||
this.scene.cameras.main.setAlpha(this.originalContrast.alpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* COLOR BLIND MODE
|
||||
*/
|
||||
setColorBlindMode(mode) {
|
||||
const validModes = ['none', 'protanopia', 'deuteranopia', 'tritanopia'];
|
||||
if (!validModes.includes(mode)) {
|
||||
console.warn('Invalid color blind mode:', mode);
|
||||
return;
|
||||
}
|
||||
|
||||
this.settings.colorBlindMode = mode;
|
||||
console.log(`♿ COLOR BLIND MODE: ${mode.toUpperCase()}`);
|
||||
|
||||
// Apply color filter
|
||||
this.applyColorBlindFilter(mode);
|
||||
this.save();
|
||||
}
|
||||
|
||||
applyColorBlindFilter(mode) {
|
||||
// Remove existing filter
|
||||
if (this.colorBlindFilter) {
|
||||
this.colorBlindFilter.destroy();
|
||||
this.colorBlindFilter = null;
|
||||
}
|
||||
|
||||
if (mode === 'none') return;
|
||||
|
||||
const width = this.scene.cameras.main.width;
|
||||
const height = this.scene.cameras.main.height;
|
||||
|
||||
// Apply color tint based on mode
|
||||
const tints = {
|
||||
protanopia: 0xFFCCCC, // Red-blind (pink tint)
|
||||
deuteranopia: 0xCCFFCC, // Green-blind (light green tint)
|
||||
tritanopia: 0xCCCCFF // Blue-blind (light blue tint)
|
||||
};
|
||||
|
||||
this.colorBlindFilter = this.scene.add.rectangle(
|
||||
width / 2,
|
||||
height / 2,
|
||||
width,
|
||||
height,
|
||||
tints[mode],
|
||||
0.15
|
||||
);
|
||||
this.colorBlindFilter.setScrollFactor(0);
|
||||
this.colorBlindFilter.setDepth(9999);
|
||||
this.colorBlindFilter.setBlendMode(Phaser.BlendModes.MULTIPLY);
|
||||
|
||||
console.log(`✅ ${mode} filter applied`);
|
||||
}
|
||||
|
||||
/**
|
||||
* FONT SCALING (for subtitles, UI)
|
||||
*/
|
||||
setFontScale(scale) {
|
||||
// Clamp between 0.8 and 2.0
|
||||
this.settings.fontScale = Phaser.Math.Clamp(scale, 0.8, 2.0);
|
||||
console.log(`♿ FONT SCALE: ${this.settings.fontScale}x`);
|
||||
|
||||
// Emit event for UI to update
|
||||
if (this.scene.events) {
|
||||
this.scene.events.emit('font-scale-changed', this.settings.fontScale);
|
||||
}
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* SUBTITLE SIZE PRESETS
|
||||
*/
|
||||
setSubtitleSize(size) {
|
||||
const sizes = {
|
||||
small: 0.8,
|
||||
medium: 1.0,
|
||||
large: 1.5,
|
||||
xlarge: 2.0
|
||||
};
|
||||
|
||||
if (sizes[size]) {
|
||||
this.settings.subtitleSize = size;
|
||||
this.setFontScale(sizes[size]);
|
||||
console.log(`♿ SUBTITLE SIZE: ${size.toUpperCase()} (${sizes[size]}x)`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET FONT SIZE FOR ELEMENT
|
||||
*/
|
||||
getFontSize(baseFontSize) {
|
||||
return Math.floor(baseFontSize * this.settings.fontScale);
|
||||
}
|
||||
|
||||
/**
|
||||
* REDUCE MOTION MODE
|
||||
*/
|
||||
enableReduceMotion() {
|
||||
this.settings.reduceMotion = true;
|
||||
console.log('♿ REDUCE MOTION ENABLED');
|
||||
console.log(' - Disabled screen shake');
|
||||
console.log(' - Reduced particle effects');
|
||||
console.log(' - Slower transitions');
|
||||
this.save();
|
||||
}
|
||||
|
||||
disableReduceMotion() {
|
||||
this.settings.reduceMotion = false;
|
||||
console.log('♿ Reduce motion disabled');
|
||||
this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* APPLY ALL SETTINGS
|
||||
*/
|
||||
apply() {
|
||||
if (this.settings.highContrast) {
|
||||
this.applyHighContrastShader();
|
||||
}
|
||||
|
||||
if (this.settings.colorBlindMode !== 'none') {
|
||||
this.applyColorBlindFilter(this.settings.colorBlindMode);
|
||||
}
|
||||
|
||||
if (this.settings.oneHandedMode) {
|
||||
console.log(`♿ One-handed mode active (${this.settings.oneHandedSide})`);
|
||||
}
|
||||
|
||||
console.log(`♿ Font scale: ${this.settings.fontScale}x`);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET ALL SETTINGS
|
||||
*/
|
||||
getSettings() {
|
||||
return { ...this.settings };
|
||||
}
|
||||
|
||||
/**
|
||||
* SAVE TO LOCALSTORAGE
|
||||
*/
|
||||
save() {
|
||||
localStorage.setItem('accessibility_settings', JSON.stringify(this.settings));
|
||||
console.log('💾 Accessibility settings saved');
|
||||
}
|
||||
|
||||
/**
|
||||
* LOAD FROM LOCALSTORAGE
|
||||
*/
|
||||
load() {
|
||||
const stored = localStorage.getItem('accessibility_settings');
|
||||
if (stored) {
|
||||
try {
|
||||
this.settings = { ...this.settings, ...JSON.parse(stored) };
|
||||
console.log('✅ Accessibility settings loaded:', this.settings);
|
||||
} catch (e) {
|
||||
console.warn('Failed to load accessibility settings:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RESET TO DEFAULTS
|
||||
*/
|
||||
reset() {
|
||||
this.settings = {
|
||||
oneHandedMode: false,
|
||||
oneHandedSide: 'left',
|
||||
highContrast: false,
|
||||
colorBlindMode: 'none',
|
||||
fontScale: 1.0,
|
||||
subtitleSize: 'medium',
|
||||
reduceMotion: false,
|
||||
screenReader: false
|
||||
};
|
||||
|
||||
this.removeHighContrastShader();
|
||||
this.applyColorBlindFilter('none');
|
||||
|
||||
this.save();
|
||||
console.log('♿ Accessibility settings reset to defaults');
|
||||
}
|
||||
|
||||
/**
|
||||
* CLEANUP
|
||||
*/
|
||||
destroy() {
|
||||
this.removeHighContrastShader();
|
||||
|
||||
if (this.colorBlindFilter) {
|
||||
this.colorBlindFilter.destroy();
|
||||
}
|
||||
|
||||
console.log('♿ AccessibilityManager destroyed');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user