Compare commits
4 Commits
13c68b7d9d
...
45502c4249
Author | SHA1 | Date |
---|---|---|
transoptimal | 45502c4249 | |
transoptimal | de041fdf15 | |
transoptimal | 87042b0777 | |
transoptimal | a94f866241 |
|
@ -12,6 +12,8 @@ there's a demo video at `/assets/demo_video.webm`.
|
|||
|
||||
- arrow keys: move around.
|
||||
- QWEASD: paint with RGBYCM colors, respectively.
|
||||
- ZX: paint with background colors.
|
||||
- CV: paint with black and white.
|
||||
- space: create box.
|
||||
- backspace: delete/clear/empty tiles to your left and right, and where you're standing.
|
||||
|
||||
|
|
|
@ -11,4 +11,8 @@
|
|||
|
||||
<body>
|
||||
<canvas id="canvas" width="729" height="729"></canvas>
|
||||
<br>
|
||||
<button id="save">save game to file</button>
|
||||
<button id="load">load game from file</button>
|
||||
<button id="restart">create new world</button>
|
||||
</body>
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import * as graphics from "./graphics.mjs";
|
||||
import { BOX_SIZE, get_tile } from "./world.mjs";
|
||||
import { get_player_box } from "./player.mjs";
|
||||
import { save_in_browser, load_from_browser } from "./save_load.mjs";
|
||||
|
||||
|
||||
|
||||
load_from_browser();
|
||||
|
||||
function render (timestamp) {
|
||||
graphics.clear();
|
||||
graphics.draw_world(get_player_box());
|
||||
|
@ -13,3 +16,9 @@ function render (timestamp) {
|
|||
}
|
||||
|
||||
requestAnimationFrame(render);
|
||||
|
||||
function autosave () {
|
||||
save_in_browser();
|
||||
}
|
||||
|
||||
const autosave_interval = setInterval(autosave, 3000);
|
||||
|
|
|
@ -57,9 +57,17 @@ function set_painting_key (key, color) {
|
|||
});
|
||||
}
|
||||
|
||||
// RGB
|
||||
set_painting_key("q", "#f00");
|
||||
set_painting_key("w", "#0f0");
|
||||
set_painting_key("e", "#00f");
|
||||
// YCM
|
||||
set_painting_key("a", "#ff0");
|
||||
set_painting_key("s", "#0ff");
|
||||
set_painting_key("d", "#f0f");
|
||||
// background colors
|
||||
set_painting_key("z", "#888");
|
||||
set_painting_key("x", "#aaa");
|
||||
// black and white
|
||||
set_painting_key("c", "#000");
|
||||
set_painting_key("v", "#fff");
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import * as world from "./world.mjs";
|
||||
|
||||
|
||||
|
||||
const DEFAULT_SAVE_NAME = "boxes_save_0";
|
||||
|
||||
const get_elem = id => document.getElementById(id);
|
||||
const create_elem = type => document.createElement(type);
|
||||
|
||||
const save_button = get_elem("save");
|
||||
const load_button = get_elem("load");
|
||||
const restart_button = get_elem("restart");
|
||||
|
||||
save_button.onclick = _ => {
|
||||
const timestamp = new Date().getTime();
|
||||
const filename = `boxes_${timestamp}.boxes`;
|
||||
|
||||
save_text(filename, generate_save_data());
|
||||
};
|
||||
|
||||
function generate_save_data () {
|
||||
const root = world.get_root();
|
||||
return btoa(JSON.stringify(root));
|
||||
}
|
||||
|
||||
function save_text (filename, text) {
|
||||
const link = create_elem("a");
|
||||
const file = new Blob([text], { type: "text/plain" });
|
||||
link.href = URL.createObjectURL(file);
|
||||
link.download = filename;
|
||||
link.click();
|
||||
}
|
||||
|
||||
load_button.onclick = _ => {
|
||||
const input = create_elem("input");
|
||||
input.type = "file";
|
||||
input.addEventListener("change", e => {
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => load_from_text(e.target.result);
|
||||
reader.onerror = error => alert("couldn't load the file.");
|
||||
reader.readAsText(file);
|
||||
});
|
||||
input.click();
|
||||
}
|
||||
|
||||
function load_from_text (text) {
|
||||
const data = JSON.parse(atob(text));
|
||||
world.replace_root(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function save_in_browser (save_name = DEFAULT_SAVE_NAME) {
|
||||
const data = generate_save_data();
|
||||
localStorage.setItem(save_name, data);
|
||||
}
|
||||
|
||||
export function load_from_browser (save_name = DEFAULT_SAVE_NAME) {
|
||||
if (localStorage.getItem(save_name) === null) return;
|
||||
const data = localStorage.getItem(save_name);
|
||||
load_from_text(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
restart_button.onclick = _ => {
|
||||
if (confirm("are you sure you want to create a new world? this will delete any unsaved creations.")) {
|
||||
world.replace_root(world.create_new());
|
||||
}
|
||||
};
|
15
js/world.mjs
15
js/world.mjs
|
@ -1,3 +1,5 @@
|
|||
import { iter_2d, range } from "./utils/range.mjs";
|
||||
|
||||
export const BOX_SIZE = 9;
|
||||
export const CENTER = Math.floor(BOX_SIZE / 2);
|
||||
|
||||
|
@ -5,11 +7,15 @@ export const CENTER = Math.floor(BOX_SIZE / 2);
|
|||
|
||||
function create_world (size) {
|
||||
return {
|
||||
tiles: Array(BOX_SIZE).fill(0).map(_ => Array(BOX_SIZE).fill(0)),
|
||||
tiles: Array(size).fill(0).map(_ => Array(size).fill(0)),
|
||||
};
|
||||
}
|
||||
const world = create_world(BOX_SIZE);
|
||||
|
||||
export function create_new () {
|
||||
return create_world(BOX_SIZE);
|
||||
}
|
||||
|
||||
export function set_tile (world, x, y, tile) {
|
||||
if (out_of_bounds(x, y)) return;
|
||||
world.tiles[x][y] = tile;
|
||||
|
@ -35,3 +41,10 @@ export function out_of_bounds (x, y) {
|
|||
return x < 0 || x >= BOX_SIZE ||
|
||||
y < 0 || y >= BOX_SIZE
|
||||
}
|
||||
|
||||
export function replace_root (new_world) {
|
||||
// maybe unnecessary to go over each tile instead of just each column.
|
||||
iter_2d(range(0, BOX_SIZE - 1), (x, y) => {
|
||||
set_tile(world, x, y, get_tile(new_world, x, y));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue