docs: clean start 30.1.2026 - updated master diary and bible, renamed project

This commit is contained in:
2026-01-30 21:03:47 +01:00
parent 01d042fd50
commit 2669a435eb
802 changed files with 15796 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
// Disable Electron Security Warnings
if (typeof process !== 'undefined' && process.env) {
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true';
}
import GrassScene from './scenes/GrassScene_Clean.js';
import UIScene from './scenes/UIScene.js';
const config = {
type: Phaser.AUTO,
width: 1920,
height: 1080,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
},
backgroundColor: '#1a1a1a', // Temno siva, da slika izstopa
parent: 'body',
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: { y: 0 }
}
},
scene: [GrassScene, UIScene]
};
const game = new Phaser.Game(config);

View File

@@ -0,0 +1,191 @@
export default class GrassScene extends Phaser.Scene {
constructor() {
super({ key: 'GrassScene' });
this.baseTime = 12; // Start at 12:00 (Noon)
this.timeSpeed = 0.5; // How fast time passes
}
preload() {
console.log("🌿 Loading Environment assets...");
// 1. DEFINICIJA POTI (ASSETS) - Absolute paths from server root
const ASSETS = {
ground: '/assets/DEMO_FAZA1/tla_trava_tekstura.png',
fog: '/assets/DEMO_FAZA1/megla_ozadje.png',
trees: {
faza1: '/assets/DEMO_FAZA1/drevo_faza_1.png',
faza2: '/assets/DEMO_FAZA1/drevo_faza_2.png',
small: '/assets/DEMO_FAZA1/drevo_majhno.png',
medium: '/assets/DEMO_FAZA1/drevo_srednje.png',
large: '/assets/DEMO_FAZA1/drevo_veliko.png',
dry: '/assets/DEMO_FAZA1/suho_drevo.png'
},
foliage: {
clump: '/assets/DEMO_FAZA1/trava_sop.png',
tall: '/assets/DEMO_FAZA1/visoka_trava.png'
}
};
this.paths = ASSETS; // Store for reference if needed
// Load specific images
this.load.image('ground_dead', ASSETS.ground);
this.load.image('fog', ASSETS.fog);
// Trees
this.load.image('tree_faza1', ASSETS.trees.faza1);
this.load.image('tree_faza2', ASSETS.trees.faza2);
this.load.image('tree_small', ASSETS.trees.small);
this.load.image('tree_medium', ASSETS.trees.medium);
this.load.image('tree_large', ASSETS.trees.large);
this.load.image('tree_dead', ASSETS.trees.dry);
// Foliage
this.load.image('grass_tuft', ASSETS.foliage.clump);
this.load.image('grass_tall', ASSETS.foliage.tall);
// Add error handling for loading
this.load.on('loaderror', (file) => {
console.error('File failed to load:', file.src);
});
}
create() {
const { width, height } = this.scale;
// 2. NALOGA: Implementiraj 'tla_trava_tekstura.png' kot ponavljajočo se podlago
this.ground = this.add.tileSprite(0, 0, width, height, 'ground_dead').setOrigin(0, 0);
// Vegetation Groups
this.trees = [];
this.grasses = [];
// Generate World
this.generateVegetation(width, height);
// 2. NALOGA: Dodaj 'megla_ozadje.png' kot zgornji sloj
this.fog1 = this.add.tileSprite(0, 0, width, height, 'fog')
.setOrigin(0, 0)
.setAlpha(0.3)
.setBlendMode(Phaser.BlendModes.ADD); // Light atmosphere
this.fog2 = this.add.tileSprite(0, 0, width, height, 'fog')
.setOrigin(0, 0)
.setAlpha(0.2)
.setBlendMode(Phaser.BlendModes.ADD);
// Day/Night Overlay
this.dayNightOverlay = this.add.rectangle(0, 0, width, height, 0x000000, 0).setOrigin(0, 0).setDepth(1000);
// UI Info
this.infoText = this.add.text(20, 20, 'Time: 12:00', {
fontFamily: 'monospace',
fontSize: '18px',
fill: '#ffffff',
stroke: '#000000',
strokeThickness: 3
}).setDepth(2000);
}
// 2. NALOGA: Pripravi funkcijo 'spawnTree(x, y, type)'
spawnTree(x, y, type) {
// Map simplified names to texture keys if needed, or use direct keys
// Types: 'tree_faza1', 'tree_faza2', 'tree_small', 'tree_medium', 'tree_large', 'tree_dead'
let tree = this.add.image(x, y, type);
// Custom scale logic based on type could go here, for now standardized or specific per tree
if (type === 'tree_large') tree.setScale(0.8);
else if (type === 'tree_medium') tree.setScale(0.6);
else if (type === 'tree_small') tree.setScale(0.5);
else tree.setScale(0.7); // Default for others
tree.setDepth(y); // Y-Sort
tree.setOrigin(0.5, 0.95); // Pivot at bottom center
// Sway properties for update loop
tree.swaySpeed = 0.001 + (Math.random() * 0.001);
tree.swayOffset = Math.random() * 100;
this.trees.push(tree);
return tree;
}
generateVegetation(w, h) {
// Place Gras (Foliage)
for (let i = 0; i < 50; i++) {
let x = Phaser.Math.Between(50, w - 50);
let y = Phaser.Math.Between(50, h - 50);
let grass = this.add.image(x, y, 'grass_tall');
grass.setScale(0.15 + (Math.random() * 0.1));
grass.setDepth(y);
grass.setOrigin(0.5, 1);
grass.swaySpeed = 0.002 + (Math.random() * 0.002);
grass.swayOffset = Math.random() * 100;
this.grasses.push(grass);
}
// Place Trees using spawnTree
const treeTypes = ['tree_faza1', 'tree_faza2', 'tree_small', 'tree_medium', 'tree_large', 'tree_dead'];
for (let i = 0; i < 20; i++) {
let x = Phaser.Math.Between(50, w - 50);
let y = Phaser.Math.Between(50, h - 50);
let type = Phaser.Math.RND.pick(treeTypes);
this.spawnTree(x, y, type);
}
}
update(time, delta) {
// Wind Animation
this.grasses.forEach(g => {
g.rotation = Math.sin((time * g.swaySpeed) + g.swayOffset) * 0.1;
});
this.trees.forEach(t => {
t.rotation = Math.sin((time * t.swaySpeed) + t.swayOffset) * 0.03;
});
// Fog Movement
this.fog1.tilePositionX += 0.5;
this.fog2.tilePositionX += 0.2; // Parallax
this.fog2.tilePositionY += 0.1;
// Day/Night Cycle
this.baseTime += (delta * 0.001 * this.timeSpeed);
if (this.baseTime >= 24) this.baseTime = 0;
this.updateLighting(this.baseTime);
this.infoText.setText(`Time: ${Math.floor(this.baseTime)}:${Math.floor((this.baseTime % 1) * 60).toString().padStart(2, '0')}`);
}
updateLighting(hour) {
// Simple ambient light logic
let alpha = 0;
let color = 0x000000; // Darkness color
if (hour >= 6 && hour < 12) {
// Sunrise -> Noon (Brightening)
alpha = Phaser.Math.Interpolation.Linear([0.6, 0.0], (hour - 6) / 6);
color = 0xffaa00; // Orange tint for sunrise
}
else if (hour >= 12 && hour < 18) {
// Noon -> Sunset (Normal)
alpha = 0;
}
else if (hour >= 18 && hour < 21) {
// Sunset -> Night (Darkening)
alpha = Phaser.Math.Interpolation.Linear([0.0, 0.5], (hour - 18) / 3);
color = 0x330066; // Purple evening tint
}
else {
// Night (Dark)
alpha = 0.7; // 70% darkness
color = 0x000022; // Deep blue night
}
this.dayNightOverlay.setFillStyle(color, alpha);
}
}

