Fix a few places where two tiles on the same layer could coexist in a cell
This commit is contained in:
parent
fe7731efe7
commit
cff756597c
107
js/game.js
107
js/game.js
@ -66,6 +66,12 @@ export class Tile {
|
|||||||
if (this.has_item('helmet') || (this.type.is_actor && ! this.type.ttl && other.has_item('helmet')))
|
if (this.has_item('helmet') || (this.type.is_actor && ! this.type.ttl && other.has_item('helmet')))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Blocks being pulled are blocked by their pullers (which are, presumably, the only things
|
||||||
|
// they can be moving towards)
|
||||||
|
// FIXME something about this broke pulling blocks through teleporters; see #99 Delirium
|
||||||
|
if (this.type.is_actor && other.type.is_block && other.is_pulled)
|
||||||
|
return true;
|
||||||
|
|
||||||
// FIXME get this out of here
|
// FIXME get this out of here
|
||||||
if (this.type.thin_walls &&
|
if (this.type.thin_walls &&
|
||||||
this.type.thin_walls.has(DIRECTIONS[direction].opposite) &&
|
this.type.thin_walls.has(DIRECTIONS[direction].opposite) &&
|
||||||
@ -1507,8 +1513,6 @@ export class Level extends LevelInterface {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
let original_cell = actor.cell;
|
let original_cell = actor.cell;
|
||||||
this.remove_tile(actor);
|
|
||||||
this.add_tile(actor, goal_cell);
|
|
||||||
|
|
||||||
// Announce we're leaving, for the handful of tiles that care about it
|
// Announce we're leaving, for the handful of tiles that care about it
|
||||||
for (let tile of Array.from(original_cell)) {
|
for (let tile of Array.from(original_cell)) {
|
||||||
@ -1536,7 +1540,7 @@ export class Level extends LevelInterface {
|
|||||||
// Announce we're approaching. Slide mode is set here, since it's about the tile we're
|
// Announce we're approaching. Slide mode is set here, since it's about the tile we're
|
||||||
// moving towards and needs to last through our next decision
|
// moving towards and needs to last through our next decision
|
||||||
this.make_slide(actor, null);
|
this.make_slide(actor, null);
|
||||||
for (let tile of Array.from(actor.cell)) {
|
for (let tile of Array.from(goal_cell)) {
|
||||||
if (tile === actor)
|
if (tile === actor)
|
||||||
continue;
|
continue;
|
||||||
if (actor.ignores(tile.type.name))
|
if (actor.ignores(tile.type.name))
|
||||||
@ -1566,6 +1570,11 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now physically move the actor; we wait until here in case some of those callbacks handled
|
||||||
|
// interactions between actors on the same layer (e.g. monsters erasing splashes)
|
||||||
|
this.remove_tile(actor);
|
||||||
|
this.add_tile(actor, goal_cell);
|
||||||
|
|
||||||
// If we're a monster stepping on the player's tail, that also kills her immediately; the
|
// If we're a monster stepping on the player's tail, that also kills her immediately; the
|
||||||
// player and a monster must be strictly more than 4 tics apart
|
// player and a monster must be strictly more than 4 tics apart
|
||||||
// FIXME this only works for the /current/ player but presumably applies to all of them,
|
// FIXME this only works for the /current/ player but presumably applies to all of them,
|
||||||
@ -1737,7 +1746,7 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drop_item(actor, force = false) {
|
drop_item(actor) {
|
||||||
if (this.stored_level.use_cc1_boots)
|
if (this.stored_level.use_cc1_boots)
|
||||||
return false;
|
return false;
|
||||||
if (actor.movement_cooldown > 0)
|
if (actor.movement_cooldown > 0)
|
||||||
@ -1745,38 +1754,58 @@ export class Level extends LevelInterface {
|
|||||||
if (! actor.toolbelt || actor.toolbelt.length === 0)
|
if (! actor.toolbelt || actor.toolbelt.length === 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (actor.cell.get_item() && ! force)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Drop the oldest item, i.e. the first one
|
// Drop the oldest item, i.e. the first one
|
||||||
let name = actor.toolbelt[0];
|
let name = actor.toolbelt[0];
|
||||||
if (name === 'teleport_yellow') {
|
if (this._place_dropped_item(name, actor.cell, actor)) {
|
||||||
// We can only be dropped on regular floor
|
actor.toolbelt.shift();
|
||||||
let terrain = actor.cell.get_terrain();
|
this._push_pending_undo(() => actor.toolbelt.unshift(name));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to place an item in the world, as though dropped by an actor
|
||||||
|
_place_dropped_item(name, cell, dropping_actor) {
|
||||||
|
let type = TILE_TYPES[name];
|
||||||
|
if (type.draw_layer === 0) {
|
||||||
|
// Terrain items (i.e., yellow teleports) can only be dropped on regular floor
|
||||||
|
let terrain = cell.get_terrain();
|
||||||
if (terrain.type.name !== 'floor')
|
if (terrain.type.name !== 'floor')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this.transmute_tile(terrain, 'teleport_yellow');
|
this.transmute_tile(terrain, name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let type = TILE_TYPES[name];
|
// Note that we can't drop a bowling ball if there's already an item, even though a
|
||||||
|
// dropped bowling ball is really an actor (TODO arguably a bug)
|
||||||
|
if (cell.get_item())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (type.on_drop) {
|
if (type.on_drop) {
|
||||||
name = type.on_drop(this, actor);
|
// FIXME quirky things happen if a dropped bowling ball can't enter the facing cell
|
||||||
|
// (mostly it disappears) (also arguably a bug)
|
||||||
|
// FIXME does this even need to be a function lol
|
||||||
|
name = type.on_drop(this);
|
||||||
if (name) {
|
if (name) {
|
||||||
type = TILE_TYPES[name];
|
type = TILE_TYPES[name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let tile = new Tile(type);
|
let tile = new Tile(type);
|
||||||
this.add_tile(tile, actor.cell);
|
|
||||||
if (type.is_actor) {
|
if (type.is_actor) {
|
||||||
|
// This is tricky -- the item has become an actor, but whatever dropped it is
|
||||||
|
// already in this cell's actor layer. But we also know for sure that there's no
|
||||||
|
// item in this cell, so we'll cheat a little: add it in the item layer, set it
|
||||||
|
// rolling (which should shift it into the next cell over), then switch it to the
|
||||||
|
// actor layer.
|
||||||
|
// TODO do that
|
||||||
this.add_actor(tile);
|
this.add_actor(tile);
|
||||||
this.attempt_out_of_turn_step(tile, actor.direction);
|
this.attempt_out_of_turn_step(tile, dropping_actor.direction);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.add_tile(tile, cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actor.toolbelt.shift();
|
|
||||||
this._push_pending_undo(() => actor.toolbelt.unshift(name));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2248,11 +2277,32 @@ export class Level extends LevelInterface {
|
|||||||
|
|
||||||
// Have an actor try to pick up a particular tile; it's prevented if there's a no sign, and the
|
// Have an actor try to pick up a particular tile; it's prevented if there's a no sign, and the
|
||||||
// tile is removed if successful
|
// tile is removed if successful
|
||||||
|
// FIXME do not allow overflow dropping before picking up the new item
|
||||||
attempt_take(actor, tile) {
|
attempt_take(actor, tile) {
|
||||||
let mod = tile.cell.get_item_mod();
|
let cell = tile.cell;
|
||||||
|
let mod = cell.get_item_mod();
|
||||||
if (mod && mod.type.item_modifier === 'ignore')
|
if (mod && mod.type.item_modifier === 'ignore')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Handling a full inventory is a teeny bit complicated. We want the following:
|
||||||
|
// - At no point are two items in the same cell
|
||||||
|
// - A yellow teleporter cannot be dropped in exchange for another yellow teleporter
|
||||||
|
// - If the oldest item can't be dropped, the pickup fails
|
||||||
|
// Thus we have to check whether dropping is possible FIRST, but only place the dropped item
|
||||||
|
// AFTER the pickup.
|
||||||
|
let dropped_item;
|
||||||
|
if (! tile.type.is_key && actor.toolbelt && actor.toolbelt.length >= 4) {
|
||||||
|
let oldest_item_type = TILE_TYPES[actor.toolbelt[0]];
|
||||||
|
if (oldest_item_type.draw_layer === 0 && cell.get_terrain().type.name !== 'floor') {
|
||||||
|
// This is a yellow teleporter, and we are not standing on floor; abort!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Otherwise, it's either an item or a yellow teleporter we're allowed to drop, so steal
|
||||||
|
// it out of their inventory to be dropped later
|
||||||
|
dropped_item = actor.toolbelt.shift();
|
||||||
|
this._push_pending_undo(() => actor.toolbelt.unshift(dropped_item));
|
||||||
|
}
|
||||||
|
|
||||||
if (this.give_actor(actor, tile.type.name)) {
|
if (this.give_actor(actor, tile.type.name)) {
|
||||||
if (tile.type.draw_layer === 0) {
|
if (tile.type.draw_layer === 0) {
|
||||||
// This should only happen for the yellow teleporter
|
// This should only happen for the yellow teleporter
|
||||||
@ -2264,8 +2314,16 @@ export class Level extends LevelInterface {
|
|||||||
if (mod && mod.type.item_modifier === 'pickup') {
|
if (mod && mod.type.item_modifier === 'pickup') {
|
||||||
this.remove_tile(mod);
|
this.remove_tile(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drop any overflowed item
|
||||||
|
if (dropped_item) {
|
||||||
|
// TODO what if this fails??
|
||||||
|
this._place_dropped_item(dropped_item, cell, actor);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// TODO what happens to the dropped item if the give fails somehow?
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2289,12 +2347,11 @@ export class Level extends LevelInterface {
|
|||||||
actor.toolbelt = [];
|
actor.toolbelt = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing can hold more than four items, so try to drop one first. Note that this may
|
// Nothing can hold more than four items, so try to drop one first. Note that normally,
|
||||||
// temporarily cause there to be two items in the cell if we're in the middle of picking
|
// this should already have happened in attempt_take, so this should only come up when
|
||||||
// one up, and it means we can't pick up a yellow teleport and swap out another for it
|
// forcibly given an item via debug tools
|
||||||
// FIXME two items at once is bad, please fix caller somehow
|
if (actor.toolbelt.length >= 4) {
|
||||||
if (actor.toolbelt.length === 4) {
|
if (! this.drop_item(actor))
|
||||||
if (! this.drop_item(actor, true))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2311,7 +2311,7 @@ const TILE_TYPES = {
|
|||||||
is_item: true,
|
is_item: true,
|
||||||
is_tool: true,
|
is_tool: true,
|
||||||
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
|
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
|
||||||
on_drop(level, owner) {
|
on_drop(level) {
|
||||||
return 'rolling_ball';
|
return 'rolling_ball';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -2656,7 +2656,7 @@ const TILE_TYPES = {
|
|||||||
|
|
||||||
// VFX
|
// VFX
|
||||||
splash: {
|
splash: {
|
||||||
draw_layer: DRAW_LAYERS.vfx,
|
draw_layer: DRAW_LAYERS.actor,
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
collision_mask: 0,
|
collision_mask: 0,
|
||||||
blocks_collision: COLLISION.real_player,
|
blocks_collision: COLLISION.real_player,
|
||||||
@ -2668,7 +2668,7 @@ const TILE_TYPES = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
explosion: {
|
explosion: {
|
||||||
draw_layer: DRAW_LAYERS.vfx,
|
draw_layer: DRAW_LAYERS.actor,
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
collision_mask: 0,
|
collision_mask: 0,
|
||||||
blocks_collision: COLLISION.real_player,
|
blocks_collision: COLLISION.real_player,
|
||||||
@ -2688,7 +2688,7 @@ const TILE_TYPES = {
|
|||||||
},
|
},
|
||||||
// Custom VFX (identical function, but different aesthetic)
|
// Custom VFX (identical function, but different aesthetic)
|
||||||
splash_slime: {
|
splash_slime: {
|
||||||
draw_layer: DRAW_LAYERS.vfx,
|
draw_layer: DRAW_LAYERS.actor,
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
collision_mask: 0,
|
collision_mask: 0,
|
||||||
blocks_collision: COLLISION.real_player,
|
blocks_collision: COLLISION.real_player,
|
||||||
@ -2700,27 +2700,25 @@ const TILE_TYPES = {
|
|||||||
// New VFX (not in CC2, so they don't block to avoid altering gameplay)
|
// New VFX (not in CC2, so they don't block to avoid altering gameplay)
|
||||||
// TODO would like these to play faster but the first frame is often skipped due to other bugs
|
// TODO would like these to play faster but the first frame is often skipped due to other bugs
|
||||||
player1_exit: {
|
player1_exit: {
|
||||||
draw_layer: DRAW_LAYERS.actor,
|
draw_layer: DRAW_LAYERS.vfx,
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
collision_mask: 0,
|
collision_mask: 0,
|
||||||
ttl: 8 * 3,
|
ttl: 8 * 3,
|
||||||
},
|
},
|
||||||
player2_exit: {
|
player2_exit: {
|
||||||
draw_layer: DRAW_LAYERS.actor,
|
draw_layer: DRAW_LAYERS.vfx,
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
collision_mask: 0,
|
collision_mask: 0,
|
||||||
ttl: 8 * 3,
|
ttl: 8 * 3,
|
||||||
},
|
},
|
||||||
teleport_flash: {
|
teleport_flash: {
|
||||||
// TODO probably not the right layer, vfx might need their own idk
|
draw_layer: DRAW_LAYERS.vfx,
|
||||||
draw_layer: DRAW_LAYERS.actor,
|
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
collision_mask: 0,
|
collision_mask: 0,
|
||||||
ttl: 8 * 3,
|
ttl: 8 * 3,
|
||||||
},
|
},
|
||||||
transmogrify_flash: {
|
transmogrify_flash: {
|
||||||
// TODO probably not the right layer, vfx might need their own idk
|
draw_layer: DRAW_LAYERS.vfx,
|
||||||
draw_layer: DRAW_LAYERS.actor,
|
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
collision_mask: 0,
|
collision_mask: 0,
|
||||||
ttl: 6 * 3,
|
ttl: 6 * 3,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user