2020-02-16 05:00:26 +00:00
|
|
|
import { KeyName } from "../Applet/Keyboard";
|
|
|
|
import { DrawSet, Layer } from "../Applet/Render";
|
2019-12-14 23:11:00 +00:00
|
|
|
import { Data as EcsData } from "../Ecs/Components";
|
2020-02-16 05:00:26 +00:00
|
|
|
import { copy, SparseStore } from "../Ecs/Data";
|
|
|
|
import { DumbMotion } from "../Ecs/Location";
|
|
|
|
import { INPUT_FREQUENCY, LockstepProcessor } from "../Ecs/Lockstep";
|
|
|
|
import { Buttons } from "./Input";
|
2019-12-14 23:11:00 +00:00
|
|
|
|
|
|
|
export enum GamePhase {
|
|
|
|
TITLE,
|
|
|
|
PLAYING,
|
|
|
|
LOST,
|
|
|
|
WON
|
|
|
|
}
|
|
|
|
export type RGB = [number, number, number];
|
|
|
|
export class World {
|
|
|
|
width = 500;
|
|
|
|
height = 400;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Core Game Status
|
|
|
|
*/
|
|
|
|
phase = GamePhase.TITLE;
|
|
|
|
score = 0;
|
|
|
|
|
|
|
|
constructor() {}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Drawing Layers
|
|
|
|
*/
|
|
|
|
groundLayer = new Layer(0);
|
|
|
|
debugLayer = new Layer(2);
|
|
|
|
bulletLayer = new Layer(10);
|
|
|
|
playerLayer = new Layer(15);
|
|
|
|
smokeLayer = new Layer(16);
|
|
|
|
hudLayer = new Layer(20);
|
|
|
|
|
|
|
|
bgColor: RGB = [255, 255, 255];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Catch-all debug tool
|
|
|
|
*/
|
|
|
|
debug: Record<string, any> = {};
|
|
|
|
|
|
|
|
drawHud(drawSet: DrawSet) {
|
|
|
|
drawSet.queue(this.hudLayer.toRender((cx, dt) => {
|
|
|
|
cx.font = "16px monospace";
|
|
|
|
cx.textAlign = "left";
|
|
|
|
cx.textBaseline = "middle";
|
|
|
|
|
|
|
|
const score = `Score: ${this.score}`;
|
|
|
|
cx.fillStyle = "#000";
|
|
|
|
cx.fillText(score, this.width/3 + 1, this.height - 18 + 1, this.width/4);
|
|
|
|
cx.fillStyle = "#0ff";
|
|
|
|
cx.fillText(score, this.width/3, this.height - 18, this.width/4);
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Data extends EcsData {
|
2020-01-16 03:49:52 +00:00
|
|
|
boss: SparseStore<Boss> = {};
|
|
|
|
bullet: SparseStore<Bullet> = {};
|
|
|
|
hp: SparseStore<Hp> = {};
|
|
|
|
lifetime: SparseStore<Lifetime> = {};
|
|
|
|
message: SparseStore<Message> = {};
|
2020-02-16 04:17:13 +00:00
|
|
|
|
|
|
|
constructor(source?: Partial<Data>) {
|
|
|
|
super(source);
|
|
|
|
if(source?.boss) this.boss = copy(source.boss);
|
|
|
|
if(source?.bullet) this.bullet = copy(source.bullet);
|
|
|
|
if(source?.hp) this.hp = copy(source.hp);
|
|
|
|
if(source?.lifetime) this.lifetime = copy(source.lifetime);
|
|
|
|
if(source?.message) this.message = copy(source.message);
|
|
|
|
}
|
2019-12-14 23:11:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export enum Teams {
|
|
|
|
PLAYER,
|
|
|
|
ENEMY
|
|
|
|
}
|
|
|
|
export class Bullet {
|
|
|
|
hit = false;
|
|
|
|
constructor(
|
|
|
|
public team: Teams,
|
|
|
|
public attack: number
|
|
|
|
) {};
|
|
|
|
}
|
|
|
|
export class Hp {
|
|
|
|
receivedDamage = 0;
|
|
|
|
constructor(
|
|
|
|
public team: Teams,
|
|
|
|
public hp: number
|
|
|
|
) {};
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Lifetime {
|
|
|
|
constructor(
|
|
|
|
public time: number
|
|
|
|
) {};
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Boss {
|
|
|
|
constructor(
|
|
|
|
public name: string
|
|
|
|
) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Message {
|
|
|
|
targetY = 0;
|
|
|
|
constructor(
|
|
|
|
public layer: Layer,
|
|
|
|
public color: string,
|
|
|
|
public message: string,
|
|
|
|
public timeout = 3
|
|
|
|
) {}
|
|
|
|
}
|
2020-02-16 05:00:26 +00:00
|
|
|
|
|
|
|
export class Engine implements LockstepProcessor<KeyName[], Data> {
|
|
|
|
cloneState(old: Data) {
|
|
|
|
return new Data(old);
|
|
|
|
}
|
|
|
|
|
|
|
|
compareInput(a: KeyName[], b: KeyName[]): boolean {
|
|
|
|
if (a.length != b.length) return false;
|
|
|
|
|
|
|
|
let matches = true;
|
|
|
|
a.forEach((keyA, i) => {
|
|
|
|
if (keyA != b[i]) {
|
|
|
|
matches = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
|
|
|
advanceState(state: Data, input: KeyName[]) {
|
|
|
|
DumbMotion(state, INPUT_FREQUENCY);
|
|
|
|
}
|
|
|
|
}
|