posodobitve
This commit is contained in:
@@ -464,6 +464,21 @@ class GameScene extends Phaser.Scene {
|
||||
this.particleEffects = new ParticleEffects(this);
|
||||
this.particleEffects.createFallingLeaves();
|
||||
|
||||
// Initialize Accessibility System
|
||||
console.log('♿ Initializing Accessibility System...');
|
||||
this.accessibilitySystem = new AccessibilitySystem(this);
|
||||
|
||||
// Show epilepsy warning on first launch
|
||||
const hasSeenWarning = localStorage.getItem('novafarma_epilepsy_warning');
|
||||
if (!hasSeenWarning) {
|
||||
this.time.delayedCall(2000, () => {
|
||||
this.accessibilitySystem.showEpilepsyWarning(() => {
|
||||
localStorage.setItem('novafarma_epilepsy_warning', 'true');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Generate Item Sprites for UI
|
||||
TextureGenerator.createItemSprites(this);
|
||||
|
||||
|
||||
@@ -375,72 +375,79 @@ class PreloadScene extends Phaser.Scene {
|
||||
const width = this.cameras.main.width;
|
||||
const height = this.cameras.main.height;
|
||||
|
||||
// Background for loading screen
|
||||
// Warm background (Stardew Valley style)
|
||||
const bg = this.add.graphics();
|
||||
bg.fillStyle(0x000000, 1);
|
||||
bg.fillStyle(0x2d1b00, 1);
|
||||
bg.fillRect(0, 0, width, height);
|
||||
|
||||
// Styling
|
||||
const primaryColor = 0x00ff41; // Matrix Green
|
||||
const secondaryColor = 0xffffff;
|
||||
|
||||
// Logo / Title
|
||||
const title = this.add.text(width / 2, height / 2 - 80, 'NOVA FARMA', {
|
||||
fontFamily: 'Courier New', fontSize: '32px', fontStyle: 'bold', fill: '#00cc00'
|
||||
// Simple "LOADING" text
|
||||
const title = this.add.text(width / 2, height / 2 - 80, 'LOADING', {
|
||||
fontFamily: 'Georgia, serif',
|
||||
fontSize: '36px',
|
||||
fontStyle: 'bold',
|
||||
fill: '#f4e4c1',
|
||||
stroke: '#2d1b00',
|
||||
strokeThickness: 4
|
||||
}).setOrigin(0.5);
|
||||
|
||||
const tipText = this.add.text(width / 2, height - 50, 'Tip: Zombies drop blueprints for new tech...', {
|
||||
fontFamily: 'monospace', fontSize: '14px', fill: '#888888', fontStyle: 'italic'
|
||||
}).setOrigin(0.5);
|
||||
// Progress Bar container (wooden style)
|
||||
const barWidth = 400;
|
||||
const barHeight = 30;
|
||||
const barX = width / 2 - barWidth / 2;
|
||||
const barY = height / 2;
|
||||
|
||||
// Progress Bar container
|
||||
const progressBox = this.add.graphics();
|
||||
progressBox.fillStyle(0x111111, 0.8);
|
||||
progressBox.fillRoundedRect(width / 2 - 160, height / 2 - 15, 320, 30, 5);
|
||||
progressBox.lineStyle(2, 0x333333, 1);
|
||||
progressBox.strokeRoundedRect(width / 2 - 160, height / 2 - 15, 320, 30, 5);
|
||||
progressBox.fillStyle(0x4a3520, 0.9);
|
||||
progressBox.fillRoundedRect(barX, barY, barWidth, barHeight, 5);
|
||||
progressBox.lineStyle(3, 0xd4a574, 1);
|
||||
progressBox.strokeRoundedRect(barX, barY, barWidth, barHeight, 5);
|
||||
|
||||
const progressBar = this.add.graphics();
|
||||
|
||||
const percentText = this.add.text(width / 2, height / 2, '0%', {
|
||||
font: '16px Courier New',
|
||||
fill: '#ffffff',
|
||||
fontStyle: 'bold'
|
||||
// Zombie sprite walking on the bar
|
||||
const zombie = this.add.text(barX, barY + barHeight / 2, '🧟', {
|
||||
fontSize: '32px'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
const assetLoadingText = this.add.text(width / 2, height / 2 + 30, 'Initializing...', {
|
||||
font: '12px console', fill: '#aaaaaa'
|
||||
// Percentage text
|
||||
const percentText = this.add.text(width / 2, barY + barHeight / 2, '0%', {
|
||||
font: '16px Georgia',
|
||||
fill: '#f4e4c1',
|
||||
fontStyle: 'bold'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
this.load.on('progress', (value) => {
|
||||
percentText.setText(parseInt(value * 100) + '%');
|
||||
progressBar.clear();
|
||||
progressBar.fillStyle(primaryColor, 1);
|
||||
progressBar.fillStyle(0x6b4423, 1);
|
||||
|
||||
// Smooth fill
|
||||
const w = 310 * value;
|
||||
const w = (barWidth - 10) * value;
|
||||
if (w > 0) {
|
||||
progressBar.fillRoundedRect(width / 2 - 155, height / 2 - 10, w, 20, 2);
|
||||
progressBar.fillRoundedRect(barX + 5, barY + 5, w, barHeight - 10, 2);
|
||||
}
|
||||
});
|
||||
|
||||
this.load.on('fileprogress', (file) => {
|
||||
assetLoadingText.setText(`Loading: ${file.key}`);
|
||||
// Move zombie along the bar (moderate speed)
|
||||
const zombieX = barX + (barWidth * value);
|
||||
zombie.setX(zombieX);
|
||||
|
||||
// Gentle bounce animation
|
||||
const bounce = Math.sin(value * 20) * 3;
|
||||
zombie.setY(barY + barHeight / 2 + bounce);
|
||||
});
|
||||
|
||||
this.load.on('complete', () => {
|
||||
// Fade out animation
|
||||
this.tweens.add({
|
||||
targets: [progressBar, progressBox, percentText, assetLoadingText, title, tipText, bg],
|
||||
targets: [progressBar, progressBox, percentText, title, zombie, bg],
|
||||
alpha: 0,
|
||||
duration: 1000,
|
||||
duration: 800,
|
||||
onComplete: () => {
|
||||
progressBar.destroy();
|
||||
progressBox.destroy();
|
||||
percentText.destroy();
|
||||
assetLoadingText.destroy();
|
||||
title.destroy();
|
||||
tipText.destroy();
|
||||
zombie.destroy();
|
||||
bg.destroy();
|
||||
}
|
||||
});
|
||||
@@ -451,60 +458,10 @@ class PreloadScene extends Phaser.Scene {
|
||||
console.log('✅ PreloadScene: Assets loaded!');
|
||||
window.gameState.currentScene = 'PreloadScene';
|
||||
|
||||
const width = this.cameras.main.width;
|
||||
const height = this.cameras.main.height;
|
||||
|
||||
const title = this.add.text(width / 2, height / 2 - 50, 'KRVAVA ŽETEV', {
|
||||
fontFamily: 'Courier New',
|
||||
fontSize: '48px',
|
||||
fill: '#ff0000',
|
||||
fontStyle: 'bold',
|
||||
stroke: '#000000',
|
||||
strokeThickness: 6
|
||||
});
|
||||
title.setOrigin(0.5);
|
||||
|
||||
const subtitle = this.add.text(width / 2, height / 2 + 10, 'Zombie Roots', {
|
||||
fontFamily: 'Courier New',
|
||||
fontSize: '24px',
|
||||
fill: '#00ff41'
|
||||
});
|
||||
subtitle.setOrigin(0.5);
|
||||
|
||||
const instruction = this.add.text(width / 2, height / 2 + 60, 'Press SPACE to start', {
|
||||
fontFamily: 'Courier New',
|
||||
fontSize: '16px',
|
||||
fill: '#888888'
|
||||
});
|
||||
instruction.setOrigin(0.5);
|
||||
|
||||
this.tweens.add({
|
||||
targets: instruction,
|
||||
alpha: 0.3,
|
||||
duration: 800,
|
||||
yoyo: true,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
const startGame = () => {
|
||||
// Go directly to main menu (StoryScene)
|
||||
this.time.delayedCall(500, () => {
|
||||
console.log('🎮 Starting StoryScene...');
|
||||
this.input.keyboard.off('keydown');
|
||||
this.input.off('pointerdown');
|
||||
this.scene.start('StoryScene');
|
||||
};
|
||||
|
||||
this.time.delayedCall(3000, () => {
|
||||
startGame();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keydown', (event) => {
|
||||
if (event.code === 'Space' || event.code === 'Enter') {
|
||||
startGame();
|
||||
}
|
||||
});
|
||||
|
||||
this.input.on('pointerdown', () => {
|
||||
startGame();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -7,107 +7,61 @@ class StoryScene extends Phaser.Scene {
|
||||
const width = this.cameras.main.width;
|
||||
const height = this.cameras.main.height;
|
||||
|
||||
// Dark background with gradient
|
||||
const bg = this.add.rectangle(0, 0, width, height, 0x0a0a0a);
|
||||
// Warm background (Stardew Valley style)
|
||||
const bg = this.add.rectangle(0, 0, width, height, 0x2d1b00);
|
||||
bg.setOrigin(0);
|
||||
|
||||
// GLOW EFFECT for title (multiple Text objects)
|
||||
const titleGlow1 = this.add.text(width / 2, 80, 'NOVAFARMA', {
|
||||
fontSize: '72px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#00ff41',
|
||||
fontStyle: 'bold'
|
||||
});
|
||||
titleGlow1.setOrigin(0.5);
|
||||
titleGlow1.setAlpha(0.3);
|
||||
titleGlow1.setScale(1.05);
|
||||
// Add subtle texture overlay
|
||||
const overlay = this.add.rectangle(0, 0, width, height, 0x000000, 0.3);
|
||||
overlay.setOrigin(0);
|
||||
|
||||
const titleGlow2 = this.add.text(width / 2, 80, 'NOVAFARMA', {
|
||||
fontSize: '72px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#00ff41',
|
||||
fontStyle: 'bold'
|
||||
});
|
||||
titleGlow2.setOrigin(0.5);
|
||||
titleGlow2.setAlpha(0.5);
|
||||
titleGlow2.setScale(1.02);
|
||||
// MAIN TITLE (horizontal, top center)
|
||||
const titleBg = this.add.rectangle(width / 2, 80, 480, 70, 0x4a3520, 0.9);
|
||||
titleBg.setStrokeStyle(3, 0xd4a574);
|
||||
|
||||
// Main title
|
||||
const title = this.add.text(width / 2, 80, 'NOVAFARMA', {
|
||||
fontSize: '72px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#00ff41',
|
||||
fontSize: '42px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: '#f4e4c1',
|
||||
fontStyle: 'bold',
|
||||
stroke: '#003311',
|
||||
strokeThickness: 8
|
||||
stroke: '#2d1b00',
|
||||
strokeThickness: 4
|
||||
});
|
||||
title.setOrigin(0.5);
|
||||
|
||||
// Pulsing glow animation
|
||||
this.tweens.add({
|
||||
targets: [titleGlow1, titleGlow2],
|
||||
scaleX: 1.08,
|
||||
scaleY: 1.08,
|
||||
alpha: 0.6,
|
||||
duration: 2000,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
|
||||
// Subtle title bounce
|
||||
// Subtle glow
|
||||
this.tweens.add({
|
||||
targets: title,
|
||||
y: 75,
|
||||
duration: 3000,
|
||||
alpha: 0.9,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
duration: 2000,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
|
||||
// Subtitle
|
||||
const subtitle = this.add.text(width / 2, 150, '2084 - Survival Farm', {
|
||||
fontSize: '20px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#888888'
|
||||
const subtitle = this.add.text(width / 2, 120, '~ 2084 - Survival Farm ~', {
|
||||
fontSize: '14px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: '#d4a574',
|
||||
fontStyle: 'italic'
|
||||
});
|
||||
subtitle.setOrigin(0.5);
|
||||
|
||||
// Accessibility Info Panel (Prominent)
|
||||
const accessibilityInfo = this.add.text(width / 2, 190,
|
||||
'♿ ACCESSIBILITY OPTIONS AVAILABLE BELOW ↓\nFor players with visual, motor, or cognitive needs',
|
||||
{
|
||||
fontSize: '14px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#44ff44',
|
||||
align: 'center',
|
||||
backgroundColor: '#1a1a2e',
|
||||
padding: { x: 15, y: 8 }
|
||||
}
|
||||
);
|
||||
accessibilityInfo.setOrigin(0.5);
|
||||
|
||||
// Pulsing animation for visibility
|
||||
this.tweens.add({
|
||||
targets: accessibilityInfo,
|
||||
alpha: 0.7,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
duration: 1500,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
|
||||
// Main Menu Buttons
|
||||
// Main Menu Buttons (center)
|
||||
this.createMainMenu(width, height);
|
||||
|
||||
// Language selector (bottom)
|
||||
// Accessibility icon (top-right)
|
||||
this.createAccessibilityIcon(width, height);
|
||||
|
||||
// Language selector with rotating globe (bottom-right)
|
||||
this.createLanguageSelector(width, height);
|
||||
|
||||
// Version info
|
||||
const version = this.add.text(10, height - 30, 'v0.9.0 ALPHA', {
|
||||
fontSize: '14px',
|
||||
color: '#444444',
|
||||
fontFamily: 'Courier New'
|
||||
color: '#6b4423',
|
||||
fontFamily: 'Georgia, serif'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -115,111 +69,152 @@ class StoryScene extends Phaser.Scene {
|
||||
const buttons = [
|
||||
{
|
||||
label: '▶ NEW GAME',
|
||||
color: '#00ff41',
|
||||
action: () => this.startNewGame(),
|
||||
description: 'Start a new adventure'
|
||||
color: '#8fbc8f',
|
||||
action: () => this.startNewGame()
|
||||
},
|
||||
{
|
||||
label: '📁 LOAD GAME',
|
||||
color: '#4477ff',
|
||||
action: () => this.loadGame(),
|
||||
description: 'Continue your saved game'
|
||||
color: '#87ceeb',
|
||||
action: () => this.loadGame()
|
||||
},
|
||||
{
|
||||
label: '⚙️ SETTINGS',
|
||||
color: '#ffaa00',
|
||||
action: () => this.showSettings(),
|
||||
description: 'Graphics, audio, controls'
|
||||
},
|
||||
{
|
||||
label: '♿ ACCESSIBILITY',
|
||||
color: '#44ff44',
|
||||
action: () => this.showAccessibility(),
|
||||
description: 'Options for disabilities: Vision, Motor, Cognitive, Epilepsy'
|
||||
color: '#daa520',
|
||||
action: () => this.showSettings()
|
||||
},
|
||||
{
|
||||
label: '❌ EXIT',
|
||||
color: '#ff4444',
|
||||
action: () => this.exitGame(),
|
||||
description: 'Close the game'
|
||||
color: '#cd5c5c',
|
||||
action: () => this.exitGame()
|
||||
}
|
||||
];
|
||||
|
||||
const startY = 230;
|
||||
const spacing = 75;
|
||||
const startY = 170;
|
||||
const spacing = 58;
|
||||
|
||||
buttons.forEach((btn, index) => {
|
||||
const y = startY + (index * spacing);
|
||||
|
||||
// Button background
|
||||
const bg = this.add.rectangle(width / 2, y, 400, 60, 0x1a1a2e, 1);
|
||||
bg.setStrokeStyle(3, 0x00ff41);
|
||||
// Wooden button background (Stardew style)
|
||||
const bg = this.add.rectangle(width / 2, y, 280, 48, 0x6b4423, 1);
|
||||
bg.setStrokeStyle(2, 0xd4a574);
|
||||
|
||||
// Inner shadow effect
|
||||
const innerShadow = this.add.rectangle(width / 2, y + 2, 270, 38, 0x4a3520, 0.5);
|
||||
|
||||
// Button text
|
||||
const text = this.add.text(width / 2, y, btn.label, {
|
||||
fontSize: '28px',
|
||||
fontFamily: 'Courier New',
|
||||
fontSize: '20px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: btn.color,
|
||||
fontStyle: 'bold'
|
||||
fontStyle: 'bold',
|
||||
stroke: '#2d1b00',
|
||||
strokeThickness: 3
|
||||
});
|
||||
text.setOrigin(0.5);
|
||||
|
||||
// Description text (hidden by default)
|
||||
const desc = this.add.text(width / 2, y + 40, btn.description, {
|
||||
fontSize: '12px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#888888',
|
||||
align: 'center'
|
||||
});
|
||||
desc.setOrigin(0.5);
|
||||
desc.setAlpha(0); // Hidden initially
|
||||
|
||||
// Make interactive
|
||||
bg.setInteractive({ useHandCursor: true });
|
||||
bg.on('pointerover', () => {
|
||||
bg.setFillStyle(0x2a2a4e);
|
||||
text.setScale(1.1);
|
||||
desc.setAlpha(1); // Show description
|
||||
bg.setFillStyle(0x8b5a3c);
|
||||
text.setScale(1.05);
|
||||
bg.setStrokeStyle(4, 0xf4e4c1);
|
||||
});
|
||||
bg.on('pointerout', () => {
|
||||
bg.setFillStyle(0x1a1a2e);
|
||||
bg.setFillStyle(0x6b4423);
|
||||
text.setScale(1.0);
|
||||
desc.setAlpha(0); // Hide description
|
||||
bg.setStrokeStyle(3, 0xd4a574);
|
||||
});
|
||||
bg.on('pointerdown', () => {
|
||||
// Flash effect
|
||||
// Press effect
|
||||
this.tweens.add({
|
||||
targets: bg,
|
||||
alpha: 0.5,
|
||||
yoyo: true,
|
||||
targets: [bg, text, innerShadow],
|
||||
y: y + 3,
|
||||
duration: 100,
|
||||
yoyo: true,
|
||||
onComplete: btn.action
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createAccessibilityIcon(width, height) {
|
||||
// Accessibility icon (top-right) - Stardew style
|
||||
const iconBg = this.add.circle(width - 50, 40, 26, 0x6b4423);
|
||||
iconBg.setStrokeStyle(2, 0xd4a574);
|
||||
|
||||
const icon = this.add.text(width - 50, 40, '♿', {
|
||||
fontSize: '32px',
|
||||
color: '#8fbc8f'
|
||||
});
|
||||
icon.setOrigin(0.5);
|
||||
icon.setInteractive({ useHandCursor: true });
|
||||
|
||||
// Gentle pulse animation
|
||||
this.tweens.add({
|
||||
targets: [icon, iconBg],
|
||||
scale: 1.08,
|
||||
duration: 1500,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
|
||||
icon.on('pointerover', () => {
|
||||
icon.setScale(1.2);
|
||||
iconBg.setScale(1.2);
|
||||
iconBg.setFillStyle(0x8b5a3c);
|
||||
});
|
||||
icon.on('pointerout', () => {
|
||||
icon.setScale(1.0);
|
||||
iconBg.setScale(1.0);
|
||||
iconBg.setFillStyle(0x6b4423);
|
||||
});
|
||||
icon.on('pointerdown', () => {
|
||||
this.showAccessibility();
|
||||
});
|
||||
}
|
||||
|
||||
createLanguageSelector(width, height) {
|
||||
// Initialize localization
|
||||
if (!window.i18n) {
|
||||
window.i18n = new LocalizationSystem();
|
||||
}
|
||||
|
||||
// Globe button (bottom-right)
|
||||
const globeBtn = this.add.text(width - 80, height - 60, '🌍', {
|
||||
fontSize: '48px'
|
||||
// Wooden circle background for globe (Stardew style)
|
||||
const globeBg = this.add.circle(width - 60, height - 60, 30, 0x6b4423);
|
||||
globeBg.setStrokeStyle(2, 0xd4a574);
|
||||
|
||||
// Rotating globe button
|
||||
const globeBtn = this.add.text(width - 60, height - 60, '🌍', {
|
||||
fontSize: '42px'
|
||||
});
|
||||
globeBtn.setOrigin(0.5);
|
||||
globeBtn.setInteractive({ useHandCursor: true });
|
||||
|
||||
// Continuous rotation animation
|
||||
this.tweens.add({
|
||||
targets: globeBtn,
|
||||
angle: 360,
|
||||
duration: 8000,
|
||||
repeat: -1,
|
||||
ease: 'Linear'
|
||||
});
|
||||
|
||||
let langMenuOpen = false;
|
||||
let langMenu = null;
|
||||
|
||||
globeBtn.on('pointerover', () => {
|
||||
globeBtn.setScale(1.2);
|
||||
globeBtn.setScale(1.15);
|
||||
globeBg.setScale(1.15);
|
||||
globeBg.setFillStyle(0x8b5a3c);
|
||||
});
|
||||
globeBtn.on('pointerout', () => {
|
||||
if (!langMenuOpen) globeBtn.setScale(1.0);
|
||||
if (!langMenuOpen) {
|
||||
globeBtn.setScale(1.0);
|
||||
globeBg.setScale(1.0);
|
||||
globeBg.setFillStyle(0x6b4423);
|
||||
}
|
||||
});
|
||||
globeBtn.on('pointerdown', () => {
|
||||
if (langMenuOpen) {
|
||||
@@ -245,45 +240,55 @@ class StoryScene extends Phaser.Scene {
|
||||
const container = this.add.container(0, 0);
|
||||
|
||||
const languages = [
|
||||
{ code: 'slo', flag: '🇸🇮', name: 'SLO' },
|
||||
{ code: 'en', flag: '🇬🇧', name: 'ENG' },
|
||||
{ code: 'de', flag: '🇩🇪', name: 'DEU' },
|
||||
{ code: 'it', flag: '🇮🇹', name: 'ITA' },
|
||||
{ code: 'slo', flag: '🇸🇮', name: 'Slovenščina' },
|
||||
{ code: 'en', flag: '🇬🇧', name: 'English' },
|
||||
{ code: 'de', flag: '🇩🇪', name: 'Deutsch' },
|
||||
{ code: 'it', flag: '🇮🇹', name: 'Italiano' },
|
||||
{ code: 'cn', flag: '🇨🇳', name: '中文' }
|
||||
];
|
||||
|
||||
const menuX = width - 220;
|
||||
const menuX = width - 200;
|
||||
const menuY = height - 350;
|
||||
const menuW = 200;
|
||||
const menuH = 280;
|
||||
const menuW = 180;
|
||||
const menuH = 290;
|
||||
|
||||
// Panel background
|
||||
const panel = this.add.rectangle(menuX, menuY, menuW, menuH, 0x1a1a2e, 0.98);
|
||||
panel.setStrokeStyle(3, 0x00ff41);
|
||||
// Wooden panel background (Stardew style)
|
||||
const panel = this.add.rectangle(menuX, menuY, menuW, menuH, 0x6b4423, 0.98);
|
||||
panel.setStrokeStyle(4, 0xd4a574);
|
||||
container.add(panel);
|
||||
|
||||
// Title
|
||||
const title = this.add.text(menuX, menuY - 120, 'LANGUAGE', {
|
||||
fontSize: '18px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: '#f4e4c1',
|
||||
fontStyle: 'bold'
|
||||
});
|
||||
title.setOrigin(0.5);
|
||||
container.add(title);
|
||||
|
||||
// Language buttons
|
||||
languages.forEach((lang, index) => {
|
||||
const btnY = menuY - 100 + (index * 55);
|
||||
const btnY = menuY - 90 + (index * 56);
|
||||
const isActive = window.i18n.getCurrentLanguage() === lang.code;
|
||||
|
||||
const btn = this.add.text(menuX, btnY, `${lang.flag} ${lang.name}`, {
|
||||
fontSize: '20px',
|
||||
fontFamily: 'Courier New',
|
||||
color: isActive ? '#00ff41' : '#ffffff',
|
||||
backgroundColor: isActive ? '#333333' : '#1a1a2e',
|
||||
padding: { x: 15, y: 8 }
|
||||
fontSize: '16px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: isActive ? '#8fbc8f' : '#f4e4c1',
|
||||
backgroundColor: isActive ? '#4a3520' : '#6b4423',
|
||||
padding: { x: 12, y: 6 }
|
||||
});
|
||||
btn.setOrigin(0.5);
|
||||
btn.setInteractive({ useHandCursor: true });
|
||||
|
||||
btn.on('pointerover', () => {
|
||||
btn.setScale(1.05);
|
||||
if (!isActive) btn.setBackgroundColor('#2a2a4e');
|
||||
if (!isActive) btn.setBackgroundColor('#8b5a3c');
|
||||
});
|
||||
btn.on('pointerout', () => {
|
||||
btn.setScale(1.0);
|
||||
if (!isActive) btn.setBackgroundColor('#1a1a2e');
|
||||
if (!isActive) btn.setBackgroundColor('#6b4423');
|
||||
});
|
||||
btn.on('pointerdown', () => {
|
||||
window.i18n.setLanguage(lang.code);
|
||||
|
||||
@@ -66,6 +66,15 @@ class UIScene extends Phaser.Scene {
|
||||
this.toggleSkillTree();
|
||||
});
|
||||
|
||||
// Q/E za Tool Swap
|
||||
this.input.keyboard.on('keydown-Q', () => {
|
||||
this.swapToolPrevious();
|
||||
});
|
||||
|
||||
this.input.keyboard.on('keydown-E', () => {
|
||||
this.swapToolNext();
|
||||
});
|
||||
|
||||
// Define Recipes
|
||||
this.craftingRecipes = [
|
||||
{ id: 'axe', name: 'Stone Axe', req: { 'wood': 3, 'stone': 3 }, output: 1, type: 'tool', desc: 'Used for chopping trees.' },
|
||||
@@ -94,6 +103,15 @@ class UIScene extends Phaser.Scene {
|
||||
this.createOxygenBar();
|
||||
this.createZombieStatsPanel();
|
||||
this.createFarmStatsPanel();
|
||||
this.createEquipmentPreview(); // Equipment preview (top-left)
|
||||
}
|
||||
|
||||
update() {
|
||||
// Update equipment preview
|
||||
this.updateEquipmentPreview();
|
||||
|
||||
// Update minimap
|
||||
this.updateMinimap();
|
||||
}
|
||||
|
||||
// ... (rest of class) ...
|
||||
@@ -342,6 +360,11 @@ class UIScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
drawUI() {
|
||||
// Initialize i18n if not available
|
||||
if (!window.i18n) {
|
||||
window.i18n = new LocalizationSystem();
|
||||
}
|
||||
|
||||
const x = 20;
|
||||
const y = 20;
|
||||
const width = 150; // Zmanjšana širina (300 -> 150)
|
||||
@@ -349,27 +372,58 @@ class UIScene extends Phaser.Scene {
|
||||
const padding = 10;
|
||||
|
||||
// 1. Health Bar
|
||||
this.healthBar = this.drawBar(x, y, width, height, 0xff0000, 100, 'HP');
|
||||
this.healthBar = this.drawBar(x, y, width, height, 0xff0000, 100, window.i18n.t('ui.hp', 'HP'));
|
||||
|
||||
// 2. Hunger Bar
|
||||
this.hungerBar = this.drawBar(x, y + height + padding, width, height, 0xff8800, 80, 'HUN');
|
||||
this.hungerBar = this.drawBar(x, y + height + padding, width, height, 0xff8800, 80, window.i18n.t('ui.hun', 'HUN'));
|
||||
|
||||
// 3. Thirst Bar
|
||||
this.thirstBar = this.drawBar(x, y + (height + padding) * 2, width, height, 0x0088ff, 90, 'H2O');
|
||||
this.thirstBar = this.drawBar(x, y + (height + padding) * 2, width, height, 0x0088ff, 90, window.i18n.t('ui.h2o', 'H2O'));
|
||||
|
||||
// 4. XP Bar
|
||||
this.XPBar = this.drawBar(x, y + (height + padding) * 3, width, height, 0xFFD700, 0, 'XP');
|
||||
this.XPBar = this.drawBar(x, y + (height + padding) * 3, width, height, 0xFFD700, 0, window.i18n.t('ui.xp', 'XP'));
|
||||
|
||||
// 5. Level Display
|
||||
this.LevelDisplay = this.add.text(x, y + (height + padding) * 4, 'LV: 1', {
|
||||
this.LevelDisplay = this.add.text(x, y + (height + padding) * 4, window.i18n.t('ui.lv', 'LV') + ': 1', {
|
||||
fontSize: '18px', fontFamily: 'Courier New', fill: '#FFD700', fontStyle: 'bold'
|
||||
});
|
||||
}
|
||||
|
||||
refreshUIBars() {
|
||||
// Destroy existing bars
|
||||
if (this.healthBar) {
|
||||
this.healthBar.bg.destroy();
|
||||
this.healthBar.fill.destroy();
|
||||
if (this.healthBar.label) this.healthBar.label.destroy();
|
||||
}
|
||||
if (this.hungerBar) {
|
||||
this.hungerBar.bg.destroy();
|
||||
this.hungerBar.fill.destroy();
|
||||
if (this.hungerBar.label) this.hungerBar.label.destroy();
|
||||
}
|
||||
if (this.thirstBar) {
|
||||
this.thirstBar.bg.destroy();
|
||||
this.thirstBar.fill.destroy();
|
||||
if (this.thirstBar.label) this.thirstBar.label.destroy();
|
||||
}
|
||||
if (this.XPBar) {
|
||||
this.XPBar.bg.destroy();
|
||||
this.XPBar.fill.destroy();
|
||||
if (this.XPBar.label) this.XPBar.label.destroy();
|
||||
}
|
||||
if (this.LevelDisplay) {
|
||||
this.LevelDisplay.destroy();
|
||||
}
|
||||
|
||||
// Recreate with new translations
|
||||
this.drawUI();
|
||||
}
|
||||
|
||||
drawBar(x, y, width, height, color, initialPercent = 100, label = '') {
|
||||
// Label
|
||||
let labelText = null;
|
||||
if (label) {
|
||||
this.add.text(x, y - 12, label, { fontSize: '12px', fontFamily: 'Courier New', fill: '#ffffff' });
|
||||
labelText = this.add.text(x, y - 12, label, { fontSize: '12px', fontFamily: 'Courier New', fill: '#ffffff' });
|
||||
}
|
||||
|
||||
// Background
|
||||
@@ -387,7 +441,7 @@ class UIScene extends Phaser.Scene {
|
||||
const currentWidth = (maxWidth * initialPercent) / 100;
|
||||
fill.fillRect(x + 2, y + 2, currentWidth, height - 4);
|
||||
|
||||
return { bg, fill, x, y, width, height, color };
|
||||
return { bg, fill, label: labelText, x, y, width, height, color };
|
||||
}
|
||||
|
||||
setBarValue(bar, percent) {
|
||||
@@ -1890,10 +1944,21 @@ class UIScene extends Phaser.Scene {
|
||||
if (!isActive) btn.setBackgroundColor('#222222');
|
||||
});
|
||||
btn.on('pointerdown', () => {
|
||||
console.log('🌍 Changing language to:', lang.code);
|
||||
window.i18n.setLanguage(lang.code);
|
||||
// Refresh settings menu to update colors
|
||||
this.toggleSettingsMenu();
|
||||
|
||||
// Refresh UI bars with new language
|
||||
this.refreshUIBars();
|
||||
|
||||
// Close settings menu
|
||||
this.toggleSettingsMenu();
|
||||
|
||||
// Close pause menu and resume game
|
||||
if (this.pauseMenuActive) {
|
||||
this.togglePauseMenu();
|
||||
}
|
||||
|
||||
console.log('✅ Language changed and game resumed!');
|
||||
});
|
||||
|
||||
this.settingsContainer.add(btn);
|
||||
@@ -1920,8 +1985,11 @@ class UIScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
togglePauseMenu() {
|
||||
console.log('🎮 Toggle Pause Menu - Active:', this.pauseMenuActive);
|
||||
|
||||
if (this.pauseMenuActive) {
|
||||
// Close pause menu
|
||||
console.log('📴 Closing pause menu...');
|
||||
if (this.pauseMenuContainer) {
|
||||
this.pauseMenuContainer.destroy();
|
||||
this.pauseMenuContainer = null;
|
||||
@@ -1931,15 +1999,18 @@ class UIScene extends Phaser.Scene {
|
||||
// Resume game
|
||||
if (this.gameScene) {
|
||||
this.gameScene.physics.resume();
|
||||
console.log('▶️ Game resumed!');
|
||||
}
|
||||
} else {
|
||||
// Open pause menu
|
||||
console.log('⏸️ Opening pause menu...');
|
||||
this.createPauseMenu();
|
||||
this.pauseMenuActive = true;
|
||||
|
||||
// Pause game
|
||||
if (this.gameScene) {
|
||||
this.gameScene.physics.pause();
|
||||
console.log('⏸️ Game paused!');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1957,28 +2028,30 @@ class UIScene extends Phaser.Scene {
|
||||
this.pauseMenuContainer.setScrollFactor(0);
|
||||
this.pauseMenuContainer.setDepth(15000);
|
||||
|
||||
// Semi-transparent background
|
||||
const bg = this.add.rectangle(0, 0, width, height, 0x000000, 0.85);
|
||||
// Warm semi-transparent background (Stardew style)
|
||||
const bg = this.add.rectangle(0, 0, width, height, 0x2d1b00, 0.85);
|
||||
bg.setOrigin(0);
|
||||
this.pauseMenuContainer.add(bg);
|
||||
|
||||
// Panel
|
||||
const panelW = 500;
|
||||
const panelH = 550;
|
||||
// Wooden panel
|
||||
const panelW = 450;
|
||||
const panelH = 500;
|
||||
const panelX = width / 2 - panelW / 2;
|
||||
const panelY = height / 2 - panelH / 2;
|
||||
|
||||
const panel = this.add.rectangle(panelX, panelY, panelW, panelH, 0x1a1a2e, 1);
|
||||
const panel = this.add.rectangle(panelX, panelY, panelW, panelH, 0x6b4423, 1);
|
||||
panel.setOrigin(0);
|
||||
panel.setStrokeStyle(4, 0x00ff41);
|
||||
panel.setStrokeStyle(4, 0xd4a574);
|
||||
this.pauseMenuContainer.add(panel);
|
||||
|
||||
// Title
|
||||
const title = this.add.text(width / 2, panelY + 40, '⏸️ ' + window.i18n.t('pause.title', 'PAUSED'), {
|
||||
fontSize: '48px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#00ff41',
|
||||
fontStyle: 'bold'
|
||||
const title = this.add.text(width / 2, panelY + 50, window.i18n.t('pause.title', 'PAUSED'), {
|
||||
fontSize: '42px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: '#f4e4c1',
|
||||
fontStyle: 'bold',
|
||||
stroke: '#2d1b00',
|
||||
strokeThickness: 4
|
||||
});
|
||||
title.setOrigin(0.5);
|
||||
this.pauseMenuContainer.add(title);
|
||||
@@ -1987,74 +2060,83 @@ class UIScene extends Phaser.Scene {
|
||||
const buttons = [
|
||||
{
|
||||
label: window.i18n.t('pause.resume', '▶ Resume'),
|
||||
color: '#00ff41',
|
||||
color: '#8fbc8f',
|
||||
action: () => this.togglePauseMenu()
|
||||
},
|
||||
{
|
||||
label: window.i18n.t('pause.save', '💾 Save Game'),
|
||||
color: '#4477ff',
|
||||
color: '#87ceeb',
|
||||
action: () => this.saveGame()
|
||||
},
|
||||
{
|
||||
label: window.i18n.t('pause.settings', '⚙️ Settings'),
|
||||
color: '#ffaa00',
|
||||
color: '#daa520',
|
||||
action: () => { this.togglePauseMenu(); this.toggleSettingsMenu(); }
|
||||
},
|
||||
{
|
||||
label: window.i18n.t('pause.quit', '🚪 Quit to Menu'),
|
||||
color: '#ff4444',
|
||||
color: '#cd5c5c',
|
||||
action: () => this.quitToMenu()
|
||||
}
|
||||
];
|
||||
|
||||
const startY = panelY + 150;
|
||||
const spacing = 90;
|
||||
const startY = panelY + 140;
|
||||
const spacing = 80;
|
||||
|
||||
buttons.forEach((btn, index) => {
|
||||
const y = startY + (index * spacing);
|
||||
|
||||
// Button background
|
||||
const btnBg = this.add.rectangle(width / 2, y, 400, 70, 0x2a2a4e, 1);
|
||||
btnBg.setStrokeStyle(3, btn.color);
|
||||
// Wooden button background
|
||||
const btnBg = this.add.rectangle(width / 2, y, 350, 60, 0x4a3520, 1);
|
||||
btnBg.setStrokeStyle(3, 0xd4a574);
|
||||
|
||||
// Inner shadow
|
||||
const innerShadow = this.add.rectangle(width / 2, y + 2, 340, 50, 0x2d1b00, 0.3);
|
||||
|
||||
// Button text
|
||||
const btnText = this.add.text(width / 2, y, btn.label, {
|
||||
fontSize: '28px',
|
||||
fontFamily: 'Courier New',
|
||||
fontSize: '24px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: btn.color,
|
||||
fontStyle: 'bold'
|
||||
fontStyle: 'bold',
|
||||
stroke: '#2d1b00',
|
||||
strokeThickness: 3
|
||||
});
|
||||
btnText.setOrigin(0.5);
|
||||
|
||||
// Make interactive
|
||||
btnBg.setInteractive({ useHandCursor: true });
|
||||
btnBg.on('pointerover', () => {
|
||||
btnBg.setFillStyle(0x3a3a6e);
|
||||
btnBg.setFillStyle(0x6b4423);
|
||||
btnText.setScale(1.05);
|
||||
btnBg.setStrokeStyle(4, 0xf4e4c1);
|
||||
});
|
||||
btnBg.on('pointerout', () => {
|
||||
btnBg.setFillStyle(0x2a2a4e);
|
||||
btnBg.setFillStyle(0x4a3520);
|
||||
btnText.setScale(1.0);
|
||||
btnBg.setStrokeStyle(3, 0xd4a574);
|
||||
});
|
||||
btnBg.on('pointerdown', () => {
|
||||
this.tweens.add({
|
||||
targets: btnBg,
|
||||
alpha: 0.5,
|
||||
yoyo: true,
|
||||
targets: [btnBg, btnText, innerShadow],
|
||||
y: y + 3,
|
||||
duration: 100,
|
||||
yoyo: true,
|
||||
onComplete: btn.action
|
||||
});
|
||||
});
|
||||
|
||||
this.pauseMenuContainer.add(innerShadow);
|
||||
this.pauseMenuContainer.add(btnBg);
|
||||
this.pauseMenuContainer.add(btnText);
|
||||
});
|
||||
|
||||
// Hint text
|
||||
const hint = this.add.text(width / 2, panelY + panelH - 30, window.i18n.t('pause.hint', 'Press ESC to resume'), {
|
||||
fontSize: '16px',
|
||||
fontFamily: 'Courier New',
|
||||
color: '#888888'
|
||||
const hint = this.add.text(width / 2, panelY + panelH - 40, window.i18n.t('pause.hint', 'Press ESC to resume'), {
|
||||
fontSize: '14px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
color: '#d4a574',
|
||||
fontStyle: 'italic'
|
||||
});
|
||||
hint.setOrigin(0.5);
|
||||
this.pauseMenuContainer.add(hint);
|
||||
@@ -2099,8 +2181,8 @@ class UIScene extends Phaser.Scene {
|
||||
// ========== NEW: ZOMBIE & FARM STATS PANELS ==========
|
||||
|
||||
createZombieStatsPanel() {
|
||||
const panelWidth = 220;
|
||||
const panelHeight = 140;
|
||||
const panelWidth = 180;
|
||||
const panelHeight = 130;
|
||||
const x = 20;
|
||||
const y = 120; // Below player stats
|
||||
|
||||
@@ -2117,8 +2199,8 @@ class UIScene extends Phaser.Scene {
|
||||
this.zombieStatsContainer.add(bg);
|
||||
|
||||
// Title
|
||||
const title = this.add.text(panelWidth / 2, 15, '🧟 ZOMBIE WORKER', {
|
||||
fontSize: '14px',
|
||||
const title = this.add.text(panelWidth / 2, 12, '🧟 ZOMBIE', {
|
||||
fontSize: '12px',
|
||||
fontFamily: 'Courier New',
|
||||
fill: '#8a2be2',
|
||||
fontStyle: 'bold'
|
||||
@@ -2126,29 +2208,29 @@ class UIScene extends Phaser.Scene {
|
||||
this.zombieStatsContainer.add(title);
|
||||
|
||||
// Stats Text
|
||||
this.zombieNameText = this.add.text(10, 35, 'Name: Worker #1', {
|
||||
fontSize: '12px',
|
||||
this.zombieNameText = this.add.text(8, 30, 'Name: Worker #1', {
|
||||
fontSize: '10px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
this.zombieStatsContainer.add(this.zombieNameText);
|
||||
|
||||
this.zombieTaskText = this.add.text(10, 55, 'Task: IDLE', {
|
||||
fontSize: '12px',
|
||||
this.zombieTaskText = this.add.text(8, 48, 'Task: IDLE', {
|
||||
fontSize: '10px',
|
||||
fill: '#ffff00'
|
||||
});
|
||||
this.zombieStatsContainer.add(this.zombieTaskText);
|
||||
|
||||
this.zombieLevelText = this.add.text(10, 75, 'Level: 1 (0/100 XP)', {
|
||||
fontSize: '12px',
|
||||
this.zombieLevelText = this.add.text(8, 66, 'Level: 1 (0/100 XP)', {
|
||||
fontSize: '10px',
|
||||
fill: '#00ff00'
|
||||
});
|
||||
this.zombieStatsContainer.add(this.zombieLevelText);
|
||||
|
||||
// Energy Bar
|
||||
const energyLabel = this.add.text(10, 95, 'ENERGY:', { fontSize: '11px', fill: '#aaaaaa' });
|
||||
const energyLabel = this.add.text(8, 84, 'ENERGY:', { fontSize: '9px', fill: '#aaaaaa' });
|
||||
this.zombieStatsContainer.add(energyLabel);
|
||||
|
||||
this.zombieEnergyBar = this.drawMiniBar(10, 110, panelWidth - 20, 15, 0x00aaff, 100);
|
||||
this.zombieEnergyBar = this.drawMiniBar(8, 98, panelWidth - 16, 12, 0x00aaff, 100);
|
||||
this.zombieStatsContainer.add(this.zombieEnergyBar.bg);
|
||||
this.zombieStatsContainer.add(this.zombieEnergyBar.fill);
|
||||
|
||||
@@ -2157,10 +2239,10 @@ class UIScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
createFarmStatsPanel() {
|
||||
const panelWidth = 220;
|
||||
const panelHeight = 120;
|
||||
const panelWidth = 180;
|
||||
const panelHeight = 110;
|
||||
const x = 20;
|
||||
const y = 280; // Below zombie stats
|
||||
const y = 270; // Below zombie stats
|
||||
|
||||
// Container
|
||||
this.farmStatsContainer = this.add.container(x, y);
|
||||
@@ -2175,8 +2257,8 @@ class UIScene extends Phaser.Scene {
|
||||
this.farmStatsContainer.add(bg);
|
||||
|
||||
// Title
|
||||
const title = this.add.text(panelWidth / 2, 15, '🌾 FARM STATS', {
|
||||
fontSize: '14px',
|
||||
const title = this.add.text(panelWidth / 2, 12, '🌾 FARM', {
|
||||
fontSize: '12px',
|
||||
fontFamily: 'Courier New',
|
||||
fill: '#00ff00',
|
||||
fontStyle: 'bold'
|
||||
@@ -2184,26 +2266,26 @@ class UIScene extends Phaser.Scene {
|
||||
this.farmStatsContainer.add(title);
|
||||
|
||||
// Stats
|
||||
this.farmCropsPlantedText = this.add.text(10, 40, 'Crops Planted: 0', {
|
||||
fontSize: '12px',
|
||||
this.farmCropsPlantedText = this.add.text(8, 32, 'Planted: 0', {
|
||||
fontSize: '10px',
|
||||
fill: '#ffffff'
|
||||
});
|
||||
this.farmStatsContainer.add(this.farmCropsPlantedText);
|
||||
|
||||
this.farmCropsHarvestedText = this.add.text(10, 60, 'Total Harvested: 0', {
|
||||
fontSize: '12px',
|
||||
this.farmCropsHarvestedText = this.add.text(8, 50, 'Harvested: 0', {
|
||||
fontSize: '10px',
|
||||
fill: '#ffff00'
|
||||
});
|
||||
this.farmStatsContainer.add(this.farmCropsHarvestedText);
|
||||
|
||||
this.farmGoldEarnedText = this.add.text(10, 80, 'Gold Earned: 0g', {
|
||||
fontSize: '12px',
|
||||
this.farmGoldEarnedText = this.add.text(8, 68, 'Gold: 0g', {
|
||||
fontSize: '10px',
|
||||
fill: '#ffd700'
|
||||
});
|
||||
this.farmStatsContainer.add(this.farmGoldEarnedText);
|
||||
|
||||
this.farmDaysText = this.add.text(10, 100, 'Days Farmed: 0', {
|
||||
fontSize: '12px',
|
||||
this.farmDaysText = this.add.text(8, 86, 'Days: 0', {
|
||||
fontSize: '10px',
|
||||
fill: '#aaaaaa'
|
||||
});
|
||||
this.farmStatsContainer.add(this.farmDaysText);
|
||||
@@ -2252,10 +2334,10 @@ class UIScene extends Phaser.Scene {
|
||||
updateFarmStats(stats) {
|
||||
if (!stats || !this.farmStatsContainer) return;
|
||||
|
||||
if (this.farmCropsPlantedText) this.farmCropsPlantedText.setText(`Crops Planted: ${stats.cropsPlanted || 0}`);
|
||||
if (this.farmCropsHarvestedText) this.farmCropsHarvestedText.setText(`Total Harvested: ${stats.totalHarvested || 0}`);
|
||||
if (this.farmGoldEarnedText) this.farmGoldEarnedText.setText(`Gold Earned: ${stats.goldEarned || 0}g`);
|
||||
if (this.farmDaysText) this.farmDaysText.setText(`Days Farmed: ${stats.daysFarmed || 0}`);
|
||||
if (this.farmCropsPlantedText) this.farmCropsPlantedText.setText(`Planted: ${stats.cropsPlanted || 0}`);
|
||||
if (this.farmCropsHarvestedText) this.farmCropsHarvestedText.setText(`Harvested: ${stats.totalHarvested || 0}`);
|
||||
if (this.farmGoldEarnedText) this.farmGoldEarnedText.setText(`Gold: ${stats.goldEarned || 0}g`);
|
||||
if (this.farmDaysText) this.farmDaysText.setText(`Days: ${stats.daysFarmed || 0}`);
|
||||
}
|
||||
|
||||
createMinimap() {
|
||||
@@ -2365,5 +2447,108 @@ class UIScene extends Phaser.Scene {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Q/E Tool Swap Methods
|
||||
swapToolPrevious() {
|
||||
if (!this.gameScene || !this.gameScene.inventorySystem) return;
|
||||
const inv = this.gameScene.inventorySystem;
|
||||
|
||||
// Cycle backwards
|
||||
let newSlot = inv.selectedSlot - 1;
|
||||
if (newSlot < 0) newSlot = 8;
|
||||
|
||||
this.selectSlot(newSlot);
|
||||
|
||||
// Sound effect
|
||||
if (this.gameScene.soundManager) {
|
||||
this.gameScene.soundManager.beepUIClick();
|
||||
}
|
||||
|
||||
console.log(`🔄 Tool swap: Slot ${newSlot}`);
|
||||
}
|
||||
|
||||
swapToolNext() {
|
||||
if (!this.gameScene || !this.gameScene.inventorySystem) return;
|
||||
const inv = this.gameScene.inventorySystem;
|
||||
|
||||
// Cycle forwards
|
||||
let newSlot = inv.selectedSlot + 1;
|
||||
if (newSlot > 8) newSlot = 0;
|
||||
|
||||
this.selectSlot(newSlot);
|
||||
|
||||
// Sound effect
|
||||
if (this.gameScene.soundManager) {
|
||||
this.gameScene.soundManager.beepUIClick();
|
||||
}
|
||||
|
||||
console.log(`🔄 Tool swap: Slot ${newSlot}`);
|
||||
}
|
||||
|
||||
// Equipment Preview
|
||||
createEquipmentPreview() {
|
||||
const previewX = 20;
|
||||
const previewY = 150;
|
||||
|
||||
// Background
|
||||
this.equipmentBg = this.add.graphics();
|
||||
this.equipmentBg.fillStyle(0x000000, 0.6);
|
||||
this.equipmentBg.fillRoundedRect(previewX, previewY, 80, 80, 8);
|
||||
this.equipmentBg.setScrollFactor(0);
|
||||
this.equipmentBg.setDepth(1000);
|
||||
|
||||
// Label
|
||||
this.equipmentLabel = this.add.text(
|
||||
previewX + 40,
|
||||
previewY - 5,
|
||||
'EQUIPPED',
|
||||
{
|
||||
font: 'bold 10px Arial',
|
||||
fill: '#ffff00'
|
||||
}
|
||||
);
|
||||
this.equipmentLabel.setOrigin(0.5, 1);
|
||||
this.equipmentLabel.setScrollFactor(0);
|
||||
this.equipmentLabel.setDepth(1001);
|
||||
|
||||
// Icon sprite placeholder
|
||||
this.equipmentIcon = this.add.rectangle(previewX + 40, previewY + 40, 32, 32, 0x888888);
|
||||
this.equipmentIcon.setScrollFactor(0);
|
||||
this.equipmentIcon.setDepth(1001);
|
||||
|
||||
// Tool name
|
||||
this.equipmentName = this.add.text(
|
||||
previewX + 40,
|
||||
previewY + 75,
|
||||
'None',
|
||||
{
|
||||
font: 'bold 12px Arial',
|
||||
fill: '#ffffff'
|
||||
}
|
||||
);
|
||||
this.equipmentName.setOrigin(0.5, 0);
|
||||
this.equipmentName.setScrollFactor(0);
|
||||
this.equipmentName.setDepth(1001);
|
||||
|
||||
console.log('🎮 Equipment preview created!');
|
||||
}
|
||||
|
||||
updateEquipmentPreview() {
|
||||
if (!this.gameScene || !this.gameScene.inventorySystem) return;
|
||||
if (!this.equipmentName) return;
|
||||
|
||||
const inv = this.gameScene.inventorySystem;
|
||||
const selectedItem = inv.items[inv.selectedSlot];
|
||||
|
||||
if (selectedItem) {
|
||||
// Update name
|
||||
this.equipmentName.setText(selectedItem.name || selectedItem.id);
|
||||
this.equipmentIcon.setVisible(true);
|
||||
this.equipmentName.setVisible(true);
|
||||
} else {
|
||||
// Hide if no item
|
||||
this.equipmentName.setText('None');
|
||||
this.equipmentIcon.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
345
src/systems/AccessibilitySystem.js
Normal file
345
src/systems/AccessibilitySystem.js
Normal file
@@ -0,0 +1,345 @@
|
||||
class AccessibilitySystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
|
||||
// Settings
|
||||
this.settings = {
|
||||
highContrast: 'none', // 'none', 'bw', 'yellow_black'
|
||||
largeUI: 1.0, // 1.0, 1.5, 2.0
|
||||
boldOutlines: false,
|
||||
colorBlindMode: 'none', // 'none', 'protanopia', 'deuteranopia', 'tritanopia', 'achromatopsia'
|
||||
photosensitivity: false,
|
||||
motionSickness: false,
|
||||
brightnessLimit: 1.0 // 0.5 - 1.0
|
||||
};
|
||||
|
||||
// Flash limiter
|
||||
this.flashCount = 0;
|
||||
this.flashWindow = 1000; // 1 second
|
||||
this.maxFlashes = 3;
|
||||
this.lastFlashTime = 0;
|
||||
|
||||
// Load saved settings
|
||||
this.loadSettings();
|
||||
|
||||
// Apply settings
|
||||
this.applySettings();
|
||||
|
||||
console.log('♿ AccessibilitySystem: Initialized');
|
||||
}
|
||||
|
||||
// HIGH CONTRAST MODES
|
||||
enableBlackWhite() {
|
||||
// Apply grayscale filter
|
||||
this.scene.cameras.main.setPostPipeline('GrayscalePipeline');
|
||||
console.log('🎨 Black & White mode enabled');
|
||||
}
|
||||
|
||||
disableBlackWhite() {
|
||||
this.scene.cameras.main.clearPostPipeline();
|
||||
}
|
||||
|
||||
enableYellowOnBlack() {
|
||||
// Simple color replacement - make everything yellow/white on black
|
||||
const cam = this.scene.cameras.main;
|
||||
cam.setBackgroundColor('#000000');
|
||||
|
||||
// Apply tint to all sprites (simplified version)
|
||||
this.scene.children.list.forEach(child => {
|
||||
if (child.setTint) {
|
||||
child.setTint(0xffff00); // Yellow tint
|
||||
}
|
||||
});
|
||||
|
||||
console.log('🎨 Yellow on Black mode enabled');
|
||||
}
|
||||
|
||||
disableYellowOnBlack() {
|
||||
this.scene.cameras.main.setBackgroundColor('#87CEEB'); // Sky blue
|
||||
|
||||
this.scene.children.list.forEach(child => {
|
||||
if (child.clearTint) {
|
||||
child.clearTint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// LARGE UI
|
||||
setUIScale(scale) {
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
// Scale UI elements (simplified - would need more work for production)
|
||||
uiScene.scale.setZoom(scale);
|
||||
|
||||
console.log(`🔍 UI Scale: ${scale * 100}%`);
|
||||
}
|
||||
|
||||
// BOLD OUTLINES
|
||||
enableBoldOutlines() {
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
// Increase stroke on all text elements
|
||||
uiScene.children.list.forEach(child => {
|
||||
if (child.type === 'Text') {
|
||||
child.setStroke('#000000', 6);
|
||||
child.setShadow(2, 2, '#000000', 2, true, true);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✏️ Bold outlines enabled');
|
||||
}
|
||||
|
||||
disableBoldOutlines() {
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
uiScene.children.list.forEach(child => {
|
||||
if (child.type === 'Text') {
|
||||
child.setStroke('#000000', 3); // Normal stroke
|
||||
child.setShadow(0, 0, '#000000', 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// COLOR BLIND MODES (Simplified - full implementation would use shaders)
|
||||
setColorBlindMode(mode) {
|
||||
this.settings.colorBlindMode = mode;
|
||||
|
||||
switch (mode) {
|
||||
case 'protanopia':
|
||||
// Red-blind: Replace reds with browns/grays
|
||||
this.applyColorFilter(0xaa8866);
|
||||
break;
|
||||
case 'deuteranopia':
|
||||
// Green-blind: Replace greens with yellows/browns
|
||||
this.applyColorFilter(0xccaa66);
|
||||
break;
|
||||
case 'tritanopia':
|
||||
// Blue-blind: Replace blues with greens/grays
|
||||
this.applyColorFilter(0x88aa88);
|
||||
break;
|
||||
case 'achromatopsia':
|
||||
// Total color blind: Grayscale
|
||||
this.enableBlackWhite();
|
||||
break;
|
||||
default:
|
||||
this.clearColorFilter();
|
||||
}
|
||||
|
||||
console.log(`👁️ Color Blind Mode: ${mode}`);
|
||||
}
|
||||
|
||||
applyColorFilter(tint) {
|
||||
// Simplified color filter
|
||||
this.scene.children.list.forEach(child => {
|
||||
if (child.setTint) {
|
||||
child.setTint(tint);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clearColorFilter() {
|
||||
this.scene.children.list.forEach(child => {
|
||||
if (child.clearTint) {
|
||||
child.clearTint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// PHOTOSENSITIVITY PROTECTION
|
||||
canFlash() {
|
||||
if (!this.settings.photosensitivity) return true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
disableLightning() {
|
||||
if (this.scene.weatherSystem) {
|
||||
this.scene.weatherSystem.lightningEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
reduceParticles(level = 0.5) {
|
||||
this.scene.children.list.forEach(child => {
|
||||
if (child.type === 'ParticleEmitter') {
|
||||
const originalFreq = child.frequency || 100;
|
||||
child.setFrequency(originalFreq / level);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
enableBrightnessLimiter(maxBrightness = 0.8) {
|
||||
if (this.brightnessOverlay) {
|
||||
this.brightnessOverlay.destroy();
|
||||
}
|
||||
|
||||
const overlay = this.scene.add.graphics();
|
||||
overlay.fillStyle(0x000000, 1 - maxBrightness);
|
||||
overlay.fillRect(0, 0, this.scene.cameras.main.width * 2, this.scene.cameras.main.height * 2);
|
||||
overlay.setScrollFactor(0);
|
||||
overlay.setDepth(9999);
|
||||
|
||||
this.brightnessOverlay = overlay;
|
||||
|
||||
console.log(`💡 Brightness limited to ${maxBrightness * 100}%`);
|
||||
}
|
||||
|
||||
disableBrightnessLimiter() {
|
||||
if (this.brightnessOverlay) {
|
||||
this.brightnessOverlay.destroy();
|
||||
this.brightnessOverlay = null;
|
||||
}
|
||||
}
|
||||
|
||||
// MOTION SICKNESS MODE
|
||||
enableMotionSicknessMode() {
|
||||
// Disable camera shake
|
||||
const originalShake = this.scene.cameras.main.shake;
|
||||
this.scene.cameras.main.shake = () => {
|
||||
console.log('🚫 Camera shake disabled (motion sickness mode)');
|
||||
};
|
||||
|
||||
// Disable parallax
|
||||
if (this.scene.parallaxSystem) {
|
||||
this.scene.parallaxSystem.enabled = false;
|
||||
}
|
||||
|
||||
console.log('🤢 Motion sickness mode enabled');
|
||||
}
|
||||
|
||||
disableMotionSicknessMode() {
|
||||
// Re-enable effects
|
||||
if (this.scene.parallaxSystem) {
|
||||
this.scene.parallaxSystem.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// SETTINGS MANAGEMENT
|
||||
applySettings() {
|
||||
// High Contrast
|
||||
if (this.settings.highContrast === 'bw') {
|
||||
this.enableBlackWhite();
|
||||
} else if (this.settings.highContrast === 'yellow_black') {
|
||||
this.enableYellowOnBlack();
|
||||
}
|
||||
|
||||
// Large UI
|
||||
if (this.settings.largeUI > 1.0) {
|
||||
this.setUIScale(this.settings.largeUI);
|
||||
}
|
||||
|
||||
// Bold Outlines
|
||||
if (this.settings.boldOutlines) {
|
||||
this.enableBoldOutlines();
|
||||
}
|
||||
|
||||
// Color Blind Mode
|
||||
if (this.settings.colorBlindMode !== 'none') {
|
||||
this.setColorBlindMode(this.settings.colorBlindMode);
|
||||
}
|
||||
|
||||
// Photosensitivity
|
||||
if (this.settings.photosensitivity) {
|
||||
this.disableLightning();
|
||||
this.reduceParticles(0.3);
|
||||
this.enableBrightnessLimiter(this.settings.brightnessLimit);
|
||||
}
|
||||
|
||||
// Motion Sickness
|
||||
if (this.settings.motionSickness) {
|
||||
this.enableMotionSicknessMode();
|
||||
}
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
localStorage.setItem('novafarma_accessibility', JSON.stringify(this.settings));
|
||||
console.log('💾 Accessibility settings saved');
|
||||
}
|
||||
|
||||
loadSettings() {
|
||||
const saved = localStorage.getItem('novafarma_accessibility');
|
||||
if (saved) {
|
||||
this.settings = JSON.parse(saved);
|
||||
console.log('📂 Accessibility settings loaded');
|
||||
}
|
||||
}
|
||||
|
||||
// EPILEPSY WARNING
|
||||
showEpilepsyWarning(onContinue) {
|
||||
const warning = this.scene.add.container(
|
||||
this.scene.cameras.main.centerX,
|
||||
this.scene.cameras.main.centerY
|
||||
);
|
||||
warning.setDepth(10000);
|
||||
warning.setScrollFactor(0);
|
||||
|
||||
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\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, 100, '[ ENABLE PROTECTION ]', {
|
||||
fontSize: '20px',
|
||||
color: '#00ff00',
|
||||
backgroundColor: '#003300',
|
||||
padding: { x: 20, y: 10 }
|
||||
}).setOrigin(0.5);
|
||||
|
||||
enableBtn.setInteractive({ useHandCursor: true });
|
||||
enableBtn.on('pointerdown', () => {
|
||||
this.settings.photosensitivity = true;
|
||||
this.applySettings();
|
||||
this.saveSettings();
|
||||
warning.destroy();
|
||||
if (onContinue) onContinue();
|
||||
});
|
||||
|
||||
const continueBtn = this.scene.add.text(0, 150, '[ CONTINUE WITHOUT ]', {
|
||||
fontSize: '16px',
|
||||
color: '#888888'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
continueBtn.setInteractive({ useHandCursor: true });
|
||||
continueBtn.on('pointerdown', () => {
|
||||
warning.destroy();
|
||||
if (onContinue) onContinue();
|
||||
});
|
||||
|
||||
warning.add([bg, title, text, enableBtn, continueBtn]);
|
||||
|
||||
return warning;
|
||||
}
|
||||
}
|
||||
320
src/systems/CameraSystem.js
Normal file
320
src/systems/CameraSystem.js
Normal file
@@ -0,0 +1,320 @@
|
||||
class CameraSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.camera = scene.cameras.main;
|
||||
|
||||
// Camera modes
|
||||
this.mode = 'follow'; // 'follow', 'free', 'cinematic', 'screenshot'
|
||||
|
||||
// Free camera controls
|
||||
this.freeCamSpeed = 5;
|
||||
this.freeCamZoom = 1.0;
|
||||
|
||||
// Cinematic mode
|
||||
this.cinematicPath = [];
|
||||
this.cinematicIndex = 0;
|
||||
this.cinematicPlaying = false;
|
||||
|
||||
// Screenshot mode
|
||||
this.screenshotMode = false;
|
||||
this.uiHidden = false;
|
||||
|
||||
// Saved camera positions
|
||||
this.savedPositions = [];
|
||||
|
||||
this.setupControls();
|
||||
|
||||
console.log('📷 CameraSystem: Initialized');
|
||||
}
|
||||
|
||||
setupControls() {
|
||||
// F6 - Free Camera Mode
|
||||
this.scene.input.keyboard.on('keydown-F6', () => {
|
||||
this.toggleFreeCamera();
|
||||
});
|
||||
|
||||
// F7 - Screenshot Mode (hide UI)
|
||||
this.scene.input.keyboard.on('keydown-F7', () => {
|
||||
this.toggleScreenshotMode();
|
||||
});
|
||||
|
||||
// F8 - Save Camera Position
|
||||
this.scene.input.keyboard.on('keydown-F8', () => {
|
||||
this.saveCameraPosition();
|
||||
});
|
||||
|
||||
// F9 - Cinematic Mode
|
||||
this.scene.input.keyboard.on('keydown-F10', () => {
|
||||
this.toggleCinematicMode();
|
||||
});
|
||||
|
||||
// Arrow keys for free camera
|
||||
this.cursors = this.scene.input.keyboard.createCursorKeys();
|
||||
|
||||
// Page Up/Down for zoom
|
||||
this.scene.input.keyboard.on('keydown-PAGEUP', () => {
|
||||
this.adjustZoom(0.1);
|
||||
});
|
||||
|
||||
this.scene.input.keyboard.on('keydown-PAGEDOWN', () => {
|
||||
this.adjustZoom(-0.1);
|
||||
});
|
||||
}
|
||||
|
||||
// FREE CAMERA MODE
|
||||
toggleFreeCamera() {
|
||||
if (this.mode === 'free') {
|
||||
this.mode = 'follow';
|
||||
this.camera.startFollow(this.scene.player.sprite);
|
||||
console.log('📷 Camera: Follow mode');
|
||||
} else {
|
||||
this.mode = 'free';
|
||||
this.camera.stopFollow();
|
||||
console.log('📷 Camera: Free mode (Arrow keys to move, PgUp/PgDn to zoom)');
|
||||
}
|
||||
}
|
||||
|
||||
updateFreeCamera(delta) {
|
||||
if (this.mode !== 'free') return;
|
||||
|
||||
const speed = this.freeCamSpeed * (delta / 16);
|
||||
|
||||
if (this.cursors.left.isDown) {
|
||||
this.camera.scrollX -= speed;
|
||||
}
|
||||
if (this.cursors.right.isDown) {
|
||||
this.camera.scrollX += speed;
|
||||
}
|
||||
if (this.cursors.up.isDown) {
|
||||
this.camera.scrollY -= speed;
|
||||
}
|
||||
if (this.cursors.down.isDown) {
|
||||
this.camera.scrollY += speed;
|
||||
}
|
||||
}
|
||||
|
||||
adjustZoom(delta) {
|
||||
this.freeCamZoom = Phaser.Math.Clamp(this.freeCamZoom + delta, 0.3, 3.0);
|
||||
this.camera.setZoom(this.freeCamZoom);
|
||||
console.log(`📷 Zoom: ${this.freeCamZoom.toFixed(2)}x`);
|
||||
}
|
||||
|
||||
// SCREENSHOT MODE
|
||||
toggleScreenshotMode() {
|
||||
this.screenshotMode = !this.screenshotMode;
|
||||
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
if (this.screenshotMode) {
|
||||
// Hide all UI
|
||||
uiScene.children.list.forEach(child => {
|
||||
if (child.setVisible) {
|
||||
child.wasVisible = child.visible;
|
||||
child.setVisible(false);
|
||||
}
|
||||
});
|
||||
this.uiHidden = true;
|
||||
console.log('📷 Screenshot mode: UI hidden (F7 to restore)');
|
||||
} else {
|
||||
// Restore UI
|
||||
uiScene.children.list.forEach(child => {
|
||||
if (child.setVisible && child.wasVisible !== undefined) {
|
||||
child.setVisible(child.wasVisible);
|
||||
}
|
||||
});
|
||||
this.uiHidden = false;
|
||||
console.log('📷 Screenshot mode: UI restored');
|
||||
}
|
||||
}
|
||||
|
||||
// SAVE CAMERA POSITION
|
||||
saveCameraPosition() {
|
||||
const pos = {
|
||||
x: this.camera.scrollX,
|
||||
y: this.camera.scrollY,
|
||||
zoom: this.camera.zoom
|
||||
};
|
||||
|
||||
this.savedPositions.push(pos);
|
||||
console.log(`📷 Camera position saved (${this.savedPositions.length}): x=${pos.x.toFixed(0)}, y=${pos.y.toFixed(0)}, zoom=${pos.zoom.toFixed(2)}`);
|
||||
}
|
||||
|
||||
loadCameraPosition(index) {
|
||||
if (index < 0 || index >= this.savedPositions.length) return;
|
||||
|
||||
const pos = this.savedPositions[index];
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: this.camera,
|
||||
scrollX: pos.x,
|
||||
scrollY: pos.y,
|
||||
zoom: pos.zoom,
|
||||
duration: 1000,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
|
||||
console.log(`📷 Camera position loaded (${index + 1})`);
|
||||
}
|
||||
|
||||
// CINEMATIC MODE
|
||||
toggleCinematicMode() {
|
||||
if (this.cinematicPlaying) {
|
||||
this.stopCinematic();
|
||||
} else {
|
||||
this.startCinematic();
|
||||
}
|
||||
}
|
||||
|
||||
startCinematic() {
|
||||
if (this.savedPositions.length < 2) {
|
||||
console.log('📷 Need at least 2 saved positions for cinematic!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.cinematicPlaying = true;
|
||||
this.cinematicIndex = 0;
|
||||
this.mode = 'cinematic';
|
||||
|
||||
this.playCinematicStep();
|
||||
|
||||
console.log('📷 Cinematic mode: Playing...');
|
||||
}
|
||||
|
||||
playCinematicStep() {
|
||||
if (!this.cinematicPlaying) return;
|
||||
if (this.cinematicIndex >= this.savedPositions.length) {
|
||||
this.stopCinematic();
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = this.savedPositions[this.cinematicIndex];
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: this.camera,
|
||||
scrollX: pos.x,
|
||||
scrollY: pos.y,
|
||||
zoom: pos.zoom,
|
||||
duration: 3000,
|
||||
ease: 'Sine.easeInOut',
|
||||
onComplete: () => {
|
||||
this.cinematicIndex++;
|
||||
this.scene.time.delayedCall(500, () => {
|
||||
this.playCinematicStep();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stopCinematic() {
|
||||
this.cinematicPlaying = false;
|
||||
this.mode = 'follow';
|
||||
this.camera.startFollow(this.scene.player.sprite);
|
||||
console.log('📷 Cinematic mode: Stopped');
|
||||
}
|
||||
|
||||
// PRESET CAMERA ANGLES
|
||||
setPresetAngle(preset) {
|
||||
const presets = {
|
||||
'overview': { zoom: 0.5, y: -200 },
|
||||
'closeup': { zoom: 1.5, y: 0 },
|
||||
'wide': { zoom: 0.3, y: -300 },
|
||||
'action': { zoom: 1.2, y: -50 }
|
||||
};
|
||||
|
||||
const p = presets[preset];
|
||||
if (!p) return;
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: this.camera,
|
||||
zoom: p.zoom,
|
||||
scrollY: this.camera.scrollY + p.y,
|
||||
duration: 1000,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
|
||||
console.log(`📷 Preset angle: ${preset}`);
|
||||
}
|
||||
|
||||
// SHAKE EFFECTS
|
||||
shake(intensity = 0.005, duration = 200) {
|
||||
this.camera.shake(duration, intensity);
|
||||
}
|
||||
|
||||
// FLASH EFFECTS
|
||||
flash(color = 0xffffff, duration = 200) {
|
||||
this.camera.flash(duration,
|
||||
(color >> 16) & 0xff,
|
||||
(color >> 8) & 0xff,
|
||||
color & 0xff
|
||||
);
|
||||
}
|
||||
|
||||
// FADE EFFECTS
|
||||
fadeOut(duration = 1000, callback) {
|
||||
this.camera.fadeOut(duration, 0, 0, 0);
|
||||
if (callback) {
|
||||
this.camera.once('camerafadeoutcomplete', callback);
|
||||
}
|
||||
}
|
||||
|
||||
fadeIn(duration = 1000, callback) {
|
||||
this.camera.fadeIn(duration, 0, 0, 0);
|
||||
if (callback) {
|
||||
this.camera.once('camerafadeincomplete', callback);
|
||||
}
|
||||
}
|
||||
|
||||
// PAN TO LOCATION
|
||||
panTo(x, y, duration = 2000) {
|
||||
this.scene.tweens.add({
|
||||
targets: this.camera,
|
||||
scrollX: x - this.camera.width / 2,
|
||||
scrollY: y - this.camera.height / 2,
|
||||
duration: duration,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
}
|
||||
|
||||
// ZOOM TO
|
||||
zoomTo(zoom, duration = 1000) {
|
||||
this.scene.tweens.add({
|
||||
targets: this.camera,
|
||||
zoom: zoom,
|
||||
duration: duration,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
update(delta) {
|
||||
this.updateFreeCamera(delta);
|
||||
}
|
||||
|
||||
// EXPORT CAMERA DATA (for trailer editing)
|
||||
exportCameraData() {
|
||||
const data = {
|
||||
savedPositions: this.savedPositions,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
const json = JSON.stringify(data, null, 2);
|
||||
console.log('📷 Camera data:', json);
|
||||
|
||||
// Copy to clipboard (if available)
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(json);
|
||||
console.log('📷 Camera data copied to clipboard!');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// IMPORT CAMERA DATA
|
||||
importCameraData(data) {
|
||||
if (data.savedPositions) {
|
||||
this.savedPositions = data.savedPositions;
|
||||
console.log(`📷 Imported ${this.savedPositions.length} camera positions`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,11 @@ class LocalizationSystem {
|
||||
'ui.oxygen': 'Kisik',
|
||||
'ui.day': 'Dan',
|
||||
'ui.season': 'Letni čas',
|
||||
'ui.hp': 'ZDR',
|
||||
'ui.hun': 'LAK',
|
||||
'ui.h2o': 'H2O',
|
||||
'ui.xp': 'IZK',
|
||||
'ui.lv': 'NIV',
|
||||
|
||||
// Items
|
||||
'item.wood': 'Les',
|
||||
@@ -49,6 +54,14 @@ class LocalizationSystem {
|
||||
'season.autumn': 'Jesen',
|
||||
'season.winter': 'Zima',
|
||||
|
||||
// Pause Menu
|
||||
'pause.title': 'PAVZA',
|
||||
'pause.resume': '▶ Nadaljuj',
|
||||
'pause.save': '💾 Shrani igro',
|
||||
'pause.settings': '⚙️ Nastavitve',
|
||||
'pause.quit': '🚪 Izhod v meni',
|
||||
'pause.hint': 'Pritisni ESC za nadaljevanje',
|
||||
|
||||
// Messages
|
||||
'msg.demo_end': 'Demo končan! Hvala za igranje.',
|
||||
'msg.freezing': '❄️ Zmrzuješ!',
|
||||
@@ -63,6 +76,11 @@ class LocalizationSystem {
|
||||
'ui.oxygen': 'Oxygen',
|
||||
'ui.day': 'Day',
|
||||
'ui.season': 'Season',
|
||||
'ui.hp': 'HP',
|
||||
'ui.hun': 'HUN',
|
||||
'ui.h2o': 'H2O',
|
||||
'ui.xp': 'XP',
|
||||
'ui.lv': 'LV',
|
||||
|
||||
// Items
|
||||
'item.wood': 'Wood',
|
||||
@@ -83,35 +101,159 @@ class LocalizationSystem {
|
||||
'season.autumn': 'Autumn',
|
||||
'season.winter': 'Winter',
|
||||
|
||||
// Pause Menu
|
||||
'pause.title': 'PAUSED',
|
||||
'pause.resume': '▶ Resume',
|
||||
'pause.save': '💾 Save Game',
|
||||
'pause.settings': '⚙️ Settings',
|
||||
'pause.quit': '🚪 Quit to Menu',
|
||||
'pause.hint': 'Press ESC to resume',
|
||||
|
||||
// Messages
|
||||
'msg.demo_end': 'Demo Ended! Thanks for playing.',
|
||||
'msg.freezing': '❄️ Freezing!',
|
||||
'msg.overheating': '🔥 Overheating!'
|
||||
},
|
||||
'de': {
|
||||
// UI
|
||||
'ui.inventory': 'Inventar',
|
||||
'ui.crafting': 'Handwerk',
|
||||
'ui.health': 'Gesundheit',
|
||||
'ui.hunger': 'Hunger',
|
||||
'ui.oxygen': 'Sauerstoff',
|
||||
'ui.day': 'Tag',
|
||||
'ui.season': 'Jahreszeit',
|
||||
'ui.hp': 'LP',
|
||||
'ui.hun': 'HUN',
|
||||
'ui.h2o': 'H2O',
|
||||
'ui.xp': 'EP',
|
||||
'ui.lv': 'ST',
|
||||
|
||||
// Items
|
||||
'item.wood': 'Holz',
|
||||
'item.stone': 'Stein',
|
||||
'item.seeds': 'Samen',
|
||||
'item.wheat': 'Weizen',
|
||||
'item.corn': 'Mais',
|
||||
|
||||
// Actions
|
||||
'action.plant': 'Pflanzen',
|
||||
'action.harvest': 'Ernten',
|
||||
'action.craft': 'Herstellen',
|
||||
'action.build': 'Bauen',
|
||||
|
||||
// Seasons
|
||||
'season.spring': 'Frühling',
|
||||
'season.summer': 'Sommer',
|
||||
'season.autumn': 'Herbst',
|
||||
'season.winter': 'Winter'
|
||||
'season.winter': 'Winter',
|
||||
|
||||
// Pause Menu
|
||||
'pause.title': 'PAUSIERT',
|
||||
'pause.resume': '▶ Fortsetzen',
|
||||
'pause.save': '💾 Spiel speichern',
|
||||
'pause.settings': '⚙️ Einstellungen',
|
||||
'pause.quit': '🚪 Zum Menü',
|
||||
'pause.hint': 'ESC drücken zum Fortsetzen',
|
||||
|
||||
// Messages
|
||||
'msg.demo_end': 'Demo beendet! Danke fürs Spielen.',
|
||||
'msg.freezing': '❄️ Du erfrierst!',
|
||||
'msg.overheating': '🔥 Überhitzung!'
|
||||
},
|
||||
'it': {
|
||||
// UI
|
||||
'ui.inventory': 'Inventario',
|
||||
'ui.crafting': 'Artigianato',
|
||||
'ui.health': 'Salute',
|
||||
'ui.hunger': 'Fame',
|
||||
'ui.oxygen': 'Ossigeno',
|
||||
'ui.day': 'Giorno',
|
||||
'ui.season': 'Stagione',
|
||||
'ui.hp': 'PS',
|
||||
'ui.hun': 'FAM',
|
||||
'ui.h2o': 'H2O',
|
||||
'ui.xp': 'ESP',
|
||||
'ui.lv': 'LIV',
|
||||
|
||||
// Items
|
||||
'item.wood': 'Legno',
|
||||
'item.stone': 'Pietra',
|
||||
'item.seeds': 'Semi',
|
||||
'item.wheat': 'Grano',
|
||||
'item.corn': 'Mais',
|
||||
|
||||
// Actions
|
||||
'action.plant': 'Pianta',
|
||||
'action.harvest': 'Raccogli',
|
||||
'action.craft': 'Crea',
|
||||
'action.build': 'Costruisci',
|
||||
|
||||
// Seasons
|
||||
'season.spring': 'Primavera',
|
||||
'season.summer': 'Estate',
|
||||
'season.autumn': 'Autunno',
|
||||
'season.winter': 'Inverno'
|
||||
'season.winter': 'Inverno',
|
||||
|
||||
// Pause Menu
|
||||
'pause.title': 'IN PAUSA',
|
||||
'pause.resume': '▶ Riprendi',
|
||||
'pause.save': '💾 Salva gioco',
|
||||
'pause.settings': '⚙️ Impostazioni',
|
||||
'pause.quit': '🚪 Esci al menu',
|
||||
'pause.hint': 'Premi ESC per riprendere',
|
||||
|
||||
// Messages
|
||||
'msg.demo_end': 'Demo terminata! Grazie per aver giocato.',
|
||||
'msg.freezing': '❄️ Stai congelando!',
|
||||
'msg.overheating': '🔥 Surriscaldamento!'
|
||||
},
|
||||
'cn': {
|
||||
// UI
|
||||
'ui.inventory': '库存',
|
||||
'ui.crafting': '制作',
|
||||
'ui.health': '健康',
|
||||
'ui.hunger': '饥饿',
|
||||
'ui.oxygen': '氧气',
|
||||
'ui.day': '天数',
|
||||
'ui.season': '季节',
|
||||
'ui.hp': '生命',
|
||||
'ui.hun': '饥饿',
|
||||
'ui.h2o': '水',
|
||||
'ui.xp': '经验',
|
||||
'ui.lv': '等级',
|
||||
|
||||
// Items
|
||||
'item.wood': '木材',
|
||||
'item.stone': '石头',
|
||||
'item.seeds': '种子',
|
||||
'item.wheat': '小麦',
|
||||
'item.corn': '玉米',
|
||||
|
||||
// Actions
|
||||
'action.plant': '种植',
|
||||
'action.harvest': '收获',
|
||||
'action.craft': '制作',
|
||||
'action.build': '建造',
|
||||
|
||||
// Seasons
|
||||
'season.spring': '春天',
|
||||
'season.summer': '夏天',
|
||||
'season.autumn': '秋天',
|
||||
'season.winter': '冬天'
|
||||
'season.winter': '冬天',
|
||||
|
||||
// Pause Menu
|
||||
'pause.title': '暂停',
|
||||
'pause.resume': '▶ 继续',
|
||||
'pause.save': '💾 保存游戏',
|
||||
'pause.settings': '⚙️ 设置',
|
||||
'pause.quit': '🚪 退出到菜单',
|
||||
'pause.hint': '按ESC继续',
|
||||
|
||||
// Messages
|
||||
'msg.demo_end': '演示结束!感谢游玩。',
|
||||
'msg.freezing': '❄️ 你在冻僵!',
|
||||
'msg.overheating': '🔥 过热!'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
154
src/systems/StaminaSystem.js
Normal file
154
src/systems/StaminaSystem.js
Normal file
@@ -0,0 +1,154 @@
|
||||
class StaminaSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
|
||||
// Stamina properties
|
||||
this.maxStamina = 100;
|
||||
this.currentStamina = 100;
|
||||
this.regenRate = 5; // Stamina per second
|
||||
this.regenDelay = 2000; // Wait 2s after use before regen
|
||||
this.lastUseTime = 0;
|
||||
|
||||
// Stamina costs
|
||||
this.costs = {
|
||||
'till': 5,
|
||||
'plant': 3,
|
||||
'harvest': 4,
|
||||
'build': 10,
|
||||
'attack': 8,
|
||||
'sprint': 2 // per second
|
||||
};
|
||||
|
||||
console.log('⚡ StaminaSystem: Initialized');
|
||||
}
|
||||
|
||||
// Use stamina for action
|
||||
useStamina(action) {
|
||||
const cost = this.costs[action] || 0;
|
||||
|
||||
if (this.currentStamina < cost) {
|
||||
console.log(`⚡ Not enough stamina! (${this.currentStamina}/${cost})`);
|
||||
return false; // Not enough stamina
|
||||
}
|
||||
|
||||
this.currentStamina -= cost;
|
||||
this.lastUseTime = Date.now();
|
||||
|
||||
// Update UI
|
||||
this.updateUI();
|
||||
|
||||
console.log(`⚡ Used ${cost} stamina (${action}). Remaining: ${this.currentStamina}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Restore stamina (from food)
|
||||
restoreStamina(amount) {
|
||||
this.currentStamina = Math.min(this.maxStamina, this.currentStamina + amount);
|
||||
this.updateUI();
|
||||
|
||||
console.log(`⚡ Restored ${amount} stamina. Current: ${this.currentStamina}`);
|
||||
}
|
||||
|
||||
// Auto-regenerate stamina
|
||||
update(delta) {
|
||||
// Check if enough time has passed since last use
|
||||
const timeSinceUse = Date.now() - this.lastUseTime;
|
||||
|
||||
if (timeSinceUse > this.regenDelay && this.currentStamina < this.maxStamina) {
|
||||
// Regenerate stamina
|
||||
const regenAmount = (this.regenRate * delta) / 1000;
|
||||
this.currentStamina = Math.min(this.maxStamina, this.currentStamina + regenAmount);
|
||||
|
||||
// Update UI
|
||||
this.updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
// Update stamina bar in UI
|
||||
updateUI() {
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
// Update stamina bar (if exists)
|
||||
if (uiScene.staminaBar) {
|
||||
const percentage = this.currentStamina / this.maxStamina;
|
||||
uiScene.staminaBar.clear();
|
||||
|
||||
// Background
|
||||
uiScene.staminaBar.fillStyle(0x000000, 0.5);
|
||||
uiScene.staminaBar.fillRect(20, 120, 200, 20);
|
||||
|
||||
// Stamina fill (yellow)
|
||||
uiScene.staminaBar.fillStyle(0xffff00, 1.0);
|
||||
uiScene.staminaBar.fillRect(20, 120, 200 * percentage, 20);
|
||||
|
||||
// Border
|
||||
uiScene.staminaBar.lineStyle(2, 0xffffff, 1.0);
|
||||
uiScene.staminaBar.strokeRect(20, 120, 200, 20);
|
||||
}
|
||||
|
||||
// Update stamina text
|
||||
if (uiScene.staminaText) {
|
||||
uiScene.staminaText.setText(`⚡ ${Math.floor(this.currentStamina)}/${this.maxStamina}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Create stamina UI
|
||||
createUI() {
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
// Stamina bar (below health)
|
||||
uiScene.staminaBar = uiScene.add.graphics();
|
||||
uiScene.staminaBar.setScrollFactor(0);
|
||||
uiScene.staminaBar.setDepth(1000);
|
||||
|
||||
// Stamina text
|
||||
uiScene.staminaText = uiScene.add.text(
|
||||
20,
|
||||
105,
|
||||
`⚡ ${this.currentStamina}/${this.maxStamina}`,
|
||||
{
|
||||
fontSize: '14px',
|
||||
color: '#ffff00',
|
||||
fontStyle: 'bold'
|
||||
}
|
||||
);
|
||||
uiScene.staminaText.setScrollFactor(0);
|
||||
uiScene.staminaText.setDepth(1001);
|
||||
|
||||
// Initial update
|
||||
this.updateUI();
|
||||
|
||||
console.log('⚡ Stamina UI created');
|
||||
}
|
||||
|
||||
// Check if player has enough stamina
|
||||
hasStamina(action) {
|
||||
const cost = this.costs[action] || 0;
|
||||
return this.currentStamina >= cost;
|
||||
}
|
||||
|
||||
// Get stamina percentage (0-1)
|
||||
getPercentage() {
|
||||
return this.currentStamina / this.maxStamina;
|
||||
}
|
||||
|
||||
// Save/Load
|
||||
getSaveData() {
|
||||
return {
|
||||
currentStamina: this.currentStamina,
|
||||
maxStamina: this.maxStamina
|
||||
};
|
||||
}
|
||||
|
||||
loadSaveData(data) {
|
||||
if (data.currentStamina !== undefined) {
|
||||
this.currentStamina = data.currentStamina;
|
||||
}
|
||||
if (data.maxStamina !== undefined) {
|
||||
this.maxStamina = data.maxStamina;
|
||||
}
|
||||
this.updateUI();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user