Skip to content

Commit

Permalink
Add game over scene (#45)
Browse files Browse the repository at this point in the history
* changedd how when do i load the service from game scene to main menu scene

* finalize the game over scene
  • Loading branch information
MounirAia authored Aug 20, 2024
1 parent 63e11d2 commit 6c5ed24
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 29 deletions.
65 changes: 65 additions & 0 deletions src/GameStatManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { IServiceSceneManager } from './SceneManager';
import { ServiceLocator } from './ServiceLocator';
import { IServiceWaveManager } from './WaveManager/WaveManager';

export interface IGameStatManager {
GetTimePlayerHasSurvived(): string;
HasPlayerWon(): { hasWon: boolean; round: number };
}

// Manages the gme stats related to if the player wins or loses the game
// how many rounds the player survived, how long the player survived, etc.
export class GameStatManager implements IGameStatManager {
private timePlayerSurvived: number;
private winningRound: number;

constructor() {
this.timePlayerSurvived = 0;
this.winningRound = 100;
ServiceLocator.AddService('GameStatManager', this);
}

public Update(dt: number) {
this.timePlayerSurvived = Math.round((this.timePlayerSurvived + dt) * 100) / 100;
const waveManager = ServiceLocator.GetService<IServiceWaveManager>('WaveManager');
// if the player has survived 100 rounds (thus reached 101 round), then the player has won the game
if (waveManager.Round > this.winningRound) {
ServiceLocator.GetService<IServiceSceneManager>('SceneManager').PlayMainScene('GameOver');
}
}

public GetTimePlayerHasSurvived(): string {
return this.formatTime({ seconds: this.timePlayerSurvived });
}

public HasPlayerWon(): { hasWon: boolean; round: number } {
const waveManager = ServiceLocator.GetService<IServiceWaveManager>('WaveManager');
const round = waveManager.Round;
if (round > this.winningRound) {
return { hasWon: true, round };
}
return { hasWon: false, round };
}

private formatTime(parameters: { seconds: number }): string {
const { seconds } = parameters;
const date = new Date(0);
date.setSeconds(seconds);

const hours = String(date.getUTCHours()).padStart(2, '0');
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
const secs = String(date.getUTCSeconds()).padStart(2, '0');

return `${hours}:${minutes}:${secs}`;
}
}

let gameStatManager: GameStatManager;

export function LoadGameStatManager() {
gameStatManager = new GameStatManager();
}

export function UpdateGameStatManager(dt: number) {
gameStatManager.Update(dt);
}
4 changes: 3 additions & 1 deletion src/Keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ type PossibleCommands =
| 'CloseInGameMenu'
| 'OpenShopMenu'
| 'CloseShopMenu'
| 'CloseOptionMenu';
| 'CloseOptionMenu'
| 'ToggleInGameTimer';
export interface IServiceKeyboardManager {
GetCommandState(parameters: { command: PossibleCommands }): Key;
}
Expand All @@ -188,6 +189,7 @@ export class ServiceKeyboardManager implements IServiceKeyboardManager {
this.commandsStates.set('OpenShopMenu', Keyboard['KeyH']);
this.commandsStates.set('CloseShopMenu', Keyboard['KeyH']);
this.commandsStates.set('CloseOptionMenu', Keyboard['Escape']);
this.commandsStates.set('ToggleInGameTimer', Keyboard['KeyT']);

ServiceLocator.AddService('KeyboardManager', this);
}
Expand Down
3 changes: 2 additions & 1 deletion src/SceneManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SelectSkillScene } from './Scenes/SelectSkillScene.js';
import { ServiceLocator } from './ServiceLocator.js';
import { InGameMenuScene } from './Scenes/InGameMenuScene.js';
import { ShoppingMenuScene } from './Scenes/ShoppingMenuScene.js';
import { GameOverScene } from './Scenes/GameOverScene.js';

