programing is a hard job, i have nothing to say

This commit is contained in:
koneko 2024-11-28 00:38:21 +01:00
parent c6a1ae71b1
commit b7d084439f
9 changed files with 325 additions and 42 deletions

90
src/classes/Assets.ts Normal file
View File

@ -0,0 +1,90 @@
import * as PIXI from 'pixi.js';
import { CreepStatsDefinition, MissionDefinition, TowerDefinition } from './Definitions';
export default class Assets {
public static async LoadAssets() {
console.log('Loading Texture Assets');
Assets.Button01Texture = await PIXI.Assets.load({
src: '/assets/gui/button_01.png',
});
Assets.Button02Texture = await PIXI.Assets.load({
src: '/assets/gui/button_02.png',
});
Assets.Frame01Texture = await PIXI.Assets.load({
src: '/assets/gui/frame_01.png',
});
Assets.Frame02Texture = await PIXI.Assets.load({
src: '/assets/gui/frame_02.png',
});
Assets.FrameGreenTexture = await PIXI.Assets.load({
src: '/assets/gui/frame_green.png',
});
Assets.FrameRedTexture = await PIXI.Assets.load({
src: '/assets/gui/frame_red.png',
});
Assets.FrameVioletTexture = await PIXI.Assets.load({
src: '/assets/gui/frame_violet.png',
});
Assets.HealthTexture = await PIXI.Assets.load({
src: '/assets/gui/heart.png',
});
Assets.GoldTexture = await PIXI.Assets.load({
src: '/assets/gui/star.png',
});
Assets.BasicCreepTexture = await PIXI.Assets.load({
src: '/assets/creeps/basic.jpg',
});
await this.LoadMissions();
await this.LoadTowers();
await this.LoadCreepStats();
}
public static async LoadCreepStats() {
const res = await fetch('/assets/CreepStats.json');
const stats = await res.json();
this.CreepStats = stats;
}
private static async LoadMissions() {
Assets.Missions = [await this.LoadMission('/assets/missions/mission_01.json')];
}
private static async LoadTowers() {
const res = await fetch('/assets/Towers.json');
const towers = await res.json();
Assets.Towers = towers;
}
private static async LoadMission(missionUrl: string) {
const res = await fetch(missionUrl);
const mission = await res.json();
await this.LoadBackground(mission.mapImage.url);
return mission;
}
private static async LoadBackground(backgroundUrl: string) {
let index = this.MissionBackgrounds.length - 1;
if (index == -1) index = 0;
this.MissionBackgrounds[index] = await PIXI.Assets.load({
src: backgroundUrl,
});
}
public static BasicCreepTexture: PIXI.Texture;
public static Frame01Texture: PIXI.Texture;
public static Frame02Texture: PIXI.Texture;
public static FrameGreenTexture: PIXI.Texture;
public static FrameRedTexture: PIXI.Texture;
public static FrameVioletTexture: PIXI.Texture;
public static Button01Texture: PIXI.Texture;
public static Button02Texture: PIXI.Texture;
public static HealthTexture: PIXI.Texture;
public static GoldTexture: PIXI.Texture;
public static MissionBackgrounds: PIXI.Texture[] = [];
public static Missions: MissionDefinition[];
public static Towers: TowerDefinition[];
public static CreepStats: CreepStatsDefinition[];
public static DebuggingEnabled: boolean = true;
}

View File

