Working on end-game menu
This commit is contained in:
parent
13f4c4610e
commit
f6879046fa
@ -63,7 +63,7 @@ export default class EndGameDialog extends GuiObject {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupButtons(lost: boolean) {
|
private showButtons(lost: boolean) {
|
||||||
const buttonContainer = new PIXI.Container();
|
const buttonContainer = new PIXI.Container();
|
||||||
const buttonWidth = 200;
|
const buttonWidth = 200;
|
||||||
const buttonHeight = 80;
|
const buttonHeight = 80;
|
||||||
|
@ -1,127 +1,24 @@
|
|||||||
import * as PIXI from 'pixi.js';
|
import * as PIXI from 'pixi.js';
|
||||||
|
import ModalDialogBase from './ModalDialog';
|
||||||
import GuiObject from '../GuiObject';
|
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 {
|
export default class MessageBox extends ModalDialogBase {
|
||||||
private overlay: PIXI.Graphics;
|
private caption: string;
|
||||||
private buttonPadding = 10;
|
|
||||||
private buttons: Button[] = [];
|
|
||||||
private escapeKeyIndex: number;
|
|
||||||
private keyboardManagerUnsubscribe: () => void;
|
|
||||||
|
|
||||||
constructor(caption: string, buttons: string[], escapeKeyIndex: number = buttons.length - 1) {
|
constructor(caption: string, buttons: string[], escapeKeyIndex: number = buttons.length - 1) {
|
||||||
super();
|
super(buttons, escapeKeyIndex);
|
||||||
console.log(`MessageBox(caption: ${caption}, buttons: ${buttons})`);
|
this.caption = caption;
|
||||||
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);
|
|
||||||
|
|
||||||
|
protected override createContent(): PIXI.Container | GuiObject {
|
||||||
const text = new PIXI.Text({
|
const text = new PIXI.Text({
|
||||||
text: caption,
|
text: this.caption,
|
||||||
style: new PIXI.TextStyle({
|
style: new PIXI.TextStyle({
|
||||||
fill: 0xffffff,
|
fill: 0xffffff,
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
text.anchor.set(0.5, 0.5);
|
return text;
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,13 +28,7 @@ export default class MessageBox extends GuiObject {
|
|||||||
* @returns A promise that resolves with the button that was clicked.
|
* @returns A promise that resolves with the button that was clicked.
|
||||||
*/
|
*/
|
||||||
public static show(caption: string, buttons: string[], escapeKeyButtonIndex: number = 0): Promise<string> {
|
public static show(caption: string, buttons: string[], escapeKeyButtonIndex: number = 0): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
const messageBox = new MessageBox(caption, buttons);
|
||||||
const messageBox = new MessageBox(caption, buttons);
|
return messageBox.show();
|
||||||
Engine.app.stage.addChild(messageBox.container);
|
|
||||||
messageBox.onButtonClicked = (button) => {
|
|
||||||
messageBox.destroy();
|
|
||||||
resolve(button);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
158
src/classes/gui/ModalDialog.ts
Normal file
158
src/classes/gui/ModalDialog.ts
Normal file
@ -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<string> {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/classes/gui/PlayerNameInput.ts
Normal file
45
src/classes/gui/PlayerNameInput.ts
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,10 @@ export default class TextInput extends GuiObject {
|
|||||||
private text: PIXI.Text;
|
private text: PIXI.Text;
|
||||||
private maxLength: number;
|
private maxLength: number;
|
||||||
|
|
||||||
|
public getText(): string {
|
||||||
|
return this.text.text;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(bounds: PIXI.Rectangle, maxLength: number) {
|
constructor(bounds: PIXI.Rectangle, maxLength: number) {
|
||||||
super();
|
super();
|
||||||
this.bounds = bounds;
|
this.bounds = bounds;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user