Update GrassScene: S1 Max Logic, Infinite Grass mode, new assets (visoka_trava_v2), physics enabled. Time: 15:15
This commit is contained in:
BIN
assets/.DS_Store
vendored
BIN
assets/.DS_Store
vendored
Binary file not shown.
BIN
assets/DEMO_FAZA1/.DS_Store
vendored
BIN
assets/DEMO_FAZA1/.DS_Store
vendored
Binary file not shown.
BIN
assets/DEMO_FAZA1/Environment/.DS_Store
vendored
BIN
assets/DEMO_FAZA1/Environment/.DS_Store
vendored
Binary file not shown.
BIN
assets/DEMO_FAZA1/Vegetation/visoka_trava_v2.png
Normal file
BIN
assets/DEMO_FAZA1/Vegetation/visoka_trava_v2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
28
index.html
Normal file
28
index.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="sl">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Nova Farma - Clean Start</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: #000;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
image-rendering: unique; /* or pixelated */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- Phaser Library -->
|
||||||
|
<script src="node_modules/phaser/dist/phaser.js"></script>
|
||||||
|
<!-- Game Entry -->
|
||||||
|
<script type="module" src="src/game.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
48
main.js
Normal file
48
main.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const { app, BrowserWindow, ipcMain } = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
let mainWindow;
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
width: 1280,
|
||||||
|
height: 720,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false
|
||||||
|
},
|
||||||
|
backgroundColor: '#000000',
|
||||||
|
title: 'Mrtva Dolina - Death Valley'
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.loadFile('index.html');
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
|
||||||
|
mainWindow.on('closed', () => {
|
||||||
|
mainWindow = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.on('log-action', (event, message) => {
|
||||||
|
console.log('[LOG]', message);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
createWindow();
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle security warnings
|
||||||
|
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
59
scripts/clean_pink_grass.py
Normal file
59
scripts/clean_pink_grass.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
|
||||||
|
def remove_pink_bg():
|
||||||
|
input_path = '/Users/davidkotnik/.gemini/antigravity/brain/63340bd3-91a9-439d-b1d9-5692ce5adaea/visoka_trava_v2_pink_bg_1769436757738.png'
|
||||||
|
output_path = 'assets/DEMO_FAZA1/Vegetation/visoka_trava_v2.png'
|
||||||
|
|
||||||
|
# Ensure directory exists
|
||||||
|
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||||
|
|
||||||
|
# Read image
|
||||||
|
img = cv2.imread(input_path)
|
||||||
|
if img is None:
|
||||||
|
print(f"Error: Could not read {input_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Convert to RGBA
|
||||||
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
|
||||||
|
|
||||||
|
# Define Magenta range (B, G, R) - standard magenta is (255, 0, 255) in BGR
|
||||||
|
# We'll allow a small tolerance to catch anti-aliasing edges
|
||||||
|
sensitivity = 20
|
||||||
|
lower_magenta = np.array([255 - sensitivity, 0, 255 - sensitivity])
|
||||||
|
upper_magenta = np.array([255, sensitivity, 255])
|
||||||
|
|
||||||
|
# Create mask
|
||||||
|
# Note: OpenCV reads as BGR, so Pink #FF00FF is (255, 0, 255)
|
||||||
|
# Using simple color thresholding
|
||||||
|
mask = cv2.inRange(img[:,:,:3], lower_magenta, upper_magenta)
|
||||||
|
|
||||||
|
# Invert mask (we want to keep non-magenta)
|
||||||
|
mask_inv = cv2.bitwise_not(mask)
|
||||||
|
|
||||||
|
# Apply mask to alpha channel
|
||||||
|
# Where mask is white (magenta pixels), alpha becomes 0
|
||||||
|
# But mask_inv is white for KEEP pixels.
|
||||||
|
# So we want Alpha to be 0 where mask is 255.
|
||||||
|
|
||||||
|
# Better approach:
|
||||||
|
# 1. Split channels
|
||||||
|
b, g, r, a = cv2.split(img)
|
||||||
|
|
||||||
|
# 2. Update alpha channel using the inverted mask
|
||||||
|
# If mask pixel is 255 (magenta), mask_inv is 0. We want alpha 0 there.
|
||||||
|
# If mask pixel is 0 (not magenta), mask_inv is 255. We want alpha 255 there.
|
||||||
|
# However, original alpha is 255 everywhere inside the image bounds.
|
||||||
|
# So we can just take bitwise_and or just set alpha to mask_inv.
|
||||||
|
|
||||||
|
img[:, :, 3] = mask_inv
|
||||||
|
|
||||||
|
# Optional: Basic edge smoothing/despiking if needed, but for pixel/vector art simple cut is often better.
|
||||||
|
|
||||||
|
# Save
|
||||||
|
cv2.imwrite(output_path, img)
|
||||||
|
print(f"Successfully saved transparent image to {output_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
remove_pink_bg()
|
||||||
71
scripts/process_hay.py
Normal file
71
scripts/process_hay.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
|
||||||
|
def process_hay():
|
||||||
|
# Paths
|
||||||
|
input_path = 'assets/references/proizvodnja_sena.png'
|
||||||
|
output_dir = 'assets/DEMO_FAZA1/Items/Hay'
|
||||||
|
|
||||||
|
if not os.path.exists(output_dir):
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
# Load image
|
||||||
|
img = cv2.imread(input_path)
|
||||||
|
if img is None:
|
||||||
|
print(f"Error: Could not load {input_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Convert to RGBA
|
||||||
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
|
||||||
|
|
||||||
|
# Define Green Key (assuming bright green background)
|
||||||
|
# Range for Chroma Key Green in HSV is best, but BGR is fine for simple flat green
|
||||||
|
# Pure Green in BGR is (0, 255, 0)
|
||||||
|
|
||||||
|
# Using HSV for better masking
|
||||||
|
hsv = cv2.cvtColor(img[:,:,:3], cv2.COLOR_BGR2HSV)
|
||||||
|
|
||||||
|
# Mask for Green (Hue 35-85 approx for standard green screen)
|
||||||
|
lower_green = np.array([35, 50, 50])
|
||||||
|
upper_green = np.array([85, 255, 255])
|
||||||
|
|
||||||
|
mask = cv2.inRange(hsv, lower_green, upper_green)
|
||||||
|
|
||||||
|
# Invert mask (we want non-green parts)
|
||||||
|
mask_inv = cv2.bitwise_not(mask)
|
||||||
|
|
||||||
|
# Set Alpha channel based on mask
|
||||||
|
# Everything green becomes transparent
|
||||||
|
img[:, :, 3] = mask_inv
|
||||||
|
|
||||||
|
# Find contours of objects
|
||||||
|
# Use the mask_inv (where 255 is the object)
|
||||||
|
contours, _ = cv2.findContours(mask_inv, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
|
print(f"Found {len(contours)} objects.")
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for cnt in contours:
|
||||||
|
x, y, w, h = cv2.boundingRect(cnt)
|
||||||
|
|
||||||
|
# Filter noise
|
||||||
|
if w < 10 or h < 10:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Crop
|
||||||
|
crop = img[y:y+h, x:x+w]
|
||||||
|
|
||||||
|
# Determine if it's a small pile or big bale based on size
|
||||||
|
# This is a heuristic. We'll label them by size.
|
||||||
|
area = w * h
|
||||||
|
label = "hay_piece"
|
||||||
|
|
||||||
|
# Save
|
||||||
|
output_filename = f"{output_dir}/hay_drop_{count}.png"
|
||||||
|
cv2.imwrite(output_filename, crop)
|
||||||
|
print(f"Saved {output_filename} (Size: {w}x{h})")
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
process_hay()
|
||||||
@@ -6,6 +6,13 @@ const config = {
|
|||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
backgroundColor: '#1a1a1a', // Temno siva, da slika izstopa
|
backgroundColor: '#1a1a1a', // Temno siva, da slika izstopa
|
||||||
parent: 'body',
|
parent: 'body',
|
||||||
|
physics: {
|
||||||
|
default: 'arcade',
|
||||||
|
arcade: {
|
||||||
|
debug: false,
|
||||||
|
gravity: { y: 0 }
|
||||||
|
}
|
||||||
|
},
|
||||||
scene: [GrassScene]
|
scene: [GrassScene]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,29 @@
|
|||||||
export default class GrassScene extends Phaser.Scene {
|
export default class GrassScene extends Phaser.Scene {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ key: 'GrassScene' });
|
super({ key: 'GrassScene' });
|
||||||
this.baseTime = 12; // Start at 12:00
|
this.baseTime = 12;
|
||||||
this.timeSpeed = 0.5;
|
this.timeSpeed = 0.5;
|
||||||
|
this.playerSpeed = 200;
|
||||||
|
this.hayCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
console.log("🌿 Loading Clean Assets...");
|
console.log("🌿 Loading Clean Assets...");
|
||||||
|
|
||||||
// 1. PATHS (Absolute from server root)
|
|
||||||
const PATHS = {
|
const PATHS = {
|
||||||
ground: '/assets/DEMO_FAZA1/Ground/',
|
ground: 'assets/DEMO_FAZA1/Ground/',
|
||||||
veg: '/assets/DEMO_FAZA1/Vegetation/'
|
veg: 'assets/DEMO_FAZA1/Vegetation/',
|
||||||
|
char: 'assets/references/',
|
||||||
|
items: 'assets/DEMO_FAZA1/Items/Hay/',
|
||||||
|
audio: 'assets/audio/_NEW/'
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. LOAD ASSETS
|
|
||||||
// Ground
|
|
||||||
this.load.image('ground_base', PATHS.ground + 'tla_trava_tekstura.png');
|
this.load.image('ground_base', PATHS.ground + 'tla_trava_tekstura.png');
|
||||||
|
|
||||||
// Vegetation - Grass
|
|
||||||
this.load.image('grass_tuft', PATHS.veg + 'trava_sop.png');
|
|
||||||
this.load.image('grass_tall', PATHS.veg + 'visoka_trava.png');
|
this.load.image('grass_tall', PATHS.veg + 'visoka_trava.png');
|
||||||
|
this.load.image('hay_drop', PATHS.items + 'hay_drop_0.png');
|
||||||
|
this.load.image('kai', PATHS.char + 'kaj.png');
|
||||||
|
this.load.audio('step_grass', PATHS.audio + 'footstep_grass_000.ogg');
|
||||||
|
|
||||||
// Vegetation - Trees (All phases)
|
|
||||||
this.load.image('tree_f1', PATHS.veg + 'drevo_faza_1.png'); // Kalcek
|
|
||||||
this.load.image('tree_f2', PATHS.veg + 'drevo_faza_2.png'); // Mlado
|
|
||||||
this.load.image('tree_small', PATHS.veg + 'drevo_majhno.png');
|
|
||||||
this.load.image('tree_medium', PATHS.veg + 'drevo_srednje.png');
|
|
||||||
this.load.image('tree_large', PATHS.veg + 'drevo_veliko.png'); // Hero
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
this.load.on('loaderror', (file) => {
|
this.load.on('loaderror', (file) => {
|
||||||
console.error('FAILED TO LOAD:', file.src);
|
console.error('FAILED TO LOAD:', file.src);
|
||||||
});
|
});
|
||||||
@@ -38,127 +32,185 @@ export default class GrassScene extends Phaser.Scene {
|
|||||||
create() {
|
create() {
|
||||||
const { width, height } = this.scale;
|
const { width, height } = this.scale;
|
||||||
|
|
||||||
// 1. BACKGROUND (Tiling Sprite - Base Layer)
|
// 1. BACKGROUND
|
||||||
this.ground = this.add.tileSprite(0, 0, width, height, 'ground_base')
|
this.ground = this.add.tileSprite(0, 0, width, height, 'ground_base')
|
||||||
.setOrigin(0, 0);
|
.setOrigin(0, 0);
|
||||||
|
|
||||||
// 2. VEGETATION GROUPS
|
// 2. GROUPS
|
||||||
this.grasses = [];
|
this.tallGrassGroup = this.physics.add.group();
|
||||||
this.trees = [];
|
this.hayGroup = this.physics.add.group();
|
||||||
|
|
||||||
// 3. GENERATE WORLD
|
this.allGrass = [];
|
||||||
this.generateGrass(width, height);
|
|
||||||
this.generateTrees(width, height);
|
|
||||||
|
|
||||||
// 4. DAY/NIGHT OVERLAY
|
// 3. GENERATE WORLD - ONLY GRASS
|
||||||
|
// "Vsa drevesa odstranjena. Samo trava ostane."
|
||||||
|
// "Nastavi neskončno travo čez celo mapo"
|
||||||
|
this.generateInfiniteGrass(width, height);
|
||||||
|
|
||||||
|
// 4. KAI (Player)
|
||||||
|
this.player = this.physics.add.sprite(width / 2, height / 2, 'kai');
|
||||||
|
this.player.setScale(0.5);
|
||||||
|
this.player.setOrigin(0.5, 0.92);
|
||||||
|
this.player.setCollideWorldBounds(true);
|
||||||
|
this.player.setDepth(this.player.y);
|
||||||
|
|
||||||
|
this.lastStepTime = 0;
|
||||||
|
|
||||||
|
// 5. INTERACTION LOGIC
|
||||||
|
this.physics.add.overlap(this.player, this.tallGrassGroup, this.handleMow, null, this);
|
||||||
|
this.physics.add.overlap(this.player, this.hayGroup, this.collectHay, null, this);
|
||||||
|
|
||||||
|
// Input
|
||||||
|
this.cursors = this.input.keyboard.createCursorKeys();
|
||||||
|
this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
|
||||||
|
|
||||||
|
// 6. AMNESIA FADE IN
|
||||||
|
this.cameras.main.fadeIn(3000, 0, 0, 0);
|
||||||
|
|
||||||
|
// DYNAMIC VIGNETTE
|
||||||
|
const canvasTexture = this.textures.createCanvas('vignette_tex', width, height);
|
||||||
|
const ctx = canvasTexture.context;
|
||||||
|
const grd = ctx.createRadialGradient(width / 2, height / 2, height * 0.3, width / 2, height / 2, height * 0.8);
|
||||||
|
grd.addColorStop(0, "rgba(0,0,0,0)");
|
||||||
|
grd.addColorStop(1, "rgba(0,0,0,0.9)");
|
||||||
|
|
||||||
|
ctx.fillStyle = grd;
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
canvasTexture.refresh();
|
||||||
|
|
||||||
|
this.vignette = this.add.image(width / 2, height / 2, 'vignette_tex')
|
||||||
|
.setScrollFactor(0)
|
||||||
|
.setAlpha(0)
|
||||||
|
.setDepth(8000);
|
||||||
|
|
||||||
|
// 7. DAY/NIGHT OVERLAY
|
||||||
this.dayNightOverlay = this.add.rectangle(0, 0, width, height, 0x000000, 0)
|
this.dayNightOverlay = this.add.rectangle(0, 0, width, height, 0x000000, 0)
|
||||||
.setOrigin(0, 0)
|
.setOrigin(0, 0)
|
||||||
.setDepth(9000); // Top layer
|
.setDepth(9000);
|
||||||
|
|
||||||
// 5. UI INFO
|
// 8. UI INFO
|
||||||
this.infoText = this.add.text(20, 20, 'Time: 12:00', {
|
this.infoText = this.add.text(20, 20, '', {
|
||||||
fontFamily: 'monospace', fontSize: '24px', fill: '#ffffff', stroke: '#000000', strokeThickness: 4
|
fontFamily: 'monospace', fontSize: '20px', fill: '#ffffff', stroke: '#000000', strokeThickness: 4
|
||||||
|
}).setDepth(10000);
|
||||||
|
|
||||||
|
this.inventoryText = this.add.text(width - 150, 20, 'Seno: 0', {
|
||||||
|
fontFamily: 'monospace', fontSize: '20px', fill: '#ffff00', stroke: '#000000', strokeThickness: 4
|
||||||
}).setDepth(10000);
|
}).setDepth(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateGrass(w, h) {
|
generateInfiniteGrass(w, h) {
|
||||||
// A) Small Tufts (Decoration) - High Density
|
// "Density = 1.0f" -> High density, cover everything.
|
||||||
// "Naključno razporedi trava_sop.png za vizualno gostoto"
|
// We'll use a grid loop with some randomization offset
|
||||||
for (let i = 0; i < 150; i++) {
|
// Grass scale is 0.2, original image is ~512?
|
||||||
let x = Phaser.Math.Between(0, w);
|
// 0.2 * 512 = ~100px width.
|
||||||
let y = Phaser.Math.Between(0, h);
|
// Step size ~50px for overlap.
|
||||||
|
|
||||||
let tuft = this.add.image(x, y, 'grass_tuft');
|
const stepX = 40;
|
||||||
tuft.setScale(0.3 + Math.random() * 0.2); // Random size
|
const stepY = 30;
|
||||||
tuft.setAlpha(0.9);
|
|
||||||
tuft.setDepth(y); // Simple Y-sort
|
|
||||||
}
|
|
||||||
|
|
||||||
// B) Tall Grass (Interactive) - Medium Density
|
for (let y = 0; y < h; y += stepY) {
|
||||||
// "Postavi visoka_trava.png ... dodaj nežen sinusni efekt"
|
for (let x = 0; x < w; x += stepX) {
|
||||||
for (let i = 0; i < 40; i++) {
|
// Add some randomness
|
||||||
let x = Phaser.Math.Between(50, w - 50);
|
let gx = x + Phaser.Math.Between(-10, 10);
|
||||||
let y = Phaser.Math.Between(50, h - 50);
|
let gy = y + Phaser.Math.Between(-10, 10);
|
||||||
|
|
||||||
let grass = this.add.image(x, y, 'grass_tall');
|
// create(x, y, key)
|
||||||
grass.setScale(0.4);
|
let grass = this.tallGrassGroup.create(gx, gy, 'grass_tall');
|
||||||
grass.setOrigin(0.5, 1); // Pivot at bottom
|
grass.setScale(0.2);
|
||||||
grass.setDepth(y);
|
grass.setOrigin(0.5, 0.95);
|
||||||
|
grass.setDepth(gy + 1);
|
||||||
|
|
||||||
// Custom properties for wind animation
|
grass.swaySpeed = 0.002 + Math.random() * 0.001;
|
||||||
grass.swaySpeed = 0.002 + Math.random() * 0.001;
|
grass.swayOffset = Math.random() * 100;
|
||||||
grass.swayOffset = Math.random() * 100;
|
|
||||||
|
|
||||||
this.grasses.push(grass);
|
this.allGrass.push(grass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateTrees(w, h) {
|
handleMow(player, grass) {
|
||||||
const treeTypes = ['tree_f1', 'tree_f2', 'tree_small', 'tree_medium', 'tree_large'];
|
if (this.spaceKey.isDown) {
|
||||||
|
let hay = this.hayGroup.create(grass.x, grass.y + 10, 'hay_drop');
|
||||||
for (let i = 0; i < 12; i++) {
|
hay.setScale(0.6);
|
||||||
let x = Phaser.Math.Between(50, w - 50);
|
hay.setDepth(1);
|
||||||
let y = Phaser.Math.Between(50, h - 50);
|
hay.setAngle(Phaser.Math.Between(-20, 20));
|
||||||
|
grass.destroy();
|
||||||
// Randomly pick a growth stage
|
const index = this.allGrass.indexOf(grass);
|
||||||
let type = Phaser.Math.RND.pick(treeTypes);
|
if (index > -1) this.allGrass.splice(index, 1);
|
||||||
|
|
||||||
let tree = this.add.image(x, y, type);
|
|
||||||
tree.setOrigin(0.5, 0.9); // Pivot near bottom
|
|
||||||
tree.setDepth(y); // Sort by Y
|
|
||||||
|
|
||||||
// Scale adjustments to look good
|
|
||||||
if (type === 'tree_large') tree.setScale(0.8);
|
|
||||||
else if (type === 'tree_medium') tree.setScale(0.7);
|
|
||||||
else tree.setScale(0.6);
|
|
||||||
|
|
||||||
// Subtle sway for trees too
|
|
||||||
tree.swaySpeed = 0.0005 + Math.random() * 0.0005;
|
|
||||||
tree.swayOffset = Math.random() * 100;
|
|
||||||
|
|
||||||
this.trees.push(tree);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collectHay(player, hay) {
|
||||||
|
hay.destroy();
|
||||||
|
this.hayCount++;
|
||||||
|
this.inventoryText.setText(`Seno: ${this.hayCount}`);
|
||||||
|
}
|
||||||
|
|
||||||
update(time, delta) {
|
update(time, delta) {
|
||||||
// 1. WIND ANIMATION
|
// MOVEMENT LOGIC
|
||||||
// "Dodaj nežen sinusni efekt za plapolanje"
|
let currentSpeed = this.playerSpeed;
|
||||||
this.grasses.forEach(g => {
|
let isMoving = false;
|
||||||
g.rotation = Math.sin((time * g.swaySpeed) + g.swayOffset) * 0.15; // Stronger sway for grass
|
|
||||||
});
|
|
||||||
|
|
||||||
this.trees.forEach(t => {
|
// HIDING CHECK (Always effectively in grass in this mode, but let's check overlap technically)
|
||||||
t.rotation = Math.sin((time * t.swaySpeed) + t.swayOffset) * 0.02; // Very subtle for trees
|
let inTallGrass = this.physics.overlap(this.player, this.tallGrassGroup);
|
||||||
});
|
|
||||||
|
if (inTallGrass) {
|
||||||
|
currentSpeed *= 0.6;
|
||||||
|
// "Player.CharacterAlpha = 0.4f"
|
||||||
|
this.player.setAlpha(0.4);
|
||||||
|
if (this.vignette.alpha < 1) this.vignette.alpha += 0.05;
|
||||||
|
} else {
|
||||||
|
// If he mows a clearing, he becomes visible
|
||||||
|
this.player.setAlpha(1.0);
|
||||||
|
if (this.vignette.alpha > 0) this.vignette.alpha -= 0.05;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.player.setVelocity(0);
|
||||||
|
if (this.cursors.left.isDown) {
|
||||||
|
this.player.setVelocityX(-currentSpeed);
|
||||||
|
this.player.setFlipX(true);
|
||||||
|
isMoving = true;
|
||||||
|
} else if (this.cursors.right.isDown) {
|
||||||
|
this.player.setVelocityX(currentSpeed);
|
||||||
|
this.player.setFlipX(false);
|
||||||
|
isMoving = true;
|
||||||
|
}
|
||||||
|
if (this.cursors.up.isDown) {
|
||||||
|
this.player.setVelocityY(-currentSpeed);
|
||||||
|
isMoving = true;
|
||||||
|
} else if (this.cursors.down.isDown) {
|
||||||
|
this.player.setVelocityY(currentSpeed);
|
||||||
|
isMoving = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AUDIO
|
||||||
|
if (isMoving && inTallGrass) {
|
||||||
|
if (time > this.lastStepTime + 350) {
|
||||||
|
this.sound.play('step_grass', { volume: 0.4 });
|
||||||
|
this.lastStepTime = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VISUALS
|
||||||
|
this.player.setDepth(this.player.y);
|
||||||
|
this.allGrass.forEach(g => { if (g.active) g.rotation = Math.sin((time * g.swaySpeed) + g.swayOffset) * 0.15; });
|
||||||
|
|
||||||
// 2. DAY/NIGHT CYCLE
|
|
||||||
this.baseTime += (delta * 0.001 * this.timeSpeed);
|
this.baseTime += (delta * 0.001 * this.timeSpeed);
|
||||||
if (this.baseTime >= 24) this.baseTime = 0;
|
if (this.baseTime >= 24) this.baseTime = 0;
|
||||||
this.updateLighting(this.baseTime);
|
this.updateLighting(this.baseTime);
|
||||||
|
|
||||||
// Update UI
|
|
||||||
let hour = Math.floor(this.baseTime);
|
let hour = Math.floor(this.baseTime);
|
||||||
let minute = Math.floor((this.baseTime % 1) * 60).toString().padStart(2, '0');
|
let minute = Math.floor((this.baseTime % 1) * 60).toString().padStart(2, '0');
|
||||||
this.infoText.setText(`Time: ${hour}:${minute}`);
|
this.infoText.setText(`Time: ${hour}:${minute}\n[Arrows] Premik\n[Space] Košnja`);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLighting(hour) {
|
updateLighting(hour) {
|
||||||
let alpha = 0;
|
let alpha = 0;
|
||||||
let color = 0x000000;
|
let color = 0x000000;
|
||||||
|
if (hour >= 20 || hour < 5) { alpha = 0.7; color = 0x000022; }
|
||||||
// Simple Day/Night Logic
|
else if (hour >= 5 && hour < 8) { alpha = 0.3; color = 0xFF4500; }
|
||||||
if (hour >= 20 || hour < 5) {
|
else if (hour >= 18 && hour < 20) { alpha = 0.4; color = 0xFF4500; }
|
||||||
alpha = 0.7; // Night
|
else { alpha = 0; }
|
||||||
color = 0x000022;
|
|
||||||
} else if (hour >= 5 && hour < 8) {
|
|
||||||
alpha = 0.3; // Dawn
|
|
||||||
color = 0xFF4500;
|
|
||||||
} else if (hour >= 18 && hour < 20) {
|
|
||||||
alpha = 0.4; // Dusk
|
|
||||||
color = 0xFF4500;
|
|
||||||
} else {
|
|
||||||
alpha = 0; // Day
|
|
||||||
}
|
|
||||||
this.dayNightOverlay.setFillStyle(color, alpha);
|
this.dayNightOverlay.setFillStyle(color, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user