Private
Public Access
1
0

Init
Some checks failed
Build & Deploy Game / build-and-deploy (push) Failing after 6s

This commit is contained in:
Sebastian Unterschütz
2025-11-24 22:32:10 +01:00
commit 6fa7e0a7c7
20 changed files with 2355 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -0,0 +1,211 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<font id="PressStart2P" horiz-adv-x="1000">
<font-face font-family="Press Start 2P" units-per-em="1000" panose-1="0 0 5 0 0 0 0 0 0 0" ascent="1000" descent="0" alphabetic="0" />
<glyph unicode=" " glyph-name="space" horiz-adv-x="1000" />
<glyph unicode="!" glyph-name="exclam" horiz-adv-x="1000" d="M250,375v625H625V625H500V375H250Zm0-250V250H500V125H250Z" />
<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="1000" d="M125,625v375H375V625H125Zm375,0v375H750V625H500Z" />
<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="1000" d="M125,125V250H0V375H125V750H0V875H125v125H375V875H500v125H750V875H875V750H750V375H875V250H750V125H500V250H375V125H125ZM375,375H500V750H375V375Z" />
<glyph unicode="$" glyph-name="dollar" horiz-adv-x="1000" d="M375,125V250H0V375H375V500H125V625H0V750H125V875H375v125H500V875H750V750H500V625H750V500H875V375H750V250H500V125H375ZM250,625H375V750H250V625ZM500,375H625V500H500V375Z" />
<glyph unicode="%" glyph-name="percent" horiz-adv-x="1000" d="M250,875H125v125H375V750H250V875Zm625,125V875H750v125H875ZM250,625H0V875H125V750H250V625ZM625,875H750V750H625V875ZM500,750H625V625H500V750ZM375,625H500V500H375V625ZM250,500H375V375H250V500Zm375,0H875V250H750V375H625V500ZM125,375H250V250H125V375ZM625,250H750V125H500V375H625V250ZM0,250H125V125H0V250Z" />
<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="1000" d="M625,375H750V250H875V125H125V250H0V500H125V625H0V875H125v125H500V875H625V625H500V500H625V375ZM250,625H375V875H250V625ZM500,375H375V500H250V250H500V375ZM875,500V375H750V500H875Z" />
<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="1000" d="M250,625v375H500V625H250Z" />
<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="1000" d="M500,125V250H375V375H250V750H375V875H500v125H750V875H625V750H500V375H625V250H750V125H500Z" />
<glyph unicode=")" glyph-name="parenright" horiz-adv-x="1000" d="M125,125V250H250V375H375V750H250V875H125v125H375V875H500V750H625V375H500V250H375V125H125Z" />
<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="1000" d="M125,250V375H250V500H0V625H250V750H125V875H375V750H500V875H750V750H625V625H875V500H625V375H750V250H500V375H375V250H125Z" />
<glyph unicode="+" glyph-name="plus" horiz-adv-x="1000" d="M375,250V500H125V625H375V875H625V625H875V500H625V250H375Z" />
<glyph unicode="," glyph-name="comma" horiz-adv-x="1000" d="M125,0V125H250V375H500V125H375V0H125Z" />
<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="1000" d="M125,500V625H875V500H125Z" />
<glyph unicode="." glyph-name="period" horiz-adv-x="1000" d="M250,125V375H500V125H250Z" />
<glyph unicode="/" glyph-name="slash" horiz-adv-x="1000" d="M875,1000V875H750v125H875ZM625,875H750V750H625V875ZM500,750H625V625H500V750ZM375,625H500V500H375V625ZM250,500H375V375H250V500ZM125,375H250V250H125V375ZM0,250H125V125H0V250Z" />
<glyph unicode="0" glyph-name="zero" horiz-adv-x="1000" d="M250,125V250H125V375H0V750H125V875H250v125H625V875H750V750H875V375H750V250H625V125H250ZM375,250H625V750H500V875H250V375H375V250Z" />
<glyph unicode="1" glyph-name="one" horiz-adv-x="1000" d="M125,125V250H375V750H250V875H375v125H625V250H875V125H125Z" />
<glyph unicode="2" glyph-name="two" horiz-adv-x="1000" d="M0,125V375H125V500H250V625H500V750H625V875H250V750H0V875H125v125H750V875H875V625H750V500H625V375H375V250H875V125H0Z" />
<glyph unicode="3" glyph-name="three" horiz-adv-x="1000" d="M125,125V250H0V375H250V250H625V500H250V625H375V750H500V875H125v125H875V875H750V750H625V625H750V500H875V250H750V125H125Z" />
<glyph unicode="4" glyph-name="four" horiz-adv-x="1000" d="M500,125V375H0V625H125V750H250V875H375v125H750V500H875V375H750V125H500ZM250,500H500V750H375V625H250V500Z" />
<glyph unicode="5" glyph-name="five" horiz-adv-x="1000" d="M125,125V250H0V375H250V250H625V625H0v375H750V875H250V750H750V625H875V250H750V125H125Z" />
<glyph unicode="6" glyph-name="six" horiz-adv-x="1000" d="M125,125V250H0V750H125V875H250v125H750V875H375V750H250V625H750V500H875V250H750V125H125ZM250,250H625V500H250V250Z" />
<glyph unicode="7" glyph-name="seven" horiz-adv-x="1000" d="M250,125V500H375V625H500V750H625V875H250V750H0v250H875V750H750V625H625V500H500V125H250Z" />
<glyph unicode="8" glyph-name="eight" horiz-adv-x="1000" d="M125,125V250H0V500H125V625H0V875H125v125H625V875H750V625H625V500H875V250H750V125H125ZM375,625H625V875H250V750H375V625ZM125,250H625V375H375V500H125V250Z" />
<glyph unicode="9" glyph-name="nine" horiz-adv-x="1000" d="M125,125V250H500V375H625V500H125V625H0V875H125v125H750V875H875V375H750V250H625V125H125ZM250,625H625V875H250V625Z" />
<glyph unicode=":" glyph-name="colon" horiz-adv-x="1000" d="M250,625V875H500V625H250Zm0-375V500H500V250H250Z" />
<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="1000" d="M250,625V875H500V625H250ZM125,125V250H250V500H500V250H375V125H125Z" />
<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="1000" d="M500,125V250H375V375H250V500H125V625H250V750H375V875H500v125H750V875H625V750H500V625H375V500H500V375H625V250H750V125H500Z" />
<glyph unicode="=" glyph-name="equal" horiz-adv-x="1000" d="M0,625V750H875V625H0ZM0,375V500H875V375H0Z" />
<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="1000" d="M125,125V250H250V375H375V500H500V625H375V750H250V875H125v125H375V875H500V750H625V625H750V500H625V375H500V250H375V125H125Z" />
<glyph unicode="?" glyph-name="question" horiz-adv-x="1000" d="M250,375V500H500V625H625V750H250V625H0V875H125v125H750V875H875V625H750V500H625V375H250Zm0-250V250H625V125H250Z" />
<glyph unicode="@" glyph-name="at" horiz-adv-x="1000" d="M750,1000V875H125v125H750ZM0,875H125V250H0V875Zm875,0V375H250V750H625V500H750V875H875ZM500,625H375V500H500V625ZM125,125V250H750V125H125Z" />
<glyph unicode="A" glyph-name="A" horiz-adv-x="1000" d="M0,125V750H125V875H250v125H625V875H750V750H875V125H625V375H250V125H0ZM250,500H625V750H500V875H375V750H250V500Z" />
<glyph unicode="B" glyph-name="B" horiz-adv-x="1000" d="M0,125v875H750V875H875V625H750V500H875V250H750V125H0ZM250,625H625V875H250V625Zm0-375H625V500H250V250Z" />
<glyph unicode="C" glyph-name="C" horiz-adv-x="1000" d="M250,125V250H125V375H0V750H125V875H250v125H750V875H875V750H625V875H375V750H250V375H375V250H625V375H875V250H750V125H250Z" />
<glyph unicode="D" glyph-name="D" horiz-adv-x="1000" d="M0,125v875H625V875H750V750H875V375H750V250H625V125H0ZM250,250H500V375H625V750H500V875H250V250Z" />
<glyph unicode="E" glyph-name="E" horiz-adv-x="1000" d="M0,125v875H875V875H250V625H750V500H250V250H875V125H0Z" />
<glyph unicode="F" glyph-name="F" horiz-adv-x="1000" d="M0,125v875H875V875H250V625H750V500H250V125H0Z" />
<glyph unicode="G" glyph-name="G" horiz-adv-x="1000" d="M250,125V250H125V375H0V750H125V875H250v125H875V875H375V750H250V375H375V250H625V500H500V625H875V125H250Z" />
<glyph unicode="H" glyph-name="H" horiz-adv-x="1000" d="M0,125v875H250V625H625v375H875V125H625V500H250V125H0Z" />
<glyph unicode="I" glyph-name="I" horiz-adv-x="1000" d="M125,125V250H375V875H125v125H875V875H625V250H875V125H125Z" />
<glyph unicode="J" glyph-name="J" horiz-adv-x="1000" d="M125,125V250H0V375H250V250H625v750H875V250H750V125H125Z" />
<glyph unicode="K" glyph-name="K" horiz-adv-x="1000" d="M0,125v875H250V625H375V750H500V875H625v125H875V875H750V750H625V625H500V500H625V375H750V250H875V125H500V250H375V375H250V125H0Z" />
<glyph unicode="L" glyph-name="L" horiz-adv-x="1000" d="M125,125v875H375V250H875V125H125Z" />
<glyph unicode="M" glyph-name="M" horiz-adv-x="1000" d="M0,125v875H250V875H375V750H500V875H625v125H875V125H625V625H500V375H375V625H250V125H0Z" />
<glyph unicode="N" glyph-name="N" horiz-adv-x="1000" d="M0,125v875H250V875H375V750H500V625H625v375H875V125H625V375H500V500H375V625H250V125H0Z" />
<glyph unicode="O" glyph-name="O" horiz-adv-x="1000" d="M125,125V250H0V875H125v125H750V875H875V250H750V125H125ZM250,250H625V875H250V250Z" />
<glyph unicode="P" glyph-name="P" horiz-adv-x="1000" d="M0,125v875H750V875H875V500H750V375H250V125H0ZM250,500H625V875H250V500Z" />
<glyph unicode="Q" glyph-name="Q" horiz-adv-x="1000" d="M875,875V375H750V250H625V125H125V250H0V875H125v125H750V875H875Zm-250,0H250V250H500V375H375V500H625V875ZM750,125V250H875V125H750Z" />
<glyph unicode="R" glyph-name="R" horiz-adv-x="1000" d="M0,125v875H750V875H875V500H625V375H750V250H875V125H500V250H375V375H250V125H0ZM250,500H500V625H625V875H250V500Z" />
<glyph unicode="S" glyph-name="S" horiz-adv-x="1000" d="M125,125V250H0V375H250V250H625V500H125V625H0V875H125v125H750V875H875V750H625V875H250V625H750V500H875V250H750V125H125Z" />
<glyph unicode="T" glyph-name="T" horiz-adv-x="1000" d="M375,125V875H125v125H875V875H625V125H375Z" />
<glyph unicode="U" glyph-name="U" horiz-adv-x="1000" d="M125,125V250H0v750H250V250H625v750H875V250H750V125H125Z" />
<glyph unicode="V" glyph-name="V" horiz-adv-x="1000" d="M375,125V250H250V375H125V500H0v500H250V625H375V500H500V625H625v375H875V500H750V375H625V250H500V125H375Z" />
<glyph unicode="W" glyph-name="W" horiz-adv-x="1000" d="M125,125V250H0v750H250V500H375v500H500V500H625v500H875V250H750V125H625V250H500V375H375V250H250V125H125Z" />
<glyph unicode="X" glyph-name="X" horiz-adv-x="1000" d="M0,125V375H125V500H250V625H125V750H0v250H250V750H375V625H500V750H625v250H875V750H750V625H625V500H750V375H875V125H625V375H500V500H375V375H250V125H0Z" />
<glyph unicode="Y" glyph-name="Y" horiz-adv-x="1000" d="M375,125V500H250V625H125v375H375V625H625v375H875V625H750V500H625V125H375Z" />
<glyph unicode="Z" glyph-name="Z" horiz-adv-x="1000" d="M0,125V375H125V500H250V625H375V750H500V875H0v125H875V750H750V625H625V500H500V375H375V250H875V125H0Z" />
<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="1000" d="M250,125v875H750V875H500V250H750V125H250Z" />
<glyph unicode="\" glyph-name="backslash" horiz-adv-x="1000" d="M125,1000V875H0v125H125ZM250,875V750H125V875H250ZM375,750V625H250V750H375ZM500,625V500H375V625H500ZM625,500V375H500V500H625ZM750,375V250H625V375H750ZM875,250V125H750V250H875Z" />
<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="1000" d="M125,125V250H375V875H125v125H625V125H125Z" />
<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="1000" d="M125,750V875H250v125H625V875H750V750H500V875H375V750H125Z" />
<glyph unicode="_" glyph-name="underscore" horiz-adv-x="1000" d="M0,0V125H875V0H0Z" />
<glyph unicode="`" glyph-name="grave" horiz-adv-x="1000" d="M500,1000V875H375v125H500ZM625,875V750H500V875H625Z" />
<glyph unicode="a" glyph-name="a" horiz-adv-x="1000" d="M125,125V250H0V375H125V500H625V625H125V750H750V625H875V125H125ZM250,250H625V375H250V250Z" />
<glyph unicode="b" glyph-name="b" horiz-adv-x="1000" d="M125,125V250H0v750H250V750H750V625H875V250H750V125H125ZM250,250H625V625H250V250Z" />
<glyph unicode="c" glyph-name="c" horiz-adv-x="1000" d="M125,125V250H0V625H125V750H875V625H250V250H875V125H125Z" />
<glyph unicode="d" glyph-name="d" horiz-adv-x="1000" d="M125,125V250H0V625H125V750H625v250H875V125H125ZM250,250H625V625H250V250Z" />
<glyph unicode="e" glyph-name="e" horiz-adv-x="1000" d="M125,125V250H0V625H125V750H750V625H875V375H250V250H750V125H125ZM250,500H625V625H250V500Z" />
<glyph unicode="f" glyph-name="f" horiz-adv-x="1000" d="M375,125V625H125V750H375V875H500v125H875V875H625V750H875V625H625V125H375Z" />
<glyph unicode="g" glyph-name="g" horiz-adv-x="1000" d="M125,0V125H625V250H125V375H0V625H125V750H875V125H750V0H125ZM250,375H625V625H250V375Z" />
<glyph unicode="h" glyph-name="h" horiz-adv-x="1000" d="M0,125v875H250V750H750V625H875V125H625V625H250V125H0Z" />
<glyph unicode="i" glyph-name="i" horiz-adv-x="1000" d="M375,875v125H625V875H375ZM125,125V250H375V625H250V750H625V250H875V125H125Z" />
<glyph unicode="j" glyph-name="j" horiz-adv-x="1000" d="M500,875v125H750V875H500ZM125,0V125H500V625H375V750H750V125H625V0H125Z" />
<glyph unicode="k" glyph-name="k" horiz-adv-x="1000" d="M0,125v875H250V500H500V625H625V750H875V625H750V500H625V375H750V250H875V125H625V250H500V375H250V125H0Z" />
<glyph unicode="l" glyph-name="l" horiz-adv-x="1000" d="M125,125V250H375V875H250v125H625V250H875V125H125Z" />
<glyph unicode="m" glyph-name="m" horiz-adv-x="1000" d="M0,125V750H750V625H875V125H625V625H500V125H250V625H125V125H0Z" />
<glyph unicode="n" glyph-name="n" horiz-adv-x="1000" d="M0,125V750H750V625H875V125H625V625H250V125H0Z" />
<glyph unicode="o" glyph-name="o" horiz-adv-x="1000" d="M125,125V250H0V625H125V750H750V625H875V250H750V125H125ZM250,250H625V625H250V250Z" />
<glyph unicode="p" glyph-name="p" horiz-adv-x="1000" d="M0,0V750H750V625H875V375H750V250H250V0H0ZM250,375H625V625H250V375Z" />
<glyph unicode="q" glyph-name="q" horiz-adv-x="1000" d="M625,0V250H125V375H0V625H125V750H875V0H625ZM250,375H625V625H250V375Z" />
<glyph unicode="r" glyph-name="r" horiz-adv-x="1000" d="M375,625H500V500H375V125H125V750H375V625ZM875,750V625H500V750H875Z" />
<glyph unicode="s" glyph-name="s" horiz-adv-x="1000" d="M0,125V250H625V375H125V500H0V625H125V750H750V625H250V500H750V375H875V250H750V125H0Z" />
<glyph unicode="t" glyph-name="t" horiz-adv-x="1000" d="M375,125V625H125V750H375v250H625V750H875V625H625V125H375Z" />
<glyph unicode="u" glyph-name="u" horiz-adv-x="1000" d="M125,125V250H0V750H250V250H625V750H875V125H125Z" />
<glyph unicode="v" glyph-name="v" horiz-adv-x="1000" d="M375,125V250H250V375H125V750H375V375H625V750H875V375H750V250H625V125H375Z" />
<glyph unicode="w" glyph-name="w" horiz-adv-x="1000" d="M250,250H375V125H125V250H0V750H250V250ZM500,750V250H375V750H500Zm375,0V250H750V125H500V250H625V750H875Z" />
<glyph unicode="x" glyph-name="x" horiz-adv-x="1000" d="M0,125V250H125V375H250V500H125V625H0V750H250V625H375V500H500V625H625V750H875V625H750V500H625V375H750V250H875V125H625V250H500V375H375V250H250V125H0Z" />
<glyph unicode="y" glyph-name="y" horiz-adv-x="1000" d="M125,0V125H625V250H125V375H0V750H250V375H625V750H875V125H750V0H125Z" />
<glyph unicode="z" glyph-name="z" horiz-adv-x="1000" d="M0,125V250H125V375H250V500H375V625H0V750H875V625H750V500H625V375H500V250H875V125H0Z" />
<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="1000" d="M500,125V250H375V500H250V625H375V875H500v125H750V875H625V625H500V500H625V250H750V125H500Z" />
<glyph unicode="|" glyph-name="bar" horiz-adv-x="1000" d="M375,125v875H625V125H375Z" />
<glyph unicode="}" glyph-name="braceright" horiz-adv-x="1000" d="M125,125V250H250V500H375V625H250V875H125v125H375V875H500V625H625V500H500V250H375V125H125Z" />
<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="1000" d="M625,500H750V375H375V500H250V625H125V750H500V625H625V500ZM0,625H125V500H0V625Zm875,0V500H750V625H875Z" />
<glyph unicode="&#xa0;" glyph-name="uni00A0" horiz-adv-x="1000" />
<glyph unicode="&#xa1;" glyph-name="exclamdown" horiz-adv-x="1000" d="M375,875v125H625V875H375ZM250,125V500H375V750H625V125H250Z" />
<glyph unicode="&#xa2;" glyph-name="cent" horiz-adv-x="1000" d="M375,125V250H125V375H0V750H125V875H375v125H500V875H750V750H875V625H625V750H500V375H625V500H875V375H750V250H500V125H375ZM250,375H375V750H250V375Z" />
<glyph unicode="&#xa3;" glyph-name="sterling" horiz-adv-x="1000" d="M0,125V250H125V500H0V625H125V875H250v125H750V875H875V750H625V875H375V625H750V500H375V250H875V125H0Z" />
<glyph unicode="&#xa4;" glyph-name="currency" horiz-adv-x="1000" d="M250,875V750H125V875H250ZM625,750H375V875H625V750ZM875,875V750H750V875H875ZM375,750V375H250V750H375Zm250,0H750V375H625V750ZM125,375H250V250H125V375Zm250,0H625V250H375V375ZM750,250V375H875V250H750Z" />
<glyph unicode="&#xa5;" glyph-name="yen" horiz-adv-x="1000" d="M375,125V250H125V375H375V500H125V625H250V750H125v250H375V750H625v250H875V750H750V625H875V500H625V375H875V250H625V125H375Z" />
<glyph unicode="&#xa6;" glyph-name="brokenbar" horiz-adv-x="1000" d="M375,625v375H625V625H375Zm0-500V500H625V125H375Z" />
<glyph unicode="&#xa7;" glyph-name="section" horiz-adv-x="1000" d="M875,875V750H625V875H375V750H625V625H375V500H250V625H125V875H250v125H750V875H875ZM625,500V625H750V500H875V250H750V125H250V250H125V375H375V250H625V375H375V500H625Z" />
<glyph unicode="&#xa8;" glyph-name="dieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500Z" />
<glyph unicode="&#xa9;" glyph-name="copyright" horiz-adv-x="1000" d="M750,875H250v125H750V875ZM250,750H125V875H250V750Zm500,0V875H875V750H750Zm-625,0V250H0V750H125Zm250,0H625V625H375V750Zm625,0V250H875V750h125ZM250,625H375V375H250V625ZM375,250V375H625V250H375Zm-125,0V125H125V250H250Zm500,0H875V125H750V250ZM250,125H750V0H250V125Z" />
<glyph unicode="&#xaa;" glyph-name="ordfeminine" horiz-adv-x="1000" d="M250,500V625H125V750H250V875H125v125H625V875H750V500H250ZM375,625H500V750H375V625Z" />
<glyph unicode="&#xab;" glyph-name="guillemotleft" horiz-adv-x="1000" d="M250,250V375H125V500H0V625H125V750H250V875H500V750H625V875H875V750H750V625H625V500H750V375H875V250H625V375H500V250H250ZM375,375H500V500H375V625H500V750H375V625H250V500H375V375Z" />
<glyph unicode="&#xac;" glyph-name="logicalnot" horiz-adv-x="1000" d="M625,375V625H125V750H875V375H625Z" />
<glyph unicode="&#xad;" glyph-name="uni00AD" horiz-adv-x="1000" d="M125,500V625H875V500H125Z" />
<glyph unicode="&#xae;" glyph-name="registered" horiz-adv-x="1000" d="M750,875H250v125H750V875ZM250,750H125V875H250V750Zm500,0V875H875V750H750Zm-625,0V250H0V750H125ZM250,250V750H625V625H375V500H625V375H375V250H250Zm750,500V250H875V750h125ZM625,500V625H750V500H625Zm0-250V375H750V250H625Zm-375,0V125H125V250H250Zm500,0H875V125H750V250ZM250,125H750V0H250V125Z" />
<glyph unicode="&#xaf;" glyph-name="macron" horiz-adv-x="1000" d="M125,875v125H750V875H125Z" />
<glyph unicode="&#xb0;" glyph-name="degree" horiz-adv-x="1000" d="M500,875H375v125H500V875Zm-125,0V750H250V875H375Zm250,0V750H500V875H625ZM375,750H500V625H375V750Z" />
<glyph unicode="&#xb1;" glyph-name="plusminus" horiz-adv-x="1000" d="M375,375V625H125V750H375v250H625V750H875V625H625V375H375ZM125,125V250H875V125H125Z" />
<glyph unicode="&#xb2;" glyph-name="uni00B2" horiz-adv-x="1000" d="M250,500V625H375V750H500V875H250v125H625V875H750V750H625V625H750V500H250Z" />
<glyph unicode="&#xb3;" glyph-name="uni00B3" horiz-adv-x="1000" d="M250,500V625H500V750H375V875H250v125H750V875H625V750H750V625H625V500H250Z" />
<glyph unicode="&#xb4;" glyph-name="acute" horiz-adv-x="1000" d="M625,1000V875H500v125H625ZM375,875H500V750H375V875Z" />
<glyph unicode="&#xb5;" glyph-name="uni00B5" horiz-adv-x="1000" d="M250,250H500V125H250V0H0V750H250V250Zm625,0V125H625V250H500V750H750V250H875Z" />
<glyph unicode="&#xb6;" glyph-name="paragraph" horiz-adv-x="1000" d="M500,125V375H250V500H125V875H250v125H875V125H750V375H625V125H500ZM375,500H500V875H375V750H250V625H375V500Zm250,0H750V875H625V500Z" />
<glyph unicode="&#xb7;" glyph-name="periodcentered" horiz-adv-x="1000" d="M250,375V625H500V375H250Z" />
<glyph unicode="&#xb8;" glyph-name="cedilla" horiz-adv-x="1000" d="M625,250V125H500V250H625ZM250,125H500V0H250V125Z" />
<glyph unicode="&#xb9;" glyph-name="uni00B9" horiz-adv-x="1000" d="M250,500V625H375V750H250V875H375v125H625V625H750V500H250Z" />
<glyph unicode="&#xba;" glyph-name="ordmasculine" horiz-adv-x="1000" d="M250,500V625H125V875H250v125H625V875H750V625H625V500H250ZM375,625H500V875H375V625Z" />
<glyph unicode="&#xbb;" glyph-name="guillemotright" horiz-adv-x="1000" d="M0,250V375H125V500H250V625H125V750H0V875H250V750H375V875H625V750H750V625H875V500H750V375H625V250H375V375H250V250H0ZM375,375H500V500H625V625H500V750H375V625H500V500H375V375Z" />
<glyph unicode="&#xbc;" glyph-name="onequarter" horiz-adv-x="1000" d="M250,1000V500H125V750H0V875H125v125H250Zm625,0V875H750v125H875ZM625,875H750V750H625V875ZM500,750H625V625H500V750ZM375,625H500V500H375V625Zm375,0H875V125H750V250H500V375H625V500H750V625ZM250,500H375V375H250V500ZM125,375H250V250H125V375ZM0,250H125V125H0V250Z" />
<glyph unicode="&#xbd;" glyph-name="onehalf" horiz-adv-x="1000" d="M250,1000V500H125V750H0V875H125v125H250Zm625,0V875H750v125H875ZM625,875H750V750H625V875ZM500,750H625V625H500V750ZM375,625H500V500H375V625ZM625,500V625H875V375H750V500H625Zm-375,0H375V375H250V500ZM125,375H250V250H125V375Zm500,0H750V250H875V125H500V250H625V375ZM0,250H125V125H0V250Z" />
<glyph unicode="&#xbe;" glyph-name="threequarters" horiz-adv-x="1000" d="M250,875V750H125V875H0v125H375V875H250Zm625,125V875H750v125H875ZM625,875H750V750H625V875ZM375,625H250V750H375V625ZM500,750H625V625H500V750ZM250,625V500H0V625H250Zm125,0H500V500H375V625Zm375,0H875V125H750V250H500V375H625V500H750V625ZM250,500H375V375H250V500ZM125,375H250V250H125V375ZM0,250H125V125H0V250Z" />
<glyph unicode="&#xbf;" glyph-name="questiondown" horiz-adv-x="1000" d="M250,875v125H625V875H250ZM125,125V250H0V500H125V625H250V750H625V625H375V500H250V375H625V500H875V250H750V125H125Z" />
<glyph unicode="&#xc0;" glyph-name="Agrave" horiz-adv-x="1000" d="M375,1000V875H250v125H375ZM875,500V125H625V250H250V125H0V500H125V625H250V750H375V875H500V750H625V625H750V500H875Zm-250,0H500V625H375V500H250V375H625V500Z" />
<glyph unicode="&#xc1;" glyph-name="Aacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM875,500V125H625V250H250V125H0V500H125V625H250V750H375V875H500V750H625V625H750V500H875Zm-250,0H500V625H375V500H250V375H625V500Z" />
<glyph unicode="&#xc2;" glyph-name="Acircumflex" horiz-adv-x="1000" d="M0,125V500H125V625H250V750H125V875H250v125H625V875H750V750H625V625H750V500H875V125H625V250H250V125H0ZM375,750H500V875H375V750ZM250,375H625V500H500V625H375V500H250V375Z" />
<glyph unicode="&#xc3;" glyph-name="Atilde" horiz-adv-x="1000" d="M875,500V125H625V250H250V125H0V500H125V625H250V750H375V875H250v125H500V875H625V625H750V500H875ZM750,875H625v125H750V875Zm-500,0V750H125V875H250ZM625,500H500V625H375V500H250V375H625V500Z" />
<glyph unicode="&#xc4;" glyph-name="Adieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM0,125V500H125V625H250V750H625V625H750V500H875V125H625V250H250V125H0ZM250,375H625V500H500V625H375V500H250V375Z" />
<glyph unicode="&#xc5;" glyph-name="Aring" horiz-adv-x="1000" d="M0,125V500H125V625H250V875H375v125H500V875H625V625H750V500H875V125H625V250H250V125H0ZM375,750H500V875H375V750ZM250,375H625V500H500V625H375V500H250V375Z" />
<glyph unicode="&#xc6;" glyph-name="AE" horiz-adv-x="1000" d="M0,125V750H125V875H250v125H875V875H625V625H875V500H625V250H875V125H375V375H250V125H0ZM250,500H375V750H250V500Z" />
<glyph unicode="&#xc7;" glyph-name="Ccedilla" horiz-adv-x="1000" d="M875,875V750H625V875H375V750H250V500H375V375H625V500H875V375H750V250H625V125H500V250H250V375H125V500H0V750H125V875H250v125H750V875H875ZM250,125H500V0H250V125Z" />
<glyph unicode="&#xc8;" glyph-name="Egrave" horiz-adv-x="1000" d="M375,1000V875H250v125H375ZM875,750V625H250V500H750V375H250V250H875V125H0V750H375V875H500V750H875Z" />
<glyph unicode="&#xc9;" glyph-name="Eacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM875,750V625H250V500H750V375H250V250H875V125H0V750H375V875H500V750H875Z" />
<glyph unicode="&#xca;" glyph-name="Ecircumflex" horiz-adv-x="1000" d="M0,125V750H125V875H250v125H625V875H750V750H875V625H250V500H750V375H250V250H875V125H0ZM375,750H500V875H375V750Z" />
<glyph unicode="&#xcb;" glyph-name="Edieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM0,125V750H875V625H250V500H750V375H250V250H875V125H0Z" />
<glyph unicode="&#xcc;" glyph-name="Igrave" horiz-adv-x="1000" d="M500,1000V875H375v125H500ZM875,750V625H625V250H875V125H125V250H375V625H125V750H500V875H625V750H875Z" />
<glyph unicode="&#xcd;" glyph-name="Iacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM875,750V625H625V250H875V125H125V250H375V625H125V750H375V875H500V750H875Z" />
<glyph unicode="&#xce;" glyph-name="Icircumflex" horiz-adv-x="1000" d="M125,125V250H375V625H125V750H250V875H375v125H625V875H750V750H875V625H625V250H875V125H125Z" />
<glyph unicode="&#xcf;" glyph-name="Idieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm500,0v125H875V875H625ZM125,125V250H375V625H125V750H875V625H625V250H875V125H125Z" />
<glyph unicode="&#xd0;" glyph-name="Eth" horiz-adv-x="1000" d="M125,125V500H0V625H125v375H625V875H750V750H875V375H750V250H625V125H125ZM375,250H500V375H625V750H500V875H375V625H500V500H375V250Z" />
<glyph unicode="&#xd1;" glyph-name="Ntilde" horiz-adv-x="1000" d="M375,875H250v125H500V875H625V750H375V875Zm375,0H625v125H750V875ZM875,750V125H500V250H375V375H250V125H0V750H125V875H250V750H375V625H500V500H625V750H875Z" />
<glyph unicode="&#xd2;" glyph-name="Ograve" horiz-adv-x="1000" d="M375,1000V875H250v125H375ZM875,625V250H750V125H125V250H0V625H125V750H375V875H500V750H750V625H875Zm-250,0H250V250H625V625Z" />
<glyph unicode="&#xd3;" glyph-name="Oacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM875,625V250H750V125H125V250H0V625H125V750H375V875H500V750H750V625H875Zm-250,0H250V250H625V625Z" />
<glyph unicode="&#xd4;" glyph-name="Ocircumflex" horiz-adv-x="1000" d="M125,125V250H0V625H125V875H250v125H625V875H750V625H875V250H750V125H125ZM375,750H500V875H375V750ZM250,250H625V625H250V250Z" />
<glyph unicode="&#xd5;" glyph-name="Otilde" horiz-adv-x="1000" d="M125,125V250H0V625H125V875H250v125H500V875H625v125H750V875H625V750H750V625H875V250H750V125H125ZM250,750H375V875H250V750Zm0-500H625V625H250V250Z" />
<glyph unicode="&#xd6;" glyph-name="Odieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,125V250H0V625H125V750H750V625H875V250H750V125H125ZM250,250H625V625H250V250Z" />
<glyph unicode="&#xd7;" glyph-name="multiply" horiz-adv-x="1000" d="M250,875V750H125V875H250Zm500,0V750H625V875H750ZM375,750V625H250V750H375Zm125,0H625V625H500V750ZM375,625H500V500H375V625ZM250,500H375V375H250V500ZM500,375V500H625V375H500Zm-375,0H250V250H125V375ZM625,250V375H750V250H625Z" />
<glyph unicode="&#xd8;" glyph-name="Oslash" horiz-adv-x="1000" d="M125,125V250H0V875H125v125H750V875H875V250H750V125H125ZM250,250H625V625H500V750H625V875H250V500H375V375H250V250ZM375,500V625H500V500H375Z" />
<glyph unicode="&#xd9;" glyph-name="Ugrave" horiz-adv-x="1000" d="M375,875H250v125H375V875Zm0-125V875H500V750H375Zm500,0V250H750V125H125V250H0V750H250V250H625V750H875Z" />
<glyph unicode="&#xda;" glyph-name="Uacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM500,750H375V875H500V750Zm375,0V250H750V125H125V250H0V750H250V250H625V750H875Z" />
<glyph unicode="&#xdb;" glyph-name="Ucircumflex" horiz-adv-x="1000" d="M125,750V875H250v125H625V875H750V750H500V875H375V750H125Zm0-625V250H0V625H250V250H625V625H875V250H750V125H125Z" />
<glyph unicode="&#xdc;" glyph-name="Udieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,125V250H0V750H250V250H625V750H875V250H750V125H125Z" />
<glyph unicode="&#xdd;" glyph-name="Yacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM500,750H375V875H500V750Zm375,0V500H750V375H625V125H375V375H250V500H125V750H375V500H625V750H875Z" />
<glyph unicode="&#xde;" glyph-name="Thorn" horiz-adv-x="1000" d="M0,125v875H250V875H750V750H875V375H750V250H250V125H0ZM250,375H625V750H250V375Z" />
<glyph unicode="&#xdf;" glyph-name="germandbls" horiz-adv-x="1000" d="M875,875V625H750V500H875V250H750V125H500V250H625V500H500V625H625V875H375V375H500V250H375V125H125V875H250v125H750V875H875Z" />
<glyph unicode="&#xe0;" glyph-name="agrave" horiz-adv-x="1000" d="M375,1000V875H250v125H375ZM875,625V125H125V250H0V375H125V500H625V625H125V750H375V875H500V750H750V625H875ZM625,375H250V250H625V375Z" />
<glyph unicode="&#xe1;" glyph-name="aacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM875,625V125H125V250H0V375H125V500H625V625H125V750H375V875H500V750H750V625H875ZM625,375H250V250H625V375Z" />
<glyph unicode="&#xe2;" glyph-name="acircumflex" horiz-adv-x="1000" d="M125,125V250H0V375H125V500H625V625H125V875H250v125H625V875H750V625H875V125H125ZM375,750H500V875H375V750ZM250,250H625V375H250V250Z" />
<glyph unicode="&#xe3;" glyph-name="atilde" horiz-adv-x="1000" d="M125,125V250H0V375H125V500H625V625H125V875H250v125H500V875H625v125H750V875H625V750H750V625H875V125H125ZM250,750H375V875H250V750Zm0-500H625V375H250V250Z" />
<glyph unicode="&#xe4;" glyph-name="adieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,125V250H0V375H125V500H625V625H125V750H750V625H875V125H125ZM250,250H625V375H250V250Z" />
<glyph unicode="&#xe5;" glyph-name="aring" horiz-adv-x="1000" d="M125,125V250H0V375H125V500H625V625H125V750H250V875H375v125H500V875H625V750H750V625H875V125H125ZM375,750H500V875H375V750ZM250,250H625V375H250V250Z" />
<glyph unicode="&#xe6;" glyph-name="ae" horiz-adv-x="1000" d="M125,125V250H0V375H125V500H375V625H125V750H750V625H875V375H500V250H750V125H125ZM500,500H625V625H500V500ZM250,250H375V375H250V250Z" />
<glyph unicode="&#xe7;" glyph-name="ccedilla" horiz-adv-x="1000" d="M875,750V625H250V375H875V250H625V125H500V250H125V375H0V625H125V750H875ZM250,125H500V0H250V125Z" />
<glyph unicode="&#xe8;" glyph-name="egrave" horiz-adv-x="1000" d="M375,1000V875H250v125H375ZM875,625V375H250V250H750V125H125V250H0V625H125V750H375V875H500V750H750V625H875Zm-250,0H250V500H625V625Z" />
<glyph unicode="&#xe9;" glyph-name="eacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM875,625V375H250V250H750V125H125V250H0V625H125V750H375V875H500V750H750V625H875Zm-250,0H250V500H625V625Z" />
<glyph unicode="&#xea;" glyph-name="ecircumflex" horiz-adv-x="1000" d="M125,125V250H0V625H125V875H250v125H625V875H750V625H875V375H250V250H750V125H125ZM375,750H500V875H375V750ZM250,500H625V625H250V500Z" />
<glyph unicode="&#xeb;" glyph-name="edieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,125V250H0V625H125V750H750V625H875V375H250V250H750V125H125ZM250,500H625V625H250V500Z" />
<glyph unicode="&#xec;" glyph-name="igrave" horiz-adv-x="1000" d="M375,875H250v125H375V875Zm0-125V875H500V750H375ZM875,250V125H125V250H375V500H250V625H625V250H875Z" />
<glyph unicode="&#xed;" glyph-name="iacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM500,750H375V875H500V750ZM875,250V125H125V250H375V500H250V625H625V250H875Z" />
<glyph unicode="&#xee;" glyph-name="icircumflex" horiz-adv-x="1000" d="M125,750V875H250v125H625V875H750V750H500V875H375V750H125Zm0-625V250H375V500H250V625H625V250H875V125H125Z" />
<glyph unicode="&#xef;" glyph-name="idieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,125V250H375V625H250V750H625V250H875V125H125Z" />
<glyph unicode="&#xf0;" glyph-name="eth" horiz-adv-x="1000" d="M875,500V250H750V125H125V250H0V500H125V625H375V750H125v250H375V875H625V625H750V500H875ZM750,875H625v125H750V875ZM125,750V625H0V750H125ZM625,500H250V250H625V500Z" />
<glyph unicode="&#xf1;" glyph-name="ntilde" horiz-adv-x="1000" d="M0,125V750H125V875H250v125H500V875H625v125H750V875H625V750H750V625H875V125H625V625H250V125H0ZM250,750H375V875H250V750Z" />
<glyph unicode="&#xf2;" glyph-name="ograve" horiz-adv-x="1000" d="M375,1000V875H250v125H375ZM875,625V250H750V125H125V250H0V625H125V750H375V875H500V750H750V625H875Zm-250,0H250V250H625V625Z" />
<glyph unicode="&#xf3;" glyph-name="oacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM875,625V250H750V125H125V250H0V625H125V750H375V875H500V750H750V625H875Zm-250,0H250V250H625V625Z" />
<glyph unicode="&#xf4;" glyph-name="ocircumflex" horiz-adv-x="1000" d="M125,125V250H0V625H125V875H250v125H625V875H750V625H875V250H750V125H125ZM375,750H500V875H375V750ZM250,250H625V625H250V250Z" />
<glyph unicode="&#xf5;" glyph-name="otilde" horiz-adv-x="1000" d="M125,125V250H0V625H125V875H250v125H500V875H625v125H750V875H625V750H750V625H875V250H750V125H125ZM250,750H375V875H250V750Zm0-500H625V625H250V250Z" />
<glyph unicode="&#xf6;" glyph-name="odieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,125V250H0V625H125V750H750V625H875V250H750V125H125ZM250,250H625V625H250V250Z" />
<glyph unicode="&#xf7;" glyph-name="divide" horiz-adv-x="1000" d="M375,750V875H625V750H375ZM125,500V625H875V500H125ZM375,250V375H625V250H375Z" />
<glyph unicode="&#xf8;" glyph-name="oslash" horiz-adv-x="1000" d="M125,125V250H0V625H125V750H750V625H875V250H750V125H125ZM375,250H625V500H500V625H250V375H375V250Zm0,125V500H500V375H375Z" />
<glyph unicode="&#xf9;" glyph-name="ugrave" horiz-adv-x="1000" d="M375,875H250v125H375V875Zm0-125V875H500V750H375Zm500,0V125H125V250H0V750H250V250H625V750H875Z" />
<glyph unicode="&#xfa;" glyph-name="uacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM500,750H375V875H500V750Zm375,0V125H125V250H0V750H250V250H625V750H875Z" />
<glyph unicode="&#xfb;" glyph-name="ucircumflex" horiz-adv-x="1000" d="M125,750V875H250v125H625V875H750V750H500V875H375V750H125Zm0-625V250H0V625H250V250H625V625H875V125H125Z" />
<glyph unicode="&#xfc;" glyph-name="udieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,125V250H0V750H250V250H625V750H875V125H125Z" />
<glyph unicode="&#xfd;" glyph-name="yacute" horiz-adv-x="1000" d="M625,875H500v125H625V875ZM500,750H375V875H500V750Zm375,0V125H750V0H125V125H625V250H125V375H0V750H250V375H625V750H875Z" />
<glyph unicode="&#xfe;" glyph-name="thorn" horiz-adv-x="1000" d="M0,0V1000H250V750H750V625H875V375H750V250H250V0H0ZM250,375H625V625H250V375Z" />
<glyph unicode="&#xff;" glyph-name="ydieresis" horiz-adv-x="1000" d="M125,875v125H375V875H125Zm375,0v125H750V875H500ZM125,0V125H625V250H125V375H0V750H250V375H625V750H875V125H750V0H125Z" />
<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="1000" d="M0,500V625H875V500H0Z" />
<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="1000" d="M0,500V625H1000V500H0Z" />
<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="1000" d="M250,625V875H375v125H625V875H500V625H250Z" />
<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="1000" d="M250,625V750H375v250H625V750H500V625H250Z" />
<glyph unicode="&#x201a;" glyph-name="quotesinglbase" horiz-adv-x="1000" d="M250,125V250H375V500H625V250H500V125H250Z" />
<glyph unicode="&#x201c;" glyph-name="quotedblleft" horiz-adv-x="1000" d="M500,1000V875H375V625H125V875H250v125H500Zm375,0V875H750V625H500V875H625v125H875Z" />
<glyph unicode="&#x201d;" glyph-name="quotedblright" horiz-adv-x="1000" d="M500,1000V750H375V625H125V750H250v250H500Zm375,0V750H750V625H500V750H625v250H875Z" />
<glyph unicode="&#x201e;" glyph-name="quotedblbase" horiz-adv-x="1000" d="M375,500V250H250V125H0V250H125V500H375Zm375,0V250H625V125H375V250H500V500H750Z" />
<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="1000" d="M375,250V375H250V625H375V750H625V625H750V375H625V250H375Z" />
<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="1000" d="M500,250V375H375V500H250V625H375V750H500V875H750V750H625V625H500V500H625V375H750V250H500Z" />
<glyph unicode="&#x203a;" glyph-name="guilsinglright" horiz-adv-x="1000" d="M125,250V375H250V500H375V625H250V750H125V875H375V750H500V625H625V500H500V375H375V250H125Z" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 35 KiB

