implement electric tower and buff tower
@ -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": [],
|
||||||
|
BIN
public/assets/projectiles/lightning/0.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
public/assets/projectiles/lightning/1.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
public/assets/projectiles/lightning/2.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
public/assets/projectiles/lightning/3.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.2 KiB |
@ -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) {
|
||||||
|
@ -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++) {
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
41
src/classes/game/DebrisManager.ts
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -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`
|
||||||
);
|
);
|
||||||
|
console.log(
|
||||||
Engine.Grid.rangePreview.circle(
|
Engine.Grid.rangePreview.circle(
|
||||||
this.column * Engine.GridCellSize + Engine.GridCellSize / 2,
|
this.column * Engine.GridCellSize + Engine.GridCellSize / 2,
|
||||||
this.row * Engine.GridCellSize + Engine.GridCellSize / 2,
|
this.row * Engine.GridCellSize + Engine.GridCellSize / 2,
|
||||||
range * Engine.GridCellSize
|
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) {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|