Download
238 lines 6.7 KiBHTMLModified 2026-06-01 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
<!doctype html>
<html lang="en">
  <head>
    <title>PICO-8 Cartridge</title>
    <meta name="viewport" content="width=device-width, user-scalable=no" />
    <style>
      html,
      body {
        height: 100%;
        width: 100%;
        padding: 0;
        margin: 0;
        background-color: #0ff;
        overflow: hidden;
        display: flex;
        align-items: center;
        justify-content: center;
      }
      #canvas {
        image-rendering: -moz-crisp-edges;
        image-rendering: -webkit-crisp-edges;
        image-rendering: pixelated;
        image-rendering: crisp-edges;
        display: block;
      }
      #log {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        max-height: 50%;
        overflow: auto;
        z-index: 10000;
        font-family: monospace;
        white-space: pre-wrap;
        background: rgba(17, 17, 17, 0.9);
        color: #8f8;
        padding: 10px;
        font-weight: 800;
        pointer-events: none;
      }
    </style>
  </head>

  <body>
    <div id="log"></div>
    <script>
      const logElem = document.getElementById('log');
      const log = (type, args) => {
        logElem.innerHTML += `<span style="color:${type==='error'?'#f55':type==='warn'?'#fa0':'#0f0'}">[${type}] ${[...args].map(a=>typeof a==='object'?JSON.stringify(a,null,2):a).join(' ')}</span>\n`;
      };

      ['log','warn','error','info'].forEach(t => {
        const orig = console[t];
        console[t] = (...args) => { log(t, args); orig.apply(console, args); };
      });

      window.onerror = (msg, src, line, col, err) => {
        logElem.style.display = 'block';
        log('error', [`${msg} (${src}:${line}:${col})`]);
      };

      window.onunhandledrejection = e => {
        logElem.style.display = 'block';
        log('error', ['Unhandled promise rejection:', e.reason]);
      };
      console.log("Starting...");
    </script>
    <script type="text/javascript">
      // PICO-8 globals
      var pico8_state = {
        is_paused: 0,
        sound_volume: 100,
        frame_number: 0,
        has_focus: 1,
        show_dpad: 0,
        request_pointer_lock: 0,
        require_page_navigate_confirmation: 0,
        shutdown_requested: 0
      };
      var pico8_buttons = [0, 0, 0, 0, 0, 0, 0, 0];
      var pico8_mouse = [0, 0, 0];
      var pico8_gpio = new Array(128);
      var p8_touch_detected = false;
      var codo_key_buffer = [];
      var codo_wheel_dx = 0;
      var codo_wheel_dy = 0;
      var codo_mounted_filesystem = 0;
      var codo_command = 0;
      var codo_command_p = 0;

      var pico8_audio_context;

      function p8_update_layout() {
        var canvas = document.getElementById("canvas");
        if (!canvas) {
          requestAnimationFrame(p8_update_layout);
          return;
        }
        var csize = 128;
        while (2*csize <= Math.min(window.innerWidth, window.innerHeight))
          csize *= 2;
        canvas.style.width = csize + "px";
        canvas.style.height = csize + "px";
        requestAnimationFrame(p8_update_layout);
      }

      function p8_create_audio_context() {
        if (pico8_audio_context) {
          try {
            pico8_audio_context.resume();
          } catch (err) {
            console.log("** pico8_audio_context.resume() failed");
          }
          return;
        }

        var webAudioAPI =
          window.AudioContext ||
          window.webkitAudioContext ||
          window.mozAudioContext ||
          window.oAudioContext ||
          window.msAudioContext;
        if (webAudioAPI) {
          pico8_audio_context = new webAudioAPI();
        }
      }

      var p8_is_running = false;
      var p8_script = null;
      var Module = null;

      function p8_run_cart() {
        if (p8_is_running) return;
        p8_is_running = true;

        p8_create_audio_context();

        console.log("Loading cart.js...");
        var e = document.createElement("script");
        p8_script = e;
        e.type = "application/javascript";
        e.src = "cart.js";
        e.id = "e_script";

        document.body.appendChild(e);
      }
    </script>

    <div id="p8_playarea" style="display: flex">
      <canvas
        class="emscripten"
        id="canvas"
        oncontextmenu="event.preventDefault()"
      ></canvas>
    </div>

    <textarea
      id="codo_textarea"
      class="emscripten"
      style="display: none; position: absolute; left: -9999px"
    ></textarea>

    <script type="text/javascript">
      // Mocks for PICO-8 engine
      var p8_frame = {
        offsetWidth: window.innerWidth,
        offsetHeight: window.innerHeight,
        parentNode: { parentNode: { offsetHeight: window.innerHeight } },
      };
      var p8_container = document.getElementById("p8_playarea");
      var body_0 = { style: {} };
      var p8_frame_0 = {
        parentNode: { appendChild: function () {} },
        style: {},
      };

      p8_update_layout();

      // Pre-initialize Module
      Module = {
        canvas: document.getElementById("canvas"),
        onRuntimeInitialized: function() {
          console.log("PICO-8 Runtime initialized.");
        },
        print: function(text) { console.log(text); },
        printErr: function(text) { console.error(text); }
      };

      // Auto-start game when AudioContext is ready
      var temp_context = new AudioContext();
      temp_context.onstatechange = function () {
        if (temp_context.state == "running") {
          p8_run_cart();
          temp_context.close();
        }
      };
      // For some environments, we might need a backup start
      setTimeout(() => {
          if (!p8_is_running) {
              console.log("Backup start triggered...");
              p8_run_cart();
          }
      }, 2000);
    </script>

    <script type="module">
      import { PLAYER_1, PLAYER_2 } from "@rcade/plugin-input-classic";

      function updateRcade() {
        if (typeof pico8_buttons !== "undefined") {
          // Player 1
          let b1 = 0;
          if (PLAYER_1.DPAD.left) b1 |= 0x1;
          if (PLAYER_1.DPAD.right) b1 |= 0x2;
          if (PLAYER_1.DPAD.up) b1 |= 0x4;
          if (PLAYER_1.DPAD.down) b1 |= 0x8;
          if (PLAYER_1.A) b1 |= 0x10;
          if (PLAYER_1.B) b1 |= 0x20;
          pico8_buttons[0] = b1;

          // Player 2
          let b2 = 0;
          if (PLAYER_2.DPAD.left) b2 |= 0x1;
          if (PLAYER_2.DPAD.right) b2 |= 0x2;
          if (PLAYER_2.DPAD.up) b2 |= 0x4;
          if (PLAYER_2.DPAD.down) b2 |= 0x8;
          if (PLAYER_2.A) b2 |= 0x10;
          if (PLAYER_2.B) b2 |= 0x20;
          pico8_buttons[1] = b2;
        }
        requestAnimationFrame(updateRcade);
      }
      updateRcade();
    </script>
  </body>
</html>