BIN
static/assets/player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

538
static/game.js Normal file
View File

@@ -0,0 +1,538 @@
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const container = document.getElementById('game-container');
const startScreen = document.getElementById('startScreen');
const startBtn = document.getElementById('startBtn');
const loadingText = document.getElementById('loadingText');
const gameOverScreen = document.getElementById('gameOverScreen');
class PseudoRNG {
constructor(seed) {
this.state = BigInt(seed);
}
nextFloat() {
const a = 1664525n;
const c = 1013904223n;
const m = 4294967296n;
this.state = (this.state * a + c) % m;
return Number(this.state) / Number(m);
}
nextRange(min, max) {
return min + (this.nextFloat() * (max - min));
}
pick(array) {
if (!array || array.length === 0) return null;
const idx = Math.floor(this.nextRange(0, array.length));
return array[idx];
}
}
const GAME_WIDTH = 800;
const GAME_HEIGHT = 400;
canvas.width = GAME_WIDTH;
canvas.height = GAME_HEIGHT;
const GRAVITY = 0.6;
const JUMP_POWER = -12;
const GROUND_Y = 350;
const GAME_SPEED = 5;
const CHUNK_SIZE = 60;
let gameConfig = null;
let isLoaded = false;
let isGameRunning = false;
let isGameOver = false;
let sessionID = null;
let rng = null;
let score = 0;
let currentTick = 0;
let lastSentTick = 0;
let inputLog = [];
let isCrouching = false;
let sprites = {};
let playerSprite = new Image();
let bgSprite = new Image();
let player = {
x: 50, y: 300, w: 30, h: 50, color: "red",
vy: 0, grounded: false
};
let obstacles = [];
function resize() {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const targetRatio = GAME_WIDTH / GAME_HEIGHT;
let finalWidth, finalHeight;
if (windowWidth / windowHeight < targetRatio) {
finalWidth = windowWidth;
finalHeight = windowWidth / targetRatio;
} else {
finalHeight = windowHeight;
finalWidth = finalHeight * targetRatio;
}
canvas.style.width = `${finalWidth}px`;
canvas.style.height = `${finalHeight}px`;
if(container) {
container.style.width = `${finalWidth}px`;
container.style.height = `${finalHeight}px`;
}
}
window.addEventListener('resize', resize);
resize();
async function loadAssets() {
playerSprite.src = "assets/player.png";
if (gameConfig.backgrounds && gameConfig.backgrounds.length > 0) {
const bgName = gameConfig.backgrounds[0];
if (!bgName.startsWith("#")) {
bgSprite.src = "assets/" + bgName;
}
}
const promises = gameConfig.obstacles.map(def => {
return new Promise((resolve) => {
if (!def.image) { resolve(); return; }
const img = new Image();
img.src = "assets/" + def.image;
img.onload = () => { sprites[def.id] = img; resolve(); };
img.onerror = () => { resolve(); };
});
});
if (bgSprite.src) {
promises.push(new Promise(r => {
bgSprite.onload = r;
bgSprite.onerror = r;
}));
}
await Promise.all(promises);
}
window.startGameClick = async function() {
if (!isLoaded) return;
startScreen.style.display = 'none';
try {
const sRes = await fetch('/api/start', {method:'POST'});
const sData = await sRes.json();
sessionID = sData.sessionId;
rng = new PseudoRNG(sData.seed);
isGameRunning = true;
} catch(e) {
location.reload();
}
};
function handleInput(action, active) {
if (isGameOver) { if(active) location.reload(); return; }
const relativeTick = currentTick - lastSentTick;
if (action === "JUMP" && active) {
if (player.grounded && !isCrouching) {
player.vy = JUMP_POWER;
player.grounded = false;
inputLog.push({ t: relativeTick, act: "JUMP" });
}
}
if (action === "DUCK") {
isCrouching = active;
}
}
window.addEventListener('keydown', (e) => {
if (e.code === 'Space' || e.code === 'ArrowUp') handleInput("JUMP", true);
if (e.code === 'ArrowDown' || e.code === 'KeyS') handleInput("DUCK", true);
});
window.addEventListener('keyup', (e) => {
if (e.code === 'ArrowDown' || e.code === 'KeyS') handleInput("DUCK", false);
});
window.addEventListener('mousedown', (e) => {
if (e.target === canvas && e.button === 0) {
handleInput("JUMP", true);
}
});
let touchStartY = 0;
window.addEventListener('touchstart', (e) => {
if(e.target === canvas) {
e.preventDefault();
touchStartY = e.touches[0].clientY;
}
}, { passive: false });
window.addEventListener('touchend', (e) => {
if(e.target === canvas) {
e.preventDefault();
const touchEndY = e.changedTouches[0].clientY;
const diff = touchEndY - touchStartY;
if (diff < -30) {
handleInput("JUMP", true);
} else if (diff > 30) {
handleInput("DUCK", true);
setTimeout(() => handleInput("DUCK", false), 800);
} else if (Math.abs(diff) < 10) {
handleInput("JUMP", true);
}
}
});
async function sendChunk() {
const ticksToSend = currentTick - lastSentTick;
if (ticksToSend <= 0) return;
const payload = {
sessionId: sessionID,
inputs: [...inputLog],
totalTicks: ticksToSend
};
inputLog = [];
lastSentTick = currentTick;
try {
const res = await fetch('/api/validate', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
});
const data = await res.json();
if (data.status === "dead") {
gameOver("Vom Server gestoppt");
} else {
const sScore = data.verifiedScore;
if (Math.abs(score - sScore) > 200) {
score = sScore;
}
}
} catch (e) {
console.error(e);
}
}
function updateGameLogic() {
if (isCrouching) {
const relativeTick = currentTick - lastSentTick;
inputLog.push({ t: relativeTick, act: "DUCK" });
}
const originalHeight = 50;
const crouchHeight = 25;
player.h = isCrouching ? crouchHeight : originalHeight;
let drawY = player.y;
if (isCrouching) {
drawY = player.y + (originalHeight - crouchHeight);
}
player.vy += GRAVITY;
if (isCrouching && !player.grounded) player.vy += 2.0;
player.y += player.vy;
if (player.y + originalHeight >= GROUND_Y) {
player.y = GROUND_Y - originalHeight;
player.vy = 0;
player.grounded = true;
} else {
player.grounded = false;
}
let nextObstacles = [];
let rightmostX = 0;
for (let obs of obstacles) {
obs.x -= GAME_SPEED;
const playerHitbox = {
x: player.x,
y: drawY,
w: player.w,
h: player.h
};
if (checkCollision(playerHitbox, obs)) {
player.color = "darkred";
if (!isGameOver) {
sendChunk();
gameOver("Kollision (Client)");
}
}
if (obs.x + obs.def.width > -100) {
nextObstacles.push(obs);
if (obs.x + obs.def.width > rightmostX) {
rightmostX = obs.x + obs.def.width;
}
}
}
obstacles = nextObstacles;
if (rightmostX < GAME_WIDTH - 10 && gameConfig && gameConfig.obstacles) {
const gap = Math.floor(400 + rng.nextRange(0, 500));
let spawnX = rightmostX + gap;
if (spawnX < GAME_WIDTH) spawnX = GAME_WIDTH;
let possibleObs = [];
gameConfig.obstacles.forEach(def => {
if (def.id === "eraser") {
if (score >= 500) possibleObs.push(def);
} else {
possibleObs.push(def);
}
});
const def = rng.pick(possibleObs);
let speech = null;
if (def && def.canTalk) {
if (rng.nextFloat() > 0.7) {
speech = rng.pick(def.speechLines);
}
}
if (def) {
const yOffset = def.yOffset || 0;
obstacles.push({
x: spawnX,
y: GROUND_Y - def.height - yOffset,
def: def,
speech: speech
});
}
}
}
function checkCollision(p, obs) {
const paddingX = 10;
const paddingY_Top = 25;
const paddingY_Bottom = 5;
return (
p.x + p.w - paddingX > obs.x + paddingX &&
p.x + paddingX < obs.x + obs.def.width - paddingX &&
p.y + p.h - paddingY_Bottom > obs.y + paddingY_Top &&
p.y + paddingY_Top < obs.y + obs.def.height - paddingY_Bottom
);
}
window.submitScore = async function() {
const nameInput = document.getElementById('playerNameInput');
const name = nameInput.value;
const btn = document.getElementById('submitBtn');
if (!name) return alert("Namen eingeben!");
btn.disabled = true;
try {
const res = await fetch('/api/submit-name', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ sessionId: sessionID, name: name })
});
const data = await res.json();
const claimCode = data.claimCode;
let myClaims = JSON.parse(localStorage.getItem('escape_claims') || '[]');
myClaims.push({
name: name,
score: Math.floor(score / 10),
code: claimCode,
date: new Date().toLocaleString('de-DE'),
sessionId: sessionID
});
localStorage.setItem('escape_claims', JSON.stringify(myClaims));
alert(`Gespeichert!\nDein Beweis-Code: ${claimCode}\n(Findest du unter "Meine Codes")`);
document.getElementById('inputSection').style.display = 'none';
loadLeaderboard();
} catch (e) {
console.error(e);
alert("Fehler beim Speichern!");
btn.disabled = false;
}
};
async function loadLeaderboard() {
const res = await fetch(`/api/leaderboard?sessionId=${sessionID}`);
const entries = await res.json();
let html = "<h3>BESTENLISTE</h3>";
entries.forEach(e => {
const color = e.isMe ? "yellow" : "white";
html += `<div style="display:flex; justify-content:space-between; color:${color}; margin-bottom:5px;">
<span>#${e.rank} ${e.name}</span><span>${Math.floor(e.score/10)}</span>
</div>`;
if(e.rank===3 && entries.length>3) html+="<div style='color:gray'>...</div>";
});
document.getElementById('leaderboard').innerHTML = html;
}
function gameOver(reason) {
if (isGameOver) return;
isGameOver = true;
const finalScoreVal = Math.floor(score / 10);
const currentHighscore = localStorage.getItem('escape_highscore') || 0;
if (finalScoreVal > currentHighscore) {
localStorage.setItem('escape_highscore', finalScoreVal);
}
gameOverScreen.style.display = 'flex';
document.getElementById('finalScore').innerText = finalScoreVal;
loadLeaderboard();
drawGame();
}
function drawGame() {
ctx.clearRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
if (bgSprite.complete && bgSprite.naturalHeight !== 0) {
ctx.drawImage(bgSprite, 0, 0, GAME_WIDTH, GAME_HEIGHT);
} else {
const bgColor = (gameConfig && gameConfig.backgrounds) ? gameConfig.backgrounds[0] : "#eee";
if (bgColor.startsWith("#")) ctx.fillStyle = bgColor;
else ctx.fillStyle = "#f0f0f0";
ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
}
ctx.fillStyle = "rgba(60, 60, 60, 0.8)";
ctx.fillRect(0, GROUND_Y, GAME_WIDTH, 50);
obstacles.forEach(obs => {
const img = sprites[obs.def.id];
if (img) {
ctx.drawImage(img, obs.x, obs.y, obs.def.width, obs.def.height);
} else {
ctx.fillStyle = obs.def.color;
ctx.fillRect(obs.x, obs.y, obs.def.width, obs.def.height);
}
if(obs.speech) drawSpeechBubble(obs.x, obs.y, obs.speech);
});
const drawY = isCrouching ? player.y + 25 : player.y;
const drawH = isCrouching ? 25 : 50;
if (playerSprite.complete && playerSprite.naturalHeight !== 0) {
ctx.drawImage(playerSprite, player.x, drawY, player.w, drawH);
} else {
ctx.fillStyle = player.color;
ctx.fillRect(player.x, drawY, player.w, drawH);
}
if (isGameOver) {
ctx.fillStyle = "rgba(0,0,0,0.7)";
ctx.fillRect(0,0,GAME_WIDTH, GAME_HEIGHT);
}
}
function drawSpeechBubble(x, y, text) {
const bX = x-20; const bY = y-40; const bW = 120; const bH = 30;
ctx.fillStyle="white"; ctx.fillRect(bX,bY,bW,bH);
ctx.strokeRect(bX,bY,bW,bH);
ctx.fillStyle="black"; ctx.font="10px Arial"; ctx.textAlign="center";
ctx.fillText(text, bX+bW/2, bY+20);
}
function gameLoop() {
if (!isLoaded) return;
if (isGameRunning && !isGameOver) {
updateGameLogic();
currentTick++;
score++;
const scoreEl = document.getElementById('score');
if (scoreEl) scoreEl.innerText = Math.floor(score / 10);
if (currentTick - lastSentTick >= CHUNK_SIZE) sendChunk();
}
drawGame();
requestAnimationFrame(gameLoop);
}
async function initGame() {
try {
const cRes = await fetch('/api/config');
gameConfig = await cRes.json();
await loadAssets();
await loadStartScreenLeaderboard();
isLoaded = true;
if(loadingText) loadingText.style.display = 'none';
if(startBtn) startBtn.style.display = 'inline-block';
const savedHighscore = localStorage.getItem('escape_highscore') || 0;
const hsEl = document.getElementById('localHighscore');
if(hsEl) hsEl.innerText = savedHighscore;
requestAnimationFrame(gameLoop);
} catch(e) {
if(loadingText) loadingText.innerText = "Fehler!";
}
}
// Lädt die Top-Liste für den Startbildschirm (ohne Session ID)
async function loadStartScreenLeaderboard() {
try {
const listEl = document.getElementById('startLeaderboardList');
if (!listEl) return;
// Anfrage an API (ohne SessionID gibt der Server automatisch die Top 3 zurück)
const res = await fetch('/api/leaderboard');
const entries = await res.json();
if (entries.length === 0) {
listEl.innerHTML = "<div style='text-align:center; padding:20px; color:#666;'>Noch keine Scores.</div>";
return;
}
let html = "";
entries.forEach(e => {
// Medaillen Icons für Top 3
let icon = "#" + e.rank;
if (e.rank === 1) icon = "🥇";
if (e.rank === 2) icon = "🥈";
if (e.rank === 3) icon = "🥉";
html += `
<div class="hof-entry">
<span><span class="hof-rank">${icon}</span> ${e.name}</span>
<span class="hof-score">${Math.floor(e.score / 10)}</span>
</div>`;
});
listEl.innerHTML = html;
} catch (e) {
console.error("Konnte Leaderboard nicht laden", e);
}
}
initGame();

