docs: clean start 30.1.2026 - updated master diary and bible, renamed project
This commit is contained in:
29
nova farma TRAE/src/game.js
Normal file
29
nova farma TRAE/src/game.js
Normal 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);
|
||||
191
nova farma TRAE/src/scenes/GrassScene.js
Normal file
191
nova farma TRAE/src/scenes/GrassScene.js
Normal 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);
|
||||
}
|
||||
}
|
||||
675
nova farma TRAE/src/scenes/GrassScene_Clean.js
Normal file
675
nova farma TRAE/src/scenes/GrassScene_Clean.js
Normal 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);
|
||||
}
|
||||
}
|
||||
49
nova farma TRAE/src/scenes/UIScene.js
Normal file
49
nova farma TRAE/src/scenes/UIScene.js
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user