implement electric tower and buff tower

This commit is contained in:
koneko 2025-02-16 23:15:46 +01:00
parent 9c053ece61
commit f84108847b
18 changed files with 241 additions and 75 deletions

View File

@ -57,9 +57,9 @@
} }
}, },
{ {
"name": "Quick Tower", "name": "Buff Tower",
"behaviour": "QuickTowerBehaviour", "behaviour": "BuffTowerBehaviour",
"sprite": "quick_tower", "sprite": "buff_tower",
"texture": null, "texture": null,
"projectile": "blue", "projectile": "blue",
"projectileTextures": [], "projectileTextures": [],
@ -133,9 +133,9 @@
} }
}, },
{ {
"name": "Advanced Tower", "name": "Debuff Tower",
"behaviour": "AdvancedTowerBehaviour", "behaviour": "DebuffTowerBehaviour",
"sprite": "advanced_tower", "sprite": "debuff_tower",
"texture": null, "texture": null,
"projectile": "red", "projectile": "red",
"projectileTextures": [], "projectileTextures": [],

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -28,6 +28,7 @@ export default class GameAssets {
public static TitleTexture: PIXI.Texture; public static TitleTexture: PIXI.Texture;
public static BannerGemsmith: PIXI.Texture; public static BannerGemsmith: PIXI.Texture;
public static EndScreenDialog: PIXI.Texture; public static EndScreenDialog: PIXI.Texture;
public static SpecialLightning: PIXI.Texture[] = [];
public static Tutorial01: PIXI.Texture; public static Tutorial01: PIXI.Texture;
public static Tutorial02: PIXI.Texture; public static Tutorial02: PIXI.Texture;
@ -193,6 +194,9 @@ export default class GameAssets {
} }
tower.texture = await this.Load(`./assets/towers/${tower.sprite}.png`); tower.texture = await this.Load(`./assets/towers/${tower.sprite}.png`);
} }
for (let idx = 0; idx < 4; idx++) {
this.SpecialLightning[idx] = await this.Load(`./assets/projectiles/lightning/${idx}.png`);
}
} }
private static async LoadMission(missionUrl: string) { private static async LoadMission(missionUrl: string) {

View File

@ -9,6 +9,8 @@ import { AnimationManager } from './game/AnimationManager';
import NotificationManager from './game/NotificationManager'; import NotificationManager from './game/NotificationManager';
import Gem from './game/Gem'; import Gem from './game/Gem';
import GameAssets from './Assets'; import GameAssets from './Assets';
import { TowerType } from './Definitions';
import DebrisManager from './game/DebrisManager';
export class Engine { export class Engine {
public static app: PIXI.Application; public static app: PIXI.Application;
@ -18,6 +20,7 @@ export class Engine {
public static TowerManager: TowerManager; public static TowerManager: TowerManager;
public static AnimationManager: AnimationManager; public static AnimationManager: AnimationManager;
public static NotificationManager: NotificationManager; public static NotificationManager: NotificationManager;
public static DebrisManager: DebrisManager;
public static GameScene: GameScene; public static GameScene: GameScene;
public static latestCommit: string; public static latestCommit: string;
public static latestGemId = 0; public static latestGemId = 0;
@ -32,7 +35,7 @@ export class Engine {
let params = new URLSearchParams(location.href); let params = new URLSearchParams(location.href);
if (params.entries().next().value[1] != 'game') return; if (params.entries().next().value[1] != 'game') return;
Engine.NotificationManager.Notify('Loaded testing suite.', 'danger'); Engine.NotificationManager.Notify('Loaded testing suite.', 'danger');
let tower = GameAssets.Towers[0]; let tower = GameAssets.Towers[TowerType.Electric];
Engine.TowerManager.ToggleChoosingTowerLocation('RESET'); Engine.TowerManager.ToggleChoosingTowerLocation('RESET');
Engine.TowerManager.PlaceTower(tower, 6, 10, tower.behaviour, true); Engine.TowerManager.PlaceTower(tower, 6, 10, tower.behaviour, true);
for (let i = 0; i < 29; i++) { for (let i = 0; i < 29; i++) {

View File

@ -125,9 +125,9 @@ export enum TowerType {
Basic = 0, Basic = 0,
Circle = 1, Circle = 1,
Electric = 2, Electric = 2,
Quick = 3, Buff = 3,
Strong = 4, Strong = 4,
Rail = 5, Rail = 5,
Trapper = 6, Trapper = 6,
Advanced = 7, Debuff = 7,
} }

View File

@ -0,0 +1,41 @@
import GameObject from '../GameObject';
import { Container } from 'pixi.js';
class Debris {
public ticksToDestroyAt: number;
private debris: Container | GameObject;
constructor(debris: Container | GameObject, ticksToDestroyAt) {
this.debris = debris;
this.ticksToDestroyAt = ticksToDestroyAt;
}
public destroy() {
this.debris.destroy();
}
public update(elapsedMS) {
console.log(this.debris instanceof GameObject);
if (this.debris instanceof GameObject) {
this.debris.update(elapsedMS);
}
}
}
export default class DebrisManager extends GameObject {
private ticks: number = 0;
private debris: Debris[] = [];
public update(elapsedMS) {
this.ticks++;
for (let idx = this.debris.length - 1; idx >= 0; idx--) {
const db = this.debris[idx];
if (this.ticks >= db.ticksToDestroyAt) {
db.destroy();
this.debris.splice(idx, 1);
} else {
db.update(elapsedMS);
}
}
}
public CreateDebris(affectedObject: Container | GameObject, ticksToDestroyAt?: number) {
if (!ticksToDestroyAt) ticksToDestroyAt = 120;
this.debris.push(new Debris(affectedObject, this.ticks + ticksToDestroyAt));
}
}

View File

@ -1,10 +1,11 @@
import * as PIXI from 'pixi.js'; import * as PIXI from 'pixi.js';
import GameObject from '../GameObject'; import GameObject from '../GameObject';
import { GameMapDefinition, TerrainType } from '../Definitions'; import { GameMapDefinition, TerrainType, TowerType } from '../Definitions';
import GameAssets from '../Assets'; import GameAssets from '../Assets';
import { Engine } from '../Bastion'; import { Engine } from '../Bastion';
import Creep from './Creep'; import Creep from './Creep';
import { CreepEvents, TowerEvents, GridEvents } from '../Events'; import { CreepEvents, TowerEvents, GridEvents } from '../Events';
import { distance, Tower } from './Tower';
let genPath = []; let genPath = [];
@ -15,6 +16,7 @@ export class Cell extends GameObject {
public isPath: boolean = false; public isPath: boolean = false;
public g: PIXI.Graphics; public g: PIXI.Graphics;
public hasTowerPlaced: boolean = false; public hasTowerPlaced: boolean = false;
public isBuffedBy: Tower[] = [];
public clickDetector: PIXI.Graphics; public clickDetector: PIXI.Graphics;
constructor(type: TerrainType, row: number, column: number, isPath: boolean) { constructor(type: TerrainType, row: number, column: number, isPath: boolean) {
@ -55,19 +57,32 @@ export class Cell extends GameObject {
this.clickDetector.on('pointerleave', (e) => { this.clickDetector.on('pointerleave', (e) => {
if (!Engine.Grid.gridInteractionEnabled || Engine.GameScene.towerPanel.isShown) return; if (!Engine.Grid.gridInteractionEnabled || Engine.GameScene.towerPanel.isShown) return;
Engine.GameScene.events.emit(GridEvents.CellMouseLeave, this); Engine.GameScene.events.emit(GridEvents.CellMouseLeave, this);
Engine.Grid.rangePreview.clear();
}); });
Engine.GameScene.events.on(TowerEvents.TowerPlacedEvent, (_, row, col) => { Engine.GameScene.events.on(TowerEvents.TowerPlacedEvent, (towerName, row, col) => {
if (row == this.row && col == this.column) { if (row == this.row && col == this.column) {
this.hasTowerPlaced = true; this.hasTowerPlaced = true;
Engine.Grid.rangePreview.clear(); Engine.Grid.rangePreview.clear();
} else if (towerName == GameAssets.Towers[TowerType.Buff].name) {
let twr = Engine.TowerManager.GetTowerByRowAndCol(row, col);
if (Engine.Grid.IsCellInRangeOfOtherCell(row, col, twr.computedRange, this)) {
this.isBuffedBy.push(twr);
}
} }
}); });
Engine.GameScene.events.on(TowerEvents.TowerSoldEvent, (_, row, col) => { Engine.GameScene.events.on(TowerEvents.TowerSoldEvent, (towerName, row, col) => {
console.log(towerName, row, col);
if (row == this.row && col == this.column) { if (row == this.row && col == this.column) {
this.hasTowerPlaced = false; this.hasTowerPlaced = false;
Engine.Grid.rangePreview.clear(); Engine.Grid.rangePreview.clear();
} else if (towerName == GameAssets.Towers[TowerType.Buff].name) {
console.log('TRIpped!');
let twr = Engine.TowerManager.GetTowerByRowAndCol(row, col);
if (Engine.Grid.IsCellInRangeOfOtherCell(row, col, twr.computedRange, this)) {
console.log('REMOVED!');
this.isBuffedBy.splice(this.isBuffedBy.indexOf(twr), 1);
console.log(this.isBuffedBy);
}
} }
}); });
@ -118,10 +133,12 @@ export class Cell extends GameObject {
this.row * Engine.GridCellSize + Engine.GridCellSize / 2 this.row * Engine.GridCellSize + Engine.GridCellSize / 2
}y` }y`
); );
Engine.Grid.rangePreview.circle( console.log(
this.column * Engine.GridCellSize + Engine.GridCellSize / 2, Engine.Grid.rangePreview.circle(
this.row * Engine.GridCellSize + Engine.GridCellSize / 2, this.column * Engine.GridCellSize + Engine.GridCellSize / 2,
range * Engine.GridCellSize this.row * Engine.GridCellSize + Engine.GridCellSize / 2,
range * Engine.GridCellSize
)
); );
Engine.Grid.rangePreview.fill({ color: color, alpha: 0.3 }); Engine.Grid.rangePreview.fill({ color: color, alpha: 0.3 });
} }
@ -187,6 +204,10 @@ export class Grid extends GameObject {
} }
this.rangePreview = new PIXI.Graphics({ this.rangePreview = new PIXI.Graphics({
zIndex: 10, zIndex: 10,
x: 0,
y: 0,
width: Engine.app.canvas.width,
height: Engine.app.canvas.height,
}); });
this.container.addChild(this.rangePreview); this.container.addChild(this.rangePreview);
} }
@ -216,6 +237,17 @@ export class Grid extends GameObject {
console.log(JSON.stringify(newGrid)); console.log(JSON.stringify(newGrid));
} }
public IsCellInRangeOfOtherCell(row, col, range, otherCell) {
range = range * Engine.GridCellSize;
const x = otherCell.column * Engine.GridCellSize + Engine.GridCellSize / 2;
const y = otherCell.row * Engine.GridCellSize + Engine.GridCellSize / 2;
const cellX = col * Engine.GridCellSize + Engine.GridCellSize / 2;
const cellY = row * Engine.GridCellSize + Engine.GridCellSize / 2;
const d = distance(cellX, cellY, x, y);
if (d < range + Engine.GridCellSize / 2) return true;
else return false;
}
public toggleGrid(force?: 'hide' | 'show') { public toggleGrid(force?: 'hide' | 'show') {
this.cells.forEach((cell) => { this.cells.forEach((cell) => {
if (force) { if (force) {

View File

@ -3,8 +3,9 @@ import GameObject from '../GameObject';
import { Engine } from '../Bastion'; import { Engine } from '../Bastion';
import Creep from './Creep'; import Creep from './Creep';
import { CreepEvents } from '../Events'; import { CreepEvents } from '../Events';
import { Tower } from './Tower'; import { distance, Tower } from './Tower';
import { CreepResistancesDefinition } from '../Definitions'; import { CreepResistancesDefinition } from '../Definitions';
import GameAssets from '../Assets';
export function calculateAngleToPoint(x, y, targetX, targetY) { export function calculateAngleToPoint(x, y, targetX, targetY) {
const dx = targetX - x; const dx = targetX - x;
@ -55,6 +56,7 @@ export default class Projectile extends GameObject {
this.angle = angle; this.angle = angle;
this.speed = 0.9; this.speed = 0.9;
} }
public destroy(): void { public destroy(): void {
super.destroy(); super.destroy();
this.deleteMe = true; this.deleteMe = true;
@ -72,7 +74,7 @@ export default class Projectile extends GameObject {
if (!exists) { if (!exists) {
this.collidedCreepIDs.push(creep); this.collidedCreepIDs.push(creep);
this.pierce--; this.pierce--;
this.onCollide(creep); this.onCollide(creep, this);
return; return;
} }
} }
@ -84,14 +86,14 @@ export default class Projectile extends GameObject {
this.container.y = this.y; this.container.y = this.y;
} }
public onCollide(creep) { public onCollide(creep, proj) {
/* /*
Note: Note:
Right now it is possible for the bullet to 'overshoot' the creep if the bullet speed is too fast and the position is updated so that the Right now it is possible for the bullet to 'overshoot' the creep if the bullet speed is too fast and the position is updated so that the
new position is beyond the creep (i.e. the bullet is never 'in the creep'). new position is beyond the creep (i.e. the bullet is never 'in the creep').
This should be fixed so that we calculate the hit if the creep is in a line from the previous position to the new position. This should be fixed so that we calculate the hit if the creep is in a line from the previous position to the new position.
*/ */
Engine.GameScene.events.emit(CreepEvents.TakenDamage, creep.id, this.damage, this.gemResistanceModifications); Engine.GameScene.events.emit(CreepEvents.TakenDamage, creep.id, proj.damage, proj.gemResistanceModifications);
} }
public checkCollision(creep: Creep) { public checkCollision(creep: Creep) {
@ -102,3 +104,42 @@ export default class Projectile extends GameObject {
return mybb.getBounds().intersects(otherbb.getBounds()); return mybb.getBounds().intersects(otherbb.getBounds());
} }
} }
export class VisualLightning extends GameObject {
public deleteMe: boolean = false;
private c: Creep;
private oc: Creep;
private Lightning: PIXI.AnimatedSprite;
constructor(creep: Creep, otherCreep: Creep) {
super();
this.c = creep;
this.oc = otherCreep;
let lightningAngle = calculateAngleToPoint(creep.x, creep.y, otherCreep.x, otherCreep.y);
this.Lightning = new PIXI.AnimatedSprite({
textures: GameAssets.SpecialLightning,
x: creep.x,
y: creep.y,
width: distance(this.c.x, this.c.y, this.oc.x, this.oc.y),
height: 64,
scale: 1.2,
rotation: lightningAngle,
});
this.Lightning.anchor.set(0, 0.5);
this.Lightning.play();
Engine.GameMaster.currentScene.stage.addChild(this.Lightning);
Engine.DebrisManager.CreateDebris(this, 30);
}
public destroy(): void {
this.deleteMe = true;
this.container.destroy();
this.Lightning.destroy();
}
public update() {
if (this.deleteMe) {
return;
}
this.Lightning.x = this.c.x;
this.Lightning.y = this.c.y;
this.Lightning.width = distance(this.c.x, this.c.y, this.oc.x, this.oc.y);
}
}

View File

@ -8,11 +8,11 @@ import Projectile, { calculateAngleToPoint } from './Projectile';
import Creep from './Creep'; import Creep from './Creep';
import Gem from './Gem'; import Gem from './Gem';
import { import {
AdvancedTowerBehaviour, DebuffTowerBehaviour,
BasicTowerBehaviour, BasicTowerBehaviour,
CircleTowerBehaviour, CircleTowerBehaviour,
ElectricTowerBehaviour, ElectricTowerBehaviour,
QuickTowerBehaviour, BuffTowerBehaviour,
RailTowerBehaviour, RailTowerBehaviour,
StrongTowerBehaviour, StrongTowerBehaviour,
TrapperTowerBehaviour, TrapperTowerBehaviour,
@ -35,12 +35,18 @@ export class Tower extends GameObject {
public sprite: PIXI.Sprite; public sprite: PIXI.Sprite;
public millisecondsUntilNextShot: number; public millisecondsUntilNextShot: number;
public graphics: PIXI.Graphics = new PIXI.Graphics(); public graphics: PIXI.Graphics = new PIXI.Graphics();
public computedDamageToDeal: number; public computedDamageToDeal: number = 0;
public computedCooldown: number; public computedCooldown: number = 0;
public computedRange: number; public computedRange: number = 0;
public computedTimeToLive: number; public computedTimeToLive: number = 0;
public computedPierce: number; public computedPierce: number = 0;
public totalGemResistanceModifications: CreepResistancesDefinition; public totalGemResistanceModifications: CreepResistancesDefinition = {
fire: 0,
frostfire: 0,
divine: 0,
ice: 0,
physical: 0,
};
public parent: Cell; public parent: Cell;
constructor(row, column, texture, definition, behaviour) { constructor(row, column, texture, definition, behaviour) {
@ -59,6 +65,7 @@ export class Tower extends GameObject {
}); });
this.container.addChild(this.sprite); this.container.addChild(this.sprite);
this.computedDamageToDeal = this.definition.stats.damage; this.computedDamageToDeal = this.definition.stats.damage;
this.computedRange = this.definition.stats.range;
this.parent.container.addChild(this.container); this.parent.container.addChild(this.container);
this.container.interactiveChildren = true; this.container.interactiveChildren = true;
this.parent.clickDetector.on('pointerenter', this.onParentCellEnter); this.parent.clickDetector.on('pointerenter', this.onParentCellEnter);
@ -113,6 +120,7 @@ export class Tower extends GameObject {
return d < radius + (Engine.GridCellSize * 2) / 3; return d < radius + (Engine.GridCellSize * 2) / 3;
}); });
} }
public Shoot(angle) { public Shoot(angle) {
let x = this.column * Engine.GridCellSize + Engine.GridCellSize / 2; let x = this.column * Engine.GridCellSize + Engine.GridCellSize / 2;
let y = this.row * Engine.GridCellSize + Engine.GridCellSize / 2; let y = this.row * Engine.GridCellSize + Engine.GridCellSize / 2;
@ -153,11 +161,11 @@ export class Tower extends GameObject {
if (this.behaviour == TowerBehaviours.BasicTowerBehaviour) BasicTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.BasicTowerBehaviour) BasicTowerBehaviour(this, elapsedMS);
if (this.behaviour == TowerBehaviours.CircleTowerBehaviour) CircleTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.CircleTowerBehaviour) CircleTowerBehaviour(this, elapsedMS);
if (this.behaviour == TowerBehaviours.ElectricTowerBehaviour) ElectricTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.ElectricTowerBehaviour) ElectricTowerBehaviour(this, elapsedMS);
if (this.behaviour == TowerBehaviours.QuickTowerBehaviour) QuickTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.BuffTowerBehaviour) BuffTowerBehaviour(this, elapsedMS);
if (this.behaviour == TowerBehaviours.StrongTowerBehaviour) StrongTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.StrongTowerBehaviour) StrongTowerBehaviour(this, elapsedMS);
if (this.behaviour == TowerBehaviours.RailTowerBehaviour) RailTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.RailTowerBehaviour) RailTowerBehaviour(this, elapsedMS);
if (this.behaviour == TowerBehaviours.TrapperTowerBehaviour) TrapperTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.TrapperTowerBehaviour) TrapperTowerBehaviour(this, elapsedMS);
if (this.behaviour == TowerBehaviours.AdvancedTowerBehaviour) AdvancedTowerBehaviour(this, elapsedMS); if (this.behaviour == TowerBehaviours.DebuffTowerBehaviour) DebuffTowerBehaviour(this, elapsedMS);
} }
public destroy(): void { public destroy(): void {

View File

@ -1,6 +1,11 @@
import GameAssets from '../Assets';
import { Engine } from '../Bastion'; import { Engine } from '../Bastion';
import { calculateAngleToPoint } from './Projectile'; import { TowerType } from '../Definitions';
import { Tower } from './Tower'; import { CreepEvents } from '../Events';
import Creep from './Creep';
import Projectile, { calculateAngleToPoint, VisualLightning } from './Projectile';
import { distance, Tower } from './Tower';
import * as PIXI from 'pixi.js';
/** /**
* Checks the projectiles of the tower and updates or removes them based on their state. * Checks the projectiles of the tower and updates or removes them based on their state.
@ -44,34 +49,47 @@ export function computeGemImprovements(tower: Tower) {
physical: 0, physical: 0,
}; };
tower.slottedGems.forEach((gem) => { tower.slottedGems.forEach((gem) => {
let ccurrentGemImprovements = gem.currentGemImprovement(); let improvements = gem.currentGemImprovement();
gemDamage += ccurrentGemImprovements.damageUp; gemDamage += improvements.damageUp;
gemAttackSpeedUp += ccurrentGemImprovements.attackSpeedUp; gemAttackSpeedUp += improvements.attackSpeedUp;
gemRangeUp += ccurrentGemImprovements.rangeUp; gemRangeUp += improvements.rangeUp;
gemTimeToLiveUp += ccurrentGemImprovements.timeToLiveUp; gemTimeToLiveUp += improvements.timeToLiveUp;
gemPierceUp += ccurrentGemImprovements.pierceUp; gemPierceUp += improvements.pierceUp;
let gemResMod = gem.currentGemResistanceModifications(); let resistances = gem.currentGemResistanceModifications();
tower.totalGemResistanceModifications.physical += gemResMod.physical; tower.totalGemResistanceModifications.physical += resistances.physical;
tower.totalGemResistanceModifications.ice += gemResMod.ice; tower.totalGemResistanceModifications.ice += resistances.ice;
tower.totalGemResistanceModifications.fire += gemResMod.fire; tower.totalGemResistanceModifications.fire += resistances.fire;
tower.totalGemResistanceModifications.divine += gemResMod.divine; tower.totalGemResistanceModifications.divine += resistances.divine;
tower.totalGemResistanceModifications.frostfire += gemResMod.frostfire; tower.totalGemResistanceModifications.frostfire += resistances.frostfire;
}); });
tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage; tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage;
tower.computedCooldown = tower.definition.stats.cooldown - gemAttackSpeedUp; tower.computedCooldown = tower.definition.stats.cooldown - gemAttackSpeedUp;
tower.computedRange = tower.definition.stats.range + gemRangeUp; tower.computedRange = tower.definition.stats.range + gemRangeUp;
tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp; tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp;
tower.computedPierce = tower.definition.stats.pierce + gemPierceUp; tower.computedPierce = tower.definition.stats.pierce + gemPierceUp;
// Buff tower
if (tower.parent.isBuffedBy.length > 0 && tower.definition.name != GameAssets.Towers[TowerType.Buff].name) {
let buffedBy = tower.parent.isBuffedBy[0];
tower.computedDamageToDeal += Math.ceil(buffedBy.computedDamageToDeal / 3);
tower.computedCooldown -= Math.ceil(buffedBy.computedCooldown / 5);
tower.computedRange += Math.ceil(buffedBy.computedRange / 10);
tower.computedTimeToLive += Math.ceil(buffedBy.computedTimeToLive / 5);
tower.computedPierce += Math.ceil(buffedBy.computedPierce / 4);
tower.totalGemResistanceModifications.physical +=
(buffedBy.totalGemResistanceModifications.physical * 100) / 2 / 100;
tower.totalGemResistanceModifications.ice += (buffedBy.totalGemResistanceModifications.ice * 100) / 2 / 100;
tower.totalGemResistanceModifications.fire += (buffedBy.totalGemResistanceModifications.fire * 100) / 2 / 100;
tower.totalGemResistanceModifications.divine +=
(buffedBy.totalGemResistanceModifications.divine * 100) / 2 / 100;
tower.totalGemResistanceModifications.frostfire +=
(buffedBy.totalGemResistanceModifications.frostfire * 100) / 2 / 100;
}
} }
/**
* Defines the basic behavior of a tower, including computing damage, checking projectiles,
* and handling shooting at creeps within range.
*
* @param tower - The tower whose behavior is being defined.
* @param elapsedMS - The elapsed time in milliseconds since the last update.
*/
export function BasicTowerBehaviour(tower: Tower, elapsedMS: number) { export function BasicTowerBehaviour(tower: Tower, elapsedMS: number) {
computeGemImprovements(tower); computeGemImprovements(tower);
projectileCheck(tower, elapsedMS); projectileCheck(tower, elapsedMS);
@ -96,7 +114,6 @@ export function CircleTowerBehaviour(tower: Tower, elapsedMS: number) {
tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier;
let creepsInRange = tower.GetCreepsInRange(); let creepsInRange = tower.GetCreepsInRange();
if (creepsInRange.length > 0) { if (creepsInRange.length > 0) {
let focus = creepsInRange[0];
if (tower.millisecondsUntilNextShot <= 0) { if (tower.millisecondsUntilNextShot <= 0) {
tower.millisecondsUntilNextShot = tower.computedCooldown; tower.millisecondsUntilNextShot = tower.computedCooldown;
let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2;
@ -119,34 +136,52 @@ export function ElectricTowerBehaviour(tower: Tower, elapsedMS: number) {
if (tower.millisecondsUntilNextShot > 0) if (tower.millisecondsUntilNextShot > 0)
tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier;
let creepsInRange = tower.GetCreepsInRange(); let creepsInRange = tower.GetCreepsInRange();
if (creepsInRange.length > 0) { if (creepsInRange.length > 0) {
let focus = creepsInRange[0]; let focus = creepsInRange[0];
if (tower.millisecondsUntilNextShot <= 0) { if (tower.millisecondsUntilNextShot <= 0) {
let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2;
let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2;
tower.millisecondsUntilNextShot = tower.computedCooldown; tower.millisecondsUntilNextShot = tower.computedCooldown;
tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y)); let proj = tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y));
proj.onCollide = (creep: Creep, proj: Projectile) => {
proj.pierce = 0;
let nearByCreeps = Engine.Grid.creeps.filter((nCreep) => {
if (nCreep.id != creep.id) {
const x = nCreep.x;
const y = nCreep.y;
const radius = 3.5 * Engine.GridCellSize;
const d = distance(creep.x, creep.y, x, y);
return d < radius;
}
});
nearByCreeps.forEach((nearCreep) => {
new VisualLightning(creep, nearCreep);
Engine.GameScene.events.emit(
CreepEvents.TakenDamage,
nearCreep.id,
proj.damage / 2,
proj.gemResistanceModifications
);
});
Engine.GameScene.events.emit(
CreepEvents.TakenDamage,
creep.id,
proj.damage,
proj.gemResistanceModifications
);
};
} }
} }
} }
export function QuickTowerBehaviour(tower: Tower, elapsedMS: number) { export function BuffTowerBehaviour(tower: Tower, elapsedMS: number) {
computeGemImprovements(tower); computeGemImprovements(tower);
projectileCheck(tower, elapsedMS); // Buff tower does not do anything and doesn't have a behaviour.
// For how its implemented, Cell tracks when it's placed and removed (via event)
if (tower.millisecondsUntilNextShot > 0) // and tower takes improvements via computeGemImprovements()
tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier;
let creepsInRange = tower.GetCreepsInRange();
if (creepsInRange.length > 0) {
let focus = creepsInRange[0];
if (tower.millisecondsUntilNextShot <= 0) {
let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2;
let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2;
tower.millisecondsUntilNextShot = tower.computedCooldown;
tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y));
}
}
} }
export function StrongTowerBehaviour(tower: Tower, elapsedMS: number) { export function StrongTowerBehaviour(tower: Tower, elapsedMS: number) {
@ -203,7 +238,7 @@ export function TrapperTowerBehaviour(tower: Tower, elapsedMS: number) {
} }
} }
export function AdvancedTowerBehaviour(tower: Tower, elapsedMS: number) { export function DebuffTowerBehaviour(tower: Tower, elapsedMS: number) {
computeGemImprovements(tower); computeGemImprovements(tower);
projectileCheck(tower, elapsedMS); projectileCheck(tower, elapsedMS);

View File

@ -2,7 +2,7 @@ import * as PIXI from 'pixi.js';
import { Engine } from '../Bastion'; import { Engine } from '../Bastion';
import { TerrainType, TowerDefinition } from '../Definitions'; import { TerrainType, TowerDefinition } from '../Definitions';
import GameAssets from '../Assets'; import GameAssets from '../Assets';
import { Tower } from './Tower'; import { distance, Tower } from './Tower';
import { Cell } from './Grid'; import { Cell } from './Grid';
import { GridEvents, TowerEvents } from '../Events'; import { GridEvents, TowerEvents } from '../Events';
@ -10,11 +10,11 @@ export enum TowerBehaviours {
BasicTowerBehaviour = 'BasicTowerBehaviour', BasicTowerBehaviour = 'BasicTowerBehaviour',
CircleTowerBehaviour = 'CircleTowerBehaviour', CircleTowerBehaviour = 'CircleTowerBehaviour',
ElectricTowerBehaviour = 'ElectricTowerBehaviour', ElectricTowerBehaviour = 'ElectricTowerBehaviour',
QuickTowerBehaviour = 'QuickTowerBehaviour', BuffTowerBehaviour = 'BuffTowerBehaviour',
StrongTowerBehaviour = 'StrongTowerBehaviour', StrongTowerBehaviour = 'StrongTowerBehaviour',
RailTowerBehaviour = 'RailTowerBehaviour', RailTowerBehaviour = 'RailTowerBehaviour',
TrapperTowerBehaviour = 'TrapperTowerBehaviour', TrapperTowerBehaviour = 'TrapperTowerBehaviour',
AdvancedTowerBehaviour = 'AdvancedTowerBehaviour', DebuffTowerBehaviour = 'DebuffTowerBehaviour',
} }
export default class TowerManager { export default class TowerManager {
@ -127,10 +127,10 @@ export default class TowerManager {
while (twr.slottedGems.length > 0) { while (twr.slottedGems.length > 0) {
twr.UnslotGem(0); twr.UnslotGem(0);
} }
Engine.GameScene.events.emit(TowerEvents.TowerSoldEvent, twr.definition.name, twr.row, twr.column);
Engine.GameScene.MissionStats.earnGold(twr.definition.stats.cost); Engine.GameScene.MissionStats.earnGold(twr.definition.stats.cost);
twr.destroy(); twr.destroy();
this.towers.splice(idx, 1); this.towers.splice(idx, 1);
Engine.GameScene.events.emit(TowerEvents.TowerSoldEvent, twr.name, twr.row, twr.column);
} else twr.update(elapsedMS); } else twr.update(elapsedMS);
}); });
} }

