basic pausing

This commit is contained in:
koneko 2025-02-09 22:52:10 +01:00
parent 52c67d9b1c
commit 07db180b5d
13 changed files with 142 additions and 26 deletions

View File

@ -17,7 +17,7 @@ List of things to implement following the "release" of the minimum viable produc
- [x] Tower info on click - [x] Tower info on click
- [x] Animate projectiles - [x] Animate projectiles
- [x] Better mouseover tracking when placing tower and showing radius - [x] Better mouseover tracking when placing tower and showing radius
- [ ] Sell tower button - [x] Sell tower button
## Gems ## Gems

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -49,5 +49,56 @@
"frostfire": 0 "frostfire": 0
} }
} }
},
{
"name": "cloaker",
"textures": [],
"textureArrayLength": 12,
"stats": {
"health": 12,
"speed": 2,
"special": null,
"resistance": {
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0
}
}
},
{
"name": "demon",
"textures": [],
"textureArrayLength": 8,
"stats": {
"health": 12,
"speed": 2,
"special": null,
"resistance": {
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0
}
}
},
{
"name": "maker",
"textures": [],
"textureArrayLength": 11,
"stats": {
"health": 11,
"speed": 2,
"special": null,
"resistance": {
"physical": 0,
"divine": 0,
"fire": 0,
"ice": 0,
"frostfire": 0
}
}
} }
] ]

View File

