implement electric tower and buff tower
@ -57,9 +57,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Quick Tower",
|
||||
"behaviour": "QuickTowerBehaviour",
|
||||
"sprite": "quick_tower",
|
||||
"name": "Buff Tower",
|
||||
"behaviour": "BuffTowerBehaviour",
|
||||
"sprite": "buff_tower",
|
||||
"texture": null,
|
||||
"projectile": "blue",
|
||||
"projectileTextures": [],
|
||||
@ -133,9 +133,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Advanced Tower",
|
||||
"behaviour": "AdvancedTowerBehaviour",
|
||||
"sprite": "advanced_tower",
|
||||
"name": "Debuff Tower",
|
||||
"behaviour": "DebuffTowerBehaviour",
|
||||
"sprite": "debuff_tower",
|
||||
"texture": null,
|
||||
"projectile": "red",
|
||||
"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 BannerGemsmith: PIXI.Texture;
|
||||
public static EndScreenDialog: PIXI.Texture;
|
||||
public static SpecialLightning: PIXI.Texture[] = [];
|
||||
|
||||
public static Tutorial01: 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`);
|
||||
}
|
||||
for (let idx = 0; idx < 4; idx++) {
|
||||
this.SpecialLightning[idx] = await this.Load(`./assets/projectiles/lightning/${idx}.png`);
|
||||
}
|
||||
}
|
||||
|
||||
private static async LoadMission(missionUrl: string) {
|
||||
|
@ -9,6 +9,8 @@ import { AnimationManager } from './game/AnimationManager';
|
||||
import NotificationManager from './game/NotificationManager';
|
||||
import Gem from './game/Gem';
|
||||
import GameAssets from './Assets';
|
||||
import { TowerType } from './Definitions';
|
||||
import DebrisManager from './game/DebrisManager';
|
||||
|
||||
export class Engine {
|
||||
public static app: PIXI.Application;
|
||||
@ -18,6 +20,7 @@ export class Engine {
|
||||
public static TowerManager: TowerManager;
|
||||
public static AnimationManager: AnimationManager;
|
||||
public static NotificationManager: NotificationManager;
|
||||
public static DebrisManager: DebrisManager;
|
||||
public static GameScene: GameScene;
|
||||
public static latestCommit: string;
|
||||
public static latestGemId = 0;
|
||||
@ -32,7 +35,7 @@ export class Engine {
|
||||
let params = new URLSearchParams(location.href);
|
||||
if (params.entries().next().value[1] != 'game') return;
|
||||
Engine.NotificationManager.Notify('Loaded testing suite.', 'danger');
|
||||
let tower = GameAssets.Towers[0];
|
||||
let tower = GameAssets.Towers[TowerType.Electric];
|
||||
Engine.TowerManager.ToggleChoosingTowerLocation('RESET');
|
||||
Engine.TowerManager.PlaceTower(tower, 6, 10, tower.behaviour, true);
|
||||
for (let i = 0; i < 29; i++) {
|
||||
|
@ -125,9 +125,9 @@ export enum TowerType {
|
||||
Basic = 0,
|
||||
Circle = 1,
|
||||
Electric = 2,
|
||||
Quick = 3,
|
||||
Buff = 3,
|
||||
Strong = 4,
|
||||
Rail = 5,
|
||||
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 GameObject from '../GameObject';
|
||||
import { GameMapDefinition, TerrainType } from '../Definitions';
|
||||
import { GameMapDefinition, TerrainType, TowerType } from '../Definitions';
|
||||
import GameAssets from '../Assets';
|
||||
import { Engine } from '../Bastion';
|
||||
import Creep from './Creep';
|
||||
import { CreepEvents, TowerEvents, GridEvents } from '../Events';
|
||||
import { distance, Tower } from './Tower';
|
||||
|
||||
let genPath = [];
|
||||
|
||||
@ -15,6 +16,7 @@ export class Cell extends GameObject {
|
||||
public isPath: boolean = false;
|
||||
public g: PIXI.Graphics;
|
||||
public hasTowerPlaced: boolean = false;
|
||||
public isBuffedBy: Tower[] = [];
|
||||
public clickDetector: PIXI.Graphics;
|
||||
|
||||
constructor(type: TerrainType, row: number, column: number, isPath: boolean) {
|
||||
@ -55,19 +57,32 @@ export class Cell extends GameObject {
|
||||
this.clickDetector.on('pointerleave', (e) => {
|
||||
if (!Engine.Grid.gridInteractionEnabled || Engine.GameScene.towerPanel.isShown) return;
|
||||
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) {
|
||||
this.hasTowerPlaced = true;
|
||||
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) {
|
||||
this.hasTowerPlaced = false;
|
||||
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
|
||||
}y`
|
||||
);
|
||||
Engine.Grid.rangePreview.circle(
|
||||
this.column * Engine.GridCellSize + Engine.GridCellSize / 2,
|
||||
this.row * Engine.GridCellSize + Engine.GridCellSize / 2,
|
||||
range * Engine.GridCellSize
|
||||
console.log(
|
||||
Engine.Grid.rangePreview.circle(
|
||||
this.column * Engine.GridCellSize + Engine.GridCellSize / 2,
|
||||
this.row * Engine.GridCellSize + Engine.GridCellSize / 2,
|
||||
range * Engine.GridCellSize
|
||||
)
|
||||
);
|
||||
Engine.Grid.rangePreview.fill({ color: color, alpha: 0.3 });
|
||||
}
|
||||
@ -187,6 +204,10 @@ export class Grid extends GameObject {
|
||||
}
|
||||
this.rangePreview = new PIXI.Graphics({
|
||||
zIndex: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: Engine.app.canvas.width,
|
||||
height: Engine.app.canvas.height,
|
||||
});
|
||||
this.container.addChild(this.rangePreview);
|
||||
}
|
||||
@ -216,6 +237,17 @@ export class Grid extends GameObject {
|
||||
|
||||
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') {
|
||||
this.cells.forEach((cell) => {
|
||||
if (force) {
|
||||
|
@ -3,8 +3,9 @@ import GameObject from '../GameObject';
|
||||
import { Engine } from '../Bastion';
|
||||
import Creep from './Creep';
|
||||
import { CreepEvents } from '../Events';
|
||||
import { Tower } from './Tower';
|
||||
import { distance, Tower } from './Tower';
|
||||
import { CreepResistancesDefinition } from '../Definitions';
|
||||
import GameAssets from '../Assets';
|
||||
|
||||
export function calculateAngleToPoint(x, y, targetX, targetY) {
|
||||
const dx = targetX - x;
|
||||
@ -55,6 +56,7 @@ export default class Projectile extends GameObject {
|
||||
this.angle = angle;
|
||||
this.speed = 0.9;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
super.destroy();
|
||||
this.deleteMe = true;
|
||||
@ -72,7 +74,7 @@ export default class Projectile extends GameObject {
|
||||
if (!exists) {
|
||||
this.collidedCreepIDs.push(creep);
|
||||
this.pierce--;
|
||||
this.onCollide(creep);
|
||||
this.onCollide(creep, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -84,14 +86,14 @@ export default class Projectile extends GameObject {
|
||||
this.container.y = this.y;
|
||||
}
|
||||
|
||||
public onCollide(creep) {
|
||||
public onCollide(creep, proj) {
|
||||
/*
|
||||
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
|
||||
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.
|
||||
*/
|
||||
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) {
|
||||
@ -102,3 +104,42 @@ export default class Projectile extends GameObject {
|
||||
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 Gem from './Gem';
|
||||
import {
|
||||
AdvancedTowerBehaviour,
|
||||
DebuffTowerBehaviour,
|
||||
BasicTowerBehaviour,
|
||||
CircleTowerBehaviour,
|
||||
ElectricTowerBehaviour,
|
||||
QuickTowerBehaviour,
|
||||
BuffTowerBehaviour,
|
||||
RailTowerBehaviour,
|
||||
StrongTowerBehaviour,
|
||||
TrapperTowerBehaviour,
|
||||
@ -35,12 +35,18 @@ export class Tower extends GameObject {
|
||||
public sprite: PIXI.Sprite;
|
||||
public millisecondsUntilNextShot: number;
|
||||
public graphics: PIXI.Graphics = new PIXI.Graphics();
|
||||
public computedDamageToDeal: number;
|
||||
public computedCooldown: number;
|
||||
public computedRange: number;
|
||||
public computedTimeToLive: number;
|
||||
public computedPierce: number;
|
||||
public totalGemResistanceModifications: CreepResistancesDefinition;
|
||||
public computedDamageToDeal: number = 0;
|
||||
public computedCooldown: number = 0;
|
||||
public computedRange: number = 0;
|
||||
public computedTimeToLive: number = 0;
|
||||
public computedPierce: number = 0;
|
||||
public totalGemResistanceModifications: CreepResistancesDefinition = {
|
||||
fire: 0,
|
||||
frostfire: 0,
|
||||
divine: 0,
|
||||
ice: 0,
|
||||
physical: 0,
|
||||
};
|
||||
public parent: Cell;
|
||||
|
||||
constructor(row, column, texture, definition, behaviour) {
|
||||
@ -59,6 +65,7 @@ export class Tower extends GameObject {
|
||||
});
|
||||
this.container.addChild(this.sprite);
|
||||
this.computedDamageToDeal = this.definition.stats.damage;
|
||||
this.computedRange = this.definition.stats.range;
|
||||
this.parent.container.addChild(this.container);
|
||||
this.container.interactiveChildren = true;
|
||||
this.parent.clickDetector.on('pointerenter', this.onParentCellEnter);
|
||||
@ -113,6 +120,7 @@ export class Tower extends GameObject {
|
||||
return d < radius + (Engine.GridCellSize * 2) / 3;
|
||||
});
|
||||
}
|
||||
|
||||
public Shoot(angle) {
|
||||
let x = this.column * 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.CircleTowerBehaviour) CircleTowerBehaviour(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.RailTowerBehaviour) RailTowerBehaviour(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 {
|
||||
|
@ -1,6 +1,11 @@
|
||||
import GameAssets from '../Assets';
|
||||
import { Engine } from '../Bastion';
|
||||
import { calculateAngleToPoint } from './Projectile';
|
||||
import { Tower } from './Tower';
|
||||
import { TowerType } from '../Definitions';
|
||||
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.
|
||||
@ -44,34 +49,47 @@ export function computeGemImprovements(tower: Tower) {
|
||||
physical: 0,
|
||||
};
|
||||
tower.slottedGems.forEach((gem) => {
|
||||
let ccurrentGemImprovements = gem.currentGemImprovement();
|
||||
gemDamage += ccurrentGemImprovements.damageUp;
|
||||
gemAttackSpeedUp += ccurrentGemImprovements.attackSpeedUp;
|
||||
gemRangeUp += ccurrentGemImprovements.rangeUp;
|
||||
gemTimeToLiveUp += ccurrentGemImprovements.timeToLiveUp;
|
||||
gemPierceUp += ccurrentGemImprovements.pierceUp;
|
||||
let improvements = gem.currentGemImprovement();
|
||||
gemDamage += improvements.damageUp;
|
||||
gemAttackSpeedUp += improvements.attackSpeedUp;
|
||||
gemRangeUp += improvements.rangeUp;
|
||||
gemTimeToLiveUp += improvements.timeToLiveUp;
|
||||
gemPierceUp += improvements.pierceUp;
|
||||
|
||||
let gemResMod = gem.currentGemResistanceModifications();
|
||||
tower.totalGemResistanceModifications.physical += gemResMod.physical;
|
||||
tower.totalGemResistanceModifications.ice += gemResMod.ice;
|
||||
tower.totalGemResistanceModifications.fire += gemResMod.fire;
|
||||
tower.totalGemResistanceModifications.divine += gemResMod.divine;
|
||||
tower.totalGemResistanceModifications.frostfire += gemResMod.frostfire;
|
||||
let resistances = gem.currentGemResistanceModifications();
|
||||
tower.totalGemResistanceModifications.physical += resistances.physical;
|
||||
tower.totalGemResistanceModifications.ice += resistances.ice;
|
||||
tower.totalGemResistanceModifications.fire += resistances.fire;
|
||||
tower.totalGemResistanceModifications.divine += resistances.divine;
|
||||
tower.totalGemResistanceModifications.frostfire += resistances.frostfire;
|
||||
});
|
||||
|
||||
tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage;
|
||||
tower.computedCooldown = tower.definition.stats.cooldown - gemAttackSpeedUp;
|
||||
tower.computedRange = tower.definition.stats.range + gemRangeUp;
|
||||
tower.computedTimeToLive = tower.definition.stats.timeToLive + gemTimeToLiveUp;
|
||||
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) {
|
||||
computeGemImprovements(tower);
|
||||
projectileCheck(tower, elapsedMS);
|
||||
@ -96,7 +114,6 @@ export function CircleTowerBehaviour(tower: Tower, elapsedMS: number) {
|
||||
tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier;
|
||||
let creepsInRange = tower.GetCreepsInRange();
|
||||
if (creepsInRange.length > 0) {
|
||||
let focus = creepsInRange[0];
|
||||
if (tower.millisecondsUntilNextShot <= 0) {
|
||||
tower.millisecondsUntilNextShot = tower.computedCooldown;
|
||||
let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2;
|
||||
@ -119,34 +136,52 @@ export function ElectricTowerBehaviour(tower: Tower, elapsedMS: number) {
|
||||
|
||||
if (tower.millisecondsUntilNextShot > 0)
|
||||
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));
|
||||
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);
|
||||
projectileCheck(tower, elapsedMS);
|
||||
|
||||
if (tower.millisecondsUntilNextShot > 0)
|
||||
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));
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
// and tower takes improvements via computeGemImprovements()
|
||||
}
|
||||
|
||||
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);
|
||||
projectileCheck(tower, elapsedMS);
|
||||
|
||||
|
@ -2,7 +2,7 @@ import * as PIXI from 'pixi.js';
|
||||
import { Engine } from '../Bastion';
|
||||
import { TerrainType, TowerDefinition } from '../Definitions';
|
||||
import GameAssets from '../Assets';
|
||||
import { Tower } from './Tower';
|
||||
import { distance, Tower } from './Tower';
|
||||
import { Cell } from './Grid';
|
||||
import { GridEvents, TowerEvents } from '../Events';
|
||||
|
||||
@ -10,11 +10,11 @@ export enum TowerBehaviours {
|
||||
BasicTowerBehaviour = 'BasicTowerBehaviour',
|
||||
CircleTowerBehaviour = 'CircleTowerBehaviour',
|
||||
ElectricTowerBehaviour = 'ElectricTowerBehaviour',
|
||||
QuickTowerBehaviour = 'QuickTowerBehaviour',
|
||||
BuffTowerBehaviour = 'BuffTowerBehaviour',
|
||||
StrongTowerBehaviour = 'StrongTowerBehaviour',
|
||||
RailTowerBehaviour = 'RailTowerBehaviour',
|
||||
TrapperTowerBehaviour = 'TrapperTowerBehaviour',
|
||||
AdvancedTowerBehaviour = 'AdvancedTowerBehaviour',
|
||||
DebuffTowerBehaviour = 'DebuffTowerBehaviour',
|
||||
}
|
||||
|
||||
export default class TowerManager {
|
||||
@ -127,10 +127,10 @@ export default class TowerManager {
|
||||
while (twr.slottedGems.length > 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);
|
||||
twr.destroy();
|
||||
this.towers.splice(idx, 1);
|
||||
Engine.GameScene.events.emit(TowerEvents.TowerSoldEvent, twr.name, twr.row, twr.column);
|
||||
} else twr.update(elapsedMS);
|
||||
});
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { AnimationManager } from './classes/game/AnimationManager';
|
||||
import NotificationManager from './classes/game/NotificationManager';
|
||||
import GameUIConstants from './classes/GameUIConstants';
|
||||
import KeyboardManager from './classes/game/KeyboardManager';
|
||||
import { GemType } from './classes/Definitions';
|
||||
import DebrisManager from './classes/game/DebrisManager';
|
||||
|
||||
(async () => {
|
||||
const app = new PIXI.Application();
|
||||
@ -54,10 +54,12 @@ import { GemType } from './classes/Definitions';
|
||||
new GameMaster();
|
||||
Engine.AnimationManager = new AnimationManager();
|
||||
Engine.NotificationManager = new NotificationManager();
|
||||
Engine.DebrisManager = new DebrisManager();
|
||||
globalThis.Engine = Engine;
|
||||
PIXI.Ticker.shared.add((ticker) => {
|
||||
Engine.NotificationManager.update(ticker.elapsedMS);
|
||||
Engine.AnimationManager.update(ticker.elapsedMS);
|
||||
Engine.DebrisManager.update(ticker.elapsedMS);
|
||||
});
|
||||
app.canvas.addEventListener('pointermove', function (event) {
|
||||
Engine.MouseX = ((event.clientX - app.canvas.offsetLeft) / app.canvas.offsetWidth) * 1920;
|
||||
|