View File

@ -7,7 +7,7 @@ import { AnimationManager } from './classes/game/AnimationManager';
import NotificationManager from './classes/game/NotificationManager'; import NotificationManager from './classes/game/NotificationManager';
import GameUIConstants from './classes/GameUIConstants'; import GameUIConstants from './classes/GameUIConstants';
import KeyboardManager from './classes/game/KeyboardManager'; import KeyboardManager from './classes/game/KeyboardManager';
import { GemType } from './classes/Definitions'; import DebrisManager from './classes/game/DebrisManager';
(async () => { (async () => {
const app = new PIXI.Application(); const app = new PIXI.Application();
@ -54,10 +54,12 @@ import { GemType } from './classes/Definitions';
new GameMaster(); new GameMaster();
Engine.AnimationManager = new AnimationManager(); Engine.AnimationManager = new AnimationManager();
Engine.NotificationManager = new NotificationManager(); Engine.NotificationManager = new NotificationManager();
Engine.DebrisManager = new DebrisManager();
globalThis.Engine = Engine; globalThis.Engine = Engine;
PIXI.Ticker.shared.add((ticker) => { PIXI.Ticker.shared.add((ticker) => {
Engine.NotificationManager.update(ticker.elapsedMS); Engine.NotificationManager.update(ticker.elapsedMS);
Engine.AnimationManager.update(ticker.elapsedMS); Engine.AnimationManager.update(ticker.elapsedMS);
Engine.DebrisManager.update(ticker.elapsedMS);
}); });
app.canvas.addEventListener('pointermove', function (event) { app.canvas.addEventListener('pointermove', function (event) {
Engine.MouseX = ((event.clientX - app.canvas.offsetLeft) / app.canvas.offsetWidth) * 1920; Engine.MouseX = ((event.clientX - app.canvas.offsetLeft) / app.canvas.offsetWidth) * 1920;