import { ComponentType, PositionComponent, TrailingPositionComponent, SpriteSheetComponent, SpriteSpec, } from "./component"; import { Game } from "./game"; import { System, SystemType } from "./system"; import { drawLaserPen } from "laser-pen"; export class RenderSystem extends System { constructor(private readonly canvas: HTMLCanvasElement) { super(SystemType.RENDER); } public set_world_dimensions(width: number, height: number) { this.canvas.width = width; this.canvas.height = height; } public update(dt: number, game: Game) { const ctx = this.canvas.getContext("2d"); if (ctx === null) return; ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); game.for_each_entity_with_component(ComponentType.RENDERABLE, (entity) => { if (ComponentType.TRAILING_POSITION in entity.components) { const trailing_position = entity.components[ ComponentType.TRAILING_POSITION ] as TrailingPositionComponent; if (trailing_position.trails.length < 3) return; drawLaserPen(ctx, trailing_position.trails); return; } if ( ComponentType.POSITION in entity.components && ComponentType.SPRITESHEET in entity.components ) { const position = entity.components[ ComponentType.POSITION ] as PositionComponent; const spritesheet = entity.components[ ComponentType.SPRITESHEET ] as SpriteSheetComponent; if (typeof spritesheet.sheet === "undefined") { const img = new Image(); img.src = spritesheet.source; spritesheet.sheet = img; } const spritespec = spritesheet.state_to_spritespec[spritesheet.current_state]; this.blit_sprite(ctx, spritesheet, position); spritesheet.last_update += dt; if (spritesheet.last_update > spritespec.ms_per_frame) { spritesheet.current_frame++; spritesheet.current_frame %= spritespec.frames; spritesheet.last_update = 0; } return; } }); } private blit_sprite( ctx: CanvasRenderingContext2D, spritesheet: SpriteSheetComponent, position: { x: number; y: number }, ) { ctx.save(); ctx.translate(position.x, position.y); ctx.translate(-position.x, -position.y); if (typeof spritesheet.sheet === "undefined") return; const spritespec = spritesheet.state_to_spritespec[spritesheet.current_state]; ctx.drawImage( spritesheet.sheet, ...this.get_sprite_args(spritesheet.current_frame, spritespec), ...this.get_draw_args(spritespec, position), ); ctx.restore(); } private get_sprite_args( current_frame: number, sprite_spec: SpriteSpec, ): [sx: number, sy: number, sw: number, sh: number] { const [width, height] = this.get_dimensions(sprite_spec); return [ sprite_spec.top_x, sprite_spec.top_y + current_frame * height, width, height, ]; } private get_draw_args( sprite_spec: SpriteSpec, position: { x: number; y: number }, ): [dx: number, dy: number, dw: number, dh: number] { const [width, height] = this.get_dimensions(sprite_spec); return [position.x - width / 2, position.y - height / 2, width, height]; } private get_dimensions(sprite_spec: SpriteSpec): [number, number] { return [ sprite_spec.end_x - sprite_spec.top_x, (sprite_spec.end_y - sprite_spec.top_y) / sprite_spec.frames, ]; } }