bastion.overflow.fun/src/classes/game/TowerBehaviours.ts

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));
}
}
}