View File

@@ -0,0 +1,675 @@
export default class GrassSceneClean extends Phaser.Scene {
constructor() {
super({ key: 'GrassSceneClean' });
}
preload() {
this.load.path = 'assets/';
// --- ASSETS ---
// 1. Podlaga (Foundation)
this.load.image('ground_base', 'DEMO_FAZA1/Ground/tla_trava_tekstura.png');
// 2. Vodni kanali (Water) - NEW CLEAN ASSET V7 (Aggressive Clean)
this.load.image('stream_final_v7', 'DEMO_FAZA1/Environment/stream_final_v7.png');
// Removed extensions for now
// 3. Foliage
this.load.image('grass_dense', 'DEMO_FAZA1/Vegetation/grass_cluster_dense.png');
this.load.image('grass_tall', 'DEMO_FAZA1/Vegetation/visoka_trava.png');
// 4. Items & Charts
this.load.image('hay', 'DEMO_FAZA1/Items/hay_drop_0.png');
this.load.image('trnje', 'DEMO_FAZA1/Obstacles/trnje.png');
this.load.image('toxic_fog', 'DEMO_FAZA1/VFX/toxic_fog.png');
this.load.image('amnesia_fog', 'DEMO_FAZA1/VFX/megla_ozadje.png');
// Generated Assets (Slices)
// Trees
for (let i = 0; i <= 5; i++) this.load.image(`tree_adult_${i}`, `DEMO_FAZA1/Trees/tree_adult_${i}.png`);
// Environment (Dead Nature, Fence, Water)
for (let i = 0; i <= 8; i++) this.load.image(`dead_nature_${i}`, `DEMO_FAZA1/Environment/dead_nature_${i}.png`);
for (let i = 0; i <= 2; i++) this.load.image(`fence_sign_${i}`, `DEMO_FAZA1/Environment/fence_sign_${i}.png`);
for (let i = 0; i < 16; i++) this.load.image(`water_tile_${i}`, `DEMO_FAZA1/Environment/water_tile_${i}.png`);
// Misc Env
this.load.image('sotor', 'DEMO_FAZA1/Environment/sotor.png');
this.load.image('campfire', 'DEMO_FAZA1/Environment/taborni_ogenj.png');
this.load.image('sign_danger', 'DEMO_FAZA1/Environment/sign_danger.png');
// Vegetation
const veg = ['bush_hiding_spot', 'drevo_faza_1', 'drevo_faza_2', 'drevo_srednje', 'drevo_veliko', 'grass_cluster_dense', 'grass_cluster_flowery', 'trava_sop'];
veg.forEach(k => this.load.image(k, `DEMO_FAZA1/Vegetation/${k}.png`));
// Ground (Path)
for (let i = 0; i < 16; i++) {
this.load.image(`path_tile_${i}`, `DEMO_FAZA1/Ground/path_tile_${i}.png`);
}
// REPLACED STATIC KAI WITH SPRITE SHEET
// Frame size 256x256 based on 1024x1024 sheet
this.load.spritesheet('kai', 'DEMO_FAZA1/Characters/kai_walk_sheet.png', {
frameWidth: 256,
frameHeight: 256
}); // Loading as 'kai' to keep existing references working, but now it has frames.
// 5. UI Assets (Loaded in UIScene now to prevent duplicates)
// REMOVED
// 6. Camp Assets
this.load.image('campfire', 'DEMO_FAZA1/Environment/taborni_ogenj.png');
this.load.image('tent', 'DEMO_FAZA1/Environment/sotor.png');
this.load.image('sleeping_bag', 'DEMO_FAZA1/Items/spalna_vreca.png');
}
create() {
const WORLD_W = 2500;
const WORLD_H = 2500;
this.physics.world.setBounds(0, 0, WORLD_W, WORLD_H);
this.cameras.main.setBounds(0, 0, WORLD_W, WORLD_H);
this.cameras.main.setBackgroundColor('#1a1a1a');
// --- ZOOM CONTROL ---
this.cameras.main.setZoom(1.5); // Default start zoom
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
// MACBOOK OPTIMIZATION: Prevent Zoom when hovering over Editor Sidebar
if (this.editorEnabled) {
const SIDEBAR_W = 320;
const PALETTE_X = this.scale.width - SIDEBAR_W;
if (pointer.x > PALETTE_X) {
return; // Stop processing zoom if over sidebar
}
}
// Zoom In/Out based on wheel delta
// deltaY > 0 means scroll down (zoom out), deltaY < 0 means scroll up (zoom in)
const zoomFactor = -0.001;
const newZoom = this.cameras.main.zoom + deltaY * zoomFactor;
// Clamp zoom to reasonable limits (e.g., 0.5x to 3x)
this.cameras.main.setZoom(Phaser.Math.Clamp(newZoom, 0.5, 3.0));
});
// --- 1. PODLAGA (The Foundation) ---
// Level 0, Locked to Z = -100
this.ground = this.add.tileSprite(WORLD_W / 2, WORLD_H / 2, WORLD_W, WORLD_H, 'ground_base');
this.ground.setTileScale(1, 1);
this.ground.setDepth(-100);
// --- 2. STREAM SYSTEM (Head + Extensions) ---
// Center the whole construction
const startX = WORLD_W / 2;
const startY = WORLD_H / 2 - 300; // Start higher up to leave room for extensions
// Main Head (Pipe + Splash)
// Showing V7 Asset (Aggressive Clean + Transparent Green)
this.stream = this.physics.add.staticImage(startX, startY, 'stream_final_v7');
this.stream.setOrigin(0.5, 0.5);
this.stream.setDepth(-50);
this.stream.setInteractive({ draggable: true }); // Enable Dragging
// Physics Body for Main
this.stream.body.setSize(this.stream.width * 0.8, this.stream.height * 0.2);
this.stream.body.setOffset(this.stream.width * 0.1, this.stream.height * 0.4);
// Extensions removed for reset
// for (let i = 1; i <= 3; i++) { ... }
// Collider added later after Kai creation
// --- 3. FOLIAGE (Trava - Šopi) ---
// Removed as requested
// --- 4. ITEMS & OBSTACLES ---
// Trnje (Thorns) - Draggable
this.trnje = this.add.image(startX - 200, startY + 100, 'trnje');
this.trnje.setScale(0.5); // Adjust scale if needed
this.trnje.setInteractive({ draggable: true });
// Trigger Amnesia Clear on interaction
this.trnje.on('pointerdown', () => {
this.clearAmnesia();
});
// General Drag Event
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
gameObject.x = dragX;
gameObject.y = dragY;
});
// --- EDITOR MODE SYSTEM ---
this.editorEnabled = false;
this.selectedTile = 'path_tile_0';
this.editorGroup = this.add.group(); // Saved tiles
// Initialize Default State
this.selectedTile = 'path_tile_0';
this.editorEnabled = false;
// UI Palette (Hidden by default)
// FIX: Use viewport dimensions for UI
const VIEW_W = this.scale.width;
const VIEW_H = this.scale.height;
// --- EDITOR UI SETUP (Clean Sidebar) ---
const SIDEBAR_W = 320;
const PALETTE_X = VIEW_W - SIDEBAR_W;
// UI Container Group (for toggling visibility)
this.editorUI = this.add.group();
// 1. Sidebar Background (Clean Glass Look)
let sidebarBg = this.add.rectangle(PALETTE_X + SIDEBAR_W / 2, VIEW_H / 2, SIDEBAR_W, VIEW_H, 0x222222, 0.95)
.setScrollFactor(0).setDepth(2000).setStrokeStyle(2, 0x444444);
this.editorUI.add(sidebarBg);
// Title
let sidebarTitle = this.add.text(PALETTE_X + 20, 20, "TILES PALETTE", {
fontSize: '24px', fontFamily: 'Arial', color: '#ffffff', fontStyle: 'bold'
}).setScrollFactor(0).setDepth(2002);
this.editorUI.add(sidebarTitle);
// 2. Layer Switcher (Tabs)
this.currentLayer = 'ground';
const layerBtns = [];
const tabsY = 60;
const createLayerBtn = (label, mode, index) => {
let x = PALETTE_X + 20 + (index * 90);
let btnBg = this.add.rectangle(x + 40, tabsY + 15, 80, 30, 0x333333).setScrollFactor(0).setDepth(2002).setInteractive({ useHandCursor: true });
let txt = this.add.text(x + 10, tabsY + 5, label, { fontSize: '14px', color: '#888', fontStyle: 'bold' })
.setScrollFactor(0).setDepth(2003);
// Hit area on bg
btnBg.on('pointerdown', () => {
this.currentLayer = mode;
layerBtns.forEach(b => {
b.txt.setColor('#888');
b.bg.setFillStyle(0x333333);
});
txt.setColor('#ffffff');
btnBg.setFillStyle(0x0077ff);
});
this.editorUI.add(btnBg);
this.editorUI.add(txt);
return { txt, bg: btnBg };
};
layerBtns.push(createLayerBtn("Ground", 'ground', 0));
layerBtns.push(createLayerBtn("Deco", 'deco', 1));
layerBtns.push(createLayerBtn("Build", 'building', 2));
// Select first default
layerBtns[0].txt.setColor('#ffffff');
layerBtns[0].bg.setFillStyle(0x0077ff);
// 3. Palette Content (Scrollable)
const contentY = 120; // Below tabs
const itemContainer = this.add.container(PALETTE_X, contentY).setScrollFactor(0).setDepth(2001);
// Mask for scrolling
const maskShape = this.make.graphics();
maskShape.fillStyle(0xffffff);
maskShape.fillRect(PALETTE_X, contentY, SIDEBAR_W, VIEW_H - contentY);
const mask = maskShape.createGeometryMask();
itemContainer.setMask(mask);
// Prepare Palette Items
const paletteItems = [];
for (let i = 0; i < 16; i++) paletteItems.push(`path_tile_${i}`);
for (let i = 0; i < 16; i++) paletteItems.push(`water_tile_${i}`);
for (let i = 0; i <= 2; i++) paletteItems.push(`fence_sign_${i}`);
paletteItems.push('sign_danger');
for (let i = 0; i <= 5; i++) paletteItems.push(`tree_adult_${i}`);
for (let i = 0; i <= 8; i++) paletteItems.push(`dead_nature_${i}`);
['bush_hiding_spot', 'drevo_faza_1', 'drevo_faza_2', 'drevo_srednje', 'drevo_veliko', 'grass_cluster_dense', 'grass_cluster_flowery', 'trava_sop'].forEach(k => paletteItems.push(k));
paletteItems.push('sotor', 'campfire', 'eraser_icon');
// Grid Layout
let col = 0, row = 0;
const CELL_SZ = 90;
// Selector Highlight (Clean Blue Border)
let selector = this.add.rectangle(0, 0, 80, 80).setStrokeStyle(4, 0x00aaff).setVisible(false);
itemContainer.add(selector);
paletteItems.forEach((key) => {
let ix = 50 + (col * CELL_SZ);
let iy = 50 + (row * CELL_SZ);
let icon = this.add.image(ix, iy, key).setScale(0.3).setInteractive({ useHandCursor: true });
itemContainer.add(icon);
icon.on('pointerdown', () => {
this.selectedTile = key;
selector.setPosition(ix, iy).setVisible(true);
// Update Ghost
if (key === 'eraser_icon') {
this.ghostSprite.setVisible(false);
} else {
this.ghostSprite.setTexture(key);
this.ghostSprite.setScale(key.includes('path') || key.includes('water') ? 0.5 : 1.0); // Simple scaling logic
}
});
col++;
if (col >= 3) { col = 0; row++; }
});
// Scroll Logic
let scrollY = 0;
const MAX_SCROLL = Math.max(0, (row * CELL_SZ) + 150 - (VIEW_H - contentY));
this.input.on('wheel', (ptr, gameObjs, dx, dy, dz) => {
if (this.editorEnabled && ptr.x > PALETTE_X) {
scrollY -= dy;
if (scrollY > 0) scrollY = 0;
if (scrollY < -MAX_SCROLL) scrollY = -MAX_SCROLL;
itemContainer.y = contentY + scrollY;
}
});
// 4. Ghost Cursor
this.ghostSprite = this.add.image(0, 0, this.selectedTile)
.setAlpha(0.6).setDepth(3000).setVisible(false); // Topmost
// Toggle Visibility Helpers
const toggleEditor = (state) => {
this.editorEnabled = state;
this.editorUI.setVisible(state);
itemContainer.setVisible(state);
this.ghostSprite.setVisible(state && this.selectedTile !== 'eraser_icon');
console.log("Editor:", state);
};
toggleEditor(false); // Start hidden
// Toggle Key
this.input.keyboard.on('keydown-E', () => {
toggleEditor(!this.editorEnabled);
});
this.input.on('pointermove', (pointer) => {
if (!this.editorEnabled) return;
// Hide Ghost if over UI
if (pointer.x > PALETTE_X) {
this.ghostSprite.setVisible(false);
return;
} else if (this.selectedTile !== 'eraser_icon') {
this.ghostSprite.setVisible(true);
}
// Snap calculation
const SNAP = 128;
const sx = Math.floor(pointer.worldX / SNAP) * SNAP + (SNAP / 2);
const sy = Math.floor(pointer.worldY / SNAP) * SNAP + (SNAP / 2);
this.ghostSprite.setPosition(sx, sy);
});
// Painting Logic
this.input.on('pointerdown', (pointer) => {
if (!this.editorEnabled) return;
// Ignore UI clicks
if (pointer.x > PALETTE_X) return;
// ERASER
if (this.selectedTile === 'eraser_icon') return; // Handled by object click
// Snap
const SNAP = 128;
const sx = Math.floor(pointer.worldX / SNAP) * SNAP + (SNAP / 2);
const sy = Math.floor(pointer.worldY / SNAP) * SNAP + (SNAP / 2);
// Remove existing tile at this spot to avoid stacking
let existing = this.editorGroup.getChildren().find(c => Math.abs(c.x - sx) < 10 && Math.abs(c.y - sy) < 10);
if (existing) existing.destroy();
let placedStub = this.add.image(sx, sy, this.selectedTile);
placedStub.setInteractive();
placedStub.on('pointerdown', () => {
if (this.editorEnabled && this.selectedTile === 'eraser_icon') {
const isWater = placedStub.texture.key.startsWith('water_tile_');
placedStub.destroy();
// If we deleted water, update neighbors
if (isWater) {
const neighbors = [
{ dx: 0, dy: -SNAP }, { dx: SNAP, dy: 0 }, { dx: 0, dy: SNAP }, { dx: -SNAP, dy: 0 }
];
neighbors.forEach(n => this.updateAutotile(sx + n.dx, sy + n.dy));
}
}
});
// Layer Logic
if (this.currentLayer === 'ground') {
placedStub.setDepth(-40);
placedStub.setScale(0.5); // Grid tiles
} else {
placedStub.setDepth(sy); // Y-sort
}
this.editorGroup.add(placedStub);
// AUTO-TILE UPDATE
if (this.selectedTile.startsWith('water_tile_')) {
this.updateAutotile(sx, sy);
}
});
// --- PREVIOUSLY GENERATED PROPS (Draggable Example) ---
// Commented out per request "samo blatno potko"
/*
const propAssets = ['tree_adult_0', 'tree_adult_1', 'dead_nature_0'];
for (let i = 0; i < propAssets.length; i++) {
let item = this.add.image(startX + 200 + (i * 100), startY + 200, propAssets[i]);
item.setInteractive({ draggable: true });
}
*/
// --- RECONSTRUCT PATH (4x4 GRID) ---
// REMOVED PER USER REQUEST "samo blatno potko"
/*
// Image was 1024x1024, sliced into 256x256 (4 cols, 4 rows)
// Indices 0..15
let pIndex = 0;
const GRID_SZ = 256;
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
// Determine Tile Key
let key = `path_tile_${pIndex}`;
// Place it
// Center offset: -1.5 * size to center the 4x4 block
let px = startX + (c * GRID_SZ) + 200;
let py = startY + (r * GRID_SZ) + 200;
let tile = this.add.image(px, py, key);
tile.setDepth(-40); // Ground level
// Optional: make draggable? User said "naredi", maybe fixed?
// tile.setInteractive({ draggable: true });
pIndex++;
}
}
*/
// --- 5. CHAR (Kai) ---
this.kai = this.physics.add.sprite(WORLD_W / 2, WORLD_H / 2, 'kai');
// Povečava na 160px višine
this.kai.setScale(160 / 256); // Scale based on actual frame height (256) -> target 160
this.kai.setCollideWorldBounds(true);
this.kai.setOrigin(0.5, 0.9);
// Adjust Physics Body for larger size
// Width ~40, Height ~30 (relative to scaled sprite)
this.kai.body.setSize(50, 40);
this.kai.body.setOffset(256 / 2 - 25, 256 - 40); // Pivot offset based on 256 frame
// Collider Stream <-> Kai
// this.physics.add.collider(this.kai, this.stream);
// --- ANIMATIONS ---
// 0-3: Down, 4-7: Left, 8-11: Right, 12-15: Up
this.anims.create({
key: 'walk-down',
frames: this.anims.generateFrameNumbers('kai', { start: 0, end: 3 }),
frameRate: 8,
repeat: -1
});
this.anims.create({
key: 'walk-left',
frames: this.anims.generateFrameNumbers('kai', { start: 4, end: 7 }),
frameRate: 8,
repeat: -1
});
this.anims.create({
key: 'walk-right',
frames: this.anims.generateFrameNumbers('kai', { start: 8, end: 11 }),
frameRate: 8,
repeat: -1
});
this.anims.create({
key: 'walk-up',
frames: this.anims.generateFrameNumbers('kai', { start: 12, end: 15 }),
frameRate: 8,
repeat: -1
});
// Idle
this.kai.play('walk-down');
this.kai.stop();
// Camera setup logic
this.cameras.main.startFollow(this.kai, true, 0.1, 0.1);
this.cursors = this.input.keyboard.createCursorKeys();
// Add WASD keys
this.keys = this.input.keyboard.addKeys({
up: Phaser.Input.Keyboard.KeyCodes.W,
down: Phaser.Input.Keyboard.KeyCodes.S,
left: Phaser.Input.Keyboard.KeyCodes.A,
right: Phaser.Input.Keyboard.KeyCodes.D
});
// Collider added later after Kai creation
// Collider added later after Kai creation
// --- 3. FOLIAGE (Trava - Šopi) ---
// Removed as requested
// --- 4. ITEMS (Seno) ---
// Removed as requested
// Collider Stream <-> Kai
// this.physics.add.collider(this.kai, this.stream);
// --- ANIMATIONS ---
// 0-3: Down, 4-7: Left, 8-11: Right, 12-15: Up
// This is a duplicate animation creation block, removing it.
// this.anims.create({
// key: 'walk-down',
// frames: this.anims.generateFrameNumbers('kai', { start: 0, end: 3 }),
// frameRate: 8,
// repeat: -1
// });
// this.anims.create({
// key: 'walk-left',
// frames: this.anims.generateFrameNumbers('kai', { start: 4, end: 7 }),
// frameRate: 8,
// repeat: -1
// });
// this.anims.create({
// key: 'walk-right',
// frames: this.anims.generateFrameNumbers('kai', { start: 8, end: 11 }),
// frameRate: 8,
// repeat: -1
// });
// this.anims.create({
// key: 'walk-up',
// frames: this.anims.generateFrameNumbers('kai', { start: 12, end: 15 }),
// frameRate: 8,
// repeat: -1
// });
// Launch UI Scene
if (!this.scene.get('UIScene').scene.settings.active) {
this.scene.launch('UIScene');
}
// --- AMNESIA INIT ---
// 1. PostFX Blur (Keep it for extra effect)
// this.amnesiaBlur = this.cameras.main.postFX.addBlur(0, 2, 2, 1);
// 2. Overlay Texture (Megla Pozabe)
/*
this.amnesiaOverlay = this.add.image(WORLD_W / 2, WORLD_H / 2, 'amnesia_fog');
this.amnesiaOverlay.setScrollFactor(0); // Stuck to camera
this.amnesiaOverlay.setDepth(6000); // Topmost
this.amnesiaOverlay.setAlpha(0.8);
this.amnesiaOverlay.setScale(Math.max(VIEW_W / this.amnesiaOverlay.width, VIEW_H / this.amnesiaOverlay.height)); // Cover screen
*/
// --- TOXIC FOG (Visuals) ---
// Layer 1: Pinkish (The "Toxic" Part)
/*
this.fog1 = this.add.tileSprite(0, 0, WORLD_W, WORLD_H, 'toxic_fog');
this.fog1.setOrigin(0, 0);
this.fog1.setAlpha(0.3);
this.fog1.setDepth(5000);
this.fog1.setBlendMode(Phaser.BlendModes.SCREEN); // Screen for glowing effect
this.fog1.setTint(0xff00cc); // PINK
*/
// Layer 2: Greenish (The "Radiation" Part)
/*
this.fog2 = this.add.tileSprite(0, 0, WORLD_W, WORLD_H, 'toxic_fog');
this.fog2.setOrigin(0, 0);
this.fog2.setAlpha(0.2);
this.fog2.setDepth(5001);
this.fog2.setScale(1.5);
this.fog2.setBlendMode(Phaser.BlendModes.SCREEN);
this.fog2.setTint(0x00ff00); // GREEN
*/
}
// --- AMNESIA SYSTEM ---
clearAmnesia() {
if (this.amnesiaBlur) {
this.tweens.add({
targets: [this.amnesiaBlur, this.amnesiaOverlay], // Fade both
strength: 0, // For blur
alpha: 0, // For overlay
duration: 2000,
onComplete: () => {
this.cameras.main.postFX.remove(this.amnesiaBlur);
this.amnesiaBlur = null;
if (this.amnesiaOverlay) this.amnesiaOverlay.destroy();
}
});
}
}
// --- AUTO-TILING SYSTEM ---
getWaterBitmask(x, y) {
// Check 4 neighbors: Top (1), Right (2), Bottom (4), Left (8)
// Grid size is 128 (SNAP)
const SNAP = 128;
let mask = 0;
// Helper to check if a tile at (tx, ty) is water
const isWater = (tx, ty) => {
// Check existing tiles in editorGroup
// This is O(N), for optimization use a Map in production
let found = this.editorGroup.getChildren().find(c =>
Math.abs(c.x - tx) < 10 && Math.abs(c.y - ty) < 10 &&
c.texture.key.startsWith('water_tile_')
);
return !!found;
};
if (isWater(x, y - SNAP)) mask += 1; // Top
if (isWater(x + SNAP, y)) mask += 2; // Right
if (isWater(x, y + SNAP)) mask += 4; // Bottom
if (isWater(x - SNAP, y)) mask += 8; // Left
return mask;
}
updateAutotile(x, y) {
const SNAP = 128;
// 1. Find the tile at x,y
let tile = this.editorGroup.getChildren().find(c =>
Math.abs(c.x - x) < 10 && Math.abs(c.y - y) < 10
);
if (!tile || !tile.texture.key.startsWith('water_tile_')) return;
// 2. Calculate new mask
let mask = this.getWaterBitmask(x, y);
tile.setTexture(`water_tile_${mask}`);
// 3. Update neighbors
const neighbors = [
{ dx: 0, dy: -SNAP }, // Top
{ dx: SNAP, dy: 0 }, // Right
{ dx: 0, dy: SNAP }, // Bottom
{ dx: -SNAP, dy: 0 } // Left
];
neighbors.forEach(n => {
let nx = x + n.dx;
let ny = y + n.dy;
let neighbor = this.editorGroup.getChildren().find(c =>
Math.abs(c.x - nx) < 10 && Math.abs(c.y - ny) < 10
);
if (neighbor && neighbor.texture.key.startsWith('water_tile_')) {
let nMask = this.getWaterBitmask(nx, ny);
neighbor.setTexture(`water_tile_${nMask}`);
}
});
}
update() {
// --- FOG MOVEMENT ---
/*
if (this.fog1) {
this.fog1.tilePositionX += 0.5;
this.fog1.tilePositionY += 0.2;
}
if (this.fog2) {
this.fog2.tilePositionX -= 0.3;
this.fog2.tilePositionY += 0.1;
}
*/
const speed = 250;
this.kai.setVelocity(0);
let moving = false;
// Input helpers
const left = this.cursors.left.isDown || this.keys.left.isDown;
const right = this.cursors.right.isDown || this.keys.right.isDown;
const up = this.cursors.up.isDown || this.keys.up.isDown;
const down = this.cursors.down.isDown || this.keys.down.isDown;
if (left) {
this.kai.setVelocityX(-speed);
this.kai.play('walk-left', true);
moving = true;
} else if (right) {
this.kai.setVelocityX(speed);
this.kai.play('walk-right', true);
moving = true;
}
if (up) {
this.kai.setVelocityY(-speed);
if (!left && !right) {
this.kai.play('walk-up', true);
}
moving = true;
} else if (down) {
this.kai.setVelocityY(speed);
if (!left && !right) {
this.kai.play('walk-down', true);
}
moving = true;
}
if (this.kai.body.velocity.length() > 0) {
this.kai.body.velocity.normalize().scale(speed);
} else {
this.kai.stop();
// Optional: reset to idle frame?
}
// --- Z-SORTING SYSTEM ---
// Player
this.kai.setDepth(this.kai.y);
}
}

