VISUAL UPDATE: Demo Phase 1 setup (GrassScene with wind, fog, day/night). Pending path fix for assets.

This commit is contained in:
2026-01-25 14:29:20 +01:00
parent c78cb26aba
commit c93603c5c6
15 changed files with 132 additions and 244 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -41,3 +41,14 @@ Posodobili smo dokument `docs/GAME_DESIGN_NOTES.md` z naslednjimi koncepti:
* 10-Fazna struktura izdajanja.
* **Licence Preverjene:** Najdene so v (Apache, MIT, BSD), kar je pričakovano za Python knjižnice.
* **Koda in Dokumentacija:** Vse posodobljeno in pripravljeno na vnos referenc.
### 5. Priprava Assetov za Fazo 1 (25.01.2026 13:36 CET)
* **Referenčne slike:** Vse prekopirane v (132 datotek).
* **Obdelava:**
* Konvertirano v PNG.
* *Intelligent Resize* (Gronk 512px, Kai 256px, Insekti 96px).
* *Sharpening* (LANCZOS + Unsharp Mask).
* Ohranjena originalna ozadja.
* **Struktura:**
* Ustvarjena mapa .
* Izbrisane stare neurejene podmape.

BIN
assets/.DS_Store vendored

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -1,267 +1,144 @@
export default class GrassScene extends Phaser.Scene {
constructor() {
super({ key: 'GrassScene' });
this.currentZoom = 1.0;
this.targetZoom = 1.0;
this.baseTime = 12; // Start at 12:00 (Noon)
this.timeSpeed = 0.5; // How fast time passes
}
preload() {
console.log("🌲 Loading Interactive Forest...");
console.log("🌿 Loading Environment assets...");
// TERRAIN
this.load.image('grass', 'assets/slike/environment/trava .png');
this.load.image('dirt_path', 'assets/slike/nova mapa faza 0-1/Environment/podlaga/dirt_path.png');
this.load.image('tall_grass', 'assets/slike/environment/tall_grass.png');
// Load assets from DEMO_FAZA1
this.load.image('ground_dead', 'assets/DEMO_FAZA1/tla_trava_tekstura.png');
this.load.image('grass_tall', 'assets/DEMO_FAZA1/visoka_trava.png');
this.load.image('grass_tuft', 'assets/DEMO_FAZA1/trava_sop.png');
// CAMP - Cleaned PNG files (256px)
this.load.image('tent', 'assets/slike/nova mapa faza 0-1/Environment/stavbe/MOJE_SLIKE_KONCNA_ostalo_tent_basic_style32.png');
this.load.image('campfire', 'assets/slike/nova mapa faza 0-1/Environment/props/MOJE_SLIKE_KONCNA_ostalo_campfire_frame1.png');
// Trees (Growth Stages)
this.load.image('tree_small', 'assets/DEMO_FAZA1/drevo_majhno.png');
this.load.image('tree_medium', 'assets/DEMO_FAZA1/drevo_srednje.png');
this.load.image('tree_large', 'assets/DEMO_FAZA1/drevo_veliko.png');
this.load.image('tree_dead', 'assets/DEMO_FAZA1/suho_drevo.png');
// NATURE - Cleaned PNG trees (512px) from narava subfolder
this.load.image('dead_tree', 'assets/slike/nova mapa faza 0-1/Environment/narava/unnamed.png');
this.load.image('bush', 'assets/slike/nova mapa faza 0-1/Environment/narava/MOJE_SLIKE_KONCNA_okolje_bush_green.png');
this.load.image('rock_small', 'assets/slike/nova mapa faza 0-1/Environment/narava/MOJE_SLIKE_KONCNA_okolje_biomi_gore_rock_small_style32.png');
this.load.image('rock_medium', 'assets/slike/nova mapa faza 0-1/Environment/narava/MOJE_SLIKE_KONCNA_okolje_biomi_gore_rock_medium_style32.png');
// Weather
this.load.image('fog', 'assets/DEMO_FAZA1/megla_ozadje.png');
// PROPS - Cleaned PNG (256px)
this.load.image('wood_log', 'assets/slike/nova mapa faza 0-1/Environment/props/wood_log.png');
this.load.image('broken_board', 'assets/slike/nova mapa faza 0-1/UI/icon_longboard.png');
// CHARACTER
this.load.image('kai', 'assets/slike/characters/kai_idle_west_2_styleA_1024x1024.png');
// GLSL Wind Shader (Inline for simplicity)
this.windPipeline = null; // Will create in create()
}
create() {
console.log("🏕️ Building Interactive Forest...");
const { width, height } = this.cameras.main;
const { width, height } = this.scale;
// Store all objects for DOF effect
this.backgroundObjects = [];
this.foregroundObjects = [];
// 1. DEAD GROUND (Tiling background)
// Uses the 'dead' texture as the base canvas
this.ground = this.add.tileSprite(0, 0, width, height, 'ground_dead').setOrigin(0, 0);
// --- LAYER 0: GRASS BACKGROUND ---
const grass = this.add.tileSprite(0, 0, width, height, 'grass').setOrigin(0, 0);
grass.setDepth(0);
// 2. VEGETATION GROUPS
this.trees = [];
this.grasses = [];
// --- LAYER 1: DIRT PATH with FEATHERING ---
for (let i = 0; i < 8; i++) {
const pathX = width / 2 + (i * 120);
const pathY = height / 2 + 100 + (i * 40);
const path = this.add.image(pathX, pathY, 'dirt_path');
path.setScale(0.5);
path.setDepth(1);
path.setAlpha(0.7 + (i * 0.025));
}
// 3. GENERATE WORLD (Procedural placement)
this.generateVegetation(width, height);
// --- LAYER 2: BACKGROUND TREES ---
const bgTree1 = this.add.image(width / 2 + 450, height / 2 - 120, 'dead_tree');
bgTree1.setScale(0.4);
bgTree1.setDepth(2);
bgTree1.setAlpha(0.5);
bgTree1.setTint(0x666666);
this.backgroundObjects.push(bgTree1);
// 4. WEATHER LAYERS (Fog)
this.fog1 = this.add.tileSprite(0, 0, width, height, 'fog').setOrigin(0, 0).setAlpha(0.2).setBlendMode(Phaser.BlendModes.ADD);
this.fog2 = this.add.tileSprite(0, 0, width, height, 'fog').setOrigin(0, 0).setAlpha(0.15).setBlendMode(Phaser.BlendModes.ADD);
const bgTree2 = this.add.image(width / 2 + 650, height / 2 + 20, 'dead_tree');
bgTree2.setScale(0.38);
bgTree2.setDepth(2);
bgTree2.setAlpha(0.45);
bgTree2.setTint(0x666666);
this.backgroundObjects.push(bgTree2);
// 5. LIGHTING / DAY-NIGHT OVERLAY
this.dayNightOverlay = this.add.rectangle(0, 0, width, height, 0x000000, 0).setOrigin(0, 0).setDepth(1000);
// --- LAYER 3: VIRUS GLOW ---
const virusGlow1 = this.add.circle(width / 2 + 280, height / 2 + 50, 80, 0x00ff00, 0.25);
virusGlow1.setDepth(3);
virusGlow1.setBlendMode(Phaser.BlendModes.ADD);
const virusGlow2 = this.add.circle(width / 2 + 530, height / 2 + 200, 70, 0x00ff00, 0.2);
virusGlow2.setDepth(3);
virusGlow2.setBlendMode(Phaser.BlendModes.ADD);
const virusGlow3 = this.add.circle(width / 2 + 650, height / 2 + 50, 60, 0x00ff00, 0.18);
virusGlow3.setDepth(3);
virusGlow3.setBlendMode(Phaser.BlendModes.ADD);
this.tweens.add({
targets: [virusGlow1, virusGlow2, virusGlow3],
alpha: { from: 0.15, to: 0.35 },
scaleX: { from: 0.85, to: 1.15 },
scaleY: { from: 0.85, to: 1.15 },
duration: 2500,
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut'
});
// --- LAYER 4: CAMP ---
const tent = this.add.image(width / 2 - 300, height / 2 - 50, 'tent');
tent.setScale(0.4);
tent.setDepth(10);
const campfire = this.add.image(width / 2 - 100, height / 2 + 30, 'campfire');
campfire.setScale(0.3);
campfire.setDepth(11);
// --- LAYER 5: PATH DECORATIONS ---
const rock1 = this.add.image(width / 2 + 150, height / 2 + 120, 'rock_small');
rock1.setScale(0.28);
rock1.setDepth(8);
const rock2 = this.add.image(width / 2 + 380, height / 2 + 210, 'rock_medium');
rock2.setScale(0.32);
rock2.setDepth(8);
const rock3 = this.add.image(width / 2 + 520, height / 2 + 260, 'rock_small');
rock3.setScale(0.25);
rock3.setDepth(8);
const log1 = this.add.image(width / 2 + 240, height / 2 + 150, 'wood_log');
log1.setScale(0.22);
log1.setDepth(9);
log1.setAngle(35);
const brokenBoard = this.add.image(width / 2 + 460, height / 2 + 235, 'broken_board');
brokenBoard.setScale(0.08);
brokenBoard.setDepth(9);
brokenBoard.setAngle(-20);
brokenBoard.setTint(0x555555);
// --- LAYER 6: MID-GROUND TREES ---
const tree1 = this.add.image(width / 2 + 280, height / 2 + 20, 'dead_tree');
tree1.setScale(0.55);
tree1.setDepth(13);
tree1.setAngle(-12);
this.backgroundObjects.push(tree1);
const tree2 = this.add.image(width / 2 + 530, height / 2 + 170, 'dead_tree');
tree2.setScale(0.58);
tree2.setDepth(13);
tree2.setAngle(10);
this.backgroundObjects.push(tree2);
// Bushes
const bush1 = this.add.image(width / 2 + 330, height / 2 + 75, 'bush');
bush1.setScale(0.32);
bush1.setDepth(12);
const bush2 = this.add.image(width / 2 + 460, height / 2 + 165, 'bush');
bush2.setScale(0.35);
bush2.setDepth(12);
const bush3 = this.add.image(width / 2 + 590, height / 2 + 240, 'bush');
bush3.setScale(0.3);
bush3.setDepth(12);
// --- LAYER 7: DYNAMIC GRASS TUFTS (wind effect) ---
this.grassTufts = [];
const tuftsPositions = [
{ x: width / 2 + 180, y: height / 2 + 110, delay: 0, duration: 3500 },
{ x: width / 2 + 350, y: height / 2 + 60, delay: 0.5, duration: 4000 },
{ x: width / 2 + 270, y: height / 2 + 180, delay: 1.0, duration: 3200 },
{ x: width / 2 + 490, y: height / 2 + 150, delay: 1.5, duration: 3800 },
{ x: width / 2 + 400, y: height / 2 + 240, delay: 0.8, duration: 3600 },
{ x: width / 2 + 560, y: height / 2 + 220, delay: 1.2, duration: 4200 },
];
tuftsPositions.forEach(pos => {
const tuft = this.add.image(pos.x, pos.y, 'tall_grass');
tuft.setScale(0.12);
tuft.setDepth(16);
tuft.setOrigin(0.5, 1); // Bottom center
// Wind sway animation
this.tweens.add({
targets: tuft,
angle: { from: -3, to: 3 },
duration: pos.duration,
delay: pos.delay * 1000,
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut'
});
this.grassTufts.push(tuft);
});
// --- LAYER 8: FOREGROUND TREE ---
const fgTree1 = this.add.image(width / 2 + 200, height / 2 + 180, 'dead_tree');
fgTree1.setScale(0.7);
fgTree1.setDepth(25);
fgTree1.setAngle(-5);
fgTree1.setAlpha(0.95);
this.foregroundObjects.push(fgTree1);
// --- LAYER 9: KAI (focus point) ---
this.kai = this.add.image(width / 2 + 200, height / 2 + 140, 'kai');
this.kai.setScale(0.16);
this.kai.setDepth(20);
// --- CAMERA SETUP ---
this.cameras.main.setBounds(0, 0, width * 2, height * 2);
this.cameras.main.startFollow(this.kai, false, 0.1, 0.1);
// --- MOUSE WHEEL ZOOM ---
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
if (deltaY > 0) {
// Zoom Out
this.targetZoom = Math.max(0.5, this.targetZoom - 0.15);
} else {
// Zoom In
this.targetZoom = Math.min(2.5, this.targetZoom + 0.15);
}
});
// --- UI ---
const uiText = this.add.text(20, 20, "🎮 INTERACTIVE FOREST", {
fontFamily: 'Courier New, monospace',
fontSize: '20px',
color: '#d4af37',
backgroundColor: '#000000',
padding: { x: 10, y: 5 },
fixedToCamera: true
}).setScrollFactor(0).setDepth(100);
const controlsText = this.add.text(20, 55, "🖱️ Mouse Wheel: Zoom In/Out", {
fontFamily: 'Courier New, monospace',
fontSize: '14px',
color: '#ffffff',
backgroundColor: '#000000',
padding: { x: 10, y: 5 }
}).setScrollFactor(0).setDepth(100);
this.zoomText = this.add.text(20, 90, `📷 Zoom: ${this.currentZoom.toFixed(2)}x`, {
fontFamily: 'Courier New, monospace',
fontSize: '14px',
color: '#00ff88',
backgroundColor: '#000000',
padding: { x: 10, y: 5 }
}).setScrollFactor(0).setDepth(100);
console.log("✅ Interactive forest ready! Use mouse wheel to zoom!");
// UI Info
this.infoText = this.add.text(10, 10, 'Time: 12:00', { font: '16px monospace', fill: '#fff' }).setDepth(2000);
}
update() {
// Smooth zoom transition
if (Math.abs(this.currentZoom - this.targetZoom) > 0.01) {
this.currentZoom = Phaser.Math.Linear(this.currentZoom, this.targetZoom, 0.1);
this.cameras.main.setZoom(this.currentZoom);
generateVegetation(w, h) {
// Randomly place HIGH GRASS (for cutting/hiding)
for (let i = 0; i < 50; i++) {
let x = Phaser.Math.Between(50, w - 50);
let y = Phaser.Math.Between(50, h - 50);
// Update zoom text
if (this.zoomText) {
this.zoomText.setText(`📷 Zoom: ${this.currentZoom.toFixed(2)}x`);
}
// Depth of Field effect based on zoom
const dofFactor = Phaser.Math.Clamp((this.currentZoom - 1) / 1.5, 0, 1);
// Blur background when zoomed in
this.backgroundObjects.forEach(obj => {
obj.setAlpha(0.5 - (dofFactor * 0.3)); // Fade out
});
// Slight blur on foreground too
this.foregroundObjects.forEach(obj => {
obj.setAlpha(0.95 - (dofFactor * 0.2));
});
// Swaying tall grass
let grass = this.add.image(x, y, 'grass_tall');
grass.setScale(0.15 + (Math.random() * 0.1)); // Random size
grass.setDepth(y); // Y-sort
grass.setOrigin(0.5, 1); // Anchor at bottom
grass.swaySpeed = 0.002 + (Math.random() * 0.002);
grass.swayOffset = Math.random() * 100;
this.grasses.push(grass);
}
// Randomly place TREES
for (let i = 0; i < 15; i++) {
let x = Phaser.Math.Between(50, w - 50);
let y = Phaser.Math.Between(50, h - 50);
let type = Phaser.Math.RND.pick(['tree_small', 'tree_medium', 'tree_large', 'tree_dead']);
let tree = this.add.image(x, y, type);
// Size adjustments based on type
if (type === 'tree_large') tree.setScale(0.8);
else if (type === 'tree_medium') tree.setScale(0.6);
else tree.setScale(0.5);
tree.setDepth(y);
tree.setOrigin(0.5, 0.95); // Anchor near bottom
tree.swaySpeed = 0.001 + (Math.random() * 0.001); // Slower sway for trees
tree.swayOffset = Math.random() * 100;
this.trees.push(tree);
}
}
update(time, delta) {
// --- 1. WIND ANIMATION (Manual Vertex Sway Simulation) ---
// Since we aren't using a complex shader pipeline yet, we use rotation/skew
this.grasses.forEach(g => {
// Simple sway using Sine wave
g.rotation = Math.sin((time * g.swaySpeed) + g.swayOffset) * 0.1; // +/- 0.1 radians
});
this.trees.forEach(t => {
// Trees sway less
t.rotation = Math.sin((time * t.swaySpeed) + t.swayOffset) * 0.03;
});
// --- 2. FOG MOVEMENT ---
this.fog1.tilePositionX += 0.5;
this.fog2.tilePositionX += 0.2;
this.fog2.tilePositionY += 0.1;
// --- 3. DAY/NIGHT CYCLE ---
this.baseTime += (delta * 0.001 * this.timeSpeed); // Simulated hours
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

@@ -10,8 +10,8 @@
</head>
<body>
<!-- Phaser Library -->
<script src="node_modules/phaser/dist/phaser.js"></script>
<script src="../node_modules/phaser/dist/phaser.js"></script>
<!-- Game Entry -->
<script type="module" src="src/game.js"></script>
<script type="module" src="../src/game.js"></script>
</body>
</html>