diff --git a/js/defs.js b/js/defs.js index 2431ee4..5d8ee47 100644 --- a/js/defs.js +++ b/js/defs.js @@ -3,24 +3,28 @@ export const TICS_PER_SECOND = 20; export const DIRECTIONS = { north: { movement: [0, -1], + bit: 0x01, left: 'west', right: 'east', opposite: 'south', }, south: { movement: [0, 1], + bit: 0x04, left: 'east', right: 'west', opposite: 'north', }, west: { movement: [-1, 0], + bit: 0x08, left: 'south', right: 'north', opposite: 'east', }, east: { movement: [1, 0], + bit: 0x02, left: 'north', right: 'south', opposite: 'west', diff --git a/js/format-c2m.js b/js/format-c2m.js index a2c0ea6..75a6d83 100644 --- a/js/format-c2m.js +++ b/js/format-c2m.js @@ -64,9 +64,15 @@ class CC2Demo { } } + +function parse_modifier_wire(tile, modifier) { + tile.wire_directions = modifier & 0x0f; + tile.wire_tunnel_directions = (modifier & 0xf0) >> 4; +} + // TODO assert that direction + next match the tile types const TILE_ENCODING = { - 0x01: ['#mod', 'floor'], // XXX wire + 0x01: ['floor', parse_modifier_wire], 0x02: 'wall', 0x03: 'ice', 0x04: 'ice_sw', @@ -81,8 +87,8 @@ const TILE_ENCODING = { 0x0d: 'force_floor_w', 0x0e: 'green_wall', 0x0f: 'green_floor', - 0x10: ['#mod', 'teleport_red'], // XXX wire - 0x11: ['#mod', 'teleport_blue'], // XXX wire + 0x10: ['teleport_red', parse_modifier_wire], + 0x11: ['teleport_blue', parse_modifier_wire], 0x12: 'teleport_yellow', 0x13: 'teleport_green', 0x14: 'exit', @@ -143,9 +149,9 @@ const TILE_ENCODING = { 0x4b: ['swivel_se', 'swivel_floor'], 0x4c: ['stopwatch_bonus', '#next'], 0x4d: ['stopwatch_toggle', '#next'], - 0x4e: ['#mod', 'transmogrifier'], // XXX wire + 0x4e: ['transmogrifier', parse_modifier_wire], 0x4f: ['#mod', 'railroad'], - 0x50: ['#mod', 'steel'], // XXX wire + 0x50: ['steel', parse_modifier_wire], 0x51: ['dynamite', '#next'], 0x52: ['helmet', '#next'], 0x56: ['player2', '#direction', '#next'], @@ -155,7 +161,7 @@ const TILE_ENCODING = { 0x5a: ['no_player2_sign'], 0x5b: ['no_player1_sign'], // 0x5c: Inverter gate (N) : Modifier allows other gates, see below - 0x5e: ['#mod', 'button_pink'], // XXX wire + 0x5e: ['button_pink', parse_modifier_wire], 0x5f: 'flame_jet_off', 0x60: 'flame_jet_on', 0x61: 'button_orange', @@ -188,7 +194,7 @@ const TILE_ENCODING = { 0x82: ['floor_mimic', '#direction', '#next'], 0x83: ['green_bomb', '#next'], 0x84: ['green_chip', '#next'], - 0x87: ['#mod', 'button_black'], // XXX wire + 0x87: ['button_black', parse_modifier_wire], // 0x88: ON/OFF switch (OFF) : // 0x89: ON/OFF switch (ON) : 0x8a: 'thief_keys', @@ -407,10 +413,14 @@ export function parse_level(buf) { modifier = map_view.getUint32(p, true); p += 4; } + [name, ...args] = read_spec(); + /* + * TODO check for modifier expected and warn? let mod_marker; [mod_marker, name, ...args] = read_spec(); if (mod_marker !== '#mod') throw new Error(`Expected a tile requiring a modifier; got ${name}`); + */ } if (name === '#mod') { @@ -502,6 +512,10 @@ export function parse_level(buf) { } tile.directional_block_arrows = arrows; } + else if (arg instanceof Function) { + // Functions handle modifiers + arg(tile, modifier); + } else { // Anything else is an implicit next tile, e.g. // turtles imply water underneath diff --git a/js/tileset.js b/js/tileset.js index 7f25263..0f74fc0 100644 --- a/js/tileset.js +++ b/js/tileset.js @@ -1,3 +1,5 @@ +import { DIRECTIONS } from './defs.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 @@ -9,6 +11,8 @@ // - 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 export const CC2_TILESET_LAYOUT = { + '#wire-width': 1/16, + door_red: [0, 1], door_blue: [1, 1], door_yellow: [2, 1], @@ -27,7 +31,12 @@ export const CC2_TILESET_LAYOUT = { ice_nw: [14, 1], cloner: [15, 1], - floor: [0, 2], + floor: { + // Wiring! + base: [0, 2], + wired: [8, 26], + is_wired_optional: true, + }, wall_invisible: [0, 2], wall_appearing: [0, 2], wall: [1, 2], @@ -105,8 +114,14 @@ export const CC2_TILESET_LAYOUT = { button_green: [9, 6], button_red: [10, 6], button_brown: [11, 6], - button_pink: [12, 6], - button_black: [13, 6], + button_pink: { + base: [0, 2], + wired: [12, 6], + }, + button_black: { + base: [0, 2], + wired: [13, 6], + }, button_orange: [14, 6], button_yellow: [15, 6], @@ -275,6 +290,9 @@ export const CC2_TILESET_LAYOUT = { [15, 24], ], + '#unpowered': [13, 26], + '#powered': [15, 26], + player2: { moving: { north: [[0, 27], [1, 27], [2, 27], [3, 27], [4, 27], [5, 27], [6, 27], [7, 27]], @@ -501,6 +519,47 @@ export class Tileset { // tile, so some adjustments are needed; see below coords = drawspec.tile; } + else if (drawspec.wired) { + if (tile && tile.wire_directions !== undefined && tile.wire_directions !== 0) { + // TODO all four is a different thing entirely + // Draw the appropriate wire underlay + this.draw_type('#unpowered', tile, level, ctx, x, y); + + // Draw a masked part of the base tile + let wiredir = tile.wire_directions; + let wire_radius = this.layout['#wire-width'] / 2; + let wire0 = 0.5 - wire_radius; + let wire1 = 0.5 + wire_radius; + let [bx, by] = drawspec.base; + if ((wiredir & DIRECTIONS['north'].bit) === 0) { + this.blit(ctx, bx, by, x, y, 1, wire0); + } + if ((wiredir & DIRECTIONS['south'].bit) === 0) { + this.blit(ctx, bx, by + wire1, x, y + wire1, 1, wire0); + } + if ((wiredir & DIRECTIONS['west'].bit) === 0) { + this.blit(ctx, bx, by, x, y, wire0, 1); + } + if ((wiredir & DIRECTIONS['east'].bit) === 0) { + this.blit(ctx, bx + wire1, by, x + wire1, y, wire0, 1); + } + + // Then draw the wired tile as normal + coords = drawspec.wired; + } + else { + // There's no wiring here, so just draw the base and then draw the wired part on top + // as normal. If the wired part is optional (as is the case for flooring in the CC2 + // tileset), draw the base as normal instead. + if (drawspec.is_wired_optional) { + coords = drawspec.base; + } + else { + this.blit(ctx, drawspec.base[0], drawspec.base[1], x, y); + coords = drawspec.wired; + } + } + } // Unwrap animations etc. if (!(coords instanceof Array)) { diff --git a/js/tiletypes.js b/js/tiletypes.js index f9aa82e..f06c19a 100644 --- a/js/tiletypes.js +++ b/js/tiletypes.js @@ -8,10 +8,15 @@ const LAYER_OVERLAY = 3; // TODO cc2 order is: swivel, thinwalls, canopy (and yes you can have them all in the same tile) // TODO get rid of mentions of 'modifier' here, put them in the c2m loader +// TODO maybe get rid of 'load' entirely and copy everything from the tile template. also make the template ref the type instead of just having a name. in fact just make them fucking tiles? (ah but they shouldn't have state like inventory) const TILE_TYPES = { // Floors and walls floor: { draw_layer: LAYER_TERRAIN, + load(me, template) { + me.wire_directions = template.wire_directions; + me.wire_tunnel_directions = template.wire_tunnel_directions; + }, }, floor_letter: { draw_layer: LAYER_TERRAIN, @@ -784,6 +789,10 @@ const TILE_TYPES = { button_pink: { // TODO not implemented draw_layer: LAYER_TERRAIN, + load(me, template) { + me.wire_directions = template.wire_directions; + me.wire_tunnel_directions = template.wire_tunnel_directions; + }, }, button_black: { // TODO not implemented