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 { points: FixedPoint[]; constructor(from: Partial) { super(from); this.points = from.points?.slice() ?? [] }; clone() { return new PolygonComponent(this); } } export class Location extends Component { X: FixedPoint; Y: FixedPoint; Angle: number; VX: number; VY: number; VAngle: number; constructor(from: Partial) { 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 { public name: string; constructor(from: Partial) { super(from); this.name = from.name ?? "unknown"; }; clone() { return new CollisionClass(this); } } export class RenderBounds extends Component { public color: string; public layer: number; constructor(from: Partial) { super(from); this.color = from.color ?? "#f00"; this.layer = from.layer ?? 1; }; clone() { return new RenderBounds(this); } }; export class RenderSprite extends Component { // 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 & { 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 { entity: EntityState[]; location: Store; bounds: Store; renderBounds: Store; renderSprite: Store; collisionSourceClass: Store; collisionTargetClass: Store; layers: Layer[] = [new Layer(0), new Layer(1)]; constructor(from: Partial) { 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); } }