151 lines
4.2 KiB
TypeScript
151 lines
4.2 KiB
TypeScript
|
|
import { Layer, SpriteSheet } from "../applet/Render";
|
|
import { Component, copyDense, copySparse, EntityState, StateForSchema, Store } from "./Data";
|
|
|
|
/**
|
|
* Nominal type used to remind users to enforce some level
|
|
* of quantization on numbers kept in game state. This reduces
|
|
* physics precision in theory, but lets us feel more comfortable
|
|
* about determinism for rollback physics.
|
|
*/
|
|
export type FixedPoint = number & {_type: "fixed point"};
|
|
|
|
export function Floor(x: number): FixedPoint {
|
|
return Math.floor(x) as FixedPoint;
|
|
}
|
|
|
|
export class Box {
|
|
constructor(
|
|
public x: FixedPoint, public y: FixedPoint,
|
|
public w: FixedPoint, public h: FixedPoint
|
|
) { };
|
|
};
|
|
|
|
/**
|
|
* Return source moved towards target by speed, without going past.
|
|
*/
|
|
export function Approach(source: number, target: number, speed: number): number {
|
|
const delta = target - source;
|
|
if (Math.abs(delta) <= speed) {
|
|
return target;
|
|
} else {
|
|
return source + Math.sign(delta) * speed;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pairs of vertex coordinates in ccw winding order
|
|
*/
|
|
export interface Polygon {
|
|
points: FixedPoint[];
|
|
}
|
|
export class PolygonComponent extends Component<PolygonComponent> {
|
|
points: FixedPoint[];
|
|
constructor(from: Partial<PolygonComponent>) {
|
|
super(from);
|
|
this.points = from.points?.slice() ?? []
|
|
};
|
|
clone() {
|
|
return new PolygonComponent(this);
|
|
}
|
|
}
|
|
|
|
export class Location extends Component<Location> {
|
|
X: FixedPoint;
|
|
Y: FixedPoint;
|
|
Angle: number;
|
|
VX: number;
|
|
VY: number;
|
|
VAngle: number;
|
|
constructor(from: Partial<Location>) {
|
|
super(from);
|
|
this.X = from.X ?? (0 as FixedPoint);
|
|
this.Y = from.Y ?? (0 as FixedPoint);
|
|
this.Angle = from.Angle ?? 0;
|
|
this.VX = from.VX ?? 0;
|
|
this.VY = from.VY ?? 0;
|
|
this.VAngle = from.VAngle ?? 0;
|
|
};
|
|
clone() {
|
|
return new Location(this);
|
|
}
|
|
}
|
|
|
|
export class CollisionClass extends Component<CollisionClass> {
|
|
public name: string;
|
|
constructor(from: Partial<CollisionClass>) {
|
|
super(from);
|
|
this.name = from.name ?? "unknown";
|
|
};
|
|
clone() {
|
|
return new CollisionClass(this);
|
|
}
|
|
}
|
|
|
|
export class RenderBounds extends Component<RenderBounds> {
|
|
public color: string;
|
|
public layer: number;
|
|
constructor(from: Partial<RenderBounds>) {
|
|
super(from);
|
|
this.color = from.color ?? "#f00";
|
|
this.layer = from.layer ?? 1;
|
|
};
|
|
clone() {
|
|
return new RenderBounds(this);
|
|
}
|
|
};
|
|
|
|
export class RenderSprite extends Component<RenderSprite> {
|
|
// TODO: make this an id/handle for serializability
|
|
public sheet: SpriteSheet;
|
|
public layer: number;
|
|
public index: number;
|
|
public offsetX: number;
|
|
public offsetY: number;
|
|
constructor(from: Partial<RenderSprite> & { sheet: SpriteSheet }) {
|
|
super(from);
|
|
this.sheet = from.sheet;
|
|
this.layer = from.layer ?? 1;
|
|
this.index = from.index ?? 0;
|
|
this.offsetX = from.offsetX ?? 0;
|
|
this.offsetY = from.offsetY ?? 0;
|
|
};
|
|
clone() {
|
|
return new RenderSprite(this);
|
|
}
|
|
};
|
|
|
|
export interface ComponentSchema {
|
|
location: Location;
|
|
bounds: PolygonComponent;
|
|
renderBounds: RenderBounds;
|
|
renderSprite: RenderSprite;
|
|
collisionSourceClass: CollisionClass;
|
|
collisionTargetClass: CollisionClass;
|
|
}
|
|
|
|
export class Data implements StateForSchema<ComponentSchema> {
|
|
entity: EntityState[];
|
|
|
|
location: Store<Location>;
|
|
bounds: Store<PolygonComponent>;
|
|
renderBounds: Store<RenderBounds>;
|
|
renderSprite: Store<RenderSprite>;
|
|
collisionSourceClass: Store<CollisionClass>;
|
|
collisionTargetClass: Store<CollisionClass>;
|
|
|
|
layers: Layer[] = [new Layer(0), new Layer(1)];
|
|
|
|
constructor(from: Partial<Data>) {
|
|
this.entity = copyDense(from.entity);
|
|
this.location = copyDense(from.location);
|
|
this.bounds = copyDense(from.bounds);
|
|
this.renderBounds = copySparse(from.renderBounds);
|
|
this.renderSprite = copySparse(from.renderSprite);
|
|
this.collisionSourceClass = copySparse(from.collisionSourceClass);
|
|
this.collisionTargetClass = copySparse(from.collisionTargetClass);
|
|
}
|
|
clone() {
|
|
return new Data(this);
|
|
}
|
|
}
|