@ -108,6 +108,9 @@ export enum CreepType {
Basic = 0, Basic = 0,
Quick = 1, Quick = 1,
Tank = 2, Tank = 2,
Cloaker = 3,
Demon = 4,
Maker = 5,
} }
export enum GemType { export enum GemType {

View File

@ -67,6 +67,7 @@ export class Cell extends GameObject {
Engine.GameScene.events.on(TowerEvents.TowerSoldEvent, (_, row, col) => { Engine.GameScene.events.on(TowerEvents.TowerSoldEvent, (_, 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();
} }
}); });

View File

@ -1,7 +1,7 @@
/** /**
* Handles keyboard events. * Handles keyboard events.
*/ */
class KeyboardManager { export default class KeyboardManager {
private static listeners: ((event: KeyboardEvent) => void)[] = []; private static listeners: ((event: KeyboardEvent) => void)[] = [];
public static init() { public static init() {
@ -35,7 +35,7 @@ class KeyboardManager {
private static handleKeyDown(event: KeyboardEvent) { private static handleKeyDown(event: KeyboardEvent) {
if (KeyboardManager.listeners.length > 0) { if (KeyboardManager.listeners.length > 0) {
console.log(`Key down: ${event.key}`); // console.log(`Key down: ${event.key}`);
for (let i = KeyboardManager.listeners.length - 1; i >= 0; i--) { for (let i = KeyboardManager.listeners.length - 1; i >= 0; i--) {
KeyboardManager.listeners[i](event); KeyboardManager.listeners[i](event);
if (event.defaultPrevented) { if (event.defaultPrevented) {
@ -45,5 +45,3 @@ class KeyboardManager {
} }
} }
} }
export default KeyboardManager;

View File

@ -16,6 +16,8 @@ export function distance(x1, y1, x2, y2) {
export class Tower extends GameObject { export class Tower extends GameObject {
public row: number; public row: number;
public column: number; public column: number;
public setAsSold: boolean = false;
public sold: boolean = false;
public definition: TowerDefinition; public definition: TowerDefinition;
public slottedGems: Array<Gem> = []; public slottedGems: Array<Gem> = [];
public damageDealt: number = 0; public damageDealt: number = 0;
@ -74,6 +76,7 @@ export class Tower extends GameObject {
} }
public UnslotGem(index) { public UnslotGem(index) {
const gem = this.slottedGems.splice(index, 1)[0]; const gem = this.slottedGems.splice(index, 1)[0];
if (gem == null || !gem) return console.warn('UnslotGem: Gem is null.');
Engine.GameScene.MissionStats.giveGem(gem, true); Engine.GameScene.MissionStats.giveGem(gem, true);
for (let i = index; i < this.slottedGems.length - 1; i++) { for (let i = index; i < this.slottedGems.length - 1; i++) {
if (this.slottedGems[i] == null) { if (this.slottedGems[i] == null) {
@ -113,14 +116,6 @@ export class Tower extends GameObject {
} }
combinedTint = color; combinedTint = color;
} }
// this.slottedGems.forEach((gem) => {
// let rgb = new PIXI.Color(gem.definition.color).toRgb();
// combinedTint =
// ((combinedTint & 0xff0000) + (rgb.r << 16)) |
// ((combinedTint & 0x00ff00) + (rgb.g << 8)) |
// ((combinedTint & 0x0000ff) + rgb.b);
// });
// combinedTint = new PIXI.Color(this.slottedGems[0].definition.color).
let proj = new Projectile( let proj = new Projectile(
x, x,
y, y,
@ -135,7 +130,15 @@ export class Tower extends GameObject {
this.projectiles.push(proj); this.projectiles.push(proj);
return proj; return proj;
} }
public Sell() {
this.setAsSold = true;
// Selling logic is handled in TowerManager.update()
}
public update(elapsedMS: any): void { public update(elapsedMS: any): void {
if (this.sold) return;
if (this.setAsSold) {
this.sold = true;
}
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);
} }

View File

@ -12,12 +12,13 @@ import { Tower } from './Tower';
*/ */
function projectileCheck(tower: Tower, elapsedMS: number) { function projectileCheck(tower: Tower, elapsedMS: number) {
tower.projectiles.forEach((proj) => { tower.projectiles.forEach((proj) => {
if (proj.deleteMe) { if (proj.deleteMe || tower.sold) {
proj.collidedCreepIDs.forEach(() => { proj.collidedCreepIDs.forEach(() => {
tower.damageDealt += tower.computedDamageToDeal; tower.damageDealt += tower.computedDamageToDeal;
}); });
proj.collidedCreepIDs = []; proj.collidedCreepIDs = [];
tower.projectiles.splice(tower.projectiles.indexOf(proj), 1); tower.projectiles.splice(tower.projectiles.indexOf(proj), 1);
proj.destroy();
proj = null; proj = null;
} else proj.update(elapsedMS); } else proj.update(elapsedMS);
}); });

View File

@ -114,8 +114,17 @@ export default class TowerManager {
} }
} }
public update(elapsedMS) { public update(elapsedMS) {
this.towers.forEach((twr) => { this.towers.forEach((twr, idx) => {
twr.update(elapsedMS); if (twr.sold) {
twr.slottedGems = twr.slottedGems.filter((gem) => gem != null);
while (twr.slottedGems.length > 0) {
twr.UnslotGem(0);
}
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);
}); });
} }
} }

View File

@ -1,6 +1,5 @@
import * as PIXI from 'pixi.js'; import * as PIXI from 'pixi.js';
import ModalDialogBase from './ModalDialog'; import ModalDialogBase from './ModalDialog';
import GuiObject from '../GuiObject';
export default class MessageBox extends ModalDialogBase { export default class MessageBox extends ModalDialogBase {
private caption: string; private caption: string;

View File

@ -1,6 +1,5 @@
import * as PIXI from 'pixi.js'; import * as PIXI from 'pixi.js';
import GuiObject from '../GuiObject'; import GuiObject from '../GuiObject';
import Assets from '../Assets';
import { Engine } from '../Bastion'; import { Engine } from '../Bastion';
import GameAssets from '../Assets'; import GameAssets from '../Assets';
import Button, { ButtonTexture } from './Button'; import Button, { ButtonTexture } from './Button';
@ -46,9 +45,9 @@ export default abstract class ModalDialogBase extends GuiObject {
const contentBounds = `x: ${Math.round(this.dialogContent.x)}, y: ${Math.round( const contentBounds = `x: ${Math.round(this.dialogContent.x)}, y: ${Math.round(
this.dialogContent.y this.dialogContent.y
)}, width: ${Math.round(this.dialogContent.width)}, height: ${Math.round(this.dialogContent.height)}`; )}, width: ${Math.round(this.dialogContent.width)}, height: ${Math.round(this.dialogContent.height)}`;
console.debug( // console.debug(
`ModalDialogBase.show(dialog: ${dialogBounds}, content: ${contentBounds}, buttons: ${this.buttonCaptions})` // `ModalDialogBase.show(dialog: ${dialogBounds}, content: ${contentBounds}, buttons: ${this.buttonCaptions})`
); // );
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Engine.app.stage.addChild(this.container); Engine.app.stage.addChild(this.container);
this.onClosed = (button) => { this.onClosed = (button) => {

View File

@ -114,6 +114,7 @@ export default class TowerPanel extends GuiObject {
public frostFireResDamage: PIXI.Text; public frostFireResDamage: PIXI.Text;
public divineResDamage: PIXI.Text; public divineResDamage: PIXI.Text;
public physicalResDamage: PIXI.Text; public physicalResDamage: PIXI.Text;
private sellButton: Button;
constructor(bounds: PIXI.Rectangle) { constructor(bounds: PIXI.Rectangle) {
super(false); super(false);
@ -281,6 +282,14 @@ export default class TowerPanel extends GuiObject {
}), }),
}); });
this.container.addChild(this.physicalResDamage); this.container.addChild(this.physicalResDamage);
this.sellButton = new Button(
new PIXI.Rectangle(5, this.towerPanel.height - 70, this.towerPanel.width - 115, 60),
'Sell',
ButtonTexture.Button02,
true
);
this.sellButton.container.removeFromParent();
this.container.addChild(this.sellButton.container);
} }
private MakeSlots(tower: Tower) { private MakeSlots(tower: Tower) {
this.vGems.forEach((vGem) => { this.vGems.forEach((vGem) => {
@ -320,7 +329,7 @@ export default class TowerPanel extends GuiObject {
this.MakeSlots(tower); this.MakeSlots(tower);
this.showingTower = tower; this.showingTower = tower;
Engine.GameScene.sidebar.gemTab.selectingGemTowerObject = tower; Engine.GameScene.sidebar.gemTab.selectingGemTowerObject = tower;
if (tower.container.parent.x < 900) { if (tower.container.parent.x < 1270) {
this.ShowRight(); this.ShowRight();
} else { } else {
this.ShowLeft(); this.ShowLeft();
@ -339,6 +348,11 @@ export default class TowerPanel extends GuiObject {
this.frostFireResDamage.text = `+${tower.totalGemResistanceModifications.frostfire * 100}% FrostFire damage`; this.frostFireResDamage.text = `+${tower.totalGemResistanceModifications.frostfire * 100}% FrostFire damage`;
this.divineResDamage.text = `+${tower.totalGemResistanceModifications.divine * 100}% Divine damage`; this.divineResDamage.text = `+${tower.totalGemResistanceModifications.divine * 100}% Divine damage`;
this.physicalResDamage.text = `+${tower.totalGemResistanceModifications.physical * 100}% Physical damage`; this.physicalResDamage.text = `+${tower.totalGemResistanceModifications.physical * 100}% Physical damage`;
this.sellButton.setCaption('Sell for ' + tower.definition.stats.cost + ' gold');
this.sellButton.onClick = () => {
tower.Sell();
this.Hide();
};
} }
private ShowLeft() { private ShowLeft() {
this.towerPanel.x = -100; this.towerPanel.x = -100;

View File

@ -22,7 +22,7 @@ import HighScoreDialog, { HighScoreDialogButtons } from '../classes/gui/HighScor
enum RoundMode { enum RoundMode {
Purchase = 0, Purchase = 0,
Combat = 1, Combat = 1,
OfferingGems = 2, Misc = 2,
} }
export class GameScene extends Scene { export class GameScene extends Scene {
@ -36,6 +36,8 @@ export class GameScene extends Scene {
public sidebar: Sidebar; public sidebar: Sidebar;
public tooltip: Tooltip; public tooltip: Tooltip;
public towerPanel: TowerPanel; public towerPanel: TowerPanel;
public isPaused: boolean = false;
private pauseButton: Button;
private visualGems: VisualGemSlot[] = []; private visualGems: VisualGemSlot[] = [];
private currentRound: number = 0; private currentRound: number = 0;
private isWaveManagerFinished: boolean = false; private isWaveManagerFinished: boolean = false;
@ -104,12 +106,12 @@ export class GameScene extends Scene {
if (this.roundMode == RoundMode.Combat) if (this.roundMode == RoundMode.Combat)
return Engine.NotificationManager.Notify('Wave is already in progress.', 'warn'); return Engine.NotificationManager.Notify('Wave is already in progress.', 'warn');
if (this.isGameOver) return Engine.NotificationManager.Notify('No more waves.', 'danger'); if (this.isGameOver) return Engine.NotificationManager.Notify('No more waves.', 'danger');
if (this.roundMode == RoundMode.OfferingGems) return; if (this.roundMode == RoundMode.Misc) return;
this.setRoundMode(RoundMode.Combat); this.setRoundMode(RoundMode.Combat);
this.changeRoundButton.buttonIcon.texture = GameAssets.ExclamationIconTexture; this.changeRoundButton.buttonIcon.texture = GameAssets.ExclamationIconTexture;
this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`); this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`);
}; };
this.MissionStats = new MissionStats(100, 200); this.MissionStats = new MissionStats(125, 450);
this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => { this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => {
if (gem == null) { if (gem == null) {
if (!this.MissionStats.checkIfPlayerHasAnyGems()) if (!this.MissionStats.checkIfPlayerHasAnyGems())
@ -120,6 +122,30 @@ export class GameScene extends Scene {
} }
this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower); this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower);
}); });
this.pauseButton = new Button(new PIXI.Rectangle(5, 5, 120, 80), '', ButtonTexture.Button01, true);
this.pauseButton.container.removeFromParent();
this.stage.addChild(this.pauseButton.container);
this.pauseButton.CustomButtonLogic = () => {
this.pauseButton.buttonIcon = new PIXI.Sprite({
texture: GameAssets.PauseIconTexture,
x: this.pauseButton.container.width / 2,
y: this.pauseButton.container.height / 2,
scale: 0.2,
});
this.pauseButton.buttonIcon.anchor.set(0.5, 0.5);
this.pauseButton.container.addChild(this.pauseButton.buttonIcon);
};
this.pauseButton.CustomButtonLogic();
this.pauseButton.onClick = () => {
if (this.isPaused) {
this.UnpauseGame();
} else {
this.ShowPauseDialog();
this.PauseGame();
}
};
this.ticker = new PIXI.Ticker(); this.ticker = new PIXI.Ticker();
this.ticker.maxFPS = 60; this.ticker.maxFPS = 60;
this.ticker.minFPS = 30; this.ticker.minFPS = 30;
@ -183,7 +209,7 @@ export class GameScene extends Scene {
Engine.Grid.gridInteractionEnabled = false; Engine.Grid.gridInteractionEnabled = false;
Engine.GameScene.sidebar.towerTab.resetTint(); Engine.GameScene.sidebar.towerTab.resetTint();
Engine.TowerManager.ResetChooseTower(); Engine.TowerManager.ResetChooseTower();
this.setRoundMode(RoundMode.OfferingGems); this.setRoundMode(RoundMode.Misc);
let gemsToOffer = this.mission.rounds[this.currentRound].offeredGems; let gemsToOffer = this.mission.rounds[this.currentRound].offeredGems;
this.DarkenScreen(); this.DarkenScreen();
this.offerGemsSprite = new PIXI.NineSliceSprite({ this.offerGemsSprite = new PIXI.NineSliceSprite({
@ -245,6 +271,18 @@ export class GameScene extends Scene {
this.setRoundMode(RoundMode.Purchase); this.setRoundMode(RoundMode.Purchase);
} }
public PauseGame() {
this.isPaused = true;
this.ticker.stop();
}
public UnpauseGame() {
this.isPaused = false;
this.ticker.start();
}
public ShowPauseDialog() {
console.warn("Pause dialog doesn't exist.");
}
private async ShowEndgameDialog(lost) { private async ShowEndgameDialog(lost) {
const endGameDialog = new EndGameDialog(this.mission.name, this.MissionStats, lost); const endGameDialog = new EndGameDialog(this.mission.name, this.MissionStats, lost);
await endGameDialog.show(); await endGameDialog.show();