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;
|
||||
|
||||
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))
|
||||
return true;
|
||||
|
||||
if (this.inventory) {
|
||||
for (let [item, count] of Object.entries(this.inventory)) {
|
||||
if (count === 0)
|
||||
continue;
|
||||
|
||||
if (this.toolbelt) {
|
||||
for (let item of this.toolbelt) {
|
||||
let item_type = TILE_TYPES[item];
|
||||
if (item_type.item_ignores && item_type.item_ignores.has(name))
|
||||
return true;
|
||||
@ -87,24 +85,11 @@ export class Tile {
|
||||
|
||||
// Inventory stuff
|
||||
has_item(name) {
|
||||
return this.inventory[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;
|
||||
if (TILE_TYPES[name].is_key) {
|
||||
return this.keyring && (this.keyring[name] ?? 0) > 0;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
return this.toolbelt && this.toolbelt.some(item => item === name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1009,15 +994,56 @@ export class Level {
|
||||
}
|
||||
|
||||
give_actor(actor, name) {
|
||||
if (! actor.type.has_inventory)
|
||||
if (! actor.type.is_actor)
|
||||
return false;
|
||||
|
||||
let current = actor.inventory[name];
|
||||
this.pending_undo.push(() => actor.inventory[name] = current);
|
||||
actor.inventory[name] = (current ?? 0) + 1;
|
||||
let type = TILE_TYPES[name];
|
||||
if (type.is_key) {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
make_slide(actor, 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 = {};
|
||||
let floor_tile = this.render_inventory_tile('floor');
|
||||
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;
|
||||
this.pending_player_move = null;
|
||||
@ -436,28 +450,25 @@ class Player extends PrimaryView {
|
||||
load_level(stored_level) {
|
||||
this.level = new Level(stored_level, this.compat);
|
||||
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.update_ui();
|
||||
// Force a redraw, which won't happen on its own since the game isn't running
|
||||
this._redraw();
|
||||
this._clear_state();
|
||||
}
|
||||
|
||||
restart_level() {
|
||||
this.level.restart(this.compat);
|
||||
this._clear_state();
|
||||
}
|
||||
|
||||
// Call after loading or restarting a level
|
||||
_clear_state() {
|
||||
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.time_el.classList.remove('--frozen');
|
||||
this.time_el.classList.remove('--danger');
|
||||
@ -465,6 +476,7 @@ class Player extends PrimaryView {
|
||||
this.root.classList.remove('--bonus-visible');
|
||||
|
||||
this.update_ui();
|
||||
// Force a redraw, which won't happen on its own since the game isn't running
|
||||
this._redraw();
|
||||
}
|
||||
|
||||
@ -597,6 +609,7 @@ class Player extends PrimaryView {
|
||||
else {
|
||||
// Rewind by undoing one tic every tic
|
||||
this.level.undo();
|
||||
this.update_ui();
|
||||
}
|
||||
}
|
||||
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.inventory_el.textContent = '';
|
||||
// FIXME why does this stuff run when opening the /editor/
|
||||
for (let [name, count] of Object.entries(this.level.player.inventory)) {
|
||||
if (count > 0) {
|
||||
this.inventory_el.append(mk('img', {src: this.render_inventory_tile(name)}));
|
||||
// Keys appear in a consistent order
|
||||
for (let [key, nodes] of Object.entries(this.inventory_key_nodes)) {
|
||||
let count = this.level.player.keyring[key] ?? 0;
|
||||
if (this.current_keyring[key] === count)
|
||||
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)) {
|
||||
@ -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) {
|
||||
if (new_state === this.state)
|
||||
return;
|
||||
|
||||
@ -251,7 +251,7 @@ const TILE_TYPES = {
|
||||
return ! (other.type.has_inventory && other.has_item('key_red'));
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
@ -262,7 +262,7 @@ const TILE_TYPES = {
|
||||
return ! (other.type.has_inventory && other.has_item('key_blue'));
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
@ -273,7 +273,7 @@ const TILE_TYPES = {
|
||||
return ! (other.type.has_inventory && other.has_item('key_yellow'));
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
@ -284,7 +284,7 @@ const TILE_TYPES = {
|
||||
return ! (other.type.has_inventory && other.has_item('key_green'));
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
@ -474,13 +474,7 @@ const TILE_TYPES = {
|
||||
blocks_monsters: true,
|
||||
blocks_blocks: true,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.inventory) {
|
||||
for (let [name, count] of Object.entries(other.inventory)) {
|
||||
if (count > 0 && TILE_TYPES[name].is_tool) {
|
||||
other.take_item(name, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
level.take_all_tools_from_actor(other);
|
||||
if (other.type.is_player) {
|
||||
level.adjust_bonus(0, 0.5);
|
||||
}
|
||||
@ -491,13 +485,7 @@ const TILE_TYPES = {
|
||||
blocks_monsters: true,
|
||||
blocks_blocks: true,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.inventory) {
|
||||
for (let [name, count] of Object.entries(other.inventory)) {
|
||||
if (count > 0 && TILE_TYPES[name].is_key) {
|
||||
other.take_item(name, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
level.take_all_keys_from_actor(other);
|
||||
if (other.type.is_player) {
|
||||
level.adjust_bonus(0, 0.5);
|
||||
}
|
||||
|
||||
28
style.css
28
style.css
@ -472,20 +472,38 @@ dl.score-chart .-sum {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inventory {
|
||||
#player .inventory {
|
||||
grid-area: inventory;
|
||||
justify-self: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: start;
|
||||
display: grid;
|
||||
grid: auto-flow calc(var(--tile-height) * var(--scale)) / repeat(4, calc(var(--tile-width) * var(--scale)));
|
||||
|
||||
background-size: calc(var(--tile-width) * var(--scale)) calc(var(--tile-height) * var(--scale));
|
||||
width: calc(4 * var(--tile-width) * var(--scale));
|
||||
min-height: calc(2 * var(--tile-height) * var(--scale));
|
||||
}
|
||||
.inventory img {
|
||||
#player .inventory img {
|
||||
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 {
|
||||
grid-area: controls;
|
||||
display: flex;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user