@ -1,47 +1,62 @@
import * as PIXI from 'pixi.js';
import DynamicObject from './DynamicObject';
import GameObject from './GameObject';
import GuiObject from './GuiObject';
import Scene from '../scenes/Scene';
export class Foundation {
public static _PIXIApp: PIXI.Application;
public static Master: Master;
export class environment {
public static app: PIXI.Application;
public static GameMaster: GameMaster;
public static WindowHeight: number;
public static WindowWidth: number;
public static AspectRatio: number = 4 / 3;
}
export default class Master {
private DynamicObjects: DynamicObject[];
export default class GameMaster {
public currentScene: Scene;
private GameObjects: GameObject[];
private ticker: PIXI.Ticker;
public stage: PIXI.Container = new PIXI.Container({
width: Foundation.WindowWidth,
height: Foundation.WindowHeight,
});
constructor() {
Foundation.Master = this;
environment.GameMaster = this;
this.ticker = new PIXI.Ticker();
this.ticker.maxFPS = 60;
this.ticker.minFPS = 30;
this.ticker.add(() => this.update(this.ticker.elapsedMS));
}
public _CreateDynamicObject(object: DynamicObject) {
this.DynamicObjects.push(object);
public _CreateGameObject(object: GameObject) {
this.GameObjects.push(object);
environment.app.stage.addChild(object.container);
}
public _RemoveDynamicObject(object: DynamicObject) {
this.DynamicObjects.splice(this.DynamicObjects.indexOf(object), 1);
public _RemoveGameObject(object: GameObject) {
this.GameObjects.splice(this.GameObjects.indexOf(object), 1);
environment.app.stage.removeChild(object.container);
}
public RefreshStage() {
Foundation._PIXIApp.stage.removeChildren();
this.stage.width = Foundation.WindowWidth;
this.stage.height = Foundation.WindowHeight;
Foundation._PIXIApp.stage.addChild(this.stage);
public _CreateGuiObject(object: GuiObject) {
this.currentScene.gui.push(object);
environment.app.stage.addChild(object.container);
}
public _RemoveGuiObject(object: GuiObject) {
this.currentScene.gui.splice(this.currentScene.gui.indexOf(object), 1);
environment.app.stage.removeChild(object.container);
}
public changeScene(newScene: Scene) {
if (this.currentScene) {
this.currentScene.destroy();
}
this.GameObjects.forEach((element) => {
element.destroy();
});
this.currentScene = newScene;
this.currentScene.init();
}
public update(elapsedMS) {
this.DynamicObjects.forEach((element) => {
this.GameObjects.forEach((element) => {
element.update(elapsedMS);
});
}

View File

@ -0,0 +1,84 @@
export type MissionDefinition = {
name: string;
description: string;
mapImage: MapImageDefinition;
gameMap: GameMapDefinition;
rounds: MissionRoundDefinition[];
};
export type MapImageDefinition = {
url: string;
width: number;
height: number;
};
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 CreepStatsDefinition = {
health: number;
speed: number;
special: Function;
resistance: CreepResistancesDefinition;
};
export type CreepResistancesDefinition = {
physical: number;
divine: number;
fire: number;
ice: number;
frostfire: number;
};
export type TowerDefinition = {
name: string;
stats: TowerStatsDefinition;
};
export type TowerStatsDefinition = {
damage: number;
cooldown: number;
gemSlotsAmount: number;
cost: number;
range: number;
};
export type PathDefinition = [[row: number, column: number]];
export enum CreepType {
Basic = 0,
Fast = 1,
}
export enum TerrainType {
Restricted = 0,
Buildable = 1,
Path = 9,
}
export enum GemType {
Fire = 0,
Yeti = 1,
Titalium = 2,
Soulforge = 3,
}
export enum TowerType {
Shooting = 0,
Circle = 1,
}

View File

@ -1,7 +1,7 @@
import * as PIXI from 'pixi.js';
import { Foundation } from './Bastion';
import { environment } from './Bastion';
export default abstract class DynamicObject {
export default abstract class GameObject {
public readonly name: string = this.constructor.name;
protected _container: PIXI.Container = new PIXI.Container();
@ -9,10 +9,10 @@ export default abstract class DynamicObject {
protected _events: PIXI.EventEmitter = new PIXI.EventEmitter();
public destroy() {
environment.GameMaster._RemoveGameObject(this);
this._events.removeAllListeners();
if (this._container.parent) this._container.parent.removeChild(this._container);
this._container.destroy();
Foundation.Master._RemoveDynamicObject(this);
}
public get container(): PIXI.Container {
@ -23,9 +23,10 @@ export default abstract class DynamicObject {
return this._events;
}
public update(elapsedMS) {}
public abstract update(elapsedMS): void;
constructor() {
Foundation.Master._CreateDynamicObject(this);
// Define stuff that goes into this.container (visual elements), then call super().
environment.GameMaster._CreateGameObject(this);
}
}

View File

@ -1,12 +1,15 @@
import * as PIXI from 'pixi.js';
import { environment } from './Bastion';
export default abstract class GuiObject {
public readonly name: string = this.constructor.name;
protected _container: PIXI.Container;
protected _container: PIXI.Container = new PIXI.Container();
protected _events: PIXI.EventEmitter = new PIXI.EventEmitter();
protected enabled: boolean;
public destroy() {
this._events.removeAllListeners();
if (this._container.parent) this._container.parent.removeChild(this._container);
@ -24,18 +27,24 @@ export default abstract class GuiObject {
public onClick(e: PIXI.FederatedPointerEvent) {
console.warn(`[${this.name} does not implement GuiObject.onClick()]`);
}
public onWheel(e: PIXI.FederatedWheelEvent) {
console.warn(`[${this.name} does not implement GuiObject.onWheel()]`);
}
constructor() {
this._container = new PIXI.Container();
public setEnabled(enabled: boolean) {
this.enabled = enabled;
}
constructor(interactive?: boolean) {
environment.GameMaster._CreateGuiObject(this);
if (!interactive) return;
this._container.interactive = true;
this._container.onwheel = (e) => {
this.onWheel(e);
if (this.enabled) this.onWheel(e);
};
this._container.onclick = (e) => {
this.onClick(e);
if (this.enabled) this.onClick(e);
};
}
}

60
src/classes/gui/Button.ts Normal file
View File

@ -0,0 +1,60 @@
import * as PIXI from 'pixi.js';
import GuiObject from '../GuiObject';
import Assets from '../Assets';
export enum ButtonTexture {
Button01 = 0,
Button02 = 1,
}
export default class Button extends GuiObject {
private caption: string;
private bounds: PIXI.Rectangle;
private buttonTexture: PIXI.Texture;
private buttonSprite: PIXI.NineSliceSprite;
private buttonText: PIXI.Text;
setCaption(caption: string) {
this.caption = caption;
this.buttonText.text = caption;
}
constructor(bounds: PIXI.Rectangle, caption: string, buttonTexture: ButtonTexture, enabled: boolean = true) {
super(true);
if (buttonTexture == ButtonTexture.Button01) this.buttonTexture = Assets.Button01Texture;
if (buttonTexture == ButtonTexture.Button02) this.buttonTexture = Assets.Button02Texture;
this.caption = caption;
this.enabled = enabled;
this.bounds = bounds;
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.buttonSprite = new PIXI.NineSliceSprite({
texture: this.buttonTexture,
leftWidth: 100,
topHeight: 100,
rightWidth: 100,
bottomHeight: 100,
});
this.buttonSprite.x = 0;
this.buttonSprite.y = 0;
this.buttonSprite.width = this.bounds.width;
this.buttonSprite.height = this.bounds.height;
this.container.addChild(this.buttonSprite);
this.buttonText = new PIXI.Text({
text: this.caption,
style: new PIXI.TextStyle({
fill: 0xffffff,
fontSize: 24,
}),
});
this.container.addChild(this.buttonText);
this.buttonText.anchor.set(0.5, 0.5);
this.buttonText.x = this.bounds.width / 2;
this.buttonText.y = this.bounds.height / 2;
this.container.x = this.bounds.x;
this.container.y = this.bounds.y;
}
}

View File

@ -1,16 +1,17 @@
import * as PIXI from 'pixi.js';
import Master, { Foundation } from './classes/Bastion';
import GameMaster, { environment } from './classes/Bastion';
import Assets from './classes/Assets';
(async () => {
const app = new PIXI.Application();
const aspectRatio = Foundation.AspectRatio;
const aspectRatio = environment.AspectRatio;
const maxWidth = window.innerWidth;
const maxHeight = window.innerHeight;
const width = Math.min(maxWidth * 0.75, maxHeight * aspectRatio);
const height = width / aspectRatio;
Foundation.WindowWidth = width;
Foundation.WindowHeight = height;
Foundation._PIXIApp = app;
environment.WindowWidth = width;
environment.WindowHeight = height;
environment.app = app;
await app.init({
width: width,
@ -19,16 +20,14 @@ import Master, { Foundation } from './classes/Bastion';
sharedTicker: true,
preference: 'webgl',
});
document.body.appendChild(app.canvas);
let master = new Master();
master.RefreshStage();
await Assets.LoadAssets();
new GameMaster();
window.addEventListener('resize', () => {
const newWidth = Math.min(window.innerWidth * 0.75, window.innerHeight * aspectRatio);
const newHeight = newWidth / aspectRatio;
Foundation.WindowWidth = newWidth;
Foundation.WindowHeight = newHeight;
environment.WindowWidth = newWidth;
environment.WindowHeight = newHeight;
app.renderer.resize(newWidth, newHeight);
master.RefreshStage();
});
})();

View File

@ -0,0 +1,9 @@
import Button from '../classes/gui/Button';
import Scene from './Scene';
import * as PIXI from 'pixi.js';
export class MainScene extends Scene {
public init() {
new Button(new PIXI.Bounds(0, 0, 200, 200));
}
}

16
src/scenes/Scene.ts Normal file
View File

@ -0,0 +1,16 @@
import GuiObject from '../classes/GuiObject';
export default class Scene {
public gui: GuiObject[];
public destroy() {
this.gui.forEach((element) => {
element.destroy();
});
}
public GetGuiObject(object: GuiObject) {
return this.gui.find((obj) => obj == object);
}
public init() {
// Definitions for scene elements.
}
}