From 13f4c4610e77f89b452fa8befe8e48708efd2579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20=C4=8Carapi=C4=87?= Date: Wed, 5 Feb 2025 21:43:16 +0100 Subject: [PATCH 01/11] Working on end-game menu --- public/Tileset.tsx | 2 +- src/classes/Assets.ts | 2 + src/classes/Events.ts | 6 ++ src/classes/GameUIConstants.ts | 14 ++- src/classes/game/Creep.ts | 1 + src/classes/game/KeyboardManager.ts | 52 ++++++++++ src/classes/game/TowerManager.ts | 1 + src/classes/gui/Button.ts | 4 + src/classes/gui/EndGameDialog.ts | 119 +++++++++++++++++++++++ src/classes/gui/MessageBox.ts | 143 ++++++++++++++++++++++++++++ src/classes/gui/TextInput.ts | 53 +++++++++++ src/main.ts | 3 + src/scenes/Game.ts | 25 +++-- 13 files changed, 415 insertions(+), 10 deletions(-) create mode 100644 src/classes/game/KeyboardManager.ts create mode 100644 src/classes/gui/EndGameDialog.ts create mode 100644 src/classes/gui/MessageBox.ts create mode 100644 src/classes/gui/TextInput.ts diff --git a/public/Tileset.tsx b/public/Tileset.tsx index 7f536c1..f5b0984 100644 --- a/public/Tileset.tsx +++ b/public/Tileset.tsx @@ -1,4 +1,4 @@ - + diff --git a/src/classes/Assets.ts b/src/classes/Assets.ts index ee2edbe..ea3f2c5 100644 --- a/src/classes/Assets.ts +++ b/src/classes/Assets.ts @@ -24,6 +24,7 @@ export default class GameAssets { public static SwordsTexture: PIXI.Texture; public static TitleTexture: PIXI.Texture; public static BannerGemsmith: PIXI.Texture; + public static EndScreenDialog: PIXI.Texture; public static PlayIconTexture: PIXI.Texture; public static PauseIconTexture: PIXI.Texture; @@ -96,6 +97,7 @@ export default class GameAssets { this.Load('./assets/gui/frame_green.png').then((texture) => (this.GreenBackground = texture)), this.Load('./assets/gui/frame_blue.png').then((texture) => (this.BlueBackground = texture)), this.Load('./assets/gui/banner_01.png').then((texture) => (this.BannerGemsmith = texture)), + this.Load('./assets/gui/note.png').then((texture) => (this.EndScreenDialog = texture)), this.Load('./assets/gui/heart.png').then((texture) => (this.HealthTexture = texture)), this.Load('./assets/gui/money.png').then((texture) => (this.GoldTexture = texture)), this.Load('./assets/gui/wave.png').then((texture) => (this.WaveTexture = texture)), diff --git a/src/classes/Events.ts b/src/classes/Events.ts index 67be16b..581ff8e 100644 --- a/src/classes/Events.ts +++ b/src/classes/Events.ts @@ -28,3 +28,9 @@ export enum StatsEvents { export enum GemEvents { TowerPanelSelectGem = 'towerTabSelectGem', } + +export enum EndMissionDialogEvents { + NextMission = 'nextMission', + RetryMission = 'retryMission', + MainMenu = 'mainMenu', +} diff --git a/src/classes/GameUIConstants.ts b/src/classes/GameUIConstants.ts index a875166..f78bf33 100644 --- a/src/classes/GameUIConstants.ts +++ b/src/classes/GameUIConstants.ts @@ -1,8 +1,10 @@ import * as PIXI from 'pixi.js'; import { Engine } from './Bastion'; export default class GameUIConstants { - public static SidebarRect; - public static ChangeRoundButtonRect; + public static SidebarRect: PIXI.Rectangle; + public static ChangeRoundButtonRect: PIXI.Rectangle; + public static EndGameDialogRect: PIXI.Rectangle; + public static init() { GameUIConstants.SidebarRect = new PIXI.Rectangle( Engine.app.canvas.width - 360, @@ -11,5 +13,13 @@ export default class GameUIConstants { Engine.app.canvas.height ); GameUIConstants.ChangeRoundButtonRect = new PIXI.Rectangle(50, Engine.app.canvas.height - 100, 310, 100); + const endGameDialogWidth = 600; + const endGameDialogHeight = 800; + GameUIConstants.EndGameDialogRect = new PIXI.Rectangle( + (Engine.app.canvas.width - endGameDialogWidth) / 2, + (Engine.app.canvas.height - endGameDialogHeight) / 2, + endGameDialogWidth, + endGameDialogHeight + ); } } diff --git a/src/classes/game/Creep.ts b/src/classes/game/Creep.ts index af4f4e1..37e75a0 100644 --- a/src/classes/game/Creep.ts +++ b/src/classes/game/Creep.ts @@ -51,6 +51,7 @@ export default class Creep extends GameObject { // Added + 32 to center them. this.x = path[0][1] * Engine.GridCellSize + Engine.GridCellSize / 2; this.y = path[0][0] * Engine.GridCellSize + Engine.GridCellSize / 2; + // TODO: Unsubscribe from events once the scene is destroyed Engine.GameScene.events.on(CreepEvents.TakenDamage, (creepID, damage) => { if (creepID != this.id) return; this.health -= damage; diff --git a/src/classes/game/KeyboardManager.ts b/src/classes/game/KeyboardManager.ts new file mode 100644 index 0000000..37ab5db --- /dev/null +++ b/src/classes/game/KeyboardManager.ts @@ -0,0 +1,52 @@ +/** + * Handles keyboard events. + */ +class KeyboardManager { + private static listeners: Map void)[]> = new Map(); + + public static init() { + window.addEventListener('keydown', KeyboardManager.handleKeyDown); + } + + /** + * Add a callback to be called when the specified key is pressed. + * Note: Calling preventDefault() on the event will prevent other callbacks from being called. + * @param key The key to listen for. + * @param callback The callback to call when the key is pressed. + * @returns A function that can be called to remove the callback. + */ + public static onKey(key: string, callback: (event: KeyboardEvent) => void) { + if (!KeyboardManager.listeners.has(key)) { + KeyboardManager.listeners.set(key, []); + } + KeyboardManager.listeners.get(key).push(callback); + return () => KeyboardManager.offKey(key, callback); + } + + /** + * Remove a callback from the specified key. + */ + public static offKey(key: string, callback: (event: KeyboardEvent) => void) { + if (KeyboardManager.listeners.has(key)) { + const index = KeyboardManager.listeners.get(key).indexOf(callback); + if (index >= 0) { + KeyboardManager.listeners.get(key).splice(index, 1); + } + } + } + + private static handleKeyDown(event: KeyboardEvent) { + if (KeyboardManager.listeners.has(event.key)) { + console.log(`Key down: ${event.key}`); + const callbacks = KeyboardManager.listeners.get(event.key); + for (let i = callbacks.length - 1; i >= 0; i--) { + callbacks[i](event); + if (event.defaultPrevented) { + break; + } + } + } + } +} + +export default KeyboardManager; diff --git a/src/classes/game/TowerManager.ts b/src/classes/game/TowerManager.ts index d63d0e6..1e425d9 100644 --- a/src/classes/game/TowerManager.ts +++ b/src/classes/game/TowerManager.ts @@ -23,6 +23,7 @@ export default class TowerManager { }); private towers: Tower[] = []; constructor() { + // TODO: Unsubscribe from events once the scene is destroyed Engine.TowerManager = this; Engine.GameScene.events.on(GridEvents.CellMouseOver, (cell: Cell) => { if (this.isPlacingTower) { diff --git a/src/classes/gui/Button.ts b/src/classes/gui/Button.ts index 05fb20a..fcf6dd5 100644 --- a/src/classes/gui/Button.ts +++ b/src/classes/gui/Button.ts @@ -22,6 +22,10 @@ export default class Button extends GuiObject { this.buttonText.text = caption; } + getCaption(): string { + return this.caption; + } + constructor(bounds: PIXI.Rectangle, caption: string, buttonTexture: ButtonTexture, enabled: boolean = true) { super(true); if (buttonTexture == ButtonTexture.Button01) this.buttonTexture = Assets.Button01Texture; diff --git a/src/classes/gui/EndGameDialog.ts b/src/classes/gui/EndGameDialog.ts new file mode 100644 index 0000000..43e67c5 --- /dev/null +++ b/src/classes/gui/EndGameDialog.ts @@ -0,0 +1,119 @@ +import * as PIXI from 'pixi.js'; +import GuiObject from '../GuiObject'; +import GameAssets from '../Assets'; +import GameUIConstants from '../GameUIConstants'; +import Button, { ButtonTexture } from './Button'; +import { Engine } from '../Bastion'; +import { EndMissionDialogEvents } from '../Events'; +import MessageBox from './MessageBox'; +import KeyboardManager from '../game/KeyboardManager'; + +export default class EndGameDialog extends GuiObject { + private dialogSprite: PIXI.NineSliceSprite; + private dialogCaption: PIXI.Text; + private nextMissionButton: Button; + private retryButton: Button; + private mainMenuButton: Button; + private overlay: PIXI.Graphics; + private lost: boolean; + private keyboardManagerUnsubscribe: () => void; + + constructor(bounds: PIXI.Rectangle, lost: boolean) { + super(); + this.lost = lost; + // Show overlay to prevent user from interacting with the game + this.overlay = new PIXI.Graphics(); + this.overlay.rect(0, 0, bounds.width, bounds.height); + this.overlay.fill({ color: 0x000000, alpha: 0.5 }); + // Prevent interaction with the underlying scene + this.overlay.interactive = true; + this.container.addChild(this.overlay); + + this.dialogSprite = new PIXI.NineSliceSprite({ + texture: GameAssets.EndScreenDialog, + leftWidth: 50, + topHeight: 100, + rightWidth: 50, + bottomHeight: 50, + }); + this.dialogSprite.x = GameUIConstants.EndGameDialogRect.x; + this.dialogSprite.y = GameUIConstants.EndGameDialogRect.y; + this.dialogSprite.width = GameUIConstants.EndGameDialogRect.width; + this.dialogSprite.height = GameUIConstants.EndGameDialogRect.height; + this.container.addChild(this.dialogSprite); + this.dialogCaption = new PIXI.Text({ + text: lost ? 'You lost!' : 'You won!', + style: new PIXI.TextStyle({ + fill: 0xffffff, + fontSize: 36, + }), + }); + this.container.addChild(this.dialogCaption); + this.dialogCaption.anchor.set(0.5, 0.5); + this.dialogCaption.x = GameUIConstants.EndGameDialogRect.x + GameUIConstants.EndGameDialogRect.width / 2; + this.dialogCaption.y = GameUIConstants.EndGameDialogRect.y + 50; + //this.setupButtons(lost); + this.keyboardManagerUnsubscribe = KeyboardManager.onKey('Escape', (e) => { + if (e.key === 'Escape') { + this.onMainMission(); + } + }); + this.container.on('destroyed', () => { + this.keyboardManagerUnsubscribe(); + }); + } + + private setupButtons(lost: boolean) { + const buttonContainer = new PIXI.Container(); + const buttonWidth = 200; + const buttonHeight = 80; + const buttonPadding = 10; + if (lost) { + this.retryButton = new Button( + new PIXI.Rectangle(0, 0, buttonWidth, buttonHeight), + 'Retry', + ButtonTexture.Button01 + ); + this.retryButton.onClick = () => { + Engine.GameScene.events.emit(EndMissionDialogEvents.RetryMission); + }; + buttonContainer.addChild(this.retryButton.container); + } else { + this.nextMissionButton = new Button( + new PIXI.Rectangle(0, 0, buttonWidth, buttonHeight), + 'Next Mission', + ButtonTexture.Button01 + ); + this.nextMissionButton.onClick = () => { + Engine.GameScene.events.emit(EndMissionDialogEvents.NextMission); + }; + buttonContainer.addChild(this.nextMissionButton.container); + } + this.mainMenuButton = new Button( + new PIXI.Rectangle(0, buttonHeight + buttonPadding, buttonWidth, buttonHeight), + 'Main Menu', + ButtonTexture.Button01 + ); + this.mainMenuButton.onClick = this.onMainMission.bind(this); + buttonContainer.addChild(this.mainMenuButton.container); + this.container.addChild(buttonContainer); + buttonContainer.x = + GameUIConstants.EndGameDialogRect.x + + GameUIConstants.EndGameDialogRect.width / 2 - + buttonContainer.width / 2; + buttonContainer.y = + GameUIConstants.EndGameDialogRect.y + GameUIConstants.EndGameDialogRect.height - buttonContainer.height; + } + + private showingMessageBox: boolean; + + private async onMainMission() { + if (this.showingMessageBox) return; + this.showingMessageBox = true; + const result = await MessageBox.show('Are you sure you want to return to the main menu?', ['Yes', 'No']); + this.showingMessageBox = false; + if (result === 'Yes') { + Engine.GameScene.events.emit(EndMissionDialogEvents.MainMenu); + } + } +} diff --git a/src/classes/gui/MessageBox.ts b/src/classes/gui/MessageBox.ts new file mode 100644 index 0000000..3583674 --- /dev/null +++ b/src/classes/gui/MessageBox.ts @@ -0,0 +1,143 @@ +import * as PIXI from 'pixi.js'; +import GuiObject from '../GuiObject'; +import Assets from '../Assets'; +import { Engine } from '../Bastion'; +import GameAssets from '../Assets'; +import Button, { ButtonTexture } from './Button'; +import KeyboardManager from '../game/KeyboardManager'; + +export default class MessageBox extends GuiObject { + private overlay: PIXI.Graphics; + private buttonPadding = 10; + private buttons: Button[] = []; + private escapeKeyIndex: number; + private keyboardManagerUnsubscribe: () => void; + + constructor(caption: string, buttons: string[], escapeKeyIndex: number = buttons.length - 1) { + super(); + console.log(`MessageBox(caption: ${caption}, buttons: ${buttons})`); + this.escapeKeyIndex = escapeKeyIndex; + // Show overlay to prevent user from interacting with the game + this.overlay = new PIXI.Graphics(); + this.overlay.rect(0, 0, Engine.app.canvas.width, Engine.app.canvas.height); + this.overlay.fill({ color: 0x000000, alpha: 0.5 }); + // Prevent interaction with the underlying scene + this.overlay.interactive = true; + this.container.addChild(this.overlay); + + const buttonDefs = buttons.map((btn) => ({ + caption: btn, + width: btn.length * 10 + 40, + height: 60, + click: () => this.buttonClicked(btn), + })); + let buttonTotalWidth = 0; + for (const buttonDef of buttonDefs) { + if (buttonTotalWidth > 0) buttonTotalWidth += this.buttonPadding; + buttonTotalWidth += buttonDef.width; + } + const captionWidth = caption.length * 10 + 100; + let width = Math.max(buttonTotalWidth, captionWidth); + + const height = 150; + const inputContainerBounds = new PIXI.Rectangle( + Engine.app.canvas.width / 2 - width / 2, + Engine.app.canvas.height / 2 - height / 2, + width, + height + ); + + const inputContainer = new PIXI.Container(); + inputContainer.x = inputContainerBounds.x; + inputContainer.y = inputContainerBounds.y; + inputContainer.width = inputContainerBounds.width; + inputContainer.height = inputContainerBounds.height; + + const background = new PIXI.NineSliceSprite({ + texture: GameAssets.Frame04Texture, + leftWidth: 200, + topHeight: 200, + rightWidth: 200, + bottomHeight: 200, + }); + background.x = 0; + background.y = 0; + background.width = inputContainerBounds.width; + background.height = inputContainerBounds.height; + inputContainer.addChild(background); + + const text = new PIXI.Text({ + text: caption, + style: new PIXI.TextStyle({ + fill: 0xffffff, + fontSize: 24, + }), + }); + text.anchor.set(0.5, 0.5); + text.x = inputContainerBounds.width / 2; + text.y = 40; + inputContainer.addChild(text); + + let buttonXPos = inputContainerBounds.width / 2 - buttonTotalWidth / 2; + for (const buttonDef of buttonDefs) { + const button = new Button( + new PIXI.Rectangle( + buttonXPos, + inputContainerBounds.height - buttonDef.height - 20, + buttonDef.width, + buttonDef.height + ), + buttonDef.caption, + ButtonTexture.Button01 + ); + button.onClick = buttonDef.click; + this.buttons.push(button); + inputContainer.addChild(button.container); + buttonXPos += buttonDef.width + this.buttonPadding; + } + this.container.addChild(inputContainer); + this.keyboardManagerUnsubscribe = KeyboardManager.onKey('Escape', this.onKeyPress.bind(this)); + } + + /** + * Event that is triggered when the button is clicked. + */ + public onButtonClicked: (button: string) => void; + + override destroy(): void { + this.keyboardManagerUnsubscribe(); + super.destroy(); + } + + private buttonClicked(button: string) { + if (this.onButtonClicked) this.onButtonClicked(button); + this.destroy(); + } + + private onKeyPress(event: KeyboardEvent) { + // Message box is modal, so we can safely prevent the default behavior + event.preventDefault(); + if (event.key === 'Escape') { + this.onButtonClicked(this.buttons[this.escapeKeyIndex].getCaption()); + } else if (event.key === 'Enter') { + this.onButtonClicked(this.buttons[0].getCaption()); + } + } + + /** + * Shows a message box with the specified caption and buttons. + * @param caption The caption of the message box. + * @param buttons The buttons to show. + * @returns A promise that resolves with the button that was clicked. + */ + public static show(caption: string, buttons: string[], escapeKeyButtonIndex: number = 0): Promise { + return new Promise((resolve, reject) => { + const messageBox = new MessageBox(caption, buttons); + Engine.app.stage.addChild(messageBox.container); + messageBox.onButtonClicked = (button) => { + messageBox.destroy(); + resolve(button); + }; + }); + } +} diff --git a/src/classes/gui/TextInput.ts b/src/classes/gui/TextInput.ts new file mode 100644 index 0000000..80595d4 --- /dev/null +++ b/src/classes/gui/TextInput.ts @@ -0,0 +1,53 @@ +import * as PIXI from 'pixi.js'; +import GuiObject from '../GuiObject'; +import Assets from '../Assets'; +import GameAssets from '../Assets'; + +export default class TextInput extends GuiObject { + private bounds: PIXI.Rectangle; + private backgroundSprite: PIXI.NineSliceSprite; + private text: PIXI.Text; + private maxLength: number; + + constructor(bounds: PIXI.Rectangle, maxLength: number) { + super(); + this.bounds = bounds; + this.maxLength = maxLength; + this.container.x = this.bounds.x; + this.container.y = this.bounds.y; + this.container.width = this.bounds.width; + this.container.height = this.bounds.height; + this.backgroundSprite = new PIXI.NineSliceSprite({ + texture: GameAssets.Frame01Texture, + leftWidth: 20, + topHeight: 20, + rightWidth: 20, + bottomHeight: 20, + }); + this.backgroundSprite.x = 0; + this.backgroundSprite.y = 0; + this.backgroundSprite.width = this.bounds.width; + this.backgroundSprite.height = this.bounds.height; + this.container.addChild(this.backgroundSprite); + this.container.x = this.bounds.x; + this.container.y = this.bounds.y; + this.text = new PIXI.Text({ + text: '', + style: new PIXI.TextStyle({ + fill: 0xffffff, + fontSize: 16, + }), + }); + this.text.x = 10; + this.text.y = 10; + this.container.addChild(this.text); + } + + private onKeyPress(event: KeyboardEvent) { + if (event.key == 'Backspace') { + this.text.text = this.text.text.slice(0, -1); + } else if (event.key.length == 1 && this.text.text.length < this.maxLength) { + this.text.text += event.key; + } + } +} diff --git a/src/main.ts b/src/main.ts index 39659c1..7b6a584 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,8 @@ import { GameScene } from './scenes/Game'; import { AnimationManager } from './classes/game/AnimationManager'; import NotificationManager from './classes/game/NotificationManager'; import GameUIConstants from './classes/GameUIConstants'; +import MessageBox from './classes/gui/MessageBox'; +import KeyboardManager from './classes/game/KeyboardManager'; (async () => { const app = new PIXI.Application(); @@ -47,6 +49,7 @@ import GameUIConstants from './classes/GameUIConstants'; resize(); await Assets.LoadAssets(); GameUIConstants.init(); + KeyboardManager.init(); new GameMaster(); Engine.AnimationManager = new AnimationManager(); Engine.NotificationManager = new NotificationManager(); diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index fb01b7c..2655e6d 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -4,7 +4,7 @@ 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 { WaveManagerEvents, CreepEvents, GemEvents, EndMissionDialogEvents } from '../classes/Events'; import Sidebar from '../classes/gui/Sidebar'; import Button, { ButtonTexture } from '../classes/gui/Button'; import Scene from './Scene'; @@ -16,6 +16,7 @@ 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'; enum RoundMode { Purchase = 0, @@ -40,6 +41,7 @@ export class GameScene extends Scene { private playerWon: boolean = false; private destroyTicker: boolean = false; private offerGemsSprite: PIXI.NineSliceSprite; + private endGameDialog: EndGameDialog; private dimGraphics: PIXI.Graphics = new PIXI.Graphics({ x: 0, y: 0, @@ -117,6 +119,9 @@ export class GameScene extends Scene { } this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower); }); + this.events.on(EndMissionDialogEvents.MainMenu, this.onEndMissionDialogMainMenuClicked.bind(this)); + this.events.on(EndMissionDialogEvents.RetryMission, this.onEndMissionDialogRetryMissionClicked.bind(this)); + this.events.on(EndMissionDialogEvents.NextMission, this.onEndMissionDialogNextMissionClicked.bind(this)); this.ticker = new PIXI.Ticker(); this.ticker.maxFPS = 60; this.ticker.minFPS = 30; @@ -130,6 +135,7 @@ export class GameScene extends Scene { }); this.ticker.start(); } + public update(elapsedMS) { if (this.isGameOver) { if (this.destroyTicker) { @@ -242,12 +248,9 @@ export class GameScene extends Scene { } private ShowScoreScreen(lost) { - // TODO: show to player for real (see how this.OfferPlayerGems() does it) - if (lost) { - console.log('LOSE!'); - } else { - console.log('WIN!'); - } + const bounds = new PIXI.Rectangle(0, 0, Engine.app.canvas.width, Engine.app.canvas.height); + this.endGameDialog = new EndGameDialog(bounds, lost); + Engine.GameMaster.currentScene.stage.addChild(this.endGameDialog.container); } public onCreepEscaped(creep: Creep) { @@ -263,6 +266,14 @@ export class GameScene extends Scene { } } + private onEndMissionDialogMainMenuClicked() { + this.ReturnToMain(); + } + + private onEndMissionDialogRetryMissionClicked() {} + + private onEndMissionDialogNextMissionClicked() {} + public destroy(): void { super.destroy(); this.isGameOver = true; From f6879046faf472103eed480141952d012c1258a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20=C4=8Carapi=C4=87?= Date: Wed, 5 Feb 2025 22:33:32 +0100 Subject: [PATCH 02/11] Working on end-game menu --- src/classes/gui/EndGameDialog.ts | 2 +- src/classes/gui/MessageBox.ts | 131 ++---------------------- src/classes/gui/ModalDialog.ts | 158 +++++++++++++++++++++++++++++ src/classes/gui/PlayerNameInput.ts | 45 ++++++++ src/classes/gui/TextInput.ts | 4 + 5 files changed, 219 insertions(+), 121 deletions(-) create mode 100644 src/classes/gui/ModalDialog.ts create mode 100644 src/classes/gui/PlayerNameInput.ts diff --git a/src/classes/gui/EndGameDialog.ts b/src/classes/gui/EndGameDialog.ts index 43e67c5..30c419a 100644 --- a/src/classes/gui/EndGameDialog.ts +++ b/src/classes/gui/EndGameDialog.ts @@ -63,7 +63,7 @@ export default class EndGameDialog extends GuiObject { }); } - private setupButtons(lost: boolean) { + private showButtons(lost: boolean) { const buttonContainer = new PIXI.Container(); const buttonWidth = 200; const buttonHeight = 80; diff --git a/src/classes/gui/MessageBox.ts b/src/classes/gui/MessageBox.ts index 3583674..9bfd95f 100644 --- a/src/classes/gui/MessageBox.ts +++ b/src/classes/gui/MessageBox.ts @@ -1,127 +1,24 @@ import * as PIXI from 'pixi.js'; +import ModalDialogBase from './ModalDialog'; import GuiObject from '../GuiObject'; -import Assets from '../Assets'; -import { Engine } from '../Bastion'; -import GameAssets from '../Assets'; -import Button, { ButtonTexture } from './Button'; -import KeyboardManager from '../game/KeyboardManager'; -export default class MessageBox extends GuiObject { - private overlay: PIXI.Graphics; - private buttonPadding = 10; - private buttons: Button[] = []; - private escapeKeyIndex: number; - private keyboardManagerUnsubscribe: () => void; +export default class MessageBox extends ModalDialogBase { + private caption: string; constructor(caption: string, buttons: string[], escapeKeyIndex: number = buttons.length - 1) { - super(); - console.log(`MessageBox(caption: ${caption}, buttons: ${buttons})`); - this.escapeKeyIndex = escapeKeyIndex; - // Show overlay to prevent user from interacting with the game - this.overlay = new PIXI.Graphics(); - this.overlay.rect(0, 0, Engine.app.canvas.width, Engine.app.canvas.height); - this.overlay.fill({ color: 0x000000, alpha: 0.5 }); - // Prevent interaction with the underlying scene - this.overlay.interactive = true; - this.container.addChild(this.overlay); - - const buttonDefs = buttons.map((btn) => ({ - caption: btn, - width: btn.length * 10 + 40, - height: 60, - click: () => this.buttonClicked(btn), - })); - let buttonTotalWidth = 0; - for (const buttonDef of buttonDefs) { - if (buttonTotalWidth > 0) buttonTotalWidth += this.buttonPadding; - buttonTotalWidth += buttonDef.width; - } - const captionWidth = caption.length * 10 + 100; - let width = Math.max(buttonTotalWidth, captionWidth); - - const height = 150; - const inputContainerBounds = new PIXI.Rectangle( - Engine.app.canvas.width / 2 - width / 2, - Engine.app.canvas.height / 2 - height / 2, - width, - height - ); - - const inputContainer = new PIXI.Container(); - inputContainer.x = inputContainerBounds.x; - inputContainer.y = inputContainerBounds.y; - inputContainer.width = inputContainerBounds.width; - inputContainer.height = inputContainerBounds.height; - - const background = new PIXI.NineSliceSprite({ - texture: GameAssets.Frame04Texture, - leftWidth: 200, - topHeight: 200, - rightWidth: 200, - bottomHeight: 200, - }); - background.x = 0; - background.y = 0; - background.width = inputContainerBounds.width; - background.height = inputContainerBounds.height; - inputContainer.addChild(background); + super(buttons, escapeKeyIndex); + this.caption = caption; + } + protected override createContent(): PIXI.Container | GuiObject { const text = new PIXI.Text({ - text: caption, + text: this.caption, style: new PIXI.TextStyle({ fill: 0xffffff, fontSize: 24, }), }); - text.anchor.set(0.5, 0.5); - text.x = inputContainerBounds.width / 2; - text.y = 40; - inputContainer.addChild(text); - - let buttonXPos = inputContainerBounds.width / 2 - buttonTotalWidth / 2; - for (const buttonDef of buttonDefs) { - const button = new Button( - new PIXI.Rectangle( - buttonXPos, - inputContainerBounds.height - buttonDef.height - 20, - buttonDef.width, - buttonDef.height - ), - buttonDef.caption, - ButtonTexture.Button01 - ); - button.onClick = buttonDef.click; - this.buttons.push(button); - inputContainer.addChild(button.container); - buttonXPos += buttonDef.width + this.buttonPadding; - } - this.container.addChild(inputContainer); - this.keyboardManagerUnsubscribe = KeyboardManager.onKey('Escape', this.onKeyPress.bind(this)); - } - - /** - * Event that is triggered when the button is clicked. - */ - public onButtonClicked: (button: string) => void; - - override destroy(): void { - this.keyboardManagerUnsubscribe(); - super.destroy(); - } - - private buttonClicked(button: string) { - if (this.onButtonClicked) this.onButtonClicked(button); - this.destroy(); - } - - private onKeyPress(event: KeyboardEvent) { - // Message box is modal, so we can safely prevent the default behavior - event.preventDefault(); - if (event.key === 'Escape') { - this.onButtonClicked(this.buttons[this.escapeKeyIndex].getCaption()); - } else if (event.key === 'Enter') { - this.onButtonClicked(this.buttons[0].getCaption()); - } + return text; } /** @@ -131,13 +28,7 @@ export default class MessageBox extends GuiObject { * @returns A promise that resolves with the button that was clicked. */ public static show(caption: string, buttons: string[], escapeKeyButtonIndex: number = 0): Promise { - return new Promise((resolve, reject) => { - const messageBox = new MessageBox(caption, buttons); - Engine.app.stage.addChild(messageBox.container); - messageBox.onButtonClicked = (button) => { - messageBox.destroy(); - resolve(button); - }; - }); + const messageBox = new MessageBox(caption, buttons); + return messageBox.show(); } } diff --git a/src/classes/gui/ModalDialog.ts b/src/classes/gui/ModalDialog.ts new file mode 100644 index 0000000..a63480a --- /dev/null +++ b/src/classes/gui/ModalDialog.ts @@ -0,0 +1,158 @@ +import * as PIXI from 'pixi.js'; +import GuiObject from '../GuiObject'; +import Assets from '../Assets'; +import { Engine } from '../Bastion'; +import GameAssets from '../Assets'; +import Button, { ButtonTexture } from './Button'; +import KeyboardManager from '../game/KeyboardManager'; + +export default abstract class ModalDialogBase extends GuiObject { + private overlay: PIXI.Graphics; + private dialogPadding = 40; + private contentPadding = 10; + private buttonPadding = 10; + private buttonAreaHeight = 40; + private buttonHeight = 60; + private buttonCaptions: string[]; + private buttons: Button[] = []; + private escapeKeyIndex: number; + private keyboardManagerUnsubscribe: () => void; + private pixiContent: PIXI.Container; + private guiContent: GuiObject; + private generated = false; + + constructor(buttonCaptions: string[], escapeKeyIndex: number = buttonCaptions.length - 1) { + super(); + this.escapeKeyIndex = escapeKeyIndex; + this.buttonCaptions = buttonCaptions; + this.keyboardManagerUnsubscribe = KeyboardManager.onKey('Escape', this.onKeyPress.bind(this)); + } + + protected generate() { + if (this.generated) return; + this.generated = true; + // Show overlay to prevent user from interacting with the game + this.overlay = new PIXI.Graphics(); + this.overlay.rect(0, 0, Engine.app.canvas.width, Engine.app.canvas.height); + this.overlay.fill({ color: 0x000000, alpha: 0.5 }); + // Prevent interaction with the underlying scene + this.overlay.interactive = true; + this.container.addChild(this.overlay); + + const content = this.createContent(); + if (content instanceof GuiObject) { + this.guiContent = content; + this.pixiContent = content.container; + } else { + this.pixiContent = content; + } + + const buttonDefs = this.buttonCaptions.map((btnCaption) => ({ + caption: btnCaption, + width: btnCaption.length * 16 + 40, + height: this.buttonHeight, + click: () => this.buttonClickHandler(btnCaption), + })); + let buttonTotalWidth = 0; + for (const buttonDef of buttonDefs) { + if (buttonTotalWidth > 0) buttonTotalWidth += this.buttonPadding; + buttonTotalWidth += buttonDef.width; + } + const contentWidth = this.pixiContent.width + this.contentPadding * 2; + const contentHeight = this.pixiContent.height + this.contentPadding * 2; + let width = Math.max(buttonTotalWidth, contentWidth) + this.dialogPadding * 2; + + const height = contentHeight + this.buttonAreaHeight + this.dialogPadding * 2; + const modalBounds = new PIXI.Rectangle( + Engine.app.canvas.width / 2 - width / 2, + Engine.app.canvas.height / 2 - height / 2, + width, + height + ); + + const modalContainer = new PIXI.Container(); + modalContainer.x = modalBounds.x; + modalContainer.y = modalBounds.y; + + const background = new PIXI.NineSliceSprite({ + texture: GameAssets.Frame04Texture, + leftWidth: 60, + topHeight: 60, + rightWidth: 60, + bottomHeight: 60, + }); + background.x = 0; + background.y = 0; + background.width = modalBounds.width; + background.height = modalBounds.height; + modalContainer.addChild(background); + + if (this.pixiContent.width < modalBounds.width) + this.pixiContent.x = modalBounds.width / 2 - this.pixiContent.width / 2; + if (this.pixiContent.height < modalBounds.height - this.buttonAreaHeight) + this.pixiContent.y = (modalBounds.height - this.buttonAreaHeight) / 2 - this.pixiContent.height / 2; + modalContainer.addChild(this.pixiContent); + + let buttonXPos = modalBounds.width / 2 - buttonTotalWidth / 2; + for (const buttonDef of buttonDefs) { + const button = new Button( + new PIXI.Rectangle( + buttonXPos, + modalBounds.height - this.buttonAreaHeight - this.dialogPadding, + buttonDef.width, + buttonDef.height + ), + buttonDef.caption, + ButtonTexture.Button01 + ); + button.onClick = buttonDef.click; + this.buttons.push(button); + modalContainer.addChild(button.container); + buttonXPos += buttonDef.width + this.buttonPadding; + } + this.container.addChild(modalContainer); + } + + protected abstract createContent(): PIXI.Container | GuiObject; + + public show(): Promise { + this.generate(); + console.debug( + `ModalDialogBase.show(content: ${this.pixiContent.width}x${this.pixiContent.height}, buttons: ${this.buttonCaptions})` + ); + return new Promise((resolve, reject) => { + Engine.app.stage.addChild(this.container); + this.onButtonClicked = (button) => { + this.destroy(); + resolve(button); + }; + }); + } + + /** + * Event that is triggered when the button is clicked. + */ + public onButtonClicked: (button: string) => void; + + override destroy(): void { + this.keyboardManagerUnsubscribe(); + this.guiContent?.destroy(); + super.destroy(); + } + + protected buttonClickHandler(button: string) { + if (this.onButtonClicked) this.onButtonClicked(button); + this.destroy(); + } + + private onKeyPress(event: KeyboardEvent) { + if (this.buttons.length === 0) return; + // Message box is modal, so we can safely prevent the default behavior + event.preventDefault(); + if (event.key === 'Escape') { + this.buttonClickHandler(this.buttons[this.escapeKeyIndex].getCaption()); + } else if (event.key === 'Enter') { + this.buttonClickHandler(this.buttons[0].getCaption()); + } + } +} diff --git a/src/classes/gui/PlayerNameInput.ts b/src/classes/gui/PlayerNameInput.ts new file mode 100644 index 0000000..59c1a9d --- /dev/null +++ b/src/classes/gui/PlayerNameInput.ts @@ -0,0 +1,45 @@ +import * as PIXI from 'pixi.js'; +import { Engine } from '../Bastion'; +import ModalDialogBase from './ModalDialog'; +import TextInput from './TextInput'; +import GuiObject from '../GuiObject'; + +const maxNameLength = 20; + +export default class PlayerNameInput extends ModalDialogBase { + private textInput: TextInput; + + constructor(content: PIXI.Container) { + super(['OK', 'Cancel']); + } + + public getName(): string { + return this.textInput.getText(); + } + + protected override createContent(): PIXI.Container | GuiObject { + const container = new PIXI.Container(); + const caption = new PIXI.Text({ + text: 'Enter your name:', + style: new PIXI.TextStyle({ + fill: 0xffffff, + fontSize: 24, + }), + }); + container.addChild(caption); + this.textInput = new TextInput(new PIXI.Rectangle(0, 0, maxNameLength * 20, 40), maxNameLength); + this.textInput.container.y = caption.height + 10; + container.addChild(this.textInput.container); + return container; + } + + override buttonClickHandler(button: string) { + if (button === 'OK') { + if (this.textInput.getText().length > 0) { + super.buttonClickHandler(button); + } + } else { + super.buttonClickHandler(button); + } + } +} diff --git a/src/classes/gui/TextInput.ts b/src/classes/gui/TextInput.ts index 80595d4..9bcacc3 100644 --- a/src/classes/gui/TextInput.ts +++ b/src/classes/gui/TextInput.ts @@ -9,6 +9,10 @@ export default class TextInput extends GuiObject { private text: PIXI.Text; private maxLength: number; + public getText(): string { + return this.text.text; + } + constructor(bounds: PIXI.Rectangle, maxLength: number) { super(); this.bounds = bounds; From 2e1c73c9dc90de4bd2c08b8dbc7138ae1f4b81aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20=C4=8Carapi=C4=87?= Date: Thu, 6 Feb 2025 18:20:00 +0100 Subject: [PATCH 03/11] Working on end-game menu --- src/classes/Bastion.ts | 4 - src/classes/Events.ts | 6 - src/classes/GameUIConstants.ts | 14 +- src/classes/game/KeyboardManager.ts | 35 +++-- src/classes/gui/EndGameDialog.ts | 158 +++++++++------------ src/classes/gui/HighScoreDialog.ts | 78 +++++++++++ src/classes/gui/MessageBox.ts | 9 +- src/classes/gui/ModalDialog.ts | 204 +++++++++++++++++----------- src/classes/gui/PlayerNameInput.ts | 45 ------ src/classes/gui/TextInput.ts | 23 ++-- src/scenes/Game.ts | 37 +++-- 11 files changed, 316 insertions(+), 297 deletions(-) create mode 100644 src/classes/gui/HighScoreDialog.ts delete mode 100644 src/classes/gui/PlayerNameInput.ts diff --git a/src/classes/Bastion.ts b/src/classes/Bastion.ts index 36c9f56..9a9f95d 100644 --- a/src/classes/Bastion.ts +++ b/src/classes/Bastion.ts @@ -41,7 +41,6 @@ export class Engine { export default class GameMaster { public currentScene: Scene; - private GameObjects: GameObject[] = []; constructor() { Engine.GameMaster = this; @@ -61,9 +60,6 @@ export default class GameMaster { if (this.currentScene) { this.currentScene.destroy(); } - this.GameObjects.forEach((element) => { - element.destroy(); - }); this.currentScene = newScene; this.currentScene.init(); } diff --git a/src/classes/Events.ts b/src/classes/Events.ts index 581ff8e..67be16b 100644 --- a/src/classes/Events.ts +++ b/src/classes/Events.ts @@ -28,9 +28,3 @@ export enum StatsEvents { export enum GemEvents { TowerPanelSelectGem = 'towerTabSelectGem', } - -export enum EndMissionDialogEvents { - NextMission = 'nextMission', - RetryMission = 'retryMission', - MainMenu = 'mainMenu', -} diff --git a/src/classes/GameUIConstants.ts b/src/classes/GameUIConstants.ts index f78bf33..abae08f 100644 --- a/src/classes/GameUIConstants.ts +++ b/src/classes/GameUIConstants.ts @@ -3,7 +3,9 @@ import { Engine } from './Bastion'; export default class GameUIConstants { public static SidebarRect: PIXI.Rectangle; public static ChangeRoundButtonRect: PIXI.Rectangle; - public static EndGameDialogRect: PIXI.Rectangle; + public static StandardDialogWidth: number; + public static StandardDialogHeight: number; + public static MaximumPlayerNameLength = 20; public static init() { GameUIConstants.SidebarRect = new PIXI.Rectangle( @@ -13,13 +15,7 @@ export default class GameUIConstants { Engine.app.canvas.height ); GameUIConstants.ChangeRoundButtonRect = new PIXI.Rectangle(50, Engine.app.canvas.height - 100, 310, 100); - const endGameDialogWidth = 600; - const endGameDialogHeight = 800; - GameUIConstants.EndGameDialogRect = new PIXI.Rectangle( - (Engine.app.canvas.width - endGameDialogWidth) / 2, - (Engine.app.canvas.height - endGameDialogHeight) / 2, - endGameDialogWidth, - endGameDialogHeight - ); + GameUIConstants.StandardDialogWidth = 600; + GameUIConstants.StandardDialogHeight = 800; } } diff --git a/src/classes/game/KeyboardManager.ts b/src/classes/game/KeyboardManager.ts index 37ab5db..bffe515 100644 --- a/src/classes/game/KeyboardManager.ts +++ b/src/classes/game/KeyboardManager.ts @@ -2,45 +2,42 @@ * Handles keyboard events. */ class KeyboardManager { - private static listeners: Map void)[]> = new Map(); + private static listeners: ((event: KeyboardEvent) => void)[] = []; public static init() { window.addEventListener('keydown', KeyboardManager.handleKeyDown); } /** - * Add a callback to be called when the specified key is pressed. + * Add a callback to be called when a key is pressed. * Note: Calling preventDefault() on the event will prevent other callbacks from being called. * @param key The key to listen for. * @param callback The callback to call when the key is pressed. * @returns A function that can be called to remove the callback. */ - public static onKey(key: string, callback: (event: KeyboardEvent) => void) { - if (!KeyboardManager.listeners.has(key)) { - KeyboardManager.listeners.set(key, []); - } - KeyboardManager.listeners.get(key).push(callback); - return () => KeyboardManager.offKey(key, callback); + public static onKeyPressed(callback: (event: KeyboardEvent) => void) { + KeyboardManager.listeners = [...KeyboardManager.listeners, callback]; + return () => KeyboardManager.offKey(callback); } /** - * Remove a callback from the specified key. + * Remove a callback. */ - public static offKey(key: string, callback: (event: KeyboardEvent) => void) { - if (KeyboardManager.listeners.has(key)) { - const index = KeyboardManager.listeners.get(key).indexOf(callback); - if (index >= 0) { - KeyboardManager.listeners.get(key).splice(index, 1); - } + public static offKey(callback: (event: KeyboardEvent) => void) { + const index = KeyboardManager.listeners.indexOf(callback); + if (index >= 0) { + KeyboardManager.listeners = [ + ...KeyboardManager.listeners.slice(0, index), + ...KeyboardManager.listeners.slice(index + 1), + ]; } } private static handleKeyDown(event: KeyboardEvent) { - if (KeyboardManager.listeners.has(event.key)) { + if (KeyboardManager.listeners.length > 0) { console.log(`Key down: ${event.key}`); - const callbacks = KeyboardManager.listeners.get(event.key); - for (let i = callbacks.length - 1; i >= 0; i--) { - callbacks[i](event); + for (let i = KeyboardManager.listeners.length - 1; i >= 0; i--) { + KeyboardManager.listeners[i](event); if (event.defaultPrevented) { break; } diff --git a/src/classes/gui/EndGameDialog.ts b/src/classes/gui/EndGameDialog.ts index 30c419a..99e0e86 100644 --- a/src/classes/gui/EndGameDialog.ts +++ b/src/classes/gui/EndGameDialog.ts @@ -1,119 +1,91 @@ import * as PIXI from 'pixi.js'; -import GuiObject from '../GuiObject'; import GameAssets from '../Assets'; import GameUIConstants from '../GameUIConstants'; -import Button, { ButtonTexture } from './Button'; -import { Engine } from '../Bastion'; -import { EndMissionDialogEvents } from '../Events'; +import ModalDialogBase from './ModalDialog'; +import TextInput from './TextInput'; import MessageBox from './MessageBox'; -import KeyboardManager from '../game/KeyboardManager'; -export default class EndGameDialog extends GuiObject { - private dialogSprite: PIXI.NineSliceSprite; +export const EndGameDialogButtons = { + Confirm: 'OK', + Skip: 'Skip', +}; + +export default class EndGameDialog extends ModalDialogBase { private dialogCaption: PIXI.Text; - private nextMissionButton: Button; - private retryButton: Button; - private mainMenuButton: Button; - private overlay: PIXI.Graphics; + private playerNameTextInput: TextInput; private lost: boolean; - private keyboardManagerUnsubscribe: () => void; - constructor(bounds: PIXI.Rectangle, lost: boolean) { - super(); + constructor(lost: boolean) { + super( + [EndGameDialogButtons.Confirm, EndGameDialogButtons.Skip], + EndGameDialogButtons.Confirm, + EndGameDialogButtons.Skip + ); this.lost = lost; - // Show overlay to prevent user from interacting with the game - this.overlay = new PIXI.Graphics(); - this.overlay.rect(0, 0, bounds.width, bounds.height); - this.overlay.fill({ color: 0x000000, alpha: 0.5 }); - // Prevent interaction with the underlying scene - this.overlay.interactive = true; - this.container.addChild(this.overlay); + } - this.dialogSprite = new PIXI.NineSliceSprite({ + protected override generate(): void { + super.generate(); + this.dialogCaption = new PIXI.Text({ + text: this.lost ? 'You lost!' : 'You won!', + style: new PIXI.TextStyle({ + fill: 0xffffff, + fontSize: 36, + }), + }); + this.dialogContainer.addChild(this.dialogCaption); + this.dialogCaption.anchor.set(0.5, 0.5); + this.dialogCaption.x = this.dialogContainer.width / 2; + this.dialogCaption.y = 50; + } + + protected override createDialogBackground(width: number, height: number): PIXI.Container { + const background = new PIXI.NineSliceSprite({ texture: GameAssets.EndScreenDialog, leftWidth: 50, topHeight: 100, rightWidth: 50, bottomHeight: 50, }); - this.dialogSprite.x = GameUIConstants.EndGameDialogRect.x; - this.dialogSprite.y = GameUIConstants.EndGameDialogRect.y; - this.dialogSprite.width = GameUIConstants.EndGameDialogRect.width; - this.dialogSprite.height = GameUIConstants.EndGameDialogRect.height; - this.container.addChild(this.dialogSprite); - this.dialogCaption = new PIXI.Text({ - text: lost ? 'You lost!' : 'You won!', + background.x = 0; + background.y = 0; + background.width = width; + background.height = height; + return background; + } + + protected override createContent(): PIXI.Container { + const container = new PIXI.Container(); + const caption = new PIXI.Text({ + text: 'Enter your name:', style: new PIXI.TextStyle({ fill: 0xffffff, - fontSize: 36, + fontSize: 24, }), }); - this.container.addChild(this.dialogCaption); - this.dialogCaption.anchor.set(0.5, 0.5); - this.dialogCaption.x = GameUIConstants.EndGameDialogRect.x + GameUIConstants.EndGameDialogRect.width / 2; - this.dialogCaption.y = GameUIConstants.EndGameDialogRect.y + 50; - //this.setupButtons(lost); - this.keyboardManagerUnsubscribe = KeyboardManager.onKey('Escape', (e) => { - if (e.key === 'Escape') { - this.onMainMission(); - } - }); - this.container.on('destroyed', () => { - this.keyboardManagerUnsubscribe(); - }); - } - - private showButtons(lost: boolean) { - const buttonContainer = new PIXI.Container(); - const buttonWidth = 200; - const buttonHeight = 80; - const buttonPadding = 10; - if (lost) { - this.retryButton = new Button( - new PIXI.Rectangle(0, 0, buttonWidth, buttonHeight), - 'Retry', - ButtonTexture.Button01 - ); - this.retryButton.onClick = () => { - Engine.GameScene.events.emit(EndMissionDialogEvents.RetryMission); - }; - buttonContainer.addChild(this.retryButton.container); - } else { - this.nextMissionButton = new Button( - new PIXI.Rectangle(0, 0, buttonWidth, buttonHeight), - 'Next Mission', - ButtonTexture.Button01 - ); - this.nextMissionButton.onClick = () => { - Engine.GameScene.events.emit(EndMissionDialogEvents.NextMission); - }; - buttonContainer.addChild(this.nextMissionButton.container); - } - this.mainMenuButton = new Button( - new PIXI.Rectangle(0, buttonHeight + buttonPadding, buttonWidth, buttonHeight), - 'Main Menu', - ButtonTexture.Button01 + container.addChild(caption); + this.playerNameTextInput = new TextInput( + GameUIConstants.MaximumPlayerNameLength * 20, + GameUIConstants.MaximumPlayerNameLength ); - this.mainMenuButton.onClick = this.onMainMission.bind(this); - buttonContainer.addChild(this.mainMenuButton.container); - this.container.addChild(buttonContainer); - buttonContainer.x = - GameUIConstants.EndGameDialogRect.x + - GameUIConstants.EndGameDialogRect.width / 2 - - buttonContainer.width / 2; - buttonContainer.y = - GameUIConstants.EndGameDialogRect.y + GameUIConstants.EndGameDialogRect.height - buttonContainer.height; + this.playerNameTextInput.container.y = caption.height + 10; + container.addChild(this.playerNameTextInput.container); + return container; } - private showingMessageBox: boolean; - - private async onMainMission() { - if (this.showingMessageBox) return; - this.showingMessageBox = true; - const result = await MessageBox.show('Are you sure you want to return to the main menu?', ['Yes', 'No']); - this.showingMessageBox = false; - if (result === 'Yes') { - Engine.GameScene.events.emit(EndMissionDialogEvents.MainMenu); + override close(button?: string): void { + if (button === EndGameDialogButtons.Confirm && this.playerNameTextInput.getText().length == 0) { + MessageBox.show('Please enter your name.', ['OK']); + } else { + super.close(button); } } + + protected override getWidth(): number | undefined { + return GameUIConstants.StandardDialogWidth; + } + + protected override getHeight(): number | undefined { + return GameUIConstants.StandardDialogHeight; + } } diff --git a/src/classes/gui/HighScoreDialog.ts b/src/classes/gui/HighScoreDialog.ts new file mode 100644 index 0000000..e07980a --- /dev/null +++ b/src/classes/gui/HighScoreDialog.ts @@ -0,0 +1,78 @@ +import * as PIXI from 'pixi.js'; +import GameAssets from '../Assets'; +import GameUIConstants from '../GameUIConstants'; +import ModalDialogBase from './ModalDialog'; +import TextInput from './TextInput'; + +export const HighScoreDialogButtons = { + Retry: 'Retry', + MainMenu: 'Main Menu', + NextMission: 'Next Mission', +}; + +export default class HighScoreDialog extends ModalDialogBase { + private dialogCaption: PIXI.Text; + private playerNameTextInput: TextInput; + private lost: boolean; + + constructor(nextMissionAvailable: boolean) { + super( + nextMissionAvailable + ? [HighScoreDialogButtons.Retry, HighScoreDialogButtons.NextMission, HighScoreDialogButtons.MainMenu] + : [HighScoreDialogButtons.Retry, HighScoreDialogButtons.MainMenu], + nextMissionAvailable ? HighScoreDialogButtons.NextMission : HighScoreDialogButtons.Retry, + HighScoreDialogButtons.MainMenu + ); + } + + protected override generate(): void { + super.generate(); + this.dialogCaption = new PIXI.Text({ + text: 'Highscore', + style: new PIXI.TextStyle({ + fill: 0xffffff, + fontSize: 36, + }), + }); + this.dialogContainer.addChild(this.dialogCaption); + this.dialogCaption.anchor.set(0.5, 0.5); + this.dialogCaption.x = this.dialogContainer.width / 2; + this.dialogCaption.y = 50; + } + + protected override createDialogBackground(width: number, height: number): PIXI.Container { + const background = new PIXI.NineSliceSprite({ + texture: GameAssets.EndScreenDialog, + leftWidth: 50, + topHeight: 100, + rightWidth: 50, + bottomHeight: 50, + }); + background.x = 0; + background.y = 0; + background.width = width; + background.height = height; + return background; + } + + protected override createContent(): PIXI.Container { + const container = new PIXI.Container(); + const caption = new PIXI.Text({ + text: 'Leaderboard:', + style: new PIXI.TextStyle({ + fill: 0xffffff, + fontSize: 24, + }), + }); + container.addChild(caption); + return container; + } + + protected override getWidth(): number | undefined { + return GameUIConstants.StandardDialogWidth; + } + + protected override getHeight(): number | undefined { + return GameUIConstants.StandardDialogHeight; + } +} diff --git a/src/classes/gui/MessageBox.ts b/src/classes/gui/MessageBox.ts index 9bfd95f..7febad1 100644 --- a/src/classes/gui/MessageBox.ts +++ b/src/classes/gui/MessageBox.ts @@ -5,12 +5,15 @@ import GuiObject from '../GuiObject'; export default class MessageBox extends ModalDialogBase { private caption: string; - constructor(caption: string, buttons: string[], escapeKeyIndex: number = buttons.length - 1) { - super(buttons, escapeKeyIndex); + constructor(caption: string, buttons: string[], escapeKeyButton?: string | null, enterKeyButton?: string | null) { + if (!enterKeyButton && buttons.length > 0) enterKeyButton = buttons[0]; + if (!escapeKeyButton && buttons.length > 0) escapeKeyButton = buttons[buttons.length - 1]; + + super(buttons, escapeKeyButton, enterKeyButton); this.caption = caption; } - protected override createContent(): PIXI.Container | GuiObject { + protected override createContent(): PIXI.Container { const text = new PIXI.Text({ text: this.caption, style: new PIXI.TextStyle({ diff --git a/src/classes/gui/ModalDialog.ts b/src/classes/gui/ModalDialog.ts index a63480a..c7a9495 100644 --- a/src/classes/gui/ModalDialog.ts +++ b/src/classes/gui/ModalDialog.ts @@ -7,25 +7,97 @@ import Button, { ButtonTexture } from './Button'; import KeyboardManager from '../game/KeyboardManager'; export default abstract class ModalDialogBase extends GuiObject { - private overlay: PIXI.Graphics; - private dialogPadding = 40; - private contentPadding = 10; - private buttonPadding = 10; - private buttonAreaHeight = 40; - private buttonHeight = 60; - private buttonCaptions: string[]; - private buttons: Button[] = []; - private escapeKeyIndex: number; - private keyboardManagerUnsubscribe: () => void; - private pixiContent: PIXI.Container; - private guiContent: GuiObject; - private generated = false; + protected overlay: PIXI.Graphics; + protected dialogPadding = 40; + protected contentPadding = 10; + protected buttonPadding = 10; + protected buttonAreaHeight = 40; + protected buttonHeight = 60; + protected buttonCaptions: string[]; + protected buttons: Button[] = []; + protected dialogContent: PIXI.Container; + protected dialogContainer: PIXI.Container; - constructor(buttonCaptions: string[], escapeKeyIndex: number = buttonCaptions.length - 1) { + private generated = false; + private escapeKeyButton?: string | null; + private enterKeyButton?: string | null; + private keyboardManagerUnsubscribe: () => void; + + protected constructor(buttons: string[], enterKeyButton?: string | null, escapeKeyButton?: string | null) { super(); - this.escapeKeyIndex = escapeKeyIndex; - this.buttonCaptions = buttonCaptions; - this.keyboardManagerUnsubscribe = KeyboardManager.onKey('Escape', this.onKeyPress.bind(this)); + this.buttonCaptions = buttons; + if (escapeKeyButton && !buttons.includes(escapeKeyButton)) + throw new Error(`Escape key button "${escapeKeyButton}" not found in buttons: ${buttons}`); + this.escapeKeyButton = escapeKeyButton; + if (enterKeyButton && !buttons.includes(enterKeyButton)) + throw new Error(`Enter key button "${enterKeyButton}" not found in buttons: ${buttons}`); + this.enterKeyButton = enterKeyButton; + this.keyboardManagerUnsubscribe = KeyboardManager.onKeyPressed(this.onKeyPress.bind(this)); + } + + /** + * Show the dialog and await the user's response. + * @param width The width of the dialog (optional, if not provided then it is automatically calculated). + * @param height The height of the dialog (optional, if not provided then it is automatically calculated). + * @returns Button that was clicked or null if dialog was closed without clicking a button (potentially no buttons defined). + */ + public show(): Promise { + this.generate(); + console.debug( + `ModalDialogBase.show(content: ${this.dialogContainer.width}x${this.dialogContainer.height}, buttons: ${this.buttonCaptions})` + ); + return new Promise((resolve, reject) => { + Engine.app.stage.addChild(this.container); + this.onClosed = (button) => { + this.destroy(); + resolve(button); + }; + }); + } + + /** + * Event that is triggered when the dialog is closed. + * @param button The button that was clicked or null if the dialog was closed without clicking a button. + */ + public onClosed: (button?: string) => void; + + /** + * Create the content of the dialog. + */ + protected abstract createContent(): PIXI.Container; + + /** + * Creates dialog background. + */ + protected createDialogBackground(width: number, height: number): PIXI.Container { + const background = new PIXI.NineSliceSprite({ + texture: GameAssets.Frame04Texture, + leftWidth: 60, + topHeight: 60, + rightWidth: 60, + bottomHeight: 60, + }); + background.x = 0; + background.y = 0; + background.width = width; + background.height = height; + return background; + } + + /** + * Gets the width of the dialog. + * If not overridden, the width is automatically calculated. + */ + protected getWidth(): number | undefined { + return undefined; + } + + /** + * Gets the height of the dialog. + * If not overridden, the height is automatically calculated. + */ + protected getHeight(): number | undefined { + return undefined; } protected generate() { @@ -39,30 +111,22 @@ export default abstract class ModalDialogBase extends GuiObject { this.overlay.interactive = true; this.container.addChild(this.overlay); - const content = this.createContent(); - if (content instanceof GuiObject) { - this.guiContent = content; - this.pixiContent = content.container; - } else { - this.pixiContent = content; - } - + this.dialogContent = this.createContent(); const buttonDefs = this.buttonCaptions.map((btnCaption) => ({ caption: btnCaption, width: btnCaption.length * 16 + 40, height: this.buttonHeight, - click: () => this.buttonClickHandler(btnCaption), + click: () => this.close(btnCaption), })); let buttonTotalWidth = 0; for (const buttonDef of buttonDefs) { if (buttonTotalWidth > 0) buttonTotalWidth += this.buttonPadding; buttonTotalWidth += buttonDef.width; } - const contentWidth = this.pixiContent.width + this.contentPadding * 2; - const contentHeight = this.pixiContent.height + this.contentPadding * 2; - let width = Math.max(buttonTotalWidth, contentWidth) + this.dialogPadding * 2; - - const height = contentHeight + this.buttonAreaHeight + this.dialogPadding * 2; + const contentWidth = this.dialogContent.width + this.contentPadding * 2; + const contentHeight = this.dialogContent.height + this.contentPadding * 2; + let width = this.getWidth() || Math.max(buttonTotalWidth, contentWidth) + this.dialogPadding * 2; + let height = this.getHeight() || contentHeight + this.buttonAreaHeight + this.dialogPadding * 2; const modalBounds = new PIXI.Rectangle( Engine.app.canvas.width / 2 - width / 2, Engine.app.canvas.height / 2 - height / 2, @@ -70,28 +134,18 @@ export default abstract class ModalDialogBase extends GuiObject { height ); - const modalContainer = new PIXI.Container(); - modalContainer.x = modalBounds.x; - modalContainer.y = modalBounds.y; + this.dialogContainer = new PIXI.Container(); + this.dialogContainer.x = modalBounds.x; + this.dialogContainer.y = modalBounds.y; - const background = new PIXI.NineSliceSprite({ - texture: GameAssets.Frame04Texture, - leftWidth: 60, - topHeight: 60, - rightWidth: 60, - bottomHeight: 60, - }); - background.x = 0; - background.y = 0; - background.width = modalBounds.width; - background.height = modalBounds.height; - modalContainer.addChild(background); + const background = this.createDialogBackground(modalBounds.width, modalBounds.height); + this.dialogContainer.addChild(background); - if (this.pixiContent.width < modalBounds.width) - this.pixiContent.x = modalBounds.width / 2 - this.pixiContent.width / 2; - if (this.pixiContent.height < modalBounds.height - this.buttonAreaHeight) - this.pixiContent.y = (modalBounds.height - this.buttonAreaHeight) / 2 - this.pixiContent.height / 2; - modalContainer.addChild(this.pixiContent); + if (this.dialogContent.width < modalBounds.width) + this.dialogContent.x = modalBounds.width / 2 - this.dialogContent.width / 2; + if (this.dialogContent.height < modalBounds.height - this.buttonAreaHeight) + this.dialogContent.y = (modalBounds.height - this.buttonAreaHeight) / 2 - this.dialogContent.height / 2; + this.dialogContainer.addChild(this.dialogContent); let buttonXPos = modalBounds.width / 2 - buttonTotalWidth / 2; for (const buttonDef of buttonDefs) { @@ -107,52 +161,36 @@ export default abstract class ModalDialogBase extends GuiObject { ); button.onClick = buttonDef.click; this.buttons.push(button); - modalContainer.addChild(button.container); + this.dialogContainer.addChild(button.container); buttonXPos += buttonDef.width + this.buttonPadding; } - this.container.addChild(modalContainer); - } - - protected abstract createContent(): PIXI.Container | GuiObject; - - public show(): Promise { - this.generate(); - console.debug( - `ModalDialogBase.show(content: ${this.pixiContent.width}x${this.pixiContent.height}, buttons: ${this.buttonCaptions})` - ); - return new Promise((resolve, reject) => { - Engine.app.stage.addChild(this.container); - this.onButtonClicked = (button) => { - this.destroy(); - resolve(button); - }; - }); + this.container.addChild(this.dialogContainer); } /** - * Event that is triggered when the button is clicked. + * Close the dialog. + * Only use this method if you want to close the dialog without clicking a button. */ - public onButtonClicked: (button: string) => void; + public close(button?: string) { + this.destroy(); + if (this.onClosed) this.onClosed(button); + } override destroy(): void { this.keyboardManagerUnsubscribe(); - this.guiContent?.destroy(); super.destroy(); } - protected buttonClickHandler(button: string) { - if (this.onButtonClicked) this.onButtonClicked(button); - this.destroy(); - } - private onKeyPress(event: KeyboardEvent) { if (this.buttons.length === 0) return; - // Message box is modal, so we can safely prevent the default behavior - event.preventDefault(); - if (event.key === 'Escape') { - this.buttonClickHandler(this.buttons[this.escapeKeyIndex].getCaption()); - } else if (event.key === 'Enter') { - this.buttonClickHandler(this.buttons[0].getCaption()); + if (this.escapeKeyButton && event.key === 'Escape') { + // Message box is modal, so we can safely prevent the default behavior + event.preventDefault(); + this.close(this.escapeKeyButton); + } else if (this.enterKeyButton && event.key === 'Enter') { + // Message box is modal, so we can safely prevent the default behavior + event.preventDefault(); + this.close(this.enterKeyButton); } } } diff --git a/src/classes/gui/PlayerNameInput.ts b/src/classes/gui/PlayerNameInput.ts deleted file mode 100644 index 59c1a9d..0000000 --- a/src/classes/gui/PlayerNameInput.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as PIXI from 'pixi.js'; -import { Engine } from '../Bastion'; -import ModalDialogBase from './ModalDialog'; -import TextInput from './TextInput'; -import GuiObject from '../GuiObject'; - -const maxNameLength = 20; - -export default class PlayerNameInput extends ModalDialogBase { - private textInput: TextInput; - - constructor(content: PIXI.Container) { - super(['OK', 'Cancel']); - } - - public getName(): string { - return this.textInput.getText(); - } - - protected override createContent(): PIXI.Container | GuiObject { - const container = new PIXI.Container(); - const caption = new PIXI.Text({ - text: 'Enter your name:', - style: new PIXI.TextStyle({ - fill: 0xffffff, - fontSize: 24, - }), - }); - container.addChild(caption); - this.textInput = new TextInput(new PIXI.Rectangle(0, 0, maxNameLength * 20, 40), maxNameLength); - this.textInput.container.y = caption.height + 10; - container.addChild(this.textInput.container); - return container; - } - - override buttonClickHandler(button: string) { - if (button === 'OK') { - if (this.textInput.getText().length > 0) { - super.buttonClickHandler(button); - } - } else { - super.buttonClickHandler(button); - } - } -} diff --git a/src/classes/gui/TextInput.ts b/src/classes/gui/TextInput.ts index 9bcacc3..6843f94 100644 --- a/src/classes/gui/TextInput.ts +++ b/src/classes/gui/TextInput.ts @@ -2,25 +2,21 @@ import * as PIXI from 'pixi.js'; import GuiObject from '../GuiObject'; import Assets from '../Assets'; import GameAssets from '../Assets'; +import KeyboardManager from '../game/KeyboardManager'; export default class TextInput extends GuiObject { - private bounds: PIXI.Rectangle; private backgroundSprite: PIXI.NineSliceSprite; private text: PIXI.Text; private maxLength: number; + private keyboardManagerUnsubscribe: () => void; public getText(): string { return this.text.text; } - constructor(bounds: PIXI.Rectangle, maxLength: number) { + constructor(width: number, maxLength: number) { super(); - this.bounds = bounds; this.maxLength = maxLength; - this.container.x = this.bounds.x; - this.container.y = this.bounds.y; - this.container.width = this.bounds.width; - this.container.height = this.bounds.height; this.backgroundSprite = new PIXI.NineSliceSprite({ texture: GameAssets.Frame01Texture, leftWidth: 20, @@ -30,21 +26,20 @@ export default class TextInput extends GuiObject { }); this.backgroundSprite.x = 0; this.backgroundSprite.y = 0; - this.backgroundSprite.width = this.bounds.width; - this.backgroundSprite.height = this.bounds.height; + this.backgroundSprite.width = width; + this.backgroundSprite.height = 60; this.container.addChild(this.backgroundSprite); - this.container.x = this.bounds.x; - this.container.y = this.bounds.y; this.text = new PIXI.Text({ text: '', style: new PIXI.TextStyle({ fill: 0xffffff, - fontSize: 16, + fontSize: 24, }), }); - this.text.x = 10; - this.text.y = 10; + this.text.x = 20; + this.text.y = 20; this.container.addChild(this.text); + this.keyboardManagerUnsubscribe = KeyboardManager.onKeyPressed(this.onKeyPress.bind(this)); } private onKeyPress(event: KeyboardEvent) { diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index 2655e6d..05c3b7c 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -4,7 +4,7 @@ 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, EndMissionDialogEvents } from '../classes/Events'; +import { WaveManagerEvents, CreepEvents, GemEvents } from '../classes/Events'; import Sidebar from '../classes/gui/Sidebar'; import Button, { ButtonTexture } from '../classes/gui/Button'; import Scene from './Scene'; @@ -17,6 +17,7 @@ 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'; enum RoundMode { Purchase = 0, @@ -41,7 +42,6 @@ export class GameScene extends Scene { private playerWon: boolean = false; private destroyTicker: boolean = false; private offerGemsSprite: PIXI.NineSliceSprite; - private endGameDialog: EndGameDialog; private dimGraphics: PIXI.Graphics = new PIXI.Graphics({ x: 0, y: 0, @@ -119,9 +119,6 @@ export class GameScene extends Scene { } this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower); }); - this.events.on(EndMissionDialogEvents.MainMenu, this.onEndMissionDialogMainMenuClicked.bind(this)); - this.events.on(EndMissionDialogEvents.RetryMission, this.onEndMissionDialogRetryMissionClicked.bind(this)); - this.events.on(EndMissionDialogEvents.NextMission, this.onEndMissionDialogNextMissionClicked.bind(this)); this.ticker = new PIXI.Ticker(); this.ticker.maxFPS = 60; this.ticker.minFPS = 30; @@ -168,10 +165,10 @@ export class GameScene extends Scene { if (this.MissionStats.getHP() <= 0) { this.isGameOver = true; - this.ShowScoreScreen(true); + this.ShowEndgameDialog(true); } else if (this.playerWon) { this.isGameOver = true; - this.ShowScoreScreen(false); + this.ShowEndgameDialog(false); } } public DarkenScreen() { @@ -247,10 +244,18 @@ export class GameScene extends Scene { this.setRoundMode(RoundMode.Purchase); } - private ShowScoreScreen(lost) { - const bounds = new PIXI.Rectangle(0, 0, Engine.app.canvas.width, Engine.app.canvas.height); - this.endGameDialog = new EndGameDialog(bounds, lost); - Engine.GameMaster.currentScene.stage.addChild(this.endGameDialog.container); + private async ShowEndgameDialog(lost) { + const endGameDialog = new EndGameDialog(lost); + await endGameDialog.show(); + const highScore = new HighScoreDialog(this.missionIndex + 1 < GameAssets.Missions.length); + const result = await highScore.show(); + if (result === HighScoreDialogButtons.MainMenu) { + this.ReturnToMain(); + } else if (result === HighScoreDialogButtons.NextMission) { + Engine.GameMaster.changeScene(new GameScene(GameAssets.Missions[this.missionIndex + 1].name)); + } else if (result === HighScoreDialogButtons.Retry) { + Engine.GameMaster.changeScene(new GameScene(this.mission.name)); + } } public onCreepEscaped(creep: Creep) { @@ -266,14 +271,6 @@ export class GameScene extends Scene { } } - private onEndMissionDialogMainMenuClicked() { - this.ReturnToMain(); - } - - private onEndMissionDialogRetryMissionClicked() {} - - private onEndMissionDialogNextMissionClicked() {} - public destroy(): void { super.destroy(); this.isGameOver = true; @@ -282,8 +279,6 @@ export class GameScene extends Scene { } private ReturnToMain() { - this.destroy(); - Engine.GameMaster.currentScene.stage.removeChildren(); Engine.GameMaster.changeScene(new MissionPickerScene()); } } From 93da72f55c59e0d6ebf7488bf1b4b979eed36519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20=C4=8Carapi=C4=87?= Date: Thu, 6 Feb 2025 19:51:11 +0100 Subject: [PATCH 04/11] Working on end-game menu --- src/classes/GameUIConstants.ts | 4 - src/classes/game/HighScoreManager.ts | 71 +++++++++++++++++ src/classes/game/MissionStats.ts | 35 +++++++- src/classes/gui/EndGameDialog.ts | 115 +++++++++++++++++++++------ src/classes/gui/HighScoreDialog.ts | 82 ++++++++++++++----- src/classes/gui/ModalDialog.ts | 74 ++++++++++------- src/classes/gui/TextInput.ts | 6 +- src/scenes/Game.ts | 6 +- 8 files changed, 304 insertions(+), 89 deletions(-) create mode 100644 src/classes/game/HighScoreManager.ts diff --git a/src/classes/GameUIConstants.ts b/src/classes/GameUIConstants.ts index abae08f..060dff4 100644 --- a/src/classes/GameUIConstants.ts +++ b/src/classes/GameUIConstants.ts @@ -3,8 +3,6 @@ import { Engine } from './Bastion'; export default class GameUIConstants { public static SidebarRect: PIXI.Rectangle; public static ChangeRoundButtonRect: PIXI.Rectangle; - public static StandardDialogWidth: number; - public static StandardDialogHeight: number; public static MaximumPlayerNameLength = 20; public static init() { @@ -15,7 +13,5 @@ export default class GameUIConstants { Engine.app.canvas.height ); GameUIConstants.ChangeRoundButtonRect = new PIXI.Rectangle(50, Engine.app.canvas.height - 100, 310, 100); - GameUIConstants.StandardDialogWidth = 600; - GameUIConstants.StandardDialogHeight = 800; } } diff --git a/src/classes/game/HighScoreManager.ts b/src/classes/game/HighScoreManager.ts new file mode 100644 index 0000000..3015554 --- /dev/null +++ b/src/classes/game/HighScoreManager.ts @@ -0,0 +1,71 @@ +/** + * Handles the high score system. + */ +export class HighScoreManager { + private static readonly STORAGE_KEY_PREFIX = 'highscore_'; + private static readonly MAX_SCORES = 10; + + public readonly missionName: string; + private scores: PlayerScore[]; + + constructor(missionName: string) { + this.missionName = missionName; + this.scores = this.loadScores(); + this.scores.sort((a, b) => b.score - a.score || a.timestamp - b.timestamp); + } + + private loadScores(): PlayerScore[] { + const storedScores = localStorage.getItem(HighScoreManager.STORAGE_KEY_PREFIX + this.missionName); + return HighScoreManager.parseStoredScores(storedScores); + } + + private saveScores(): void { + localStorage.setItem(HighScoreManager.STORAGE_KEY_PREFIX + this.missionName, JSON.stringify(this.scores)); + } + + public addScore(playerScore: PlayerScore): void { + this.scores.push(playerScore); + this.scores.sort((a, b) => b.score - a.score); + if (this.scores.length > HighScoreManager.MAX_SCORES) { + this.scores.length = HighScoreManager.MAX_SCORES; + } + this.saveScores(); + } + + public getScores(): PlayerScore[] { + return this.scores; + } + + private static parseStoredScores(storedScores: string | null): PlayerScore[] { + if (!storedScores) { + return []; + } + try { + const parsedScores = JSON.parse(storedScores); + if ( + Array.isArray(parsedScores) && + parsedScores.every( + (score) => + typeof score.playerName === 'string' && + typeof score.score === 'number' && + typeof score.timestamp === 'number' + ) + ) { + return parsedScores.map((score) => ({ + playerName: score.playerName, + score: score.score, + timestamp: score.timestamp, + })); + } + } catch (e) { + console.error('Failed to parse stored scores:', e); + } + return []; + } +} + +export type PlayerScore = { + playerName: string; + score: number; + timestamp: number; +}; diff --git a/src/classes/game/MissionStats.ts b/src/classes/game/MissionStats.ts index f7b894c..d9074f2 100644 --- a/src/classes/game/MissionStats.ts +++ b/src/classes/game/MissionStats.ts @@ -8,14 +8,16 @@ import Gem from './Gem'; export default class MissionStats extends GameObject { private hp: number = 100; private gold: number = 0; + private goldEarned: number = 0; + private goldSpent: number = 0; + private wavesSurvived: number = 0; + private damageDealt: number = 0; + private creepsKilled: number = 0; private goldText: PIXI.Text; private healthText: PIXI.Text; private waveText: PIXI.Text; private inventory: Gem[] = []; - // TODO: implement score keeping for leaderboards. - private score: number = 0; - public getHP() { return this.hp; } @@ -147,5 +149,32 @@ export default class MissionStats extends GameObject { }); } + public getStats() { + return { + hp: this.hp, + gold: this.gold, + wavesSurvived: this.wavesSurvived, + goldEarned: this.goldEarned, + goldSpent: this.goldSpent, + score: this.calculateScore(), + }; + } + + private calculateScore() { + const uniqueGems = []; + for (const gem of this.inventory) { + if (!uniqueGems.includes(gem.definition.name)) { + uniqueGems.push(gem.definition.name); + } + } + return ( + this.damageDealt * 2 + + this.hp * 10 + + (this.goldEarned - this.goldSpent) * 3 + + this.wavesSurvived * 100 + + uniqueGems.length * 100 + ); + } + public update() {} } diff --git a/src/classes/gui/EndGameDialog.ts b/src/classes/gui/EndGameDialog.ts index 99e0e86..b0d0c52 100644 --- a/src/classes/gui/EndGameDialog.ts +++ b/src/classes/gui/EndGameDialog.ts @@ -4,6 +4,8 @@ import GameUIConstants from '../GameUIConstants'; import ModalDialogBase from './ModalDialog'; import TextInput from './TextInput'; import MessageBox from './MessageBox'; +import { HighScoreManager } from '../game/HighScoreManager'; +import MissionStats from '../game/MissionStats'; export const EndGameDialogButtons = { Confirm: 'OK', @@ -14,14 +16,18 @@ export default class EndGameDialog extends ModalDialogBase { private dialogCaption: PIXI.Text; private playerNameTextInput: TextInput; private lost: boolean; + private highScore: HighScoreManager; + private missionStats: MissionStats; - constructor(lost: boolean) { + constructor(missionName: string, missionStats: MissionStats, lost: boolean) { super( [EndGameDialogButtons.Confirm, EndGameDialogButtons.Skip], EndGameDialogButtons.Confirm, EndGameDialogButtons.Skip ); this.lost = lost; + this.highScore = new HighScoreManager(missionName); + this.missionStats = missionStats; } protected override generate(): void { @@ -31,61 +37,118 @@ export default class EndGameDialog extends ModalDialogBase { style: new PIXI.TextStyle({ fill: 0xffffff, fontSize: 36, + stroke: { color: 0x000000, width: 2 }, + dropShadow: { + color: 0x000000, + blur: 8, + distance: 0, + }, }), }); - this.dialogContainer.addChild(this.dialogCaption); this.dialogCaption.anchor.set(0.5, 0.5); this.dialogCaption.x = this.dialogContainer.width / 2; this.dialogCaption.y = 50; + this.dialogContainer.addChild(this.dialogCaption); } - protected override createDialogBackground(width: number, height: number): PIXI.Container { - const background = new PIXI.NineSliceSprite({ + protected override createDialogBackground(): PIXI.NineSliceSprite { + return new PIXI.NineSliceSprite({ texture: GameAssets.EndScreenDialog, leftWidth: 50, topHeight: 100, rightWidth: 50, bottomHeight: 50, }); - background.x = 0; - background.y = 0; - background.width = width; - background.height = height; - return background; } protected override createContent(): PIXI.Container { const container = new PIXI.Container(); - const caption = new PIXI.Text({ - text: 'Enter your name:', - style: new PIXI.TextStyle({ - fill: 0xffffff, - fontSize: 24, - }), - }); - container.addChild(caption); - this.playerNameTextInput = new TextInput( - GameUIConstants.MaximumPlayerNameLength * 20, - GameUIConstants.MaximumPlayerNameLength - ); - this.playerNameTextInput.container.y = caption.height + 10; + const lineHeight = 35; + const lblScore = this.createText('Mission details:', '#fee', true); + container.addChild(lblScore); + const stats = this.missionStats.getStats(); + const width = this.getWidth() - this.background.leftWidth - this.background.rightWidth - 20; + const labels = [ + this.createText('HP:'), + this.createText('Gold:'), + this.createText('Waves Survived:'), + this.createText('Gold Earned:'), + this.createText('Gold Spent:'), + this.createText('----'), + this.createText('Score:'), + ]; + const values = [ + this.createText(stats.hp.toString(), 'yellow'), + this.createText(stats.gold.toString(), 'yellow'), + this.createText(stats.wavesSurvived.toString(), 'yellow'), + this.createText(stats.goldEarned.toString(), 'yellow'), + this.createText(stats.goldSpent.toString(), 'yellow'), + this.createText('----', 'yellow'), + this.createText(stats.score.toString(), 'yellow'), + ]; + const valueX = 300; + for (let i = 0; i < labels.length; i++) { + if (labels[i].text === '----') { + const line = new PIXI.Graphics(); + const y = lblScore.y + lblScore.height + 10 + i * lineHeight + lineHeight / 2; + line.moveTo(10, y); + line.lineTo(width, y); + line.stroke({ color: 'yellow', width: 2 }); + container.addChild(line); + } else { + labels[i].x = 10; + labels[i].y = lblScore.y + lblScore.height + 10 + i * lineHeight; + container.addChild(labels[i]); + + values[i].x = valueX; + values[i].y = lblScore.y + lblScore.height + 10 + i * lineHeight; + container.addChild(values[i]); + } + } + const offsetY = values[values.length - 1].y + lineHeight + 80; + + const lblName = this.createText('Enter your name:'); + lblName.y = offsetY; + container.addChild(lblName); + this.playerNameTextInput = new TextInput(width, GameUIConstants.MaximumPlayerNameLength); + this.playerNameTextInput.container.y = lblName.y + lblName.height + 10; container.addChild(this.playerNameTextInput.container); return container; } override close(button?: string): void { - if (button === EndGameDialogButtons.Confirm && this.playerNameTextInput.getText().length == 0) { - MessageBox.show('Please enter your name.', ['OK']); + if (button === EndGameDialogButtons.Confirm) { + if (this.playerNameTextInput.getText().length == 0) { + MessageBox.show('Please enter your name.', ['OK']); + } else { + this.highScore.addScore({ + playerName: this.playerNameTextInput.getText(), + score: this.missionStats.getStats().score, + timestamp: Date.now(), + }); + super.close(button); + } } else { super.close(button); } } + private createText(caption: string, color: string = '#fff', bold = false): PIXI.Text { + return new PIXI.Text({ + text: caption, + style: new PIXI.TextStyle({ + fill: color, + fontSize: 24, + fontWeight: bold ? 'bold' : 'normal', + }), + }); + } + protected override getWidth(): number | undefined { - return GameUIConstants.StandardDialogWidth; + return 600; } protected override getHeight(): number | undefined { - return GameUIConstants.StandardDialogHeight; + return 800; } } diff --git a/src/classes/gui/HighScoreDialog.ts b/src/classes/gui/HighScoreDialog.ts index e07980a..082631a 100644 --- a/src/classes/gui/HighScoreDialog.ts +++ b/src/classes/gui/HighScoreDialog.ts @@ -1,8 +1,7 @@ import * as PIXI from 'pixi.js'; import GameAssets from '../Assets'; -import GameUIConstants from '../GameUIConstants'; import ModalDialogBase from './ModalDialog'; -import TextInput from './TextInput'; +import { HighScoreManager } from '../game/HighScoreManager'; export const HighScoreDialogButtons = { Retry: 'Retry', @@ -12,10 +11,9 @@ export const HighScoreDialogButtons = { export default class HighScoreDialog extends ModalDialogBase { private dialogCaption: PIXI.Text; - private playerNameTextInput: TextInput; - private lost: boolean; + private highScore: HighScoreManager; - constructor(nextMissionAvailable: boolean) { + constructor(missionName: string, nextMissionAvailable: boolean) { super( nextMissionAvailable ? [HighScoreDialogButtons.Retry, HighScoreDialogButtons.NextMission, HighScoreDialogButtons.MainMenu] @@ -23,6 +21,7 @@ export default class HighScoreDialog extends ModalDialogBase { nextMissionAvailable ? HighScoreDialogButtons.NextMission : HighScoreDialogButtons.Retry, HighScoreDialogButtons.MainMenu ); + this.highScore = new HighScoreManager(missionName); } protected override generate(): void { @@ -32,47 +31,86 @@ export default class HighScoreDialog extends ModalDialogBase { style: new PIXI.TextStyle({ fill: 0xffffff, fontSize: 36, + stroke: { color: 0x000000, width: 2 }, + dropShadow: { + color: 0x000000, + blur: 8, + distance: 0, + }, }), }); - this.dialogContainer.addChild(this.dialogCaption); this.dialogCaption.anchor.set(0.5, 0.5); this.dialogCaption.x = this.dialogContainer.width / 2; this.dialogCaption.y = 50; + this.dialogContainer.addChild(this.dialogCaption); } - protected override createDialogBackground(width: number, height: number): PIXI.Container { - const background = new PIXI.NineSliceSprite({ + protected override createDialogBackground(): PIXI.NineSliceSprite { + return new PIXI.NineSliceSprite({ texture: GameAssets.EndScreenDialog, leftWidth: 50, topHeight: 100, rightWidth: 50, bottomHeight: 50, }); - background.x = 0; - background.y = 0; - background.width = width; - background.height = height; - return background; } protected override createContent(): PIXI.Container { const container = new PIXI.Container(); - const caption = new PIXI.Text({ - text: 'Leaderboard:', - style: new PIXI.TextStyle({ - fill: 0xffffff, - fontSize: 24, - }), - }); + const caption = this.createText('Mission: ' + this.highScore.missionName, '#fee', true); container.addChild(caption); + const lineHeight = 35; + const scores = this.highScore.getScores(); + while (scores.length < 10) { + scores.push({ playerName: '---', score: 0, timestamp: 0 }); + } + + const numberTexts = [ + this.createText('#', '#fee'), + ...scores.map((_, i) => this.createText((i + 1).toString())), + ]; + const playerTexts = [ + this.createText('Player', '#fee', true), + ...scores.map((score) => this.createText(score.playerName)), + ]; + const scoreTexts = [ + this.createText('Score', '#fee', true), + ...scores.map((score) => this.createText(score.score.toString())), + ]; + const playerX = numberTexts.reduce((maxX, text) => Math.max(maxX, text.width), 0) + 20; + const scoreX = playerX + playerTexts.reduce((maxX, text) => Math.max(maxX, text.width), 0) + 20; + for (let i = 0; i < playerTexts.length; i++) { + numberTexts[i].x = 10; + numberTexts[i].y = lineHeight + 10 + i * lineHeight; + container.addChild(numberTexts[i]); + + playerTexts[i].x = playerX; + playerTexts[i].y = lineHeight + 10 + i * lineHeight; + container.addChild(playerTexts[i]); + + scoreTexts[i].x = scoreX; + scoreTexts[i].y = lineHeight + 10 + i * lineHeight; + container.addChild(scoreTexts[i]); + } return container; } + private createText(caption: string, color: string = '#fff', bold = false): PIXI.Text { + return new PIXI.Text({ + text: caption, + style: new PIXI.TextStyle({ + fill: color, + fontSize: 24, + fontWeight: bold ? 'bold' : 'normal', + }), + }); + } + protected override getWidth(): number | undefined { - return GameUIConstants.StandardDialogWidth; + return 600; } protected override getHeight(): number | undefined { - return GameUIConstants.StandardDialogHeight; + return 800; } } diff --git a/src/classes/gui/ModalDialog.ts b/src/classes/gui/ModalDialog.ts index c7a9495..8f682b4 100644 --- a/src/classes/gui/ModalDialog.ts +++ b/src/classes/gui/ModalDialog.ts @@ -8,15 +8,12 @@ import KeyboardManager from '../game/KeyboardManager'; export default abstract class ModalDialogBase extends GuiObject { protected overlay: PIXI.Graphics; - protected dialogPadding = 40; - protected contentPadding = 10; - protected buttonPadding = 10; - protected buttonAreaHeight = 40; - protected buttonHeight = 60; + protected buttonHeight = 65; protected buttonCaptions: string[]; protected buttons: Button[] = []; protected dialogContent: PIXI.Container; protected dialogContainer: PIXI.Container; + protected background: PIXI.NineSliceSprite; private generated = false; private escapeKeyButton?: string | null; @@ -43,8 +40,14 @@ export default abstract class ModalDialogBase extends GuiObject { */ public show(): Promise { this.generate(); + const dialogBounds = `x: ${Math.round(this.dialogContainer.x)}, y: ${Math.round( + this.dialogContainer.y + )}, width: ${Math.round(this.dialogContainer.width)}, height: ${Math.round(this.dialogContainer.height)}`; + const contentBounds = `x: ${Math.round(this.dialogContent.x)}, y: ${Math.round( + this.dialogContent.y + )}, width: ${Math.round(this.dialogContent.width)}, height: ${Math.round(this.dialogContent.height)}`; console.debug( - `ModalDialogBase.show(content: ${this.dialogContainer.width}x${this.dialogContainer.height}, buttons: ${this.buttonCaptions})` + `ModalDialogBase.show(dialog: ${dialogBounds}, content: ${contentBounds}, buttons: ${this.buttonCaptions})` ); return new Promise((resolve, reject) => { Engine.app.stage.addChild(this.container); @@ -69,19 +72,14 @@ export default abstract class ModalDialogBase extends GuiObject { /** * Creates dialog background. */ - protected createDialogBackground(width: number, height: number): PIXI.Container { - const background = new PIXI.NineSliceSprite({ + protected createDialogBackground(): PIXI.NineSliceSprite { + return new PIXI.NineSliceSprite({ texture: GameAssets.Frame04Texture, leftWidth: 60, topHeight: 60, rightWidth: 60, bottomHeight: 60, }); - background.x = 0; - background.y = 0; - background.width = width; - background.height = height; - return background; } /** @@ -110,23 +108,31 @@ export default abstract class ModalDialogBase extends GuiObject { // Prevent interaction with the underlying scene this.overlay.interactive = true; this.container.addChild(this.overlay); - - this.dialogContent = this.createContent(); const buttonDefs = this.buttonCaptions.map((btnCaption) => ({ caption: btnCaption, - width: btnCaption.length * 16 + 40, + width: btnCaption.length * 14 + 60, height: this.buttonHeight, click: () => this.close(btnCaption), })); + + this.background = this.createDialogBackground(); + this.dialogContent = this.createContent(); + let buttonTotalWidth = 0; for (const buttonDef of buttonDefs) { - if (buttonTotalWidth > 0) buttonTotalWidth += this.buttonPadding; + if (buttonTotalWidth > 0) buttonTotalWidth += 10; buttonTotalWidth += buttonDef.width; } - const contentWidth = this.dialogContent.width + this.contentPadding * 2; - const contentHeight = this.dialogContent.height + this.contentPadding * 2; - let width = this.getWidth() || Math.max(buttonTotalWidth, contentWidth) + this.dialogPadding * 2; - let height = this.getHeight() || contentHeight + this.buttonAreaHeight + this.dialogPadding * 2; + const buttonAreaHeight = this.buttonCaptions.length > 0 ? this.buttonHeight + 10 : 0; + + let width = + this.getWidth() || + Math.max(buttonTotalWidth, this.dialogContent.width) + + this.background.leftWidth + + this.background.rightWidth; + let height = + this.getHeight() || + this.dialogContent.height + buttonAreaHeight + this.background.topHeight + this.background.bottomHeight; const modalBounds = new PIXI.Rectangle( Engine.app.canvas.width / 2 - width / 2, Engine.app.canvas.height / 2 - height / 2, @@ -137,14 +143,24 @@ export default abstract class ModalDialogBase extends GuiObject { this.dialogContainer = new PIXI.Container(); this.dialogContainer.x = modalBounds.x; this.dialogContainer.y = modalBounds.y; + this.background.width = width; + this.background.height = height; + this.dialogContainer.addChild(this.background); - const background = this.createDialogBackground(modalBounds.width, modalBounds.height); - this.dialogContainer.addChild(background); - - if (this.dialogContent.width < modalBounds.width) + if (this.dialogContent.width < modalBounds.width) { this.dialogContent.x = modalBounds.width / 2 - this.dialogContent.width / 2; - if (this.dialogContent.height < modalBounds.height - this.buttonAreaHeight) - this.dialogContent.y = (modalBounds.height - this.buttonAreaHeight) / 2 - this.dialogContent.height / 2; + } + if ( + this.dialogContent.height < + modalBounds.height - buttonAreaHeight - this.background.topHeight - this.background.bottomHeight + ) { + this.dialogContent.y = + this.background.topHeight + + (modalBounds.height - buttonAreaHeight - this.background.topHeight - this.background.bottomHeight) / 2 - + this.dialogContent.height / 2; + } else { + this.dialogContent.y = this.background.topHeight; + } this.dialogContainer.addChild(this.dialogContent); let buttonXPos = modalBounds.width / 2 - buttonTotalWidth / 2; @@ -152,7 +168,7 @@ export default abstract class ModalDialogBase extends GuiObject { const button = new Button( new PIXI.Rectangle( buttonXPos, - modalBounds.height - this.buttonAreaHeight - this.dialogPadding, + modalBounds.height - this.buttonHeight - this.background.bottomHeight, buttonDef.width, buttonDef.height ), @@ -162,7 +178,7 @@ export default abstract class ModalDialogBase extends GuiObject { button.onClick = buttonDef.click; this.buttons.push(button); this.dialogContainer.addChild(button.container); - buttonXPos += buttonDef.width + this.buttonPadding; + buttonXPos += buttonDef.width + 10; } this.container.addChild(this.dialogContainer); } diff --git a/src/classes/gui/TextInput.ts b/src/classes/gui/TextInput.ts index 6843f94..8c95640 100644 --- a/src/classes/gui/TextInput.ts +++ b/src/classes/gui/TextInput.ts @@ -27,7 +27,7 @@ export default class TextInput extends GuiObject { this.backgroundSprite.x = 0; this.backgroundSprite.y = 0; this.backgroundSprite.width = width; - this.backgroundSprite.height = 60; + this.backgroundSprite.height = 80; this.container.addChild(this.backgroundSprite); this.text = new PIXI.Text({ text: '', @@ -36,8 +36,8 @@ export default class TextInput extends GuiObject { fontSize: 24, }), }); - this.text.x = 20; - this.text.y = 20; + this.text.x = 30; + this.text.y = 25; this.container.addChild(this.text); this.keyboardManagerUnsubscribe = KeyboardManager.onKeyPressed(this.onKeyPress.bind(this)); } diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index 05c3b7c..92d90f6 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -245,15 +245,17 @@ export class GameScene extends Scene { } private async ShowEndgameDialog(lost) { - const endGameDialog = new EndGameDialog(lost); + const endGameDialog = new EndGameDialog(this.mission.name, this.MissionStats, lost); await endGameDialog.show(); - const highScore = new HighScoreDialog(this.missionIndex + 1 < GameAssets.Missions.length); + const highScore = new HighScoreDialog(this.mission.name, 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 GameScene(GameAssets.Missions[this.missionIndex + 1].name)); } else if (result === HighScoreDialogButtons.Retry) { + this.destroy(); Engine.GameMaster.changeScene(new GameScene(this.mission.name)); } } From 465b42046182426ea5a0e148e4ca1e962783d439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20=C4=8Carapi=C4=87?= Date: Fri, 7 Feb 2025 11:44:41 +0100 Subject: [PATCH 05/11] Working on missions --- public/assets/maps/01_first_steps.png | Bin 0 -> 11747 bytes public/assets/maps/02_the_turn.png | Bin 0 -> 19442 bytes public/assets/maps/03_fork_in_the_road.png | Bin 0 -> 20493 bytes public/assets/maps/04_crossroads.png | Bin 0 -> 18329 bytes public/assets/maps/05_the_maze.png | Bin 0 -> 26503 bytes public/assets/maps/06_multiple_fronts.png | Bin 0 -> 24797 bytes public/assets/maps/mission_01.png | Bin 122969 -> 119007 bytes public/assets/missions/01_first_steps.json | 76 ++++++ public/assets/missions/02_the_turn.json | 99 ++++++++ .../assets/missions/03_fork_in_the_road.json | 141 +++++++++++ public/assets/missions/04_crossroads.json | 191 +++++++++++++++ public/assets/missions/05_the_maze.json | 202 ++++++++++++++++ .../assets/missions/06_multiple_fronts.json | 221 ++++++++++++++++++ public/maps.tiled-session | 73 +++++- public/tiled/01_first_steps.tmx | 25 ++ public/tiled/02_the_turn.tmx | 25 ++ public/tiled/03_fork_in_the_road.tmx | 25 ++ public/tiled/04_crossroads.tmx | 25 ++ public/tiled/05_the_maze.tmx | 25 ++ public/tiled/06_multiple_fronts.tmx | 25 ++ public/tiled/Mission01.tmx | 25 ++ src/classes/Assets.ts | 21 +- src/classes/gui/HighScoreDialog.ts | 16 +- src/scenes/Game.ts | 6 +- 24 files changed, 1196 insertions(+), 25 deletions(-) create mode 100644 public/assets/maps/01_first_steps.png create mode 100644 public/assets/maps/02_the_turn.png create mode 100644 public/assets/maps/03_fork_in_the_road.png create mode 100644 public/assets/maps/04_crossroads.png create mode 100644 public/assets/maps/05_the_maze.png create mode 100644 public/assets/maps/06_multiple_fronts.png create mode 100644 public/assets/missions/01_first_steps.json create mode 100644 public/assets/missions/02_the_turn.json create mode 100644 public/assets/missions/03_fork_in_the_road.json create mode 100644 public/assets/missions/04_crossroads.json create mode 100644 public/assets/missions/05_the_maze.json create mode 100644 public/assets/missions/06_multiple_fronts.json create mode 100644 public/tiled/01_first_steps.tmx create mode 100644 public/tiled/02_the_turn.tmx create mode 100644 public/tiled/03_fork_in_the_road.tmx create mode 100644 public/tiled/04_crossroads.tmx create mode 100644 public/tiled/05_the_maze.tmx create mode 100644 public/tiled/06_multiple_fronts.tmx create mode 100644 public/tiled/Mission01.tmx diff --git a/public/assets/maps/01_first_steps.png b/public/assets/maps/01_first_steps.png new file mode 100644 index 0000000000000000000000000000000000000000..4cedff4696a92a70cb2523e2fe30c28c4f89f89b GIT binary patch literal 11747 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 U$@GEBVpPa_Yg?<#uiU=>Ut;EHy8r+H literal 0 HcmV?d00001 diff --git a/public/assets/maps/03_fork_in_the_road.png b/public/assets/maps/03_fork_in_the_road.png new file mode 100644 index 0000000000000000000000000000000000000000..46a726651a808c7f80b0f608d6334c5a2eba44ce GIT binary patch literal 20493 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 KlljG_@BasC6&UmY literal 0 HcmV?d00001 diff --git a/public/assets/maps/04_crossroads.png b/public/assets/maps/04_crossroads.png new file mode 100644 index 0000000000000000000000000000000000000000..d7d29934dece6bcadb2b2358277ea2c0db4974b1 GIT binary patch literal 18329 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℞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 F{|AsM&m;f< literal 0 HcmV?d00001 diff --git a/public/assets/maps/06_multiple_fronts.png b/public/assets/maps/06_multiple_fronts.png new file mode 100644 index 0000000000000000000000000000000000000000..bb81ed953ef6c9dae4198aa01010d995e606c453 GIT binary patch literal 24797 zcmeHw2~<;8_ivo5wIX%EDiVq~)KUg>D}IB?0|{dM*Y>VX`H^*a(!N2-c=`k z_hLtD|EE@bqw}$kKUc^kB0;t*m#L6NS>eCZ6|wjuHTjKzI)Qhp z{h&I<<=_N2{aLSm?qJYl67K8kdOadm<@b(o9(6dnIb z*`*je`Al>{miwkQ<&;f=z$!9jBYs|n^c z+Bi8m^#ooz+XMWi!iQ)8vdxj_^pT3Pmf#Qd8pa(;y7lCGVY~jb+%J4jDO>)+xOcql zmdvtb(}B?%&Q+_q%0#FC>0ioM_K|j}+Puc{c2Po|K>j4Ku22cY2fGNUEU`Qorb_7% z2}7N!!VZ*WJ~zj)H0dGGtHIqhabKe^+Ct4-cQO( z4sk$$;AC8jfUy(A`{dA9RwQ@C=%?SOzU^mhJTJEItN(AqNyJPE>*F)yZ24y4^na(O zrfS*q8OT1?K|%ot?ZzHa)jDJW=PV^rX#F^8RPW=J52Q8@4t|TwqYDcQw`L2}1lH9H z?Ck7bc64~tY1e&_Z-nhcL*Zsv3=9?CvxPOFti9r+J$6(t0gVnF@7mRZKtruq_r)iz z931{5E>N7_S(a|47SB|Mz9Tqv?EhsT)!=-2bLN|ZtRBOI4}nDR?Y&yUcTdDURhsGs!Fb`QTLENQ3R4CVjr##n!MTf*xU)?sJC(E-v&;2Kq5D5{u2*_gW7^1;%2@pNI zF!R>V#h?9GZIz~Tm134s!F?1(yW1ytSXZcMFDom%QCN7WdiOMSq1KN9%pk2mXKrnH z`xM;zKSW-IRq&m~l4mlQl=nv2WXn$gp}O0}0LxZZhk1>`tnA2hDkzFt!&QC&1E?Kk zD%4>dx{g9#%Vo_UxuJ%~h=>H0zSAS3F9uGn<0?Pp4}F!_ia*gN>v|X7o>nOKPAL8G zH}6?(&a>3jE4SxtQQP)*KXOsUZ{??C&u}`xu`On!XOF!U8+AtdLMAhvZhR<;1M+yk zs!A$T?)S05-5C-@z+Ct$B~X|xs~h=Y--FGW)oQE(9i1mhff4;(=N}x9oyTj4sZoAP zwu{^iwuI;wx#PYlxX!t1{%0$>N@rNrNC2~k*{lx$#a6-M9%QpL6f)}Ih|z1jHhm|vEzJHTJUd^7DZ)I;^h{-|#b2u{>KO?$_P* zUru#dOw=w-$atu%6KC5G2e@H8WCR1&g*;n*ME6%|Ni~|)%G*2SqdRFAZMLd z8*!{NJGIc$m1CE#o&+f7Lj?yq^IFQi*3C<>h}n{iq9zj1eN7=)LzOakA;ec_x&T2u~9O^bohhc@EiGIR5~0uZ0E-IEt6WONiD)B(y6 zsYczvQ!Iv}n$D$iTbfR@i&ZY;tH;<1nc5OSw#i6udjVOb$yGHt3~c=)V}CU6@9 zjiPLgY&Xi*z-5*?Fx;8o?ptQByZg(kRE8+2azYAb4e#9i;tGvjAIV@o-}YQ@$MtWg znS2DN0fSI$h zx>;Fi>*VaJ@V#6pdI^Vfy^rbX+X1GLi9#f%Vft2@rInT5-s#%}?FEJ3)#jFZ!D*WE z6EGB$)zver->wqiaJ?si+UV%$@{ntC*-ojXB+z+_3~fp2xR-#vFh+7p%I&76U6+hS zQQzD0Z-zLvH~t$$5q$8nwtN7m4pQ$B<{p0bopG#sllL{mdUjzwKx!XoM0<|vcq(1>vABc<4l&wL>b^Do1v}_fdD6hCJ)Pr>EEH1K0n=@ zgB-f`Ubd#^oH#t6;Y4>xH8Ec%`3S@02FJ!n<7l*y zyCG&VhYlV3u6Aib)*)C>Lb+~6#==M=xWuiBOl5r9F0v7~G(f@wnQ}1XF&3bG=}*Gw2+O%or9V1yW0gwA(tkZ%3E5aP)I0Cw>+k z{oSOb3!;LK18@mD(hX?0xFiyZwm#R>VwN~QzuDiP-%O%JRLO{SAe~X2b98nTJMt0@ zTqm4Hd3-t_m$vfT%bS;H3j>4AIk@Xjt>L8hFYB8cQRvCI7g_>lCUou;$sVSsj!Y*a zeq{c?M$+ux7U(|_1pVu8w0nu#>)ygn5AoLPKC;xQXS3crX5O^2qMIrwuePF7r4 zQo_7Zy!mF{Ht)K?U_WM%OUEYo)p8zq?X;eDd1s>w38;VI!&%xVDB&)bH)qaw-Igsd zPeESaxd-d&$;cATaHyrPSY5qfUV_aJQ;;ljb^Ym-l)cVLl^!@CNZlu3hG=2IzP^*W z0|RUIxcYN+&Mwy~<*n)kMV8Is+yb0#=g{c^aPR)$!wqx16h>v>sO*~}YH>H_`z z{5ti;03Gh8r%NitNR01?dNgga-?j`ib@y>`2g2@NM`B z{1n3Rh^Z+f5MHZg{ZZMBFR_?LvqAw?+nrN?%mUecg@=ISGSi`oweB-T#K&;TjY}Z) zu~B7?+A@Zz>Dx|ZL8&#?y?Qr5I0=yzIKlL+*o^hbDakSt;B{Og;AFPTOpABx)+%sS zodvgGLFGF?c5wK`0HV0)|;TIH*K@ zV&#pV5j3__M#jPJobERT`Z_wd-RZ_K7+cw|F`m}oJi`LN#6KU2#(HoL{BW+Zek!dr z>PfEmZ^2+`zzupF=-g)ozm!@acF>bs4;|kdtVL=Jny!~+YkaR(m*=P`oM03 zV_T7E@RJ?V-#P`C_9?)}X@R()FvT8sy>$+pjJtcGtCN^GHX82$(<9(_1S;O}*Q;rW zF9Wa$(E4jSs$GKv&}^v=ZslHX{cUh^%J!F;SXjhlbhV=OZt%v*>YZI(8P8ole_4Au z7hE?J#&UxYv`yMm2%<_AeSX>AwUC`49v%BI4w?PVgGXNP^ni7RIS2v|XSBs?r|zDb zP=2nI*j{qA7;qvltI}f$4oIWbAC5XRidMYb#3b64shm69>)wdUauTURscy9AsqCL_ z6Tm7S@51tTg*Hz%0Q>X$`rCk#z;3c-$1G6G&jHOG# z^liPod5j&Z&(58=6v^{D+u<+J3UM>-jf1nmFe8XH`l6_ynb<2wgpc~RuqV9qRC{7H z8Y)A;mUg)1JKgRQE!HWCoYlB92ymgeUv6YOCD55sBnVi1B+aXSAQFaUzd)>4SNHSx zpRz@;h2bP>FMv#6NTL*=!|=47uqhacxiMz?uCLrz!(NbH0Cc2>Cq@x**eo>vfULW( zhdoqD6mEu&&W^x}s1c8c{vREs&@#RY$aIzp<0ShA8im&M(;Z*)OmDo0Yqp%OY(p)g& z!sDM@1JE-(hIyW_Vm$s_{T4%@wn`AF65xrCRbV+R%b^aDT8+J@p_cq9uC|9~3?gML zQS_;Iwk2sNCMJ%tM{JvjQC`SK6jR7F-zSDyd~5iO^#E%S@@A|yJh@H<7Bdzi9;V%A zn##9>%$fq8rDkOONErXKhm(v(v($HRxYb`18sMyQTxhR;&0bVbE5AZi`+&C4QbnHX#bk8!t-00|Al{&;&L$nPBuZtiW zD+!kS6g&D&C9Vw`}CVKPf`R(3Xh zZ}zm#w$0nXjZ5a7)-am}zs4OP=qhIp+z{k?2RLsb-ks%Y9C8Z;I^_b0N@C|DuNPyy zTAU#=U0vO49S2FP@|_zR&3`U{ZmpTymMk)ZEY=hcVN>~!DIJ2^~n0| zFGE=-NW!cfESq535Hc|a0@n!INrTshoGLnVFNv3H_H6Fhq#$Y7!3>uwIW;U3nAm2( z{P2>vQ9R_AV`lSDwXeUG!y4T1mvUsTU8E+_!p;*1` z^E3U7w)ONLmyEM=(|r2N=_QA9GWw6aSa){yzO?nDWFGz+qum-UkDsrNK6Hq-(!*+I z`SFx=-rA}4T&L=X&nm)L&B0NRMI&`>`6qPc)@%5`HF_10a-LYR_YFyF77ITj=`XId z@K=)Mk}{10Ajc)n<4U)`pWf@Nn(FxiATQb z>@;krx3f$ua?GY5TIIQ4LK5Dtq?`e>!+nV)9XOFJVXmUARVbfusk-rPWldT=OvbTz z@Q*|er+e0Js(#F!aN{enFFa(Jb*Jw3KY)Vi=gliqU8(G3wIfUgcb|pPKm4HV>=8S& zUpbaY>COGh?yTxS#YGknRopCNNc^dV3K=7TsR*x4{ctxQ6Xhi_3rjA+@Lk_pG zv9Vrw{o!gb)XX5}gQ~}b78DaQjCineM23;56|=;%wRs-35fS}&C;OzcY`DtUHzdc+ zvLba7YK|)VO@{)*9pFf`QYdB|7;}f?l~nB0IrAfhj0Xi3Lnx@+gGvM}a@beOdHz+} zNnLtujZ|K40cD6k22oWctW^~m##vHGq|_B$A?xE09c-@aXVze+89#J#JC5y(Fg zYmk9htW?P~m9w&EKBW{e?B9;r;oz{!S#WfhQ`{2omF=JFN!y=RE+T^m*hOKP!fwtfM?-!lcv&^maejHeL z%X;=)B#ut{6aMxu-ILXOl2zngdqWX43kbHdI+f<9keFU;-N@+uZm<^khr$x|gSBEt zo3%&5k$n`G*s0D~x&j24(|^=@74n5c__~!;QHY4r0o9OMRG*9yIHGH@(viP3yb8-} z0^|q_6q#jkdr^-y`!G zWB+uD_he9>nNR2p$PPlxRom9fi#qRhFT43@T8n4otM!8$`A204#v&VwH96|3EP+Ld zIBlM+HO>Ncb?ZQRIN(k!_C3sKA(a%R;UiTw-F>iP7;8KK!@-Ty&xcS8j~SYSr`&vF z#OIw@D;*rxduK#PFQ}?WE!cDZLnM8$EW;yRBZJj~6G($VHqL+CowBl(m$bGNa^{!o zLS}!xOHtP+$TwTuyuOC!Y+*ym9Su7DA}#mzgfmh*Cs&Z%51z@*H3dmLqyORD~= zbsCU;nnCtyt&IAXa+|I#-}Ba%m(T-Mz(~*}KzJ!ADeRBX8Z;8BfOOR+MfOmkl=gIO z@k!nN_vIS_xq%jUP@WS|+Vl9+S1O%K+rP}hdHvL*p8ZMPhYU2#*YQcC2JAVG3D609 zj&p&Gs7_JAa;j_Yn?Pr4zR}U0VCFyLAHrc&E6fI&Z6C_I zT=)L{`xd*-xC5O1lM*1oL@$<}Q}(cwa;{RE%KB#NDyvynYxXb))E*gmY{|LPU392F zj5r2EotS%b3!qYgKF_ zngr8>Gc&YLlHh78J-bx4M@>x)@|xhKLGB5{8AwDesJK>j)pXF3{BeCOG&!kmX9*On zu<Tct(7$arn4*OE*iS{KS7goFEJ6db`~w8YTK4R5dyrdPf0wQ=2bN%aCpTKF<{7K z)6rl^`K+l!2($98lA`<{N+*J=ZLQ?8%zDkg$~L1Q)ClQej#C+n(qA3fsvX;(ATfte zi}da7?XS9Ir+Os;00Gm*N5ShJaSh^VnrV*H7w-8^?aZu2?v8t(LzwOMYIAI+v1d})6bA>% zA~;>T9j#;O9Y8@>Q%D$@j6t)xXkX}YF{4V#3BH^qD#3(-UEFdqg-4DfbeLK3mloy# z6&12@ClJl!6;1Zp%?l6eWTkB>z7L^n@+5nx6D->2c>LCmtI-l^pq16sP%815kXab% zKMq@nAQwaYtv0X4z?4nIM8n29G4or)G5?$`5WAGt|7NSVrQ(Ehr76~CO}=bL_Q%}K z?eFW7NRQ6}vJA++%|Gw`OhqLd+334zUO6xJvq&N4CX>e!wqH25tN+E^X2_n*#?({t zLe*Jan~w#q*>kG8IQPM7cx+jMr8k-~{*oB}&|E66>49qv)gge`oy$KV8J$|7>p_WOsz_aM7GZl_X9C zC$}xH_Cm%&9q><&h_b;|(pnC~wfAGD+;_4;B3+$PGJc=GKfe(I%F+{rC*wNz^>F8X$*9dvu2XuQ~m~Mq5Rtb5fC|+<3Uu7^K1ULcmyql7M_`BVU9~rfU z>cD=VAp1SP`NfM*K_2U>Vl#Oss+!v|6%|y3DQuI^zzIojvbF+A^9v3Z@Nr>=ME&gU z1{KzYms~WSjoYia1UJ%c^1K5)$g4Ck%svGg=AU11@J`M^W&f?jLqrKRuCnPV83?Q~FW9-0<0(LtR!o9yiAthvpPAUiu%7-pyFS*S6{W)NT48D><2t zWN)mAGO1M*5E9R9_g%=#RsfBo6s4zlqnwXv*nG@tKW(H!|&7aP_1Lfpw(jvok)u!QCV)MRg1qz4)k~m_ zC>gSPiI~6`*Nl%>omt}P>DkF+C{++}ta;ZtuV>u^ED1+Wf6X`2=Q{P8gETGjbs0>8 z{gVrS1G9^79`pGdF9}57J(~xzboW|oILMzHn*i#4018NJ6ml{%4nnQZ^mMOkePqyp z*G*lGHE;c<)wbmhUts6M_%h^InbQpZ&;R*uKV;kMIo&>8tW9`6S02Gw$@{xS6W zSTuDXJR5y>fkl%Ku*s}@f*xM;E+{u&2d8~!s6ZHWAn`uzp&jAHi6c|gBX-I@67^*~9eYw^gXp|F43N@}6drtvT1Aiz6 zbDVZZ&@Ql3uX$UPL@0`z0Fg@967jb2O3GDH=|;fi7n`n5_pgQ01mH~i?}Y6OcR4ac z%KJ&YiD&Ps&f3Hy5P<@xcY}7+RNsSaEsf396@|k^Y{50XL$`LQ|0QUI+_bL_k%Ux- zsXzHdz?Ixv{{Tri104Sw=-hBd6(CAyz|!vQio_!S#Fg(HX4FxcC~)UV9+Su(ZbnwrY_F;mL|3C#Ko=&pgDC=`ZypS_Y%3%z+6=1t?Iw&8yJ*G9&o? zsS3v-%9N$g1xEpLaC2XFqmzV*pMMvv(-+)c-;Z1?$z3(^EAq7}! z*n#2&hnw#oHmI`>6th7WT*K-#02qi7V85|wX)=$HSRNV@5?$T6TiV_D*6?+Az8tJS zdp0p|h`b7~erVhJ&Gva+ox}(h=6jehJpr6YX^Ia9MKB$Y_&hWIW%Cm2fqiQBC>1uJ z9n|U4WBD^Xo6Zs?bZ`3E5=c2xkd^9n>Q1*=ylzW)vhnNg(C3WjVwWg~l*IMNcU57P zigy#Y?WtGo(!Jce)ea8oosHKw@LPJ`9RCnxrFHicvN!5PncSKoCdq^JH=sm&ySTnM zWEcVu#U6mNCY#A}?I4hlW9^g~h4^;GpBbcly$8}5t*w`kN*a?x+_2No&K-}S@kVte2hq>OpcAEkSs5cyfA zawNMuI5@4g?<9& z1+&?UtdJv?>EL8k7$inwGt9)~EpG4!j(^IX5O>T}($g~)3c3(KlXz3E1@bpTn*iz( z01EI8+nX~opw$4n?FZLK20a~`KM;}|=5#BluxsEgcuRAc?|rr#N`?WMRc ztJ{ZirjV5piBD@u6Kk0%LJw2x`Et-T4NR#Sbc@gkDeh<2gj!k(m916P*MGC+)uc)Zbf z!?UzD6_s1rFt7El_=tfMkE9A+AjM1N(|+xEobIxZ>F4+G1@ikM5!INl<8?9J)ul(Z zK$34q(3joVQP*HlF4KZ>8m{YA?6|rWlFHz{?sS|UnqnQV8yO4ApZozzLc@2{R=t>o zkmS?(cS!o5++vtuxL1igXmX(~Xcok_#BRi|=m!Y2WnYEaAI7@TktNQJQ{B!@#&q!=zO z3L+A>!iiO^^coM2!o6m6CV9y9t9J=6X8a6Lz%-97$O4mDVYU|rQ*>Iv7HA&31p??h zk%$+Z9cF_fjAz4|ATC;U{i2S}Su+EO?^3XC7juA+xKB$<3mn2{ih&zphx;Sim4Li% zJTz}?L|)dnOla-!o)R1!Y;Fq+)0{(?eh#prxH&0|--+96NX*UH?eQT+pwh~+DKNb6 z2JjEeDul_d5&3UW2^+TNe+v5*lA0y@?$o?4Gl&9YTFpoLBebMy(n7)ZUz&Fv+f_qc zI76~+LEvfB`s9IKX4dMGJ54j?u;l}2KdiARw;4=#ek(N(dF1R|rFr;*Yo?H{w;#5J=sH#ff(RveS& z4RV#LS5aaO8$b3Au}}8^G@AD1%k$CqH-&!R!S&&wmYy#9jFih-TObVh(*o&VEF+v+ zsfrSS=j-AXu@E%|nW2UR5G&%@TPNvkdU=S5uWDV`<}Q<)>M$c_VNhceuQ}j<_Zqk# z!5oKItB&mo?LZMg5MmzEJTie-(ZoS-Sk-gyHf`czNDwhINm!LPoK1xzt zU^h28RHMH{iz31@ClB zeV{^5B=&=6?I46;6sKe^I0zPAVyXdqVrquI+MQjF(;;7>Nz3%ZG~S7nrCUl@8}&{4fM^s11yc50<^h%3^~)hoHXy=6u|O`rKs6(s9ak1evpdF z8r_ptDa|BwI!qF2zLO!V!}=DShMC>{=vevz*WE6uc=7Y601K{F0CCaLcNSwih9%4F zC=5An7xocqj{1jOj?fl(V+S$PP)!2%yZHdH#GT~ol9QvNqQE%}Vlc9_+$}hTEI{F> z#MCt)3UiJ_Co$OQx;kfjTo{k2A0|11#QkaY0cNc0H);sWbo+hzLq}p)e&+QKo1Q+zRg8E=S_d`K}}5orZEi zZ>=|Y5)k`;{^e=A*uD92x;FQ+JjmW?8j{rpK_xl30($7uykE8}+v^VTfXYEM?(R`| zQT==+Iz5BQ4c&ysLhQw82qyTp9JB2A!TW?PJE5{cN?gsS`9(#L%&TrxrLMp$123)_ z0gFo_gr`tE#3!KA`6bpcU*9%DvE3J2Dnew+slYGjnt}HB)$upbXED*ktv|B_lH!Ge z?^g|kR6j|pzc~v z>mVbfH4n6JFjsqT&(d)JrKjykRc$a0dq4T~>05NFZ_e;6G%*H5Zvz%WP%yBsh6Ey| zBohp){HGcuam~(be4k!^pI&~SUIqj5KE3=ty$sO%KE2EagYiDS{64)5V(>n_{64+> z|7m)eP1V*bbo0Eu$7@<>ulYJ|&BOR)@(xAzrrfx>JK-IQmtNU!+_$GQcFYeu)tq+J zOh3QXFmOekJaS2yNpntjn~BtKG>!X|jN z5UV>l|ACOjMY^>QK(X?hHtraBa-h@5^6T?rLJLC@oX^kZikaiIAv#?wPmzSfL!CE2 z(ms}j1gl==&83 z56}1>YI^zby1d=uSuP~)wa-720z=4KGFaokeoGkm6FgLEP%!CLDxq|)z#n?9)@C)Y zW-U`pvcw@$UwlVN9x6o_1DyrZ58^J9F^UBeF7yGzYXn>ql>21Gm_wgnoK%U=S1zrs zY{zG3G<$;!uhi{@-C=c$`Wrh<^wTt;-6VgIP@rU-th+T^*{8^B`*bV}5F-Sk2+z(I zh_*($tbzw3A5&{*`dB<~jU3x~X68+J@(rHA&sNqkC{uaO!-mR*krccbVX(uk>bfx2 z4x&=XXs^3JKQdOGJXl|@v1o5@(h2B@FFY|fWkQ#OimMbh=ux7caBL{@-Si}>*|nta zaiw}k?=K}3^u9yeR5CPEC39K`kBUC4J(AuxC>CqeTv_Ekl$>mK>H1GLRSDC26cIZb zB**^gUd><9yiy{|U(}q`IJq)wNHF0I4$HzT2Zy+ru~1*=eET~S*=rfBvvtRDg?;IY z^1*A#ma$ykN*t@__A(Nl}I~kDSfO;K-!PfzQR9l>N;>iO*p3 zU^dk(j~aIhnlu_3Kw5ekonu>vWb0y&h_RDv?FMXvW0(S>Hoy@#8wDB;%OFiQ`S2ev zDqR8ZtbhmM#D;7w!UvJS6?(>Fud|+FGRaa4s_CHq1 z_=+;@m6I}sc1cP9fxfN;H&yEY+=jtj!)8kQ7NUs_cunY)n!UX)={hDlZWf@o!r>j_wf3PFQXrR_qc3GX;rU{`MBsO)gG*HDk&UzJ?}!6=te=oL3oNd zdBU1sRbT^BDiJZVly75&tlD04kkh!^Ag9^NPn-oHq`oE*lB%$aAd{l-(Qj;9 zEtdvIm5ptT63jXox6Sa0U>lL8s>Fxx^ABuq4q+K%;`P6DJ$d@+k$AnmDljXuIZN%Mk2pF*SCN9h~ z{w9M!Kzxh9b+Lka?%GSOglu5a5_mb3k*0Y8^wK~1gU#@qfIV0F@xVHkf=hJ^Dd~hI zAZ*rexc8Z@f>(uEhv0d$MKhofm2FmC@ulq)c-nvL{uPk)Oz5_OG6K~|wKFPs#&+Kv zd?cl;u`>}@*WF^!IIR;VRjb*X*ZD%MhOcFa=)c|teKxlI9=U8KKfa|(Wl*m8hwYQSc?LRNUTy&xx5|{A zgeQyYvIW)={SI=l5Lx=!e-(Orffx(XmWy4517^@SSdazfoIe{8S-Z*#L+>SLps>;- z!S`eB>qI=#-v~JP=9>5nm2Cw{HoTxE{BdH&io>f@A%C}F$Y9}PN%ks^ukl!#WL^Nd zyhCB*b&qBI*?4B+yRDL5K_Q)8uaGfoq#wYn?vl`H@a8Emc#L$uycM)?ZLg#6p09)6pY3ZH>w0Yf4?3H+yqMpdP{N~yeKTlX0RhFCgg{!HId{ZZT2m+1V&TwUj^+;z zc5BFJ=;n%h4B23KncmYq#t)O>=?PomoDm0}vS`|KRYqU+ewy~d_tTuV3kL-GT{((& zF_heWPvFY5*s7-iBtvy(O*#Iuj0U<8AJV?o&XxUDeJg|ZU>q6iW5CBV&sZPAQ{^wg zQ+f9TdKN5ZQ2U49EMP0f(Kd8rVM}{+rUUw2FDvZbUDp|aA=uBd;~T9PNZNC5r}`P$ z`a_|HnGDzOYeF@jkmsb|$Zt#c$qJM@JGr`Y?=~OdHO`3TWq0|EGH|Cxu#*YNQ}+r)LazSYPW%N)X8#+YUc-4@*YsX*f8^`R zpee8;9vV3}H?Al_YCcD5pc^%;d!QnbBilDM1jL2ry9RfeZM>_nrq_JrgDG+~TG797 z#D)tKGn#!i%9xB19bxY-Onn5eMKD-qv3&VjlaJ!!fvJ8`s`vT-%Qj=s7<%EHgip+2#I~_F4YjV~4 zduvM)tR*6i!dD7S3c5|WrA*+R&^OeG{+ z_I*Zn24gVB?CeqFmlAO(aDg=GHk528;kql|M*RyTa@5#xyX1Z8_jmF2%_5bWU z9_1qOQunNuQi7T9dqMZ%B)R8VFZ;7}Q(k-*i%OREfPXaOZ;`f+s%YrsbolJ8>|Kw# zvyqFxIE$h6AZSZvU|HZxD{lQd@%PiuZ;k~2eimT|+rIToB4*p}ttW98c8P91QJXj% zy!8b3-xvLN3t<1H!GE{lzfk}sg69NBT(O3VHP)T{&UfDMO%vT7K`Skp34_smnb6BK+X4{}lch)bykC#Ndvd_=fW>tL}qSXKe_6VcKUh=9Q!DG9g0 z-WeA-D{nrmPIxz4TK-kQ^T#&VgoKn1}Wx|Ia%^exe!7GRI@)H(HA;KLt7C z{{lOdwr=3;^~ri{y>e{%n?54oQBFv5?tJnYRfVIzuF+rzE-uQ7oM6&ywl{r;n5e68)yT3` zTHlC?L@|q?U1T-<-pl45ysPg-He5A4Jlq#we}q_v^z6B^3|;Iwb$Q8+`6y2C!)+P& zr5}yDohsDE0gkF?LEhe{>{Gc&1y0Em!_!e+%O9GYJ%9Zi5te|?^Y=Ewlb}Un+0=FC zgHcq6Ud;>xctr6H@^|~vE=z~Tg@xgGG}yk=hdHp@?-8bnAqw&%&swr?@$!;r(fRJP z{hRS9JZgRb{bFmguWTI=JS>nRIf-Mogqzw-Atz|lo6){Rq4|Ph>FMV(dNA0X;9aV) zA=3{lQpky+oT?xaS3{Z}ZarY`h;ad0X0t2upFHaPg@{SsngsqNnqZ8Yz*UifyzwDAh+4VUx+bfoS-ixAMN zIJ0cnC^|+@xA{SAx)&VYcnAu!TKY!&%b<-Q(F$%37_2`_u7_2_twO4bO&#s;~9k7w5U|Rx9WU7QtD%JqO{u2s}%Rh_I`_~Eh2q;p} zCCuon+y>IqhNH;Y=;hm|3Ry5%aAtfuCN-+UH3lAVHAHDM_r(p;@`&Q3a)sNv%EzIA z_d_UwD56a1?roQ0zJL^5TzAog3-kbPTQhcEut5ouTDpE)mX zoRJ?H?eTqfegXPS5JPh~$uma6{osDUNAlDXLDoP4*=f%XbQ^o7U3HL%UYUP?{)*38 z)&9BV)~AhG(|#AIi=P2hz}AzIUc||lerrAyTq#B+poCZ&yF7Xnv3}%EqQ?nbAi@4# zX^L*AO^&gT)yfK4&?M7PwrQ$o7RjRufJo<=&9bZ0{f8$+Pz?7MVPO!!A;h-wpN*Ou zK&R6cHxRbS{T{!eVwA)LN$!hXyUinsE+!H|8?|wq-Zv>p0^jcQ!+nRHdNYj+zSYqa z0+liyCk5tG&JxQqwEJvGex3qw4PDK zN6vh;RsVoFqhKlPQuCTMOJsYtb#^o|7GH0j1%zkN6ABZT)-MD_OxlRz#C)&UrPqvW zCuL8$1tKRM;|utclP2eT*9*r0E_}IXUGyG(fqajtV4C8rP%@+gFjX^C^#CI%Z~$)ZcS|cu{GoZ~2Q_mStr8 zFOML?aF(CvM1W=O=}{zcvJ>8fV3N_%%@{uE#I$A4szTc@1*v|rq4ba{}d!q`mM9LKOUDaHTy?GdmZx%|_H?mY?>8$8K32wVjM zYw@l3#FN|AL4~Oy*77#m61V*TDXi{!*R7%xG=v*+WK7}xelaoGXE99__GYY@iIkAT zRRpWcrV(1;7@p2b@4zigH?GZ+D$u0l;*w9j$U;lu7_Q7r13}^Fl-W)@l#i_SC3O9~ zCy}+`)+C5WKG4ULe$m-N2sB%AB>m6!^ZbT40z!)o3M7Mq!72ar1-KE|BOI(Qv?zK9 zgcFL9iH$50LCmXGU^t%di$$Ohv+;Pw*!vI+v(jtHakO&HzB42?F)M3x^;hQR3KR2< zv1o~Rfr=nF;=n(}!|j3=yHhpj<80_CsEQ%ind2rNPD>q*lnBeW`|CEgf;nB0c{ry2kj~N$} zwpDV$r`13unANcE=En}vr^}9W!(e+g77*Cm)~Tqf%B4{7SpzmTywOer)m-~DKEOZf zsGx*wP%H=i2ToRpg`){Yy5B;(qEb6Pyb54Q)CVvsiC z0IY5@2dry&1yEJ?S~X)46vU?2NX9$})bV$$3Y(%LmCcFi>%`^=zu66kwhH4bSk{`> z)_Vzv*mVM;g#{MFUiM`pEdyP|#QMWT5zM*AGM8bn@$h$oWW$52DG9h(-55J`)6wa} z()X`I@73o6&<*Megl1K&X!2DNR4s~L1mfNo?=SAy_akBva32XPC9#Q_qGx;W*-aa| zijMeE3N%J5jia59k5Q0z(H=oW z9PdNw{n;HU==KJR&t8_k)n(NjMQ*J=3fGTzo+KoBA@QCpX!@5o(0pw?_H*nUVmO**+ zQ&kbX)idk4#1Hl)N0%cmJUrA`Swtm0dT3^~5H6eS0XRy7K>?s|8`Gusmr~8ceI8Is z--45@L}iuCfA|pc%0<>~nBsg~xKA6^$!WZG$2M%&ExZOyZ=)N%R2H#WfY@X%PlnmB z#6M1aFC{s*pHmiQ9xAMe zoBz<*>y;mPb9Ld1TG?}=>*S|XUk|_i9l+52g@8My;IKV?q96h5{OHR?-f-f5*x56v zV*uh~u*_`qkmf!ZY=3bBYzHX6Oln25C&|mPe%6VzYWtKTu7(eQePH!%uniy0XzzRh zH5WbMQ(b)@<>T5j`QxU0vrF{(1wdo%Itq%wX{nW@=p0u{!*OvQw^B-sF;W5?<~s35 z#GkDwVEBhQ9}=q)bS!zb(bTLH-h?3MU7sD5dG)1Lu;ovmBl&WZX2x`_x(E z*N)iLfmJ{suH}7?qDZ{+PU*mqvrmsFjZ6&N!XU5!v>YvA1S`<_o}b2zkhQ8g>J~&v zOD`fH4fr%N>-jbS20Q$&jR#jpYX}b)khA_~{$YvrSm$_NLthTJiMZGVp$Hq(SJszK zv|9S0QEd9mD}eb%GEw^)c$zq!Xlk{@OhK(ugL<9FT3LqitR{3Kny75pS)Y-4mGV#;ze=bMg8__j9D(4A${z;qN2bW*IL)Sm+ifKbYuX% z#lG~+rAxK*RmS;;JUu;&Yios~ZbTUf7y1ydjeC*prv4kF_~{s>u9 zc8jOBYuJ6_UGk}+`zcmAyAHH=%BQrY``V-Ks*3f?X=2h+qXP=+XFKcjdgi~i4kf3G zbk@F%XmZFTvi?vE!C2p|o?aQMTh=DwAif{m2q5r*SnYgZ?NTS_CYLp_v6GV`mNs1O z6xeURh`dg<=GW~7k;AG3gq-@jf;!b z2W?7$>=)+c1?3Vn2W@T3Zg0Ioqlz{}vmRipeY%z14-PN+P(-EDv^BE~ht{%~*WdiR zGC&-PTrslb$0Dz(V9}1%hj{A!hSthFyf3g;^x5y|n~l!$DB7=(t4pAJ`dIpnTMcYF zcYdFvi}@X-B76)JGh|Q9*QiHQ0s)D$A*j~-qR*!5>B0lxfc@dP%XR_7L+yE@c9!sf z%Fx1w`l%mTIIbKv1F*-2Sim(=%%(no_+dCr80<~OZvaWa0o5Y7M>@H63mp;xI#`X7 z>gK(qHJq|cEutJ`KR5?6NwYk5_a9!=D6)*PR28sGUic#u8WeIjC$?r1 zLgF?Y&k2|}CxQTunB_MGHy-$+-5!*kYtm`M)IPXA7!ntC@-zro%fxzU>DQk0nA;7;+y!jlWF0LIS*$>7-IJq{f; ztALX4YKKbj_@N~qW&2rsJAeO+-MLV1P;~ZN3(2CXlHeAiD18}AH=)Zmo6?b<>l-Fv&II1 z&^!OCeI{M?!6xWXvs)v0)LyN%>vA3hSl8uRVdv-P7p8w~s~P}GycIy5Aw4*}Yeh{% zEECWYIyb^2^y~Xy&5#1OG1u~TgAWE99E*^9f+INRQA4R5!d|bh(a3JU)w{=ABpcmN zhpVJ7(T6LDEOXyYUS6QX-TO51phf^};uAjM;6<-E&wifZ4M3_xYLgCv{GBR$_p^vC zT67H1bWZ=J3-?QhzR#}FX>HD)7wuD9Rcu-h?IAV!;^$ReJ1T7LecUQ~L9fLrzjT}*BRvsYWL!Xwz8~st)GN)eCI3$i5bg$ z2OZ9D2`&?4I;*D#uYX)+GvAPZA4uNu!S&jm{fw`S@>%0ync~_YRaR&9pN5C+|Hg(5 z7%C>2h4QEx{algKo}LcWq0jC|oO&mL4xt~QsP_ykPQMU4dq^}*FMBqXu7WuSXu>S@ zT^Atoos!WgCf#54A=r`5wb^S5snI&eO9|MpE2?}qTI=xg2*rwd(#p*f_VxuF+tpU@ zm!<&u!7j-893%jQ=!>MS%L;G7X$&i!BMuNOnWZI4u}Y-;8}5uTS51WxU+VL#JrJI| zbP^CH#8Dn-s}Rz(PG7d3^t5*EYh=}AU|wR7j&4Ojy#jgDU8mNcp11_8*f?hZkxk7S zb{`C4K_8``{T`xY3Ah3~P*hkwefTvgQ064wXJ`M0;anr7;*gl|@UpTp2ZhvB+0M?{ zHBjofgRoG*fVb>}4he0z4!2?JX=fRJ;CA%jAL%RyxnN#bM)DO<&)?*M_Pj%7*o z6rm-7uAai0T#}d70=WNs7J%rbgyfXXcgjDbq9WXGUrJmNw?zua2@d3`*jSjEN5a{2 zMb)t~KzXb>kE%@DHQr0_SQ3nH=VnDVhLcMR(UWz`Q0HKmHFk01m*zLx%6&pCX30mA zMNZJ^Q(-LbqPaQUi#e9oJubTj2=O8ZuVTqjGh+f_v50hvGF$1b1P$cU*<5a^zxbjV! zO!(GQmMT4+E=7h0fs=|sGpD+Wc@Sb^8iZ6%_L2>^Ew`s&;eUjE3=JDgBV)DM^GgUm zjyNyJ*|AIWbUyfbCe1g)2K{_~01H%x1RXYM!&fPU-sSMYAjadwz*-p)#h%H*RTor& zkUIgo;LS85QhB+4O#?`e5!zMfJ|ee`PkkCm1N!r&Kf>)*HU$XF&BP$$$QgI02PNza zklH1yKR9Pf|db1qwjmbeS1MgQvBG_@1`Z6{#TSif0E197+uq_M$w0o(icj~Sa_tB9_E ziaC}YZL}rrpe)b*n~y;SkUdUMHc84HbA<;e!%_X{P13R(+kTX`F%meer<4}+%j3jR za{Ok0*^RE8SatA==|rDToPK3{V46i^)=S)S;9r44eTHf4iXWkZml?&HDbxzsIxly*;?S(*!S?93TdRSoxdAP58K*t~-w_|yjM)T$}1ZcvVG zc0bw67|N71$T!X~l)d&)jgYe!SU{S(>~UIW8|>ayRg6AEB^Cr6_~_X!0}NwAi>*gs zI-3E-w(fAX`E&*V$XnAvceQSC&ZW7b^+i~~Cpm^Xx%#w$Y4AABit{rGphXl2{#yFx zS2+PqwnHqX4gM`QddS)SPDKt-?>Q_6k&1#Qs@8)gxX&o?HV0oN;0WBqtYhj}^2HDj z{cIqesBKWxmuV#J9VQF-IE1VK*MtWoQeL@|RRk$t=N!Opc;BI4JcIJTGqQc$4HXe#wy%g17v{|U*m><2K@ra=J-yL4bJ8W5+uD3lax0y@oF<)wT=N_iGvyw zKG^ffK@QY7s4&IQt+oN?^D*km+x=C}g|kjv1uU){o5OXL^Q5);p_eHx%kyYn-gc_- zXyECphk}!K(O3Ink4C|zNEw~6A;2|otG?Es&mCBk^4q15I0G&C`?A-;TSQ#5~*^i-DXGB;7s; zKx93_*;S<{2+Q@i+j?hbJIG765T#}(phjwN!G_qrGS=0Y$&c2X{WAhAUm#FGbOz7}2x;hl;m3WB8 zzX=2E!H+c2s1^nXH}Gn$fCIS!PSNu4iDq!?e3Qvw-#Xr8+q5rMesn92i;W;C+mH>8 zNN*que%Hli-w!8GHG)favsCscbnF@8P>cC#9BYyfWaz|L43uxrxO zwmPav9K|gcVvuf2ickQ~-9_E4C%P0_-!7Q4E3X<|E6Z)xp8%@+Saa4D)@Pt;&ZUIU!Ivt<3LC zMekY}(A2T3w1VYHpeX?;49r0W0SM0PvAk1k3J7(fPY)fpn&#*Md>gp7jM|k8` zE`3-CgQE|?89nSd)%1W>Xjg<&oPC%+k)Z7a_zv8sUO9ZvvKM6dYGnz50I8)8lOgyA zv|P-V`D{UM88P@tP7vIFW{-7rV-`7KJ1Aw(_9jF2RbwjOz!t4jr`_7>+v)5~#2XMh zK$hWaZ6*8y4TGbAV|;shESLTt`uSUJLi5uF`1x&pT-`z?P`d z>?Q1JrK5QEBqGxC?V~qI5TTj?ZTaLf5sB7+;6~X3{pKO`JCoqrfEB)Cl-j4t%EQwh z{{Op`lpuF@c52r2;Ccc}Q@p&CoVqOMkTo9?r?Vd!bCMd(e zG3-JzAe+_~O|s#x8#5VrIXvhp7a9a~^)d%voj$$P$scBG1bUJpD&V_z|5dvIM2g| z@-^xXrgfO>U@b>C%mL#k6pe*9l{uyIn`3~wpW7Te+1d#wGk$sI1Kq-%Sr$=wI>5bW z$@{$sA_Dl1%k;@9F!KP$Zk7?)vKJ8Gr`kh)1Y;GEAkx_xi&VF;GQg+NbOH2{f7@<}w+_{?i)=;6|lWq&#Gnb+QfZ!0#C#_79L)xPM z)&i3wB*jf>!JfDx&}R!Dm{Ik=@TBD!VNfG7;K zafgZl0iL#HJNHD1KPt@D%t`tjqJ}9hF3u|_4w{TiCCh00(jziUxTsgenW5e*_%b!f zWB!&}Q4^c}-llr{j`6)I77mSR7Lx!g13ED$_!aQGRvXufD2ySnfAfs-nt>le(C7M2gWL* zYYvRl%N9>l?T*sb104tGD1VzQwcY3q+O1rpgHce!8n2ga2k{5P=Y7lR} zQ(dw-gUsH9XIL_4L|WM%#Jis;#e+&PNhdHD1PrT)(8erNhovRrx6Qt;!>DOTEQZ;D zXqxzXrIV6$qY5$~KN&|xkQ^;P91>5{Yjh|$C^wq~+BkaH*8-poZ26Egsrje>WpDbg z$lQeP*=bUoKAc?e|FJbSsy?B}_mS^%AR5a-Ky+XG2})~61_aTsUPbnmF@;BUbt@*m z2*?H28v_Lq&YH(~!Lcu0n)DGtk8+qh#y9II8|~rYDon*%&?$i`noal1-uV-tT`k?% zt|vChLIv1|*|*k^MdwaV$-6VPj~ykmDojS_-TL%gE%Tc|+o=A5hr#1v>;ufq5x%m+ z(!z_<23tYshJ_}})p)jKA;p@a&LHZCBM`k0S{ioZBUzwV=wr=1SjM$WuY-JYkYF{s zpqGmvBu_GVRL95)PXXHlj0A^dKyWjW->e@{PoX9LcyH0acn?UIrR~$plYdN1#7Wst z!=yRV;(M3%Te8y=76VT+&0E|8ms%gR1IzU%gk0ZTq%NN;KLzZ-el>oP( zRfa$UTIAW3p0dhTgDoBBPZ>0l4TTz3CLWNx2nrjIU8<@B$>uhe$1~OD z08>!5km1_^-iv}TLtXttVL?-CG98NtAB%drO z)x)x^!Nda4Gl|#0W~c#WKu%V6nW|u_Ja&8>@Pso6O8b5=jQGvh6(0_o0;3&T8x{{{pUU>hUV(q#@#RItV=lYsE5*x*gIHgkaEref3b zQXRr1JTv0Ll^?TURA;7K1z{wj^tkRK3jD(W7PP9fK%@pV`urTrr>3sxSMyUaK!CIf zWKbJ)O&-@ur?z{!WgRH4*9Z^o+@_Ze;5k}ZdU)htH?j;r6c%bI z)uGJvvn(T4Z?ff!HnwIKwus^NPSpxjbZo@3|L6z-J_NhO6r_e{0?}s!RomUvMO1A4_hve!m&(B(A8Spb<2= z%PMX?z_H-r(abd(p0|tmGlu|~SWt#_W8aRsHm?<7h;+|CDkbs}6%tQsNn^g%v53I+ zcQtW^GAgj=*@m)>rkVV9(X{t>zq83`1_qjdT9dDkbW|>sEVIcJ&;;$B4;;-T574dU zWq|t$bVyM7*0|~hWLoZ>1#+#%+nX1D`Z^~9alDq*uLp5w^nk^k_KtQ}+E=J|xJdWF zKe&>1d+X5u4C*3yIs(~Gjq}ThotIM0=ad;>{%HYN8(^yQB~WPOOLaj?{WDyt+utCE zE}o|XrZY!RnNN=5ptIr^zQ2bf#KrbBGWVH4hBkpb2ZTtr!XMLprFA5*k3ha!(3;pf zli~B09|#Z}ZsJ=K%>dnZEH$eU$2Xs)ny>&Ab!hT3)?Z%_P#+b{0J<&E<1ENcYAwqS zmyqBcCqDpuepMM+v5BTMMMahLplWvh4cLWLg~E-IpOroZ#6aLfJPw-G@_sklm&&yk zXyL2^Y;~Z!0P9#?T}NNPdBL1$F>>;htc#L?SfVyGeGphUP?j1Ew*)o%g?1JPdv_jG z^8`zjYZb-B4Cjr$QI&?AfN)60tXsbp`hR}bGOc|OBbG5)(=7G`=#s{mvtaVk1@UxS zoTVpMDm&FiL}ZzcLzx^{z71wM79M$zq~HYunBgfc_o@;6_T4Ff?4_#W9m{B}&8pw7 zd~D+XcIBt-w_N#lr#E1Y;4LGCH~R7RDzx0j~rPhChBF?B$r8~K}q~SXrl#t zc-WjWUTAdk+VO-T=2eR}1v>nNi`9Bi#|p=|<->~DjkYJi@vU-}1#%!Bu4;oM_QMaX zQG@7J{h|p#o!>q~v6~th#Qbn`~+npom~BB(l-=ondbJ zNQZqXBsl@|0+>IE8$aIf0?Q%bbI}9aw6aQAa(5<09qbGeLWB-N?%0;q`$w+Rw@25E z;k?Yyr9+q2Rlb8mXY++u&@f`7ymygi_wIV z0&Pu8ZJ#M`U7eko2}u8&6UJ#Ud={31NhQ#69uL`+c090C1f#n&z%0V@h;CPB;n3sGW~%c}>S>l($kElxsbcDFX6chh5gkAZCjJzI?Q4jHze+ksDDrAg2=e z>_DW$w^~2{GeZZV28t=!gD0ea16qEd(qQ0ig#_GMAh7LKumZuQ#IN6HWX^*o0>stZ zZX7BP6cA80Gn6bBQJ*yhMoh}f zc9a7zuV@{^WQCwxgWKBMuayoh;y9eaXad;V*XyprV1xpKU1%v_)t2ZYjv^C*kkNd{ zEcTKv7$1}XEn{_TSSzQp){b#!$6f`z?F`iIpxuyeXmK!g^U1uxjw<-7<(^Ni%GB0u zK^=Z3AEZAuNHsFl_SrKbMtK4gbxC;r`XTU9h!LhFYNuAwU)^B-18gi(H?u?f_ZSDX zgalBj3-n5`hPmHXS6AQ5Tn26;7<{K!@9V#LLJ;(bNlAfnUi9e(%`>E#rEpWMK+qO@ zn)$^6yU+(%fsyr*AT&)5?tC;7n`hoayl|5^UCDY1FbbHdU9a7I26(;${FF}GAWuF} zSATnR&^_%5bFru2!VHuEDSl3LRu))Crz<-`HFn)iA|%XS9k-G3%a(FStocsJ7F0nA!cjK@MsHi zfNhVv2XrV%qfoU5UJVAz?nL6zlfzxI8bPKtX@8+cp@xV+UXFb;Z$#5_%g+VHrF00L z!4U`YZ{8?i1nCRP(3FQ}&0NpJKRw!S6i8V9w2{q1Z!D9mqSrbE7y@%#KONq<^Zp&5 z;Sk>+`rNrSzS*GUdadW;RBy|@4vA)qK%WYqa*w{2yt@~ac+F&!-=vHPp#2hk;sgV!VuL_CBy^`1to$)nkf zTf}V>CIlJ&sCHdGt(gZbpSDg0c&r6eQ?SBo^6U>YCI|25)Qt5HJimE}-SQfNqX`L8 z;C``k1?kreqiC)-)YY3u`l4h^wXwScD|z~y+mt}F8Ki2|_*d^JCQ`0DgtpfglsBJbKs z-qJ9rP>T-k>7UOGm5SWG zgJbs&dh(*Q;Q0$NT+Q>fvpUg@`@5{iS}ddmKe}-G#A=YXUVToe5cani^mSA%*XT;s zgiZg#n+(@qq@F9yc|325S0`t95TEC#OJT4&=kevqQ*-z2CYr&SXvQA<7#?oVC8-8H zRq)0A(M7P)kT>c6--maLWoX#(T~Ps_H* zF_sn{nb~vvN(t=tyoyLa!P_)xr$BUIiZD2wl$+uUZ|}YQWCmyR2tqNUFUgKb6DxNS ztS-!4?&saRgEv}Z)=}d%H3jpZ$E*z7{cW=MT=lX_`AM#Fmrflso{{oj&whqw+74Pt zBHrE1xxbp}Xzo`@9rd*k6H~e<;;O1?7$qk3ysmhh$Re^i&-W}2ZjgGm8(q7ieq^~c zYVyKIm%tgsS{rQ>J@^uQ7CL|`xjNA0E;M(4Oxa-nj&0hek55vH?)n|J8~_6i*T#_E z$w~Lkw6c44@1QBl2=PO!-+FxB%C8q#>Z_-B{fbWP*eRvWUOlw7Jl>*ztIAMA;=p6k z-M21ev~%{DY+l_Ppm_TV1jBETm0$2LD;$+kV5 zFDv=#{yOEtwu9GrOHMYFe0Yq_&aseMV2#{(yI&TI91HxAyl*NpP4cn2y4Tt1NTv0X zU+1r385`aVQa=%C^7veH!sS;PiY+CV)-cL34V&nfU|R!`(NrD;`VlBCLd}$^irkwg z_j*snwa(r#chHq1 z-pUce{~XxBw#s?k8S+>BjbtHrP&33Z5Gx1U5NV8t?$$aYu4w4|;U)*-mrE=8wTq~OBbH8 z!y>kz8E%Dgi+=4g@_pJ9S8~uSDPBh+ko9-LP+7g)X&sKs=Q9G1?f>*rVTDr0w_}_8 zsTjlDf2u9QM0fwRHSbhR{8zHT!4e;^{C^+7u)DZsG!!)mDz)yfINRJo zj}k{S{>Us)ycj>NYlkj$mz?ZfG{3>8Yc;^TCv-n|7`>iU?$U62FZgZiU%4k+(7_u8(-#+CMc>NS6aGPHDcNf#Qd>{` zwEIho{+rtuw_Q;`=gU3m#+GQx@rSb^NIbY{;@Nv< zPx*VQ(|3b3rmHkF+IW7~EqV6!c7(LS0eU@Wv&+T6D#K;J!i!bsZb&+6>ONOOCpRC9 z-0n=@EZ8q5meCb4F@+G&v3>T6LITSJB^J}hzzy9wy?^n-5pN;RYMgoY&HX#n`=kzD zdz>Nl)4R^8Zsv~Yp!>wa1#U2Dsy59RtZ{12Ha9%a%;iMOox>WPyIwDyZ2gL3Sr3@k z|Bkz*<{ykU^nNHWFtvzvAwQ`bpJka^I>bx!>?YUO7VD;2+W5NPJk_$d}s%5D2QBWdc><@f$q^75ns zco+2hYhqkRPUr1XzxBxoD$&j0gafRNMYm}AtWeD}02->*lN)4KPz)($`T2iP&s z7?ZohzJf{sw!>Y>txr=d{(?_FB3MHgw{iW{HG};IO099ihX%ag5|lisLed&L1I)s1 zmx^BRj!%Da{rQf3dvdLyY%pwN?KZwB@rF_%YiJ+QUF$$G^D8?;0A!qwi8X@L40Zl4 zNe)XtOqQMe@xyknb|rlw#y+C+`pd0=!cb?$(c@UgEW-0f)^oS9qRX3!dMn})-JkFC z#5|GEw!S3*?f3(h!SC4RSq*4Iq*8MeCNF>+hN&gJ$GGZ;Uglxg_{m+@r<99^E zLqm5K)jITj2$;O>S+*#33^9vjZ0%|XRrK-(Sgt2P>aDoWMoRR2 zVeAf*44hC)cwlbd$vRg^xDuVi7pT(g9@l=KX~X53c`2CiHP0LIKJKL2!6)C7mFS_p z!klzD>pM#ehhV`cZ|n}czj!|?;V(77^XC^D4}tiWHX-YyDHN$`hT9c0tNusQLrnQd zE`dkb6L*L2A1i$L3Y5I#3zXQ2oMyIJ1B9tz2{uCSNYmHvoAGb|^7#{wog-A#K-3(9 z!no6y<+``Q;ap(Fha@hpFKUw=IX9(xyT|L`BjtAw?{f4hAP|0(?7jY?mf3S>ly)aS zZdX=vU~*<&4f(Nl)2a93AJdf5mwc9qU{`}^1pK%%A_#6M^$%!KGDPplS*Ra333iFa z$%FyEE}0f7o>xx`+8_PT-x1r@FR!E*r5tBX`@$!|oAJok7SC+YjA55qqIU7t{k#Jc z93o88*?AJX5(EI|!Q_-c;Q2M>`2|C-AOj`V9%s)^Sg(yq>>`7QY{C@ zlmK(MUAm|4!_}yVDv~mL8-lI-0~Oj`OHOhn-G6I&;_Ch#Qjy!wXNc{8iN7xW=FEI= z>pKFU{_1Oo69r->r;+r>&WNGUr2_-CDA9Q%A@DFM-d^V{njj;Va7s;%p4<(9edxJf zBdMR}h+j%~akizmJS7eoUHfVnP?CQ%Ha}B@{Q%Guv|Jk^0QHO6IxMHba)n5JhTU<{ zA+c?w6K|e8^s72#%wVdz=*Qn>`cF)gLY-x~G9CBUVmFW$NuiEqdkpr^R+~gfMdIj- znmwN)`cEk2HL1%Uz8Di#6I;Tecom_QbWEa5Dyj4J*B^#Qs7l^`5rxG6&Q06;*ttX3Pwg>SWPDg73)oC-NMfkV`nM%&%TSJslS<1Nc#H=F za}Byd1eGDoCwi!%X~u{5S{<9y>2~}h)Wuz=hO2f=4;99Y>10j6t4PiI6zLgdu>0$# z{MfZI$BRA&P=6OL6*6`ilz(s)@WAsxnFA*>q8{+yeRbS}IyzXCrXyi+KA*43Z&~xe zF`(~N$wq*La~^pr6&WWne-jrqvykR~X`~Msh=xJUn;*LY6J3UeeHZX%{OXu2rd(B5 zpER>D`!-x9KRDbn{_8#3CN?KeH1}G(-a{>iB4Z4S(P~3p-09(ZW`SOIIqP{YpMmtO zR1@2*30hP_EB@@Z=PgJ57@tnB>lr7XFEYD@y^kOJ=iWAgQ_Exh-jOAH>A{v4cg&AFav)x;%bNWB=PnIkOVUla-Z7#UAffB9jOGw5Rofq{nMEBP`HzCm?9o zuAhpm=ro14kl)iL9PPb%F{jjiqCy=HL7SQlS! z?|w>Ez&C=AcV7cx+u4N{IoQ;V(q|xd&(~*%e?d}&j)Q@ z&MR(-6T1!k%G%Sy%s`(jlpW||+3X%K^HOSo-;nf3)9TuP3gMRWfF51tdIHN_K%Y@g zIKPs;bU-2fP<+eL@5wrDq1OxVOf;f))}-v&+oNI1UpMJ`_7rE}kaYU;3+R>LM{VM^ zP9N{0Z!ae+cehTcmDLl-O0TO!9!XsS#!hCuM6P?YGi>*+5Wo*gPKxvDs~NmVud}jV zR{T*|N{kL#O#g#KwbYwKud zd!^5I5LFW4nAKhEGUomFjqMqEQ#Epz_s40!2h5sR4fa^=vtZGumrozP#2g9_AKuBD zkxL!}qQP<|DJh5}ns3}d4>dvoXmw)IUK(tdZ(UTLnZn%{e6An~mTZ5d8vM>+xWE>- zVOP@curgcW+@S{NE#!;^yt!gA{MCiW=Z~iueyslR#b*Jxqxp^c-{Fm3J8nGmeq(s2 zMZF}G6mYBbqH=K4wfKt`$I%A^5_N0nQX^&mjHW7LqZM822`aeZmc5rA$IC2)=k38& z`Mk|aVjz$#&B2XL-I0j!Ifs*3w<-hN8v}zNwX4|zLw8M$40HEC^^0C+joBF<{@rHW zjpZ>^hg6hbZ^7N&)AIE#ywlLUkvgr>qy77E&Ax1fGUzSYw^<=R@`s#VV^Nw&&-*__ z>+~5Qlm)D#=o_~$N&!(MP0v@yb+A0zS%HxFb@#3(U@CThW9F&8^E`mUTD&@|z+~ko zTcKHq&6e0=2JIDPb0e*pBT?s~R9fU@0T~2vQ>nn<;IObGP&&@3fG`(yvs@8z1GOU? z%ZetJXY2Br7Qglq7%-HT39qV9Yo(OjBv0z@p-iGk8~4CHTEG*ro!l4e<- ztQKA}Zk8R^lfyKEjay>+22fAFll8S6)+h0bl@XfXj@#kIZ{^jDzCCEiY>NqwW9SO27tMWE1J$zQudix>6skteU4Q zio}b5`nyy2ozp&CH;RgBr&Ef-|9Q=~*n*+hER!CZcGZ)dqb~j(#EhW82)UzHo~sQ- zLyHE(E$6iRt?DYb8yx)jLeIaGm$xg(LGQ_HCGR~?MAzB7#Kf+Q7g;BahLpAf_Mzxj zAJyt+L!`F@;c^_R3F*5r)~Zp8^;H&y$w0wVu299yUB5?nsvkuy*H~%sk6(}lP?-7~ z4#7##@3JN_?547FI;~$!csA}TdrR>1ucX22h{~j{c7R)zp^hK>|UM`q%=hv}(?d5vh6&)@d)DQ#O!RuYK)v{pxdLR+5%bv8UNzM!a zMWBp{Hh!uJaNl`j{$SqwP&BKdE*>fhnvy`}^k1@a6>x#s=KM&YyAh+oG8)zrkn=|a?3Vzb&!)+-a z9+%}4Dya=T8Uf5#4<|BhSl}=A4)hm(uv=OP=)C>{{4TZv6Qdq^kx!qdit0JbM0RvOBB$nAY3^wis-g z`j~v`N|6!obM@~fy{kuO<*dFzD3`TX^tVvNdYeqRSW*WT6vEa)+@NzyP#mZcyEv>& z9`D$%Huc%*uP4EvF)`Q=DI-yh?^UV4E^sf9m7aVVH;T0rDkNmY$$bt*w^W7*HD9am zt^2g%lq?$%OK=qq9LgY;hYYv!$3N2qkK%-u=rTDVX9mqC>x;jymv*ND5|r_xmF`;e z!kG8=F?^Bmy@CJ3*n37bxkX!`K~zvcK%`5RCQ?*-jWp>XNUzeR_g;d9E}%38q)Tto zJ5do35s=;Dmt0MH{R+p2-@m36q}$*I=9cu?q3de{gHN%??K{S@IVRi- zVBJ}Oe{jM4#-DG&1WYz!Aq^p@H^=XAq3(ZPtDM;N>hLWo3fSR3Jp9Jh&-DyWTiX_n zocPn*vj&323hwrYPQ56%N9{#JvRyd!1F?&-?NZnR1Wz#AgsLy!%o`f%_=k&675 ziVKF4!!3ecwT%@qCej4G4rSpW{r1RvtW(_2E7IZSO1Yd~beET)VaFCUx6NYpU=*){&?{A#?mp{Hn?*q7vwDPPK!`~IB8ED2PiA?FDh zwvYNdVCQkk94wK&S*9mj-^D?w0Ak&>*~A5)1C{aQ<7Mw$jOMzk#ECF478~|ZJ6ngD z`|-}zu>=ILNG0}Z)-;v*?hW4{zUY2>uy&ha@Hb$X9`!9@YfZms6l|jEjD31XYyqxVmbn3Q`{ONmZwl$<_na%I7+90`XuADC_WRNMF5TBVgI?p!%9}LV>o=ifBAG5j;x3 z``K~A|G>|d1K}Sx#oHSj?(J!eDADxb*TphHXKC$Ue*_Fj*mY3R%5oTgqCOV>SXUEK zm%J?$w<8%OK2p|dcJI;19Ru-<=Ovxl4)^|!O?>ouIDtnY<25mEY|o|IqTdE$`V>j0 zf%xRbea$chh)*iWn|pyMhG|edd&Xp($w8RuV*9OM$duZ8m!$#l2TDun$}*~vZ4sFu;N)%Me`S- zaE6`VF`&p`waCYI{c0buUqpeVb&P@eJC}E-U~HFNN_+q)w<&I6CIq-vz;ka{^)8tc z5~iH=a7t6299Ij>xA-|0M1dzq?=<5&5<;zzllT56K#21|{*>4Hba*(Lqw393$K_%M z-wd7y^d1gU>}H?|doIiwL522~BG^O{C_Lq=xBfgou*u2rLGe5E?Q*lX)j0?0U1$=*A=9|pQ3BcBF= zMAlw(D@S3szmDjnW40Urn7EhH+q(MFhsW*^+&hELD$QBp4dYei_F@x#Iv?M~z4Chh zoSB(19x(r`G=#!k)wf0a5m>grnR1o-xVp2-*W(X(y1OuoK>4I|7 zR}{}D9!0m_zHwFNhQf!0;vD$#-J9LJummvntCKvd)B;VrJE0Pykeka_mK%NPZL7ng z1Q`Hp?FuLwC^yJZ2vBkWA{8Mho%JC|hM+$K4^mXse}zoz55E55&ro6XVw7SPME<;}b1o_4A z{Z|)M`%2m0Uq|qFJ*8r~%sewcEN1w6KOC78`T~?_8^e!0L=_v1V>x}qz17)3T()88 zzo;i%7bDLq_x9$U^;yoz1S!6d!^DyBtXCjK?pk|b-H9oO z?FYuD(QG^w>m{(1rzNo!k)*SzrRFr&Bn4Q|>P;DblxC#U1Lmo;Wrf zy87zqf&mJ~N{II3w{?e_St&|@5<`tGg2?0f&CwHR>G=vjK~tnd@aTM(&-S+h0efX) zZoYM51-)Ir^<#-46bFL-=LI!KcE9;amq?3jYl}HBT~HIqVeaS|Zl@5y6Bte~uwwLoLSw=O(`fNqLUcVwnuE(CSh_jzyT>ZTKoZirNx zUxsX2SL3%ygHScr-`j&eJKVqyjyP_LDy7)qxHsNl?<5442RDS6!97hcFvRcQV;1_Y zoVGRrPMXBb-+itu=mO>W-)jXG_%o!P2)1abSQPSWgTH>5v#9y`OC5K`^~MjbCtTvm*WI*6!|YsPd$b{c z5}ELyCN-!OF3wrMbC&pdCsw)QtB4eITWX4KW1fPXJl569k(%DPMX7L6=_&V6@yVn7 zE8vrcYNRMwBaS0CZ|JB*9;^T>+uQ zyJ4=noTrzwS}h!>AQvOzDnry4BW$LR9wjoVuTtfiaz3cfa>_R02GCKwog@0W$SxiP zAlmnhL$1L5W5{9r0N{#_zE#4ArIaggTvVtmZ~f;50QD^Rtk+op+uWnA9`X$*m=QetJ)Lg1+PmY`~z$}4ynKI;dSvG?A zxotEsA-7kz^LQ#u)nv%R=^bP8S+#pce&aR&rxt3OVLlDOPBQ~12t>;2k`>A_B!>4) zMr%JN_JHs6wA2V~0n-pnFKFbyB27~RgGEle%HFut3J9Bc7_v03(!Ax}b1H_i4_&Fl zc0rnIX`C1HQrbOCshfiOH~eunlbFtxiji>V>51tbv&y#gO2*ARr;~Bkc98Deky{&c zh=}xoHr>Z2Ycld)9`d@ElzFFy=(lhS&*-Y^U;h60xS?p>-6~z?|IrNlpq_UgfGPA@ z`*f7J-s~J4W~d#v6lq-l`wTeF{{q+Uk8JQc{^+w!3~02&Hu?i#>~S$sC9SP=s9O)z zzV9*+4?GJ)e_AF3_9)Q;+?&>v{nHnle*F*sBWC+!4FZRuvHBNP&Q z&wAp@Ypt#lmz8s^@;EpXsVR?1eFq^|z}qot33XHI`dg9~rRiZ^C0YI@!M{~GFi=d_ zzX;MgrZa{6@6V=OA%lFQ{PB$AL0WzV2QL{hgfnk(!rEJ?>q=d9K*sQLV&Oeoc`hWwd1wB8wX9^`Hd&-nu1%lKl%X<9&jKoN=)$b~uj zB}#?YTutr~jf{|)Dn`Amc)e`cdo~^Y6xgC)+xX|^17}kthkA+)Futd(0+qD6 z;?^4_;<`$Idh9fAWU)QCuLqVC@F#$sja0abMYZGYw|A7llwc^N<+}mA43Q7>ZY6KL zdwJ9ozv2lngAg9;)z8lwVjq6NcRja#-i7T)(5gbb7)sGy^@ev<5m>==ZM;H;LXPo(;^_knG;}F#?@*Zv836@28)VXXp|l@)_GXi5rNe@ zhfizES#9vhb2}nu64zBT{DXp-IkR4bbt|mqvljjP{^v)RV<5oE<20T5Wl+$8vB8{c z$dV5DV0cJFSh#Z|rSx|srzZ|D(3=TwoKRBCthtGt#R|rB*-STyroyc8$Zh_}@>cW6 z-B5UXuY4K#g!%XAeinO;FosD8KntHAFfSy|f&~6}$?K&I@y7S&YpdcPyyjufSZ@<& zrXln(*%alw*}ZmJ7Z5DIEH1AFcKmo~5(8vuCH@cOSA{ro!h^F0czxi%yjI6ndE>|R z`iWs5!Pk}5V#{5bhya!iKe3O~%6UY%7A6P;o<=G~@eIahyi<$x&Rr}Qc&@%3XvM^= zfA&^I<`QlLiSYoIB|yH_Nt&6f9?fcUCyUpYLNChv;q3*HnML_UGDe&jVCv{x=Ic?l zN!Q60$bs7SCuHb$z9TZhqYAL&Av;nUPqly?lgA_owt}e5_bRa&UCl6tD>fMbwJzp+ zuxqF&S4F2WhuB4#2W;0H%}m@)WCn6&MX(9RZ^P4R2KGy;y*3~v zANX1|vOiFD=8x9QA!1Raf4ccfp*jk5zb{7?G*U4&YmV}Wx*)J0z;HBJ;e@Lbq~^S5 z{LQib=onDs*u=feINxLI|Xs~-d{ADX&78{UlP6csss=6 z=nIC##4gr(8f?K=1l)Dg;&Cx{E#f}+GY;eMaJ=#EN}n%F@MDJ!P?9@sS#t!m0{MsG zj;g)u9?nC8D4!3s3TgSLB+FuBLV+!sF`j9Io+S=(0#!ao5*g_Ce{H^LADPzyq%trQ z-%5say;Z@XI6%k8c~mVWxBS-IlO?^D;j0Fl--H7@OR4+O+-- z#7(u*I!skb=SQFAt9q3ca-lLHh|>Sp{tO<#$A9+c z$$HC)ZMk1pPc*cAs(prH7(xVez zHY%eq`vp-L7N^Vfz-qa>k0~T{xHoo>3otFS-NrwAyT0Q5{L`gm)B$+J0L%-Lj!!UqjtpMF8&l211_kp-$;z;0kK@J>GUX-Vu^ zj0vRw=y3E^08r0fZfH){G~R#nL|h(_|Gwn%WD4)$UW3Q1gpj4HD-Wn;fNsFk`#h#g z!ROrbG!hxWMa$9m*PK1*N6J?0JC|3F^Ja^1u$wf-hAE20HZE48w0zNT1faHzQo#p@t#e!-*%W5{=ST{ zshH9wra#VF3W9Tl$)4fOc5n-i}}Er-s>Z&Upku2^r%d8}w;szKhUj%J&0dJ(?_WY}(Y%?hT?HgZ!d>R-JTrnnEOdG)U`1|D7 z2~4sJt6b$wj|^gL;r9W%inJx9y#N!ks3pTjgts?sJ`M>j43S>?T)#C1TP(fom4N*$ZE3Q34!yP-gP@-E=4Yzo%31;+;ix95(AG9s{z ze_isUP9~w{X}X~6%nI6r5WRL)0n2Cyc?29lhA%W{miNSf4XVUYA?If>{Om;i1|H2u zZZp#g6!X8AvKKZcfgL9J&RSPW^J4a15!Y4VIi45^i|e#x7$_$9F5nBjcmeNg-N~nX zj{mRgC>br;VOT2=^PB8-*#Y)wir`A8X+}S9mHZ*NV38Js3+02y4KWihKpMyzzPxq^ zpuS{S{rERr*dfng4xbG0O&Bv%|$J|Kds=$9!Jx=M6j4y=f^X|@N(5A`Q}t7J}2F7?GQAcVRjUZWApu(?ACzXugGI zR?uSZt@hP2O!yF`u0pBWnG!P!atz=@D>^x=VNgKjx7|u3DSg&;$?1RW+_tsO^x+oS zw&O-Be885ioRrO_G9M!P1Df@}k4oNsk9ve%nJ$oNS)~clw9?b5-2P~AfNgHtCztC} zeY`KP8({ISbz_lagcpID{fO(vqZ&ux46ib9`Y@!M$mc)0G+z3-n&pwB>-ey(+1sS47D(@CfQ_3K`~)Qp zY$FBBaCOvxD3#XhJJqCwnyQT%EZbFnTw-ZT<6dCx$-ckNnTeZH@DC&-~}*Hgmy^-Q-1}JOMR4Qhs!d;?0jE zpK<3~Dp(?VEg|uvdw3XMXS|4-3zj!t2-`~%zi90?szsSrZ$S}9^LU_EMT%nLZPNht z<&bin`vwtM+;gxnOFWR5y=U7|kwqE46Daqa-GK}cAPEp&M)Ti__>z+{${mMz0)xSR z#mo~J0u!R}bb|MdzT*!J`vZ3WgXG@idpY9w+Gz^6;5<2j+o~y?b|Ky5pk~VIOq|1*nTpr!$lPysH?x zW%9Lmw+)CQzMRLp>yu9P8yQYLeuO2gIZj?7ZMJC>K+y)ZFP`A`1q}zSl+S?WqqvEU z5QyEyetcIDRbm)sTGJ8&6zH76n`V1i6V*J9MMU4h(*UG&zBU7eVZOHghn77@ACgqH zg4KJXaW8;o4fL6SSf8^14dRkVTz>35?_eSOM6Q)t*iQK)rSn?H)YRNOj7LO8Wkihv zd*Ta=4|v;x{p(oP{PW@&GuZgEb$J>1U%TCZiCzQy6mlnDy(mM|9HelU#_4@%@7@SM zA5Hi(mHb7N5H%kN%c3KwYm-`L0_nZEdZqq5h4!g;xi5cNxT~WZrgiVv7i@(JI}dB( z7LO1F!rzdESWF*h4)SDSE#L;o2s((s6}Mk^VQ#}TSkCgv6S1s;jv_jcp$`L&iMoL` zzTO!OhD!bgYCbk)S!S|O3z~1#d#2DpuUSqLomVQo10(J?fen1nlf;j>ty)74WaBx_ zm8|*y5VE+nK7Yk89oc2!ucfxWEOjf*e3 zYZlAxQ>Av$fQ)jG&zRT{i$jcxZu{{zf;0_tENajF8FYf|=jf+1ZgHvS?eNr!t%C^0|D z1vi?{PsrHYyjZ_S#(s4!vwVU<_6_`N`=fZR(P^w=B7e(5DHoze*18I`HW+fY8vy(n zUnam+8mJw+Y8X@`p?&!D#JN z9Hm|rs&ikcDnYjUjg28_MI4rXRcsLw^AFD<0O{-J7+kmGGaxKnb!Odh>{!BTaa(Fq zm!w-aP;Sg8u|iY2=h)%@mpNK<X9-x|61+9Qxx`ra?H(+O z@&TXrq%0+6NBCCjx|XLKQ{oT7+drU8NPH-EEwJ@GlSDqf=y)f_K*IMjkYXig4k|Yo zbSZ&peCRgG72b6z>@GiSw1(=Wsk3Nj{Bp8Nf$g^JXD(1tI8T!)kmGtW-UX3Z^;S0v z%P@`9n9eqUBNN^OE8Q^^J!ya<2o2KeX4;ly41|J_WCQ?b2UwEb!Fo559`}{#cCSjj zZ6)&9zUl6arg;9Z8Owv8e02K6^y$+IHx4l(dPUJM_j~U>9qgYkwQD*3G?_BQ-Ou}w zg%G5$nC4NB&0MmVRLSI8ms`ex!g=RE(DU&1NG_PKlz^&}ub6;q*#O-jaL4435&oB- zzBBKvz!FJ6J|v|{2AZl2h3LJ5r@;cN;)UJg1(@X#kC!A9;Ha-g1PBXz-=xO5filr; z*o6V0UvA3B?Pp=#;!8mrZ9yag3KCn}SzG(EKumL3wtIEJdq7fO}Ig9?XxfB*$v zwzY}+?dI3)cv`x9vu}a2&;A&cT7=A@511h`WX!mR-EcQi$$&;Nc4ciu+$4mI_;TB^ z0M2}TgGM*cQGM?%D%J)Y0?JN5aQ;ZfgnM*uXq%I}CF&My?q_#2Cj`LGyM1#|tlHS8 zae9Bze!t@kVE-*xW~L;~D>80Gae=Bg)MBcL>BI!gKj4k7cmkRN$l}A9l{3!lAGV#4 zK#%m`dzrCKSHic+3L9^tICUnoucDEZtbSmQ;-i1f{Lly z`slutKJ1m}n?qalT;uNW$l8b@~9_?1N3XpA$VKs+SjETs*}39#2cmnfyS1{?hmuGn7KvP2X zf)21Fz4_O_cWQTsL>!*je|EH;4U)WqFhjd+BaUX(=J%dRq~77=0a_RKdt;AN zaF_J2{*6;Yg}D}YhrM#~l?7EOs8-)7u`v@y4AUeUiN(DLlHLKvU@xp8UIr9l!2wMJ zt?oeO+uLR|Gc@^i5rEr!Uv{xeAIr8W$m0;#yxtgI4RhN*)a9ux$1(Bq&U=A@C#Ny| zz_%+ie#QfD4`2H7TqGxDep=DRm2_HDxYZf-nEwJMx%-AzWoB#<3tVw_pwnJFA8F79_8$B#!vrkDf%0=S^4$r3V2u46#zjAnnG~q-NmEA zEDOXOKF-~0&{93OPcv#jd~QH@$Ktu19o@37eyG@_0k{BM8#v$td}jwhmYPAce8jy0 zwwJmS_6JB24|djI*I!JY>kkaw{5z5kv>Bo|B5vU@=< zfj7jgZN<#NhXmABxRRqvs}Q)%_caCz%wm-VrU#5QekQ$^+`C8!gzef(*@KTniWL;O zi$Nw;j6q;~-ty|Sr0^%ovIPBE(0pTH@F#P)_cHJ$?TE+ORbP?>yElNwu(x^x0i`m{ zrcaM*d~6tyYI#XxGErfda34Wc$ci;)c{-M!o0pru1tGw`f~}z;ujyhIaN8d2P0^iu zm}^Tu4S)eNXbb4{y6K;gXnX_ESLnsD!GIk z_3v@>peSv=ot7ZcvBZ4M8TeJpj~5FI_V9F`LgX0Y79I6HIJLr zNbTdH>y@U>zojZ#yg@^r6$8#!W-S>FUch=+77S{R1q4{bziqHrVn82}RkFfx7WQBs zF|tUzvkR0q{%z-Mha*>~3Vq_%iGf?7sYfz-FLS_}$gj!#LA${bh6qB~pkB|L0V7F2 zX39S5b0i7eZBZsuC^Kz)j-HcNX_CabXhGXzY|arL#OQuh{{9`m(?9pt3DSDN-W7#! z|F7YBs5y6~D{#KM1-mlvM)K~Pk&stCVV7{*Xj~vXsUQRx^?#%6gBzDVB)adoH|eY! zl8VqWZGQc*Jq8;8Sl@h`=oaN0_Xbi;>mC{4cv6*QZ^X~rM_AC{zin6PlAD-0!cH9} zk|e3JS5|n4({{`4ia7*6gH2EwYV z&}ufY5<+K!Ujb`vbos0lV9|r1icZr<(LB_*`eu!npZfWK@P&JpO#$B-}p7 z!H7%&uPX9+QOT3r8BVtsjxw0q>$VT)a`$o^tiARlE5<#kDXHWV@uh+-*E3 z5E4*2?ZbK{ogxO6D`Q()`v<|W~JpKj?cTY&?x zt;qxmFwU3bupJW02pF`5AKkr!-L1?R0Q)Oo)>mO$rfwB!R4&2D!wC@?V%Bp5?Ipm? zgMJ1q#vDsOhy7zW7q$Xu)VaL!XDarMh z>6O#KGtiz4HM0-Wi+T7ZvRI}2>bvQzpp|5Z6)0n%(8pEhi$@Nq6OUx?-=F+4fr>6s z=_SAb3EY%F1dt-DR1u|XQc79TdB4VKYQe-Yq_S>;sqY)!-wX_Jxfkt}Go@um3lZLy z!mV6jt;L4`yTJ>!`nyM$gr|K(kpKu=fQs2S0dSb&V2g&{*&}*m{y6JB{HHUM*oc;R zoR9C3)jfbXH%!rUJ=VK!o_|J*nsVce%|l|-$N3vfE;88)#W+iVHXH>G<3Aj>m)r8N zzdCn-TisY9&=e}iUdJ`2*Uy|IgyC)T-#2w~b@JPh@nre|+)0!8=XvOx;%Mz3&?spI zfAm~;zj94766oVN8|S*k>haN^wMuMG{AA>_kL4FGW#pVvM$aYjaP~FVRTBw=rKmOI1J`e1s0r5LhbFytAUSWP^X|XD! zUsJ;_0tL9~<1f=%#${3MC>kC1O{uxQC18t{YVW|l!*wboY1AD=_fQ|kwH%XHI}`Xa zN_S?}n;)Tl4TX3&tv!m;gg^nnW+>ZN`x=9tY-w4k?-LxrLN72Y{lU9oKU8mdr+!-b zrmt^8kB)F%omBDcdd7=lvEL=Z`1?ku49=sBUg|sj{aEH%>|BqUdVL_bd#ewRtF>|5 zZ7tR-()de^AJwBsvz4N*-(E+F-B%)O{Y>A(ZgL#E(HgTUjyd41FfkH~n^?{;*{pt(P(8=OMKwfB4Hx@5-uIRE);zQ>v#8)5(Ze z(1MUT4)#m0B6GaN+q7e9HMEQME;~ap#{IYGglZSMmPRGkPdYXtyk0zjN+Qi%eB@k& z?{wU)HT;$#_W3fzfM*el=JP~nz1=}kU^U(eLOigm(8oht17^@NzW^ix)70Rq(>ACr zoS3jtFnZK-ipcwOXs3~&_BGi%K2!S?6EXYv?0A9T*-@|VY_q$1r?w$1sJ1*~R%&*- znf5f*H4}Yz{IQ`~TTZn8_{!n?1Q#RaeymCs+-1sQzH>&<4vp@W_Gqup?f0sMFY-+5`D;@G(kzm2c| zm=NjiN(Sft6y305y}@KK$SE_k(>Wf`ofB=Gd6orpY7 zPir5GV)80%hh;|R?|s3}y|dZfRA4~x%-7Wz>(q>{pes+10uXTG0Yt5;riHWEyx($; z?OWn%Wq3XGY>_$#)$X@-n@~faTAV7TrKhXEPuC=$XNy!3cNyo6fBG$S0b)P`F3+^nza&`S*W-ug*~ zlxVZ&dkA=?*bS*~oR;TptbSZ1so;fta$v!jlPr4}GQfh=<6SCCJ@R?xnamMSh8DLc zf1G@cdONL%&%DO|wuOiBhq>(2q#Y(HO3yc%m6?b3FD#3HAkBC8?QmxnKtw+u^~b+} zxl(xqoXm~=$daN-Wuer&ET{c+@`F9_vJBY;RpcqLm}YJ-T3qRn8?55jqaIALAwI-$r;`@4|E7 zXVGee_>riPRxXSi7OaQVTW~}*ghysKyX^SP_p(Sudf5A+D~cgX!M_pOQ48q{PSQ}F z>kEz1BJ)fqVBWn^`f-@*J>=3bx_tl>;FOl8-!Ho!^)juzU}|d;Wn;u-TgV32Nbr74 zRNO!edB?B|6XNB@5X>f`?rpk4rt6c{*mO&oNW5m-Q-@@lgbvt#ALVtQ9>j#8=cjm{ z2Cg0!Pc>h?#+~Wo6Eg*V(4oFIH`eC~&C)%cj@XB{{tI$$7v@Q8_X9MKU6}fOR5GfQ zK7Y=#yP%Qal`Fu+>7bB2`z^DCWFyks{4pwl`O(q=Zu-YA^9Co2Q+aw@J52ppk4z~0M=+qFw43d>d6z`%RD=%N z-bqapKLoelyIkS47Q;*!p1?Ge<>>u(z--=6hwVC$U=y9=`CY_&YNc;(p>!+*wvNiu zhgO+j%*s=|d@HB;kRK5zaB(UItexS;#)lK8c!tisRsK6x8=>srGL3kZ&y-1+pp6nR zIsG3pox@n{;VlL|Nw7Wtc2n}v-r1A<%uqj&B7*))5ie{@%eSbZnXQN$b?aKZzm++7 zv&EEKX7y^su+NJ(SP5@S-QRdjx9=cq$Nxg8oRswVNT-yM{ZqAz3X@c0X?fq5iC2q( z#v+Gn1KX+zjKOfvXgz!g)w91B$iMWPRCu_$NRT4=isRRrl4+Cs%>fh_t<4|n937eJQSFk^Dp1Z80TKP zKjoGP{u|wG8+iXgLOW*fIA_P1DnVcXlKjCVANKf{XyKjCsyN%!9;P0`cIN4SB)n7m+e1?%G~1H>QcWL)Q<+v63mDb;(Q0G}zL_H3~` znI{+m(Wl&nHTB=ZbX(q|+`Hs2q`Bo|+~%UGH==$2jSi16vgf^CsgPduM)nNdRQvIt zXBG3}X|y||bOC$l(cy3IrZjfuF5%`mSL?Qao0w_)>++SHx&`QqFqs+JRpAm6>>cQ^$f;W^B{O0 zB>(%wR_um%_DvnFl& zH%jufB0*hShRAm4elt@y?n6da9=v;+@RP9Z;FBjG&z6v-pUcED4=} z#7#SH%fhLA`YSR1aBs4HuJvZtthuAtL9@Yl-0m6XH>pBKGlKG zI*I=CWX#2VkM5TKWXEy+WvsK3q5uZGm%V;{(G5c#h#?LI!v|S)vh#KQ9gl7yg}#3W zd2<@`v_Bm}TOJlnRlaf(+cpAU&}5@(>FVN^=yRp3-;NmB*+*2!8sbA5*CVkTb}5B& zmGjL)wZUUT*TqbyjE^jN`%vk6tw$5YKM`?n+a2ns*yJF; zt}wjyl`Ok@z~Z zHk?fxJbix(X4gwH*$-=2ZdrbFfB%@*j4r-Yx0Dpx^F*Gw)3T~5-rMjXKuU;?#5>Sb z%zafSUz*$Ogk(fv&VfgC@6*nqJt*;4CwyP zZ|j+?FL$UDKQL+-PhRz(nwWUEVE`ufnpc2DOi6|Kz<$?vL!DCH$@;dRL#zN1(CuOu zY00fNcPRup>>WpgjmYc%UQ7{EZxaLfoUN*hG#uuAW@bB-o3f0HMh?JX(_LUHBmp1a+M)_**?C-S7;Jp zV+hWw2LIAzuMbLqyDgrA#a;>a!6H+udE*x$PtM4&D7|}_EQN`A0+;V&v&M=$XTwl5 z48YFWA9p$_vOj6xJ;(Cz^ZH~p=(}xEu>DbZkzTRJ%((g__mVAKtiLfehb_WBO@-~c z$4$jIN=pZ^(wqhM8a%xg^27>x`{hklN?P9)}fd7 zc3-q4eYueG3BB|=!<@eb9PbSIB>uhmwp5U-YWN_0Z632-g~I|A$Liu*Qmt8u7C%xN zzcr>djTakSO0g-Pbh_5I(!Bkh<3a9Vk=n4%#snSX@X~$v4`6*)a6SjGJWYeYM8>_?GE1SG_BXw_oHsC>%M#*t~C?v;Z33HP4iG5S(ay} zxwno`eglg5QTw`NxWAm1L`mO<7${jND?`ls2p}fRi&*Qo;2E<~#oVTg1*dug;+3TB ztygYyO6(oNfPk}-qQd#0Qos8-U;5~q-JRva?SFq<+HNj89!N~TN;4*T~!Z9xs+`x{19F)Z~~Z$lP|zQ{zKnqLpc_5ibab zU?q454im_&lbE-3rg57Ymmj&E?SDugYa@k@%tnwVsTGbo)z#Is6oT@Hf(7E%LxvrE zDmda$H{vAJJ?B`5&yZxyot&;cGT7(4^LtEl`?fD497OD`WNz^%cQ1;Hl-6-y2N&oT zg_L2QC)Ftzx)Fb9C&V#Ro3M6ZaDk05;Og^ChuHq8E3YHTwZ2A{&Eyv717ZFJ=Fv%7 zMZyQ8Jb~Y0F$EK6Oyql z!f(D-ibcj#P(O&BEZBypUom}vYP4o82FDUCh8VOu)Rh>u`7DM|R+jI;xZ(%7VdvOaCuBf*LVJm`F@nyKr;}iW$ci zbhNcaq2^#Bd9vF}Vd{B$I#qsZheoEeX8QHcz3MGL$V|!Rk9CXnz^&QH6SF5=sY~Mp zQ(s8xA*CuwFjmfgF_0eF8iF(r$^V_z`)-p;HQ>O@F2|VwQg(R}yDWz$7^8^E#s{~# z3aV=@NtZhrUe-n_+pXA4AJ~Lejdc6(Lz$-hch1~v@`a2w(_1C8E~SMSlLBVK5r>8( zHMW7}n^fha4n6ylqx5I<%ybzc?bOcjTK@TCc4eu<(~aQwA5WU(!CBmHFHfLxCz@!2 zl+Ok}xL+9ReT;)uU&a7{`u)&vw4k|QzX0iBK=5rWmJ;kCS%d01MWAP+%7d@ z;3ul)Y2V2*m<~(`=pa=Od3EGpGdc!1iT)7{(A#qh!^DE2z&^9F_*;0}wKEa-9mRB6 zo+4MX!nUz!n`PFtx*M6Rv7l3ZTqD`bfPMIMb2SSlob(3_N<^3j9u8jozrLgqLO|*I zG^dwT8Y${U^|aHmbg^z#yxn>FjyZmiQG%83b>f8ki0ksLwt zE*lx-i4-|D3VVH*$$;bX1Kd3mX-m>%viqSpeLIeMg=FU?`UTa+;_@~h9`UHRkQLZ} zk5p^FbWeT%xiJM_%bR3`s6d0gk$LRV?r-2DGJt)BEkePUG5-594+F+bq<*9K9j|%A zjZgr|U!`lYT^D;i&8Gqy9s+q&l`3>DfQ`(#5VlLA{c0%9jnYAB#x_6+Y{Y{{!HL1| zt(hA5hD-8}9|r085?{Z6P3%`X_WQlBXSg68iBQ^E>ML+j>Mg4YI{p$hdt1JfT7fI_ z9VKaF`gy^U0icc;W7^F+f2U9eZPEzOj~@mespJk zt>0?eaN_S^4Vl(jbq62Tqu+(cpO8MG<$87rT!aZ(uMwKWTaZp_|DMC))Py4gJkJN^ zFki;M*udb;I1VVDI&v&O^&<46+23T!rzC#QWIac(JbXLdOQRhrto}H~`zWIM1@pU& z!_qI+WvLE{-J5@cAENvY=pc|XD46uJVA2!)gC5H45w0OI{N~`}ZW{K*mhR)YtmUH(qRQ?Jzp7_6$d%I5LX1XH+5> z?*KZacZE$k_Mx0^WeLEw^T#%ZDa2Oo{u<#HT#WI(cZmTq)VZH;%_=s2k811Jp9g)m zp1R)FqJH(h-`?`a}w{X%~U!%Q_NSg1< z9#@|~=(N;Ic2EE%EO^xgsCTyI(#3YoYze48F1A)FwqE~A8^2mU`1B>;r>QOP<R$YMepFq75O3D}ofZ0v>M1)nrG7CNeU$0*{O6SA`2d9*7#GXJU7NHGL>;O!lo!s&Yl=^XHe^~;goOVi)EfiG}oc?CAlL6-*m>mbZh7 zq}!Q|^D$eiV3&cw#Xx1wlD;pCF6;dJ#6RuxIoNnx<-1v7rG2v6ASF7)?!N5lwVMvu zO$=-X)U@*1M#1ae-#PV5qjaFk(cwv69$n$LxBzMN`-%mK7ew3tk|ga{t9s1hf~Kue zl0f>vowa6)L}mtfsYhLT(AXOb`TiNcVZmk5{vL@6;F<|Rtg6Plx=Mvj#*|#-uqsk2hiYb^I(?Hd!?^8gm`TuE_{k+#qcwNm_z!W z;P3JiI63KPQ4w|buM5W<`%VYc_~OWnhG|_MItJBO*8BdGRTE$D)3_!wi|j=SH1yVQ zsTyCn1)`#D_sE*p$QI-1tT1c1$+)g$`^3J3@6=<}CqLHHITw23aRk5|3OQga9%n(L z!pQ)D(`=KsX_(2waEqeZ-DUH|)HcXRjYw>aA}*M&KKmO2leV2_M*#VfF`J9o+Wmj{ zdJ}jm*Y9n77tw@JltM`eA#=t^hBD7nB=cP6OuHgPC}Rjk$Xv$ENm6DRHnw>lVw>l^ z_q#To-}n5!|M&f$k56Z)^X&aR_x-GOt?Rnhy_}0htAP<50c5WDb;rOQGR>n1dkrP5yy?PO}Ej%Z$P#uH!*NQ*Oj29pV z{6qN`Citukf19@3SiP;vZeYs`%)OfRFo|6i{Bl=A$->7x`w!8|BK_AG-tGuPU2+kZ z%s7UpgphfbRWgGVw#9wL9wqB}3nOFXYE_aL@=STMj;)nt=|{(8^0uwLzG*zy8ee{h z>yg^E0@AQ?|DHxD4xXca8PT9iNMF2?pZJp|^{x8yxCKkKrOZp}yDx~PRPsaC+cz`T z*Tn<}x-Px|v50&O51$OVl*qE~07bopVmZ+Owcda!(Q+)Z*m=oF9Nb`^7Ku%m1r*t2 z+18yCJ}m6+CEll|kvTEZ7nW|#9@}!2A)F&6i0O5ZVrP^++Nm>yoAS{anbZ#7-)z5= zeReiB2Y%Z9d~^XeP<#A@djma`$0~{yd#V0_Y|-)@*;^z z74&bKOBUMW3LW~pYey$ms7-!RWDYkp0Ap;aeE))`SXx9FZY2X^FV=|#ICglhSB5ia zgb{}PV|Y&{9MOE}Kk3n6;r&ABUU9n*hmqIVyc$QAE@w%*+o&0o8}Kh-lGQ)0t0=3V z%vmvF02+Vv;6vu?*v@opitfzR4%XTlBpk8FR3>eoMLd~g!WkQVM~e`>*uqUGP_{R(I$PE%+U|`_f#y3lk)_G3USBK9Z1|KLyxH!)LNQoy# z2wQ%~H$8_1q5Ydg$G!*9_HtHxpDE8zh_5_5{RomjK(Rn^BNuh@?3;CcpGk|SB!s%c z`0N_*%(`b5YL0I~G&g@A7YK&}LF}Tt{N~7UL@e<#e&r&p*IzhuST`n9T9B_d4@#wN zJ5d+B9gFwVuJQ7!Jjh^i=+dKbn`wR#!Cto^H)np)sVsW&3J7ZF*$`_V0=Z|?J$Jw2 zRm;zUX1-F4N_cV;uTp)RaO!Eq!|EV>>VcHGB)pJcGKNnqageTT$u zc%5;Ii*zSJG{1y~3#c4n(F6__O%ZYxjhl1a9g%YP-H(Q~5bgVY*F46mucNyb5>~t2 zlG2pdZ@-D+pV=tqh`# zSkH`N2Mh}((|l)N?31%Zz;<_7X~vi*mbYX87{HM%C|M))@l7FmLTVuvv_%qM`C?>;xL9!vgxdL#8{k<22ho=yI z{Xo~q;?0nk`)ZfME6v{>@YRs2J7bfD_Si+C8m$ZgU*|-Z6wim;+5XPu-*0C) z^P7G~FEl=)G5BUmv!HBH`;)AS@_LkV#+er~T$SRhA2m7rN*M9irYS?|`X8FE%+bRU z#XF;e7uzXRs$&xdUnR6O@|?7%ak5nYbO=EcLRc08k~cqC{7dnnvtz~zBol)WiC^wHw>B*MSAqU z?=uQgOtt(^5)U}o_3RZ4Ka&(ZlovDin22j z0^XUsleHUF6pU7vlas$VX4B8Oe)gSni%-uq*z_mu1BJZ�>yS`yqfH5Ko?fn)#ub}T4J0k%{__4e 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