Remove stale play standalone file: README and INSTALL point to the title-screen download
This commit is contained in:
1 parent
5928acf7f6
commit
f164293d21
4 files changed
+8
-268
No files matched your search
@@ -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
@@ -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 |
|
||||||
@@ -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)**.
|
||||||
|
|
||||||
|
|||||||
@@ -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,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">
|
|
||||||
<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>
|
|
||||||
Reference in new issue
Block a user