CirQuit = function() { this.name = "CirQuit"; this.tickrate = 1000 / 60.098814; CBDL.Window.setTitle(this.name); var display; var events; var loop; var buttons = []; LEFT = 37; UP = 38; RIGHT = 39; DOWN = 40; SELECT = 32; START = 13; A = 88; B = 90; var button_list = [LEFT, UP, RIGHT, DOWN, SELECT, START, A, B]; for (i = 0; i < 8; i++) { buttons[button_list[i]] = 0; } var ignore_input = false; var font; var menu_sprites; var qat_sprites; var game_sprites; var width = 256; var height = 224; var scale = 1.0; var CurrentState; this.Main = function() { if (!(display = new CBDL.Graphics.Display(document.getElementById("cirquit"), new CBDL.Graphics.VideoMode(256, 224, CBDL.Graphics.VM.SCALE), CBDL.Graphics.BACKEND.DIV))) { return 1; } display.Init(); display.Fill(0x00, 0x00, 0x00); onResize(); events = new CBDL.Event(["resize", "mousedown", "keydown", "keyup"], window); events.addEvent(["touchstart", "touchmove", "touchend"], document.body); events.addEvent(["keydown", "keyup"], document.body); font = display.Spritesheet("font.png", {width: 128, height:48}, {width: 8, height: 8}); menu_sprites = display.Spritesheet("menu.png", {width: 128, height:96}, {width: 8, height: 8}); qat_sprites = display.Spritesheet("qat.png", {width: 128, height:96}, {width: 8, height: 8}); game_sprites = display.Spritesheet("first_2.png", {width: 128, height:96}, {width: 8, height: 8}); CurrentState = LoadState; CurrentState.onOpen(); (loop = new CBDL.Loop(this, doLoop)).start(); }; function doLoop(delta) { // let's just handle events globally, since the game really only needs to handle 8 buttons // also, instead of handling events in the current state, we'll just toggle the state of buttons and address them directly in need. The other option would be to run an events poll loop for the buttons (which would have explicit integer values, such as A=0, B=1, etc.). This looks neater, however. while (event_ = events.pollEvent()) { if (event_.type == "keydown") { if (buttons[event_.keyCode] != 2) { buttons[event_.keyCode] = 1; } } else if (event_.type == "keyup") { buttons[event_.keyCode] = 0; } else if (event_.type == "resize") { onResize(event_); } else if (event_.type == "mousedown") { } else if (event_.type == "touchstart") { buttons[START] = 1; } else if (event_.type == "touchend") { buttons[START] = 0; } }; CurrentState.onTick(delta, display); var wait_time = this.tickrate-delta; wait_time = Math.round((wait_time <= 0 ? 0 : wait_time)); return wait_time; }; var onResize = function(event) { var viewport = CBDL.Graphics.getViewport(); // resize window to nearest 4th var ratio = Math.min(viewport.width / width, viewport.height / height); ratio *= 4; ratio = Math.floor(ratio) / 4; display.target_element.style.width = width*ratio+"px"; display.target_element.style.height = height*ratio+"px"; display.target_element.style.left = (viewport.width/2) - (width*ratio/2)+"px" display.target_element.style.top = (viewport.height/2) - (height*ratio/2)+"px" display.updateViewport(); }; var swapState = function(next_state) { // "reset" buttons and ignore input until keyup for (i = 0; i < button_list.length; i++) { if (buttons[button_list[i]] == 1) buttons[button_list[i]] = 2; } CurrentState.onClose(display); CurrentState = next_state; CurrentState.onOpen(display); }; checkButton = function(i) { if (buttons[i] == 1) return 1; return 0; }; lockButton = function(i) { buttons[i] = 2; return 0; }; /* ================================================================ Load State ================================================================ */ var LoadState = { }; LoadState.onOpen = function(display) { }; LoadState.onTick = function(delta, display) { if (display.checkLoading() == 0) { swapState(MenuState); } }; LoadState.onClose = function(display) { }; /* ================================================================ Menu State ================================================================ */ var MenuState = { start_button: null, start_blink: 0, player_button: null, menu_item_count: 1, menu_items: [], //menu_item_names: ["1 player", "2 player", "hi-scores", "options"], menu_item_names: ["1 player"], menu_arrow: null, menu_arrow_timer: 0, menu_arrow_dir: 0, menu_pos: 0, qat: [], qat_anim: 0, qat_move: 0, rain_count: 24, rain: [], title_w: 14, title_h: 2, title: [], credits: null, credits_string: String.fromCharCode(64)+"2014 kts of kettek", mode: 0, // 0 = wait for start, 1 = show menu s_mode: 0, // submode, used for transition between modes sfx_click: null, sfx_select: null }; MenuState.onOpen = function(display) { this.mode = 0; // create menu elements this.start_button = new StringSprite(font, "Press ENTER", 0, 0); this.start_button.x = 256/2 - this.start_button.s_width/2; this.start_button.y = 224/2 + 32; // player buttonz var offset_y = 224/2 - (8 * this.menu_item_count)/2; this.menu_items[0] = new StringSprite(font, this.menu_item_names[0], 0, offset_y); this.menu_items[0].x = 256/2 - this.menu_items[0].s_width/2; this.menu_arrow = new NesSprite(menu_sprites, 0, 4, 0, offset_y); this.menu_arrow.x = this.menu_items[0].x-16; for (var i = 1; i < this.menu_item_count; i++) { offset_y += 16; this.menu_items[i] = new StringSprite(font, this.menu_item_names[i], 0, offset_y); this.menu_items[i].x = this.menu_items[0].x; } // credits this.credits = new StringSprite(font, this.credits_string, width/2 - (this.credits_string.length)*8/2, 224 - 32); // title var i = 0; for (var x = 0; x < this.title_w; x++) { for (var y = 0; y < this.title_h; y++) { this.title[i++] = new NesSprite(menu_sprites, 0+x, 6+y, (width/2-(this.title_w*8/2))+(8*x), 16+(8*y)); } } // spritez var offset_y = this.menu_items[0].y - 32; var qat_x = 256/2 - 16 var qat_y = 8; var i = 0; for (var x = 0; x < 4; x++) { for (var y = 0; y < 4; y++) { this.qat[i++] = new NesSprite(menu_sprites, x, y, qat_x + (8*x), offset_y - qat_y + (8*y)); } } for (j = 0; j < this.rain_count; j++) { this.rain[j] = new NesSprite(menu_sprites, 4, 4, 0, 0); this.rain[j].y = -8; this.rain[j].s = Math.floor(Math.random() * (3 - 1)) + 1; } this.sfx_click = new CBDL.audio.Sound("sfx/fwip.wav", 0.5); this.sfx_select = new CBDL.audio.Sound("sfx/bobloop.wav", 1.0); }; MenuState.onTick = function(delta, display) { display.Fill(0x00, 0x00, 0x00); if (this.qat_anim == 0) { this.qat_move += 0.1; if (this.qat_move > 5) this.qat_anim = 1; } else { this.qat_move -= 0.1; if (this.qat_move < 1) this.qat_anim = 0; } for (var i = 0; i < this.qat.length; i++) { this.qat[i].draw(0, Math.round(this.qat_move)); } var i = 0; for (var x = 0; x < this.title_w; x++) { for (var y = 0; y < this.title_h; y++) { this.title[i++].draw(0, 0); } } this.credits.draw(0, 0); switch(this.mode) { case 0: // Press Start screen // Wait for Start if (this.start_blink < 30) { if (this.start_blink < 15) { // blink every 30th tick (1/2 sec) this.start_button.draw(0, 0); } else { this.start_button.draw(-1000, -1000); } this.start_blink++; } else { this.start_blink = 0; } if (checkButton(START)) { this.sfx_select.play(); this.start_button.destroy(); this.start_button = null; this.mode = 1; buttons[START] = 2; var i = 0; for (var x = 0; x < 4; x++) { for (var y = 0; y < 4; y++) { this.qat[i++].set(x+4, y); } } } break; case 1: // Menu screen if (checkButton(UP)) { this.sfx_click.play(); if (this.menu_pos > 0) this.menu_pos--; buttons[UP] = 2; // lock it } else if (checkButton(DOWN)) { this.sfx_click.play(); if (this.menu_pos < this.menu_item_count-1) this.menu_pos++; buttons[DOWN] = 2; // lock it } if (this.menu_arrow_dir == 0) { this.menu_arrow_timer++; if (this.menu_arrow_timer > 30) this.menu_arrow_dir = 1; } else { this.menu_arrow_timer--; if (this.menu_arrow_timer < 1) this.menu_arrow_dir = 0; } this.menu_arrow.set(Math.floor(this.menu_arrow_timer/8), 4); // draw if (this.tile_move == 0) { this.tile_timer += 0.05; if (this.tile_timer > Math.PI*2) this.tile_move = 1; } else { this.tile_timer -= 0.05; if (this.tile_timer <= 0) this.tile_move = 0; } for (j = 0; j < this.rain_count; j++) { if (this.rain[j].y > -32) { this.rain[j].y -= this.rain[j].s; this.rain[j].s += 0.01; } else { this.rain[j].x = Math.floor(Math.random() * (248 - 8)) + 8; this.rain[j].y = 224 + Math.floor(Math.random() * (48 - 8)) + 8; this.rain[j].s = Math.floor(Math.random() * (1 - 0.5)) + 0.5; } this.rain[j].draw(0, 0); } for (var i = 0; i < this.menu_item_count; i++) { this.menu_items[i].draw(0, 0); } this.menu_arrow.draw(Math.floor(this.menu_arrow_timer/6), this.menu_pos*(16)); // hue hue if (checkButton(START)) { this.sfx_select.play(); if (this.menu_pos == 0) { swapState(IntroState); } else if (this.menu_pos == 1) { } else if (this.menu_pos == 2) { // ?? } } break; } }; MenuState.onClose = function(display) { for (j = 0; j < this.rain_count; j++) { this.rain[j].destroy(); this.rain[j] = null; } this.rain = []; for (var i = 0; i < this.menu_item_count; i++) { this.menu_items[i].destroy(); this.menu_items[i] = null; } this.menu_arrow.destroy(); this.menu_arrow = null; for (var i = 0; i < this.qat.length; i++) { this.qat[i].destroy(); this.qat[i] = null; } for (var i = 0; i < this.title.length; i++) { this.title[i].destroy(); this.title[i] = null; } this.credits.destroy(); this.credits = null; this.sfx_select.destroy(); this.sfx_select = null; this.sfx_click.destroy(); this.sfx_click = null; }; /* ================================================================ Intro State ================================================================ */ var IntroState = { lines: [], messages: [ "", "Deep within the Factory of Earth, and even deeper within the circuitry of the Eternal Machines, a spark of conscious life is taking shape.", "Perhaps from chance, or perhaps from intent, this budding life seeks to escape the monotonous rhythms and functions of the Eternal Machines - to be something more.", "You are this life form and you must make your way out from the Eternal Machines.", "Endless bliss awaits." ], line_messages: [ ], message_index: 0, line_index: 0, char_index: 0, char_timer: 0, message_wait: 0, message_timer: 0 }; IntroState.onOpen = function(display) { this.lines = []; this.line_messages = []; this.line_index = this.char_index = 0; this.message_index = 0; this.message_wait = 0; this.message_timer = 0; }; IntroState.onTick = function(delta, display) { display.Fill(0x00, 0x00, 0x00); if (this.line_index >= this.lines.length) { this.message_timer += delta; if (this.message_timer > this.message_wait) { if (this.message_index > this.messages.length-1) { swapState(SplashState); return; } this.message_timer = 0; // reset timer this.line_index = this.char_index = 0; for (var i = 0; i < this.lines.length; i++) { this.lines[i].destroy(); this.lines[i] = null; this.line_messages[i] = null; } var max_cols = Math.round((255 / 8)); var offset = 0; var remaining = this.messages[this.message_index].length; this.message_wait = 2500; var j = 0; this.lines = []; this.line_messages = []; while (offset < remaining) { if (this.messages[this.message_index].charAt(offset) == ' ') offset++; var new_str = this.messages[this.message_index].substr(offset, max_cols); end = new_str.length; if (offset+end < remaining) { while (new_str.charCodeAt(end-1) != 32 && end > 0) { end--; } } new_str = this.messages[this.message_index].substr(offset, end); this.line_messages[j] = this.messages[this.message_index].substr(offset, end); this.lines[j++] = new StringSprite(font, "", width/2 - ((end-1)*8)/2, 64+(12*j)); offset += end; } this.message_index++; } } else { this.char_timer += delta; if (this.char_index < this.line_messages[this.line_index].length) { while (this.char_timer >= 50) { this.char_timer -= 50; this.lines[this.line_index].addChar(this.line_messages[this.line_index].charAt(this.char_index)); this.char_index++; } } else { this.line_index++; this.char_index = 0; this.char_timer = 0; } } for (var i = 0; i < this.lines.length; i++) { this.lines[i].draw(0, 0); } if (checkButton(START)) { swapState(SplashState); } }; IntroState.onClose = function(display) { for (var i = 0; i < this.lines.length; i++) { this.lines[i].destroy(); this.lines[i] = null; } }; /* ================================================================ Retry State ================================================================ */ var RetryState = { message: null, menu_pos: 0, menu_items: [], menu_arrow_dir: 0, menu_arrow_timer: 0, menu_arrow: null, amp_string: null, amp_sprite: null, auto_timer: 0, mode: 0, sfx_click: null, sfx_select: null }; RetryState.onOpen = function(display) { this.menu_pos = 0; this.menu_items = []; this.amp_string = null; this.amp_sprite = null; this.message = null; this.menu_arrow_dir = 0; this.menu_arrow_timer = 0; this.menu_arrow = null; this.auto_timer = 0; this.mode = 0; if (GameState.amperage > 128) { var offset_y = 224/2 - (8 * 2)/2; this.menu_items.push(new StringSprite(game_sprites, "Retry", 0, offset_y)); this.menu_items[0].x = 256/2 - this.menu_items[0].s_width/2; this.menu_arrow = new NesSprite(menu_sprites, 0, 4, 0, offset_y); this.menu_arrow.x = this.menu_items[0].x-16; offset_y += 16; this.menu_items.push(new StringSprite(game_sprites, "Quit", 8, offset_y)); this.menu_items[1].x = this.menu_items[0].x; var offset_x = this.menu_items[0].x + this.menu_items[0].s_width + 32; this.amp_string = new StringSprite(game_sprites, "-128", offset_x, this.menu_items[0].y); offset_x += this.amp_string.s_width + 8; this.amp_sprite = new NesSprite(game_sprites, 12, 0, offset_x, this.menu_items[0].y); } else { this.mode = 1; this.message = new StringSprite(game_sprites, "Game Over", 0, 224/2 - (8*2)/2); this.message.x = 256/2 - this.message.s_width/2; } this.sfx_click = new CBDL.audio.Sound("sfx/fwip.wav", 0.5); this.sfx_select = new CBDL.audio.Sound("sfx/bobloop.wav", 1.0); }; RetryState.onClose = function(display) { if (this.message) this.message.destroy(); for (var i = 0; i < this.menu_items.length; i++) { this.menu_items[i].destroy(); this.menu_items[i] = null; } if (this.menu_arrow) this.menu_arrow.destroy(); if (this.amp_sprite) this.amp_sprite.destroy(); if (this.amp_string) this.amp_string.destroy(); this.sfx_click.destroy(); this.sfx_select.destroy(); }; RetryState.onTick = function(delta, display) { display.Fill(0x00, 0x00, 0x00); var menu_swap = false; if (this.mode == 0) { if (checkButton(UP)) { this.sfx_click.play(); if (this.menu_pos > 0) this.menu_pos--; lockButton(UP); } else if (checkButton(DOWN)) { this.sfx_click.play(); if (this.menu_pos < this.menu_items.length-1) this.menu_pos++; lockButton(DOWN); } if (this.menu_arrow_dir == 0) { this.menu_arrow_timer++; if (this.menu_arrow_timer > 30) this.menu_arrow_dir = 1; } else { this.menu_arrow_timer--; if (this.menu_arrow_timer < 1) this.menu_arrow_dir = 0; } this.menu_arrow.set(Math.floor(this.menu_arrow_timer/8), 4); } else { this.auto_timer += delta; if (this.auto_timer >= 5000) { menu_swap = true; } } if (this.message != null) { this.message.draw(0, 0); } for (var i = 0; i < this.menu_items.length; i++) { this.menu_items[i].draw(0, 0); } if (this.amp_sprite) this.amp_sprite.draw(0, 0); if (this.amp_string) this.amp_string.draw(0, 0); if (this.menu_arrow) this.menu_arrow.draw(Math.floor(this.menu_arrow_timer/6), this.menu_pos*(16)); if (checkButton(START)) { this.sfx_select.play(); if (this.mode == 0) { if (this.menu_pos == 0) { GameState.mode = 1; GameState.amperage -= 128; swapState(GameState); } else { GameState.mode = 0; GameState.amperage = 256; GameState.map_index = 0; swapState(MenuState); } } else { menu_swap = true; } } if (menu_swap == true) { GameState.mode = 0; swapState(MenuState); } }; /* ================================================================ Splash State ================================================================ */ var SplashState = { map_string: null, amp_sprite: null, amp_string: null, power_string: null, power_sprite: null, timer: 0, sfx_open: null, has_played: false }; SplashState.onOpen = function(display) { var amperage = (GameState.amperage+(Maps[GameState.map_index].tiles.length*64)).toString(); var len = 6 - amperage.length; for (var i = 0; i < len; i++) { amperage = "0"+amperage; } var offset_y = 224/2 - (8 * 6)/2; this.map_string = new StringSprite(game_sprites, Maps[GameState.map_index].name, 0, offset_y); this.map_string.x = 256/2 - this.map_string.s_width/2; offset_y += 30; this.amp_string = new StringSprite(game_sprites, amperage, 0, offset_y); this.amp_string.x = 256/2 - this.amp_string.s_width/2; offset_x = this.amp_string.x + this.amp_string.s_width + 8; this.amp_sprite = new NesSprite(game_sprites, 12, 0, offset_x, this.amp_string.y); offset_y += 12; this.power_string = new StringSprite(game_sprites, 0+"/"+Maps[GameState.map_index].power, 0, offset_y); this.power_string.x = (this.amp_string.x + this.amp_string.s_width) - this.power_string.s_width; offset_x = this.amp_sprite.x; this.power_sprite = new NesSprite(game_sprites, 13, 0, offset_x, this.power_string.y); this.sfx_open = new CBDL.audio.Sound("sfx/omin.wav", 0.25); }; SplashState.onClose = function(display) { this.map_string.destroy(); this.amp_string.destroy(); this.amp_sprite.destroy(); this.power_string.destroy(); this.power_sprite.destroy(); this.sfx_open.destroy(); this.has_played = false; this.timer = 0; }; SplashState.onTick = function(delta, display) { if (!this.has_played) { this.sfx_open.play(); this.has_played = true; } display.Fill(0x00, 0x00, 0x00); this.map_string.draw(0, 0); this.amp_string.draw(0, 0); this.amp_sprite.draw(0, 0); this.power_string.draw(0, 0); this.power_sprite.draw(0, 0); this.timer += delta; if (this.timer >= 4000) { swapState(GameState); } }; /* ================================================================ Game State ================================================================ */ var GameState = { mode: 0, // 0 = new game, 1 = retry, 2 = continue state: 0, // 0 = load, 1 = game, 2 = end tile: null, blink: 0, message: null, message_timer: 0, message_wait: 0, message_index: 0, cache: [], cursor_x: 2, cursor_y: 2, tile_index: 0, cells: [], update: [], // cells to update outline: [], pointless_angle: 0, map: null, map_index: 0, map_offset_x: 0, map_offset_y: 48, do_draw: true, draw_cells: true, draw_ui: true, ui_sprites: [], ui_strings: [], // 0 = map name, 1 = amperage, 2 = power amperage: 256, amperage_timer: 0, power: 0, qat: [], qat_anim: 0, qat_move: 0, qat_i: 0, start_qat: false, enable_pointless: true, show_tooltips: true, tooltips: [ "Use the Arrow Keys to move the flashing node around the field.", "Hold Z and use the Arrow Keys to rotate the node.", "Press X to drop the node next to an adjoining node.", "When dropped, a new node is taken from the bottom cache.", "Your objective is to create powered circuits to unpowered exit nodes.", "However, powering nodes uses energy, so build quickly and efficiently.", "Powering bonus power nodes provides additional power.", "Be expeditious." ], tooltip_showing: false, tooltip_index: 0, tooltip_strings: [], tooltip_part_index: 0, tooltip_parts: [], tooltip_timer: 0, tooltip_char: 0, tooltip_char_timer: 0, has_moved: false, has_dropped: false, has_rotated: false, has_powered: false, has_bonused: false, has_finished: false, sfx_failure: null, sfx_move: null, sfx_place: null, sfx_rotate: null, sfx_node: null, sfx_power: null, sfx_win: null }; GameState.onOpen = function(display) { if (this.mode == 0) { this.map_index = 0; this.qat_i = 0; this.amperage = 256; } this.pointless_angle = 0; this.start_qat = false; this.state = 0; this.power = 0; this.cache = []; this.cells = []; this.update = []; this.outline = []; this.ui_sprites = []; this.ui_strings = []; this.qat = []; this.message_timer = this.message_index = 0; this.tooltip_strings = []; this.tooltip_char = 0; this.tooltip_char_timer = 0; this.tooltip_part_index = 0; this.tooltip_parts = []; this.sfx_failure = this.sfx_move = this.sfx_place = this.sfx_rotate = this.sfx_node = this.sfx_power = this.sfx_win = null; this.do_draw = true; if (this.map == null) { this.map = copyObject(Maps[this.map_index]); } for (var y = 0; y < this.map.height; y++) { if (!this.cells[y]) this.cells[y] = []; for (var x = 0; x < this.map.width; x++) { this.cells[y][x] = null; } } this.map_offset_x = 256/2 - ((this.map.width-1)*24)/2; this.map_offset_y = Math.max(64, 224/2 - ((this.map.height-1)*9)/2); if (this.enable_pointless) { for (var y = 0; y < this.map.height; y++) { this.outline.push(new NesSprite(game_sprites, 13, 2, this.map_offset_x-8, this.map_offset_y + y*8)); } } else { for (var y = 0; y < this.map.height; y++) { this.outline.push(new NesSprite(game_sprites, 12, 2, this.map_offset_x-8, this.map_offset_y + y*8)); this.outline.push(new NesSprite(game_sprites, 12, 2, this.map_offset_x+(this.map.width*18)+12, this.map_offset_y + y*8)); } for (var x = 0; x < this.map.width; x++) { this.outline.push(new NesSprite(game_sprites, 12, 2, this.map_offset_x+(x*24), this.map_offset_y-6)); this.outline.push(new NesSprite(game_sprites, 12, 2, this.map_offset_x+(x*24), this.map_offset_y + this.map.height*7 + 10)); } } for (var cell in this.map.cells) { var y = this.map.cells[cell].y; var x = this.map.cells[cell].x; var m = this.map.cells[cell].m; var t = this.map.cells[cell].t; this.cells[y][x] = new Tile(game_sprites, m, t, this.map_offset_x+x*18+((y%2) == 1 ? 9 : 0), this.map_offset_y+y*7); this.cells[y][x].m_y = y; this.cells[y][x].m_x = x; if (t == 0 || t == 2) { this.createTileBorder(x, y, m); } if (t == ENTRANCE) { for (var y = this.map.cells[cell].y-1; y < this.map.cells[cell].y+1; y++) { if (!this.cells[y]) continue; for (var x = this.map.cells[cell].x-1; x < this.map.cells[cell].x+1; x++) { if (!this.cells[y][x]) continue; if (this.cells[y][x].type == OUTLINE) { this.cursor_x = x; this.cursor_y = y; break; } } } } } for (var i = 0; i < this.map.tiles.length; i++) { this.cache[i] = new Tile(game_sprites, this.map.tiles[i].m, this.map.tiles[i].t, 256 - (24*6) + (24*i), 224-22); } this.tile = this.cache[0]; this.tile.setSprite(15, 4); this.cache.shift(); // set up UI this.ui_strings.push(new StringSprite(game_sprites, this.map.name, 8, 8)); this.ui_sprites.push(new NesSprite(game_sprites, 12, 0, 256-16, 8)); if (this.mode == 0) { this.amperage += this.map.tiles.length * 64; } else if (this.mode == 2) { this.amperage += this.map.tiles.length * 64; } this.ui_strings.push(new StringSprite(game_sprites, "000000", 256-(9*8), 8)); // required power this.ui_sprites.push(new NesSprite(game_sprites, 13, 0, 256-16, 20)); this.ui_strings.push(new StringSprite(game_sprites, "0/0", 256-(6*8), 20)); this.updateUi(); // floating qat var qat_x = 256/2 - 16; var qat_y = 0; var i = 0; for (var x = 0; x < 4; x++) { for (var y = 0; y < 4; y++) { this.qat[i++] = new NesSprite(menu_sprites, x+(this.qat_i*4), y, qat_x + (8*x), qat_y + (8*y)); } } this.sfx_failure = new CBDL.audio.Sound("sfx/failure.wav", 0.15); this.sfx_rotate = new CBDL.audio.Sound("sfx/fwip.wav", 0.25); this.sfx_move = new CBDL.audio.Sound("sfx/bobloop.wav", 0.2); this.sfx_place = new CBDL.audio.Sound("sfx/bobloop.wav", 0.25); this.sfx_node = new CBDL.audio.Sound("sfx/sfx.wav", 0.5); this.sfx_power = new CBDL.audio.Sound("sfx/point.wav", 0.5); this.sfx_win = new CBDL.audio.Sound("sfx/levelup.wav", 0.25); }; GameState.onTick = function(delta, display) { // update cells as needed if (this.state == 0) { // map start this.state = 1; } else if (this.state == 1) { // game mode if (this.update.length > 0) { this.updateCell(this.update.shift()); } // drain 'em if (this.amperage_timer >= 15) { this.amperage--; this.updateUi(); this.amperage_timer = 0; } else { this.amperage_timer++; } if ((this.tile == null && this.cache.length <= 0 && this.update.length <= 0) || (this.amperage < 0 && this.update.length <= 0)) { this.state = 4; if (this.tile) { this.tile.destroy(); this.tile = null; } } else { if (this.tile) this.handleTile(delta, display); if (this.power >= this.map.power) { this.state = 3; this.sfx_win.play(); if (this.tile) { this.tile.destroy(); this.tile = null; } for (var i = 0; i < this.tooltip_strings.length; i++) { this.tooltip_strings[i].destroy(); this.tooltip_strings[i] = null; this.tooltip_parts[i] = null; } this.tooltip_strings = []; this.tooltip_parts = []; } if (this.show_tooltips) { if (!this.tooltip_showing) { if (!this.has_moved) { this.createTooltip(display, 0); this.tooltip_index = 1; } else if (this.has_moved && !this.has_rotated) { this.createTooltip(display, 1); this.tooltip_index = 2; } else if (this.has_rotated && !this.has_dropped) { this.createTooltip(display, 2); this.tooltip_index = 3; } else if (this.has_dropped && this.tooltip_index < 4) { this.createTooltip(display, 3); this.tooltip_index = 4; } else if (this.has_powered && this.tooltip_index == 4) { this.createTooltip(display, 4); this.tooltip_index = 5; } else if (!this.has_bonused && this.tooltip_index == 5) { this.createTooltip(display, 5); this.tooltip_index = 6; } else if (this.has_bonused && this.tooltip_index == 6) { this.createTooltip(display, 6); this.tooltip_index = 7; } else if (this.tooltip_index == 7) { this.createTooltip(display, 7); this.tooltip_index = 8; } else if (this.tooltip_index == 8) { this.show_tooltips = false; } } else { if (this.has_moved && this.tooltip_index == 0) { this.tooltip_timer = 2500; this.tooltip_part_index = this.tooltip_parts.length; } else if (this.has_rotated && this.tooltip_index <= 2) { this.tooltip_timer = 2500; this.tooltip_part_index = this.tooltip_parts.length; } else if (this.has_dropped && this.tooltip_index <= 3) { this.tooltip_timer = 2500; this.tooltip_part_index = this.tooltip_parts.length; } } this.handleTooltip(delta, display); } } if (this.enable_pointless) { for (var i = 0; i < this.outline.length; i++) { var x = (this.map.width*12) * Math.cos(this.pointless_angle); var y = (this.map.height*5) * Math.sin(this.pointless_angle); this.outline[i].setXY(128+x, y+(this.map_offset_y + (this.map.height*7)/2)); this.pointless_angle += 0.01; } } } else if (this.state == 3) { // map succeed this.show_tooltips = false; if (this.enable_pointless) { for (var i = 0; i < this.outline.length; i++) { var x = (this.map.width*12) * Math.cos(this.pointless_angle); var y = (this.map.height*5) * Math.sin(this.pointless_angle); this.outline[i].setXY(128+x, y+(this.map_offset_y + (this.map.height*7)/2)); this.pointless_angle += 0.01; } } for (var y = 0; y < this.map.height; y++) { for (var x = 0; x < this.map.width; x++) { if (this.cells[y][x] != null) { if (this.cells[y][x].state == 1) continue; if (this.cells[y][x].sprites[0].y > 230) { this.cells[y][x].destroy(); this.cells[y][x] = null; this.start_qat = true; continue; } cell = this.cells[y][x]; var c_y = cell.sprites[0].y + Math.floor(Math.random() * (6 - 4)) + 4; //var c_x = cell.sprites[0].x + (cell.sprites[0].x > 255/2 ? -1 : 1); var c_x = cell.sprites[0].x; cell.sprites[0].setXY(c_x, c_y); cell.lines[0].setXY(c_x, c_y); //c_x = cell.sprites[1].x + (cell.sprites[1].x > 255/2 ? -1 : 1); c_x = cell.sprites[1].x; cell.sprites[1].setXY(c_x, c_y); cell.lines[1].setXY(c_x, c_y); //c_x = cell.sprites[2].x + (cell.sprites[2].x > 255/2 ? -1 : 1); c_x = cell.sprites[2].x; cell.sprites[2].setXY(c_x, c_y); cell.lines[2].setXY(c_x, c_y); //c_x = cell.sprites[3].x + (cell.sprites[3].x > 255/2 ? -1 : 1); c_x = cell.sprites[3].x; cell.sprites[3].setXY(c_x, c_y+8); cell.lines[3].setXY(c_x, c_y+8); //c_x = cell.sprites[4].x + (cell.sprites[4].x > 255/2 ? -1 : 1); c_x = cell.sprites[4].x; cell.sprites[4].setXY(c_x, c_y+8); cell.lines[4].setXY(c_x, c_y+8); //c_x = cell.sprites[5].x + (cell.sprites[5].x > 255/2 ? -1 : 1); c_x = cell.sprites[5].x; cell.sprites[5].setXY(c_x, c_y+8); cell.lines[5].setXY(c_x, c_y+8); } } } if (this.start_qat == true) { if (this.message == null) { this.message = new StringSprite(game_sprites, "", 255/2 - ((this.map.qat.length-1)*8)/2, 48); this.message_wait = 4000; this.qat_i = this.map.qat_i; var i = 0; for (var x = 0; x < 4; x++) { for (var y = 0; y < 4; y++) { this.qat[i++].set(x+(this.qat_i*4), y); } } } else { if (this.message_index < this.map.qat.length) { this.message_index++; this.message.setString(this.map.qat.slice(0, this.message_index)); } else { this.message_timer += delta; if (this.message_timer >= this.message_wait) { this.do_draw = false; this.map_index++; this.mode = 2; if (this.map_index < Maps.length) { swapState(SplashState); } else { swapState(EndState); } } } } } } else if (this.state == 4) { // failure! if (this.message == null) { this.message = new StringSprite(game_sprites, "FAILURE", 255/2 - (6*8)/2, 36); this.message_wait = 6000; this.sfx_failure.play(); } this.message_timer += delta; if (this.message_timer >= this.message_wait || checkButton(START)) { swapState(RetryState); this.do_draw = false; } } if (this.do_draw) this.doDraw(delta, display); }; GameState.doDraw = function(delta, display) { display.Fill(0x00, 0x00, 0x00); for (var i = 0; i < this.outline.length; i++) { this.outline[i].draw(0, 0); } if (this.draw_cells) { for (var y = 0; y < this.map.height; y++) { for (var x = 0; x < this.map.width; x++) { if (this.cells[y][x] != null) { if (this.cells[y][x].do_draw) this.cells[y][x].draw(0, 0); } } } //this.draw_cells = false; } if (this.tile) { // FIXME: blinking is ugly if (this.blink < 2) { this.tile.draw(-1000, -1000); this.blink++; } else if (this.blink >= 2 && this.blink < 10) { this.tile.draw(0, 0); this.blink++; } else { this.tile.draw(0, 0); this.blink = 0; } } if (this.draw_ui) { if (this.qat_anim == 0) { this.qat_move += 0.1; if (this.qat_move > 5) this.qat_anim = 1; } else { this.qat_move -= 0.1; if (this.qat_move < 1) this.qat_anim = 0; } for (var i = 0; i < this.qat.length; i++) { this.qat[i].draw(0, Math.round(this.qat_move)); } //this.draw_ui = false; for (var i = 0; i < this.cache.length; i++) { this.cache[i].draw(0, 0); } for (var i = 0; i < this.ui_sprites.length; i++) { this.ui_sprites[i].draw(0, 0); } for (var i = 0; i < this.ui_strings.length; i++) { this.ui_strings[i].draw(0, 0); } if (this.message != null) { this.message.draw(0, 0); } } for (var i = 0; i < this.tooltip_strings.length; i++) { this.tooltip_strings[i].draw(0, 0); } }; GameState.handleTile = function(delta, display) { if (checkButton(B)) { if (checkButton(LEFT)) { this.tile.rotateLeft(); lockButton(LEFT); this.sfx_rotate.play(); this.has_rotated = true; } else if (checkButton(RIGHT)) { this.tile.rotateRight(); lockButton(RIGHT); this.sfx_rotate.play(); this.has_rotated = true; } else if (checkButton(UP)) { this.tile.flipUp(); lockButton(UP); } else if (checkButton(DOWN)) { this.tile.flipDown(); lockButton(DOWN); } } else { if (checkButton(LEFT)) { if (this.cursor_x > 0) this.cursor_x--; lockButton(LEFT); this.sfx_move.play(); this.has_moved = true; } else if (checkButton(RIGHT)) { if (this.cursor_x < this.map.width-1) this.cursor_x++; lockButton(RIGHT); this.sfx_move.play(); this.has_moved = true; } if (checkButton(DOWN)) { if (this.cursor_y < this.map.height-1) this.cursor_y++; lockButton(DOWN); this.sfx_move.play(); this.has_moved = true; } else if (checkButton(UP)) { if (this.cursor_y > 0) this.cursor_y--; lockButton(UP); this.sfx_move.play(); this.has_moved = true; } this.setTileXY(this.tile, this.cursor_x, this.cursor_y); } if (checkButton(A)) { var tile = this.getTile(this.cursor_x, this.cursor_y); if (tile) { if (tile.type == OUTLINE) { this.sfx_place.play(); this.cells[this.cursor_y][this.cursor_x].destroy(); this.tile.setSprite(0, 4); this.cells[this.cursor_y][this.cursor_x] = this.tile; this.cells[this.cursor_y][this.cursor_x].m_y = this.cursor_y; this.cells[this.cursor_y][this.cursor_x].m_x = this.cursor_x; this.createTileBorder(this.cursor_x, this.cursor_y, this.tile.flags); this.update.push(this.tile); this.draw_cells = true; this.has_dropped = true; for (var y = this.cursor_y-1; y < this.cursor_y+1; y++) { if (!this.cells[y]) continue; for (var x = this.cursor_x-1; x < this.cursor_x+1; x++) { if (!this.cells[y][x]) continue; if (this.cells[y][x].type == OUTLINE) { this.cursor_x = x; this.cursor_y = y; break; } } } if (this.cache.length == 0) { this.tile = null; } else { for (var i = 0; i < this.cache.length; i++) { this.cache[i].setXY(256 - (24*6) + (24*i), 224-22); } this.tile = this.cache[0]; this.cache.shift(); } } } lockButton(A); } }; GameState.createTooltip = function(display, index) { this.tooltip_showing = true; this.tooltip_part_index = this.tooltip_char = 0; this.tooltip_char_timer = 0; this.tooltip_timer = 0; for (var i = 0; i < this.tooltip_strings.length; i++) { this.tooltip_strings[i].destroy(); this.tooltip_strings[i] = null; this.tooltip_parts[i] = null; } var max_cols = Math.round((255 / 8)); var offset = 0; var remaining = this.tooltips[index].length; var j = 0; while (offset < remaining) { if (this.tooltips[index].charAt(offset) == ' ') offset++; var new_str = this.tooltips[index].substr(offset, max_cols); end = new_str.length; if (offset+end < remaining) { while (new_str.charCodeAt(end-1) != 32 && end > 0) { end--; } } this.tooltip_parts[j] = this.tooltips[index].substr(offset, end); this.tooltip_strings[j++] = new StringSprite(font, "", width/2 - ((end-1)*8)/2, 152+(12*j)); offset += end; } }; GameState.handleTooltip = function(delta, display) { if (this.tooltip_part_index < this.tooltip_parts.length) { this.tooltip_char_timer += delta; if (this.tooltip_char < this.tooltip_parts[this.tooltip_part_index].length) { while (this.tooltip_char_timer >= 25) { this.tooltip_char_timer -= 25; this.tooltip_strings[this.tooltip_part_index].addChar(this.tooltip_parts[this.tooltip_part_index].charAt(this.tooltip_char)); this.tooltip_char++; } } else { this.tooltip_part_index++; this.tooltip_char = 0; this.tooltip_char_timer = 0; } } else { if (this.tooltip_timer >= 2500) { for (var i = 0; i < this.tooltip_parts.length; i++) { this.tooltip_strings[i].destroy(); this.tooltip_strings[i] = null; this.tooltip_parts[i] = null; } this.tooltip_strings = []; this.tooltip_parts = []; this.tooltip_timer = 0; this.tooltip_showing = false; } this.tooltip_timer += delta; } }; GameState.updateCell = function(cell) { var target = null; var y = cell.m_y; var x = cell.m_x; var m = cell.flags; var checks = []; // TODO: make dirs connect to other cross-points, such as N connecting to E of the NW tile and W of the NE tile if (y%2 != 1) { if (m & E) { if ((target = this.getTile(x+1, y))) { if (target.flags & W) checks.push(target); } if ((target = this.getTile(x, y-1))) { if (target.flags & S) checks.push(target); } if ((target = this.getTile(x, y+1))) { if (target.flags & N) checks.push(target); } } if (m & NE) { if ((target = this.getTile(x, y-1))) { if (target.flags & SW) checks.push(target); } } if (m & N) { if ((target = this.getTile(x, y-2))) { if (target.flags & S) checks.push(target); } if ((target = this.getTile(x-1, y-1))) { if (target.flags & E) checks.push(target); } if ((target = this.getTile(x, y-1))) { if (target.flags & W) checks.push(target); } } if (m & NW) { if ((target = this.getTile(x-1, y-1))) { if (target.flags & SE) checks.push(target); } } if (m & W) { if ((target = this.getTile(x-1, y))) { if (target.flags & E) checks.push(target); } if ((target = this.getTile(x-1, y-1))) { if (target.flags & S) checks.push(target); } if ((target = this.getTile(x-1, y+1))) { if (target.flags & N) checks.push(target); } } if (m & SW) { if ((target = this.getTile(x-1, y+1))) { if (target.flags & NE) checks.push(target); } } if (m & S) { if ((target = this.getTile(x, y+2))) { if (target.flags & N) checks.push(target); } if ((target = this.getTile(x-1, y+1))) { if (target.flags & E) checks.push(target); } if ((target = this.getTile(x, y+1))) { if (target.flags & W) checks.push(target); } } if (m & SE) { if ((target = this.getTile(x, y+1))) { if (target.flags & NW) checks.push(target); } } } else { if (m & E) { if ((target = this.getTile(x+1, y))) { if (target.flags & W) checks.push(target); } if ((target = this.getTile(x+1, y-1))) { if (target.flags & S) checks.push(target); } if ((target = this.getTile(x+1, y+1))) { if (target.flags & N) checks.push(target); } } if (m & NE) { if ((target = this.getTile(x+1, y-1))) { if (target.flags & SW) checks.push(target); } } if (m & N) { if ((target = this.getTile(x, y-2))) { if (target.flags & S) checks.push(target); } if ((target = this.getTile(x, y-1))) { if (target.flags & E) checks.push(target); } if ((target = this.getTile(x+1, y-1))) { if (target.flags & W) checks.push(target); } } if (m & NW) { if ((target = this.getTile(x, y-1))) { if (target.flags & SE) checks.push(target); } } if (m & W) { if ((target = this.getTile(x-1, y))) { if (target.flags & E) checks.push(target); } if ((target = this.getTile(x, y-1))) { if (target.flags & S) checks.push(target); } if ((target = this.getTile(x, y+1))) { if (target.flags & N) checks.push(target); } } if (m & SW) { if ((target = this.getTile(x, y+1))) { if (target.flags & SE) checks.push(target); } } if (m & S) { if ((target = this.getTile(x, y+2))) { if (target.flags & N) checks.push(target); } if ((target = this.getTile(x, y+1))) { if (target.flags & E) checks.push(target); } if ((target = this.getTile(x+1, y+1))) { if (target.flags & W) checks.push(target); } } if (m & SE) { if ((target = this.getTile(x+1, y+1))) { if (target.flags & NW) checks.push(target); } } } var last_state = cell.state; for (var i = 0; i < checks.length; i++) { if (checks[i].type == ENTRANCE) { cell.addSource(checks[i]); } else if (checks[i].type == 0 || checks[i].type == POWER || checks[i].type == EXIT) { if (checks[i].isPowered() && !checks[i].isPoweredBy(cell)) { cell.addSource(checks[i]); } } } if (cell.state != last_state) { if (cell.state == 0) { for (var i = 0; i < checks.length; i++) { checks[i].remSource(cell); //this.update.push(checks[i]); } if (cell.type == EXIT) { this.power--; this.updateUi(); } else if (cell.type == POWER) { this.amperage -= 64; this.updateUi(); } } else if (cell.state == 1) { this.sfx_node.play(); this.has_powered = true; for (var i = 0; i < checks.length; i++) { this.update.push(checks[i]); } this.amperage -= 32; if (cell.type == EXIT) { this.power++; this.createTileBorder(cell.m_x, cell.m_y, cell.flags); } else if (cell.type == POWER) { this.amperage += 64; this.createTileBorder(cell.m_x, cell.m_y, cell.flags); this.sfx_power.play(); this.has_bonused = true; } this.updateUi(); } } }; GameState.createTileBorder = function(x, y) { if (y%2 != 1) { for (var p_y = y-1; p_y <= y+1; p_y++) { // NE, NW, SE, SW, W if (p_y < 0 || p_y >= this.map.height) continue; for (var p_x = x-1; p_x < x+1; p_x++) { if (p_x < 0 || p_x >= this.map.width) continue; if (this.cells[p_y][p_x] == null) { this.cells[p_y][p_x] = new Tile(game_sprites, 0, 1, this.map_offset_x+p_x*18+((p_y%2) == 1 ? 9 : 0), this.map_offset_y+p_y*7); } } } if (y-2 >= 0) { // N if (this.cells[y-2][x] == null) { this.cells[y-2][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+x*18+(((y-2)%2) == 1 ? 9 : 0), this.map_offset_y+(y-2)*7); } } if (x+1 < this.map.width) { // E if (this.cells[y][x+1] == null) { this.cells[y][x+1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x+1)*18+((y%2) == 1 ? 9 : 0), this.map_offset_y+y*7); } } if (y+2 < this.map.height) { // S if (this.cells[y+2][x] == null) { this.cells[y+2][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+x*18+(((y+2)%2) == 1 ? 9 : 0), this.map_offset_y+(y+2)*7); } } } else { // alt for (var p_y = y-2; p_y <= y+2; p_y++) { // N, NW, 0, SW, S if (p_y < 0 || p_y >= this.map.height) continue; if (this.cells[p_y][x] == null) { this.cells[p_y][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+x*18+((p_y%2) == 1 ? 9 : 0), this.map_offset_y+p_y*7); } } if (x+1 < this.map.width) { for (var p_y = y-1; p_y <= y+1; p_y++) { // NE, E, SE if (p_y < 0 || p_y >= this.map.height) continue; if (this.cells[p_y][x+1] == null) { this.cells[p_y][x+1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x+1)*18+((p_y%2) == 1 ? 9 : 0), this.map_offset_y+p_y*7); } } } if (x-1 >= 0) { // W if (this.cells[y][x-1] == null) { this.cells[y][x-1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x-1)*18+((y%2) == 1 ? 9 : 0), this.map_offset_y+y*7); } } } }; GameState.createTileBorder_ = function(x, y, m) { // We're doing very silly things here because I don't want the standard isometric rotation for x & y. I want the map size to not be inhibited by its diamond shape (if std isometric is used, the diamond shape loses the corners as viable space due to x and y being mapped differently). So, we're just "shunting" every other y tile some x pixel offset to maximize playable space. There are probably smarter ways to do all this. if (y%2 != 1) { if (m & E) { if (this.isTileOpen(x+1, y)) { this.cells[y][x+1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x+1)*18+((y%2) == 1 ? 9 : 0), this.map_offset_y+y*7); } } if (m & NE) { if (this.isTileOpen(x, y-1)) { this.cells[y-1][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y-1)%2) == 1 ? 9 : 0), this.map_offset_y+(y-1)*7); } } if (m & N) { if (this.isTileOpen(x, y-2)) { this.cells[y-2][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y-2)%2) == 1 ? 9 : 0), this.map_offset_y+(y-2)*7); } } if (m & NW) { if (this.isTileOpen(x-1, y-1)) { this.cells[y-1][x-1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x-1)*18+(((y-1)%2) == 1 ? 9 : 0), this.map_offset_y+(y-1)*7); } } if (m & W) { if (this.isTileOpen(x-1, y)) { this.cells[y][x-1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x-1)*18+(((y)%2) == 1 ? 9 : 0), this.map_offset_y+(y)*7); } } if (m & SW) { if (this.isTileOpen(x-1, y+1)) { this.cells[y+1][x-1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x-1)*18+(((y+1)%2) == 1 ? 9 : 0), this.map_offset_y+(y+1)*7); } } if (m & S) { if (this.isTileOpen(x, y+2)) { this.cells[y+2][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y+2)%2) == 1 ? 9 : 0), this.map_offset_y+(y+2)*7); } } if (m & SE) { if (this.isTileOpen(x, y+1)) { this.cells[y+1][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y+1)%2) == 1 ? 9 : 0), this.map_offset_y+(y+1)*7); } } } else { if (m & E) { if (this.isTileOpen(x+1, y)) { this.cells[y][x+1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x+1)*18+((y%2) == 1 ? 9 : 0), this.map_offset_y+y*7); } } if (m & NE) { if (this.isTileOpen(x+1, y-1)) { this.cells[y-1][x+1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x+1)*18+(((y-1)%2) == 1 ? 9 : 0), this.map_offset_y+(y-1)*7); } } if (m & N) { if (this.isTileOpen(x, y-2)) { this.cells[y-2][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y-2)%2) == 1 ? 9 : 0), this.map_offset_y+(y-2)*7); } } if (m & NW) { if (this.isTileOpen(x, y-1)) { this.cells[y-1][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y-1)%2) == 1 ? 9 : 0), this.map_offset_y+(y-1)*7); } } if (m & W) { if (this.isTileOpen(x-1, y)) { this.cells[y][x-1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x-1)*18+(((y)%2) == 1 ? 9 : 0), this.map_offset_y+(y)*7); } } if (m & SW) { if (this.isTileOpen(x, y+1)) { this.cells[y+1][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y+1)%2) == 1 ? 9 : 0), this.map_offset_y+(y+1)*7); } } if (m & S) { if (this.isTileOpen(x, y+2)) { this.cells[y+2][x] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x)*18+(((y+2)%2) == 1 ? 9 : 0), this.map_offset_y+(y+2)*7); } } if (m & SE) { if (this.isTileOpen(x+1, y+1)) { this.cells[y+1][x+1] = new Tile(game_sprites, 0, 1, this.map_offset_x+(x+1)*18+(((y+1)%2) == 1 ? 9 : 0), this.map_offset_y+(y+1)*7); } } } }; GameState.getTile = function(x, y) { if (this.cells[y]) { if (this.cells[y][x]) { return this.cells[y][x]; } } return null; }; GameState.isTileOpen = function(x, y) { if (y < 0 || y >= this.map.height) return false; if (x < 0 || x >= this.map.width) return false; if (this.cells[y]) { if (!this.cells[y][x]) { return true; } } return false }; GameState.onClose = function(display) { for (var y = 0; y < this.map.height; y++) { for (var x = 0; x < this.map.width; x++) { if (this.cells[y][x] != null) { this.cells[y][x].destroy(); this.cells[y][x] = null; } } } this.map = null; this.cells = []; for (var i = 0; i < this.outline.length; i++) { this.outline[i].destroy(); this.outline[i] = null; } this.outline = []; if (this.tile) { this.tile.destroy(); this.tile = null; } for (var i = 0; i < this.cache.length; i++) { this.cache[i].destroy(); this.cache[i] = null; } this.cache = []; for (var i = 0; i < this.ui_sprites.length; i++) { this.ui_sprites[i].destroy(); this.ui_sprites[i] = null; } this.ui_sprites = []; for (var i = 0; i < this.ui_strings.length; i++) { this.ui_strings[i].destroy(); this.ui_strings[i] = null; } this.ui_strings = []; if (this.message != null) { this.message.destroy(); this.message = null; } for (var i = 0; i < this.qat.length; i++) { this.qat[i].destroy(); this.qat[i] = null; } this.qat = []; for (var i = 0; i < this.tooltip_strings.length; i++) { this.tooltip_strings[i].destroy(); this.tooltip_strings[i] = null; this.tooltip_parts[i] = null; } this.tooltip_strings = []; this.tooltip_parts = []; this.sfx_failure.destroy(); this.sfx_move.destroy(); this.sfx_place.destroy(); this.sfx_rotate.destroy(); this.sfx_node.destroy(); this.sfx_power.destroy(); this.sfx_win.destroy(); }; GameState.setTileXY = function(tile, x, y) { tile.setXY(this.map_offset_x+x*18+((y%2) == 1 ? 9 : 0), this.map_offset_y+y*7); }; GameState.updateUi = function() { var amperage = this.amperage.toString(); var len = 6 - amperage.length; for (var i = 0; i < len; i++) { amperage = "0"+amperage; } this.ui_strings[1].setString(amperage); this.ui_strings[2].setString(this.power+"/"+this.map.power); }; /* ================================================================ End State ================================================================ */ var EndState = { qat: [], qat_anim: 0, qat_move: 0, timer: 0, qat_blink: 0, qat_blink_timer: 0, thanks_string: null }; EndState.onOpen = function(display) { this.thanks_string = null; this.timer = 0; this.qat = []; this.qat_anim = 0; this.qat_move = 0; this.qat_blink = 0; this.qat_blink_timer = 0; var qat_x = 256/2 - 16 var qat_y = 8; var i = 0; var offset_y = 224/2 - 48; for (var x = 0; x < 4; x++) { for (var y = 0; y < 4; y++) { this.qat[i++] = new NesSprite(qat_sprites, x, y, qat_x + (8*x), offset_y - qat_y + (8*y)); } } this.thanks_string = new StringSprite(font, "The Qat thanks you", 0, 0); this.thanks_string.x = 256/2 - this.thanks_string.s_width/2; this.thanks_string.y = 224/2 + 8; }; EndState.onClose = function(display) { for (var i = 0; i < this.qat.length; i++) { this.qat[i].destroy(); this.qat[i] = null; } this.qat = []; this.thanks_string.destroy(); }; EndState.onTick = function(delta, display) { display.Fill(0x00, 0x00, 0x00); if (this.qat_anim == 0) { this.qat_move += 0.05; if (this.qat_move > 5) this.qat_anim = 1; } else { this.qat_move -= 0.05; if (this.qat_move < 1) this.qat_anim = 0; } this.qat_blink_timer += delta; if (this.qat_blink_timer >= 1000) { this.qat_blink_timer = 0; this.qat_blink++; if (this.qat_blink < 4) { var i = 0; for (var x = (this.qat_blink*4); x < 4+(this.qat_blink*4); x++) { for (var y = 0; y < 4; y++) { this.qat[i++].set(x, y); } } } } for (var i = 0; i < this.qat.length; i++) { this.qat[i].draw(0, Math.round(this.qat_move)); } this.thanks_string.draw(0, 0); this.timer += delta; if (this.timer >= 10000 || checkButton(START)) { swapState(MenuState); } }; /* ================================================================ String Sprites ================================================================ */ StringSprite = function(spritesheet, msg, x, y) { this.spritesheet = spritesheet; this.x = x; this.y = y; this.len = msg.length; this.chars = []; this.width = spritesheet.sprite_size.width; this.height = spritesheet.sprite_size.height; this.s_width = this.len*this.width; for (i = 0; i < this.len; i++) { var id = msg.charCodeAt(i)-32; y = Math.floor(id / spritesheet.cols); x = Math.round(id - (y*spritesheet.cols)); this.chars.push(display.Sprite(font, {x: x*this.width, y: y*this.height}, {width: this.width, height: this.height})); } this.destroy = function() { var len = this.chars.length; for (i = len-1; i >= 0; i--) { this.chars[i].destroy(); this.chars[i] = null; } }; this.draw = function(x, y) { x += this.x; y += this.y; for (i = 0; i < this.chars.length; i++) { display.draw(this.chars[i], {x: 0, y: 0}, {x: x, y: y}); x += this.width; } }; this.setString = function(new_string) { if (new_string.length > this.len) { for (var i = this.len-1; i < new_string.length; i++) { this.chars.push(display.Sprite(font, {x: 0*this.width, y: 0*this.height}, {width: this.width, height: this.height})); } } else { for (var i = this.len; i > new_string.length; i--) { //this.chars } } for (var i = 0; i < new_string.length; i++) { var id = new_string.charCodeAt(i)-32; y = Math.floor(id / spritesheet.cols); x = Math.round(id - (y*spritesheet.cols)); this.chars[i].setPosition(this.width*x, this.height*y); } }; this.addChar = function(ch) { var id = ch.charCodeAt(0)-32; y = Math.floor(id / spritesheet.cols); x = Math.round(id - (y*spritesheet.cols)); this.chars.push(display.Sprite(font, {x: x*this.width, y: y*this.height}, {width: this.width, height: this.height})); }; }; NesSprite = function(spritesheet, row, col, x, y) { this.spritesheet = spritesheet; this.x = x; this.y = y; this.row = row; this.col = col; this.width = spritesheet.sprite_size.width; this.height = spritesheet.sprite_size.height; this.sprite = display.Sprite(spritesheet, {x: row*this.width, y: col*this.height}, {width: this.width, height: this.height}); this.destroy = function() { this.sprite.destroy(); this.sprite = null; }; this.draw = function(x, y) { x += this.x; y += this.y; display.draw(this.sprite, {x: 0, y: 0}, {x: x, y: y}); }; this.set = function(x, y) { this.row = x; this.col = y; this.sprite.setPosition(this.width*x, this.height*y); }; this.setXY = function(x, y) { this.x = x; this.y = y; }; }; // tile W = 1, NW = 2, N = 4, NE = 8, E = 16, SE = 32, S = 64, SW = 128; Tile = function(spritesheet, flags, type, x, y) { this.x = x; this.y = y; this.m_x = 0; // map x this.m_y = 0; // map y this.type = type; // this.source = []; if (type == ENTRANCE) { this.state = 1; this.offset = 6; } else { this.state = 0; // 0 = unpowered, 1 = powered // offset used for unpowered and powered sprites this.offset = 0; } var type_offset = type*3; // draw flag this.do_draw = true; // tile sprites this.sprites = []; this.sprites[0] = new NesSprite(spritesheet, 0+type_offset, 4, x, y); this.sprites[1] = new NesSprite(spritesheet, 1+type_offset, 4, x+8, y); this.sprites[2] = new NesSprite(spritesheet, 2+type_offset, 4, x+16, y); this.sprites[3] = new NesSprite(spritesheet, 0+type_offset, 5, x, y+8); this.sprites[4] = new NesSprite(spritesheet, 1+type_offset, 5, x+8, y+8); this.sprites[5] = new NesSprite(spritesheet, 2+type_offset, 5, x+16, y+8); // 4 lines, for tl, tr, bl, br this.lines = []; this.lines[0] = new NesSprite(spritesheet, 0, 6+this.offset, x, y); this.lines[1] = new NesSprite(spritesheet, 0, 7+this.offset, x+8, y); this.lines[2] = new NesSprite(spritesheet, 0, 8+this.offset, x+16, y); this.lines[3] = new NesSprite(spritesheet, 0, 9+this.offset, x, y+8); this.lines[4] = new NesSprite(spritesheet, 0, 10+this.offset, x+8, y+8); this.lines[5] = new NesSprite(spritesheet, 0, 11+this.offset, x+16, y+8); this.draw = function(x, y) { this.sprites[0].draw(x, y); this.sprites[1].draw(x, y); this.sprites[2].draw(x, y); this.sprites[3].draw(x, y); this.sprites[4].draw(x, y); this.sprites[5].draw(x, y); this.lines[0].draw(x, y); this.lines[1].draw(x, y); this.lines[2].draw(x, y); this.lines[3].draw(x, y); this.lines[4].draw(x, y); this.lines[5].draw(x, y); }; this.set = function(flags) { this.flags = flags; this.tl = this.t = this.tr = this.bl = this.b = this.br = 0; // NOTE: would it be possible to mathematically derive each tile's visual offset directly from the flags themselves? if (this.flags & W) { this.tl |= 1; this.t |= 1; } if (this.flags & NW) { this.tl |= 2; this.t |= 2; } if (this.flags & N) { this.t |= 4; } if (this.flags & NE) { this.t |= 8; this.tr |= 1; } if (this.flags & E) { this.t |= 16; this.tr |= 2; } if (this.flags & SE) { this.b |= 4; this.br |= 1; } if (this.flags & S) { this.b |= 2; } if (this.flags & SW) { this.b |= 1; this.bl |= 1; } this.lines[0].set(this.tl, 6+this.offset); this.lines[1].set(this.t, 7+this.offset); this.lines[2].set(this.tr, 8+this.offset); this.lines[3].set(this.bl, 9+this.offset); this.lines[4].set(this.b, 10+this.offset); this.lines[5].set(this.br, 11+this.offset); }; this.setXY = function(x, y) { this.x = x; this.y = y; this.sprites[0].setXY(x, y); this.sprites[1].setXY(x+8, y); this.sprites[2].setXY(x+16, y); this.sprites[3].setXY(x, y+8); this.sprites[4].setXY(x+8, y+8); this.sprites[5].setXY(x+16, y+8); this.lines[0].setXY(x, y); this.lines[1].setXY(x+8, y); this.lines[2].setXY(x+16, y); this.lines[3].setXY(x, y+8); this.lines[4].setXY(x+8, y+8); this.lines[5].setXY(x+16, y+8); }; this.setSprite = function(x, y) { this.sprites[0].set(x, y); this.sprites[1].set(x+1, y); this.sprites[2].set(x+2, y); this.sprites[3].set(x, y+1); this.sprites[4].set(x+1, y+1); this.sprites[5].set(x+2, y+1); }; this.rotateLeft = function() { if (this.flags & W) { this.flags -= W; var flags = this.flags / 2; flags |= SW; } else { var flags = this.flags / 2; } this.set(flags); }; this.rotateRight = function() { if (this.flags & SW) { this.flags -= SW; var flags = this.flags * 2; flags |= W; } else { var flags = this.flags * 2; } if (flags > 255) { flags -= 256; } this.set(flags); }; this.flipUp = function() { }; this.flipDown = function() { }; this.isPowered = function() { if (this.source.length > 0) return true; return false; }; this.isPoweredBy = function(source) { for (var i = 0; i < this.source.length; i++) { if (this.source[i] == source) return true; } return false; }; this.addSource = function(source) { for (var i = 0; i < this.source.length; i++) { if (this.source[i] == source) return false; } this.source.push(source); this.state = 1; this.offset = 6; this.set(this.flags); }; this.remSource = function(source) { for (var i = 0; i < this.source.length; i++) { if (this.source[i] == source) this.source.splice(i, 1); } if (this.source.length == 0) { this.state = 0; this.offset = 0; this.set(this.flags); } }; this.destroy = function() { for (var i = 0; i < 6; i++) { this.sprites[i].destroy(); this.sprites[i] = null; this.lines[i].destroy(); this.lines[i] = null; } }; // set our lines appropriately this.set(flags); }; /* ================================================================ copyObject TODO: create/return Arrays for arrays. ================================================================ */ copyObject = function(source) { if (source instanceof Array) { var copy = new Array(); } else if (source instanceof Object) { var copy = new Object(); } for (property in source) { if (source[property] instanceof Object) { copy[property] = copyObject(source[property]); } else { copy[property] = source[property]; } } return copy; }; /* ================================================================ Maps ================================================================ */ var OUTLINE = 1, ENTRANCE = 2, EXIT = 3, POWER = 4; Maps = [ { name: "First Pulse", width: 5, height: 8, power: 1, qat: "\"...\"", qat_i: 0, cells: [ {x: 4, y: 7, m: N|NW|W, t: 2}, {x: 4, y: 4, m: N|S|SE|NW, t: 0}, {x: 0, y: 0, m: S|SE, t: 3}, {x: 1, y: 2, m: NW|SE|E, t: 4} ], tiles: [ {m: S|NE|NW, t: 0}, {m: S|N, t: 0}, {m: SE|SW|N, t: 0}, {m: S|NE, t: 0}, {m: SW|NE|N|NW, t: 0}, {m: S|SE|E, t: 0}, {m: S|SE|N, t: 0} ] }, { name: "Second Pulse", width: 8, height: 4, power: 2, qat: "\"...h..h..\"", qat_i: 0, cells: [ {x: 7, y: 2, m: W|SW|NW, t: 2}, {x: 0, y: 0, m: E|SE, t: 3}, {x: 2, y: 3, m: E|W|NW|NE, t: 3} ], tiles: [ {m: E|W|SE|NE, t: 0}, {m: E|W, t: 0}, {m: E|N|S, t: 0}, {m: E|SW|NW, t: 0}, {m: E|S|NW, t: 0}, {m: E|W|SE|NE, t: 0}, {m: E|W, t: 0}, {m: E|NE|W, t: 0} ] }, { name: "Third Pulse", width: 10, height: 7, power: 2, qat: "\"..hello..\"", qat_i: 1, cells: [ {x: 0, y: 6, m: N, t: 2}, {x: 2, y: 0, m: S|SW|E, t: 3}, {x: 4, y: 6, m: N|E|NW, t: 4}, {x: 7, y: 0, m: S|SW|W, t: 4}, {x: 9, y: 6, m: NW|N, t: 3} ], tiles: [ {m: N|NE|E|S, t: 0}, {m: N|S|W, t: 0}, {m: E|N|NE, t: 0}, {m: S|N|E, t: 0}, {m: E|W|SE|N, t: 0}, {m: W|E|NE|NW, t: 0}, {m: E|SW|NE|N, t: 0}, {m: E|SE|S, t: 0}, {m: W|E, t: 0}, {m: E|W|N, t: 0}, {m: NW|SE|SW, t: 0}, {m: NW|E|W, t: 0}, {m: S|E|W, t: 0}, {m: S|E, t: 0}, {m: E|W, t: 0}, {m: N|S|NE|W, t: 0} ] }, { name: "Heartbeat", width: 8, height: 8, power: 1, qat: "\"we are awakening..\"", qat_i: 1, cells: [ {x: 0, y: 3, m: NE|S, t: 2}, {x: 0, y: 6, m: N|E, t: 4}, {x: 3, y: 7, m: W|NE, t: 2}, {x: 7, y: 2, m: S|NW, t: 3}, {x: 4, y: 0, m: W|S|SE, t: 4} ], tiles: [ {m: S|N|E, t: 0}, {m: N|S, t: 0}, {m: N|E|S, t: 0}, {m: NW|SE|E|W, t: 0}, {m: E|W|S, t: 0}, {m: NW|SE|SW|NE, t: 0}, {m: N|E|S, t: 0}, {m: E|W, t: 0}, {m: E|W|N, t: 0} ] }, { name: "Triple Bypass", width: 7, height: 12, power: 3, qat: "\"our mind is almost here\"", qat_i: 1, cells: [ {x: 3, y: 11, m: N|NE|NW, t: 2}, {x: 0, y: 7, m: S|SE, t: 3}, {x: 6, y: 5, m: S|SW, t: 3}, {x: 3, y: 0, m: S|SE|SW, t: 3} ], tiles: [ {m: N|NE|W, t: 0}, {m: S|N|E, t: 0}, {m: N|S, t: 0}, {m: S|E|W, t: 0}, {m: E|S|W, t: 0}, {m: N|S, t: 0}, {m: SE|NW|SW, t: 0}, {m: SE|NE|N|SW, t: 0}, {m: N|S, t: 0}, {m: SE|SW|NW|E, t: 0}, {m: E|W, t: 0}, {m: SE|SW|W, t: 0}, {m: N|S, t: 0}, {m: E|NW|SE, t: 0}, {m: N|NE|W, t: 0}, ] }, { name: "Leapfrog", width: 12, height: 7, power: 4, qat: "\"We--I am aware.\"", qat_i: 2, cells: [ {x: 11, y: 1, m: S|W|SW, t: 3}, {x: 10, y: 1, m: E|W|S, t: 2}, {x: 10, y: 6, m: N|NW|NE, t: 3}, {x: 8, y: 4, m: N|E|W|S, t: 4}, {x: 6, y: 6, m: E|W|NE|NW, t: 2}, {x: 4, y: 1, m: S|E|W|SE, t: 3}, {x: 3, y: 3, m: SW|NE|SE|NW, t: 4}, {x: 1, y: 1, m: E|S, t: 2}, {x: 1, y: 6, m: N|E|NW|S, t: 3} ], tiles: [ {m: SW|NE|S, t: 0}, {m: SE|NE|S, t: 0}, {m: W|E|S, t: 0}, {m: S|NE|SW, t: 0}, {m: SE|NE|SW, t: 0}, {m: S|E|W, t: 0}, {m: SE|NW|S|W, t: 0}, {m: S|N|E, t: 0}, {m: S|NE|W, t: 0}, {m: SW|NE|E, t: 0}, {m: W|N|S, t: 0}, {m: E|N|S, t: 0}, {m: W|NE|NW|S, t: 0}, {m: E|NW|S, t: 0}, {m: N|E|W|S, t: 0}, {m: NW|E|SW|S, t: 0} ] }, { name: "Stepgap", width: 12, height: 6, power: 1, qat: "\"We..I am nearing\"", qat_i: 2, cells: [ {x: 11, y: 0, m: S|W, t: 2}, {x: 11, y: 3, m: S|N|W, t: 2}, {x: 11, y: 5, m: N|W, t: 2}, {x: 9, y: 2, m: E|SW|NW, t: 4}, {x: 9, y: 4, m: E|SW|NW, t: 4}, {x: 1, y: 3, m: S|N, t: 3} ], tiles: [ {m: W|E, t: 0}, {m: S|N|E, t: 0}, {m: W|S|N, t: 0}, {m: S|N, t: 0}, {m: NW|SE, t: 0}, {m: SW|NE|SE, t: 0}, {m: SW|NE, t: 0}, {m: SW|NE|S, t: 0}, {m: S|N|E|W, t: 0}, {m: S|N|NW|E|W, t: 0}, {m: S|NW|E|W, t: 0}, {m: S|E, t: 0}, {m: N|E|W, t: 0} ] }, { name: "Power Overwhelming", width: 8, height: 6, power: 2, qat: "\"Soon..enlightenment\"", qat_i: 2, cells: [ {x: 0, y: 1, m: S, t: 3}, {x: 0, y: 5, m: N, t: 3}, {x: 3, y: 2, m: W|NE|SE, t: 4}, {x: 3, y: 4, m: W|NE|SE, t: 4}, {x: 5, y: 1, m: W|E|SE, t: 4}, {x: 5, y: 3, m: W|E, t: 4}, {x: 5, y: 5, m: W|E|NE, t: 4}, {x: 7, y: 3, m: W|NW|SW, t: 2} ], tiles: [ {m: E|W|NW|SW, t: 0}, {m: W|NW|SW|NE, t: 0}, {m: W|E|SW|NE, t: 0}, {m: NW|SW|NE, t: 0}, {m: N|S|E, t: 0}, {m: N|E, t: 0}, {m: N|E|S, t: 0}, {m: W|E|N, t: 0}, {m: W|E, t: 0}, {m: W|E|S|N, t: 0} ] }, { name: "Last Pitstop", height: 9, width: 5, power: 1, qat: "\"for us\"", qat_i: 2, cells: [ {x: 3, y: 0, m: E|W, t: 3}, {x: 3, y: 8, m: E|W, t: 2}, {x: 0, y: 4, m: N|S, t: 4} ], tiles: [ {m: N|S, t: 0}, {m: N|E|S, t: 0}, {m: W|S|E, t: 0}, {m: NW|NE|SW|SE, t: 0}, {m: S|N|W, t: 0}, {m: NW|SE, t: 0}, {m: SE|E, t: 0}, {m: W|S, t: 0} ] }, { name: "The Absolute", height: 10, width: 5, power: 5, qat: "\"...\"", qat_i: 2, cells: [ {x: 2, y: 9, m: N, t: 2}, {x: 1, y: 0, m: SE, t: 3}, {x: 2, y: 0, m: S, t: 3}, {x: 3, y: 0, m: SW, t: 3}, {x: 3, y: 1, m: W, t: 3}, {x: 3, y: 2, m: NW, t: 3} ], tiles: [ {m: N|S, t: 4}, {m: NE|N|NW, t: 4}, {m: NW|N|NE, t: 4}, {m: N|S, t: 4}, {m: E|W|S, t: 4}, {m: NW|NE|SE, t: 4}, {m: E|NE|N|NW|W|SW|S|SE, t: 4}, {m: N|S, t: 4}, {m: N|E|S, t: 4}, {m: E|NE|N|NW|W|SW|S|SE, t: 4} ] } ]; }; CBDL.extend(CBDL.App, CirQuit);