Dalibor Čarapić f1148e1d07 Pause menu
2025-02-10 16:50:26 +01:00

345 lines
14 KiB
TypeScript

import GameAssets from '../classes/Assets';
import { Engine } from '../classes/Bastion';
import { MissionDefinition } from '../classes/Definitions';
import Creep from '../classes/game/Creep';
import { Grid } from '../classes/game/Grid';
import WaveManager from '../classes/game/WaveManager';
import { WaveManagerEvents, CreepEvents, GemEvents } from '../classes/Events';
import Sidebar from '../classes/gui/Sidebar';
import Button, { ButtonTexture } from '../classes/gui/Button';
import Scene from './Scene';
import * as PIXI from 'pixi.js';
import MissionStats from '../classes/game/MissionStats';
import TowerManager from '../classes/game/TowerManager';
import { MissionPickerScene } from './MissionPicker';
import GameUIConstants from '../classes/GameUIConstants';
import Tooltip from '../classes/gui/Tooltip';
import TowerPanel, { VisualGemSlot } from '../classes/gui/TowerPanel';
import Gem from '../classes/game/Gem';
import EndGameDialog from '../classes/gui/EndGameDialog';
import HighScoreDialog, { HighScoreDialogButtons } from '../classes/gui/HighScoreDialog';
import GamePausedDialog from '../classes/gui/GamePausedDialog';
enum RoundMode {
Purchase = 0,
Combat = 1,
Misc = 2,
}
export class GameScene extends Scene {
public isGameOver: boolean = false;
public mission: MissionDefinition;
public missionIndex: number;
public MissionStats: MissionStats;
public roundMode: RoundMode;
public ticker: PIXI.Ticker;
public changeRoundButton: Button;
public sidebar: Sidebar;
public tooltip: Tooltip;
public towerPanel: TowerPanel;
public isPaused: boolean = false;
private isFastForwarded: boolean = false;
private pauseButton: Button;
private visualGems: VisualGemSlot[] = [];
private currentRound: number = 0;
private isWaveManagerFinished: boolean = false;
private playerWon: boolean = false;
private destroyTicker: boolean = false;
private offerGemsSprite: PIXI.NineSliceSprite;
private dimGraphics: PIXI.Graphics = new PIXI.Graphics({
x: 0,
y: 0,
zIndex: 120,
});
constructor(name: string) {
super();
Engine.GameScene = this;
GameAssets.Missions.forEach((mission, index) => {
if (mission.name == name) {
this.mission = mission;
this.missionIndex = index;
}
});
}
public init() {
Engine.latestGemId = 0;
new Grid(this.mission.gameMap, this.missionIndex);
new TowerManager();
new WaveManager(this.mission.rounds, this.mission.gameMap.paths);
Engine.Grid.onGridCellClicked = (row, column) => {
if (Engine.TowerManager.isPlacingTower) {
Engine.TowerManager.PlayerClickOnGrid(row, column);
}
};
Engine.WaveManager.events.on(WaveManagerEvents.CreepSpawned, (creep: Creep) => {
Engine.Grid.addCreep(creep);
creep.events.on(CreepEvents.Escaped, () => {
this.onCreepEscaped(creep);
});
});
Engine.WaveManager.events.on(WaveManagerEvents.Finished, () => {
this.isWaveManagerFinished = true;
});
this.events.on(CreepEvents.Died, (playerAward, creepThatDied) => {
this.MissionStats.earnGold(playerAward);
});
this.towerPanel = new TowerPanel(GameUIConstants.SidebarRect);
this.sidebar = new Sidebar(GameUIConstants.SidebarRect);
this.changeRoundButton = new Button(GameUIConstants.ChangeRoundButtonRect, '', ButtonTexture.Button01, true);
this.changeRoundButton.container.removeFromParent();
this.sidebar.container.addChild(this.changeRoundButton.container);
Engine.GameMaster.currentScene.stage.addChildAt(this.dimGraphics, 0);
this.tooltip = new Tooltip(new PIXI.Rectangle(0, 0, 350, 160));
// Added custom button logic to still keep all the regular events for the button, just have an icon instead of text.
this.changeRoundButton.CustomButtonLogic = () => {
this.changeRoundButton.buttonIcon = new PIXI.Sprite({
texture: GameAssets.PlayIconTexture,
x: this.changeRoundButton.container.width / 2,
y: this.changeRoundButton.container.height / 2,
scale: 0.2,
});
this.changeRoundButton.buttonIcon.anchor.set(0.5, 0.5);
this.changeRoundButton.container.addChild(this.changeRoundButton.buttonIcon);
};
this.changeRoundButton.CustomButtonLogic();
this.changeRoundButton.onClick = () => {
if (this.playerWon) return this.ReturnToMain();
if (this.roundMode == RoundMode.Combat) {
// TODO: figure out how to actually double speed without causing bugs.
if (this.isFastForwarded) {
this.isFastForwarded = false;
Engine.NotificationManager.Notify('Regular speed.', 'info');
} else {
this.isFastForwarded = true;
Engine.NotificationManager.Notify('Fast forward activated.', 'info');
}
}
if (this.isGameOver) return Engine.NotificationManager.Notify('No more waves.', 'danger');
if (this.roundMode == RoundMode.Misc) return;
this.setRoundMode(RoundMode.Combat);
this.changeRoundButton.buttonIcon.texture = GameAssets.FastForwardIconTexture;
this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`);
};
this.MissionStats = new MissionStats(125, 450);
this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => {
if (gem == null) {
if (!this.MissionStats.checkIfPlayerHasAnyGems())
return Engine.NotificationManager.Notify(
'You require atleast 1 Gem in your inventory to slot it in a Gem slot.',
'warn'
);
}
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.maxFPS = 60;
this.ticker.minFPS = 30;
// fix tooltip behaving weirdly for some reason
this.tooltip.SetContentTower(0, 0, 0, 0);
this.tooltip.Show(Engine.MouseX, Engine.MouseY);
this.tooltip.Hide();
this.ticker.add(() => {
if (this.update) this.update(this.ticker.elapsedMS);
// if (this.isFastForwarded) this.update(this.ticker.elapsedMS);
});
this.ticker.start();
}
public update(elapsedMS) {
if (this.isGameOver) {
if (this.destroyTicker) {
this.destroyTicker = false;
this.ticker.destroy();
}
return;
}
Engine.WaveManager.update(elapsedMS);
Engine.Grid.update(elapsedMS);
Engine.TowerManager.update(elapsedMS);
// Means the round is finished.
if (this.isWaveManagerFinished && Engine.Grid.creeps.length == 0) {
this.isWaveManagerFinished = false;
this.setRoundMode(RoundMode.Purchase);
this.changeRoundButton.buttonIcon.texture = GameAssets.PlayIconTexture;
Engine.NotificationManager.Notify(
`Round ${this.currentRound + 1}/${this.mission.rounds.length} completed.`,
'info'
);
if (this.currentRound + 1 == this.mission.rounds.length) {
Engine.NotificationManager.Notify(`Mission victory!!`, 'reward');
this.changeRoundButton.buttonIcon.texture = GameAssets.HomeIconTexture;
this.playerWon = true;
} else {
this.OfferPlayerGems();
this.currentRound++;
}
}
if (this.MissionStats.getHP() <= 0) {
this.isGameOver = true;
this.ShowEndgameDialog(true);
} else if (this.playerWon) {
this.isGameOver = true;
this.ShowEndgameDialog(false);
}
}
public DarkenScreen() {
this.dimGraphics.rect(0, 0, Engine.app.canvas.width, Engine.app.canvas.height);
this.dimGraphics.fill({ color: 0x000000, alpha: 0.5 });
}
public UndarkenScreen() {
this.dimGraphics.clear();
}
private OfferPlayerGems() {
Engine.Grid.gridInteractionEnabled = false;
Engine.GameScene.sidebar.towerTab.resetTint();
Engine.TowerManager.ResetChooseTower();
this.setRoundMode(RoundMode.Misc);
let gemsToOffer = this.mission.rounds[this.currentRound].offeredGems;
this.DarkenScreen();
this.offerGemsSprite = new PIXI.NineSliceSprite({
width: 380,
height: 150,
texture: GameAssets.Frame01Texture,
leftWidth: 100,
topHeight: 100,
rightWidth: 100,
bottomHeight: 100,
zIndex: this.dimGraphics.zIndex + 1,
x: Engine.app.canvas.width / 2 - 190,
y: Engine.app.canvas.height / 2 - 75,
});
Engine.GameMaster.currentScene.stage.addChildAt(this.offerGemsSprite, 0);
let offerText = new PIXI.Text({
x: Engine.app.canvas.width / 4,
y: Engine.app.canvas.height / 4,
zIndex: this.dimGraphics.zIndex + 1,
text: 'Choose a Gem as your reward for beating this round!',
style: {
fontSize: 40,
fill: 'orange',
fontWeight: 'bold',
stroke: {
color: 0x000000,
width: 5,
},
},
});
// offerText.x -= offerText.width;
Engine.GameMaster.currentScene.stage.addChildAt(offerText, 0);
gemsToOffer.forEach((gType, index) => {
let _Gem = new Gem(gType, true);
let vGem = new VisualGemSlot(0, Engine.app.stage, _Gem);
this.visualGems.push(vGem);
vGem.container.x = this.offerGemsSprite.x - 15 + 69 * (index + 1);
vGem.container.y = this.offerGemsSprite.y + 40;
vGem.container.onpointermove = () => {
Engine.GameScene.tooltip.SetContentGem(_Gem);
Engine.GameScene.tooltip.Show(Engine.MouseX, Engine.MouseY);
};
vGem.container.onpointerleave = () => {
Engine.GameScene.tooltip.Hide();
};
vGem.onClick = () => {
Engine.GameScene.tooltip.Hide();
offerText.destroy();
this.PlayerPickedGem(new Gem(gType));
};
});
}
private PlayerPickedGem(gem: Gem) {
this.offerGemsSprite.destroy();
this.UndarkenScreen();
this.visualGems.forEach((item) => item.destroy());
Engine.Grid.gridInteractionEnabled = true;
this.MissionStats.giveGem(gem);
this.setRoundMode(RoundMode.Purchase);
}
public PauseGame() {
this.isPaused = true;
this.ticker.stop();
}
public UnpauseGame() {
this.isPaused = false;
this.ticker.start();
}
public ShowPauseDialog() {
const gamePausedDialog = new GamePausedDialog();
gamePausedDialog.show();
}
private async ShowEndgameDialog(lost) {
const endGameDialog = new EndGameDialog(this.mission.name, this.MissionStats, lost);
await endGameDialog.show();
const highScore = new HighScoreDialog(
this.mission.name,
lost,
!lost && this.missionIndex + 1 < GameAssets.Missions.length
);
const result = await highScore.show();
if (result === HighScoreDialogButtons.MainMenu) {
this.ReturnToMain();
} else if (result === HighScoreDialogButtons.NextMission) {
this.destroy();
Engine.GameMaster.changeScene(new MissionPickerScene());
Engine.GameMaster.changeScene(new GameScene(GameAssets.Missions[this.missionIndex + 1].name));
Engine.NotificationManager.Notify('Loading next mission. Good luck.', 'green');
} else if (result === HighScoreDialogButtons.Retry) {
this.destroy();
Engine.GameMaster.changeScene(new MissionPickerScene());
Engine.GameMaster.changeScene(new GameScene(this.mission.name));
Engine.NotificationManager.Notify('Retrying mission.', 'green');
}
}
public onCreepEscaped(creep: Creep) {
this.MissionStats.takeDamage(creep.health);
}
private setRoundMode(roundMode: RoundMode) {
this.roundMode = roundMode;
if (this.roundMode == RoundMode.Combat) {
Engine.WaveManager.start(this.currentRound);
} else {
Engine.WaveManager.end();
}
}
public destroy(): void {
super.destroy();
this.isGameOver = true;
this.destroyTicker = true;
Engine.GameScene = null;
}
private ReturnToMain() {
Engine.GameMaster.changeScene(new MissionPickerScene());
}
}