Simplify blitting; fix arrow blitting; impl light switch; load more items

This commit is contained in:
Eevee (Evelyn Woods) 2020-11-25 01:14:15 -07:00
parent 6c6ce8f344
commit ac6e33bb6c
5 changed files with 122 additions and 39 deletions

View File

@ -405,7 +405,7 @@ const TILE_ENCODING = {
error: "Timid chomper is not yet implemented, sorry!",
},
0x58: {
// TODO??? unused in main levels -- name: 'doppelganger2',
// TODO??? unused in main levels -- name: '',
has_next: true,
extra_args: [arg_direction],
error: "Explosion animation is not implemented, sorry!",
@ -465,7 +465,6 @@ const TILE_ENCODING = {
0x62: {
name: 'lightning_bolt',
has_next: true,
error: "The lightning bolt is not yet implemented, sorry!",
},
0x63: {
name: 'tank_yellow',
@ -609,11 +608,11 @@ const TILE_ENCODING = {
},
0x88: {
name: 'light_switch_off',
error: "The light switch is not yet implemented, sorry!",
modifier: modifier_wire,
},
0x89: {
name: 'light_switch_on',
error: "The light switch is not yet implemented, sorry!",
modifier: modifier_wire,
},
0x8a: {
name: 'thief_keys',
@ -637,12 +636,10 @@ const TILE_ENCODING = {
0x8f: {
name: 'bribe',
has_next: true,
error: "The bribe is not yet implemented, sorry!",
},
0x90: {
name: 'speed_boots',
has_next: true,
error: "The speed boots are not yet implemented, sorry!",
},
0x91: {
name: 'hook',

View File

@ -1140,9 +1140,11 @@ export class Level {
if (! actor.cell)
continue;
// Only count when they're on a tile, not in transit!
// FIXME this causes us to power mechanisms next to us, but we're only supposed to touch
// wire we're standing on?
let emitting = actor.movement_cooldown === 0 && actor.has_item('lightning_bolt');
if (emitting) {
neighbors.push([actor.cell, emitting]);
neighbors.push([actor.cell, 0x0f]);
}
if (emitting !== actor.emitting_edges) {
any_changed = true;
@ -1227,8 +1229,10 @@ export class Level {
else {
// No tunnel; this is easy
neighbor = this.get_neighboring_cell(cell, direction);
if (neighbor) {
neighbor_wire = neighbor.get_wired_tile();
}
}
if (neighbor && (neighbor.powered_edges & opposite_bit) === 0 &&
// Unwired tiles are OK; they might be something activated by power.

View File

@ -1,4 +1,4 @@
import { DIRECTIONS } from './defs.js';
import { DIRECTIONS, DRAW_LAYERS } from './defs.js';
import { mk } from './util.js';
import TILE_TYPES from './tiletypes.js';
@ -125,33 +125,44 @@ export class CanvasRenderer {
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
// neighboring terrain
// XXX layer count hardcoded here
// FIXME this is a bit inefficient when there are a lot of rarely-used layers; consider
// instead drawing everything under actors, then actors, then everything above actors?
for (let layer = 0; layer < 5; layer++) {
for (let layer = 0; layer < DRAW_LAYERS.MAX; layer++) {
for (let x = xf0; x <= x1; x++) {
for (let y = yf0; y <= y1; y++) {
for (let tile of this.level.cells[y][x]) {
if (tile.type.draw_layer !== layer)
continue;
let vx, vy;
if (tile.type.is_actor &&
// FIXME kind of a hack for the editor, which uses bare tile objects
tile.visual_position)
{
// Handle smooth scrolling
let [vx, vy] = tile.visual_position(tic_offset);
[vx, vy] = tile.visual_position(tic_offset);
// Round this to the pixel grid too!
vx = Math.floor(vx * tw + 0.5) / tw;
vy = Math.floor(vy * th + 0.5) / th;
this.tileset.draw(tile, tic, (sx, sy, dx = 0, dy = 0, w = 1, h = w) =>
this.blit(this.ctx, sx, sy, vx - x0 + dx, vy - y0 + dy, w, h));
}
else {
// Non-actors can't move
this.tileset.draw(tile, tic, (sx, sy, dx = 0, dy = 0, w = 1, h = w) =>
this.blit(this.ctx, sx, sy, x - x0 + dx, y - y0 + dy, w, h));
vx = x;
vy = y;
}
// Note that the blit we pass to the tileset has a different signature:
// blit(
// source_tile_x, source_tile_y,
// mask_x = 0, mask_y = 0, mask_width = 1, mask_height = mask_width,
// mask_dx = mask_x, mask_dy = mask_y)
// This makes it easier to use in the extremely common case of drawing
// part of one tile atop another tile, but still aligned to the grid.
this.tileset.draw(tile, tic, (tx, ty, mx = 0, my = 0, mw = 1, mh = mw, mdx = mx, mdy = my) =>
this.blit(this.ctx,
tx + mx, ty + my,
vx - x0 + mdx, vy - y0 + mdy,
mw, mh));
}
}
}

View File

@ -3,16 +3,36 @@ import TILE_TYPES from './tiletypes.js';
// TODO really need to specify this format more concretely, whoof
// XXX special kinds of drawing i know this has for a fact:
// - letter tiles draw from a block of half-tiles onto the center of the base
// - slime and walkers have double-size tiles when moving
// - letter tiles draw from a block (one of two blocks!) of half-tiles onto the center of the base
// - force floors are cropped from a double-size tile
// - wired tiles are a whole thing
// - wired tiles are a whole thing (floor)
// - thin walls are packed into just two tiles
// - rover has a half-tile overlay for its direction?
// - railroad tracks overlay a Lot
// - directional blocks have arrows in an awkward layout, not 4x4 grid but actually positioned on the edges
// - green and purple toggle walls use an overlay
// - turtles use an overlay
// - turtles use an overlay, seem to pick a tile at random every so often
// - animations are common, should maybe have configurable timing??
// - custom floors and walls /should/ be consolidated into a single tile probably
// - thin walls should probably be consolidated?
// - traps have a state
// special features i currently have
// - directions for actors, can be used anywhere
// - arrows: for directional blocks
// - mask: for thin walls (though the idea is useful in many more places)
// - wired: for wired tiles
// - overlay: for green/purple walls mostly, also some bogus cc1 tiles
// things that are currently NOT handled
// - bomb is supposed to have a fuse
// - critters should only animate when moving
// - rover animation depends on behavior, also has a quarter-tile overlay for its direction
// - slime and walkers have double-size tiles when moving
// - logic gates draw over the stuff underneath them
// - railroad tracks overlay a Lot
// - canopy, at all
// - swivel's floor (eugh)
// - xray vision
// - editor vision
export const CC2_TILESET_LAYOUT = {
'#wire-width': 1/16,
@ -76,8 +96,8 @@ export const CC2_TILESET_LAYOUT = {
green_chip: [9, 3],
chip_extra: [10, 3],
chip: [11, 3],
// bribe
// mercury boot
bribe: [12, 3],
speed_boots: [13, 3],
// canopy, xray
// TODO lit
@ -112,7 +132,7 @@ export const CC2_TILESET_LAYOUT = {
cleats: [2, 6],
suction_boots: [3, 6],
hiking_boots: [4, 6],
// speed boots...? not boots though
lightning_bolt: [5, 6],
// weird translucent spiral
// weird translucent red
button_blue: [8, 6],
@ -300,7 +320,14 @@ export const CC2_TILESET_LAYOUT = {
force_floor_all: [[0, 21], [1, 21], [2, 21], [3, 21], [4, 21], [5, 21], [6, 21], [7, 21]],
// latches
// switch
light_switch_off: {
base: [14, 21],
wired: [12, 21],
},
light_switch_on: {
base: [14, 21],
wired: [13, 21],
},
thief_keys: [15, 21],
player: {
@ -376,6 +403,12 @@ export const CC2_TILESET_LAYOUT = {
logic_gate: {
// TODO currently, 'wired' can't coexist with visual state etc...
// TODO *long sigh* of course, logic gates have parts with independent current too
// for a north-facing gate with two inputs, we have:
// - north output: just a wire, doesn't include center. -r 0 w -r
// - west output: bottom left quadrant, except for the middle wire part, but including the
// horizontal wire. 0 -r -r +r
// - east output: same but includes middle wire. -r -r +r +r
// TODO check if they're flipped for latches facing the other way
'latch-ccw': {
north: [8, 21],
east: [9, 21],
@ -742,8 +775,8 @@ export class Tileset {
tile.cell && tile.cell.powered_edges & DIRECTIONS['north'].bit ? '#powered' : '#unpowered'];
let wire_coords_ew = this.layout[
tile.cell && tile.cell.powered_edges & DIRECTIONS['east'].bit ? '#powered' : '#unpowered'];
blit(wire_coords_ns[0] + wire_inset, wire_coords_ns[1], wire_inset, 0, wire_radius * 2, 1);
blit(wire_coords_ew[0], wire_coords_ew[1] + wire_inset, 0, wire_inset, 1, wire_radius * 2);
blit(wire_coords_ns[0], wire_coords_ns[1], wire_inset, 0, wire_radius * 2, 1);
blit(wire_coords_ew[0], wire_coords_ew[1], 0, wire_inset, 1, wire_radius * 2);
// Draw the cross tile on top
coords = drawspec.wired_cross ?? drawspec.wired;
@ -771,7 +804,7 @@ export class Tileset {
x0 = 0;
}
let wire_coords = this.layout[tile.cell && tile.cell.powered_edges ? '#powered' : '#unpowered'];
blit(wire_coords[0] + x0, wire_coords[1] + y0, x0, y0, x1 - x0, y1 - y0);
blit(wire_coords[0], wire_coords[1], x0, y0, x1 - x0, y1 - y0);
// Then draw the wired tile on top of it all
coords = drawspec.wired;
@ -877,7 +910,7 @@ export class Tileset {
// Continue on with masking
coords = drawspec.tile;
let [x0, y0, w, h] = drawspec.mask;
blit(coords[0] + x0, coords[1] + y0, x0, y0, w, h);
blit(coords[0], coords[1], x0, y0, w, h);
}
else {
if (!coords) console.error(name, tile);
@ -891,38 +924,41 @@ export class Tileset {
let tunnel_length = 12/32;
let tunnel_offset = (1 - tunnel_width) / 2;
if (tile.wire_tunnel_directions & DIRECTIONS['north'].bit) {
blit(tunnel_coords[0] + tunnel_offset, tunnel_coords[1],
blit(tunnel_coords[0], tunnel_coords[1],
tunnel_offset, 0, tunnel_width, tunnel_length);
}
if (tile.wire_tunnel_directions & DIRECTIONS['south'].bit) {
blit(tunnel_coords[0] + tunnel_offset, tunnel_coords[1] + 1 - tunnel_length,
blit(tunnel_coords[0], tunnel_coords[1],
tunnel_offset, 1 - tunnel_length, tunnel_width, tunnel_length);
}
if (tile.wire_tunnel_directions & DIRECTIONS['west'].bit) {
blit(tunnel_coords[0], tunnel_coords[1] + tunnel_offset,
blit(tunnel_coords[0], tunnel_coords[1],
0, tunnel_offset, tunnel_length, tunnel_width);
}
if (tile.wire_tunnel_directions & DIRECTIONS['east'].bit) {
blit(tunnel_coords[0] + 1 - tunnel_length, tunnel_coords[1] + tunnel_offset,
blit(tunnel_coords[0], tunnel_coords[1],
1 - tunnel_length, tunnel_offset, tunnel_length, tunnel_width);
}
}
// Directional blocks have arrows drawn on top
// TODO does cc2 draw even if there are no arrows?
if (drawspec.arrows && tile && tile.arrows) {
let [x, y] = drawspec.arrows;
let x0 = 0.25, y0 = 0.25, x1 = 0.75, y1 = 0.75;
if (tile.arrows.has('north')) {
blit(x, y, 0, 0, 1, 0.25);
y0 = 0;
}
if (tile.arrows.has('east')) {
blit(x + 0.75, y, 0.75, 0, 0.25, 1);
x1 = 1;
}
if (tile.arrows.has('south')) {
blit(x, y + 0.75, 0, 0.75, 1, 0.25);
y1 = 1;
}
if (tile.arrows.has('west')) {
blit(x, y, 0, 0, 0.25, 1);
x0 = 0;
}
blit(x, y, x0, y0, x1 - x0, y1 - y0);
}
// Special behavior for special objects
@ -952,7 +988,7 @@ export class Tileset {
sy = (letter_spec.y0 + Math.floor(n / w)) * scale;
}
let offset = (1 - scale) / 2;
blit(sx, sy, offset, offset, scale, scale);
blit(sx, sy, 0, 0, 0.5, 0.5, offset, offset);
}
}
}

View File

@ -1157,6 +1157,27 @@ const TILE_TYPES = {
return me.gate_type;
},
},
// Light switches, kinda like the pink/black buttons but persistent
light_switch_off: {
draw_layer: DRAW_LAYERS.terrain,
is_power_source: true,
get_emitting_edges(me, level) {
return 0;
},
on_arrive(me, level, other) {
level.transmute_tile(me, 'light_switch_on');
},
},
light_switch_on: {
draw_layer: DRAW_LAYERS.terrain,
is_power_source: true,
get_emitting_edges(me, level) {
return me.wire_directions;
},
on_arrive(me, level, other) {
level.transmute_tile(me, 'light_switch_off');
},
},
// Time alteration
stopwatch_bonus: {
@ -1453,6 +1474,20 @@ const TILE_TYPES = {
is_tool: true,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
},
speed_boots: {
// TODO not implemented
draw_layer: DRAW_LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
},
bribe: {
// TODO not implemented
draw_layer: DRAW_LAYERS.item,
is_item: true,
is_tool: true,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid,
},
// Progression
player: {