181
static/index.html Normal file
View File

@@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Escape the Teacher</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="rotate-overlay">
<div class="icon">📱↻</div>
<p>Bitte Gerät drehen!</p>
<small>Querformat benötigt</small>
</div>
<div id="game-container">
<canvas id="gameCanvas"></canvas>
<div id="ui-layer">
SCORE: <span id="score">0</span>
</div>
<div id="startScreen">
<div class="start-left">
<h1>ESCAPE THE<br>TEACHER</h1>
<p style="font-size: 12px; color: #aaa;">Dein Rekord: <span id="localHighscore" style="color:yellow">0</span></p>
<button id="startBtn" onclick="startGameClick()">STARTEN</button>
<div id="loadingText">Lade Grafiken...</div>
<div class="info-box">
<div class="info-title">SCHUL-NEWS</div>
<p>
• Herr Müller verteilt heute Nachsitzen!<br>
• Spring über Tische und Mülleimer.<br>
• Lass dich nicht erwischen!
</p>
</div>
<div class="info-box">
<div class="info-title">STEUERUNG</div>
<p>
PC: <strong>Leertaste</strong>, <strong>Pfeil Hoch/Runter</strong> oder <strong>Mausklick</strong><br>
Handy: <strong>Tippen</strong> (Springen) oder <strong>Wischen</strong> (Ducken)
</p>
</div>
<div class="legal-bar">
<button class="legal-btn" onclick="openModal('impressum')">Impressum</button>
<button class="legal-btn" onclick="openModal('datenschutz')">Datenschutz</button>
<button class="legal-btn" onclick="showMyCodes()" style="border-color: yellow; color: yellow;">★ MEINE CODES</button>
</div>
</div>
<div class="start-right">
<div class="hall-of-fame-box">
<h3>🏆 TOP SCHÜLER</h3>
<div id="startLeaderboardList">Lade...</div>
</div>
</div>
</div>
<div id="gameOverScreen" style="display:none;">
<h1>ERWISCHT!</h1>
<p>Dein Score: <span id="finalScore" style="color:yellow; font-size: 24px;">0</span></p>
<div id="inputSection">
<input type="text" id="playerNameInput" placeholder="Dein Name" maxlength="10">
<button id="submitBtn" onclick="submitScore()">EINTRAGEN</button>
</div>
<div id="leaderboard"></div>
<button class="retry-btn" onclick="location.reload()" style="margin-top: 20px; background: #ff4444; color: white;">NOCHMAL SPIELEN</button>
</div>
</div>
<div id="modal-impressum" class="modal-overlay">
<div class="modal-content">
<button class="close-modal" onclick="closeModal()">X</button>
<h2>Impressum</h2>
<p><strong>Angaben gemäß § 5 TMG:</strong></p>
<p>Max Mustermann<br>Musterschule 1<br>12345 Musterstadt</p>
<p>Kontakt: max@schule.de</p>
<p><em>Schulprojekt. Keine kommerzielle Absicht.</em></p>
</div>
</div>
<div id="modal-codes" class="modal-overlay">
<div class="modal-content">
<button class="close-modal" onclick="closeModal()">X</button>
<h2 style="color:yellow">MEINE BEWEISE</h2>
<div id="codesList" style="font-size: 10px; line-height: 1.8;">
</div>
<p style="margin-top:20px; font-size:9px; color:#888;">Zeige diesen Code dem Lehrer für deinen Preis oder zum Löschen.</p>
</div>
</div>
<div id="modal-datenschutz" class="modal-overlay">
<div class="modal-content">
<button class="close-modal" onclick="closeModal()">X</button>
<h2>Datenschutz</h2>
<p>Wir speichern deinen Namen und Score für die Bestenliste.</p>
<p>Dein persönlicher Highscore wird lokal auf deinem Gerät gespeichert.</p>
</div>
</div>
<script src="game.js"></script>
<script>
function openModal(id) { document.getElementById('modal-' + id).style.display = 'flex'; }
function closeModal() { document.querySelectorAll('.modal-overlay').forEach(el => el.style.display = 'none'); }
window.onclick = function(event) {
if (event.target.classList.contains('modal-overlay')) closeModal();
}
async function deleteClaim(index, sid, code) {
if(!confirm("Willst du diesen Score wirklich unwiderruflich löschen?")) return;
try {
const res = await fetch('/api/claim/delete', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ sessionId: sid, claimCode: code })
});
if (!res.ok) {
alert("Fehler: Konnte nicht löschen (Vielleicht Session abgelaufen?)");
} else {
alert("Erfolgreich gelöscht!");
}
let claims = JSON.parse(localStorage.getItem('escape_claims') || '[]');
claims.splice(index, 1);
localStorage.setItem('escape_claims', JSON.stringify(claims));
showMyCodes();
} catch(e) {
alert("Verbindungsfehler");
}
}
function showMyCodes() {
openModal('codes');
const listEl = document.getElementById('codesList');
const claims = JSON.parse(localStorage.getItem('escape_claims') || '[]');
if (claims.length === 0) {
listEl.innerHTML = "Noch keine Scores eingetragen.";
return;
}
let html = "";
// Wir gehen rückwärts durch (neueste oben)
// Wir brauchen den Index 'i', um das Element auch aus dem LocalStorage zu löschen
for (let i = claims.length - 1; i >= 0; i--) {
const c = claims[i];
// Fallback, falls alte Einträge noch keine SessionID haben
const btnDisabled = c.sessionId ? "" : "disabled";
const btnStyle = c.sessionId ? "cursor:pointer; color:red;" : "color:gray;";
html += `
<div style="border-bottom:1px solid #444; padding:8px 0; display:flex; justify-content:space-between; align-items:center;">
<div>
<span style="color:white; font-weight:bold;">${c.code}</span>
<span style="color:#ffcc00;">(${c.score} Pkt)</span><br>
<span style="color:#aaa;">${c.name} - ${c.date}</span>
</div>
<button onclick="deleteClaim(${i}, '${c.sessionId}', '${c.code}')"
style="background:transparent; border:1px solid #555; padding:5px; font-size:10px; ${btnStyle}"
${btnDisabled}>
LÖSCHEN
</button>
</div>`;
}
listEl.innerHTML = html;
}
</script>
</body>
</html>

