Add obituaries, and get them out of the game itself
This commit is contained in:
1 parent
fe12c599bc
commit
57810da581
6 files changed
+182
-58
No files matched your search
+15
-18
@@ -622,7 +622,7 @@ export class Level {
|
||||
|
||||
this.time_remaining -= 1;
|
||||
if (this.time_remaining <= 0) {
|
||||
this.fail("Time's up!");
|
||||
this.fail('time');
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -767,31 +767,28 @@ export class Level {
|
||||
}
|
||||
|
||||
// Check for a couple effects that always apply immediately
|
||||
// TODO i guess this covers blocks too
|
||||
// TODO do blocks smash monsters?
|
||||
for (let tile of goal_cell) {
|
||||
if (actor.type.is_player && tile.type.is_monster) {
|
||||
this.fail(tile.type.name);
|
||||
}
|
||||
else if (actor.type.is_monster && tile.type.is_player) {
|
||||
this.fail(actor.type.name);
|
||||
}
|
||||
else if (actor.type.is_block && tile.type.is_player) {
|
||||
this.fail('squished');
|
||||
}
|
||||
|
||||
if (tile.type.slide_mode && ! actor.ignores(tile.type.name)) {
|
||||
this.make_slide(actor, tile.type.slide_mode);
|
||||
}
|
||||
if ((actor.type.is_player && tile.type.is_monster) ||
|
||||
(actor.type.is_monster && tile.type.is_player))
|
||||
{
|
||||
// TODO ooh, obituaries
|
||||
this.fail("Oops! Watch out for creatures!");
|
||||
return;
|
||||
}
|
||||
if (actor.type.is_block && tile.type.is_player) {
|
||||
// TODO ooh, obituaries
|
||||
this.fail("squish");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're stepping directly on the player, that kills them too
|
||||
// TODO this only works because i have the player move first; in lynx the check is the other
|
||||
// way around
|
||||
if (actor.type.is_monster && goal_cell === this.player_leaving_cell) {
|
||||
this.fail("Oops! Watch out for creatures!");
|
||||
this.fail(actor.type.name);
|
||||
}
|
||||
|
||||
if (this.compat.tiles_react_instantly) {
|
||||
@@ -931,7 +928,7 @@ export class Level {
|
||||
this.time_remaining = 1;
|
||||
}
|
||||
else {
|
||||
this.fail("Time's up!");
|
||||
this.fail('time');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -939,10 +936,10 @@ export class Level {
|
||||
fail(message) {
|
||||
this.pending_undo.push(() => {
|
||||
this.state = 'playing';
|
||||
this.fail_message = null;
|
||||
this.fail_reason = null;
|
||||
});
|
||||
this.state = 'failure';
|
||||
this.fail_message = message;
|
||||
this.fail_reason = message;
|
||||
throw new GameEnded;
|
||||
}
|
||||
|
||||
|
||||
+132
-25
@@ -8,7 +8,7 @@ import { Level } from './game.js';
|
||||
import CanvasRenderer from './renderer-canvas.js';
|
||||
import { Tileset, CC2_TILESET_LAYOUT, TILE_WORLD_TILESET_LAYOUT } from './tileset.js';
|
||||
import TILE_TYPES from './tiletypes.js';
|
||||
import { mk, promise_event, fetch, walk_grid } from './util.js';
|
||||
import { random_choice, mk, promise_event, fetch, walk_grid } from './util.js';
|
||||
|
||||
const PAGE_TITLE = "Lexy's Labyrinth";
|
||||
// Stackable modal overlay of some kind, usually a dialog
|
||||
@@ -136,6 +136,65 @@ const ACTION_DIRECTIONS = {
|
||||
left: 'west',
|
||||
right: 'east',
|
||||
};
|
||||
const OBITUARIES = {
|
||||
drowned: [
|
||||
"player tried out water cooling",
|
||||
"player fell into the c",
|
||||
],
|
||||
burned: [
|
||||
"player's core temp got too high",
|
||||
"player's plans went up in smoke",
|
||||
],
|
||||
exploded: [
|
||||
"player didn't watch where they step",
|
||||
"player is having a blast",
|
||||
"player tripped over something of mine",
|
||||
],
|
||||
squished: [
|
||||
"player was overwhelmed by a block of ram",
|
||||
"player became two-dimensional",
|
||||
],
|
||||
time: [
|
||||
"player tried to overclock",
|
||||
"player's time ran out",
|
||||
"player's speedrun went badly",
|
||||
],
|
||||
generic: [
|
||||
"player had a bad time",
|
||||
],
|
||||
|
||||
// Specific creatures
|
||||
ball: [
|
||||
"player is having a ball",
|
||||
],
|
||||
walker: [
|
||||
"player let it walk all over them",
|
||||
],
|
||||
fireball: [
|
||||
"player had a meltdown",
|
||||
],
|
||||
glider: [
|
||||
"player's ship came in",
|
||||
],
|
||||
tank_blue: [
|
||||
"player didn't watch where they tread",
|
||||
],
|
||||
tank_yellow: [
|
||||
"player let things get out of control",
|
||||
],
|
||||
bug: [
|
||||
"player has ants in their pants",
|
||||
],
|
||||
paramecium: [
|
||||
"player has the creepy crawlies",
|
||||
],
|
||||
teeth: [
|
||||
"player got a mega bite",
|
||||
],
|
||||
blob: [
|
||||
"player didn't do a gooed job",
|
||||
],
|
||||
};
|
||||
class Player extends PrimaryView {
|
||||
constructor(conductor) {
|
||||
super(conductor, document.body.querySelector('main#player'));
|
||||
@@ -163,12 +222,12 @@ class Player extends PrimaryView {
|
||||
this.root.style.setProperty('--tile-width', `${this.conductor.tileset.size_x}px`);
|
||||
this.root.style.setProperty('--tile-height', `${this.conductor.tileset.size_y}px`);
|
||||
this.level_el = this.root.querySelector('.level');
|
||||
this.overlay_message_el = this.root.querySelector('.overlay-message');
|
||||
this.message_el = this.root.querySelector('.message');
|
||||
this.chips_el = this.root.querySelector('.chips output');
|
||||
this.time_el = this.root.querySelector('.time output');
|
||||
this.bonus_el = this.root.querySelector('.bonus output');
|
||||
this.inventory_el = this.root.querySelector('.inventory');
|
||||
this.bummer_el = this.root.querySelector('.bummer');
|
||||
this.input_el = this.root.querySelector('.input');
|
||||
this.demo_el = this.root.querySelector('.demo');
|
||||
|
||||
@@ -589,42 +648,90 @@ class Player extends PrimaryView {
|
||||
|
||||
this.state = new_state;
|
||||
|
||||
// Populate the overlay
|
||||
let overlay_reason = '';
|
||||
let overlay_top = '';
|
||||
let overlay_middle = null;
|
||||
let overlay_bottom = '';
|
||||
if (this.state === 'waiting') {
|
||||
this.bummer_el.textContent = "Ready!";
|
||||
}
|
||||
else if (this.state === 'playing' || this.state === 'rewinding') {
|
||||
this.bummer_el.textContent = "";
|
||||
overlay_reason = 'waiting';
|
||||
overlay_middle = "Ready!";
|
||||
}
|
||||
else if (this.state === 'paused') {
|
||||
this.bummer_el.textContent = "/// paused ///";
|
||||
overlay_reason = 'paused';
|
||||
overlay_bottom = "/// paused ///";
|
||||
}
|
||||
else if (this.state === 'stopped') {
|
||||
if (this.level.state === 'failure') {
|
||||
this.bummer_el.textContent = this.level.fail_message;
|
||||
overlay_reason = 'failure';
|
||||
overlay_top = "whoops";
|
||||
let obits = OBITUARIES[this.level.fail_reason] ?? OBITUARIES['generic'];
|
||||
overlay_bottom = random_choice(obits);
|
||||
}
|
||||
else {
|
||||
this.bummer_el.textContent = "";
|
||||
overlay_reason = 'success';
|
||||
let base = (this.conductor.level_index + 1) * 500;
|
||||
let time = Math.ceil((this.level.time_remaining ?? 0) / 20) * 10;
|
||||
this.bummer_el.append(
|
||||
mk('p', "go bit buster!"),
|
||||
mk('dl.score-chart',
|
||||
mk('dt', "base score"),
|
||||
mk('dd', base),
|
||||
mk('dt', "time bonus"),
|
||||
mk('dd', `+ ${time}`),
|
||||
mk('dt', "score bonus"),
|
||||
mk('dd', `+ ${this.level.bonus_points}`),
|
||||
mk('dt.-sum', "level score"),
|
||||
mk('dd.-sum', base + time + this.level.bonus_points),
|
||||
mk('dt', "improvement"),
|
||||
mk('dd', "(TODO)"),
|
||||
mk('dt', "total score"),
|
||||
mk('dd', "(TODO)"),
|
||||
),
|
||||
// Pick a success message
|
||||
// TODO done on first try; took many tries
|
||||
let time_left_fraction = null;
|
||||
if (this.level.time_remaining !== null && this.level.stored_level.time_limit !== null) {
|
||||
time_left_fraction = this.level.time_remaining / 20 / this.level.stored_level.time_limit;
|
||||
}
|
||||
|
||||
if (this.level.chips_remaining > 0) {
|
||||
overlay_top = random_choice([
|
||||
"socket to em!", "go bug blaster!",
|
||||
]);
|
||||
}
|
||||
else if (this.level.time_remaining && this.level.time_remaining < 200) {
|
||||
overlay_top = random_choice([
|
||||
"in the nick of time!", "cutting it close!",
|
||||
]);
|
||||
}
|
||||
else if (time_left_fraction !== null && time_left_fraction > 1) {
|
||||
overlay_top = random_choice([
|
||||
"faster than light!", "impossible speed!", "pipelined!",
|
||||
]);
|
||||
}
|
||||
else if (time_left_fraction !== null && time_left_fraction > 0.75) {
|
||||
overlay_top = random_choice([
|
||||
"lightning quick!", "nice speedrun!", "eagerly evaluated!",
|
||||
]);
|
||||
}
|
||||
else {
|
||||
overlay_top = random_choice([
|
||||
"you did it!", "nice going!", "great job!", "good work!",
|
||||
"onwards!", "tubular!", "yeehaw!", "hot damn!",
|
||||
"alphanumeric!", "nice dynamic typing!",
|
||||
]);
|
||||
}
|
||||
overlay_bottom = "press spacebar to continue";
|
||||
|
||||
overlay_middle = mk('dl.score-chart',
|
||||
mk('dt', "base score"),
|
||||
mk('dd', base),
|
||||
mk('dt', "time bonus"),
|
||||
mk('dd', `+ ${time}`),
|
||||
mk('dt', "score bonus"),
|
||||
mk('dd', `+ ${this.level.bonus_points}`),
|
||||
mk('dt.-sum', "level score"),
|
||||
mk('dd.-sum', base + time + this.level.bonus_points),
|
||||
mk('dt', "improvement"),
|
||||
mk('dd', "(TODO)"),
|
||||
mk('dt', "total score"),
|
||||
mk('dd', "(TODO)"),
|
||||
);
|
||||
}
|
||||
}
|
||||
this.overlay_message_el.setAttribute('data-reason', overlay_reason);
|
||||
this.overlay_message_el.querySelector('.-top').textContent = overlay_top;
|
||||
this.overlay_message_el.querySelector('.-bottom').textContent = overlay_bottom;
|
||||
let middle = this.overlay_message_el.querySelector('.-middle');
|
||||
middle.textContent = '';
|
||||
if (overlay_middle) {
|
||||
middle.append(overlay_middle);
|
||||
}
|
||||
|
||||
// The advance and redraw methods run in a loop, but they cancel
|
||||
// themselves if the game isn't running, so restart them here
|
||||
|
||||
+4
-4
@@ -291,7 +291,7 @@ const TILE_TYPES = {
|
||||
}
|
||||
else if (other.type.is_player) {
|
||||
level.transmute_tile(other, 'player_burned');
|
||||
level.fail("Oops! You can't walk on fire without fire boots!");
|
||||
level.fail('burned');
|
||||
}
|
||||
else {
|
||||
level.remove_tile(other);
|
||||
@@ -312,7 +312,7 @@ const TILE_TYPES = {
|
||||
}
|
||||
else if (other.type.is_player) {
|
||||
level.transmute_tile(other, 'splash');
|
||||
level.fail("swimming with the fishes");
|
||||
level.fail('drowned');
|
||||
}
|
||||
else {
|
||||
level.transmute_tile(other, 'splash');
|
||||
@@ -436,7 +436,7 @@ const TILE_TYPES = {
|
||||
let was_player = other.type.is_player;
|
||||
level.transmute_tile(other, 'explosion');
|
||||
if (was_player) {
|
||||
level.fail("watch where you step");
|
||||
level.fail('exploded');
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -555,7 +555,7 @@ const TILE_TYPES = {
|
||||
let was_player = other.type.is_player;
|
||||
level.transmute_tile(other, 'explosion');
|
||||
if (was_player) {
|
||||
level.fail("watch where you step");
|
||||
level.fail('exploded');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
export function random_choice(list) {
|
||||
return list[Math.floor(Math.random() * list.length)];
|
||||
}
|
||||
|
||||
export function mk(tag_selector, ...children) {
|
||||
let [tag, ...classes] = tag_selector.split('.');
|
||||
let el = document.createElement(tag);
|
||||
|
||||
Reference in new issue
Block a user