rework level loading & restarting, and player death.

level loading is now based on events; the level loading code doesn't
know anything about how things are loaded, it just tells things to load.

player death is also now based on events; instead of directly restarting
the level, it simply sends out an event that the level handling code
listens for.
This commit is contained in:
trans_soup 2023-10-27 12:32:02 +02:00
parent 92fdab8fbd
commit b51b776137
5 changed files with 108 additions and 28 deletions

View file

@ -1,6 +1,9 @@
import * as entity from "./entity.mjs";
import { register_tick } from "./tick.mjs";
import * as behaviours from "./behaviours/main.mjs";
import * as Entity from "./entity.mjs";
import * as Event from "./event.mjs";
@ -47,6 +50,26 @@ export function add (enemy) {
enemies.push(enemy);
}
export function remove_all () {
function remove_all () {
enemies.splice(0, enemies.length);
}
Event.listen("level/unload", _ => {
remove_all();
});
Event.listen("level/load/enemy", enemies => {
for (const data of enemies) {
const {pos, size} = data;
const enemy = create(pos[0], pos[1], size[0], size[1]);
set_behaviour(enemy, data.behaviour);
const meta = Entity.get_meta(enemy);
for (const [key, value] of data.meta) {
meta.set(key, value);
}
add(enemy);
}
})

48
js/event.mjs Normal file
View file

@ -0,0 +1,48 @@
const registered_listeners = new Map();
export function trigger (event_name, data) {
for (const callback of listeners(event_name)) {
callback(data);
}
}
export function listen (event_name, callback) {
listeners(event_name).push(callback);
// to find an event later, for deletion or other things.
return [event_name, listeners(event_name).length - 1];
}
// makes a callback function stop being used to listen to an event, and returns that callback function.
export function ignore (listener_id) {
const [event_name, index] = listener_id;
return listeners(event_name).splice(index, 1)[0];
}
function listeners (event_name) {
if (!registered_listeners.has(event_name))
registered_listeners.set(event_name, []);
return registered_listeners.get(event_name);
}
export function attach_listener (target, event_name, callback) {
return listen(event_name, data => {
if (data.target === target) {
callback(data);
}
});
}
export function create_alias (event_name, alias) {
// note that aliases are one-directional; `event_name` isn't triggered when `alias` is.
return listen(event_name, data => trigger(alias, data));
}
export function create_group (group_name, members) {
// TODO: return list of event id:s so groups can be `ignore`:d.
for (const event_name of members) {
create_alias(event_name, group_name);
}
}

View file

@ -1,33 +1,27 @@
import * as Entity from "./entity.mjs";
import * as Enemy from "./enemy.mjs";
import * as Player from "./player.mjs";
import * as Levels from "./levels/main.mjs";
import * as Event from "./event.mjs";
const current = Levels.get_level(1);
export function load (data) {
Enemy.remove_all();
Player.set_pos(0, 0);
let last_loaded;
const {enemies} = current;
export function load (name) {
last_loaded = name;
for (const data of enemies) {
const {pos, size} = data;
const enemy = Enemy.create(pos[0], pos[1], size[0], size[1]);
Event.trigger("level/unload");
Enemy.set_behaviour(enemy, data.behaviour);
const level = Levels.get_level(name);
const meta = Entity.get_meta(enemy);
for (const [key, value] of data.meta) {
meta.set(key, value);
}
Enemy.add(enemy);
}
Event.trigger("level/load/player", level.player);
Event.trigger("level/load/enemy", level.enemies);
}
export function restart () {
load();
load(last_loaded);
}
Event.listen("level/restart", data => {
restart();
});

View file

@ -17,6 +17,6 @@ function main () {
requestAnimationFrame(main);
}
level.load();
level.load(1);
main();

View file

@ -4,7 +4,9 @@ import * as Entity from "./entity.mjs";
import * as Input from "./input.mjs";
import { register_tick } from "./tick.mjs";
import * as Enemy from "./enemy.mjs";
// import * as level from "./level.mjs";
import * as Event from "./event.mjs";
import { underride } from "./underride.mjs";
@ -24,12 +26,8 @@ function tick_player (deltatime) {
const enemies = Enemy.get_all();
for (const e of enemies) {
if (Entity.touches(player, e)) {
// here it should just tell other code that the player touched enemy;
// the player code shouldn't know what to do in that situation.
// in the meantime we simply refresh the page. it's hacky and won't last for long,
// but for now it works.
// level.restart();
location.reload();
Event.trigger("level/restart");
return;
}
}
}
@ -39,3 +37,20 @@ export function set_pos (x, y) {
}
register_tick(tick_player);
Event.listen("level/unload", _ => {
Entity.set_visible(player, false);
});
Event.listen("level/load/player", (data = {}) => {
data = underride(data, {
pos: {
x: 0,
y: 0,
},
});
Entity.set_pos(player, data.pos.x, data.pos.y);
Entity.set_visible(player, true);
});