335
static/style.css Normal file
View File

@@ -0,0 +1,335 @@
/* =========================================
0. LOKALE SCHRIFTARTEN (DSGVO Konform)
========================================= */
/* press-start-2p-regular - latin */
@font-face {
font-display: swap; /* Zeigt Text sofort an (Fallback), bis Schrift geladen ist */
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
src: url('assets/fonts/press-start-2p-v16-latin-regular.woff2') format('woff2'),
url('assets/fonts/press-start-2p-v16-latin-regular.woff') format('woff');
}
/* =========================================
1. GRUNDLAGEN & GLOBAL
========================================= */
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: #1a1a1a;
color: white;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Press Start 2P', cursive;
font-size: 14px;
}
#game-container {
position: relative;
box-shadow: 0 0 50px rgba(0,0,0,0.8);
border: 4px solid #444;
background: #000;
max-width: 100%;
max-height: 100%;
}
canvas {
display: block;
background-color: #f0f0f0;
image-rendering: pixelated;
image-rendering: crisp-edges;
width: 100%;
height: auto;
}
/* =========================================
2. UI & SCORE
========================================= */
#ui-layer {
position: absolute;
top: 25px;
right: 25px;
color: #333;
font-size: 24px;
font-weight: bold;
pointer-events: none;
text-shadow: 2px 2px 0px rgba(255,255,255,0.8);
z-index: 5;
}
/* =========================================
3. OVERLAYS (Start, Game Over)
========================================= */
#startScreen, #gameOverScreen {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
box-sizing: border-box;
padding: 20px;
}
h1 {
font-size: 32px;
color: #ff4444;
text-shadow: 4px 4px 0px #000;
line-height: 1.4;
margin: 10px 0 25px 0;
text-align: center;
text-transform: uppercase;
}
/* --- FIX: INPUT SECTION ZENTRIEREN --- */
#inputSection {
display: flex;
flex-direction: column; /* Untereinander */
align-items: center; /* Horizontal mittig */
justify-content: center;
width: 100%;
margin: 15px 0;
}
/* =========================================
4. START SCREEN LAYOUT
========================================= */
#startScreen {
flex-direction: row;
gap: 40px;
}
.start-left {
flex: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 60%;
}
.start-right {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 35%;
}
.hall-of-fame-box {
background: rgba(0, 0, 0, 0.6);
border: 4px solid #ffcc00;
padding: 15px;
width: 100%;
max-height: 300px;
overflow-y: auto;
box-shadow: 0 0 15px rgba(255, 204, 0, 0.1);
}
.hall-of-fame-box h3 {
color: #ffcc00;
text-align: center;
margin-top: 0;
margin-bottom: 10px;
border-bottom: 2px solid #555;
padding-bottom: 8px;
font-size: 14px;
}
.hof-entry {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px dotted #444;
font-size: 11px;
font-family: sans-serif;
color: #ddd;
line-height: 1.4;
}
.hof-rank { color: #ffcc00; font-weight: bold; margin-right: 8px; }
.hof-score { color: white; font-weight: bold; }
/* =========================================
5. BUTTONS & INPUTS
========================================= */
button {
font-family: 'Press Start 2P', cursive;
background: #ffcc00;
border: 4px solid #fff;
padding: 18px 30px;
font-size: 18px;
cursor: pointer;
color: #000;
box-shadow: 0 6px 0 #997a00;
text-transform: uppercase;
margin: 12px;
}
button:active { transform: translateY(4px); box-shadow: 0 1px 0 #997a00; }
button:disabled { background: #555; color: #888; box-shadow: none; }
input {
font-family: 'Press Start 2P', cursive;
padding: 12px;
font-size: 16px;
border: 3px solid white;
background: #222;
color: white;
text-align: center;
margin-bottom: 15px;
width: 250px;
outline: none;
}
/* =========================================
6. INFO BOXEN
========================================= */
.info-box {
background: rgba(255, 255, 255, 0.1);
border: 2px solid #555;
padding: 12px;
margin: 8px 0;
width: 100%;
max-width: 320px;
text-align: left;
box-sizing: border-box;
}
.info-box p {
font-family: sans-serif;
font-size: 14px;
color: #ccc;
line-height: 1.4;
margin: 0;
}
.info-title {
color: #ffcc00;
font-size: 12px;
margin-bottom: 6px;
text-align: center;
text-decoration: underline;
}
/* Game Over Screen Anpassung */
#gameOverScreen { flex-direction: column; }
#leaderboard {
margin-top: 20px;
font-size: 12px;
width: 90%;
max-width: 450px;
background: rgba(0,0,0,0.5);
padding: 15px;
border: 2px solid #666;
}
/* =========================================
7. RECHTLICHES
========================================= */
.legal-bar {
margin-top: 20px;
display: flex;
gap: 15px;
}
.legal-btn {
font-size: 10px;
padding: 8px 12px;
margin: 0;
}
.modal-overlay {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.95);
z-index: 999;
align-items: center;
justify-content: center;
}
.modal-content {
background: #222;
border: 4px solid #fff;
padding: 30px;
width: 80%;
max-width: 600px;
max-height: 80%;
overflow-y: auto;
color: #ccc;
font-family: sans-serif;
text-align: left;
position: relative;
font-size: 16px;
line-height: 1.6;
}
.modal-content h2 { color: #ffcc00; font-family: 'Press Start 2P'; margin-top: 0; font-size: 20px; }
.close-modal {
position: absolute;
top: 10px; right: 10px;
background: #ff4444; border: 2px solid white;
width: 35px; height: 35px;
font-size: 16px;
line-height: 30px;
}
/* =========================================
8. PC / DESKTOP SPEZIAL
========================================= */
@media (min-width: 1024px) {
h1 { font-size: 48px; margin-bottom: 40px; }
button { font-size: 22px; padding: 20px 40px; }
input { width: 350px; font-size: 20px; padding: 15px; }
.info-box { max-width: 500px; }
.info-box p { font-size: 16px; }
.info-title { font-size: 14px; }
.hall-of-fame-box { max-height: 400px; }
.hof-entry { font-size: 14px; padding: 8px 0; }
.hall-of-fame-box h3 { font-size: 18px; margin-bottom: 15px; }
}
/* =========================================
9. MOBILE ANPASSUNG
========================================= */
@media (max-width: 700px) {
#startScreen {
flex-direction: column;
gap: 15px;
justify-content: flex-start;
overflow-y: auto;
}
.start-left, .start-right { max-width: 100%; width: 100%; }
.start-right { height: auto; min-height: 200px; margin-top: 20px; }
.hall-of-fame-box { max-height: 200px; }
h1 { font-size: 24px; margin: 15px 0; }
button { padding: 12px 20px; font-size: 14px; }
input { width: 200px; }
}
/* =========================================
10. ROTATE OVERLAY
========================================= */
#rotate-overlay {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: #222;
z-index: 9999;
color: white;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.icon { font-size: 60px; margin-bottom: 20px; }
@media screen and (orientation: portrait) {
#rotate-overlay { display: flex; }
#game-container { display: none !important; }
}