Bring death and spring mining more into line with CC2
- Players and monsters do, in fact, block each other. The helmet only prevents death. - Death happens during collision check, which is the entire reason items don't save you: you're collided with first! This allows removing several special cases. - Spring mining is prevented almost incidentally, by virtue of collision being checked both at decision time and movement time. It /can/ happen to actors other than the player, but seemingly not blocks. - Some monsters, whose movement is essentially forced anyway, skip the decision time collision check. This includes doppelgangers, which is why they always spring mine.
This commit is contained in:
parent
24a55d7c88
commit
9883dcf4ef
10
js/defs.js
10
js/defs.js
@ -148,6 +148,10 @@ export const COMPAT_FLAGS = [
|
|||||||
key: 'player_moves_last',
|
key: 'player_moves_last',
|
||||||
label: "Player always moves last",
|
label: "Player always moves last",
|
||||||
rulesets: new Set(['lynx', 'ms']),
|
rulesets: new Set(['lynx', 'ms']),
|
||||||
|
}, {
|
||||||
|
key: 'player_dies_during_movement',
|
||||||
|
label: "Players can't get trampled when standing on items",
|
||||||
|
rulesets: new Set(['lynx']),
|
||||||
}, {
|
}, {
|
||||||
key: 'emulate_60fps',
|
key: 'emulate_60fps',
|
||||||
label: "Game runs at 60 FPS",
|
label: "Game runs at 60 FPS",
|
||||||
@ -203,16 +207,12 @@ export const COMPAT_FLAGS = [
|
|||||||
key: 'monsters_ignore_keys',
|
key: 'monsters_ignore_keys',
|
||||||
label: "Monsters completely ignore keys",
|
label: "Monsters completely ignore keys",
|
||||||
rulesets: new Set(['ms']),
|
rulesets: new Set(['ms']),
|
||||||
}, {
|
|
||||||
key: 'monsters_blocked_by_items',
|
|
||||||
label: "Monsters can't step on items to get the player",
|
|
||||||
rulesets: new Set(['lynx']),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Blocks
|
// Blocks
|
||||||
{
|
{
|
||||||
key: 'no_early_push',
|
key: 'no_early_push',
|
||||||
label: "Player pushes blocks at move time",
|
label: "Pushing blocks happens at move time",
|
||||||
rulesets: new Set(['lynx', 'ms']),
|
rulesets: new Set(['lynx', 'ms']),
|
||||||
}, {
|
}, {
|
||||||
key: 'use_legacy_hooking',
|
key: 'use_legacy_hooking',
|
||||||
|
|||||||
145
js/game.js
145
js/game.js
@ -55,30 +55,16 @@ export class Tile {
|
|||||||
// TODO don't love that the arg order is different here vs tile type, but also don't love that
|
// TODO don't love that the arg order is different here vs tile type, but also don't love that
|
||||||
// the name is the same?
|
// the name is the same?
|
||||||
blocks(other, direction, level) {
|
blocks(other, direction, level) {
|
||||||
// Extremely awkward special case: items don't block monsters if the cell also contains an
|
// Special case: item layer collision is ignored if the cell has an item mod
|
||||||
// item modifier (i.e. "no" sign) or a real player
|
if (this.type.layer === LAYERS.item && this.cell.get_item_mod())
|
||||||
// TODO would love to get this outta here
|
|
||||||
if ((this.type.is_item || this.type.is_chip) && ! level.compat.monsters_blocked_by_items) {
|
|
||||||
let item_mod = this.cell.get_item_mod();
|
|
||||||
if (item_mod && item_mod.type.item_modifier)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let actor = this.cell.get_actor();
|
|
||||||
if (actor && actor.type.is_real_player)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level.compat.monsters_ignore_keys && this.type.is_key)
|
if (level.compat.monsters_ignore_keys && this.type.is_key)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (this.type.blocks_collision & other.type.collision_mask)
|
if (this.type.blocks_collision & other.type.collision_mask)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// FIXME bowling ball isn't affected by helmet? also not sure bowling ball is stopped by
|
|
||||||
// helmet?
|
|
||||||
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
|
// Blocks being pulled are blocked by their pullers (which are, presumably, the only things
|
||||||
// they can be moving towards)
|
// they can be moving towards)
|
||||||
// FIXME something about this broke pulling blocks through teleporters; see #99 Delirium
|
// FIXME something about this broke pulling blocks through teleporters; see #99 Delirium
|
||||||
@ -950,7 +936,7 @@ export class Level extends LevelInterface {
|
|||||||
if (actor.type.ttl) {
|
if (actor.type.ttl) {
|
||||||
// Animations, bizarrely, do their cooldown at decision time, so they're removed
|
// Animations, bizarrely, do their cooldown at decision time, so they're removed
|
||||||
// early on the tic that they expire
|
// early on the tic that they expire
|
||||||
this._do_actor_cooldown(actor, this.compat.emulate_60fps ? 1 : 3);
|
this._do_actor_cooldown(actor, this.update_rate);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1430,10 +1416,19 @@ export class Level extends LevelInterface {
|
|||||||
if (actor.type.decide_movement) {
|
if (actor.type.decide_movement) {
|
||||||
direction_preference = actor.type.decide_movement(actor, this);
|
direction_preference = actor.type.decide_movement(actor, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check which of those directions we *can*, probably, move in
|
|
||||||
if (! direction_preference)
|
if (! direction_preference)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// In CC2, some monsters can only ever have one direction to choose from, so they don't
|
||||||
|
// bother checking collision at all. (Unfortunately, this causes spring mining.)
|
||||||
|
// TODO compat flag for this
|
||||||
|
if (actor.type.skip_decision_time_collision_check) {
|
||||||
|
actor.decision = direction_preference[0] ?? null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which of those directions we *can*, probably, move in
|
||||||
|
let push_mode = this.compat.no_early_push ? 'slap' : 'push';
|
||||||
for (let [i, direction] of direction_preference.entries()) {
|
for (let [i, direction] of direction_preference.entries()) {
|
||||||
if (! direction) {
|
if (! direction) {
|
||||||
// This actor is giving up! Alas.
|
// This actor is giving up! Alas.
|
||||||
@ -1447,7 +1442,7 @@ export class Level extends LevelInterface {
|
|||||||
|
|
||||||
direction = actor.cell.redirect_exit(actor, direction);
|
direction = actor.cell.redirect_exit(actor, direction);
|
||||||
|
|
||||||
if (this.check_movement(actor, actor.cell, direction, 'bump')) {
|
if (this.check_movement(actor, actor.cell, direction, push_mode)) {
|
||||||
// We found a good direction! Stop here
|
// We found a good direction! Stop here
|
||||||
actor.decision = direction;
|
actor.decision = direction;
|
||||||
break;
|
break;
|
||||||
@ -1505,19 +1500,19 @@ export class Level extends LevelInterface {
|
|||||||
// - An actor with foil MUST NOT bump a wall on the other side of a thin wall.
|
// - An actor with foil MUST NOT bump a wall on the other side of a thin wall.
|
||||||
// - A ghost with foil MUST bump a wall (even on the other side of a thin wall) and be
|
// - A ghost with foil MUST bump a wall (even on the other side of a thin wall) and be
|
||||||
// deflected by the resulting steel.
|
// deflected by the resulting steel.
|
||||||
|
// - An actor with foil MUST NOT bump a wall under a "no foil" sign.
|
||||||
// - A bowling ball MUST NOT destroy an actor on the other side of a thin wall, or on top of
|
// - A bowling ball MUST NOT destroy an actor on the other side of a thin wall, or on top of
|
||||||
// a regular wall.
|
// a regular wall.
|
||||||
// - A fireball MUST melt an ice block AND ALSO still be deflected by it, even if the ice
|
// - A fireball MUST melt an ice block AND ALSO still be deflected by it, even if the ice
|
||||||
// block is on top of an item (which blocks the fireball), but NOT one on the other side
|
// block is on top of an item (which blocks the fireball), but NOT one on the other side
|
||||||
// of a thin wall.
|
// of a thin wall.
|
||||||
// - A rover MUST NOT bump walls underneath a canopy (which blocks it).
|
// - A rover MUST NOT bump walls underneath a canopy (which blocks it).
|
||||||
// It seems the order is thus: canopy + thin wall; terrain; actor; item. Which is the usual
|
// It seems the order is thus: canopy + thin wall + item mod (indistinguishable); terrain;
|
||||||
// ordering from the top down, except that terrain is checked before actors. Really, the
|
// actor; item. In other words, some physically logical sense of "outer" to "inner".
|
||||||
// ordering is from "outermost" to "innermost", which makes physical sense.
|
|
||||||
let still_blocked = false;
|
let still_blocked = false;
|
||||||
for (let layer of [
|
for (let layer of [
|
||||||
LAYERS.canopy, LAYERS.thin_wall, LAYERS.terrain, LAYERS.swivel,
|
LAYERS.canopy, LAYERS.thin_wall, LAYERS.item_mod, LAYERS.terrain, LAYERS.swivel,
|
||||||
LAYERS.actor, LAYERS.item_mod, LAYERS.item])
|
LAYERS.actor, LAYERS.item])
|
||||||
{
|
{
|
||||||
let tile = cell[layer];
|
let tile = cell[layer];
|
||||||
if (! tile)
|
if (! tile)
|
||||||
@ -1529,6 +1524,16 @@ export class Level extends LevelInterface {
|
|||||||
tile.type.on_bumped(tile, this, actor);
|
tile.type.on_bumped(tile, this, actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Death happens here: if a monster or block even thinks about moving into a player, or
|
||||||
|
// a player thinks about moving into a monster, the player dies. A player standing on a
|
||||||
|
// wall is only saved by the wall being checked first. This is also why standing on an
|
||||||
|
// item won't save you: actors are checked before items!
|
||||||
|
// In Lynx, on the other hand, this is deferred until later (and only happens if the
|
||||||
|
// move is allowed), so hold off.
|
||||||
|
if (layer === LAYERS.actor && ! this.compat.player_dies_during_movement) {
|
||||||
|
this._check_for_player_death(actor, tile);
|
||||||
|
}
|
||||||
|
|
||||||
if (! tile.blocks(actor, direction, this))
|
if (! tile.blocks(actor, direction, this))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1628,19 +1633,46 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In push mode, check one last time for being blocked, in case we e.g. pushed a block
|
// In push mode, check one last time for being blocked, in case we e.g. pushed a block
|
||||||
// off of a recessed wall
|
// off of a recessed wall.
|
||||||
// TODO unclear if this is the right way to emulate spring mining, but without the check
|
// This is the check that prevents spring mining, the phenomenon where (a) actor pushes
|
||||||
// for a player, it happens /too/ often; try allowing for ann actors and running the 163
|
// a block off of a recessed wall or lilypad, (b) the wall/lilypad becomes blocking as a
|
||||||
// BLOX replay, and right at the end ice blocks spring mine each other. also, the wiki
|
// result, (c) the actor moves into the cell anyway. In most cases this is prevented on
|
||||||
// suggests something about another actor moving away at the same time?
|
// accident, because pushes happen at decision time during the collision check, and then
|
||||||
if (! (this.compat.emulate_spring_mining && actor.type.is_real_player) &&
|
// the actual movement happens later with a second collision check.
|
||||||
push_mode === 'push' && cell.some(tile => tile && tile.blocks(actor, direction, this)))
|
// Note that there is one exception: CC2 does seem to have spring mining prevention when
|
||||||
|
// pushing a row of ice blocks, so we keep the check if we're a block. See BLOX replay;
|
||||||
|
// without this, ice blocks spring mine around 61.9s.
|
||||||
|
if ((! this.compat.emulate_spring_mining || actor.type.is_block) &&
|
||||||
|
push_mode === 'push' &&
|
||||||
|
cell.some(tile => tile && tile.blocks(actor, direction, this)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ! still_blocked;
|
return ! still_blocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_check_for_player_death(actor, tile) {
|
||||||
|
if (actor.has_item('helmet') || tile.has_item('helmet')) {
|
||||||
|
// Helmet disables this, do nothing. In most cases, normal collision will kick
|
||||||
|
// in. Note that this doesn't protect you from bowling balls, which aren't
|
||||||
|
// blocked by anything.
|
||||||
|
}
|
||||||
|
else if (tile.type.is_real_player) {
|
||||||
|
if (actor.type.is_monster) {
|
||||||
|
this.kill_actor(tile, actor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (actor.type.is_block && ! actor.is_pulled) {
|
||||||
|
this.kill_actor(tile, actor, null, null, 'squished');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (actor.type.is_real_player && tile.type.is_monster) {
|
||||||
|
this.kill_actor(actor, tile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check_movement(actor, orig_cell, direction, push_mode) {
|
check_movement(actor, orig_cell, direction, push_mode) {
|
||||||
// Lynx: Players can't override backwards on force floors, and it functions like blocking,
|
// Lynx: Players can't override backwards on force floors, and it functions like blocking,
|
||||||
// but does NOT act like a bonk (hence why it's here)
|
// but does NOT act like a bonk (hence why it's here)
|
||||||
@ -1721,9 +1753,17 @@ export class Level extends LevelInterface {
|
|||||||
if (! success)
|
if (! success)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// In Lynx, checking for player trampling happens right about here, more or less.
|
||||||
|
let goal_cell = this.get_neighboring_cell(actor.cell, direction);
|
||||||
|
if (this.compat.player_dies_during_movement) {
|
||||||
|
let tramplee = goal_cell.get_actor();
|
||||||
|
if (tramplee && this._check_for_player_death(actor, tramplee))
|
||||||
|
// We stepped on the player (or vice versa); don't move, or we'll erase something
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// We're clear! Compute our speed and move us
|
// We're clear! Compute our speed and move us
|
||||||
// FIXME this feels clunky
|
// FIXME this feels clunky
|
||||||
let goal_cell = this.get_neighboring_cell(actor.cell, direction);
|
|
||||||
let terrain = goal_cell.get_terrain();
|
let terrain = goal_cell.get_terrain();
|
||||||
if (terrain && terrain.type.speed_factor && ! actor.ignores(terrain.type.name) && !actor.slide_ignores(terrain.type.name)) {
|
if (terrain && terrain.type.speed_factor && ! actor.ignores(terrain.type.name) && !actor.slide_ignores(terrain.type.name)) {
|
||||||
speed /= terrain.type.speed_factor;
|
speed /= terrain.type.speed_factor;
|
||||||
@ -1778,7 +1818,7 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_do_extra_cooldown(actor) {
|
_do_extra_cooldown(actor) {
|
||||||
this._do_actor_cooldown(actor, this.compat.emulate_60fps ? 1 : 3);
|
this._do_actor_cooldown(actor, this.update_rate);
|
||||||
// Only Lexy has double-cooldown protection
|
// Only Lexy has double-cooldown protection
|
||||||
if (! this.compat.use_lynx_loop) {
|
if (! this.compat.use_lynx_loop) {
|
||||||
this._set_tile_prop(actor, 'last_extra_cooldown_tic', this.tic_counter);
|
this._set_tile_prop(actor, 'last_extra_cooldown_tic', this.tic_counter);
|
||||||
@ -1830,22 +1870,6 @@ export class Level extends LevelInterface {
|
|||||||
if (actor.slide_ignores(tile.type.name))
|
if (actor.slide_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.kill_actor(actor, tile);
|
|
||||||
}
|
|
||||||
else if (actor.type.is_monster && tile.type.is_real_player) {
|
|
||||||
this.kill_actor(tile, actor);
|
|
||||||
}
|
|
||||||
else if (actor.type.is_block && tile.type.is_real_player && ! actor.is_pulled) {
|
|
||||||
// Note that blocks squish players if they move for ANY reason, even if pushed by
|
|
||||||
// another player! The only exception is being pulled
|
|
||||||
this.kill_actor(tile, actor, null, null, '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);
|
||||||
}
|
}
|
||||||
@ -1866,19 +1890,6 @@ export class Level extends LevelInterface {
|
|||||||
this.add_tile(actor, goal_cell);
|
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,
|
|
||||||
// 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'))
|
|
||||||
{
|
|
||||||
this.kill_actor(this.player, actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -2564,7 +2575,7 @@ export class Level extends LevelInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, lose the game
|
// Otherwise, lose the game
|
||||||
this.fail(fail_reason || killer.type.name, null, actor);
|
this.fail(fail_reason || killer.type.name, killer, actor);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2603,12 +2614,18 @@ export class Level extends LevelInterface {
|
|||||||
if (player) {
|
if (player) {
|
||||||
player.fail_reason = null;
|
player.fail_reason = null;
|
||||||
}
|
}
|
||||||
|
if (killer) {
|
||||||
|
killer.is_killer = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.state = 'failure';
|
this.state = 'failure';
|
||||||
this.fail_reason = reason;
|
this.fail_reason = reason;
|
||||||
if (player) {
|
if (player) {
|
||||||
player.fail_reason = reason;
|
player.fail_reason = reason;
|
||||||
}
|
}
|
||||||
|
if (killer) {
|
||||||
|
killer.is_killer = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
win() {
|
win() {
|
||||||
|
|||||||
@ -235,7 +235,7 @@ const COMMON_MONSTER = {
|
|||||||
is_actor: true,
|
is_actor: true,
|
||||||
is_monster: true,
|
is_monster: true,
|
||||||
collision_mask: COLLISION.monster_generic,
|
collision_mask: COLLISION.monster_generic,
|
||||||
blocks_collision: COLLISION.all_but_real_player,
|
blocks_collision: COLLISION.all,
|
||||||
// Despite the name, this means we only pick up items that are always picked up
|
// Despite the name, this means we only pick up items that are always picked up
|
||||||
item_pickup_priority: PICKUP_PRIORITIES.always,
|
item_pickup_priority: PICKUP_PRIORITIES.always,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
@ -2517,7 +2517,7 @@ const TILE_TYPES = {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return [direction];
|
return [direction];
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
tank_yellow: {
|
tank_yellow: {
|
||||||
...COMMON_MONSTER,
|
...COMMON_MONSTER,
|
||||||
@ -2535,17 +2535,20 @@ const TILE_TYPES = {
|
|||||||
if (me.pending_decision) {
|
if (me.pending_decision) {
|
||||||
let decision = me.pending_decision;
|
let decision = me.pending_decision;
|
||||||
level._set_tile_prop(me, 'pending_decision', null);
|
level._set_tile_prop(me, 'pending_decision', null);
|
||||||
// Yellow tanks don't keep trying to move if blocked
|
// Yellow tanks don't keep trying to move if blocked, but they DO turn regardless
|
||||||
|
// XXX consider a compat flag; this is highly unintuitive to me
|
||||||
|
level.set_actor_direction(me, decision);
|
||||||
return [decision, null];
|
return [decision, null];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
blob: {
|
blob: {
|
||||||
...COMMON_MONSTER,
|
...COMMON_MONSTER,
|
||||||
movement_speed: 8,
|
movement_speed: 8,
|
||||||
|
skip_decision_time_collision_check: true,
|
||||||
decide_movement(me, level) {
|
decide_movement(me, level) {
|
||||||
// move completely at random
|
// move completely at random
|
||||||
let d;
|
let d;
|
||||||
@ -2781,7 +2784,7 @@ const TILE_TYPES = {
|
|||||||
is_actor: true,
|
is_actor: true,
|
||||||
is_monster: true,
|
is_monster: true,
|
||||||
collision_mask: COLLISION.block_cc1,
|
collision_mask: COLLISION.block_cc1,
|
||||||
blocks_collision: COLLISION.all_but_real_player,
|
blocks_collision: COLLISION.all,
|
||||||
item_pickup_priority: PICKUP_PRIORITIES.always,
|
item_pickup_priority: PICKUP_PRIORITIES.always,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
// FIXME especially for buttons, destroyed actors should on_depart (behind compat flag)
|
// FIXME especially for buttons, destroyed actors should on_depart (behind compat flag)
|
||||||
@ -2979,7 +2982,7 @@ const TILE_TYPES = {
|
|||||||
is_player: true,
|
is_player: true,
|
||||||
is_real_player: true,
|
is_real_player: true,
|
||||||
collision_mask: COLLISION.real_player1,
|
collision_mask: COLLISION.real_player1,
|
||||||
blocks_collision: COLLISION.real_player,
|
blocks_collision: COLLISION.all,
|
||||||
item_pickup_priority: PICKUP_PRIORITIES.real_player,
|
item_pickup_priority: PICKUP_PRIORITIES.real_player,
|
||||||
can_reveal_walls: true,
|
can_reveal_walls: true,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
@ -3003,7 +3006,7 @@ const TILE_TYPES = {
|
|||||||
is_player: true,
|
is_player: true,
|
||||||
is_real_player: true,
|
is_real_player: true,
|
||||||
collision_mask: COLLISION.real_player2,
|
collision_mask: COLLISION.real_player2,
|
||||||
blocks_collision: COLLISION.real_player,
|
blocks_collision: COLLISION.all,
|
||||||
item_pickup_priority: PICKUP_PRIORITIES.real_player,
|
item_pickup_priority: PICKUP_PRIORITIES.real_player,
|
||||||
can_reveal_walls: true,
|
can_reveal_walls: true,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
@ -3028,7 +3031,8 @@ const TILE_TYPES = {
|
|||||||
is_player: true,
|
is_player: true,
|
||||||
is_monster: true,
|
is_monster: true,
|
||||||
collision_mask: COLLISION.doppel1,
|
collision_mask: COLLISION.doppel1,
|
||||||
blocks_collision: COLLISION.all_but_real_player,
|
blocks_collision: COLLISION.all,
|
||||||
|
skip_decision_time_collision_check: true,
|
||||||
item_pickup_priority: PICKUP_PRIORITIES.player,
|
item_pickup_priority: PICKUP_PRIORITIES.player,
|
||||||
can_reveal_walls: true, // XXX i think?
|
can_reveal_walls: true, // XXX i think?
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
@ -3055,7 +3059,8 @@ const TILE_TYPES = {
|
|||||||
is_player: true,
|
is_player: true,
|
||||||
is_monster: true,
|
is_monster: true,
|
||||||
collision_mask: COLLISION.doppel2,
|
collision_mask: COLLISION.doppel2,
|
||||||
blocks_collision: COLLISION.all_but_real_player,
|
blocks_collision: COLLISION.all,
|
||||||
|
skip_decision_time_collision_check: true,
|
||||||
item_pickup_priority: PICKUP_PRIORITIES.player,
|
item_pickup_priority: PICKUP_PRIORITIES.player,
|
||||||
can_reveal_walls: true, // XXX i think?
|
can_reveal_walls: true, // XXX i think?
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user