From 7ccbdf1109c08c412f97401010781b6221ec7aea Mon Sep 17 00:00:00 2001 From: cassowarii <2374677+cassowarii@users.noreply.github.com> Date: Sat, 13 Jun 2026 15:13:57 -0700 Subject: [PATCH] zucchinibread.js 2.0... --- cover.png | Bin 38574 -> 0 bytes game.js | 395 +++++++++++++---------------- pause.png | Bin 93 -> 0 bytes zucchinibread.js | 750 +++++++++++++++++++++++++++---------------------------- 4 files changed, 542 insertions(+), 603 deletions(-) delete mode 100644 cover.png delete mode 100644 pause.png diff --git a/cover.png b/cover.png deleted file mode 100644 index 5026f848c6879c1c5cd705cc210489021f949e2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38574 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 diff --git a/game.js b/game.js index d22e86e..e9e9957 100644 --- a/game.js +++ b/game.js @@ -1,8 +1,6 @@ "use strict"; -let game; - -/* --------- definitions ---------- */ +/* --------- setup ---------- */ /* state: * TITLE: title screen @@ -15,23 +13,16 @@ let game; */ let State = { TITLE: -1, STAND: 0, DRAG: 1, DESTROY: 2, REPLACE: 3, LIGHTNING: 4, GAMEOVER: 5 }; -let can_continue = false; - -let save_data = 1; const SAVE_KEY = "casso.signalconnectinggame.save" -zb.ready(function() { - game = zb.create_game({ +zb.ready(() => { + zb.create_game({ canvas: 'canvas', - canvas_w: 486, - canvas_h: 756, draw_scale: 3, tile_size: 16, level_w: 8, level_h: 8, background_color: '#f4ecce', - run_in_background: true, - load_with_progress_bar: true, save_key: SAVE_KEY, state: State.STAND, events: { @@ -41,13 +32,6 @@ zb.ready(function() { mousedown: handle_mousedown, }, modes: { - menu: { - draw: do_draw_menu, - update: do_update_menu, - }, - youwon: { - draw: do_draw_youwon, - }, game: { draw: do_draw, update: do_update, @@ -55,9 +39,8 @@ zb.ready(function() { }, mode: 'game', buttons: {}, - }); - - game.register_images({ + }) + .register_images({ connector: 'img/connector.png', gameover: 'img/gameover.png', lightning: 'img/lightning.png', @@ -72,9 +55,8 @@ zb.ready(function() { timebar_inside: 'img/timebar_inside.png', timebar_outside: 'img/timebar_outside.png', title: 'img/title.png', - }); - - game.register_sfx({ + }) + .register_sfx({ ok: { path: 'sfx/ok.wav', volume: 0.7, @@ -96,102 +78,92 @@ zb.ready(function() { path: 'sfx/gameover.mp3', volume: 0.3, }, - }); - - game.register_music({ + }) + .register_music({ bgm: { - path: 'music/suitgamesong', + path: 'music/suitgamesong.mp3', volume: 0.5, }, - }); - - game.resources_ready(); - - board_offset_x = game.screen_w / 2 - TILE_W * BOARD_W / 2; - board_offset_y = game.screen_h / 2 - TILE_H * BOARD_H / 2; - - high_score = game.load('high_score') || 0; - - game.buttons.menu = zb.buttons.create({ - img: game.img.menubuttons, - button_w: 64, - button_h: 16, - buttons: [ ], - }); - - game.buttons.lightning = zb.buttons.create({ - img: game.img.lightningbutton, - button_w: 36, - button_h: 18, - buttons: [ - { - /* Lightning button */ - x: game.screen_w / 2 - 16, - y: board_offset_y + BOARD_H * TILE_H + 12, - id: 0, - disabled: true, - callback: function() { - if (game.state === State.STAND) { - game.state = State.LIGHTNING; - game.buttons.lightning.buttons[0].disabled = true; - } + }) + .resources_ready() + .setup(() => { + board_offset_x = game.screen_w / 2 - TILE_W * BOARD_W / 2; + board_offset_y = game.screen_h / 2 - TILE_H * BOARD_H / 2; + + high_score = game.load('high_score') || 0; + + game.buttons.lightning = zb.buttons.create({ + img: game.img.lightningbutton, + button_w: 36, + button_h: 18, + buttons: [ + { + /* Lightning button */ + x: game.screen_w / 2 - 16, + y: board_offset_y + BOARD_H * TILE_H + 12, + id: 0, + disabled: true, + callback: function() { + if (game.state === State.STAND) { + game.state = State.LIGHTNING; + game.buttons.lightning.buttons[0].disabled = true; + } + }, }, - }, - ], - }); + ], + }); - game.buttons.play = zb.buttons.create({ - img: game.img.playbutton, - button_w: 36, - button_h: 18, - buttons: [ - { - /* Play button */ - x: game.screen_w / 2 - 16, - y: board_offset_y + BOARD_H * TILE_H + 12, - id: 0, - callback: function() { - if (game.state === State.TITLE) { - initialize(); - score = 0; - displayed_score = 0; - timer_running = false; - lightnings = 0; - } + game.buttons.play = zb.buttons.create({ + img: game.img.playbutton, + button_w: 36, + button_h: 18, + buttons: [ + { + /* Play button */ + x: game.screen_w / 2 - 16, + y: board_offset_y + BOARD_H * TILE_H + 12, + id: 0, + callback: function() { + if (game.state === State.TITLE) { + initialize(); + score = 0; + displayed_score = 0; + timer_running = false; + lightnings = 0; + } + }, }, - }, - ], - }); + ], + }); - game.buttons.replay = zb.buttons.create({ - img: game.img.replaybutton, - button_w: 36, - button_h: 18, - buttons: [ - { - /* Replay button */ - x: game.screen_w / 2 - 16, - y: board_offset_y + BOARD_H * TILE_H + 12, - id: 0, - callback: function() { - if (game.state === State.GAMEOVER) { - initialize(); - score = 0; - displayed_score = 0; - timer_running = false; - lightnings = 0; - n_colors = 2; - game.music.bgm.currentTime = 0; - } + game.buttons.replay = zb.buttons.create({ + img: game.img.replaybutton, + button_w: 36, + button_h: 18, + buttons: [ + { + /* Replay button */ + x: game.screen_w / 2 - 16, + y: board_offset_y + BOARD_H * TILE_H + 12, + id: 0, + callback: function() { + if (game.state === State.GAMEOVER) { + initialize(); + score = 0; + displayed_score = 0; + timer_running = false; + lightnings = 0; + n_colors = 2; + game.music.bgm.currentTime = 0; + } + }, }, - }, - ], - }); - - game.transition.color = game.background_color; + ], + }); - initialize(); + game.transition.color = game.background_color; + }); }); /* ------ relevant constant types --------- */ @@ -552,172 +524,156 @@ function do_update_menu(delta) { /* ---------- draw functions ----------- */ /* DRAW */ -function do_draw(ctx) { - ctx.save(); - - if (game.state === State.TITLE || show_title) { - zb.image_draw(ctx, game.img.title, board_offset_x + TILE_W * BOARD_W / 2 - game.img.title.width / 2, board_offset_y + TILE_H * BOARD_H / 2 - game.img.title.height / 2); - - zb.buttons.draw(ctx, game.buttons.play); - } - - draw_score(ctx); +function do_draw() { + draw.scoped(() => { + if (game.state === State.TITLE || show_title) { + draw.image(game.img.title, + board_offset_x + TILE_W * BOARD_W / 2 - game.img.title.width / 2, + board_offset_y + TILE_H * BOARD_H / 2 - game.img.title.height / 2); + + draw.buttons(game.buttons.play); + } - draw_tiles(ctx); + draw_score(); - draw_lightning(ctx); + draw_tiles(); - draw_selector(ctx); + draw_lightning(); - if (game.state === State.LIGHTNING) { - zb.sprite_draw(ctx, game.img.lightning, 9, 9, 0, 0, mouse_x - 4, mouse_y - 4); - } + draw_selector(); - if (game.state === State.GAMEOVER) { - zb.image_draw(ctx, game.img.gameover, board_offset_x + BOARD_W * TILE_W / 2 - game.img.gameover.width / 2, board_offset_y + BOARD_H * TILE_H / 2 - game.img.gameover.height / 2); + if (game.state === State.LIGHTNING) { + draw.sprite(game.img.lightning, 9, 9, 0, 0, mouse_x - 4, mouse_y - 4); + } - zb.buttons.draw(ctx, game.buttons.replay); - } + if (game.state === State.GAMEOVER) { + draw.image(game.img.gameover, + board_offset_x + BOARD_W * TILE_W / 2 - game.img.gameover.width / 2, + board_offset_y + BOARD_H * TILE_H / 2 - game.img.gameover.height / 2); - ctx.restore(); + draw.buttons(game.buttons.replay); + } + }); } -function draw_score_num(ctx, x, y, num) { +function draw_score_num(x, y, num) { let render_count = num; let col = 1; let lb = game.buttons.lightning.buttons[0]; while (col <= 8 || render_count > 0) { - zb.sprite_draw(ctx, game.img.scorenumbers, 8, 8, render_count % 10, 0, x + (8 - col) * 8, y); + draw.sprite(game.img.scorenumbers, 8, 8, render_count % 10, 0, x + (8 - col) * 8, y); render_count = Math.floor(render_count / 10); col ++; } } -function draw_score(ctx) { +function draw_score() { let timebar_x = board_offset_x + BOARD_W * TILE_W / 2 - game.img.timebar_outside.width / 2 + 16; let timebar_y = board_offset_y - 18 - game.img.timebar_outside.height / 2; if (game.state === State.GAMEOVER || game.state === State.TITLE) { - draw_score_num(ctx, timebar_x, timebar_y + 2, high_score); + draw_score_num(timebar_x, timebar_y + 2, high_score); - zb.sprite_draw(ctx, game.img.text, 32, 12, 0, 0, timebar_x - game.img.text.width, timebar_y + 2); // "best" + draw.sprite(game.img.text, 32, 12, 0, 0, timebar_x - game.img.text.width, timebar_y + 2); // "best" } else { - zb.image_draw(ctx, game.img.timebar_outside, timebar_x, timebar_y); + draw.image(game.img.timebar_outside, timebar_x, timebar_y); let timebar_width = Math.round((timer / MAX_TIMER) * game.img.timebar_inside.width); - zb.sprite_draw(ctx, game.img.timebar_inside, timebar_width, game.img.timebar_inside.height, 0, 0, timebar_x, timebar_y); + draw.sprite(game.img.timebar_inside, timebar_width, game.img.timebar_inside.height, 0, 0, timebar_x, timebar_y); - zb.sprite_draw(ctx, game.img.text, 32, 12, 0, 2, timebar_x - game.img.text.width, timebar_y + 2); // "time" + draw.sprite(game.img.text, 32, 12, 0, 2, timebar_x - game.img.text.width, timebar_y + 2); // "time" } - zb.sprite_draw(ctx, game.img.text, 32, 12, 0, 1, timebar_x - game.img.text.width, timebar_y - 10); // "score" + draw.sprite(game.img.text, 32, 12, 0, 1, timebar_x - game.img.text.width, timebar_y - 10); // "score" - draw_score_num(ctx, timebar_x, timebar_y - 10, displayed_score); + draw_score_num(timebar_x, timebar_y - 10, displayed_score); } -function draw_tiles(ctx) { - ctx.save(); +function draw_tiles() { + draw.scoped(() => { + draw.translate(board_offset_x, board_offset_y); - ctx.translate(board_offset_x, board_offset_y); - - for (let t of level) { - if (t == null) continue; - - zb.sprite_draw(ctx, game.img.tiles, TILE_W, TILE_H, t.suit, t.color, t.x, t.y); - } + for (let t of level) { + if (t == null) continue; - ctx.restore(); + draw.sprite(game.img.tiles, TILE_W, TILE_H, t.suit, t.color, t.x, t.y); + } + }); } -function draw_selector(ctx) { - ctx.save(); - - ctx.translate(board_offset_x, board_offset_y); +function draw_selector() { + draw.scoped(() => { + draw.translate(board_offset_x, board_offset_y); - if ((game.state === State.STAND || game.state === State.LIGHTNING) && selector.x !== -1 && selector.y !== -1) { - zb.sprite_draw(ctx, game.img.selector, 20, 20, 0, 0, selector.x * TILE_W - 2, selector.y * TILE_H - 2); - } else if (game.state === State.DRAG) { - let last_p = null; - for (let p of path) { - if (last_p === null) { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 0, 0, p[0] * TILE_W - 2, p[1] * TILE_H - 2); - } else { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 0, 0, p[0] * TILE_W - 2, p[1] * TILE_H - 2); - } - if (last_p) { - if (last_p[0] == p[0]) { - for (let y = Math.min(last_p[1], p[1]) + 1; y <= Math.max(last_p[1], p[1]) - 1; y++) { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 2, 0, p[0] * TILE_W - 2, y * TILE_H - 2); - } - } else if (last_p[1] == p[1]) { - for (let x = Math.min(last_p[0], p[0]) + 1; x <= Math.max(last_p[0], p[0]) - 1; x++) { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 1, 0, x * TILE_W - 2, p[1] * TILE_H - 2); + if ((game.state === State.STAND || game.state === State.LIGHTNING) && selector.x !== -1 && selector.y !== -1) { + draw.sprite(game.img.selector, 20, 20, 0, 0, selector.x * TILE_W - 2, selector.y * TILE_H - 2); + } else if (game.state === State.DRAG) { + let last_p = null; + for (let p of path) { + if (last_p === null) { + draw.sprite(game.img.connector, 20, 20, 0, 0, p[0] * TILE_W - 2, p[1] * TILE_H - 2); + } else { + draw.sprite(game.img.connector, 20, 20, 0, 0, p[0] * TILE_W - 2, p[1] * TILE_H - 2); + } + if (last_p) { + if (last_p[0] == p[0]) { + for (let y = Math.min(last_p[1], p[1]) + 1; y <= Math.max(last_p[1], p[1]) - 1; y++) { + draw.sprite(game.img.connector, 20, 20, 2, 0, p[0] * TILE_W - 2, y * TILE_H - 2); + } + } else if (last_p[1] == p[1]) { + for (let x = Math.min(last_p[0], p[0]) + 1; x <= Math.max(last_p[0], p[0]) - 1; x++) { + draw.sprite(game.img.connector, 20, 20, 1, 0, x * TILE_W - 2, p[1] * TILE_H - 2); + } } } + last_p = p; } - last_p = p; - } - last_p = null; - for (let p of path) { - if (last_p === null) { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 0, 1, p[0] * TILE_W - 2, p[1] * TILE_H - 2); - } else { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 0, 1, p[0] * TILE_W - 2, p[1] * TILE_H - 2); - } - if (last_p) { - if (last_p[0] == p[0]) { - for (let y = Math.min(last_p[1], p[1]) + 1; y <= Math.max(last_p[1], p[1]) - 1; y++) { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 2, 1, p[0] * TILE_W - 2, y * TILE_H - 2); - } - } else if (last_p[1] == p[1]) { - for (let x = Math.min(last_p[0], p[0]) + 1; x <= Math.max(last_p[0], p[0]) - 1; x++) { - zb.sprite_draw(ctx, game.img.connector, 20, 20, 1, 1, x * TILE_W - 2, p[1] * TILE_H - 2); + last_p = null; + for (let p of path) { + if (last_p === null) { + draw.sprite(game.img.connector, 20, 20, 0, 1, p[0] * TILE_W - 2, p[1] * TILE_H - 2); + } else { + draw.sprite(game.img.connector, 20, 20, 0, 1, p[0] * TILE_W - 2, p[1] * TILE_H - 2); + } + if (last_p) { + if (last_p[0] == p[0]) { + for (let y = Math.min(last_p[1], p[1]) + 1; y <= Math.max(last_p[1], p[1]) - 1; y++) { + draw.sprite(game.img.connector, 20, 20, 2, 1, p[0] * TILE_W - 2, y * TILE_H - 2); + } + } else if (last_p[1] == p[1]) { + for (let x = Math.min(last_p[0], p[0]) + 1; x <= Math.max(last_p[0], p[0]) - 1; x++) { + draw.sprite(game.img.connector, 20, 20, 1, 1, x * TILE_W - 2, p[1] * TILE_H - 2); + } } } + last_p = p; } - last_p = p; } - } - - ctx.restore(); + }); } -function draw_lightning(ctx) { +function draw_lightning() { if (game.state !== State.GAMEOVER && game.state !== State.TITLE) { - zb.buttons.draw(ctx, game.buttons.lightning); + draw.buttons(game.buttons.lightning); let render_count = lightnings; let col = 1; let lb = game.buttons.lightning.buttons[0]; while (render_count > 0 || col == 1) { - zb.sprite_draw(ctx, game.img.lightningnumbers, 5, 8, render_count % 10, 0, lb.x + game.buttons.lightning.button_w - 7 - 5 * col, lb.y + 4); + draw.sprite(game.img.lightningnumbers, 5, 8, render_count % 10, 0, lb.x + game.buttons.lightning.button_w - 7 - 5 * col, lb.y + 4); render_count = Math.floor(render_count / 10); col ++; } } for (let lp of lightning_particles) { - zb.sprite_draw(ctx, game.img.lightning, 9, 9, 0, 0, lp.x - 4, lp.y - 4); + draw.sprite(game.img.lightning, 9, 9, 0, 0, lp.x - 4, lp.y - 4); } } -function do_draw_menu(ctx) { - ctx.save(); - ctx.translate(0, Math.sin(title_timer / TITLE_BOUNCE_LENGTH * 2 * Math.PI) * TITLE_BOUNCE_DISTANCE); - zb.screen_draw(ctx, game.img.title); - ctx.restore(); - - zb.screen_draw(ctx, game.img.titlegraphic); - zb.buttons.draw(ctx, game.buttons.menu); -} - -function do_draw_youwon(ctx) { - zb.screen_draw(ctx, game.img.youwin); -} - /* ---------- event handlers ------------ */ -function handle_gamestart(game) { +function handle_gamestart() { console.log("Game start!"); game.state = State.TITLE; @@ -734,7 +690,7 @@ function toggle_mute_button() { game.toggle_mute(); } -function handle_mousedown(game, e, x, y) { +function handle_mousedown(e, x, y) { if (game.state === State.TITLE) { if (zb.buttons.click(game.buttons.play, x, y)) return; } else if (game.state === State.GAMEOVER) { @@ -790,7 +746,7 @@ function handle_mousedown(game, e, x, y) { } } -function handle_mousemove(game, e, x, y) { +function handle_mousemove(e, x, y) { mouse_x = x; mouse_y = y; @@ -878,16 +834,11 @@ function handle_mousemove(game, e, x, y) { let clicked_once_lightning = false; -function handle_mouseup(game, e, x, y) { +function handle_mouseup(e, x, y) { if (game.state === State.TITLE) { zb.buttons.update(game.buttons.play, x, y); } else if (game.state === State.GAMEOVER) { zb.buttons.update(game.buttons.replay, x, y); - } else if (game.mode === 'youwon') { - game.long_transition(zb.transition.type.fade, 1000, function() { - game.mode = 'menu'; - continue_level = 1; - }); } else { zb.buttons.update(game.buttons.lightning, x, y); diff --git a/pause.png b/pause.png deleted file mode 100644 index 7479e89bb4cfb6f5df1d343f118e468e2bdfacf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^3P8-v!3HEhIq~KIDaPU;cPEB*=VV?2IpUr!jv*3L plT#8Bew;UGXvkF1l^5Y-U}&Dh$T}@t{2fpUgQu&X%Q~loCID)074QH6 diff --git a/zucchinibread.js b/zucchinibread.js index f46ddc6..6c399cf 100644 --- a/zucchinibread.js +++ b/zucchinibread.js @@ -1,7 +1,7 @@ "use strict"; -/* Please forgive my strictly procedural/functional style here. - * Thanks for reading though! */ +/* There are programming patterns in here that no one should ever have discovered. + * Proceed at your own risk. */ let zb = (function() { /* ---- Util ---- */ @@ -47,6 +47,53 @@ let zb = (function() { } function rand_int(a, b) { + let min = 0; + let max = 0; + if (b === undefined) { + max = a; + } else { + min = a; + max = b; + } + return Math.floor(Math.random() * (max - min) + min); + } + + /* ---- Interface to game code ---- */ + + function implicit_bind(context, func) { + /* cursed and evil javascript magic */ + /* lets us temporarily bind globals like dynamic scope, + * so we can pass in properties like 'game' to our functions + * without needing to call them directly */ + /* this lets us namespace stuff under 'game' or 'draw' + * but also not have those namespaces actually accessible + * from the global scope. but they will be accessible + * in all child function calls! aah! */ + let original_props = {} + for (let key of Object.keys(context)) { + original_props[key] = globalThis[key]; + globalThis[key] = context[key]; + } + + let result = func(); + + for (let key of Object.keys(context)) { + globalThis[key] = original_props[key]; + } + + return result; + } + + function call_game_func(game, func, args) { + return implicit_bind({ game: game }, () => { + func.apply(game, args); + }); + } + + function call_draw_func(game, draw, func, args) { + return implicit_bind({ game: game, draw: draw }, () => { + func.apply(game, args); + }); } /* ---- Resource loading / loading screen ---- */ @@ -55,6 +102,30 @@ let zb = (function() { let _SFX_ARRAY_SIZE = 10; + /* Update progress bar if the progress bar is activated. */ + function _update_progress_bar(game) { + if (game.load_with_progress_bar) { + if (game._progressbar_img_loaded) { + game.ctx.global.save(); + game.ctx.global.scale(game.draw_scale, game.draw_scale); + if (!game.img._progressbar.failed_to_load) { + game.ctx.global.drawImage( + game.img._progressbar, + 0, + 0, + Math.round(game._things_loaded / game._total_things_to_load * game.img._progressbar.width), + game.img._progressbar.height, + Math.round(game.canvas_w / 2 / game.draw_scale - game.img._progressbar.width / 2), + Math.round(game.canvas_h / 2 / game.draw_scale - game.img._progressbar.height / 2), + Math.round(game._things_loaded / game._total_things_to_load * game.img._progressbar.width), + game.img._progressbar.height, + ); + } + game.ctx.global.restore(); + } + } + } + /* Returns a callback that should be called when a resource finishes loading. */ function _register_resource(name, game, callback) { game._total_things_to_load ++; @@ -63,24 +134,7 @@ let zb = (function() { if (!game.ready_to_go) { game._things_loaded ++; console.log("Loaded", name + ". Things loaded:", game._things_loaded, "/", game._total_things_to_load); - if (game.load_with_progress_bar) { - if (game._progressbar_img_loaded) { - game.ctx.global.save(); - game.ctx.global.scale(game.draw_scale, game.draw_scale); - game.ctx.global.drawImage( - game.img._progressbar, - 0, - 0, - Math.round(game._things_loaded / game._total_things_to_load * game.img._progressbar.width), - game.img._progressbar.height, - Math.round(game.canvas_w / 2 / game.draw_scale - game.img._progressbar.width / 2), - Math.round(game.canvas_h / 2 / game.draw_scale - game.img._progressbar.height / 2), - Math.round(game._things_loaded / game._total_things_to_load * game.img._progressbar.width), - game.img._progressbar.height, - ); - game.ctx.global.restore(); - } - } + _update_progress_bar(game); _check_if_loaded(game); if (callback) { callback(); @@ -89,16 +143,29 @@ let zb = (function() { } } - /* Check if audio failed to load bc file doesn't exist and print error message. */ - /* TODO add the same thing for an image */ - function _sound_resource_error(name, game, audio) { - return function(e) { - if (audio === undefined) { - console.error("Error loading resource, skipping:", name); - game._things_loaded ++; - _check_if_loaded(game); - } + function _set_resource_load_handlers(game, resource, success_event, name, callback) { + /* Check if resource successfully loaded and update loading screen. */ + resource.addEventListener(success_event, _register_resource(name, game, callback), false); + + /* Check if resource failed to load bc file doesn't exist and print error message. */ + resource.addEventListener('error', function(e) { + resource.failed_to_load = true; + game._things_loaded ++; + console.error("Error loading resource, skipping:", name); + _update_progress_bar(game); + _check_if_loaded(game); + }, false); + } + + /* Call any queued setup functions in game context (after waiting for things to load, maybe) + * and clear the setup function queue */ + function _call_setup_funcs(game) { + for (let setupfunc of game._setup_funcs) { + call_game_func(game, setupfunc, []); } + + /* clear queue */ + game._setup_funcs = []; } /* Check if the game has loaded everything and if so show the 'click to start' image */ @@ -108,6 +175,8 @@ let zb = (function() { if (game._things_loaded >= game._total_things_to_load) { console.log("Ready"); game.ready_to_go = true; + game._waiting_for_resources = false; + _call_setup_funcs(game); game._on_ready(); } } @@ -117,11 +186,9 @@ let zb = (function() { let sfx_size = sfxdata[key].copies || _SFX_ARRAY_SIZE; let sfx_array = new Array(sfx_size); for (let i = 0; i < sfx_size; i++) { + let resource_name = sfxdata[key].path + '#' + (i+1); sfx_array[i] = new Audio(sfxdata[key].path); - sfx_array[i].addEventListener('canplaythrough', - _register_resource(sfxdata[key].path + '#' + (i+1), game), false); - sfx_array[i].addEventListener('error', - _sound_resource_error(sfxdata[key].path + '#' + (i+1), game), false, sfx_array[i]); + _set_resource_load_handlers(game, sfx_array[i], 'canplaythrough', resource_name); if (sfxdata[key].hasOwnProperty('volume')) { sfx_array[i].volume = sfxdata[key].volume; } @@ -130,6 +197,8 @@ let zb = (function() { _array: sfx_array, _index: 0, play: function() { + if (this.failed_to_load) return; + if (!game.muted) { this._array[this._index].currentTime = 0; this._array[this._index].play(); @@ -139,19 +208,21 @@ let zb = (function() { } } } + + return game; } function _register_music(musicdata, game) { for (let key in musicdata) { let musicpath; - if (musicdata[key].path.match(/\.[a-z]{3}$/)) { + if (musicdata[key].path.match(/\.[a-z0-9]+$/i)) { musicpath = musicdata[key].path; } else if (_audiocheck.canPlayType('audio/mpeg')) { musicpath = musicdata[key].path + '.mp3'; } else if (_audiocheck.canPlayType('audio/ogg')) { musicpath = musicdata[key].path + '.ogg'; } else { - console.log("No supported music type known :("); + console.error("Browser knows how to play neither mp3 nor ogg :("); return; } let music = new Audio(musicpath); @@ -163,13 +234,15 @@ let zb = (function() { /* We loop by default unless 'loop: false' is specified. */ music.loop = true; } - music.addEventListener('canplaythrough', _register_resource(musicpath, game), false); - music.addEventListener('error', _sound_resource_error(musicpath, game, music), false); + + _set_resource_load_handlers(game, music, 'canplaythrough', musicpath); /* Handle music changes when muted, so when we unmute, * the correct music will be playing. */ music._og_play = music.play; music.play = function() { + if (music.failed_to_load) return; + if (game.muted) { music.was_playing = true; } else { @@ -179,6 +252,8 @@ let zb = (function() { music._og_pause = music.pause; music.pause = function() { + if (music.failed_to_load) return; + if (game.muted) { music.was_playing = false; } else { @@ -188,6 +263,8 @@ let zb = (function() { game.music[key] = music; } + + return game; } function _register_images(imgdata, game) { @@ -198,7 +275,7 @@ let zb = (function() { result[key] = _recursive_load_images(pathmap[key]); } else { result[key] = new Image(); - result[key].onload = _register_resource(pathmap[key], game); + _set_resource_load_handlers(game, result[key], 'load', pathmap[key]); result[key].src = pathmap[key]; } } @@ -209,6 +286,8 @@ let zb = (function() { for (let k in loaded_imgs) { game.img[k] = loaded_imgs[k]; } + + return game; } function _create_canvas_context(canvas) { @@ -224,26 +303,75 @@ let zb = (function() { return new_ctx; } - function create_game(params) { - let game_props = {...params}; + function _generate_draw_context(canvas_ctx) { + /* this generates the magic 'draw' objects that get passed in + * implicitly to the draw event functions. we can still get + * at the original canvas context by `draw.context`, but this + * way, what is getting drawn onto is implicitly passed with + * `implicit_bind`, so we don't have to remember to pass `ctx` + * to every drawing function (which i always forget) but we + * can still draw to the correct canvas for stuff like + * custom transitions */ + return { + context: canvas_ctx, + + image: ((function(ctx) { + return function(img, x, y) { + image_draw(ctx, img, x, y); + }; + })(canvas_ctx)), - game_props.frame_rate = params.frame_rate || 60; + screen: ((function(ctx) { + return function(img) { + screen_draw(ctx); + }; + })(canvas_ctx)), - game_props.canvas_w = params.canvas_w || 640; - game_props.canvas_h = params.canvas_h || 480; - game_props.draw_scale = params.draw_scale || 4; - game_props.top_border = params.top_border || 0; - game_props.bottom_border = params.bottom_border || 8; - game_props.left_border = params.left_border || 0; - game_props.right_border = params.right_border || 0; + sprite: ((function(ctx) { + return function(img, section_w, section_h, section_x, section_y, dest_x, dest_y) { + sprite_draw(ctx, img, section_w, section_h, section_x, section_y, dest_x, dest_y); + }; + })(canvas_ctx)), - game_props.tile_size = params.tile_size || 16; - game_props.level_w = params.level_w || 10; - game_props.level_h = params.level_h || 7; + buttons: ((function(ctx) { + return function(button_data) { + button_draw(ctx, button_data); + }; + })(canvas_ctx)), - game_props.screen_w = game_props.canvas_w / game_props.draw_scale; - game_props.screen_h = game_props.canvas_h / game_props.draw_scale; + scoped: ((function(ctx) { + return function(scopefunc) { + ctx.save(); + scopefunc(); + ctx.restore(); + }; + })(canvas_ctx)), + translate: ((function(ctx) { + return function(x, y) { + ctx.translate(Math.round(x), Math.round(y)); + }; + })(canvas_ctx)), + + rotate: ((function(ctx) { + return function(radians) { + ctx.rotate(radians); + }; + })(canvas_ctx)), + + rotate_deg: ((function(ctx) { + return function(degrees) { + ctx.rotate(degrees * Math.PI / 180); + }; + })(canvas_ctx)), + }; + } + + function create_game(params) { + let game_props = {...params}; + + game_props.frame_rate = params.frame_rate || 60; + game_props.draw_scale = params.draw_scale || 4; game_props.background_color = params.background_color || '#000000'; let canvas = document.getElementById(game_props.canvas); @@ -252,6 +380,7 @@ let zb = (function() { global_ctx.imageSmoothingEnabled = false; global_ctx.webkitImageSmoothingEnabled = false; global_ctx.mozImageSmoothingEnabled = false; + let global_draw = _generate_draw_context(global_ctx); let mask_canvas = document.createElement('canvas'); mask_canvas.width = canvas.width; @@ -260,6 +389,7 @@ let zb = (function() { mask_ctx.imageSmoothingEnabled = false; mask_ctx.webkitImageSmoothingEnabled = false; mask_ctx.mozImageSmoothingEnabled = false; + let mask_draw = _generate_draw_context(mask_ctx); let copy_canvas = document.createElement('canvas'); copy_canvas.width = canvas.width; @@ -268,6 +398,7 @@ let zb = (function() { copy_ctx.imageSmoothingEnabled = false; copy_ctx.webkitImageSmoothingEnabled = false; copy_ctx.mozImageSmoothingEnabled = false; + let copy_draw = _generate_draw_context(copy_ctx); let draw_canvas = document.createElement('canvas'); draw_canvas.width = canvas.width; @@ -276,8 +407,13 @@ let zb = (function() { draw_ctx.imageSmoothingEnabled = false; draw_ctx.webkitImageSmoothingEnabled = false; draw_ctx.mozImageSmoothingEnabled = false; + let draw_draw = _generate_draw_context(draw_ctx); let game = { + /* Defaults that can be overridden */ + run_in_background: true, + load_with_progress_bar: true, + /* General properties */ ...game_props, @@ -287,30 +423,61 @@ let zb = (function() { ready_to_go: false, _total_things_to_load: 1, _things_loaded: 0, + _waiting_for_resources: false, + _setup_funcs: [], resources_ready: function() { this._things_loaded ++; console.log("Finished enumerating resources to load. Things loaded:", this._things_loaded, "/", this._total_things_to_load); + this._waiting_for_resources = true; _check_if_loaded(this); + return this; }, _on_ready: function() { - this.ready_to_go = true; - if (game.img._clicktostart && game.img._clicktostart.complete) { - game.ctx.global.save(); - game.ctx.global.scale(game.draw_scale, game.draw_scale); - game.ctx.global.drawImage(game.img._clicktostart, 0, 0); - game.ctx.global.restore(); + if (this.img._clicktostart && this.img._clicktostart.complete) { + this.ctx.global.save(); + this.ctx.global.scale(this.draw_scale, this.draw_scale); + if (!this.img._clicktostart.failed_to_load) { + this.ctx.global.drawImage(this.img._clicktostart, 0, 0); + } + this.ctx.global.restore(); } }, playing: false, play: function() { this.playing = true; if (this.events.gamestart) { - this.events.gamestart(this); + call_game_func(this, this.events.gamestart, []); } _loop(this); }, _norun: false, + setup: function(setupfunc) { + if (this._waiting_for_resources) { + /* If we said resources_ready() before calling setup, then + * the setup function will get queued until everything is + * loaded. This allows us to let the setup function depend + * on properties of the things that are loaded (eg image + * sizes, etc) */ + /* TODO: It would be kind of neat to queue the other resource + * loading functions as well if _waiting_for_resources is + * set so that resources_ready() really always waits for + * stuff to load before proceeding. But that might mess + * with the loading screen, and I don't know why this would + * be needed (maybe if somehow one image got loaded depending + * on a property of another image?) so low priority. + * Right now setup() is probably the only thing that depends + * on waiting for loads to complete. */ + this._setup_funcs.push(setupfunc); + } else { + /* If we aren't waiting for resources (ie either everything + * already loaded, or we called setup() before calling + * resources_ready()), just call the setupfunc directly */ + call_game_func(this, setupfunc, []); + } + + return this; + }, /* Audio */ sfx: {}, @@ -330,10 +497,10 @@ let zb = (function() { } }, register_sfx: function(sfxdata) { - _register_sfx(sfxdata, this); + return _register_sfx(sfxdata, this); }, register_music: function(musicdata) { - _register_music(musicdata, this); + return _register_music(musicdata, this); }, /* Drawing */ @@ -343,13 +510,19 @@ let zb = (function() { copy: copy_ctx, /* context for copying the old screen on transition */ draw: draw_ctx, /* context for drawing the real level */ }, + draw_context: { + global: global_draw, + mask: mask_draw, + copy: copy_draw, + draw: draw_draw, + }, create_canvas_context: function() { /* Function to return a fresh screen-sized canvas context, if we need it. */ return _create_canvas_context(this.canvas); }, img: {}, register_images: function(imgdata) { - _register_images(imgdata, this); + return _register_images(imgdata, this); }, /* Save */ @@ -389,12 +562,24 @@ let zb = (function() { }; })(); + /* Calculate screen size */ + if (!game.canvas_w) { + game.canvas_w = game.canvas.width; + } + + if (!game.canvas_h) { + game.canvas_h = game.canvas.height; + } + + game.screen_w = game.canvas_w / game.draw_scale; + game.screen_h = game.canvas_h / game.draw_scale; + /* Register event listeners */ for (let ev in params.events) { /* Register any other events I guess */ canvas['on' + ev] = function(e) { if (game.playing && !game._norun) { - params.events[ev](game, e); + call_game_func(game, params.events[ev], [e]); } } } @@ -444,14 +629,14 @@ let zb = (function() { canvas.onkeydown = function(e) { if (!game._norun && game.events.keydown) { - game.events.keydown(game, e); + call_game_func(game, game.events.keydown, [e]); e.preventDefault(); } } canvas.onkeyup = function(e) { if (!game._norun && game.events.keyup) { - game.events.keyup(game, e); + call_game_func(game, game.events.keyup, [e]); e.preventDefault(); } } @@ -475,15 +660,18 @@ let zb = (function() { /* Set loading stuff */ let loading_img = new Image(); - loading_img.onload = _register_resource('loading.png', game, function() { + _set_resource_load_handlers(game, loading_img, 'load', 'loading.png', function() { if (!game.ready_to_go) { game.ctx.global.save(); game.ctx.global.scale(game.draw_scale, game.draw_scale); - game.ctx.global.drawImage(loading_img, 0, 0); + if (!loading_img.failed_to_load) { + game.ctx.global.drawImage(loading_img, 0, 0); + } game.ctx.global.restore(); } }); loading_img.src = 'loading.png'; + game.img._loading = loading_img; if (game.load_with_progress_bar) { game._progressbar_img_loaded = false; @@ -491,14 +679,16 @@ let zb = (function() { game._progressbar_height = 0; let progressbar_img = new Image(); - progressbar_img.onload = _register_resource('progressbar.png', game, function() { + _set_resource_load_handlers(game, progressbar_img, 'load', 'progressbar.png', function() { game._progressbar_img_loaded = true; game._progressbar_width = progressbar_img.width; game._progressbar_height = progressbar_img.height; if (!game.ready_to_go) { game.ctx.global.save(); game.ctx.global.scale(game.draw_scale, game.draw_scale); - game.ctx.global.drawImage(loading_img, 0, 0); + if (!loading_img.failed_to_load) { + game.ctx.global.drawImage(loading_img, 0, 0); + } game.ctx.global.restore(); } }); @@ -507,25 +697,21 @@ let zb = (function() { } if (!game.run_in_background) { - let pause_img = new Image(); - pause_img.onload = _register_resource('pause.png', game); - pause_img.src = 'pause.png'; - game.img._pause = pause_img; + _register_images({ _pause: 'pause.png' }, game); } if (game.hasOwnProperty('save_key')) { - let saveerror_img = new Image(); - saveerror_img.onload = _register_resource('saveerror.png', game); - saveerror_img.src = 'saveerror.png'; - game.img._saveerror = saveerror_img; + _register_images({ _saveerror: 'saveerror.png' }, game); } let clicktostart_img = new Image(); - clicktostart_img.onload = _register_resource('clicktostart.png', game, function() { + _set_resource_load_handlers(game, clicktostart_img, 'load', 'clicktostart.png', function() { if (game.ready_to_go) { game.ctx.global.save(); game.ctx.global.scale(game.draw_scale, game.draw_scale); - game.ctx.global.drawImage(game.img._clicktostart, 0, 0); + if (!game.img._clicktostart.failed_to_load) { + game.ctx.global.drawImage(game.img._clicktostart, 0, 0); + } game.ctx.global.restore(); } }); @@ -578,15 +764,22 @@ let zb = (function() { if (timestamp == undefined) { timestamp = 0; _last_frame_time = timestamp; - } else if (_last_frame_time == 0) { - _last_frame_time = timestamp; - } else { - _timedelta += timestamp - _last_frame_time; - _last_frame_time = timestamp; - - while (_timedelta >= 1000 / game.frame_rate) { - _update(game, 1000 / game.frame_rate); - _timedelta -= 1000 / game.frame_rate; + } + _timedelta += timestamp - _last_frame_time; + _last_frame_time = timestamp; + + let times_updated = 0; + while (_timedelta >= 1000 / game.frame_rate) { + _update(game, 1000 / game.frame_rate); + _timedelta -= 1000 / game.frame_rate; + + /* Draw at least once every 4 frames */ + /* (also sometimes we get a really big delta e.g. if + * run_in_background: false, and we don't want to honor that) */ + times_updated ++; + if (times_updated >= 4) { + _timedelta = 0; + break; } } @@ -601,11 +794,11 @@ let zb = (function() { function _update(game, delta) { if (game.update_func) { - game.update_func(delta); + call_game_func(game, game.update_func, [delta]); } if (game.modes && game.modes.hasOwnProperty(game.mode) && game.modes[game.mode].update) { - game.modes[game.mode].update(delta); + call_game_func(game, game.modes[game.mode].update, [delta]); } if (game.transition.is_transitioning) { @@ -670,7 +863,7 @@ let zb = (function() { game._clicked_button.state = 0; _draw(game); if (game._clicked_button.callback) { - game._clicked_button.callback(); + call_game_func(game, game._clicked_button.callback, []); } game._clicked_button = null; return; @@ -680,19 +873,21 @@ let zb = (function() { /* ---- Mouse ---- */ function _handle_mousedown(game, e) { - if (!game.playing) return; - game.touchmode = false; + if (!game.playing) return; + const rect = game.canvas.getBoundingClientRect(); let x = Math.round((e.clientX - rect.left) / rect.width * game.screen_w) - 1; let y = Math.round((e.clientY - rect.top) / rect.height * game.screen_h) - 1; if (e.button === 0 && game.events.mousedown) { - game.events.mousedown(game, e, x, y); + call_game_func(game, game.events.mousedown, [e, x, y]); } } function _handle_mouseup(game, e) { + game.touchmode = false; + if (!game.playing && game.ready_to_go) { /* Click to start */ if (game.hasOwnProperty('save_key') && !game._show_save_error) { @@ -712,7 +907,9 @@ let zb = (function() { game._show_save_error = true; game.ctx.global.save(); game.ctx.global.scale(game.draw_scale, game.draw_scale); - game.ctx.global.drawImage(game.img._saveerror, 0, 0); + if (!game.img._saveerror.failed_to_load) { + game.ctx.global.drawImage(game.img._saveerror, 0, 0); + } game.ctx.global.restore(); return; } @@ -721,8 +918,6 @@ let zb = (function() { return; } - game.touchmode = false; - const rect = game.canvas.getBoundingClientRect(); let x = Math.round((e.clientX - rect.left) / rect.width * game.screen_w) - 1; let y = Math.round((e.clientY - rect.top) / rect.height * game.screen_h) - 1; @@ -730,18 +925,18 @@ let zb = (function() { _handle_clicked_button(game); if (e.button === 0 && game.events.mouseup) { - game.events.mouseup(game, e, x, y); + call_game_func(game, game.events.mouseup, [e, x, y]); } } function _handle_mousemove(game, e) { game.touchmode = false; - const rect = game.canvas.getBoundingClientRect(); - let x = Math.round((e.clientX - rect.left) / rect.width * game.screen_w) - 1; - let y = Math.round((e.clientY - rect.top) / rect.height * game.screen_h) - 1; - if (game.events.mousemove) { - game.events.mousemove(game, e, x, y); + if (game.playing && game.events.mousemove) { + const rect = game.canvas.getBoundingClientRect(); + let x = Math.round((e.clientX - rect.left) / rect.width * game.screen_w) - 1; + let y = Math.round((e.clientY - rect.top) / rect.height * game.screen_h) - 1; + call_game_func(game, game.events.mousemove, [e, x, y]); } } @@ -750,10 +945,10 @@ let zb = (function() { let _last_touch_event; function _handle_touchstart(game, e) { - if (!game.playing) return; - game.touchmode = true; + if (!game.playing) return; + if (e.touches) e = e.touches[0]; _last_touch_event = e; @@ -762,24 +957,24 @@ let zb = (function() { let x = Math.round((e.clientX - rect.left) / rect.width * game.screen_w) - 1; let y = Math.round((e.clientY - rect.top) / rect.height * game.screen_h) - 1; if (game.events.touchstart) { - game.events.touchstart(game, e, x, y); + call_game_func(game, game.events.touchstart, [e, x, y]); } else if (game.events.mousedown) { if (game.events.mousemove) { - game.events.mousemove(game, e, x, y); + call_game_func(game, game.events.mousemove, [e, x, y]); } - game.events.mousedown(game, e, x, y); + call_game_func(game, game.events.mousedown, [e, x, y]); } } function _handle_touchend(game, e) { + game.touchmode = true; + if (!game.playing && game.ready_to_go) { /* Click to start */ game.play(); return; } - game.touchmode = true; - const rect = game.canvas.getBoundingClientRect(); let x = Math.round((_last_touch_event.clientX - rect.left) / rect.width * game.screen_w) - 1; let y = Math.round((_last_touch_event.clientY - rect.top) / rect.height * game.screen_h) - 1; @@ -797,10 +992,10 @@ let zb = (function() { } function _handle_touchmove(game, e) { - if (!game.playing) return; - game.touchmode = true; + if (!game.playing) return; + if (e.touches) e = e.touches[0]; _last_touch_event = e; @@ -809,9 +1004,9 @@ let zb = (function() { let x = Math.round((e.clientX - rect.left) / rect.width * game.screen_w) - 1; let y = Math.round((e.clientY - rect.top) / rect.height * game.screen_h) - 1; if (game.events.touchmove) { - game.events.touchmove(game, e, x, y); + call_game_func(game, game.events.touchmove, [e, x, y]); } else if (game.events.mousemove) { - game.events.mousemove(game, e, x, y); + call_game_func(game, game.events.mousemove, [e, x, y]); } } @@ -820,16 +1015,17 @@ let zb = (function() { function _draw(game) { game.ctx.draw.save(); + game.ctx.draw.save(); game.ctx.draw.fillStyle = game.background_color; - game.ctx.draw.fillRect(0, 0, game.screen_w, game.screen_h); + game.ctx.draw.restore(); if (game.draw_func) { - game.draw_func(game.ctx.draw); + call_draw_func(game, game.draw_context.draw, game.draw_func, []); } if (game.modes && game.modes.hasOwnProperty(game.mode) && game.modes[game.mode].draw) { - game.modes[game.mode].draw(game.ctx.draw); + call_draw_func(game, game.draw_context.draw, game.modes[game.mode].draw, []); } if (game.transition.mid_long) { @@ -840,16 +1036,15 @@ let zb = (function() { game.ctx.draw.restore(); if (game.transition.is_transitioning) { - _draw_transition(game, game.ctx.mask); + call_draw_func(game, game.draw_context.mask, _draw_transition, []); } else { game.ctx.mask.drawImage(game.ctx.draw.canvas, 0, 0); } if (game.modes && game.modes.hasOwnProperty(game.mode) && game.modes[game.mode].draw_after_transition) { - game.modes[game.mode].draw_after_transition(game.ctx.mask); + call_draw_func(game, game.draw_context.mask, game.modes[game.mode].draw_after_transition, []); } - game.ctx.global.fillStyle = 'rgb(0, 0, 0)'; game.ctx.global.fillRect(0, 0, game.screen_w * game.draw_scale, game.screen_h * game.draw_scale); @@ -859,7 +1054,7 @@ let zb = (function() { game.ctx.global.drawImage(game.ctx.mask.canvas, 0, 0); - if (game._norun) { + if (game._norun && !game.img._pause.failed_to_load) { game.ctx.global.drawImage(game.img._pause, 0, 0); } @@ -869,19 +1064,25 @@ let zb = (function() { /* Draw a particular section of an image/spritesheet, * without having to do as much math or type the destination size */ function sprite_draw(ctx, img, section_w, section_h, section_x, section_y, dest_x, dest_y) { - ctx.drawImage(img, - Math.round(section_w * section_x), Math.round(section_h * section_y), section_w, section_h, - Math.round(dest_x), Math.round(dest_y), section_w, section_h) + if (!img.failed_to_load) { + ctx.drawImage(img, + section_w * section_x, section_h * section_y, section_w, section_h, + Math.round(dest_x), Math.round(dest_y), section_w, section_h) + } } /* Draw an image over the whole screen lol */ function screen_draw(ctx, img) { - ctx.drawImage(img, 0, 0); + if (!img.failed_to_load) { + ctx.drawImage(img, 0, 0); + } } /* Draw an image at a specified position */ function image_draw(ctx, img, dest_x, dest_y) { - ctx.drawImage(img, Math.round(dest_x), Math.round(dest_y)); + if (!img.failed_to_load) { + ctx.drawImage(img, Math.round(dest_x), Math.round(dest_y)); + } } /* Draw a set of buttons. */ @@ -995,7 +1196,7 @@ let zb = (function() { let tdih = game.transition.dir_invert_h; _internal_start_transition(game, type, length, function() { game.transition.mid_long = false; - callback(); + call_game_func(game, callback, []); game.transition.dir_invert_v = tdiv; game.transition.dir_invert_h = tdih; }); @@ -1020,7 +1221,7 @@ let zb = (function() { game.ctx.copy.drawImage(game.ctx.draw.canvas, 0, 0); - callback(); + call_game_func(game, callback, []); game.transition.is_transitioning = true; } @@ -1031,283 +1232,67 @@ let zb = (function() { if (game.transition.done_func) { setTimeout(function() { - game.transition.done_func(); + call_game_func(game, game.transition.done_func, []); game.transition.done_func = null; }, 400); } } - function _draw_transition(game, ctx) { + function _draw_transition() { let frac = game.transition.timer / game.transition.end_time; - ctx.save(); - ctx.clearRect(0, 0, game.screen_w, game.screen_h); - game.transition.type(game, ctx, game.ctx.copy, game.ctx.draw, frac, game.transition.invert_shape); - ctx.restore(); + draw.scoped(() => { + draw.context.clearRect(0, 0, game.screen_w, game.screen_h); + game.transition.type(game.ctx.copy.canvas, game.ctx.draw.canvas, frac, game.transition.invert_shape); + }); } - function _draw_slide_down_transition(game, ctx, oldc, newc, frac, reverse) { + function _draw_slide_down_transition(from, to, frac, reverse) { let offset = frac * game.screen_h; - ctx.drawImage(oldc.canvas, 0, -offset); - ctx.drawImage(newc.canvas, 0, game.screen_h - offset); + draw.image(from, 0, -offset); + draw.image(to, 0, game.screen_h - offset); } - function _draw_slide_up_transition(game, ctx, oldc, newc, frac, reverse) { + function _draw_slide_up_transition(from, to, frac, reverse) { let offset = frac * game.screen_h; - ctx.drawImage(oldc.canvas, 0, offset); - ctx.drawImage(newc.canvas, 0, - game.screen_h + offset); + draw.image(from, 0, offset); + draw.image(to, 0, - game.screen_h + offset); } - function _draw_fade_transition(game, ctx, oldc, newc, frac, reverse) { - ctx.globalAlpha = 1; - ctx.drawImage(oldc.canvas, 0, 0); - ctx.globalAlpha = frac; - ctx.drawImage(newc.canvas, 0, 0); + function _draw_fade_transition(from, to, frac, reverse) { + draw.context.globalAlpha = 1; + draw.image(from, 0, 0); + draw.context.globalAlpha = frac; + draw.image(to, 0, 0); } - /* ---- former contents of ready.js ---- */ - - // Due to Timo Huovinen - // https://stackoverflow.com/questions/799981/document-ready-equivalent-without-jquery - var ready = (function(){ - - var readyList, - DOMContentLoaded, - class2type = {}; - class2type["[object Boolean]"] = "boolean"; - class2type["[object Number]"] = "number"; - class2type["[object String]"] = "string"; - class2type["[object Function]"] = "function"; - class2type["[object Array]"] = "array"; - class2type["[object Date]"] = "date"; - class2type["[object RegExp]"] = "regexp"; - class2type["[object Object]"] = "object"; - - var ReadyObj = { - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - ReadyObj.readyWait++; - } else { - ReadyObj.ready( true ); - } - }, - // Handle when the DOM is ready - ready: function( wait ) { - // Either a released hold or an DOMready/load event and not yet ready - if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( ReadyObj.ready, 1 ); - } - - // Remember that the DOM is ready - ReadyObj.isReady = true; - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --ReadyObj.readyWait > 0 ) { - return; - } - // If there are functions bound, to execute - readyList.resolveWith( document, [ ReadyObj ] ); - - // Trigger any bound ready events - //if ( ReadyObj.fn.trigger ) { - // ReadyObj( document ).trigger( "ready" ).unbind( "ready" ); - //} - } - }, - bindReady: function() { - if ( readyList ) { - return; - } - readyList = ReadyObj._Deferred(); - - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - return setTimeout( ReadyObj.ready, 1 ); - } - - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - // A fallback to window.onload, that will always work - window.addEventListener( "load", ReadyObj.ready, false ); - - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", ReadyObj.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; - - try { - toplevel = window.frameElement == null; - } catch(e) {} - - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, - _Deferred: function() { - var // callbacks list - callbacks = [], - // stored [ context , args ] - fired, - // to avoid firing when already doing so - firing, - // flag to know if the deferred has been cancelled - cancelled, - // the deferred itself - deferred = { - - // done( f1, f2, ...) - done: function() { - if ( !cancelled ) { - var args = arguments, - i, - length, - elem, - type, - _fired; - if ( fired ) { - _fired = fired; - fired = 0; - } - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = ReadyObj.type( elem ); - if ( type === "array" ) { - deferred.done.apply( deferred, elem ); - } else if ( type === "function" ) { - callbacks.push( elem ); - } - } - if ( _fired ) { - deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); - } - } - return this; - }, - - // resolve with given context and args - resolveWith: function( context, args ) { - if ( !cancelled && !fired && !firing ) { - // make sure args are available (#8421) - args = args || []; - firing = 1; - try { - while( callbacks[ 0 ] ) { - callbacks.shift().apply( context, args );//shifts a callback, and applies it to document - } - } - finally { - fired = [ context, args ]; - firing = 0; - } - } - return this; - }, - - // resolve with this as context and given arguments - resolve: function() { - deferred.resolveWith( this, arguments ); - return this; - }, - - // Has this deferred been resolved? - isResolved: function() { - return !!( firing || fired ); - }, - - // Cancel - cancel: function() { - cancelled = 1; - callbacks = []; - return this; - } - }; - - return deferred; - }, - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ Object.prototype.toString.call(obj) ] || "object"; - } - } - // The DOM ready check for Internet Explorer - function doScrollCheck() { - if ( ReadyObj.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch(e) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - ReadyObj.ready(); - } - // Cleanup functions for the document ready method - if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - ReadyObj.ready(); - }; - - } else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - ReadyObj.ready(); - } - }; - } - function ready( fn ) { - // Attach the listeners - ReadyObj.bindReady(); - - var type = ReadyObj.type( fn ); - - // Add the callback - readyList.done( fn );//readyList is result of _Deferred() + /* document.ready */ + /* Apparently, we have had DOMContentLoaded widely available since 2015. + * I've been carrying around this ancient document.ready invocation in these games since 2017, + * when using scary new js features might break things, but now it's been almost ten years + * and it's not like the rest of the game code would work in IE8 or whatever. So here's the + * simple version of ready. */ + function ready(ready_func) { + if (document.readyState === "loading") { + ready_func(); + } else { + document.addEventListener("DOMContentLoaded", ready_func); } - return ready; - })(); + } /* ---- direction objects ---- */ const _dir_up = { x: 0, y: -1, index: 0, rotate: 0, horizontal: false, vertical: true }; - const _dir_right = { x: 1, y: 0, index: 1, rotate: Math.PI/2, horizontal: true, vertical: false }; + const _dir_right = { x: 1, y: 0, index: 1, rotate: Math.PI / 2, horizontal: true, vertical: false }; const _dir_down = { x: 0, y: 1, index: 2, rotate: Math.PI, horizontal: false, vertical: true }; - const _dir_left = { x: -1, y: 0, index: 3, rotate: 3*Math.PI/2, horizontal: true, vertical: false }; + const _dir_left = { x: -1, y: 0, index: 3, rotate: 3 * Math.PI / 2, horizontal: true, vertical: false }; _dir_up.opposite = _dir_down; _dir_down.opposite = _dir_up; _dir_right.opposite = _dir_left; _dir_left.opposite = _dir_right; + /* ---- export zb ---- */ return { create_game: create_game, @@ -1318,9 +1303,12 @@ let zb = (function() { copy_list: copy_list, copy_flat_objlist: copy_flat_objlist, - sprite_draw: sprite_draw, - screen_draw: screen_draw, - image_draw: image_draw, + draw: { + sprite: sprite_draw, + screen: screen_draw, + image: image_draw, + buttons: button_draw, + }, buttons: { create: create_buttons, @@ -1345,5 +1333,5 @@ let zb = (function() { slide_down: _draw_slide_down_transition, }, }, - } + }; })(); -- 1.8.3.1