diff --git a/assets/ammo.png b/assets/ammo.png new file mode 100644 index 0000000..a49e320 Binary files /dev/null and b/assets/ammo.png differ diff --git a/assets/ammo.psd b/assets/ammo.psd new file mode 100644 index 0000000..bc869d8 Binary files /dev/null and b/assets/ammo.psd differ diff --git a/assets/towers.png b/assets/towers.png index 80ff321..e855b34 100644 Binary files a/assets/towers.png and b/assets/towers.png differ diff --git a/assets/towers.psd b/assets/towers.psd index c99c0f5..daf399d 100644 Binary files a/assets/towers.psd and b/assets/towers.psd differ diff --git a/src/levels/level.js b/src/levels/level.js index faa9ba4..4fdfcb8 100644 --- a/src/levels/level.js +++ b/src/levels/level.js @@ -13,6 +13,10 @@ export class Level extends Phaser.Scene { preload() { this.load.tilemapTiledJSON('level1', 'assets/level1.json'); this.load.image('terrain', 'assets/terrain.png'); + this.load.spritesheet('ammo', 'assets/ammo.png', { + frameWidth: 50, + frameHeight: 50 + }); this.load.spritesheet('basic-enemies', 'assets/basic-enemies.png', { frameWidth: 50, frameHeight: 50 @@ -57,23 +61,23 @@ export class Level extends Phaser.Scene { } addControls() { - this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY) => { - const zoomSpeed = 0.1; - if (deltaY < 0) { - // Zoom in - this.cameras.main.zoom += zoomSpeed; - } else if (deltaY > 0) { - // Zoom out - this.cameras.main.zoom -= zoomSpeed; - } + // this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY) => { + // const zoomSpeed = 0.1; + // if (deltaY < 0) { + // // Zoom in + // this.cameras.main.zoom += zoomSpeed; + // } else if (deltaY > 0) { + // // Zoom out + // this.cameras.main.zoom -= zoomSpeed; + // } - // Limit zoom range to prevent extreme zoom levels - this.cameras.main.zoom = Phaser.Math.Clamp(this.cameras.main.zoom, 0.5, 2); + // // Limit zoom range to prevent extreme zoom levels + // this.cameras.main.zoom = Phaser.Math.Clamp(this.cameras.main.zoom, 0.5, 2); - // Zoom toward mouse position - const worldPoint = this.input.activePointer.positionToCamera(this.cameras.main); - this.cameras.main.centerOn(worldPoint.x, worldPoint.y); - }); + // // Zoom toward mouse position + // const worldPoint = this.input.activePointer.positionToCamera(this.cameras.main); + // this.cameras.main.centerOn(worldPoint.x, worldPoint.y); + // }); // Add camera panning functionality this.input.on('pointerdown', (pointer) => { diff --git a/src/support/interfaceManager.js b/src/support/interfaceManager.js index 544449d..0526913 100644 --- a/src/support/interfaceManager.js +++ b/src/support/interfaceManager.js @@ -256,10 +256,6 @@ export class InterfaceManager { x = 0; } } - // this.gridAdd(0, 0, 'Gatlin Gun', 100, 'gun'); - // this.gridAdd(0, 1, 'Flamethrower', 150, 'gun'); - // this.gridAdd(1, 0, 'Laser', 200, 'gun'); - // this.gridAdd(1, 1, 'Cannon', 200, 'cannon'); } gridAdd(x, y, text, cost, type) { @@ -367,6 +363,11 @@ export class InterfaceManager { const towerInteractive = this.scene.add.rectangle(0, 0, 200, 200, 0x000000, 0); this.selectedTower.add(towerInteractive); + const rangeCircle = this.scene.add.circle(0, 0, TOWERS_CONFIG[type].level1.range, 0xc0b15c, 0.2) + .setOrigin(0.5) + .setScrollFactor(0); + this.selectedTower.add(rangeCircle); + const towerBase = this.scene.add.sprite(0, 0, 'towers', 7) .setOrigin(0.5) .setScrollFactor(0) @@ -442,14 +443,15 @@ export class InterfaceManager { const tile = platformsLayer.getTileAt(tileX, tileY); if (tile) { this.selectedTower.iterate((child) => { - if (child.type !== 'Rectangle') { - child.setTint(0x89ff5b); + if (child.type !== 'Rectangle' && child.type !== 'Arc') { + child.setTint(0x48ff00); } }); this.selectedTower.safe = true; } else { this.selectedTower.iterate((child) => { - if (child.type !== 'Rectangle') { + if (child.type !== 'Rectangle' && child.type !== 'Arc') { + console.log(child.type); child.setTint(0xa32a00); } }); diff --git a/src/support/towerConfig.js b/src/support/towerConfig.js index 8a187d6..4f5478d 100644 --- a/src/support/towerConfig.js +++ b/src/support/towerConfig.js @@ -3,56 +3,87 @@ export const TOWERS_CONFIG = { 'name': 'Gatlin Gun', 'cost': 100, 'spriteStart': 0, - 'type': 'direct', - 'aoe': 0, + 'dmgType': 'direct', 'level1': { 'dmgLow': 10, 'dmgHigh': 30, 'rate': 2000, 'duration': 500, - 'range': 275 + 'range': 300 }, 'level2': { 'dmgLow': 15, 'dmgHigh': 35, 'rate': 1500, 'duration': 750, - 'range': 300 + 'range': 350 }, 'level3': { 'dmgLow': 25, 'dmgHigh': 50, 'rate': 1000, 'duration': 1000, - 'range': 325 + 'range': 400 } }, 'cannon': { 'name': 'Cannon', 'cost': 200, 'spriteStart': 10, - 'type': 'aoe', - 'aoe': 50, + 'dmgType': 'aoe', 'level1': { 'dmgLow': 15, 'dmgHigh': 35, - 'rate': 2000, + 'rate': 2500, 'duration': 200, - 'range': 400 + 'range': 400, + 'aoe': 50, }, 'level2': { 'dmgLow': 25, 'dmgHigh': 45, - 'rate': 2000, + 'rate': 2500, 'duration': 500, - 'range': 450 + 'range': 450, + 'aoe': 25, }, 'level3': { 'dmgLow': 35, 'dmgHigh': 65, - 'rate': 2000, + 'rate': 2500, 'duration': 500, - 'range': 500 + 'range': 500, + 'aoe': 25, + } + }, + 'flame': { + 'name': 'Flamethrower', + 'cost': 150, + 'spriteStart': 20, + 'dmgType': 'aoe', + 'level1': { + 'dmgLow': 1, + 'dmgHigh': 5, + 'rate': 1000, + 'duration': 1000, + 'range': 300, + 'aoe': 100, + }, + 'level2': { + 'dmgLow': 3, + 'dmgHigh': 8, + 'rate': 1000, + 'duration': 1000, + 'range': 350, + 'aoe': 100, + }, + 'level3': { + 'dmgLow': 5, + 'dmgHigh': 12, + 'rate': 1000, + 'duration': 1000, + 'range': 400, + 'aoe': 100, } } } \ No newline at end of file diff --git a/src/support/towerManager.js b/src/support/towerManager.js index 1416073..83368c8 100644 --- a/src/support/towerManager.js +++ b/src/support/towerManager.js @@ -89,13 +89,19 @@ export class TowerManager { }); this.following[tower.id] = furthestEnemy.props.id; - this.attackTarget(tower, furthestEnemy); + const dmgType = TOWERS_CONFIG[tower.props.type].dmgType; + if (dmgType === 'aoe') { + this.aoeAttackTarget(tower, furthestEnemy); + } else { + this.attackTarget(tower, furthestEnemy); + } this.lastFired[tower.id] = time; } } }); } + attackTarget(tower, enemy) { // Tower Properties const type = tower.props.type; @@ -121,11 +127,98 @@ export class TowerManager { if (enemy.props.health > 0) { this.createHealthBar(enemy); + if (type === 'gun') { + const gunfire = this.scene.physics.add.sprite(enemy.x, enemy.y, 'ammo', 0); + gunfire.play('gunfire'); + gunfire.setVelocity(enemy.body.velocity.x, enemy.body.velocity.y); + this.scene.time.delayedCall(config.duration, () => { + gunfire.destroy(); + }); + } } else { this.destroyEnemy(enemy, tower); } } + aoeAttackTarget(tower, enemy) { + // Tower Properties + const type = tower.props.type; + const level = 'level'+tower.props.level; + const config = TOWERS_CONFIG[type][level]; + const duration = TOWERS_CONFIG[type][level].duration; + + if (!config) return; + + const dmgLow = config.dmgLow; + const dmgHigh = config.dmgHigh; + + const angle = Phaser.Math.Angle.Between(tower.x, tower.y, enemy.x, enemy.y); + tower.rotation = angle; + + if (type === 'cannon') { + const projDistance = 50; + const projX = tower.x + Math.cos(angle) * projDistance; + const projY = tower.y + Math.sin(angle) * projDistance; + const projectile = this.scene.add.sprite(projX, projY, 'ammo', 3); + this.scene.tweens.add({ + targets: projectile, + x: enemy.x, + y: enemy.y, + duration: 100, + onComplete: () => { + projectile.setTexture('ammo', 4); + this.scene.time.delayedCall(200, () => { + projectile.destroy(); + }); + } + }); + tower.play(`${type}-${level}-fire`, { 'duration': duration }); + } + + if (type === 'flame') { + const projDistance = 185; + const projDistance2 = 220; + const projX = tower.x + Math.cos(angle) * projDistance; + const projY = tower.y + Math.sin(angle) * projDistance; + const proj2X = tower.x + Math.cos(angle) * projDistance2; + const proj2Y = tower.y + Math.sin(angle) * projDistance2; + const projectile = this.scene.add.sprite(projX, projY, 'towers', 23).setScale(1.5).setAlpha(.5); + projectile.rotation = angle; + projectile.play(`flamethrower`); + this.scene.tweens.add({ + targets: projectile, + x: proj2X, + y: proj2Y, + duration:1000, + onComplete: () => { + projectile.destroy(); + } + }); + + } + + // Calculate damage (random between low and high) + const damage = Phaser.Math.Between(dmgLow, dmgHigh); + + // Get AOE distance + const aoeDistance = config.aoe; + + // Apply damage to all enemies in range + const enemiesInRange = this.scene.enemies.getChildren().filter(e => { + return Phaser.Math.Distance.Between(e.x, e.y, enemy.x, enemy.y) <= aoeDistance; + }); + + enemiesInRange.forEach(targetEnemy => { + targetEnemy.props.health -= damage; + + if (targetEnemy.props.health > 0) { + this.createHealthBar(targetEnemy); + } else { + this.destroyEnemy(targetEnemy, tower); + } + }); + } + createHealthBar(enemy) { const barWidth = 30; const barHeight = 5; @@ -230,6 +323,15 @@ export class TowerManager { } createAnims() { + this.scene.anims.create({ + key: 'gunfire', + frames: this.scene.anims.generateFrameNumbers('ammo', { + start:0, + end:2 + }), + framerate: 15, + repeat: -1 + }); this.scene.anims.create({ key: 'gun-level1-fire', frames: this.scene.anims.generateFrameNumbers('towers', { @@ -237,7 +339,7 @@ export class TowerManager { end: 1, }), frameRate: 15, - repeat: 5, + repeat: 6, yoyo: true }); this.scene.anims.create({ @@ -263,12 +365,20 @@ export class TowerManager { this.scene.anims.create({ key: 'cannon-level1-fire', frames: this.scene.anims.generateFrameNumbers('towers', { - start: 10, - end: 11 + start: 11, + end: 10 }), - frameRate:3, - repeat: 0, - yoyo: true + frameRate: 5, + repeat: 0 + }); + this.scene.anims.create({ + key: 'flamethrower', + frames: this.scene.anims.generateFrameNumbers('towers', { + start: 23, + end:25 + }), + framerate: 4, + repeat: -1 }) }