Overhaul inventory: taking items is now undoable; inventory UI is correct, shows key counts
This commit is contained in:
parent
4edc83ae4f
commit
32be0d0d71
78
js/game.js
78
js/game.js
@ -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 {
|
else {
|
||||||
this.inventory[name] = Math.max(0, this.inventory[name] - (amount || 1));
|
return this.toolbelt && this.toolbelt.some(item => item === name);
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
|||||||
76
js/main.js
76
js/main.js
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
28
style.css
28
style.css
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user