Overhaul inventory: taking items is now undoable; inventory UI is correct, shows key counts

This commit is contained in:
Eevee (Evelyn Woods) 2020-09-19 22:43:04 -06:00
parent 4edc83ae4f
commit 32be0d0d71
4 changed files with 137 additions and 69 deletions

View File

@ -11,7 +11,8 @@ export class Tile {
this.movement_cooldown = 0; this.movement_cooldown = 0;
if (type.has_inventory) { if (type.has_inventory) {
this.inventory = {}; this.keyring = {};
this.toolbelt = [];
} }
} }
@ -64,11 +65,8 @@ export class Tile {
if (this.type.ignores && this.type.ignores.has(name)) if (this.type.ignores && this.type.ignores.has(name))
return true; return true;
if (this.inventory) { if (this.toolbelt) {
for (let [item, count] of Object.entries(this.inventory)) { for (let item of this.toolbelt) {
if (count === 0)
continue;
let item_type = TILE_TYPES[item]; let item_type = TILE_TYPES[item];
if (item_type.item_ignores && item_type.item_ignores.has(name)) if (item_type.item_ignores && item_type.item_ignores.has(name))
return true; return true;
@ -87,24 +85,11 @@ export class Tile {
// Inventory stuff // Inventory stuff
has_item(name) { has_item(name) {
return this.inventory[name] ?? 0 > 0; if (TILE_TYPES[name].is_key) {
} return this.keyring && (this.keyring[name] ?? 0) > 0;
// TODO remove, not undoable
take_item(name, amount = null) {
if (this.inventory[name] && this.inventory[name] >= 1) {
if (amount == null && this.type.infinite_items && this.type.infinite_items[name]) {
// Some items can't be taken away normally, by which I mean,
// green keys
;
}
else {
this.inventory[name] = Math.max(0, this.inventory[name] - (amount || 1));
}
return true;
} }
else { else {
return false; return this.toolbelt && this.toolbelt.some(item => item === name);
} }
} }
} }
@ -1009,15 +994,56 @@ export class Level {
} }
give_actor(actor, name) { give_actor(actor, name) {
if (! actor.type.has_inventory) if (! actor.type.is_actor)
return false; return false;
let current = actor.inventory[name]; let type = TILE_TYPES[name];
this.pending_undo.push(() => actor.inventory[name] = current); if (type.is_key) {
actor.inventory[name] = (current ?? 0) + 1; actor.keyring ??= {};
actor.keyring[name] ??= 0;
actor.keyring[name] += 1;
this.pending_undo.push(() => actor.keyring[name] -= 1);
}
else {
// tool, presumably
actor.toolbelt ??= [];
actor.toolbelt.push(name);
this.pending_undo.push(() => actor.toolbelt.pop());
}
return true; return true;
} }
take_key_from_actor(actor, name) {
if (actor.keyring && (actor.keyring[name] ?? 0) > 0) {
if (actor.type.infinite_items && actor.type.infinite_items[name]) {
// Some items can't be taken away normally, by which I mean, green or yellow keys
return true;
}
this.pending_undo.push(() => actor.keyring[name] += 1);
actor.keyring[name] -= 1;
return true;
}
return false;
}
take_all_keys_from_actor(actor) {
if (actor.keyring) {
let keyring = actor.keyring;
this.pending_undo.push(() => actor.keyring = keyring);
actor.keyring = {};
}
}
take_all_tools_from_actor(actor) {
if (actor.toolbelt) {
let toolbelt = actor.toolbelt;
this.pending_undo.push(() => actor.toolbelt = toolbelt);
actor.toolbelt = [];
}
}
// Mark an actor as sliding // Mark an actor as sliding
make_slide(actor, mode) { make_slide(actor, mode) {
let current = actor.slide_mode; let current = actor.slide_mode;

View File

@ -315,6 +315,20 @@ class Player extends PrimaryView {
this._inventory_tiles = {}; this._inventory_tiles = {};
let floor_tile = this.render_inventory_tile('floor'); let floor_tile = this.render_inventory_tile('floor');
this.inventory_el.style.backgroundImage = `url(${floor_tile})`; this.inventory_el.style.backgroundImage = `url(${floor_tile})`;
this.inventory_key_nodes = {};
this.inventory_tool_nodes = [];
for (let key of ['key_red', 'key_blue', 'key_yellow', 'key_green']) {
let img = mk('img', {src: this.render_inventory_tile(key)});
let count = mk('span.-count');
let root = mk('span', img, count);
this.inventory_key_nodes[key] = {root, img, count};
this.inventory_el.append(root);
}
for (let i = 0; i < 4; i++) {
let img = mk('img');
this.inventory_tool_nodes.push(img);
this.inventory_el.append(img);
}
let last_key; let last_key;
this.pending_player_move = null; this.pending_player_move = null;
@ -436,28 +450,25 @@ class Player extends PrimaryView {
load_level(stored_level) { load_level(stored_level) {
this.level = new Level(stored_level, this.compat); this.level = new Level(stored_level, this.compat);
this.renderer.set_level(this.level); this.renderer.set_level(this.level);
// waiting: haven't yet pressed a key so the timer isn't going
// playing: playing normally
// paused: um, paused
// rewinding: playing backwards
// stopped: level has ended one way or another
this.set_state('waiting');
this.tic_offset = 0;
this.last_advance = 0;
this.demo_faucet = null;
this.root.classList.toggle('--has-demo', !!this.level.stored_level.demo); this.root.classList.toggle('--has-demo', !!this.level.stored_level.demo);
this._clear_state();
this.update_ui();
// Force a redraw, which won't happen on its own since the game isn't running
this._redraw();
} }
restart_level() { restart_level() {
this.level.restart(this.compat); this.level.restart(this.compat);
this._clear_state();
}
// Call after loading or restarting a level
_clear_state() {
this.set_state('waiting'); this.set_state('waiting');
this.tic_offset = 0;
this.last_advance = 0;
this.demo_faucet = null;
this.current_keyring = {};
this.current_toolbelt = [];
this.chips_el.classList.remove('--done'); this.chips_el.classList.remove('--done');
this.time_el.classList.remove('--frozen'); this.time_el.classList.remove('--frozen');
this.time_el.classList.remove('--danger'); this.time_el.classList.remove('--danger');
@ -465,6 +476,7 @@ class Player extends PrimaryView {
this.root.classList.remove('--bonus-visible'); this.root.classList.remove('--bonus-visible');
this.update_ui(); this.update_ui();
// Force a redraw, which won't happen on its own since the game isn't running
this._redraw(); this._redraw();
} }
@ -597,6 +609,7 @@ class Player extends PrimaryView {
else { else {
// Rewind by undoing one tic every tic // Rewind by undoing one tic every tic
this.level.undo(); this.level.undo();
this.update_ui();
} }
} }
this._advance_handle = window.setTimeout(this._advance_bound, 1000 / TICS_PER_SECOND); this._advance_handle = window.setTimeout(this._advance_bound, 1000 / TICS_PER_SECOND);
@ -667,12 +680,30 @@ class Player extends PrimaryView {
} }
this.message_el.textContent = this.level.hint_shown ?? ""; this.message_el.textContent = this.level.hint_shown ?? "";
this.inventory_el.textContent = ''; // Keys appear in a consistent order
// FIXME why does this stuff run when opening the /editor/ for (let [key, nodes] of Object.entries(this.inventory_key_nodes)) {
for (let [name, count] of Object.entries(this.level.player.inventory)) { let count = this.level.player.keyring[key] ?? 0;
if (count > 0) { if (this.current_keyring[key] === count)
this.inventory_el.append(mk('img', {src: this.render_inventory_tile(name)})); continue;
nodes.root.classList.toggle('--hidden', count <= 0);
nodes.count.classList.toggle('--hidden', count <= 1);
nodes.count.textContent = count;
this.current_keyring[key] = count;
}
// Tools are whatever order we picked them up
for (let [i, node] of this.inventory_tool_nodes.entries()) {
let tool = this.level.player.toolbelt[i] ?? null;
if (this.current_toolbelt[i] === tool)
continue;
node.classList.toggle('--hidden', tool === null);
if (tool) {
node.src = this.render_inventory_tile(tool);
} }
this.current_toolbelt[i] = tool;
} }
for (let action of Object.keys(ACTION_LABELS)) { for (let action of Object.keys(ACTION_LABELS)) {
@ -689,6 +720,11 @@ class Player extends PrimaryView {
} }
} }
// waiting: haven't yet pressed a key so the timer isn't going
// playing: playing normally
// paused: um, paused
// rewinding: playing backwards
// stopped: level has ended one way or another
set_state(new_state) { set_state(new_state) {
if (new_state === this.state) if (new_state === this.state)
return; return;

View File

@ -251,7 +251,7 @@ const TILE_TYPES = {
return ! (other.type.has_inventory && other.has_item('key_red')); return ! (other.type.has_inventory && other.has_item('key_red'));
}, },
on_arrive(me, level, other) { on_arrive(me, level, other) {
if (other.type.has_inventory && other.take_item('key_red')) { if (level.take_key_from_actor(other, 'key_red')) {
level.transmute_tile(me, 'floor'); level.transmute_tile(me, 'floor');
} }
}, },
@ -262,7 +262,7 @@ const TILE_TYPES = {
return ! (other.type.has_inventory && other.has_item('key_blue')); return ! (other.type.has_inventory && other.has_item('key_blue'));
}, },
on_arrive(me, level, other) { on_arrive(me, level, other) {
if (other.type.has_inventory && other.take_item('key_blue')) { if (level.take_key_from_actor(other, 'key_blue')) {
level.transmute_tile(me, 'floor'); level.transmute_tile(me, 'floor');
} }
}, },
@ -273,7 +273,7 @@ const TILE_TYPES = {
return ! (other.type.has_inventory && other.has_item('key_yellow')); return ! (other.type.has_inventory && other.has_item('key_yellow'));
}, },
on_arrive(me, level, other) { on_arrive(me, level, other) {
if (other.type.has_inventory && other.take_item('key_yellow')) { if (level.take_key_from_actor(other, 'key_yellow')) {
level.transmute_tile(me, 'floor'); level.transmute_tile(me, 'floor');
} }
}, },
@ -284,7 +284,7 @@ const TILE_TYPES = {
return ! (other.type.has_inventory && other.has_item('key_green')); return ! (other.type.has_inventory && other.has_item('key_green'));
}, },
on_arrive(me, level, other) { on_arrive(me, level, other) {
if (other.type.has_inventory && other.take_item('key_green')) { if (level.take_key_from_actor(other, 'key_green')) {
level.transmute_tile(me, 'floor'); level.transmute_tile(me, 'floor');
} }
}, },
@ -474,13 +474,7 @@ const TILE_TYPES = {
blocks_monsters: true, blocks_monsters: true,
blocks_blocks: true, blocks_blocks: true,
on_arrive(me, level, other) { on_arrive(me, level, other) {
if (other.inventory) { level.take_all_tools_from_actor(other);
for (let [name, count] of Object.entries(other.inventory)) {
if (count > 0 && TILE_TYPES[name].is_tool) {
other.take_item(name, count);
}
}
}
if (other.type.is_player) { if (other.type.is_player) {
level.adjust_bonus(0, 0.5); level.adjust_bonus(0, 0.5);
} }
@ -491,13 +485,7 @@ const TILE_TYPES = {
blocks_monsters: true, blocks_monsters: true,
blocks_blocks: true, blocks_blocks: true,
on_arrive(me, level, other) { on_arrive(me, level, other) {
if (other.inventory) { level.take_all_keys_from_actor(other);
for (let [name, count] of Object.entries(other.inventory)) {
if (count > 0 && TILE_TYPES[name].is_key) {
other.take_item(name, count);
}
}
}
if (other.type.is_player) { if (other.type.is_player) {
level.adjust_bonus(0, 0.5); level.adjust_bonus(0, 0.5);
} }

View File

@ -472,20 +472,38 @@ dl.score-chart .-sum {
display: none; display: none;
} }
.inventory { #player .inventory {
grid-area: inventory; grid-area: inventory;
justify-self: center; justify-self: center;
display: flex; display: grid;
flex-wrap: wrap; grid: auto-flow calc(var(--tile-height) * var(--scale)) / repeat(4, calc(var(--tile-width) * var(--scale)));
align-items: start;
background-size: calc(var(--tile-width) * var(--scale)) calc(var(--tile-height) * var(--scale)); background-size: calc(var(--tile-width) * var(--scale)) calc(var(--tile-height) * var(--scale));
width: calc(4 * var(--tile-width) * var(--scale)); width: calc(4 * var(--tile-width) * var(--scale));
min-height: calc(2 * var(--tile-height) * var(--scale)); min-height: calc(2 * var(--tile-height) * var(--scale));
} }
.inventory img { #player .inventory img {
width: calc(var(--tile-width) * var(--scale)); width: calc(var(--tile-width) * var(--scale));
} }
#player .inventory .--hidden {
visibility: hidden;
pointer-events: none;
}
#player .inventory > span {
position: relative;
}
#player .inventory .-count {
font-size: calc(0.25 * var(--tile-height) * var(--scale));
position: absolute;
top: 0;
right: 0;
padding: 0.25em 0.5em;
margin: 0.25em; /* 2px, for a 32px tileset */
line-height: 1;
border-radius: 0.25em;
background: #0009;
color: white;
}
#player .controls { #player .controls {
grid-area: controls; grid-area: controls;
display: flex; display: flex;