type AvailableScenes = 'Game' | 'MainMenu' | 'SelectSkill' | 'GameOver';
type AvailableSecondaryScenes = 'None' | 'InGameMenu' | 'ShoppingMenu';
Expand Down Expand Up @@ -71,7 +72,7 @@ export class SceneManager implements IServiceSceneManager {
case 'SelectSkill':
return new SelectSkillScene();
case 'GameOver':
// return new GameOverScene();
return new GameOverScene();
default:
return new MainMenuScene();
}
Expand Down
115 changes: 115 additions & 0 deletions src/Scenes/GameOverScene.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { IGameStatManager } from '../GameStatManager';
import { IScene, IServiceSceneManager } from '../SceneManager';
import { CANVA_SCALEX, CANVA_SCALEY } from '../ScreenConstant';
import { ServiceLocator } from '../ServiceLocator';
import { FieldWithText } from './BaseUserInterface/FieldWithText';
import { UIManager } from './BaseUserInterface/UIManager';

export class GameOverScene implements IScene {
private gameOverUiManager: UIManager;
Load(): void {
this.loadUI();
}
Update(dt: number): void {
this.gameOverUiManager.Update(dt);
}
Draw(ctx: CanvasRenderingContext2D): void {
this.gameOverUiManager.Draw(ctx);
}
Unload(): void {}

private loadUI() {
this.gameOverUiManager = new UIManager();

/* Return to Main-Menu Button*/
const widthReturnButton = 62 * CANVA_SCALEX;
const heightReturnButton = 10 * CANVA_SCALEY;
const xReturnButton = 235 * CANVA_SCALEX;
const yReturnButton = 165 * CANVA_SCALEY;
const gameOverReturnButton = new FieldWithText({
x: xReturnButton,
y: yReturnButton,
width: widthReturnButton,
height: heightReturnButton,
text: 'MAIN-MENU',
fontSize: UIManager.Typography.button.fontSize,
fontFamily: UIManager.Typography.button.fontFamily,
HasHovered: true,
onClick: () => {
const SceneManager = ServiceLocator.GetService<IServiceSceneManager>('SceneManager');
SceneManager.PlayMainScene('MainMenu');
},
});
this.gameOverUiManager.AddComponent(gameOverReturnButton);

/* Text that show the time the player finished 100 rounds */
const gameStatManager = ServiceLocator.GetService<IGameStatManager>('GameStatManager');
const { hasWon, round } = gameStatManager.HasPlayerWon();
const timePlayerSurvived = gameStatManager.GetTimePlayerHasSurvived();
if (hasWon) {
this.UIToShowIfPlayerWin({ uiManager: this.gameOverUiManager, round, timePlayerSurvived });
} else {
this.UIToShowIfPlayerLose({ uiManager: this.gameOverUiManager, round, timePlayerSurvived });
}
}

private UIToShowIfPlayerWin(parameters: { uiManager: UIManager; round: number; timePlayerSurvived: string }) {
/* Text that show the time the player finished 100 rounds */
const widthTimeText = 255 * CANVA_SCALEX;
const heightTimeText = 6 * CANVA_SCALEY;
const xTimeText = 21 * CANVA_SCALEX;
const yTimeText = 19 * CANVA_SCALEY;
const gameOverTimeText = new FieldWithText({
x: xTimeText,
y: yTimeText,
width: widthTimeText,
height: heightTimeText,
text: `YOU WON, YOU DID A 100 ROUNDS IN: ${parameters.timePlayerSurvived}`,
fontSize: UIManager.Typography.button.fontSize,
fontFamily: UIManager.Typography.button.fontFamily,
leftAlign: true,
});
gameOverTimeText.HasBorderOnAllSide = false;
parameters.uiManager.AddComponent(gameOverTimeText);
}

private UIToShowIfPlayerLose(parameters: { uiManager: UIManager; round: number; timePlayerSurvived: string }) {
/* Text that show how many rounds the player survived */
const widthTimeText = 215 * CANVA_SCALEX;
const heightTimeText = 6 * CANVA_SCALEY;
const xTimeText = 21 * CANVA_SCALEX;
const yTimeText = 19 * CANVA_SCALEY;
const gameOverTimeText = new FieldWithText({
x: xTimeText,
y: yTimeText,
width: widthTimeText,
height: heightTimeText,
text: `YOU LOST, YOU REACHED ROUND: ${parameters.round}`,
fontSize: UIManager.Typography.button.fontSize,
fontFamily: UIManager.Typography.button.fontFamily,
leftAlign: true,
});
gameOverTimeText.HasBorderOnAllSide = false;
parameters.uiManager.AddComponent(gameOverTimeText);

/* Text that shows how long the player survived */
const widthSurvivalTimeText = 215 * CANVA_SCALEX;
const heightSurvivalTimeText = 6 * CANVA_SCALEY;
const xSurvivalTimeText = 21 * CANVA_SCALEX;
const ySurvivalTimeText = 29 * CANVA_SCALEY;

const gameOverSurvivalTimeText = new FieldWithText({
x: xSurvivalTimeText,
y: ySurvivalTimeText,
width: widthSurvivalTimeText,
height: heightSurvivalTimeText,
text: `YOU SURVIVED FOR: ${parameters.timePlayerSurvived}`,
fontSize: UIManager.Typography.button.fontSize,
fontFamily: UIManager.Typography.button.fontFamily,
leftAlign: true,
});

gameOverSurvivalTimeText.HasBorderOnAllSide = false;
parameters.uiManager.AddComponent(gameOverSurvivalTimeText);
}
}
61 changes: 47 additions & 14 deletions src/Scenes/GameScene.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { UnloadEventManager } from '../EventManager';
import { IGameStatManager, UpdateGameStatManager } from '../GameStatManager';
import { IServiceImageLoader } from '../ImageLoader';
import { IServiceKeyboardManager } from '../Keyboard';
import { IScene, IServiceSceneManager } from '../SceneManager';
import { CANVA_SCALEX, CANVA_SCALEY, canvas } from '../ScreenConstant';
import { ServiceLocator } from '../ServiceLocator';
import {
DrawGeneratedSpritesManager,
UnloadGeneratedSpritesManager,
UpdateGeneratedSpritesManager,
} from '../Sprites/GeneratedSpriteManager';
import { DrawPlayer, IServicePlayer, UnloadPlayer, UpdatePlayer } from '../Sprites/Player';
import { DrawGeneratedSpritesManager, UpdateGeneratedSpritesManager } from '../Sprites/GeneratedSpriteManager';
import { DrawPlayer, IServicePlayer, UpdatePlayer } from '../Sprites/Player';
import { Sprite } from '../Sprites/Sprite';
import { IServiceUtilManager } from '../UtilManager';
import { DrawWaveManager, IServiceWaveManager, UnloadWaveManager, UpdateWaveManager } from '../WaveManager/WaveManager';
import { DrawWaveManager, IServiceWaveManager, UpdateWaveManager } from '../WaveManager/WaveManager';
import { BaseField } from './BaseUserInterface/BaseField';
import { FieldSkillFactory } from './BaseUserInterface/FieldSkill';
import { FieldWithText } from './BaseUserInterface/FieldWithText';
Expand Down Expand Up @@ -321,15 +317,48 @@ class UserStateUI {
}
}

