feat: Add Editor Mode (Palette, Eraser, Path Tiles), generate assets, enable WASD

This commit is contained in:
2026-01-29 20:21:50 +01:00
parent 177049e470
commit c70e651020
41 changed files with 323 additions and 8 deletions

View File

@@ -20,6 +20,19 @@ export default class GrassSceneClean extends Phaser.Scene {
// 4. Items & Charts
this.load.image('hay', 'DEMO_FAZA1/Items/hay_drop_0.png');
this.load.image('trnje', 'DEMO_FAZA1/Obstacles/trnje.png');
// Generated Assets (Slices)
this.load.image('tree_adult_0', 'DEMO_FAZA1/Trees/tree_adult_0.png');
this.load.image('tree_adult_1', 'DEMO_FAZA1/Trees/tree_adult_1.png');
this.load.image('dead_nature_0', 'DEMO_FAZA1/Environment/dead_nature_0.png'); // Stump
this.load.image('fence_sign_0', 'DEMO_FAZA1/Environment/fence_sign_0.png'); // Fence
// this.load.image('path_mud_0', 'DEMO_FAZA1/Ground/path_mud_0.png'); // Old single slice
// Tileset (Grid Slices)
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', {
@@ -73,6 +86,7 @@ export default class GrassSceneClean extends Phaser.Scene {
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);
@@ -86,8 +100,154 @@ export default class GrassSceneClean extends Phaser.Scene {
// --- 3. FOLIAGE (Trava - Šopi) ---
// Removed as requested
// --- 4. ITEMS (Seno) ---
// 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 });
// 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
// Toggle Key
this.input.keyboard.on('keydown-E', () => {
this.editorEnabled = !this.editorEnabled;
this.paletteContainer.setVisible(this.editorEnabled);
console.log("Editor Mode:", this.editorEnabled);
});
// UI Palette (Hidden by default)
// FIX: Use viewport dimensions for UI
const VIEW_W = this.scale.width;
const VIEW_H = this.scale.height;
this.paletteContainer = this.add.container(0, 0).setScrollFactor(0).setVisible(false).setDepth(1000);
// Background for Palette (TOP OF SCREEN)
// Moved to y=100 so it covers top 0-200px
let bg = this.add.rectangle(VIEW_W / 2, 100, VIEW_W, 200, 0x000000, 0.7);
this.paletteContainer.add(bg);
// Generate Eraser Texture
let g = this.make.graphics().fillStyle(0xFF0000).fillRect(0, 0, 64, 64);
g.generateTexture('eraser_icon', 64, 64);
g.destroy();
// Populate Palette
// 16 Path Tiles + Fence + Stump + Eraser
const paletteItems = [];
for (let i = 0; i < 16; i++) paletteItems.push(`path_tile_${i}`);
paletteItems.push('fence_sign_0');
paletteItems.push('eraser_icon');
let px = 100;
let py = 100; // TOP
const icons = []; // Store references for tinting
console.log("Palette View:", VIEW_W, VIEW_H); // Debug
paletteItems.forEach((key) => {
let icon = this.add.image(px, py, key).setScale(0.3).setInteractive({ useHandCursor: true });
icons.push(icon); // Track it
icon.on('pointerover', () => {
if (this.selectedTile !== key) icon.setTint(0xFFFF00); // Yellow on hover
});
icon.on('pointerout', () => {
if (this.selectedTile !== key) icon.clearTint(); // Clear if not selected
else icon.setTint(0x00FF00); // Keep Green if selected
});
icon.on('pointerdown', () => {
this.selectedTile = key;
console.log("Selected Brush:", key);
// Visual feedback: Tint selected Green, clear others
icons.forEach(i => i.clearTint());
icon.setTint(0x00FF00);
});
this.paletteContainer.add(icon);
px += 80;
if (px > VIEW_W - 100) { px = 100; py += 80; } // wrap
});
// Painting Logic
this.input.on('pointerdown', (pointer) => {
if (!this.editorEnabled) return;
// Ignore clicks on UI (Check Y < 200)
if (pointer.y < 200) return;
// ERASER MODE: Handled via object clicks
if (this.selectedTile === 'eraser_icon') return;
// Snap to Grid (128px for finer control, or 256px for full tiles)
const SNAP = 128;
const wx = pointer.worldX;
const wy = pointer.worldY;
const sx = Math.floor(wx / SNAP) * SNAP + (SNAP / 2);
const sy = Math.floor(wy / SNAP) * SNAP + (SNAP / 2);
let placedStub = this.add.image(sx, sy, this.selectedTile);
placedStub.setInteractive(); // Enable erase interaction
// Delete if clicked with eraser
placedStub.on('pointerdown', () => {
if (this.editorEnabled && this.selectedTile === 'eraser_icon') {
placedStub.destroy();
// Prevent click propagation?
}
});
if (this.selectedTile.includes('path')) {
placedStub.setDepth(-40); // Above ground
placedStub.setScale(0.5);
} else {
placedStub.setDepth(sy); // Y-sort
// placedStub.setInteractive({ draggable: true }); // Draggable requires careful event handling vs eraser
// Let's enable drag ONLY if not eraser?
// For now, prioritize Eraser. Dragging might conflict with 'pointerdown' erase.
// Or better: enable Drag, but if Eraser selected, destroy on click.
}
this.editorGroup.add(placedStub);
});
// --- 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) ---
// 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');
@@ -138,6 +298,13 @@ export default class GrassSceneClean extends Phaser.Scene {
// 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
});
@@ -196,25 +363,31 @@ export default class GrassSceneClean extends Phaser.Scene {
let moving = false;
if (this.cursors.left.isDown) {
// 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 (this.cursors.right.isDown) {
} else if (right) {
this.kai.setVelocityX(speed);
this.kai.play('walk-right', true);
moving = true;
}
if (this.cursors.up.isDown) {
if (up) {
this.kai.setVelocityY(-speed);
if (!this.cursors.left.isDown && !this.cursors.right.isDown) {
if (!left && !right) {
this.kai.play('walk-up', true);
}
moving = true;
} else if (this.cursors.down.isDown) {
} else if (down) {
this.kai.setVelocityY(speed);
if (!this.cursors.left.isDown && !this.cursors.right.isDown) {
if (!left && !right) {
this.kai.play('walk-down', true);
}
moving = true;