WIP
This commit is contained in:
parent
7f2f1f514e
commit
7e9e178dc3
51
public/assets/missions/mission_01.json
Normal file
51
public/assets/missions/mission_01.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "Mission 1",
|
||||
"description": "This is the first mission",
|
||||
"gameMap": {
|
||||
"rows": 10,
|
||||
"columns": 10,
|
||||
"cells": [
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 0, 1],
|
||||
[1, 1, 1, 1, 1, 0, 0, 0, 1, 1],
|
||||
[1, 1, 1, 1, 1, 0, 0, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
],
|
||||
"paths": [
|
||||
[
|
||||
[0, 2],
|
||||
[1, 2],
|
||||
[2, 2],
|
||||
[3, 2],
|
||||
[4, 2],
|
||||
[5, 2],
|
||||
[6, 2],
|
||||
[7, 2],
|
||||
[8, 2],
|
||||
[9, 2]
|
||||
]
|
||||
],
|
||||
"waves": [
|
||||
{
|
||||
"firstCreepSpawnTick": 120,
|
||||
"spawnIntervalTicks": 60,
|
||||
"creeps": [0, 0, 0, 0, 0, 1, 1, 1, 0]
|
||||
},
|
||||
{
|
||||
"firstCreepSpawnTick": 480,
|
||||
"spawnIntervalTicks": 60,
|
||||
"creeps": [0, 0, 0, 0, 0, 1, 1, 1, 0]
|
||||
},
|
||||
{
|
||||
"firstCreepSpawnTick": 480,
|
||||
"spawnIntervalTicks": 60,
|
||||
"creeps": [0, 0, 0, 0, 0, 1, 1, 1, 0]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,11 +1,29 @@
|
||||
import * as PIXI from "pixi.js";
|
||||
import { MissionDefinition } from "./Definitions";
|
||||
|
||||
export default class Assets {
|
||||
public static async LoadAssets() {
|
||||
console.log("Loading Texture Assets");
|
||||
Assets.ButtonTexture = await PIXI.Assets.load({
|
||||
src: "/assets/gui/button_02.png",
|
||||
});
|
||||
console.log("Loading Missions");
|
||||
await this.LoadMissions();
|
||||
}
|
||||
|
||||
private static async LoadMissions() {
|
||||
Assets.Missions = [
|
||||
await this.LoadMission("/assets/missions/mission_01.json"),
|
||||
];
|
||||
}
|
||||
|
||||
private static async LoadMission(missionUrl: string) {
|
||||
const res = await fetch(missionUrl);
|
||||
const mission = await res.json();
|
||||
return mission;
|
||||
}
|
||||
|
||||
public static ButtonTexture: PIXI.Texture;
|
||||
|
||||
public static Missions: MissionDefinition[];
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ export default class Button extends GameObject {
|
||||
|
||||
constructor(
|
||||
caption: string,
|
||||
bounds: PIXI.Rectangle,
|
||||
color: PIXI.Color,
|
||||
enabled: boolean = true
|
||||
enabled: boolean = true,
|
||||
bounds?: PIXI.Rectangle
|
||||
) {
|
||||
super(bounds);
|
||||
this.caption = caption;
|
||||
@ -33,9 +33,6 @@ export default class Button extends GameObject {
|
||||
}
|
||||
|
||||
protected draw() {
|
||||
console.log(
|
||||
`Drawing button ${this.caption} at ${JSON.stringify(this.bounds)}`
|
||||
);
|
||||
this.container.removeChildren();
|
||||
// const button = new PIXI.Graphics();
|
||||
// button.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
|
43
src/base/Definitions.ts
Normal file
43
src/base/Definitions.ts
Normal file
@ -0,0 +1,43 @@
|
||||
export type MissionDefinition = {
|
||||
name: string;
|
||||
description: string;
|
||||
mapImageUrl: string;
|
||||
gameMap: GameMapDefinition;
|
||||
};
|
||||
|
||||
export type GameMapDefinition = {
|
||||
rows: number;
|
||||
columns: number;
|
||||
cells: TerrainType[][];
|
||||
paths: PathDefinition[];
|
||||
};
|
||||
|
||||
export type MissionRoundDefinition = {
|
||||
waves: WaveDefinition[];
|
||||
offeredGems: GemType[];
|
||||
};
|
||||
|
||||
export type WaveDefinition = {
|
||||
firstCreepSpawnTick: number;
|
||||
spawnIntervalTicks: number;
|
||||
creeps: CreepType[];
|
||||
};
|
||||
|
||||
export type PathDefinition = [row: number, column: number];
|
||||
|
||||
export enum CreepType {
|
||||
Basic = 0,
|
||||
Fast = 1,
|
||||
}
|
||||
|
||||
export enum TerrainType {
|
||||
Restricted = 0,
|
||||
Buildable = 1,
|
||||
}
|
||||
|
||||
export enum GemType {
|
||||
Fire = 0,
|
||||
Yeti = 1,
|
||||
Titalium = 2,
|
||||
Soulforge = 3,
|
||||
}
|
@ -35,7 +35,7 @@ export default class Game extends GameObject {
|
||||
const missionSelectScene = new MissionMenuSelect(this.bounds);
|
||||
missionSelectScene.events.on("mission", (mission) => {
|
||||
console.log("Mission selected", mission);
|
||||
this.setScene(new GameScene(this.bounds));
|
||||
this.setScene(new GameScene(mission, this.bounds));
|
||||
});
|
||||
missionSelectScene.events.on("back", () => {
|
||||
this.onMainMenu();
|
||||
|
@ -6,8 +6,19 @@ export default abstract class GameObject {
|
||||
|
||||
private _events: PIXI.EventEmitter = new PIXI.EventEmitter();
|
||||
|
||||
public setBounds(x: number, y: number, width: number, height: number) {
|
||||
this.bounds = new PIXI.Rectangle(x, y, width, height);
|
||||
public setBounds(bounds: PIXI.Rectangle): void;
|
||||
public setBounds(x: number, y: number, width: number, height: number): void;
|
||||
public setBounds(
|
||||
boundsOrX: PIXI.Rectangle | number,
|
||||
y?: number,
|
||||
width?: number,
|
||||
height?: number
|
||||
) {
|
||||
if (boundsOrX instanceof PIXI.Rectangle) {
|
||||
this.bounds = boundsOrX;
|
||||
} else {
|
||||
this.bounds = new PIXI.Rectangle(boundsOrX, y, width, height);
|
||||
}
|
||||
this.triggerBoundsChanged(); // GameObject implements this.
|
||||
}
|
||||
|
||||
@ -32,8 +43,8 @@ export default abstract class GameObject {
|
||||
|
||||
protected abstract draw(): void;
|
||||
|
||||
constructor(bounds: PIXI.Rectangle) {
|
||||
this.bounds = bounds;
|
||||
constructor(bounds?: PIXI.Rectangle) {
|
||||
this.bounds = bounds ?? new PIXI.Rectangle(0, 0, 0, 0);
|
||||
this._container = new PIXI.Container();
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
import * as PIXI from "pixi.js";
|
||||
import GameObject from "../base/GameObject";
|
||||
|
||||
export enum CellType {
|
||||
Path,
|
||||
NoBuild,
|
||||
Build,
|
||||
Undefined,
|
||||
}
|
||||
import { GameMapDefinition, TerrainType } from "../base/Definitions";
|
||||
|
||||
export class Cell extends GameObject {
|
||||
public type: CellType;
|
||||
constructor(bounds: PIXI.Rectangle, type: CellType) {
|
||||
public type: TerrainType;
|
||||
public row: number;
|
||||
public column: number;
|
||||
|
||||
constructor(
|
||||
type: TerrainType,
|
||||
row: number,
|
||||
column: number,
|
||||
bounds?: PIXI.Rectangle
|
||||
) {
|
||||
super(bounds);
|
||||
this.type = type;
|
||||
this.row = row;
|
||||
this.column = column;
|
||||
this.draw();
|
||||
}
|
||||
|
||||
@ -21,17 +25,11 @@ export class Cell extends GameObject {
|
||||
let g = new PIXI.Graphics();
|
||||
g.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
switch (this.type) {
|
||||
case CellType.Path:
|
||||
g.fill(0x00ff00);
|
||||
break;
|
||||
case CellType.NoBuild:
|
||||
case TerrainType.Restricted:
|
||||
g.fill(0xff0000);
|
||||
break;
|
||||
case CellType.Build:
|
||||
g.fill(0x0000ff);
|
||||
break;
|
||||
case CellType.Undefined:
|
||||
g.fill(0x000000);
|
||||
case TerrainType.Buildable:
|
||||
g.fill(0x00ff00);
|
||||
break;
|
||||
}
|
||||
this.container.addChild(g);
|
||||
@ -41,35 +39,45 @@ export class Cell extends GameObject {
|
||||
}
|
||||
|
||||
export class Grid extends GameObject {
|
||||
public rows: number;
|
||||
public columns: number;
|
||||
public cells: Array<Cell>;
|
||||
private gameMap: GameMapDefinition;
|
||||
private cells: Cell[] = [];
|
||||
|
||||
constructor(bounds: PIXI.Rectangle, rows, columns) {
|
||||
constructor(map: GameMapDefinition, bounds?: PIXI.Rectangle) {
|
||||
super(bounds);
|
||||
this.rows = rows;
|
||||
this.columns = columns;
|
||||
for (let y = 0; y < rows; y++) {
|
||||
for (let x = 0; x < columns; x++) {
|
||||
let cell = new Cell(
|
||||
new PIXI.Rectangle(
|
||||
x,
|
||||
y,
|
||||
this.gridUnitsToPixels(1),
|
||||
this.gridUnitsToPixels(1)
|
||||
),
|
||||
CellType.Undefined
|
||||
);
|
||||
this.gameMap = map;
|
||||
for (let y = 0; y < this.gameMap.rows; y++) {
|
||||
for (let x = 0; x < this.gameMap.columns; x++) {
|
||||
let cell = new Cell(this.gameMap.cells[x][y], x, y);
|
||||
this.cells.push(cell);
|
||||
}
|
||||
}
|
||||
console.log(this.cells);
|
||||
this.draw();
|
||||
}
|
||||
|
||||
protected draw() {}
|
||||
protected draw() {
|
||||
console.log("Drawing Grid", this.bounds);
|
||||
this.container.removeChildren();
|
||||
let g = new PIXI.Graphics();
|
||||
g.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
g.fill(0x00aa00);
|
||||
this.container.addChild(g);
|
||||
for (let cell of this.cells) {
|
||||
cell.setBounds(
|
||||
this.gridUnitsToPixels(cell.column),
|
||||
this.gridUnitsToPixels(cell.row),
|
||||
this.gridUnitsToPixels(1),
|
||||
this.gridUnitsToPixels(1)
|
||||
);
|
||||
this.container.addChild(cell.container);
|
||||
}
|
||||
this.container.x = this.bounds.x;
|
||||
this.container.y = this.bounds.y;
|
||||
}
|
||||
|
||||
private getPixelScalingFactor() {
|
||||
const pixelScaleX = this.container.width / this.columns;
|
||||
const pixelScaleY = this.container.height / this.rows;
|
||||
const pixelScaleX = this.container.width / this.gameMap.columns;
|
||||
const pixelScaleY = this.container.height / this.gameMap.rows;
|
||||
return pixelScaleX < pixelScaleY ? pixelScaleX : pixelScaleY;
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,41 @@ import GameObject from "../base/GameObject";
|
||||
import * as PIXI from "pixi.js";
|
||||
|
||||
export default class MissionStats extends GameObject {
|
||||
constructor(bounds: PIXI.Rectangle) {
|
||||
super(bounds);
|
||||
private hp: number = 100;
|
||||
private gold: number = 0;
|
||||
|
||||
public setHP(hp: number) {
|
||||
this.hp = hp;
|
||||
this.draw();
|
||||
}
|
||||
|
||||
protected draw() {}
|
||||
public setGold(gold: number) {
|
||||
this.gold = gold;
|
||||
this.draw();
|
||||
}
|
||||
|
||||
constructor(initialHP: number, initialGold: number, bounds?: PIXI.Rectangle) {
|
||||
super(bounds);
|
||||
this.hp = initialHP;
|
||||
this.gold = initialGold;
|
||||
this.draw();
|
||||
}
|
||||
|
||||
protected draw() {
|
||||
this.container.removeChildren();
|
||||
const g = new PIXI.Graphics();
|
||||
g.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
g.fill(0x000000);
|
||||
this.container.addChild(g);
|
||||
const text = new PIXI.Text({
|
||||
text: `HP: ${this.hp}\nGold: ${this.gold}`,
|
||||
style: new PIXI.TextStyle({
|
||||
fill: "white",
|
||||
fontSize: 24,
|
||||
}),
|
||||
});
|
||||
this.container.addChild(text);
|
||||
this.container.x = this.bounds.x;
|
||||
this.container.y = this.bounds.y;
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,62 @@
|
||||
import Button from "../base/Button";
|
||||
import { MissionDefinition } from "../base/Definitions";
|
||||
import { Grid } from "../components/Grid";
|
||||
import MissionStats from "../components/MissionStats";
|
||||
import SceneBase from "./SceneBase";
|
||||
import * as PIXI from "pixi.js";
|
||||
|
||||
export default class GameScene extends SceneBase {
|
||||
private _ticker: PIXI.Ticker;
|
||||
private ticker: PIXI.Ticker;
|
||||
private stats: MissionStats;
|
||||
private grid: Grid;
|
||||
|
||||
constructor(bounds: PIXI.Rectangle) {
|
||||
constructor(mission: MissionDefinition, bounds: PIXI.Rectangle) {
|
||||
super(bounds);
|
||||
this._ticker = new PIXI.Ticker();
|
||||
this._ticker = new PIXI.Ticker();
|
||||
this._ticker.maxFPS = 60;
|
||||
this._ticker.minFPS = 30;
|
||||
this._ticker.add(this.update);
|
||||
this._ticker.start();
|
||||
this.ticker = new PIXI.Ticker();
|
||||
this.ticker = new PIXI.Ticker();
|
||||
this.ticker.maxFPS = 60;
|
||||
this.ticker.minFPS = 30;
|
||||
this.ticker.add(this.update);
|
||||
this.ticker.start();
|
||||
this.stats = new MissionStats(100, 200);
|
||||
this.grid = new Grid(mission.gameMap);
|
||||
this.draw();
|
||||
}
|
||||
|
||||
private getStatusBounds(): PIXI.Rectangle {
|
||||
// Top / Center
|
||||
return new PIXI.Rectangle(this.bounds.width / 2 - 200 / 2, 0, 200, 100);
|
||||
}
|
||||
|
||||
private getGridBounds(): PIXI.Rectangle {
|
||||
// Center / Center
|
||||
return new PIXI.Rectangle(
|
||||
this.bounds.width / 2 - 600 / 2,
|
||||
this.bounds.height / 2 - 600 / 2,
|
||||
600,
|
||||
600
|
||||
);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
super.destroy();
|
||||
this._ticker.stop();
|
||||
this._ticker.destroy();
|
||||
this.ticker.stop();
|
||||
this.ticker.destroy();
|
||||
}
|
||||
|
||||
public update() {}
|
||||
|
||||
protected draw() {
|
||||
console.log("Creating Game Scene ", this.bounds);
|
||||
console.log("Drawing Game Scene ", this.bounds);
|
||||
this.container.removeChildren();
|
||||
const g = new PIXI.Graphics();
|
||||
g.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
g.fill(0x000033);
|
||||
this.container.addChild(g);
|
||||
this.stats.setBounds(this.getStatusBounds());
|
||||
this.grid.setBounds(this.getGridBounds());
|
||||
this.container.addChild(this.stats.container);
|
||||
this.container.addChild(this.grid.container);
|
||||
this.container.x = this.bounds.x;
|
||||
this.container.y = this.bounds.y;
|
||||
}
|
||||
|
@ -6,8 +6,16 @@ export default class MainMenu extends SceneBase {
|
||||
private _newGameButton: Button;
|
||||
private _settingsButton: Button;
|
||||
|
||||
constructor(bounds: PIXI.Rectangle) {
|
||||
constructor(bounds?: PIXI.Rectangle) {
|
||||
super(bounds);
|
||||
this._newGameButton = new Button("New Game", new PIXI.Color("blue"));
|
||||
this._newGameButton.events.on("click", () => {
|
||||
this.events.emit("newGame");
|
||||
});
|
||||
this._settingsButton = new Button("Settings", new PIXI.Color("gray"));
|
||||
this._settingsButton.events.on("click", () => {
|
||||
this.events.emit("settings");
|
||||
});
|
||||
this.draw();
|
||||
}
|
||||
|
||||
@ -18,34 +26,19 @@ export default class MainMenu extends SceneBase {
|
||||
g.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
g.fill(0x000000);
|
||||
this.container.addChild(g);
|
||||
this._newGameButton = new Button(
|
||||
"New Game",
|
||||
new PIXI.Rectangle(
|
||||
this._newGameButton.setBounds(
|
||||
this.bounds.width / 2 - 300 / 2,
|
||||
this.bounds.height / 2 - 80,
|
||||
300,
|
||||
60
|
||||
),
|
||||
new PIXI.Color("blue")
|
||||
);
|
||||
this._newGameButton.events.on("click", () => {
|
||||
this.events.emit("newGame");
|
||||
});
|
||||
this.container.addChild(this._newGameButton.container);
|
||||
|
||||
this._settingsButton = new Button(
|
||||
"Settings",
|
||||
new PIXI.Rectangle(
|
||||
this._settingsButton.setBounds(
|
||||
this.bounds.width / 2 - 300 / 2,
|
||||
this.bounds.height / 2 + 20,
|
||||
300,
|
||||
60
|
||||
),
|
||||
new PIXI.Color("gray")
|
||||
);
|
||||
this._settingsButton.events.on("click", () => {
|
||||
this.events.emit("settings");
|
||||
});
|
||||
this.container.addChild(this._newGameButton.container);
|
||||
this.container.addChild(this._settingsButton.container);
|
||||
this.container.x = this.bounds.x;
|
||||
this.container.y = this.bounds.y;
|
||||
|
@ -1,4 +1,6 @@
|
||||
import Assets from "../base/Assets";
|
||||
import Button from "../base/Button";
|
||||
import { MissionDefinition } from "../base/Definitions";
|
||||
import SceneBase from "./SceneBase";
|
||||
import * as PIXI from "pixi.js";
|
||||
|
||||
@ -7,42 +9,40 @@ export default class MissionMenuSelect extends SceneBase {
|
||||
|
||||
constructor(bounds: PIXI.Rectangle) {
|
||||
super(bounds);
|
||||
for (const mission of Assets.Missions) {
|
||||
this.addMission(mission);
|
||||
}
|
||||
this.addButton("Back", () => {
|
||||
this.events.emit("back");
|
||||
});
|
||||
this.draw();
|
||||
}
|
||||
|
||||
protected draw() {
|
||||
this.container.removeChildren();
|
||||
this._buttons = [];
|
||||
const g = new PIXI.Graphics();
|
||||
g.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
g.fill(0x000000);
|
||||
this.container.addChild(g);
|
||||
this.addMission("Mission 1");
|
||||
this.addMission("Mission 2");
|
||||
this.addMission("Mission 3");
|
||||
this.addMission("Mission 4");
|
||||
this.addButton("Back", () => {
|
||||
this.events.emit("back");
|
||||
});
|
||||
let y = 50;
|
||||
for (const button of this._buttons) {
|
||||
button.setBounds(this.bounds.width / 2 - 300 / 2, y, 300, 60);
|
||||
y += 80;
|
||||
this.container.addChild(button.container);
|
||||
}
|
||||
this.container.x = this.bounds.x;
|
||||
this.container.y = this.bounds.y;
|
||||
}
|
||||
|
||||
private addMission(mission: string) {
|
||||
this.addButton(mission, () => {
|
||||
private addMission(mission: MissionDefinition) {
|
||||
this.addButton(mission.name, () => {
|
||||
this.events.emit("mission", mission);
|
||||
});
|
||||
}
|
||||
|
||||
private addButton(caption: string, onClick: () => void) {
|
||||
const yOffset = this._buttons.length * 80 + 100;
|
||||
const button = new Button(
|
||||
caption,
|
||||
new PIXI.Rectangle(100, yOffset, this.bounds.width - 200, 60),
|
||||
new PIXI.Color("white")
|
||||
);
|
||||
const button = new Button(caption, new PIXI.Color("white"));
|
||||
button.events.on("click", onClick);
|
||||
this._buttons.push(button);
|
||||
this.container.addChild(button.container);
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,6 @@ import SceneBase from "./SceneBase";
|
||||
import * as PIXI from "pixi.js";
|
||||
|
||||
export default class SettingsMenu extends SceneBase {
|
||||
private _newGameButton: Button;
|
||||
private _settingsButton: Button;
|
||||
|
||||
constructor(bounds: PIXI.Rectangle) {
|
||||
super(bounds);
|
||||
this.draw();
|
||||
@ -16,36 +13,7 @@ export default class SettingsMenu extends SceneBase {
|
||||
const g = new PIXI.Graphics();
|
||||
g.rect(0, 0, this.bounds.width, this.bounds.height);
|
||||
g.fill(0x000000);
|
||||
this.container.addChild(g);
|
||||
this._newGameButton = new Button(
|
||||
"New Game",
|
||||
new PIXI.Rectangle(
|
||||
100,
|
||||
this.bounds.height / 2 - 80,
|
||||
this.bounds.width - 200,
|
||||
60
|
||||
),
|
||||
new PIXI.Color("blue")
|
||||
);
|
||||
this._newGameButton.events.on("click", () => {
|
||||
this.events.emit("newGame");
|
||||
});
|
||||
this.container.addChild(this._newGameButton.container);
|
||||
|
||||
this._settingsButton = new Button(
|
||||
"Settings",
|
||||
new PIXI.Rectangle(
|
||||
100,
|
||||
this.bounds.height / 2 + 20,
|
||||
this.bounds.width - 200,
|
||||
60
|
||||
),
|
||||
new PIXI.Color("gray")
|
||||
);
|
||||
this._settingsButton.events.on("click", () => {
|
||||
this.events.emit("settings");
|
||||
});
|
||||
this.container.addChild(this._settingsButton.container);
|
||||
this.container.x = this.bounds.x;
|
||||
this.container.y = this.bounds.y;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user