Remove stale play standalone file: README and INSTALL point to the title-screen download

This commit is contained in:
Richard Zink committed 2026-11-04 20:39:10 +01:00
1 parent 5928acf7f6
commit f164293d21
4 files changed
+8 -268

No files matched your search

+1
View File
@@ -3,3 +3,4 @@ Cargo.lock.bak
# WASM build outputs copied into www/ for the standalone file (the Docker image builds its own) # WASM build outputs copied into www/ for the standalone file (the Docker image builds its own)
www/dungeoncraft.wasm www/dungeoncraft.wasm
www/mq_js_bundle.js www/mq_js_bundle.js
play/
+3 -3
View File
@@ -2,8 +2,9 @@
> *Technical matters, master. Crookfinger does not understand half of it, but the imps say it works.* > *Technical matters, master. Crookfinger does not understand half of it, but the imps say it works.*
**Just want to play?** Download [`play/DungeonCraft.html`](play/DungeonCraft.html) and open it in a browser - **Just want to play?** Open <https://dungeon.familie-zink.org/> in a browser. On the title screen, logged-in
no server, no installation, no login. Savegames are downloaded as `.dcsave` files and loaded again with players can use *Download Standalone* to save `DungeonCraft.html`: the whole game in one file, without login and
without server. Savegames from the standalone version are downloaded as `.dcsave` files and loaded again with
*Load Game* (or dropped onto the game window). *Load Game* (or dropped onto the game window).
Everything below is for running your own server (with user accounts and savegames on the server), Everything below is for running your own server (with user accounts and savegames on the server),
@@ -136,7 +137,6 @@ number by one (a new month starts again at `00001`) and prints the old and the n
| File | How | | File | How |
|---|---| |---|---|
| `play/DungeonCraft.html` | build the WASM file, then `python3 tools/make_standalone.py <www dir with dungeoncraft.wasm and mq_js_bundle.js> play/DungeonCraft.html` (the Docker image builds the same file under `/standalone/`) |
| `docs/img/units`, `docs/img/icons` | `cargo test --release export_doc_images -- --ignored` | | `docs/img/units`, `docs/img/icons` | `cargo test --release export_doc_images -- --ignored` |
| `BESTIARY.md` | `python3 tools/make_bestiary.py` | | `BESTIARY.md` | `python3 tools/make_bestiary.py` |
| `VERSION` | `python3 tools/bump_version.py` - with every change to the game | | `VERSION` | `python3 tools/bump_version.py` - with every change to the game |
+4 -3
View File
@@ -16,7 +16,7 @@ An evil little dungeon game for the browser - in glorious pixels, in 3D, for fre
</p> </p>
<p align="center"> <p align="center">
<a href="play/DungeonCraft.html"><b>▶ Play now - download one HTML file, open it, done</b></a><br> <a href="https://dungeon.familie-zink.org/"><b>▶ Play now - open it in your browser, or download the standalone file on the title screen</b></a><br>
<a href="BESTIARY.md">Bestiary</a> · <a href="INSTALL.md">Run your own server</a> · <a href="DESIGN.md">Design notes</a> <a href="BESTIARY.md">Bestiary</a> · <a href="INSTALL.md">Run your own server</a> · <a href="DESIGN.md">Design notes</a>
</p> </p>
@@ -122,8 +122,9 @@ caves, rival masters, and me, telling you the story. With tea.
## How to play ## How to play
1. **Download [`play/DungeonCraft.html`](play/DungeonCraft.html)** and open it in your browser. That's all. 1. **Open the game online**: <https://dungeon.familie-zink.org/>. It runs on desktop and on phones (touch controls switch on by themselves).
It runs offline, on desktop and on phones (touch controls switch on by themselves). Logged-in players can download the whole game as one HTML file from the title screen (*Download Standalone*);
it then runs offline, and downloaded savegame files load there too.
2. Or **run your own server** with Docker - with user accounts, savegames on the server and the game as an 2. Or **run your own server** with Docker - with user accounts, savegames on the server and the game as an
installable app: see **[INSTALL.md](INSTALL.md)**. installable app: see **[INSTALL.md](INSTALL.md)**.
-262
View File
@@ -1,262 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DungeonCraft</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#080608">
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAF1UlEQVR4nO3dy26bVRSGYduF1KQJtEnkqkiVIiHBhBEj1DtgxhX0KhhwCQy4il5MhYTEiBGjSkVCdg9JnANNwTYTBgzWi9glp+3vfYar7p+/2/qy1aV9GI43xquBFGp03S8gXScDoGgGQNEMgKIZAEV7j/5g9vTxVb6HdKkmj56UdWcARTMAimYAFM0AKJoBUDTsAhH637R0E7R2L50BFM0AKJoBUDQDoGgGQNGau0DEtUO6ShfVjXQGUDQDoGgGQNEMgKIZAEW7sC4Qce2Q/o/L7i46AyiaAVA0A6BoBkDRDICiGQBFMwCKZgAUzQAomgFQNAOgaJe+FmhdbUD9zrD+nUKfJ2+hfrpaNn1e/84ZQNEMgKIZAEUzAIpmABTNLtDftobDsj4Z3irrO6P6dwc95/agrpPzwaqsn6zq+utl3R2arRZNz0njDKBoBkDRDICiGQBFMwCKFtcFug9rdR7eqofiwajuAu1BF+hDeP74P7zbP72B+hzWAr2ELtDOsn6f54s/y/oUnr+unAEUzQAomgFQNAOgaAZA0da2C0Tdnk+g27MP9YfQBTr7aLes34b3qZ/C6DlbUJ8cvSrr27A2CXeohXWHnAEUzQAomgFQNAOgaAZA0brvAtEOLFrbQ92ezXuTsk77pu5D/Q7U34c6+QPqp1CfQ1dqEz6/fzAr63ge0aJ+o953ljkDKJoBUDQDoGgGQNEMgKJ13wWic3toJxet7aFeRt1bGQzuQf2r778t6z9+8x38jdo51I+h3tpl2oVxOG48d+hkVa8d6oUzgKIZAEUzAIpmABTNAChaN10g2sFEpzTTuT20k4vW9lC352vo9mzD5z+GOjmDOu0UI7SmaArjsHf4oqzTOP8KG8V6ubPMGUDRDICiGQBFMwCKZgAUrZsuEN3ATjvC6JRm6qLQTi5a20PdHvKg8fPzxs/T2iHaQfYB1EeN40zfy9tOzhFyBlA0A6BoBkDRDICiGQBF66YLRGuB6AZ2upOLTmmmHVW0k4vW9lC3pz51qB11dai7Rf8u+uLpPnsaZzxluhPOAIpmABTNACiaAVA0A6BoBkDRDICiGQBFMwCKZgAUzQAoWjdrgeicmXM41/kNfJ7WzND5ObTTis7tad3JReg59HPpPenfRWc603NonHs5/4c4AyiaAVA0A6BoBkDRDICiddMFOoVzZuim8jl8foueD3W6k6v1lGZ6PqFuzyHU6T3p5/4O9ZPGcabvpRfOAIpmABTNACiaAVA0A6Bo3XSBaM0J3WD+EuqTo1dlfQ53ZrXewE5raVq7Rq03xR9AndYUbcI4PINxo3F2LZDUMQOgaAZA0QyAohkAReumC0Rmq0VZ31nW2d6Gu642G38u7bSitTet3aTW51O35zXUz5b1uP0GdRrn3jkDKJoBUDQDoGgGQNEMgKJ13wWinUrPF/XJN3Sn1f7BrKzvjupbxaawdohuYG8daDq3h3Zy0doe6vY8g/GhcaNx7p0zgKIZAEUzAIpmABTNACha910gMqXzaqDLQTubjqH7sXf4oqyPhvXvFLqBndCOMDq3h3Zy0doe6vbguK0pZwBFMwCKZgAUzQAomgFQtLXtAhHqcny2dbesH8FzqH7T0E636WG99imNM4CiGQBFMwCKZgAUzQAoWlwX6Mu7k7Le+ynHrWgcfgjrDjkDKJoBUDQDoGgGQNEMgKKtbRfoC7s974TG7ac17Q45AyiaAVA0A6BoBkDRDICidd8F+txuz5Wgcf658+6QM4CiGQBFMwCKZgAUzQAoWjddoE/t9txI9L380kl3yBlA0QyAohkARTMAimYAFK2bLlAvXQX1xRlA0QyAohkARTMAimYAFO3Su0Czp48v+0dI78wZQNEMgKIZAEUzAIpmABTtwrpAk0dPLupR0pVxBlA0A6BoBkDRDICiGQBFa+4CubZH68QZQNEMgKIZAEUzAIpmABRtON4Yr677JaTr4gygaAZA0QyAohkARTMAivYXEawTv/k1XA4AAAAASUVORK5CYII=">
<style>
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: #080608; }
canvas { display: block; width: 100%; height: 100%; outline: none; image-rendering: pixelated; }
</style>
</head>
<body>
<canvas id="glcanvas" tabindex="1" oncontextmenu="return false"></canvas>
<!-- DungeonCraft standalone - MIT License - (c) 2026 Richard Zink - https://git.familie-zink.org/zink/dungeoncraft - made with the help of Claude Code (https://claude.com/claude-code) -->
<script>var register_plugin; /* expected by the quad_net plugin in the bundle */</script>
<script>
// the game module is embedded: fetching it is answered from these bytes
var DC_WASM = "AGFzbQEAAAAB0wZmYAF/AGAAAGADf39/AX9gA39/fwBgBn9/f39/fwBgBX9/f39/AGAEf39/fwBgAn9/AGAHf39/f39/fwF/YAJ/fwF/YAF/AX9gBH9/f38Bf2AFf39/f38Bf2ADf319AGAFf319fX0AYAd/fX19fX19AGAGf39/fX9/AGAGf39/f39/AX9gAAF8YAABf2AAAX1gCH9/f39/f39/AGAJf39/f39/f39/AGAHf39/f39/fwBgBH19fX0AYAF9AGAKf39/f39/f39/fwBgBH5+f38BfmADfn5/AX5gAX4Bf2AEf39/fQF/YAJ+fwF/YAJ/fQBgAn1/AX9gBn99fXx/fwBgB39/f39/f3wAYAV/f39/fABgC39/f39/f399fX98AGAGf39/f319AGAEf399fQBgBX9/fX1/AGAHf39/fX1/fABgB39/f39/fX0AYAN/f30AYAV/f398fwBgDX9/fX19f39/f39/f38AYAV9fX19fwBgBn19fX19fwBgBX99fX9/AGAHf39/fX19fwBgBn9/f398fwBgCH9/f39/f398AGAJf319f399f39/AGAMf319f399f39/f39/AGABfwF9YAJ/fgBgB399fX99f30AYAJ9fQF/YAZ/f399fX8Bf2AJf39/f319fX99AGADf399AX1gB39/fX19f38AYAJ9fQF9YAh/f39/fX19fQBgCX9/fX19f39/fwBgAn98AGAFf31/f38AYAh/f319fX19fwBgBH19f30AYAR/f31/AGADf31/AGAEf31/fwBgCn9/f39/f39/fX0AYAZ/f319fX0Bf2ADf399AX9gAn9/AX1gCH9/fX9/f39/AGAFf399fX8Bf2AEf39/fQBgBH9/f34AYAV/f39+fwBgB399fX19f38AYAR/f319AX1gE39/f39/f39/f39/f39/f39/f38Bf2ABfwF+YAZ/fn9/f38AYAl/f39/f39+fn4AYAR/fX9/AX9gA399fwF/YAN/fn4AYAt/f39/f39/f39/fwF/YAABfmACf34Bf2ADfn5+AX5gCH9/f39/f39/AX9gBX9/fn19AGADf35/AGALf39/f39/f39/f38AYAJ8fwF/YAF9AX1gBX9+fn5+AGACfX8BfQLXFHoDZW52C2NvbnNvbGVfbG9nAAADZW52B2RjX2h0dHAAEQNlbnYMZGNfc2V0X3Rva2VuAAcDZW52A25vdwASA2VudhFkY19jb2Fyc2VfcG9pbnRlcgATA2VudgxkY19maWxlX3BvbGwAEwNlbnYMZGNfZmlsZV90YWtlAAADZW52DGRjX2h0dHBfcG9sbAAKA2VudgtkY19odHRwX2xlbgAKA2VudgxkY19odHRwX3Rha2UABwNlbnYOZGNfc3RvcmFnZV9zZXQABgNlbnYXZGNfZmV0Y2hfZG93bmxvYWRfc3RhdGUAEwNlbnYLZGNfZG93bmxvYWQABgNlbnYMZGNfb3Blbl9maWxlAAEDZW52C2RjX29wZW5fdXJsAAcDZW52DGRjX2NhY2hlX3NldAAGA2VudhFkY19mZXRjaF9kb3dubG9hZAAGA2Vudgppbml0X3dlYmdsAAADZW52EXNldHVwX2NhbnZhc19zaXplAAADZW52DGNhbnZhc193aWR0aAATA2Vudg1jYW52YXNfaGVpZ2h0ABMDZW52CWRwaV9zY2FsZQAUA2VudhJydW5fYW5pbWF0aW9uX2xvb3AAAANlbnYOZGNfc3RvcmFnZV9nZXQACQNlbnYPZGNfc3RvcmFnZV90YWtlAAADZW52CWRjX3Byb21wdAALA2Vudg1nbEJpbmRUZXh0dXJlAAcDZW52EGdsQ29weVRleEltYWdlMkQAFQNlbnYNZ2xHZXRJbnRlZ2VydgAHA2VudhFnbEdlblZlcnRleEFycmF5cwAHA2VudhFnbEJpbmRWZXJ0ZXhBcnJheQAAA2VudgtnbEdldFN0cmluZwAKA2VudhRzYXBwX3NjaGVkdWxlX3VwZGF0ZQABA2VudgxnbEdlblF1ZXJpZXMABwNlbnYMZ2xCZWdpblF1ZXJ5AAcDZW52CmdsRW5kUXVlcnkAAANlbnYHZ2xGbHVzaAABA2VudghnbEZpbmlzaAABA2Vudgxjb25zb2xlX3dhcm4AAANlbnYPZ2xDcmVhdGVQcm9ncmFtABMDZW52DmdsQXR0YWNoU2hhZGVyAAcDZW52DWdsTGlua1Byb2dyYW0AAANlbnYOZ2xEZXRhY2hTaGFkZXIABwNlbnYOZ2xEZWxldGVTaGFkZXIAAANlbnYOZ2xHZXRQcm9ncmFtaXYAAwNlbnYTZ2xHZXRQcm9ncmFtSW5mb0xvZwAGA2VudgxnbFVzZVByb2dyYW0AAANlbnYUZ2xHZXRVbmlmb3JtTG9jYXRpb24ACQNlbnYNZ2xHZW5UZXh0dXJlcwAHA2Vudg9nbEFjdGl2ZVRleHR1cmUAAANlbnYNZ2xQaXhlbFN0b3JlaQAHA2VudhJnbEdlblJlbmRlcmJ1ZmZlcnMABwNlbnYSZ2xCaW5kUmVuZGVyYnVmZmVyAAcDZW52IGdsUmVuZGVyYnVmZmVyU3RvcmFnZU11bHRpc2FtcGxlAAUDZW52DGdsVGV4SW1hZ2UyRAAWA2Vudg9nbFRleFBhcmFtZXRlcmkAAwNlbnYQZ2xHZW5lcmF0ZU1pcG1hcAAAA2VudhFnbEdlbkZyYW1lYnVmZmVycwAHA2VudhFnbEJpbmRGcmFtZWJ1ZmZlcgAHA2VudhZnbEZyYW1lYnVmZmVyVGV4dHVyZTJEAAUDZW52DGdsUmVhZFBpeGVscwAXA2VudhRnbERlbGV0ZUZyYW1lYnVmZmVycwAHA2Vudg9nbFRleFN1YkltYWdlMkQAFgNlbnYZZ2xGcmFtZWJ1ZmZlclJlbmRlcmJ1ZmZlcgAGA2Vudg1nbERyYXdCdWZmZXJzAAcDZW52GGdsQ2hlY2tGcmFtZWJ1ZmZlclN0YXR1cwAKA2VudhVnbERlbGV0ZVJlbmRlcmJ1ZmZlcnMABwNlbnYQZ2xEZWxldGVUZXh0dXJlcwAHA2VudhNnbEdldEF0dHJpYkxvY2F0aW9uAAkDZW52CGdsRW5hYmxlAAADZW52CWdsRGlzYWJsZQAAA2VudgtnbERlcHRoRnVuYwAAA2VudgtnbEZyb250RmFjZQAAA2VudgpnbEN1bGxGYWNlAAADZW52E2dsQmxlbmRGdW5jU2VwYXJhdGUABgNlbnYXZ2xCbGVuZEVxdWF0aW9uU2VwYXJhdGUABwNlbnYLZ2xCbGVuZEZ1bmMABwNlbnYTZ2xTdGVuY2lsT3BTZXBhcmF0ZQAGA2VudhVnbFN0ZW5jaWxGdW5jU2VwYXJhdGUABgNlbnYVZ2xTdGVuY2lsTWFza1NlcGFyYXRlAAcDZW52C2dsQ29sb3JNYXNrAAYDZW52DGdsR2VuQnVmZmVycwAHA2VudgxnbEJpbmRCdWZmZXIABwNlbnYMZ2xCdWZmZXJEYXRhAAYDZW52D2dsQnVmZmVyU3ViRGF0YQAGA2Vudg9nbERlbGV0ZUJ1ZmZlcnMABwNlbnYaZ2xEaXNhYmxlVmVydGV4QXR0cmliQXJyYXkAAANlbnYPZ2xEZWxldGVQcm9ncmFtAAADZW52CmdsVmlld3BvcnQABgNlbnYJZ2xTY2lzc29yAAYDZW52C2dsVW5pZm9ybTFpAAcDZW52FmdsVmVydGV4QXR0cmliSVBvaW50ZXIABQNlbnYVZ2xWZXJ0ZXhBdHRyaWJQb2ludGVyAAQDZW52FWdsVmVydGV4QXR0cmliRGl2aXNvcgAHA2VudhlnbEVuYWJsZVZlcnRleEF0dHJpYkFycmF5AAADZW52DGdsVW5pZm9ybTFmdgADA2VudgxnbFVuaWZvcm0yZnYAAwNlbnYMZ2xVbmlmb3JtM2Z2AAMDZW52DGdsVW5pZm9ybTRmdgADA2VudgxnbFVuaWZvcm0xaXYAAwNlbnYMZ2xVbmlmb3JtMml2AAMDZW52DGdsVW5pZm9ybTNpdgADA2VudgxnbFVuaWZvcm00aXYAAwNlbnYSZ2xVbmlmb3JtTWF0cml4NGZ2AAYDZW52DGdsQ2xlYXJDb2xvcgAYA2Vudg1nbENsZWFyRGVwdGhmABkDZW52DmdsQ2xlYXJTdGVuY2lsAAADZW52B2dsQ2xlYXIAAANlbnYMZ2xSZWFkQnVmZmVyAAADZW52EWdsQmxpdEZyYW1lYnVmZmVyABoDZW52F2dsRHJhd0VsZW1lbnRzSW5zdGFuY2VkAAUDZW52DmdsQ3JlYXRlU2hhZGVyAAoDZW52DmdsU2hhZGVyU291cmNlAAYDZW52D2dsQ29tcGlsZVNoYWRlcgAAA2Vudg1nbEdldFNoYWRlcml2AAMDZW52EmdsR2V0U2hhZGVySW5mb0xvZwAGA2VudhJzYXBwX3NldF9jbGlwYm9hcmQABwNlbnYSZnNfZ2V0X2J1ZmZlcl9zaXplAAoDZW52DmZzX3Rha2VfYnVmZmVyAAMDZW52FHNhcHBfc2V0X2N1cnNvcl9ncmFiAAADZW52D3NhcHBfc2V0X2N1cnNvcgAHA2VudhNzYXBwX3NldF9mdWxsc2NyZWVuAAADrgqsCgMDCQYCAAMJAwEJAwcJAwACAxcKAAcABwAWFxUVFQIWBRUGFQYJBwMbCRwJHAkBAAEAAQEBBgUAAAAAAAAAAAAAAAAAAwAAAAAABwAAAAAAAAAAAAAAAAAFAwcHAgMCBwcCBwcHCQkACQMCAAMJHQcHAAMEBwUHBQcFBwUDBAMEAwQHBgwLCQsLCwsMDAMDAwMBAwMGBgYDBwcHAwMXBAQEBBcXFwYHBAMHBwcHCQkLAgwMAgcLCx4LCx8LCwwHIAkMAgIJAgICAgwCAgkCBwIDAwoKCQwJAwMLLine truncated
(function () {
var realFetch = window.fetch ? window.fetch.bind(window) : null;
window.fetch = function (url, opts) {
if (typeof url === "string" && url.indexOf("dungeoncraft.wasm") >= 0) {
var bin = atob(DC_WASM);
var bytes = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
return Promise.resolve(new Response(bytes, { headers: { "Content-Type": "application/wasm" } }));
}
return realFetch ? realFetch(url, opts) : Promise.reject(new Error("offline"));
};
})();
</script>
<script>"use strict";const version=2,canvas=document.querySelector("#glcanvas");var gl,wasm_memory,animation_frame_timeout,FS,GL,Module,wasm_exports,emscripten_shaders_hack,importObject,clipboard=null,plugins=[],high_dpi=!1,blocking_event_loop=!1;function init_webgl(e){if(e==1){gl=canvas.getContext("webgl");function t(e){var t=e.getExtension("OES_vertex_array_object");t?(e.createVertexArray=function(){return t.createVertexArrayOES()},e.deleteVertexArray=function(e){t.deleteVertexArrayOES(e)},e.bindVertexArray=function(e){t.bindVertexArrayOES(e)},e.isVertexArray=function(e){return t.isVertexArrayOES(e)}):alert("Unable to get OES_vertex_array_object extension")}function n(e){var t=e.getExtension("ANGLE_instanced_arrays");t&&(e.vertexAttribDivisor=function(e,n){t.vertexAttribDivisorANGLE(e,n)},e.drawArraysInstanced=function(e,n,s,o){t.drawArraysInstancedANGLE(e,n,s,o)},e.drawElementsInstanced=function(e,n,s,o,i){t.drawElementsInstancedANGLE(e,n,s,o,i)})}function s(e){var t=e.getExtension("EXT_disjoint_timer_query");t&&(e.createQuery=function(){return t.createQueryEXT()},e.beginQuery=function(e,n){return t.beginQueryEXT(e,n)},e.endQuery=function(e){return t.endQueryEXT(e)},e.deleteQuery=function(e){t.deleteQueryEXT(e)},e.getQueryObject=function(e,n){return t.getQueryObjectEXT(e,n)})}function o(e){var t=e.getExtension("WEBGL_draw_buffers");t&&(e.drawBuffers=function(e){return t.drawBuffersWEBGL(e)})}try{gl.getExtension("EXT_shader_texture_lod"),gl.getExtension("OES_standard_derivatives")}catch(e){console.warn(e)}t(gl),n(gl),s(gl),o(gl),gl.getExtension("WEBGL_depth_texture")==null&&alert("Cant initialize WEBGL_depth_texture extension")}else gl=canvas.getContext("webgl2");gl===null&&alert("Unable to initialize WebGL. Your browser or machine may not support it.")}canvas.focus(),canvas.requestPointerLock=canvas.requestPointerLock||canvas.mozRequestPointerLock||function(){},document.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||function(){};function assert(e,t){e==!1&&alert(t)}function getArray(e,t,n){return new t(wasm_memory.buffer,e,n)}function UTF8ToString(e,t){let i=new Uint8Array(wasm_memory.buffer,e);for(var n,a,r,c,s=0,l=s+t,o="";!(s>=l);){if(n=i[s++],!n)return o;if(!(n&128)){o+=String.fromCharCode(n);continue}if(a=i[s++]&63,(n&224)==192){o+=String.fromCharCode((n&31)<<6|a);continue}r=i[s++]&63,(n&240)==224?n=(n&15)<<12|a<<6|r:((n&248)!=240&&console.warn("Invalid UTF-8 leading byte 0x"+n.toString(16)+" encountered when deserializing a UTF-8 string on the asm.js/wasm heap to a JS string!"),n=(n&7)<<18|a<<12|r<<6|i[s++]&63),n<65536?o+=String.fromCharCode(n):(c=n-65536,o+=String.fromCharCode(55296|c>>10,56320|c&1023))}return o}function stringToUTF8(e,t,n,s){for(var o,r,c=n,i=n+s,a=0;a<e.length;++a)if(o=e.charCodeAt(a),o>=55296&&o<=57343&&(r=e.charCodeAt(++a),o=65536+((o&1023)<<10)|r&1023),o<=127){if(n>=i)break;t[n++]=o}else if(o<=2047){if(n+1>=i)break;t[n++]=192|o>>6,t[n++]=128|o&63}else if(o<=65535){if(n+2>=i)break;t[n++]=224|o>>12,t[n++]=128|o>>6&63,t[n++]=128|o&63}else{if(n+3>=i)break;o>=2097152&&console.warn("Invalid Unicode code point 0x"+o.toString(16)+" encountered when serializing a JS string to an UTF-8 string on the asm.js/wasm heap! (Valid unicode code points should be in range 0-0x1FFFFF)."),t[n++]=240|o>>18,t[n++]=128|o>>12&63,t[n++]=128|o>>6&63,t[n++]=128|o&63}return n-c}FS={loaded_files:[],unique_id:0},GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],timerQueries:[],contexts:{},programInfos:{},getNewId:function(e){for(var n=GL.counter++,t=e.length;t<n;t++)e[t]=null;return n},validateGLObjectID:function(e,t,n,s){t!=0&&(e[t]===null?console.error(n+" called with an already deleted "+s+" ID "+t+"!"):e[t]||console.error(n+" called with an invalid "+s+" ID "+t+"!"))},getSource:function(e,t,n,s){for(var a,i="",o=0;o<t;++o)a=s==0?0[0]:getArray(s+o*4,Uint32Array,1)[0],i+=UTF8ToString(getArray(n+o*4,Uint32Array,1)[0],a);return i},populateUniformTable:function(e){GL.validateGLObjectID(GL.programs,e,"populateUniformTable","program");for(var t,n,s,i,a,l,o=GL.programs[e],r=GL.programInfos[e]={uniforms:{},maxUniformLength:0,maxAttributeLength:-1,maxUniformBlockNameLength:-1},d=r.uniforms,u=gl.getProgramParameter(o,35718),c=0;c<u;++c)if(i=gl.getActiveUniform(o,c),t=i.name,r.maxUniformLength=Math.max(r.maxUniformLength,t.length+1),t.slice(-1)=="]"&&(t=t.slice(0,t.lastIndexOf("["))),n=gl.getUniformLocation(o,t),n){s=GL.getNewId(GL.uniforms),d[t]=[i.size,s],GL.uniforms[s]=n;for(a=1;a<i.size;++a)l=t+"["+a+"]",n=gl.getUniformLocation(o,l),s=GL.getNewId(GL.uniforms),GL.uniforms[s]=n}}};function _glGenObject(e,t,n,s,o){for(var i,a,r=0;r<e;r++)i=gl[n](),a=i&&GL.getNewId(s),i?(i.name=a,s[a]=i):(console.error("GL_INVALID_OPERATION"),GL.recordError(1282),alert("GL_INVALID_OPERATION in "+o+": GLctx."+n+" returned null - most likely GL context is lost!")),getArray(t+r*4,Int32Array,1)[0]=a}function _webglGet(e,t,n){if(!tLine truncated
`,o=o.replace(/gl_FragColor/g,"GL_FragColor")),o.indexOf("attribute")!=-1?(o=o.replace(/attribute/g,"in"),o=o.replace(/varying/g,"out")):o=o.replace(/varying/g,"in"),o=o.replace(/textureCubeLodEXT/g,"textureCubeLod"),o=o.replace(/texture2DLodEXT/g,"texture2DLod"),o=o.replace(/texture2DProjLodEXT/g,"texture2DProjLod"),o=o.replace(/texture2DGradEXT/g,"texture2DGrad"),o=o.replace(/texture2DProjGradEXT/g,"texture2DProjGrad"),o=o.replace(/textureCubeGradEXT/g,"textureCubeGrad"),o=o.replace(/textureCube/g,"texture"),o=o.replace(/texture1D/g,"texture"),o=o.replace(/texture2D/g,"texture"),o=o.replace(/texture3D/g,"texture"),o=o.replace(/#version 100/g,`#version 300 es
`+i)),gl.shaderSource(GL.shaders[e],o)},glGetProgramInfoLog:function(e,t,n,s){GL.validateGLObjectID(GL.programs,e,"glGetProgramInfoLog","program");var o,i=gl.getProgramInfoLog(GL.programs[e]);assert(i!==null);let a=getArray(s,Uint8Array,t);for(o=0;o<t;o++)a[o]=i.charCodeAt(o)},glGetString:function(e){var t=gl.getParameter(e).toString(),n=t.length+1,s=wasm_exports.allocate_vec_u8(n),o=new Uint8Array(wasm_memory.buffer,s,n);return o[t.length]=0,stringToUTF8(t,o,0,n),s},glCompileShader:function(e){GL.validateGLObjectID(GL.shaders,e,"glCompileShader","shader"),gl.compileShader(GL.shaders[e])},glGetShaderiv:function(e,t,n){if(assert(n),GL.validateGLObjectID(GL.shaders,e,"glGetShaderiv","shader"),t==35716){var s,i,o=gl.getShaderInfoLog(GL.shaders[e]);assert(o!==null),getArray(n,Int32Array,1)[0]=o.length+1}else t==35720?(s=gl.getShaderSource(GL.shaders[e]),i=s===null||s.length==0?0:s.length+1,getArray(n,Int32Array,1)[0]=i):getArray(n,Int32Array,1)[0]=gl.getShaderParameter(GL.shaders[e],t)},glGetShaderInfoLog:function(e,t,n,s){GL.validateGLObjectID(GL.shaders,e,"glGetShaderInfoLog","shader");var o,i=gl.getShaderInfoLog(GL.shaders[e]);assert(i!==null);let a=getArray(s,Uint8Array,t);for(o=0;o<t;o++)a[o]=i.charCodeAt(o)},glVertexAttribDivisor:function(e,t){gl.vertexAttribDivisor(e,t)},glDrawArraysInstanced:function(e,t,n,s){gl.drawArraysInstanced(e,t,n,s)},glDrawElementsInstanced:function(e,t,n,s,o){gl.drawElementsInstanced(e,t,n,s,o)},glDeleteShader:function(e){var t=GL.shaders[e];if(t==null)return;gl.deleteShader(t),GL.shaders[e]=null},glDeleteProgram:function(e){var t=GL.programs[e];if(t==null)return;gl.deleteProgram(t),GL.programs[e]=null},glDeleteBuffers:function(e,t){for(var n,o,s=0;s<e;s++){if(o=getArray(t+s*4,Uint32Array,1)[0],n=GL.buffers[o],!n)continue;gl.deleteBuffer(n),n.name=0,GL.buffers[o]=null}},glDeleteFramebuffers:function(e,t){for(var n,o,s=0;s<e;s++){if(o=getArray(t+s*4,Uint32Array,1)[0],n=GL.framebuffers[o],!n)continue;gl.deleteFramebuffer(n),n.name=0,GL.framebuffers[o]=null}},glDeleteRenderbuffers:function(e,t){for(var n,o,s=0;s<e;s++){if(o=getArray(t+s*4,Uint32Array,1)[0],n=GL.renderbuffers[o],!n)continue;gl.deleteRenderbuffer(n),n.name=0,GL.renderbuffers[o]=null}},glDeleteTextures:function(e,t){for(var n,o,s=0;s<e;s++){if(o=getArray(t+s*4,Uint32Array,1)[0],n=GL.textures[o],!n)continue;gl.deleteTexture(n),n.name=0,GL.textures[o]=null}},glGenQueries:function(e,t){_glGenObject(e,t,"createQuery",GL.timerQueries,"glGenQueries")},glDeleteQueries:function(e){for(var n,o,s=0;s<e;s++){if(o=getArray(textures+s*4,Uint32Array,1)[0],n=GL.timerQueries[o],!n)continue;gl.deleteQuery(n),n.name=0,GL.timerQueries[o]=null}},glBeginQuery:function(e,t){GL.validateGLObjectID(GL.timerQueries,t,"glBeginQuery","id"),gl.beginQuery(e,GL.timerQueries[t])},glEndQuery:function(e){gl.endQuery(e)},glGetQueryObjectiv:function(e,t,n){GL.validateGLObjectID(GL.timerQueries,e,"glGetQueryObjectiv","id");let s=gl.getQueryObject(GL.timerQueries[e],t);getArray(n,Uint32Array,1)[0]=s},glGetQueryObjectui64v:function(e,t,n){GL.validateGLObjectID(GL.timerQueries,e,"glGetQueryObjectui64v","id");let o=gl.getQueryObject(GL.timerQueries[e],t),s=getArray(n,Uint32Array,2);s[0]=o,s[1]=(o-s[0])/4294967296},glGenerateMipmap:function(e){gl.generateMipmap(e)},glRenderbufferStorageMultisample:function(e,t,n,s,o){gl.renderbufferStorageMultisample(e,t,n,s,o)},glFramebufferRenderbuffer:function(e,t,n,s){GL.validateGLObjectID(GL.renderbuffers,s,"glFramebufferRenderbuffer","renderbuffer"),gl.framebufferRenderbuffer(e,t,n,GL.renderbuffers[s])},glCheckFramebufferStatus:function(e){return gl.checkFramebufferStatus(e)},glReadBuffer:function(e){gl.readBuffer(e)},glBlitFramebuffer:function(e,t,n,s,o,i,a,r,c,l){gl.blitFramebuffer(e,t,n,s,o,i,a,r,c,l)},setup_canvas_size:function(e){window.high_dpi=e,resize(canvas)},run_animation_loop:function(e){canvas.onmousemove=function(e){var t=mouse_relative_position(e.clientX,e.clientY),n=t.x,s=t.y;wasm_exports.mouse_move(Math.floor(n),Math.floor(s)),(e.movementX!=0||e.movementY!=0)&&wasm_exports.raw_mouse_move(Math.floor(e.movementX),Math.floor(e.movementY))},canvas.onmousedown=function(e){var t=mouse_relative_position(e.clientX,e.clientY),n=t.x,s=t.y,o=into_sapp_mousebutton(e.button);wasm_exports.mouse_down(n,s,o)},canvas.addEventListener("wheel",function(e){e.preventDefault(),wasm_exports.mouse_wheel(-e.deltaX,-e.deltaY)}),canvas.onmouseup=function(e){var t=mouse_relative_position(e.clientX,e.clientY),n=t.x,s=t.y,o=into_sapp_mousebutton(e.button);wasm_exports.mouse_up(n,s,o)},canvas.onkeydown=function(e){var n,t=into_sapp_keycode(e.code);switch(t){case 32:case 262:case 263:case 264:case 265:case 290:case 291:case 292:case 293:case 294:case 295:case 296:case 297:case 298:case 299:case 259:case 258:case 39:case 47:e.preventDefault();break}n=0,e.ctrlKey&&(n|=SAPP_MODIFIER_CTRL),e.shiftKey&&(n|=SAPP_MODIFIER_SHIFT),e.altKey&&(n|=SAPP_MODIFIER_ALT),wasm_exports.key_down(t,n,e.repeat),(t==32||t==39||t==47)&&wasm_exports.key_presLine truncated
<script>// DungeonCraft browser plugin for miniquad: storage, file download/upload, save-on-leave,
// HTTP requests (savegame service), audio output.
(function () {
"use strict";
var pending = null; // value handed over by dc_storage_get
var file = null; // bytes of a file chosen via the file picker
var cache = {}; // key -> string, persisted to localStorage when the page is hidden/closed
var http = {}; // request id -> { done, status, body }
var httpNext = 1;
var token = ""; // login session of the savegame service (sent with every request)
var fetched = { state: 0 }; // download of a file from the server: 0 idle, 1 running, 2 done, 3 failed
function bytes(ptr, len) {
return new Uint8Array(wasm_memory.buffer, ptr, len);
}
function str(ptr, len) {
return new TextDecoder().decode(bytes(ptr, len));
}
function flushCache() {
for (var k in cache) {
try { window.localStorage.setItem(k, cache[k]); } catch (e) { }
}
}
window.addEventListener("pagehide", flushCache);
document.addEventListener("visibilitychange", function () {
if (document.visibilityState === "hidden") flushCache();
});
// hidden <input type=file> for loading savegames
var input = document.createElement("input");
input.type = "file";
input.accept = ".dcsave,.json,application/json";
input.style.display = "none";
input.addEventListener("change", function () {
var f = input.files && input.files[0];
if (!f) return;
f.arrayBuffer().then(function (buf) { file = new Uint8Array(buf); });
input.value = "";
});
document.addEventListener("DOMContentLoaded", function () { document.body.appendChild(input); });
// Asynchronous HTTP request; the result is polled by the game.
// status 0 = network error (e.g. no service, or the page was opened as a local file)
function httpStart(method, url, body) {
var id = httpNext++;
var req = { done: false, status: 0, body: new Uint8Array(0) };
http[id] = req;
var ctrl = typeof AbortController !== "undefined" ? new AbortController() : null;
var timer = setTimeout(function () { if (ctrl) ctrl.abort(); }, 15000);
try {
var opts = { method: method, cache: "no-store", headers: {} };
if (token) opts.headers["Authorization"] = "Bearer " + token;
if (ctrl) opts.signal = ctrl.signal;
if (body !== null) { opts.body = body; opts.headers["Content-Type"] = "application/json"; }
fetch(url, opts).then(function (r) {
req.status = r.status;
return r.arrayBuffer();
}).then(function (buf) {
req.body = new Uint8Array(buf);
req.done = true;
clearTimeout(timer);
}).catch(function () {
req.status = 0;
req.done = true;
clearTimeout(timer);
});
} catch (e) {
req.done = true;
}
return id;
}
function register(importObject) {
var env = importObject.env;
env.dc_set_token = function (tp, tl) {
token = tl > 0 ? str(tp, tl) : "";
};
// fetches a file (with the login) and offers it as a download, without copying it into the game
env.dc_fetch_download = function (up, ul, np, nl) {
var url = str(up, ul), name = str(np, nl);
fetched.state = 1;
var opts = { cache: "no-store", headers: {} };
if (token) opts.headers["Authorization"] = "Bearer " + token;
fetch(url, opts).then(function (r) {
if (!r.ok) throw new Error("status " + r.status);
return r.blob();
}).then(function (blob) {
var a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = name;
document.body.appendChild(a);
a.click();
setTimeout(function () { URL.revokeObjectURL(a.href); a.remove(); }, 2000);
fetched.state = 2;
}).catch(function () { fetched.state = 3; });
};
env.dc_fetch_download_state = function () {
var s = fetched.state;
if (s >= 2) fetched.state = 0;
return s;
};
env.dc_http = function (mp, ml, up, ul, bp, bl) {
var body = bl > 0 ? bytes(bp, bl).slice() : null;
return httpStart(str(mp, ml), str(up, ul), body);
};
// -2 = still running, otherwise the HTTP status (0 = network error)
env.dc_http_poll = function (id) {
var r = http[id];
if (!r) return 0;
return r.done ? r.status : -2;
};
env.dc_http_len = function (id) {
var r = http[id];
return r ? r.body.length : 0;
};
// copies the response body and forgets the request
env.dc_http_take = function (id, ptr) {
var r = http[id];
if (!r) return;
bytes(ptr, r.body.length).set(r.body);
delete http[id];
};
env.dc_storage_get = function (kp, kl) {
var v = null;
try { v = window.localStorage.getItem(str(kp, kl)); } catch (e) { }
if (v === null) { pending = null; return -1; }
pending = new TextEncoder().encode(v);
return pending.length;
};
env.dc_storage_take = function (ptr) {
if (pending === null) return;
bytes(ptr, pending.length).set(pending);
pending = null;
};
// browser text dialog (touch devices have no keyboard in the canvas); result via dc_storage_take
env.dc_prompt = function (tp, tl, dp, dl) {
var v = window.prompt(str(tp, tl), str(dp, dl));
if (v === null || v === undefined) { pending = null; return -1; }
pending = new TextEncoder().encode(v);
return pending.length;
};
// opens a web page in a new tab (repository link)
env.dc_open_url = function (up, ul) {
try { window.open(str(up, ul), "_blank", "noopener"); } catch (e) { }
};
// the device's main pointer is a finger (phones, tablets)
env.dc_coarse_pointer = function () {
try { return window.matchMedia("(pointer: coarse)").matches ? 1 : 0; } catch (e) { return 0; }
};
env.dc_storage_set = function (kp, kl, vp, vl) {
try { window.localStorage.setItem(str(kp, kl), str(vp, vl)); } catch (e) { }
};
env.dc_cache_set = function (kp, kl, vp, vl) {
cache[str(kp, kl)] = str(vp, vl);
};
env.dc_download = function (np, nl, dp, dl) {
var blob = new Blob([bytes(dp, dl).slice()], { type: "application/json" });
var a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = str(np, nl);
document.body.appendChild(a);
a.click();
setTimeout(function () { URL.revokeObjectURL(a.href); a.remove(); }, 1000);
};
env.dc_open_file = function () {
file = null;
input.click();
};
env.dc_file_poll = function () {
return file === null ? -1 : file.length;
};
env.dc_file_take = function (ptr) {
if (file === null) return;
bytes(ptr, file.length).set(file);
file = null;
};
}
// ---- audio: the Rust mixer renders on demand (dc_audio_render), started by the first user input
var actx = null;
var stats = { frames: 0, peak: 0 };
// diagnostics, e.g. for automated tests: state of the audio output
window.dc_audio_stats = function () {
return { state: actx ? actx.state : "not started", frames: stats.frames, peak: stats.peak };
};
function startAudio() {
if (typeof wasm_exports === "undefined" || !wasm_exports.dc_audio_render) return;
if (actx) {
if (actx.state !== "running") actx.resume();
return;
}
var AC = window.AudioContext || window.webkitAudioContext;
if (!AC) return;
actx = new AC();
wasm_exports.dc_audio_init(actx.sampleRate);
var node = actx.createScriptProcessor(2048, 0, 2);
node.onaudioprocess = function (e) {
var n = e.outputBuffer.length;
var l = e.outputBuffer.getChannelData(0);
var r = e.outputBuffer.getChannelData(1);
var ptr = wasm_exports.dc_audio_render(n);
if (!ptr) { l.fill(0); r.fill(0); return; }
var buf = new Float32Array(wasm_memory.buffer, ptr, n * 2);
var peak = 0;
for (var i = 0; i < n; i++) {
l[i] = buf[2 * i]; r[i] = buf[2 * i + 1];
var a = Math.abs(l[i]); if (a > peak) peak = a;
}
stats.frames += n;
stats.peak = Math.max(stats.peak * 0.9, peak);
};
node.connect(actx.destination);
}
["mousedown", "keydown", "touchstart"].forEach(function (ev) {
window.addEventListener(ev, startAudio, true);
});
miniquad_add_plugin({ register_plugin: register, version: 1, name: "dungeoncraft" });
})();
</script>
<script>
document.getElementById("glcanvas").focus();
load("dungeoncraft.wasm");
</script>
</body>
</html>