diff --git a/game/actor.ts b/game/actor.ts new file mode 100644 index 00000000..8785dbec --- /dev/null +++ b/game/actor.ts @@ -0,0 +1,99 @@ +import { load_texture } from "./assets"; +import { btn, Button } from "./input"; +import { Vec2, new_v2, vadd, vnorm, vmul } from "./vector"; +import { spr, use_texture, Texture } from "./graphics"; + +export class Actor { + velocity: Vec2 = new_v2(0); + friction: number = 0.6; + id: string; + position: Vec2; + bounds: Vec2; + + constructor(id: string, position: Vec2, bounds: Vec2) { + this.id = id; + this.position = position; + this.bounds = bounds; + } + + update() {} + + update_physics() { + const velocity = vmul(this.velocity, this.friction); + // Zero if we're smaller than some epsilon. + if (Math.abs(velocity.x) < 0.01) { + velocity.x = 0; + } + if (Math.abs(velocity.y) < 0.01) { + velocity.y = 0; + } + + const new_position = vadd(this.position, velocity); + + // TODO: Collision detection + // const { w, h } = this.bounds; + + this.velocity = velocity; + this.position = new_position; + } + + draw(_clock: number) {} + + bonk(_other: Actor) {} +} + +const robo_info = { + width: 32, + height: 32, + sprite: "./bot.png", + animations: [ + { start: 0, length: 1, speed: 20 }, + { start: 1, length: 4, speed: 8 }, + ], +}; + +export class Robo extends Actor { + bot_sprite: Texture | undefined = undefined; + + constructor(pos: Vec2) { + super("robo", pos, new_v2(robo_info.width, robo_info.height)); + load_texture(robo_info.sprite).then((texture) => { + this.bot_sprite = texture; + }); + } + + update() { + // Acceleration from input + let a = new_v2(0); + if (btn(Button.Up)) { + a.y -= 1; + } + if (btn(Button.Down)) { + a.y += 1; + } + if (btn(Button.Left)) { + a.x -= 1; + } + if (btn(Button.Right)) { + a.x += 1; + } + vnorm(a); + this.velocity = vadd(this.velocity, vmul(a, 1.5)); + } + + draw(clock: number) { + if (this.bot_sprite != undefined) { + use_texture(this.bot_sprite); + + const vel = this.velocity; + const moving = vel.x != 0 || vel.y != 0; + const anim = robo_info.animations[moving ? 1 : 0]; + + const { x: w, y: h } = this.bounds; + const { x, y } = this.position; + + const frame = (anim.start + ((clock / anim.speed) % anim.length)) >> 0; + spr(x, y, w, h, frame * w, 0, 32, 32); + } + } +} diff --git a/game/main.ts b/game/main.ts index 5cbf622c..d5f19c05 100644 --- a/game/main.ts +++ b/game/main.ts @@ -1,16 +1,12 @@ -import { cls, print, spr, use_texture, Texture } from "./graphics"; -import { load_texture } from "./assets"; +import { cls, print } from "./graphics"; import { since_start } from "./time"; -import { btn, Button } from "./input"; -import { new_v2, vadd, vmul, vnorm } from "./vector"; +import { new_v2 } from "./vector"; import { load_world, World, Level, draw_level } from "./level"; - -/// TODO: Support reload by saving and restoring state from init/update/restore. +import { Actor, Robo } from "./actor"; /// A nice looping frame counter. let clock = 0; -let bot_sprite: Texture | undefined = undefined; let world: World | undefined = undefined; let level: Level | undefined = undefined; @@ -18,15 +14,8 @@ let level: Level | undefined = undefined; // zelda screen is 16x11 tiles // from a feeling point of view this is sufficient, apparently :D -let loaded = false; - function load_assets() { // Start this load, but then... - let texture_load = load_texture("./bot.png").then((n) => { - print("Bot loaded at", since_start()); - bot_sprite = n; - }); - let map_load = load_world("./overworld.ldtk").then((w) => { print("World loaded at", since_start()); world = w; @@ -36,106 +25,56 @@ function load_assets() { if (!level) { throw new Error("UNABLE TO FIND LEVEL AT 0,0: CANNOT START"); } + + // TODO: SPAWN ACTORS BASED ON LEVEL. }); - Promise.all([texture_load, map_load]).then(() => { - loaded = true; + Promise.all([map_load]).then(() => { print("All are loaded."); }); } +const actors: Actor[] = []; + +// TODO: Build a system whereby the signatures of the fundamental functions can be checked. + export function init() { print("Hello world!"); load_assets(); + actors.push(new Robo(new_v2(10, 10))); } export function suspend() { return { clock }; } -export function resume(snapshot) { - if (snapshot) { - print("!Resuming!"); - const { clock: new_clock } = snapshot; - if (new_clock) { - clock = new_clock; - } - } -} - -const friction = 0.6; -let robo_vel = new_v2(0); -let robo_pos = new_v2(10); +export function resume() {} export function update() { clock = (clock + 1) % 20160; - // Acceleration from input - let a = new_v2(0); - if (btn(Button.Up)) { - a.y -= 1; - } - if (btn(Button.Down)) { - a.y += 1; - } - if (btn(Button.Left)) { - a.x -= 1; - } - if (btn(Button.Right)) { - a.x += 1; - } - vnorm(a); - a = vmul(a, 0.8); // Speed. - - // Friction - let v = vmul(robo_vel, friction); - v = vadd(v, a); - - // Zero if we're smaller than some epsilon. - if (Math.abs(v.x) < 0.01) { - v.x = 0; - } - if (Math.abs(v.y) < 0.01) { - v.y = 0; + for (const actor of actors) { + actor.update(); } - // Motion - let np = vadd(robo_pos, v); + for (const actor of actors) { + actor.update_physics(); + } - // TODO: Collide. - - robo_pos = np; - robo_vel = v; + // TODO: Bonks + // TODO: Transients } -// GARBAGE OBVIOUSLY -const robo_info = { - width: 32, - height: 32, - animations: [ - { start: 0, length: 1, speed: 20 }, - { start: 1, length: 4, speed: 8 }, - ], -}; - export function draw() { cls(0.1, 0.2, 0.3); - if (!loaded) { - return; - } if (level != undefined) { draw_level(level, 0, 0); } - if (bot_sprite != undefined) { - // ...it gets resolved here? - use_texture(bot_sprite); - - const moving = robo_vel.x != 0 || robo_vel.y != 0; - const anim = robo_info.animations[moving ? 1 : 0]; - const frame = (anim.start + ((clock / anim.speed) % anim.length)) >> 0; - spr(robo_pos.x, robo_pos.y, 32, 32, frame * robo_info.width, 0, 32, 32); + for (const actor of actors) { + actor.draw(clock); } + // print("FRAME TIME:", since_last_frame()); }