Abort the tic and draw a final frame after the game ends; fix dead player appearance
This commit is contained in:
parent
1d809601ae
commit
fe12c599bc
30
js/game.js
30
js/game.js
@ -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
|
||||||
|
|||||||
18
js/main.js
18
js/main.js
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user