mesano
This commit is contained in:
@@ -24,8 +24,17 @@ class NPC {
|
||||
this.pauseTime = 0;
|
||||
this.maxPauseTime = 2000;
|
||||
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
|
||||
this.createSprite();
|
||||
@@ -128,6 +137,14 @@ class NPC {
|
||||
texKey = 'merchant_texture'; // Fallback na proceduralno
|
||||
}
|
||||
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)) {
|
||||
TextureGenerator.createNPCSprite(this.scene, texKey, this.type);
|
||||
}
|
||||
@@ -144,8 +161,10 @@ class NPC {
|
||||
if (isAnimated) {
|
||||
this.sprite.setScale(1.5);
|
||||
} else {
|
||||
// Merchant manjši, ostali večji
|
||||
const scale = (this.type === 'merchant') ? 0.2 : 0.5;
|
||||
// Scale po tipu
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -206,7 +225,27 @@ class NPC {
|
||||
}
|
||||
|
||||
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) {
|
||||
const playerPos = this.scene.player.getPosition();
|
||||
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) {
|
||||
this.updateDepth();
|
||||
this.updatePosition();
|
||||
@@ -544,19 +583,73 @@ class NPC {
|
||||
this.healthBar.fillRect(-16, -70, 32 * percent, 4);
|
||||
}
|
||||
|
||||
// WHITE FLASH Effect
|
||||
if (this.sprite) {
|
||||
this.sprite.setTint(0xff0000);
|
||||
this.scene.time.delayedCall(100, () => {
|
||||
this.sprite.setTint(0xFFFFFF); // White flash
|
||||
this.scene.time.delayedCall(50, () => {
|
||||
if (this.sprite) {
|
||||
this.sprite.clearTint();
|
||||
// Re-apply tamed tint if tamed
|
||||
if (this.state === 'TAMED' || this.state === 'FOLLOW' || this.state === 'STAY') {
|
||||
this.sprite.setTint(0xAAFFAA);
|
||||
}
|
||||
this.sprite.setTint(0xFF0000); // Red flash
|
||||
this.scene.time.delayedCall(50, () => {
|
||||
if (this.sprite) {
|
||||
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}`);
|
||||
|
||||
if (this.hp <= 0) {
|
||||
@@ -572,12 +665,19 @@ class NPC {
|
||||
this.scene.soundManager.playDeath();
|
||||
}
|
||||
|
||||
// Spawn loot - BONE
|
||||
// Spawn loot - Elite zombies drop better items!
|
||||
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) {
|
||||
// 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)
|
||||
|
||||
@@ -340,14 +340,56 @@ class Player {
|
||||
const tile = terrainSystem.tiles[targetY][targetX];
|
||||
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}`;
|
||||
if (terrainSystem.decorationsMap.has(key)) {
|
||||
const decor = terrainSystem.decorationsMap.get(key);
|
||||
const solidTypes = ['tree', 'stone', 'bush', 'wall', 'ruin', 'fence', 'house', 'gravestone'];
|
||||
if (solidTypes.includes(decor.type)) {
|
||||
console.log('⛔ Blocked by:', decor.type);
|
||||
// Vsi trdni objekti - igralec ne more hoditi skozi njih
|
||||
const solidTypes = [
|
||||
'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;
|
||||
}
|
||||
}
|
||||
@@ -397,16 +439,18 @@ class Player {
|
||||
|
||||
updatePosition() {
|
||||
const screenPos = this.iso.toScreen(this.gridX, this.gridY);
|
||||
this.sprite.setPosition(
|
||||
screenPos.x + this.offsetX,
|
||||
screenPos.y + this.offsetY
|
||||
);
|
||||
|
||||
// Pixel-perfect positioning
|
||||
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 handOffset = facingRight ? 10 : -10;
|
||||
this.handSprite.setPosition(
|
||||
screenPos.x + this.offsetX + handOffset,
|
||||
screenPos.y + this.offsetY - 15
|
||||
Math.round(x + handOffset),
|
||||
Math.round(y - 15)
|
||||
);
|
||||
|
||||
this.updateDepth();
|
||||
|
||||
Reference in New Issue
Block a user