class InGameTimer {
private timerField: FieldWithText;
constructor() {
const gameStatManager = ServiceLocator.GetService<IGameStatManager>('GameStatManager');
const timePlayerSurvived = gameStatManager.GetTimePlayerHasSurvived();
this.timerField = new FieldWithText({
x: 3 * CANVA_SCALEX,
y: 20 * CANVA_SCALEY,
width: 75 * CANVA_SCALEX,
height: 10 * CANVA_SCALEY,
text: timePlayerSurvived,
leftAlign: true,
fontSize: UIManager.Typography.title.fontSize,
fontFamily: UIManager.Typography.title.fontFamily,
});
this.timerField.HasBorderOnAllSide = false;
}

public Update(dt: number): void {
const gameStatManager = ServiceLocator.GetService<IGameStatManager>('GameStatManager');
const timePlayerSurvived = gameStatManager.GetTimePlayerHasSurvived();
this.timerField.SetText(timePlayerSurvived);
}

public Draw(ctx: CanvasRenderingContext2D): void {
this.timerField.Draw(ctx);
}
}

export class GameScene implements IScene {
private cityBackgroundManager: CityBackgroundManager;
private userStateUI: UserStateUI;
private timerIsToggled: boolean;
private inGameTimer: InGameTimer;

Load() {
this.timerIsToggled = false;
this.loadUI();
}

Update(dt: number) {
UpdateGameStatManager(dt);
UpdateWaveManager(dt);
UpdatePlayer(dt);
UpdateGeneratedSpritesManager(dt);
Expand All @@ -342,30 +371,34 @@ export class GameScene implements IScene {
if (keyboardManager.GetCommandState({ command: 'OpenShopMenu' }).IsPressed) {
ServiceLocator.GetService<IServiceSceneManager>('SceneManager').PlaySecondaryScene('ShoppingMenu');
}

if (keyboardManager.GetCommandState({ command: 'ToggleInGameTimer' }).IsPressed) {
this.timerIsToggled = !this.timerIsToggled;
}
}

Draw(ctx: CanvasRenderingContext2D) {
this.drawUI(ctx);
DrawGeneratedSpritesManager(ctx);
DrawPlayer(ctx);
DrawWaveManager(ctx);
if (this.timerIsToggled) {
this.inGameTimer.Draw(ctx);
}
}

Unload(): void {
UnloadEventManager();
UnloadGeneratedSpritesManager();
UnloadWaveManager();
UnloadPlayer();
}
Unload(): void {}

private loadUI() {
this.cityBackgroundManager = new CityBackgroundManager();
this.userStateUI = new UserStateUI();
this.inGameTimer = new InGameTimer();
}

private updateUI(dt: number) {
this.cityBackgroundManager.Update(dt);
this.userStateUI.Update(dt);
this.inGameTimer.Update(dt);
}

private drawUI(ctx: CanvasRenderingContext2D) {
Expand Down
12 changes: 12 additions & 0 deletions src/Scenes/MainMenuScene.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { LoadEventManager } from '../EventManager.js';
import { LoadGameStatManager } from '../GameStatManager.js';
import { IScene, IServiceSceneManager } from '../SceneManager.js';
import { CANVA_SCALEX, CANVA_SCALEY } from '../ScreenConstant.js';
import { ServiceLocator } from '../ServiceLocator.js';
import { LoadGeneratedSpritesManager } from '../Sprites/GeneratedSpriteManager.js';
import { LoadPlayer } from '../Sprites/Player.js';
import { LoadWaveManager } from '../WaveManager/WaveManager.js';
import { FieldWithText } from './BaseUserInterface/FieldWithText.js';
import { UIManager } from './BaseUserInterface/UIManager.js';

export class MainMenuScene implements IScene {
private mainMenuUiManager: UIManager;
Load() {
// Load the services that should be reseted when the game is restarted
LoadEventManager();
LoadGeneratedSpritesManager();
LoadWaveManager();
LoadGameStatManager();
LoadPlayer();

this.loadUI();
}

Expand Down
3 changes: 2 additions & 1 deletion src/ServiceLocator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ type AvailableServices =
| 'CollideManager'
| 'EventManager'
| 'UtilManager'
| 'KeyboardManager';
| 'KeyboardManager'
| 'GameStatManager';

export class ServiceLocator {
private static services: { [key: string]: unknown } = {};
Expand Down
4 changes: 2 additions & 2 deletions src/Sprites/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,8 @@ export function LoadPlayer() {
const imgPlayer =
ServiceLocator.GetService<IServiceImageLoader>('ImageLoader').GetImage('images/Player/Player.png');

const x = 250;
const y = 250;
const x = 60 * CANVA_SCALEX;
const y = 60 * CANVA_SCALEY;

player = new Player(imgPlayer, x, y);
}
Expand Down
Loading

0 comments on commit 6c5ed24

Please sign in to comment.