258 lines
11 KiB
TypeScript
258 lines
11 KiB
TypeScript
import GameAssets from '../Assets';
|
|
import { Engine } from '../Bastion';
|
|
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.
|
|
* If a projectile is marked for deletion, it is removed from the tower's projectiles array
|
|
* and the tower's damage dealt is incremented.
|
|
*
|
|
* @param tower - The tower whose projectiles are being checked.
|
|
* @param elapsedMS - The elapsed time in milliseconds since the last update.
|
|
*/
|
|
function projectileCheck(tower: Tower, elapsedMS: number) {
|
|
tower.projectiles.forEach((proj) => {
|
|
if (proj.deleteMe || tower.sold) {
|
|
proj.collidedCreepIDs.forEach(() => {
|
|
tower.damageDealt += tower.computedDamageToDeal;
|
|
});
|
|
proj.collidedCreepIDs = [];
|
|
tower.projectiles.splice(tower.projectiles.indexOf(proj), 1);
|
|
proj.destroy();
|
|
proj = null;
|
|
} else proj.update(elapsedMS);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Computes the total damage the tower can deal by summing its base damage and the damage
|
|
* improvements from its slotted gems. Mutates passed tower.
|
|
*
|
|
* @param tower - The tower whose damage is being computed.
|
|
*/
|
|
export function computeGemImprovements(tower: Tower) {
|
|
let gemDamage = 0;
|
|
let gemAttackSpeedUp = 0;
|
|
let gemRangeUp = 0;
|
|
let gemTimeToLiveUp = 0;
|
|
let gemPierceUp = 0;
|
|
tower.totalGemResistanceModifications = {
|
|
fire: 0,
|
|
frostfire: 0,
|
|
divine: 0,
|
|
ice: 0,
|
|
physical: 0,
|
|
};
|
|
tower.slottedGems.forEach((gem) => {
|
|
let improvements = gem.currentGemImprovement();
|
|
gemDamage += improvements.damageUp;
|
|
gemAttackSpeedUp += improvements.attackSpeedUp;
|
|
gemRangeUp += improvements.rangeUp;
|
|
gemTimeToLiveUp += improvements.timeToLiveUp;
|
|
gemPierceUp += improvements.pierceUp;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
export function BasicTowerBehaviour(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));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function CircleTowerBehaviour(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) {
|
|
if (tower.millisecondsUntilNextShot <= 0) {
|
|
tower.millisecondsUntilNextShot = tower.computedCooldown;
|
|
let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2;
|
|
let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2;
|
|
tower.Shoot(calculateAngleToPoint(x, y, x, y + 10)); // Up
|
|
tower.Shoot(calculateAngleToPoint(x, y, x + 10, y)); // Right
|
|
tower.Shoot(calculateAngleToPoint(x, y, x - 10, y)); // Left
|
|
tower.Shoot(calculateAngleToPoint(x, y, x, y - 10)); // Down
|
|
tower.Shoot(calculateAngleToPoint(x, y, x + 10, y + 10)); // Up right
|
|
tower.Shoot(calculateAngleToPoint(x, y, x - 10, y + 10)); // Up left
|
|
tower.Shoot(calculateAngleToPoint(x, y, x - 10, y - 10)); // Down left
|
|
tower.Shoot(calculateAngleToPoint(x, y, x + 10, y - 10)); // Down right
|
|
}
|
|
}
|
|
}
|
|
|
|
export function ElectricTowerBehaviour(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;
|
|
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 BuffTowerBehaviour(tower: Tower, elapsedMS: number) {
|
|
computeGemImprovements(tower);
|
|
// 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) {
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function RailTowerBehaviour(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));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function TrapperTowerBehaviour(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));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function DebuffTowerBehaviour(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));
|
|
}
|
|
}
|
|
}
|