Fixed several issues with animation and movement; quick stab at replay UI
- Animation now has its own timer and isn't linked to movement cooldown, which is good for blocks since they don't have movement cooldown - Destroyed actors don't crash the game again (oops) - Slide and cooldown handling was reshuffled to better support the CC2 approach of landing on tiles with a delay; in particular, you move at double speed on sliding tiles again! - Demo playback got some rough UI so I don't have to keep editing the source code to decide whether to play a demo
This commit is contained in:
parent
bee6ba4c80
commit
2df8607243
172
js/main.js
172
js/main.js
@ -39,13 +39,15 @@ class Tile {
|
|||||||
visual_position(tic_offset = 0) {
|
visual_position(tic_offset = 0) {
|
||||||
let x = this.cell.x;
|
let x = this.cell.x;
|
||||||
let y = this.cell.y;
|
let y = this.cell.y;
|
||||||
if (! this.movement_cooldown) {
|
if (! this.animation_speed) {
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let p = (- this.movement_cooldown + tic_offset) / this.movement_speed;
|
let p = (this.animation_progress + tic_offset) / this.animation_speed;
|
||||||
let motion = DIRECTIONS[this.direction].movement;
|
return [
|
||||||
return [x + p * motion[0], y + p * motion[1]];
|
(1 - p) * this.previous_cell.x + p * x,
|
||||||
|
(1 - p) * this.previous_cell.y + p * y,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,18 +301,35 @@ class Level {
|
|||||||
|
|
||||||
// XXX this entire turn order is rather different in ms rules
|
// XXX this entire turn order is rather different in ms rules
|
||||||
for (let actor of this.actors) {
|
for (let actor of this.actors) {
|
||||||
|
// Actors with no cell were destroyed
|
||||||
|
if (! actor.cell)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Decrement the cooldown here, but only check it later because
|
||||||
|
// stepping on cells in the next block might reset it
|
||||||
if (actor.movement_cooldown > 0) {
|
if (actor.movement_cooldown > 0) {
|
||||||
this._set_prop(actor, 'movement_cooldown', actor.movement_cooldown - 1);
|
this._set_prop(actor, 'movement_cooldown', actor.movement_cooldown - 1);
|
||||||
if (actor.movement_cooldown > 0) {
|
}
|
||||||
continue;
|
|
||||||
}
|
if (actor.animation_speed) {
|
||||||
else if (! this.compat.tiles_react_instantly) {
|
// Deal with movement animation
|
||||||
// For delayed arrival (usually paired with smooth
|
actor.animation_progress += 1;
|
||||||
// scrolling), tiles only react once actors finish moving
|
if (actor.animation_progress >= actor.animation_speed) {
|
||||||
// onto them
|
actor.previous_cell = null;
|
||||||
this.step_on_cell(actor);
|
actor.animation_progress = null;
|
||||||
|
actor.animation_speed = null;
|
||||||
|
if (! this.compat.tiles_react_instantly) {
|
||||||
|
this.step_on_cell(actor);
|
||||||
|
// May have been destroyed here, too!
|
||||||
|
if (! actor.cell)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actor.movement_cooldown > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
// XXX does the cooldown drop while in a trap? is this even right?
|
// XXX does the cooldown drop while in a trap? is this even right?
|
||||||
// TODO should still attempt to move (so chip turns), just will be stuck (but wait, do monsters turn? i don't think so)
|
// TODO should still attempt to move (so chip turns), just will be stuck (but wait, do monsters turn? i don't think so)
|
||||||
if (actor.stuck)
|
if (actor.stuck)
|
||||||
@ -432,17 +451,6 @@ class Level {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only set movement cooldown if we actually moved!
|
|
||||||
if (moved) {
|
|
||||||
// Speed multiplier is based on the tile we landed /on/.
|
|
||||||
let speed = actor.type.movement_speed;
|
|
||||||
if (actor.slide_mode !== null) {
|
|
||||||
speed /= 2;
|
|
||||||
}
|
|
||||||
this._set_prop(actor, 'movement_cooldown', speed);
|
|
||||||
this._set_prop(actor, 'movement_speed', speed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO do i need to do this more aggressively?
|
// TODO do i need to do this more aggressively?
|
||||||
if (this.state === 'success' || this.state === 'failure')
|
if (this.state === 'success' || this.state === 'failure')
|
||||||
break;
|
break;
|
||||||
@ -466,15 +474,38 @@ class Level {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip out any destroyed actors from the acting order
|
||||||
|
let p = 0;
|
||||||
|
for (let i = 0, l = this.actors.length; i < l; i++) {
|
||||||
|
let actor = this.actors[i];
|
||||||
|
if (actor.cell) {
|
||||||
|
if (p !== i) {
|
||||||
|
this.actors[p] = actor;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.actors.length = p;
|
||||||
|
|
||||||
// Commit the undo state at the end of each tic
|
// Commit the undo state at the end of each tic
|
||||||
this.commit();
|
this.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to move the given actor one tile in the given direction and update
|
// Try to move the given actor one tile in the given direction and update
|
||||||
// their cooldown. Return true if successful.
|
// their cooldown. Return true if successful.
|
||||||
attempt_step(actor, direction) {
|
attempt_step(actor, direction, speed = null) {
|
||||||
|
// If speed is given, we're being pushed by something so we're using
|
||||||
|
// its speed. Otherwise, use our movement speed. If we're moving onto
|
||||||
|
// a sliding tile, we'll halve it later
|
||||||
|
let check_for_slide = false;
|
||||||
|
if (speed === null) {
|
||||||
|
speed = actor.type.movement_speed;
|
||||||
|
check_for_slide = true;
|
||||||
|
}
|
||||||
|
|
||||||
let move = DIRECTIONS[direction].movement;
|
let move = DIRECTIONS[direction].movement;
|
||||||
let original_cell = actor.cell;
|
let original_cell = actor.cell;
|
||||||
|
if (!original_cell) console.error(actor);
|
||||||
let goal_x = original_cell.x + move[0];
|
let goal_x = original_cell.x + move[0];
|
||||||
let goal_y = original_cell.y + move[1];
|
let goal_y = original_cell.y + move[1];
|
||||||
|
|
||||||
@ -500,11 +531,16 @@ class Level {
|
|||||||
if (! blocked) {
|
if (! blocked) {
|
||||||
let goal_cell = this.cells[goal_y][goal_x];
|
let goal_cell = this.cells[goal_y][goal_x];
|
||||||
for (let tile of Array.from(goal_cell)) {
|
for (let tile of Array.from(goal_cell)) {
|
||||||
|
if (check_for_slide && tile.type.slide_mode) {
|
||||||
|
check_for_slide = false;
|
||||||
|
speed /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (! tile.blocks(actor, direction))
|
if (! tile.blocks(actor, direction))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (actor.type.pushes && actor.type.pushes[tile.type.name]) {
|
if (actor.type.pushes && actor.type.pushes[tile.type.name]) {
|
||||||
if (this.attempt_step(tile, direction))
|
if (this.attempt_step(tile, direction, speed))
|
||||||
// It moved out of the way!
|
// It moved out of the way!
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -533,21 +569,28 @@ class Level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We're clear!
|
// We're clear!
|
||||||
this.move_to(actor, goal_x, goal_y);
|
this.move_to(actor, goal_x, goal_y, speed);
|
||||||
|
|
||||||
|
// Set movement cooldown since we just moved
|
||||||
|
this._set_prop(actor, 'movement_cooldown', speed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the given actor to the given position and perform any appropriate
|
// Move the given actor to the given position and perform any appropriate
|
||||||
// tile interactions. Does NOT check for whether the move is actually
|
// tile interactions. Does NOT check for whether the move is actually
|
||||||
// legal; use attempt_step for that!
|
// legal; use attempt_step for that!
|
||||||
move_to(actor, x, y) {
|
move_to(actor, x, y, speed) {
|
||||||
let original_cell = actor.cell;
|
let original_cell = actor.cell;
|
||||||
if (x === original_cell.x && y === original_cell.y)
|
if (x === original_cell.x && y === original_cell.y)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
actor.previous_cell = actor.cell;
|
||||||
|
actor.animation_speed = speed;
|
||||||
|
actor.animation_progress = 0;
|
||||||
|
|
||||||
let goal_cell = this.cells[y][x];
|
let goal_cell = this.cells[y][x];
|
||||||
this.remove_tile(actor);
|
this.remove_tile(actor);
|
||||||
actor.slide_mode = null;
|
this.make_slide(actor, null);
|
||||||
this.add_tile(actor, goal_cell);
|
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
|
||||||
@ -562,11 +605,13 @@ class Level {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for hitting a monster, which is always instant and ends the
|
// Check for a couple effects that always apply immediately
|
||||||
// game right here
|
|
||||||
// TODO i guess this covers blocks too
|
// TODO i guess this covers blocks too
|
||||||
// TODO do blocks smash monsters?
|
// TODO do blocks smash monsters?
|
||||||
for (let tile of goal_cell) {
|
for (let tile of goal_cell) {
|
||||||
|
if (tile.type.slide_mode) {
|
||||||
|
this.make_slide(actor, tile.type.slide_mode);
|
||||||
|
}
|
||||||
if ((actor.type.is_player && tile.type.is_monster) ||
|
if ((actor.type.is_player && tile.type.is_monster) ||
|
||||||
(actor.type.is_monster && tile.type.is_player))
|
(actor.type.is_monster && tile.type.is_player))
|
||||||
{
|
{
|
||||||
@ -609,6 +654,7 @@ class Level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle teleporting, now that the dust has cleared
|
// Handle teleporting, now that the dust has cleared
|
||||||
|
// FIXME something funny happening here, your input isn't ignore while walking out of it?
|
||||||
let current_cell = actor.cell;
|
let current_cell = actor.cell;
|
||||||
if (teleporter) {
|
if (teleporter) {
|
||||||
let goal = teleporter.connection;
|
let goal = teleporter.connection;
|
||||||
@ -759,6 +805,9 @@ class Overlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
|
// FIXME ah, but keystrokes can still go to the game, including
|
||||||
|
// spacebar to begin it if it was waiting. how do i completely disable
|
||||||
|
// an entire chunk of the page?
|
||||||
if (this.game.state === 'playing') {
|
if (this.game.state === 'playing') {
|
||||||
this.game.set_state('paused');
|
this.game.set_state('paused');
|
||||||
}
|
}
|
||||||
@ -990,20 +1039,18 @@ const GAME_UI_HTML = `
|
|||||||
</div>
|
</div>
|
||||||
<div class="inventory"></div>
|
<div class="inventory"></div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button class="control-pause" type="button">Pause</button>
|
<div class="play-controls">
|
||||||
<button class="control-restart" type="button">Restart</button>
|
<button class="control-pause" type="button">Pause</button>
|
||||||
<button class="control-undo" type="button">Undo</button>
|
<button class="control-restart" type="button">Restart</button>
|
||||||
<button class="control-rewind" type="button">Rewind</button>
|
<button class="control-undo" type="button">Undo</button>
|
||||||
</div>
|
<button class="control-rewind" type="button">Rewind</button>
|
||||||
<div class="demo">
|
</div>
|
||||||
<h2>Solution demo available</h2>
|
|
||||||
<div class="demo-controls">
|
<div class="demo-controls">
|
||||||
<button class="demo-play" type="button">Restart and play</button>
|
<button class="demo-play" type="button">View replay</button>
|
||||||
<button class="demo-step-1" type="button">Step 1 tic</button>
|
<button class="demo-step-1" type="button">Step 1 tic</button>
|
||||||
<button class="demo-step-4" type="button">Step 1 move</button>
|
<button class="demo-step-4" type="button">Step 1 move</button>
|
||||||
<button class="demo-step-20" type="button">Step 1 second</button>
|
<div class="input"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="input"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
`;
|
`;
|
||||||
@ -1125,14 +1172,23 @@ class Game {
|
|||||||
ev.target.blur();
|
ev.target.blur();
|
||||||
});
|
});
|
||||||
// Demo playback
|
// Demo playback
|
||||||
this.container.querySelector('.demo .demo-step-1').addEventListener('click', ev => {
|
this.container.querySelector('.demo-controls .demo-play').addEventListener('click', ev => {
|
||||||
|
if (this.state === 'playing' || this.state === 'paused' || this.state === 'rewinding') {
|
||||||
|
new ConfirmOverlay(this, "Abandon your progress and watch the replay?", () => {
|
||||||
|
this.play_demo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.play_demo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.container.querySelector('.demo-controls .demo-step-1').addEventListener('click', ev => {
|
||||||
this.advance_by(1);
|
this.advance_by(1);
|
||||||
|
this._redraw();
|
||||||
});
|
});
|
||||||
this.container.querySelector('.demo .demo-step-4').addEventListener('click', ev => {
|
this.container.querySelector('.demo-controls .demo-step-4').addEventListener('click', ev => {
|
||||||
this.advance_by(4);
|
this.advance_by(4);
|
||||||
});
|
this._redraw();
|
||||||
this.container.querySelector('.demo .demo-step-20').addEventListener('click', ev => {
|
|
||||||
this.advance_by(20);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Populate inventory
|
// Populate inventory
|
||||||
@ -1224,17 +1280,6 @@ class Game {
|
|||||||
// Done with UI, now we can load a level
|
// Done with UI, now we can load a level
|
||||||
this.load_level(0);
|
this.load_level(0);
|
||||||
|
|
||||||
if (false && this.level.stored_level.demo) {
|
|
||||||
this.demo = this.level.stored_level.demo[Symbol.iterator]();
|
|
||||||
// FIXME should probably start playback on first input
|
|
||||||
this.set_state('playing');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO update these, as appropriate, when loading a level
|
|
||||||
this.input_el.style.display = 'none';
|
|
||||||
this.demo_el.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-size the level canvas, both now and on resize
|
// Auto-size the level canvas, both now and on resize
|
||||||
this.adjust_scale();
|
this.adjust_scale();
|
||||||
window.addEventListener('resize', ev => {
|
window.addEventListener('resize', ev => {
|
||||||
@ -1265,6 +1310,9 @@ class Game {
|
|||||||
this.nav_prev_button.disabled = level_index <= 0;
|
this.nav_prev_button.disabled = level_index <= 0;
|
||||||
this.nav_next_button.disabled = level_index >= this.stored_game.levels.length;
|
this.nav_next_button.disabled = level_index >= this.stored_game.levels.length;
|
||||||
|
|
||||||
|
this.demo_faucet = null;
|
||||||
|
this.container.classList.toggle('--has-demo', !!this.level.stored_level.demo);
|
||||||
|
|
||||||
this.update_ui();
|
this.update_ui();
|
||||||
// Force a redraw, which won't happen on its own since the game isn't running
|
// Force a redraw, which won't happen on its own since the game isn't running
|
||||||
this._redraw();
|
this._redraw();
|
||||||
@ -1274,11 +1322,19 @@ class Game {
|
|||||||
this.level.restart(this.compat);
|
this.level.restart(this.compat);
|
||||||
this.set_state('waiting');
|
this.set_state('waiting');
|
||||||
this.update_ui();
|
this.update_ui();
|
||||||
|
this._redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
play_demo() {
|
||||||
|
this.demo_faucet = this.level.stored_level.demo[Symbol.iterator]();
|
||||||
|
this.restart_level();
|
||||||
|
// FIXME should probably start playback on first real input
|
||||||
|
this.set_state('playing');
|
||||||
}
|
}
|
||||||
|
|
||||||
get_input() {
|
get_input() {
|
||||||
if (this.demo) {
|
if (this.demo_faucet) {
|
||||||
let step = this.demo.next();
|
let step = this.demo_faucet.next();
|
||||||
if (step.done) {
|
if (step.done) {
|
||||||
return new Set;
|
return new Set;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -487,7 +487,7 @@ export class Tileset {
|
|||||||
// TODO this is getting really ad-hoc and clumsy lol, maybe
|
// TODO this is getting really ad-hoc and clumsy lol, maybe
|
||||||
// have tiles expose a single 'state' prop or something
|
// have tiles expose a single 'state' prop or something
|
||||||
if (coords.moving) {
|
if (coords.moving) {
|
||||||
if (tile.movement_cooldown) {
|
if (tile.animation_speed) {
|
||||||
coords = coords.moving;
|
coords = coords.moving;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -150,12 +150,11 @@ const TILE_TYPES = {
|
|||||||
turtle: {
|
turtle: {
|
||||||
},
|
},
|
||||||
ice: {
|
ice: {
|
||||||
on_arrive(me, level, other) {
|
slide_mode: 'ice',
|
||||||
level.make_slide(other, 'ice');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ice_sw: {
|
ice_sw: {
|
||||||
thin_walls: new Set(['south', 'west']),
|
thin_walls: new Set(['south', 'west']),
|
||||||
|
slide_mode: 'ice',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'south') {
|
if (other.direction === 'south') {
|
||||||
other.direction = 'east';
|
other.direction = 'east';
|
||||||
@ -163,11 +162,11 @@ const TILE_TYPES = {
|
|||||||
else {
|
else {
|
||||||
other.direction = 'north';
|
other.direction = 'north';
|
||||||
}
|
}
|
||||||
level.make_slide(other, 'ice');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ice_nw: {
|
ice_nw: {
|
||||||
thin_walls: new Set(['north', 'west']),
|
thin_walls: new Set(['north', 'west']),
|
||||||
|
slide_mode: 'ice',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'north') {
|
if (other.direction === 'north') {
|
||||||
other.direction = 'east';
|
other.direction = 'east';
|
||||||
@ -175,11 +174,11 @@ const TILE_TYPES = {
|
|||||||
else {
|
else {
|
||||||
other.direction = 'south';
|
other.direction = 'south';
|
||||||
}
|
}
|
||||||
level.make_slide(other, 'ice');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ice_ne: {
|
ice_ne: {
|
||||||
thin_walls: new Set(['north', 'east']),
|
thin_walls: new Set(['north', 'east']),
|
||||||
|
slide_mode: 'ice',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'north') {
|
if (other.direction === 'north') {
|
||||||
other.direction = 'west';
|
other.direction = 'west';
|
||||||
@ -187,11 +186,11 @@ const TILE_TYPES = {
|
|||||||
else {
|
else {
|
||||||
other.direction = 'south';
|
other.direction = 'south';
|
||||||
}
|
}
|
||||||
level.make_slide(other, 'ice');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ice_se: {
|
ice_se: {
|
||||||
thin_walls: new Set(['south', 'east']),
|
thin_walls: new Set(['south', 'east']),
|
||||||
|
slide_mode: 'ice',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
if (other.direction === 'south') {
|
if (other.direction === 'south') {
|
||||||
other.direction = 'west';
|
other.direction = 'west';
|
||||||
@ -199,38 +198,37 @@ const TILE_TYPES = {
|
|||||||
else {
|
else {
|
||||||
other.direction = 'north';
|
other.direction = 'north';
|
||||||
}
|
}
|
||||||
level.make_slide(other, 'ice');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
force_floor_n: {
|
force_floor_n: {
|
||||||
|
slide_mode: 'force',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'north';
|
other.direction = 'north';
|
||||||
level.make_slide(other, 'force');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
force_floor_e: {
|
force_floor_e: {
|
||||||
|
slide_mode: 'force',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'east';
|
other.direction = 'east';
|
||||||
level.make_slide(other, 'force');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
force_floor_s: {
|
force_floor_s: {
|
||||||
|
slide_mode: 'force',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'south';
|
other.direction = 'south';
|
||||||
level.make_slide(other, 'force');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
force_floor_w: {
|
force_floor_w: {
|
||||||
|
slide_mode: 'force',
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'west';
|
other.direction = 'west';
|
||||||
level.make_slide(other, 'force');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
force_floor_all: {
|
force_floor_all: {
|
||||||
|
slide_mode: 'force',
|
||||||
// TODO ms: this is random, and an acting wall to monsters (!)
|
// TODO ms: this is random, and an acting wall to monsters (!)
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = level.get_force_floor_direction();
|
other.direction = level.get_force_floor_direction();
|
||||||
level.make_slide(other, 'force');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
bomb: {
|
bomb: {
|
||||||
@ -325,7 +323,6 @@ const TILE_TYPES = {
|
|||||||
connects_to: 'teleport_blue',
|
connects_to: 'teleport_blue',
|
||||||
connect_order: 'backward',
|
connect_order: 'backward',
|
||||||
is_teleporter: true,
|
is_teleporter: true,
|
||||||
// TODO implement 'backward'
|
|
||||||
// TODO to make this work, i need to be able to check if a spot is blocked /ahead of time/
|
// TODO to make this work, i need to be able to check if a spot is blocked /ahead of time/
|
||||||
},
|
},
|
||||||
// Buttons
|
// Buttons
|
||||||
@ -565,6 +562,14 @@ const TILE_TYPES = {
|
|||||||
chip_extra: {
|
chip_extra: {
|
||||||
is_chip: true,
|
is_chip: true,
|
||||||
is_object: true,
|
is_object: true,
|
||||||
|
blocks_monsters: true,
|
||||||
|
blocks_blocks: true,
|
||||||
|
on_arrive(me, level, other) {
|
||||||
|
if (other.type.is_player) {
|
||||||
|
level.collect_chip();
|
||||||
|
level.remove_tile(me);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
score_10: {
|
score_10: {
|
||||||
is_object: true,
|
is_object: true,
|
||||||
|
|||||||
23
style.css
23
style.css
@ -152,9 +152,8 @@ main {
|
|||||||
"level time"
|
"level time"
|
||||||
"level bonus"
|
"level bonus"
|
||||||
"level message" 1fr
|
"level message" 1fr
|
||||||
"level inventory"
|
"level inventory"
|
||||||
"level controls"
|
"controls controls"
|
||||||
"demo demo"
|
|
||||||
/* Need explicit min-content to force the hint to wrap */
|
/* Need explicit min-content to force the hint to wrap */
|
||||||
/ min-content min-content
|
/ min-content min-content
|
||||||
;
|
;
|
||||||
@ -319,18 +318,28 @@ dl.score-chart .-sum {
|
|||||||
.controls {
|
.controls {
|
||||||
grid-area: controls;
|
grid-area: controls;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
}
|
||||||
|
.play-controls,
|
||||||
|
.demo-controls {
|
||||||
|
display: flex;
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
.controls > button {
|
.play-controls {
|
||||||
flex: 1;
|
align-self: start;
|
||||||
}
|
}
|
||||||
.demo {
|
.demo-controls {
|
||||||
grid-area: demo;
|
display: none;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
main.--has-demo .demo-controls {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug stuff */
|
/* Debug stuff */
|
||||||
.input {
|
.input {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
display: none;
|
||||||
grid:
|
grid:
|
||||||
"drop up cycle" 1.5em
|
"drop up cycle" 1.5em
|
||||||
"left swap right" 1.5em
|
"left swap right" 1.5em
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user