Abort the tic and draw a final frame after the game ends; fix dead player appearance

This commit is contained in:
Eevee (Evelyn Woods) 2020-09-16 18:14:06 -06:00
parent 1d809601ae
commit fe12c599bc
4 changed files with 41 additions and 31 deletions

View File

@ -163,6 +163,8 @@ export class Cell extends Array {
} }
} }
class GameEnded extends Error {}
export class Level { export class Level {
constructor(stored_level, compat = {}) { constructor(stored_level, compat = {}) {
this.stored_level = stored_level; this.stored_level = stored_level;
@ -345,6 +347,23 @@ export class Level {
return; return;
} }
try {
this._advance_tic(p1_primary_direction, p1_secondary_direction);
}
catch (e) {
if (e instanceof GameEnded) {
// Do nothing, the game ended and we just wanted to skip the rest
}
else {
throw e;
}
}
// Commit the undo state at the end of each tic
this.commit();
}
_advance_tic(p1_primary_direction, p1_secondary_direction) {
// Player's secondary direction is set immediately; it applies on arrival to cells even if // Player's secondary direction is set immediately; it applies on arrival to cells even if
// it wasn't held the last time the player started moving // it wasn't held the last time the player started moving
this._set_prop(this.player, 'secondary_direction', p1_secondary_direction); this._set_prop(this.player, 'secondary_direction', p1_secondary_direction);
@ -352,8 +371,6 @@ export class Level {
// Used to check for a monster chomping the player's tail // Used to check for a monster chomping the player's tail
this.player_leaving_cell = this.player.cell; this.player_leaving_cell = this.player.cell;
// XXX this entire turn order is rather different in ms rules
// FIXME OK, do a pass to make everyone decide their movement, and then actually do it. the question iiis, where does that fit in with animation
// First pass: tick cooldowns and animations; have actors arrive in their cells // First pass: tick cooldowns and animations; have actors arrive in their cells
for (let actor of this.actors) { for (let actor of this.actors) {
// Actors with no cell were destroyed // Actors with no cell were destroyed
@ -554,10 +571,6 @@ export class Level {
let old_cell = actor.cell; let old_cell = actor.cell;
this.attempt_step(actor, actor.decision); this.attempt_step(actor, actor.decision);
// TODO do i need to do this more aggressively?
if (this.state === 'success' || this.state === 'failure')
break;
// Players can also bump the tiles in the cell next to the one they're leaving // Players can also bump the tiles in the cell next to the one they're leaving
let dir2 = actor.secondary_direction; let dir2 = actor.secondary_direction;
if (actor.type.is_player && dir2 && if (actor.type.is_player && dir2 &&
@ -617,9 +630,6 @@ export class Level {
this.tic_counter = tic_counter; this.tic_counter = tic_counter;
}); });
} }
// Commit the undo state at the end of each tic
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
@ -933,11 +943,13 @@ export class Level {
}); });
this.state = 'failure'; this.state = 'failure';
this.fail_message = message; this.fail_message = message;
throw new GameEnded;
} }
win() { win() {
this.pending_undo.push(() => this.state = 'playing'); this.pending_undo.push(() => this.state = 'playing');
this.state = 'success'; this.state = 'success';
throw new GameEnded;
} }
// Get the next direction a random force floor will use. They share global // Get the next direction a random force floor will use. They share global

View File

