1
0
Fork 0
mirror of https://github.com/EDeev/pixel_gamble.git synced 2026-10-07 23:49:29 +03:00
pixel_gamble/web/template.tmpl
Egor Deev 4ecb9257c8 Pixel Gamble 2.0: законченная игра на ресурсах Ninja Adventure
- 10 волн, 5 видов врагов со своим поведением и босс в три фазы
- меч, копьё и посох, 11 улучшений между волнами, опыт и уровни
- меню, пауза, настройки, итоги партии и рекорд, русский и английский
- мир 480×270 с масштабированием, интерфейс в 960×540, скорости от dt
- асинхронный цикл: та же игра в браузере через pygbag
- тесты без окна, автоигрок, CI, релизы exe/Linux, GitHub Pages
- README на двух языках; исходная версия — релиз v1.0.0
2026-10-07 12:29:41 +00:00

445 lines
13 KiB
Cheetah
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<html lang="en-us"><script src="{{cookiecutter.cdn}}pythons.js" type=module id="site" data-python="python{{cookiecutter.PYBUILD}}" data-LINES=42 data-COLUMNS=132 data-os="vtx,snd,gui" async defer>#<!--
# Шаблон pygbag 0.9.3 (по умолчанию) с правками Pixel Gamble: тёмный фон, чёткие пиксели, стиль загрузки.
print("""
Loading {{cookiecutter.title}} from {{cookiecutter.archive}}.apk
Pygbag Version : {{cookiecutter.version}}
Template Version : 0.9.3
Python : {{cookiecutter.PYBUILD}}
CDN URL : {{cookiecutter.cdn}}
Screen : {{cookiecutter.width}}x{{cookiecutter.height}}
Title : {{cookiecutter.title}}
Folder : {{cookiecutter.directory}}
Authors : {{cookiecutter.authors}}
SPDX-License-Identifier: {{cookiecutter.spdx}}
""")
import sys
import asyncio
import platform
import json
from pathlib import Path
# do not rename
async def custom_site():
import embed
platform.document.body.style.background = "#1a141c"
platform.window.transfer.hidden = true
platform.window.canvas.style.visibility = "visible"
bundle = "{{cookiecutter.archive}}"
# the C or js loader could do that but be explicit.
appdir = Path(f"/data/data/{bundle}") # /data/data/{{cookiecutter.archive}}
appdir.mkdir()
# unpack filesystem from compressed archive into work dir
if platform.window.location.host.find('.itch.zone')>0:
import zipfile
async with platform.fopen("{{cookiecutter.archive}}.apk", "rb") as archive:
with zipfile.ZipFile(archive) as zip_ref:
zip_ref.extractall(appdir.as_posix())
else:
import tarfile
async with platform.fopen("{{cookiecutter.archive}}.tar.gz", "rb") as archive:
tar = tarfile.open(fileobj=archive, mode="r:gz")
tar.extractall(path=appdir.as_posix(), filter='tar')
tar.close()
# preloader will change to work dir and prepend it to sys.path
platform.run_main(PyConfig, loaderhome= appdir / "assets", loadermain=None)
# wait preloading complete : that includes images and wasm compilation of bundled modules
while embed.counter()<0:
await asyncio.sleep(.1)
main = appdir / "assets" / "main.py"
# TODO: test for window.webkitAudioContext and block aio loop until gesture if accessing media manager play
# before a gesture is recorded ( touch or click )
# test/wait user media interaction
if not platform.window.MM.UME:
# now that apk is mounted we have access to font cache
# but we need to fill __file__ that is not yet set
__import__(__name__).__file__ = main
# now make a prompt
msg = "Ready to start ! Please click/touch page"
platform.window.infobox.innerText = msg
print("\n"*4, f" * Waiting for media user engagement. {msg} *" , "\n"*4)
while not platform.window.MM.UME:
await asyncio.sleep(.1)
# start async top level machinery if not started and add a console in any case if requested.
await TopLevel_async_handler.start_toplevel(platform.shell, console=window.python.config.debug)
# now that apk is mounted we have access to font cache
# but we need to fill __file__/__name__ that are not yet set
__import__(__name__).__file__ = main
def ui_callback(pkg):
platform.window.infobox.innerText = f"installing {pkg}"
await shell.source(main, callback=ui_callback)
# if you don't reach that step
# your main.py has an infinite sync loop somewhere !
platform.window.infobox.style.display = "none"
platform.window.config.gui_divider = 1
platform.window.window_resize()
print("default.tmpl: done")
shell.interactive()
asyncio.run( custom_site() )
# BEGIN BLOCK
#
# now this is the html part you can (and should) customize
# It is not mandatory : pygame-script when it reads the first line (also called
# shebang ) of above code create absolute minimal widget set
# required for running with default rules
#
# do not alter that comment block it is separating python code from html code
# =============================================================================
# --></script><head><!--
//=============================================================================
//
//
//
//
//
//
//
{%- if cookiecutter.comment != "" -%}
{{cookiecutter.comment}}
{% endif %}
--><script type="application/javascript">
// END BLOCK
// this dict is available under PyConfig.config from __main__
config = {
xtermjs : "{{cookiecutter.xtermjs}}" ,
_sdl2 : "canvas",
user_canvas : 0,
user_canvas_managed : 0,
gui_divider : 2,
ume_block : {{cookiecutter.ume_block}},
can_close : {{cookiecutter.can_close}},
archive : "{{cookiecutter.archive}}",
gui_debug : 2,
cdn : "{{cookiecutter.cdn}}",
autorun : {{cookiecutter.autorun}},
PYBUILD : "{{cookiecutter.PYBUILD}}",
fb_ar : 1.77,
fb_width : "{{cookiecutter.width}}",
fb_height : "{{cookiecutter.height}}"
}
function show_infobox() {
infobox.style.display = "block";
// Measure box
const w = infobox.offsetWidth;
const h = infobox.offsetHeight;
// Center in viewport
const left = (window.innerWidth - w) / 2;
const top = (window.innerHeight - h) / 2;
infobox.style.left = left + "px";
infobox.style.top = top + "px";
}
</script>
<title>{{cookiecutter.title}}</title>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="height=device-height, initial-scale=1.0">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"/>
<link rel="icon" type="image/png" href="favicon.png" sizes="16x16">
<style>
#status {
display: inline-block;
vertical-align: top;
margin-top: 20px;
margin-left: 30px;
font-weight: bold;
color: rgb(120, 120, 120);
}
#progress {
height: 20px;
width: 300px;
}
#infobox {
position: fixed; /* center relative to viewport */
background: #2c2028;
color: #ffd65c;
font-family: monospace;
font-weight: bold;
border: 3px solid #5c4046;
border-radius: 6px;
padding: 12px 24px;
/* display: none; */
z-index: 999999;
}
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
div.thick_border { border: 4px solid black; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
/* average size of droid screen 470dp x 320dp */
canvas.emscripten {
image-rendering: pixelated;
image-rendering: crisp-edges;
border: 0px none;
background-color: transparent;
/* кадр 16:9 вписывается в окно без искажений (pygbag ставит размеры сам — перебиваем) */
width: min(100vw, calc(100vh * 16 / 9)) !important;
height: min(100vh, calc(100vw * 9 / 16)) !important;
z-index: 5;
padding: 0;
margin: 0 auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
body {
font-family: arial;
margin: 0;
padding: none;
background-color: #1a141c;
}
.topright{
position:absolute;
top:0px;
right:0px;
}
.bottomright {
position:absolute;
top: 40%;
right: 0px;
}
.center {
display: flex;
align-items: center;
justify-content: center;
}
.trinfo{
position: relative;
right: 0px;
border: 1px solid black;
}
.framed{
position: relative;
top: 150px;
right: 10px;
border: 1px solid black;
}
</style>
<script>
/*
if (navigator.serviceWorker)
navigator.serviceWorker.register("{{cookiecutter.cdn}}pygbag{{cookiecutter.version}}.js")
else
console.warn("Service workers not supported")
*/
</script>
<script src="{{cookiecutter.cdn}}/browserfs.min.js"></script>
</head>
<body>
<div id="transfer" align=center>
<!-- <div class="spinner" id='spinner'></div> -->
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress"></progress>
</div>
</div>
<canvas class="emscripten" id="canvas"
width="1px"
height="1px"
oncontextmenu="event.preventDefault()" tabindex=1>
</canvas>
<canvas class="emscripten" id="canvas3d"
width="{{cookiecutter.width}}px"
height="{{cookiecutter.height}}px"
oncontextmenu="event.preventDefault()" tabindex=1 hidden>
</canvas>
<div id="infobox">Loading, please wait ...</div>
<div id=html></div>
<div id=crt class=bottomright >
<div id="system" hidden>
<div class="button-container">
<button id="aiostop" disabled>AIO ⏏︎</button>
<button id="aiopaused_true" disabled>AIO ■</button>
<button id="aiopaused_false" disabled>AIO ▶</button>
<button id="pygame_mixer_music_pause" disabled>Music ■</button>
</div>
<div class="button-container">
<div id=load_min>min</div>
<div id=load_avg>avg</div>
<div id=load_max>max</div>
<button id="load_rst" disabled>RESET</button>
</div>
<div id="level">(battery level unknown)</div>
<div id="stateBattery">(charging state unknown)</div>
</div>
<div id=box class="emscripten_border" hidden=true>
<div id="info" class="trinfo"></div>
<iframe id="iframe" class="framed" name="iframe"
width="470px" height="90%"
allowtransparency="true"
style="z-index: 10;"
style="background: #FFFFFF;"
frameborder="1"
allowfullscreen="true"
webkitallowfullscreen="true"
msallowfullscreen="true"
mozallowfullscreen="true"
sandbox="allow-same-origin allow-top-navigation allow-scripts allow-pointer-lock"
allow="autoplay; fullscreen *; geolocation; microphone; camera; midi; monetization; xr-spatial-tracking; gamepad; gyroscope; accelerometer; xr; cross-origin-isolated"
src="{{cookiecutter.cdn}}empty.html"
scrolling="yes">
</iframe>
</div>
</div>
<div id="dlg" hidden>
<input type="file" id="dlg_multifile" multiple accept="image/*">
<label for="dlg_multifile">Select files</label>
</div>
<div id="pyconsole">
<div id="terminal" tabIndex=1 align="left"></div>
</div>
<script type="application/javascript">
globalThis.__canvas_resized = (self, ecw, ech) => {
console.warn("TODO: panda3d canvas monitor", self, ecw, ech)
}
async function custom_onload(debug_hidden) {
// this is called before anything python is loaded
// make your js customization here
console.log(__FILE__, "custom_onload")
pyconsole.hidden = debug_hidden
system.hidden = debug_hidden
transfer.hidden = debug_hidden
info.hidden = debug_hidden
box.hidden = debug_hidden
show_infobox()
}
function custom_prerun(){
// no python main and no (MEMFS + VFS) yet.
console.log(__FILE__, "custom_prerun")
}
function custom_postrun(){
// python main and no VFS filesystem yet.
console.log(__FILE__, "custom_postrun")
}
function debug() {
// allow to gain access to dev tools from js console
// but only on desktop. difficult to reach when in iframe
python.config.debug = true
custom_onload(false)
Module.PyRun_SimpleString("shell.uptime()")
window_resize()
}
function info_inline(data){
document.getElementById("info").innerHTML = data
}
function info_online(url) {
// display info about current APK
fetch( url /*, options */)
.then((response) => response.text())
.then((html) => {
info_inline(html);
})
.catch((error) => {
console.warn(error);
});
}
function frame_online(url) {
window.frames["iframe"].location = url;
}
</script>
</body>
</html>