summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAmir Rajan <[email protected]>2020-09-02 13:31:23 -0500
committerGitHub <[email protected]>2020-09-02 13:31:23 -0500
commit8b7b61f3caae44224d1e9fa94cfb33988e01dba8 (patch)
tree10ef7e5d01ef7be5af345cfc913e3e62f9f66a8c
parentac97a8511dbb21c94633867cf8723d7080361eef (diff)
parentae28b5f4ef18f574b3bc34f921dbfab7b337b234 (diff)
downloaddragonruby-game-toolkit-contrib-8b7b61f3caae44224d1e9fa94cfb33988e01dba8.tar.gz
dragonruby-game-toolkit-contrib-8b7b61f3caae44224d1e9fa94cfb33988e01dba8.zip
Merge branch 'master' into patch-1
-rw-r--r--.gitignore6
-rw-r--r--deploy_template/.dragonruby/stubs/html5/dragonruby-html5-loader.js688
-rw-r--r--deploy_template/.dragonruby/stubs/html5/stub/game.css8
-rw-r--r--deploy_template/.dragonruby/stubs/html5/stub/index.html93
-rw-r--r--deploy_template/CHANGELOG.txt291
-rw-r--r--deploy_template/mygame/app/main.rb3
-rw-r--r--deploy_template/mygame/app/tests.rb2
-rw-r--r--deploy_template/mygame/sprites/border-black.pngbin0 -> 908 bytes
-rw-r--r--deploy_template/mygame/sprites/dragon-0.pngbin0 -> 12896 bytes
-rw-r--r--deploy_template/mygame/sprites/dragon-1.pngbin0 -> 2964 bytes
-rw-r--r--deploy_template/mygame/sprites/dragon-2.pngbin0 -> 3047 bytes
-rw-r--r--deploy_template/mygame/sprites/dragon-3.pngbin0 -> 2655 bytes
-rw-r--r--deploy_template/mygame/sprites/dragon-4.pngbin0 -> 2725 bytes
-rw-r--r--deploy_template/mygame/sprites/dragon-5.pngbin0 -> 2655 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-0.pngbin0 -> 267 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-1.pngbin0 -> 4585 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-2.pngbin0 -> 4675 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-3.pngbin0 -> 4724 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-4.pngbin0 -> 4773 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-5.pngbin0 -> 4742 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-6.pngbin0 -> 4665 bytes
-rw-r--r--deploy_template/mygame/sprites/explosion-sheet.pngbin0 -> 2584 bytes
-rw-r--r--deploy_template/mygame/sprites/star.pngbin0 -> 711 bytes
-rw-r--r--docs/docs.css2
-rw-r--r--docs/docs.html359
-rw-r--r--docs/docs.txt268
-rw-r--r--docs/parse_log.txt1491
-rw-r--r--docs/search_results.txt104
-rw-r--r--dragon/args.rb20
-rw-r--r--dragon/assert.rb92
-rw-r--r--dragon/console.rb101
-rw-r--r--dragon/console_menu.rb103
-rw-r--r--dragon/console_prompt.rb15
-rw-r--r--dragon/docs.rb50
-rw-r--r--dragon/geometry.rb44
-rw-r--r--dragon/grid.rb39
-rw-r--r--dragon/inputs.rb6
-rw-r--r--dragon/kernel_docs.rb3
-rw-r--r--dragon/log.rb34
-rw-r--r--dragon/numeric.rb53
-rw-r--r--dragon/numeric_docs.rb240
-rw-r--r--dragon/outputs_docs.rb8
-rw-r--r--dragon/readme_docs.rb45
-rw-r--r--dragon/string.rb7
-rw-r--r--dragon/tests.rb13
-rw-r--r--dragon/trace.rb3
-rw-r--r--samples/00_beginner_ruby_primer/app/main.rb4
-rw-r--r--samples/19_lowrez_jam/app/lowrez.rb24
-rw-r--r--samples/19_lowrez_jam/app/main.rb42
-rw-r--r--samples/99_sample_sprite_animation_creator/app/main.rb447
-rw-r--r--samples/99_sample_sprite_animation_creator/license-for-sample.txt9
-rw-r--r--samples/99_sample_sprite_animation_creator/replay.txt908
-rw-r--r--samples/99_sample_sprite_animation_creator/sprites/square-blue.pngbin0 -> 283 bytes
-rw-r--r--samples/99_sample_sprite_animation_creator/sprites/square-white.pngbin0 -> 279 bytes
54 files changed, 4759 insertions, 866 deletions
diff --git a/.gitignore b/.gitignore
index 642c1d1..dd17806 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
-*DS_Store* \ No newline at end of file
+*DS_Store*
+/docs/source.txt
+/docs/docs.txt
+/docs/docs.html
+/docs/search_results.txt
diff --git a/deploy_template/.dragonruby/stubs/html5/dragonruby-html5-loader.js b/deploy_template/.dragonruby/stubs/html5/dragonruby-html5-loader.js
new file mode 100644
index 0000000..c321422
--- /dev/null
+++ b/deploy_template/.dragonruby/stubs/html5/dragonruby-html5-loader.js
@@ -0,0 +1,688 @@
+function syncDataFiles(dbname, baseurl)
+{
+ var retval = {};
+ if (typeof (dbname) === "undefined") { dbname = "files"; }
+ if (typeof (baseurl) === "undefined") { baseurl = ""; }
+
+ // this is appended to files as an arg to defeat XMLHttpRequest cacheing.
+ // (Don't do this on most hosting services, from itch.io to
+ // playonjump.com, since we generally don't update the datafiles anyhow
+ // once we're shipping, and it defeats Cloudflare cacheing, causing it
+ // to abuse Amazon S3, etc.)
+ var urlrandomizerarg = '';
+ if (false) {
+ urlrandomizerarg = "?nocache=" + (Date.now() / 1000 | 0);
+ }
+
+ var state = {
+ db: null,
+ reported_result: false,
+ xhrs: {},
+ remote_manifest: {},
+ remote_manifest_loaded: false,
+ local_manifest: {},
+ local_manifest_loaded: false,
+ total_to_download: 0,
+ total_downloaded: 0,
+ total_files: 0,
+ pending_files: 0
+ };
+
+ var log = function(str) { console.log("CACHEAPPDATA: " + str); }
+ var debug = function(str) {}
+ //debug = function(str) { log(str); }
+
+ var clear_state = function() {
+ for (var i in state.xhrs) {
+ state.xhrs[i].abort();
+ }
+ delete state.db;
+ delete state.xhrs;
+ delete state.remote_manifest;
+ delete state.local_manifest;
+ };
+
+ var failed = function(why) {
+ if (state.reported_result) { return; }
+ state.reported_result = true;
+ log("[FAILURE] " + why);
+ clear_state();
+ if (retval.onerror) {
+ retval.onerror(why);
+ }
+ };
+
+ retval.abort = function() {
+ failed("Aborted.");
+ }
+
+ var succeeded = function() {
+ if (state.reported_result) { return; }
+ state.reported_result = true;
+ var why = "File data synchronized (downloaded " + Math.ceil(state.total_downloaded / 1048576) + " megabytes in " + state.total_files + " files)";
+ log("[SUCCESS] " + why);
+ retval.db = state.db;
+ retval.manifest = state.remote_manifest;
+ clear_state();
+ if (retval.onsuccess) {
+ retval.onsuccess(why);
+ }
+ };
+
+ var prevprogress = "";
+ var progress = function(str) {
+ if (state.reported_result) { return; }
+ if (str == prevprogress) { return; }
+ prevprogress = str;
+ log("[PROGRESS] " + str);
+ if (retval.onprogress) {
+ retval.onprogress(str, state.total_downloaded, state.total_to_download);
+ }
+ }
+
+ debug("Database name is '" + dbname + "'.");
+ progress("Opening database...");
+ var dbopen = window.indexedDB.open(dbname, 1);
+
+ // this is called if we change the version or the database doesn't exist.
+ // Use it to create the schema.
+ dbopen.onupgradeneeded = function(event) {
+ progress("Upgrading/creating local database...");
+ var db = event.target.result;
+ var metadataStore = db.createObjectStore("metadata", { keyPath: 'filename' });
+ var dataStore = db.createObjectStore("data", { keyPath: 'chunkid', autoIncrement: true });
+ dataStore.createIndex("data", "filename", { unique: false });
+ };
+
+ dbopen.onerror = function(event) {
+ failed("Couldn't open local database: " + event.target.error.message);
+ };
+
+ var finished_file = function(fname) {
+ debug("Finished writing '" + fname + "' to the database!");
+ state.pending_files--;
+ if (state.pending_files < 0) {
+ state.pending_files = 0;
+ debug("Uhoh, pending_files went negative?!");
+ }
+ if (state.pending_files == 0) {
+ succeeded();
+ }
+ };
+
+ var store_file = function(xhr) {
+ // write to the database...
+ var databuf = xhr.response;
+ var transaction = state.db.transaction(["metadata", "data"], "readwrite");
+ var objstoremetadata = transaction.objectStore("metadata");
+ var objstoredata = transaction.objectStore("data");
+
+ objstoremetadata.add({ filename: xhr.filename, filesize: xhr.filesize, filetime: xhr.filetime });
+ // !!! FIXME: _of course_ this crashes Safari on large files
+ /*
+ var chunksize = 1048576; // 1 megabyte each.
+ var chunks = Math.ceil(xhr.response.byteLength / chunksize);
+ for (var i = 0; i < chunks; i++) {
+ var bufoffset = i * chunksize;
+ objstoredata.add({
+ filename: xhr.filename,
+ offset: bufoffset,
+ chunk: new Uint8Array(databuf, bufoffset, chunksize);
+ });
+ }
+ */
+ objstoredata.add({ filename: xhr.filename, offset: 0, chunk: databuf });
+
+ transaction.oncomplete = function(event) {
+ finished_file(xhr.filename); // all done here!
+ };
+ };
+
+ var download_new_files = function() {
+ if (state.reported_result) { return; }
+ progress("Downloading new files...");
+ var downloadme = [];
+ for (var i in state.remote_manifest) {
+ var remoteitem = state.remote_manifest[i];
+ var remotefname = i;
+ if (typeof state.local_manifest[remotefname] !== "undefined") {
+ debug("remote filename '" + remotefname + "' already downloaded.");
+ } else {
+ debug("remote filename '" + remotefname + "' needs downloading.");
+ // the browser will let a handful of these go in parallel, and
+ // then will queue the rest, firing events as appropriate
+ // when it gets around to them, so just fire them all off
+ // here.
+
+ // !!! FIXME: use the Fetch API, plus streaming, as an option.
+ // !!! FIXME: It can use less memory, since it doesn't need
+ // !!! FIXME: to keep the whole file in memory.
+ state.total_to_download += remoteitem.filesize;
+ state.total_files++;
+ state.pending_files++;
+
+ var xhr = new XMLHttpRequest();
+ state.xhrs[remotefname] = xhr;
+ xhr.previously_loaded = 0;
+ xhr.filename = remotefname;
+ xhr.filesize = state.remote_manifest[i].filesize;
+ xhr.filetime = state.remote_manifest[i].filetime;
+ xhr.expected_filesize = remoteitem.filesize;
+ xhr.responseType = "arraybuffer";
+ xhr.addEventListener("error", function(e) { failed("Download error on '" + e.target.filename + "'!"); });
+ xhr.addEventListener("timeout", function(e) { failed("Download timeout on '" + e.target.filename + "'!"); });
+ xhr.addEventListener("abort", function(e) { failed("Download abort on '" + e.target.filename + "'!"); });
+
+ xhr.addEventListener('progress', function(e) {
+ if (state.reported_result) { return; }
+ var xhr = e.target;
+ var additional = e.loaded - xhr.previously_loaded;
+ state.total_downloaded += additional;
+ xhr.previously_loaded = e.loaded;
+ debug("Downloaded " + additional + " more bytes for file '" + xhr.filename + "'");
+ var percent = state.total_to_download ? Math.floor((state.total_downloaded / state.total_to_download) * 100.0) : 0;
+ progress("Downloaded " + percent + "% (" + Math.ceil(state.total_downloaded / 1048576) + "/" + Math.ceil(state.total_to_download / 1048576) + " megabytes)");
+ });
+
+ xhr.addEventListener("load", function(e) {
+ if (state.reported_result) { return; }
+ var xhr = e.target;
+ if (xhr.status != 200) {
+ failed("Server reported failure downloading '" + xhr.filename + "'!");
+ } else {
+ debug("Finished download of '" + xhr.filename + "'!");
+ state.total_downloaded -= xhr.previously_loaded;
+ state.total_downloaded += xhr.expected_filesize;
+ xhr.previously_loaded = xhr.expected_filesize;
+ delete state.xhrs[xhr.filename];
+ var percent = state.total_to_download ? Math.floor((state.total_downloaded / state.total_to_download) * 100.0) : 0;
+ progress("Downloaded " + percent + "% (" + Math.ceil(state.total_downloaded / 1048576) + "/" + Math.ceil(state.total_to_download / 1048576) + " megabytes)");
+ store_file(xhr);
+ }
+ });
+
+ xhr.open("get", baseurl + remotefname + urlrandomizerarg, true);
+ xhr.send();
+ }
+ }
+
+ if (state.pending_files == 0) {
+ succeeded(); // we're already done. :)
+ }
+ };
+
+ var delete_old_files = function() {
+ if (state.reported_result) { return; }
+ var deleteme = []
+ for (var i in state.local_manifest) {
+ var localitem = state.local_manifest[i];
+ var localfname = localitem.filename;
+ var removeme = false;
+ if (typeof state.remote_manifest[localfname] === "undefined") {
+ removeme = true;
+ } else {
+ var remoteitem = state.remote_manifest[localfname];
+ if ( (localitem.filesize != remoteitem.filesize) ||
+ (localitem.filetime != remoteitem.filetime) ) {
+ removeme = true;
+ }
+ }
+
+ if (removeme) {
+ debug("Marking old file '" + localfname + "' for removal.");
+ deleteme.push(localfname);
+ delete state.local_manifest[i];
+ }
+ }
+
+ if (deleteme.length == 0) {
+ debug("No old files to delete.");
+ download_new_files(); // just move on to the next stage.
+ } else {
+ progress("Cleaning up old files...");
+ var transaction = state.db.transaction(["data", "metadata"], "readwrite");
+ transaction.oncomplete = function(event) {
+ debug("All old files are deleted.");
+ download_new_files();
+ };
+
+ var objstoremetadata = transaction.objectStore("metadata");
+ var objstoredata = transaction.objectStore("data");
+ var dataindex = objstoredata.index("data");
+ for (var i of deleteme) {
+ debug("Deleting metadata for '" + i + "'.");
+ objstoremetadata.delete(i);
+ dataindex.openCursor(IDBKeyRange.only(i)).onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ debug("Deleting file chunk " + cursor.value.chunkid + " for '" + cursor.value.filename + "' (offset=" + cursor.value.offset + ", size=" + cursor.value.size + ").");
+ objstoredata.delete(cursor.value.chunkid);
+ cursor.continue();
+ }
+ }
+ }
+ }
+ };
+
+ var manifest_loaded = function() {
+ if (state.reported_result) { return; }
+ if (state.local_manifest_loaded && state.remote_manifest_loaded) {
+ debug("both manifests loaded, moving on to next step.");
+ delete_old_files(); // on success, will start downloads.
+ }
+ };
+
+ var load_local_manifest = function(db) {
+ if (state.reported_result) { return; }
+ debug("Loading local manifest...");
+ var transaction = db.transaction("metadata", "readonly");
+ var objstore = transaction.objectStore("metadata");
+ var cursor = objstore.openCursor();
+
+ // this gets called once for each item in the object store.
+ cursor.onsuccess = function(event) {
+ if (state.reported_result) { return; }
+ var cursor = event.target.result;
+ if (cursor) {
+ debug("Another local manifest item: '" + cursor.value.filename + "'");
+ state.local_manifest[cursor.value.filename] = cursor.value;
+ cursor.continue();
+ } else {
+ debug("All local manifest items iterated.");
+ state.local_manifest_loaded = true;
+ manifest_loaded(); // maybe move on to next step.
+ }
+ };
+ };
+
+ dbopen.onsuccess = function(event) {
+ debug("Database is open!");
+ var db = event.target.result;
+ state.db = db;
+
+ // just catch all database errors here, where they will bubble up
+ // from objectstores and transactions.
+ db.onerror = function(event) {
+ failed("Database error: " + event.target.error.message);
+ };
+
+ progress("Loading file manifests...");
+
+ // this is async, so it happens while remote manifest downloads.
+ load_local_manifest(db);
+
+ debug("Loading remote manifest...");
+ var xhr = new XMLHttpRequest();
+ xhr.responseType = "text";
+ xhr.addEventListener("error", function(e) { failed("Manifest download error!"); });
+ xhr.addEventListener("timeout", function(e) { failed("Manifest download timeout!"); });
+ xhr.addEventListener("abort", function(e) { failed("Manifest download abort!"); });
+ xhr.addEventListener("load", function(e) {
+ if (e.target.status != 200) {
+ failed("Server reported failure downloading manifest!");
+ } else {
+ debug("Remote manifest loaded!");
+ debug("json: " + e.target.responseText);
+ state.remote_manifest_loaded = true;
+ try {
+ state.remote_manifest = JSON.parse(e.target.responseText);
+ } catch (e) {
+ failed("Remote manifest is corrupted.");
+ }
+ delete state.remote_manifest[""]
+ manifest_loaded(); // maybe move on to next step.
+ }
+ });
+ xhr.open("get", "manifest.json" + urlrandomizerarg, true);
+ xhr.send();
+ };
+
+ return retval;
+}
+
+var statusElement = document.getElementById('status');
+var progressElement = document.getElementById('progress');
+var canvasElement = document.getElementById('canvas');
+
+canvasElement.style.width = '1280px';
+canvasElement.style.height = '720px';
+canvasElement.style.display = 'block';
+canvasElement.style['margin-left'] = 'auto';
+canvasElement.style['margin-right'] = 'auto';
+
+statusElement.style.display = 'none';
+progressElement.style.display = 'none';
+document.getElementById('progressdiv').style.display = 'none';
+document.getElementById('output').style.display = 'none';
+document.getElementById('game-input').style.display = "none"
+
+if (!window.parent.window.gtk) {
+ window.parent.window.gtk = {};
+}
+
+window.parent.window.gtk.saveMain = function(text) {
+ FS.writeFile('app/main.rb', text);
+ window.gtk.play();
+}
+
+
+var loadDataFiles = function(dbname, baseurl, onsuccess) {
+ var syncdata = syncDataFiles(dbname, baseurl);
+ window.gtk.syncdata = syncdata;
+
+ syncdata.onerror = function(why) {
+ Module.setStatus(why);
+ }
+
+ syncdata.onprogress = function(why, total_downloaded, total_to_download) {
+ Module.setStatus(why);
+ }
+
+ syncdata.onsuccess = function(why) {
+ //Module.setStatus(why);
+ console.log(why);
+
+ GGameFilesDatabase = syncdata.db;
+ window.gtk.filedb = syncdata.db;
+
+ var db = syncdata.db;
+ var manifest = syncdata.manifest;
+ syncdata.failed = false;
+ syncdata.num_requests = 0;
+ syncdata.total_requests = 0;
+
+ db.onerror = function(event) {
+ Module.setStatus("Database error: " + event.target.error.message);
+ syncdata.failed = true;
+ };
+
+ var transaction = db.transaction("data", "readonly");
+ var objstore = transaction.objectStore("data");
+ var dataindex = objstore.index("data");
+
+ for (var i in manifest) {
+ // !!! FIXME: this assumes the whole file is in one chunk, but
+ // !!! FIXME: that was not my original plan.
+ syncdata.total_requests++;
+ syncdata.num_requests++;
+ //console.log("'" + i + "' is headed for MEMFS...");
+ var req = dataindex.get(i);
+ req.filesize = manifest[i].filesize;
+ req.onsuccess = function(event) {
+ var path = "/" + event.target.result.filename;
+ //console.log("'" + path + "' is loaded in from IndexedDB...");
+ var ui8arr = new Uint8Array(event.target.result.chunk);
+ var len = event.target.filesize;
+ var arr = new Array(len);
+ for (var i = 0; i < len; ++i) {
+ arr[i] = ui8arr[i];
+ }
+
+ var basedir = PATH.dirname(path);
+ FS.mkdirTree(basedir);
+
+ var okay = false;
+ try {
+ okay = FS.createDataFile(basedir, PATH.basename(path), arr, true, true, true);
+ } catch (err) { // throws if file exists, etc. Nuke and try one more time.
+ FS.unlink(path);
+ try {
+ okay = FS.createDataFile(basedir, PATH.basename(path), arr, true, true, true);
+ } catch (err) {
+ okay = false; // oh well.
+ }
+ }
+
+ if (!okay) {
+ Module.setStatus("ERROR: Failed to put '" + path + "' in MEMFS.");
+ } else {
+ var completed = syncdata.total_requests - syncdata.num_requests;
+ var percent = Math.floor((completed / syncdata.total_requests) * 100.0);
+ Module.setStatus("Preparing game data: " + percent + "%");
+ //console.log("'" + path + "' has made it to MEMFS! (" + syncdata.num_requests + " to go)");
+ syncdata.num_requests--;
+ if (syncdata.num_requests <= 0) {
+ if (!syncdata.failed) {
+ onsuccess();
+ }
+ }
+ }
+ };
+ }
+ }
+}
+
+// https://stackoverflow.com/a/7372816
+var base64Encode = function(ui8array) {
+ var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ var out = "", i = 0, len = ui8array.length, c1, c2, c3;
+ while (i < len) {
+ c1 = ui8array[i++] & 0xff;
+ if (i == len) {
+ out += CHARS.charAt(c1 >> 2);
+ out += CHARS.charAt((c1 & 0x3) << 4);
+ out += "==";
+ break;
+ }
+ c2 = ui8array[i++];
+ if (i == len) {
+ out += CHARS.charAt(c1 >> 2);
+ out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
+ out += CHARS.charAt((c2 & 0xF) << 2);
+ out += "=";
+ break;
+ }
+ c3 = ui8array[i++];
+ out += CHARS.charAt(c1 >> 2);
+ out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+ out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
+ out += CHARS.charAt(c3 & 0x3F);
+ }
+ return out;
+}
+
+var Module = {
+ noInitialRun: true,
+ preInit: [],
+ clickedToPlay: false,
+ clickToPlayListener: function() {
+ if (Module.clickedToPlay) return;
+ Module.clickedToPlay = true;
+ var div = document.getElementById('clicktoplaydiv');
+ if (div) {
+ div.removeEventListener('click', Module.clickToPlayListener);
+ document.body.removeChild(div);
+ }
+ if (window.parent.window.gtk.starting) {
+ window.parent.window.gtk.starting();
+ }
+ Module["callMain"](); // go go go!
+ },
+ startClickToPlay: function() {
+ var base64 = base64Encode(FS.readFile(GDragonRubyIcon, {}));
+ var div = document.createElement('div');
+ var leftPx = ((window.innerWidth - 640) / 2);
+ var leftPerc = Math.floor((leftPx / window.innerWidth) * 100);
+ div.id = 'clicktoplaydiv';
+ div.style.backgroundColor = 'rgb(40, 44, 52)';
+ div.style.left = leftPerc.toString() + "%";
+ div.style.top = '10%';
+ div.style.display = 'block';
+ div.style.position = 'absolute';
+ div.style.width = "640px"
+ div.style.height = "360px"
+
+
+
+ var img = new Image();
+ img.onload = function() { // once we know its size, scale it, keeping aspect ratio.
+ var zoomRatio = 100.0 / this.width;
+ img.style.width = (zoomRatio * this.width.toString()) + "px";
+ img.style.height = (zoomRatio * this.height.toString()) + "px";
+ img.style.display = "block";
+ img.style['margin-left'] = 'auto';
+ img.style['margin-right'] = 'auto';
+ }
+
+ img.style.display = 'none';
+ img.src = 'data:image/png;base64,' + base64;
+ div.appendChild(img);
+
+
+ var p;
+
+ p = document.createElement('h1');
+ p.textContent = GDragonRubyGameTitle + " " + GDragonRubyGameVersion + " by " + GDragonRubyDevTitle;
+ p.style.textAlign = 'center';
+ p.style.color = '#FFFFFF';
+ p.style.width = '100%';
+ p.style['font-family'] = "monospace";
+ div.appendChild(p);
+
+ p = document.createElement('p');
+ p.innerHTML = 'Click here to begin.';
+ p.style['font-family'] = "monospace";
+ p.style['font-size'] = "20px";
+ p.style.textAlign = 'center';
+ p.style.backgroundColor = 'rgb(40, 44, 52)';
+ p.style.color = '#FFFFFF';
+ p.style.width = '100%';
+ div.appendChild(p);
+
+ document.body.appendChild(div);
+ div.addEventListener('click', Module.clickToPlayListener);
+ window.gtk.play = Module.clickToPlayListener;
+ },
+ preRun: function() {
+ // set up a persistent store for save games, etc.
+ FS.mkdir('/persistent');
+ FS.mount(IDBFS, {}, '/persistent');
+ FS.syncfs(true, function(err) {
+ if (err) {
+ console.log("WARNING: Failed to populate persistent store. Save games likely lost?");
+ } else {
+ console.log("Read in from persistent store.");
+ }
+
+ loadDataFiles(GDragonRubyGameId, 'gamedata/', function() {
+ console.log("Game data is sync'd to MEMFS. Starting click-to-play()...");
+ //Module.setStatus("Ready!");
+ //setTimeout(function() { Module.setStatus(""); statusElement.style.display='none'; }, 1000);
+ Module.setStatus("");
+ statusElement.style.display='none';
+ Module.startClickToPlay();
+ });
+ });
+ },
+ postRun: [],
+ print: (function() {
+ var element = document.getElementById('output');
+ if (element) element.value = ''; // clear browser cache
+ return function(text) {
+ if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+ // These replacements are necessary if you render to raw HTML
+ //text = text.replace(/&/g, "&amp;");
+ //text = text.replace(/</g, "&lt;");
+ //text = text.replace(/>/g, "&gt;");
+ //text = text.replace('\n', '<br>', 'g');
+ console.log(text);
+ if (element) {
+ element.value += text + "\n";
+ element.scrollTop = element.scrollHeight; // focus on bottom
+ }
+ };
+ })(),
+ printErr: function(text) {
+ if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+ if (0) { // XXX disabled for safety typeof dump == 'function') {
+ dump(text + '\n'); // fast, straight to the real console
+ } else {
+ console.error(text);
+ }
+ },
+ canvas: (function() {
+ var canvas = document.getElementById('canvas');
+
+ // As a default initial behavior, pop up an alert when webgl context is lost. To make your
+ // application robust, you may want to override this behavior before shipping!
+ // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+ canvas.addEventListener("click", function() {
+ document.getElementById('toplevel').click();
+ document.getElementById('toplevel').focus();
+ document.getElementById('game-input').style.display = "inline"
+ document.getElementById('game-input').focus();
+ document.getElementById('game-input').blur();
+ document.getElementById('game-input').style.display = "none"
+ canvas.focus();
+ });
+
+
+ return canvas;
+ })(),
+ setStatus: function(text) {
+ if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
+ if (text === Module.setStatus.text) return;
+ var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
+ var now = Date.now();
+ if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
+ if (m) {
+ text = m[1];
+ progressElement.value = parseInt(m[2])*100;
+ progressElement.max = parseInt(m[4])*100;
+ progressElement.hidden = false;
+ } else {
+ progressElement.value = null;
+ progressElement.max = null;
+ progressElement.hidden = true;
+ }
+ statusElement.innerHTML = text;
+ },
+ totalDependencies: 0,
+ monitorRunDependencies: function(left) {
+ this.totalDependencies = Math.max(this.totalDependencies, left);
+ Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+ }
+};
+Module.setStatus('Downloading...');
+window.onerror = function(event) {
+ // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
+ Module.setStatus('Exception thrown, see JavaScript console');
+ Module.setStatus = function(text) {
+ if (text) Module.printErr('[post-exception status] ' + text);
+ };
+};
+
+var hasWebAssembly = false;
+if (typeof WebAssembly==="object" && typeof WebAssembly.Memory==="function") {
+ hasWebAssembly = true;
+}
+//console.log("Do we have WebAssembly? " + ((hasWebAssembly) ? "YES" : "NO"));
+
+var buildtype = hasWebAssembly ? "wasm" : "asmjs";
+var module = "dragonruby-" + buildtype + ".js";
+window.gtk = {};
+window.gtk.module = Module;
+
+//console.log("Our main module is: " + module);
+
+var script = document.createElement('script');
+script.src = module;
+if (hasWebAssembly) {
+ script.async = true;
+} else {
+ script.async = false; // !!! FIXME: can this be async?
+ (function() {
+ var memoryInitializer = module + '.mem';
+ if (typeof Module['locateFile'] === 'function') {
+ memoryInitializer = Module['locateFile'](memoryInitializer);
+ } else if (Module['memoryInitializerPrefixURL']) {
+ memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
+ }
+ var meminitXHR = Module['memoryInitializerRequest'] = new XMLHttpRequest();
+ meminitXHR.open('GET', memoryInitializer, true);
+ meminitXHR.responseType = 'arraybuffer';
+ meminitXHR.send(null);
+ })();
+}
+document.body.appendChild(script);
diff --git a/deploy_template/.dragonruby/stubs/html5/stub/game.css b/deploy_template/.dragonruby/stubs/html5/stub/game.css
new file mode 100644
index 0000000..3fb16d4
--- /dev/null
+++ b/deploy_template/.dragonruby/stubs/html5/stub/game.css
@@ -0,0 +1,8 @@
+body {
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+canvas {
+ background-color: rgb(40, 44, 52);
+}
diff --git a/deploy_template/.dragonruby/stubs/html5/stub/index.html b/deploy_template/.dragonruby/stubs/html5/stub/index.html
new file mode 100644
index 0000000..b2bcf9f
--- /dev/null
+++ b/deploy_template/.dragonruby/stubs/html5/stub/index.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <link rel="icon" href="favicon.png" type="image/png" />
+ <title>DragonRuby</title>
+ <style>
+ body {
+ font-family: arial;
+ margin: 0;
+ padding: none;
+ background: #000000;
+ }
+
+ .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
+ div.emscripten { text-align: center; }
+ div.emscripten_border { border: 1px solid black; }
+ /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
+ canvas.emscripten { border: 0px none; background-color: black; }
+
+ #emscripten_logo {
+ display: inline-block;
+ margin: 0;
+ }
+
+ @-webkit-keyframes rotation {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(360deg);}
+ }
+ @-moz-keyframes rotation {
+ from {-moz-transform: rotate(0deg);}
+ to {-moz-transform: rotate(360deg);}
+ }
+ @-o-keyframes rotation {
+ from {-o-transform: rotate(0deg);}
+ to {-o-transform: rotate(360deg);}
+ }
+ @keyframes rotation {
+ from {transform: rotate(0deg);}
+ to {transform: rotate(360deg);}
+ }
+
+ #status {
+ display: inline-block;
+ font-weight: bold;
+ color: rgb(120, 120, 120);
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ #progress {
+ height: 20px;
+ width: 30px;
+ }
+
+ #output {
+ width: 100%;
+ height: 200px;
+ margin: 0 auto;
+ margin-top: 10px;
+ border-left: 0px;
+ border-right: 0px;
+ padding-left: 0px;
+ padding-right: 0px;
+ display: none;
+ background-color: black;
+ color: white;
+ font-family: 'Lucida Console', Monaco, monospace;
+ outline: none;
+ }
+ </style>
+ <link rel="stylesheet" href="game.css" />
+ <title>DragonRuby Game Toolkit Tutorial</title>
+ </head>
+ <body id='toplevel'>
+ <div class="emscripten_border" id='borderdiv'>
+ <input id="game-input" autocomplete="off" />
+ <canvas class="game emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
+ </div>
+ <br/>
+ <textarea style="display: none;" id="output" rows="8"></textarea>
+
+ <div class="emscripten" id="status"></div>
+ <div class="emscripten" id='progressdiv'>
+ <progress value="0" max="100" id="progress"></progress>
+ </div>
+
+ <script type='text/javascript' src='dragonruby-html5-loader.js'></script>
+ </body>
+</html>
diff --git a/deploy_template/CHANGELOG.txt b/deploy_template/CHANGELOG.txt
index 9bde121..cf22fc9 100644
--- a/deploy_template/CHANGELOG.txt
+++ b/deploy_template/CHANGELOG.txt
@@ -1,3 +1,261 @@
+= 1.14 =
+
+ * [Support] Better HTML5 template. Additional JS events added to
+ handle loss of keyboard input within an iframe.
+ * [Bugfix] `args.outputs.screenshots` regression fixed.
+ * [Docs] Added documentation for a few more Numeric methods.
+ * [Samples] Brand new advanced sample app: 99_sample_sprite_animation_creator.
+ The sample app uses `args.outputs.screenshots` and `render_targets` heavily along with
+ in memory queues as a means to consolidate events coming from
+ different parts of the app.
+
+
+= 1.13 =
+
+ * [API] Sprite angle now accepts fractional degrees.
+ * [Samples] Better font added to LOWREZJAM 2020 template.
+ * [API] Added `args.outputs[RENDER_TARGET_NAME]` as an alias to
+ `args.render_target(RENDER_TARGET_NAME)`. Either of the following will work:
+
+ ```ruby
+ def tick args
+ if args.state.tick_count == 1
+ args.render_target(:camera).width = 100
+ args.render_target(:camera).height = 100
+ args.render_target(:camera).solids << [0, 0, 50, 50, 255, 0, 0]
+ end
+
+ if args.state.tick_count > 0
+ args.outputs.sprites << { x: 0,
+ y: 0,
+ w: 500,
+ h: 500,
+ source_x: 0,
+ source_y: 0,
+ source_w: 50,
+ source_h: 50,
+ path: :camera }
+ end
+ end
+
+ $gtk.reset
+ ```
+
+ Is the same as:
+
+ ```ruby
+ def tick args
+ if args.state.tick_count == 1
+ args.outputs[:camera].width = 100
+ args.outputs[:camera].height = 100
+ args.outputs[:camera].solids << [0, 0, 50, 50, 255, 0, 0]
+ end
+
+ if args.state.tick_count > 0
+ args.outputs.sprites << { x: 0,
+ y: 0,
+ w: 500,
+ h: 500,
+ source_x: 0,
+ source_y: 0,
+ source_w: 50,
+ source_h: 50,
+ path: :camera }
+ end
+ end
+
+ $gtk.reset
+ ```
+
+= 1.12 =
+
+ * [Samples] LOWREZ Jam sample app reworked in preparation for LOWREZ
+ Jam 2020 (starts on August 1st so hurry and join).
+ * [Docs] Docs added for GTK::Mouse, you can access them via the
+ Console by typing `GTK::Mouse.docs` or `$gtk.args.inputs.mouse.docs`.
+ * [MacOS] Updated minimum OS support to include MacOS 10.9+.
+
+= 1.11 =
+
+ * [Bugfix] Fixed error in docs_search "TERM".
+
+= 1.10 =
+
+ * [Support] Documentation infrastructure added (take a look at docs/docs.html). Bring up the DragonRuby Console and:
+
+ To search docs you can type `docs_search "SEARCH TERM"`
+
+ If you want to get fancy you can provide a `lambda` to filter documentation:
+
+ docs_search { |entry| (entry.include? "Array") && (!entry.include? "Enumerable") }
+
+ * [Bugfix] Fixed sprite rendering issues with source_(x|y|w|h) properties on sprites.
+ * [Support] Removed double buffering of game if framerate drops below 60 fps.
+ * [Support] Console now supports mouse wheel scrolling.
+ * [Support] One time notifications have less noise/easier to read.
+ * [Bugfix] Rogue ~app/main.rb~ directory will no longer be created if you run a sample app.
+
+= 1.9 =
+
+ * [Bugfix] HTTP on windows should now work, for real this time.
+ * [Bugfix] Non-720p render targets now use correct coordinate system.
+
+= 1.8 =
+
+ * [Experimental] Added the ability to control the logical game size. You can use cli
+ arguments to set it. Example ultra-wide support would be:
+ `./dragonruby --window_width 3840 --window_height 1080`
+ * [Bugfix] HTTP on windows should now work.
+ * [Bugfix] `even?` and `odd?` return the correct result for Fixnum.
+ * [Bugfix] args.intputs.mouse_wheel now reports the delta change in x and y correctly.
+ * [Bugfix] Improved analog joystick accuracy when converting to percentages.
+ * [Support] Incorporated pull request from https://github.com/kfischer-okarin that adds
+ autocompletion to the Console. This is the PR:
+ - https://github.com/DragonRuby/dragonruby-game-toolkit-contrib/commit/da0fdcfbd2bd9739fe056eb646920df79a32954c
+ - https://github.com/DragonRuby/dragonruby-game-toolkit-contrib/commit/99305ca79118fa0704c8681f4019738b8c7a500d
+
+= 1.7 =
+
+ * [BREAKING] args.inputs.mouse.wheel.point is gone. Use args.inputs.mouse.x
+ and .y if you need cursor position.
+ * [BREAKING] args.inputs.mouse.wheel.x and .y now represent the amount the
+ mousewheel/trackpad has moved since the last tick and not the mouse cursor
+ position. Use args.inputs.mouse.x and .y if you need cursor position.
+
+= 1.6 =
+
+ * [API] Sprite now supports source_(x|y|w|h). These properties are consistent with the origin
+ being in the bottom left. The existing properties tile_(x|y|w|h) assumes that origin 0, 0 is in the top left.
+ The code below will render the same sprite (in their respective coordinate systems):
+
+ # using tile_(x|y|w|h) properties
+ args.outputs.sprites << { x: 0,
+ y: 0,
+ w: 1280,
+ h: 100,
+ path: :block,
+ tile_x: 0,
+ tile_y: 720 - 100,
+ tile_w: 1280,
+ tile_h: 100 }
+
+ is equivalent to:
+
+ # using source_(x|y|w|h) properties
+ args.outputs.sprites << { x: 0,
+ y: 0,
+ w: 1280,
+ h: 100,
+ path: :block,
+ source_x: 0,
+ source_y: 0,
+ source_w: 1280,
+ source_h: 100 }
+
+ Note: if you provide both tile_(x|y|w|h) and source_(x|y|w|h). The values of tile_ will "win" so as not to
+ break existing code out there.
+ * [Bugfix] Updated require to remove duplicate requires of the same file (or files that have recently been required).
+ * [Bugfix] Strict entities of different types/names serialize and deserialize correctly.
+ * [Samples] Updated render targets sample app to show two render targets with transparencies.
+ * [API] No really, render targets now have a transparent background and respect opacity.
+
+= 1.5 =
+
+ * [API] Added $gtk.show_cursor and $gtk.hide_cursor to show and hide the mouse cursor. The
+ function only needs to be called once. EG: args.gtk.hide_cursor if args.state.tick_count == 0.
+ * [Samples] Jam Craft 2020 sample app updated to have more comments and demonstrate a custom
+ mouse cursor.
+
+= 1.4 =
+
+ * [Bugfix] Adding $gtk.reset at the bottom of main.rb will no longer cause an infinite loop.
+ * [Samples] Sample app added for Jam Craft 2020.
+
+= 1.3 =
+
+ * [Bugfix] Adding $gtk.reset at the bottom of main.rb will no longer cause an infinite loop.
+ * [Samples] Better instructions added to various sample apps.
+
+= 1.2 =
+
+ * [Bugfix] Top-level require statements within main.rb will load before
+ invoking the rest of the code in main.rb.
+ * [Samples] Better keyboard input sample app.
+ * [Samples] New sample app that shows how to use Numeric#ease_spline.
+ * [Bugfix] Fixed "FFI::Draw cannot be serialized" error message.
+
+= 1.1 =
+
+ * [Bugfix] Fixed exception associated with providing serialization related help.
+ * [Bugfix] Fixed comments on how to run tests from CLI.
+ * [Support] More helpful global variables added. Here's a list:
+ - $gtk
+ - $console
+ - $args
+ - $state
+ - $tests
+ - $record
+ - $replay
+ * [API] inputs.keyboard.key_(down|held|up).any? and inputs.keyboard.key_(down|held|up).all?
+ added.
+ * [Support] Recording gameplay and replaying streamlined a bit more. GIVE THE
+ REPLAY FEATURE A SHOT! IT'S AWESOME!! Bring up the console and run: $record.start SEED_NUMBER.
+ * [Support] Bringing up the console will stop a replay if one is running.
+
+= 1.0 =
+
+ * [News] DragonRuby Game Toolkit turns 1.0. Happy birthday!
+ * [Bugfix] args.state.new_entity_strict serializes and deserializes correctly now.
+ * [BREAKING] Entity#hash has been renamed to Entity#as_hash so as not to redefine
+ Object#hash. This is a "private" method so you probably don't have to worry about
+ anything breaking on your end.
+ * [BREAKING] gtk.save_state and gtk.load_state have been replaced with gtk.serialize_state
+ and gtk.deserialize_state (helpful error messages have been added).
+ * [Support] Console can now render sprites (this is still in its early stages). Try
+ $gtk.console.addsprite(w: 50, h: 50, path: "some_path.png").
+ * [API] Render targets now have a transparent background and respect opacity.
+ * [API] Render targets can be cached/programatically created once and reused.
+ * [Samples] A new render target sample app has been created to show how to cache them.
+ * [Samples] Easing sample app reworked/simplified.
+ * [Support] GTK will keep a backup of your source file changes under the tmp directory.
+ One day this feature will save your ass.
+
+= 20200301 =
+
+ * [Samples] Added sample app that shows how you can draw a cubic bezier curves.
+ * [Support] Keyup event prints key and raw_key to the console.
+ * [Support] Circumflex now opens the console.
+
+= 20200227 =
+
+ * [Bugfix] Game will now auto restart in the event of a syntax error.
+ * [Samples] Sample app added to show how to use a sprite sheet for sprite animations:
+ 09_sprite_animation_using_tile_sheet.
+ * [Samples] Sample app added to show how to use a tile sheet for a roguelike:
+ 20_roguelike_starting_point_two.
+ * [Samples] Example code added to show how sort an array with a custom sort block:
+ 00_intermediate_ruby_primer/07_powerful_arrays.txt
+ * [OSS] The following files have been open sourced at https://github.com/DragonRuby/dragonruby-game-toolkit-contrib:
+ - modified: dragon/args.rb
+ - new file: dragon/assert.rb
+ - new file: dragon/attr_gtk.rb
+ - modified: dragon/console.rb
+ - new file: dragon/docs.rb
+ - new file: dragon/geometry.rb
+ - new file: dragon/help.rb
+ - modified: dragon/index.rb
+ - modified: dragon/inputs.rb
+ - new file: dragon/log.rb
+ - new file: dragon/numeric.rb
+ - new file: dragon/string.rb
+ - new file: dragon/tests.rb
+ - new file: dragon/trace.rb
+ * [Support] Added isometric placeholder sprites.
+ * [Support] Added $gtk.reset_sprite 'path' to refresh a sprite from
+ the file system (while the game is running). Future releases will
+ automatically auto load sprites but you can use this to reload them
+ on demand.
+
= 20200225 =
* [Bugfix] Fixed macOS compatibility back to Mac OS X 10.9 or so.
@@ -22,9 +280,8 @@
= 20200217 =
* [Bugfix] `dragonruby-publish` would only build html5. It now builds
- all of the platforms again. Ryan "The Juggernaut" Gordon has given Amir
- a warning and has told him to be more careful releasing. Amir cackled
- in defiance.
+ all of the platforms again. Ryan has given Amir a warning and has told him
+ to be more careful releasing. Amir cackled in defiance.
= 20200213 =
@@ -87,7 +344,7 @@
http.
* [Support] [Samples] Added a sample app that shows how create
collision detection associated with constraint points against
- a ramp. This sample app also demostrates the use of `gtk.slowmo!`
+ a ramp. This sample app also demonstrates the use of `gtk.slowmo!`
which you can use the slow down the tick execution of your game
to see things frame by frame (still experimental).
* [Support] Added sprites to default template folder. The sprites
@@ -165,7 +422,7 @@
* [Samples] Added sample app that shows how trace can be used within a class.
* [Support] Added $gtk.log_level. Valid values are :on,
- :off. When the value is set to :on, GTK log mesages will show up
+ :off. When the value is set to :on, GTK log messages will show up
in the DragonRuby Console *AND* be written to logs/log.txt. When the
value is set to :off, GTK log messages will *NOT* show up in the
console, but will *STILL* be written to logs/log.txt
@@ -306,7 +563,7 @@
* [Samples] New sample app called 02_collisions_02_moving_objects has been added.
* [Support] Previous console messages are subdued if unhandled exception is resolved.
This removes visual confusion if another exception is thrown (easier to determine what the current exception is).
- * [Support] Added api documentation about thruthy_keys for keyboards and controllers.
+ * [Support] Added api documentation about truthy_keys for keyboards and controllers.
* [API] [Experimental] Hashes now quack like render primitives. For example some_hash[:x] is the same as
some_hash.x and can be interchangeable with some_array.x (this is a work in progress
so there may be some geometric/collision apis that aren't covered).
@@ -316,7 +573,7 @@
loops, and arrays. Special thanks to @raulrita (https://www.twitch.tv/raulrita)
for live streaming and bringing light to these enhancements.
* [Support] `puts` statements that begin with "====" are rendered as teal in the Console. This
- provides a visual seperation that will help with print line debugging.
+ provides a visual separation that will help with print line debugging.
* [OSS] The DragonRuby Game Toolkit Console has been open sourced under an MIT license. You can find it
here: https://github.com/DragonRuby/dragonruby-game-toolkit-contrib
@@ -325,9 +582,9 @@
* [Support] The mygame directory now contains a documentation folder that provides high level
APIs for the engine (it's a work in progress, but a good starting point).
* [Samples] A sample app called "hash primitives" has been added that shows how you can use a Hash
- to render a primitive. This will make specifying sprite's advanced properites much easier.
+ to render a primitive. This will make specifying sprite's advanced properties much easier.
* [Samples] The sprite limits sample app shows how you can ducktype a class and render it as a sprite.
- Doing this is a tiny bit more work, but there is a huge perfomance benefit.
+ Doing this is a tiny bit more work, but there is a huge performance benefit.
* [Bugfix] Internal limits total textures/render targets/fonts is removed.
* [BREAKING] args.dragon has been deprecated, you must now use the new property args.gtk.
* [BREAKING] args.game has been deprecated, you must now use the new property args.state.
@@ -390,7 +647,7 @@
= release-20190731 =
- * [Bugfix] Fixed bug in dragon ruby console returning evalued value.
+ * [Bugfix] Fixed bug in DragonRuby console returning evaled value.
* [Support] Updated collisions sample app with comments.
* [Support] Added comments for sprite animation sample app.
* [Support] dragonruby-publish will look for your game in the
@@ -472,9 +729,9 @@
* [API] A new entity type has been introduced and is accessible via `args.state.new_entity_strict`
the usage of StrictEntity over OpenEntity (`args.state.new_entity`) yield significantly faster
property access. The downside is that `args.state.new_entity_strict` requires
- you to define all properties upfront within the implicit block. You will recieve
+ you to define all properties upfront within the implicit block. You will receive
an exception if you attempt to access a property that hasn't be
- pre-defined. For usage info and preformance differences, take a look at the Sprite Limit
+ pre-defined. For usage info and performance differences, take a look at the Sprite Limit
sample app.
* [Support] Exception messages have been significantly improved. Hashes and Type .to_s
information is well formatted. "stack too deep" exceptions resolved.
@@ -484,7 +741,7 @@
* [Support] Framerate warnings wait 10 seconds before calculating the moving average. If your
framerates begin to drop, consider using `args.state.new_entity_static` for game structures that have been
fleshed out.
- * [Performance] Rendering of primitives is can support over twice as many sprites at 60 fps (see Sprit Limits
+ * [Performance] Rendering of primitives is can support over twice as many sprites at 60 fps (see Sprite Limits
sample app for demonstration).
* [Support] Headless testing has been added. Take a look at the Basic Gorillas sample app to see
how headless testing can help you dial into frame-by-frame problems within your game.
@@ -507,7 +764,7 @@
args.outputs.sprites << create_sprite(x: 0, y: 0, w: 100, h: 100, vflip: true)
```
- We're still chewing on the API above before it get's integrated
+ We're still chewing on the API above before it gets integrated
into GTK proper.
* [API] "Superscript Two" can be used to bring up the DragonRuby
Console. People with international keyboards (which don't have a ~
@@ -597,11 +854,11 @@
course of Ruby the programming language.
* [Sample] The composition of primitives in DragonRuby GTK are
incredibly flexible. A sample app called "Fluid Primitives" has
- been beed added to show this flexibility.
+ been added to show this flexibility.
* [MacOS] [Linux] [Windows] If your screen resolution is below 720p,
the game will start at a smaller (but still aspect-correct) resolution.
* [Sample] Sample added showing `intersects_rect?` collision
- tollerances as a topdown level (similar to what's
+ tolerances as a topdown level (similar to what's
in Zelda for the NES).
= release-20190516 =
@@ -675,7 +932,7 @@
`notepad.exe`.
* [Packaging] Default icon added to `mygame`.
* [Samples] Reworked `doomwipe` (render targets tech demo) sample app so that
- it's less eratic before the effect is revealed.
+ it's less erratic before the effect is revealed.
* [Windows] Fixed bug where rendering would stop on Windows if the screen was
resized.
* [Size] Deleted files that don't need to be packaged with DragonRuby GTK.
diff --git a/deploy_template/mygame/app/main.rb b/deploy_template/mygame/app/main.rb
index a7d9ad6..5683cc2 100644
--- a/deploy_template/mygame/app/main.rb
+++ b/deploy_template/mygame/app/main.rb
@@ -1,6 +1,5 @@
def tick args
args.outputs.labels << [ 580, 500, 'Hello World!' ]
- args.outputs.labels << [ 475, 150, '(Consider reading README.txt now.)' ]
+ args.outputs.labels << [ 640, 460, 'Go to docs/docs.html and read it!', 5, 1 ]
args.outputs.sprites << [ 576, 310, 128, 101, 'dragonruby.png' ]
end
-
diff --git a/deploy_template/mygame/app/tests.rb b/deploy_template/mygame/app/tests.rb
index a60c8be..db71ff6 100644
--- a/deploy_template/mygame/app/tests.rb
+++ b/deploy_template/mygame/app/tests.rb
@@ -4,7 +4,7 @@
# Here is an example test and game
-# To run the test: ./dragonruby mygame --eval tests.rb --no-tick
+# To run the test: ./dragonruby mygame --eval app/tests.rb --no-tick
class MySuperHappyFunGame
attr_gtk
diff --git a/deploy_template/mygame/sprites/border-black.png b/deploy_template/mygame/sprites/border-black.png
new file mode 100644
index 0000000..c9d0bad
--- /dev/null
+++ b/deploy_template/mygame/sprites/border-black.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/dragon-0.png b/deploy_template/mygame/sprites/dragon-0.png
new file mode 100644
index 0000000..fb179af
--- /dev/null
+++ b/deploy_template/mygame/sprites/dragon-0.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/dragon-1.png b/deploy_template/mygame/sprites/dragon-1.png
new file mode 100644
index 0000000..8cfe531
--- /dev/null
+++ b/deploy_template/mygame/sprites/dragon-1.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/dragon-2.png b/deploy_template/mygame/sprites/dragon-2.png
new file mode 100644
index 0000000..cb462e1
--- /dev/null
+++ b/deploy_template/mygame/sprites/dragon-2.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/dragon-3.png b/deploy_template/mygame/sprites/dragon-3.png
new file mode 100644
index 0000000..04c4977
--- /dev/null
+++ b/deploy_template/mygame/sprites/dragon-3.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/dragon-4.png b/deploy_template/mygame/sprites/dragon-4.png
new file mode 100644
index 0000000..b29fa3d
--- /dev/null
+++ b/deploy_template/mygame/sprites/dragon-4.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/dragon-5.png b/deploy_template/mygame/sprites/dragon-5.png
new file mode 100644
index 0000000..99f4e74
--- /dev/null
+++ b/deploy_template/mygame/sprites/dragon-5.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-0.png b/deploy_template/mygame/sprites/explosion-0.png
new file mode 100644
index 0000000..f48636f
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-0.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-1.png b/deploy_template/mygame/sprites/explosion-1.png
new file mode 100644
index 0000000..b4018d9
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-1.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-2.png b/deploy_template/mygame/sprites/explosion-2.png
new file mode 100644
index 0000000..3abaedd
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-2.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-3.png b/deploy_template/mygame/sprites/explosion-3.png
new file mode 100644
index 0000000..fe94a5a
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-3.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-4.png b/deploy_template/mygame/sprites/explosion-4.png
new file mode 100644
index 0000000..ed04237
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-4.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-5.png b/deploy_template/mygame/sprites/explosion-5.png
new file mode 100644
index 0000000..2cd8f06
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-5.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-6.png b/deploy_template/mygame/sprites/explosion-6.png
new file mode 100644
index 0000000..e55909c
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-6.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/explosion-sheet.png b/deploy_template/mygame/sprites/explosion-sheet.png
new file mode 100644
index 0000000..8559a5c
--- /dev/null
+++ b/deploy_template/mygame/sprites/explosion-sheet.png
Binary files differ
diff --git a/deploy_template/mygame/sprites/star.png b/deploy_template/mygame/sprites/star.png
new file mode 100644
index 0000000..e0ee0f9
--- /dev/null
+++ b/deploy_template/mygame/sprites/star.png
Binary files differ
diff --git a/docs/docs.css b/docs/docs.css
index 7be4b7c..25cc677 100644
--- a/docs/docs.css
+++ b/docs/docs.css
@@ -84,7 +84,7 @@ hr:before {
pre {
border: solid 1px silver;
padding: 10px;
- font-size: 16px;
+ font-size: 14px;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
diff --git a/docs/docs.html b/docs/docs.html
index bb15adf..b92e9f8 100644
--- a/docs/docs.html
+++ b/docs/docs.html
@@ -28,8 +28,8 @@
<li><a href='#--using--args.state--to-store-your-game-state'>Using <code>args.state</code> To Store Your Game State</a></li>
<li><a href='#--frequently-asked-questions,-comments,-and-concerns'>Frequently Asked Questions, Comments, and Concerns</a></li>
<li><a href='#--docs---gtk--runtime-'><code>GTK::Runtime</code></a></li>
-<li><a href='#--docs---gtk--runtime#calcstringbox-'><code>GTK::Runtime#calcstringbox</code></a></li>
<li><a href='#--docs---gtk--runtime#reset-'><code>GTK::Runtime#reset</code></a></li>
+<li><a href='#--docs---gtk--runtime#calcstringbox-'><code>GTK::Runtime#calcstringbox</code></a></li>
<li><a href='#--docs---array-'><code>Array</code></a></li>
<li><a href='#--docs---array#map-'><code>Array#map</code></a></li>
<li><a href='#--docs---array#each-'><code>Array#each</code></a></li>
@@ -40,39 +40,38 @@
<li><a href='#--docs---array#include_any?-'><code>Array#include_any?</code></a></li>
<li><a href='#--docs---array#any_intersect_rect?-'><code>Array#any_intersect_rect?</code></a></li>
<li><a href='#--docs---gtk--outputs-'><code>GTK::Outputs</code></a></li>
-<li><a href='#--docs---gtk--outputs#borders-'><code>GTK::Outputs#borders</code></a></li>
<li><a href='#--docs---gtk--outputs#solids-'><code>GTK::Outputs#solids</code></a></li>
+<li><a href='#--docs---gtk--outputs#borders-'><code>GTK::Outputs#borders</code></a></li>
<li><a href='#--docs---gtk--mouse-'><code>GTK::Mouse</code></a></li>
<li><a href='#--docs---gtk--mousepoint-'><code>GTK::MousePoint</code></a></li>
<li><a href='#--docs---gtk--openentity-'><code>GTK::OpenEntity</code></a></li>
<li><a href='#--docs---gtk--openentity#as_hash-'><code>GTK::OpenEntity#as_hash</code></a></li>
<li><a href='#--docs---numeric#frame_index-'><code>Numeric#frame_index</code></a></li>
+<li><a href='#--docs---numeric#elapsed_time-'><code>Numeric#elapsed_time</code></a></li>
+<li><a href='#--docs---numeric#elapsed?-'><code>Numeric#elapsed?</code></a></li>
+<li><a href='#--docs---numeric#created?-'><code>Numeric#created?</code></a></li>
<li><a href='#--docs---kernel-'><code>Kernel</code></a></li>
<li><a href='#--docs---kernel--tick_count-'><code>Kernel::tick_count</code></a></li>
<li><a href='#--docs---kernel--global_tick_count-'><code>Kernel::global_tick_count</code></a></li>
</ul>
</div>
<div id='content'>
- </ol>
-<h1 id='--dragonruby-game-toolkit-live-docs'>DragonRuby Game Toolkit Live Docs</h1>
+ <h1 id='--dragonruby-game-toolkit-live-docs'>DragonRuby Game Toolkit Live Docs</h1>
<p>
The information contained here is all available via the DragonRuby Console. You can Open the DragonRuby Console by pressing [`] [~] [²] [^] [º] or [§] within your game.
</p>
<p>
To search docs you can type <code>docs_search "SEARCH TERM"</code> or if you want to get fancy you can provide a <code>lambda</code> to filter documentation:
</p>
-</ol>
<pre>docs_search { |entry| (entry.include? "Array") && (!entry.include? "Enumerable") }
</pre>
<p>
<img src='docs_search.gif'></img>
</p>
-</ol>
<h1 id='--hello-world'>Hello World</h1>
<p>
Welcome to DragonRuby Game Toolkit. Take the steps below to get started.
</p>
-</ol>
<h1 id='--join-the-discord-and-subscribe-to-the-news-letter'>Join the Discord and Subscribe to the News Letter</h1>
<p>
Our Discord channel is <a href='http://discord.dragonruby.org'>http://discord.dragonruby.org</a>.
@@ -83,7 +82,6 @@ The News Letter will keep you in the loop with regards to current DragonRuby Eve
<p>
Those who use DragonRuby are called Dragon Riders. This identity is incredibly important to us. When someone asks you:
</p>
-</ol>
<blockquote>
<p>
What game engine do you use?
@@ -92,13 +90,11 @@ What game engine do you use?
<p>
Reply with:
</p>
-</ol>
<blockquote>
<p>
I am a Dragon Rider.
</p>
</blockquote>
-</ol>
<h1 id='--watch-some-intro-videos'>Watch Some Intro Videos</h1>
<p>
Each video is only 20 minutes and all of them will fit into a lunch break. So please watch them:
@@ -114,12 +110,10 @@ The second and third videos are not required if you are proficient with Ruby, bu
<p>
You may also want to try this free course provided at <a href='http://dragonruby.school'>http://dragonruby.school</a>.
</p>
-</ol>
<h1 id='--getting-started-tutorial'>Getting Started Tutorial</h1>
<p>
This is a tutorial written by Ryan C Gordon (a Juggernaut in the industry who has contracted to Valve, Epic, Activision, and EA... check out his Wikipedia page: <a href='https://en.wikipedia.org/wiki/Ryan_C._Gordon'>https://en.wikipedia.org/wiki/Ryan_C._Gordon</a>).
</p>
-</ol>
<h2>Introduction</h2>
<p>
Welcome!
@@ -136,7 +130,6 @@ Did you not know that? Did you think you couldn't write a game because you're a
<p>
Here, we're going to be programming in a language called "Ruby." In the interest of full disclosure, I (Ryan Gordon) wrote the C parts of this toolkit and Ruby looks a little strange to me (Amir Rajan wrote the Ruby parts, discounting the parts I mangled), but I'm going to walk you through the basics because we're all learning together, and if you mostly think of yourself as someone that writes C (or C++, C#, Objective-C), PHP, or Java, then you're only a step behind me right now.
</p>
-</ol>
<h2>Prerequisites</h2>
<p>
Here's the most important thing you should know: Ruby lets you do some complicated things really easily, and you can learn that stuff later. I'm going to show you one or two cool tricks, but that's all.
@@ -144,7 +137,6 @@ Here's the most important thing you should know: Ruby lets you do some complicat
<p>
Do you know what an if statement is? A for-loop? An array? That's all you'll need to start.
</p>
-</ol>
<h2>The Game Loop</h2>
<p>
Ok, here are few rules with regards to game development with GTK:
@@ -160,7 +152,6 @@ That's an entire video game in one run-on sentence.
<p>
Here's that function. You're going to want to put this in mygame/app/main.rb, because that's where we'll look for it by default. Load it up in your favorite text editor.
</p>
-</ol>
<pre>def tick args
args.outputs.labels << [580, 400, 'Hello World!']
end
@@ -168,12 +159,10 @@ end
<p>
Now run <code>dragonruby</code> ...did you get a window with "Hello World!" written in it? Good, you're officially a game developer!
</p>
-</ol>
<h2>Breakdown Of The <code>tick</code> Method</h2>
<p>
<code>mygame/app/main.rb</code>, is where the Ruby source code is located. This looks a little strange, so I'll break it down line by line. In Ruby, a '#' character starts a single-line comment, so I'll talk about this inline.
</p>
-</ol>
<pre># This "def"ines a function, named "tick," which takes a single argument
# named "args". DragonRuby looks for this function and calls it every
# frame, 60 times a second. "args" is a magic structure with lots of
@@ -195,7 +184,6 @@ end
<p>
Once your <code>tick</code> function finishes, we look at all the arrays you made and figure out how to draw it. You don't need to know about graphics APIs. You're just setting up some arrays! DragonRuby clears out these arrays every frame, so you just need to add what you need _right now_ each time.
</p>
-</ol>
<h2>Rendering A Sprite</h2>
<p>
Now let's spice this up a little.
@@ -206,7 +194,6 @@ We're going to add some graphics. Each 2D image in DragonRuby is called a "sprit
<p>
There's a "dragonruby.png" file included, just to get you started. Let's have it draw every frame with our text:
</p>
-</ol>
<pre>def tick args
args.outputs.labels << [580, 400, 'Hello World!']
args.outputs.sprites << [576, 100, 128, 101, 'dragonruby.png']
@@ -218,7 +205,6 @@ end
<p>
That <code>.sprites</code> line says "add a sprite to the list of sprites we're drawing, and draw it at position (576, 100) at a size of 128x101 pixels". You can find the image to draw at dragonruby.png.
</p>
-</ol>
<h2>Coordinate System and Virtual Canvas</h2>
<p>
Quick note about coordinates: (0, 0) is the bottom left corner of the screen, and positive numbers go up and to the right. This is more "geometrically correct," even if it's not how you remember doing 2D graphics, but we chose this for a simpler reason: when you're making Super Mario Brothers and you want Mario to jump, you should be able to add to Mario's y position as he goes up and subtract as he falls. It makes things easier to understand.
@@ -229,7 +215,6 @@ Also: your game screen is _always_ 1280x720 pixels. If you resize the window, we
<p>
Ok, now we have an image on the screen, let's animate it:
</p>
-</ol>
<pre>def tick args
args.state.rotation ||= 0
args.outputs.labels << [580, 400, 'Hello World!' ]
@@ -240,7 +225,6 @@ end
<p>
Now you can see that this function is getting called a lot!
</p>
-</ol>
<h2>Game State</h2>
<p>
Here's a fun Ruby thing: <code>args.state.rotation ||= 0</code> is shorthand for "if args.state.rotation isn't initialized, set it to zero." It's a nice way to embed your initialization code right next to where you need the variable.
@@ -248,7 +232,6 @@ Here's a fun Ruby thing: <code>args.state.rotation ||= 0</code> is shorthand for
<p>
<code>args.state</code> is a place you can hang your own data and have it survive past the life of the function call. In this case, the current rotation of our sprite, which is happily spinning at 60 frames per second. If you don't specify rotation (or alpha, or color modulation, or a source rectangle, etc), DragonRuby picks a reasonable default, and the array is ordered by the most likely things you need to tell us: position, size, name.
</p>
-</ol>
<h2>There Is No Delta Time</h2>
<p>
One thing we decided to do in DragonRuby is not make you worry about delta time: your function runs at 60 frames per second (about 16 milliseconds) and that's that. Having to worry about framerate is something massive triple-AAA games do, but for fun little 2D games? You'd have to work really hard to not hit 60fps. All your drawing is happening on a GPU designed to run Fortnite quickly; it can definitely handle this.
@@ -256,12 +239,10 @@ One thing we decided to do in DragonRuby is not make you worry about delta time:
<p>
Since we didn't make you worry about delta time, you can just move the rotation by 1 every time and it works without you having to keep track of time and math. Want it to move faster? Subtract 2.
</p>
-</ol>
<h2>Handling User Input</h2>
<p>
Now, let's move that image around.
</p>
-</ol>
<pre>def tick args
args.state.rotation ||= 0
args.state.x ||= 576
@@ -286,7 +267,6 @@ end
<p>
Everywhere you click your mouse, the image moves there. We set a default location for it with <code>args.state.x ||= 576</code>, and then we change those variables when we see the mouse button in action. You can get at the keyboard and game controllers in similar ways.
</p>
-</ol>
<h2>Coding On A Raspberry Pi</h2>
<p>
We have only tested DragonRuby on a Raspberry Pi 3, Models B and B+, but we believe it _should_ work on any model with comparable specs.
@@ -297,7 +277,6 @@ If you're running DragonRuby Game Toolkit on a Raspberry Pi, or trying to run a
<p>
You're probably running a desktop environment: menus, apps, web browsers, etc. This is okay! Launch the terminal app and type:
</p>
-</ol>
<pre>do raspi-config
</pre>
<p>
@@ -309,12 +288,10 @@ If you're _still_ having problems and have a Raspberry Pi 2 or better, go back t
<p>
Note that you can also run DragonRuby without X11 at all: if you run it from a virtual terminal it will render fullscreen and won't need the "Full KMS" option. This might be attractive if you want to use it as a game console sort of thing, or develop over ssh, or launch it from RetroPie, etc.
</p>
-</ol>
<h2>Conclusion</h2>
<p>
There is a lot more you can do with DragonRuby, but now you've already got just about everything you need to make a simple game. After all, even the most fancy games are just creating objects and moving them around. Experiment a little. Add a few more things and have them interact in small ways. Want something to go away? Just don't add it to <code>args.output</code> anymore.
</p>
-</ol>
<h2>IMPORTANT: Go Through All Of The Sample Apps! Study Them Thoroughly!!</h2>
<p>
Now that you've completed the Hello World tutorial. Head over to the `samples` directory. It is very very important that you study the sample apps thoroughly! Go through them in order. Here is a short description of each sample app.
@@ -364,9 +341,7 @@ Now that you've completed the Hello World tutorial. Head over to the `samples` d
<li> 14_sprite_limits_static_references: Upper limit for how many sprites can be rendered to the screen using <code>static</code> output collections (which are updated by reference as opposed to by value).</li>
<li> 15_collision_limits: How many collisions can be processed across many primitives.</li>
<li> 18_moddable_game: How you can make a game where content is authored by the player (modding support).</li>
-<li> 19_lowrez_jam_01_hello_world: How to use <code>render_targets</code> to create a low resolution game.</li>
-<li> 19_lowrez_jam_02_buttons: How to use <code>render_targets</code> to create a low resolution game.</li>
-<li> 19_lowrez_jam_03_space_shooter: How to use <code>render_targets</code> to create a low resolution game.</li>
+<li> 19_lowrez_jam: How to use <code>render_targets</code> to create a low resolution game.</li>
<li> 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.</li>
<li> 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.</li>
<li> 21_mailbox_usage: How to do interprocess communication.</li>
@@ -392,7 +367,6 @@ Now that you've completed the Hello World tutorial. Head over to the `samples` d
<p>
Once you've built your game, you're all set to deploy! Good luck in your game dev journey and if you get stuck, come to the Discord channel!
</p>
-</ol>
<h2>Creating Your Game Landing Page</h2>
<p>
Log into Itch.io and go to <a href='https://itch.io/game/new'>https://itch.io/game/new</a>.
@@ -408,7 +382,6 @@ Log into Itch.io and go to <a href='https://itch.io/game/new'>https://itch.io/ga
<p>
You can fill out all the other options later.
</p>
-</ol>
<h2>Update Your Game's Metadata</h2>
<p>
Point your text editor at mygame/metadata/game_metadata.txt and make it look like this:
@@ -416,7 +389,6 @@ Point your text editor at mygame/metadata/game_metadata.txt and make it look lik
<p>
NOTE: Remove the <code>#</code> at the beginning of each line.
</p>
-</ol>
<pre>vid=bob
vtitle=Bob The Game Developer
meid=mygame
@@ -426,12 +398,10 @@ rsion=0.1
<p>
The <code>devid</code> property is the username you use to log into Itch.io. The <code>devtitle</code> is your name or company name (it can contain spaces). The <code>gameid</code> is the Project URL value. The <code>gametitle</code> is the name of your game (it can contain spaces). The <code>version</code> can be any <code>major.minor</code> number format.
</p>
-</ol>
<h2>Building Your Game For Distribution</h2>
<p>
Open up the terminal and run this from the command line:
</p>
-</ol>
<pre>dragonruby-publish --only-package mygame
</pre>
<p>
@@ -446,7 +416,6 @@ For the HTML version of your game after you upload it. Check the checkbox labele
<p>
For subsequent updates you can use an automated deployment to Itch.io:
</p>
-</ol>
<pre>dragonruby-publish mygame
</pre>
<p>
@@ -455,17 +424,14 @@ DragonRuby will package _and publish_ your game to itch.io! Tell your friends to
<p>
If you make changes to your game, just re-run dragonruby-publish and it'll update the downloads for you.
</p>
-</ol>
<h2>DragonRuby's Philosophy</h2>
<p>
The following tenants of DragonRuby are what set us apart from other game engines. Given that Game Toolkit is a relatively new engine, there are definitely features that are missing. So having a big check list of "all the cool things" is not this engine's forte. This is compensated with a strong commitment to the following principals.
</p>
-</ol>
<h3>Challenge The Status Quo</h3>
<p>
Game engines of today are in a local maximum and don't take into consideration the challenges of this day and age. Unity and GameMaker specifically rot your brain. It's not sufficient to say:
</p>
-</ol>
<blockquote>
<p>
But that's how we've always done it.
@@ -474,7 +440,6 @@ But that's how we've always done it.
<p>
It's a hard pill to swallow, but forget blindly accepted best practices and try to figure out the underlying motivation for a specific approach to game development. Collaborate with us.
</p>
-</ol>
<h3>Release Often And Quickly</h3>
<p>
The biggest mistake game devs make is spending too much time in isolation building their game. Release something, however small, and release it quickly.
@@ -485,13 +450,11 @@ Stop worrying about everything being pixel perfect. Don't wait until your game i
<p>
Remember:
</p>
-</ol>
<blockquote>
<p>
Real artists ship.
</p>
</blockquote>
-</ol>
<h3>Sustainable And Ethical Monetization</h3>
<p>
We all aspire to put food on the table doing what we love. Whether it is building games, writing tools to support game development, or anything in between.
@@ -499,7 +462,6 @@ We all aspire to put food on the table doing what we love. Whether it is buildin
<p>
Charge a fair amount of money for the things you create. It's expected and encouraged within the community. Give what you create away for free to those that can't afford it.
</p>
-</ol>
<h3>Sustainable And Ethical Open Source</h3>
<p>
This goes hand in hand with sustainable and ethical monetization. The current state of open source is not sustainable. There is an immense amount of contributor burnout. Users of open source expect everything to be free, and few give back. This is a problem we want to fix (we're still trying to figure out the best solution).
@@ -507,17 +469,14 @@ This goes hand in hand with sustainable and ethical monetization. The current st
<p>
So, don't be "that guy" in the Discord that says "DragonRuby should be free and open source!" You will be personally flogged by Amir.
</p>
-</ol>
<h3>People Over Entities</h3>
<p>
We prioritize the endorsement of real people over faceless entities. This game engine, and other products we create, are not insignificant line items of a large company. And you aren't a generic "commodity" or "corporate resource". So be active in the community Discord and you'll reap the benefits as more devs use DragonRuby.
</p>
-</ol>
<h3>Building A Game Should Be Fun And Bring Happiness</h3>
<p>
We will prioritize the removal of pain. The aesthetics of Ruby make it such a joy to work with, and we want to capture that within the engine.
</p>
-</ol>
<h3>Real World Application Drives Features</h3>
<p>
We are bombarded by marketing speak day in and day out. We don't do that here. There are things that are really great in the engine, and things that need a lot of work. Collaborate with us so we can help you reach your goals. Ask for features you actually need as opposed to anything speculative.
@@ -525,27 +484,22 @@ We are bombarded by marketing speak day in and day out. We don't do that here. T
<p>
We want DragonRuby to *actually* help you build the game you want to build (as opposed to sell you something piece of demoware that doesn't work).
</p>
-</ol>
<h1 id='--how-to-determine-what-frame-you-are-on'>How To Determine What Frame You Are On</h1>
<p>
There is a property on <code>state</code> called <code>tick_count</code> that is incremented by DragonRuby every time the <code>tick</code> method is called. The following code renders a label that displays the current <code>tick_count</code>.
</p>
-</ol>
<pre>def tick args
args.outputs.labels << [10, 670, "#{args.state.tick_count}"]
end
</pre>
-</ol>
<h1 id='--how-to-get-current-framerate'>How To Get Current Framerate</h1>
<p>
Current framerate is a top level property on the Game Toolkit Runtime and is accessible via <code>args.gtk.current_framerate</code>.
</p>
-</ol>
<pre>def tick args
args.outputs.labels << [10, 710, "framerate: #{args.gtk.current_framerate.round}"]
end
</pre>
-</ol>
<h1 id='--how-to-render-a-sprite-using-an-array'>How To Render A Sprite Using An Array</h1>
<p>
All file paths should use the forward slash <code>/</code> *not* backslash <code></code>. Game Toolkit includes a number of sprites in the <code>sprites</code> folder (everything about your game is located in the <code>mygame</code> directory).
@@ -556,7 +510,6 @@ The following code renders a sprite with a <code>width</code> and <code>height</
<p>
<code>args.outputs.sprites</code> is used to render a sprite.
</p>
-</ol>
<pre>def tick args
args.outputs.sprites << [
640 - 50, # X
@@ -567,12 +520,10 @@ The following code renders a sprite with a <code>width</code> and <code>height</
]
end
</pre>
-</ol>
<h1 id='--more-sprite-properties-as-an-array'>More Sprite Properties As An Array</h1>
<p>
Here are all the properties you can set on a sprite.
</p>
-</ol>
<pre>def tick args
args.outputs.sprites << [
100, # X
@@ -588,7 +539,6 @@ Here are all the properties you can set on a sprite.
]
end
</pre>
-</ol>
<h1 id='--different-sprite-representations'>Different Sprite Representations</h1>
<p>
Using ordinal positioning can get a little unruly given so many properties you have control over.
@@ -596,7 +546,6 @@ Using ordinal positioning can get a little unruly given so many properties you h
<p>
You can represent a sprite as a <code>Hash</code>:
</p>
-</ol>
<pre>def tick args
args.outputs.sprites << {
x: 640 - 50,
@@ -623,7 +572,6 @@ end
<p>
You can represent a sprite as an <code>object</code>:
</p>
-</ol>
<pre># Create type with ALL sprite properties AND primitive_marker
class Sprite
attr_accessor :x, :y, :w, :h, :path, :angle, :a, :r, :g, :b,
@@ -654,7 +602,6 @@ def tick args
h: 50)
end
</pre>
-</ol>
<h1 id='--how-to-render-a-label'>How To Render A Label</h1>
<p>
<code>args.outputs.labels</code> is used to render labels.
@@ -665,24 +612,19 @@ Labels are how you display text. This code will go directly inside of the <code>
<p>
Here is the minimum code:
</p>
-</ol>
<pre>def tick args
# X Y TEXT
args.outputs.labels << [640, 360, "I am a black label."]
end
</pre>
-</ol>
<h1 id='--a-colored-label'>A Colored Label</h1>
-</ol>
<pre>def tick args
# A colored label
# X Y TEXT, RED GREEN BLUE ALPHA
args.outputs.labels << [640, 360, "I am a redish label.", 255, 128, 128, 255]
end
</pre>
-</ol>
<h1 id='--extended-label-properties'>Extended Label Properties</h1>
-</ol>
<pre>def tick args
# A colored label
# X Y TEXT SIZE ALIGNMENT RED GREEN BLUE ALPHA FONT FILE
@@ -706,12 +648,10 @@ A <code>SIZE_ENUM</code> of <code>0</code> represents "default size". A <code>ne
<p>
An <code>ALIGNMENT_ENUM</code> of <code>0</code> represents "left aligned". <code>1</code> represents "center aligned". <code>2</code> represents "right aligned".
</p>
-</ol>
<h1 id='--rendering-a-label-as-a--hash-'>Rendering A Label As A <code>Hash</code></h1>
<p>
You can add additional metadata about your game within a label, which requires you to use a `Hash` instead.
</p>
-</ol>
<pre>def tick args
args.outputs.labels << {
x: 200,
@@ -734,12 +674,10 @@ You can add additional metadata about your game within a label, which requires y
}
end
</pre>
-</ol>
<h1 id='--getting-the-size-of-a-piece-of-text'>Getting The Size Of A Piece Of Text</h1>
<p>
You can get the render size of any string using <code>args.gtk.calcstringbox</code>.
</p>
-</ol>
<pre>def tick args
# TEXT SIZE_ENUM FONT
w, h = args.gtk.calcstringbox("some string", 0, "font.ttf")
@@ -755,12 +693,10 @@ You can get the render size of any string using <code>args.gtk.calcstringbox</co
]
end
</pre>
-</ol>
<h1 id='--how-to-play-a-sound'>How To Play A Sound</h1>
<p>
Sounds that end <code>.wav</code> will play once:
</p>
-</ol>
<pre>def tick args
# Play a sound every second
if (args.state.tick_count % 60) == 0
@@ -771,7 +707,6 @@ end
<p>
Sounds that end <code>.ogg</code> is considered background music and will loop:
</p>
-</ol>
<pre>def tick args
# Start a sound loop at the beginning of the game
if args.state.tick_count == 0
@@ -782,7 +717,6 @@ end
<p>
If you want to play a <code>.ogg</code> once as if it were a sound effect, you can do:
</p>
-</ol>
<pre>def tick args
# Play a sound every second
if (args.state.tick_count % 60) == 0
@@ -790,7 +724,6 @@ If you want to play a <code>.ogg</code> once as if it were a sound effect, you c
end
end
</pre>
-</ol>
<h1 id='--using--args.state--to-store-your-game-state'>Using <code>args.state</code> To Store Your Game State</h1>
<p>
<code>args.state</code> is a open data structure that allows you to define properties that are arbitrarily nested. You don't need to define any kind of <code>class</code>.
@@ -801,7 +734,6 @@ To initialize your game state, use the <code>||=</code> operator. Any value on t
<p>
To assign a value every frame, just use the <code>=</code> operator, but _make sure_ you've initialized a default value.
</p>
-</ol>
<pre>def tick args
# initialize your game state ONCE
args.player.x ||= 0
@@ -826,14 +758,11 @@ To assign a value every frame, just use the <code>=</code> operator, but _make s
]
end
</pre>
-</ol>
<h1 id='--frequently-asked-questions,-comments,-and-concerns'>Frequently Asked Questions, Comments, and Concerns</h1>
<p>
Here are questions, comments, and concerns that frequently come up.
</p>
-</ol>
<h2>Frequently Asked Questions</h2>
-</ol>
<h3>What is DragonRuby LLP?</h3>
<p>
DragonRuby LLP is a partnership of four devs who came together with the goal of bringing the aesthetics and joy of Ruby, everywhere possible.
@@ -861,25 +790,21 @@ NOTE: We leave the "A DragonRuby LLP Product" off of this one because that just
<p>
NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass identifier huh?
</p>
-</ol>
<h3>What is DragonRuby?</h3>
<p>
The response to this question requires a few subparts. First we need to clarify some terms. Specifically _language specification_ vs _runtime_.
</p>
-</ol>
<h3>Okay... so what is the difference between a language specification and a runtime?</h3>
<p>
-A runtime is an _implementation_ of a langauge specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."
+A runtime is an _implementation_ of a language specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."
</p>
<p>
But, there are many Ruby Runtimes: CRuby/MRI, JRuby, Truffle, Rubinius, Artichoke, and (last but certainly not least) DragonRuby.
</p>
-</ol>
<h3>Okay... what language specification does DragonRuby use then?</h3>
<p>
DragonRuby's goal is to be compliant with the ISO/IEC 30170:2012 standard. It's syntax is Ruby 2.x compatible, but also contains semantic changes that help it natively interface with platform specific libraries.
</p>
-</ol>
<h3>So... why another runtime?</h3>
<p>
The elevator pitch is:
@@ -887,7 +812,6 @@ The elevator pitch is:
<p>
DragonRuby is a Multilevel Cross-platform Runtime. The "multiple levels" within the runtime allows us to target platforms no other Ruby can target: PC, Mac, Linux, Raspberry Pi, WASM, iOS, Android, Nintendo Switch, PS4, Xbox, and Scadia.
</p>
-</ol>
<h3>What does Multilevel Cross-platform mean?</h3>
<p>
There are complexities associated with targeting all the platforms we support. Because of this, the runtime had to be architected in such a way that new platforms could be easily added (which lead to us partitioning the runtime internally):
@@ -900,27 +824,24 @@ There are complexities associated with targeting all the platforms we support. B
C-Extensions.</li>
</ul>
<p>
-Levels 1 through 3 are fairly commonplace in many runtime implemenations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:
+Levels 1 through 3 are fairly commonplace in many runtime implementations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:
</p>
<ul>
<li>Level 4 consists of shared abstractions around hardware I/O and operating
- system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a lowlevel multimedia component library that has been in active development for 22 years and counting).</li>
-<li>Level 5 is a codegeneration layer which creates metadata that allows
- for native interopability with host runtime libraries. It also includes OS specific message pump orchestrations.</li>
+ system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a low level multimedia component library that has been in active development for 22 years and counting).</li>
+<li>Level 5 is a code generation layer which creates metadata that allows
+ for native interoperability with host runtime libraries. It also includes OS specific message pump orchestrations.</li>
<li>Level 6 is a Ahead of Time/Just in Time Ruby compiler built with LLVM. This
compiler outputs _very_ fast platform specific bitcode, but only supports a subset of the Ruby language specification.</li>
</ul>
<p>
-These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good seperation between these two worlds; and provides a means to add new platforms without going insane.
+These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good separation between these two worlds; and provides a means to add new platforms without going insane.
</p>
-</ol>
<h3>Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?</h3>
<p>
-DragonRuby is a Ruby runtime implentation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.
+DragonRuby is a Ruby runtime implementation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.
</p>
-</ol>
<h2>Frequent Comments</h2>
-</ol>
<h3>But Ruby is dead.</h3>
<p>
Let's check the official source for the answer to this question: isrubydead.com: <a href='https://isrubydead.com/'>https://isrubydead.com/</a>.
@@ -934,31 +855,27 @@ What really matters is _quality/maturity_. Here is the latest (StackOverflow Sur
<p>
Let's stop making this comment shall we?
</p>
-</ol>
<h3>But Ruby is slow.</h3>
<p>
That doesn't make any sense. A language specification can't be slow... it's a language spec. Sure, an _implementation/runtime_ can be slow though, but then we'd have to talk about which runtime.
</p>
-</ol>
-<h3>Dynamic langauges are slow.</h3>
+<h3>Dynamic languages are slow.</h3>
<p>
They are certainly slower than statically compiled languages. With the processing power and compiler optimizations we have today, dynamic languages like Ruby are _fast enough_.
</p>
<p>
-Unless you are writing in some form of intermediate representation by hand, your langauge of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.
+Unless you are writing in some form of intermediate representation by hand, your language of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.
</p>
<p>
-NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skillset. Email us ^_^.
+NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skill set. Email us ^_^.
</p>
-</ol>
<h2>Frequent Concerns</h2>
-</ol>
<h3>DragonRuby is not open source. That's not right.</h3>
<p>
-The current state of open source is unsustainable. Contributors work for free, most all open source repositories are serverly understaffed, and burnout from core members is rampant.
+The current state of open source is unsustainable. Contributors work for free, most all open source repositories are severely under-staffed, and burnout from core members is rampant.
</p>
<p>
-We believe in open source very strongly. Parts of DragonRuby are infact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.
+We believe in open source very strongly. Parts of DragonRuby are in fact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.
</p>
<p>
If you have ideas on how we can do this, email us!
@@ -966,7 +883,6 @@ If you have ideas on how we can do this, email us!
<p>
If the reason above isn't sufficient, then definitely use something else.
</p>
-</ol>
<h3>DragonRuby is for pay. You should offer a free version.</h3>
<p>
If you can afford to pay for DragonRuby, you should (and will). We don't go around telling writers that they should give us their books for free, and only require payment if we read the entire thing. It's time we stop asking that of software products.
@@ -989,7 +905,6 @@ You qualify for a free, unrestricted license to DragonRuby products if any of th
<p>
Just contact Amir at [email protected] with a short explanation of your current situation and he'll set you up. No questions asked.
</p>
-</ol>
<h3>But still, you should offer a free version. So I can try it out and see if I like it.</h3>
<p>
You can try our [web-based sandbox environment](). But it won't do the runtime justice. Or just come to our [Slack]() or [Discord]() channel and ask questions. We'd be happy to have a one on one video chat with you and show off all the cool stuff we're doing.
@@ -997,49 +912,41 @@ You can try our [web-based sandbox environment](). But it won't do the runtime j
<p>
Seriously just buy it. Get a refund if you don't like it. We make it stupid easy to do so.
</p>
-</ol>
<h3>I still think you should do a free version. Think of all people who would give it a shot.</h3>
<p>
Free isn't a sustainable financial model. We don't want to spam your email. We don't want to collect usage data off of you either. We just want to provide quality toolchains to quality developers (as opposed to a large quantity of developers).
</p>
<p>
-The peiple that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.
+The people that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.
</p>
-</ol>
<h3>What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent.</h3>
<p>
That won't happen if the development world stop asking for free stuff and non-trivially compensate open source developers. Look, we want to be able to work on the stuff we love, every day of our lives. And we'll go to great lengths to make that happen.
</p>
<p>
-But, in the event that sad day comes, our partnershiop bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.
+But, in the event that sad day comes, our partnership bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.
</p>
-</ol>
<h1 id='--docs---gtk--runtime-'><code>GTK::Runtime</code></h1>
<p>
The GTK::Runtime class is the core of DragonRuby. It is globally accessible via <code>$gtk</code>.
</p>
-</ol>
+<h1 id='--docs---gtk--runtime#reset-'><code>GTK::Runtime#reset</code></h1>
+<p>
+This function will reset Kernel.tick_count to 0 and will remove all data from args.state.
+</p>
<h1 id='--docs---gtk--runtime#calcstringbox-'><code>GTK::Runtime#calcstringbox</code></h1>
<p>
This function returns the width and height of a string.
</p>
-</ol>
<pre>def tick args
args.state.string_size ||= args.gtk.calcstringbox "Hello World"
args.state.string_size_font_size ||= args.gtk.calcstringbox "Hello World"
end
</pre>
-</ol>
-<h1 id='--docs---gtk--runtime#reset-'><code>GTK::Runtime#reset</code></h1>
-<p>
-This function will reset Kernel.tick_count to 0 and will remove all data from args.state.
-</p>
-</ol>
<h1 id='--docs---array-'><code>Array</code></h1>
<p>
The Array class has been extend to provide methods that will help in common game development tasks. Array is one of the most powerful classes in Ruby and a very fundamental component of Game Toolkit.
</p>
-</ol>
<h1 id='--docs---array#map-'><code>Array#map</code></h1>
<p>
The function given a block returns a new <code>Enumerable</code> of values.
@@ -1047,7 +954,6 @@ The function given a block returns a new <code>Enumerable</code> of values.
<p>
Example of using <code>Array#map</code> in conjunction with <code>args.state</code> and <code>args.outputs.sprites</code> to render sprites to the screen.
</p>
-</ol>
<pre>def tick args
# define the colors of the rainbow in ~args.state~
# as an ~Array~ of ~Hash~es with :order and :name.
@@ -1079,7 +985,6 @@ Example of using <code>Array#map</code> in conjunction with <code>args.state</co
end
end
</pre>
-</ol>
<h1 id='--docs---array#each-'><code>Array#each</code></h1>
<p>
The function, given a block, invokes the block for each item in the <code>Array</code>. <code>Array#each</code> is synonymous to foreach constructs in other languages.
@@ -1087,7 +992,6 @@ The function, given a block, invokes the block for each item in the <code>Array<
<p>
Example of using <code>Array#each</code> in conjunction with <code>args.state</code> and <code>args.outputs.sprites</code> to render sprites to the screen:
</p>
-</ol>
<pre>def tick args
# define the colors of the rainbow in ~args.state~
# as an ~Array~ of ~Hash~es with :order and :name.
@@ -1118,12 +1022,10 @@ Example of using <code>Array#each</code> in conjunction with <code>args.state</c
end
end
</pre>
-</ol>
<h1 id='--docs---array#reject_nil-'><code>Array#reject_nil</code></h1>
<p>
Returns an <code>Enumerable</code> rejecting items that are <code>nil</code>, this is an alias for <code>Array#compact</code>:
</p>
-</ol>
<pre>repl do
a = [1, nil, 4, false, :a]
puts a.reject_nil
@@ -1132,19 +1034,16 @@ Returns an <code>Enumerable</code> rejecting items that are <code>nil</code>, th
# => [1, 4, false, :a]
end
</pre>
-</ol>
<h1 id='--docs---array#reject_false-'><code>Array#reject_false</code></h1>
<p>
Returns an `Enumerable` rejecting items that are `nil` or `false`.
</p>
-</ol>
<pre>repl do
a = [1, nil, 4, false, :a]
puts a.reject_false
# => [1, 4, :a]
end
</pre>
-</ol>
<h1 id='--docs---array#product-'><code>Array#product</code></h1>
<p>
Returns all combinations of values between two arrays.
@@ -1152,14 +1051,12 @@ Returns all combinations of values between two arrays.
<p>
Here are some examples of using <code>product</code>. Paste the following code at the bottom of main.rb and save the file to see the results:
</p>
-</ol>
<pre>repl do
a = [0, 1]
puts a.product
# => [[0, 0], [0, 1], [1, 0], [1, 1]]
end
</pre>
-</ol>
<pre>repl do
a = [ 0, 1]
b = [:a, :b]
@@ -1167,12 +1064,10 @@ end
# => [[0, :a], [0, :b], [1, :a], [1, :b]]
end
</pre>
-</ol>
<h1 id='--docs---array#map_2d-'><code>Array#map_2d</code></h1>
<p>
Assuming the array is an array of arrays, Given a block, each 2D array index invoked against the block. A 2D array is a common way to store data/layout for a stage.
</p>
-</ol>
<pre>repl do
stage = [
[:enemy, :empty, :player],
@@ -1195,12 +1090,10 @@ Assuming the array is an array of arrays, Given a block, each 2D array index inv
puts occupied_tiles
end
</pre>
-</ol>
<h1 id='--docs---array#include_any?-'><code>Array#include_any?</code></h1>
<p>
Given a collection of items, the function will return <code>true</code> if any of <code>self</code>'s items exists in the collection of items passed in:
</p>
-</ol>
<h1 id='--docs---array#any_intersect_rect?-'><code>Array#any_intersect_rect?</code></h1>
<p>
Assuming the array contains objects that respond to <code>left</code>, <code>right</code>, <code>top</code>, <code>bottom</code>, this method returns <code>true</code> if any of the elements within the array intersect the object being passed in. You are given an optional parameter called <code>tolerance</code> which informs how close to the other rectangles the elements need to be for it to be considered intersecting.
@@ -1208,7 +1101,6 @@ Assuming the array contains objects that respond to <code>left</code>, <code>rig
<p>
The default tolerance is set to <code>0.1</code>, which means that the primitives are not considered intersecting unless they are overlapping by more than <code>0.1</code>.
</p>
-</ol>
<pre>repl do
# Here is a player class that has position and implement
# the ~attr_rect~ contract.
@@ -1264,59 +1156,27 @@ The default tolerance is set to <code>0.1</code>, which means that the primitive
puts ""
end
</pre>
-</ol>
<h1 id='--docs---gtk--outputs-'><code>GTK::Outputs</code></h1>
<p>
Outputs is how you render primitives to the screen. The minimal setup for rendering something to the screen is via a <code>tick</code> method defined in mygame/app/main.rb
</p>
-</ol>
<pre>def tick args
# code goes here
end
</pre>
-</ol>
-<h1 id='--docs---gtk--outputs#borders-'><code>GTK::Outputs#borders</code></h1>
-<p>
-Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.
-</p>
-<p>
-The only difference between the two primitives is where they are added.
-</p>
-<p>
-Instead of using <code>args.outputs.solids</code>:
-</p>
-</ol>
-<pre>def tick args
- # X Y WIDTH HEIGHT
- args.outputs.solids << [100, 100, 160, 90]
-end
-</pre>
-<p>
-You have to use <code>args.outputs.borders</code>:
-</p>
-</ol>
-<pre>def tick args
- # X Y WIDTH HEIGHT
- args.outputs.borders << [100, 100, 160, 90]
-end
-</pre>
-</ol>
<h1 id='--docs---gtk--outputs#solids-'><code>GTK::Outputs#solids</code></h1>
<p>
Add primitives to this collection to render a solid to the screen.
</p>
-</ol>
<h2>Rendering a solid using an Array</h2>
<p>
Creates a solid black rectangle located at 100, 100. 160 pixels wide and 90 pixels tall.
</p>
-</ol>
<pre>def tick args
# X Y WIDTH HEIGHT
args.outputs.solids << [100, 100, 160, 90]
end
</pre>
-</ol>
<h2>Rendering a solid using an Array with colors and alpha</h2>
<p>
The value for the color and alpha is an number between <code>0</code> and <code>255</code>. The alpha property is optional and will be set to <code>255</code> if not specified.
@@ -1324,18 +1184,15 @@ The value for the color and alpha is an number between <code>0</code> and <code>
<p>
Creates a green solid rectangle with an opacity of 50%.
</p>
-</ol>
<pre>def tick args
# X Y WIDTH HEIGHT RED GREEN BLUE ALPHA
args.outputs.solids << [100, 100, 160, 90, 0, 255, 0, 128]
end
</pre>
-</ol>
<h2>Rendering a solid using a Hash</h2>
<p>
If you want a more readable invocation. You can use the following hash to create a solid. Any parameters that are not specified will be given a default value. The keys of the hash can be provided in any order.
</p>
-</ol>
<pre>def tick args
args.outputs.solids << {
x: 0,
@@ -1349,7 +1206,6 @@ If you want a more readable invocation. You can use the following hash to create
}
end
</pre>
-</ol>
<h2>Rendering a solid using a Class</h2>
<p>
You can also create a class with solid/border properties and render it as a primitive. ALL properties must on the class. *Additionally*, a method called <code>primitive_marker</code> must be defined on the class.
@@ -1357,7 +1213,6 @@ You can also create a class with solid/border properties and render it as a prim
<p>
Here is an example:
</p>
-</ol>
<pre># Create type with ALL solid properties AND primitive_marker
class Solid
attr_accessor :x, :y, :w, :h, :r, :g, :b, :a
@@ -1383,12 +1238,33 @@ def tick args
args.outputs.solids << Square.new(10, 10, 32)
end
</pre>
-</ol>
+<h1 id='--docs---gtk--outputs#borders-'><code>GTK::Outputs#borders</code></h1>
+<p>
+Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.
+</p>
+<p>
+The only difference between the two primitives is where they are added.
+</p>
+<p>
+Instead of using <code>args.outputs.solids</code>:
+</p>
+<pre>def tick args
+ # X Y WIDTH HEIGHT
+ args.outputs.solids << [100, 100, 160, 90]
+end
+</pre>
+<p>
+You have to use <code>args.outputs.borders</code>:
+</p>
+<pre>def tick args
+ # X Y WIDTH HEIGHT
+ args.outputs.borders << [100, 100, 160, 90]
+end
+</pre>
<h1 id='--docs---gtk--mouse-'><code>GTK::Mouse</code></h1>
<p>
The mouse is accessible via <code>args.inputs.mouse</code>:
</p>
-</ol>
<pre>def tick args
# Rendering a label that shows the mouse's x and y position (via args.inputs.mouse).
args.outputs.labels << [
@@ -1439,7 +1315,6 @@ The <code>GTK::MousePoint</code> has the following properties.
<p>
<code>GTK::OpenEntity</code> is accessible within the DragonRuby's top level <code>tick</code> function via the <code>args.state</code> property.
</p>
-</ol>
<pre>def tick args
args.state.x ||= 100
args.outputs.labels << [10, 710, "value of x is: #{args.state.x}."]
@@ -1451,7 +1326,6 @@ The primary benefit of using <code>args.state</code> as opposed to instance vari
<p>
For example:
</p>
-</ol>
<pre>def tick args
# intermediate player object does not need to be created
args.state.player.x ||= 100
@@ -1463,7 +1337,6 @@ For example:
]
end
</pre>
-</ol>
<h1 id='--docs---gtk--openentity#as_hash-'><code>GTK::OpenEntity#as_hash</code></h1>
<p>
Returns a reference to the <code>GTK::OpenEntity</code> as a <code>Hash</code>. This property is useful when you want to treat <code>args.state</code> as a <code>Hash</code> and invoke methods such as <code>Hash#each</code>.
@@ -1471,7 +1344,6 @@ Returns a reference to the <code>GTK::OpenEntity</code> as a <code>Hash</code>.
<p>
Example:
</p>
-</ol>
<pre>def tick args
args.state.x ||= 100
args.state.y ||= 100
@@ -1488,7 +1360,6 @@ Example:
end
end
</pre>
-</ol>
<h1 id='--docs---numeric#frame_index-'><code>Numeric#frame_index</code></h1>
<p>
This function is helpful for determining the index of frame-by-frame sprite animation. The numeric value <code>self</code> represents the moment the animation started.
@@ -1507,7 +1378,6 @@ This function is helpful for determining the index of frame-by-frame sprite an
<p>
Example using variables:
</p>
-</ol>
<pre>def tick args
start_looping_at = 0
number_of_sprites = 6
@@ -1533,7 +1403,6 @@ end
<p>
Example using named parameters:
</p>
-</ol>
<pre>def tick args
start_looping_at = 0
@@ -1554,17 +1423,143 @@ Example using named parameters:
]
end
</pre>
-</ol>
+<h1 id='--docs---numeric#elapsed_time-'><code>Numeric#elapsed_time</code></h1>
+<p>
+For a given number, the elapsed frames since that number is returned. `Kernel.tick_count` is used to determine how many frames have elapsed. An optional numeric argument can be passed in which will be used instead of `Kernel.tick_count`.
+</p>
+<p>
+Here is an example of how elapsed_time can be used.
+</p>
+<pre>def tick args
+ args.state.last_click_at ||= 0
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.tick_count
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if args.state.last_click_at.elapsed_time > 120
+ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]
+ end
+end
+</pre>
+<p>
+And here is an example where the override parameter is passed in:
+</p>
+<pre>def tick args
+ args.state.last_click_at ||= 0
+
+ # create a state variable that tracks time at half the speed of args.state.tick_count
+ args.state.simulation_tick = args.state.tick_count.idiv 2
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.simulation_tick
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120
+ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]
+ end
+end
+</pre>
+<h1 id='--docs---numeric#elapsed?-'><code>Numeric#elapsed?</code></h1>
+<p>
+Returns true if <code>Numeric#elapsed_time</code> is greater than the number. An optional parameter can be passed into <code>elapsed?</code> which is added to the number before evaluating whether <code>elapsed?</code> is true.
+</p>
+<p>
+Example usage (no optional parameter):
+</p>
+<pre>def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :green,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :blue,
+ destroy_at: args.state.tick_count + 120 }
+ end
+
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:destroy_at].elapsed? }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: #{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+end
+</pre>
+<p>
+Example usage (with optional parameter):
+</p>
+<pre>def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :green,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :blue,
+ create_at: args.state.tick_count + 120,
+ lifespan: 120 }
+ end
+
+ # lifespan is passed in as a parameter to ~elapsed?~
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].elapsed? b[:lifespan] }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: #{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+end
+</pre>
+<h1 id='--docs---numeric#created?-'><code>Numeric#created?</code></h1>
+<p>
+Returns true if <code>Numeric#elapsed_time == 0</code>. Essentially communicating that number is equal to the current frame.
+</p>
+<p>
+Example usage:
+</p>
+<pre>def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 60 }
+ end
+
+ boxes_to_spawn_this_frame = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].new? }
+
+ boxes_to_spawn_this_frame.each { |b| puts "box #{b} was new? on #{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_spawn_this_frame
+end
+</pre>
<h1 id='--docs---kernel-'><code>Kernel</code></h1>
<p>
Kernel in the DragonRuby Runtime has patches for how standard out is handled and also contains a unit of time in games called a tick.
</p>
-</ol>
<h1 id='--docs---kernel--tick_count-'><code>Kernel::tick_count</code></h1>
<p>
Returns the current tick of the game. This value is reset if you call $gtk.reset.
</p>
-</ol>
<h1 id='--docs---kernel--global_tick_count-'><code>Kernel::global_tick_count</code></h1>
<p>
Returns the current tick of the application from the point it was started. This value is never reset.
diff --git a/docs/docs.txt b/docs/docs.txt
index 8627f1b..6b94787 100644
--- a/docs/docs.txt
+++ b/docs/docs.txt
@@ -358,29 +358,27 @@ description of each sample app.
42. 14_sprite_limits_static_references: Upper limit for how many sprites can be rendered to the screen using ~static~ output collections (which are updated by reference as opposed to by value).
43. 15_collision_limits: How many collisions can be processed across many primitives.
44. 18_moddable_game: How you can make a game where content is authored by the player (modding support).
-45. 19_lowrez_jam_01_hello_world: How to use ~render_targets~ to create a low resolution game.
-46. 19_lowrez_jam_02_buttons: How to use ~render_targets~ to create a low resolution game.
-47. 19_lowrez_jam_03_space_shooter: How to use ~render_targets~ to create a low resolution game.
-48. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.
-49. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.
-50. 21_mailbox_usage: How to do interprocess communication.
-51. 22_trace_debugging: Debugging techniques and tracing execution through your game.
-52. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.
-53. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.
-54. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.
-55. 24_http_example: How to make http requests.
-56. 25_3d_experiment_01_square: How to create 3D objects.
-57. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.
-58. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.
-59. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.
-60. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.
-61. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.
-62. 99_sample_game_pong: Reference implementation of pong.
-63. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.
-64. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.
-65. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.
-66. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.
-67. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.
+45. 19_lowrez_jam: How to use ~render_targets~ to create a low resolution game.
+46. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.
+47. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.
+48. 21_mailbox_usage: How to do interprocess communication.
+49. 22_trace_debugging: Debugging techniques and tracing execution through your game.
+50. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.
+51. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.
+52. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.
+53. 24_http_example: How to make http requests.
+54. 25_3d_experiment_01_square: How to create 3D objects.
+55. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.
+56. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.
+57. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.
+58. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.
+59. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.
+60. 99_sample_game_pong: Reference implementation of pong.
+61. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.
+62. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.
+63. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.
+64. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.
+65. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.
@@ -889,7 +887,7 @@ to clarify some terms. Specifically _language specification_ vs _runtime_.
*** Okay... so what is the difference between a language specification and a runtime?
-A runtime is an _implementation_ of a langauge specification. When
+A runtime is an _implementation_ of a language specification. When
people say "Ruby," they are usually referring to "the Ruby 3.0+ language
specification implemented via the CRuby/MRI Runtime."
@@ -925,18 +923,18 @@ runtime internally):
C-Extensions.
Levels 1 through 3 are fairly commonplace in many runtime
-implemenations (with level 1 being the most portable, and level 3
+implementations (with level 1 being the most portable, and level 3
being the fastest). But the DragonRuby Runtime has taken things a
bit further:
- Level 4 consists of shared abstractions around hardware I/O and operating
system resources. This level leverages open source and proprietary
- components within Simple DirectMedia Layer (a lowlevel multimedia
+ components within Simple DirectMedia Layer (a low level multimedia
component library that has been in active development for 22 years
and counting).
-- Level 5 is a codegeneration layer which creates metadata that allows
- for native interopability with host runtime libraries. It also
+- Level 5 is a code generation layer which creates metadata that allows
+ for native interoperability with host runtime libraries. It also
includes OS specific message pump orchestrations.
- Level 6 is a Ahead of Time/Just in Time Ruby compiler built with LLVM. This
@@ -945,12 +943,12 @@ bit further:
These levels allow us to stay up to date with open source
implementations of Ruby; provide fast, native code execution
-on proprietary platforms; ensure good seperation between these two
+on proprietary platforms; ensure good separation between these two
worlds; and provides a means to add new platforms without going insane.
*** Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?
-DragonRuby is a Ruby runtime implentation that takes all the lessons
+DragonRuby is a Ruby runtime implementation that takes all the lessons
we've learned from MRI/CRuby, and merges it with the latest and greatest
compiler and OSS technologies.
@@ -975,30 +973,30 @@ That doesn't make any sense. A language specification can't be
slow... it's a language spec. Sure, an _implementation/runtime_ can be slow though, but then we'd
have to talk about which runtime.
-*** Dynamic langauges are slow.
+*** Dynamic languages are slow.
They are certainly slower than statically compiled languages. With the
processing power and compiler optimizations we have today,
dynamic languages like Ruby are _fast enough_.
Unless you are writing in some form of intermediate representation by hand,
-your langauge of choice also suffers this same fallacy of slow. Like, nothing is
+your language of choice also suffers this same fallacy of slow. Like, nothing is
faster than a low level assembly-like language. So unless you're
writing in that, let's stop making this comment.
NOTE: If you _are_ hand writing LLVM IR, we are always open to
-bringing on new partners with such a skillset. Email us ^_^.
+bringing on new partners with such a skill set. Email us ^_^.
** Frequent Concerns
*** DragonRuby is not open source. That's not right.
The current state of open source is unsustainable. Contributors work
-for free, most all open source repositories are serverly understaffed,
+for free, most all open source repositories are severely under-staffed,
and burnout from core members is rampant.
We believe in open source very strongly. Parts of DragonRuby are
-infact, open source. Just not all of it (for legal reasons, and
+in fact, open source. Just not all of it (for legal reasons, and
because the IP we've created has value). And we promise that we are
looking for (or creating) ways to _sustainably_ open source everything we do.
@@ -1049,7 +1047,7 @@ email. We don't want to collect usage data off of you either. We just
want to provide quality toolchains to quality developers (as opposed
to a large quantity of developers).
-The peiple that pay for DragonRuby and make an effort to understand it are the
+The people that pay for DragonRuby and make an effort to understand it are the
ones we want to build a community around, partner with, and collaborate
with. So having that small monetary wall deters entitled individuals
that don't value the same things we do.
@@ -1061,13 +1059,16 @@ and non-trivially compensate open source developers. Look, we want to be
able to work on the stuff we love, every day of our lives. And we'll go
to great lengths to make that happen.
-But, in the event that sad day comes, our partnershiop bylaws state that
+But, in the event that sad day comes, our partnership bylaws state that
_all_ DragonRuby IP that can be legally open sourced, will be released
under a permissive license.
* DOCS: ~GTK::Runtime~
The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.
+* DOCS: ~GTK::Runtime#reset~
+This function will reset Kernel.tick_count to 0 and will remove all data from args.state.
+
* DOCS: ~GTK::Runtime#calcstringbox~
This function returns the width and height of a string.
@@ -1078,9 +1079,6 @@ This function returns the width and height of a string.
end
#+end_src
-* DOCS: ~GTK::Runtime#reset~
-This function will reset Kernel.tick_count to 0 and will remove all data from args.state.
-
* DOCS: ~Array~
The Array class has been extend to provide methods that
@@ -1343,32 +1341,6 @@ mygame/app/main.rb
#+end_src
-* DOCS: ~GTK::Outputs#borders~
-
-Add primitives to this collection to render an unfilled solid to the screen. Take a look at the
-documentation for Outputs#solids.
-
-The only difference between the two primitives is where they are added.
-
-Instead of using ~args.outputs.solids~:
-
-#+begin_src
- def tick args
- # X Y WIDTH HEIGHT
- args.outputs.solids << [100, 100, 160, 90]
- end
-#+end_src
-
-You have to use ~args.outputs.borders~:
-
-#+begin_src
- def tick args
- # X Y WIDTH HEIGHT
- args.outputs.borders << [100, 100, 160, 90]
- end
-#+end_src
-
-
* DOCS: ~GTK::Outputs#solids~
Add primitives to this collection to render a solid to the screen.
@@ -1456,6 +1428,32 @@ Here is an example:
#+end_src
+* DOCS: ~GTK::Outputs#borders~
+
+Add primitives to this collection to render an unfilled solid to the screen. Take a look at the
+documentation for Outputs#solids.
+
+The only difference between the two primitives is where they are added.
+
+Instead of using ~args.outputs.solids~:
+
+#+begin_src
+ def tick args
+ # X Y WIDTH HEIGHT
+ args.outputs.solids << [100, 100, 160, 90]
+ end
+#+end_src
+
+You have to use ~args.outputs.borders~:
+
+#+begin_src
+ def tick args
+ # X Y WIDTH HEIGHT
+ args.outputs.borders << [100, 100, 160, 90]
+ end
+#+end_src
+
+
* DOCS: ~GTK::Mouse~
The mouse is accessible via ~args.inputs.mouse~:
@@ -1631,6 +1629,144 @@ Example using named parameters:
#+end_src
+* DOCS: ~Numeric#elapsed_time~
+For a given number, the elapsed frames since that number is returned.
+`Kernel.tick_count` is used to determine how many frames have elapsed.
+An optional numeric argument can be passed in which will be used instead
+of `Kernel.tick_count`.
+
+Here is an example of how elapsed_time can be used.
+
+#+begin_src ruby
+ def tick args
+ args.state.last_click_at ||= 0
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.tick_count
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if args.state.last_click_at.elapsed_time > 120
+ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]
+ end
+ end
+#+end_src
+
+And here is an example where the override parameter is passed in:
+
+#+begin_src ruby
+ def tick args
+ args.state.last_click_at ||= 0
+
+ # create a state variable that tracks time at half the speed of args.state.tick_count
+ args.state.simulation_tick = args.state.tick_count.idiv 2
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.simulation_tick
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120
+ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]
+ end
+ end
+#+end_src
+
+
+* DOCS: ~Numeric#elapsed?~
+Returns true if ~Numeric#elapsed_time~ is greater than the number. An optional parameter can be
+passed into ~elapsed?~ which is added to the number before evaluating whether ~elapsed?~ is true.
+
+Example usage (no optional parameter):
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :green,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :blue,
+ destroy_at: args.state.tick_count + 120 }
+ end
+
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:destroy_at].elapsed? }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: #{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+ end
+#+end_src
+
+Example usage (with optional parameter):
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :green,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :blue,
+ create_at: args.state.tick_count + 120,
+ lifespan: 120 }
+ end
+
+ # lifespan is passed in as a parameter to ~elapsed?~
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].elapsed? b[:lifespan] }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: #{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+ end
+#+end_src
+
+
+* DOCS: ~Numeric#created?~
+Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that
+number is equal to the current frame.
+
+Example usage:
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 60 }
+ end
+
+ boxes_to_spawn_this_frame = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].new? }
+
+ boxes_to_spawn_this_frame.each { |b| puts "box #{b} was new? on #{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_spawn_this_frame
+ end
+#+end_src
+
* DOCS: ~Kernel~
Kernel in the DragonRuby Runtime has patches for how standard out is handled and also
diff --git a/docs/parse_log.txt b/docs/parse_log.txt
index 5c1d663..4b8486f 100644
--- a/docs/parse_log.txt
+++ b/docs/parse_log.txt
@@ -1054,102 +1054,94 @@ Now that you've completed the Hello World tutorial. Head over to the `samples` d
- Line was identified as a continuation of a list.
*** True Line Result
43. 15_collision_limits: How many collisions can be processed across many primitives.
-** Processing line: ~45. 19_lowrez_jam_01_hello_world: How to use ~render_targets~ to create a low resolution game.~
+** Processing line: ~45. 19_lowrez_jam: How to use ~render_targets~ to create a low resolution game.~
- Line was identified as a continuation of a list.
*** True Line Result
44. 18_moddable_game: How you can make a game where content is authored by the player (modding support).
-** Processing line: ~46. 19_lowrez_jam_02_buttons: How to use ~render_targets~ to create a low resolution game.~
+** Processing line: ~46. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.~
- Line was identified as a continuation of a list.
*** True Line Result
-45. 19_lowrez_jam_01_hello_world: How to use ~render_targets~ to create a low resolution game.
-** Processing line: ~47. 19_lowrez_jam_03_space_shooter: How to use ~render_targets~ to create a low resolution game.~
+45. 19_lowrez_jam: How to use ~render_targets~ to create a low resolution game.
+** Processing line: ~47. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.~
- Line was identified as a continuation of a list.
*** True Line Result
-46. 19_lowrez_jam_02_buttons: How to use ~render_targets~ to create a low resolution game.
-** Processing line: ~48. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.~
+46. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.
+** Processing line: ~48. 21_mailbox_usage: How to do interprocess communication.~
- Line was identified as a continuation of a list.
*** True Line Result
-47. 19_lowrez_jam_03_space_shooter: How to use ~render_targets~ to create a low resolution game.
-** Processing line: ~49. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.~
+47. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.
+** Processing line: ~49. 22_trace_debugging: Debugging techniques and tracing execution through your game.~
- Line was identified as a continuation of a list.
*** True Line Result
-48. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.
-** Processing line: ~50. 21_mailbox_usage: How to do interprocess communication.~
+48. 21_mailbox_usage: How to do interprocess communication.
+** Processing line: ~50. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.~
- Line was identified as a continuation of a list.
*** True Line Result
-49. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.
-** Processing line: ~51. 22_trace_debugging: Debugging techniques and tracing execution through your game.~
+49. 22_trace_debugging: Debugging techniques and tracing execution through your game.
+** Processing line: ~51. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.~
- Line was identified as a continuation of a list.
*** True Line Result
-50. 21_mailbox_usage: How to do interprocess communication.
-** Processing line: ~52. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.~
+50. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.
+** Processing line: ~52. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.~
- Line was identified as a continuation of a list.
*** True Line Result
-51. 22_trace_debugging: Debugging techniques and tracing execution through your game.
-** Processing line: ~53. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.~
+51. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.
+** Processing line: ~53. 24_http_example: How to make http requests.~
- Line was identified as a continuation of a list.
*** True Line Result
-52. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.
-** Processing line: ~54. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.~
+52. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.
+** Processing line: ~54. 25_3d_experiment_01_square: How to create 3D objects.~
- Line was identified as a continuation of a list.
*** True Line Result
-53. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.
-** Processing line: ~55. 24_http_example: How to make http requests.~
+53. 24_http_example: How to make http requests.
+** Processing line: ~55. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.~
- Line was identified as a continuation of a list.
*** True Line Result
-54. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.
-** Processing line: ~56. 25_3d_experiment_01_square: How to create 3D objects.~
+54. 25_3d_experiment_01_square: How to create 3D objects.
+** Processing line: ~56. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.~
- Line was identified as a continuation of a list.
*** True Line Result
-55. 24_http_example: How to make http requests.
-** Processing line: ~57. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.~
+55. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.
+** Processing line: ~57. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.~
- Line was identified as a continuation of a list.
*** True Line Result
-56. 25_3d_experiment_01_square: How to create 3D objects.
-** Processing line: ~58. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.~
+56. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.
+** Processing line: ~58. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.~
- Line was identified as a continuation of a list.
*** True Line Result
-57. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.
-** Processing line: ~59. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.~
+57. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.
+** Processing line: ~59. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.~
- Line was identified as a continuation of a list.
*** True Line Result
-58. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.
-** Processing line: ~60. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.~
+58. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.
+** Processing line: ~60. 99_sample_game_pong: Reference implementation of pong.~
- Line was identified as a continuation of a list.
*** True Line Result
-59. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.
-** Processing line: ~61. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.~
+59. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.
+** Processing line: ~61. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.~
- Line was identified as a continuation of a list.
*** True Line Result
-60. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.
-** Processing line: ~62. 99_sample_game_pong: Reference implementation of pong.~
+60. 99_sample_game_pong: Reference implementation of pong.
+** Processing line: ~62. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.~
- Line was identified as a continuation of a list.
*** True Line Result
-61. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.
-** Processing line: ~63. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.~
+61. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.
+** Processing line: ~63. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.~
- Line was identified as a continuation of a list.
*** True Line Result
-62. 99_sample_game_pong: Reference implementation of pong.
-** Processing line: ~64. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.~
+62. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.
+** Processing line: ~64. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.~
- Line was identified as a continuation of a list.
*** True Line Result
-63. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.
-** Processing line: ~65. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.~
+63. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.
+** Processing line: ~65. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.~
- Line was identified as a continuation of a list.
*** True Line Result
-64. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.
-** Processing line: ~66. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.~
-- Line was identified as a continuation of a list.
-*** True Line Result
-65. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.
-** Processing line: ~67. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.~
-- Line was identified as a continuation of a list.
-*** True Line Result
-66. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.
+64. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-67. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.
+65. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
@@ -2888,13 +2880,13 @@ The response to this question requires a few subparts. First we need to clarify
- End of paragraph detected.
*** True Line Result
-** Processing line: ~A runtime is an _implementation_ of a langauge specification. When~
+** Processing line: ~A runtime is an _implementation_ of a language specification. When~
** Processing line: ~people say "Ruby," they are usually referring to "the Ruby 3.0+ language~
** Processing line: ~specification implemented via the CRuby/MRI Runtime."~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-A runtime is an _implementation_ of a langauge specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."
+A runtime is an _implementation_ of a language specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."
** Processing line: ~But, there are many Ruby Runtimes: CRuby/MRI, JRuby, Truffle, Rubinius, Artichoke,~
** Processing line: ~and (last but certainly not least) DragonRuby.~
** Processing line: ~~
@@ -2980,37 +2972,37 @@ There are complexities associated with targeting all the platforms we support. B
- Level 3 consists of portable C libraries and their Ruby
C-Extensions.
** Processing line: ~Levels 1 through 3 are fairly commonplace in many runtime~
-** Processing line: ~implemenations (with level 1 being the most portable, and level 3~
+** Processing line: ~implementations (with level 1 being the most portable, and level 3~
** Processing line: ~being the fastest). But the DragonRuby Runtime has taken things a~
** Processing line: ~bit further:~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-Levels 1 through 3 are fairly commonplace in many runtime implemenations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:
+Levels 1 through 3 are fairly commonplace in many runtime implementations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:
** Processing line: ~- Level 4 consists of shared abstractions around hardware I/O and operating~
- Line was identified as a list.
*** True Line Result
** Processing line: ~ system resources. This level leverages open source and proprietary~
-** Processing line: ~ components within Simple DirectMedia Layer (a lowlevel multimedia~
+** Processing line: ~ components within Simple DirectMedia Layer (a low level multimedia~
** Processing line: ~ component library that has been in active development for 22 years~
** Processing line: ~ and counting).~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
- Level 4 consists of shared abstractions around hardware I/O and operating
- system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a lowlevel multimedia component library that has been in active development for 22 years and counting).
-** Processing line: ~- Level 5 is a codegeneration layer which creates metadata that allows~
+ system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a low level multimedia component library that has been in active development for 22 years and counting).
+** Processing line: ~- Level 5 is a code generation layer which creates metadata that allows~
- Line was identified as a list.
*** True Line Result
-** Processing line: ~ for native interopability with host runtime libraries. It also~
+** Processing line: ~ for native interoperability with host runtime libraries. It also~
** Processing line: ~ includes OS specific message pump orchestrations.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-- Level 5 is a codegeneration layer which creates metadata that allows
- for native interopability with host runtime libraries. It also includes OS specific message pump orchestrations.
+- Level 5 is a code generation layer which creates metadata that allows
+ for native interoperability with host runtime libraries. It also includes OS specific message pump orchestrations.
** Processing line: ~- Level 6 is a Ahead of Time/Just in Time Ruby compiler built with LLVM. This~
- Line was identified as a list.
*** True Line Result
@@ -3024,12 +3016,12 @@ Levels 1 through 3 are fairly commonplace in many runtime implemenations (with l
compiler outputs _very_ fast platform specific bitcode, but only supports a subset of the Ruby language specification.
** Processing line: ~These levels allow us to stay up to date with open source~
** Processing line: ~implementations of Ruby; provide fast, native code execution~
-** Processing line: ~on proprietary platforms; ensure good seperation between these two~
+** Processing line: ~on proprietary platforms; ensure good separation between these two~
** Processing line: ~worlds; and provides a means to add new platforms without going insane.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good seperation between these two worlds; and provides a means to add new platforms without going insane.
+These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good separation between these two worlds; and provides a means to add new platforms without going insane.
** Processing line: ~*** Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?~
- Header detected.
*** True Line Result
@@ -3040,13 +3032,13 @@ These levels allow us to stay up to date with open source implementations of Rub
- End of paragraph detected.
*** True Line Result
-** Processing line: ~DragonRuby is a Ruby runtime implentation that takes all the lessons~
+** Processing line: ~DragonRuby is a Ruby runtime implementation that takes all the lessons~
** Processing line: ~we've learned from MRI/CRuby, and merges it with the latest and greatest~
** Processing line: ~compiler and OSS technologies.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-DragonRuby is a Ruby runtime implentation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.
+DragonRuby is a Ruby runtime implementation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.
** Processing line: ~** Frequent Comments~
- Header detected.
*** True Line Result
@@ -3107,12 +3099,12 @@ Let's stop making this comment shall we?
- End of paragraph detected.
*** True Line Result
That doesn't make any sense. A language specification can't be slow... it's a language spec. Sure, an _implementation/runtime_ can be slow though, but then we'd have to talk about which runtime.
-** Processing line: ~*** Dynamic langauges are slow.~
+** Processing line: ~*** Dynamic languages are slow.~
- Header detected.
*** True Line Result
*** True Line Result
-*** Dynamic langauges are slow.
+*** Dynamic languages are slow.
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
@@ -3125,19 +3117,19 @@ That doesn't make any sense. A language specification can't be slow... it's a la
*** True Line Result
They are certainly slower than statically compiled languages. With the processing power and compiler optimizations we have today, dynamic languages like Ruby are _fast enough_.
** Processing line: ~Unless you are writing in some form of intermediate representation by hand,~
-** Processing line: ~your langauge of choice also suffers this same fallacy of slow. Like, nothing is~
+** Processing line: ~your language of choice also suffers this same fallacy of slow. Like, nothing is~
** Processing line: ~faster than a low level assembly-like language. So unless you're~
** Processing line: ~writing in that, let's stop making this comment.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-Unless you are writing in some form of intermediate representation by hand, your langauge of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.
+Unless you are writing in some form of intermediate representation by hand, your language of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.
** Processing line: ~NOTE: If you _are_ hand writing LLVM IR, we are always open to~
-** Processing line: ~bringing on new partners with such a skillset. Email us ^_^.~
+** Processing line: ~bringing on new partners with such a skill set. Email us ^_^.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skillset. Email us ^_^.
+NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skill set. Email us ^_^.
** Processing line: ~** Frequent Concerns~
- Header detected.
*** True Line Result
@@ -3159,20 +3151,20 @@ NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new p
*** True Line Result
** Processing line: ~The current state of open source is unsustainable. Contributors work~
-** Processing line: ~for free, most all open source repositories are serverly understaffed,~
+** Processing line: ~for free, most all open source repositories are severely under-staffed,~
** Processing line: ~and burnout from core members is rampant.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-The current state of open source is unsustainable. Contributors work for free, most all open source repositories are serverly understaffed, and burnout from core members is rampant.
+The current state of open source is unsustainable. Contributors work for free, most all open source repositories are severely under-staffed, and burnout from core members is rampant.
** Processing line: ~We believe in open source very strongly. Parts of DragonRuby are~
-** Processing line: ~infact, open source. Just not all of it (for legal reasons, and~
+** Processing line: ~in fact, open source. Just not all of it (for legal reasons, and~
** Processing line: ~because the IP we've created has value). And we promise that we are~
** Processing line: ~looking for (or creating) ways to _sustainably_ open source everything we do.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-We believe in open source very strongly. Parts of DragonRuby are infact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.
+We believe in open source very strongly. Parts of DragonRuby are in fact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.
** Processing line: ~If you have ideas on how we can do this, email us!~
** Processing line: ~~
- End of paragraph detected.
@@ -3291,14 +3283,14 @@ Seriously just buy it. Get a refund if you don't like it. We make it stupid easy
- End of paragraph detected.
*** True Line Result
Free isn't a sustainable financial model. We don't want to spam your email. We don't want to collect usage data off of you either. We just want to provide quality toolchains to quality developers (as opposed to a large quantity of developers).
-** Processing line: ~The peiple that pay for DragonRuby and make an effort to understand it are the~
+** Processing line: ~The people that pay for DragonRuby and make an effort to understand it are the~
** Processing line: ~ones we want to build a community around, partner with, and collaborate~
** Processing line: ~with. So having that small monetary wall deters entitled individuals~
** Processing line: ~that don't value the same things we do.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-The peiple that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.
+The people that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.
** Processing line: ~*** What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent.~
- Header detected.
*** True Line Result
@@ -3317,13 +3309,13 @@ The peiple that pay for DragonRuby and make an effort to understand it are the o
- End of paragraph detected.
*** True Line Result
That won't happen if the development world stop asking for free stuff and non-trivially compensate open source developers. Look, we want to be able to work on the stuff we love, every day of our lives. And we'll go to great lengths to make that happen.
-** Processing line: ~But, in the event that sad day comes, our partnershiop bylaws state that~
+** Processing line: ~But, in the event that sad day comes, our partnership bylaws state that~
** Processing line: ~_all_ DragonRuby IP that can be legally open sourced, will be released~
** Processing line: ~under a permissive license.~
** Processing line: ~~
- End of paragraph detected.
*** True Line Result
-But, in the event that sad day comes, our partnershiop bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.
+But, in the event that sad day comes, our partnership bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.
** Processing line: ~* DOCS: ~GTK::Runtime~~
- Header detected.
*** True Line Result
@@ -3335,6 +3327,17 @@ But, in the event that sad day comes, our partnershiop bylaws state that _all_ D
- End of paragraph detected.
*** True Line Result
The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.
+** Processing line: ~* DOCS: ~GTK::Runtime#reset~~
+- Header detected.
+*** True Line Result
+
+*** True Line Result
+* DOCS: ~GTK::Runtime#reset~
+** Processing line: ~This function will reset Kernel.tick_count to 0 and will remove all data from args.state.~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+This function will reset Kernel.tick_count to 0 and will remove all data from args.state.
** Processing line: ~* DOCS: ~GTK::Runtime#calcstringbox~~
- Header detected.
*** True Line Result
@@ -3376,17 +3379,6 @@ This function returns the width and height of a string.
- End of paragraph detected.
*** True Line Result
-** Processing line: ~* DOCS: ~GTK::Runtime#reset~~
-- Header detected.
-*** True Line Result
-
-*** True Line Result
-* DOCS: ~GTK::Runtime#reset~
-** Processing line: ~This function will reset Kernel.tick_count to 0 and will remove all data from args.state.~
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-This function will reset Kernel.tick_count to 0 and will remove all data from args.state.
** Processing line: ~* DOCS: ~Array~~
- Header detected.
*** True Line Result
@@ -4380,101 +4372,6 @@ Outputs is how you render primitives to the screen. The minimal setup for render
- End of paragraph detected.
*** True Line Result
-** Processing line: ~* DOCS: ~GTK::Outputs#borders~~
-- Header detected.
-*** True Line Result
-
-*** True Line Result
-* DOCS: ~GTK::Outputs#borders~
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-
-** Processing line: ~Add primitives to this collection to render an unfilled solid to the screen. Take a look at the~
-** Processing line: ~documentation for Outputs#solids.~
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.
-** Processing line: ~The only difference between the two primitives is where they are added.~
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-The only difference between the two primitives is where they are added.
-** Processing line: ~Instead of using ~args.outputs.solids~:~
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-Instead of using ~args.outputs.solids~:
-** Processing line: ~#+begin_src~
-- Line was identified as the beginning of a code block.
-*** True Line Result
-
-*** True Line Result
-#+begin_src
-** Processing line: ~ def tick args~
-- Inside source: true
-*** True Line Result
- def tick args
-** Processing line: ~ # X Y WIDTH HEIGHT~
-- Inside source: true
-*** True Line Result
- # X Y WIDTH HEIGHT
-** Processing line: ~ args.outputs.solids << [100, 100, 160, 90]~
-- Inside source: true
-*** True Line Result
- args.outputs.solids << [100, 100, 160, 90]
-** Processing line: ~ end~
-- Inside source: true
-*** True Line Result
- end
-** Processing line: ~#+end_src~
-- Line was identified as the end of a code block.
-*** True Line Result
-#+end_src
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-
-** Processing line: ~You have to use ~args.outputs.borders~:~
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-You have to use ~args.outputs.borders~:
-** Processing line: ~#+begin_src~
-- Line was identified as the beginning of a code block.
-*** True Line Result
-
-*** True Line Result
-#+begin_src
-** Processing line: ~ def tick args~
-- Inside source: true
-*** True Line Result
- def tick args
-** Processing line: ~ # X Y WIDTH HEIGHT~
-- Inside source: true
-*** True Line Result
- # X Y WIDTH HEIGHT
-** Processing line: ~ args.outputs.borders << [100, 100, 160, 90]~
-- Inside source: true
-*** True Line Result
- args.outputs.borders << [100, 100, 160, 90]
-** Processing line: ~ end~
-- Inside source: true
-*** True Line Result
- end
-** Processing line: ~#+end_src~
-- Line was identified as the end of a code block.
-*** True Line Result
-#+end_src
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-
-** Processing line: ~~
-- End of paragraph detected.
-*** True Line Result
-
** Processing line: ~* DOCS: ~GTK::Outputs#solids~~
- Header detected.
*** True Line Result
@@ -4802,6 +4699,101 @@ Here is an example:
- End of paragraph detected.
*** True Line Result
+** Processing line: ~* DOCS: ~GTK::Outputs#borders~~
+- Header detected.
+*** True Line Result
+
+*** True Line Result
+* DOCS: ~GTK::Outputs#borders~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~Add primitives to this collection to render an unfilled solid to the screen. Take a look at the~
+** Processing line: ~documentation for Outputs#solids.~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.
+** Processing line: ~The only difference between the two primitives is where they are added.~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+The only difference between the two primitives is where they are added.
+** Processing line: ~Instead of using ~args.outputs.solids~:~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Instead of using ~args.outputs.solids~:
+** Processing line: ~#+begin_src~
+- Line was identified as the beginning of a code block.
+*** True Line Result
+
+*** True Line Result
+#+begin_src
+** Processing line: ~ def tick args~
+- Inside source: true
+*** True Line Result
+ def tick args
+** Processing line: ~ # X Y WIDTH HEIGHT~
+- Inside source: true
+*** True Line Result
+ # X Y WIDTH HEIGHT
+** Processing line: ~ args.outputs.solids << [100, 100, 160, 90]~
+- Inside source: true
+*** True Line Result
+ args.outputs.solids << [100, 100, 160, 90]
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~#+end_src~
+- Line was identified as the end of a code block.
+*** True Line Result
+#+end_src
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~You have to use ~args.outputs.borders~:~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+You have to use ~args.outputs.borders~:
+** Processing line: ~#+begin_src~
+- Line was identified as the beginning of a code block.
+*** True Line Result
+
+*** True Line Result
+#+begin_src
+** Processing line: ~ def tick args~
+- Inside source: true
+*** True Line Result
+ def tick args
+** Processing line: ~ # X Y WIDTH HEIGHT~
+- Inside source: true
+*** True Line Result
+ # X Y WIDTH HEIGHT
+** Processing line: ~ args.outputs.borders << [100, 100, 160, 90]~
+- Inside source: true
+*** True Line Result
+ args.outputs.borders << [100, 100, 160, 90]
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~#+end_src~
+- Line was identified as the end of a code block.
+*** True Line Result
+#+end_src
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
** Processing line: ~* DOCS: ~GTK::Mouse~~
- Header detected.
*** True Line Result
@@ -5461,6 +5453,535 @@ Example using named parameters:
- End of paragraph detected.
*** True Line Result
+** Processing line: ~* DOCS: ~Numeric#elapsed_time~~
+- Header detected.
+*** True Line Result
+
+*** True Line Result
+* DOCS: ~Numeric#elapsed_time~
+** Processing line: ~For a given number, the elapsed frames since that number is returned.~
+** Processing line: ~`Kernel.tick_count` is used to determine how many frames have elapsed.~
+** Processing line: ~An optional numeric argument can be passed in which will be used instead~
+** Processing line: ~of `Kernel.tick_count`.~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+For a given number, the elapsed frames since that number is returned. `Kernel.tick_count` is used to determine how many frames have elapsed. An optional numeric argument can be passed in which will be used instead of `Kernel.tick_count`.
+** Processing line: ~Here is an example of how elapsed_time can be used.~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Here is an example of how elapsed_time can be used.
+** Processing line: ~#+begin_src ruby~
+- Line was identified as the beginning of a code block.
+*** True Line Result
+
+*** True Line Result
+#+begin_src ruby
+** Processing line: ~ def tick args~
+- Inside source: true
+*** True Line Result
+ def tick args
+** Processing line: ~ args.state.last_click_at ||= 0~
+- Inside source: true
+*** True Line Result
+ args.state.last_click_at ||= 0
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ # record when a mouse click occurs~
+- Inside source: true
+*** True Line Result
+ # record when a mouse click occurs
+** Processing line: ~ if args.inputs.mouse.click~
+- Inside source: true
+*** True Line Result
+ if args.inputs.mouse.click
+** Processing line: ~ args.state.last_click_at = args.state.tick_count~
+- Inside source: true
+*** True Line Result
+ args.state.last_click_at = args.state.tick_count
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ # Use Numeric#elapsed_time to determine how long it's been~
+- Inside source: true
+*** True Line Result
+ # Use Numeric#elapsed_time to determine how long it's been
+** Processing line: ~ if args.state.last_click_at.elapsed_time > 120~
+- Inside source: true
+*** True Line Result
+ if args.state.last_click_at.elapsed_time > 120
+** Processing line: ~ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]~
+- Inside source: true
+*** True Line Result
+ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~#+end_src~
+- Line was identified as the end of a code block.
+*** True Line Result
+#+end_src
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~And here is an example where the override parameter is passed in:~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+And here is an example where the override parameter is passed in:
+** Processing line: ~#+begin_src ruby~
+- Line was identified as the beginning of a code block.
+*** True Line Result
+
+*** True Line Result
+#+begin_src ruby
+** Processing line: ~ def tick args~
+- Inside source: true
+*** True Line Result
+ def tick args
+** Processing line: ~ args.state.last_click_at ||= 0~
+- Inside source: true
+*** True Line Result
+ args.state.last_click_at ||= 0
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ # create a state variable that tracks time at half the speed of args.state.tick_count~
+- Inside source: true
+*** True Line Result
+ # create a state variable that tracks time at half the speed of args.state.tick_count
+** Processing line: ~ args.state.simulation_tick = args.state.tick_count.idiv 2~
+- Inside source: true
+*** True Line Result
+ args.state.simulation_tick = args.state.tick_count.idiv 2
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ # record when a mouse click occurs~
+- Inside source: true
+*** True Line Result
+ # record when a mouse click occurs
+** Processing line: ~ if args.inputs.mouse.click~
+- Inside source: true
+*** True Line Result
+ if args.inputs.mouse.click
+** Processing line: ~ args.state.last_click_at = args.state.simulation_tick~
+- Inside source: true
+*** True Line Result
+ args.state.last_click_at = args.state.simulation_tick
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ # Use Numeric#elapsed_time to determine how long it's been~
+- Inside source: true
+*** True Line Result
+ # Use Numeric#elapsed_time to determine how long it's been
+** Processing line: ~ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120~
+- Inside source: true
+*** True Line Result
+ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120
+** Processing line: ~ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]~
+- Inside source: true
+*** True Line Result
+ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~#+end_src~
+- Line was identified as the end of a code block.
+*** True Line Result
+#+end_src
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~* DOCS: ~Numeric#elapsed?~~
+- Header detected.
+*** True Line Result
+
+*** True Line Result
+* DOCS: ~Numeric#elapsed?~
+** Processing line: ~Returns true if ~Numeric#elapsed_time~ is greater than the number. An optional parameter can be~
+** Processing line: ~passed into ~elapsed?~ which is added to the number before evaluating whether ~elapsed?~ is true.~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Returns true if ~Numeric#elapsed_time~ is greater than the number. An optional parameter can be passed into ~elapsed?~ which is added to the number before evaluating whether ~elapsed?~ is true.
+** Processing line: ~Example usage (no optional parameter):~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Example usage (no optional parameter):
+** Processing line: ~#+begin_src ruby~
+- Line was identified as the beginning of a code block.
+*** True Line Result
+
+*** True Line Result
+#+begin_src ruby
+** Processing line: ~ def tick args~
+- Inside source: true
+*** True Line Result
+ def tick args
+** Processing line: ~ args.state.box_queue ||= []~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue ||= []
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ if args.state.box_queue.empty?~
+- Inside source: true
+*** True Line Result
+ if args.state.box_queue.empty?
+** Processing line: ~ args.state.box_queue << { name: :red,~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue << { name: :red,
+** Processing line: ~ destroy_at: args.state.tick_count + 60 }~
+- Inside source: true
+*** True Line Result
+ destroy_at: args.state.tick_count + 60 }
+** Processing line: ~ args.state.box_queue << { name: :green,~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue << { name: :green,
+** Processing line: ~ destroy_at: args.state.tick_count + 60 }~
+- Inside source: true
+*** True Line Result
+ destroy_at: args.state.tick_count + 60 }
+** Processing line: ~ args.state.box_queue << { name: :blue,~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue << { name: :blue,
+** Processing line: ~ destroy_at: args.state.tick_count + 120 }~
+- Inside source: true
+*** True Line Result
+ destroy_at: args.state.tick_count + 120 }
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ boxes_to_destroy = args.state~
+- Inside source: true
+*** True Line Result
+ boxes_to_destroy = args.state
+** Processing line: ~ .box_queue~
+- Inside source: true
+*** True Line Result
+ .box_queue
+** Processing line: ~ .find_all { |b| b[:destroy_at].elapsed? }~
+- Inside source: true
+*** True Line Result
+ .find_all { |b| b[:destroy_at].elapsed? }
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ if !boxes_to_destroy.empty?~
+- Inside source: true
+*** True Line Result
+ if !boxes_to_destroy.empty?
+** Processing line: ~ puts "boxes to destroy count: #{boxes_to_destroy.length}"~
+- Inside source: true
+*** True Line Result
+ puts "boxes to destroy count: #{boxes_to_destroy.length}"
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }~
+- Inside source: true
+*** True Line Result
+ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ args.state.box_queue -= boxes_to_destroy~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue -= boxes_to_destroy
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~#+end_src~
+- Line was identified as the end of a code block.
+*** True Line Result
+#+end_src
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~Example usage (with optional parameter):~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Example usage (with optional parameter):
+** Processing line: ~#+begin_src ruby~
+- Line was identified as the beginning of a code block.
+*** True Line Result
+
+*** True Line Result
+#+begin_src ruby
+** Processing line: ~ def tick args~
+- Inside source: true
+*** True Line Result
+ def tick args
+** Processing line: ~ args.state.box_queue ||= []~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue ||= []
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ if args.state.box_queue.empty?~
+- Inside source: true
+*** True Line Result
+ if args.state.box_queue.empty?
+** Processing line: ~ args.state.box_queue << { name: :red,~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue << { name: :red,
+** Processing line: ~ create_at: args.state.tick_count + 120,~
+- Inside source: true
+*** True Line Result
+ create_at: args.state.tick_count + 120,
+** Processing line: ~ lifespan: 60 }~
+- Inside source: true
+*** True Line Result
+ lifespan: 60 }
+** Processing line: ~ args.state.box_queue << { name: :green,~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue << { name: :green,
+** Processing line: ~ create_at: args.state.tick_count + 120,~
+- Inside source: true
+*** True Line Result
+ create_at: args.state.tick_count + 120,
+** Processing line: ~ lifespan: 60 }~
+- Inside source: true
+*** True Line Result
+ lifespan: 60 }
+** Processing line: ~ args.state.box_queue << { name: :blue,~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue << { name: :blue,
+** Processing line: ~ create_at: args.state.tick_count + 120,~
+- Inside source: true
+*** True Line Result
+ create_at: args.state.tick_count + 120,
+** Processing line: ~ lifespan: 120 }~
+- Inside source: true
+*** True Line Result
+ lifespan: 120 }
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ # lifespan is passed in as a parameter to ~elapsed?~~
+- Inside source: true
+*** True Line Result
+ # lifespan is passed in as a parameter to ~elapsed?~
+** Processing line: ~ boxes_to_destroy = args.state~
+- Inside source: true
+*** True Line Result
+ boxes_to_destroy = args.state
+** Processing line: ~ .box_queue~
+- Inside source: true
+*** True Line Result
+ .box_queue
+** Processing line: ~ .find_all { |b| b[:create_at].elapsed? b[:lifespan] }~
+- Inside source: true
+*** True Line Result
+ .find_all { |b| b[:create_at].elapsed? b[:lifespan] }
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ if !boxes_to_destroy.empty?~
+- Inside source: true
+*** True Line Result
+ if !boxes_to_destroy.empty?
+** Processing line: ~ puts "boxes to destroy count: #{boxes_to_destroy.length}"~
+- Inside source: true
+*** True Line Result
+ puts "boxes to destroy count: #{boxes_to_destroy.length}"
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }~
+- Inside source: true
+*** True Line Result
+ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ args.state.box_queue -= boxes_to_destroy~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue -= boxes_to_destroy
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~#+end_src~
+- Line was identified as the end of a code block.
+*** True Line Result
+#+end_src
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
+** Processing line: ~* DOCS: ~Numeric#created?~~
+- Header detected.
+*** True Line Result
+
+*** True Line Result
+* DOCS: ~Numeric#created?~
+** Processing line: ~Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that~
+** Processing line: ~number is equal to the current frame.~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that number is equal to the current frame.
+** Processing line: ~Example usage:~
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+Example usage:
+** Processing line: ~#+begin_src ruby~
+- Line was identified as the beginning of a code block.
+*** True Line Result
+
+*** True Line Result
+#+begin_src ruby
+** Processing line: ~ def tick args~
+- Inside source: true
+*** True Line Result
+ def tick args
+** Processing line: ~ args.state.box_queue ||= []~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue ||= []
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ if args.state.box_queue.empty?~
+- Inside source: true
+*** True Line Result
+ if args.state.box_queue.empty?
+** Processing line: ~ args.state.box_queue << { name: :red,~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue << { name: :red,
+** Processing line: ~ create_at: args.state.tick_count + 60 }~
+- Inside source: true
+*** True Line Result
+ create_at: args.state.tick_count + 60 }
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ boxes_to_spawn_this_frame = args.state~
+- Inside source: true
+*** True Line Result
+ boxes_to_spawn_this_frame = args.state
+** Processing line: ~ .box_queue~
+- Inside source: true
+*** True Line Result
+ .box_queue
+** Processing line: ~ .find_all { |b| b[:create_at].new? }~
+- Inside source: true
+*** True Line Result
+ .find_all { |b| b[:create_at].new? }
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ boxes_to_spawn_this_frame.each { |b| puts "box #{b} was new? on #{args.state.tick_count}." }~
+- Inside source: true
+*** True Line Result
+ boxes_to_spawn_this_frame.each { |b| puts "box #{b} was new? on #{args.state.tick_count}." }
+** Processing line: ~~
+- Inside source: true
+*** True Line Result
+
+** Processing line: ~ args.state.box_queue -= boxes_to_spawn_this_frame~
+- Inside source: true
+*** True Line Result
+ args.state.box_queue -= boxes_to_spawn_this_frame
+** Processing line: ~ end~
+- Inside source: true
+*** True Line Result
+ end
+** Processing line: ~#+end_src~
+- Line was identified as the end of a code block.
+*** True Line Result
+#+end_src
+** Processing line: ~~
+- End of paragraph detected.
+*** True Line Result
+
** Processing line: ~* DOCS: ~Kernel~~
- Header detected.
*** True Line Result
@@ -5513,10 +6034,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~Returns the current tick of the application from the point it was started. This value is never reset.~
* Processing Html Given True Lines
** Processing line: ~* DragonRuby Game Toolkit Live Docs~
+- H1 detected.
- Formatting line: ~DragonRuby Game Toolkit Live Docs~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~The information contained here is all available via the DragonRuby Console. You can Open the DragonRuby Console by pressing [`] [~] [²] [^] [º] or [§] within your game.~
- P detected.
@@ -5544,10 +6065,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- LINK detected.
** Processing line: ~~
** Processing line: ~* Hello World~
+- H1 detected.
- Formatting line: ~Hello World~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Welcome to DragonRuby Game Toolkit. Take the steps below to get started.~
- P detected.
@@ -5556,10 +6077,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~* Join the Discord and Subscribe to the News Letter~
+- H1 detected.
- Formatting line: ~Join the Discord and Subscribe to the News Letter~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Our Discord channel is [[http://discord.dragonruby.org]].~
- P detected.
@@ -5607,10 +6128,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* Watch Some Intro Videos~
+- H1 detected.
- Formatting line: ~Watch Some Intro Videos~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Each video is only 20 minutes and all of them will fit into a lunch break. So please watch them:~
- P detected.
@@ -5651,10 +6172,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- LINK detected.
** Processing line: ~~
** Processing line: ~* Getting Started Tutorial~
+- H1 detected.
- Formatting line: ~Getting Started Tutorial~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~This is a tutorial written by Ryan C Gordon (a Juggernaut in the industry who has contracted to Valve, Epic, Activision, and EA... check out his Wikipedia page: [[https://en.wikipedia.org/wiki/Ryan_C._Gordon]]).~
- P detected.
@@ -5664,10 +6185,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- LINK detected.
** Processing line: ~~
** Processing line: ~** Introduction~
+- H2 detected.
- Formatting line: ~Introduction~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Introduction~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -5699,10 +6220,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Prerequisites~
+- H2 detected.
- Formatting line: ~Prerequisites~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Prerequisites~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -5719,10 +6240,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** The Game Loop~
+- H2 detected.
- Formatting line: ~The Game Loop~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~The Game Loop~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -5777,11 +6298,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~** Breakdown Of The ~tick~ Method~
+- H2 detected.
- Formatting line: ~Breakdown Of The ~tick~ Method~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H2 detected.
- Formatting line: ~Breakdown Of The ~tick~ Method~
- Line's tilde count is: 2
- Line contains link marker: false
@@ -5824,10 +6345,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~** Rendering A Sprite~
+- H2 detected.
- Formatting line: ~Rendering A Sprite~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Rendering A Sprite~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -5870,10 +6391,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~** Coordinate System and Virtual Canvas~
+- H2 detected.
- Formatting line: ~Coordinate System and Virtual Canvas~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Coordinate System and Virtual Canvas~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -5912,10 +6433,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Game State~
+- H2 detected.
- Formatting line: ~Game State~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Game State~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -5934,10 +6455,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~** There Is No Delta Time~
+- H2 detected.
- Formatting line: ~There Is No Delta Time~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~There Is No Delta Time~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -5954,10 +6475,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Handling User Input~
+- H2 detected.
- Formatting line: ~Handling User Input~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Handling User Input~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6001,10 +6522,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~** Coding On A Raspberry Pi~
+- H2 detected.
- Formatting line: ~Coding On A Raspberry Pi~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Coding On A Raspberry Pi~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6048,10 +6569,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Conclusion~
+- H2 detected.
- Formatting line: ~Conclusion~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Conclusion~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6064,10 +6585,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~** IMPORTANT: Go Through All Of The Sample Apps! Study Them Thoroughly!!~
+- H2 detected.
- Formatting line: ~IMPORTANT: Go Through All Of The Sample Apps! Study Them Thoroughly!!~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~IMPORTANT: Go Through All Of The Sample Apps! Study Them Thoroughly!!~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6310,120 +6831,108 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Formatting line: ~ 18_moddable_game: How you can make a game where content is authored by the player (modding support).~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~45. 19_lowrez_jam_01_hello_world: How to use ~render_targets~ to create a low resolution game.~
+** Processing line: ~45. 19_lowrez_jam: How to use ~render_targets~ to create a low resolution game.~
- LI detected.
-- Formatting line: ~ 19_lowrez_jam_01_hello_world: How to use ~render_targets~ to create a low resolution game.~
+- Formatting line: ~ 19_lowrez_jam: How to use ~render_targets~ to create a low resolution game.~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-** Processing line: ~46. 19_lowrez_jam_02_buttons: How to use ~render_targets~ to create a low resolution game.~
-- LI detected.
-- Formatting line: ~ 19_lowrez_jam_02_buttons: How to use ~render_targets~ to create a low resolution game.~
-- Line's tilde count is: 2
-- Line contains link marker: false
-- CODE detected.
-** Processing line: ~47. 19_lowrez_jam_03_space_shooter: How to use ~render_targets~ to create a low resolution game.~
-- LI detected.
-- Formatting line: ~ 19_lowrez_jam_03_space_shooter: How to use ~render_targets~ to create a low resolution game.~
-- Line's tilde count is: 2
-- Line contains link marker: false
-- CODE detected.
-** Processing line: ~48. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.~
+** Processing line: ~46. 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.~
- LI detected.
- Formatting line: ~ 20_roguelike_starting_point: A starting point for a roguelike and explores concepts such as line of sight.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~49. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.~
+** Processing line: ~47. 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.~
- LI detected.
- Formatting line: ~ 20_roguelike_starting_point_two: A starting point for a roguelike where sprites are provided from a tile map/tile sheet.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~50. 21_mailbox_usage: How to do interprocess communication.~
+** Processing line: ~48. 21_mailbox_usage: How to do interprocess communication.~
- LI detected.
- Formatting line: ~ 21_mailbox_usage: How to do interprocess communication.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~51. 22_trace_debugging: Debugging techniques and tracing execution through your game.~
+** Processing line: ~49. 22_trace_debugging: Debugging techniques and tracing execution through your game.~
- LI detected.
- Formatting line: ~ 22_trace_debugging: Debugging techniques and tracing execution through your game.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~52. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.~
+** Processing line: ~50. 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.~
- LI detected.
- Formatting line: ~ 22_trace_debugging_classes: Debugging techniques and tracing execution through your game.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~53. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.~
+** Processing line: ~51. 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.~
- LI detected.
- Formatting line: ~ 23_hexagonal_grid: How to make a tactical grid/map made of hexagons.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~54. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.~
+** Processing line: ~52. 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.~
- LI detected.
- Formatting line: ~ 23_isometric_grid: How to make a tactical grid/map made of isometric sprites.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~55. 24_http_example: How to make http requests.~
+** Processing line: ~53. 24_http_example: How to make http requests.~
- LI detected.
- Formatting line: ~ 24_http_example: How to make http requests.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~56. 25_3d_experiment_01_square: How to create 3D objects.~
+** Processing line: ~54. 25_3d_experiment_01_square: How to create 3D objects.~
- LI detected.
- Formatting line: ~ 25_3d_experiment_01_square: How to create 3D objects.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~57. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.~
+** Processing line: ~55. 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.~
- LI detected.
- Formatting line: ~ 26_jam_craft: Starting point for crafting game. It also shows how to customize the mouse cursor.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~58. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.~
+** Processing line: ~56. 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.~
- LI detected.
- Formatting line: ~ 99_sample_game_basic_gorillas: Reference implementation of a full game. Topics covered: physics, keyboard input, collision, sprite animation.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~59. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.~
+** Processing line: ~57. 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.~
- LI detected.
- Formatting line: ~ 99_sample_game_clepto_frog: Reference implementation of a full game. Topics covered: camera control, spring/rope physics, scene orchestration.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~60. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.~
+** Processing line: ~58. 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.~
- LI detected.
- Formatting line: ~ 99_sample_game_dueling_starships: Reference implementation that shows local multiplayer. Topics covered: vectors, particles, friction, inertia.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~61. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.~
+** Processing line: ~59. 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.~
- LI detected.
- Formatting line: ~ 99_sample_game_flappy_dragon: Reference implementation that is a clone of Flappy Bird. Topics covered: scene orchestration, collision, sound, sprite animations, lerping.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~62. 99_sample_game_pong: Reference implementation of pong.~
+** Processing line: ~60. 99_sample_game_pong: Reference implementation of pong.~
- LI detected.
- Formatting line: ~ 99_sample_game_pong: Reference implementation of pong.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~63. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.~
+** Processing line: ~61. 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.~
- LI detected.
- Formatting line: ~ 99_sample_game_return_of_serenity: Reference implementation of low resolution story based game.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~64. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.~
+** Processing line: ~62. 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.~
- LI detected.
- Formatting line: ~ 99_sample_game_the_little_probe: Reference implementation of a full game. Topics covered: Arbitrary collision detection, loading map data, bounce/ball physics.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~65. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.~
+** Processing line: ~63. 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.~
- LI detected.
- Formatting line: ~ 99_sample_nddnug_workshop: Reference implementation of a full game. Topics covered: vectors, controller input, sound, trig functions.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~66. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.~
+** Processing line: ~64. 99_sample_snakemoji: Shows that Ruby supports coding with emojis.~
- LI detected.
- Formatting line: ~ 99_sample_snakemoji: Shows that Ruby supports coding with emojis.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~67. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.~
+** Processing line: ~65. 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.~
- LI detected.
- Formatting line: ~ 99_zz_gtk_unit_tests: A collection of unit tests that exercise parts of DragonRuby's API.~
- Line's tilde count is: 0
@@ -6432,10 +6941,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* Deploying To Itch.io~
+- H1 detected.
- Formatting line: ~Deploying To Itch.io~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Once you've built your game, you're all set to deploy! Good luck in your game dev journey and if you get stuck, come to the Discord channel!~
- P detected.
@@ -6444,10 +6953,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Creating Your Game Landing Page~
+- H2 detected.
- Formatting line: ~Creating Your Game Landing Page~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Creating Your Game Landing Page~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6495,10 +7004,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Update Your Game's Metadata~
+- H2 detected.
- Formatting line: ~Update Your Game's Metadata~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Update Your Game's Metadata~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6533,10 +7042,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~** Building Your Game For Distribution~
+- H2 detected.
- Formatting line: ~Building Your Game For Distribution~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Building Your Game For Distribution~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6593,10 +7102,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** DragonRuby's Philosophy~
+- H2 detected.
- Formatting line: ~DragonRuby's Philosophy~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~DragonRuby's Philosophy~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6608,10 +7117,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Challenge The Status Quo~
+- H3 detected.
- Formatting line: ~Challenge The Status Quo~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Challenge The Status Quo~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6639,10 +7148,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Release Often And Quickly~
+- H3 detected.
- Formatting line: ~Release Often And Quickly~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Release Often And Quickly~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6675,10 +7184,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~*** Sustainable And Ethical Monetization~
+- H3 detected.
- Formatting line: ~Sustainable And Ethical Monetization~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Sustainable And Ethical Monetization~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6695,10 +7204,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Sustainable And Ethical Open Source~
+- H3 detected.
- Formatting line: ~Sustainable And Ethical Open Source~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Sustainable And Ethical Open Source~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6715,10 +7224,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** People Over Entities~
+- H3 detected.
- Formatting line: ~People Over Entities~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~People Over Entities~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6730,10 +7239,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Building A Game Should Be Fun And Bring Happiness~
+- H3 detected.
- Formatting line: ~Building A Game Should Be Fun And Bring Happiness~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Building A Game Should Be Fun And Bring Happiness~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6745,10 +7254,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Real World Application Drives Features~
+- H3 detected.
- Formatting line: ~Real World Application Drives Features~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Real World Application Drives Features~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -6765,10 +7274,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~* How To Determine What Frame You Are On~
+- H1 detected.
- Formatting line: ~How To Determine What Frame You Are On~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~There is a property on ~state~ called ~tick_count~ that is incremented by DragonRuby every time the ~tick~ method is called. The following code renders a label that displays the current ~tick_count~.~
- P detected.
@@ -6787,10 +7296,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* How To Get Current Framerate~
+- H1 detected.
- Formatting line: ~How To Get Current Framerate~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Current framerate is a top level property on the Game Toolkit Runtime and is accessible via ~args.gtk.current_framerate~.~
- P detected.
@@ -6809,10 +7318,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* How To Render A Sprite Using An Array~
+- H1 detected.
- Formatting line: ~How To Render A Sprite Using An Array~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~All file paths should use the forward slash ~/~ *not* backslash ~~. Game Toolkit includes a number of sprites in the ~sprites~ folder (everything about your game is located in the ~mygame~ directory).~
- P detected.
@@ -6849,10 +7358,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* More Sprite Properties As An Array~
+- H1 detected.
- Formatting line: ~More Sprite Properties As An Array~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Here are all the properties you can set on a sprite.~
- P detected.
@@ -6881,10 +7390,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* Different Sprite Representations~
+- H1 detected.
- Formatting line: ~Different Sprite Representations~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Using ordinal positioning can get a little unruly given so many properties you have control over.~
- P detected.
@@ -6968,10 +7477,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* How To Render A Label~
+- H1 detected.
- Formatting line: ~How To Render A Label~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~~args.outputs.labels~ is used to render labels.~
- P detected.
@@ -7002,10 +7511,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* A Colored Label~
+- H1 detected.
- Formatting line: ~A Colored Label~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~~
** Processing line: ~#+begin_src~
@@ -7020,10 +7529,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* Extended Label Properties~
+- H1 detected.
- Formatting line: ~Extended Label Properties~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~~
** Processing line: ~#+begin_src~
@@ -7061,11 +7570,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~* Rendering A Label As A ~Hash~~
+- H1 detected.
- Formatting line: ~Rendering A Label As A ~Hash~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~You can add additional metadata about your game within a label, which requires you to use a `Hash` instead.~
- P detected.
@@ -7101,10 +7610,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* Getting The Size Of A Piece Of Text~
+- H1 detected.
- Formatting line: ~Getting The Size Of A Piece Of Text~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~You can get the render size of any string using ~args.gtk.calcstringbox~.~
- P detected.
@@ -7134,10 +7643,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* How To Play A Sound~
+- H1 detected.
- Formatting line: ~How To Play A Sound~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Sounds that end ~.wav~ will play once:~
- P detected.
@@ -7195,11 +7704,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* Using ~args.state~ To Store Your Game State~
+- H1 detected.
- Formatting line: ~Using ~args.state~ To Store Your Game State~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~~args.state~ is a open data structure that allows you to define properties that are arbitrarily nested. You don't need to define any kind of ~class~.~
- P detected.
@@ -7250,10 +7759,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* Frequently Asked Questions, Comments, and Concerns~
+- H1 detected.
- Formatting line: ~Frequently Asked Questions, Comments, and Concerns~
- Line's tilde count is: 0
- Line contains link marker: false
-- H1 detected.
** Processing line: ~~
** Processing line: ~Here are questions, comments, and concerns that frequently come up.~
- P detected.
@@ -7262,20 +7771,20 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Frequently Asked Questions~
+- H2 detected.
- Formatting line: ~Frequently Asked Questions~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Frequently Asked Questions~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~~
** Processing line: ~*** What is DragonRuby LLP?~
+- H3 detected.
- Formatting line: ~What is DragonRuby LLP?~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~What is DragonRuby LLP?~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7336,10 +7845,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** What is DragonRuby?~
+- H3 detected.
- Formatting line: ~What is DragonRuby?~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~What is DragonRuby?~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7351,17 +7860,17 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Okay... so what is the difference between a language specification and a runtime?~
+- H3 detected.
- Formatting line: ~Okay... so what is the difference between a language specification and a runtime?~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Okay... so what is the difference between a language specification and a runtime?~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
-** Processing line: ~A runtime is an _implementation_ of a langauge specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."~
+** Processing line: ~A runtime is an _implementation_ of a language specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."~
- P detected.
-- Formatting line: ~A runtime is an _implementation_ of a langauge specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."~
+- Formatting line: ~A runtime is an _implementation_ of a language specification. When people say "Ruby," they are usually referring to "the Ruby 3.0+ language specification implemented via the CRuby/MRI Runtime."~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~But, there are many Ruby Runtimes: CRuby/MRI, JRuby, Truffle, Rubinius, Artichoke, and (last but certainly not least) DragonRuby.~
@@ -7371,10 +7880,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Okay... what language specification does DragonRuby use then?~
+- H3 detected.
- Formatting line: ~Okay... what language specification does DragonRuby use then?~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Okay... what language specification does DragonRuby use then?~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7386,10 +7895,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** So... why another runtime?~
+- H3 detected.
- Formatting line: ~So... why another runtime?~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~So... why another runtime?~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7406,10 +7915,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** What does Multilevel Cross-platform mean?~
+- H3 detected.
- Formatting line: ~What does Multilevel Cross-platform mean?~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~What does Multilevel Cross-platform mean?~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7440,27 +7949,27 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
C-Extensions.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~Levels 1 through 3 are fairly commonplace in many runtime implemenations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:~
+** Processing line: ~Levels 1 through 3 are fairly commonplace in many runtime implementations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:~
- UL end detected.
- P detected.
-- Formatting line: ~Levels 1 through 3 are fairly commonplace in many runtime implemenations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:~
+- Formatting line: ~Levels 1 through 3 are fairly commonplace in many runtime implementations (with level 1 being the most portable, and level 3 being the fastest). But the DragonRuby Runtime has taken things a bit further:~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~- Level 4 consists of shared abstractions around hardware I/O and operating
- system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a lowlevel multimedia component library that has been in active development for 22 years and counting).~
+ system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a low level multimedia component library that has been in active development for 22 years and counting).~
- UL start detected.
- LI detected.
- Formatting line: ~Level 4 consists of shared abstractions around hardware I/O and operating
- system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a lowlevel multimedia component library that has been in active development for 22 years and counting).~
+ system resources. This level leverages open source and proprietary components within Simple DirectMedia Layer (a low level multimedia component library that has been in active development for 22 years and counting).~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
-** Processing line: ~- Level 5 is a codegeneration layer which creates metadata that allows
- for native interopability with host runtime libraries. It also includes OS specific message pump orchestrations.~
+** Processing line: ~- Level 5 is a code generation layer which creates metadata that allows
+ for native interoperability with host runtime libraries. It also includes OS specific message pump orchestrations.~
- LI detected.
-- Formatting line: ~Level 5 is a codegeneration layer which creates metadata that allows
- for native interopability with host runtime libraries. It also includes OS specific message pump orchestrations.~
+- Formatting line: ~Level 5 is a code generation layer which creates metadata that allows
+ for native interoperability with host runtime libraries. It also includes OS specific message pump orchestrations.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
@@ -7471,43 +7980,43 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
compiler outputs _very_ fast platform specific bitcode, but only supports a subset of the Ruby language specification.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good seperation between these two worlds; and provides a means to add new platforms without going insane.~
+** Processing line: ~These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good separation between these two worlds; and provides a means to add new platforms without going insane.~
- UL end detected.
- P detected.
-- Formatting line: ~These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good seperation between these two worlds; and provides a means to add new platforms without going insane.~
+- Formatting line: ~These levels allow us to stay up to date with open source implementations of Ruby; provide fast, native code execution on proprietary platforms; ensure good separation between these two worlds; and provides a means to add new platforms without going insane.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?~
+- H3 detected.
- Formatting line: ~Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
-** Processing line: ~DragonRuby is a Ruby runtime implentation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.~
+** Processing line: ~DragonRuby is a Ruby runtime implementation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.~
- P detected.
-- Formatting line: ~DragonRuby is a Ruby runtime implentation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.~
+- Formatting line: ~DragonRuby is a Ruby runtime implementation that takes all the lessons we've learned from MRI/CRuby, and merges it with the latest and greatest compiler and OSS technologies.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Frequent Comments~
+- H2 detected.
- Formatting line: ~Frequent Comments~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Frequent Comments~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~~
** Processing line: ~*** But Ruby is dead.~
+- H3 detected.
- Formatting line: ~But Ruby is dead.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~But Ruby is dead.~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7535,10 +8044,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** But Ruby is slow.~
+- H3 detected.
- Formatting line: ~But Ruby is slow.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~But Ruby is slow.~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7549,12 +8058,12 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
-** Processing line: ~*** Dynamic langauges are slow.~
-- Formatting line: ~Dynamic langauges are slow.~
+** Processing line: ~*** Dynamic languages are slow.~
+- H3 detected.
+- Formatting line: ~Dynamic languages are slow.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
-- Formatting line: ~Dynamic langauges are slow.~
+- Formatting line: ~Dynamic languages are slow.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
@@ -7563,44 +8072,44 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Formatting line: ~They are certainly slower than statically compiled languages. With the processing power and compiler optimizations we have today, dynamic languages like Ruby are _fast enough_.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~Unless you are writing in some form of intermediate representation by hand, your langauge of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.~
+** Processing line: ~Unless you are writing in some form of intermediate representation by hand, your language of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.~
- P detected.
-- Formatting line: ~Unless you are writing in some form of intermediate representation by hand, your langauge of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.~
+- Formatting line: ~Unless you are writing in some form of intermediate representation by hand, your language of choice also suffers this same fallacy of slow. Like, nothing is faster than a low level assembly-like language. So unless you're writing in that, let's stop making this comment.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skillset. Email us ^_^.~
+** Processing line: ~NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skill set. Email us ^_^.~
- P detected.
-- Formatting line: ~NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skillset. Email us ^_^.~
+- Formatting line: ~NOTE: If you _are_ hand writing LLVM IR, we are always open to bringing on new partners with such a skill set. Email us ^_^.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Frequent Concerns~
+- H2 detected.
- Formatting line: ~Frequent Concerns~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Frequent Concerns~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~~
** Processing line: ~*** DragonRuby is not open source. That's not right.~
+- H3 detected.
- Formatting line: ~DragonRuby is not open source. That's not right.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~DragonRuby is not open source. That's not right.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
-** Processing line: ~The current state of open source is unsustainable. Contributors work for free, most all open source repositories are serverly understaffed, and burnout from core members is rampant.~
+** Processing line: ~The current state of open source is unsustainable. Contributors work for free, most all open source repositories are severely under-staffed, and burnout from core members is rampant.~
- P detected.
-- Formatting line: ~The current state of open source is unsustainable. Contributors work for free, most all open source repositories are serverly understaffed, and burnout from core members is rampant.~
+- Formatting line: ~The current state of open source is unsustainable. Contributors work for free, most all open source repositories are severely under-staffed, and burnout from core members is rampant.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~We believe in open source very strongly. Parts of DragonRuby are infact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.~
+** Processing line: ~We believe in open source very strongly. Parts of DragonRuby are in fact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.~
- P detected.
-- Formatting line: ~We believe in open source very strongly. Parts of DragonRuby are infact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.~
+- Formatting line: ~We believe in open source very strongly. Parts of DragonRuby are in fact, open source. Just not all of it (for legal reasons, and because the IP we've created has value). And we promise that we are looking for (or creating) ways to _sustainably_ open source everything we do.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~If you have ideas on how we can do this, email us!~
@@ -7615,10 +8124,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** DragonRuby is for pay. You should offer a free version.~
+- H3 detected.
- Formatting line: ~DragonRuby is for pay. You should offer a free version.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~DragonRuby is for pay. You should offer a free version.~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7677,10 +8186,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** But still, you should offer a free version. So I can try it out and see if I like it.~
+- H3 detected.
- Formatting line: ~But still, you should offer a free version. So I can try it out and see if I like it.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~But still, you should offer a free version. So I can try it out and see if I like it.~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7697,10 +8206,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** I still think you should do a free version. Think of all people who would give it a shot.~
+- H3 detected.
- Formatting line: ~I still think you should do a free version. Think of all people who would give it a shot.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~I still think you should do a free version. Think of all people who would give it a shot.~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7710,17 +8219,17 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Formatting line: ~Free isn't a sustainable financial model. We don't want to spam your email. We don't want to collect usage data off of you either. We just want to provide quality toolchains to quality developers (as opposed to a large quantity of developers).~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~The peiple that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.~
+** Processing line: ~The people that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.~
- P detected.
-- Formatting line: ~The peiple that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.~
+- Formatting line: ~The people that pay for DragonRuby and make an effort to understand it are the ones we want to build a community around, partner with, and collaborate with. So having that small monetary wall deters entitled individuals that don't value the same things we do.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~*** What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent.~
+- H3 detected.
- Formatting line: ~What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent.~
- Line's tilde count is: 0
- Line contains link marker: false
-- H3 detected.
- Formatting line: ~What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent.~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -7730,18 +8239,18 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Formatting line: ~That won't happen if the development world stop asking for free stuff and non-trivially compensate open source developers. Look, we want to be able to work on the stuff we love, every day of our lives. And we'll go to great lengths to make that happen.~
- Line's tilde count is: 0
- Line contains link marker: false
-** Processing line: ~But, in the event that sad day comes, our partnershiop bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.~
+** Processing line: ~But, in the event that sad day comes, our partnership bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.~
- P detected.
-- Formatting line: ~But, in the event that sad day comes, our partnershiop bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.~
+- Formatting line: ~But, in the event that sad day comes, our partnership bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license.~
- Line's tilde count is: 0
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::Runtime~~
+- H1 detected.
- Formatting line: ~~GTK::Runtime~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.~
- P detected.
- Formatting line: ~The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.~
@@ -7749,12 +8258,24 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
- CODE detected.
** Processing line: ~~
+** Processing line: ~* DOCS: ~GTK::Runtime#reset~~
+- H1 detected.
+- Formatting line: ~~GTK::Runtime#reset~~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~This function will reset Kernel.tick_count to 0 and will remove all data from args.state.~
+- P detected.
+- Formatting line: ~This function will reset Kernel.tick_count to 0 and will remove all data from args.state.~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::Runtime#calcstringbox~~
+- H1 detected.
- Formatting line: ~~GTK::Runtime#calcstringbox~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~This function returns the width and height of a string.~
- P detected.
- Formatting line: ~This function returns the width and height of a string.~
@@ -7771,24 +8292,12 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- PRE end detected.
** Processing line: ~~
** Processing line: ~~
-** Processing line: ~* DOCS: ~GTK::Runtime#reset~~
-- Formatting line: ~~GTK::Runtime#reset~~
-- Line's tilde count is: 2
-- Line contains link marker: false
-- CODE detected.
-- H1 detected.
-** Processing line: ~This function will reset Kernel.tick_count to 0 and will remove all data from args.state.~
-- P detected.
-- Formatting line: ~This function will reset Kernel.tick_count to 0 and will remove all data from args.state.~
-- Line's tilde count is: 0
-- Line contains link marker: false
-** Processing line: ~~
** Processing line: ~* DOCS: ~Array~~
+- H1 detected.
- Formatting line: ~~Array~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~The Array class has been extend to provide methods that will help in common game development tasks. Array is one of the most powerful classes in Ruby and a very fundamental component of Game Toolkit.~
- P detected.
@@ -7798,11 +8307,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#map~~
+- H1 detected.
- Formatting line: ~~Array#map~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~The function given a block returns a new ~Enumerable~ of values.~
- P detected.
@@ -7855,11 +8364,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#each~~
+- H1 detected.
- Formatting line: ~~Array#each~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~The function, given a block, invokes the block for each item in the ~Array~. ~Array#each~ is synonymous to foreach constructs in other languages.~
- P detected.
@@ -7911,11 +8420,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#reject_nil~~
+- H1 detected.
- Formatting line: ~~Array#reject_nil~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Returns an ~Enumerable~ rejecting items that are ~nil~, this is an alias for ~Array#compact~:~
- P detected.
@@ -7939,11 +8448,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#reject_false~~
+- H1 detected.
- Formatting line: ~~Array#reject_false~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Returns an `Enumerable` rejecting items that are `nil` or `false`.~
- P detected.
@@ -7964,11 +8473,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#product~~
+- H1 detected.
- Formatting line: ~~Array#product~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Returns all combinations of values between two arrays.~
- P detected.
@@ -8007,11 +8516,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#map_2d~~
+- H1 detected.
- Formatting line: ~~Array#map_2d~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Assuming the array is an array of arrays, Given a block, each 2D array index invoked against the block. A 2D array is a common way to store data/layout for a stage.~
- P detected.
@@ -8048,11 +8557,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#include_any?~~
+- H1 detected.
- Formatting line: ~~Array#include_any?~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Given a collection of items, the function will return ~true~ if any of ~self~'s items exists in the collection of items passed in:~
- P detected.
@@ -8063,11 +8572,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Array#any_intersect_rect?~~
+- H1 detected.
- Formatting line: ~~Array#any_intersect_rect?~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Assuming the array contains objects that respond to ~left~, ~right~, ~top~, ~bottom~, this method returns ~true~ if any of the elements within the array intersect the object being passed in. You are given an optional parameter called ~tolerance~ which informs how close to the other rectangles the elements need to be for it to be considered intersecting.~
- P detected.
@@ -8144,11 +8653,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::Outputs~~
+- H1 detected.
- Formatting line: ~~GTK::Outputs~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Outputs is how you render primitives to the screen. The minimal setup for rendering something to the screen is via a ~tick~ method defined in mygame/app/main.rb~
- P detected.
@@ -8167,63 +8676,12 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~~
-** Processing line: ~* DOCS: ~GTK::Outputs#borders~~
-- Formatting line: ~~GTK::Outputs#borders~~
-- Line's tilde count is: 2
-- Line contains link marker: false
-- CODE detected.
-- H1 detected.
-** Processing line: ~~
-** Processing line: ~Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.~
-- P detected.
-- Formatting line: ~Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.~
-- Line's tilde count is: 0
-- Line contains link marker: false
-** Processing line: ~The only difference between the two primitives is where they are added.~
-- P detected.
-- Formatting line: ~The only difference between the two primitives is where they are added.~
-- Line's tilde count is: 0
-- Line contains link marker: false
-** Processing line: ~Instead of using ~args.outputs.solids~:~
-- P detected.
-- Formatting line: ~Instead of using ~args.outputs.solids~:~
-- Line's tilde count is: 2
-- Line contains link marker: false
-- CODE detected.
-** Processing line: ~~
-** Processing line: ~#+begin_src~
-- PRE start detected.
-** Processing line: ~ def tick args~
-** Processing line: ~ # X Y WIDTH HEIGHT~
-** Processing line: ~ args.outputs.solids << [100, 100, 160, 90]~
-** Processing line: ~ end~
-** Processing line: ~#+end_src~
-- PRE end detected.
-** Processing line: ~~
-** Processing line: ~You have to use ~args.outputs.borders~:~
-- P detected.
-- Formatting line: ~You have to use ~args.outputs.borders~:~
-- Line's tilde count is: 2
-- Line contains link marker: false
-- CODE detected.
-** Processing line: ~~
-** Processing line: ~#+begin_src~
-- PRE start detected.
-** Processing line: ~ def tick args~
-** Processing line: ~ # X Y WIDTH HEIGHT~
-** Processing line: ~ args.outputs.borders << [100, 100, 160, 90]~
-** Processing line: ~ end~
-** Processing line: ~#+end_src~
-- PRE end detected.
-** Processing line: ~~
-** Processing line: ~~
-** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::Outputs#solids~~
+- H1 detected.
- Formatting line: ~~GTK::Outputs#solids~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Add primitives to this collection to render a solid to the screen.~
- P detected.
@@ -8232,10 +8690,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- Line contains link marker: false
** Processing line: ~~
** Processing line: ~** Rendering a solid using an Array~
+- H2 detected.
- Formatting line: ~Rendering a solid using an Array~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Rendering a solid using an Array~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -8257,10 +8715,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~** Rendering a solid using an Array with colors and alpha~
+- H2 detected.
- Formatting line: ~Rendering a solid using an Array with colors and alpha~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Rendering a solid using an Array with colors and alpha~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -8288,10 +8746,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~** Rendering a solid using a Hash~
+- H2 detected.
- Formatting line: ~Rendering a solid using a Hash~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Rendering a solid using a Hash~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -8321,10 +8779,10 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~** Rendering a solid using a Class~
+- H2 detected.
- Formatting line: ~Rendering a solid using a Class~
- Line's tilde count is: 0
- Line contains link marker: false
-- H2 detected.
- Formatting line: ~Rendering a solid using a Class~
- Line's tilde count is: 0
- Line contains link marker: false
@@ -8372,12 +8830,63 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~~
+** Processing line: ~* DOCS: ~GTK::Outputs#borders~~
+- H1 detected.
+- Formatting line: ~~GTK::Outputs#borders~~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~~
+** Processing line: ~Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.~
+- P detected.
+- Formatting line: ~Add primitives to this collection to render an unfilled solid to the screen. Take a look at the documentation for Outputs#solids.~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~The only difference between the two primitives is where they are added.~
+- P detected.
+- Formatting line: ~The only difference between the two primitives is where they are added.~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~Instead of using ~args.outputs.solids~:~
+- P detected.
+- Formatting line: ~Instead of using ~args.outputs.solids~:~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~~
+** Processing line: ~#+begin_src~
+- PRE start detected.
+** Processing line: ~ def tick args~
+** Processing line: ~ # X Y WIDTH HEIGHT~
+** Processing line: ~ args.outputs.solids << [100, 100, 160, 90]~
+** Processing line: ~ end~
+** Processing line: ~#+end_src~
+- PRE end detected.
+** Processing line: ~~
+** Processing line: ~You have to use ~args.outputs.borders~:~
+- P detected.
+- Formatting line: ~You have to use ~args.outputs.borders~:~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~~
+** Processing line: ~#+begin_src~
+- PRE start detected.
+** Processing line: ~ def tick args~
+** Processing line: ~ # X Y WIDTH HEIGHT~
+** Processing line: ~ args.outputs.borders << [100, 100, 160, 90]~
+** Processing line: ~ end~
+** Processing line: ~#+end_src~
+- PRE end detected.
+** Processing line: ~~
+** Processing line: ~~
+** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::Mouse~~
+- H1 detected.
- Formatting line: ~~GTK::Mouse~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~The mouse is accessible via ~args.inputs.mouse~:~
- P detected.
@@ -8498,11 +9007,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
- CODE detected.
** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::MousePoint~~
+- H1 detected.
- Formatting line: ~~GTK::MousePoint~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~The ~GTK::MousePoint~ has the following properties.~
- P detected.
@@ -8581,11 +9090,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::OpenEntity~~
+- H1 detected.
- Formatting line: ~~GTK::OpenEntity~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~~GTK::OpenEntity~ is accessible within the DragonRuby's top level ~tick~ function via the ~args.state~ property.~
- P detected.
@@ -8633,11 +9142,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~GTK::OpenEntity#as_hash~~
+- H1 detected.
- Formatting line: ~~GTK::OpenEntity#as_hash~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Returns a reference to the ~GTK::OpenEntity~ as a ~Hash~. This property is useful when you want to treat ~args.state~ as a ~Hash~ and invoke methods such as ~Hash#each~.~
- P detected.
@@ -8674,11 +9183,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Numeric#frame_index~~
+- H1 detected.
- Formatting line: ~~Numeric#frame_index~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~This function is helpful for determining the index of frame-by-frame sprite animation. The numeric value ~self~ represents the moment the animation started.~
- P detected.
@@ -8780,12 +9289,204 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~~
+** Processing line: ~* DOCS: ~Numeric#elapsed_time~~
+- H1 detected.
+- Formatting line: ~~Numeric#elapsed_time~~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~For a given number, the elapsed frames since that number is returned. `Kernel.tick_count` is used to determine how many frames have elapsed. An optional numeric argument can be passed in which will be used instead of `Kernel.tick_count`.~
+- P detected.
+- Formatting line: ~For a given number, the elapsed frames since that number is returned. `Kernel.tick_count` is used to determine how many frames have elapsed. An optional numeric argument can be passed in which will be used instead of `Kernel.tick_count`.~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~Here is an example of how elapsed_time can be used.~
+- P detected.
+- Formatting line: ~Here is an example of how elapsed_time can be used.~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~~
+** Processing line: ~#+begin_src ruby~
+- PRE start detected.
+** Processing line: ~ def tick args~
+** Processing line: ~ args.state.last_click_at ||= 0~
+** Processing line: ~~
+** Processing line: ~ # record when a mouse click occurs~
+** Processing line: ~ if args.inputs.mouse.click~
+** Processing line: ~ args.state.last_click_at = args.state.tick_count~
+** Processing line: ~ end~
+** Processing line: ~~
+** Processing line: ~ # Use Numeric#elapsed_time to determine how long it's been~
+** Processing line: ~ if args.state.last_click_at.elapsed_time > 120~
+** Processing line: ~ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]~
+** Processing line: ~ end~
+** Processing line: ~ end~
+** Processing line: ~#+end_src~
+- PRE end detected.
+** Processing line: ~~
+** Processing line: ~And here is an example where the override parameter is passed in:~
+- P detected.
+- Formatting line: ~And here is an example where the override parameter is passed in:~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~~
+** Processing line: ~#+begin_src ruby~
+- PRE start detected.
+** Processing line: ~ def tick args~
+** Processing line: ~ args.state.last_click_at ||= 0~
+** Processing line: ~~
+** Processing line: ~ # create a state variable that tracks time at half the speed of args.state.tick_count~
+** Processing line: ~ args.state.simulation_tick = args.state.tick_count.idiv 2~
+** Processing line: ~~
+** Processing line: ~ # record when a mouse click occurs~
+** Processing line: ~ if args.inputs.mouse.click~
+** Processing line: ~ args.state.last_click_at = args.state.simulation_tick~
+** Processing line: ~ end~
+** Processing line: ~~
+** Processing line: ~ # Use Numeric#elapsed_time to determine how long it's been~
+** Processing line: ~ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120~
+** Processing line: ~ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]~
+** Processing line: ~ end~
+** Processing line: ~ end~
+** Processing line: ~#+end_src~
+- PRE end detected.
+** Processing line: ~~
+** Processing line: ~~
+** Processing line: ~~
+** Processing line: ~* DOCS: ~Numeric#elapsed?~~
+- H1 detected.
+- Formatting line: ~~Numeric#elapsed?~~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~Returns true if ~Numeric#elapsed_time~ is greater than the number. An optional parameter can be passed into ~elapsed?~ which is added to the number before evaluating whether ~elapsed?~ is true.~
+- P detected.
+- Formatting line: ~Returns true if ~Numeric#elapsed_time~ is greater than the number. An optional parameter can be passed into ~elapsed?~ which is added to the number before evaluating whether ~elapsed?~ is true.~
+- Line's tilde count is: 6
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~Example usage (no optional parameter):~
+- P detected.
+- Formatting line: ~Example usage (no optional parameter):~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~~
+** Processing line: ~#+begin_src ruby~
+- PRE start detected.
+** Processing line: ~ def tick args~
+** Processing line: ~ args.state.box_queue ||= []~
+** Processing line: ~~
+** Processing line: ~ if args.state.box_queue.empty?~
+** Processing line: ~ args.state.box_queue << { name: :red,~
+** Processing line: ~ destroy_at: args.state.tick_count + 60 }~
+** Processing line: ~ args.state.box_queue << { name: :green,~
+** Processing line: ~ destroy_at: args.state.tick_count + 60 }~
+** Processing line: ~ args.state.box_queue << { name: :blue,~
+** Processing line: ~ destroy_at: args.state.tick_count + 120 }~
+** Processing line: ~ end~
+** Processing line: ~~
+** Processing line: ~ boxes_to_destroy = args.state~
+** Processing line: ~ .box_queue~
+** Processing line: ~ .find_all { |b| b[:destroy_at].elapsed? }~
+** Processing line: ~~
+** Processing line: ~ if !boxes_to_destroy.empty?~
+** Processing line: ~ puts "boxes to destroy count: #{boxes_to_destroy.length}"~
+** Processing line: ~ end~
+** Processing line: ~~
+** Processing line: ~ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }~
+** Processing line: ~~
+** Processing line: ~ args.state.box_queue -= boxes_to_destroy~
+** Processing line: ~ end~
+** Processing line: ~#+end_src~
+- PRE end detected.
+** Processing line: ~~
+** Processing line: ~Example usage (with optional parameter):~
+- P detected.
+- Formatting line: ~Example usage (with optional parameter):~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~~
+** Processing line: ~#+begin_src ruby~
+- PRE start detected.
+** Processing line: ~ def tick args~
+** Processing line: ~ args.state.box_queue ||= []~
+** Processing line: ~~
+** Processing line: ~ if args.state.box_queue.empty?~
+** Processing line: ~ args.state.box_queue << { name: :red,~
+** Processing line: ~ create_at: args.state.tick_count + 120,~
+** Processing line: ~ lifespan: 60 }~
+** Processing line: ~ args.state.box_queue << { name: :green,~
+** Processing line: ~ create_at: args.state.tick_count + 120,~
+** Processing line: ~ lifespan: 60 }~
+** Processing line: ~ args.state.box_queue << { name: :blue,~
+** Processing line: ~ create_at: args.state.tick_count + 120,~
+** Processing line: ~ lifespan: 120 }~
+** Processing line: ~ end~
+** Processing line: ~~
+** Processing line: ~ # lifespan is passed in as a parameter to ~elapsed?~~
+** Processing line: ~ boxes_to_destroy = args.state~
+** Processing line: ~ .box_queue~
+** Processing line: ~ .find_all { |b| b[:create_at].elapsed? b[:lifespan] }~
+** Processing line: ~~
+** Processing line: ~ if !boxes_to_destroy.empty?~
+** Processing line: ~ puts "boxes to destroy count: #{boxes_to_destroy.length}"~
+** Processing line: ~ end~
+** Processing line: ~~
+** Processing line: ~ boxes_to_destroy.each { |b| puts "box #{b} was elapsed? on #{args.state.tick_count}." }~
+** Processing line: ~~
+** Processing line: ~ args.state.box_queue -= boxes_to_destroy~
+** Processing line: ~ end~
+** Processing line: ~#+end_src~
+- PRE end detected.
+** Processing line: ~~
+** Processing line: ~~
+** Processing line: ~~
+** Processing line: ~* DOCS: ~Numeric#created?~~
+- H1 detected.
+- Formatting line: ~~Numeric#created?~~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that number is equal to the current frame.~
+- P detected.
+- Formatting line: ~Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that number is equal to the current frame.~
+- Line's tilde count is: 2
+- Line contains link marker: false
+- CODE detected.
+** Processing line: ~Example usage:~
+- P detected.
+- Formatting line: ~Example usage:~
+- Line's tilde count is: 0
+- Line contains link marker: false
+** Processing line: ~~
+** Processing line: ~#+begin_src ruby~
+- PRE start detected.
+** Processing line: ~ def tick args~
+** Processing line: ~ args.state.box_queue ||= []~
+** Processing line: ~~
+** Processing line: ~ if args.state.box_queue.empty?~
+** Processing line: ~ args.state.box_queue << { name: :red,~
+** Processing line: ~ create_at: args.state.tick_count + 60 }~
+** Processing line: ~ end~
+** Processing line: ~~
+** Processing line: ~ boxes_to_spawn_this_frame = args.state~
+** Processing line: ~ .box_queue~
+** Processing line: ~ .find_all { |b| b[:create_at].new? }~
+** Processing line: ~~
+** Processing line: ~ boxes_to_spawn_this_frame.each { |b| puts "box #{b} was new? on #{args.state.tick_count}." }~
+** Processing line: ~~
+** Processing line: ~ args.state.box_queue -= boxes_to_spawn_this_frame~
+** Processing line: ~ end~
+** Processing line: ~#+end_src~
+- PRE end detected.
+** Processing line: ~~
+** Processing line: ~~
** Processing line: ~* DOCS: ~Kernel~~
+- H1 detected.
- Formatting line: ~~Kernel~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Kernel in the DragonRuby Runtime has patches for how standard out is handled and also contains a unit of time in games called a tick.~
- P detected.
@@ -8795,11 +9496,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Kernel::tick_count~~
+- H1 detected.
- Formatting line: ~~Kernel::tick_count~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Returns the current tick of the game. This value is reset if you call $gtk.reset.~
- P detected.
@@ -8809,11 +9510,11 @@ Returns the current tick of the game. This value is reset if you call $gtk.reset
** Processing line: ~~
** Processing line: ~~
** Processing line: ~* DOCS: ~Kernel::global_tick_count~~
+- H1 detected.
- Formatting line: ~~Kernel::global_tick_count~~
- Line's tilde count is: 2
- Line contains link marker: false
- CODE detected.
-- H1 detected.
** Processing line: ~~
** Processing line: ~Returns the current tick of the application from the point it was started. This value is never reset.~
- P detected.
diff --git a/docs/search_results.txt b/docs/search_results.txt
index b4c5e12..50311a3 100644
--- a/docs/search_results.txt
+++ b/docs/search_results.txt
@@ -1,63 +1,71 @@
-* DOCS: ~Numeric#frame_index~
+* DOCS: ~Numeric#elapsed_time~
+For a given number, the elapsed frames since that number is returned.
+`Kernel.tick_count` is used to determine how many frames have elapsed.
+An optional numeric argument can be passed in which will be used instead
+of `Kernel.tick_count`.
-This function is helpful for determining the index of frame-by-frame
- sprite animation. The numeric value ~self~ represents the moment the
- animation started.
+Here is an example of how elapsed_time can be used.
-~frame_index~ takes three additional parameters:
+#+begin_src ruby
+ def tick args
+ args.state.last_click_at ||= 0
-- How many frames exist in the sprite animation.
-- How long to hold each animation for.
-- Whether the animation should repeat.
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.tick_count
+ end
-~frame_index~ will return ~nil~ if the time for the animation is out
-of bounds of the parameter specification.
+ # Use Numeric#elapsed_time to determine how long it's been
+ if args.state.last_click_at.elapsed_time > 120
+ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]
+ end
+ end
+#+end_src
-Example using variables:
+And here is an example where the override parameter is passed in:
#+begin_src ruby
def tick args
- start_looping_at = 0
- number_of_sprites = 6
- number_of_frames_to_show_each_sprite = 4
- does_sprite_loop = true
-
- sprite_index =
- start_looping_at.frame_index number_of_sprites,
- number_of_frames_to_show_each_sprite,
- does_sprite_loop
-
- sprite_index ||= 0
-
- args.outputs.sprites << [
- 640 - 50,
- 360 - 50,
- 100,
- 100,
- "sprites/dragon-#{sprite_index}.png"
- ]
+ args.state.last_click_at ||= 0
+
+ # create a state variable that tracks time at half the speed of args.state.tick_count
+ args.state.simulation_tick = args.state.tick_count.idiv 2
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.simulation_tick
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120
+ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]
+ end
end
#+end_src
-Example using named parameters:
+
+* DOCS: ~Numeric#created?~
+Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that
+number is equal to the current frame.
+
+Example usage:
#+begin_src ruby
- start_looping_at = 0
-
- sprite_index =
- start_looping_at.frame_index count: 6,
- hold_for: 4,
- repeat: true
-
- sprite_index ||= 0
-
- args.outputs.sprites << [
- 640 - 50,
- 360 - 50,
- 100,
- 100,
- "sprites/dragon-#{sprite_index}.png"
- ]
-#+end_src
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 60 }
+ end
+ boxes_to_spawn_this_frame = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].new? }
+
+ boxes_to_spawn_this_frame.each { |b| puts "box #{b} was new? on #{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_spawn_this_frame
+ end
+#+end_src
diff --git a/dragon/args.rb b/dragon/args.rb
index 0e6ff6f..38a28b7 100644
--- a/dragon/args.rb
+++ b/dragon/args.rb
@@ -41,7 +41,7 @@ module GTK
# @return [OpenEntity]
attr_accessor :state
- # Gives you access to the top level DragonRuby runtime.
+ # Gives you access to the top level DragonRuby runtime.
#
# @return [Runtime]
attr_accessor :runtime
@@ -49,20 +49,24 @@ module GTK
attr_accessor :passes
+ attr_accessor :wizards
+
def initialize runtime, recording
@inputs = Inputs.new
- @outputs = Outputs.new
+ @outputs = Outputs.new args: self
@passes = []
@state = OpenEntity.new
@state.tick_count = -1
@runtime = runtime
@recording = recording
- @grid = Grid.new runtime.ffi_draw
+ @grid = Grid.new runtime
@render_targets = {}
@all_tests = []
@geometry = GTK::Geometry
+ @wizards = Wizards.new
end
+
# The number of ticks since the start of the game.
#
# @return [Integer]
@@ -89,13 +93,21 @@ module GTK
end
def clear_render_targets
+ render_targets_clear
+ end
+
+ def render_targets_clear
@render_targets = {}
end
+ def render_targets
+ @render_targets
+ end
+
def render_target name
name = name.to_s
if !@render_targets[name]
- @render_targets[name] = Outputs.new(target: name, background_color_override: [255, 255, 255, 0])
+ @render_targets[name] = Outputs.new(args: self, target: name, background_color_override: [255, 255, 255, 0])
@passes << @render_targets[name]
end
@render_targets[name]
diff --git a/dragon/assert.rb b/dragon/assert.rb
index 0eb8c2a..0c26f29 100644
--- a/dragon/assert.rb
+++ b/dragon/assert.rb
@@ -4,13 +4,57 @@
# assert.rb has been released under MIT (*only this file*).
module GTK
+=begin
+This is a tiny assertion api for the unit testing portion of Game Toolkit.
+
+@example
+
+1. Create a file called tests.rb under mygame.
+2. Any method that begins with the word test_ will be considered a test.
+
+def test_this_works args, assert
+ assert.equal! 1, 1
+end
+
+3. To run a test, save the file while the game is running.
+
+@example
+
+To add an assertion open up this class and write:
+
+class Assert
+ def custom_assertion actual, expected, message = nil
+ # this tell Game Toolkit that an assertion was performed (so that the test isn't marked inconclusive).
+ @assertion_performed = true
+
+ # perform your custom logic here and rais an exception to denote a failure.
+
+ raise "Some Error. #{message}."
+ end
+end
+=end
class Assert
attr :assertion_performed
+=begin
+Us this if you are throwing your own exceptions and you want to mark the tests as ran (so that it wont be marked as inconclusive).
+=end
def ok!
@assertion_performed = true
end
+=begin
+Assert if a value is a thruthy value. All assert method take an optional final parameter that is the message to display to the user.
+
+@example
+
+def test_does_this_work args, assert
+ some_result = Person.new
+ assert.true! some_result
+ # OR
+ assert.true! some_result, "Person was not created."
+end
+=end
def true! value, message = nil
@assertion_performed = true
if !value
@@ -20,6 +64,16 @@ module GTK
nil
end
+=begin
+Assert if a value is a falsey value.
+
+@example
+
+def test_does_this_work args, assert
+ some_result = nil
+ assert.false! some_result
+end
+=end
def false! value, message = nil
@assertion_performed = true
if value
@@ -29,16 +83,39 @@ module GTK
nil
end
+=begin
+Assert if two values are equal.
+
+@example
+
+def test_does_this_work args, assert
+ a = 1
+ b = 1
+ assert.equal! a, b
+end
+=end
def equal! actual, expected, message = nil
@assertion_performed = true
if actual != expected
actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip
- message = "actual: #{actual_string} did not equal expected: #{expected}.\n#{message}"
+ message = "actual:\n#{actual_string} did not equal\nexpected:\n#{expected}.\n#{message}"
raise message
end
nil
end
+=begin
+Assert if a value is explicitly nil (not false).
+
+@example
+
+def test_does_this_work args, assert
+ a = nil
+ b = false
+ assert.nil! a # this will pass
+ assert.nil! b # this will throw an exception.
+end
+=end
def nil! value, message = nil
@assertion_performed = true
if !value.nil?
@@ -47,18 +124,5 @@ module GTK
end
nil
end
-
- def raises! exception_class, message = nil
- @assertion_performed = true
- begin
- yield
- rescue exception_class
- return # Success
- rescue Exception => e
- raise "Expected #{exception_class.name} to be raised but instead #{e.class.name} was raised\n#{message}"
- else
- raise "Expected #{exception_class.name} to be raised but nothing was raised\n#{message}"
- end
- end
end
end
diff --git a/dragon/console.rb b/dragon/console.rb
index acacfd2..c90755a 100644
--- a/dragon/console.rb
+++ b/dragon/console.rb
@@ -13,11 +13,12 @@ module GTK
:last_command_errored, :last_command, :error_color, :shown_at,
:header_color, :archived_log, :last_log_lines, :last_log_lines_count,
:suppress_left_arrow_behavior, :command_set_at,
- :toast_ids,
- :font_style
+ :toast_ids, :bottom,
+ :font_style, :menu
def initialize
@font_style = FontStyle.new(font: 'font.ttf', size_enum: -1, line_height: 1.1)
+ @menu = Menu.new self
@disabled = false
@log_offset = 0
@visible = false
@@ -26,6 +27,7 @@ module GTK
@log = [ 'Console ready.' ]
@max_log_lines = 1000 # I guess...?
@max_history = 1000 # I guess...?
+ @log_invocation_count = 0
@command_history = []
@command_history_index = -1
@nonhistory_input = ''
@@ -76,6 +78,7 @@ module GTK
end
def addsprite obj
+ @log_invocation_count += 1
obj[:id] ||= "id_#{obj[:path]}_#{Time.now.to_i}".to_sym
if @last_line_log_index &&
@@ -103,6 +106,7 @@ module GTK
def addtext obj
@last_log_lines_count ||= 1
+ @log_invocation_count += 1
str = obj.to_s
@@ -450,7 +454,7 @@ S
def write_primitive_and_return_offset(args, left, y, str, archived: false)
if str.is_a?(Hash)
padding = 10
- args.outputs.reserved << [left + 10, y - padding * 1.66, str[:w], str[:h], str[:path]].sprite
+ args.outputs.reserved << [left + 10, y + 5, str[:w], str[:h], str[:path]].sprite
return str[:h] + padding
else
write_line args, left, y, str, archived: archived
@@ -465,25 +469,25 @@ S
args.outputs.reserved << font_style.label(x: left.shift_right(10), y: y, text: str, color: color)
end
+ def should_tick?
+ return false if !@toggled_at
+ return false if slide_progress == 0
+ return false if @disabled
+ return visible?
+ end
+
def render args
return if !@toggled_at
+ return if slide_progress == 0
- if visible?
- percent = @toggled_at.global_ease(@animation_duration, :flip, :quint, :flip)
- else
- percent = @toggled_at.global_ease(@animation_duration, :flip, :quint)
- end
-
- return if percent == 0
-
- bottom = top - (h * percent)
- args.outputs.reserved << [left, bottom, w, h, *@background_color.mult_alpha(percent)].solid
- args.outputs.reserved << [right.shift_left(110), bottom.shift_up(630), 100, 100, @logo, 0, (80.0 * percent).to_i].sprite
+ @bottom = top - (h * slide_progress)
+ args.outputs.reserved << [left, @bottom, w, h, *@background_color.mult_alpha(slide_progress)].solid
+ args.outputs.reserved << [right.shift_left(110), @bottom.shift_up(630), 100, 100, @logo, 0, (80.0 * slide_progress).to_i].sprite
- y = bottom + 2 # just give us a little padding at the bottom.
+ y = @bottom + 2 # just give us a little padding at the bottom.
prompt.render args, x: left.shift_right(10), y: y
y += line_height_px * 1.5
- args.outputs.reserved << line(y: y, color: @text_color.mult_alpha(percent))
+ args.outputs.reserved << line(y: y, color: @text_color.mult_alpha(slide_progress))
y += line_height_px.to_f / 2.0
((@log.size - @log_offset) - 1).downto(0) do |idx|
@@ -492,8 +496,8 @@ S
break if y > top
end
- # past log seperator
- args.outputs.reserved << line(y: y + line_height_px.half, color: @text_color.mult_alpha(0.25 * percent))
+ # past log separator
+ args.outputs.reserved << line(y: y + line_height_px.half, color: @text_color.mult_alpha(0.25 * slide_progress))
y += line_height_px
@@ -504,10 +508,19 @@ S
end
render_log_offset args
- render_help args, top if percent == 1
end
- def render_help args, top
+ def tick_help args
+ tick_help_debounce args
+ alpha_rate = 20
+ @render_help_target_alpha ||= 255
+ @render_help_current_alpha ||= 255
+ @render_help_target_alpha += 4 if @render_help_current_alpha == @render_help_target_alpha
+ @render_help_current_alpha = (@render_help_current_alpha.towards @render_help_target_alpha, 20)
+
+ @render_help_target_alpha = @render_help_target_alpha.clamp(-255, 255)
+ @render_help_current_alpha = @render_help_current_alpha.clamp(-255, 255)
+
[
"* Prompt Commands: ",
"You can type any of the following ",
@@ -525,8 +538,33 @@ S
].each_with_index do |s, i|
args.outputs.reserved << [args.grid.right - 10,
top - 100 - line_height_px * i * 0.8,
- s, -3, 2, 180, 180, 180].label
+ s, -3, 2, 180, 180, 180, (@render_help_current_alpha.clamp 0, 255)].label
+ end
+ end
+
+ def tick_help_debounce args
+ hide_log_alpha = -255
+ if hidden?
+ @render_help_current_alpha = -255
+ end
+
+ if prompt.last_input_str_changed
+ @render_help_target_alpha = hide_log_alpha
end
+
+ if args.inputs.mouse.moved
+ @render_help_target_alpha = hide_log_alpha
+ end
+
+ if args.inputs.mouse.wheel
+ @render_help_target_alpha = hide_log_alpha
+ end
+
+ if @render_help_last_log_invocation_count != @log_invocation_count
+ @render_help_target_alpha = hide_log_alpha
+ end
+
+ @render_help_last_log_invocation_count = @log_invocation_count
end
def render_log_offset args
@@ -582,9 +620,18 @@ S
begin
return if @disabled
render args
- calc args
process_inputs args
+ return unless should_tick?
+ calc args
+ tick_help args
+ prompt.tick
+ menu.tick args
rescue Exception => e
+ begin
+ puts "#{e}"
+ puts "* FATAL: The GTK::Console console threw an unhandled exception and has been reset. You should report this exception (along with reproduction steps) to DragonRuby."
+ rescue
+ end
@disabled = true
$stdout.puts e
$stdout.puts "* FATAL: The GTK::Console console threw an unhandled exception and has been reset. You should report this exception (along with reproduction steps) to DragonRuby."
@@ -722,5 +769,15 @@ S
@prompt.clear
:console_silent_eval
end
+
+ def slide_progress
+ return 0 if !@toggled_at
+ if visible?
+ @slide_progress = @toggled_at.global_ease(@animation_duration, :flip, :quint, :flip)
+ else
+ @slide_progress = @toggled_at.global_ease(@animation_duration, :flip, :quint)
+ end
+ @slide_progress
+ end
end
end
diff --git a/dragon/console_menu.rb b/dragon/console_menu.rb
new file mode 100644
index 0000000..e8ce973
--- /dev/null
+++ b/dragon/console_menu.rb
@@ -0,0 +1,103 @@
+# Copyright 2019 DragonRuby LLC
+# MIT License
+# console_menu.rb has been released under MIT (*only this file*).
+
+module GTK
+ class Console
+ class Menu
+ def initialize console
+ @console = console
+ end
+
+ def record_clicked
+ $recording.start 100
+ end
+
+ def replay_clicked
+ $replay.start 'replay.txt'
+ end
+
+ def reset_clicked
+ $gtk.reset
+ end
+
+ def scroll_up_clicked
+ @console.scroll_up_half
+ end
+
+ def scroll_down_clicked
+ @console.scroll_down_half
+ end
+
+ def close_clicked
+ @console.hide
+ end
+
+ def tick args
+ return unless @console.visible?
+
+ # defaults
+ @buttons = [
+ (button id: :record, row: 0, col: 15, text: "record", method: :record_clicked),
+ (button id: :replay, row: 0, col: 16, text: "replay", method: :replay_clicked),
+ (button id: :reset, row: 0, col: 17, text: "reset", method: :reset_clicked),
+ (button id: :scroll_up, row: 0, col: 18, text: "scroll up", method: :scroll_up_clicked),
+ (button id: :scroll_down, row: 0, col: 19, text: "scroll down", method: :scroll_down_clicked),
+ (button id: :close, row: 0, col: 20, text: "close", method: :close_clicked),
+ ]
+
+ # render
+ args.outputs.reserved << @buttons.map { |b| b[:primitives] }
+
+ # inputs
+ if args.inputs.mouse.click
+ clicked = @buttons.find { |b| args.inputs.mouse.inside_rect? b[:rect] }
+ if clicked
+ send clicked[:method]
+ end
+ end
+ end
+
+ def rect_for_layout row, col
+ col_width = 50
+ row_height = 50
+ col_margin = 5
+ row_margin = 5
+ x = (col_margin + (col * col_width) + (col * col_margin))
+ y = (row_margin + (row * row_height) + (row * row_margin) + row_height).from_top
+ { x: x, y: y, w: row_height, h: col_width }
+ end
+
+ def button args
+ id, row, col, text, method = args[:id], args[:row], args[:col], args[:text], args[:method]
+
+ font_height = @console.font_style.line_height_px.half
+ {
+ id: id,
+ rect: (rect_for_layout row, col),
+ method: method
+ }.let do |entity|
+ primitives = []
+ primitives << entity[:rect].merge(a: 80).solid
+ primitives << entity[:rect].merge(r: 255, g: 255, b: 255).border
+ primitives << text.wrapped_lines(5)
+ .map_with_index do |l, i|
+ [
+ entity[:rect][:x] + entity[:rect][:w].half,
+ entity[:rect][:y] + entity[:rect][:h].half + font_height - (i * (font_height + 2)),
+ l, -3, 1, 255, 255, 255
+ ]
+ end.labels
+
+ entity.merge(primitives: primitives)
+ end
+ end
+
+ def serialize
+ {
+ not_supported: "#{self}"
+ }
+ end
+ end
+ end
+end
diff --git a/dragon/console_prompt.rb b/dragon/console_prompt.rb
index dd454f7..aea5df8 100644
--- a/dragon/console_prompt.rb
+++ b/dragon/console_prompt.rb
@@ -8,7 +8,7 @@
module GTK
class Console
class Prompt
- attr_accessor :current_input_str, :font_style, :console_text_width
+ attr_accessor :current_input_str, :font_style, :console_text_width, :last_input_str, :last_input_str_changed
def initialize(font_style:, text_color:, console_text_width:)
@prompt = '-> '
@@ -24,6 +24,7 @@ module GTK
def <<(str)
@current_input_str << str
+ @current_input_changed_at = Kernel.global_tick_count
reset_autocomplete
end
@@ -114,6 +115,18 @@ S
args.outputs.reserved << font_style.label(x: x - 2, y: y + 3, text: (" " * (@prompt.length + current_input_str.length)) + "|", color: @cursor_color)
end
+ def tick
+ if (@current_input_changed_at) &&
+ (@current_input_changed_at < Kernel.global_tick_count) &&
+ (@last_input_str != @current_input_str)
+ @last_input_str_changed = true
+ @last_input_str = "#{@current_input_str}"
+ @current_input_changed_at = nil
+ else
+ @last_input_str_changed = false
+ end
+ end
+
private
def last_period_index
diff --git a/dragon/docs.rb b/dragon/docs.rb
index 16d9f3b..8f5e8bf 100644
--- a/dragon/docs.rb
+++ b/dragon/docs.rb
@@ -354,11 +354,17 @@ S
end
close_list_if_needed = lambda do |inside_ul, inside_ol|
- result = ""
- if inside_ul
- result = "</ul>\n"
- elsif inside_ol
- result = "</ol>\n"
+ begin
+ result = ""
+ if inside_ul
+ result = "</ul>\n"
+ elsif inside_ol
+ result = "</ol>\n"
+ else
+ result
+ end
+ rescue Exception => e
+ raise "* ERROR in determining close_list_if_needed lambda result. #{e}."
end
end
@@ -370,39 +376,39 @@ S
true_lines.each do |l|
parse_log << "** Processing line: ~#{l.rstrip}~"
if l.start_with? "* "
+ parse_log << "- H1 detected."
content_html += close_list_if_needed.call inside_ul, inside_ol
inside_ol = false
inside_ul = false
formatted_html = __docs_line_to_html__ l, parse_log
link_id = text_to_id.call l
toc += "<li><a href='##{link_id}'>#{formatted_html}</a></li>\n"
- parse_log << "- H1 detected."
content_html += "<h1 id='#{link_id}'>#{formatted_html}</h1>\n"
elsif l.start_with? "** "
+ parse_log << "- H2 detected."
content_html += close_list_if_needed.call inside_ul, inside_ol
inside_ol = false
inside_ul = false
formatted_html = __docs_line_to_html__ l, parse_log
link_id = text_to_id.call l
# toc += "<a href='##{link_id}'>#{formatted_html}</a></br>\n"
- parse_log << "- H2 detected."
content_html += "<h2>#{__docs_line_to_html__ l, parse_log}</h2>\n"
elsif l.start_with? "*** "
+ parse_log << "- H3 detected."
content_html += close_list_if_needed.call inside_ul, inside_ol
inside_ol = false
inside_ul = false
formatted_html = __docs_line_to_html__ l, parse_log
link_id = text_to_id.call l
# toc += "<a href='##{link_id}'>#{formatted_html}</a></br>\n"
- parse_log << "- H3 detected."
content_html += "<h3>#{__docs_line_to_html__ l, parse_log}</h3>\n"
elsif l.strip.length == 0 && !inside_pre
# do nothing
elsif l.start_with? "#+begin_src"
+ parse_log << "- PRE start detected."
content_html += close_list_if_needed.call inside_ul, inside_ol
inside_ol = false
inside_ul = false
- parse_log << "- PRE start detected."
inside_pre = true
content_html << "<pre>"
elsif l.start_with? "#+end_src"
@@ -412,16 +418,18 @@ S
inside_pre = false
content_html << "</pre>\n"
elsif l.start_with? "#+begin_quote"
+ parse_log << "- BLOCKQUOTE start detected."
content_html += close_list_if_needed.call inside_ul, inside_ol
inside_ol = false
inside_ul = false
- parse_log << "- BLOCKQUOTE start detected."
content_html << "<blockquote>\n"
elsif l.start_with? "#+end_quote"
parse_log << "- BLOCKQUOTE end detected."
content_html << "</blockquote>\n"
elsif (l.start_with? "1. ") && !inside_ol
parse_log << "- OL start detected."
+ parse_log << "- LI detected."
+
inside_ol = true
content_html << "<ol>\n"
@@ -432,9 +440,10 @@ S
elsif l.split(".")[0].length == 3
l = l[4..-1]
end
- parse_log << "- LI detected."
+
content_html << "<li>#{__docs_line_to_html__ l, parse_log}</li>\n"
elsif inside_ol && (l[1] == "." || l[2] == "." || l[3] == ".")
+ parse_log << "- LI detected."
if l.split(".")[0].length == 1
l = l[2..-1]
@@ -444,31 +453,34 @@ S
l = l[4..-1]
end
- parse_log << "- LI detected."
content_html << "<li>#{__docs_line_to_html__ l, parse_log}</li>\n"
elsif (l.start_with? "- ") && !inside_ul
parse_log << "- UL start detected."
+ parse_log << "- LI detected."
+
inside_ul = true
content_html << "<ul>\n"
l = l[2..-1]
- parse_log << "- LI detected."
content_html << "<li>#{__docs_line_to_html__ l, parse_log}</li>\n"
elsif (l.start_with? "- ") && inside_ul
+ parse_log << "- LI detected."
+
l = l[2..-1]
- parse_log << "- LI detected."
content_html << "<li>#{__docs_line_to_html__ l, parse_log}</li>\n"
else
if inside_ul
- inside_ul = false
parse_log << "- UL end detected."
+
+ inside_ul = false
content_html << "</ul>\n"
end
if inside_ol
- inside_ol = false
parse_log << "- OL end detected."
+
+ inside_ol = false
content_html << "</ol>\n"
end
@@ -476,6 +488,7 @@ S
content_html << "#{l.rstrip[2..-1]}\n"
else
parse_log << "- P detected."
+
content_html << "<p>\n#{__docs_line_to_html__ l, parse_log}\n</p>\n"
end
end
@@ -490,6 +503,9 @@ S
html: final_html,
parse_log: parse_log
}
+ rescue Exception => e
+ $gtk.write_file 'docs/parse_log.txt', (parse_log.join "\n")
+ raise "* ERROR in Docs::__docs_to_html__. #{e}"
end
def __docs_line_to_html__ line, parse_log
@@ -558,7 +574,7 @@ S
return line_to_format
rescue Exception => e
- parse_log << "Failed to parse line: ~#{line}~, #{e}"
+ parse_log << "* ERROR: Failed to parse line: ~#{line}~, #{e}"
return line.rstrip
end
end
diff --git a/dragon/geometry.rb b/dragon/geometry.rb
index 1be0379..607520f 100644
--- a/dragon/geometry.rb
+++ b/dragon/geometry.rb
@@ -76,6 +76,44 @@ module GTK
Geometry.point_inside_circle? self, circle_center_point, radius
end
+ def center_inside_rect other_rect
+ offset_x = (other_rect.w - w).half
+ offset_y = (other_rect.h - h).half
+ new_rect = self.shift_rect(0, 0)
+ new_rect.x = other_rect.x + offset_x
+ new_rect.y = other_rect.y + offset_y
+ new_rect
+ rescue Exception => e
+ raise e, <<-S
+* ERROR:
+center_inside_rect for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
+S
+ end
+
+ def center_inside_rect_y other_rect
+ offset_y = (other_rect.h - h).half
+ new_rect = self.shift_rect(0, 0)
+ new_rect.y = other_rect.y + offset_y
+ new_rect
+ rescue Exception => e
+ raise e, <<-S
+* ERROR:
+center_inside_rect_y for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
+S
+ end
+
+ def center_inside_rect_x other_rect
+ offset_x = (other_rect.w - w).half
+ new_rect = self.shift_rect(0, 0)
+ new_rect.x = other_rect.x + offset_x
+ new_rect
+ rescue Exception => e
+ raise e, <<-S
+* ERROR:
+center_inside_rect_x for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
+S
+ end
+
# Returns a primitive that is anchored/repositioned based off its retangle.
# @gtk
def anchor_rect anchor_x, anchor_y
@@ -83,13 +121,17 @@ module GTK
current_h = self.h
delta_x = -1 * (anchor_x * current_w)
delta_y = -1 * (anchor_y * current_h)
- self.rect_shift(delta_x, delta_y)
+ self.shift_rect(delta_x, delta_y)
end
def angle_given_point other_point
raise ":angle_given_point has been deprecated use :angle_from instead."
end
+ def distance_to other_point
+ Geometry.distance(self, other_point)
+ end
+
# @gtk
def self.shift_line line, x, y
if line.is_a?(Array) || line.is_a?(Hash)
diff --git a/dragon/grid.rb b/dragon/grid.rb
index e5c21a3..c2a6a43 100644
--- a/dragon/grid.rb
+++ b/dragon/grid.rb
@@ -60,8 +60,9 @@ module GTK
attr_accessor :left_margin, :bottom_margin
- def initialize ffi_draw
- @ffi_draw = ffi_draw
+ def initialize runtime
+ @runtime = runtime
+ @ffi_draw = runtime.ffi_draw
origin_bottom_left!
end
@@ -109,18 +110,18 @@ module GTK
return if @name == :bottom_left
@name = :bottom_left
@origin_x = 0.0
- @origin_y = $gtk.logical_height
+ @origin_y = @runtime.logical_height
@left = 0.0
- @right = $gtk.logical_width
- @top = $gtk.logical_height
+ @right = @runtime.logical_width
+ @top = @runtime.logical_height
@bottom = 0.0
@left_margin = 0.0
@bottom_margin = 0.0
- @center_x = $gtk.logical_width.half
- @center_y = $gtk.logical_height.half
- @rect = [@left, @bottom, $gtk.logical_width, $gtk.logical_height].rect
+ @center_x = @runtime.logical_width.half
+ @center_y = @runtime.logical_height.half
+ @rect = [@left, @bottom, @runtime.logical_width, @runtime.logical_height].rect
@center = [@center_x, @center_y].point
- @ffi_draw.set_gtk_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
+ @ffi_draw.set_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
end
# Sets the rendering coordinate system to have its origin in the center.
@@ -130,24 +131,24 @@ module GTK
def origin_center!
return if @name == :center
@name = :center
- @origin_x = $gtk.logical_width.half
- @origin_y = $gtk.logical_height.half
- @left = -$gtk.logical_width.half
- @right = $gtk.logical_width.half
- @top = $gtk.logical_height.half
- @bottom = -$gtk.logical_height.half
+ @origin_x = @runtime.logical_width.half
+ @origin_y = @runtime.logical_height.half
+ @left = [email protected]_width.half
+ @right = @runtime.logical_width.half
+ @top = @runtime.logical_height.half
+ @bottom = [email protected]_height.half
@center_x = 0.0
@center_y = 0.0
- @rect = [@left, @bottom, $gtk.logical_width, $gtk.logical_height].rect
+ @rect = [@left, @bottom, @runtime.logical_width, @runtime.logical_height].rect
@center = [@center_x, @center_y].point
- @ffi_draw.set_gtk_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
+ @ffi_draw.set_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
end
# The logical width used for rendering.
#
# @return [Float]
def w
- $gtk.logical_width
+ @runtime.logical_width
end
# Half the logical width used for rendering.
@@ -161,7 +162,7 @@ module GTK
#
# @return [Float]
def h
- $gtk.logical_height
+ @runtime.logical_height
end
# Half the logical height used for rendering.
diff --git a/dragon/inputs.rb b/dragon/inputs.rb
index fc6d8ef..a4ef40f 100644
--- a/dragon/inputs.rb
+++ b/dragon/inputs.rb
@@ -295,7 +295,7 @@ S
return self.send m
rescue Exception => e
- log_important "#{e}}"
+ log_important "#{e}"
end
raise <<-S
@@ -498,6 +498,10 @@ module GTK
[@x, @y].point
end
+ def inside_rect? rect
+ point.inside_rect? rect
+ end
+
alias_method :position, :point
def clear
diff --git a/dragon/kernel_docs.rb b/dragon/kernel_docs.rb
index 0a91b4d..00eadf6 100644
--- a/dragon/kernel_docs.rb
+++ b/dragon/kernel_docs.rb
@@ -49,9 +49,12 @@ S
DocsOrganizer.sort_docs_classes!
final_string = ""
$docs_classes.each do |k|
+ log "* INFO: Retrieving docs for #{k.name}."
final_string += k.docs_all
end
+ final_string += "\n" + (($gtk.read_file "docs/source.txt") || "")
+
html_parse_result = (__docs_to_html__ final_string)
$gtk.write_file 'docs/docs.txt', "#{final_string}"
diff --git a/dragon/log.rb b/dragon/log.rb
index 60246e6..b2ee0e1 100644
--- a/dragon/log.rb
+++ b/dragon/log.rb
@@ -81,6 +81,30 @@ module GTK
"
end
+ def self.puts_error *args
+ args ||= []
+ title = args[0]
+ additional = args[1..-1] || []
+ additional = "" if additional.length == 0
+ if !title.multiline? && join_lines(additional).multiline?
+ message = headline "ERROR: #{title}" do
+ dynamic_block do
+ additional
+ end
+ end
+ elsif title.multiline?
+ message = headline "ERROR: " do
+ dynamic_block do
+ args
+ end
+ end
+ else
+ message = "* ERROR: #{title} #{additional}".strip
+ end
+
+ self.puts message
+ end
+
def self.puts_info *args
args ||= []
title = args[0]
@@ -110,14 +134,14 @@ module GTK
@once ||= {}
return if @once[id]
@once[id] = id
+ if !$gtk.cli_arguments[:replay] && !$gtk.cli_arguments[:record]
+ $gtk.notify!("Open the DragonRuby Console by pressing [`] [~] [²] [^] [º] or [§]. [Message ID: #{id}].")
+ end
write_to_log_and_puts ""
write_to_log_and_puts "#{message.strip}"
write_to_log_and_puts ""
write_to_log_and_puts "[Message ID: #{id}]"
write_to_log_and_puts ""
- return if $gtk.cli_arguments[:replay]
- return if $gtk.cli_arguments[:record]
- $gtk.notify!("One time notification occurred. [Message ID: #{id}] (Open console for more info.)")
end
def self.puts_once_info *ids, message
@@ -222,6 +246,10 @@ class Object
log_with_color XTERM_COLOR[:bright_white], *args
end
+ def log_error *args
+ GTK::Log.puts_error(*args)
+ end
+
def log_info *args
GTK::Log.puts_info(*args)
end
diff --git a/dragon/numeric.rb b/dragon/numeric.rb
index c2c461f..5485ff8 100644
--- a/dragon/numeric.rb
+++ b/dragon/numeric.rb
@@ -9,6 +9,11 @@ class Numeric
alias_method :gte, :>=
alias_method :lte, :<=
+
+ # Converts numeric value to distance from top of stage
+ def from_top
+ $gtk.args.grid.h - self
+ end
# Converts a numeric value representing seconds into frames.
#
@@ -28,12 +33,6 @@ class Numeric
clamp(0, 255).to_i
end
- # For a given number, the elapsed frames since that number is returned.
- # `Kernel.tick_count` is used to determine how many frames have elapsed.
- # An override numeric value can be passed in which will be used instead
- # of `Kernel.tick_count`.
- #
- # @gtk
def elapsed_time tick_count_override = nil
(tick_count_override || Kernel.tick_count) - self
end
@@ -51,25 +50,42 @@ class Numeric
# moment in time.
#
# @gtk
- def elapsed? offset, tick_count_override = nil
- (self + offset) < (tick_count_override || Kernel.tick_count)
+ def elapsed? offset = 0, tick_count_override = Kernel.tick_count
+ (self + offset) < tick_count_override
end
- # This is helpful for determining the index of frame-by-frame sprite animation.
- # The numeric value `self` represents the moment the animation started. `frame_index`
- # takes three additional parameters: how many frames exist in the sprite animation;
- # how long to hold each animation for; and whether the animation should repeat.
- #
- # @gtk
- def frame_index frame_count, hold_for, repeat, tick_count_override = nil
+ def frame_index *opts
+ frame_count_or_hash, hold_for, repeat, tick_count_override = opts
+ if frame_count_or_hash.is_a? Hash
+ frame_count = frame_count_or_hash[:count]
+ hold_for = frame_count_or_hash[:hold_for]
+ repeat = frame_count_or_hash[:repeat]
+ tick_count_override = frame_count_or_hash[:tick_count_override]
+ else
+ frame_count = frame_count_or_hash
+ end
+
+ tick_count_override ||= Kernel.tick_count
animation_frame_count = frame_count
animation_frame_hold_time = hold_for
animation_length = animation_frame_hold_time * animation_frame_count
- if !repeat && self.+(animation_length) < (tick_count_override || Kernel.tick_count).-(1)
+ return nil if Kernel.tick_count < self
+
+ if !repeat && (self + animation_length) < (tick_count_override - 1)
return nil
else
return self.elapsed_time.-(1).idiv(animation_frame_hold_time) % animation_frame_count
end
+ rescue Exception => e
+ raise <<-S
+* ERROR:
+#{opts}
+#{e}
+S
+ end
+
+ def zero?
+ self == 0
end
def zero
@@ -479,6 +495,11 @@ S
def serialize
self
end
+
+ def from_top
+ return 720 - self unless $gtk
+ $gtk.args.grid.h - self
+ end
end
class Fixnum
diff --git a/dragon/numeric_docs.rb b/dragon/numeric_docs.rb
new file mode 100644
index 0000000..fb8a94d
--- /dev/null
+++ b/dragon/numeric_docs.rb
@@ -0,0 +1,240 @@
+# coding: utf-8
+# Copyright 2019 DragonRuby LLC
+# MIT License
+# numeric_docs.rb has been released under MIT (*only this file*).
+
+module NumericDocs
+ def docs_method_sort_order
+ [
+ :docs_frame_index,
+ :docs_elapsed_time,
+ :docs_elapsed?,
+ :docs_new?
+ ]
+ end
+
+ def docs_frame_index
+ <<-S
+* DOCS: ~Numeric#frame_index~
+
+This function is helpful for determining the index of frame-by-frame
+ sprite animation. The numeric value ~self~ represents the moment the
+ animation started.
+
+~frame_index~ takes three additional parameters:
+
+- How many frames exist in the sprite animation.
+- How long to hold each animation for.
+- Whether the animation should repeat.
+
+~frame_index~ will return ~nil~ if the time for the animation is out
+of bounds of the parameter specification.
+
+Example using variables:
+
+#+begin_src ruby
+ def tick args
+ start_looping_at = 0
+ number_of_sprites = 6
+ number_of_frames_to_show_each_sprite = 4
+ does_sprite_loop = true
+
+ sprite_index =
+ start_looping_at.frame_index number_of_sprites,
+ number_of_frames_to_show_each_sprite,
+ does_sprite_loop
+
+ sprite_index ||= 0
+
+ args.outputs.sprites << [
+ 640 - 50,
+ 360 - 50,
+ 100,
+ 100,
+ "sprites/dragon-\#{sprite_index}.png"
+ ]
+ end
+#+end_src
+
+Example using named parameters:
+
+#+begin_src ruby
+ def tick args
+ start_looping_at = 0
+
+ sprite_index =
+ start_looping_at.frame_index count: 6,
+ hold_for: 4,
+ repeat: true,
+ tick_count_override: args.state.tick_count
+
+ sprite_index ||= 0
+
+ args.outputs.sprites << [
+ 640 - 50,
+ 360 - 50,
+ 100,
+ 100,
+ "sprites/dragon-\#{sprite_index}.png"
+ ]
+ end
+#+end_src
+
+S
+ end
+
+ def docs_new?
+ <<-S
+* DOCS: ~Numeric#created?~
+Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that
+number is equal to the current frame.
+
+Example usage:
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 60 }
+ end
+
+ boxes_to_spawn_this_frame = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].new? }
+
+ boxes_to_spawn_this_frame.each { |b| puts "box \#{b} was new? on \#{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_spawn_this_frame
+ end
+#+end_src
+S
+ end
+
+ def docs_elapsed?
+ <<-S
+* DOCS: ~Numeric#elapsed?~
+Returns true if ~Numeric#elapsed_time~ is greater than the number. An optional parameter can be
+passed into ~elapsed?~ which is added to the number before evaluating whether ~elapsed?~ is true.
+
+Example usage (no optional parameter):
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :green,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :blue,
+ destroy_at: args.state.tick_count + 120 }
+ end
+
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:destroy_at].elapsed? }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: \#{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box \#{b} was elapsed? on \#{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+ end
+#+end_src
+
+Example usage (with optional parameter):
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :green,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :blue,
+ create_at: args.state.tick_count + 120,
+ lifespan: 120 }
+ end
+
+ # lifespan is passed in as a parameter to ~elapsed?~
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].elapsed? b[:lifespan] }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: \#{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box \#{b} was elapsed? on \#{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+ end
+#+end_src
+
+S
+ end
+
+ def docs_elapsed_time
+ <<-S
+* DOCS: ~Numeric#elapsed_time~
+For a given number, the elapsed frames since that number is returned.
+`Kernel.tick_count` is used to determine how many frames have elapsed.
+An optional numeric argument can be passed in which will be used instead
+of `Kernel.tick_count`.
+
+Here is an example of how elapsed_time can be used.
+
+#+begin_src ruby
+ def tick args
+ args.state.last_click_at ||= 0
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.tick_count
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if args.state.last_click_at.elapsed_time > 120
+ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]
+ end
+ end
+#+end_src
+
+And here is an example where the override parameter is passed in:
+
+#+begin_src ruby
+ def tick args
+ args.state.last_click_at ||= 0
+
+ # create a state variable that tracks time at half the speed of args.state.tick_count
+ args.state.simulation_tick = args.state.tick_count.idiv 2
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.simulation_tick
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120
+ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]
+ end
+ end
+#+end_src
+
+S
+ end
+end
+
+class Numeric
+ extend Docs
+ extend NumericDocs
+end
diff --git a/dragon/outputs_docs.rb b/dragon/outputs_docs.rb
index 1e8eecc..16c97ec 100644
--- a/dragon/outputs_docs.rb
+++ b/dragon/outputs_docs.rb
@@ -4,6 +4,14 @@
# outputs_docs.rb has been released under MIT (*only this file*).
module OutputsDocs
+ def docs_method_sort_order
+ [
+ :docs_class,
+ :docs_solids,
+ :docs_borders
+ ]
+ end
+
def docs_class
<<-S
* DOCS: ~GTK::Outputs~
diff --git a/dragon/readme_docs.rb b/dragon/readme_docs.rb
index 4b10414..39fc283 100644
--- a/dragon/readme_docs.rb
+++ b/dragon/readme_docs.rb
@@ -509,7 +509,22 @@ It's a hard pill to swallow, but forget blindly accepted best
practices and try to figure out the underlying motivation for a
specific approach to game development. Collaborate with us.
-*** Release Often And Quickly
+*** Continuity of Design
+
+There is a programming idiom in software called "the pit of
+success". The term normalizes up front pain as a necessity in the
+(hopes that the investment will yield dividends "when you become
+successful"). This results in more "Enterprise TM" code upfront, and
+makes it more difficult to get started when you are new to programming.
+
+DragonRuby's philosophy is to provide a spectrum across the "make it
+fast" vs "make it right" spectrum and provide incremental, intuitive
+transitions between points on that spectrum. This is captured in how
+render primitives can be represented as tuples/arrays, hashes, open
+structs/entities, and then finally classes (as opposed to forcing devs
+to use classes upfront).
+
+*** Release Often And Soon
The biggest mistake game devs make is spending too much time in
isolation building their game. Release something, however small, and
@@ -981,7 +996,7 @@ to clarify some terms. Specifically _language specification_ vs _runtime_.
*** Okay... so what is the difference between a language specification and a runtime?
-A runtime is an _implementation_ of a langauge specification. When
+A runtime is an _implementation_ of a language specification. When
people say "Ruby," they are usually referring to "the Ruby 3.0+ language
specification implemented via the CRuby/MRI Runtime."
@@ -1017,18 +1032,18 @@ runtime internally):
C-Extensions.
Levels 1 through 3 are fairly commonplace in many runtime
-implemenations (with level 1 being the most portable, and level 3
+implementations (with level 1 being the most portable, and level 3
being the fastest). But the DragonRuby Runtime has taken things a
bit further:
- Level 4 consists of shared abstractions around hardware I/O and operating
system resources. This level leverages open source and proprietary
- components within Simple DirectMedia Layer (a lowlevel multimedia
+ components within Simple DirectMedia Layer (a low level multimedia
component library that has been in active development for 22 years
and counting).
-- Level 5 is a codegeneration layer which creates metadata that allows
- for native interopability with host runtime libraries. It also
+- Level 5 is a code generation layer which creates metadata that allows
+ for native interoperability with host runtime libraries. It also
includes OS specific message pump orchestrations.
- Level 6 is a Ahead of Time/Just in Time Ruby compiler built with LLVM. This
@@ -1037,12 +1052,12 @@ bit further:
These levels allow us to stay up to date with open source
implementations of Ruby; provide fast, native code execution
-on proprietary platforms; ensure good seperation between these two
+on proprietary platforms; ensure good separation between these two
worlds; and provides a means to add new platforms without going insane.
*** Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?
-DragonRuby is a Ruby runtime implentation that takes all the lessons
+DragonRuby is a Ruby runtime implementation that takes all the lessons
we've learned from MRI/CRuby, and merges it with the latest and greatest
compiler and OSS technologies.
@@ -1067,30 +1082,30 @@ That doesn't make any sense. A language specification can't be
slow... it's a language spec. Sure, an _implementation/runtime_ can be slow though, but then we'd
have to talk about which runtime.
-*** Dynamic langauges are slow.
+*** Dynamic languages are slow.
They are certainly slower than statically compiled languages. With the
processing power and compiler optimizations we have today,
dynamic languages like Ruby are _fast enough_.
Unless you are writing in some form of intermediate representation by hand,
-your langauge of choice also suffers this same fallacy of slow. Like, nothing is
+your language of choice also suffers this same fallacy of slow. Like, nothing is
faster than a low level assembly-like language. So unless you're
writing in that, let's stop making this comment.
NOTE: If you _are_ hand writing LLVM IR, we are always open to
-bringing on new partners with such a skillset. Email us ^_^.
+bringing on new partners with such a skill set. Email us ^_^.
** Frequent Concerns
*** DragonRuby is not open source. That's not right.
The current state of open source is unsustainable. Contributors work
-for free, most all open source repositories are serverly understaffed,
+for free, most all open source repositories are severely under-staffed,
and burnout from core members is rampant.
We believe in open source very strongly. Parts of DragonRuby are
-infact, open source. Just not all of it (for legal reasons, and
+in fact, open source. Just not all of it (for legal reasons, and
because the IP we've created has value). And we promise that we are
looking for (or creating) ways to _sustainably_ open source everything we do.
@@ -1141,7 +1156,7 @@ email. We don't want to collect usage data off of you either. We just
want to provide quality toolchains to quality developers (as opposed
to a large quantity of developers).
-The peiple that pay for DragonRuby and make an effort to understand it are the
+The people that pay for DragonRuby and make an effort to understand it are the
ones we want to build a community around, partner with, and collaborate
with. So having that small monetary wall deters entitled individuals
that don't value the same things we do.
@@ -1153,7 +1168,7 @@ and non-trivially compensate open source developers. Look, we want to be
able to work on the stuff we love, every day of our lives. And we'll go
to great lengths to make that happen.
-But, in the event that sad day comes, our partnershiop bylaws state that
+But, in the event that sad day comes, our partnership bylaws state that
_all_ DragonRuby IP that can be legally open sourced, will be released
under a permissive license.
S
diff --git a/dragon/string.rb b/dragon/string.rb
index 8d2d9ba..5fa273c 100644
--- a/dragon/string.rb
+++ b/dragon/string.rb
@@ -38,6 +38,7 @@ S
self[0..-2]
end
+ # @gtk
def wrapped_lines length
self.each_line.map do |l|
l = l.rstrip
@@ -50,20 +51,22 @@ S
end.flatten
end
+ # @gtk
def wrap length
wrapped_lines(length).join.rstrip
end
+ # @gtk
def multiline?
include? "\n"
end
- def indent_lines amount
+ def indent_lines amount, char = " "
self.each_line.each_with_index.map do |l, i|
if i == 0
l
else
- " " * amount + l
+ char * amount + l
end
end.join
end
diff --git a/dragon/tests.rb b/dragon/tests.rb
index 5cda2fe..7cbba09 100644
--- a/dragon/tests.rb
+++ b/dragon/tests.rb
@@ -16,7 +16,6 @@ module GTK
def run_test m
args = Args.new $gtk, nil
assert = Assert.new
- setup(args) if respond_to?(:setup)
begin
log_test_running m
send(m, args, assert)
@@ -32,7 +31,6 @@ module GTK
mark_test_failed m, e
end
end
- teardown if respond_to?(:teardown)
end
def test_methods_focused
@@ -43,6 +41,7 @@ module GTK
Object.methods.find_all { |m| m.start_with? "test_" }
end
+ # @gtk
def start
log "* TEST: gtk.test.start has been invoked."
if test_methods_focused.length != 0
@@ -73,10 +72,6 @@ module GTK
def log_inconclusive m
self.inconclusive << {m: m}
log "Inconclusive."
- log_once :assertion_ok_note, <<-S
-NOTE FOR INCONCLUSIVE TESTS: No assertion was performed in the test.
-Add assert.ok! at the end of the test if you are using your own assertions.
-S
end
def log_passed m
@@ -125,6 +120,12 @@ S
log "#{self.passed.length} test(s) passed."
self.passed.each { |h| log "**** :#{h[:m]}" }
log "*** Inconclusive"
+ if self.inconclusive.length > 0
+ log_once :assertion_ok_note, <<-S
+NOTE FOR INCONCLUSIVE TESTS: No assertion was performed in the test.
+Add assert.ok! at the end of the test if you are using your own assertions.
+S
+ end
log "#{self.inconclusive.length} test(s) inconclusive."
self.inconclusive.each { |h| log "**** :#{h[:m]}" }
log "*** Failed"
diff --git a/dragon/trace.rb b/dragon/trace.rb
index c3b4bbd..04067bf 100644
--- a/dragon/trace.rb
+++ b/dragon/trace.rb
@@ -65,7 +65,7 @@ module GTK
@traced_classes.clear
$trace_enabled = false
if !$gtk.production
- $gtk.write_file 'logs/trace.txt', "Add trace!(SOMEOBJECT) to the top of `tick` and this file will be populated with invocation information.\n"
+ $gtk.write_file 'logs/trace.txt', "Add trace!(SOMEOBJECT) to the top of ~tick~ and this file will be populated with invocation information.\n"
end
end
@@ -95,6 +95,7 @@ module GTK
$trace_puts.clear
end
+ # @gtk
def self.trace! instance = nil
$trace_history ||= []
$trace_enabled = true
diff --git a/samples/00_beginner_ruby_primer/app/main.rb b/samples/00_beginner_ruby_primer/app/main.rb
index 33c6dbf..6822cf3 100644
--- a/samples/00_beginner_ruby_primer/app/main.rb
+++ b/samples/00_beginner_ruby_primer/app/main.rb
@@ -76,8 +76,8 @@ end
def tick_reset_button
return unless state.hello_dragonruby_confirmed
- $gtk.reserved_primitives << state.reset_button.background
- $gtk.reserved_primitives << state.reset_button.label
+ $gtk.args.outputs.reserved << state.reset_button.background
+ $gtk.args.outputs.reserved << state.reset_button.label
if inputs.mouse.click && inputs.mouse.click.point.inside_rect?(state.reset_button.background)
restart_tutorial
end
diff --git a/samples/19_lowrez_jam/app/lowrez.rb b/samples/19_lowrez_jam/app/lowrez.rb
index d3a212b..78578e2 100644
--- a/samples/19_lowrez_jam/app/lowrez.rb
+++ b/samples/19_lowrez_jam/app/lowrez.rb
@@ -38,23 +38,29 @@ class LowrezOutputs
def background_color= opts
@background_color = opts
@args.outputs.background_color = @background_color
- @args.render_target(:lowrez).solids << [0, 0, LOWREZ_SIZE, LOWREZ_SIZE, @background_color]
+
+ outputs_lowrez.solids << [0, 0, LOWREZ_SIZE, LOWREZ_SIZE, @background_color]
+ end
+
+ def outputs_lowrez
+ return @args.outputs if @args.state.tick_count <= 0
+ return @args.outputs[:lowrez]
end
def solids
- @args.render_target(:lowrez).solids
+ outputs_lowrez.solids
end
def borders
- @args.render_target(:lowrez).borders
+ outputs_lowrez.borders
end
def sprites
- @args.render_target(:lowrez).sprites
+ outputs_lowrez.sprites
end
def labels
- @args.render_target(:lowrez).labels
+ outputs_lowrez.labels
end
def default_label
@@ -73,11 +79,11 @@ class LowrezOutputs
end
def lines
- @args.render_target(:lowrez).lines
+ outputs_lowrez.lines
end
def primitives
- @args.render_target(:lowrez).primitives
+ outputs_lowrez.primitives
end
def click
@@ -131,10 +137,12 @@ module GTK
def tick_core
@args.init_lowrez
__original_tick_core__
+
+ return if @args.state.tick_count <= 0
+
@args.render_target(:lowrez)
.labels
.each do |l|
- # l.text = l.text.downcase
l.y += 1
end
diff --git a/samples/19_lowrez_jam/app/main.rb b/samples/19_lowrez_jam/app/main.rb
index c0ff37c..a30d75a 100644
--- a/samples/19_lowrez_jam/app/main.rb
+++ b/samples/19_lowrez_jam/app/main.rb
@@ -125,25 +125,25 @@ def how_to_render_a_label args
# NOTE: Text is aligned from the TOP LEFT corner
# Render an EXTRA LARGE/XL label (remove the "#" in front of each line below)
- args.lowrez.labels << { x: 0, y: 57, text: "hello world",
+ args.lowrez.labels << { x: 0, y: 57, text: "Hello World",
size_enum: LOWREZ_FONT_XL,
r: 0, g: 0, b: 0, a: 255,
font: LOWREZ_FONT_PATH }
# Render a LARGE/LG label (remove the "#" in front of each line below)
- args.lowrez.labels << { x: 0, y: 36, text: "hello world",
+ args.lowrez.labels << { x: 0, y: 36, text: "Hello World",
size_enum: LOWREZ_FONT_LG,
r: 0, g: 0, b: 0, a: 255,
font: LOWREZ_FONT_PATH }
# Render a MEDIUM/MD label (remove the "#" in front of each line below)
- args.lowrez.labels << { x: 0, y: 20, text: "hello world",
+ args.lowrez.labels << { x: 0, y: 20, text: "Hello World",
size_enum: LOWREZ_FONT_MD,
r: 0, g: 0, b: 0, a: 255,
font: LOWREZ_FONT_PATH }
# Render a SMALL/SM label (remove the "#" in front of each line below)
- args.lowrez.labels << { x: 0, y: 9, text: "hello world",
+ args.lowrez.labels << { x: 0, y: 9, text: "Hello World",
size_enum: LOWREZ_FONT_SM,
r: 0, g: 0, b: 0, a: 255,
font: LOWREZ_FONT_PATH }
@@ -153,13 +153,13 @@ def how_to_render_a_label args
# Example 1
args.lowrez.labels << args.lowrez
.default_label
- .merge(text: "default")
+ .merge(text: "Default")
# Example 2
args.lowrez.labels << args.lowrez
.default_label
.merge(x: 31,
- text: "default",
+ text: "Default",
r: 128,
g: 128,
b: 128)
@@ -179,7 +179,7 @@ def how_to_render_solids args
args.lowrez.solids << { x: 3, y: 3, w: 3, h: 3, r: 255, g: 0, b: 0 }
# Render a red square at 6, 6 with a width and height of 4
- args.lowrez.solids << { x: 7, y: 7, w: 4, h: 4, r: 255, g: 0, b: 0 }
+ args.lowrez.solids << { x: 6, y: 6, w: 4, h: 4, r: 255, g: 0, b: 0 }
end
## # =============================================================================
@@ -266,7 +266,7 @@ def how_to_animate_a_sprite args
.default_label
.merge(x: 32,
y: 32,
- text: "count down: #{countdown_in_seconds}",
+ text: "Count Down: #{countdown_in_seconds}",
alignment_enum: 1)
end
@@ -275,7 +275,7 @@ def how_to_animate_a_sprite args
.default_label
.merge(x: 0,
y: 11,
- text: "tick: #{args.state.tick_count}")
+ text: "Tick: #{args.state.tick_count}")
args.lowrez.labels << args.lowrez
.default_label
.merge(x: 0,
@@ -317,7 +317,7 @@ def how_to_animate_a_sprite_sheet args
.default_label
.merge(x: 32,
y: 32,
- text: "count down: #{countdown_in_seconds}",
+ text: "Count Down: #{countdown_in_seconds}",
alignment_enum: 1)
end
@@ -341,19 +341,19 @@ def how_to_move_a_sprite args
args.lowrez.labels << args.lowrez
.default_label
.merge(x: 32,
- y: 62, text: "use arrow keys",
+ y: 62, text: "Use Arrow Keys",
alignment_enum: 1)
args.lowrez.labels << args.lowrez
.default_label
.merge(x: 32,
- y: 56, text: "use wasd",
+ y: 56, text: "Use WASD",
alignment_enum: 1)
args.lowrez.labels << args.lowrez
.default_label
.merge(x: 32,
- y: 50, text: "or click",
+ y: 50, text: "Or Click",
alignment_enum: 1)
# set the initial values for x and y using ||= ("or equal operator")
@@ -410,7 +410,7 @@ def how_to_determine_collision args
args.lowrez.labels << args.lowrez
.default_label
.merge(x: 32,
- y: 62, text: "click anywhere",
+ y: 62, text: "Click Anywhere",
alignment_enum: 1)
# if a mouse click occurs:
@@ -459,7 +459,7 @@ def how_to_determine_collision args
.default_label
.merge(x: 31,
y: 5,
- text: "collision!",
+ text: "Collision!",
alignment_enum: 1)
else
# if collision occurred, render the words no collision.
@@ -467,7 +467,7 @@ def how_to_determine_collision args
.default_label
.merge(x: 31,
y: 5,
- text: "no collision.",
+ text: "No Collision.",
alignment_enum: 1)
end
else
@@ -490,7 +490,7 @@ def how_to_create_buttons args
args.state.label_style = { r: 80, g: 80, b: 80 }
# Render instructions
- args.state.button_message ||= "press a button!"
+ args.state.button_message ||= "Press a Button!"
args.lowrez.labels << args.lowrez
.default_label
.merge(args.state.label_style)
@@ -508,7 +508,7 @@ def how_to_create_buttons args
.merge(args.state.label_style)
.merge(x: args.state.button_one_border.x + 2,
y: args.state.button_one_border.y + LOWREZ_FONT_SM_HEIGHT + 2,
- text: "button one")
+ text: "Button One")
# Creates button two using a border and a label
args.state.button_two_border = args.state.button_style.merge( x: 1, y: 20)
@@ -519,7 +519,7 @@ def how_to_create_buttons args
.merge(args.state.label_style)
.merge(x: args.state.button_two_border.x + 2,
y: args.state.button_two_border.y + LOWREZ_FONT_SM_HEIGHT + 2,
- text: "button two")
+ text: "Button Two")
# Initialize the state variable that tracks which button was clicked to "" (empty stringI
args.state.last_button_clicked ||= "--"
@@ -529,9 +529,9 @@ def how_to_create_buttons args
# set args.state.last_button_clicked accordingly
if args.lowrez.mouse_click
if args.lowrez.mouse_click.inside_rect? args.state.button_one_border
- args.state.last_button_clicked = "one clicked!"
+ args.state.last_button_clicked = "One Clicked!"
elsif args.lowrez.mouse_click.inside_rect? args.state.button_two_border
- args.state.last_button_clicked = "two clicked!"
+ args.state.last_button_clicked = "Two Clicked!"
else
args.state.last_button_clicked = "--"
end
diff --git a/samples/99_sample_sprite_animation_creator/app/main.rb b/samples/99_sample_sprite_animation_creator/app/main.rb
new file mode 100644
index 0000000..14456e3
--- /dev/null
+++ b/samples/99_sample_sprite_animation_creator/app/main.rb
@@ -0,0 +1,447 @@
+class OneBitLowrezPaint
+ attr_gtk
+
+ def tick
+ outputs.background_color = [0, 0, 0]
+ defaults
+ render_instructions
+ render_canvas
+ render_buttons_frame_selection
+ render_animation_frame_thumbnails
+ render_animation
+ input_mouse_click
+ input_keyboard
+ calc_auto_export
+ calc_buttons_frame_selection
+ calc_animation_frames
+ process_queue_create_sprite
+ process_queue_reset_sprite
+ process_queue_update_rt_animation_frame
+ end
+
+ def defaults
+ state.animation_frames_per_second = 12
+ queues.create_sprite ||= []
+ queues.reset_sprite ||= []
+ queues.update_rt_animation_frame ||= []
+
+ if !state.animation_frames
+ state.animation_frames ||= []
+ add_animation_frame_to_end
+ end
+
+ state.last_mouse_down ||= 0
+ state.last_mouse_up ||= 0
+
+ state.buttons_frame_selection.left = 10
+ state.buttons_frame_selection.top = grid.top - 10
+ state.buttons_frame_selection.size = 20
+
+ defaults_canvas_sprite
+
+ state.edit_mode ||= :drawing
+ end
+
+ def defaults_canvas_sprite
+ rt_canvas.size = 16
+ rt_canvas.zoom = 30
+ rt_canvas.width = rt_canvas.size * rt_canvas.zoom
+ rt_canvas.height = rt_canvas.size * rt_canvas.zoom
+ rt_canvas.sprite = { x: 0,
+ y: 0,
+ w: rt_canvas.width,
+ h: rt_canvas.height,
+ path: :rt_canvas }.center_inside_rect(x: 0, y: 0, w: 640, h: 720)
+
+ return unless state.tick_count == 1
+
+ outputs[:rt_canvas].width = rt_canvas.width
+ outputs[:rt_canvas].height = rt_canvas.height
+ outputs[:rt_canvas].sprites << (rt_canvas.size + 1).map_with_index do |x|
+ (rt_canvas.size + 1).map_with_index do |y|
+ path = 'sprites/square-white.png'
+ path = 'sprites/square-blue.png' if x == 7 || x == 8
+ { x: x * rt_canvas.zoom,
+ y: y * rt_canvas.zoom,
+ w: rt_canvas.zoom,
+ h: rt_canvas.zoom,
+ path: path,
+ a: 50 }
+ end
+ end
+ end
+
+ def render_instructions
+ instructions = <<-S
+* Instructions:
+- All data is stored in the ~canvas~ directory.
+- Hold ~d~ to set the edit mode to erase.
+- Release ~d~ to set the edit mode drawing.
+- Press ~a~ to added a frame to the end.
+- Press ~b~ to select the previous frame.
+- Press ~f~ to select the next frame.
+- Press ~c~ to copy a frame.
+- Press ~v~ to paste a copied frame into the selected frame.
+- Press ~x~ to delete the currently selected frame.
+- Press ~w~ to save the canvas and export all sprites.
+- Press ~l~ to load the canvas.
+S
+
+ instructions.strip.each_line.with_index do |l, i|
+ outputs.labels << { x: 840, y: 500 - (i * 20), text: "#{l}",
+ r: 180, g: 180, b: 180, size_enum: -3 }
+ end
+ end
+
+ def render_canvas
+ return if state.tick_count.zero?
+ outputs.sprites << rt_canvas.sprite
+ end
+
+ def render_buttons_frame_selection
+ args.outputs.primitives << state.buttons_frame_selection.items.map_with_index do |b, i|
+ label = { x: b.x + state.buttons_frame_selection.size.half,
+ y: b.y,
+ text: "#{i + 1}", r: 180, g: 180, b: 180,
+ size_enum: -4, alignment_enum: 1 }.label
+
+ selection_border = b.merge(r: 40, g: 40, b: 40).border
+
+ if i == state.animation_frames_selected_index
+ selection_border = b.merge(r: 40, g: 230, b: 200).border
+ end
+
+ [selection_border, label]
+ end
+ end
+
+ def render_animation_frame_thumbnails
+ return if state.tick_count.zero?
+
+ outputs[:current_animation_frame].width = rt_canvas.size
+ outputs[:current_animation_frame].height = rt_canvas.size
+ outputs[:current_animation_frame].solids << selected_animation_frame[:pixels].map_with_index do |f, i|
+ { x: f.x,
+ y: f.y,
+ w: 1,
+ h: 1, r: 255, g: 255, b: 255 }
+ end
+
+ outputs.sprites << rt_canvas.sprite.merge(path: :current_animation_frame)
+
+ state.animation_frames.map_with_index do |animation_frame, animation_frame_index|
+ outputs.sprites << state.buttons_frame_selection[:items][animation_frame_index][:inner_rect]
+ .merge(path: animation_frame[:rt_name])
+ end
+ end
+
+ def render_animation
+ sprite_index = 0.frame_index count: state.animation_frames.length,
+ hold_for: 60 / state.animation_frames_per_second,
+ repeat: true
+
+ args.outputs.sprites << { x: 700 - 8,
+ y: 120,
+ w: 16,
+ h: 16,
+ path: (sprite_path sprite_index) }
+
+ args.outputs.sprites << { x: 700 - 16,
+ y: 230,
+ w: 32,
+ h: 32,
+ path: (sprite_path sprite_index) }
+
+ args.outputs.sprites << { x: 700 - 32,
+ y: 360,
+ w: 64,
+ h: 64,
+ path: (sprite_path sprite_index) }
+
+ args.outputs.sprites << { x: 700 - 64,
+ y: 520,
+ w: 128,
+ h: 128,
+ path: (sprite_path sprite_index) }
+ end
+
+ def input_mouse_click
+ if inputs.mouse.up
+ state.last_mouse_up = state.tick_count
+ elsif inputs.mouse.moved && user_is_editing?
+ edit_current_animation_frame inputs.mouse.point
+ end
+
+ return unless inputs.mouse.click
+
+ clicked_frame_button = state.buttons_frame_selection.items.find do |b|
+ inputs.mouse.point.inside_rect? b
+ end
+
+ if (clicked_frame_button)
+ state.animation_frames_selected_index = clicked_frame_button[:index]
+ end
+
+ if (inputs.mouse.point.inside_rect? rt_canvas.sprite)
+ state.last_mouse_down = state.tick_count
+ edit_current_animation_frame inputs.mouse.point
+ end
+ end
+
+ def input_keyboard
+ # w to save
+ if inputs.keyboard.key_down.w
+ t = Time.now
+ state.save_description = "Time: #{t} (#{t.to_i})"
+ gtk.serialize_state 'canvas/state.txt', state
+ gtk.serialize_state "tmp/canvas_backups/#{t.to_i}/state.txt", state
+ animation_frames.each_with_index do |animation_frame, i|
+ queues.update_rt_animation_frame << { index: i,
+ at: state.tick_count + i,
+ queue_sprite_creation: true }
+ queues.create_sprite << { index: i,
+ at: state.tick_count + animation_frames.length + i,
+ path_override: "tmp/canvas_backups/#{t.to_i}/sprite-#{i}.png" }
+ end
+ gtk.notify! "Canvas saved."
+ end
+
+ # l to load
+ if inputs.keyboard.key_down.l
+ args.state = gtk.deserialize_state 'canvas/state.txt'
+ animation_frames.each_with_index do |a, i|
+ queues.update_rt_animation_frame << { index: i,
+ at: state.tick_count + i,
+ queue_sprite_creation: true }
+ end
+ gtk.notify! "Canvas loaded."
+ end
+
+ # d to go into delete mode, release to paint
+ if inputs.keyboard.key_held.d
+ state.edit_mode = :erasing
+ gtk.notify! "Erasing." if inputs.keyboard.key_held.d == (state.tick_count - 1)
+ elsif inputs.keyboard.key_up.d
+ state.edit_mode = :drawing
+ gtk.notify! "Drawing."
+ end
+
+ # a to add a frame to the end
+ if inputs.keyboard.key_down.a
+ queues.create_sprite << { index: state.animation_frames_selected_index,
+ at: state.tick_count }
+ queues.create_sprite << { index: state.animation_frames_selected_index + 1,
+ at: state.tick_count }
+ add_animation_frame_to_end
+ gtk.notify! "Frame added to end."
+ end
+
+ # c or t to copy
+ if (inputs.keyboard.key_down.c || inputs.keyboard.key_down.t)
+ state.clipboard = [selected_animation_frame[:pixels]].flatten
+ gtk.notify! "Current frame copied."
+ end
+
+ # v or q to paste
+ if (inputs.keyboard.key_down.v || inputs.keyboard.key_down.q) && state.clipboard
+ selected_animation_frame[:pixels] = [state.clipboard].flatten
+ queues.update_rt_animation_frame << { index: state.animation_frames_selected_index,
+ at: state.tick_count,
+ queue_sprite_creation: true }
+ gtk.notify! "Pasted."
+ end
+
+ # f to go forward/next frame
+ if (inputs.keyboard.key_down.f)
+ if (state.animation_frames_selected_index == (state.animation_frames.length - 1))
+ state.animation_frames_selected_index = 0
+ else
+ state.animation_frames_selected_index += 1
+ end
+ gtk.notify! "Next frame."
+ end
+
+ # b to go back/previous frame
+ if (inputs.keyboard.key_down.b)
+ if (state.animation_frames_selected_index == 0)
+ state.animation_frames_selected_index = state.animation_frames.length - 1
+ else
+ state.animation_frames_selected_index -= 1
+ end
+ gtk.notify! "Previous frame."
+ end
+
+ # x to delete frame
+ if (inputs.keyboard.key_down.x) && animation_frames.length > 1
+ state.clipboard = selected_animation_frame[:pixels]
+ state.animation_frames = animation_frames.find_all { |v| v[:index] != state.animation_frames_selected_index }
+ if state.animation_frames_selected_index >= state.animation_frames.length
+ state.animation_frames_selected_index = state.animation_frames.length - 1
+ end
+ gtk.notify! "Frame deleted."
+ end
+ end
+
+ def calc_auto_export
+ return if user_is_editing?
+ return if state.last_mouse_up.elapsed_time != 30
+ # auto export current animation frame if there is no editing for 30 ticks
+ queues.create_sprite << { index: state.animation_frames_selected_index,
+ at: state.tick_count }
+ end
+
+ def calc_buttons_frame_selection
+ state.buttons_frame_selection.items = animation_frames.length.map_with_index do |i|
+ { x: state.buttons_frame_selection.left + i * state.buttons_frame_selection.size,
+ y: state.buttons_frame_selection.top - state.buttons_frame_selection.size,
+ inner_rect: {
+ x: (state.buttons_frame_selection.left + 2) + i * state.buttons_frame_selection.size,
+ y: (state.buttons_frame_selection.top - state.buttons_frame_selection.size + 2),
+ w: 16,
+ h: 16,
+ },
+ w: state.buttons_frame_selection.size,
+ h: state.buttons_frame_selection.size,
+ index: i }
+ end
+ end
+
+ def calc_animation_frames
+ animation_frames.each_with_index do |animation_frame, i|
+ animation_frame[:index] = i
+ animation_frame[:rt_name] = "animation_frame_#{i}"
+ end
+ end
+
+ def process_queue_create_sprite
+ sprites_to_create = queues.create_sprite
+ .find_all { |h| h[:at].elapsed? }
+
+ queues.create_sprite = queues.create_sprite - sprites_to_create
+
+ sprites_to_create.each do |h|
+ export_animation_frame h[:index], h[:path_override]
+ end
+ end
+
+ def process_queue_reset_sprite
+ sprites_to_reset = queues.reset_sprite
+ .find_all { |h| h[:at].elapsed? }
+
+ queues.reset_sprite -= sprites_to_reset
+
+ sprites_to_reset.each { |h| gtk.reset_sprite (sprite_path h[:index]) }
+ end
+
+ def process_queue_update_rt_animation_frame
+ animation_frames_to_update = queues.update_rt_animation_frame
+ .find_all { |h| h[:at].elapsed? }
+
+ queues.update_rt_animation_frame -= animation_frames_to_update
+
+ animation_frames_to_update.each do |h|
+ update_animation_frame_render_target animation_frames[h[:index]]
+
+ if h[:queue_sprite_creation]
+ queues.create_sprite << { index: h[:index],
+ at: state.tick_count + 1 }
+ end
+ end
+ end
+
+ def update_animation_frame_render_target animation_frame
+ return if !animation_frame
+
+ outputs[animation_frame[:rt_name]].width = state.rt_canvas.size
+ outputs[animation_frame[:rt_name]].height = state.rt_canvas.size
+ outputs[animation_frame[:rt_name]].solids << animation_frame[:pixels].map do |f|
+ { x: f.x,
+ y: f.y,
+ w: 1,
+ h: 1, r: 255, g: 255, b: 255 }
+ end
+ end
+
+ def animation_frames
+ state.animation_frames
+ end
+
+ def add_animation_frame_to_end
+ animation_frames << {
+ index: animation_frames.length,
+ pixels: [],
+ rt_name: "animation_frame_#{animation_frames.length}"
+ }
+
+ state.animation_frames_selected_index = (animation_frames.length - 1)
+ queues.update_rt_animation_frame << { index: state.animation_frames_selected_index,
+ at: state.tick_count,
+ queue_sprite_creation: true }
+ end
+
+ def sprite_path i
+ "canvas/sprite-#{i}.png"
+ end
+
+ def export_animation_frame i, path_override = nil
+ return if !state.animation_frames[i]
+
+ outputs.screenshots << state.buttons_frame_selection
+ .items[i][:inner_rect]
+ .merge(path: path_override || (sprite_path i))
+
+ outputs.screenshots << state.buttons_frame_selection
+ .items[i][:inner_rect]
+ .merge(path: "tmp/sprite_backups/#{Time.now.to_i}-sprite-#{i}.png")
+
+ queues.reset_sprite << { index: i, at: state.tick_count }
+ end
+
+ def selected_animation_frame
+ state.animation_frames[state.animation_frames_selected_index]
+ end
+
+ def edit_current_animation_frame point
+ draw_area_point = (to_draw_area point)
+ if state.edit_mode == :drawing && (!selected_animation_frame[:pixels].include? draw_area_point)
+ selected_animation_frame[:pixels] << draw_area_point
+ queues.update_rt_animation_frame << { index: state.animation_frames_selected_index,
+ at: state.tick_count,
+ queue_sprite_creation: !user_is_editing? }
+ elsif state.edit_mode == :erasing && (selected_animation_frame[:pixels].include? draw_area_point)
+ selected_animation_frame[:pixels] = selected_animation_frame[:pixels].reject { |p| p == draw_area_point }
+ queues.update_rt_animation_frame << { index: state.animation_frames_selected_index,
+ at: state.tick_count,
+ queue_sprite_creation: !user_is_editing? }
+ end
+ end
+
+ def user_is_editing?
+ state.last_mouse_down > state.last_mouse_up
+ end
+
+ def to_draw_area point
+ x, y = point
+ x -= rt_canvas.sprite.x
+ y -= rt_canvas.sprite.y
+ { x: x.idiv(rt_canvas.zoom),
+ y: y.idiv(rt_canvas.zoom) }
+ end
+
+ def rt_canvas
+ state.rt_canvas ||= state.new_entity(:rt_canvas)
+ end
+
+ def queues
+ state.queues ||= state.new_entity(:queues)
+ end
+end
+
+$game = OneBitLowrezPaint.new
+
+def tick args
+ $game.args = args
+ $game.tick
+end
+
+# $gtk.reset
diff --git a/samples/99_sample_sprite_animation_creator/license-for-sample.txt b/samples/99_sample_sprite_animation_creator/license-for-sample.txt
new file mode 100644
index 0000000..376dd0e
--- /dev/null
+++ b/samples/99_sample_sprite_animation_creator/license-for-sample.txt
@@ -0,0 +1,9 @@
+Copyright 2019 Anton K. (ai Doge)
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/samples/99_sample_sprite_animation_creator/replay.txt b/samples/99_sample_sprite_animation_creator/replay.txt
new file mode 100644
index 0000000..ca25af6
--- /dev/null
+++ b/samples/99_sample_sprite_animation_creator/replay.txt
@@ -0,0 +1,908 @@
+replay_version 2.0
+stopped_at 2856
+seed 100
+recorded_at Thu Aug 06 06:37:39 2020
+[:key_up_raw, 13, 0, 2, 1, 2]
+[:mouse_move, 280, 591, 2, 2, 73]
+[:mouse_move, 290, 613, 2, 3, 74]
+[:mouse_move, 292, 621, 2, 4, 75]
+[:mouse_move, 284, 628, 2, 5, 76]
+[:mouse_move, 272, 628, 2, 6, 77]
+[:mouse_move, 255, 628, 2, 7, 78]
+[:mouse_move, 215, 606, 2, 8, 79]
+[:mouse_move, 193, 590, 2, 9, 80]
+[:mouse_move, 156, 544, 2, 10, 81]
+[:mouse_move, 151, 535, 2, 11, 82]
+[:mouse_move, 138, 503, 2, 12, 83]
+[:mouse_move, 137, 490, 2, 13, 84]
+[:mouse_move, 137, 472, 2, 14, 85]
+[:mouse_move, 139, 470, 2, 15, 86]
+[:mouse_move, 145, 462, 2, 16, 87]
+[:mouse_move, 149, 459, 2, 17, 89]
+[:mouse_move, 150, 458, 2, 18, 90]
+[:mouse_move, 151, 457, 2, 19, 91]
+[:mouse_move, 152, 457, 2, 20, 92]
+[:mouse_move, 152, 456, 2, 21, 93]
+[:mouse_move, 152, 451, 2, 22, 114]
+[:mouse_move, 153, 442, 2, 23, 115]
+[:mouse_move, 158, 427, 2, 24, 116]
+[:mouse_move, 160, 422, 2, 25, 117]
+[:mouse_move, 166, 404, 2, 26, 118]
+[:mouse_move, 169, 398, 2, 27, 119]
+[:mouse_move, 175, 386, 2, 28, 120]
+[:mouse_move, 180, 378, 2, 29, 121]
+[:mouse_move, 183, 373, 2, 30, 122]
+[:mouse_move, 185, 370, 2, 31, 123]
+[:mouse_move, 188, 366, 2, 32, 124]
+[:mouse_move, 189, 364, 2, 33, 125]
+[:mouse_move, 190, 362, 2, 34, 126]
+[:mouse_move, 191, 361, 2, 35, 128]
+[:mouse_button_pressed, 1, 0, 1, 36, 291]
+[:mouse_move, 190, 358, 2, 37, 291]
+[:mouse_move, 190, 357, 2, 38, 292]
+[:mouse_move, 189, 354, 2, 39, 293]
+[:mouse_move, 186, 346, 2, 40, 294]
+[:mouse_move, 185, 344, 2, 41, 295]
+[:mouse_move, 182, 326, 2, 42, 296]
+[:mouse_move, 182, 323, 2, 43, 296]
+[:mouse_move, 182, 318, 2, 44, 297]
+[:mouse_move, 182, 315, 2, 45, 298]
+[:mouse_move, 182, 311, 2, 46, 299]
+[:mouse_move, 183, 307, 2, 47, 301]
+[:mouse_move, 185, 302, 2, 48, 302]
+[:mouse_move, 187, 299, 2, 49, 303]
+[:mouse_move, 190, 295, 2, 50, 304]
+[:mouse_move, 193, 292, 2, 51, 305]
+[:mouse_move, 199, 287, 2, 52, 306]
+[:mouse_move, 203, 283, 2, 53, 307]
+[:mouse_move, 206, 280, 2, 54, 308]
+[:mouse_move, 216, 273, 2, 55, 309]
+[:mouse_move, 220, 271, 2, 56, 310]
+[:mouse_move, 232, 264, 2, 57, 311]
+[:mouse_move, 238, 261, 2, 58, 312]
+[:mouse_move, 250, 256, 2, 59, 313]
+[:mouse_move, 256, 253, 2, 60, 314]
+[:mouse_move, 266, 251, 2, 61, 315]
+[:mouse_move, 275, 248, 2, 62, 316]
+[:mouse_move, 288, 245, 2, 63, 317]
+[:mouse_move, 294, 245, 2, 64, 318]
+[:mouse_move, 307, 245, 2, 65, 319]
+[:mouse_move, 316, 245, 2, 66, 320]
+[:mouse_move, 332, 247, 2, 67, 321]
+[:mouse_move, 343, 250, 2, 68, 322]
+[:mouse_move, 350, 253, 2, 69, 323]
+[:mouse_move, 367, 261, 2, 70, 324]
+[:mouse_move, 385, 272, 2, 71, 325]
+[:mouse_move, 394, 279, 2, 72, 326]
+[:mouse_move, 408, 294, 2, 73, 327]
+[:mouse_move, 416, 301, 2, 74, 328]
+[:mouse_move, 426, 318, 2, 75, 329]
+[:mouse_move, 432, 326, 2, 76, 330]
+[:mouse_move, 439, 342, 2, 77, 331]
+[:mouse_move, 442, 351, 2, 78, 332]
+[:mouse_move, 447, 369, 2, 79, 333]
+[:mouse_move, 449, 382, 2, 80, 334]
+[:mouse_move, 451, 405, 2, 81, 335]
+[:mouse_move, 452, 410, 2, 82, 336]
+[:mouse_move, 452, 417, 2, 83, 337]
+[:mouse_move, 449, 426, 2, 84, 338]
+[:mouse_move, 444, 437, 2, 85, 338]
+[:mouse_move, 440, 441, 2, 86, 339]
+[:mouse_move, 430, 450, 2, 87, 340]
+[:mouse_move, 423, 456, 2, 88, 341]
+[:mouse_move, 412, 464, 2, 89, 342]
+[:mouse_move, 407, 467, 2, 90, 343]
+[:mouse_move, 402, 470, 2, 91, 344]
+[:mouse_move, 382, 480, 2, 92, 345]
+[:mouse_move, 371, 485, 2, 93, 346]
+[:mouse_move, 351, 491, 2, 94, 347]
+[:mouse_move, 344, 492, 2, 95, 348]
+[:mouse_move, 321, 498, 2, 96, 349]
+[:mouse_move, 312, 499, 2, 97, 350]
+[:mouse_move, 290, 497, 2, 98, 351]
+[:mouse_move, 283, 496, 2, 99, 352]
+[:mouse_move, 266, 490, 2, 100, 353]
+[:mouse_move, 260, 487, 2, 101, 354]
+[:mouse_move, 246, 478, 2, 102, 355]
+[:mouse_move, 241, 472, 2, 103, 356]
+[:mouse_move, 230, 461, 2, 104, 357]
+[:mouse_move, 226, 455, 2, 105, 358]
+[:mouse_move, 217, 443, 2, 106, 359]
+[:mouse_move, 213, 435, 2, 107, 360]
+[:mouse_move, 207, 422, 2, 108, 361]
+[:mouse_move, 205, 418, 2, 109, 362]
+[:mouse_move, 201, 401, 2, 110, 363]
+[:mouse_move, 199, 396, 2, 111, 364]
+[:mouse_move, 197, 385, 2, 112, 365]
+[:mouse_move, 197, 380, 2, 113, 366]
+[:mouse_move, 196, 372, 2, 114, 367]
+[:mouse_move, 196, 367, 2, 115, 368]
+[:mouse_move, 195, 361, 2, 116, 369]
+[:mouse_move, 195, 358, 2, 117, 370]
+[:mouse_move, 195, 353, 2, 118, 371]
+[:mouse_move, 195, 347, 2, 119, 372]
+[:mouse_move, 196, 344, 2, 120, 373]
+[:mouse_move, 196, 338, 2, 121, 374]
+[:mouse_move, 197, 337, 2, 122, 375]
+[:mouse_move, 197, 334, 2, 123, 376]
+[:mouse_move, 197, 333, 2, 124, 377]
+[:mouse_move, 197, 332, 2, 125, 379]
+[:mouse_button_up, 1, 0, 1, 126, 393]
+[:mouse_move, 224, 331, 2, 127, 394]
+[:mouse_move, 237, 327, 2, 128, 395]
+[:mouse_move, 257, 317, 2, 129, 396]
+[:mouse_move, 267, 310, 2, 130, 397]
+[:mouse_move, 289, 295, 2, 131, 398]
+[:mouse_move, 315, 268, 2, 132, 399]
+[:mouse_move, 340, 234, 2, 133, 400]
+[:mouse_move, 353, 204, 2, 134, 401]
+[:mouse_move, 359, 188, 2, 135, 402]
+[:mouse_move, 361, 172, 2, 136, 403]
+[:mouse_move, 361, 163, 2, 137, 404]
+[:mouse_move, 361, 154, 2, 138, 405]
+[:mouse_move, 361, 152, 2, 139, 406]
+[:mouse_move, 361, 151, 2, 140, 407]
+[:mouse_move, 361, 150, 2, 141, 408]
+[:key_down_raw, 97, 0, 2, 142, 408]
+[:mouse_move, 360, 150, 2, 143, 408]
+[:mouse_move, 360, 151, 2, 144, 412]
+[:key_up_raw, 97, 0, 2, 145, 412]
+[:mouse_move, 360, 152, 2, 146, 413]
+[:mouse_move, 360, 153, 2, 147, 414]
+[:mouse_move, 360, 155, 2, 148, 415]
+[:mouse_move, 360, 157, 2, 149, 416]
+[:mouse_move, 361, 170, 2, 150, 417]
+[:mouse_move, 361, 171, 2, 151, 418]
+[:mouse_move, 361, 174, 2, 152, 419]
+[:mouse_move, 361, 175, 2, 153, 421]
+[:mouse_move, 361, 176, 2, 154, 423]
+[:mouse_move, 359, 179, 2, 155, 425]
+[:mouse_move, 359, 180, 2, 156, 427]
+[:mouse_move, 358, 181, 2, 157, 428]
+[:mouse_move, 358, 182, 2, 158, 429]
+[:mouse_move, 358, 183, 2, 159, 430]
+[:mouse_move, 345, 202, 2, 160, 431]
+[:mouse_move, 340, 207, 2, 161, 432]
+[:mouse_move, 337, 210, 2, 162, 433]
+[:mouse_move, 335, 213, 2, 163, 434]
+[:mouse_move, 331, 217, 2, 164, 435]
+[:mouse_move, 327, 221, 2, 165, 436]
+[:mouse_move, 325, 223, 2, 166, 437]
+[:mouse_move, 325, 224, 2, 167, 438]
+[:mouse_move, 322, 226, 2, 168, 439]
+[:mouse_move, 322, 228, 2, 169, 440]
+[:mouse_move, 321, 229, 2, 170, 441]
+[:mouse_move, 321, 230, 2, 171, 445]
+[:mouse_move, 320, 231, 2, 172, 463]
+[:mouse_move, 319, 233, 2, 173, 465]
+[:mouse_move, 318, 233, 2, 174, 466]
+[:mouse_move, 318, 234, 2, 175, 467]
+[:mouse_move, 316, 236, 2, 176, 468]
+[:mouse_move, 315, 236, 2, 177, 469]
+[:mouse_move, 314, 238, 2, 178, 470]
+[:mouse_move, 313, 238, 2, 179, 471]
+[:mouse_move, 312, 239, 2, 180, 472]
+[:mouse_move, 311, 240, 2, 181, 474]
+[:mouse_move, 310, 240, 2, 182, 477]
+[:mouse_button_pressed, 1, 0, 1, 183, 499]
+[:mouse_move, 307, 239, 2, 184, 499]
+[:mouse_move, 303, 238, 2, 185, 500]
+[:mouse_move, 295, 234, 2, 186, 501]
+[:mouse_move, 291, 233, 2, 187, 502]
+[:mouse_move, 262, 231, 2, 188, 503]
+[:mouse_move, 256, 232, 2, 189, 504]
+[:mouse_move, 254, 233, 2, 190, 505]
+[:mouse_move, 249, 236, 2, 191, 506]
+[:mouse_move, 246, 239, 2, 192, 507]
+[:mouse_move, 243, 242, 2, 193, 508]
+[:mouse_move, 241, 244, 2, 194, 509]
+[:mouse_move, 235, 250, 2, 195, 510]
+[:mouse_move, 234, 252, 2, 196, 511]
+[:mouse_move, 230, 258, 2, 197, 512]
+[:mouse_move, 227, 262, 2, 198, 513]
+[:mouse_move, 223, 268, 2, 199, 514]
+[:mouse_move, 221, 270, 2, 200, 515]
+[:mouse_move, 219, 273, 2, 201, 516]
+[:mouse_move, 217, 277, 2, 202, 517]
+[:mouse_move, 215, 281, 2, 203, 518]
+[:mouse_move, 213, 288, 2, 204, 519]
+[:mouse_move, 212, 292, 2, 205, 520]
+[:mouse_move, 211, 302, 2, 206, 521]
+[:mouse_move, 210, 307, 2, 207, 522]
+[:mouse_move, 210, 318, 2, 208, 523]
+[:mouse_move, 210, 323, 2, 209, 524]
+[:mouse_move, 212, 335, 2, 210, 525]
+[:mouse_move, 214, 340, 2, 211, 526]
+[:mouse_move, 218, 351, 2, 212, 527]
+[:mouse_move, 222, 356, 2, 213, 528]
+[:mouse_move, 230, 368, 2, 214, 529]
+[:mouse_move, 235, 374, 2, 215, 530]
+[:mouse_move, 247, 387, 2, 216, 531]
+[:mouse_move, 254, 393, 2, 217, 532]
+[:mouse_move, 268, 407, 2, 218, 533]
+[:mouse_move, 275, 413, 2, 219, 534]
+[:mouse_move, 290, 424, 2, 220, 535]
+[:mouse_move, 298, 429, 2, 221, 536]
+[:mouse_move, 311, 436, 2, 222, 537]
+[:mouse_move, 319, 441, 2, 223, 538]
+[:mouse_move, 333, 445, 2, 224, 539]
+[:mouse_move, 337, 446, 2, 225, 540]
+[:mouse_move, 350, 448, 2, 226, 541]
+[:mouse_move, 357, 448, 2, 227, 542]
+[:mouse_move, 368, 445, 2, 228, 543]
+[:mouse_move, 376, 440, 2, 229, 544]
+[:mouse_move, 382, 435, 2, 230, 545]
+[:mouse_move, 393, 424, 2, 231, 546]
+[:mouse_move, 398, 418, 2, 232, 547]
+[:mouse_move, 405, 404, 2, 233, 548]
+[:mouse_move, 408, 397, 2, 234, 549]
+[:mouse_move, 414, 385, 2, 235, 550]
+[:mouse_move, 415, 380, 2, 236, 551]
+[:mouse_move, 418, 368, 2, 237, 552]
+[:mouse_move, 419, 362, 2, 238, 553]
+[:mouse_move, 420, 352, 2, 239, 554]
+[:mouse_move, 420, 349, 2, 240, 555]
+[:mouse_move, 420, 342, 2, 241, 556]
+[:mouse_move, 420, 340, 2, 242, 557]
+[:mouse_move, 420, 339, 2, 243, 558]
+[:mouse_move, 420, 337, 2, 244, 559]
+[:mouse_move, 420, 336, 2, 245, 560]
+[:mouse_move, 420, 334, 2, 246, 561]
+[:mouse_move, 417, 332, 2, 247, 562]
+[:mouse_move, 416, 331, 2, 248, 563]
+[:mouse_move, 412, 327, 2, 249, 564]
+[:mouse_move, 410, 325, 2, 250, 565]
+[:mouse_move, 402, 320, 2, 251, 566]
+[:mouse_move, 397, 317, 2, 252, 567]
+[:mouse_move, 385, 312, 2, 253, 568]
+[:mouse_move, 379, 310, 2, 254, 569]
+[:mouse_move, 366, 305, 2, 255, 570]
+[:mouse_move, 358, 303, 2, 256, 571]
+[:mouse_move, 352, 302, 2, 257, 572]
+[:mouse_move, 338, 300, 2, 258, 573]
+[:mouse_move, 331, 300, 2, 259, 574]
+[:mouse_move, 314, 300, 2, 260, 575]
+[:mouse_move, 307, 301, 2, 261, 576]
+[:mouse_move, 293, 302, 2, 262, 577]
+[:mouse_move, 288, 303, 2, 263, 578]
+[:mouse_move, 275, 305, 2, 264, 579]
+[:mouse_move, 267, 305, 2, 265, 580]
+[:mouse_move, 258, 307, 2, 266, 581]
+[:mouse_move, 250, 308, 2, 267, 582]
+[:mouse_move, 239, 310, 2, 268, 583]
+[:mouse_move, 233, 311, 2, 269, 584]
+[:mouse_move, 225, 312, 2, 270, 585]
+[:mouse_move, 222, 313, 2, 271, 586]
+[:mouse_move, 217, 315, 2, 272, 587]
+[:mouse_move, 215, 315, 2, 273, 588]
+[:mouse_move, 212, 317, 2, 274, 589]
+[:mouse_move, 212, 318, 2, 275, 590]
+[:mouse_move, 211, 319, 2, 276, 591]
+[:mouse_move, 211, 320, 2, 277, 593]
+[:mouse_button_up, 1, 0, 1, 278, 608]
+[:mouse_move, 215, 335, 2, 279, 608]
+[:mouse_move, 220, 349, 2, 280, 609]
+[:mouse_move, 225, 378, 2, 281, 610]
+[:mouse_move, 226, 403, 2, 282, 612]
+[:mouse_move, 226, 419, 2, 283, 613]
+[:mouse_move, 220, 442, 2, 284, 614]
+[:mouse_move, 210, 449, 2, 285, 615]
+[:mouse_move, 196, 450, 2, 286, 616]
+[:mouse_move, 187, 450, 2, 287, 617]
+[:mouse_move, 173, 441, 2, 288, 618]
+[:mouse_move, 168, 435, 2, 289, 619]
+[:mouse_move, 164, 427, 2, 290, 620]
+[:mouse_move, 161, 422, 2, 291, 621]
+[:mouse_move, 159, 415, 2, 292, 622]
+[:mouse_move, 159, 414, 2, 293, 623]
+[:mouse_move, 159, 412, 2, 294, 624]
+[:mouse_move, 159, 411, 2, 295, 625]
+[:mouse_move, 159, 410, 2, 296, 627]
+[:mouse_move, 160, 410, 2, 297, 632]
+[:mouse_move, 161, 410, 2, 298, 639]
+[:key_down_raw, 97, 0, 2, 299, 673]
+[:key_up_raw, 97, 0, 2, 300, 677]
+[:mouse_move, 161, 409, 2, 301, 680]
+[:mouse_move, 162, 408, 2, 302, 682]
+[:mouse_move, 162, 407, 2, 303, 683]
+[:mouse_move, 163, 405, 2, 304, 684]
+[:mouse_move, 164, 403, 2, 305, 685]
+[:mouse_move, 164, 402, 2, 306, 689]
+[:key_down_raw, 98, 0, 2, 307, 705]
+[:key_up_raw, 98, 0, 2, 308, 709]
+[:key_down_raw, 99, 0, 2, 309, 723]
+[:key_up_raw, 99, 0, 2, 310, 729]
+[:key_down_raw, 98, 0, 2, 311, 763]
+[:key_up_raw, 98, 0, 2, 312, 769]
+[:key_down_raw, 102, 0, 2, 313, 874]
+[:key_up_raw, 102, 0, 2, 314, 877]
+[:key_down_raw, 102, 0, 2, 315, 881]
+[:key_up_raw, 102, 0, 2, 316, 886]
+[:key_down_raw, 113, 0, 2, 317, 898]
+[:key_up_raw, 113, 0, 2, 318, 902]
+[:mouse_move, 167, 401, 2, 319, 945]
+[:mouse_move, 170, 399, 2, 320, 946]
+[:mouse_move, 180, 393, 2, 321, 947]
+[:mouse_move, 190, 388, 2, 322, 948]
+[:mouse_move, 202, 382, 2, 323, 949]
+[:mouse_move, 216, 376, 2, 324, 950]
+[:mouse_move, 226, 375, 2, 325, 951]
+[:mouse_move, 235, 374, 2, 326, 952]
+[:mouse_move, 245, 375, 2, 327, 953]
+[:mouse_move, 247, 377, 2, 328, 954]
+[:mouse_move, 255, 383, 2, 329, 955]
+[:mouse_move, 261, 387, 2, 330, 956]
+[:mouse_move, 270, 391, 2, 331, 957]
+[:mouse_move, 275, 393, 2, 332, 958]
+[:mouse_move, 285, 393, 2, 333, 959]
+[:mouse_move, 296, 392, 2, 334, 960]
+[:mouse_move, 318, 377, 2, 335, 961]
+[:mouse_move, 331, 365, 2, 336, 962]
+[:mouse_move, 368, 335, 2, 337, 963]
+[:mouse_move, 386, 319, 2, 338, 964]
+[:mouse_move, 404, 303, 2, 339, 965]
+[:mouse_move, 426, 281, 2, 340, 966]
+[:mouse_move, 438, 269, 2, 341, 967]
+[:mouse_move, 449, 257, 2, 342, 968]
+[:mouse_move, 456, 250, 2, 343, 969]
+[:mouse_move, 467, 237, 2, 344, 970]
+[:mouse_move, 468, 235, 2, 345, 971]
+[:mouse_move, 471, 231, 2, 346, 972]
+[:mouse_move, 472, 230, 2, 347, 973]
+[:mouse_move, 473, 228, 2, 348, 974]
+[:mouse_move, 474, 228, 2, 349, 975]
+[:mouse_move, 475, 226, 2, 350, 976]
+[:mouse_move, 475, 225, 2, 351, 977]
+[:mouse_move, 476, 223, 2, 352, 978]
+[:mouse_move, 476, 222, 2, 353, 980]
+[:mouse_button_pressed, 1, 0, 1, 354, 992]
+[:mouse_move, 473, 223, 2, 355, 992]
+[:mouse_move, 470, 225, 2, 356, 993]
+[:mouse_move, 466, 226, 2, 357, 994]
+[:mouse_move, 460, 229, 2, 358, 995]
+[:mouse_move, 455, 231, 2, 359, 996]
+[:mouse_move, 401, 266, 2, 360, 997]
+[:mouse_move, 381, 280, 2, 361, 998]
+[:mouse_move, 371, 287, 2, 362, 999]
+[:mouse_move, 354, 301, 2, 363, 1000]
+[:mouse_move, 346, 308, 2, 364, 1001]
+[:mouse_move, 329, 323, 2, 365, 1002]
+[:mouse_move, 322, 331, 2, 366, 1003]
+[:mouse_move, 307, 343, 2, 367, 1004]
+[:mouse_move, 301, 349, 2, 368, 1005]
+[:mouse_move, 287, 362, 2, 369, 1006]
+[:mouse_move, 281, 369, 2, 370, 1007]
+[:mouse_move, 264, 387, 2, 371, 1008]
+[:mouse_move, 256, 396, 2, 372, 1009]
+[:mouse_move, 246, 407, 2, 373, 1010]
+[:mouse_move, 234, 420, 2, 374, 1012]
+[:mouse_move, 231, 423, 2, 375, 1013]
+[:mouse_move, 226, 430, 2, 376, 1014]
+[:mouse_move, 224, 432, 2, 377, 1015]
+[:mouse_move, 223, 434, 2, 378, 1016]
+[:mouse_move, 221, 436, 2, 379, 1017]
+[:mouse_move, 220, 438, 2, 380, 1018]
+[:mouse_move, 219, 439, 2, 381, 1019]
+[:mouse_move, 218, 440, 2, 382, 1020]
+[:mouse_move, 217, 441, 2, 383, 1021]
+[:mouse_move, 216, 442, 2, 384, 1023]
+[:mouse_move, 214, 445, 2, 385, 1024]
+[:mouse_move, 212, 447, 2, 386, 1025]
+[:mouse_move, 211, 448, 2, 387, 1026]
+[:mouse_move, 209, 450, 2, 388, 1027]
+[:mouse_move, 209, 451, 2, 389, 1028]
+[:mouse_move, 208, 452, 2, 390, 1029]
+[:mouse_move, 207, 452, 2, 391, 1030]
+[:mouse_move, 206, 453, 2, 392, 1031]
+[:mouse_move, 206, 454, 2, 393, 1032]
+[:mouse_move, 204, 456, 2, 394, 1033]
+[:mouse_move, 203, 457, 2, 395, 1034]
+[:mouse_move, 202, 458, 2, 396, 1035]
+[:mouse_button_up, 1, 0, 1, 397, 1050]
+[:mouse_move, 217, 484, 2, 398, 1050]
+[:mouse_move, 232, 510, 2, 399, 1051]
+[:mouse_move, 256, 554, 2, 400, 1052]
+[:mouse_move, 266, 571, 2, 401, 1053]
+[:mouse_move, 278, 588, 2, 402, 1054]
+[:mouse_move, 288, 604, 2, 403, 1055]
+[:mouse_move, 299, 620, 2, 404, 1056]
+[:mouse_move, 302, 624, 2, 405, 1057]
+[:mouse_move, 303, 626, 2, 406, 1058]
+[:mouse_move, 303, 624, 2, 407, 1062]
+[:mouse_move, 302, 622, 2, 408, 1063]
+[:mouse_move, 302, 621, 2, 409, 1064]
+[:mouse_move, 301, 619, 2, 410, 1065]
+[:mouse_move, 301, 618, 2, 411, 1066]
+[:mouse_move, 301, 617, 2, 412, 1068]
+[:mouse_move, 301, 616, 2, 413, 1072]
+[:mouse_move, 301, 615, 2, 414, 1075]
+[:mouse_move, 301, 614, 2, 415, 1076]
+[:mouse_move, 301, 612, 2, 416, 1077]
+[:mouse_move, 301, 611, 2, 417, 1078]
+[:mouse_move, 299, 604, 2, 418, 1079]
+[:mouse_move, 297, 599, 2, 419, 1080]
+[:mouse_move, 292, 586, 2, 420, 1081]
+[:mouse_move, 268, 518, 2, 421, 1082]
+[:mouse_move, 261, 492, 2, 422, 1083]
+[:mouse_move, 252, 466, 2, 423, 1084]
+[:mouse_move, 243, 437, 2, 424, 1085]
+[:mouse_move, 239, 425, 2, 425, 1086]
+[:mouse_move, 218, 366, 2, 426, 1087]
+[:mouse_move, 216, 360, 2, 427, 1088]
+[:mouse_move, 215, 358, 2, 428, 1089]
+[:mouse_move, 216, 358, 2, 429, 1100]
+[:mouse_move, 216, 360, 2, 430, 1102]
+[:mouse_move, 217, 360, 2, 431, 1103]
+[:mouse_move, 221, 366, 2, 432, 1104]
+[:mouse_move, 225, 372, 2, 433, 1105]
+[:mouse_move, 232, 383, 2, 434, 1106]
+[:mouse_move, 237, 391, 2, 435, 1107]
+[:mouse_move, 242, 399, 2, 436, 1108]
+[:mouse_move, 245, 404, 2, 437, 1109]
+[:mouse_move, 248, 409, 2, 438, 1110]
+[:mouse_move, 249, 410, 2, 439, 1111]
+[:mouse_move, 250, 411, 2, 440, 1112]
+[:mouse_move, 250, 412, 2, 441, 1118]
+[:mouse_move, 251, 412, 2, 442, 1125]
+[:mouse_move, 251, 413, 2, 443, 1127]
+[:key_down_raw, 97, 0, 2, 444, 1217]
+[:key_up_raw, 97, 0, 2, 445, 1221]
+[:key_down_raw, 97, 0, 2, 446, 1233]
+[:key_up_raw, 97, 0, 2, 447, 1237]
+[:key_down_raw, 98, 0, 2, 448, 1248]
+[:key_up_raw, 98, 0, 2, 449, 1252]
+[:key_down_raw, 98, 0, 2, 450, 1256]
+[:key_up_raw, 98, 0, 2, 451, 1258]
+[:key_down_raw, 98, 0, 2, 452, 1293]
+[:key_up_raw, 98, 0, 2, 453, 1297]
+[:key_down_raw, 99, 0, 2, 454, 1334]
+[:key_up_raw, 99, 0, 2, 455, 1339]
+[:key_down_raw, 98, 0, 2, 456, 1419]
+[:key_up_raw, 98, 0, 2, 457, 1422]
+[:key_down_raw, 98, 0, 2, 458, 1426]
+[:key_up_raw, 98, 0, 2, 459, 1429]
+[:key_down_raw, 98, 0, 2, 460, 1473]
+[:key_up_raw, 98, 0, 2, 461, 1478]
+[:key_down_raw, 113, 0, 2, 462, 1534]
+[:key_up_raw, 113, 0, 2, 463, 1538]
+[:key_down_raw, 98, 0, 2, 464, 1636]
+[:key_up_raw, 98, 0, 2, 465, 1642]
+[:key_down_raw, 102, 0, 2, 466, 1676]
+[:key_up_raw, 102, 0, 2, 467, 1680]
+[:key_down_raw, 102, 0, 2, 468, 1684]
+[:key_up_raw, 102, 0, 2, 469, 1688]
+[:key_down_raw, 113, 0, 2, 470, 1697]
+[:key_up_raw, 113, 0, 2, 471, 1700]
+[:key_down_raw, 98, 0, 2, 472, 1739]
+[:key_up_raw, 98, 0, 2, 473, 1742]
+[:key_down_raw, 98, 0, 2, 474, 1746]
+[:key_up_raw, 98, 0, 2, 475, 1748]
+[:key_down_raw, 102, 0, 2, 476, 1866]
+[:key_up_raw, 102, 0, 2, 477, 1871]
+[:key_down_raw, 120, 0, 2, 478, 1891]
+[:key_up_raw, 120, 0, 2, 479, 1896]
+[:mouse_move, 251, 415, 2, 480, 1934]
+[:mouse_move, 255, 423, 2, 481, 1935]
+[:mouse_move, 261, 433, 2, 482, 1936]
+[:mouse_move, 277, 463, 2, 483, 1937]
+[:mouse_move, 288, 481, 2, 484, 1938]
+[:mouse_move, 302, 510, 2, 485, 1939]
+[:mouse_move, 310, 526, 2, 486, 1940]
+[:mouse_move, 323, 551, 2, 487, 1941]
+[:mouse_move, 324, 554, 2, 488, 1942]
+[:mouse_move, 327, 562, 2, 489, 1943]
+[:mouse_move, 328, 565, 2, 490, 1944]
+[:mouse_move, 328, 567, 2, 491, 1945]
+[:mouse_move, 328, 566, 2, 492, 1949]
+[:mouse_move, 326, 564, 2, 493, 1950]
+[:mouse_move, 325, 562, 2, 494, 1951]
+[:mouse_move, 323, 556, 2, 495, 1952]
+[:mouse_move, 323, 553, 2, 496, 1953]
+[:mouse_move, 321, 549, 2, 497, 1954]
+[:mouse_move, 320, 545, 2, 498, 1955]
+[:mouse_move, 319, 540, 2, 499, 1956]
+[:mouse_move, 319, 538, 2, 500, 1957]
+[:mouse_move, 319, 535, 2, 501, 1958]
+[:mouse_move, 319, 534, 2, 502, 1959]
+[:mouse_move, 319, 533, 2, 503, 2199]
+[:mouse_move, 318, 533, 2, 504, 2201]
+[:mouse_move, 317, 531, 2, 505, 2207]
+[:mouse_move, 316, 530, 2, 506, 2208]
+[:mouse_move, 314, 527, 2, 507, 2209]
+[:mouse_move, 311, 522, 2, 508, 2210]
+[:mouse_move, 300, 505, 2, 509, 2211]
+[:mouse_move, 293, 492, 2, 510, 2212]
+[:mouse_move, 272, 455, 2, 511, 2213]
+[:key_down_raw, 100, 0, 2, 512, 2214]
+[:mouse_move, 259, 434, 2, 513, 2214]
+[:mouse_move, 243, 403, 2, 514, 2215]
+[:mouse_move, 232, 382, 2, 515, 2216]
+[:mouse_move, 221, 355, 2, 516, 2217]
+[:mouse_move, 220, 351, 2, 517, 2218]
+[:mouse_move, 218, 344, 2, 518, 2219]
+[:mouse_move, 216, 335, 2, 519, 2220]
+[:mouse_move, 216, 332, 2, 520, 2221]
+[:mouse_move, 216, 327, 2, 521, 2222]
+[:key_down_raw, 100, 0, 2, 522, 2229]
+[:key_down_raw, 100, 0, 2, 523, 2231]
+[:key_down_raw, 100, 0, 2, 524, 2233]
+[:key_down_raw, 100, 0, 2, 525, 2235]
+[:key_down_raw, 100, 0, 2, 526, 2237]
+[:mouse_button_pressed, 1, 0, 1, 527, 2238]
+[:key_down_raw, 100, 0, 2, 528, 2239]
+[:mouse_move, 216, 325, 2, 529, 2239]
+[:mouse_move, 216, 315, 2, 530, 2240]
+[:key_down_raw, 100, 0, 2, 531, 2241]
+[:mouse_move, 216, 305, 2, 532, 2241]
+[:mouse_move, 219, 281, 2, 533, 2242]
+[:key_down_raw, 100, 0, 2, 534, 2243]
+[:mouse_move, 230, 234, 2, 535, 2243]
+[:mouse_move, 231, 228, 2, 536, 2243]
+[:key_down_raw, 100, 0, 2, 537, 2244]
+[:mouse_move, 233, 223, 2, 538, 2244]
+[:mouse_move, 237, 214, 2, 539, 2245]
+[:mouse_move, 244, 207, 2, 540, 2246]
+[:key_down_raw, 100, 0, 2, 541, 2246]
+[:mouse_move, 247, 204, 2, 542, 2247]
+[:mouse_move, 254, 199, 2, 543, 2248]
+[:key_down_raw, 100, 0, 2, 544, 2248]
+[:mouse_move, 257, 197, 2, 545, 2249]
+[:mouse_move, 262, 194, 2, 546, 2250]
+[:key_down_raw, 100, 0, 2, 547, 2250]
+[:mouse_move, 264, 194, 2, 548, 2251]
+[:mouse_move, 268, 193, 2, 549, 2252]
+[:key_down_raw, 100, 0, 2, 550, 2252]
+[:mouse_move, 269, 193, 2, 551, 2253]
+[:mouse_move, 271, 193, 2, 552, 2254]
+[:key_down_raw, 100, 0, 2, 553, 2254]
+[:mouse_move, 272, 195, 2, 554, 2255]
+[:mouse_move, 274, 202, 2, 555, 2256]
+[:key_down_raw, 100, 0, 2, 556, 2256]
+[:mouse_move, 275, 208, 2, 557, 2257]
+[:mouse_move, 276, 225, 2, 558, 2258]
+[:key_down_raw, 100, 0, 2, 559, 2258]
+[:mouse_move, 275, 234, 2, 560, 2259]
+[:mouse_move, 270, 247, 2, 561, 2260]
+[:key_down_raw, 100, 0, 2, 562, 2260]
+[:mouse_move, 265, 255, 2, 563, 2261]
+[:mouse_move, 254, 270, 2, 564, 2262]
+[:key_down_raw, 100, 0, 2, 565, 2262]
+[:mouse_move, 248, 275, 2, 566, 2263]
+[:mouse_move, 235, 285, 2, 567, 2264]
+[:key_down_raw, 100, 0, 2, 568, 2264]
+[:mouse_move, 232, 287, 2, 569, 2265]
+[:mouse_move, 223, 291, 2, 570, 2266]
+[:key_down_raw, 100, 0, 2, 571, 2266]
+[:mouse_move, 219, 293, 2, 572, 2267]
+[:mouse_move, 214, 295, 2, 573, 2268]
+[:key_down_raw, 100, 0, 2, 574, 2268]
+[:mouse_move, 212, 296, 2, 575, 2269]
+[:mouse_move, 210, 297, 2, 576, 2270]
+[:key_down_raw, 100, 0, 2, 577, 2270]
+[:mouse_move, 208, 297, 2, 578, 2270]
+[:mouse_move, 208, 298, 2, 579, 2271]
+[:mouse_move, 207, 298, 2, 580, 2272]
+[:key_down_raw, 100, 0, 2, 581, 2272]
+[:mouse_move, 206, 298, 2, 582, 2273]
+[:key_down_raw, 100, 0, 2, 583, 2274]
+[:mouse_move, 206, 297, 2, 584, 2276]
+[:key_down_raw, 100, 0, 2, 585, 2276]
+[:mouse_move, 207, 291, 2, 586, 2276]
+[:mouse_move, 209, 285, 2, 587, 2277]
+[:mouse_move, 213, 271, 2, 588, 2278]
+[:key_down_raw, 100, 0, 2, 589, 2278]
+[:mouse_move, 215, 265, 2, 590, 2278]
+[:mouse_move, 221, 251, 2, 591, 2279]
+[:mouse_move, 227, 238, 2, 592, 2280]
+[:key_down_raw, 100, 0, 2, 593, 2280]
+[:mouse_move, 231, 228, 2, 594, 2280]
+[:mouse_move, 235, 219, 2, 595, 2281]
+[:mouse_move, 240, 211, 2, 596, 2282]
+[:key_down_raw, 100, 0, 2, 597, 2282]
+[:mouse_move, 247, 203, 2, 598, 2283]
+[:mouse_move, 250, 199, 2, 599, 2284]
+[:key_down_raw, 100, 0, 2, 600, 2284]
+[:mouse_move, 259, 194, 2, 601, 2285]
+[:mouse_move, 264, 193, 2, 602, 2286]
+[:key_down_raw, 100, 0, 2, 603, 2286]
+[:mouse_move, 276, 192, 2, 604, 2287]
+[:mouse_move, 281, 192, 2, 605, 2288]
+[:key_down_raw, 100, 0, 2, 606, 2288]
+[:mouse_move, 289, 194, 2, 607, 2289]
+[:mouse_move, 294, 194, 2, 608, 2290]
+[:key_down_raw, 100, 0, 2, 609, 2290]
+[:mouse_move, 301, 197, 2, 610, 2291]
+[:key_down_raw, 100, 0, 2, 611, 2292]
+[:mouse_move, 305, 199, 2, 612, 2292]
+[:mouse_move, 308, 201, 2, 613, 2293]
+[:key_down_raw, 100, 0, 2, 614, 2294]
+[:mouse_move, 310, 201, 2, 615, 2294]
+[:mouse_move, 311, 202, 2, 616, 2295]
+[:mouse_move, 312, 203, 2, 617, 2296]
+[:key_down_raw, 100, 0, 2, 618, 2296]
+[:mouse_move, 312, 205, 2, 619, 2298]
+[:key_down_raw, 100, 0, 2, 620, 2298]
+[:mouse_move, 312, 207, 2, 621, 2299]
+[:mouse_move, 310, 216, 2, 622, 2300]
+[:key_down_raw, 100, 0, 2, 623, 2300]
+[:mouse_move, 306, 224, 2, 624, 2301]
+[:mouse_move, 298, 235, 2, 625, 2302]
+[:key_down_raw, 100, 0, 2, 626, 2302]
+[:mouse_move, 295, 238, 2, 627, 2303]
+[:mouse_move, 286, 247, 2, 628, 2304]
+[:key_down_raw, 100, 0, 2, 629, 2304]
+[:mouse_move, 280, 252, 2, 630, 2305]
+[:mouse_move, 270, 261, 2, 631, 2306]
+[:key_down_raw, 100, 0, 2, 632, 2306]
+[:mouse_move, 267, 263, 2, 633, 2307]
+[:mouse_move, 258, 274, 2, 634, 2308]
+[:key_down_raw, 100, 0, 2, 635, 2308]
+[:mouse_move, 254, 279, 2, 636, 2309]
+[:mouse_move, 245, 295, 2, 637, 2310]
+[:key_down_raw, 100, 0, 2, 638, 2310]
+[:mouse_move, 242, 303, 2, 639, 2311]
+[:mouse_move, 240, 315, 2, 640, 2312]
+[:key_down_raw, 100, 0, 2, 641, 2312]
+[:mouse_move, 238, 323, 2, 642, 2313]
+[:mouse_move, 238, 335, 2, 643, 2314]
+[:key_down_raw, 100, 0, 2, 644, 2314]
+[:mouse_move, 238, 338, 2, 645, 2315]
+[:mouse_move, 238, 345, 2, 646, 2316]
+[:key_down_raw, 100, 0, 2, 647, 2316]
+[:mouse_move, 241, 349, 2, 648, 2317]
+[:mouse_move, 247, 350, 2, 649, 2318]
+[:key_down_raw, 100, 0, 2, 650, 2318]
+[:mouse_move, 252, 350, 2, 651, 2319]
+[:mouse_move, 260, 344, 2, 652, 2320]
+[:key_down_raw, 100, 0, 2, 653, 2320]
+[:mouse_move, 264, 340, 2, 654, 2321]
+[:mouse_move, 266, 337, 2, 655, 2322]
+[:key_down_raw, 100, 0, 2, 656, 2322]
+[:mouse_move, 268, 334, 2, 657, 2322]
+[:mouse_move, 270, 331, 2, 658, 2323]
+[:mouse_move, 271, 329, 2, 659, 2324]
+[:key_down_raw, 100, 0, 2, 660, 2324]
+[:mouse_move, 272, 326, 2, 661, 2324]
+[:mouse_move, 272, 323, 2, 662, 2325]
+[:mouse_move, 272, 319, 2, 663, 2326]
+[:key_down_raw, 100, 0, 2, 664, 2326]
+[:mouse_move, 271, 317, 2, 665, 2326]
+[:mouse_move, 266, 312, 2, 666, 2327]
+[:mouse_move, 262, 310, 2, 667, 2328]
+[:key_down_raw, 100, 0, 2, 668, 2328]
+[:mouse_move, 256, 308, 2, 669, 2328]
+[:mouse_move, 249, 306, 2, 670, 2329]
+[:mouse_move, 243, 305, 2, 671, 2330]
+[:key_down_raw, 100, 0, 2, 672, 2330]
+[:mouse_move, 236, 304, 2, 673, 2330]
+[:mouse_move, 231, 304, 2, 674, 2331]
+[:mouse_move, 226, 306, 2, 675, 2332]
+[:key_down_raw, 100, 0, 2, 676, 2332]
+[:mouse_move, 219, 309, 2, 677, 2332]
+[:mouse_move, 211, 315, 2, 678, 2333]
+[:mouse_move, 206, 323, 2, 679, 2334]
+[:key_down_raw, 100, 0, 2, 680, 2334]
+[:mouse_move, 200, 332, 2, 681, 2334]
+[:mouse_move, 197, 340, 2, 682, 2335]
+[:mouse_move, 195, 344, 2, 683, 2336]
+[:key_down_raw, 100, 0, 2, 684, 2336]
+[:mouse_move, 190, 353, 2, 685, 2336]
+[:mouse_move, 190, 358, 2, 686, 2337]
+[:mouse_move, 188, 363, 2, 687, 2338]
+[:key_down_raw, 100, 0, 2, 688, 2338]
+[:mouse_move, 187, 376, 2, 689, 2339]
+[:mouse_move, 187, 379, 2, 690, 2340]
+[:key_down_raw, 100, 0, 2, 691, 2340]
+[:mouse_move, 195, 388, 2, 692, 2341]
+[:mouse_move, 201, 391, 2, 693, 2342]
+[:key_down_raw, 100, 0, 2, 694, 2342]
+[:mouse_move, 212, 395, 2, 695, 2343]
+[:mouse_move, 218, 395, 2, 696, 2344]
+[:key_down_raw, 100, 0, 2, 697, 2344]
+[:mouse_move, 229, 395, 2, 698, 2345]
+[:mouse_move, 234, 393, 2, 699, 2346]
+[:key_down_raw, 100, 0, 2, 700, 2346]
+[:mouse_move, 241, 385, 2, 701, 2347]
+[:key_down_raw, 100, 0, 2, 702, 2348]
+[:mouse_move, 245, 380, 2, 703, 2348]
+[:mouse_move, 247, 374, 2, 704, 2349]
+[:mouse_move, 251, 366, 2, 705, 2350]
+[:key_down_raw, 100, 0, 2, 706, 2350]
+[:mouse_move, 252, 363, 2, 707, 2351]
+[:mouse_move, 254, 359, 2, 708, 2352]
+[:key_down_raw, 100, 0, 2, 709, 2352]
+[:mouse_move, 254, 357, 2, 710, 2353]
+[:mouse_move, 255, 356, 2, 711, 2354]
+[:key_down_raw, 100, 0, 2, 712, 2354]
+[:mouse_move, 256, 357, 2, 713, 2356]
+[:key_down_raw, 100, 0, 2, 714, 2356]
+[:mouse_move, 257, 360, 2, 715, 2357]
+[:mouse_move, 259, 373, 2, 716, 2358]
+[:key_down_raw, 100, 0, 2, 717, 2358]
+[:mouse_move, 260, 382, 2, 718, 2359]
+[:mouse_move, 263, 395, 2, 719, 2360]
+[:key_down_raw, 100, 0, 2, 720, 2360]
+[:mouse_move, 263, 402, 2, 721, 2361]
+[:mouse_move, 264, 414, 2, 722, 2362]
+[:key_down_raw, 100, 0, 2, 723, 2362]
+[:mouse_move, 265, 420, 2, 724, 2363]
+[:mouse_move, 266, 428, 2, 725, 2364]
+[:key_down_raw, 100, 0, 2, 726, 2364]
+[:mouse_move, 267, 431, 2, 727, 2365]
+[:mouse_move, 270, 434, 2, 728, 2366]
+[:key_down_raw, 100, 0, 2, 729, 2366]
+[:mouse_move, 273, 437, 2, 730, 2367]
+[:mouse_move, 279, 437, 2, 731, 2368]
+[:key_down_raw, 100, 0, 2, 732, 2368]
+[:mouse_move, 281, 437, 2, 733, 2369]
+[:mouse_move, 290, 436, 2, 734, 2370]
+[:key_down_raw, 100, 0, 2, 735, 2370]
+[:mouse_move, 294, 434, 2, 736, 2371]
+[:mouse_move, 301, 431, 2, 737, 2372]
+[:key_down_raw, 100, 0, 2, 738, 2372]
+[:mouse_move, 304, 431, 2, 739, 2373]
+[:mouse_move, 312, 428, 2, 740, 2374]
+[:key_down_raw, 100, 0, 2, 741, 2374]
+[:mouse_move, 318, 427, 2, 742, 2375]
+[:mouse_move, 325, 425, 2, 743, 2376]
+[:key_down_raw, 100, 0, 2, 744, 2376]
+[:mouse_move, 327, 424, 2, 745, 2377]
+[:mouse_move, 330, 423, 2, 746, 2378]
+[:key_down_raw, 100, 0, 2, 747, 2378]
+[:mouse_move, 333, 422, 2, 748, 2378]
+[:mouse_move, 335, 422, 2, 749, 2379]
+[:mouse_move, 338, 420, 2, 750, 2380]
+[:key_down_raw, 100, 0, 2, 751, 2380]
+[:mouse_move, 340, 420, 2, 752, 2380]
+[:mouse_move, 344, 419, 2, 753, 2381]
+[:mouse_move, 347, 419, 2, 754, 2382]
+[:key_down_raw, 100, 0, 2, 755, 2382]
+[:mouse_move, 351, 419, 2, 756, 2382]
+[:mouse_move, 353, 419, 2, 757, 2383]
+[:mouse_move, 358, 419, 2, 758, 2384]
+[:key_down_raw, 100, 0, 2, 759, 2384]
+[:mouse_move, 360, 419, 2, 760, 2384]
+[:mouse_move, 365, 419, 2, 761, 2385]
+[:mouse_move, 367, 419, 2, 762, 2386]
+[:key_down_raw, 100, 0, 2, 763, 2386]
+[:mouse_move, 373, 420, 2, 764, 2386]
+[:mouse_move, 378, 420, 2, 765, 2387]
+[:mouse_move, 382, 420, 2, 766, 2388]
+[:key_down_raw, 100, 0, 2, 767, 2388]
+[:mouse_move, 389, 419, 2, 768, 2389]
+[:mouse_move, 394, 417, 2, 769, 2390]
+[:key_down_raw, 100, 0, 2, 770, 2390]
+[:mouse_move, 401, 411, 2, 771, 2391]
+[:mouse_move, 404, 407, 2, 772, 2392]
+[:key_down_raw, 100, 0, 2, 773, 2392]
+[:mouse_move, 411, 398, 2, 774, 2393]
+[:mouse_move, 412, 394, 2, 775, 2394]
+[:key_down_raw, 100, 0, 2, 776, 2394]
+[:mouse_move, 415, 387, 2, 777, 2395]
+[:mouse_move, 416, 383, 2, 778, 2396]
+[:key_down_raw, 100, 0, 2, 779, 2396]
+[:mouse_move, 418, 375, 2, 780, 2397]
+[:mouse_move, 418, 370, 2, 781, 2398]
+[:key_down_raw, 100, 0, 2, 782, 2398]
+[:mouse_move, 419, 359, 2, 783, 2399]
+[:mouse_move, 419, 354, 2, 784, 2400]
+[:key_down_raw, 100, 0, 2, 785, 2400]
+[:mouse_move, 419, 348, 2, 786, 2401]
+[:mouse_move, 419, 344, 2, 787, 2402]
+[:key_down_raw, 100, 0, 2, 788, 2402]
+[:mouse_move, 419, 341, 2, 789, 2403]
+[:mouse_move, 419, 338, 2, 790, 2404]
+[:key_down_raw, 100, 0, 2, 791, 2404]
+[:mouse_move, 419, 337, 2, 792, 2405]
+[:mouse_move, 417, 336, 2, 793, 2406]
+[:key_down_raw, 100, 0, 2, 794, 2406]
+[:mouse_move, 415, 336, 2, 795, 2408]
+[:key_down_raw, 100, 0, 2, 796, 2408]
+[:mouse_move, 414, 336, 2, 797, 2409]
+[:key_down_raw, 100, 0, 2, 798, 2410]
+[:key_down_raw, 100, 0, 2, 799, 2412]
+[:mouse_move, 414, 334, 2, 800, 2414]
+[:key_down_raw, 100, 0, 2, 801, 2414]
+[:mouse_move, 416, 334, 2, 802, 2416]
+[:key_down_raw, 100, 0, 2, 803, 2416]
+[:mouse_move, 418, 339, 2, 804, 2418]
+[:key_down_raw, 100, 0, 2, 805, 2418]
+[:mouse_move, 419, 347, 2, 806, 2419]
+[:mouse_move, 420, 366, 2, 807, 2420]
+[:key_down_raw, 100, 0, 2, 808, 2420]
+[:mouse_move, 420, 383, 2, 809, 2421]
+[:mouse_move, 412, 412, 2, 810, 2422]
+[:key_down_raw, 100, 0, 2, 811, 2422]
+[:mouse_move, 409, 420, 2, 812, 2423]
+[:mouse_move, 402, 433, 2, 813, 2424]
+[:key_down_raw, 100, 0, 2, 814, 2424]
+[:mouse_move, 399, 439, 2, 815, 2425]
+[:mouse_move, 395, 446, 2, 816, 2426]
+[:key_down_raw, 100, 0, 2, 817, 2426]
+[:mouse_move, 393, 448, 2, 818, 2427]
+[:mouse_move, 390, 451, 2, 819, 2428]
+[:key_down_raw, 100, 0, 2, 820, 2428]
+[:mouse_move, 389, 452, 2, 821, 2429]
+[:mouse_move, 387, 453, 2, 822, 2430]
+[:key_down_raw, 100, 0, 2, 823, 2430]
+[:mouse_move, 385, 453, 2, 824, 2431]
+[:mouse_move, 383, 453, 2, 825, 2432]
+[:key_down_raw, 100, 0, 2, 826, 2432]
+[:mouse_move, 381, 453, 2, 827, 2432]
+[:mouse_move, 379, 452, 2, 828, 2433]
+[:mouse_move, 377, 451, 2, 829, 2434]
+[:key_down_raw, 100, 0, 2, 830, 2434]
+[:mouse_move, 376, 449, 2, 831, 2434]
+[:mouse_move, 374, 445, 2, 832, 2435]
+[:mouse_move, 373, 442, 2, 833, 2436]
+[:key_down_raw, 100, 0, 2, 834, 2436]
+[:mouse_move, 371, 437, 2, 835, 2436]
+[:mouse_move, 370, 435, 2, 836, 2437]
+[:mouse_move, 370, 432, 2, 837, 2438]
+[:key_down_raw, 100, 0, 2, 838, 2438]
+[:mouse_move, 370, 428, 2, 839, 2438]
+[:mouse_move, 370, 424, 2, 840, 2439]
+[:mouse_move, 370, 419, 2, 841, 2440]
+[:key_down_raw, 100, 0, 2, 842, 2440]
+[:mouse_move, 370, 417, 2, 843, 2440]
+[:mouse_move, 371, 414, 2, 844, 2441]
+[:mouse_move, 373, 411, 2, 845, 2442]
+[:key_down_raw, 100, 0, 2, 846, 2442]
+[:mouse_move, 376, 409, 2, 847, 2442]
+[:mouse_move, 378, 407, 2, 848, 2443]
+[:mouse_move, 380, 406, 2, 849, 2444]
+[:key_down_raw, 100, 0, 2, 850, 2444]
+[:mouse_move, 383, 405, 2, 851, 2444]
+[:mouse_move, 385, 405, 2, 852, 2445]
+[:mouse_move, 386, 404, 2, 853, 2446]
+[:key_down_raw, 100, 0, 2, 854, 2446]
+[:mouse_move, 388, 403, 2, 855, 2447]
+[:mouse_move, 389, 403, 2, 856, 2448]
+[:key_down_raw, 100, 0, 2, 857, 2448]
+[:key_down_raw, 100, 0, 2, 858, 2450]
+[:key_down_raw, 100, 0, 2, 859, 2452]
+[:key_down_raw, 100, 0, 2, 860, 2454]
+[:key_down_raw, 100, 0, 2, 861, 2456]
+[:mouse_move, 389, 404, 2, 862, 2458]
+[:key_down_raw, 100, 0, 2, 863, 2458]
+[:key_down_raw, 100, 0, 2, 864, 2460]
+[:key_down_raw, 100, 0, 2, 865, 2462]
+[:key_down_raw, 100, 0, 2, 866, 2464]
+[:key_down_raw, 100, 0, 2, 867, 2466]
+[:key_up_raw, 100, 0, 2, 868, 2468]
+[:mouse_button_up, 1, 0, 1, 869, 2473]
+[:mouse_move, 276, 362, 2, 870, 2473]
+[:mouse_move, 239, 339, 2, 871, 2474]
+[:mouse_move, 210, 314, 2, 872, 2475]
+[:mouse_move, 186, 291, 2, 873, 2476]
+[:mouse_move, 167, 265, 2, 874, 2477]
+[:mouse_move, 159, 253, 2, 875, 2478]
+[:mouse_move, 154, 245, 2, 876, 2479]
+[:mouse_move, 150, 236, 2, 877, 2480]
+[:mouse_move, 149, 234, 2, 878, 2481]
+[:mouse_move, 149, 232, 2, 879, 2482]
+[:mouse_move, 152, 232, 2, 880, 2484]
+[:mouse_move, 154, 233, 2, 881, 2485]
+[:mouse_move, 157, 236, 2, 882, 2486]
+[:mouse_move, 159, 238, 2, 883, 2487]
+[:mouse_move, 163, 242, 2, 884, 2488]
+[:mouse_move, 165, 245, 2, 885, 2489]
+[:mouse_move, 170, 253, 2, 886, 2490]
+[:mouse_move, 173, 257, 2, 887, 2491]
+[:mouse_move, 180, 270, 2, 888, 2492]
+[:mouse_move, 183, 275, 2, 889, 2493]
+[:mouse_move, 190, 287, 2, 890, 2494]
+[:mouse_move, 192, 290, 2, 891, 2495]
+[:mouse_move, 199, 303, 2, 892, 2496]
+[:mouse_move, 203, 307, 2, 893, 2497]
+[:mouse_move, 206, 313, 2, 894, 2498]
+[:mouse_move, 207, 317, 2, 895, 2499]
+[:mouse_move, 209, 321, 2, 896, 2500]
+[:mouse_move, 210, 322, 2, 897, 2501]
+[:mouse_move, 211, 324, 2, 898, 2502]
+[:mouse_move, 212, 325, 2, 899, 2504]
+[:mouse_move, 213, 326, 2, 900, 2505]
+[:mouse_move, 214, 326, 2, 901, 2510]
+[:key_down_raw, 96, 0, 2, 902, 2711]
+[:key_up_raw, 96, 0, 2, 903, 2716]
+[:key_down_raw, 13, 0, 2, 904, 2856]
diff --git a/samples/99_sample_sprite_animation_creator/sprites/square-blue.png b/samples/99_sample_sprite_animation_creator/sprites/square-blue.png
new file mode 100644
index 0000000..b840849
--- /dev/null
+++ b/samples/99_sample_sprite_animation_creator/sprites/square-blue.png
Binary files differ
diff --git a/samples/99_sample_sprite_animation_creator/sprites/square-white.png b/samples/99_sample_sprite_animation_creator/sprites/square-white.png
new file mode 100644
index 0000000..378c565
--- /dev/null
+++ b/samples/99_sample_sprite_animation_creator/sprites/square-white.png
Binary files differ