<q:application id="hopper" type="game"> <!-- Hopper: the platformer that drives the game language (PLAN_GAMES_2.md). A title (a new game, or the saved one), a world map and three levels. A character that walks and jumps on a tilemap, blocks with power-ups in them, coins, walkers to stomp or be hurt by, pits, checkpoints, spikes, a flag at the end of each level, a game over. Art and sounds: Kenney's Pixel Platformer (CC0), in assets/kenney/. --> <!-- The game's state: kept across scenes. --> <q:set name="lives" value="3" type="number" /> <q:set name="score" value="0" type="number" /> <q:set name="cleared" value="[]" type="array" /> <q:set name="map_at" value="level-1" /> <!-- the checkpoint reached: its level and where the character starts again there --> <q:set name="check_at" value="" /> <q:set name="check_x" value="0" type="number" /> <q:set name="check_y" value="0" type="number" /> <!-- Jump on space, Z, X, up or W; the other actions keep their default keys. --> <qg:input action="jump" keys="Space, Z, X, Up, W" /> <!-- level 4's tiles of a kind (qg:tile kind=): a ladder, water, snow slid on as ice, spikes that hurt --> <qg:tileset name="kenney" src="assets/kenney/tilemap_packed.png" tile="18"> <qg:tile frame="71" kind="ladder" /> <qg:tile frame="33" kind="water" /> <qg:tile frame="74" kind="water" /> <qg:tile frame="81" kind="ice" /> <qg:tile frame="68" kind="hurt" shape="0,10; 18,10; 18,18; 0,18" /> </qg:tileset> <qg:spritesheet name="chars" src="assets/kenney/tilemap-characters_packed.png" tile="24" /> <qg:sound bus="effects" name="jump" src="assets/kenney/audio/jump.ogg" /> <qg:sound bus="effects" name="coin" src="assets/kenney/audio/coin.ogg" /> <qg:sound bus="effects" name="stomp" src="assets/kenney/audio/stomp.ogg" /> <qg:sound bus="effects" name="hurt" src="assets/kenney/audio/hurt.ogg" /> <qg:sound bus="effects" name="block" src="assets/kenney/audio/block.ogg" /> <qg:sound bus="effects" name="powerup" src="assets/kenney/audio/powerup.ogg" /> <qg:sound bus="effects" name="win" src="assets/kenney/audio/win.ogg" /> <!-- the bouncer's hop: heard from where it is, to one side, softer the farther off --> <qg:sound bus="effects" name="hop" src="assets/kenney/audio/jump.ogg" /> <!-- effects: only seen, the game's state never depends on them --> <qg:particles name="sparkle" preset="stars" color="#ffe060" /> <qg:particles name="pop" preset="puff" color="#f0e0c0" /> <qg:particles name="power" preset="sparks" color="#ff7070" /> <qg:prefab name="Coin" tag="coin" sheet="kenney" frame="151" hitbox="12x12" /> <qg:prefab name="QBlock" tag="qblock" sheet="kenney" frame="10" hitbox="18x18" solid="true" /> <qg:prefab name="UsedBlock" tag="usedblock" sheet="kenney" frame="11" hitbox="18x18" solid="true" /> <qg:prefab name="Shroom" tag="shroom" sheet="kenney" frame="128" hitbox="12x12" ai="patrol" speed="40" direction="right" turns-at="wall" /> <qg:prefab name="Spikes" tag="spikes" sheet="kenney" frame="68" hitbox="18x8" /> <qg:prefab name="Checkpoint" tag="checkpoint" sheet="kenney" frame="111" hitbox="18x18" /> <qg:prefab name="CheckpointOn" tag="checkpoint-on" sheet="kenney" frame="112" hitbox="18x18" /> <qg:prefab name="Flag" tag="flag" sheet="kenney" frame="153" hitbox="18x18" /> <!-- A ledge to jump through from below and stand on; a lift that rises and comes back. --> <qg:prefab name="Ledge" tag="ledge" sheet="kenney" frame="23" hitbox="18x6" solid="true" one-way="true" /> <qg:prefab name="Lift" tag="lift" sheet="kenney" frame="43" hitbox="18x8" solid="true" ai="shuttle" dy="-80" period="240" /> <!-- an enemy made of parts: it hops to and fro on the snow, and over what it bumps; each hop is heard from where it is, so it is heard before it is seen --> <qg:prefab name="Bouncer" tag="enemy" sheet="chars" frame="11" hitbox="18x18" ai="behavior" speed="36" turns-at="edge"> <qg:animation name="walk" frames="11,12" fps="4" /> <qg:behavior> <qg:gravity /> <qg:leap height="20" every="45" when="{true}" sound="hop" /> <qg:patrol heading="left" distance="18" /> </qg:behavior> </qg:prefab> <qg:prefab name="Walker" tag="enemy" sheet="chars" frame="18" hitbox="18x18" ai="patrol" speed="30" direction="left" turns-at="edge"> <qg:animation name="walk" frames="18,19,20" fps="6" /> </qg:prefab> <!-- the game in Portuguese and Spanish (a web page's ?lang=pt, the command line's q-lang, or the system's language) --> <!-- on a phone: what the keys do, under a finger, in every scene --> <qg:touch-stick x="40" y="184" /> <qg:touch-button action="jump" label="A" x="220" y="184" /> <qg:language lang="pt" name="Português"> <qg:phrase text="SCORE" as="PONTOS" /> <qg:phrase text="LIVES" as="VIDAS" /> <qg:phrase text="COINS" as="MOEDAS" /> <qg:phrase text="TIME" as="TEMPO" /> <qg:phrase text="YOU WIN" as="VOCÊ VENCEU" /> <qg:phrase text="GAME OVER - PRESS JUMP" as="FIM DE JOGO - APERTE PULAR" /> <qg:phrase text="New game" as="Novo jogo" /> <qg:phrase text="Continue" as="Continuar" /> <qg:phrase text="Language" as="Idioma" /> <qg:phrase text="Options" as="Opções" /> <qg:phrase text="OPTIONS" as="OPÇÕES" /> <qg:phrase text="Sounds" as="Sons" /> <qg:phrase text="Full screen" as="Tela cheia" /> <qg:phrase text="on" as="sim" /> <qg:phrase text="off" as="não" /> <qg:phrase text="Back" as="Voltar" /> <qg:phrase text="Jump" as="Pular" /></qg:language> <qg:language lang="es" name="Español"> <qg:phrase text="SCORE" as="PUNTOS" /> <qg:phrase text="LIVES" as="VIDAS" /> <qg:phrase text="COINS" as="MONEDAS" /> <qg:phrase text="TIME" as="TIEMPO" /> <qg:phrase text="YOU WIN" as="HAS GANADO" /> <qg:phrase text="GAME OVER - PRESS JUMP" as="FIN DEL JUEGO - PULSA SALTAR" /> <qg:phrase text="New game" as="Nueva partida" /> <qg:phrase text="Continue" as="Continuar" /> <qg:phrase text="Language" as="Idioma" /> <qg:phrase text="Options" as="Opciones" /> <qg:phrase text="OPTIONS" as="OPCIONES" /> <qg:phrase text="Sounds" as="Sonidos" /> <qg:phrase text="Full screen" as="Pantalla completa" /> <qg:phrase text="on" as="sí" /> <qg:phrase text="off" as="no" /> <qg:phrase text="Back" as="Volver" /> <qg:phrase text="Jump" as="Saltar" /></qg:language> <!-- ===== The title: a new game, or the saved one (each level cleared saves it) ===== --> <qg:scene name="title" width="256" height="224" background="#3b7d4f"> <qg:hud position="top-center" size="32"><qg:text value="{'HOPPER'}" /></qg:hud> <qg:menu position="center"> <qg:button label="New game"><qg:goto-scene name="map" /></qg:button> <qg:button label="Continue" if="{has_save()}"><qg:continue /></qg:button> <qg:button label="Options"><qg:goto-scene name="options" /></qg:button> <qg:button label="{tr('Language') + ': ' + language()}"><qg:next-language /></qg:button> </qg:menu> </qg:scene> <!-- ===== The world map ===== --> <qg:scene name="map" width="256" height="224" background="#3b7d4f"> <qg:map-node name="level-1" x="48" y="112" sheet="kenney" frame="153" scene="level-1" /> <qg:map-node name="level-2" x="128" y="80" sheet="kenney" frame="153" scene="level-2" /> <qg:map-node name="level-3" x="208" y="112" sheet="kenney" frame="153" scene="level-3" /> <qg:map-node name="level-4" x="128" y="168" sheet="kenney" frame="153" scene="level-4" /> <qg:map-path from="level-1" to="level-2" requires="level-1" /> <qg:map-path from="level-2" to="level-3" requires="level-2" /> <qg:map-path from="level-3" to="level-4" requires="level-3" /> <qg:character id="player" controller="map" sheet="chars" frame="0" at="{map_at}" speed="60" x="0" y="0" hitbox="18x22"> <qg:animation name="idle" frames="0" /> <qg:animation name="walk" frames="1,2" fps="8" /> </qg:character> <qg:hud position="top-left"> <qg:counter bind="score" label="SCORE" /> <qg:counter bind="lives" label="LIVES" /> </qg:hud> </qg:scene> <!-- ===== Level 1 ===== --> <qg:scene name="level-1" width="256" height="224" background="#5c94fc" seed="7"> <q:if condition="{check_at == 'level-1'}"> <qg:put target="player" x="{check_x}" y="{check_y}" /> </q:if> <q:set name="coins" value="0" type="number" /> <q:set name="message" value="" /> <q:set name="time" value="99" type="number" /> <!-- The clock: a second off every 60 ticks; at zero the level is lost. --> <qg:timer every="60"> <q:set name="time" value="{time - 1}" /> <q:if condition="{time <= 0}"> <q:set name="lives" value="{lives - 1}" /> <q:set name="check_at" value="" /> <qg:goto-scene name="map" /> </q:if> </qg:timer> <q:function name="die" params="me"> <qg:play sound="hurt" /> <q:set name="lives" value="{lives - 1}" /> <q:if condition="{lives <= 0}"> <qg:goto-scene name="game-over" /> <q:else> <qg:goto-scene name="level-1" /> <!-- the level again, enemies and all: from the start or its checkpoint --> </q:else> </q:if> </q:function> <!-- 24 columns x 13 rows of 18 px = 432 x 234. 0 is empty; 23 is the grass top, 25 the dirt below it. A platform at row 6, a pit at columns 13-14. --> <qg:tilemap tileset="kenney" collision="true">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,023,23,23,23,23,23,23,23,23,23,23,23,23,0,0,23,23,23,23,23,23,23,23,2325,25,25,25,25,25,25,25,25,25,25,25,25,0,0,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,25,0,0,25,25,25,25,25,25,25,25,25 </qg:tilemap> <qg:character id="player" controller="platformer" sheet="chars" frame="0" x="40" y="160" hitbox="18x22" run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6" jump-sound="jump"> <qg:state name="small" hitbox="18x22" frame="0" initial="true"> <qg:animation name="idle" frames="0" /> <qg:animation name="walk" frames="1,2" fps="8" /> <qg:animation name="jump" frames="1" /> </qg:state> <qg:state name="big" hitbox="18x22" frame="3"> <qg:animation name="idle" frames="3" /> <qg:animation name="walk" frames="4,5" fps="8" /> <qg:animation name="jump" frames="4" /> </qg:state> <qg:on-collision with="coin"> <qg:emit particles="sparkle" at="other" /> <qg:destroy target="other" /> <qg:play sound="coin" /> <q:set name="coins" value="{coins + 1}" /> <q:set name="score" value="{score + 10}" /> </qg:on-collision> <qg:on-collision with="qblock" side="bottom"> <qg:swap target="other" prefab="UsedBlock" /> <qg:spawn prefab="Shroom" at="other" dy="-18" /> <qg:play sound="block" /> </qg:on-collision> <qg:on-collision with="shroom"> <qg:destroy target="other" /> <qg:play sound="powerup" /> <qg:emit particles="power" at="me" /> <qg:become target="me" state="big" /> <q:set name="score" value="{score + 50}" /> </qg:on-collision> <qg:on-collision with="enemy" side="top"> <qg:emit particles="pop" at="other" /> <qg:destroy target="other" /> <qg:bounce target="me" height="32" /> <qg:play sound="stomp" /> <q:set name="score" value="{score + 100}" /> </qg:on-collision> <qg:on-collision with="enemy" side="any" cooldown="60"> <q:if condition="{me.state == 'big'}"> <qg:play sound="hurt" /> <qg:become target="me" state="small" /> <q:else> <q:call function="die" args="me" /> </q:else> </q:if> </qg:on-collision> <qg:on-collision with="spikes" cooldown="60"> <q:call function="die" args="me" /> </qg:on-collision> <qg:on-collision with="checkpoint"> <qg:checkpoint target="me" at="other" /> <qg:swap target="other" prefab="CheckpointOn" /> <q:set name="check_at" value="level-1" /> <q:set name="check_x" value="297" /> <q:set name="check_y" value="169" /> </qg:on-collision> <qg:on-collision with="flag" cooldown="100000"> <qg:play sound="win" /> <q:set name="score" value="{score + 500}" /> <q:set name="message" value="YOU WIN" /> <q:set name="cleared" value="{cleared + ['level-1']}" /> <q:set name="check_at" value="" /> <qg:save scene="map" /> <qg:goto-scene name="map" /> </qg:on-collision> <qg:on-fall> <q:call function="die" args="me" /> </qg:on-fall> </qg:character> <qg:instance prefab="QBlock" x="45" y="135" /> <qg:instance prefab="Coin" x="120" y="160" /> <qg:instance prefab="Coin" x="171" y="96" /> <qg:instance prefab="Walker" x="200" y="160" /> <qg:instance prefab="Checkpoint" x="297" y="171" if="{check_at != 'level-1'}" /> <qg:instance prefab="CheckpointOn" x="297" y="171" if="{check_at == 'level-1'}" /> <qg:instance prefab="Spikes" x="351" y="176" /> <qg:instance prefab="Flag" x="405" y="171" /> <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" /> <qg:hud position="top-left"> <qg:counter bind="score" label="SCORE" /> <qg:counter bind="coins" label="COINS" /> <qg:counter bind="lives" label="LIVES" /> <qg:counter bind="time" label="TIME" /> <qg:text bind="message" /> </qg:hud> </qg:scene> <!-- ===== Level 2: platforms and two walkers ===== --> <qg:scene name="level-2" width="256" height="224" background="#4a7cc9" seed="11"> <q:if condition="{check_at == 'level-2'}"> <qg:put target="player" x="{check_x}" y="{check_y}" /> </q:if> <q:set name="coins" value="0" type="number" /> <q:function name="die" params="me"> <qg:play sound="hurt" /> <q:set name="lives" value="{lives - 1}" /> <q:if condition="{lives <= 0}"> <qg:goto-scene name="game-over" /> <q:else> <qg:goto-scene name="level-2" /> <!-- the level again, enemies and all: from the start or its checkpoint --> </q:else> </q:if> </q:function> <!-- The level is drawn in Tiled (levels/level-2.tmx): a decor layer, a ground layer with a collision property, and an object layer whose points place the coins, the walkers and the flag by their class. --> <qg:tilemap tileset="kenney" src="levels/level-2.tmx" /> <qg:character id="player" controller="platformer" sheet="chars" frame="0" x="40" y="160" hitbox="18x22" run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6" jump-sound="jump"> <qg:animation name="idle" frames="0" /> <qg:animation name="walk" frames="1,2" fps="8" /> <qg:animation name="jump" frames="1" /> <qg:on-collision with="coin"> <qg:emit particles="sparkle" at="other" /> <qg:destroy target="other" /> <qg:play sound="coin" /> <q:set name="coins" value="{coins + 1}" /> <q:set name="score" value="{score + 10}" /> </qg:on-collision> <qg:on-collision with="enemy" side="top"> <qg:emit particles="pop" at="other" /> <qg:destroy target="other" /> <qg:bounce target="me" height="32" /> <qg:play sound="stomp" /> <q:set name="score" value="{score + 100}" /> </qg:on-collision> <qg:on-collision with="enemy" side="any" cooldown="60"> <q:call function="die" args="me" /> </qg:on-collision> <qg:on-collision with="flag" cooldown="100000"> <qg:play sound="win" /> <q:set name="score" value="{score + 500}" /> <q:set name="cleared" value="{cleared + ['level-2']}" /> <q:set name="check_at" value="" /> <qg:save scene="map" /> <qg:goto-scene name="map" /> </qg:on-collision> <qg:on-fall> <q:call function="die" args="me" /> </qg:on-fall> </qg:character> <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" /> <qg:hud position="top-left"> <qg:counter bind="score" label="SCORE" /> <qg:counter bind="coins" label="COINS" /> <qg:counter bind="lives" label="LIVES" /> </qg:hud> </qg:scene> <!-- ===== Level 3: the spikes gauntlet ===== --> <qg:scene name="level-3" width="256" height="224" background="#7a4fa8" seed="13"> <q:if condition="{check_at == 'level-3'}"> <qg:put target="player" x="{check_x}" y="{check_y}" /> </q:if> <q:set name="coins" value="0" type="number" /> <q:function name="die" params="me"> <qg:play sound="hurt" /> <q:set name="lives" value="{lives - 1}" /> <q:if condition="{lives <= 0}"> <qg:goto-scene name="game-over" /> <q:else> <qg:goto-scene name="level-3" /> <!-- the level again, enemies and all: from the start or its checkpoint --> </q:else> </q:if> </q:function> <qg:tilemap tileset="kenney" collision="true">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,23,23,0,0,0,0,23,23,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,023,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,2325,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 </qg:tilemap> <qg:character id="player" controller="platformer" sheet="chars" frame="0" x="40" y="160" hitbox="18x22" run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6" jump-sound="jump"> <qg:animation name="idle" frames="0" /> <qg:animation name="walk" frames="1,2" fps="8" /> <qg:animation name="jump" frames="1" /> <qg:on-collision with="coin"> <qg:emit particles="sparkle" at="other" /> <qg:destroy target="other" /> <qg:play sound="coin" /> <q:set name="coins" value="{coins + 1}" /> <q:set name="score" value="{score + 10}" /> </qg:on-collision> <qg:on-collision with="spikes" cooldown="60"> <q:call function="die" args="me" /> </qg:on-collision> <qg:on-collision with="checkpoint"> <qg:checkpoint target="me" at="other" /> <qg:swap target="other" prefab="CheckpointOn" /> <q:set name="check_at" value="level-3" /> <q:set name="check_x" value="207" /> <q:set name="check_y" value="169" /> </qg:on-collision> <qg:on-collision with="flag" cooldown="100000"> <qg:play sound="win" /> <q:set name="score" value="{score + 1000}" /> <q:set name="cleared" value="{cleared + ['level-3']}" /> <q:set name="check_at" value="" /> <qg:save scene="map" /> <qg:goto-scene name="map" /> </qg:on-collision> <qg:on-fall> <q:call function="die" args="me" /> </qg:on-fall> </qg:character> <qg:instance prefab="Spikes" x="135" y="176" /> <qg:instance prefab="Spikes" x="153" y="176" /> <qg:instance prefab="Coin" x="144" y="96" /> <qg:instance prefab="Checkpoint" x="207" y="171" if="{check_at != 'level-3'}" /> <qg:instance prefab="CheckpointOn" x="207" y="171" if="{check_at == 'level-3'}" /> <qg:instance prefab="Spikes" x="243" y="176" /> <qg:instance prefab="Spikes" x="261" y="176" /> <qg:instance prefab="Spikes" x="279" y="176" /> <qg:instance prefab="Coin" x="252" y="96" /> <qg:instance prefab="Flag" x="405" y="171" /> <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" /> <qg:hud position="top-left"> <qg:counter bind="score" label="SCORE" /> <qg:counter bind="coins" label="COINS" /> <qg:counter bind="lives" label="LIVES" /> </qg:hud> </qg:scene> <!-- ===== Level 4: the falls. Water to swim, snow that slides, a ladder up a wall, spikes below it. ===== --> <qg:scene name="level-4" width="256" height="224" background="#4f8fb0" seed="17"> <q:set name="coins" value="0" type="number" /> <q:function name="die" params="me"> <qg:play sound="hurt" /> <q:set name="lives" value="{lives - 1}" /> <q:if condition="{lives <= 0}"> <qg:goto-scene name="game-over" /> <q:else> <qg:goto-scene name="level-4" /> </q:else> </q:if> </q:function> <!-- 23 grass, 25 dirt; 34 and 75 water (a pool two tiles deep), 82 snow (ice), 72 the ladder up the wall at columns 15-16, 69 the spikes beyond it. --> <qg:tilemap tileset="kenney" collision="true">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,23,23,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,023,23,23,23,23,23,34,34,34,34,82,82,82,82,23,25,25,69,69,69,69,23,23,2325,25,25,25,25,25,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 </qg:tilemap> <qg:character id="player" controller="platformer" sheet="chars" frame="0" x="40" y="160" hitbox="18x22" run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6" jump-sound="jump"> <qg:animation name="idle" frames="0" /> <qg:animation name="walk" frames="1,2" fps="8" /> <qg:animation name="jump" frames="1" /> <qg:on-collision with="coin"> <qg:emit particles="sparkle" at="other" /> <qg:destroy target="other" /> <qg:play sound="coin" /> <q:set name="coins" value="{coins + 1}" /> <q:set name="score" value="{score + 10}" /> </qg:on-collision> <qg:on-collision with="enemy" side="top"> <qg:emit particles="pop" at="other" /> <qg:destroy target="other" /> <qg:bounce target="me" height="32" /> <qg:play sound="stomp" /> <q:set name="score" value="{score + 100}" /> </qg:on-collision> <qg:on-collision with="enemy" side="any" cooldown="60"> <q:call function="die" args="me" /> </qg:on-collision> <!-- the spikes are tiles of a kind, not things: touching one is qg:on-tile --> <qg:on-tile kind="hurt" cooldown="60"> <q:call function="die" args="me" /> </qg:on-tile> <qg:on-collision with="flag" cooldown="100000"> <qg:play sound="win" /> <q:set name="score" value="{score + 1500}" /> <q:set name="cleared" value="{cleared + ['level-4']}" /> <q:set name="check_at" value="" /> <qg:save scene="map" /> <qg:goto-scene name="map" /> </qg:on-collision> <qg:on-fall> <q:call function="die" args="me" /> </qg:on-fall> </qg:character> <qg:instance prefab="Coin" x="144" y="200" /> <qg:instance prefab="Bouncer" x="225" y="171" /> <qg:instance prefab="Coin" x="279" y="54" /> <qg:instance prefab="Flag" x="405" y="171" /> <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" /> <qg:hud position="top-left"> <qg:counter bind="score" label="SCORE" /> <qg:counter bind="coins" label="COINS" /> <qg:counter bind="lives" label="LIVES" /> </qg:hud> </qg:scene> <!-- ===== Game over: jump to start again ===== --> <qg:scene name="game-over" width="256" height="224" background="#000000"> <q:set name="message" value="GAME OVER - PRESS JUMP" /> <qg:on-input action="jump"> <q:set name="lives" value="3" /> <q:set name="score" value="0" /> <q:set name="cleared" value="[]" /> <q:set name="map_at" value="level-1" /> <q:set name="check_at" value="" /> <qg:goto-scene name="map" /> </qg:on-input> <qg:hud position="top-center"> <qg:text bind="message" /> </qg:hud> </qg:scene> <!-- ===== Options: how loud, the full screen, the keys; kept between runs ===== --> <qg:scene name="options" width="256" height="224" background="#3b7d4f"> <qg:hud position="top-center" size="24"><qg:text value="{tr('OPTIONS')}" /></qg:hud> <qg:menu position="center" size="12"> <qg:slider label="Sounds" bus="effects" /> <qg:button label="{tr('Full screen') + ': ' + (tr('on') if fullscreen() else tr('off'))}"><qg:fullscreen /></qg:button> <qg:rebind label="Jump" action="jump" /> <qg:button label="Back"><qg:goto-scene name="title" /></qg:button> </qg:menu> </qg:scene></q:application>
<?xml version="1.0" encoding="UTF-8"?><map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="24" height="13" tilewidth="18" tileheight="18" infinite="0" nextlayerid="4" nextobjectid="9"> <tileset firstgid="1" name="kenney" tilewidth="18" tileheight="18" tilecount="180" columns="20"> <image source="../../../assets/kenney/tilemap_packed.png" width="360" height="162"/> </tileset> <layer id="1" name="decor" width="24" height="13"> <data encoding="csv">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,11,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</data> </layer> <layer id="2" name="ground" width="24" height="13"> <properties> <property name="collision" type="bool" value="true"/> </properties> <data encoding="csv">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,023,23,23,23,23,23,23,23,23,23,23,23,0,0,0,23,23,23,23,23,23,23,23,2325,25,25,25,25,25,25,25,25,25,25,25,0,0,0,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,0,0,0,25,25,25,25,25,25,25,25,25</data> </layer> <objectgroup id="3" name="things"> <object id="1" name="coin-1" class="Coin" x="171" y="96"> <point/> </object> <object id="2" name="coin-2" class="Coin" x="261" y="78"> <point/> </object> <object id="3" name="coin-3" class="Coin" x="351" y="42"> <point/> </object> <object id="4" name="walker-1" class="Walker" x="150" y="160"> <point/> </object> <object id="5" name="walker-2" class="Walker" x="380" y="160"> <point/> </object> <object id="6" name="flag" class="Flag" x="405" y="171"> <point/> </object> <object id="7" name="ledge" class="Ledge" x="99" y="144"> <point/> </object> <object id="8" name="lift" class="Lift" x="129" y="184"> <point/> </object> </objectgroup></map>