diff --git a/game/actor.ts b/game/actor.ts deleted file mode 100644 index 8785dbec..00000000 --- a/game/actor.ts +++ /dev/null @@ -1,99 +0,0 @@ -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 d5f19c05..5cbf622c 100644 --- a/game/main.ts +++ b/game/main.ts @@ -1,12 +1,16 @@ -import { cls, print } from "./graphics"; +import { cls, print, spr, use_texture, Texture } from "./graphics"; +import { load_texture } from "./assets"; import { since_start } from "./time"; -import { new_v2 } from "./vector"; +import { btn, Button } from "./input"; +import { new_v2, vadd, vmul, vnorm } from "./vector"; import { load_world, World, Level, draw_level } from "./level"; -import { Actor, Robo } from "./actor"; + +/// TODO: Support reload by saving and restoring state from init/update/restore. /// A nice looping frame counter. let clock = 0; +let bot_sprite: Texture | undefined = undefined; let world: World | undefined = undefined; let level: Level | undefined = undefined; @@ -14,8 +18,15 @@ 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; @@ -25,56 +36,106 @@ 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([map_load]).then(() => { + Promise.all([texture_load, map_load]).then(() => { + loaded = true; 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() {} +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 update() { clock = (clock + 1) % 20160; - for (const actor of actors) { - actor.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); + 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_physics(); - } + // Motion + let np = vadd(robo_pos, v); - // TODO: Bonks - // TODO: Transients + // TODO: Collide. + + robo_pos = np; + robo_vel = v; } +// 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); } - for (const actor of actors) { - actor.draw(clock); - } + 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); + } // print("FRAME TIME:", since_last_frame()); } diff --git a/src/assets.ts b/src/assets.ts index 2e572bae..b375d450 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -1,14 +1,7 @@ import * as io from "./io.ts"; import * as gfx from "./graphics.ts"; -const LOADED_TEXTURES = new Map>(); - -export function load_texture(path: string): Promise { - let promise = LOADED_TEXTURES.get(path); - if (!promise) { - promise = io.load(path).then((buffer) => gfx.create_texture(buffer, path)); - LOADED_TEXTURES.set(path, promise); - } - - return promise; +export async function load_texture(path: string): Promise { + const buffer = await io.load(path); + return gfx.create_texture(buffer, path); }