Simplify blitting; fix arrow blitting; impl light switch; load more items
This commit is contained in:
parent
6c6ce8f344
commit
ac6e33bb6c
@ -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',
|
||||
|
||||
@ -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,7 +1229,9 @@ export class Level {
|
||||
else {
|
||||
// No tunnel; this is easy
|
||||
neighbor = this.get_neighboring_cell(cell, direction);
|
||||
neighbor_wire = neighbor.get_wired_tile();
|
||||
if (neighbor) {
|
||||
neighbor_wire = neighbor.get_wired_tile();
|
||||
}
|
||||
}
|
||||
|
||||
if (neighbor && (neighbor.powered_edges & opposite_bit) === 0 &&
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user