Fix rovers once and for all; make helmet work more often; rename some stuff; simplify attempt_step
This commit is contained in:
parent
7cf92f7841
commit
d4da572940
@ -1128,7 +1128,7 @@ export function parse_level(buf, number = 1) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.warn(`Unrecognized section type '${type}' at offset ${bytes.byteOffset}`);
|
console.warn(`Unrecognized section type '${type}' at offset ${bytes.byteOffset}`, view);
|
||||||
// TODO save it, persist when editing level
|
// TODO save it, persist when editing level
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
208
js/game.js
208
js/game.js
@ -52,8 +52,6 @@ export class Tile {
|
|||||||
// Extremely awkward special case: items don't block monsters if the cell also contains an
|
// Extremely awkward special case: items don't block monsters if the cell also contains an
|
||||||
// item modifier (i.e. "no" sign) or a real player
|
// item modifier (i.e. "no" sign) or a real player
|
||||||
// TODO would love to get this outta here
|
// TODO would love to get this outta here
|
||||||
// FIXME i think this can be removed if monster/player interaction stops being a literal
|
|
||||||
// collision and starts being a result of even attempting to move
|
|
||||||
if (this.type.is_item &&
|
if (this.type.is_item &&
|
||||||
this.cell.some(tile => tile.type.item_modifier || tile.type.is_real_player))
|
this.cell.some(tile => tile.type.item_modifier || tile.type.is_real_player))
|
||||||
return false;
|
return false;
|
||||||
@ -104,7 +102,7 @@ export class Tile {
|
|||||||
direction = tile.cell.redirect_exit(tile, direction);
|
direction = tile.cell.redirect_exit(tile, direction);
|
||||||
// Need to explicitly check this here, otherwise you could /attempt/ to push a block,
|
// Need to explicitly check this here, otherwise you could /attempt/ to push a block,
|
||||||
// which would fail, but it would still change the block's direction
|
// which would fail, but it would still change the block's direction
|
||||||
return ! tile.cell.blocks_leaving(tile, direction);
|
return tile.cell.try_leaving(tile, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inventory stuff
|
// Inventory stuff
|
||||||
@ -195,57 +193,55 @@ export class Cell extends Array {
|
|||||||
return this.some(tile => tile.name === name);
|
return this.some(tile => tile.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks_leaving(actor, direction) {
|
try_leaving(actor, direction) {
|
||||||
for (let tile of this) {
|
for (let tile of this) {
|
||||||
if (tile === actor)
|
if (tile === actor)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (tile.type.traps && tile.type.traps(tile, actor))
|
if (tile.type.traps && tile.type.traps(tile, actor))
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
if (tile.type.blocks_leaving && tile.type.blocks_leaving(tile, actor, direction))
|
if (tile.type.blocks_leaving && tile.type.blocks_leaving(tile, actor, direction))
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this actor can move this direction into this cell. May have side effects, depending
|
// Check if this actor can move this direction into this cell. Returns true on success. May
|
||||||
// on the value of push_mode:
|
// have side effects, depending on the value of push_mode:
|
||||||
// - null: Default. Treat pushable objects as blocking.
|
// - null: Default. Do not impact game state. Treat pushable objects as blocking.
|
||||||
// - 'ignore': Treat pushable objects as nonblocking.
|
// - 'bump': Fire bump triggers. Don't move pushable objects, but do check whether they /could/
|
||||||
// - 'trace': Don't try to move pushable objects, but do check whether they could be pushed,
|
// be pushed, recursively if necessary.
|
||||||
// recursively if necessary.
|
// - 'push': Fire bump triggers. Attempt to move pushable objects out of the way immediately.
|
||||||
// - 'move': Attempt to move pushable objects out of the way immediately.
|
try_entering(actor, direction, level, push_mode = null) {
|
||||||
blocks_entering(actor, direction, level, push_mode = null) {
|
|
||||||
let pushable_tiles = [];
|
let pushable_tiles = [];
|
||||||
let blocked = false;
|
let blocked = false;
|
||||||
for (let tile of this) {
|
// (Note that here, and anywhere else that has any chance of altering the cell's contents,
|
||||||
|
// we iterate over a copy of the cell to insulate ourselves from tiles appearing or
|
||||||
|
// disappearing mid-iteration.)
|
||||||
|
for (let tile of Array.from(this)) {
|
||||||
|
// TODO check ignores here?
|
||||||
|
if (actor.type.on_bump) {
|
||||||
|
actor.type.on_bump(actor, level, tile, direction);
|
||||||
|
}
|
||||||
|
if (tile.type.on_bumped) {
|
||||||
|
tile.type.on_bumped(tile, level, actor);
|
||||||
|
}
|
||||||
|
|
||||||
if (! tile.blocks(actor, direction, level))
|
if (! tile.blocks(actor, direction, level))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (push_mode === null)
|
if (push_mode === null)
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
if (! actor.can_push(tile, direction)) {
|
if (! actor.can_push(tile, direction)) {
|
||||||
if (push_mode === 'move') {
|
if (push_mode === 'push') {
|
||||||
// Track this instead of returning immediately, because 'move' mode also bumps
|
// Track this instead of returning immediately, because 'push' mode also bumps
|
||||||
// every tile in the cell
|
// every tile in the cell
|
||||||
blocked = true;
|
blocked = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return true;
|
return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (push_mode === 'ignore')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (push_mode === 'move') {
|
|
||||||
if (actor.type.on_bump) {
|
|
||||||
actor.type.on_bump(actor, level, tile);
|
|
||||||
}
|
|
||||||
if (tile.type.on_bumped) {
|
|
||||||
tile.type.on_bumped(tile, level, actor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,27 +250,37 @@ export class Cell extends Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (blocked)
|
if (blocked)
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
// If we got this far, all that's left is to deal with pushables
|
// If we got this far, all that's left is to deal with pushables
|
||||||
if (pushable_tiles.length > 0) {
|
if (pushable_tiles.length > 0) {
|
||||||
let neighbor_cell = level.get_neighboring_cell(this, direction);
|
let neighbor_cell = level.get_neighboring_cell(this, direction);
|
||||||
if (! neighbor_cell)
|
if (! neighbor_cell)
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
for (let tile of pushable_tiles) {
|
for (let tile of pushable_tiles) {
|
||||||
if (push_mode === 'trace') {
|
if (push_mode === 'bump') {
|
||||||
if (neighbor_cell.blocks_entering(tile, direction, level, push_mode))
|
// FIXME and leaving!
|
||||||
return true;
|
if (! neighbor_cell.try_entering(tile, direction, level, push_mode))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (push_mode === 'push') {
|
||||||
|
if (actor === this.player) {
|
||||||
|
this._set_tile_prop(actor, 'is_pushing', true);
|
||||||
|
}
|
||||||
|
if (! level.attempt_out_of_turn_step(tile, direction)) {
|
||||||
|
if (tile.slide_mode !== null && tile.movement_cooldown !== 0) {
|
||||||
|
// If the push failed and the obstacle is in the middle of a slide,
|
||||||
|
// remember this as the next move it'll make
|
||||||
|
level._set_tile_prop(tile, 'pending_push', direction);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else if (push_mode === 'move') {
|
|
||||||
if (! level.attempt_step(tile, direction))
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special railroad ability: change the direction we attempt to leave
|
// Special railroad ability: change the direction we attempt to leave
|
||||||
@ -729,6 +735,10 @@ export class Level {
|
|||||||
success = this.attempt_step(actor, actor.decision);
|
success = this.attempt_step(actor, actor.decision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! success && actor.type.on_blocked) {
|
||||||
|
actor.type.on_blocked(actor, this, actor.decision);
|
||||||
|
}
|
||||||
|
|
||||||
// Track whether the player is blocked, for visual effect
|
// Track whether the player is blocked, for visual effect
|
||||||
if (actor === this.player && actor.decision && ! success) {
|
if (actor === this.player && actor.decision && ! success) {
|
||||||
this.sfx.play_once('blocked');
|
this.sfx.play_once('blocked');
|
||||||
@ -848,7 +858,7 @@ export class Level {
|
|||||||
else if (actor.slide_mode === 'ice') {
|
else if (actor.slide_mode === 'ice') {
|
||||||
// A sliding player that bonks into a wall still needs to turn around, but in this
|
// A sliding player that bonks into a wall still needs to turn around, but in this
|
||||||
// case they do NOT start pushing blocks early
|
// case they do NOT start pushing blocks early
|
||||||
if (! try_direction(actor.direction, 'trace')) {
|
if (! try_direction(actor.direction, 'bump')) {
|
||||||
this._handle_slide_bonk(actor);
|
this._handle_slide_bonk(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -863,7 +873,7 @@ export class Level {
|
|||||||
let open;
|
let open;
|
||||||
if (dir2 === null) {
|
if (dir2 === null) {
|
||||||
// Only one direction is held, but for consistency, "check" it anyway
|
// Only one direction is held, but for consistency, "check" it anyway
|
||||||
open = try_direction(dir1, 'move');
|
open = try_direction(dir1, 'push');
|
||||||
actor.decision = dir1;
|
actor.decision = dir1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -871,8 +881,8 @@ export class Level {
|
|||||||
// one, UNLESS it's blocked AND the other isn't
|
// one, UNLESS it's blocked AND the other isn't
|
||||||
if (dir1 === actor.direction || dir2 === actor.direction) {
|
if (dir1 === actor.direction || dir2 === actor.direction) {
|
||||||
let other_direction = dir1 === actor.direction ? dir2 : dir1;
|
let other_direction = dir1 === actor.direction ? dir2 : dir1;
|
||||||
let curr_open = try_direction(actor.direction, 'move');
|
let curr_open = try_direction(actor.direction, 'push');
|
||||||
let other_open = try_direction(other_direction, 'move');
|
let other_open = try_direction(other_direction, 'push');
|
||||||
if (! curr_open && other_open) {
|
if (! curr_open && other_open) {
|
||||||
actor.decision = other_direction;
|
actor.decision = other_direction;
|
||||||
open = true;
|
open = true;
|
||||||
@ -887,8 +897,8 @@ export class Level {
|
|||||||
// FIXME i'm told cc2 prefers orthogonal actually, but need to check on that
|
// FIXME i'm told cc2 prefers orthogonal actually, but need to check on that
|
||||||
// FIXME lynx only checks horizontal, what about cc2? it must check both
|
// FIXME lynx only checks horizontal, what about cc2? it must check both
|
||||||
// because of the behavior where pushing into a corner always pushes horizontal
|
// because of the behavior where pushing into a corner always pushes horizontal
|
||||||
let open1 = try_direction(dir1, 'move');
|
let open1 = try_direction(dir1, 'push');
|
||||||
let open2 = try_direction(dir2, 'move');
|
let open2 = try_direction(dir2, 'push');
|
||||||
if (open1 && ! open2) {
|
if (open1 && ! open2) {
|
||||||
actor.decision = dir1;
|
actor.decision = dir1;
|
||||||
open = true;
|
open = true;
|
||||||
@ -978,7 +988,7 @@ export class Level {
|
|||||||
|
|
||||||
direction = actor.cell.redirect_exit(actor, direction);
|
direction = actor.cell.redirect_exit(actor, direction);
|
||||||
|
|
||||||
if (this.check_movement(actor, actor.cell, direction, 'trace')) {
|
if (this.check_movement(actor, actor.cell, direction, 'bump')) {
|
||||||
// We found a good direction! Stop here
|
// We found a good direction! Stop here
|
||||||
actor.decision = direction;
|
actor.decision = direction;
|
||||||
all_blocked = false;
|
all_blocked = false;
|
||||||
@ -1023,8 +1033,8 @@ export class Level {
|
|||||||
check_movement(actor, orig_cell, direction, push_mode) {
|
check_movement(actor, orig_cell, direction, push_mode) {
|
||||||
let dest_cell = this.get_neighboring_cell(orig_cell, direction);
|
let dest_cell = this.get_neighboring_cell(orig_cell, direction);
|
||||||
return (dest_cell &&
|
return (dest_cell &&
|
||||||
! orig_cell.blocks_leaving(actor, direction) &&
|
orig_cell.try_leaving(actor, direction) &&
|
||||||
! dest_cell.blocks_entering(actor, direction, this, push_mode));
|
dest_cell.try_entering(actor, direction, this, push_mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to move the given actor one tile in the given direction and update their cooldown.
|
// Try to move the given actor one tile in the given direction and update their cooldown.
|
||||||
@ -1043,27 +1053,28 @@ export class Level {
|
|||||||
|
|
||||||
let move = DIRECTIONS[direction].movement;
|
let move = DIRECTIONS[direction].movement;
|
||||||
let goal_cell = this.get_neighboring_cell(actor.cell, direction);
|
let goal_cell = this.get_neighboring_cell(actor.cell, direction);
|
||||||
|
if (! goal_cell)
|
||||||
|
return false;
|
||||||
|
|
||||||
// TODO this could be a lot simpler if i could early-return! should ice bumping be
|
|
||||||
// somewhere else?
|
|
||||||
let blocked;
|
|
||||||
if (goal_cell) {
|
|
||||||
// Only bother touching the goal cell if we're not already trapped in this one
|
// Only bother touching the goal cell if we're not already trapped in this one
|
||||||
if (actor.cell.blocks_leaving(actor, direction)) {
|
if (! actor.cell.try_leaving(actor, direction))
|
||||||
blocked = true;
|
return false;
|
||||||
|
|
||||||
|
if (! goal_cell.try_entering(actor, direction, this, 'push'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// FIXME this feels clunky
|
||||||
|
let terrain = goal_cell.get_terrain();
|
||||||
|
if (terrain && terrain.type.slide_mode && ! actor.ignores(terrain.type.name)) {
|
||||||
|
double_speed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Note that here, and anywhere else that has any chance of
|
// FIXME this can probably reuse try_entering now
|
||||||
// altering the cell's contents, we iterate over a copy of the cell
|
|
||||||
// to insulate ourselves from tiles appearing or disappearing
|
|
||||||
// mid-iteration.)
|
|
||||||
// FIXME actually, this prevents flicking!
|
|
||||||
if (! blocked) {
|
|
||||||
// FIXME this can probably reuse blocks_entering now
|
|
||||||
// Try to move into the cell. This is usually a simple check of whether we can
|
// Try to move into the cell. This is usually a simple check of whether we can
|
||||||
// enter it (similar to Cell.blocks_entering), but if the only thing blocking us is
|
// enter it (similar to Cell.try_entering), but if the only thing blocking us is
|
||||||
// a pushable object, we have to do two more passes: one to push anything pushable,
|
// a pushable object, we have to do two more passes: one to push anything pushable,
|
||||||
// then one to check whether we're blocked again.
|
// then one to check whether we're blocked again.
|
||||||
|
/*
|
||||||
let blocked_by_pushable = false;
|
let blocked_by_pushable = false;
|
||||||
for (let tile of goal_cell) {
|
for (let tile of goal_cell) {
|
||||||
if (tile.blocks(actor, direction, this)) {
|
if (tile.blocks(actor, direction, this)) {
|
||||||
@ -1115,18 +1126,9 @@ export class Level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now check if we're still blocked
|
// Now check if we're still blocked
|
||||||
blocked = goal_cell.blocks_entering(actor, direction, this);
|
blocked = goal_cell.try_entering(actor, direction, this);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Hit the edge
|
|
||||||
blocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blocked) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// We're clear!
|
// We're clear!
|
||||||
if (double_speed || actor.has_item('speed_boots')) {
|
if (double_speed || actor.has_item('speed_boots')) {
|
||||||
@ -1200,33 +1202,12 @@ export class Level {
|
|||||||
this.hint_shown = null;
|
this.hint_shown = null;
|
||||||
}
|
}
|
||||||
for (let tile of goal_cell) {
|
for (let tile of goal_cell) {
|
||||||
if (actor.type.is_real_player && tile.type.is_monster) {
|
// FIXME this could go in on_approach now
|
||||||
this.fail(tile.type.name);
|
|
||||||
}
|
|
||||||
else if (actor.type.is_monster && tile.type.is_real_player) {
|
|
||||||
this.fail(actor.type.name);
|
|
||||||
}
|
|
||||||
else if (actor.type.is_block && tile.type.is_real_player) {
|
|
||||||
this.fail('squished');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actor === this.player && tile.type.is_hint) {
|
if (actor === this.player && tile.type.is_hint) {
|
||||||
this.hint_shown = tile.hint_text ?? this.stored_level.hint;
|
this.hint_shown = tile.hint_text ?? this.stored_level.hint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're stepping directly on the player, that kills them too; the player and a monster
|
|
||||||
// must be at least 5 tics apart
|
|
||||||
// TODO the rules in lynx might be slightly different?
|
|
||||||
if (actor.type.is_monster && goal_cell === this.player.previous_cell &&
|
|
||||||
// Player has decided to leave their cell, but hasn't actually taken a step yet
|
|
||||||
this.player.movement_cooldown === this.player.movement_speed &&
|
|
||||||
// See the extensive comment in attempt_out_of_turn_step
|
|
||||||
this.player.cooldown_delay_hack !== 2)
|
|
||||||
{
|
|
||||||
this.fail(actor.type.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actor === this.player && goal_cell[0].type.name === 'floor') {
|
if (actor === this.player && goal_cell[0].type.name === 'floor') {
|
||||||
this.sfx.play_once('step-floor');
|
this.sfx.play_once('step-floor');
|
||||||
}
|
}
|
||||||
@ -1240,6 +1221,22 @@ export class Level {
|
|||||||
if (actor.ignores(tile.type.name))
|
if (actor.ignores(tile.type.name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Possibly kill a player
|
||||||
|
if (actor.has_item('helmet') || tile.has_item('helmet')) {
|
||||||
|
// Helmet disables this, do nothing
|
||||||
|
}
|
||||||
|
else if (actor.type.is_real_player && tile.type.is_monster) {
|
||||||
|
this.fail(tile.type.name);
|
||||||
|
}
|
||||||
|
else if (actor.type.is_monster && tile.type.is_real_player) {
|
||||||
|
this.fail(actor.type.name);
|
||||||
|
}
|
||||||
|
else if (actor.type.is_block && tile.type.is_real_player) {
|
||||||
|
// Note that blocks squish players if they move for ANY reason, even if pushed by
|
||||||
|
// another player!
|
||||||
|
this.fail('squished');
|
||||||
|
}
|
||||||
|
|
||||||
if (tile.type.on_approach) {
|
if (tile.type.on_approach) {
|
||||||
tile.type.on_approach(tile, this, actor);
|
tile.type.on_approach(tile, this, actor);
|
||||||
}
|
}
|
||||||
@ -1248,6 +1245,21 @@ export class Level {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
// though i'm having trouble coming up with a test
|
||||||
|
// TODO the rules in lynx might be slightly different?
|
||||||
|
if (actor.type.is_monster && goal_cell === this.player.previous_cell &&
|
||||||
|
// Player has decided to leave their cell, but hasn't actually taken a step yet
|
||||||
|
this.player.movement_cooldown === this.player.movement_speed &&
|
||||||
|
! actor.has_item('helmet') && ! this.player.has_item('helmet') &&
|
||||||
|
// See the extensive comment in attempt_out_of_turn_step
|
||||||
|
this.player.cooldown_delay_hack !== 2)
|
||||||
|
{
|
||||||
|
this.fail(actor.type.name);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.compat.tiles_react_instantly) {
|
if (this.compat.tiles_react_instantly) {
|
||||||
this.step_on_cell(actor, actor.cell);
|
this.step_on_cell(actor, actor.cell);
|
||||||
}
|
}
|
||||||
@ -1294,7 +1306,7 @@ export class Level {
|
|||||||
let teleporter = actor.just_stepped_on_teleporter;
|
let teleporter = actor.just_stepped_on_teleporter;
|
||||||
actor.just_stepped_on_teleporter = null;
|
actor.just_stepped_on_teleporter = null;
|
||||||
|
|
||||||
let push_mode = actor === this.player ? 'move' : 'trace';
|
let push_mode = actor === this.player ? 'push' : 'bump';
|
||||||
let original_direction = actor.direction;
|
let original_direction = actor.direction;
|
||||||
let success = false;
|
let success = false;
|
||||||
let dest, direction;
|
let dest, direction;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ function on_ready_force_floor(me, level) {
|
|||||||
let neighbor = level.get_neighboring_cell(me.cell, actor.direction);
|
let neighbor = level.get_neighboring_cell(me.cell, actor.direction);
|
||||||
if (! neighbor)
|
if (! neighbor)
|
||||||
return;
|
return;
|
||||||
if (! neighbor.blocks_entering(actor, actor.direction, level, 'trace'))
|
if (neighbor.try_entering(actor, actor.direction, level))
|
||||||
return;
|
return;
|
||||||
let item = me.cell.get_item();
|
let item = me.cell.get_item();
|
||||||
if (! item)
|
if (! item)
|
||||||
@ -975,7 +975,7 @@ const TILE_TYPES = {
|
|||||||
draw_layer: DRAW_LAYERS.item,
|
draw_layer: DRAW_LAYERS.item,
|
||||||
is_chip: true,
|
is_chip: true,
|
||||||
is_required_chip: true,
|
is_required_chip: true,
|
||||||
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
|
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.type.is_real_player) {
|
if (other.type.is_real_player) {
|
||||||
level.collect_chip();
|
level.collect_chip();
|
||||||
@ -1991,34 +1991,14 @@ const TILE_TYPES = {
|
|||||||
},
|
},
|
||||||
_emulatees: ['teeth', 'glider', 'bug', 'ball', 'teeth_timid', 'fireball', 'paramecium', 'walker'],
|
_emulatees: ['teeth', 'glider', 'bug', 'ball', 'teeth_timid', 'fireball', 'paramecium', 'walker'],
|
||||||
decide_movement(me, level) {
|
decide_movement(me, level) {
|
||||||
|
level._set_tile_prop(me, 'attempted_moves', me.attempted_moves + 1);
|
||||||
if (me.attempted_moves >= 32) {
|
if (me.attempted_moves >= 32) {
|
||||||
level._set_tile_prop(me, 'attempted_moves', me.attempted_moves - 32); // XXX unsure if this is 0 or -32
|
level._set_tile_prop(me, 'attempted_moves', 0);
|
||||||
level._set_tile_prop(me, 'current_emulatee', (me.current_emulatee + 1) % me.type._emulatees.length);
|
level._set_tile_prop(me, 'current_emulatee', (me.current_emulatee + 1) % me.type._emulatees.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
let emulatee = me.type._emulatees[me.current_emulatee];
|
let emulatee = me.type._emulatees[me.current_emulatee];
|
||||||
let preference = TILE_TYPES[emulatee].decide_movement(me, level);
|
return TILE_TYPES[emulatee].decide_movement(me, level);
|
||||||
// Rig up the preference so a failure counts as two moves
|
|
||||||
if (preference) {
|
|
||||||
level._set_tile_prop(me, 'attempted_moves', me.attempted_moves + 1);
|
|
||||||
let last_direction = preference[preference.length - 1];
|
|
||||||
if (typeof last_direction === 'function') {
|
|
||||||
// This is tricky! We want this function to only be called ONCE max, since the
|
|
||||||
// walker uses it to carefully tune the PRNG, so replace it with one that lazily
|
|
||||||
// overwrites 'last_direction'
|
|
||||||
preference.pop();
|
|
||||||
let lazy = last_direction;
|
|
||||||
preference.push(function() {
|
|
||||||
last_direction = lazy();
|
|
||||||
return last_direction;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
preference.push(function() {
|
|
||||||
level._set_tile_prop(me, 'attempted_moves', me.attempted_moves + 1);
|
|
||||||
return last_direction;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return preference;
|
|
||||||
},
|
},
|
||||||
visual_state(me) {
|
visual_state(me) {
|
||||||
if (me && me.current_emulatee !== undefined) {
|
if (me && me.current_emulatee !== undefined) {
|
||||||
@ -2226,6 +2206,7 @@ const TILE_TYPES = {
|
|||||||
draw_layer: DRAW_LAYERS.actor,
|
draw_layer: DRAW_LAYERS.actor,
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
has_inventory: true,
|
has_inventory: true,
|
||||||
|
can_reveal_walls: true,
|
||||||
// FIXME ???????
|
// FIXME ???????
|
||||||
collision_mask: COLLISION.block_cc2,
|
collision_mask: COLLISION.block_cc2,
|
||||||
// FIXME do i start moving immediately when dropped, or next turn?
|
// FIXME do i start moving immediately when dropped, or next turn?
|
||||||
@ -2233,20 +2214,22 @@ const TILE_TYPES = {
|
|||||||
decide_movement(me, level) {
|
decide_movement(me, level) {
|
||||||
return [me.direction];
|
return [me.direction];
|
||||||
},
|
},
|
||||||
// FIXME feel like this should be on_blocked?
|
on_blocked(me, level, direction) {
|
||||||
// FIXME specific case for player!
|
|
||||||
on_bump(me, level, other) {
|
|
||||||
if (me.slide_mode)
|
if (me.slide_mode)
|
||||||
return;
|
return;
|
||||||
if (other.type.is_actor) {
|
let cell = level.get_neighboring_cell(me.cell, direction);
|
||||||
level.transmute_tile(me, 'explosion');
|
if (cell) {
|
||||||
|
let other = cell.get_actor();
|
||||||
|
if (other) {
|
||||||
|
if (other.is_real_player) {
|
||||||
|
level.fail(me.type.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
level.transmute_tile(other, 'explosion');
|
level.transmute_tile(other, 'explosion');
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
else if (other.blocks(me, me.direction, level)) {
|
}
|
||||||
|
}
|
||||||
level.transmute_tile(me, 'explosion');
|
level.transmute_tile(me, 'explosion');
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
xray_eye: {
|
xray_eye: {
|
||||||
@ -2424,7 +2407,7 @@ const TILE_TYPES = {
|
|||||||
draw_layer: DRAW_LAYERS.item,
|
draw_layer: DRAW_LAYERS.item,
|
||||||
is_chip: true,
|
is_chip: true,
|
||||||
is_required_chip: true,
|
is_required_chip: true,
|
||||||
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
|
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.type.is_real_player) {
|
if (other.type.is_real_player) {
|
||||||
level.collect_chip();
|
level.collect_chip();
|
||||||
@ -2435,7 +2418,7 @@ const TILE_TYPES = {
|
|||||||
chip_extra: {
|
chip_extra: {
|
||||||
draw_layer: DRAW_LAYERS.item,
|
draw_layer: DRAW_LAYERS.item,
|
||||||
is_chip: true,
|
is_chip: true,
|
||||||
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
|
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.type.is_real_player) {
|
if (other.type.is_real_player) {
|
||||||
level.collect_chip();
|
level.collect_chip();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user