Compare commits

...

10 Commits

5 changed files with 130 additions and 74 deletions

View File

@ -1,12 +1,14 @@
import * as world from "./world.mjs";
const BOX_SIZE = world.BOX_SIZE;
const entities = [];
export function create (x, y, color = "#fff") {
export function create (box, x = 0, y = 0, color = "#fff") {
const entity = {
id: entities.length,
box,
x,
y,
color,
@ -25,3 +27,37 @@ export function for_each (callback) {
callback(entity, entity.id);
});
}
function clamp_pos (entity) {
if (entity.x < 0) entity.x = 0;
if (entity.x >= BOX_SIZE) entity.x = BOX_SIZE - 1;
if (entity.y < 0) entity.y = 0;
if (entity.y >= BOX_SIZE) entity.y = BOX_SIZE - 1;
return entity;
}
export function set_pos (entity, x, y) {
entity.x = x;
entity.y = y;
if (out_of_bounds(entity)) {
if (entity.box.parent) {
entity.box = entity.box.parent;
// TODO: exit from side of box.
}
clamp_pos(entity);
}
if (world.get_tile(entity.box, entity.x, entity.y)?.type === "box") {
entity.box = world.get_tile(entity.box, entity.x, entity.y).box;
// TODO: enter at edge of box.
}
}
export function move (entity, d_x, d_y) {
set_pos(entity, entity.x + d_x, entity.y + d_y);
}
function out_of_bounds (entity) {
return world.out_of_bounds(entity.x, entity.y);
}

View File

@ -3,6 +3,7 @@ export * from "./graphics_core.mjs";
import { range, iter_2d } from "./utils/range.mjs";
import * as entity from "./entity.mjs";
import * as world from "./world.mjs";
@ -10,45 +11,60 @@ const canvas = document.getElementById("canvas");
const canvas_context = canvas.getContext("2d");
graphics.set_size(canvas.width, canvas.height);
const SCALE = 81;
const BASE_SCALE = 81;
export function draw_tile (x, y) {
graphics.fill_rect(x * SCALE, y * SCALE, SCALE, SCALE);
export function draw_tile (x, y, size = BASE_SCALE) {
graphics.fill_rect(x, y, size, size);
}
function checkerboard (x, y) {
return ((x ^ y) & 1) === 0;
}
export function draw_floor (box_size) {
graphics.clear("#888");
graphics.set_color("#aaa");
iter_2d(range(0, box_size - 1), (x, y) => {
if (checkerboard(x, y)) draw_tile(x, y);
export function draw_floor (start_x, start_y, scale, invert = false) {
iter_2d(range(0, world.BOX_SIZE - 1), (x, y) => {
if (checkerboard(x, y) !== invert) {
graphics.set_color("#aaa");
} else {
graphics.set_color("#888");
}
const [visual_x, visual_y] = project_pos(x, y, scale);
draw_tile(start_x + visual_x, start_y + visual_y, scale);
});
}
export function draw_world (box_size, get_tile) {
draw_floor(box_size);
export function draw_world (box, start_x = 0, start_y = 0, scale = BASE_SCALE) {
draw_floor(start_x, start_y, scale, !checkerboard(start_x, start_y));
iter_2d(range(0, box_size - 1), (x, y) => {
const tile = get_tile(x, y);
iter_2d(range(0, world.BOX_SIZE - 1), (x, y) => {
const tile = world.get_tile(box, x, y);
const [visual_x, visual_y] = project_pos(x, y, scale);
switch (tile.type) {
case "box": {
graphics.set_color("#ff0");
draw_tile(x, y);
draw_world(tile.box, start_x + visual_x, start_y + visual_y, scale / world.BOX_SIZE);
break;
}
case "paint": {
graphics.set_color(tile.color);
draw_tile(start_x + visual_x, start_y + visual_y, scale);
break;
}
}
});
entity.for_each((entity, id) => {
if (entity.box !== box) return;
graphics.set_color(entity.color);
draw_tile(entity.x, entity.y);
const [visual_x, visual_y] = project_pos(entity.x, entity.y, scale);
draw_tile(start_x + visual_x, start_y + visual_y, scale);
});
}
function project_pos (x, y, scale) {
return [x, y].map(a => a * scale);
}
export function render () {
graphics.render(canvas_context);
}

View File

@ -1,12 +1,12 @@
import * as graphics from "./graphics.mjs";
import { BOX_SIZE, get_tile } from "./world.mjs";
import "./player.mjs";
import { get_player_box } from "./player.mjs";
function render (timestamp) {
graphics.clear();
graphics.draw_world(BOX_SIZE, get_tile);
graphics.draw_world(get_player_box());
graphics.render();
requestAnimationFrame(render);

View File

@ -4,59 +4,62 @@ import * as entity from "./entity.mjs";
const player = entity.create(2, 1, "#0f0");
world.set_tile(player.x, player.y, { type: "player" });
const player = entity.create(world.get_root(), 2, 1, "#080");
function out_of_bounds (x, y) {
return x < 0 || x >= world.BOX_SIZE ||
y < 0 || y >= world.BOX_SIZE
}
// TODO: clean up this by making tile movement be performed by the world code.
export function set_player_pos (x, y) {
const previous_x = player.x;
const previous_y = player.y;
if (out_of_bounds(x, y)) {
const could_exit = world.exit_box();
if (could_exit) {
set_player_pos(0, 0);
} else {
set_player_pos(previous_x, previous_y);
}
return;
}
if (world.get_tile(x, y)?.type === "box") {
world.enter_box(world.get_tile(x, y).box);
set_player_pos(world.CENTER, world.CENTER);
return;
}
player.x = x;
player.y = y;
}
export function move_player (d_x, d_y) {
set_player_pos(player.x + d_x, player.y + d_y);
export function move (d_x, d_y) {
entity.move(player, d_x, d_y);
}
on_press("ArrowLeft", _ => {
move_player(-1, 0);
move(-1, 0);
});
on_press("ArrowRight", _ => {
move_player(1, 0);
move(1, 0);
});
on_press("ArrowUp", _ => {
move_player(0, -1);
move(0, -1);
});
on_press("ArrowDown", _ => {
move_player(0, 1);
move(0, 1);
});
on_press(" ", _ => {
world.set_tile(player.x + 1, player.y, {
world.set_tile(player.box, player.x, player.y, {
type: "box",
box: world.create_box(),
box: world.create_box(player.box),
});
});
function clear_tile (x, y) {
world.set_tile(player.box, x, y, {
type: "empty",
});
}
on_press("Backspace", _ => {
clear_tile(player.x, player.y);
clear_tile(player.x + 1, player.y);
clear_tile(player.x - 1, player.y);
});
export function get_player_box () {
return player.box;
}
function set_painting_key (key, color) {
on_press(key, _ => {
world.set_tile(player.box, player.x, player.y, {
type: "paint",
color,
});
});
}
set_painting_key("q", "#f00");
set_painting_key("w", "#0f0");
set_painting_key("e", "#00f");
set_painting_key("a", "#ff0");
set_painting_key("s", "#0ff");
set_painting_key("d", "#f0f");

View File

@ -4,33 +4,34 @@ export const CENTER = Math.floor(BOX_SIZE / 2);
function create_world (size) {
return Array(BOX_SIZE).fill(0).map(_ => Array(BOX_SIZE).fill(0));
return {
tiles: Array(BOX_SIZE).fill(0).map(_ => Array(BOX_SIZE).fill(0)),
};
}
const world = create_world(BOX_SIZE);
const parent_stack = [];
let current_world = world;
export function set_tile (x, y, tile) {
current_world[x][y] = tile;
export function set_tile (world, x, y, tile) {
if (out_of_bounds(x, y)) return;
world.tiles[x][y] = tile;
}
export function get_tile (x, y) {
return current_world[x][y];
export function get_tile (world, x, y) {
if (out_of_bounds(x, y)) return { type: "out_of_bounds" };
return world.tiles[x][y];
}
export function create_box () {
return create_world(BOX_SIZE);
export function create_box (parent) {
return {
...create_world(BOX_SIZE),
parent,
};
}
export function enter_box (box) {
parent_stack.push(current_world);
current_world = box;
export function get_root () {
return world;
}
export function exit_box () {
if (parent_stack.length > 0) {
current_world = parent_stack.pop();
return true;
}
return false;
export function out_of_bounds (x, y) {
return x < 0 || x >= BOX_SIZE ||
y < 0 || y >= BOX_SIZE
}