Implement item priority (fixes #37 #50 #51)

Also expand the blue key MS compat flag to make keys never block
monsters; allow rovers to pass through score flags; and allow
doppelgängers to pause the clock.
This commit is contained in:
Eevee (Evelyn Woods) 2021-03-01 13:13:01 -07:00
parent 20e67b491e
commit 0d59ffef85
4 changed files with 183 additions and 97 deletions

View File

@ -103,3 +103,13 @@ export const COLLISION = {
all_but_real_player: 0xffff & ~0x0003,
all: 0xffff,
};
// Item pickup priority, which both actors and items have. An actor will pick up an item if the
// item's priority is greater than or equal to the actor's.
export const PICKUP_PRIORITIES = {
never: 4, // cc2 blocks, never pick anything up
always: 3, // all actors; blue keys, yellow teleporters (everything picks up except cc2 blocks)
normal: 2, // actors with inventories; most items
player: 1, // players and doppelgangers; red keys (ignored by everything else)
real_player: 0,
};

View File

@ -67,6 +67,9 @@ export class Tile {
return false;
}
if (level.compat.monsters_ignore_keys && this.type.is_key)
return false;
if (this.type.blocks_collision & other.type.collision_mask)
return true;
@ -1776,23 +1779,32 @@ export class Level extends LevelInterface {
if (actor.ignores(tile.type.name))
continue;
if (tile.type.is_item &&
// FIXME implement item priority i'm begging you
((actor.type.has_inventory && ! (tile.type.name === 'key_red' && ! actor.type.is_player)) ||
(cell.get_item_mod() && cell.get_item_mod().type.item_modifier === 'pickup')) &&
this.attempt_take(actor, tile))
{
if (tile.type.is_key) {
this.sfx.play_once('get-key', cell);
}
else {
this.sfx.play_once('get-tool', cell);
}
}
else if (tile.type.teleport_dest_order) {
// This is used by an extra pass just after our caller, so it doesn't need to undo
if (tile.type.teleport_dest_order) {
// This is used by an extra pass just after our caller, so it doesn't need to undo.
// It DOES need to happen before items, though, or yellow teleporters never work!
actor.just_stepped_on_teleporter = tile;
}
else if (tile.type.item_priority !== undefined) {
// Possibly try to pick items up
// TODO maybe this should be a method
let mod = cell.get_item_mod();
let try_pickup = (
tile.type.item_priority >= actor.type.item_pickup_priority ||
(mod && mod.type.item_modifier === 'pickup'));
if (this.compat.monsters_ignore_keys && tile.type.is_key && actor.type.is_monster) {
try_pickup = false;
}
if (try_pickup && this.attempt_take(actor, tile)) {
if (tile.type.is_key) {
this.sfx.play_once('get-key', cell);
}
else if (tile.type.is_item) {
this.sfx.play_once('get-tool', cell);
}
continue;
}
}
else if (tile.type.on_arrive) {
tile.type.on_arrive(tile, this, actor);
}
@ -1890,7 +1902,8 @@ export class Level extends LevelInterface {
}
if (! success) {
if (actor.type.has_inventory && teleporter.type.name === 'teleport_yellow' &&
if (teleporter.type.item_priority !== undefined &&
teleporter.type.item_priority >= actor.type.item_pickup_priority &&
this.allow_taking_yellow_teleporters)
{
// Super duper special yellow teleporter behavior: you pick it the fuck up
@ -2599,6 +2612,17 @@ export class Level extends LevelInterface {
if (mod && mod.type.item_modifier === 'ignore')
return false;
// Some faux items have custom pickup behavior, e.g. chips and bonuses
if (tile.type.on_pickup) {
if (tile.type.on_pickup(tile, this, actor)) {
this.remove_tile(tile);
return true;
}
else {
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

View File

@ -2979,9 +2979,8 @@ const COMPAT_FLAGS = [{
// TODO maybe steam as well?
rulesets: new Set(['steam-strict']),
}, {
key: 'blue_keys_not_edible',
label: "Monsters do not eat blue keys",
// TODO wonder if this should also remove the special behavior of red keys
key: 'monsters_ignore_keys',
label: "Monsters ignore keys",
rulesets: new Set(['ms']),
}, {
// XXX this is goofy

View File

@ -1,6 +1,8 @@
import { COLLISION, DIRECTIONS, DIRECTION_ORDER, LAYERS, TICS_PER_SECOND } from './defs.js';
import { COLLISION, DIRECTIONS, DIRECTION_ORDER, LAYERS, TICS_PER_SECOND, PICKUP_PRIORITIES } from './defs.js';
import { random_choice } from './util.js';
// TODO factor out some repeated stuff: common monster bits, common item bits, repeated collision
// masks
function activate_me(me, level) {
me.type.activate(me, level);
}
@ -31,9 +33,11 @@ function on_begin_force_floor(me, level) {
let item = me.cell.get_item();
if (! item)
return;
if (item.type.name === 'key_red')
if (item.type.item_priority < actor.type.item_pickup_priority)
return;
if (level.attempt_take(actor, item) && actor.ignores(me.type.name)) {
if (! level.attempt_take(actor, item))
return;
if (actor.ignores(me.type.name)) {
// If they just picked up suction boots, they're no longer sliding
// TODO this feels hacky, shouldn't the slide mode be erased some other way?
level._set_tile_prop(actor, 'slide_mode', null);
@ -1097,6 +1101,7 @@ const TILE_TYPES = {
}
},
},
// Item modifiers
no_sign: {
layer: LAYERS.item_mod,
item_modifier: 'ignore',
@ -1170,6 +1175,7 @@ const TILE_TYPES = {
layer: LAYERS.actor,
collision_mask: COLLISION.block_cc1,
blocks_collision: COLLISION.all,
item_pickup_priority: PICKUP_PRIORITIES.always,
is_actor: true,
is_block: true,
ignores: new Set(['fire', 'flame_jet_on', 'electrified_floor']),
@ -1180,6 +1186,7 @@ const TILE_TYPES = {
layer: LAYERS.actor,
collision_mask: COLLISION.block_cc2,
blocks_collision: COLLISION.all,
item_pickup_priority: PICKUP_PRIORITIES.never,
is_actor: true,
is_block: true,
can_reveal_walls: true,
@ -1207,6 +1214,7 @@ const TILE_TYPES = {
layer: LAYERS.actor,
collision_mask: COLLISION.block_cc2,
blocks_collision: COLLISION.all,
item_pickup_priority: PICKUP_PRIORITIES.never,
is_actor: true,
is_block: true,
can_reveal_walls: true,
@ -1238,6 +1246,8 @@ const TILE_TYPES = {
layer: LAYERS.actor,
collision_mask: COLLISION.block_cc2,
blocks_collision: COLLISION.all,
// XXX?
item_pickup_priority: PICKUP_PRIORITIES.never,
is_actor: true,
is_block: true,
can_reveal_walls: true,
@ -1275,6 +1285,7 @@ const TILE_TYPES = {
layer: LAYERS.actor,
collision_mask: COLLISION.block_cc2,
blocks_collision: COLLISION.all,
item_pickup_priority: PICKUP_PRIORITIES.never,
is_actor: true,
is_block: true,
can_reveal_walls: true,
@ -1354,11 +1365,10 @@ const TILE_TYPES = {
is_chip: true,
is_required_chip: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
on_arrive(me, level, other) {
if (other.type.is_real_player) {
level.collect_chip();
level.remove_tile(me);
}
item_priority: PICKUP_PRIORITIES.real_player,
on_pickup(me, level, other) {
level.collect_chip();
return true;
},
// Not affected by gray buttons
},
@ -1402,6 +1412,9 @@ const TILE_TYPES = {
me.type.on_gray_button(me, level);
},
},
// ------------------------------------------------------------------------------------------------
// Floor mechanisms
cloner: {
layer: LAYERS.terrain,
blocks_collision: COLLISION.real_player | COLLISION.block_cc1 | COLLISION.monster_solid,
@ -1740,6 +1753,7 @@ const TILE_TYPES = {
},
teleport_yellow: {
layer: LAYERS.terrain,
item_priority: PICKUP_PRIORITIES.always,
slide_mode: 'teleport',
teleport_allow_override: true,
*teleport_dest_order(me, level, other) {
@ -1830,6 +1844,7 @@ const TILE_TYPES = {
},
},
// ------------------------------------------------------------------------------------------------
// Buttons
button_blue: {
layer: LAYERS.terrain,
@ -2226,33 +2241,32 @@ const TILE_TYPES = {
stopwatch_bonus: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
if (other.type.is_real_player) {
level.remove_tile(me);
level.adjust_timer(+10);
}
item_priority: PICKUP_PRIORITIES.real_player,
on_pickup(me, level, other) {
level.adjust_timer(+10);
return true;
},
},
stopwatch_penalty: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
if (other.type.is_real_player) {
level.remove_tile(me);
level.adjust_timer(-10);
}
item_priority: PICKUP_PRIORITIES.real_player,
on_pickup(me, level, other) {
level.adjust_timer(-10);
return true;
},
},
stopwatch_toggle: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
if (other.type.is_real_player) {
level.pause_timer();
}
item_priority: PICKUP_PRIORITIES.player,
on_pickup(me, level, other) {
level.pause_timer();
return false;
},
},
// ------------------------------------------------------------------------------------------------
// Critters
bug: {
layer: LAYERS.actor,
@ -2260,6 +2274,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.bug,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
decide_movement(me, level) {
// always try turning as left as possible, and fall back to less-left turns
@ -2273,6 +2288,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
decide_movement(me, level) {
// always try turning as right as possible, and fall back to less-right turns
@ -2286,6 +2302,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
decide_movement(me, level) {
// preserve current direction; if that doesn't work, bounce back the way we came
@ -2299,6 +2316,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
decide_movement(me, level) {
// preserve current direction; if that doesn't work, pick a random direction, even the
@ -2322,6 +2340,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
decide_movement(me, level) {
// always keep moving forward, but reverse if the flag is set
@ -2344,6 +2363,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
pushes: {
dirt_block: true,
ice_block: true,
@ -2371,6 +2391,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 8,
decide_movement(me, level) {
// move completely at random
@ -2384,6 +2405,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
movement_parity: 2,
decide_movement(me, level) {
@ -2403,6 +2425,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
movement_parity: 2,
decide_movement(me, level) {
@ -2422,6 +2445,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.fireball,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
ignores: new Set(['fire', 'flame_jet_on']),
decide_movement(me, level) {
@ -2437,6 +2461,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
ignores: new Set(['water', 'turtle']), // doesn't cause turtles to disappear
decide_movement(me, level) {
@ -2452,6 +2477,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.ghost,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.normal,
has_inventory: true,
ignores: new Set([
'bomb', 'green_bomb',
@ -2481,18 +2507,19 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.monster_generic,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.always,
movement_speed: 4,
movement_parity: 4,
decide_movement: pursue_player,
},
rover: {
// TODO pushes blocks apparently??
layer: LAYERS.actor,
is_actor: true,
is_monster: true,
has_inventory: true,
collision_mask: COLLISION.rover,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.normal,
can_reveal_walls: true,
movement_speed: 8,
movement_parity: 2,
@ -2536,28 +2563,18 @@ const TILE_TYPES = {
// Keys, whose behavior varies
key_red: {
// TODO Red key can ONLY be picked up by players (and doppelgangers), no other actor that
// has an inventory
// Red key is only picked up by players and doppelgangers
layer: LAYERS.item,
item_priority: PICKUP_PRIORITIES.player,
is_item: true,
is_key: true,
},
key_blue: {
// Blue key is picked up by dirt blocks and all monsters, including those that don't have an
// inventory normally
// Blue key is picked up by all actors except CC2 blocks
layer: LAYERS.item,
item_priority: PICKUP_PRIORITIES.always,
is_item: true,
is_key: true,
on_arrive(me, level, other) {
// Call it... everything except ice and directional blocks? These rules are weird.
// Note that the game itself normally handles picking items up, so we only get here for
// actors who aren't supposed to have an inventory
// TODO make this a... flag? i don't know?
// TODO major difference from lynx...
if (other.type.name !== 'ice_block' && other.type.name !== 'frame_block' && ! level.compat.blue_keys_not_edible) {
level.attempt_take(other, me);
}
},
},
key_yellow: {
layer: LAYERS.item,
@ -2565,12 +2582,14 @@ const TILE_TYPES = {
is_key: true,
// FIXME ok this is ghastly
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
key_green: {
layer: LAYERS.item,
is_item: true,
is_key: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
// Boots
// TODO note: ms allows blocks to pass over tools
@ -2579,6 +2598,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
item_ignores: new Set(['ice', 'ice_nw', 'ice_ne', 'ice_sw', 'ice_se', ]),
item_slide_ignores: new Set(['cracked_ice']),
},
@ -2587,6 +2607,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
item_ignores: new Set([
'force_floor_n',
'force_floor_s',
@ -2600,6 +2621,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
// Note that these do NOT ignore fire because of the ghost interaction
// XXX starting to wonder if this is even useful really
item_ignores: new Set(['flame_jet_on']),
@ -2609,6 +2631,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
item_ignores: new Set(['water']),
},
hiking_boots: {
@ -2616,6 +2639,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
item_ignores: new Set(['sand']),
// FIXME uhh these "ignore" that dirt and gravel block us, but they don't ignore the on_arrive, so, uhhhh
},
@ -2625,6 +2649,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
on_depart(me, level, other) {
if (other.type.is_real_player && ! me.cell.get_item_mod()) {
level._set_tile_prop(me, 'timer', 85); // FIXME?? wiki just says about 4.3 seconds what
@ -2641,6 +2666,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.dropped_item,
blocks_collision: COLLISION.all_but_real_player,
// FIXME item_pickup_priority?
// FIXME inherits a copy of player's inventory!
// FIXME holds down buttons, so needs an on_arrive
// FIXME speaking of buttons, destroyed actors should on_depart (behind compat flag)
@ -2740,6 +2766,7 @@ const TILE_TYPES = {
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
on_drop(level) {
return 'rolling_ball';
},
@ -2751,6 +2778,7 @@ const TILE_TYPES = {
has_inventory: true,
can_reveal_walls: true,
collision_mask: COLLISION.dropped_item,
item_pickup_priority: PICKUP_PRIORITIES.normal,
// FIXME do i start moving immediately when dropped, or next turn?
movement_speed: 4,
decide_movement(me, level) {
@ -2792,30 +2820,32 @@ const TILE_TYPES = {
},
},
xray_eye: {
// TODO not implemented
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
helmet: {
// TODO not implemented
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
railroad_sign: {
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
foil: {
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
lightning_bolt: {
layer: LAYERS.item,
@ -2823,36 +2853,42 @@ const TILE_TYPES = {
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_ignores: new Set(['electrified_floor']),
item_priority: PICKUP_PRIORITIES.normal,
},
speed_boots: {
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
bribe: {
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
hook: {
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
skeleton_key: {
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
halo: {
layer: LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
},
// Progression
@ -2863,6 +2899,7 @@ const TILE_TYPES = {
is_real_player: true,
collision_mask: COLLISION.real_player1,
blocks_collision: COLLISION.real_player,
item_pickup_priority: PICKUP_PRIORITIES.real_player,
has_inventory: true,
can_reveal_walls: true,
movement_speed: 4,
@ -2886,6 +2923,7 @@ const TILE_TYPES = {
is_real_player: true,
collision_mask: COLLISION.real_player2,
blocks_collision: COLLISION.real_player,
item_pickup_priority: PICKUP_PRIORITIES.real_player,
has_inventory: true,
can_reveal_walls: true,
movement_speed: 4,
@ -2910,6 +2948,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.doppel1,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.player,
has_inventory: true,
can_reveal_walls: true, // XXX i think?
movement_speed: 4,
@ -2936,6 +2975,7 @@ const TILE_TYPES = {
is_monster: true,
collision_mask: COLLISION.doppel2,
blocks_collision: COLLISION.all_but_real_player,
item_pickup_priority: PICKUP_PRIORITIES.player,
has_inventory: true,
can_reveal_walls: true, // XXX i think?
movement_speed: 4,
@ -2961,88 +3001,92 @@ const TILE_TYPES = {
is_chip: true,
is_required_chip: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
on_arrive(me, level, other) {
if (other.type.is_real_player) {
level.collect_chip();
level.remove_tile(me);
}
item_priority: PICKUP_PRIORITIES.real_player,
on_pickup(me, level, other) {
level.collect_chip();
return true;
},
},
chip_extra: {
layer: LAYERS.item,
is_chip: true,
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
on_arrive(me, level, other) {
if (other.type.is_real_player) {
level.collect_chip();
level.remove_tile(me);
}
item_priority: PICKUP_PRIORITIES.real_player,
on_pickup(me, level, other) {
level.collect_chip();
return true;
},
},
// Score bonuses; they're picked up as normal EXCEPT by ghosts, but only a real player can
// actually add to the player's bonus
score_10: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
on_pickup(me, level, other) {
if (other.type.name === 'ghost')
return false;
if (other.type.is_real_player) {
level.adjust_bonus(10);
level.sfx.play_once('get-bonus', me.cell);
}
// TODO turn this into a flag on those types?? idk
if (other.type.is_player || other.type.name === 'rover' || other.type.name === 'bowling_ball') {
level.remove_tile(me);
}
return true;
},
},
score_100: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
on_pickup(me, level, other) {
if (other.type.name === 'ghost')
return false;
if (other.type.is_real_player) {
level.adjust_bonus(100);
level.sfx.play_once('get-bonus', me.cell);
}
if (other.type.is_player || other.type.name === 'rover' || other.type.name === 'bowling_ball') {
level.remove_tile(me);
}
return true;
},
},
score_1000: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
on_pickup(me, level, other) {
if (other.type.name === 'ghost')
return false;
if (other.type.is_real_player) {
level.adjust_bonus(1000);
level.sfx.play_once('get-bonus', me.cell);
}
if (other.type.is_player || other.type.name === 'rover' || other.type.name === 'bowling_ball') {
level.remove_tile(me);
}
return true;
},
},
score_2x: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
on_pickup(me, level, other) {
if (other.type.name === 'ghost')
return false;
if (other.type.is_real_player) {
level.adjust_bonus(0, 2);
level.sfx.play_once('get-bonus2', me.cell);
}
if (other.type.is_player || other.type.name === 'rover' || other.type.name === 'bowling_ball') {
level.remove_tile(me);
}
return true;
},
},
score_5x: {
layer: LAYERS.item,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
on_arrive(me, level, other) {
blocks_collision: COLLISION.block_cc1 | (COLLISION.monster_solid & ~COLLISION.rover),
item_priority: PICKUP_PRIORITIES.normal,
on_pickup(me, level, other) {
if (other.type.name === 'ghost')
return false;
if (other.type.is_real_player) {
level.adjust_bonus(0, 5);
level.sfx.play_once('get-bonus2', me.cell);
}
if (other.type.is_player || other.type.name === 'rover' || other.type.name === 'bowling_ball') {
level.remove_tile(me);
}
return true;
},
},
@ -3197,8 +3241,17 @@ for (let [name, type] of Object.entries(TILE_TYPES)) {
console.error(`Tile type ${name} has a bad layer`);
}
if (type.is_actor && type.collision_mask === undefined) {
console.error(`Tile type ${name} is an actor but has no collision mask`);
if (type.is_actor) {
if (type.collision_mask === undefined)
console.error(`Tile type ${name} is an actor but has no collision mask`);
if (type.item_pickup_priority === undefined)
console.error(`Tile type ${name} is an actor but has no item pickup priority`);
}
if (type.is_item) {
if (type.item_priority === undefined)
console.error(`Tile type ${name} is an item but has no item priority`);
}
}