485 lines
12 KiB
Markdown
485 lines
12 KiB
Markdown
# ♿ ACCESSIBILITY FEATURES - IMPLEMENTATION PLAN
|
|
|
|
**Datum:** 12. December 2025
|
|
**Prioriteta:** HIGH
|
|
**Estimated Time:** 2-3 ure
|
|
|
|
---
|
|
|
|
## 🎯 **CILJI:**
|
|
|
|
Implementirati celovit accessibility sistem za:
|
|
- High Contrast Modes
|
|
- Color Blind Support
|
|
- Photosensitivity Protection
|
|
|
|
---
|
|
|
|
## 📋 **FAZA 1: HIGH CONTRAST MODES** (30 min)
|
|
|
|
### **1.1 Black & White Mode**
|
|
**Datoteka:** `src/systems/AccessibilitySystem.js` (nova)
|
|
|
|
```javascript
|
|
class AccessibilitySystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.settings = {
|
|
highContrast: 'none', // 'none', 'bw', 'yellow_black'
|
|
largeUI: false,
|
|
boldOutlines: false,
|
|
colorBlindMode: 'none',
|
|
photosensitivity: false
|
|
};
|
|
|
|
this.loadSettings();
|
|
}
|
|
|
|
enableBlackWhite() {
|
|
// Apply grayscale filter to entire game
|
|
this.scene.cameras.main.setPostPipeline('GrayscalePipeline');
|
|
|
|
// Increase contrast
|
|
this.scene.cameras.main.setAlpha(1.2);
|
|
|
|
console.log('🎨 Black & White mode enabled');
|
|
}
|
|
|
|
disableBlackWhite() {
|
|
this.scene.cameras.main.clearPostPipeline();
|
|
this.scene.cameras.main.setAlpha(1.0);
|
|
}
|
|
}
|
|
```
|
|
|
|
### **1.2 Yellow on Black Mode**
|
|
```javascript
|
|
enableYellowOnBlack() {
|
|
// Create color replacement pipeline
|
|
const pipeline = this.scene.game.renderer.pipelines.add('YellowBlackPipeline', {
|
|
fragShader: `
|
|
precision mediump float;
|
|
uniform sampler2D uMainSampler;
|
|
varying vec2 outTexCoord;
|
|
|
|
void main(void) {
|
|
vec4 color = texture2D(uMainSampler, outTexCoord);
|
|
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
|
|
|
|
// Yellow on black
|
|
vec3 yellow = vec3(1.0, 1.0, 0.0);
|
|
vec3 result = yellow * gray;
|
|
|
|
gl_FragColor = vec4(result, color.a);
|
|
}
|
|
`
|
|
});
|
|
|
|
this.scene.cameras.main.setPostPipeline(pipeline);
|
|
}
|
|
```
|
|
|
|
### **1.3 Large UI (150%-200%)**
|
|
```javascript
|
|
enableLargeUI(scale = 1.5) {
|
|
const uiScene = this.scene.scene.get('UIScene');
|
|
if (!uiScene) return;
|
|
|
|
// Scale all UI elements
|
|
uiScene.children.list.forEach(child => {
|
|
if (child.setScale) {
|
|
child.setScale(child.scaleX * scale, child.scaleY * scale);
|
|
}
|
|
});
|
|
|
|
// Adjust positions
|
|
this.repositionUIElements(scale);
|
|
|
|
console.log(`🔍 Large UI enabled: ${scale * 100}%`);
|
|
}
|
|
```
|
|
|
|
### **1.4 Bold Outlines**
|
|
```javascript
|
|
enableBoldOutlines() {
|
|
// Increase stroke thickness on all text
|
|
const uiScene = this.scene.scene.get('UIScene');
|
|
if (!uiScene) return;
|
|
|
|
uiScene.children.list.forEach(child => {
|
|
if (child.type === 'Text') {
|
|
child.setStroke('#000000', 6); // Thicker stroke
|
|
child.setShadow(2, 2, '#000000', 2, true, true);
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 **FAZA 2: COLOR BLIND SUPPORT** (45 min)
|
|
|
|
### **2.1 Protanopia Mode (Red-Blind)**
|
|
```javascript
|
|
enableProtanopia() {
|
|
const pipeline = this.createColorBlindPipeline('protanopia', `
|
|
// Protanopia simulation
|
|
mat3 protanopia = mat3(
|
|
0.567, 0.433, 0.0,
|
|
0.558, 0.442, 0.0,
|
|
0.0, 0.242, 0.758
|
|
);
|
|
|
|
vec3 result = protanopia * color.rgb;
|
|
`);
|
|
|
|
this.scene.cameras.main.setPostPipeline(pipeline);
|
|
}
|
|
```
|
|
|
|
### **2.2 Deuteranopia Mode (Green-Blind)**
|
|
```javascript
|
|
enableDeuteranopia() {
|
|
const pipeline = this.createColorBlindPipeline('deuteranopia', `
|
|
mat3 deuteranopia = mat3(
|
|
0.625, 0.375, 0.0,
|
|
0.7, 0.3, 0.0,
|
|
0.0, 0.3, 0.7
|
|
);
|
|
|
|
vec3 result = deuteranopia * color.rgb;
|
|
`);
|
|
|
|
this.scene.cameras.main.setPostPipeline(pipeline);
|
|
}
|
|
```
|
|
|
|
### **2.3 Tritanopia Mode (Blue-Blind)**
|
|
```javascript
|
|
enableTritanopia() {
|
|
const pipeline = this.createColorBlindPipeline('tritanopia', `
|
|
mat3 tritanopia = mat3(
|
|
0.95, 0.05, 0.0,
|
|
0.0, 0.433, 0.567,
|
|
0.0, 0.475, 0.525
|
|
);
|
|
|
|
vec3 result = tritanopia * color.rgb;
|
|
`);
|
|
|
|
this.scene.cameras.main.setPostPipeline(pipeline);
|
|
}
|
|
```
|
|
|
|
### **2.4 Achromatopsia Mode (Total Color Blind)**
|
|
```javascript
|
|
enableAchromatopsia() {
|
|
// Full grayscale
|
|
this.enableBlackWhite();
|
|
|
|
// Add high contrast
|
|
this.scene.cameras.main.setContrast(1.5);
|
|
}
|
|
```
|
|
|
|
### **2.5 Shape Coding**
|
|
```javascript
|
|
addShapeCoding() {
|
|
// Replace color-only indicators with shapes
|
|
// Example: HP bar = ❤️, Hunger = 🍖, Thirst = 💧
|
|
|
|
const uiScene = this.scene.scene.get('UIScene');
|
|
if (!uiScene) return;
|
|
|
|
// Add icons to bars
|
|
if (uiScene.healthBar) {
|
|
const icon = uiScene.add.text(
|
|
uiScene.healthBar.x - 20,
|
|
uiScene.healthBar.y,
|
|
'❤️',
|
|
{ fontSize: '16px' }
|
|
);
|
|
icon.setScrollFactor(0);
|
|
}
|
|
}
|
|
```
|
|
|
|
### **2.6 Pattern Overlays**
|
|
```javascript
|
|
addPatternOverlays() {
|
|
// Add patterns to differentiate elements
|
|
// Example: HP = solid, Hunger = stripes, Thirst = dots
|
|
|
|
const createPattern = (type) => {
|
|
const graphics = this.scene.add.graphics();
|
|
|
|
if (type === 'stripes') {
|
|
for (let i = 0; i < 10; i++) {
|
|
graphics.lineStyle(2, 0xffffff, 0.3);
|
|
graphics.lineBetween(i * 10, 0, i * 10, 100);
|
|
}
|
|
} else if (type === 'dots') {
|
|
for (let i = 0; i < 5; i++) {
|
|
for (let j = 0; j < 5; j++) {
|
|
graphics.fillStyle(0xffffff, 0.3);
|
|
graphics.fillCircle(i * 20, j * 20, 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
return graphics;
|
|
};
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 **FAZA 3: PHOTOSENSITIVITY PROTECTION** (45 min)
|
|
|
|
### **3.1 Flash Limiter**
|
|
```javascript
|
|
class FlashLimiter {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.flashCount = 0;
|
|
this.flashWindow = 1000; // 1 second
|
|
this.maxFlashes = 3; // Max 3 flashes per second
|
|
this.lastFlashTime = 0;
|
|
}
|
|
|
|
canFlash() {
|
|
const now = Date.now();
|
|
|
|
// Reset counter if window passed
|
|
if (now - this.lastFlashTime > this.flashWindow) {
|
|
this.flashCount = 0;
|
|
this.lastFlashTime = now;
|
|
}
|
|
|
|
// Check limit
|
|
if (this.flashCount >= this.maxFlashes) {
|
|
console.warn('⚠️ Flash limit reached - skipping flash');
|
|
return false;
|
|
}
|
|
|
|
this.flashCount++;
|
|
return true;
|
|
}
|
|
}
|
|
```
|
|
|
|
### **3.2 Disable Lightning Effects**
|
|
```javascript
|
|
disableLightning() {
|
|
if (this.scene.weatherSystem) {
|
|
this.scene.weatherSystem.lightningEnabled = false;
|
|
}
|
|
|
|
// Remove existing lightning effects
|
|
this.scene.children.list.forEach(child => {
|
|
if (child.name === 'lightning') {
|
|
child.destroy();
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
### **3.3 Reduce Particles**
|
|
```javascript
|
|
reduceParticles(level = 0.5) {
|
|
// Reduce particle emission rate
|
|
this.scene.children.list.forEach(child => {
|
|
if (child.type === 'ParticleEmitter') {
|
|
child.setFrequency(child.frequency / level);
|
|
child.setQuantity(Math.floor(child.quantity * level));
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
### **3.4 Epilepsy Warning Screen**
|
|
```javascript
|
|
showEpilepsyWarning() {
|
|
const warning = this.scene.add.container(
|
|
this.scene.cameras.main.centerX,
|
|
this.scene.cameras.main.centerY
|
|
);
|
|
warning.setDepth(10000);
|
|
|
|
const bg = this.scene.add.rectangle(0, 0, 600, 400, 0x000000, 0.95);
|
|
|
|
const title = this.scene.add.text(0, -150, '⚠️ EPILEPSY WARNING', {
|
|
fontSize: '32px',
|
|
color: '#ff0000',
|
|
fontStyle: 'bold'
|
|
}).setOrigin(0.5);
|
|
|
|
const text = this.scene.add.text(0, -50,
|
|
'This game contains flashing lights and patterns\n' +
|
|
'that may trigger seizures in people with\n' +
|
|
'photosensitive epilepsy.\n\n' +
|
|
'Player discretion is advised.',
|
|
{
|
|
fontSize: '18px',
|
|
color: '#ffffff',
|
|
align: 'center',
|
|
wordWrap: { width: 500 }
|
|
}
|
|
).setOrigin(0.5);
|
|
|
|
const enableBtn = this.scene.add.text(0, 120, '[ ENABLE PHOTOSENSITIVITY MODE ]', {
|
|
fontSize: '20px',
|
|
color: '#00ff00',
|
|
backgroundColor: '#003300',
|
|
padding: { x: 20, y: 10 }
|
|
}).setOrigin(0.5);
|
|
|
|
enableBtn.setInteractive({ useHandCursor: true });
|
|
enableBtn.on('pointerdown', () => {
|
|
this.enablePhotosensitivityMode();
|
|
warning.destroy();
|
|
});
|
|
|
|
const continueBtn = this.scene.add.text(0, 170, '[ CONTINUE WITHOUT ]', {
|
|
fontSize: '16px',
|
|
color: '#888888'
|
|
}).setOrigin(0.5);
|
|
|
|
continueBtn.setInteractive({ useHandCursor: true });
|
|
continueBtn.on('pointerdown', () => {
|
|
warning.destroy();
|
|
});
|
|
|
|
warning.add([bg, title, text, enableBtn, continueBtn]);
|
|
}
|
|
```
|
|
|
|
### **3.5 Motion Sickness Options**
|
|
```javascript
|
|
enableMotionSicknessMode() {
|
|
// Reduce camera shake
|
|
this.scene.cameras.main.shake = () => {};
|
|
|
|
// Reduce screen transitions
|
|
this.scene.cameras.main.fadeEffect.duration = 100; // Faster fades
|
|
|
|
// Disable parallax
|
|
if (this.scene.parallaxSystem) {
|
|
this.scene.parallaxSystem.enabled = false;
|
|
}
|
|
|
|
// Reduce zoom changes
|
|
this.scene.cameras.main.zoomTo = (zoom, duration) => {
|
|
this.scene.cameras.main.setZoom(zoom); // Instant
|
|
};
|
|
}
|
|
```
|
|
|
|
### **3.6 Brightness Limiter**
|
|
```javascript
|
|
enableBrightnessLimiter(maxBrightness = 0.8) {
|
|
// Limit maximum brightness
|
|
const overlay = this.scene.add.graphics();
|
|
overlay.fillStyle(0x000000, 1 - maxBrightness);
|
|
overlay.fillRect(0, 0, this.scene.cameras.main.width, this.scene.cameras.main.height);
|
|
overlay.setScrollFactor(0);
|
|
overlay.setDepth(9999);
|
|
|
|
this.brightnessOverlay = overlay;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 **FAZA 4: SETTINGS MENU INTEGRATION** (30 min)
|
|
|
|
### **4.1 Accessibility Settings Menu**
|
|
```javascript
|
|
createAccessibilityMenu() {
|
|
const menu = this.scene.add.container(
|
|
this.scene.cameras.main.centerX,
|
|
this.scene.cameras.main.centerY
|
|
);
|
|
|
|
// Title
|
|
const title = this.scene.add.text(0, -200, '♿ ACCESSIBILITY', {
|
|
fontSize: '32px',
|
|
fontStyle: 'bold'
|
|
}).setOrigin(0.5);
|
|
|
|
// High Contrast
|
|
this.addToggle(menu, 0, -150, 'High Contrast', [
|
|
'None', 'Black & White', 'Yellow on Black'
|
|
], (value) => this.setHighContrast(value));
|
|
|
|
// Large UI
|
|
this.addToggle(menu, 0, -100, 'UI Size', [
|
|
'100%', '150%', '200%'
|
|
], (value) => this.setUISize(value));
|
|
|
|
// Color Blind Mode
|
|
this.addToggle(menu, 0, -50, 'Color Blind Mode', [
|
|
'None', 'Protanopia', 'Deuteranopia', 'Tritanopia', 'Achromatopsia'
|
|
], (value) => this.setColorBlindMode(value));
|
|
|
|
// Photosensitivity
|
|
this.addCheckbox(menu, 0, 0, 'Photosensitivity Protection',
|
|
(checked) => this.setPhotosensitivity(checked));
|
|
|
|
// Motion Sickness
|
|
this.addCheckbox(menu, 0, 50, 'Motion Sickness Mode',
|
|
(checked) => this.setMotionSickness(checked));
|
|
|
|
menu.add(title);
|
|
return menu;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 **IMPLEMENTATION STEPS:**
|
|
|
|
1. **Ustvari AccessibilitySystem.js** (30 min)
|
|
2. **Implementiraj High Contrast Modes** (30 min)
|
|
3. **Implementiraj Color Blind Support** (45 min)
|
|
4. **Implementiraj Photosensitivity Protection** (45 min)
|
|
5. **Ustvari Settings Menu** (30 min)
|
|
6. **Integracija v GameScene** (15 min)
|
|
7. **Testing** (30 min)
|
|
|
|
**Total:** 3h 45min
|
|
|
|
---
|
|
|
|
## 🔧 **DATOTEKE:**
|
|
|
|
**Nove:**
|
|
- `src/systems/AccessibilitySystem.js` (~500 vrstic)
|
|
- `src/pipelines/ColorBlindPipeline.js` (~200 vrstic)
|
|
- `src/pipelines/GrayscalePipeline.js` (~50 vrstic)
|
|
|
|
**Posodobljene:**
|
|
- `src/scenes/GameScene.js` - Initialize AccessibilitySystem
|
|
- `src/scenes/UIScene.js` - Accessibility menu
|
|
- `index.html` - Dodaj nove skripte
|
|
|
|
---
|
|
|
|
## 🎯 **PRIORITETA:**
|
|
|
|
**HIGH** - Accessibility je pomemben za:
|
|
- Večjo dostopnost
|
|
- Širše občinstvo
|
|
- Boljšo uporabniško izkušnjo
|
|
- Compliance s standardi
|
|
|
|
---
|
|
|
|
**Status:** ⏳ **PLAN PRIPRAVLJEN - ČAKA NA IMPLEMENTACIJO**
|
|
|
|
**Priporočam:** Implementacija v naslednji seji (jutri)
|
|
|
|
**Razlog:** Seja že traja 2h 14min, accessibility zahteva 3-4 ure dela.
|
|
|
|
Želite začeti zdaj ali pustim za jutri? 🎮
|