← All the games

RPG

All of RPG is here, in Quantum's game language: rpg.q, compiled for Godot 4.4.1, with no script besides. Point at a tag or an attribute to see what it does; a tag opens its entry in the language's reference.

Play in the browser
The 37 tags it uses, of the language's 145 qg:animation qg:asset qg:bar qg:button qg:character qg:destroy qg:dialogue qg:fullscreen qg:goto-scene qg:hud qg:input qg:instance qg:language qg:line qg:menu qg:music qg:on-collision qg:on-end qg:on-input qg:pause qg:phrase qg:prefab qg:put qg:resume qg:say qg:scene qg:shake qg:slider qg:sound qg:sprite qg:spritesheet qg:text qg:tilemap qg:tileset qg:timer qg:touch-stick qg:tween
rpg.q354 lines Download On GitHub
<q:application id="rpg" type="game">  <!-- RPG: Godot's "JRPG" demo (godotengine/godot-demo-projects, 2d/role_playing_game,       MIT), transcribed. A slime walks a grassy map cell by cell, reads a key,       talks to another slime and fights it, turn by turn: Attack, Defend, Flee.       The art and the font are the demo's (assets/LICENSE.md), packed by       scripts/art/rpg.py; README.md says what is where. -->  <!-- How the last fight ended ('' before any), and the cell it was started from. -->  <q:set name="outcome" value="" />  <q:set name="back_col" value="3" type="number" />  <q:set name="back_row" value="4" type="number" />  <!-- Beyond the demo: what the player carries, and what it took from the map, by name. -->  <q:set name="keys" value="0" type="number" />  <q:set name="potions" value="0" type="number" />  <q:set name="taken" value="[]" type="array" />  <!-- The demo's ui_accept: Enter, Space, the joypad's A; and a click. -->  <qg:input action="select" keys="Enter, Space, MouseLeft, JoyA" />  <!-- Every tile of the three layers is a picture of its own; the Grid       layer's are the ones that stop a walker. -->  <!-- beyond the demo: 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="140" y="580" size="200" />  <qg:language lang="pt" name="Português">    <qg:phrase text="Hey, it's a good time to have a JRPG fight, right?" as="Ei, é uma boa hora para uma luta de JRPG, não acha?" />    <qg:phrase text="Let me introduce myself, I'm your OPPONENT." as="Deixa eu me apresentar: sou o seu OPONENTE." />    <qg:phrase text="Enough talking. Let's fight!" as="Chega de conversa. Vamos lutar!" />    <qg:phrase text="Just a key..." as="Só uma chave..." />    <qg:phrase text="I'll keep it anyway." as="Vou guardar mesmo assim." />    <qg:phrase text="A potion! In a fight, it gives back 5 life." as="Uma poção! Numa luta, ela devolve 5 de vida." />    <qg:phrase text="Congratulations, you won!" as="Parabéns, você venceu!" />    <qg:phrase text="Aha! I won, maybe you can try again next time." as="Rá! Eu venci, quem sabe você tenta de novo outra vez." />    <qg:phrase text="UNKNOWN" as="DESCONHECIDO" />    <qg:phrase text="OPPONENT" as="OPONENTE" />    <qg:phrase text="PLAYER" as="JOGADOR" />    <qg:phrase text="Player" as="Jogador" />    <qg:phrase text="Opponent" as="Oponente" />    <qg:phrase text="Attack" as="Atacar" />    <qg:phrase text="Defend" as="Defender" />    <qg:phrase text="Potion" as="Poção" />    <qg:phrase text="Flee" as="Fugir" />      <qg:phrase text="Resume" as="Continuar" />    <qg:phrase text="Music" as="Música" />    <qg:phrase text="Full screen" as="Tela cheia" />    <qg:phrase text="on" as="sim" />    <qg:phrase text="off" as="não" />    <qg:phrase text="potion" as="poção" />    <qg:phrase text="potions" as="poções" />    <qg:phrase text="key" as="chave" />    <qg:phrase text="keys" as="chaves" />    <!-- the fight's banner, drawn in Portuguese -->    <qg:asset name="banner" src="assets/banner-pt.png" /></qg:language>  <qg:language lang="es" name="Español">    <qg:phrase text="Hey, it's a good time to have a JRPG fight, right?" as="Oye, es un buen momento para una pelea de JRPG, ¿no crees?" />    <qg:phrase text="Let me introduce myself, I'm your OPPONENT." as="Déjame presentarme: soy tu OPONENTE." />    <qg:phrase text="Enough talking. Let's fight!" as="Basta de charla. ¡A pelear!" />    <qg:phrase text="Just a key..." as="Solo una llave..." />    <qg:phrase text="I'll keep it anyway." as="Me la quedo de todos modos." />    <qg:phrase text="A potion! In a fight, it gives back 5 life." as="¡Una poción! En una pelea, devuelve 5 de vida." />    <qg:phrase text="Congratulations, you won!" as="¡Felicidades, ganaste!" />    <qg:phrase text="Aha! I won, maybe you can try again next time." as="¡Ajá! Gané, tal vez puedas intentarlo de nuevo la próxima vez." />    <qg:phrase text="UNKNOWN" as="DESCONOCIDO" />    <qg:phrase text="OPPONENT" as="OPONENTE" />    <qg:phrase text="PLAYER" as="JUGADOR" />    <qg:phrase text="Player" as="Jugador" />    <qg:phrase text="Opponent" as="Oponente" />    <qg:phrase text="Attack" as="Atacar" />    <qg:phrase text="Defend" as="Defender" />    <qg:phrase text="Potion" as="Poción" />    <qg:phrase text="Flee" as="Huir" />      <qg:phrase text="Resume" as="Continuar" />    <qg:phrase text="Music" as="Música" />    <qg:phrase text="Full screen" as="Pantalla completa" />    <qg:phrase text="on" as="sí" />    <qg:phrase text="off" as="no" />    <qg:phrase text="potion" as="poción" />    <qg:phrase text="potions" as="pociones" />    <qg:phrase text="key" as="llave" />    <qg:phrase text="keys" as="llaves" />    <qg:asset name="banner" src="assets/banner-es.png" /></qg:language>  <!-- Russian: letters Montserrat has not, so a font that has them (Xolonium, assets/LICENSE.md),       and three plural forms: 1 зелье, 2 зелья, 5 зелий -->  <qg:language lang="ru" name="Русский" font="assets/Xolonium-Regular.ttf">    <qg:phrase text="Hey, it's a good time to have a JRPG fight, right?" as="Эй, самое время для JRPG-драки, правда?" />    <qg:phrase text="Let me introduce myself, I'm your OPPONENT." as="Позволь представиться: я твой ПРОТИВНИК." />    <qg:phrase text="Enough talking. Let's fight!" as="Хватит болтать. В бой!" />    <qg:phrase text="Just a key..." as="Просто ключ..." />    <qg:phrase text="I'll keep it anyway." as="Всё равно возьму." />    <qg:phrase text="A potion! In a fight, it gives back 5 life." as="Зелье! В бою оно возвращает 5 жизни." />    <qg:phrase text="Congratulations, you won!" as="Поздравляю, ты победил!" />    <qg:phrase text="Aha! I won, maybe you can try again next time." as="Ага! Я победил. Может, в следующий раз получится." />    <qg:phrase text="UNKNOWN" as="НЕЗНАКОМЕЦ" />    <qg:phrase text="OPPONENT" as="ПРОТИВНИК" />    <qg:phrase text="PLAYER" as="ИГРОК" />    <qg:phrase text="Player" as="Игрок" />    <qg:phrase text="Opponent" as="Противник" />    <qg:phrase text="Attack" as="Атака" />    <qg:phrase text="Defend" as="Защита" />    <qg:phrase text="Potion" as="Зелье" />    <qg:phrase text="Flee" as="Бежать" />    <qg:phrase text="Resume" as="Продолжить" />    <qg:phrase text="Music" as="Музыка" />    <qg:phrase text="Full screen" as="Полный экран" />    <qg:phrase text="on" as="вкл" />    <qg:phrase text="off" as="выкл" />    <qg:phrase text="potion" as="зелье" />    <qg:phrase text="potions" as="зелий" one="зелье" few="зелья" />    <qg:phrase text="key" as="ключ" />    <qg:phrase text="keys" as="ключей" one="ключ" few="ключа" />    <qg:asset name="banner" src="assets/banner-ru.png" /></qg:language>  <qg:tileset name="world" src="assets/tiles.png" tile="64" />  <qg:spritesheet name="pawns" src="assets/pawns.png" tile="64" />  <qg:spritesheet name="fighters" src="assets/fighters.png" tile="128" />  <qg:spritesheet name="arena" src="assets/arena.png" tile="1280x720" />  <!-- beyond the demo: a frame for the dialogue box that stretches to it (its 16-pixel edges stay as drawn),       and "FIGHT!" as the fight starts, drawn in each language (scripts/art/rpg_extra.py, CC0) -->  <qg:spritesheet name="frame" src="assets/frame.png" tile="48" slice="16" />  <qg:spritesheet name="banner" src="assets/banner-en.png" tile="480x120" />  <qg:prefab name="Banner" tag="banner" sheet="banner" hitbox="480x120" />  <!-- beyond the demo: music of our own (scripts/art/rpg_music.py, CC0) -->  <qg:sound bus="music" name="meadow" src="assets/meadow.ogg" loop="true" />  <qg:sound bus="music" name="clash" src="assets/clash.ogg" loop="true" />  <qg:prefab name="Opponent" tag="opponent" sheet="pawns" frame="2" hitbox="48x48" />  <qg:prefab name="Key" tag="key" sheet="pawns" frame="3" hitbox="48x48" />  <qg:prefab name="Potion" tag="potion" sheet="pawns" frame="4" hitbox="48x48" />  <!-- ===== Exploration: grid_movement/exploration.tscn ===== -->  <qg:scene name="exploration" width="1280" height="720" background="#62a033">    <qg:music sound="meadow" fade="0.6" volume="-4" />    <qg:tilemap tileset="world">1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0    </qg:tilemap>    <qg:tilemap tileset="world">0,0,0,0,0,0,0,0,0,0,0,2,3,3,3,4,0,0,0,00,0,0,0,0,0,0,0,0,0,0,5,6,6,6,7,0,0,0,00,0,8,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,11,12,4,0,0,13,13,13,13,10,0,0,0,0,0,0,0,00,0,11,12,14,0,0,15,15,15,15,14,0,0,0,0,0,0,0,00,0,5,16,7,0,0,6,6,6,6,17,0,0,0,8,13,13,12,00,0,0,0,0,0,0,0,0,0,0,18,13,19,0,20,12,12,12,00,0,0,0,0,0,0,0,0,0,0,20,21,14,0,22,12,12,12,120,0,0,0,0,0,0,0,0,0,0,5,23,17,0,24,23,23,12,120,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    </qg:tilemap>    <qg:tilemap tileset="world" collision="true">25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,2829,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,0,31,2829,0,0,0,0,0,32,33,33,33,34,0,0,0,0,0,0,0,31,2829,0,0,0,0,0,31,25,26,26,35,0,0,0,0,0,0,32,36,2829,0,0,0,0,0,31,29,0,0,0,0,0,0,0,0,0,37,38,2829,0,0,0,0,0,31,39,33,33,34,0,0,0,0,0,0,40,27,2829,41,0,0,0,0,40,42,26,26,35,0,0,0,0,0,0,0,31,2843,44,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,2828,28,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,36,2828,28,43,44,44,44,44,34,0,0,0,0,0,0,0,0,0,37,38,2828,28,28,28,28,28,28,43,44,44,44,44,44,44,44,44,44,36,38,28    </qg:tilemap>    <qg:instance prefab="Key" name="key" x="544" y="288" if="{'key' not in taken}" />    <qg:instance prefab="Potion" name="potion-1" x="96" y="96" if="{'potion-1' not in taken}" />    <qg:instance prefab="Potion" name="potion-2" x="928" y="608" if="{'potion-2' not in taken}" />    <qg:instance prefab="Opponent" x="800" y="480" />    <qg:character id="player" controller="grid" diagonal="true" step-frames="15"                  sheet="pawns" frame="0" x="224" y="288" hitbox="48x48">      <qg:animation name="idle" frames="0" fps="1" />      <qg:animation name="walk" frames="0" fps="1" />      <qg:animation name="bump" frames="1" fps="1" />      <qg:on-collision with="key">        <q:set name="taken" value="{taken + [other.name]}" />        <q:set name="keys" value="{keys + 1}" />        <qg:destroy target="other" />        <qg:say dialogue="key" />      </qg:on-collision>      <qg:on-collision with="potion">        <q:set name="taken" value="{taken + [other.name]}" />        <q:set name="potions" value="{potions + 1}" />        <qg:destroy target="other" />        <qg:say dialogue="potion" />      </qg:on-collision>      <qg:on-collision with="opponent">        <q:set name="back_col" value="{me.col}" />        <q:set name="back_row" value="{me.row}" />        <qg:say dialogue="npc" />      </qg:on-collision>    </qg:character>    <!-- dialogue/dialogue_data/*.json -->    <qg:dialogue name="npc" font="assets/montserrat_extra_bold.otf" size="28" box="frame">      <qg:line who="UNKNOWN" text="Hey, it's a good time to have a JRPG fight, right?" />      <qg:line who="UNKNOWN" text="Let me introduce myself, I'm your OPPONENT." />      <qg:line who="OPPONENT" text="Enough talking. Let's fight!" />      <qg:on-end>        <qg:goto-scene name="combat" fade="0.4" />      </qg:on-end>    </qg:dialogue>    <qg:dialogue name="key" font="assets/montserrat_extra_bold.otf" size="28" box="frame">      <qg:line who="PLAYER" text="Just a key..." />      <qg:line who="PLAYER" text="I'll keep it anyway." />    </qg:dialogue>    <qg:dialogue name="potion" font="assets/montserrat_extra_bold.otf" size="28" box="frame">      <qg:line who="PLAYER" text="A potion! In a fight, it gives back 5 life." />    </qg:dialogue>    <!-- What the player carries. -->    <!-- beyond the demo: "1 potion", "2 potions", in the player's language and its plural forms -->    <qg:hud position="top-left" font="assets/montserrat_extra_bold.otf" size="24">      <qg:text value="{str(potions) + ' ' + tr_n('potion', 'potions', potions)}" />      <qg:text value="{str(keys) + ' ' + tr_n('key', 'keys', keys)}" />    </qg:hud>    <qg:dialogue name="won" font="assets/montserrat_extra_bold.otf" size="28" box="frame">      <qg:line who="OPPONENT" text="Congratulations, you won!" />      <qg:on-end><q:set name="outcome" value="" /></qg:on-end>    </qg:dialogue>    <qg:dialogue name="lost" font="assets/montserrat_extra_bold.otf" size="28" box="frame">      <qg:line who="OPPONENT" text="Aha! I won, maybe you can try again next time." />      <qg:on-end><q:set name="outcome" value="" /></qg:on-end>    </qg:dialogue>    <!-- beyond the demo: Escape (or the joypad's B) pauses, with the options; not while someone talks -->    <qg:on-input action="cancel">      <q:if condition="{not talking()}">        <q:if condition="{paused()}"><qg:resume /><q:else><qg:pause /></q:else></q:if>      </q:if>    </qg:on-input>    <qg:menu if="{paused() and not talking()}" size="32">      <qg:button label="Resume"><qg:resume /></qg:button>      <qg:slider label="Music" bus="music" />      <qg:button label="{tr('Full screen') + ': ' + (tr('on') if fullscreen() else tr('off'))}"><qg:fullscreen /></qg:button>    </qg:menu>    <!-- Back from a fight: where it started, and what the opponent says of it. -->    <q:if condition="{outcome != ''}">      <qg:put target="player" x="{back_col * 64 + 32}" y="{back_row * 64 + 32}" />      <q:if condition="{outcome == 'won'}"><qg:say dialogue="won" /></q:if>      <q:if condition="{outcome == 'lost'}"><qg:say dialogue="lost" /></q:if>    </q:if>  </qg:scene>  <!-- ===== Combat: combat/combat.tscn, turn_queue.gd, combatant.gd, health.gd ===== -->  <qg:scene name="combat" width="1280" height="720" background="#4a4a4a">    <qg:music sound="clash" fade="0.4" volume="-6" />    <!-- player.tscn: 10 life, 2 damage, 1 defense, 1 base armor;         opponent.tscn: 7 life, 3 damage, 1 defense, no armor. -->    <q:set name="life" value="10" type="number" />    <q:set name="armor" value="1" type="number" />    <q:set name="foe_life" value="7" type="number" />    <q:set name="foe_armor" value="0" type="number" />    <q:set name="turn" value="player" />    <q:set name="wait" value="0" type="number" />   <!-- the opponent's Timer: 0.25 s -->    <!-- beyond the demo: a fighter just hit makes a face for half a second -->    <q:set name="hurt" value="0" type="number" />    <q:set name="foe_hurt" value="0" type="number" />    <!-- health.gd: a hit takes the damage less the armor. -->    <q:function name="end" params="result">      <q:set name="outcome" value="{result}" />      <qg:goto-scene name="exploration" fade="0.4" />    </q:function>    <q:function name="foes_turn">      <q:set name="turn" value="opponent" />      <q:if condition="{foe_armor >= 0 + 1}"><q:set name="foe_armor" value="0" /></q:if>      <q:set name="wait" value="15" />    </q:function>    <qg:sprite sheet="arena" x="640" y="360" />    <qg:sprite sheet="fighters" frame="4" x="440" y="470" />    <qg:sprite sheet="fighters" frame="4" x="840" y="470" />    <qg:sprite sheet="fighters" frame="{1 if hurt > 0 else 0}" x="440" y="420" />    <qg:sprite sheet="fighters" frame="{3 if foe_hurt > 0 else 2}" x="840" y="420" />    <!-- beyond the demo: FIGHT! (in the player's language) as the scene fades in, and gone a second later -->    <qg:instance prefab="Banner" name="banner" x="640" y="250" />    <qg:timer after="60">      <qg:tween target="banner" property="alpha" to="0" seconds="0.5">        <qg:on-end><qg:destroy target="me" /></qg:on-end>      </qg:tween>    </qg:timer>    <qg:hud position="top-left" font="assets/montserrat_extra_bold.otf" size="32">      <qg:text value="{'Player'}" />      <qg:bar bind="life" max="10" width="320" height="20" color="#d81b60" />    </qg:hud>    <qg:hud position="top-right" font="assets/montserrat_extra_bold.otf" size="32">      <qg:text value="{'Opponent'}" />      <qg:bar bind="foe_life" max="7" width="320" height="20" color="#d81b60" />    </qg:hud>    <!-- ui.gd: the buttons work on the player's turn; beyond the demo, in the dialogues' frame. -->    <qg:menu position="bottom-center" font="assets/montserrat_extra_bold.otf" size="36" box="frame"             if="{turn == 'player'}">      <qg:button label="Attack">        <q:set name="foe_life" value="{foe_life - 2 + foe_armor}" />        <q:set name="foe_hurt" value="30" />        <qg:shake frames="10" strength="6" />        <q:if condition="{foe_life <= 0}"><q:call function="end" args="'won'" /></q:if>        <q:if condition="{foe_life > 0}"><q:call function="foes_turn" /></q:if>      </qg:button>      <qg:button label="Defend">        <q:set name="armor" value="{armor + 1}" />        <q:call function="foes_turn" />      </qg:button>      <!-- beyond the demo: a potion gives back 5 life (health.gd's heal, up to the most) -->      <qg:button label="Potion" if="{potions > 0}">        <q:set name="potions" value="{potions - 1}" />        <q:set name="life" value="{min(10, life + 5)}" />        <q:call function="foes_turn" />      </qg:button>      <qg:button label="Flee">        <q:call function="end" args="'lost'" />      </qg:button>    </qg:menu>    <!-- opponent.gd: a quarter of a second into its turn, it attacks. -->    <qg:timer every="1">      <q:if condition="{hurt > 0}"><q:set name="hurt" value="{hurt - 1}" /></q:if>      <q:if condition="{foe_hurt > 0}"><q:set name="foe_hurt" value="{foe_hurt - 1}" /></q:if>      <q:if condition="{turn == 'opponent' and wait > 0}">        <q:set name="wait" value="{wait - 1}" />        <q:if condition="{wait == 0}">          <q:set name="life" value="{life - 3 + armor}" />          <q:set name="hurt" value="30" />          <qg:shake frames="10" strength="6" />          <q:if condition="{life <= 0}"><q:call function="end" args="'lost'" /></q:if>          <q:if condition="{life > 0}">            <q:set name="turn" value="player" />            <q:if condition="{armor >= 1 + 1}"><q:set name="armor" value="1" /></q:if>          </q:if>        </q:if>      </q:if>    </qg:timer>  </qg:scene></q:application>