View File

@@ -0,0 +1,49 @@
export default class UIScene extends Phaser.Scene {
constructor() {
super({ key: 'UIScene', active: true });
}
preload() {
this.load.path = 'assets/';
// Load UI Assets here (moved from GrassScene)
this.load.image('ui_health_bar', 'DEMO_FAZA1/UI/health_bar.png');
this.load.image('ui_weather_widget', 'DEMO_FAZA1/UI/weather_widget.png');
this.load.image('ui_minimap', 'DEMO_FAZA1/UI/minimap_frame.png');
this.load.image('ui_hotbar', 'DEMO_FAZA1/UI/hotbar_background.png');
this.load.image('ui_action_btn', 'DEMO_FAZA1/UI/action_btn.png');
this.load.image('ui_badge_trial', 'DEMO_FAZA1/UI/badge_trial.png');
}
create() {
// Simple UI Layout
const width = this.cameras.main.width;
const height = this.cameras.main.height;
const PAD = 20;
// 0. TRIAL BADGE (Top Center - The "Hook")
this.add.image(width / 2, PAD, 'ui_badge_trial')
.setOrigin(0.5, 0)
.setScale(0.8)
.setDepth(100); // Ensure it's on top
// 1. Health (Top Left)
let health = this.add.image(PAD, PAD, 'ui_health_bar').setOrigin(0, 0).setScale(0.8);
// 2. Weather (Top Right)
let weather = this.add.image(width - PAD, PAD, 'ui_weather_widget').setOrigin(1, 0).setScale(0.8);
// 3. Minimap (Bottom Right)
let minimap = this.add.image(width - PAD, height - PAD, 'ui_minimap').setOrigin(1, 1).setScale(0.7);
// 4. Hotbar (Bottom Center)
let hotbar = this.add.image(width / 2, height - PAD, 'ui_hotbar').setOrigin(0.5, 1).setScale(0.8);
// 5. Action Button (Right of Hotbar)
let action = this.add.image(width / 2 + 350, height - 30, 'ui_action_btn').setOrigin(0.5, 1).setScale(0.8);
// Debug Text
this.add.text(width / 2, 50, 'HUD LAYER ACTIVE', {
fontSize: '24px', fill: '#00ff00', stroke: '#000', strokeThickness: 4
}).setOrigin(0.5);
}
}