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

View File

@ -1140,9 +1140,11 @@ export class Level {
if (! actor.cell) if (! actor.cell)
continue; continue;
// Only count when they're on a tile, not in transit! // 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'); let emitting = actor.movement_cooldown === 0 && actor.has_item('lightning_bolt');
if (emitting) { if (emitting) {
neighbors.push([actor.cell, emitting]); neighbors.push([actor.cell, 0x0f]);
} }
if (emitting !== actor.emitting_edges) { if (emitting !== actor.emitting_edges) {
any_changed = true; any_changed = true;
@ -1227,8 +1229,10 @@ export class Level {
else { else {
// No tunnel; this is easy // No tunnel; this is easy
neighbor = this.get_neighboring_cell(cell, direction); neighbor = this.get_neighboring_cell(cell, direction);
if (neighbor) {
neighbor_wire = neighbor.get_wired_tile(); neighbor_wire = neighbor.get_wired_tile();
} }
}
if (neighbor && (neighbor.powered_edges & opposite_bit) === 0 && if (neighbor && (neighbor.powered_edges & opposite_bit) === 0 &&
// Unwired tiles are OK; they might be something activated by power. // 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 { mk } from './util.js';
import TILE_TYPES from './tiletypes.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)); 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
// XXX layer count hardcoded here
// FIXME this is a bit inefficient when there are a lot of rarely-used layers; consider // 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? // 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 x = xf0; x <= x1; x++) {
for (let 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;
let vx, vy;
if (tile.type.is_actor && if (tile.type.is_actor &&
// FIXME kind of a hack for the editor, which uses bare tile objects // FIXME kind of a hack for the editor, which uses bare tile objects
tile.visual_position) tile.visual_position)
{ {
// Handle smooth scrolling // 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! // Round this to the pixel grid too!
vx = Math.floor(vx * tw + 0.5) / tw; vx = Math.floor(vx * tw + 0.5) / tw;
vy = Math.floor(vy * th + 0.5) / th; 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 { else {
// Non-actors can't move // Non-actors can't move
this.tileset.draw(tile, tic, (sx, sy, dx = 0, dy = 0, w = 1, h = w) => vx = x;
this.blit(this.ctx, sx, sy, x - x0 + dx, y - y0 + dy, w, h)); 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 // TODO really need to specify this format more concretely, whoof
// XXX special kinds of drawing i know this has for a fact: // 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 // - letter tiles draw from a block (one of two blocks!) of half-tiles onto the center of the base
// - slime and walkers have double-size tiles when moving
// - force floors are cropped from a double-size tile // - 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 // - 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 // - 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 // - 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 = { export const CC2_TILESET_LAYOUT = {
'#wire-width': 1/16, '#wire-width': 1/16,
@ -76,8 +96,8 @@ export const CC2_TILESET_LAYOUT = {
green_chip: [9, 3], green_chip: [9, 3],
chip_extra: [10, 3], chip_extra: [10, 3],
chip: [11, 3], chip: [11, 3],
// bribe bribe: [12, 3],
// mercury boot speed_boots: [13, 3],
// canopy, xray // canopy, xray
// TODO lit // TODO lit
@ -112,7 +132,7 @@ export const CC2_TILESET_LAYOUT = {
cleats: [2, 6], cleats: [2, 6],
suction_boots: [3, 6], suction_boots: [3, 6],
hiking_boots: [4, 6], hiking_boots: [4, 6],
// speed boots...? not boots though lightning_bolt: [5, 6],
// weird translucent spiral // weird translucent spiral
// weird translucent red // weird translucent red
button_blue: [8, 6], 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]], force_floor_all: [[0, 21], [1, 21], [2, 21], [3, 21], [4, 21], [5, 21], [6, 21], [7, 21]],
// latches // latches
// switch light_switch_off: {
base: [14, 21],
wired: [12, 21],
},
light_switch_on: {
base: [14, 21],
wired: [13, 21],
},
thief_keys: [15, 21], thief_keys: [15, 21],
player: { player: {
@ -376,6 +403,12 @@ export const CC2_TILESET_LAYOUT = {
logic_gate: { logic_gate: {
// TODO currently, 'wired' can't coexist with visual state etc... // TODO currently, 'wired' can't coexist with visual state etc...
// TODO *long sigh* of course, logic gates have parts with independent current too // 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': { 'latch-ccw': {
north: [8, 21], north: [8, 21],
east: [9, 21], east: [9, 21],
@ -742,8 +775,8 @@ export class Tileset {
tile.cell && tile.cell.powered_edges & DIRECTIONS['north'].bit ? '#powered' : '#unpowered']; tile.cell && tile.cell.powered_edges & DIRECTIONS['north'].bit ? '#powered' : '#unpowered'];
let wire_coords_ew = this.layout[ let wire_coords_ew = this.layout[
tile.cell && tile.cell.powered_edges & DIRECTIONS['east'].bit ? '#powered' : '#unpowered']; 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_ns[0], 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_ew[0], wire_coords_ew[1], 0, wire_inset, 1, wire_radius * 2);
// Draw the cross tile on top // Draw the cross tile on top
coords = drawspec.wired_cross ?? drawspec.wired; coords = drawspec.wired_cross ?? drawspec.wired;
@ -771,7 +804,7 @@ export class Tileset {
x0 = 0; x0 = 0;
} }
let wire_coords = this.layout[tile.cell && tile.cell.powered_edges ? '#powered' : '#unpowered']; 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 // Then draw the wired tile on top of it all
coords = drawspec.wired; coords = drawspec.wired;
@ -877,7 +910,7 @@ export class Tileset {
// Continue on with masking // Continue on with masking
coords = drawspec.tile; coords = drawspec.tile;
let [x0, y0, w, h] = drawspec.mask; 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 { else {
if (!coords) console.error(name, tile); if (!coords) console.error(name, tile);
@ -891,38 +924,41 @@ export class Tileset {
let tunnel_length = 12/32; let tunnel_length = 12/32;
let tunnel_offset = (1 - tunnel_width) / 2; let tunnel_offset = (1 - tunnel_width) / 2;
if (tile.wire_tunnel_directions & DIRECTIONS['north'].bit) { 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); tunnel_offset, 0, tunnel_width, tunnel_length);
} }
if (tile.wire_tunnel_directions & DIRECTIONS['south'].bit) { 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); tunnel_offset, 1 - tunnel_length, tunnel_width, tunnel_length);
} }
if (tile.wire_tunnel_directions & DIRECTIONS['west'].bit) { 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); 0, tunnel_offset, tunnel_length, tunnel_width);
} }
if (tile.wire_tunnel_directions & DIRECTIONS['east'].bit) { 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); 1 - tunnel_length, tunnel_offset, tunnel_length, tunnel_width);
} }
} }
// Directional blocks have arrows drawn on top // Directional blocks have arrows drawn on top
// TODO does cc2 draw even if there are no arrows?
if (drawspec.arrows && tile && tile.arrows) { if (drawspec.arrows && tile && tile.arrows) {
let [x, y] = drawspec.arrows; let [x, y] = drawspec.arrows;
let x0 = 0.25, y0 = 0.25, x1 = 0.75, y1 = 0.75;
if (tile.arrows.has('north')) { if (tile.arrows.has('north')) {
blit(x, y, 0, 0, 1, 0.25); y0 = 0;
} }
if (tile.arrows.has('east')) { if (tile.arrows.has('east')) {
blit(x + 0.75, y, 0.75, 0, 0.25, 1); x1 = 1;
} }
if (tile.arrows.has('south')) { if (tile.arrows.has('south')) {
blit(x, y + 0.75, 0, 0.75, 1, 0.25); y1 = 1;
} }
if (tile.arrows.has('west')) { 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 // Special behavior for special objects
@ -952,7 +988,7 @@ export class Tileset {
sy = (letter_spec.y0 + Math.floor(n / w)) * scale; sy = (letter_spec.y0 + Math.floor(n / w)) * scale;
} }
let offset = (1 - scale) / 2; 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; 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 // Time alteration
stopwatch_bonus: { stopwatch_bonus: {
@ -1453,6 +1474,20 @@ const TILE_TYPES = {
is_tool: true, is_tool: true,
blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid, 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 // Progression
player: { player: {