main mechanic minimally implemented

This commit is contained in:
koneko 2025-02-04 12:06:28 +01:00
parent b99c3979cf
commit 9847b8e47d
8 changed files with 161 additions and 33 deletions

View File

@ -32,7 +32,7 @@ export class Engine {
public static TestSuite() {
Engine.NotificationManager.Notify('Loaded testing suite.', 'danger');
Engine.TowerManager.ToggleChoosingTowerLocation('RESET');
Engine.TowerManager.PlaceTower(GameAssets.Towers[1], 10, 15, GameAssets.Towers[0].behaviour, true);
Engine.TowerManager.PlaceTower(GameAssets.Towers[1], 8, 10, GameAssets.Towers[0].behaviour, true);
for (let i = 0; i < 16; i++) {
this.GameScene.MissionStats.giveGem(new Gem(GemType.Fire), true);
}

View File

@ -1,5 +1,5 @@
import * as PIXI from 'pixi.js';
import { GemType, GemDefinition } from '../Definitions';
import { GemType, GemDefinition, GenericGemImprovement } from '../Definitions';
import GameAssets from '../Assets';
let latestGemId = 0;
@ -8,11 +8,32 @@ export default class Gem {
public texture: PIXI.Texture;
public level: number = 1;
public definition: GemDefinition;
public id;
public id: number;
constructor(gemType: GemType) {
this.definition = GameAssets.Gems[gemType];
this.texture = this.definition.textures[0];
this.id = latestGemId + 1;
latestGemId++;
}
public currentGemImprovement() {
let totalGemImprovement: GenericGemImprovement = {
damageUp: 0,
attackSpeedUp: 0,
rangeUp: 0,
timeToLiveUp: 0,
pierceUp: 0,
};
for (let i = 0; i < this.level; i++) {
const item = this.definition.genericImprovements[i];
totalGemImprovement.damageUp += item.damageUp;
totalGemImprovement.attackSpeedUp += item.attackSpeedUp;
totalGemImprovement.rangeUp += item.rangeUp;
totalGemImprovement.timeToLiveUp += item.timeToLiveUp;
totalGemImprovement.pierceUp += item.pierceUp;
}
return totalGemImprovement;
}
public currentGemResistanceModifications() {
return this.definition.gemResistanceModifications[this.level - 1];
}
}

View File

@ -47,11 +47,11 @@ export class Cell extends GameObject {
else this.OpenSelectedTowerPanel();
});
this.clickDetector.on('pointerenter', (e) => {
if (!Engine.Grid.gridInteractionEnabled) return;
if (!Engine.Grid.gridInteractionEnabled || Engine.GameScene.towerPanel.isShown) return;
Engine.GameScene.events.emit(GridEvents.CellMouseOver, this);
});
this.clickDetector.on('pointerleave', (e) => {
if (!Engine.Grid.gridInteractionEnabled) return;
if (!Engine.Grid.gridInteractionEnabled || Engine.GameScene.towerPanel.isShown) return;
Engine.GameScene.events.emit(GridEvents.CellMouseLeave, this);
Engine.Grid.rangePreview.clear();
});

View File

@ -3,6 +3,7 @@ import GameObject from '../GameObject';
import { Engine } from '../Bastion';
import Creep from './Creep';
import { CreepEvents } from '../Events';
import { Tower } from './Tower';
export function calculateAngleToPoint(x, y, targetX, targetY) {
const dx = targetX - x;
@ -19,16 +20,19 @@ export default class Projectile extends GameObject {
public speed: number;
public damage: number;
public timeToLive: number = 1;
constructor(x, y, textures, angle, damage) {
public parent: Tower;
constructor(x, y, textures, angle, damage, tint, tower) {
super();
this.x = x;
this.y = y;
this.parent = tower;
this.damage = damage;
this.sprite = new PIXI.AnimatedSprite({ textures: textures, scale: 0.25, rotation: angle });
this.sprite.anchor.set(0.5, 0.5);
this.sprite.play();
this.container.x = this.x;
this.container.y = this.y;
this.sprite.tint = tint;
this.container.addChild(this.sprite);
Engine.GameMaster.currentScene.stage.addChild(this.container);

View File

@ -7,6 +7,7 @@ import { TowerBehaviours } from './TowerManager';
import Projectile, { calculateAngleToPoint } from './Projectile';
import Creep from './Creep';
import Gem from './Gem';
import { BasicTowerBehaviour } from './TowerBehaviours';
export function distance(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
@ -18,12 +19,13 @@ export class Tower extends GameObject {
public definition: TowerDefinition;
public slottedGems: Array<Gem> = [];
public damageDealt: number = 0;
private projectiles: Projectile[] = [];
private behaviour: string;
private sprite: PIXI.Sprite;
private ticksUntilNextShot: number;
private graphics: PIXI.Graphics = new PIXI.Graphics();
private parent: Cell;
public projectiles: Projectile[] = [];
public behaviour: string;
public sprite: PIXI.Sprite;
public ticksUntilNextShot: number;
public graphics: PIXI.Graphics = new PIXI.Graphics();
public computedDamageToDeal: number;
public parent: Cell;
constructor(row, column, texture, definition, behaviour) {
super();
@ -40,6 +42,7 @@ export class Tower extends GameObject {
zIndex: 130,
});
this.container.addChild(this.sprite);
this.computedDamageToDeal = this.definition.stats.damage;
this.parent.container.addChild(this.container);
this.container.interactiveChildren = true;
this.parent.clickDetector.on('pointerenter', this.onParentCellEnter);
@ -48,7 +51,11 @@ export class Tower extends GameObject {
}
private onParentCellEnter = (e) => {
if (!Engine.TowerManager.isPlacingTower && Engine.Grid.gridInteractionEnabled)
if (
!Engine.TowerManager.isPlacingTower &&
Engine.Grid.gridInteractionEnabled &&
!Engine.GameScene.towerPanel.isShown
)
this.parent.showRangePreview(false, this.definition.stats.range);
};
@ -94,29 +101,16 @@ export class Tower extends GameObject {
let x = this.column * Engine.GridCellSize + Engine.GridCellSize / 2;
let y = this.row * Engine.GridCellSize + Engine.GridCellSize / 2;
let angle = calculateAngleToPoint(x, y, creep.x, creep.y);
let tint = 0xffffff;
this.slottedGems.forEach((gem) => {
if (gem.definition.type.toString() == 'Fire') tint = 0xff0000;
});
this.projectiles.push(
new Projectile(x, y, this.definition.projectileTextures, angle, this.definition.stats.damage)
new Projectile(x, y, this.definition.projectileTextures, angle, this.computedDamageToDeal, tint, this)
);
}
public update(elapsedMS: any): void {
this.projectiles.forEach((proj) => {
if (proj.deleteMe) {
this.damageDealt += this.definition.stats.damage;
this.projectiles.splice(this.projectiles.indexOf(proj), 1);
proj = null;
} else proj.update(elapsedMS);
});
if (this.behaviour == TowerBehaviours.BasicTowerBehaviour) {
if (this.ticksUntilNextShot > 0) this.ticksUntilNextShot--;
let creepsInRange = this.GetCreepsInRange();
if (creepsInRange.length > 0) {
let focus = creepsInRange[0];
if (this.ticksUntilNextShot == 0) {
this.ticksUntilNextShot = this.definition.stats.cooldown;
this.Shoot(focus);
}
}
}
if (this.behaviour == TowerBehaviours.BasicTowerBehaviour) BasicTowerBehaviour(this, elapsedMS);
}
public destroy(): void {

View File

@ -0,0 +1,54 @@
import { Tower } from './Tower';
/**
* 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.damageDealt += tower.computedDamageToDeal;
tower.projectiles.splice(tower.projectiles.indexOf(proj), 1);
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 computeDamage(tower: Tower) {
let gemDamage = 0;
tower.slottedGems.forEach((gem) => {
gemDamage += gem.currentGemImprovement().damageUp;
});
tower.computedDamageToDeal = tower.definition.stats.damage + gemDamage;
}
/**
* 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) {
computeDamage(tower);
projectileCheck(tower, elapsedMS);
if (tower.ticksUntilNextShot > 0) tower.ticksUntilNextShot--;
let creepsInRange = tower.GetCreepsInRange();
if (creepsInRange.length > 0) {
let focus = creepsInRange[0];
if (tower.ticksUntilNextShot == 0) {
tower.ticksUntilNextShot = tower.definition.stats.cooldown;
tower.Shoot(focus);
}
}
}

View File

@ -7,6 +7,7 @@ import Button, { ButtonTexture } from './Button';
import { Tower } from '../game/Tower';
import Gem from '../game/Gem';
import { GemEvents } from '../Events';
import { computeDamage } from '../game/TowerBehaviours';
export class VisualGemSlot extends GuiObject {
public iconSprite: PIXI.Sprite;
@ -89,6 +90,9 @@ export default class TowerPanel extends GuiObject {
public showingTower: Tower = null;
public isShown: boolean = false;
public titleText: PIXI.Text;
public damageText: PIXI.Text;
public totalDamage: PIXI.Text;
public attackSpeedText: PIXI.Text;
constructor(bounds: PIXI.Rectangle) {
super(false);
@ -139,6 +143,49 @@ export default class TowerPanel extends GuiObject {
});
this.titleText.anchor.set(0.5, 0);
this.container.addChild(this.titleText);
this.damageText = new PIXI.Text({
x: 10,
y: 100,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0xffa500, // orange color
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.damageText);
this.attackSpeedText = new PIXI.Text({
x: 100,
y: 100,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0xffffff,
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.attackSpeedText);
this.totalDamage = new PIXI.Text({
x: 10,
y: 130,
zIndex: 5,
style: new PIXI.TextStyle({
fill: 0xff0000,
fontSize: 18,
stroke: {
color: 0x000000,
width: 2,
},
}),
});
this.container.addChild(this.totalDamage);
}
private MakeSlots(tower: Tower) {
this.vGems.forEach((vGem) => {
@ -173,18 +220,24 @@ export default class TowerPanel extends GuiObject {
}
public Show(tower: Tower) {
this.isShown = true;
computeDamage(tower);
this.SetContent(tower);
this.MakeSlots(tower);
this.showingTower = tower;
Engine.GameScene.sidebar.gemTab.selectingGemTowerObject = tower;
if (tower.container.x < 900) {
if (tower.container.parent.x < 900) {
this.ShowRight();
} else {
this.ShowLeft();
}
tower.parent.showRangePreview(false, tower.definition.stats.range);
}
private SetContent(tower: Tower) {
this.titleText.text = tower.definition.name;
this.damageText.text = 'Deals ' + tower.computedDamageToDeal + ' damage';
this.totalDamage.text = 'Damage dealt: ' + tower.damageDealt + ' damage';
this.attackSpeedText.x = this.damageText.width + 10;
this.attackSpeedText.text = ` every ${Math.floor(tower.definition.stats.cooldown / 60)}s`;
}
private ShowLeft() {
this.towerPanel.x = -100;
@ -202,5 +255,6 @@ export default class TowerPanel extends GuiObject {
this.isShown = false;
this.container.alpha = 0;
this.container.x = GameUIConstants.SidebarRect.x + 10;
Engine.Grid.rangePreview.clear();
}
}

View File

@ -238,6 +238,7 @@ export class GameScene extends Scene {
this.visualGems.forEach((item) => item.destroy());
Engine.Grid.gridInteractionEnabled = true;
this.MissionStats.giveGem(gem);
this.setRoundMode(RoundMode.Purchase);
}
private ShowScoreScreen(lost) {