Fix a few places where two tiles on the same layer could coexist in a cell

This commit is contained in:
Eevee (Evelyn Woods) 2021-01-03 15:18:53 -07:00
parent fe7731efe7
commit cff756597c
2 changed files with 90 additions and 35 deletions

View File

@ -66,6 +66,12 @@ export class Tile {
if (this.has_item('helmet') || (this.type.is_actor && ! this.type.ttl && other.has_item('helmet')))
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
if (this.type.thin_walls &&
this.type.thin_walls.has(DIRECTIONS[direction].opposite) &&
@ -1507,8 +1513,6 @@ export class Level extends LevelInterface {
return;
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
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
// moving towards and needs to last through our next decision
this.make_slide(actor, null);
for (let tile of Array.from(actor.cell)) {
for (let tile of Array.from(goal_cell)) {
if (tile === actor)
continue;
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
// 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,
@ -1737,7 +1746,7 @@ export class Level extends LevelInterface {
}
}
drop_item(actor, force = false) {
drop_item(actor) {
if (this.stored_level.use_cc1_boots)
return false;
if (actor.movement_cooldown > 0)
@ -1745,38 +1754,58 @@ export class Level extends LevelInterface {
if (! actor.toolbelt || actor.toolbelt.length === 0)
return false;
if (actor.cell.get_item() && ! force)
return false;
// Drop the oldest item, i.e. the first one
let name = actor.toolbelt[0];
if (name === 'teleport_yellow') {
// We can only be dropped on regular floor
let terrain = actor.cell.get_terrain();
if (this._place_dropped_item(name, actor.cell, actor)) {
actor.toolbelt.shift();
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')
return false;
this.transmute_tile(terrain, 'teleport_yellow');
this.transmute_tile(terrain, name);
}
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) {
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) {
type = TILE_TYPES[name];
}
}
let tile = new Tile(type);
this.add_tile(tile, actor.cell);
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.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;
}
@ -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
// tile is removed if successful
// FIXME do not allow overflow dropping before picking up the new item
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')
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 (tile.type.draw_layer === 0) {
// This should only happen for the yellow teleporter
@ -2264,8 +2314,16 @@ export class Level extends LevelInterface {
if (mod && mod.type.item_modifier === 'pickup') {
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;
}
// TODO what happens to the dropped item if the give fails somehow?
return false;
}
@ -2289,12 +2347,11 @@ export class Level extends LevelInterface {
actor.toolbelt = [];
}
// Nothing can hold more than four items, so try to drop one first. Note that this may
// temporarily cause there to be two items in the cell if we're in the middle of picking
// one up, and it means we can't pick up a yellow teleport and swap out another for it
// FIXME two items at once is bad, please fix caller somehow
if (actor.toolbelt.length === 4) {
if (! this.drop_item(actor, true))
// Nothing can hold more than four items, so try to drop one first. Note that normally,
// this should already have happened in attempt_take, so this should only come up when
// forcibly given an item via debug tools
if (actor.toolbelt.length >= 4) {
if (! this.drop_item(actor))
return false;
}

View File

@ -2311,7 +2311,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
on_drop(level, owner) {
on_drop(level) {
return 'rolling_ball';
},
},
@ -2656,7 +2656,7 @@ const TILE_TYPES = {
// VFX
splash: {
draw_layer: DRAW_LAYERS.vfx,
draw_layer: DRAW_LAYERS.actor,
is_actor: true,
collision_mask: 0,
blocks_collision: COLLISION.real_player,
@ -2668,7 +2668,7 @@ const TILE_TYPES = {
},
},
explosion: {
draw_layer: DRAW_LAYERS.vfx,
draw_layer: DRAW_LAYERS.actor,
is_actor: true,
collision_mask: 0,
blocks_collision: COLLISION.real_player,
@ -2688,7 +2688,7 @@ const TILE_TYPES = {
},
// Custom VFX (identical function, but different aesthetic)
splash_slime: {
draw_layer: DRAW_LAYERS.vfx,
draw_layer: DRAW_LAYERS.actor,
is_actor: true,
collision_mask: 0,
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)
// TODO would like these to play faster but the first frame is often skipped due to other bugs
player1_exit: {
draw_layer: DRAW_LAYERS.actor,
draw_layer: DRAW_LAYERS.vfx,
is_actor: true,
collision_mask: 0,
ttl: 8 * 3,
},
player2_exit: {
draw_layer: DRAW_LAYERS.actor,
draw_layer: DRAW_LAYERS.vfx,
is_actor: true,
collision_mask: 0,
ttl: 8 * 3,
},
teleport_flash: {
// TODO probably not the right layer, vfx might need their own idk
draw_layer: DRAW_LAYERS.actor,
draw_layer: DRAW_LAYERS.vfx,
is_actor: true,
collision_mask: 0,
ttl: 8 * 3,
},
transmogrify_flash: {
// TODO probably not the right layer, vfx might need their own idk
draw_layer: DRAW_LAYERS.actor,
draw_layer: DRAW_LAYERS.vfx,
is_actor: true,
collision_mask: 0,
ttl: 6 * 3,