@ -501,13 +501,6 @@ class Player extends PrimaryView {
// Redraws every frame, unless the game isn't running // Redraws every frame, unless the game isn't running
redraw() { redraw() {
// FIXME draw one more frame after losing, so we can see the player explode or whatever
// TODO for bonus points, also finish the player animation (but don't advance the game any further)
if (this.state !== 'playing' && this.state !== 'rewinding') {
this._redraw_handle = null;
return;
}
// Calculate this here, not in _redraw, because that's called at weird // Calculate this here, not in _redraw, because that's called at weird
// times when the game might not have actually advanced at all yet // times when the game might not have actually advanced at all yet
// TODO this is not gonna be right while pausing lol // TODO this is not gonna be right while pausing lol
@ -516,7 +509,16 @@ class Player extends PrimaryView {
this.tic_offset = Math.min(0.9999, (performance.now() - this.last_advance) / 1000 / (1 / TICS_PER_SECOND)); this.tic_offset = Math.min(0.9999, (performance.now() - this.last_advance) / 1000 / (1 / TICS_PER_SECOND));
this._redraw(); this._redraw();
this._redraw_handle = requestAnimationFrame(this._redraw_bound);
// Check for a stopped game *after* drawing, so that if the game ends, we still draw its
// final result before stopping the draw loop
// TODO for bonus points, also finish the player animation (but don't advance the game any further)
if (this.state === 'playing' || this.state === 'rewinding') {
this._redraw_handle = requestAnimationFrame(this._redraw_bound);
}
else {
this._redraw_handle = null;
}
} }
// Actually redraw. Used to force drawing outside of normal play // Actually redraw. Used to force drawing outside of normal play

View File

@ -100,11 +100,10 @@ export class CanvasRenderer {
let y1 = Math.min(this.level.size_y - 1, Math.ceil(y0 + this.viewport_size_y)); let y1 = Math.min(this.level.size_y - 1, Math.ceil(y0 + this.viewport_size_y));
// Draw one layer at a time, so animated objects aren't overdrawn by // Draw one layer at a time, so animated objects aren't overdrawn by
// neighboring terrain // neighboring terrain
let x, y;
// XXX layer count hardcoded here // XXX layer count hardcoded here
for (let layer = 0; layer < 4; layer++) { for (let layer = 0; layer < 4; layer++) {
for (x = xf0; x <= x1; x++) { for (let x = xf0; x <= x1; x++) {
for (y = yf0; y <= y1; y++) { for (let y = yf0; y <= y1; y++) {
for (let tile of this.level.cells[y][x]) { for (let tile of this.level.cells[y][x]) {
if (tile.type.draw_layer !== layer) if (tile.type.draw_layer !== layer)
continue; continue;

View File

@ -290,8 +290,8 @@ const TILE_TYPES = {
level.transmute_tile(me, 'water'); level.transmute_tile(me, 'water');
} }
else if (other.type.is_player) { else if (other.type.is_player) {
level.fail("Oops! You can't walk on fire without fire boots!");
level.transmute_tile(other, 'player_burned'); level.transmute_tile(other, 'player_burned');
level.fail("Oops! You can't walk on fire without fire boots!");
} }
else { else {
level.remove_tile(other); level.remove_tile(other);
@ -311,8 +311,8 @@ const TILE_TYPES = {
level.transmute_tile(me, 'ice'); level.transmute_tile(me, 'ice');
} }
else if (other.type.is_player) { else if (other.type.is_player) {
level.transmute_tile(other, 'splash');
level.fail("swimming with the fishes"); level.fail("swimming with the fishes");
level.transmute_tile(other, 'player_drowned');
} }
else { else {
level.transmute_tile(other, 'splash'); level.transmute_tile(other, 'splash');
@ -431,15 +431,13 @@ const TILE_TYPES = {
}, },
bomb: { bomb: {
draw_layer: LAYER_ITEM, draw_layer: LAYER_ITEM,
// TODO explode
on_arrive(me, level, other) { on_arrive(me, level, other) {
let cell = me.cell;
level.remove_tile(me); level.remove_tile(me);
if (other.type.is_player) { let was_player = other.type.is_player;
// Check this /before/ we change it... level.transmute_tile(other, 'explosion');
if (was_player) {
level.fail("watch where you step"); level.fail("watch where you step");
} }
level.transmute_tile(other, 'explosion');
}, },
}, },
thief_tools: { thief_tools: {
@ -553,13 +551,12 @@ const TILE_TYPES = {
draw_layer: LAYER_ITEM, draw_layer: LAYER_ITEM,
is_required_chip: true, is_required_chip: true,
on_arrive(me, level, other) { on_arrive(me, level, other) {
// TODO explode
level.remove_tile(me); level.remove_tile(me);
if (other.type.is_player) { let was_player = other.type.is_player;
// Check this /before/ we change it... level.transmute_tile(other, 'explosion');
if (was_player) {
level.fail("watch where you step"); level.fail("watch where you step");
} }
level.transmute_tile(other, 'explosion');
}, },
}, },
purple_floor: { purple_floor: {