mesano
This commit is contained in:
BIN
assets/elite_zombie.png
Normal file
BIN
assets/elite_zombie.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 304 KiB |
@@ -24,8 +24,17 @@ class NPC {
|
|||||||
this.pauseTime = 0;
|
this.pauseTime = 0;
|
||||||
this.maxPauseTime = 2000;
|
this.maxPauseTime = 2000;
|
||||||
this.attackCooldownTimer = 0;
|
this.attackCooldownTimer = 0;
|
||||||
this.hp = 20;
|
|
||||||
this.maxHp = 20;
|
// Elite Zombie Stats
|
||||||
|
if (type === 'elite_zombie') {
|
||||||
|
this.hp = 50; // 2.5x več HP
|
||||||
|
this.maxHp = 50;
|
||||||
|
this.moveSpeed = 150; // 50% hitrejši
|
||||||
|
this.gridMoveTime = 200; // Hitrejši premiki
|
||||||
|
} else {
|
||||||
|
this.hp = 20;
|
||||||
|
this.maxHp = 20;
|
||||||
|
}
|
||||||
|
|
||||||
// Kreira sprite
|
// Kreira sprite
|
||||||
this.createSprite();
|
this.createSprite();
|
||||||
@@ -128,6 +137,14 @@ class NPC {
|
|||||||
texKey = 'merchant_texture'; // Fallback na proceduralno
|
texKey = 'merchant_texture'; // Fallback na proceduralno
|
||||||
}
|
}
|
||||||
console.log('🏪 Creating MERCHANT NPC with texture:', texKey);
|
console.log('🏪 Creating MERCHANT NPC with texture:', texKey);
|
||||||
|
} else if (this.type === 'elite_zombie') {
|
||||||
|
// Elite Zombie sprite
|
||||||
|
if (this.scene.textures.exists('elite_zombie')) {
|
||||||
|
texKey = 'elite_zombie';
|
||||||
|
} else {
|
||||||
|
texKey = 'npc_elite_zombie'; // Fallback na proceduralno
|
||||||
|
}
|
||||||
|
console.log('👹 Creating ELITE ZOMBIE with texture:', texKey);
|
||||||
} else if (!this.scene.textures.exists(texKey)) {
|
} else if (!this.scene.textures.exists(texKey)) {
|
||||||
TextureGenerator.createNPCSprite(this.scene, texKey, this.type);
|
TextureGenerator.createNPCSprite(this.scene, texKey, this.type);
|
||||||
}
|
}
|
||||||
@@ -144,8 +161,10 @@ class NPC {
|
|||||||
if (isAnimated) {
|
if (isAnimated) {
|
||||||
this.sprite.setScale(1.5);
|
this.sprite.setScale(1.5);
|
||||||
} else {
|
} else {
|
||||||
// Merchant manjši, ostali večji
|
// Scale po tipu
|
||||||
const scale = (this.type === 'merchant') ? 0.2 : 0.5;
|
let scale = 0.5; // Default
|
||||||
|
if (this.type === 'merchant') scale = 0.2;
|
||||||
|
else if (this.type === 'elite_zombie') scale = 0.2; // Elite manjši
|
||||||
this.sprite.setScale(scale);
|
this.sprite.setScale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +225,27 @@ class NPC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(delta) {
|
update(delta) {
|
||||||
// 1. Distance Culling
|
// 1. Viewport Culling - če sprite ni viden na kameri, preskoči risanje
|
||||||
|
const camera = this.scene.cameras.main;
|
||||||
|
if (camera && this.sprite) {
|
||||||
|
const worldView = camera.worldView;
|
||||||
|
const isVisible = Phaser.Geom.Rectangle.Overlaps(worldView, this.sprite.getBounds());
|
||||||
|
|
||||||
|
if (!isVisible && this.sprite.visible) {
|
||||||
|
this.sprite.setVisible(false);
|
||||||
|
if (this.healthBar) {
|
||||||
|
this.healthBar.setVisible(false);
|
||||||
|
this.healthBarBg.setVisible(false);
|
||||||
|
}
|
||||||
|
if (this.eyesGroup) this.eyesGroup.setVisible(false);
|
||||||
|
return; // Preskoči AI če ni viden
|
||||||
|
} else if (isVisible && !this.sprite.visible) {
|
||||||
|
this.sprite.setVisible(true);
|
||||||
|
if (this.eyesGroup) this.eyesGroup.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Distance Culling
|
||||||
if (this.scene.player) {
|
if (this.scene.player) {
|
||||||
const playerPos = this.scene.player.getPosition();
|
const playerPos = this.scene.player.getPosition();
|
||||||
const dist = Phaser.Math.Distance.Between(this.gridX, this.gridY, playerPos.x, playerPos.y);
|
const dist = Phaser.Math.Distance.Between(this.gridX, this.gridY, playerPos.x, playerPos.y);
|
||||||
@@ -231,7 +270,7 @@ class NPC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Optimization: Update depth ONLY if moving
|
// 3. Optimization: Update depth ONLY if moving
|
||||||
if (this.isMoving) {
|
if (this.isMoving) {
|
||||||
this.updateDepth();
|
this.updateDepth();
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
@@ -544,19 +583,73 @@ class NPC {
|
|||||||
this.healthBar.fillRect(-16, -70, 32 * percent, 4);
|
this.healthBar.fillRect(-16, -70, 32 * percent, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WHITE FLASH Effect
|
||||||
if (this.sprite) {
|
if (this.sprite) {
|
||||||
this.sprite.setTint(0xff0000);
|
this.sprite.setTint(0xFFFFFF); // White flash
|
||||||
this.scene.time.delayedCall(100, () => {
|
this.scene.time.delayedCall(50, () => {
|
||||||
if (this.sprite) {
|
if (this.sprite) {
|
||||||
this.sprite.clearTint();
|
this.sprite.setTint(0xFF0000); // Red flash
|
||||||
// Re-apply tamed tint if tamed
|
this.scene.time.delayedCall(50, () => {
|
||||||
if (this.state === 'TAMED' || this.state === 'FOLLOW' || this.state === 'STAY') {
|
if (this.sprite) {
|
||||||
this.sprite.setTint(0xAAFFAA);
|
this.sprite.clearTint();
|
||||||
}
|
if (this.state === 'TAMED' || this.state === 'FOLLOW' || this.state === 'STAY') {
|
||||||
|
this.sprite.setTint(0xAAFFAA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KNOCKBACK Effect
|
||||||
|
if (this.sprite && this.scene.player) {
|
||||||
|
const knockbackDist = 10;
|
||||||
|
const angle = Phaser.Math.Angle.Between(
|
||||||
|
this.scene.player.sprite.x,
|
||||||
|
this.scene.player.sprite.y,
|
||||||
|
this.sprite.x,
|
||||||
|
this.sprite.y
|
||||||
|
);
|
||||||
|
const knockX = Math.cos(angle) * knockbackDist;
|
||||||
|
const knockY = Math.sin(angle) * knockbackDist;
|
||||||
|
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: this.sprite,
|
||||||
|
x: this.sprite.x + knockX,
|
||||||
|
y: this.sprite.y + knockY,
|
||||||
|
duration: 100,
|
||||||
|
yoyo: true,
|
||||||
|
ease: 'Quad.easeOut'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLOATING DAMAGE NUMBERS
|
||||||
|
if (this.sprite) {
|
||||||
|
const dmgTxt = this.scene.add.text(
|
||||||
|
this.sprite.x,
|
||||||
|
this.sprite.y - 40,
|
||||||
|
`-${amount}`,
|
||||||
|
{
|
||||||
|
fontSize: '16px',
|
||||||
|
fontFamily: 'Courier New',
|
||||||
|
color: '#FF0000',
|
||||||
|
stroke: '#000',
|
||||||
|
strokeThickness: 3,
|
||||||
|
fontStyle: 'bold'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
dmgTxt.setOrigin(0.5);
|
||||||
|
dmgTxt.setDepth(999999);
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: dmgTxt,
|
||||||
|
y: dmgTxt.y - 30,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 800,
|
||||||
|
ease: 'Cubic.easeOut',
|
||||||
|
onComplete: () => dmgTxt.destroy()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`Zombie HP: ${this.hp}`);
|
console.log(`Zombie HP: ${this.hp}`);
|
||||||
|
|
||||||
if (this.hp <= 0) {
|
if (this.hp <= 0) {
|
||||||
@@ -572,12 +665,19 @@ class NPC {
|
|||||||
this.scene.soundManager.playDeath();
|
this.scene.soundManager.playDeath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn loot - BONE
|
// Spawn loot - Elite zombies drop better items!
|
||||||
if (this.scene.lootSystem) {
|
if (this.scene.lootSystem) {
|
||||||
this.scene.lootSystem.spawnLoot(this.gridX, this.gridY, 'item_bone');
|
const lootItem = (this.type === 'elite_zombie') ? 'item_scrap' : 'item_bone';
|
||||||
|
this.scene.lootSystem.spawnLoot(this.gridX, this.gridY, lootItem);
|
||||||
|
|
||||||
|
// Elite bonus: 50% chance for extra chip
|
||||||
|
if (this.type === 'elite_zombie' && Math.random() < 0.5) {
|
||||||
|
this.scene.lootSystem.spawnLoot(this.gridX, this.gridY, 'item_chip');
|
||||||
|
}
|
||||||
} else if (this.scene.interactionSystem && this.scene.interactionSystem.spawnLoot) {
|
} else if (this.scene.interactionSystem && this.scene.interactionSystem.spawnLoot) {
|
||||||
// Fallback
|
// Fallback
|
||||||
this.scene.interactionSystem.spawnLoot(this.gridX, this.gridY, 'item_bone');
|
const lootItem = (this.type === 'elite_zombie') ? 'item_scrap' : 'item_bone';
|
||||||
|
this.scene.interactionSystem.spawnLoot(this.gridX, this.gridY, lootItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quest Tracking (Kill)
|
// Quest Tracking (Kill)
|
||||||
|
|||||||
@@ -340,14 +340,56 @@ class Player {
|
|||||||
const tile = terrainSystem.tiles[targetY][targetX];
|
const tile = terrainSystem.tiles[targetY][targetX];
|
||||||
let isPassable = true;
|
let isPassable = true;
|
||||||
|
|
||||||
if (tile.type.name === 'water') isPassable = false;
|
// TILE COLLISION - Preveri solid property PRVO
|
||||||
|
if (tile.solid === true) {
|
||||||
|
console.log('⛔ Blocked by solid tile property');
|
||||||
|
isPassable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nato preveri tip (fallback)
|
||||||
|
const solidTileTypes = [
|
||||||
|
'water', // Voda
|
||||||
|
'MINE_WALL', // Rudniški zidovi
|
||||||
|
'WALL_EDGE', // Robovi zidov (DODANO)
|
||||||
|
'ORE_STONE', // Kamnita ruda (dokler ni izkopana)
|
||||||
|
'ORE_IRON', // Železna ruda
|
||||||
|
'lava', // Lava (če bo dodana)
|
||||||
|
'void' // Praznina
|
||||||
|
// Opomba: PAVEMENT je WALKABLE (igralec lahko hodi po cesti)
|
||||||
|
];
|
||||||
|
|
||||||
|
const tileName = tile.type.name || tile.type;
|
||||||
|
if (isPassable && solidTileTypes.includes(tileName)) {
|
||||||
|
console.log('⛔ Blocked by solid tile:', tileName);
|
||||||
|
isPassable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DECORATION COLLISION - Trdni objekti
|
||||||
const key = `${targetX},${targetY}`;
|
const key = `${targetX},${targetY}`;
|
||||||
if (terrainSystem.decorationsMap.has(key)) {
|
if (terrainSystem.decorationsMap.has(key)) {
|
||||||
const decor = terrainSystem.decorationsMap.get(key);
|
const decor = terrainSystem.decorationsMap.get(key);
|
||||||
const solidTypes = ['tree', 'stone', 'bush', 'wall', 'ruin', 'fence', 'house', 'gravestone'];
|
// Vsi trdni objekti - igralec ne more hoditi skozi njih
|
||||||
if (solidTypes.includes(decor.type)) {
|
const solidTypes = [
|
||||||
console.log('⛔ Blocked by:', decor.type);
|
'tree', 'stone', 'bush', 'wall', 'ruin', 'fence', 'house', 'gravestone',
|
||||||
|
'rock_asset', 'rock_1', 'rock_2', 'rock_small', // Kamni
|
||||||
|
'tree_green_final', 'tree_blue_final', 'tree_dead_final', // Drevesa
|
||||||
|
'chest', 'spawner', // City struktury
|
||||||
|
'signpost_city', 'signpost_farm', 'signpost_both', // Signposti
|
||||||
|
'arena' // Arena
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check ali je tip solid - case-insensitive
|
||||||
|
const typeLower = (decor.type || '').toLowerCase();
|
||||||
|
const isSolid = solidTypes.includes(decor.type) ||
|
||||||
|
typeLower.includes('tree') ||
|
||||||
|
typeLower.includes('sapling') ||
|
||||||
|
typeLower.includes('rock') ||
|
||||||
|
typeLower.includes('stone') ||
|
||||||
|
typeLower.includes('signpost') ||
|
||||||
|
typeLower.includes('hill');
|
||||||
|
|
||||||
|
if (isSolid) {
|
||||||
|
console.log('⛔ BLOCKED:', decor.type);
|
||||||
isPassable = false;
|
isPassable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,16 +439,18 @@ class Player {
|
|||||||
|
|
||||||
updatePosition() {
|
updatePosition() {
|
||||||
const screenPos = this.iso.toScreen(this.gridX, this.gridY);
|
const screenPos = this.iso.toScreen(this.gridX, this.gridY);
|
||||||
this.sprite.setPosition(
|
|
||||||
screenPos.x + this.offsetX,
|
// Pixel-perfect positioning
|
||||||
screenPos.y + this.offsetY
|
const x = Math.round(screenPos.x + this.offsetX);
|
||||||
);
|
const y = Math.round(screenPos.y + this.offsetY);
|
||||||
|
|
||||||
|
this.sprite.setPosition(x, y);
|
||||||
|
|
||||||
const facingRight = !this.sprite.flipX;
|
const facingRight = !this.sprite.flipX;
|
||||||
const handOffset = facingRight ? 10 : -10;
|
const handOffset = facingRight ? 10 : -10;
|
||||||
this.handSprite.setPosition(
|
this.handSprite.setPosition(
|
||||||
screenPos.x + this.offsetX + handOffset,
|
Math.round(x + handOffset),
|
||||||
screenPos.y + this.offsetY - 15
|
Math.round(y - 15)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.updateDepth();
|
this.updateDepth();
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ const config = {
|
|||||||
roundPixels: true,
|
roundPixels: true,
|
||||||
transparent: false,
|
transparent: false,
|
||||||
clearBeforeRender: true,
|
clearBeforeRender: true,
|
||||||
powerPreference: 'high-performance'
|
powerPreference: 'high-performance',
|
||||||
|
// Eksplicitna NEAREST_NEIGHBOR filtracija
|
||||||
|
mipmapFilter: 'LINEAR_MIPMAP_LINEAR',
|
||||||
|
batchSize: 4096
|
||||||
},
|
},
|
||||||
physics: {
|
physics: {
|
||||||
default: 'arcade',
|
default: 'arcade',
|
||||||
@@ -70,6 +73,10 @@ const config = {
|
|||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
gamepad: true
|
gamepad: true
|
||||||
|
},
|
||||||
|
fps: {
|
||||||
|
target: 60,
|
||||||
|
forceSetTimeOut: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class BootScene extends Phaser.Scene {
|
|||||||
|
|
||||||
create() {
|
create() {
|
||||||
console.log('✅ BootScene: Complete!');
|
console.log('✅ BootScene: Complete!');
|
||||||
|
console.log('🎨 Pixel Art Mode: pixelArt=true, roundPixels=true (NEAREST filtering)');
|
||||||
window.gameState.currentScene = 'BootScene';
|
window.gameState.currentScene = 'BootScene';
|
||||||
|
|
||||||
// Global Constants for Sprites
|
// Global Constants for Sprites
|
||||||
|
|||||||
@@ -50,10 +50,57 @@ class GameScene extends Phaser.Scene {
|
|||||||
// Initial force update to render active tiles before first frame
|
// Initial force update to render active tiles before first frame
|
||||||
this.terrainSystem.updateCulling(this.cameras.main);
|
this.terrainSystem.updateCulling(this.cameras.main);
|
||||||
|
|
||||||
// FAZA 14: Spawn Ruin (Town Project) at fixed location near player
|
// INITIALIZE FARM AREA (Starter Zone @ 20,20)
|
||||||
console.log('🏚️ Spawning Ruin & Arena...');
|
this.initializeFarmWorld();
|
||||||
|
|
||||||
|
// CITY CONTENT: Ruins, Chests, Spawners
|
||||||
|
console.log('🏚️ Generating City Content...');
|
||||||
|
|
||||||
|
// Main Ruins
|
||||||
this.terrainSystem.placeStructure(55, 55, 'ruin');
|
this.terrainSystem.placeStructure(55, 55, 'ruin');
|
||||||
this.terrainSystem.placeStructure(75, 75, 'arena');
|
this.terrainSystem.placeStructure(65, 65, 'ruin');
|
||||||
|
this.terrainSystem.placeStructure(75, 75, 'ruin');
|
||||||
|
this.terrainSystem.placeStructure(60, 70, 'ruin');
|
||||||
|
this.terrainSystem.placeStructure(70, 60, 'ruin');
|
||||||
|
|
||||||
|
// Arena (Boss battle area)
|
||||||
|
this.terrainSystem.placeStructure(75, 55, 'arena');
|
||||||
|
|
||||||
|
// Treasure Chests (scattered in ruins)
|
||||||
|
this.terrainSystem.placeStructure(56, 56, 'chest');
|
||||||
|
this.terrainSystem.placeStructure(66, 66, 'chest');
|
||||||
|
this.terrainSystem.placeStructure(76, 76, 'chest');
|
||||||
|
|
||||||
|
// Zombie Spawners (in city)
|
||||||
|
this.terrainSystem.placeStructure(58, 68, 'spawner');
|
||||||
|
this.terrainSystem.placeStructure(72, 62, 'spawner');
|
||||||
|
|
||||||
|
// CESTE (ROADS) - povezava med farmo (20,20) in mestom (65,65)
|
||||||
|
console.log('🛣️ Building Roads...');
|
||||||
|
// Horizontalna cesta od farme
|
||||||
|
for (let x = 20; x <= 45; x++) {
|
||||||
|
this.terrainSystem.tiles[22][x].type = 'pavement';
|
||||||
|
this.terrainSystem.tiles[22][x].sprite.setTexture('dirt'); // Uporablj dirt kot "cesto"
|
||||||
|
this.terrainSystem.tiles[22][x].sprite.setTint(0x808080); // Siva barva
|
||||||
|
}
|
||||||
|
// Vertikalna cesta do mesta
|
||||||
|
for (let y = 22; y <= 65; y++) {
|
||||||
|
this.terrainSystem.tiles[y][45].type = 'pavement';
|
||||||
|
this.terrainSystem.tiles[y][45].sprite.setTexture('dirt');
|
||||||
|
this.terrainSystem.tiles[y][45].sprite.setTint(0x808080);
|
||||||
|
}
|
||||||
|
// Horizontalna cesta do mesta
|
||||||
|
for (let x = 45; x <= 65; x++) {
|
||||||
|
this.terrainSystem.tiles[65][x].type = 'pavement';
|
||||||
|
this.terrainSystem.tiles[65][x].sprite.setTexture('dirt');
|
||||||
|
this.terrainSystem.tiles[65][x].sprite.setTint(0x808080);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIGNPOSTS - navigacijske table
|
||||||
|
console.log('🪧 Placing Signposts...');
|
||||||
|
this.terrainSystem.placeStructure(23, 22, 'signpost_city'); // Pri farmi "→ City"
|
||||||
|
this.terrainSystem.placeStructure(65, 64, 'signpost_farm'); // Pri mestu "← Farm"
|
||||||
|
this.terrainSystem.placeStructure(45, 40, 'signpost_both'); // Na križišču
|
||||||
|
|
||||||
// Initialize Pathfinding (Worker)
|
// Initialize Pathfinding (Worker)
|
||||||
console.log('🗺️ Initializing Pathfinding...');
|
console.log('🗺️ Initializing Pathfinding...');
|
||||||
@@ -87,8 +134,17 @@ class GameScene extends Phaser.Scene {
|
|||||||
this.npcs.push(zombie);
|
this.npcs.push(zombie);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kamera sledi igralcu
|
// ELITE ZOMBIES v City območju (65,65 ± 15)
|
||||||
this.cameras.main.startFollow(this.player.sprite, true, 1.0, 1.0);
|
console.log('👹 Spawning ELITE ZOMBIES in City...');
|
||||||
|
for (let i = 0; i < 15; i++) { // Veliko elite zombijev!
|
||||||
|
const randomX = Phaser.Math.Between(50, 80); // City area
|
||||||
|
const randomY = Phaser.Math.Between(50, 80);
|
||||||
|
const elite = new NPC(this, randomX, randomY, this.terrainOffsetX, this.terrainOffsetY, 'elite_zombie');
|
||||||
|
this.npcs.push(elite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kamera sledi igralcu z gladko interpolacijo (lerp 0.1)
|
||||||
|
this.cameras.main.startFollow(this.player.sprite, true, 0.1, 0.1);
|
||||||
|
|
||||||
// Nastavi deadzone (100px border)
|
// Nastavi deadzone (100px border)
|
||||||
this.cameras.main.setDeadzone(100, 100);
|
this.cameras.main.setDeadzone(100, 100);
|
||||||
@@ -381,4 +437,46 @@ class GameScene extends Phaser.Scene {
|
|||||||
loadGame() {
|
loadGame() {
|
||||||
if (this.saveSystem) this.saveSystem.loadGame();
|
if (this.saveSystem) this.saveSystem.loadGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializeFarmWorld() {
|
||||||
|
console.log('🌾 Initializing Farm Area (Starter Zone)...');
|
||||||
|
|
||||||
|
const farmX = 20; // Farm center
|
||||||
|
const farmY = 20;
|
||||||
|
const farmRadius = 8;
|
||||||
|
|
||||||
|
// 1. Clear farm area (odstrani drevesa in kamne)
|
||||||
|
for (let x = farmX - farmRadius; x <= farmX + farmRadius; x++) {
|
||||||
|
for (let y = farmY - farmRadius; y <= farmY + farmRadius; y++) {
|
||||||
|
if (x >= 0 && x < 100 && y >= 0 && y < 100) {
|
||||||
|
const key = `${x},${y}`;
|
||||||
|
// Remove trees and rocks in farm area
|
||||||
|
if (this.terrainSystem.decorationsMap.has(key)) {
|
||||||
|
this.terrainSystem.removeDecoration(x, y);
|
||||||
|
}
|
||||||
|
// Make it grass or dirt
|
||||||
|
this.terrainSystem.tiles[y][x].type = { name: 'grass', color: 0x228B22 };
|
||||||
|
this.terrainSystem.tiles[y][x].sprite.setTint(0x228B22);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Place starter resources (chest s semeni)
|
||||||
|
this.terrainSystem.placeStructure(farmX + 3, farmY + 3, 'chest');
|
||||||
|
|
||||||
|
// 3. Place fence around farm (optional)
|
||||||
|
const fencePositions = [
|
||||||
|
[farmX - farmRadius, farmY - farmRadius],
|
||||||
|
[farmX + farmRadius, farmY - farmRadius],
|
||||||
|
[farmX - farmRadius, farmY + farmRadius],
|
||||||
|
[farmX + farmRadius, farmY + farmRadius]
|
||||||
|
];
|
||||||
|
fencePositions.forEach(([fx, fy]) => {
|
||||||
|
if (fx >= 0 && fx < 100 && fy >= 0 && fy < 100) {
|
||||||
|
this.terrainSystem.placeStructure(fx, fy, 'fence');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ Farm Area Initialized at (20,20)');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
this.load.image('tree_dead_new', 'assets/tree_dead_new.png');
|
this.load.image('tree_dead_new', 'assets/tree_dead_new.png');
|
||||||
this.load.image('flowers_new', 'assets/flowers_new.png');
|
this.load.image('flowers_new', 'assets/flowers_new.png');
|
||||||
this.load.image('merchant_new', 'assets/merchant_new.png');
|
this.load.image('merchant_new', 'assets/merchant_new.png');
|
||||||
|
this.load.image('elite_zombie', 'assets/elite_zombie.png');
|
||||||
this.load.image('hill_sprite', 'assets/hill_sprite.png');
|
this.load.image('hill_sprite', 'assets/hill_sprite.png');
|
||||||
this.load.image('fence', 'assets/fence.png');
|
this.load.image('fence', 'assets/fence.png');
|
||||||
this.load.image('gravestone', 'assets/gravestone.png');
|
this.load.image('gravestone', 'assets/gravestone.png');
|
||||||
@@ -123,6 +124,7 @@ class PreloadScene extends Phaser.Scene {
|
|||||||
'rock_2',
|
'rock_2',
|
||||||
'rock_small',
|
'rock_small',
|
||||||
'merchant_new',
|
'merchant_new',
|
||||||
|
'elite_zombie',
|
||||||
'tree_dead_new',
|
'tree_dead_new',
|
||||||
'flowers_new',
|
'flowers_new',
|
||||||
'hill_sprite',
|
'hill_sprite',
|
||||||
|
|||||||
@@ -125,6 +125,34 @@ class InteractionSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3.5 Try Opening Chest
|
||||||
|
if (!isAttack && this.scene.terrainSystem) {
|
||||||
|
const decorKey = `${gridX},${gridY}`;
|
||||||
|
const decor = this.scene.terrainSystem.decorationsMap.get(decorKey);
|
||||||
|
|
||||||
|
if (decor && decor.type === 'chest') {
|
||||||
|
// Open chest and spawn loot!
|
||||||
|
console.log('📦 Opening treasure chest!');
|
||||||
|
|
||||||
|
// Random loot
|
||||||
|
const lootTable = ['item_scrap', 'item_chip', 'item_wood', 'item_stone', 'item_bone'];
|
||||||
|
const lootCount = Phaser.Math.Between(2, 4);
|
||||||
|
|
||||||
|
for (let i = 0; i < lootCount; i++) {
|
||||||
|
const randomLoot = Phaser.Utils.Array.GetRandom(lootTable);
|
||||||
|
if (this.scene.lootSystem) {
|
||||||
|
const offsetX = Phaser.Math.Between(-1, 1);
|
||||||
|
const offsetY = Phaser.Math.Between(-1, 1);
|
||||||
|
this.scene.lootSystem.spawnLoot(gridX + offsetX, gridY + offsetY, randomLoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove chest
|
||||||
|
this.scene.terrainSystem.removeDecoration(gridX, gridY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Try Planting Tree (Manual Planting)
|
// 4. Try Planting Tree (Manual Planting)
|
||||||
if (!isAttack && (activeTool === 'tree_sapling' || activeTool === 'item_sapling')) {
|
if (!isAttack && (activeTool === 'tree_sapling' || activeTool === 'item_sapling')) {
|
||||||
const tile = this.scene.terrainSystem.getTile(gridX, gridY);
|
const tile = this.scene.terrainSystem.getTile(gridX, gridY);
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class TerrainSystem {
|
|||||||
STONE: { name: 'stone', height: 0.7, color: 0x888888 },
|
STONE: { name: 'stone', height: 0.7, color: 0x888888 },
|
||||||
PAVEMENT: { name: 'pavement', height: 0.6, color: 0x777777 },
|
PAVEMENT: { name: 'pavement', height: 0.6, color: 0x777777 },
|
||||||
RUINS: { name: 'ruins', height: 0.6, color: 0x555555 },
|
RUINS: { name: 'ruins', height: 0.6, color: 0x555555 },
|
||||||
|
WALL_EDGE: { name: 'WALL_EDGE', height: 0.8, color: 0x505050, solid: true }, // OBZIDJE
|
||||||
PATH: { name: 'path', height: -1, color: 0xc2b280 },
|
PATH: { name: 'path', height: -1, color: 0xc2b280 },
|
||||||
FARMLAND: { name: 'farmland', height: -1, color: 0x5c4033 },
|
FARMLAND: { name: 'farmland', height: -1, color: 0x5c4033 },
|
||||||
// MINE TYPES
|
// MINE TYPES
|
||||||
@@ -222,11 +223,26 @@ class TerrainSystem {
|
|||||||
if (Math.abs(x - FARM_CENTER_X) <= FARM_SIZE / 2 && Math.abs(y - FARM_CENTER_Y) <= FARM_SIZE / 2) {
|
if (Math.abs(x - FARM_CENTER_X) <= FARM_SIZE / 2 && Math.abs(y - FARM_CENTER_Y) <= FARM_SIZE / 2) {
|
||||||
terrainType = this.terrainTypes.DIRT;
|
terrainType = this.terrainTypes.DIRT;
|
||||||
}
|
}
|
||||||
|
// CITY AREA - 15x15 območje z OBZIDJEM
|
||||||
if (x >= CITY_START_X && x < CITY_START_X + CITY_SIZE &&
|
if (x >= CITY_START_X && x < CITY_START_X + CITY_SIZE &&
|
||||||
y >= CITY_START_Y && y < CITY_START_Y + CITY_SIZE) {
|
y >= CITY_START_Y && y < CITY_START_Y + CITY_SIZE) {
|
||||||
terrainType = this.terrainTypes.PAVEMENT;
|
|
||||||
if (Math.random() < 0.2) {
|
// Preverimo, ali smo na ROBOVIH (Obzidje)
|
||||||
terrainType = this.terrainTypes.RUINS;
|
const isEdge = (x === CITY_START_X ||
|
||||||
|
x === CITY_START_X + CITY_SIZE - 1 ||
|
||||||
|
y === CITY_START_Y ||
|
||||||
|
y === CITY_START_Y + CITY_SIZE - 1);
|
||||||
|
|
||||||
|
if (isEdge) {
|
||||||
|
// OBZIDJE - trdno, igralec ne more čez
|
||||||
|
terrainType = { name: 'WALL_EDGE', color: 0x505050, solid: true };
|
||||||
|
} else {
|
||||||
|
// NOTRANJOST MESTA - tlakovci (pavement)
|
||||||
|
terrainType = this.terrainTypes.PAVEMENT;
|
||||||
|
// Naključne ruševine v mestu
|
||||||
|
if (Math.random() < 0.15) {
|
||||||
|
terrainType = this.terrainTypes.RUINS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +250,8 @@ class TerrainSystem {
|
|||||||
type: terrainType.name,
|
type: terrainType.name,
|
||||||
texture: terrainType.name,
|
texture: terrainType.name,
|
||||||
hasDecoration: false,
|
hasDecoration: false,
|
||||||
hasCrop: false
|
hasCrop: false,
|
||||||
|
solid: terrainType.solid || false // Inherits from terrain type
|
||||||
};
|
};
|
||||||
|
|
||||||
// Place Trees dynamically during generation
|
// Place Trees dynamically during generation
|
||||||
@@ -712,4 +729,45 @@ class TerrainSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
placeStructure(x, y, type) {
|
||||||
|
// Generate textures if needed
|
||||||
|
if (type === 'chest' && !this.scene.textures.exists('chest')) {
|
||||||
|
TextureGenerator.createChestSprite(this.scene, 'chest');
|
||||||
|
}
|
||||||
|
if (type === 'spawner' && !this.scene.textures.exists('spawner')) {
|
||||||
|
TextureGenerator.createSpawnerSprite(this.scene, 'spawner');
|
||||||
|
}
|
||||||
|
if (type === 'ruin' && !this.scene.textures.exists('ruin')) {
|
||||||
|
TextureGenerator.createStructureSprite(this.scene, 'ruin', 'ruin');
|
||||||
|
}
|
||||||
|
if (type === 'arena' && !this.scene.exists('arena')) {
|
||||||
|
// Arena uses ruin texture for now
|
||||||
|
TextureGenerator.createStructureSprite(this.scene, 'arena', 'ruin');
|
||||||
|
}
|
||||||
|
if (type.startsWith('signpost')) {
|
||||||
|
const textMap = { 'signpost_city': '→', 'signpost_farm': '←', 'signpost_both': '⇅' };
|
||||||
|
const text = textMap[type] || '?';
|
||||||
|
if (!this.scene.textures.exists(type)) {
|
||||||
|
TextureGenerator.createSignpostSprite(this.scene, type, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place as decoration
|
||||||
|
this.addDecoration(x, y, type);
|
||||||
|
console.log(`🏛️ Place ${type} at (${x}, ${y})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSolid(x, y, isSolid) {
|
||||||
|
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
|
||||||
|
this.tiles[y][x].solid = isSolid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isSolid(x, y) {
|
||||||
|
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
|
||||||
|
return this.tiles[y][x].solid || false;
|
||||||
|
}
|
||||||
|
return true; // Out of bounds = solid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,13 +53,24 @@ class TextureGenerator {
|
|||||||
ctx.clearRect(0, 0, 32, 32);
|
ctx.clearRect(0, 0, 32, 32);
|
||||||
|
|
||||||
|
|
||||||
ctx.fillStyle = (type === 'zombie') ? '#556B2F' : '#DEB887'; // OliveDrab or Burlywood
|
// Elite Zombie: Red body, glowing pink eyes
|
||||||
ctx.fillRect(8, 6, 16, 26); // Body
|
if (type === 'elite_zombie') {
|
||||||
|
ctx.fillStyle = '#8B0000'; // DarkRed
|
||||||
|
ctx.fillRect(8, 6, 16, 26);
|
||||||
|
|
||||||
// Eyes (Red for zombie)
|
// Glowing Eyes
|
||||||
ctx.fillStyle = (type === 'zombie') ? '#FF0000' : '#000000';
|
ctx.fillStyle = '#FF1493'; // Deep Pink (glow)
|
||||||
ctx.fillRect(10, 10, 4, 4);
|
ctx.fillRect(10, 10, 4, 4);
|
||||||
ctx.fillRect(20, 10, 4, 4);
|
ctx.fillRect(20, 10, 4, 4);
|
||||||
|
} else {
|
||||||
|
ctx.fillStyle = (type === 'zombie') ? '#556B2F' : '#DEB887';
|
||||||
|
ctx.fillRect(8, 6, 16, 26);
|
||||||
|
|
||||||
|
// Eyes (Red for zombie)
|
||||||
|
ctx.fillStyle = (type === 'zombie') ? '#FF0000' : '#000000';
|
||||||
|
ctx.fillRect(10, 10, 4, 4);
|
||||||
|
ctx.fillRect(20, 10, 4, 4);
|
||||||
|
}
|
||||||
|
|
||||||
canvas.refresh();
|
canvas.refresh();
|
||||||
}
|
}
|
||||||
@@ -300,6 +311,78 @@ class TextureGenerator {
|
|||||||
canvas.refresh();
|
canvas.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static createChestSprite(scene, key = 'chest') {
|
||||||
|
if (scene.textures.exists(key)) return;
|
||||||
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
||||||
|
const ctx = canvas.getContext();
|
||||||
|
ctx.clearRect(0, 0, 32, 32);
|
||||||
|
|
||||||
|
// Chest body (brown)
|
||||||
|
ctx.fillStyle = '#8B4513'; // SaddleBrown
|
||||||
|
ctx.fillRect(6, 12, 20, 16);
|
||||||
|
|
||||||
|
// Lock (gold)
|
||||||
|
ctx.fillStyle = '#FFD700';
|
||||||
|
ctx.fillRect(14, 18, 4, 6);
|
||||||
|
|
||||||
|
// Lid
|
||||||
|
ctx.fillStyle = '#A0522D';
|
||||||
|
ctx.fillRect(6, 8, 20, 4);
|
||||||
|
|
||||||
|
canvas.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static createSpawnerSprite(scene, key = 'spawner') {
|
||||||
|
if (scene.textures.exists(key)) return;
|
||||||
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
||||||
|
const ctx = canvas.getContext();
|
||||||
|
ctx.clearRect(0, 0, 32, 32);
|
||||||
|
|
||||||
|
// Dark portal/spawner
|
||||||
|
ctx.fillStyle = '#2F4F4F'; // DarkSlateGray
|
||||||
|
ctx.fillRect(8, 8, 16, 16);
|
||||||
|
|
||||||
|
// Red glow
|
||||||
|
ctx.fillStyle = '#8B0000';
|
||||||
|
ctx.fillRect(10, 10, 12, 12);
|
||||||
|
|
||||||
|
// Center black hole
|
||||||
|
ctx.fillStyle = '#000000';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(16, 16, 4, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
canvas.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static createSignpostSprite(scene, key = 'signpost', text = '→') {
|
||||||
|
if (scene.textures.exists(key)) return;
|
||||||
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
||||||
|
const ctx = canvas.getContext();
|
||||||
|
ctx.clearRect(0, 0, 32, 32);
|
||||||
|
|
||||||
|
// Wooden post (brown)
|
||||||
|
ctx.fillStyle = '#8B4513';
|
||||||
|
ctx.fillRect(14, 8, 4, 24);
|
||||||
|
|
||||||
|
// Sign board
|
||||||
|
ctx.fillStyle = '#D2691E';
|
||||||
|
ctx.fillRect(6, 10, 20, 12);
|
||||||
|
|
||||||
|
// Border
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeRect(6, 10, 20, 12);
|
||||||
|
|
||||||
|
// Text
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.font = 'bold 14px Courier';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(text, 16, 19);
|
||||||
|
|
||||||
|
canvas.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
static createToolSprites(scene) {
|
static createToolSprites(scene) {
|
||||||
// Placeholder tool generation
|
// Placeholder tool generation
|
||||||
if (!scene.textures.exists('item_axe')) {
|
if (!scene.textures.exists('item_axe')) {
|
||||||
@@ -342,7 +425,9 @@ class TextureGenerator {
|
|||||||
{ name: 'stone', color: '#808080' }, // Siva
|
{ name: 'stone', color: '#808080' }, // Siva
|
||||||
{ name: 'seeds', color: '#90EE90' }, // Svetlo zelena
|
{ name: 'seeds', color: '#90EE90' }, // Svetlo zelena
|
||||||
{ name: 'wheat', color: '#FFD700' }, // Zlata
|
{ name: 'wheat', color: '#FFD700' }, // Zlata
|
||||||
{ name: 'item_bone', color: '#F5F5DC' } // Beige
|
{ name: 'item_bone', color: '#F5F5DC' }, // Beige
|
||||||
|
{ name: 'item_scrap', color: '#B87333' }, // Copper/Bronze (kovinski kos)
|
||||||
|
{ name: 'item_chip', color: '#00CED1' } // DarkTurquoise (elektronski chip)
|
||||||
];
|
];
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const it = typeof item === 'string' ? item : item.name;
|
const it = typeof item === 'string' ? item : item.name;
|
||||||
|
|||||||
Reference in New Issue
Block a user