diff options
| author | Amir Rajan <[email protected]> | 2020-10-13 00:45:16 -0500 |
|---|---|---|
| committer | Amir Rajan <[email protected]> | 2020-10-13 00:48:54 -0500 |
| commit | 05cbef7fb8224332795e5685be499d81d20e7d93 (patch) | |
| tree | 13ec5f1755c2f45618741f2f016ed8729dbedd41 | |
| parent | abad948c1154d88d79b9f891e3b7315540e0b0a3 (diff) | |
| download | dragonruby-game-toolkit-contrib-05cbef7fb8224332795e5685be499d81d20e7d93.tar.gz dragonruby-game-toolkit-contrib-05cbef7fb8224332795e5685be499d81d20e7d93.zip | |
Synced with 1.26.
49 files changed, 7194 insertions, 2108 deletions
diff --git a/docs/docs.html b/docs/docs.html index b21caff..0b76535 100644 --- a/docs/docs.html +++ b/docs/docs.html @@ -1,7 +1,7 @@ <html> <head> <title>DragonRuby Game Toolkit Documentation</title> - <link href="docs.css?ver=1600750589" rel="stylesheet" type="text/css" media="all"> + <link href="docs.css?ver=1602116477" rel="stylesheet" type="text/css" media="all"> </head> <body> <div id='toc'> @@ -14,20 +14,22 @@ <li><a href='#--getting-started-tutorial'>Getting Started Tutorial</a></li> <li><a href='#--important--go-through-all-of-the-sample-apps!-study-them-thoroughly!!-no-really,-you-should-definitely-do-this!'>IMPORTANT: Go through all of the sample apps! Study them thoroughly!! No really, you should definitely do this!</a></li> <li><a href='#--deploying-to-itch.io'>Deploying To Itch.io</a></li> +<li><a href='#--deploying-to-mobile-devices'>Deploying To Mobile Devices</a></li> <li><a href='#--dragonruby's-philosophy'>DragonRuby's Philosophy</a></li> -<li><a href='#--how-to-determine-what-frame-you-are-on'>How To Determine What Frame You Are On</a></li> -<li><a href='#--how-to-get-current-framerate'>How To Get Current Framerate</a></li> -<li><a href='#--how-to-render-a-sprite-using-an-array'>How To Render A Sprite Using An Array</a></li> -<li><a href='#--more-sprite-properties-as-an-array'>More Sprite Properties As An Array</a></li> -<li><a href='#--different-sprite-representations'>Different Sprite Representations</a></li> -<li><a href='#--how-to-render-a-label'>How To Render A Label</a></li> -<li><a href='#--a-colored-label'>A Colored Label</a></li> -<li><a href='#--extended-label-properties'>Extended Label Properties</a></li> -<li><a href='#--rendering-a-label-as-a--hash-'>Rendering A Label As A <code>Hash</code></a></li> -<li><a href='#--getting-the-size-of-a-piece-of-text'>Getting The Size Of A Piece Of Text</a></li> -<li><a href='#--how-to-play-a-sound'>How To Play A Sound</a></li> -<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='#--cheatsheet--how-to-determine-what-frame-you-are-on'>CHEATSHEET: How To Determine What Frame You Are On</a></li> +<li><a href='#--cheatsheet--how-to-get-current-framerate'>CHEATSHEET: How To Get Current Framerate</a></li> +<li><a href='#--cheatsheet--how-to-render-a-sprite-using-an-array'>CHEATSHEET: How To Render A Sprite Using An Array</a></li> +<li><a href='#--cheatsheet--more-sprite-properties-as-an-array'>CHEATSHEET: More Sprite Properties As An Array</a></li> +<li><a href='#--cheatsheet--different-sprite-representations'>CHEATSHEET: Different Sprite Representations</a></li> +<li><a href='#--cheatsheet--how-to-render-a-label'>CHEATSHEET: How To Render A Label</a></li> +<li><a href='#--cheatsheet--a-colored-label'>CHEATSHEET: A Colored Label</a></li> +<li><a href='#--cheatsheet--extended-label-properties'>CHEATSHEET: Extended Label Properties</a></li> +<li><a href='#--cheatsheet--rendering-a-label-as-a--hash-'>CHEATSHEET: Rendering A Label As A <code>Hash</code></a></li> +<li><a href='#--cheatsheet--getting-the-size-of-a-piece-of-text'>CHEATSHEET: Getting The Size Of A Piece Of Text</a></li> +<li><a href='#--cheatsheet--how-to-play-a-sound'>CHEATSHEET: How To Play A Sound</a></li> +<li><a href='#--cheatsheet--using--args.state--to-store-your-game-state'>CHEATSHEET: Using <code>args.state</code> To Store Your Game State</a></li> +<li><a href='#--cheatsheet--troubleshoot-performance'>CHEATSHEET: Troubleshoot Performance</a></li> <li><a href='#--docs---gtk--runtime-'>DOCS: <code>GTK::Runtime</code></a></li> <li><a href='#--docs---gtk--runtime#reset-'>DOCS: <code>GTK::Runtime#reset</code></a></li> <li><a href='#--docs---gtk--runtime#calcstringbox-'>DOCS: <code>GTK::Runtime#calcstringbox</code></a></li> @@ -94,7 +96,7 @@ <li><a href='#--mouse---coordinate-systems---main.rb'>Mouse - Coordinate Systems - main.rb</a></li> <li><a href='#--save-load---save-load-game---main.rb'>Save Load - Save Load Game - main.rb</a></li> <li><a href='#--advanced-rendering---simple-render-targets---main.rb'>Advanced Rendering - Simple Render Targets - main.rb</a></li> -<li><a href='#--advanced-rendering---render-targets-with-alphas---main.rb'>Advanced Rendering - Render Targets With Alphas - main.rb</a></li> +<li><a href='#--advanced-rendering---render-targets-with-tile-manipulation---main.rb'>Advanced Rendering - Render Targets With Tile Manipulation - main.rb</a></li> <li><a href='#--advanced-rendering---render-target-viewports---main.rb'>Advanced Rendering - Render Target Viewports - main.rb</a></li> <li><a href='#--advanced-rendering---render-primitive-hierarchies---main.rb'>Advanced Rendering - Render Primitive Hierarchies - main.rb</a></li> <li><a href='#--advanced-rendering---render-primitives-as-hash---main.rb'>Advanced Rendering - Render Primitives As Hash - main.rb</a></li> @@ -529,6 +531,21 @@ 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> +<h1 id='--deploying-to-mobile-devices'>Deploying To Mobile Devices</h1> +<p> +If you have a Pro subscription, you also have the capability to deploy to mobile devices. +</p> +<p> +To deploy to iOS, you need to have a Mac running MacOS Catalina, an iOS device, and an active/paid Developer Account with Apple. From the Console type: <code>$wizards.ios.start</code> and you will be guided through the deployment process. +</p> +<p> +To deploy to Android, you need to have an Android emulator/device, and a environment that is able to run Android SDK. <code>dragonruby-publish</code> will create an APK for you. From there, you can sign the APK and install it to your device. The signing and installation procedure varies from OS to OS. Here's an example of what the command might look like: +</p> +<pre>> adb logcat -e mygame # you'll want to run this in a separate terminal +> keytool -genkey -v -keystore mygame.keystore -alias mygame -keyalg RSA -keysize 2048 -validity 10000 +> apksigner sign --ks mygame.keystore mygame-android.apk +> adb install mygame-android.apk +</pre> <h1 id='--dragonruby's-philosophy'>DragonRuby's Philosophy</h1> <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. @@ -596,7 +613,236 @@ 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> -<h1 id='--how-to-determine-what-frame-you-are-on'>How To Determine What Frame You Are On</h1> +<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> +<h2>Frequently Asked Questions</h2> +<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. +</p> +<p> +Under DragonRuby LLP, we offer a number of products (with more on the way): +</p> +<ul> +<li>Game Toolkit (GTK): A 2D game engine that is compatible with modern + gaming platforms.</li> +<li>RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile + apps. <a href='http://rubymotion.com'>http://rubymotion.com</a></li> +</ul> +<p> +All of the products above leverage a shared core called DragonRuby. +</p> +<p> +NOTE: From an official branding standpoint each one of the products is suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is _one word, title cased_. +</p> +<p> +NOTE: We leave the "A DragonRuby LLP Product" off of this one because that just sounds really weird. +</p> +<p> +NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass identifier huh? +</p> +<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> +<h4>Okay... so what is the difference between a language specification and a runtime?</h4> +<p> +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> +<h4>Okay... what language specification does DragonRuby use then?</h4> +<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> +<h4>So... why another runtime?</h4> +<p> +The elevator pitch is: +</p> +<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> +<h4>What does Multilevel Cross-platform mean?</h4> +<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): +</p> +<ul> +<li>Level 1 we leverage a good portion of mRuby.</li> +<li>Level 2 consists of optimizations to mRuby we've made given that our + target platforms are well known.</li> +<li>Level 3 consists of portable C libraries and their Ruby + C-Extensions.</li> +</ul> +<p> +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 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 separation between these two worlds; and provides a means to add new platforms without going insane. +</p> +<h4>Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?</h4> +<p> +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> +<h3>How is DragonRuby different than MRI?</h3> +<p> +DragonRuby supports a subset of MRI apis. Our target is to support all of mRuby's standard lib. There are challenges to this given the number of platforms we are trying to support (specifically console). +</p> +<h4>Does DragonRuby support Gems?</h4> +<p> +DragonRuby does not support gems because that requires the installation of MRI Ruby on the developer's machine (which is a non-starter given that we want DragonRuby to be a zero dependency runtime). While this seems easy for Mac and Linux, it is much harder on Windows and Raspberry Pi. mRuby has taken the approach of having a git repository for compatible gems and we will most likely follow suite: <a href='https://github.com/mruby/mgem-list'>https://github.com/mruby/mgem-list</a>. +</p> +<h4>Does DragonRuby have a REPL/IRB?</h4> +<p> +You can use DragonRuby's Console within the game to inspect object and execute small pieces of code. For more complex pieces of code create a file called <code>repl.rb</code> and put it in <code>mygame/app/repl.rb</code>: +</p> +<ul> +<li>Any code you write in there will be executed when you change the file. You can organize different pieces of code using the <code>repl</code> method:</li> +</ul> +<pre>repl do + puts "hello world" + puts 1 + 1 +end +</pre> +<ul> +<li>If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal).</li> +<li>All <code>puts</code> statements will also be saved to <code>logs/log.txt</code>. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can <code>tail</code> this file.</li> +</ul> +<p> +4. To ignore code in <code>repl.rb</code>, instead of commenting it out, prefix <code>repl</code> with the letter <code>x</code> and it'll be ignored. +</p> +<pre>xrepl do # <------- line is prefixed with an "x" + puts "hello world" + puts 1 + 1 +end + +# This code will be executed when you save the file. +repl do + puts "Hello" +end + +repl do + puts "This code will also be executed." +end + +# use xrepl to "comment out" code +xrepl do + puts "This code will not be executed because of the x infront of repl". +end +</pre> +<h4>Does DragonRuby support <code>pry</code> or have any other debugging facilities?</h4> +<p> +<code>pry</code> is a gem that assumes you are using the MRI Runtime (which is incompatible with DragonRuby). Eventually DragonRuby will have a pry based experience that is compatible with a debugging infrastructure called LLDB. Take the time to read about LLDB as it shows the challenges in creating something that is compatible. +</p> +<p> +You can use DragonRuby's replay capabilities to troubleshoot: +</p> +<ol> +<li> DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added).</li> +<li> Use <code>./dragonruby mygame --record</code> to create a game play recording that you can use to find the exception (you can replay a recoding by executing <code>./dragonruby mygame --replay last_replay.txt</code> or through the DragonRuby Console using <code>$gtk.recording.start_replay "last_replay.txt"</code>.</li> +<li> DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: <code>./dragonruby . --eval some_ruby_file.rb --no-tick</code>.</li> +<li> Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to <code>args.outputs.debug</code> that will render on top of your game but will be ignored in a production release.</li> +<li> Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago.</li> +</ol> +<h2>Frequent Comments About Ruby as a Language Choice</h2> +<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>. +</p> +<p> +On a more serious note, Ruby's _quantity_ levels aren't what they used to be. And that's totally fine. Every one chases the new and shiny. +</p> +<p> +What really matters is _quality/maturity_. Here is the latest (StackOverflow Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies]. +</p> +<p> +Let's stop making this comment shall we? +</p> +<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> +<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 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 skill set. Email us ^_^. +</p> +<h2>Frequent Concerns</h2> +<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 severely under-staffed, and burnout from core members is rampant. +</p> +<p> +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! +</p> +<p> +If the reason above isn't sufficient, then definitely use something else. +</p> +<p> +All this being said, we do have parts of the engine open sourced on GitHub: <a href='https://github.com/dragonruby/dragonruby-game-toolkit-contrib/'>https://github.com/dragonruby/dragonruby-game-toolkit-contrib/</a> +</p> +<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. +</p> +<p> +That being said, we will _never_ put someone out financially. We have income assistance for anyone that can't afford a license to any one of our products. +</p> +<p> +You qualify for a free, unrestricted license to DragonRuby products if any of the following items pertain to you: +</p> +<ul> +<li>Your income is below $2,000.00 (USD) per month.</li> +<li>You are under 18 years of age.</li> +<li>You are a student of any type: traditional public school, home + schooling, college, bootcamp, or online.</li> +<li>You are a teacher, mentor, or parent who wants to teach a kid how to code.</li> +<li>You work/worked in public service or at a charitable organization: + for example public office, army, or any 501(c)(3) organization.</li> +</ul> +<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> +<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. +</p> +<p> +Seriously just buy it. Get a refund if you don't like it. We make it stupid easy to do so. +</p> +<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 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> +<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 partnership bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license. +</p> +<h1 id='--cheatsheet--how-to-determine-what-frame-you-are-on'>CHEATSHEET: 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> @@ -604,7 +850,7 @@ There is a property on <code>state</code> called <code>tick_count</code> that is args.outputs.labels << [10, 670, "#{args.state.tick_count}"] end </pre> -<h1 id='--how-to-get-current-framerate'>How To Get Current Framerate</h1> +<h1 id='--cheatsheet--how-to-get-current-framerate'>CHEATSHEET: 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> @@ -612,7 +858,7 @@ Current framerate is a top level property on the Game Toolkit Runtime and is acc args.outputs.labels << [10, 710, "framerate: #{args.gtk.current_framerate.round}"] end </pre> -<h1 id='--how-to-render-a-sprite-using-an-array'>How To Render A Sprite Using An Array</h1> +<h1 id='--cheatsheet--how-to-render-a-sprite-using-an-array'>CHEATSHEET: 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). </p> @@ -632,7 +878,7 @@ The following code renders a sprite with a <code>width</code> and <code>height</ ] end </pre> -<h1 id='--more-sprite-properties-as-an-array'>More Sprite Properties As An Array</h1> +<h1 id='--cheatsheet--more-sprite-properties-as-an-array'>CHEATSHEET: More Sprite Properties As An Array</h1> <p> Here are all the properties you can set on a sprite. </p> @@ -651,7 +897,7 @@ Here are all the properties you can set on a sprite. ] end </pre> -<h1 id='--different-sprite-representations'>Different Sprite Representations</h1> +<h1 id='--cheatsheet--different-sprite-representations'>CHEATSHEET: Different Sprite Representations</h1> <p> Using ordinal positioning can get a little unruly given so many properties you have control over. </p> @@ -714,7 +960,7 @@ def tick args h: 50) end </pre> -<h1 id='--how-to-render-a-label'>How To Render A Label</h1> +<h1 id='--cheatsheet--how-to-render-a-label'>CHEATSHEET: How To Render A Label</h1> <p> <code>args.outputs.labels</code> is used to render labels. </p> @@ -729,14 +975,14 @@ Here is the minimum code: args.outputs.labels << [640, 360, "I am a black label."] end </pre> -<h1 id='--a-colored-label'>A Colored Label</h1> +<h1 id='--cheatsheet--a-colored-label'>CHEATSHEET: A Colored Label</h1> <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> -<h1 id='--extended-label-properties'>Extended Label Properties</h1> +<h1 id='--cheatsheet--extended-label-properties'>CHEATSHEET: Extended Label Properties</h1> <pre>def tick args # A colored label # X Y TEXT SIZE ALIGNMENT RED GREEN BLUE ALPHA FONT FILE @@ -760,7 +1006,7 @@ 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> -<h1 id='--rendering-a-label-as-a--hash-'>Rendering A Label As A <code>Hash</code></h1> +<h1 id='--cheatsheet--rendering-a-label-as-a--hash-'>CHEATSHEET: 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> @@ -786,7 +1032,7 @@ You can add additional metadata about your game within a label, which requires y } end </pre> -<h1 id='--getting-the-size-of-a-piece-of-text'>Getting The Size Of A Piece Of Text</h1> +<h1 id='--cheatsheet--getting-the-size-of-a-piece-of-text'>CHEATSHEET: 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> @@ -805,7 +1051,7 @@ You can get the render size of any string using <code>args.gtk.calcstringbox</co ] end </pre> -<h1 id='--how-to-play-a-sound'>How To Play A Sound</h1> +<h1 id='--cheatsheet--how-to-play-a-sound'>CHEATSHEET: How To Play A Sound</h1> <p> Sounds that end <code>.wav</code> will play once: </p> @@ -836,7 +1082,7 @@ If you want to play a <code>.ogg</code> once as if it were a sound effect, you c end end </pre> -<h1 id='--using--args.state--to-store-your-game-state'>Using <code>args.state</code> To Store Your Game State</h1> +<h1 id='--cheatsheet--using--args.state--to-store-your-game-state'>CHEATSHEET: 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>. </p> @@ -870,234 +1116,29 @@ To assign a value every frame, just use the <code>=</code> operator, but _make s ] end </pre> -<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> -<h2>Frequently Asked Questions</h2> -<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. -</p> -<p> -Under DragonRuby LLP, we offer a number of products (with more on the way): -</p> -<ul> -<li>Game Toolkit (GTK): A 2D game engine that is compatible with modern - gaming platforms.</li> -<li>RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile - apps. <a href='http://rubymotion.com'>http://rubymotion.com</a></li> -</ul> -<p> -All of the products above leverage a shared core called DragonRuby. -</p> -<p> -NOTE: From an official branding standpoint each one of the products is suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is _one word, title cased_. -</p> -<p> -NOTE: We leave the "A DragonRuby LLP Product" off of this one because that just sounds really weird. -</p> -<p> -NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass identifier huh? -</p> -<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> -<h4>Okay... so what is the difference between a language specification and a runtime?</h4> -<p> -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> -<h4>Okay... what language specification does DragonRuby use then?</h4> -<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> -<h4>So... why another runtime?</h4> -<p> -The elevator pitch is: -</p> -<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> -<h4>What does Multilevel Cross-platform mean?</h4> -<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): -</p> -<ul> -<li>Level 1 we leverage a good portion of mRuby.</li> -<li>Level 2 consists of optimizations to mRuby we've made given that our - target platforms are well known.</li> -<li>Level 3 consists of portable C libraries and their Ruby - C-Extensions.</li> -</ul> -<p> -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 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 separation between these two worlds; and provides a means to add new platforms without going insane. -</p> -<h4>Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby?</h4> -<p> -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> -<h3>How is DragonRuby different than MRI?</h3> -<p> -DragonRuby supports a subset of MRI apis. Our target is to support all of mRuby's standard lib. There are challenges to this given the number of platforms we are trying to support (specifically console). -</p> -<h4>Does DragonRuby support Gems?</h4> -<p> -DragonRuby does not support gems because that requires the installation of MRI Ruby on the developer's machine (which is a non-starter given that we want DragonRuby to be a zero dependency runtime). While this seems easy for Mac and Linux, it is much harder on Windows and Raspberry Pi. mRuby has taken the approach of having a git repository for compatible gems and we will most likely follow suite: <a href='https://github.com/mruby/mgem-list'>https://github.com/mruby/mgem-list</a>. -</p> -<h4>Does DragonRuby have a REPL/IRB?</h4> -<p> -You can use DragonRuby's Console within the game to inspect object and execute small pieces of code. For more complex pieces of code create a file called <code>repl.rb</code> and put it in <code>mygame/app/repl.rb</code>: -</p> -<ul> -<li>Any code you write in there will be executed when you change the file. You can organize different pieces of code using the <code>repl</code> method:</li> -</ul> -<pre>repl do - puts "hello world" - puts 1 + 1 +<h1 id='--cheatsheet--troubleshoot-performance'>CHEATSHEET: Troubleshoot Performance</h1> +<ol> +<li> If you're using <code>Array</code>s for your primitives (<code>args.outputs.sprites << []</code>), use <code>Hash</code> instead (<code>args.outputs.sprites << { x: ... }</code>).</li> +<li> If you're using <code>Entity</code> for your primitives (<code>args.outputs.sprites << args.state.new_entity</code>), use <code>StrictEntity</code> instead (<code>args.outputs.sprites << args.state.new_entity_strict</code>).</li> +<li> Use <code>.each</code> instead of <code>.map</code> if you don't care about the return value.</li> +<li> When concatenating primitives to outputs, do them in bulk. Instead of:</li> +</ol> +<pre>args.state.bullets.each do |bullet| + args.outputs.sprites << bullet.sprite end </pre> -<ul> -<li>If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal).</li> -<li>All <code>puts</code> statements will also be saved to <code>logs/log.txt</code>. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can <code>tail</code> this file.</li> -</ul> <p> -4. To ignore code in <code>repl.rb</code>, instead of commenting it out, prefix <code>repl</code> with the letter <code>x</code> and it'll be ignored. +do </p> -<pre>xrepl do # <------- line is prefixed with an "x" - puts "hello world" - puts 1 + 1 -end - -# This code will be executed when you save the file. -repl do - puts "Hello" -end - -repl do - puts "This code will also be executed." -end - -# use xrepl to "comment out" code -xrepl do - puts "This code will not be executed because of the x infront of repl". +<pre>args.outputs.sprites << args.state.bullets.map do |b| + b.sprite end </pre> -<h4>Does DragonRuby support <code>pry</code> or have any other debugging facilities?</h4> -<p> -<code>pry</code> is a gem that assumes you are using the MRI Runtime (which is incompatible with DragonRuby). Eventually DragonRuby will have a pry based experience that is compatible with a debugging infrastructure called LLDB. Take the time to read about LLDB as it shows the challenges in creating something that is compatible. -</p> -<p> -You can use DragonRuby's replay capabilities to troubleshoot: -</p> -<ol> -<li> DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added).</li> -<li> Use <code>./dragonruby mygame --record</code> to create a game play recording that you can use to find the exception (you can replay a recoding by executing <code>./dragonruby mygame --replay last_replay.txt</code> or through the DragonRuby Console using <code>$gtk.recording.start_replay "last_replay.txt"</code>.</li> -<li> DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: <code>./dragonruby . --eval some_ruby_file.rb --no-tick</code>.</li> -<li> Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to <code>args.outputs.debug</code> that will render on top of your game but will be ignored in a production release.</li> -<li> Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago.</li> -</ol> -<h2>Frequent Comments About Ruby as a Language Choice</h2> -<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>. -</p> -<p> -On a more serious note, Ruby's _quantity_ levels aren't what they used to be. And that's totally fine. Every one chases the new and shiny. -</p> -<p> -What really matters is _quality/maturity_. Here is the latest (StackOverflow Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies]. -</p> -<p> -Let's stop making this comment shall we? -</p> -<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> -<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 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 skill set. Email us ^_^. -</p> -<h2>Frequent Concerns</h2> -<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 severely under-staffed, and burnout from core members is rampant. -</p> -<p> -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! -</p> -<p> -If the reason above isn't sufficient, then definitely use something else. -</p> -<p> -All this being said, we do have parts of the engine open sourced on GitHub: <a href='https://github.com/dragonruby/dragonruby-game-toolkit-contrib/'>https://github.com/dragonruby/dragonruby-game-toolkit-contrib/</a> -</p> -<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. -</p> -<p> -That being said, we will _never_ put someone out financially. We have income assistance for anyone that can't afford a license to any one of our products. -</p> -<p> -You qualify for a free, unrestricted license to DragonRuby products if any of the following items pertain to you: -</p> -<ul> -<li>Your income is below $2,000.00 (USD) per month.</li> -<li>You are under 18 years of age.</li> -<li>You are a student of any type: traditional public school, home - schooling, college, bootcamp, or online.</li> -<li>You are a teacher, mentor, or parent who wants to teach a kid how to code.</li> -<li>You work/worked in public service or at a charitable organization: - for example public office, army, or any 501(c)(3) organization.</li> -</ul> <p> -Just contact Amir at [email protected] with a short explanation of your current situation and he'll set you up. No questions asked. +5. Use <code>args.outputs.static_</code> variant for things that don't change often (take a look at the Basic Gorillas sample app and Dueling Starships sample app to see how <code>static_</code> is leveraged. </p> -<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. -</p> -<p> -Seriously just buy it. Get a refund if you don't like it. We make it stupid easy to do so. -</p> -<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 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> -<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 partnership bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license. +6. Consider using a <code>render_target</code> if you're doing some form of a camera that moves a lot of primitives (take a look at the Render Target sample apps for more info). </p> <h1 id='--docs---gtk--runtime-'>DOCS: <code>GTK::Runtime</code></h1> <p> @@ -7412,8 +7453,8 @@ end $gtk.reset </pre> -<h1 id='--advanced-rendering---render-targets-with-alphas---main.rb'>Advanced Rendering - Render Targets With Alphas - main.rb</h1> -<pre># ./samples/07_advanced_rendering/02_render_targets_with_alphas/app/main.rb +<h1 id='--advanced-rendering---render-targets-with-tile-manipulation---main.rb'>Advanced Rendering - Render Targets With Tile Manipulation - main.rb</h1> +<pre># ./samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb # This sample is meant to show you how to do that dripping transition thing # at the start of the original Doom. Most of this file is here to animate # a scene to wipe away; the actual wipe effect is in the last 20 lines or @@ -8796,6 +8837,7 @@ end </pre> <h1 id='--performance---sprites-as-hash---main.rb'>Performance - Sprites As Hash - main.rb</h1> <pre># ./samples/09_performance/01_sprites_as_hash/app/main.rb + # Sprites represented as Hashes using the queue ~args.outputs.sprites~ # code up, but are the "slowest" to render. # The reason for this is the access of the key in the Hash and also @@ -9821,11 +9863,14 @@ end </pre> <h1 id='--12-c-extensions---basics---main.rb'>12 C Extensions - Basics - main.rb</h1> <pre># ./samples/12_c_extensions/01_basics/app/main.rb -$gtk.ffi_misc.gtk_dlopen("./samples/12_c_extensions/01_basics/build.dir/ext.lib") +$gtk.ffi_misc.gtk_dlopen("ext") include FFI::CExt def tick args - args.outputs.labels << [460, 600, "square(42) = #{square(42)}"] + args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1] + args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1] + args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1] + args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1] end @@ -22111,12 +22156,12 @@ module GTK end def save_history - $gtk.ffi_file.storefile(@history_fname, @command_history.reverse.join("\n")) + $gtk.ffi_file.write_root @history_fname, (@command_history.reverse.join "\n") end def load_history @command_history.clear - str = $gtk.ffi_file.loadfile(@history_fname) + str = $gtk.ffi_file.read @history_fname return if str.nil? # no history to load. str.chomp!("\n") # Don't let endlines at the end cause extra blank line. @@ -22329,6 +22374,36 @@ S args.inputs.keyboard.key_down.any? console_toggle_keys end + def try_search_docs exception + string_e = "#{exception}" + @last_command_errored = true + + if (string_e.include? "wrong number of arguments") + method_name = ((string_e.split ":")[0].gsub "'", "") + if !(method_name.include? " ") + results = (Kernel.__docs_search_results__ method_name) + if !results.include? "* DOCS: No results found." + puts (results.join "\n") + puts <<-S +* INFO: #{results.length} matches(s) found in DOCS for ~#{method_name}~ (see above). +You can search the documentation yourself using the following command in the Console: +#+begin_src ruby + docs_search \"#{method_name}\" +#+end_src +S + log_once_info :exported_search_results, "The search results above has been seen in logs/puts.txt and docs/search_results.txt." + end + end + end + rescue Exception => se + puts <<-S +* FATAL: ~GTK::Console#try_search_docs~ +There was an exception searching for docs (~GTK::Console#try_search_docs~). You might want to let DragonRuby know about this. +** INNER EXCEPTION +#{se} +S + end + def eval_the_set_command cmd = current_input_str.strip if cmd.length != 0 @@ -22355,18 +22430,8 @@ S end @last_command_errored = false rescue Exception => e - string_e = "#{e}" - puts "* EXCEPTION: #{e}" - log "* EXCEPTION: #{e}" - @last_command_errored = true - if (string_e.include? "wrong number of arguments") - method_name = (string_e.split ":")[0].gsub "'", "" - results = (Kernel.docs_search method_name).strip - if !results.include? "* DOCS: No results found." - puts results - log results - end - end + try_search_docs e + puts "* EXCEPTION: #{e}" end end end @@ -22980,6 +23045,7 @@ module GTK if args.inputs.mouse.click clicked = @buttons.find { |b| args.inputs.mouse.inside_rect? b[:rect] } if clicked + args.inputs.mouse.click = nil send clicked[:method] end end @@ -25043,6 +25109,10 @@ module GTK point.inside_rect? rect end + def inside_circle? center, radius + point.point_inside_circle? center, radius + end + alias_method :position, :point def clear @@ -25626,6 +25696,8 @@ S if definitions.include?(:ratio) result = rand * result + elsif definitions.include?(:int) + result = (rand result) end result @@ -26138,7 +26210,12 @@ module GTK You can display these diagnostics using: #+begin_src - args.outputs.debug << args.gtk.framerate_diagnostics_primitives + def tick args + # .... + + # IMPORTANT: Put this at the END of the ~tick~ method. + args.outputs.debug << args.gtk.framerate_diagnostics_primitives + end #+end_src ** Draw Calls: ~<<~ Invocation Perf Counter diff --git a/docs/docs.txt b/docs/docs.txt index a3a240e..c79543f 100644 --- a/docs/docs.txt +++ b/docs/docs.txt @@ -418,6 +418,31 @@ friends to go to your game's very own webpage and buy it! If you make changes to your game, just re-run dragonruby-publish and it'll update the downloads for you. + +* Deploying To Mobile Devices + +If you have a Pro subscription, you also have the capability to deploy +to mobile devices. + +To deploy to iOS, you need to have a Mac running MacOS Catalina, an +iOS device, and an active/paid Developer Account with Apple. From the +Console type: ~$wizards.ios.start~ and you will be guided through the +deployment process. + +To deploy to Android, you need to have an Android emulator/device, and +a environment that is able to run Android SDK. ~dragonruby-publish~ +will create an APK for you. From there, you can sign the APK and +install it to your device. The signing and installation procedure +varies from OS to OS. Here's an example of what the command might look +like: + +#+begin_src + > adb logcat -e mygame # you'll want to run this in a separate terminal + > keytool -genkey -v -keystore mygame.keystore -alias mygame -keyalg RSA -keysize 2048 -validity 10000 + > apksigner sign --ks mygame.keystore mygame-android.apk + > adb install mygame-android.apk +#+end_src + * DragonRuby's Philosophy The following tenants of DragonRuby are what set us apart from other @@ -519,7 +544,298 @@ 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). -* How To Determine What Frame You Are On +* Frequently Asked Questions, Comments, and Concerns + +Here are questions, comments, and concerns that frequently come +up. + +** Frequently Asked Questions + +*** What is DragonRuby LLP? + +DragonRuby LLP is a partnership of four devs who came together +with the goal of bringing the aesthetics and joy of Ruby, everywhere possible. + +Under DragonRuby LLP, we offer a number of products (with more on the +way): + +- Game Toolkit (GTK): A 2D game engine that is compatible with modern + gaming platforms. +- RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile + apps. [[http://rubymotion.com]] + +All of the products above leverage a shared core called DragonRuby. + +NOTE: From an official branding standpoint each one of the products is +suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is +_one word, title cased_. + +NOTE: We leave the "A DragonRuby LLP Product" off of this one because +that just sounds really weird. + +NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass +identifier huh? + +*** What is DragonRuby? + +The response to this question requires a few subparts. First we need +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 language specification. When +people say "Ruby," they are usually referring to "the Ruby 3.0+ language +specification implemented via the CRuby/MRI Runtime." + +But, there are many Ruby Runtimes: CRuby/MRI, JRuby, Truffle, Rubinius, Artichoke, +and (last but certainly not least) DragonRuby. + +**** Okay... what language specification does DragonRuby use then? + +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. + +**** So... why another runtime? + +The elevator pitch is: + +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. + +**** What does Multilevel Cross-platform mean? + +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): + +- Level 1 we leverage a good portion of mRuby. +- Level 2 consists of optimizations to mRuby we've made given that our + target platforms are well known. +- Level 3 consists of portable C libraries and their Ruby + C-Extensions. + +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: + +- 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 low level multimedia + component library that has been in active development for 22 years + and counting). + +- 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 + compiler outputs _very_ fast platform specific bitcode, but only + supports a subset of the Ruby language specification. + +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. + +**** 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 implementation that takes all the lessons +we've learned from MRI/CRuby, and merges it with the latest and greatest +compiler and OSS technologies. + +*** How is DragonRuby different than MRI? + +DragonRuby supports a subset of MRI apis. Our target is to support all +of mRuby's standard lib. There are challenges to this given the number +of platforms we are trying to support (specifically console). + +**** Does DragonRuby support Gems? + +DragonRuby does not support gems because that requires the +installation of MRI Ruby on the developer's machine (which is a +non-starter given that we want DragonRuby to be a zero dependency +runtime). While this seems easy for Mac and Linux, it is much harder +on Windows and Raspberry Pi. mRuby has taken the approach of having a +git repository for compatible gems and we will most likely follow +suite: [[https://github.com/mruby/mgem-list]]. + +**** Does DragonRuby have a REPL/IRB? + +You can use DragonRuby's Console within the game to inspect object and +execute small pieces of code. For more complex pieces of code create a +file called ~repl.rb~ and put it in ~mygame/app/repl.rb~: + +- Any code you write in there will be executed when you change the file. You can organize different pieces of code using the ~repl~ method: + +#+begin_src ruby + repl do + puts "hello world" + puts 1 + 1 + end +#+end_src + +- If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal). + +- All ~puts~ statements will also be saved to ~logs/log.txt~. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can ~tail~ this file. + +4. To ignore code in ~repl.rb~, instead of commenting it out, prefix ~repl~ with the letter ~x~ and it'll be ignored. + +#+begin_src ruby + xrepl do # <------- line is prefixed with an "x" + puts "hello world" + puts 1 + 1 + end + + # This code will be executed when you save the file. + repl do + puts "Hello" + end + + repl do + puts "This code will also be executed." + end + + # use xrepl to "comment out" code + xrepl do + puts "This code will not be executed because of the x infront of repl". + end +#+end_src + +**** Does DragonRuby support ~pry~ or have any other debugging facilities? + +~pry~ is a gem that assumes you are using the MRI Runtime (which is +incompatible with DragonRuby). Eventually DragonRuby will have a pry +based experience that is compatible with a debugging infrastructure +called LLDB. Take the time to read about LLDB as it shows the +challenges in creating something that is compatible. + +You can use DragonRuby's replay capabilities to troubleshoot: + +1. DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added). +2. Use ~./dragonruby mygame --record~ to create a game play recording that you can use to find the exception (you can replay a recoding by executing ~./dragonruby mygame --replay last_replay.txt~ or through the DragonRuby Console using ~$gtk.recording.start_replay "last_replay.txt"~. +3. DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: ~./dragonruby . --eval some_ruby_file.rb --no-tick~. +4. Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to ~args.outputs.debug~ that will render on top of your game but will be ignored in a production release. +5. Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago. + +** Frequent Comments About Ruby as a Language Choice + +*** But Ruby is dead. + +Let's check the official source for the answer to this question: +isrubydead.com: [[https://isrubydead.com/]]. + +On a more serious note, Ruby's _quantity_ levels aren't what they used +to be. And that's totally fine. Every one chases the new and shiny. + +What really matters is _quality/maturity_. Here is the latest (StackOverflow +Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies]. + +Let's stop making this comment shall we? + +*** But Ruby is slow. + +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 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 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 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 severely under-staffed, +and burnout from core members is rampant. + +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. + +If you have ideas on how we can do this, email us! + +If the reason above isn't sufficient, then definitely use something else. + +All this being said, we do have parts of the engine open sourced on GitHub: [[https://github.com/dragonruby/dragonruby-game-toolkit-contrib/]] + +*** DragonRuby is for pay. You should offer a free version. + +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. + +That being said, we will _never_ put someone out financially. We have +income assistance for anyone that can't afford a license to any one of +our products. + +You qualify for a free, unrestricted license to DragonRuby products if +any of the following items pertain to you: + +- Your income is below $2,000.00 (USD) per month. +- You are under 18 years of age. +- You are a student of any type: traditional public school, home + schooling, college, bootcamp, or online. +- You are a teacher, mentor, or parent who wants to teach a kid how to code. +- You work/worked in public service or at a charitable organization: + for example public office, army, or any 501(c)(3) organization. + +Just contact Amir at [email protected] with a short +explanation of your current situation and he'll set you up. No +questions asked. + +*** But still, you should offer a free version. So I can try it out and see if I like it. + +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. + +Seriously just buy it. Get a refund if you don't like it. We make it +stupid easy to do so. + +*** I still think you should do a free version. Think of all people who would give it a shot. + +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). + +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. + +*** What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent. + +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. + +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. + +* CHEATSHEET: How To Determine What Frame You Are On There is a property on ~state~ called ~tick_count~ that is incremented by DragonRuby every time the ~tick~ method is called. The following @@ -531,7 +847,7 @@ code renders a label that displays the current ~tick_count~. end #+end_src -* How To Get Current Framerate +* CHEATSHEET: How To Get Current Framerate Current framerate is a top level property on the Game Toolkit Runtime and is accessible via ~args.gtk.current_framerate~. @@ -542,7 +858,7 @@ and is accessible via ~args.gtk.current_framerate~. end #+end_src -* How To Render A Sprite Using An Array +* CHEATSHEET: How To Render A Sprite Using An Array All file paths should use the forward slash ~/~ *not* backslash ~~. Game Toolkit includes a number of sprites in the ~sprites~ @@ -565,7 +881,7 @@ The following code renders a sprite with a ~width~ and ~height~ of end #+end_src -* More Sprite Properties As An Array +* CHEATSHEET: More Sprite Properties As An Array Here are all the properties you can set on a sprite. @@ -586,7 +902,7 @@ Here are all the properties you can set on a sprite. end #+end_src -* Different Sprite Representations +* CHEATSHEET: Different Sprite Representations Using ordinal positioning can get a little unruly given so many properties you have control over. @@ -652,7 +968,7 @@ You can represent a sprite as an ~object~: end #+end_src -* How To Render A Label +* CHEATSHEET: How To Render A Label ~args.outputs.labels~ is used to render labels. @@ -668,7 +984,7 @@ Here is the minimum code: end #+end_src -* A Colored Label +* CHEATSHEET: A Colored Label #+begin_src def tick args @@ -678,7 +994,7 @@ Here is the minimum code: end #+end_src -* Extended Label Properties +* CHEATSHEET: Extended Label Properties #+begin_src def tick args @@ -706,7 +1022,7 @@ label's size. An ~ALIGNMENT_ENUM~ of ~0~ represents "left aligned". ~1~ represents "center aligned". ~2~ represents "right aligned". -* Rendering A Label As A ~Hash~ +* CHEATSHEET: Rendering A Label As A ~Hash~ You can add additional metadata about your game within a label, which requires you to use a `Hash` instead. @@ -734,7 +1050,7 @@ You can add additional metadata about your game within a label, which requires y end #+end_src -* Getting The Size Of A Piece Of Text +* CHEATSHEET: Getting The Size Of A Piece Of Text You can get the render size of any string using ~args.gtk.calcstringbox~. @@ -755,7 +1071,7 @@ You can get the render size of any string using ~args.gtk.calcstringbox~. end #+end_src -* How To Play A Sound +* CHEATSHEET: How To Play A Sound Sounds that end ~.wav~ will play once: @@ -790,7 +1106,7 @@ If you want to play a ~.ogg~ once as if it were a sound effect, you can do: end #+end_src -* Using ~args.state~ To Store Your Game State +* CHEATSHEET: Using ~args.state~ To Store Your Game State ~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 @@ -828,296 +1144,25 @@ sure_ you've initialized a default value. end #+end_src -* Frequently Asked Questions, Comments, and Concerns - -Here are questions, comments, and concerns that frequently come -up. - -** Frequently Asked Questions - -*** What is DragonRuby LLP? - -DragonRuby LLP is a partnership of four devs who came together -with the goal of bringing the aesthetics and joy of Ruby, everywhere possible. - -Under DragonRuby LLP, we offer a number of products (with more on the -way): - -- Game Toolkit (GTK): A 2D game engine that is compatible with modern - gaming platforms. -- RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile - apps. [[http://rubymotion.com]] - -All of the products above leverage a shared core called DragonRuby. - -NOTE: From an official branding standpoint each one of the products is -suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is -_one word, title cased_. - -NOTE: We leave the "A DragonRuby LLP Product" off of this one because -that just sounds really weird. - -NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass -identifier huh? - -*** What is DragonRuby? - -The response to this question requires a few subparts. First we need -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 language specification. When -people say "Ruby," they are usually referring to "the Ruby 3.0+ language -specification implemented via the CRuby/MRI Runtime." - -But, there are many Ruby Runtimes: CRuby/MRI, JRuby, Truffle, Rubinius, Artichoke, -and (last but certainly not least) DragonRuby. - -**** Okay... what language specification does DragonRuby use then? - -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. - -**** So... why another runtime? - -The elevator pitch is: - -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. - -**** What does Multilevel Cross-platform mean? - -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): - -- Level 1 we leverage a good portion of mRuby. -- Level 2 consists of optimizations to mRuby we've made given that our - target platforms are well known. -- Level 3 consists of portable C libraries and their Ruby - C-Extensions. - -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: - -- 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 low level multimedia - component library that has been in active development for 22 years - and counting). - -- 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 - compiler outputs _very_ fast platform specific bitcode, but only - supports a subset of the Ruby language specification. - -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. - -**** 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 implementation that takes all the lessons -we've learned from MRI/CRuby, and merges it with the latest and greatest -compiler and OSS technologies. - -*** How is DragonRuby different than MRI? - -DragonRuby supports a subset of MRI apis. Our target is to support all -of mRuby's standard lib. There are challenges to this given the number -of platforms we are trying to support (specifically console). - -**** Does DragonRuby support Gems? - -DragonRuby does not support gems because that requires the -installation of MRI Ruby on the developer's machine (which is a -non-starter given that we want DragonRuby to be a zero dependency -runtime). While this seems easy for Mac and Linux, it is much harder -on Windows and Raspberry Pi. mRuby has taken the approach of having a -git repository for compatible gems and we will most likely follow -suite: [[https://github.com/mruby/mgem-list]]. - -**** Does DragonRuby have a REPL/IRB? - -You can use DragonRuby's Console within the game to inspect object and -execute small pieces of code. For more complex pieces of code create a -file called ~repl.rb~ and put it in ~mygame/app/repl.rb~: - -- Any code you write in there will be executed when you change the file. You can organize different pieces of code using the ~repl~ method: +* CHEATSHEET: Troubleshoot Performance +1. If you're using ~Array~s for your primitives (~args.outputs.sprites << []~), use ~Hash~ instead (~args.outputs.sprites << { x: ... }~). +2. If you're using ~Entity~ for your primitives (~args.outputs.sprites << args.state.new_entity~), use ~StrictEntity~ instead (~args.outputs.sprites << args.state.new_entity_strict~). +3. Use ~.each~ instead of ~.map~ if you don't care about the return value. +4. When concatenating primitives to outputs, do them in bulk. Instead of: #+begin_src ruby - repl do - puts "hello world" - puts 1 + 1 + args.state.bullets.each do |bullet| + args.outputs.sprites << bullet.sprite end #+end_src - -- If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal). - -- All ~puts~ statements will also be saved to ~logs/log.txt~. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can ~tail~ this file. - -4. To ignore code in ~repl.rb~, instead of commenting it out, prefix ~repl~ with the letter ~x~ and it'll be ignored. - -#+begin_src ruby - xrepl do # <------- line is prefixed with an "x" - puts "hello world" - puts 1 + 1 - end - - # This code will be executed when you save the file. - repl do - puts "Hello" - end - - repl do - puts "This code will also be executed." - end - - # use xrepl to "comment out" code - xrepl do - puts "This code will not be executed because of the x infront of repl". +do +#+begin_src + args.outputs.sprites << args.state.bullets.map do |b| + b.sprite end #+end_src - -**** Does DragonRuby support ~pry~ or have any other debugging facilities? - -~pry~ is a gem that assumes you are using the MRI Runtime (which is -incompatible with DragonRuby). Eventually DragonRuby will have a pry -based experience that is compatible with a debugging infrastructure -called LLDB. Take the time to read about LLDB as it shows the -challenges in creating something that is compatible. - -You can use DragonRuby's replay capabilities to troubleshoot: - -1. DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added). -2. Use ~./dragonruby mygame --record~ to create a game play recording that you can use to find the exception (you can replay a recoding by executing ~./dragonruby mygame --replay last_replay.txt~ or through the DragonRuby Console using ~$gtk.recording.start_replay "last_replay.txt"~. -3. DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: ~./dragonruby . --eval some_ruby_file.rb --no-tick~. -4. Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to ~args.outputs.debug~ that will render on top of your game but will be ignored in a production release. -5. Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago. - -** Frequent Comments About Ruby as a Language Choice - -*** But Ruby is dead. - -Let's check the official source for the answer to this question: -isrubydead.com: [[https://isrubydead.com/]]. - -On a more serious note, Ruby's _quantity_ levels aren't what they used -to be. And that's totally fine. Every one chases the new and shiny. - -What really matters is _quality/maturity_. Here is the latest (StackOverflow -Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies]. - -Let's stop making this comment shall we? - -*** But Ruby is slow. - -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 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 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 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 severely under-staffed, -and burnout from core members is rampant. - -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. - -If you have ideas on how we can do this, email us! - -If the reason above isn't sufficient, then definitely use something else. - -All this being said, we do have parts of the engine open sourced on GitHub: [[https://github.com/dragonruby/dragonruby-game-toolkit-contrib/]] - -*** DragonRuby is for pay. You should offer a free version. - -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. - -That being said, we will _never_ put someone out financially. We have -income assistance for anyone that can't afford a license to any one of -our products. - -You qualify for a free, unrestricted license to DragonRuby products if -any of the following items pertain to you: - -- Your income is below $2,000.00 (USD) per month. -- You are under 18 years of age. -- You are a student of any type: traditional public school, home - schooling, college, bootcamp, or online. -- You are a teacher, mentor, or parent who wants to teach a kid how to code. -- You work/worked in public service or at a charitable organization: - for example public office, army, or any 501(c)(3) organization. - -Just contact Amir at [email protected] with a short -explanation of your current situation and he'll set you up. No -questions asked. - -*** But still, you should offer a free version. So I can try it out and see if I like it. - -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. - -Seriously just buy it. Get a refund if you don't like it. We make it -stupid easy to do so. - -*** I still think you should do a free version. Think of all people who would give it a shot. - -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). - -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. - -*** What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent. - -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. - -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. +5. Use ~args.outputs.static_~ variant for things that don't change often (take a look at the Basic Gorillas sample app and Dueling Starships sample app to see how ~static_~ is leveraged. +6. Consider using a ~render_target~ if you're doing some form of a camera that moves a lot of primitives (take a look at the Render Target sample apps for more info). * DOCS: ~GTK::Runtime~ The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~. @@ -7592,9 +7637,9 @@ Follows is a source code listing for all files that have been open sourced. This #+end_src -* Advanced Rendering - Render Targets With Alphas - main.rb +* Advanced Rendering - Render Targets With Tile Manipulation - main.rb #+begin_src ruby - # ./samples/07_advanced_rendering/02_render_targets_with_alphas/app/main.rb + # ./samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb # This sample is meant to show you how to do that dripping transition thing # at the start of the original Doom. Most of this file is here to animate # a scene to wipe away; the actual wipe effect is in the last 20 lines or @@ -8993,6 +9038,7 @@ Follows is a source code listing for all files that have been open sourced. This * Performance - Sprites As Hash - main.rb #+begin_src ruby # ./samples/09_performance/01_sprites_as_hash/app/main.rb + # Sprites represented as Hashes using the queue ~args.outputs.sprites~ # code up, but are the "slowest" to render. # The reason for this is the access of the key in the Hash and also @@ -10054,11 +10100,14 @@ Follows is a source code listing for all files that have been open sourced. This * 12 C Extensions - Basics - main.rb #+begin_src ruby # ./samples/12_c_extensions/01_basics/app/main.rb - $gtk.ffi_misc.gtk_dlopen("./samples/12_c_extensions/01_basics/build.dir/ext.lib") + $gtk.ffi_misc.gtk_dlopen("ext") include FFI::CExt def tick args - args.outputs.labels << [460, 600, "square(42) = #{square(42)}"] + args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1] + args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1] + args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1] + args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1] end @@ -22448,12 +22497,12 @@ Follows is a source code listing for all files that have been open sourced. This end def save_history - $gtk.ffi_file.storefile(@history_fname, @command_history.reverse.join("\n")) + $gtk.ffi_file.write_root @history_fname, (@command_history.reverse.join "\n") end def load_history @command_history.clear - str = $gtk.ffi_file.loadfile(@history_fname) + str = $gtk.ffi_file.read @history_fname return if str.nil? # no history to load. str.chomp!("\n") # Don't let endlines at the end cause extra blank line. @@ -22666,6 +22715,36 @@ Follows is a source code listing for all files that have been open sourced. This args.inputs.keyboard.key_down.any? console_toggle_keys end + def try_search_docs exception + string_e = "#{exception}" + @last_command_errored = true + + if (string_e.include? "wrong number of arguments") + method_name = ((string_e.split ":")[0].gsub "'", "") + if !(method_name.include? " ") + results = (Kernel.__docs_search_results__ method_name) + if !results.include? "* DOCS: No results found." + puts (results.join "\n") + puts <<-S + * INFO: #{results.length} matches(s) found in DOCS for ~#{method_name}~ (see above). + You can search the documentation yourself using the following command in the Console: + #+begin_src ruby + docs_search \"#{method_name}\" + #+end_src + S + log_once_info :exported_search_results, "The search results above has been seen in logs/puts.txt and docs/search_results.txt." + end + end + end + rescue Exception => se + puts <<-S + * FATAL: ~GTK::Console#try_search_docs~ + There was an exception searching for docs (~GTK::Console#try_search_docs~). You might want to let DragonRuby know about this. + ** INNER EXCEPTION + #{se} + S + end + def eval_the_set_command cmd = current_input_str.strip if cmd.length != 0 @@ -22692,18 +22771,8 @@ Follows is a source code listing for all files that have been open sourced. This end @last_command_errored = false rescue Exception => e - string_e = "#{e}" - puts "* EXCEPTION: #{e}" - log "* EXCEPTION: #{e}" - @last_command_errored = true - if (string_e.include? "wrong number of arguments") - method_name = (string_e.split ":")[0].gsub "'", "" - results = (Kernel.docs_search method_name).strip - if !results.include? "* DOCS: No results found." - puts results - log results - end - end + try_search_docs e + puts "* EXCEPTION: #{e}" end end end @@ -23323,6 +23392,7 @@ Follows is a source code listing for all files that have been open sourced. This if args.inputs.mouse.click clicked = @buttons.find { |b| args.inputs.mouse.inside_rect? b[:rect] } if clicked + args.inputs.mouse.click = nil send clicked[:method] end end @@ -25404,6 +25474,10 @@ Follows is a source code listing for all files that have been open sourced. This point.inside_rect? rect end + def inside_circle? center, radius + point.point_inside_circle? center, radius + end + alias_method :position, :point def clear @@ -25991,6 +26065,8 @@ Follows is a source code listing for all files that have been open sourced. This if definitions.include?(:ratio) result = rand * result + elsif definitions.include?(:int) + result = (rand result) end result @@ -26505,7 +26581,12 @@ Follows is a source code listing for all files that have been open sourced. This You can display these diagnostics using: #+begin_src - args.outputs.debug << args.gtk.framerate_diagnostics_primitives + def tick args + # .... + + # IMPORTANT: Put this at the END of the ~tick~ method. + args.outputs.debug << args.gtk.framerate_diagnostics_primitives + end #+end_src ** Draw Calls: ~<<~ Invocation Perf Counter diff --git a/docs/parse_log.txt b/docs/parse_log.txt index 763949f..3c9561c 100644 --- a/docs/parse_log.txt +++ b/docs/parse_log.txt @@ -1229,6 +1229,74 @@ DragonRuby will package _and publish_ your game to itch.io! Tell your friends to - End of paragraph detected. *** True Line Result If you make changes to your game, just re-run dragonruby-publish and it'll update the downloads for you. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~* Deploying To Mobile Devices~ +- Header detected. +*** True Line Result + +*** True Line Result +* Deploying To Mobile Devices +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~If you have a Pro subscription, you also have the capability to deploy~ +** Processing line: ~to mobile devices.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +If you have a Pro subscription, you also have the capability to deploy to mobile devices. +** Processing line: ~To deploy to iOS, you need to have a Mac running MacOS Catalina, an~ +** Processing line: ~iOS device, and an active/paid Developer Account with Apple. From the~ +** Processing line: ~Console type: ~$wizards.ios.start~ and you will be guided through the~ +** Processing line: ~deployment process.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +To deploy to iOS, you need to have a Mac running MacOS Catalina, an iOS device, and an active/paid Developer Account with Apple. From the Console type: ~$wizards.ios.start~ and you will be guided through the deployment process. +** Processing line: ~To deploy to Android, you need to have an Android emulator/device, and~ +** Processing line: ~a environment that is able to run Android SDK. ~dragonruby-publish~~ +** Processing line: ~will create an APK for you. From there, you can sign the APK and~ +** Processing line: ~install it to your device. The signing and installation procedure~ +** Processing line: ~varies from OS to OS. Here's an example of what the command might look~ +** Processing line: ~like:~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +To deploy to Android, you need to have an Android emulator/device, and a environment that is able to run Android SDK. ~dragonruby-publish~ will create an APK for you. From there, you can sign the APK and install it to your device. The signing and installation procedure varies from OS to OS. Here's an example of what the command might look like: +** Processing line: ~#+begin_src~ +- Line was identified as the beginning of a code block. +*** True Line Result + +*** True Line Result +#+begin_src +** Processing line: ~ > adb logcat -e mygame # you'll want to run this in a separate terminal~ +- Inside source: true +*** True Line Result + > adb logcat -e mygame # you'll want to run this in a separate terminal +** Processing line: ~ > keytool -genkey -v -keystore mygame.keystore -alias mygame -keyalg RSA -keysize 2048 -validity 10000~ +- Inside source: true +*** True Line Result + > keytool -genkey -v -keystore mygame.keystore -alias mygame -keyalg RSA -keysize 2048 -validity 10000 +** Processing line: ~ > apksigner sign --ks mygame.keystore mygame-android.apk~ +- Inside source: true +*** True Line Result + > apksigner sign --ks mygame.keystore mygame-android.apk +** Processing line: ~ > adb install mygame-android.apk~ +- Inside source: true +*** True Line Result + > adb install mygame-android.apk +** 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: ~* DragonRuby's Philosophy~ - Header detected. *** True Line Result @@ -1476,12 +1544,815 @@ We are bombarded by marketing speak day in and day out. We don't do that here. T - End of paragraph detected. *** True Line Result 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). -** Processing line: ~* How To Determine What Frame You Are On~ +** Processing line: ~* Frequently Asked Questions, Comments, and Concerns~ +- Header detected. +*** True Line Result + +*** True Line Result +* Frequently Asked Questions, Comments, and Concerns +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~Here are questions, comments, and concerns that frequently come~ +** Processing line: ~up.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +Here are questions, comments, and concerns that frequently come up. +** Processing line: ~** Frequently Asked Questions~ +- Header detected. +*** True Line Result + +*** True Line Result +** Frequently Asked Questions +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~*** What is DragonRuby LLP?~ +- Header detected. +*** True Line Result + +*** True Line Result +*** What is DragonRuby LLP? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~DragonRuby LLP is a partnership of four devs who came together~ +** Processing line: ~with the goal of bringing the aesthetics and joy of Ruby, everywhere possible.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +DragonRuby LLP is a partnership of four devs who came together with the goal of bringing the aesthetics and joy of Ruby, everywhere possible. +** Processing line: ~Under DragonRuby LLP, we offer a number of products (with more on the~ +** Processing line: ~way):~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +Under DragonRuby LLP, we offer a number of products (with more on the way): +** Processing line: ~- Game Toolkit (GTK): A 2D game engine that is compatible with modern~ +- Line was identified as a list. +*** True Line Result + +** Processing line: ~ gaming platforms.~ +** Processing line: ~- RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile~ +- Line was identified as a list. +*** True Line Result +- Game Toolkit (GTK): A 2D game engine that is compatible with modern + gaming platforms. +** Processing line: ~ apps. [[http://rubymotion.com]]~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +- RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile + apps. [[http://rubymotion.com]] +** Processing line: ~All of the products above leverage a shared core called DragonRuby.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +All of the products above leverage a shared core called DragonRuby. +** Processing line: ~NOTE: From an official branding standpoint each one of the products is~ +** Processing line: ~suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is~ +** Processing line: ~_one word, title cased_.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +NOTE: From an official branding standpoint each one of the products is suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is _one word, title cased_. +** Processing line: ~NOTE: We leave the "A DragonRuby LLP Product" off of this one because~ +** Processing line: ~that just sounds really weird.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +NOTE: We leave the "A DragonRuby LLP Product" off of this one because that just sounds really weird. +** Processing line: ~NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass~ +** Processing line: ~identifier huh?~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass identifier huh? +** Processing line: ~*** What is DragonRuby?~ +- Header detected. +*** True Line Result + +*** True Line Result +*** What is DragonRuby? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~The response to this question requires a few subparts. First we need~ +** Processing line: ~to clarify some terms. Specifically _language specification_ vs _runtime_.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +The response to this question requires a few subparts. First we need to clarify some terms. Specifically _language specification_ vs _runtime_. +** Processing line: ~**** Okay... so what is the difference between a language specification and a runtime?~ +- Header detected. +*** True Line Result + +*** True Line Result +**** Okay... so what is the difference between a language specification and a runtime? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** 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 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: ~~ +- End of paragraph detected. +*** True Line Result +But, there are many Ruby Runtimes: CRuby/MRI, JRuby, Truffle, Rubinius, Artichoke, and (last but certainly not least) DragonRuby. +** Processing line: ~**** Okay... what language specification does DragonRuby use then?~ +- Header detected. +*** True Line Result + +*** True Line Result +**** Okay... what language specification does DragonRuby use then? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~DragonRuby's goal is to be compliant with the ISO/IEC 30170:2012 standard. It's~ +** Processing line: ~syntax is Ruby 2.x compatible, but also contains semantic changes that help~ +** Processing line: ~it natively interface with platform specific libraries.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +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. +** Processing line: ~**** So... why another runtime?~ +- Header detected. +*** True Line Result + +*** True Line Result +**** So... why another runtime? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~The elevator pitch is:~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +The elevator pitch is: +** Processing line: ~DragonRuby is a Multilevel Cross-platform Runtime. The "multiple levels"~ +** Processing line: ~within the runtime allows us to target platforms no other Ruby can~ +** Processing line: ~target: PC, Mac, Linux, Raspberry Pi, WASM, iOS, Android, Nintendo~ +** Processing line: ~Switch, PS4, Xbox, and Scadia.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +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. +** Processing line: ~**** What does Multilevel Cross-platform mean?~ +- Header detected. +*** True Line Result + +*** True Line Result +**** What does Multilevel Cross-platform mean? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~There are complexities associated with targeting all the platforms we~ +** Processing line: ~support. Because of this, the runtime had to be architected in such a~ +** Processing line: ~way that new platforms could be easily added (which lead to us partitioning the~ +** Processing line: ~runtime internally):~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +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): +** Processing line: ~- Level 1 we leverage a good portion of mRuby.~ +- Line was identified as a list. +*** True Line Result + +** Processing line: ~- Level 2 consists of optimizations to mRuby we've made given that our~ +- Line was identified as a list. +*** True Line Result +- Level 1 we leverage a good portion of mRuby. +** Processing line: ~ target platforms are well known.~ +** Processing line: ~- Level 3 consists of portable C libraries and their Ruby~ +- Line was identified as a list. +*** True Line Result +- Level 2 consists of optimizations to mRuby we've made given that our + target platforms are well known. +** Processing line: ~ C-Extensions.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +- 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: ~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 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 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 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 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 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 + +** Processing line: ~ compiler outputs _very_ fast platform specific bitcode, but only~ +** Processing line: ~ supports a subset of the Ruby language specification.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +- 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. +** 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 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 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 + +*** True Line Result +**** Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** 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 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: ~*** How is DragonRuby different than MRI?~ +- Header detected. +*** True Line Result + +*** True Line Result +*** How is DragonRuby different than MRI? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~DragonRuby supports a subset of MRI apis. Our target is to support all~ +** Processing line: ~of mRuby's standard lib. There are challenges to this given the number~ +** Processing line: ~of platforms we are trying to support (specifically console).~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +DragonRuby supports a subset of MRI apis. Our target is to support all of mRuby's standard lib. There are challenges to this given the number of platforms we are trying to support (specifically console). +** Processing line: ~**** Does DragonRuby support Gems?~ +- Header detected. +*** True Line Result + +*** True Line Result +**** Does DragonRuby support Gems? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~DragonRuby does not support gems because that requires the~ +** Processing line: ~installation of MRI Ruby on the developer's machine (which is a~ +** Processing line: ~non-starter given that we want DragonRuby to be a zero dependency~ +** Processing line: ~runtime). While this seems easy for Mac and Linux, it is much harder~ +** Processing line: ~on Windows and Raspberry Pi. mRuby has taken the approach of having a~ +** Processing line: ~git repository for compatible gems and we will most likely follow~ +** Processing line: ~suite: [[https://github.com/mruby/mgem-list]].~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +DragonRuby does not support gems because that requires the installation of MRI Ruby on the developer's machine (which is a non-starter given that we want DragonRuby to be a zero dependency runtime). While this seems easy for Mac and Linux, it is much harder on Windows and Raspberry Pi. mRuby has taken the approach of having a git repository for compatible gems and we will most likely follow suite: [[https://github.com/mruby/mgem-list]]. +** Processing line: ~**** Does DragonRuby have a REPL/IRB?~ - Header detected. *** True Line Result *** True Line Result -* How To Determine What Frame You Are On +**** Does DragonRuby have a REPL/IRB? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~You can use DragonRuby's Console within the game to inspect object and~ +** Processing line: ~execute small pieces of code. For more complex pieces of code create a~ +** Processing line: ~file called ~repl.rb~ and put it in ~mygame/app/repl.rb~:~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +You can use DragonRuby's Console within the game to inspect object and execute small pieces of code. For more complex pieces of code create a file called ~repl.rb~ and put it in ~mygame/app/repl.rb~: +** Processing line: ~- Any code you write in there will be executed when you change the file. You can organize different pieces of code using the ~repl~ method:~ +- Line was identified as a list. +*** True Line Result + +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +- Any code you write in there will be executed when you change the file. You can organize different pieces of code using the ~repl~ method: +** 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: ~ repl do~ +- Inside source: true +*** True Line Result + repl do +** Processing line: ~ puts "hello world"~ +- Inside source: true +*** True Line Result + puts "hello world" +** Processing line: ~ puts 1 + 1~ +- Inside source: true +*** True Line Result + puts 1 + 1 +** 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: ~- If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal).~ +- Line was identified as a list. +*** True Line Result + +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +- If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal). +** Processing line: ~- All ~puts~ statements will also be saved to ~logs/log.txt~. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can ~tail~ this file.~ +- Line was identified as a list. +*** True Line Result + +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +- All ~puts~ statements will also be saved to ~logs/log.txt~. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can ~tail~ this file. +** Processing line: ~4. To ignore code in ~repl.rb~, instead of commenting it out, prefix ~repl~ with the letter ~x~ and it'll be ignored.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +4. To ignore code in ~repl.rb~, instead of commenting it out, prefix ~repl~ with the letter ~x~ and it'll be ignored. +** 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: ~ xrepl do # <------- line is prefixed with an "x"~ +- Inside source: true +*** True Line Result + xrepl do # <------- line is prefixed with an "x" +** Processing line: ~ puts "hello world"~ +- Inside source: true +*** True Line Result + puts "hello world" +** Processing line: ~ puts 1 + 1~ +- Inside source: true +*** True Line Result + puts 1 + 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # This code will be executed when you save the file.~ +- Inside source: true +*** True Line Result + # This code will be executed when you save the file. +** Processing line: ~ repl do~ +- Inside source: true +*** True Line Result + repl do +** Processing line: ~ puts "Hello"~ +- Inside source: true +*** True Line Result + puts "Hello" +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ repl do~ +- Inside source: true +*** True Line Result + repl do +** Processing line: ~ puts "This code will also be executed."~ +- Inside source: true +*** True Line Result + puts "This code will also be executed." +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # use xrepl to "comment out" code~ +- Inside source: true +*** True Line Result + # use xrepl to "comment out" code +** Processing line: ~ xrepl do~ +- Inside source: true +*** True Line Result + xrepl do +** Processing line: ~ puts "This code will not be executed because of the x infront of repl".~ +- Inside source: true +*** True Line Result + puts "This code will not be executed because of the x infront of repl". +** 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: ~**** Does DragonRuby support ~pry~ or have any other debugging facilities?~ +- Header detected. +*** True Line Result + +*** True Line Result +**** Does DragonRuby support ~pry~ or have any other debugging facilities? +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~~pry~ is a gem that assumes you are using the MRI Runtime (which is~ +** Processing line: ~incompatible with DragonRuby). Eventually DragonRuby will have a pry~ +** Processing line: ~based experience that is compatible with a debugging infrastructure~ +** Processing line: ~called LLDB. Take the time to read about LLDB as it shows the~ +** Processing line: ~challenges in creating something that is compatible.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +~pry~ is a gem that assumes you are using the MRI Runtime (which is incompatible with DragonRuby). Eventually DragonRuby will have a pry based experience that is compatible with a debugging infrastructure called LLDB. Take the time to read about LLDB as it shows the challenges in creating something that is compatible. +** Processing line: ~You can use DragonRuby's replay capabilities to troubleshoot:~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +You can use DragonRuby's replay capabilities to troubleshoot: +** Processing line: ~1. DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added).~ +- Line was identified as a start of a list. +*** True Line Result + +** Processing line: ~2. Use ~./dragonruby mygame --record~ to create a game play recording that you can use to find the exception (you can replay a recoding by executing ~./dragonruby mygame --replay last_replay.txt~ or through the DragonRuby Console using ~$gtk.recording.start_replay "last_replay.txt"~.~ +- Line was identified as a continuation of a list. +*** True Line Result +1. DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added). +** Processing line: ~3. DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: ~./dragonruby . --eval some_ruby_file.rb --no-tick~.~ +- Line was identified as a continuation of a list. +*** True Line Result +2. Use ~./dragonruby mygame --record~ to create a game play recording that you can use to find the exception (you can replay a recoding by executing ~./dragonruby mygame --replay last_replay.txt~ or through the DragonRuby Console using ~$gtk.recording.start_replay "last_replay.txt"~. +** Processing line: ~4. Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to ~args.outputs.debug~ that will render on top of your game but will be ignored in a production release.~ +- Line was identified as a continuation of a list. +*** True Line Result +3. DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: ~./dragonruby . --eval some_ruby_file.rb --no-tick~. +** Processing line: ~5. Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago.~ +- Line was identified as a continuation of a list. +*** True Line Result +4. Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to ~args.outputs.debug~ that will render on top of your game but will be ignored in a production release. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +5. Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago. +** Processing line: ~** Frequent Comments About Ruby as a Language Choice~ +- Header detected. +*** True Line Result + +*** True Line Result +** Frequent Comments About Ruby as a Language Choice +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~*** But Ruby is dead.~ +- Header detected. +*** True Line Result + +*** True Line Result +*** But Ruby is dead. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~Let's check the official source for the answer to this question:~ +** Processing line: ~isrubydead.com: [[https://isrubydead.com/]].~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +Let's check the official source for the answer to this question: isrubydead.com: [[https://isrubydead.com/]]. +** Processing line: ~On a more serious note, Ruby's _quantity_ levels aren't what they used~ +** Processing line: ~to be. And that's totally fine. Every one chases the new and shiny.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +On a more serious note, Ruby's _quantity_ levels aren't what they used to be. And that's totally fine. Every one chases the new and shiny. +** Processing line: ~What really matters is _quality/maturity_. Here is the latest (StackOverflow~ +** Processing line: ~Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies].~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +What really matters is _quality/maturity_. Here is the latest (StackOverflow Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies]. +** Processing line: ~Let's stop making this comment shall we?~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +Let's stop making this comment shall we? +** Processing line: ~*** But Ruby is slow.~ +- Header detected. +*** True Line Result + +*** True Line Result +*** But Ruby is slow. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~That doesn't make any sense. A language specification can't be~ +** Processing line: ~slow... it's a language spec. Sure, an _implementation/runtime_ can be slow though, but then we'd~ +** Processing line: ~have to talk about which runtime.~ +** Processing line: ~~ +- 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 languages are slow.~ +- Header detected. +*** True Line Result + +*** True Line Result +*** Dynamic languages are slow. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~They are certainly slower than statically compiled languages. With the~ +** Processing line: ~processing power and compiler optimizations we have today,~ +** Processing line: ~dynamic languages like Ruby are _fast enough_.~ +** Processing line: ~~ +- End of paragraph detected. +*** 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 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 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 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 skill set. Email us ^_^. +** Processing line: ~** Frequent Concerns~ +- Header detected. +*** True Line Result + +*** True Line Result +** Frequent Concerns +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~*** DragonRuby is not open source. That's not right.~ +- Header detected. +*** True Line Result + +*** True Line Result +*** DragonRuby is not open source. That's not right. +** Processing line: ~~ +- End of paragraph detected. +*** 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 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 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: ~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 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. +*** True Line Result +If you have ideas on how we can do this, email us! +** Processing line: ~If the reason above isn't sufficient, then definitely use something else.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +If the reason above isn't sufficient, then definitely use something else. +** Processing line: ~All this being said, we do have parts of the engine open sourced on GitHub: [[https://github.com/dragonruby/dragonruby-game-toolkit-contrib/]]~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +All this being said, we do have parts of the engine open sourced on GitHub: [[https://github.com/dragonruby/dragonruby-game-toolkit-contrib/]] +** Processing line: ~*** DragonRuby is for pay. You should offer a free version.~ +- Header detected. +*** True Line Result + +*** True Line Result +*** DragonRuby is for pay. You should offer a free version. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~If you can afford to pay for DragonRuby, you should (and will). We don't go~ +** Processing line: ~around telling writers that they should give us their books for free,~ +** Processing line: ~and only require payment if we read the entire thing. It's time we stop asking that~ +** Processing line: ~of software products.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +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. +** Processing line: ~That being said, we will _never_ put someone out financially. We have~ +** Processing line: ~income assistance for anyone that can't afford a license to any one of~ +** Processing line: ~our products.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +That being said, we will _never_ put someone out financially. We have income assistance for anyone that can't afford a license to any one of our products. +** Processing line: ~You qualify for a free, unrestricted license to DragonRuby products if~ +** Processing line: ~any of the following items pertain to you:~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +You qualify for a free, unrestricted license to DragonRuby products if any of the following items pertain to you: +** Processing line: ~- Your income is below $2,000.00 (USD) per month.~ +- Line was identified as a list. +*** True Line Result + +** Processing line: ~- You are under 18 years of age.~ +- Line was identified as a list. +*** True Line Result +- Your income is below $2,000.00 (USD) per month. +** Processing line: ~- You are a student of any type: traditional public school, home~ +- Line was identified as a list. +*** True Line Result +- You are under 18 years of age. +** Processing line: ~ schooling, college, bootcamp, or online.~ +** Processing line: ~- You are a teacher, mentor, or parent who wants to teach a kid how to code.~ +- Line was identified as a list. +*** True Line Result +- You are a student of any type: traditional public school, home + schooling, college, bootcamp, or online. +** Processing line: ~- You work/worked in public service or at a charitable organization:~ +- Line was identified as a list. +*** True Line Result +- You are a teacher, mentor, or parent who wants to teach a kid how to code. +** Processing line: ~ for example public office, army, or any 501(c)(3) organization.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +- You work/worked in public service or at a charitable organization: + for example public office, army, or any 501(c)(3) organization. +** Processing line: ~Just contact Amir at [email protected] with a short~ +** Processing line: ~explanation of your current situation and he'll set you up. No~ +** Processing line: ~questions asked.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +Just contact Amir at [email protected] with a short explanation of your current situation and he'll set you up. No questions asked. +** Processing line: ~*** But still, you should offer a free version. So I can try it out and see if I like it.~ +- Header detected. +*** True Line Result + +*** True Line Result +*** But still, you should offer a free version. So I can try it out and see if I like it. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~You can try our [web-based sandbox environment](). But it won't do the~ +** Processing line: ~runtime justice. Or just come to our [Slack]() or [Discord]() channel~ +** Processing line: ~and ask questions. We'd be happy to have a one on one video chat with~ +** Processing line: ~you and show off all the cool stuff we're doing.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +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. +** Processing line: ~Seriously just buy it. Get a refund if you don't like it. We make it~ +** Processing line: ~stupid easy to do so.~ +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result +Seriously just buy it. Get a refund if you don't like it. We make it stupid easy to do so. +** Processing line: ~*** I still think you should do a free version. Think of all people who would give it a shot.~ +- Header detected. +*** True Line Result + +*** True Line Result +*** I still think you should do a free version. Think of all people who would give it a shot. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~Free isn't a sustainable financial model. We don't want to spam your~ +** Processing line: ~email. We don't want to collect usage data off of you either. We just~ +** Processing line: ~want to provide quality toolchains to quality developers (as opposed~ +** Processing line: ~to a large quantity of developers).~ +** Processing line: ~~ +- 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 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 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 + +*** True Line Result +*** What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent. +** Processing line: ~~ +- End of paragraph detected. +*** True Line Result + +** Processing line: ~That won't happen if the development world stop asking for free stuff~ +** Processing line: ~and non-trivially compensate open source developers. Look, we want to be~ +** Processing line: ~able to work on the stuff we love, every day of our lives. And we'll go~ +** Processing line: ~to great lengths to make that happen.~ +** Processing line: ~~ +- 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 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 partnership bylaws state that _all_ DragonRuby IP that can be legally open sourced, will be released under a permissive license. +** Processing line: ~* CHEATSHEET: How To Determine What Frame You Are On~ +- Header detected. +*** True Line Result + +*** True Line Result +* CHEATSHEET: How To Determine What Frame You Are On ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -1519,12 +2390,12 @@ There is a property on ~state~ called ~tick_count~ that is incremented by Dragon - End of paragraph detected. *** True Line Result -** Processing line: ~* How To Get Current Framerate~ +** Processing line: ~* CHEATSHEET: How To Get Current Framerate~ - Header detected. *** True Line Result *** True Line Result -* How To Get Current Framerate +* CHEATSHEET: How To Get Current Framerate ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -1561,12 +2432,12 @@ Current framerate is a top level property on the Game Toolkit Runtime and is acc - End of paragraph detected. *** True Line Result -** Processing line: ~* How To Render A Sprite Using An Array~ +** Processing line: ~* CHEATSHEET: How To Render A Sprite Using An Array~ - Header detected. *** True Line Result *** True Line Result -* How To Render A Sprite Using An Array +* CHEATSHEET: How To Render A Sprite Using An Array ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -1639,12 +2510,12 @@ The following code renders a sprite with a ~width~ and ~height~ of ~100~ in the - End of paragraph detected. *** True Line Result -** Processing line: ~* More Sprite Properties As An Array~ +** Processing line: ~* CHEATSHEET: More Sprite Properties As An Array~ - Header detected. *** True Line Result *** True Line Result -* More Sprite Properties As An Array +* CHEATSHEET: More Sprite Properties As An Array ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -1724,12 +2595,12 @@ Here are all the properties you can set on a sprite. - End of paragraph detected. *** True Line Result -** Processing line: ~* Different Sprite Representations~ +** Processing line: ~* CHEATSHEET: Different Sprite Representations~ - Header detected. *** True Line Result *** True Line Result -* Different Sprite Representations +* CHEATSHEET: Different Sprite Representations ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -1982,12 +2853,12 @@ You can represent a sprite as an ~object~: - End of paragraph detected. *** True Line Result -** Processing line: ~* How To Render A Label~ +** Processing line: ~* CHEATSHEET: How To Render A Label~ - Header detected. *** True Line Result *** True Line Result -* How To Render A Label +* CHEATSHEET: How To Render A Label ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -2038,12 +2909,12 @@ Here is the minimum code: - End of paragraph detected. *** True Line Result -** Processing line: ~* A Colored Label~ +** Processing line: ~* CHEATSHEET: A Colored Label~ - Header detected. *** True Line Result *** True Line Result -* A Colored Label +* CHEATSHEET: A Colored Label ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -2082,12 +2953,12 @@ Here is the minimum code: - End of paragraph detected. *** True Line Result -** Processing line: ~* Extended Label Properties~ +** Processing line: ~* CHEATSHEET: Extended Label Properties~ - Header detected. *** True Line Result *** True Line Result -* Extended Label Properties +* CHEATSHEET: Extended Label Properties ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -2183,12 +3054,12 @@ A ~SIZE_ENUM~ of ~0~ represents "default size". A ~negative~ value will decrease - End of paragraph detected. *** True Line Result An ~ALIGNMENT_ENUM~ of ~0~ represents "left aligned". ~1~ represents "center aligned". ~2~ represents "right aligned". -** Processing line: ~* Rendering A Label As A ~Hash~~ +** Processing line: ~* CHEATSHEET: Rendering A Label As A ~Hash~~ - Header detected. *** True Line Result *** True Line Result -* Rendering A Label As A ~Hash~ +* CHEATSHEET: Rendering A Label As A ~Hash~ ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -2296,12 +3167,12 @@ You can add additional metadata about your game within a label, which requires y - End of paragraph detected. *** True Line Result -** Processing line: ~* Getting The Size Of A Piece Of Text~ +** Processing line: ~* CHEATSHEET: Getting The Size Of A Piece Of Text~ - Header detected. *** True Line Result *** True Line Result -* Getting The Size Of A Piece Of Text +* CHEATSHEET: Getting The Size Of A Piece Of Text ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -2381,12 +3252,12 @@ You can get the render size of any string using ~args.gtk.calcstringbox~. - End of paragraph detected. *** True Line Result -** Processing line: ~* How To Play A Sound~ +** Processing line: ~* CHEATSHEET: How To Play A Sound~ - Header detected. *** True Line Result *** True Line Result -* How To Play A Sound +* CHEATSHEET: How To Play A Sound ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -2520,12 +3391,12 @@ If you want to play a ~.ogg~ once as if it were a sound effect, you can do: - End of paragraph detected. *** True Line Result -** Processing line: ~* Using ~args.state~ To Store Your Game State~ +** Processing line: ~* CHEATSHEET: Using ~args.state~ To Store Your Game State~ - Header detected. *** True Line Result *** True Line Result -* Using ~args.state~ To Store Your Game State +* CHEATSHEET: Using ~args.state~ To Store Your Game State ** Processing line: ~~ - End of paragraph detected. *** True Line Result @@ -2655,360 +3526,46 @@ To assign a value every frame, just use the ~=~ operator, but _make sure_ you've - End of paragraph detected. *** True Line Result -** Processing line: ~* Frequently Asked Questions, Comments, and Concerns~ -- Header detected. -*** True Line Result - -*** True Line Result -* Frequently Asked Questions, Comments, and Concerns -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~Here are questions, comments, and concerns that frequently come~ -** Processing line: ~up.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -Here are questions, comments, and concerns that frequently come up. -** Processing line: ~** Frequently Asked Questions~ -- Header detected. -*** True Line Result - -*** True Line Result -** Frequently Asked Questions -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~*** What is DragonRuby LLP?~ -- Header detected. -*** True Line Result - -*** True Line Result -*** What is DragonRuby LLP? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~DragonRuby LLP is a partnership of four devs who came together~ -** Processing line: ~with the goal of bringing the aesthetics and joy of Ruby, everywhere possible.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -DragonRuby LLP is a partnership of four devs who came together with the goal of bringing the aesthetics and joy of Ruby, everywhere possible. -** Processing line: ~Under DragonRuby LLP, we offer a number of products (with more on the~ -** Processing line: ~way):~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -Under DragonRuby LLP, we offer a number of products (with more on the way): -** Processing line: ~- Game Toolkit (GTK): A 2D game engine that is compatible with modern~ -- Line was identified as a list. -*** True Line Result - -** Processing line: ~ gaming platforms.~ -** Processing line: ~- RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile~ -- Line was identified as a list. -*** True Line Result -- Game Toolkit (GTK): A 2D game engine that is compatible with modern - gaming platforms. -** Processing line: ~ apps. [[http://rubymotion.com]]~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -- RubyMotion (RM): A compiler toolchain that allows you to build native, cross-platform mobile - apps. [[http://rubymotion.com]] -** Processing line: ~All of the products above leverage a shared core called DragonRuby.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -All of the products above leverage a shared core called DragonRuby. -** Processing line: ~NOTE: From an official branding standpoint each one of the products is~ -** Processing line: ~suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is~ -** Processing line: ~_one word, title cased_.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -NOTE: From an official branding standpoint each one of the products is suffixed with "A DragonRuby LLP Product" tagline. Also, DragonRuby is _one word, title cased_. -** Processing line: ~NOTE: We leave the "A DragonRuby LLP Product" off of this one because~ -** Processing line: ~that just sounds really weird.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -NOTE: We leave the "A DragonRuby LLP Product" off of this one because that just sounds really weird. -** Processing line: ~NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass~ -** Processing line: ~identifier huh?~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -NOTE: Devs who use DragonRuby are "Dragon Riders/Riders of Dragons". That's a bad ass identifier huh? -** Processing line: ~*** What is DragonRuby?~ -- Header detected. -*** True Line Result - -*** True Line Result -*** What is DragonRuby? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~The response to this question requires a few subparts. First we need~ -** Processing line: ~to clarify some terms. Specifically _language specification_ vs _runtime_.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -The response to this question requires a few subparts. First we need to clarify some terms. Specifically _language specification_ vs _runtime_. -** Processing line: ~**** Okay... so what is the difference between a language specification and a runtime?~ -- Header detected. -*** True Line Result - -*** True Line Result -**** Okay... so what is the difference between a language specification and a runtime? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** 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 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: ~~ -- End of paragraph detected. -*** True Line Result -But, there are many Ruby Runtimes: CRuby/MRI, JRuby, Truffle, Rubinius, Artichoke, and (last but certainly not least) DragonRuby. -** Processing line: ~**** Okay... what language specification does DragonRuby use then?~ -- Header detected. -*** True Line Result - -*** True Line Result -**** Okay... what language specification does DragonRuby use then? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~DragonRuby's goal is to be compliant with the ISO/IEC 30170:2012 standard. It's~ -** Processing line: ~syntax is Ruby 2.x compatible, but also contains semantic changes that help~ -** Processing line: ~it natively interface with platform specific libraries.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -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. -** Processing line: ~**** So... why another runtime?~ -- Header detected. -*** True Line Result - -*** True Line Result -**** So... why another runtime? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~The elevator pitch is:~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -The elevator pitch is: -** Processing line: ~DragonRuby is a Multilevel Cross-platform Runtime. The "multiple levels"~ -** Processing line: ~within the runtime allows us to target platforms no other Ruby can~ -** Processing line: ~target: PC, Mac, Linux, Raspberry Pi, WASM, iOS, Android, Nintendo~ -** Processing line: ~Switch, PS4, Xbox, and Scadia.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -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. -** Processing line: ~**** What does Multilevel Cross-platform mean?~ +** Processing line: ~* CHEATSHEET: Troubleshoot Performance~ - Header detected. *** True Line Result *** True Line Result -**** What does Multilevel Cross-platform mean? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~There are complexities associated with targeting all the platforms we~ -** Processing line: ~support. Because of this, the runtime had to be architected in such a~ -** Processing line: ~way that new platforms could be easily added (which lead to us partitioning the~ -** Processing line: ~runtime internally):~ +* CHEATSHEET: Troubleshoot Performance ** Processing line: ~~ - End of paragraph detected. *** True Line Result -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): -** Processing line: ~- Level 1 we leverage a good portion of mRuby.~ -- Line was identified as a list. -*** True Line Result - -** Processing line: ~- Level 2 consists of optimizations to mRuby we've made given that our~ -- Line was identified as a list. -*** True Line Result -- Level 1 we leverage a good portion of mRuby. -** Processing line: ~ target platforms are well known.~ -** Processing line: ~- Level 3 consists of portable C libraries and their Ruby~ -- Line was identified as a list. -*** True Line Result -- Level 2 consists of optimizations to mRuby we've made given that our - target platforms are well known. -** Processing line: ~ C-Extensions.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -- 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: ~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 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 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 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 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 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 - -** Processing line: ~ compiler outputs _very_ fast platform specific bitcode, but only~ -** Processing line: ~ supports a subset of the Ruby language specification.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -- 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. -** 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 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 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 - -*** True Line Result -**** Cool cool. So given that I understand everything to this point, can we answer the original question? What is DragonRuby? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** 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 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: ~*** How is DragonRuby different than MRI?~ -- Header detected. -*** True Line Result -*** True Line Result -*** How is DragonRuby different than MRI? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~DragonRuby supports a subset of MRI apis. Our target is to support all~ -** Processing line: ~of mRuby's standard lib. There are challenges to this given the number~ -** Processing line: ~of platforms we are trying to support (specifically console).~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -DragonRuby supports a subset of MRI apis. Our target is to support all of mRuby's standard lib. There are challenges to this given the number of platforms we are trying to support (specifically console). -** Processing line: ~**** Does DragonRuby support Gems?~ -- Header detected. -*** True Line Result - -*** True Line Result -**** Does DragonRuby support Gems? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~DragonRuby does not support gems because that requires the~ -** Processing line: ~installation of MRI Ruby on the developer's machine (which is a~ -** Processing line: ~non-starter given that we want DragonRuby to be a zero dependency~ -** Processing line: ~runtime). While this seems easy for Mac and Linux, it is much harder~ -** Processing line: ~on Windows and Raspberry Pi. mRuby has taken the approach of having a~ -** Processing line: ~git repository for compatible gems and we will most likely follow~ -** Processing line: ~suite: [[https://github.com/mruby/mgem-list]].~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -DragonRuby does not support gems because that requires the installation of MRI Ruby on the developer's machine (which is a non-starter given that we want DragonRuby to be a zero dependency runtime). While this seems easy for Mac and Linux, it is much harder on Windows and Raspberry Pi. mRuby has taken the approach of having a git repository for compatible gems and we will most likely follow suite: [[https://github.com/mruby/mgem-list]]. -** Processing line: ~**** Does DragonRuby have a REPL/IRB?~ -- Header detected. -*** True Line Result - -*** True Line Result -**** Does DragonRuby have a REPL/IRB? -** Processing line: ~~ -- End of paragraph detected. +** Processing line: ~1. If you're using ~Array~s for your primitives (~args.outputs.sprites << []~), use ~Hash~ instead (~args.outputs.sprites << { x: ... }~).~ +- Line was identified as a start of a list. *** True Line Result -** Processing line: ~You can use DragonRuby's Console within the game to inspect object and~ -** Processing line: ~execute small pieces of code. For more complex pieces of code create a~ -** Processing line: ~file called ~repl.rb~ and put it in ~mygame/app/repl.rb~:~ -** Processing line: ~~ -- End of paragraph detected. +** Processing line: ~2. If you're using ~Entity~ for your primitives (~args.outputs.sprites << args.state.new_entity~), use ~StrictEntity~ instead (~args.outputs.sprites << args.state.new_entity_strict~).~ +- Line was identified as a continuation of a list. *** True Line Result -You can use DragonRuby's Console within the game to inspect object and execute small pieces of code. For more complex pieces of code create a file called ~repl.rb~ and put it in ~mygame/app/repl.rb~: -** Processing line: ~- Any code you write in there will be executed when you change the file. You can organize different pieces of code using the ~repl~ method:~ -- Line was identified as a list. +1. If you're using ~Array~s for your primitives (~args.outputs.sprites << []~), use ~Hash~ instead (~args.outputs.sprites << { x: ... }~). +** Processing line: ~3. Use ~.each~ instead of ~.map~ if you don't care about the return value.~ +- Line was identified as a continuation of a list. *** True Line Result - -** Processing line: ~~ -- End of paragraph detected. +2. If you're using ~Entity~ for your primitives (~args.outputs.sprites << args.state.new_entity~), use ~StrictEntity~ instead (~args.outputs.sprites << args.state.new_entity_strict~). +** Processing line: ~4. When concatenating primitives to outputs, do them in bulk. Instead of:~ +- Line was identified as a continuation of a list. *** True Line Result -- Any code you write in there will be executed when you change the file. You can organize different pieces of code using the ~repl~ method: +3. Use ~.each~ instead of ~.map~ if you don't care about the return value. ** Processing line: ~#+begin_src ruby~ - Line was identified as the beginning of a code block. *** True Line Result - +4. When concatenating primitives to outputs, do them in bulk. Instead of: *** True Line Result #+begin_src ruby -** Processing line: ~ repl do~ +** Processing line: ~ args.state.bullets.each do |bullet|~ - Inside source: true *** True Line Result - repl do -** Processing line: ~ puts "hello world"~ + args.state.bullets.each do |bullet| +** Processing line: ~ args.outputs.sprites << bullet.sprite~ - Inside source: true *** True Line Result - puts "hello world" -** Processing line: ~ puts 1 + 1~ -- Inside source: true -*** True Line Result - puts 1 + 1 + args.outputs.sprites << bullet.sprite ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -3017,105 +3574,21 @@ You can use DragonRuby's Console within the game to inspect object and execute s - 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: ~- If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal).~ -- Line was identified as a list. -*** True Line Result - -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -- If you use the `repl` method, the code will be executed and the DragonRuby Console will automatically open so you can see the results (on Mac and Linux, the results will also be printed to the terminal). -** Processing line: ~- All ~puts~ statements will also be saved to ~logs/log.txt~. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can ~tail~ this file.~ -- Line was identified as a list. -*** True Line Result - -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -- All ~puts~ statements will also be saved to ~logs/log.txt~. So if you want to stay in your editor and not look at the terminal, or the DragonRuby Console, you can ~tail~ this file. -** Processing line: ~4. To ignore code in ~repl.rb~, instead of commenting it out, prefix ~repl~ with the letter ~x~ and it'll be ignored.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -4. To ignore code in ~repl.rb~, instead of commenting it out, prefix ~repl~ with the letter ~x~ and it'll be ignored. -** Processing line: ~#+begin_src ruby~ +** Processing line: ~do~ +** Processing line: ~#+begin_src~ - Line was identified as the beginning of a code block. *** True Line Result - -*** True Line Result -#+begin_src ruby -** Processing line: ~ xrepl do # <------- line is prefixed with an "x"~ -- Inside source: true -*** True Line Result - xrepl do # <------- line is prefixed with an "x" -** Processing line: ~ puts "hello world"~ -- Inside source: true -*** True Line Result - puts "hello world" -** Processing line: ~ puts 1 + 1~ -- Inside source: true -*** True Line Result - puts 1 + 1 -** Processing line: ~ end~ -- Inside source: true -*** True Line Result - end -** Processing line: ~~ -- Inside source: true -*** True Line Result - -** Processing line: ~ # This code will be executed when you save the file.~ -- Inside source: true -*** True Line Result - # This code will be executed when you save the file. -** Processing line: ~ repl do~ -- Inside source: true +do *** True Line Result - repl do -** Processing line: ~ puts "Hello"~ -- Inside source: true -*** True Line Result - puts "Hello" -** Processing line: ~ end~ -- Inside source: true -*** True Line Result - end -** Processing line: ~~ -- Inside source: true -*** True Line Result - -** Processing line: ~ repl do~ -- Inside source: true -*** True Line Result - repl do -** Processing line: ~ puts "This code will also be executed."~ -- Inside source: true -*** True Line Result - puts "This code will also be executed." -** Processing line: ~ end~ -- Inside source: true -*** True Line Result - end -** Processing line: ~~ +#+begin_src +** Processing line: ~ args.outputs.sprites << args.state.bullets.map do |b|~ - Inside source: true *** True Line Result - -** Processing line: ~ # use xrepl to "comment out" code~ + args.outputs.sprites << args.state.bullets.map do |b| +** Processing line: ~ b.sprite~ - Inside source: true *** True Line Result - # use xrepl to "comment out" code -** Processing line: ~ xrepl do~ -- Inside source: true -*** True Line Result - xrepl do -** Processing line: ~ puts "This code will not be executed because of the x infront of repl".~ -- Inside source: true -*** True Line Result - puts "This code will not be executed because of the x infront of repl". + b.sprite ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -3124,340 +3597,18 @@ You can use DragonRuby's Console within the game to inspect object and execute s - 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: ~**** Does DragonRuby support ~pry~ or have any other debugging facilities?~ -- Header detected. -*** True Line Result - -*** True Line Result -**** Does DragonRuby support ~pry~ or have any other debugging facilities? -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~~pry~ is a gem that assumes you are using the MRI Runtime (which is~ -** Processing line: ~incompatible with DragonRuby). Eventually DragonRuby will have a pry~ -** Processing line: ~based experience that is compatible with a debugging infrastructure~ -** Processing line: ~called LLDB. Take the time to read about LLDB as it shows the~ -** Processing line: ~challenges in creating something that is compatible.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -~pry~ is a gem that assumes you are using the MRI Runtime (which is incompatible with DragonRuby). Eventually DragonRuby will have a pry based experience that is compatible with a debugging infrastructure called LLDB. Take the time to read about LLDB as it shows the challenges in creating something that is compatible. -** Processing line: ~You can use DragonRuby's replay capabilities to troubleshoot:~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -You can use DragonRuby's replay capabilities to troubleshoot: -** Processing line: ~1. DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added).~ -- Line was identified as a start of a list. -*** True Line Result - -** Processing line: ~2. Use ~./dragonruby mygame --record~ to create a game play recording that you can use to find the exception (you can replay a recoding by executing ~./dragonruby mygame --replay last_replay.txt~ or through the DragonRuby Console using ~$gtk.recording.start_replay "last_replay.txt"~.~ -- Line was identified as a continuation of a list. -*** True Line Result -1. DragonRuby is hot loaded which gives you a very fast feedback loop (if the game throws an exception, it's because of the code you just added). -** Processing line: ~3. DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: ~./dragonruby . --eval some_ruby_file.rb --no-tick~.~ -- Line was identified as a continuation of a list. -*** True Line Result -2. Use ~./dragonruby mygame --record~ to create a game play recording that you can use to find the exception (you can replay a recoding by executing ~./dragonruby mygame --replay last_replay.txt~ or through the DragonRuby Console using ~$gtk.recording.start_replay "last_replay.txt"~. -** Processing line: ~4. Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to ~args.outputs.debug~ that will render on top of your game but will be ignored in a production release.~ +** Processing line: ~5. Use ~args.outputs.static_~ variant for things that don't change often (take a look at the Basic Gorillas sample app and Dueling Starships sample app to see how ~static_~ is leveraged.~ - Line was identified as a continuation of a list. *** True Line Result -3. DragonRuby also ships with a unit testing facility. You can invoke the following command to run a test: ~./dragonruby . --eval some_ruby_file.rb --no-tick~. -** Processing line: ~5. Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago.~ -- Line was identified as a continuation of a list. -*** True Line Result -4. Get into the habit of adding debugging facilities within the game itself. You can add drawing primitives to ~args.outputs.debug~ that will render on top of your game but will be ignored in a production release. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -5. Debugging something that runs at 60fps is (imo) not that helpful. The exception you are seeing could have been because of a change that occurred many frames ago. -** Processing line: ~** Frequent Comments About Ruby as a Language Choice~ -- Header detected. -*** True Line Result - -*** True Line Result -** Frequent Comments About Ruby as a Language Choice -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~*** But Ruby is dead.~ -- Header detected. -*** True Line Result +** Processing line: ~6. Consider using a ~render_target~ if you're doing some form of a camera that moves a lot of primitives (take a look at the Render Target sample apps for more info).~ +- Line was identified as a continuation of a list. *** True Line Result -*** But Ruby is dead. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~Let's check the official source for the answer to this question:~ -** Processing line: ~isrubydead.com: [[https://isrubydead.com/]].~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -Let's check the official source for the answer to this question: isrubydead.com: [[https://isrubydead.com/]]. -** Processing line: ~On a more serious note, Ruby's _quantity_ levels aren't what they used~ -** Processing line: ~to be. And that's totally fine. Every one chases the new and shiny.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -On a more serious note, Ruby's _quantity_ levels aren't what they used to be. And that's totally fine. Every one chases the new and shiny. -** Processing line: ~What really matters is _quality/maturity_. Here is the latest (StackOverflow~ -** Processing line: ~Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies].~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -What really matters is _quality/maturity_. Here is the latest (StackOverflow Survey sorted by highest paid developers)[https://insights.stackoverflow.com/survey/2019#top-paying-technologies]. -** Processing line: ~Let's stop making this comment shall we?~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -Let's stop making this comment shall we? -** Processing line: ~*** But Ruby is slow.~ -- Header detected. -*** True Line Result - -*** True Line Result -*** But Ruby is slow. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~That doesn't make any sense. A language specification can't be~ -** Processing line: ~slow... it's a language spec. Sure, an _implementation/runtime_ can be slow though, but then we'd~ -** Processing line: ~have to talk about which runtime.~ -** Processing line: ~~ -- 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 languages are slow.~ -- Header detected. -*** True Line Result - -*** True Line Result -*** Dynamic languages are slow. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~They are certainly slower than statically compiled languages. With the~ -** Processing line: ~processing power and compiler optimizations we have today,~ -** Processing line: ~dynamic languages like Ruby are _fast enough_.~ -** Processing line: ~~ -- End of paragraph detected. -*** 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 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 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 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 skill set. Email us ^_^. -** Processing line: ~** Frequent Concerns~ -- Header detected. -*** True Line Result - -*** True Line Result -** Frequent Concerns -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~*** DragonRuby is not open source. That's not right.~ -- Header detected. -*** True Line Result - -*** True Line Result -*** DragonRuby is not open source. That's not right. -** Processing line: ~~ -- End of paragraph detected. -*** 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 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 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: ~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 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. -*** True Line Result -If you have ideas on how we can do this, email us! -** Processing line: ~If the reason above isn't sufficient, then definitely use something else.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -If the reason above isn't sufficient, then definitely use something else. -** Processing line: ~All this being said, we do have parts of the engine open sourced on GitHub: [[https://github.com/dragonruby/dragonruby-game-toolkit-contrib/]]~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -All this being said, we do have parts of the engine open sourced on GitHub: [[https://github.com/dragonruby/dragonruby-game-toolkit-contrib/]] -** Processing line: ~*** DragonRuby is for pay. You should offer a free version.~ -- Header detected. -*** True Line Result - -*** True Line Result -*** DragonRuby is for pay. You should offer a free version. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~If you can afford to pay for DragonRuby, you should (and will). We don't go~ -** Processing line: ~around telling writers that they should give us their books for free,~ -** Processing line: ~and only require payment if we read the entire thing. It's time we stop asking that~ -** Processing line: ~of software products.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -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. -** Processing line: ~That being said, we will _never_ put someone out financially. We have~ -** Processing line: ~income assistance for anyone that can't afford a license to any one of~ -** Processing line: ~our products.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -That being said, we will _never_ put someone out financially. We have income assistance for anyone that can't afford a license to any one of our products. -** Processing line: ~You qualify for a free, unrestricted license to DragonRuby products if~ -** Processing line: ~any of the following items pertain to you:~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -You qualify for a free, unrestricted license to DragonRuby products if any of the following items pertain to you: -** Processing line: ~- Your income is below $2,000.00 (USD) per month.~ -- Line was identified as a list. -*** True Line Result - -** Processing line: ~- You are under 18 years of age.~ -- Line was identified as a list. -*** True Line Result -- Your income is below $2,000.00 (USD) per month. -** Processing line: ~- You are a student of any type: traditional public school, home~ -- Line was identified as a list. -*** True Line Result -- You are under 18 years of age. -** Processing line: ~ schooling, college, bootcamp, or online.~ -** Processing line: ~- You are a teacher, mentor, or parent who wants to teach a kid how to code.~ -- Line was identified as a list. -*** True Line Result -- You are a student of any type: traditional public school, home - schooling, college, bootcamp, or online. -** Processing line: ~- You work/worked in public service or at a charitable organization:~ -- Line was identified as a list. -*** True Line Result -- You are a teacher, mentor, or parent who wants to teach a kid how to code. -** Processing line: ~ for example public office, army, or any 501(c)(3) organization.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -- You work/worked in public service or at a charitable organization: - for example public office, army, or any 501(c)(3) organization. -** Processing line: ~Just contact Amir at [email protected] with a short~ -** Processing line: ~explanation of your current situation and he'll set you up. No~ -** Processing line: ~questions asked.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -Just contact Amir at [email protected] with a short explanation of your current situation and he'll set you up. No questions asked. -** Processing line: ~*** But still, you should offer a free version. So I can try it out and see if I like it.~ -- Header detected. -*** True Line Result - -*** True Line Result -*** But still, you should offer a free version. So I can try it out and see if I like it. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~You can try our [web-based sandbox environment](). But it won't do the~ -** Processing line: ~runtime justice. Or just come to our [Slack]() or [Discord]() channel~ -** Processing line: ~and ask questions. We'd be happy to have a one on one video chat with~ -** Processing line: ~you and show off all the cool stuff we're doing.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -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. -** Processing line: ~Seriously just buy it. Get a refund if you don't like it. We make it~ -** Processing line: ~stupid easy to do so.~ -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result -Seriously just buy it. Get a refund if you don't like it. We make it stupid easy to do so. -** Processing line: ~*** I still think you should do a free version. Think of all people who would give it a shot.~ -- Header detected. -*** True Line Result - -*** True Line Result -*** I still think you should do a free version. Think of all people who would give it a shot. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~Free isn't a sustainable financial model. We don't want to spam your~ -** Processing line: ~email. We don't want to collect usage data off of you either. We just~ -** Processing line: ~want to provide quality toolchains to quality developers (as opposed~ -** Processing line: ~to a large quantity of developers).~ -** Processing line: ~~ -- 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 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 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 - -*** True Line Result -*** What if I build something with DragonRuby, but DragonRuby LLP becomes insolvent. -** Processing line: ~~ -- End of paragraph detected. -*** True Line Result - -** Processing line: ~That won't happen if the development world stop asking for free stuff~ -** Processing line: ~and non-trivially compensate open source developers. Look, we want to be~ -** Processing line: ~able to work on the stuff we love, every day of our lives. And we'll go~ -** Processing line: ~to great lengths to make that happen.~ -** Processing line: ~~ -- 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 partnership bylaws state that~ -** Processing line: ~_all_ DragonRuby IP that can be legally open sourced, will be released~ -** Processing line: ~under a permissive license.~ +5. Use ~args.outputs.static_~ variant for things that don't change often (take a look at the Basic Gorillas sample app and Dueling Starships sample app to see how ~static_~ is leveraged. ** Processing line: ~~ - End of paragraph detected. *** True Line Result -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. +6. Consider using a ~render_target~ if you're doing some form of a camera that moves a lot of primitives (take a look at the Render Target sample apps for more info). ** Processing line: ~* DOCS: ~GTK::Runtime~~ - Header detected. *** True Line Result @@ -3469,28 +3620,19 @@ But, in the event that sad day comes, our partnership bylaws state that _all_ Dr - 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~~ +** Processing line: ~* DOCS: ~GTK::Runtime#write_file~~ - Header detected. *** True Line Result *** True Line Result -* DOCS: ~GTK::Runtime#calcstringbox~ -** Processing line: ~This function returns the width and height of a string.~ +* DOCS: ~GTK::Runtime#write_file~ +** Processing line: ~This function takes in two parameters. The first paramter is the file path and assumes the the game~ +** Processing line: ~directory is the root. The second parameter is the string that will be written. The method overwrites whatever~ +** Processing line: ~is currently in the file. Use ~GTK::Runtime#append_file~ to append to the file as opposed to overwriting.~ ** Processing line: ~~ - End of paragraph detected. *** True Line Result -This function returns the width and height of a string. +This function takes in two parameters. The first paramter is the file path and assumes the the game directory is the root. The second parameter is the string that will be written. The method overwrites whatever is currently in the file. Use ~GTK::Runtime#append_file~ to append to the file as opposed to overwriting. ** Processing line: ~#+begin_src ruby~ - Line was identified as the beginning of a code block. *** True Line Result @@ -3501,14 +3643,18 @@ This function returns the width and height of a string. - Inside source: true *** True Line Result def tick args -** Processing line: ~ args.state.string_size ||= args.gtk.calcstringbox "Hello World"~ +** Processing line: ~ if args.inputs.mouse.click~ - Inside source: true *** True Line Result - args.state.string_size ||= args.gtk.calcstringbox "Hello World" -** Processing line: ~ args.state.string_size_font_size ||= args.gtk.calcstringbox "Hello World"~ + if args.inputs.mouse.click +** Processing line: ~ args.gtk.write_file "last-mouse-click.txt", "Mouse was clicked at #{args.state.tick_count}."~ - Inside source: true *** True Line Result - args.state.string_size_font_size ||= args.gtk.calcstringbox "Hello World" + args.gtk.write_file "last-mouse-click.txt", "Mouse was clicked at #{args.state.tick_count}." +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -3521,19 +3667,17 @@ This function returns the width and height of a string. - End of paragraph detected. *** True Line Result -** Processing line: ~* DOCS: ~GTK::Runtime#write_file~~ +** Processing line: ~* DOCS: ~GTK::Runtime#calcstringbox~~ - Header detected. *** True Line Result *** True Line Result -* DOCS: ~GTK::Runtime#write_file~ -** Processing line: ~This function takes in two parameters. The first paramter is the file path and assumes the the game~ -** Processing line: ~directory is the root. The second parameter is the string that will be written. The method overwrites whatever~ -** Processing line: ~is currently in the file. Use ~GTK::Runtime#append_file~ to append to the file as opposed to overwriting.~ +* DOCS: ~GTK::Runtime#calcstringbox~ +** Processing line: ~This function returns the width and height of a string.~ ** Processing line: ~~ - End of paragraph detected. *** True Line Result -This function takes in two parameters. The first paramter is the file path and assumes the the game directory is the root. The second parameter is the string that will be written. The method overwrites whatever is currently in the file. Use ~GTK::Runtime#append_file~ to append to the file as opposed to overwriting. +This function returns the width and height of a string. ** Processing line: ~#+begin_src ruby~ - Line was identified as the beginning of a code block. *** True Line Result @@ -3544,18 +3688,14 @@ This function takes in two parameters. The first paramter is the file path and a - Inside source: true *** True Line Result def tick args -** Processing line: ~ if args.inputs.mouse.click~ -- Inside source: true -*** True Line Result - if args.inputs.mouse.click -** Processing line: ~ args.gtk.write_file "last-mouse-click.txt", "Mouse was clicked at #{args.state.tick_count}."~ +** Processing line: ~ args.state.string_size ||= args.gtk.calcstringbox "Hello World"~ - Inside source: true *** True Line Result - args.gtk.write_file "last-mouse-click.txt", "Mouse was clicked at #{args.state.tick_count}." -** Processing line: ~ end~ + args.state.string_size ||= args.gtk.calcstringbox "Hello World" +** Processing line: ~ args.state.string_size_font_size ||= args.gtk.calcstringbox "Hello World"~ - Inside source: true *** True Line Result - end + args.state.string_size_font_size ||= args.gtk.calcstringbox "Hello World" ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -3568,6 +3708,17 @@ This function takes in two parameters. The first paramter is the file path and a - 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 @@ -12468,6 +12619,666 @@ Follows is a source code listing for all files that have been open sourced. This - End of paragraph detected. *** True Line Result +** Processing line: ~* Rendering Basics - Audio Mixer - main.rb~ +- Header detected. +*** True Line Result + +*** True Line Result +* Rendering Basics - Audio Mixer - main.rb +** 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: ~ # ./samples/01_rendering_basics/06_audio_mixer/app/main.rb~ +- Inside source: true +*** True Line Result + # ./samples/01_rendering_basics/06_audio_mixer/app/main.rb +** Processing line: ~ $gtk.reset~ +- Inside source: true +*** True Line Result + $gtk.reset +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ $boxsize = 30~ +- Inside source: true +*** True Line Result + $boxsize = 30 +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def render_sources args~ +- Inside source: true +*** True Line Result + def render_sources args +** Processing line: ~ mouse_in_panel = (args.state.selected != 0) && args.inputs.mouse.position.inside_rect?([900, 450, 340, 250])~ +- Inside source: true +*** True Line Result + mouse_in_panel = (args.state.selected != 0) && args.inputs.mouse.position.inside_rect?([900, 450, 340, 250]) +** Processing line: ~ mouse_new_down = (args.state.mouse_held == 1)~ +- Inside source: true +*** True Line Result + mouse_new_down = (args.state.mouse_held == 1) +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ if (mouse_new_down && !mouse_in_panel)~ +- Inside source: true +*** True Line Result + if (mouse_new_down && !mouse_in_panel) +** Processing line: ~ args.state.selected = 0 # will reset below if we hit something.~ +- Inside source: true +*** True Line Result + args.state.selected = 0 # will reset below if we hit something. +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ args.audio.keys.each { |k|~ +- Inside source: true +*** True Line Result + args.audio.keys.each { |k| +** Processing line: ~ s = args.audio[k]~ +- Inside source: true +*** True Line Result + s = args.audio[k] +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ if (mouse_new_down) && !mouse_in_panel && args.inputs.mouse.position.inside_rect?([s[:screenx], s[:screeny], $boxsize, $boxsize])~ +- Inside source: true +*** True Line Result + if (mouse_new_down) && !mouse_in_panel && args.inputs.mouse.position.inside_rect?([s[:screenx], s[:screeny], $boxsize, $boxsize]) +** Processing line: ~ args.state.selected = k~ +- Inside source: true +*** True Line Result + args.state.selected = k +** Processing line: ~ args.state.dragging_source = true~ +- Inside source: true +*** True Line Result + args.state.dragging_source = true +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ isselected = (k == args.state.selected)~ +- Inside source: true +*** True Line Result + isselected = (k == args.state.selected) +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ if isselected && args.state.dragging_source~ +- Inside source: true +*** True Line Result + if isselected && args.state.dragging_source +** Processing line: ~ # you can hang anything on the audio hashes you want, so we store the~ +- Inside source: true +*** True Line Result + # you can hang anything on the audio hashes you want, so we store the +** Processing line: ~ # actual screen position so it doesn't scale weirdly vs your mouse.~ +- Inside source: true +*** True Line Result + # actual screen position so it doesn't scale weirdly vs your mouse. +** Processing line: ~ s[:screenx] = args.inputs.mouse.x - ($boxsize / 2)~ +- Inside source: true +*** True Line Result + s[:screenx] = args.inputs.mouse.x - ($boxsize / 2) +** Processing line: ~ s[:screeny] = args.inputs.mouse.y - ($boxsize / 2)~ +- Inside source: true +*** True Line Result + s[:screeny] = args.inputs.mouse.y - ($boxsize / 2) +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ s[:screeny] = 50 if s[:screeny] < 50~ +- Inside source: true +*** True Line Result + s[:screeny] = 50 if s[:screeny] < 50 +** Processing line: ~ s[:screeny] = (719 - $boxsize) if s[:screeny] > (719 - $boxsize)~ +- Inside source: true +*** True Line Result + s[:screeny] = (719 - $boxsize) if s[:screeny] > (719 - $boxsize) +** Processing line: ~ s[:screenx] = 0 if s[:screenx] < 0~ +- Inside source: true +*** True Line Result + s[:screenx] = 0 if s[:screenx] < 0 +** Processing line: ~ s[:screenx] = (1279 - $boxsize) if s[:screenx] > (1279 - $boxsize)~ +- Inside source: true +*** True Line Result + s[:screenx] = (1279 - $boxsize) if s[:screenx] > (1279 - $boxsize) +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ s[:x] = ((s[:screenx] / 1279.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range~ +- Inside source: true +*** True Line Result + s[:x] = ((s[:screenx] / 1279.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range +** Processing line: ~ s[:y] = ((s[:screeny] / 719.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range~ +- Inside source: true +*** True Line Result + s[:y] = ((s[:screeny] / 719.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ color = isselected ? [ 0, 255, 0, 255 ] : [ 0, 0, 255, 255 ]~ +- Inside source: true +*** True Line Result + color = isselected ? [ 0, 255, 0, 255 ] : [ 0, 0, 255, 255 ] +** Processing line: ~ args.outputs.primitives << [s[:screenx], s[:screeny], $boxsize, $boxsize, *color].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [s[:screenx], s[:screeny], $boxsize, $boxsize, *color].solid +** Processing line: ~ }~ +- Inside source: true +*** True Line Result + } +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def render_panel args~ +- Inside source: true +*** True Line Result + def render_panel args +** Processing line: ~ s = args.audio[args.state.selected]~ +- Inside source: true +*** True Line Result + s = args.audio[args.state.selected] +** Processing line: ~ return if s.nil?~ +- Inside source: true +*** True Line Result + return if s.nil? +** Processing line: ~ mouse_down = (args.state.mouse_held > 0)~ +- Inside source: true +*** True Line Result + mouse_down = (args.state.mouse_held > 0) +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ args.outputs.primitives << [900, 450, 340, 250, 127, 127, 200, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [900, 450, 340, 250, 127, 127, 200, 255].solid +** Processing line: ~ args.outputs.primitives << [1075, 690, "Source ##{args.state.selected}", 3, 1, 255, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [1075, 690, "Source ##{args.state.selected}", 3, 1, 255, 255, 255].label +** Processing line: ~ args.outputs.primitives << [910, 660, 1230, 660, 255, 255, 255].line~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [910, 660, 1230, 660, 255, 255, 255].line +** Processing line: ~ args.outputs.primitives << [910, 650, "screen: (#{s[:screenx].to_i}, #{s[:screeny].to_i})", 0, 0, 255, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [910, 650, "screen: (#{s[:screenx].to_i}, #{s[:screeny].to_i})", 0, 0, 255, 255, 255].label +** Processing line: ~ args.outputs.primitives << [910, 625, "position: (#{s[:x].round(5).to_s[0..6]}, #{s[:y].round(5).to_s[0..6]})", 0, 0, 255, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [910, 625, "position: (#{s[:x].round(5).to_s[0..6]}, #{s[:y].round(5).to_s[0..6]})", 0, 0, 255, 255, 255].label +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ slider = [1022, 586, 200, 7]~ +- Inside source: true +*** True Line Result + slider = [1022, 586, 200, 7] +** Processing line: ~ if mouse_down && args.inputs.mouse.position.inside_rect?(slider)~ +- Inside source: true +*** True Line Result + if mouse_down && args.inputs.mouse.position.inside_rect?(slider) +** Processing line: ~ s[:pitch] = ((args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0)) * 2.0~ +- Inside source: true +*** True Line Result + s[:pitch] = ((args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0)) * 2.0 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ slidercolor = (s[:pitch] / 2.0) * 255~ +- Inside source: true +*** True Line Result + slidercolor = (s[:pitch] / 2.0) * 255 +** Processing line: ~ args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid +** Processing line: ~ args.outputs.primitives << [910, 600, "pitch: #{s[:pitch].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [910, 600, "pitch: #{s[:pitch].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ slider = [1022, 561, 200, 7]~ +- Inside source: true +*** True Line Result + slider = [1022, 561, 200, 7] +** Processing line: ~ if mouse_down && args.inputs.mouse.position.inside_rect?(slider)~ +- Inside source: true +*** True Line Result + if mouse_down && args.inputs.mouse.position.inside_rect?(slider) +** Processing line: ~ s[:gain] = (args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0)~ +- Inside source: true +*** True Line Result + s[:gain] = (args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0) +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ slidercolor = s[:gain] * 255~ +- Inside source: true +*** True Line Result + slidercolor = s[:gain] * 255 +** Processing line: ~ args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid +** Processing line: ~ args.outputs.primitives << [910, 575, "gain: #{s[:gain].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [910, 575, "gain: #{s[:gain].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ checkbox = [1022, 533, 10, 12]~ +- Inside source: true +*** True Line Result + checkbox = [1022, 533, 10, 12] +** Processing line: ~ if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox)~ +- Inside source: true +*** True Line Result + if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox) +** Processing line: ~ s[:looping] = !s[:looping]~ +- Inside source: true +*** True Line Result + s[:looping] = !s[:looping] +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ checkboxcolor = s[:looping] ? 255 : 0~ +- Inside source: true +*** True Line Result + checkboxcolor = s[:looping] ? 255 : 0 +** Processing line: ~ args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid +** Processing line: ~ args.outputs.primitives << [910, 550, "looping:", 0, 0, 255, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [910, 550, "looping:", 0, 0, 255, 255, 255].label +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ checkbox = [1022, 508, 10, 12]~ +- Inside source: true +*** True Line Result + checkbox = [1022, 508, 10, 12] +** Processing line: ~ if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox)~ +- Inside source: true +*** True Line Result + if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox) +** Processing line: ~ s[:paused] = !s[:paused]~ +- Inside source: true +*** True Line Result + s[:paused] = !s[:paused] +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ checkboxcolor = s[:paused] ? 255 : 0~ +- Inside source: true +*** True Line Result + checkboxcolor = s[:paused] ? 255 : 0 +** Processing line: ~ args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid +** Processing line: ~ args.outputs.primitives << [910, 525, "paused:", 0, 0, 255, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [910, 525, "paused:", 0, 0, 255, 255, 255].label +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ button = [910, 460, 320, 20]~ +- Inside source: true +*** True Line Result + button = [910, 460, 320, 20] +** Processing line: ~ if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(button)~ +- Inside source: true +*** True Line Result + if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(button) +** Processing line: ~ args.audio.delete(args.state.selected)~ +- Inside source: true +*** True Line Result + args.audio.delete(args.state.selected) +** Processing line: ~ args.state.selected = 0~ +- Inside source: true +*** True Line Result + args.state.selected = 0 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ args.outputs.primitives << [*button, 255, 0, 0, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [*button, 255, 0, 0, 255].solid +** Processing line: ~ args.outputs.primitives << [button[0] + (button[2] / 2), button[1]+20, "DELETE SOURCE", 0, 1, 255, 255, 0].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [button[0] + (button[2] / 2), button[1]+20, "DELETE SOURCE", 0, 1, 255, 255, 0].label +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def spawn_new_sound args, num~ +- Inside source: true +*** True Line Result + def spawn_new_sound args, num +** Processing line: ~ # Spawn randomly in an area that won't be covered by UI.~ +- Inside source: true +*** True Line Result + # Spawn randomly in an area that won't be covered by UI. +** Processing line: ~ screenx = (rand * 600.0) + 200.0~ +- Inside source: true +*** True Line Result + screenx = (rand * 600.0) + 200.0 +** Processing line: ~ screeny = (rand * 400.0) + 100.0~ +- Inside source: true +*** True Line Result + screeny = (rand * 400.0) + 100.0 +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ args.state.next_sound_index += 1~ +- Inside source: true +*** True Line Result + args.state.next_sound_index += 1 +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # you can hang anything on the audio hashes you want, so we store the~ +- Inside source: true +*** True Line Result + # you can hang anything on the audio hashes you want, so we store the +** Processing line: ~ # actual screen position in here for convenience.~ +- Inside source: true +*** True Line Result + # actual screen position in here for convenience. +** Processing line: ~ args.audio[args.state.next_sound_index] = {~ +- Inside source: true +*** True Line Result + args.audio[args.state.next_sound_index] = { +** Processing line: ~ filename: "sounds/#{num}.#{(num == 6) ? 'ogg' : 'wav'}",~ +- Inside source: true +*** True Line Result + filename: "sounds/#{num}.#{(num == 6) ? 'ogg' : 'wav'}", +** Processing line: ~ screenx: screenx,~ +- Inside source: true +*** True Line Result + screenx: screenx, +** Processing line: ~ screeny: screeny,~ +- Inside source: true +*** True Line Result + screeny: screeny, +** Processing line: ~ x: ((screenx / 1279.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range~ +- Inside source: true +*** True Line Result + x: ((screenx / 1279.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range +** Processing line: ~ y: ((screeny / 719.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range~ +- Inside source: true +*** True Line Result + y: ((screeny / 719.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range +** Processing line: ~ z: 0.0,~ +- Inside source: true +*** True Line Result + z: 0.0, +** Processing line: ~ gain: 1.0,~ +- Inside source: true +*** True Line Result + gain: 1.0, +** Processing line: ~ pitch: 1.0,~ +- Inside source: true +*** True Line Result + pitch: 1.0, +** Processing line: ~ looping: true,~ +- Inside source: true +*** True Line Result + looping: true, +** Processing line: ~ paused: false~ +- Inside source: true +*** True Line Result + paused: false +** Processing line: ~ }~ +- Inside source: true +*** True Line Result + } +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ args.state.selected = args.state.next_sound_index~ +- Inside source: true +*** True Line Result + args.state.selected = args.state.next_sound_index +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def render_launcher args~ +- Inside source: true +*** True Line Result + def render_launcher args +** Processing line: ~ total = 6~ +- Inside source: true +*** True Line Result + total = 6 +** Processing line: ~ x = (1280 - (total * $boxsize * 3)) / 2~ +- Inside source: true +*** True Line Result + x = (1280 - (total * $boxsize * 3)) / 2 +** Processing line: ~ y = 10~ +- Inside source: true +*** True Line Result + y = 10 +** Processing line: ~ args.outputs.primitives << [0, 0, 1280, ((y*2) + $boxsize), 127, 127, 127, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [0, 0, 1280, ((y*2) + $boxsize), 127, 127, 127, 255].solid +** Processing line: ~ for i in 1..total~ +- Inside source: true +*** True Line Result + for i in 1..total +** Processing line: ~ args.outputs.primitives << [x, y, $boxsize, $boxsize, 255, 255, 255, 255].solid~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [x, y, $boxsize, $boxsize, 255, 255, 255, 255].solid +** Processing line: ~ args.outputs.primitives << [x+8, y+28, i.to_s, 3, 0, 0, 0, 255, 255].label~ +- Inside source: true +*** True Line Result + args.outputs.primitives << [x+8, y+28, i.to_s, 3, 0, 0, 0, 255, 255].label +** Processing line: ~ if args.inputs.mouse.click && args.inputs.mouse.click.point.inside_rect?([x, y, $boxsize, $boxsize])~ +- Inside source: true +*** True Line Result + if args.inputs.mouse.click && args.inputs.mouse.click.point.inside_rect?([x, y, $boxsize, $boxsize]) +** Processing line: ~ spawn_new_sound args, i~ +- Inside source: true +*** True Line Result + spawn_new_sound args, i +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ x = x + ($boxsize * 3)~ +- Inside source: true +*** True Line Result + x = x + ($boxsize * 3) +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def render_ui args~ +- Inside source: true +*** True Line Result + def render_ui args +** Processing line: ~ render_launcher args~ +- Inside source: true +*** True Line Result + render_launcher args +** Processing line: ~ render_panel args~ +- Inside source: true +*** True Line Result + render_panel args +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def tick args~ +- Inside source: true +*** True Line Result + def tick args +** Processing line: ~ args.state.mouse_held ||= 0~ +- Inside source: true +*** True Line Result + args.state.mouse_held ||= 0 +** Processing line: ~ args.state.dragging_source ||= false~ +- Inside source: true +*** True Line Result + args.state.dragging_source ||= false +** Processing line: ~ args.state.selected ||= 0~ +- Inside source: true +*** True Line Result + args.state.selected ||= 0 +** Processing line: ~ args.state.next_sound_index ||= 0~ +- Inside source: true +*** True Line Result + args.state.next_sound_index ||= 0 +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ if args.inputs.mouse.up~ +- Inside source: true +*** True Line Result + if args.inputs.mouse.up +** Processing line: ~ args.state.mouse_held = 0~ +- Inside source: true +*** True Line Result + args.state.mouse_held = 0 +** Processing line: ~ args.state.dragging_source = false~ +- Inside source: true +*** True Line Result + args.state.dragging_source = false +** Processing line: ~ elsif args.inputs.mouse.down || (args.state.mouse_held > 0)~ +- Inside source: true +*** True Line Result + elsif args.inputs.mouse.down || (args.state.mouse_held > 0) +** Processing line: ~ args.state.mouse_held += 1~ +- Inside source: true +*** True Line Result + args.state.mouse_held += 1 +** Processing line: ~ else~ +- Inside source: true +*** True Line Result + else +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ args.outputs.background_color = [ 0, 0, 0, 255 ]~ +- Inside source: true +*** True Line Result + args.outputs.background_color = [ 0, 0, 0, 255 ] +** Processing line: ~ render_sources args~ +- Inside source: true +*** True Line Result + render_sources args +** Processing line: ~ render_ui args~ +- Inside source: true +*** True Line Result + render_ui args +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** 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: ~* Input Basics - Keyboard - main.rb~ - Header detected. *** True Line Result @@ -29340,22 +30151,22 @@ Follows is a source code listing for all files that have been open sourced. This - End of paragraph detected. *** True Line Result -** Processing line: ~* Advanced Rendering - Render Targets With Alphas - main.rb~ +** Processing line: ~* Advanced Rendering - Render Targets With Tile Manipulation - main.rb~ - Header detected. *** True Line Result *** True Line Result -* Advanced Rendering - Render Targets With Alphas - main.rb +* Advanced Rendering - Render Targets With Tile Manipulation - main.rb ** 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: ~ # ./samples/07_advanced_rendering/02_render_targets_with_alphas/app/main.rb~ +** Processing line: ~ # ./samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb~ - Inside source: true *** True Line Result - # ./samples/07_advanced_rendering/02_render_targets_with_alphas/app/main.rb + # ./samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb ** Processing line: ~ # This sample is meant to show you how to do that dripping transition thing~ - Inside source: true *** True Line Result @@ -34980,6 +35791,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result # ./samples/09_performance/01_sprites_as_hash/app/main.rb +** Processing line: ~~ +- Inside source: true +*** True Line Result + ** Processing line: ~ # Sprites represented as Hashes using the queue ~args.outputs.sprites~~ - Inside source: true *** True Line Result @@ -38244,10 +39059,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result result = args.gtk.serialize_state args.state -** Processing line: ~ assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}"~ +** Processing line: ~ assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"~ - Inside source: true *** True Line Result - assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}" + assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}" ** Processing line: ~~ - Inside source: true *** True Line Result @@ -38264,10 +39079,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result result = args.gtk.serialize_state 'state.txt', args.state -** Processing line: ~ assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}"~ +** Processing line: ~ assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"~ - Inside source: true *** True Line Result - assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}" + assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}" ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -38388,14 +39203,18 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result serialized_state = args.gtk.serialize_state args.state -** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>1, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken"}}'~ +** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>1, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken"}}'~ - Inside source: true *** True Line Result - assert.equal! serialized_state, '{:entity_id=>1, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken"}}' + assert.equal! serialized_state, '{:entity_id=>1, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken"}}' ** Processing line: ~~ - Inside source: true *** True Line Result +** Processing line: ~ GTK::Entity.__reset_id__!~ +- Inside source: true +*** True Line Result + GTK::Entity.__reset_id__! ** Processing line: ~ deserialize_state = args.gtk.deserialize_state serialized_state~ - Inside source: true *** True Line Result @@ -38456,14 +39275,18 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result serialized_state = args.gtk.serialize_state args.state -** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>3, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken", :blood_type=>nil}}'~ +** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>9, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>2, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken", :blood_type=>nil}}'~ - Inside source: true *** True Line Result - assert.equal! serialized_state, '{:entity_id=>3, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken", :blood_type=>nil}}' + assert.equal! serialized_state, '{:entity_id=>9, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>2, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken", :blood_type=>nil}}' ** Processing line: ~~ - Inside source: true *** True Line Result +** Processing line: ~ GTK::Entity.__reset_id__!~ +- Inside source: true +*** True Line Result + GTK::Entity.__reset_id__! ** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ - Inside source: true *** True Line Result @@ -38544,6 +39367,14 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result serialized_state = args.gtk.serialize_state args.state +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ GTK::Entity.__reset_id__!~ +- Inside source: true +*** True Line Result + GTK::Entity.__reset_id__! ** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ - Inside source: true *** True Line Result @@ -38568,6 +39399,114 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result +** Processing line: ~ def test_by_reference_state args, assert~ +- Inside source: true +*** True Line Result + def test_by_reference_state args, assert +** Processing line: ~ GTK::Entity.__reset_id__!~ +- Inside source: true +*** True Line Result + GTK::Entity.__reset_id__! +** Processing line: ~ args.state.a = { name: "Jane Doe" }~ +- Inside source: true +*** True Line Result + args.state.a = { name: "Jane Doe" } +** Processing line: ~ args.state.b = args.state.a~ +- Inside source: true +*** True Line Result + args.state.b = args.state.a +** Processing line: ~ assert.equal! args.state.a.object_id, args.state.b.object_id~ +- Inside source: true +*** True Line Result + assert.equal! args.state.a.object_id, args.state.b.object_id +** Processing line: ~ serialized_state = args.gtk.serialize_state args.state~ +- Inside source: true +*** True Line Result + serialized_state = args.gtk.serialize_state args.state +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ GTK::Entity.__reset_id__!~ +- Inside source: true +*** True Line Result + GTK::Entity.__reset_id__! +** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ +- Inside source: true +*** True Line Result + deserialized_state = args.gtk.deserialize_state serialized_state +** Processing line: ~ assert.equal! deserialized_state.a.object_id, deserialized_state.b.object_id~ +- Inside source: true +*** True Line Result + assert.equal! deserialized_state.a.object_id, deserialized_state.b.object_id +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def test_by_reference_state_strict_entities args, assert~ +- Inside source: true +*** True Line Result + def test_by_reference_state_strict_entities args, assert +** Processing line: ~ GTK::Entity.__reset_id__!~ +- Inside source: true +*** True Line Result + GTK::Entity.__reset_id__! +** Processing line: ~ args.state.a = { name: "Jane Doe" }~ +- Inside source: true +*** True Line Result + args.state.a = { name: "Jane Doe" } +** Processing line: ~ args.state.strict_entity = args.state.new_entity_strict(:couple) do |e|~ +- Inside source: true +*** True Line Result + args.state.strict_entity = args.state.new_entity_strict(:couple) do |e| +** Processing line: ~ e.one = args.state.new_entity_strict(:person, name: "Jane")~ +- Inside source: true +*** True Line Result + e.one = args.state.new_entity_strict(:person, name: "Jane") +** Processing line: ~ e.two = e.one~ +- Inside source: true +*** True Line Result + e.two = e.one +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ assert.equal! args.state.strict_entity.one, args.state.strict_entity.two~ +- Inside source: true +*** True Line Result + assert.equal! args.state.strict_entity.one, args.state.strict_entity.two +** Processing line: ~ serialized_state = args.gtk.serialize_state args.state~ +- Inside source: true +*** True Line Result + serialized_state = args.gtk.serialize_state args.state +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ GTK::Entity.__reset_id__!~ +- Inside source: true +*** True Line Result + GTK::Entity.__reset_id__! +** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ +- Inside source: true +*** True Line Result + deserialized_state = args.gtk.deserialize_state serialized_state +** Processing line: ~ assert.equal! deserialized_state.strict_entity.one, deserialized_state.strict_entity.two~ +- Inside source: true +*** True Line Result + assert.equal! deserialized_state.strict_entity.one, deserialized_state.strict_entity.two +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + ** Processing line: ~ $tests.start~ - Inside source: true *** True Line Result @@ -39296,10 +40235,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result # ./samples/12_c_extensions/01_basics/app/main.rb -** Processing line: ~ $gtk.ffi_misc.gtk_dlopen("./samples/12_c_extensions/01_basics/build.dir/ext.lib")~ +** Processing line: ~ $gtk.ffi_misc.gtk_dlopen("ext")~ - Inside source: true *** True Line Result - $gtk.ffi_misc.gtk_dlopen("./samples/12_c_extensions/01_basics/build.dir/ext.lib") + $gtk.ffi_misc.gtk_dlopen("ext") ** Processing line: ~ include FFI::CExt~ - Inside source: true *** True Line Result @@ -39312,10 +40251,22 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result def tick args -** Processing line: ~ args.outputs.labels << [460, 600, "square(42) = #{square(42)}"]~ +** Processing line: ~ args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1]~ - Inside source: true *** True Line Result - args.outputs.labels << [460, 600, "square(42) = #{square(42)}"] + args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1] +** Processing line: ~ args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1]~ +- Inside source: true +*** True Line Result + args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1] +** Processing line: ~ args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1]~ +- Inside source: true +*** True Line Result + args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1] +** Processing line: ~ args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1]~ +- Inside source: true +*** True Line Result + args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1] ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -39336,6 +40287,110 @@ Follows is a source code listing for all files that have been open sourced. This - End of paragraph detected. *** True Line Result +** Processing line: ~* 12 C Extensions - Intermediate - main.rb~ +- Header detected. +*** True Line Result + +*** True Line Result +* 12 C Extensions - Intermediate - main.rb +** 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: ~ # ./samples/12_c_extensions/02_intermediate/app/main.rb~ +- Inside source: true +*** True Line Result + # ./samples/12_c_extensions/02_intermediate/app/main.rb +** Processing line: ~ $gtk.ffi_misc.gtk_dlopen("ext")~ +- Inside source: true +*** True Line Result + $gtk.ffi_misc.gtk_dlopen("ext") +** Processing line: ~ include FFI::RE~ +- Inside source: true +*** True Line Result + include FFI::RE +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def split_words(input)~ +- Inside source: true +*** True Line Result + def split_words(input) +** Processing line: ~ words = []~ +- Inside source: true +*** True Line Result + words = [] +** Processing line: ~ last = IntPointer.new~ +- Inside source: true +*** True Line Result + last = IntPointer.new +** Processing line: ~ re = re_compile("\\w+")~ +- Inside source: true +*** True Line Result + re = re_compile("\\w+") +** Processing line: ~ first = re_matchp(re, input, last)~ +- Inside source: true +*** True Line Result + first = re_matchp(re, input, last) +** Processing line: ~ while first != -1~ +- Inside source: true +*** True Line Result + while first != -1 +** Processing line: ~ words << input.slice(first, last.value)~ +- Inside source: true +*** True Line Result + words << input.slice(first, last.value) +** Processing line: ~ input = input.slice(last.value + first, input.length)~ +- Inside source: true +*** True Line Result + input = input.slice(last.value + first, input.length) +** Processing line: ~ first = re_matchp(re, input, last)~ +- Inside source: true +*** True Line Result + first = re_matchp(re, input, last) +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ words~ +- Inside source: true +*** True Line Result + words +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def tick args~ +- Inside source: true +*** True Line Result + def tick args +** Processing line: ~ args.outputs.labels << [640, 500, split_words("hello, dragonriders!").join(' '), 5, 1]~ +- Inside source: true +*** True Line Result + args.outputs.labels << [640, 500, split_words("hello, dragonriders!").join(' '), 5, 1] +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** 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: ~* 3d - 3d Cube - main.rb~ - Header detected. *** True Line Result @@ -87180,6 +88235,26 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result +** Processing line: ~ # Contains the means to interact with the audio mixer.~ +- Inside source: true +*** True Line Result + # Contains the means to interact with the audio mixer. +** Processing line: ~ #~ +- Inside source: true +*** True Line Result + # +** Processing line: ~ # @return [Hash]~ +- Inside source: true +*** True Line Result + # @return [Hash] +** Processing line: ~ attr_accessor :audio~ +- Inside source: true +*** True Line Result + attr_accessor :audio +** Processing line: ~~ +- Inside source: true +*** True Line Result + ** Processing line: ~ # Contains display size information to assist in positioning things on the screen.~ - Inside source: true *** True Line Result @@ -87312,6 +88387,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result @outputs = Outputs.new args: self +** Processing line: ~ @audio = {}~ +- Inside source: true +*** True Line Result + @audio = {} ** Processing line: ~ @passes = []~ - Inside source: true *** True Line Result @@ -88300,10 +89379,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip -** Processing line: ~ message = "actual:\n#{actual_string} did not equal\nexpected:\n#{expected}.\n#{message}"~ +** Processing line: ~ message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}.\n#{message}"~ - Inside source: true *** True Line Result - message = "actual:\n#{actual_string} did not equal\nexpected:\n#{expected}.\n#{message}" + message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}.\n#{message}" ** Processing line: ~ raise message~ - Inside source: true *** True Line Result @@ -89080,10 +90159,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result def save_history -** Processing line: ~ $gtk.ffi_file.storefile(@history_fname, @command_history.reverse.join("\n"))~ +** Processing line: ~ $gtk.ffi_file.write_root @history_fname, (@command_history.reverse.join "\n")~ - Inside source: true *** True Line Result - $gtk.ffi_file.storefile(@history_fname, @command_history.reverse.join("\n")) + $gtk.ffi_file.write_root @history_fname, (@command_history.reverse.join "\n") ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -89100,10 +90179,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result @command_history.clear -** Processing line: ~ str = $gtk.ffi_file.loadfile(@history_fname)~ +** Processing line: ~ str = $gtk.ffi_file.read @history_fname~ - Inside source: true *** True Line Result - str = $gtk.ffi_file.loadfile(@history_fname) + str = $gtk.ffi_file.read @history_fname ** Processing line: ~ return if str.nil? # no history to load.~ - Inside source: true *** True Line Result @@ -89952,6 +91031,126 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result +** Processing line: ~ def try_search_docs exception~ +- Inside source: true +*** True Line Result + def try_search_docs exception +** Processing line: ~ string_e = "#{exception}"~ +- Inside source: true +*** True Line Result + string_e = "#{exception}" +** Processing line: ~ @last_command_errored = true~ +- Inside source: true +*** True Line Result + @last_command_errored = true +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ if (string_e.include? "wrong number of arguments")~ +- Inside source: true +*** True Line Result + if (string_e.include? "wrong number of arguments") +** Processing line: ~ method_name = ((string_e.split ":")[0].gsub "'", "")~ +- Inside source: true +*** True Line Result + method_name = ((string_e.split ":")[0].gsub "'", "") +** Processing line: ~ if !(method_name.include? " ")~ +- Inside source: true +*** True Line Result + if !(method_name.include? " ") +** Processing line: ~ results = (Kernel.__docs_search_results__ method_name)~ +- Inside source: true +*** True Line Result + results = (Kernel.__docs_search_results__ method_name) +** Processing line: ~ if !results.include? "* DOCS: No results found."~ +- Inside source: true +*** True Line Result + if !results.include? "* DOCS: No results found." +** Processing line: ~ puts (results.join "\n")~ +- Inside source: true +*** True Line Result + puts (results.join "\n") +** Processing line: ~ puts <<-S~ +- Inside source: true +*** True Line Result + puts <<-S +** Processing line: ~ * INFO: #{results.length} matches(s) found in DOCS for ~#{method_name}~ (see above).~ +- Inside source: true +*** True Line Result + * INFO: #{results.length} matches(s) found in DOCS for ~#{method_name}~ (see above). +** Processing line: ~ You can search the documentation yourself using the following command in the Console:~ +- Inside source: true +*** True Line Result + You can search the documentation yourself using the following command in the Console: +** Processing line: ~ #+begin_src ruby~ +- Inside source: true +*** True Line Result + #+begin_src ruby +** Processing line: ~ docs_search \"#{method_name}\"~ +- Inside source: true +*** True Line Result + docs_search \"#{method_name}\" +** Processing line: ~ #+end_src~ +- Inside source: true +*** True Line Result + #+end_src +** Processing line: ~ S~ +- Inside source: true +*** True Line Result + S +** Processing line: ~ log_once_info :exported_search_results, "The search results above has been seen in logs/puts.txt and docs/search_results.txt."~ +- Inside source: true +*** True Line Result + log_once_info :exported_search_results, "The search results above has been seen in logs/puts.txt and docs/search_results.txt." +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => se~ +- Inside source: true +*** True Line Result + rescue Exception => se +** Processing line: ~ puts <<-S~ +- Inside source: true +*** True Line Result + puts <<-S +** Processing line: ~ * FATAL: ~GTK::Console#try_search_docs~~ +- Inside source: true +*** True Line Result + * FATAL: ~GTK::Console#try_search_docs~ +** Processing line: ~ There was an exception searching for docs (~GTK::Console#try_search_docs~). You might want to let DragonRuby know about this.~ +- Inside source: true +*** True Line Result + There was an exception searching for docs (~GTK::Console#try_search_docs~). You might want to let DragonRuby know about this. +** Processing line: ~ ** INNER EXCEPTION~ +- Inside source: true +*** True Line Result + ** INNER EXCEPTION +** Processing line: ~ #{se}~ +- Inside source: true +*** True Line Result + #{se} +** Processing line: ~ S~ +- Inside source: true +*** True Line Result + S +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + ** Processing line: ~ def eval_the_set_command~ - Inside source: true *** True Line Result @@ -90056,54 +91255,14 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result rescue Exception => e -** Processing line: ~ string_e = "#{e}"~ -- Inside source: true -*** True Line Result - string_e = "#{e}" -** Processing line: ~ puts "* EXCEPTION: #{e}"~ -- Inside source: true -*** True Line Result - puts "* EXCEPTION: #{e}" -** Processing line: ~ log "* EXCEPTION: #{e}"~ -- Inside source: true -*** True Line Result - log "* EXCEPTION: #{e}" -** Processing line: ~ @last_command_errored = true~ -- Inside source: true -*** True Line Result - @last_command_errored = true -** Processing line: ~ if (string_e.include? "wrong number of arguments")~ -- Inside source: true -*** True Line Result - if (string_e.include? "wrong number of arguments") -** Processing line: ~ method_name = (string_e.split ":")[0].gsub "'", ""~ -- Inside source: true -*** True Line Result - method_name = (string_e.split ":")[0].gsub "'", "" -** Processing line: ~ results = (Kernel.docs_search method_name).strip~ -- Inside source: true -*** True Line Result - results = (Kernel.docs_search method_name).strip -** Processing line: ~ if !results.include? "* DOCS: No results found."~ -- Inside source: true -*** True Line Result - if !results.include? "* DOCS: No results found." -** Processing line: ~ puts results~ +** Processing line: ~ try_search_docs e~ - Inside source: true *** True Line Result - puts results -** Processing line: ~ log results~ + try_search_docs e +** Processing line: ~ puts "* EXCEPTION: #{e}"~ - Inside source: true *** True Line Result - log results -** Processing line: ~ end~ -- Inside source: true -*** True Line Result - end -** Processing line: ~ end~ -- Inside source: true -*** True Line Result - end + puts "* EXCEPTION: #{e}" ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -92592,6 +93751,10 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result if clicked +** Processing line: ~ args.inputs.mouse.click = nil~ +- Inside source: true +*** True Line Result + args.inputs.mouse.click = nil ** Processing line: ~ send clicked[:method]~ - Inside source: true *** True Line Result @@ -100952,6 +102115,22 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result +** Processing line: ~ def inside_circle? center, radius~ +- Inside source: true +*** True Line Result + def inside_circle? center, radius +** Processing line: ~ point.point_inside_circle? center, radius~ +- Inside source: true +*** True Line Result + point.point_inside_circle? center, radius +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + ** Processing line: ~ alias_method :position, :point~ - Inside source: true *** True Line Result @@ -103308,6 +104487,14 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result result = rand * result +** Processing line: ~ elsif definitions.include?(:int)~ +- Inside source: true +*** True Line Result + elsif definitions.include?(:int) +** Processing line: ~ result = (rand result)~ +- Inside source: true +*** True Line Result + result = (rand result) ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -105296,6 +106483,926 @@ Follows is a source code listing for all files that have been open sourced. This - End of paragraph detected. *** True Line Result +** Processing line: ~* runtime/draw.rb~ +- Header detected. +*** True Line Result + +*** True Line Result +* runtime/draw.rb +** 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: ~ # ./dragon/runtime/draw.rb~ +- Inside source: true +*** True Line Result + # ./dragon/runtime/draw.rb +** Processing line: ~ # Copyright 2019 DragonRuby LLC~ +- Inside source: true +*** True Line Result + # Copyright 2019 DragonRuby LLC +** Processing line: ~ # MIT License~ +- Inside source: true +*** True Line Result + # MIT License +** Processing line: ~ # draw.rb has been released under MIT (*only this file*).~ +- Inside source: true +*** True Line Result + # draw.rb has been released under MIT (*only this file*). +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ module GTK~ +- Inside source: true +*** True Line Result + module GTK +** Processing line: ~ class Runtime~ +- Inside source: true +*** True Line Result + class Runtime +** Processing line: ~ module Draw~ +- Inside source: true +*** True Line Result + module Draw +** Processing line: ~ def primitives pass~ +- Inside source: true +*** True Line Result + def primitives pass +** Processing line: ~ if $top_level.respond_to? :primitives_override~ +- Inside source: true +*** True Line Result + if $top_level.respond_to? :primitives_override +** Processing line: ~ return $top_level.tick_render @args, pass~ +- Inside source: true +*** True Line Result + return $top_level.tick_render @args, pass +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # Don't change this draw order unless you understand~ +- Inside source: true +*** True Line Result + # Don't change this draw order unless you understand +** Processing line: ~ # the implications.~ +- Inside source: true +*** True Line Result + # the implications. +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.solids.each { |s| draw_solid s }~ +- Inside source: true +*** True Line Result + # pass.solids.each { |s| draw_solid s } +** Processing line: ~ # while loops are faster than each with block~ +- Inside source: true +*** True Line Result + # while loops are faster than each with block +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.solids.length~ +- Inside source: true +*** True Line Result + while idx < pass.solids.length +** Processing line: ~ draw_solid (pass.solids.value idx) # accessing an array using .value instead of [] is faster~ +- Inside source: true +*** True Line Result + draw_solid (pass.solids.value idx) # accessing an array using .value instead of [] is faster +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_solids.each { |s| draw_solid s }~ +- Inside source: true +*** True Line Result + # pass.static_solids.each { |s| draw_solid s } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_solids.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_solids.length +** Processing line: ~ draw_solid (pass.static_solids.value idx)~ +- Inside source: true +*** True Line Result + draw_solid (pass.static_solids.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.sprites.each { |s| draw_sprite s }~ +- Inside source: true +*** True Line Result + # pass.sprites.each { |s| draw_sprite s } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.sprites.length~ +- Inside source: true +*** True Line Result + while idx < pass.sprites.length +** Processing line: ~ draw_sprite (pass.sprites.value idx)~ +- Inside source: true +*** True Line Result + draw_sprite (pass.sprites.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_sprites.each { |s| draw_sprite s }~ +- Inside source: true +*** True Line Result + # pass.static_sprites.each { |s| draw_sprite s } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_sprites.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_sprites.length +** Processing line: ~ draw_sprite (pass.static_sprites.value idx)~ +- Inside source: true +*** True Line Result + draw_sprite (pass.static_sprites.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.primitives.each { |p| draw_primitive p }~ +- Inside source: true +*** True Line Result + # pass.primitives.each { |p| draw_primitive p } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.primitives.length~ +- Inside source: true +*** True Line Result + while idx < pass.primitives.length +** Processing line: ~ draw_primitive (pass.primitives.value idx)~ +- Inside source: true +*** True Line Result + draw_primitive (pass.primitives.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_primitives.each { |p| draw_primitive p }~ +- Inside source: true +*** True Line Result + # pass.static_primitives.each { |p| draw_primitive p } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_primitives.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_primitives.length +** Processing line: ~ draw_primitive (pass.static_primitives.value idx)~ +- Inside source: true +*** True Line Result + draw_primitive (pass.static_primitives.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.labels.each { |l| draw_label l }~ +- Inside source: true +*** True Line Result + # pass.labels.each { |l| draw_label l } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.labels.length~ +- Inside source: true +*** True Line Result + while idx < pass.labels.length +** Processing line: ~ draw_label (pass.labels.value idx)~ +- Inside source: true +*** True Line Result + draw_label (pass.labels.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_labels.each { |l| draw_label l }~ +- Inside source: true +*** True Line Result + # pass.static_labels.each { |l| draw_label l } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_labels.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_labels.length +** Processing line: ~ draw_label (pass.static_labels.value idx)~ +- Inside source: true +*** True Line Result + draw_label (pass.static_labels.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.lines.each { |l| draw_line l }~ +- Inside source: true +*** True Line Result + # pass.lines.each { |l| draw_line l } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.lines.length~ +- Inside source: true +*** True Line Result + while idx < pass.lines.length +** Processing line: ~ draw_line (pass.lines.value idx)~ +- Inside source: true +*** True Line Result + draw_line (pass.lines.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_lines.each { |l| draw_line l }~ +- Inside source: true +*** True Line Result + # pass.static_lines.each { |l| draw_line l } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_lines.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_lines.length +** Processing line: ~ draw_line (pass.static_lines.value idx)~ +- Inside source: true +*** True Line Result + draw_line (pass.static_lines.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.borders.each { |b| draw_border b }~ +- Inside source: true +*** True Line Result + # pass.borders.each { |b| draw_border b } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.borders.length~ +- Inside source: true +*** True Line Result + while idx < pass.borders.length +** Processing line: ~ draw_border (pass.borders.value idx)~ +- Inside source: true +*** True Line Result + draw_border (pass.borders.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_borders.each { |b| draw_border b }~ +- Inside source: true +*** True Line Result + # pass.static_borders.each { |b| draw_border b } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_borders.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_borders.length +** Processing line: ~ draw_border (pass.static_borders.value idx)~ +- Inside source: true +*** True Line Result + draw_border (pass.static_borders.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ if !$gtk.production~ +- Inside source: true +*** True Line Result + if !$gtk.production +** Processing line: ~ # pass.debug.each { |r| draw_primitive r }~ +- Inside source: true +*** True Line Result + # pass.debug.each { |r| draw_primitive r } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.debug.length~ +- Inside source: true +*** True Line Result + while idx < pass.debug.length +** Processing line: ~ draw_primitive (pass.debug.value idx)~ +- Inside source: true +*** True Line Result + draw_primitive (pass.debug.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_debug.each { |r| draw_primitive r }~ +- Inside source: true +*** True Line Result + # pass.static_debug.each { |r| draw_primitive r } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_debug.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_debug.length +** Processing line: ~ draw_primitive (pass.static_debug.value idx)~ +- Inside source: true +*** True Line Result + draw_primitive (pass.static_debug.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.reserved.each { |r| draw_primitive r }~ +- Inside source: true +*** True Line Result + # pass.reserved.each { |r| draw_primitive r } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.reserved.length~ +- Inside source: true +*** True Line Result + while idx < pass.reserved.length +** Processing line: ~ draw_primitive (pass.reserved.value idx)~ +- Inside source: true +*** True Line Result + draw_primitive (pass.reserved.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # pass.static_reserved.each { |r| draw_primitive r }~ +- Inside source: true +*** True Line Result + # pass.static_reserved.each { |r| draw_primitive r } +** Processing line: ~ idx = 0~ +- Inside source: true +*** True Line Result + idx = 0 +** Processing line: ~ while idx < pass.static_reserved.length~ +- Inside source: true +*** True Line Result + while idx < pass.static_reserved.length +** Processing line: ~ draw_primitive (pass.static_reserved.value idx)~ +- Inside source: true +*** True Line Result + draw_primitive (pass.static_reserved.value idx) +** Processing line: ~ idx += 1~ +- Inside source: true +*** True Line Result + idx += 1 +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => e~ +- Inside source: true +*** True Line Result + rescue Exception => e +** Processing line: ~ pause!~ +- Inside source: true +*** True Line Result + pause! +** Processing line: ~ pretty_print_exception_and_export! e~ +- Inside source: true +*** True Line Result + pretty_print_exception_and_export! e +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def draw_solid s~ +- Inside source: true +*** True Line Result + def draw_solid s +** Processing line: ~ return unless s~ +- Inside source: true +*** True Line Result + return unless s +** Processing line: ~ if s.respond_to? :draw_override~ +- Inside source: true +*** True Line Result + if s.respond_to? :draw_override +** Processing line: ~ s.draw_override @ffi_draw~ +- Inside source: true +*** True Line Result + s.draw_override @ffi_draw +** Processing line: ~ else~ +- Inside source: true +*** True Line Result + else +** Processing line: ~ @ffi_draw.draw_solid s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a~ +- Inside source: true +*** True Line Result + @ffi_draw.draw_solid s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => e~ +- Inside source: true +*** True Line Result + rescue Exception => e +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :solid~ +- Inside source: true +*** True Line Result + raise_conversion_for_rendering_failed s, e, :solid +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def draw_sprite s~ +- Inside source: true +*** True Line Result + def draw_sprite s +** Processing line: ~ return unless s~ +- Inside source: true +*** True Line Result + return unless s +** Processing line: ~ if s.respond_to? :draw_override~ +- Inside source: true +*** True Line Result + if s.respond_to? :draw_override +** Processing line: ~ s.draw_override @ffi_draw~ +- Inside source: true +*** True Line Result + s.draw_override @ffi_draw +** Processing line: ~ else~ +- Inside source: true +*** True Line Result + else +** Processing line: ~ @ffi_draw.draw_sprite_3 s.x, s.y, s.w, s.h,~ +- Inside source: true +*** True Line Result + @ffi_draw.draw_sprite_3 s.x, s.y, s.w, s.h, +** Processing line: ~ s.path.s_or_default,~ +- Inside source: true +*** True Line Result + s.path.s_or_default, +** Processing line: ~ s.angle,~ +- Inside source: true +*** True Line Result + s.angle, +** Processing line: ~ s.a, s.r, s.g, s.b,~ +- Inside source: true +*** True Line Result + s.a, s.r, s.g, s.b, +** Processing line: ~ s.tile_x, s.tile_y, s.tile_w, s.tile_h,~ +- Inside source: true +*** True Line Result + s.tile_x, s.tile_y, s.tile_w, s.tile_h, +** Processing line: ~ !!s.flip_horizontally, !!s.flip_vertically,~ +- Inside source: true +*** True Line Result + !!s.flip_horizontally, !!s.flip_vertically, +** Processing line: ~ s.angle_anchor_x, s.angle_anchor_y,~ +- Inside source: true +*** True Line Result + s.angle_anchor_x, s.angle_anchor_y, +** Processing line: ~ s.source_x, s.source_y, s.source_w, s.source_h~ +- Inside source: true +*** True Line Result + s.source_x, s.source_y, s.source_w, s.source_h +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => e~ +- Inside source: true +*** True Line Result + rescue Exception => e +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :sprite~ +- Inside source: true +*** True Line Result + raise_conversion_for_rendering_failed s, e, :sprite +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def draw_screenshot s~ +- Inside source: true +*** True Line Result + def draw_screenshot s +** Processing line: ~ return unless s~ +- Inside source: true +*** True Line Result + return unless s +** Processing line: ~ if s.respond_to? :draw_override~ +- Inside source: true +*** True Line Result + if s.respond_to? :draw_override +** Processing line: ~ s.draw_override @ffi_draw~ +- Inside source: true +*** True Line Result + s.draw_override @ffi_draw +** Processing line: ~ else~ +- Inside source: true +*** True Line Result + else +** Processing line: ~ @ffi_draw.draw_screenshot s.path.s_or_default,~ +- Inside source: true +*** True Line Result + @ffi_draw.draw_screenshot s.path.s_or_default, +** Processing line: ~ s.x, s.y, s.w, s.h,~ +- Inside source: true +*** True Line Result + s.x, s.y, s.w, s.h, +** Processing line: ~ s.angle,~ +- Inside source: true +*** True Line Result + s.angle, +** Processing line: ~ s.a, s.r, s.g, s.b,~ +- Inside source: true +*** True Line Result + s.a, s.r, s.g, s.b, +** Processing line: ~ s.tile_x, s.tile_y, s.tile_w, s.tile_h,~ +- Inside source: true +*** True Line Result + s.tile_x, s.tile_y, s.tile_w, s.tile_h, +** Processing line: ~ !!s.flip_horizontally, !!s.flip_vertically,~ +- Inside source: true +*** True Line Result + !!s.flip_horizontally, !!s.flip_vertically, +** Processing line: ~ s.angle_anchor_x, s.angle_anchor_y,~ +- Inside source: true +*** True Line Result + s.angle_anchor_x, s.angle_anchor_y, +** Processing line: ~ s.source_x, s.source_y, s.source_w, s.source_h~ +- Inside source: true +*** True Line Result + s.source_x, s.source_y, s.source_w, s.source_h +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => e~ +- Inside source: true +*** True Line Result + rescue Exception => e +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :screenshot~ +- Inside source: true +*** True Line Result + raise_conversion_for_rendering_failed s, e, :screenshot +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def draw_label l~ +- Inside source: true +*** True Line Result + def draw_label l +** Processing line: ~ return unless l~ +- Inside source: true +*** True Line Result + return unless l +** Processing line: ~ if l.respond_to? :draw_override~ +- Inside source: true +*** True Line Result + if l.respond_to? :draw_override +** Processing line: ~ l.draw_override @ffi_draw~ +- Inside source: true +*** True Line Result + l.draw_override @ffi_draw +** Processing line: ~ else~ +- Inside source: true +*** True Line Result + else +** Processing line: ~ @ffi_draw.draw_label l.x, l.y, l.text.s_or_default,~ +- Inside source: true +*** True Line Result + @ffi_draw.draw_label l.x, l.y, l.text.s_or_default, +** Processing line: ~ l.size_enum, l.alignment_enum,~ +- Inside source: true +*** True Line Result + l.size_enum, l.alignment_enum, +** Processing line: ~ l.r, l.g, l.b, l.a,~ +- Inside source: true +*** True Line Result + l.r, l.g, l.b, l.a, +** Processing line: ~ l.font.s_or_default(nil)~ +- Inside source: true +*** True Line Result + l.font.s_or_default(nil) +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => e~ +- Inside source: true +*** True Line Result + rescue Exception => e +** Processing line: ~ raise_conversion_for_rendering_failed l, e, :label~ +- Inside source: true +*** True Line Result + raise_conversion_for_rendering_failed l, e, :label +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def draw_line l~ +- Inside source: true +*** True Line Result + def draw_line l +** Processing line: ~ return unless l~ +- Inside source: true +*** True Line Result + return unless l +** Processing line: ~ if l.respond_to? :draw_override~ +- Inside source: true +*** True Line Result + if l.respond_to? :draw_override +** Processing line: ~ l.draw_override @ffi_draw~ +- Inside source: true +*** True Line Result + l.draw_override @ffi_draw +** Processing line: ~ else~ +- Inside source: true +*** True Line Result + else +** Processing line: ~ @ffi_draw.draw_line l.x, l.y, l.x2, l.y2, l.r, l.g, l.b, l.a~ +- Inside source: true +*** True Line Result + @ffi_draw.draw_line l.x, l.y, l.x2, l.y2, l.r, l.g, l.b, l.a +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => e~ +- Inside source: true +*** True Line Result + rescue Exception => e +** Processing line: ~ raise_conversion_for_rendering_failed l, e, :line~ +- Inside source: true +*** True Line Result + raise_conversion_for_rendering_failed l, e, :line +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def draw_border s~ +- Inside source: true +*** True Line Result + def draw_border s +** Processing line: ~ return unless s~ +- Inside source: true +*** True Line Result + return unless s +** Processing line: ~ if s.respond_to? :draw_override~ +- Inside source: true +*** True Line Result + if s.respond_to? :draw_override +** Processing line: ~ s.draw_override @ffi_draw~ +- Inside source: true +*** True Line Result + s.draw_override @ffi_draw +** Processing line: ~ else~ +- Inside source: true +*** True Line Result + else +** Processing line: ~ @ffi_draw.draw_border s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a~ +- Inside source: true +*** True Line Result + @ffi_draw.draw_border s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ rescue Exception => e~ +- Inside source: true +*** True Line Result + rescue Exception => e +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :border~ +- Inside source: true +*** True Line Result + raise_conversion_for_rendering_failed s, e, :border +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def draw_screenshots~ +- Inside source: true +*** True Line Result + def draw_screenshots +** Processing line: ~ @args.outputs.screenshots.each { |s| draw_screenshot s }~ +- Inside source: true +*** True Line Result + @args.outputs.screenshots.each { |s| draw_screenshot s } +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** 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: ~* runtime/framerate_diagnostics.rb~ - Header detected. *** True Line Result @@ -105368,10 +107475,30 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result #+begin_src -** Processing line: ~ args.outputs.debug << args.gtk.framerate_diagnostics_primitives~ +** Processing line: ~ def tick args~ +- Inside source: true +*** True Line Result + def tick args +** Processing line: ~ # ....~ - Inside source: true *** True Line Result - args.outputs.debug << args.gtk.framerate_diagnostics_primitives + # .... +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ # IMPORTANT: Put this at the END of the ~tick~ method.~ +- Inside source: true +*** True Line Result + # IMPORTANT: Put this at the END of the ~tick~ method. +** Processing line: ~ args.outputs.debug << args.gtk.framerate_diagnostics_primitives~ +- Inside source: true +*** True Line Result + args.outputs.debug << args.gtk.framerate_diagnostics_primitives +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end ** Processing line: ~ #+end_src~ - Inside source: true *** True Line Result @@ -106400,6 +108527,22 @@ Follows is a source code listing for all files that have been open sourced. This - Inside source: true *** True Line Result end +** Processing line: ~~ +- Inside source: true +*** True Line Result + +** Processing line: ~ def serialize~ +- Inside source: true +*** True Line Result + def serialize +** Processing line: ~ self~ +- Inside source: true +*** True Line Result + self +** Processing line: ~ end~ +- Inside source: true +*** True Line Result + end ** Processing line: ~ end~ - Inside source: true *** True Line Result @@ -108793,6 +110936,49 @@ Follows is a source code listing for all files that have been open sourced. This - Line's tilde count is: 0 - Line contains link marker: false ** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* Deploying To Mobile Devices~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~Deploying To Mobile Devices~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~If you have a Pro subscription, you also have the capability to deploy to mobile devices.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~If you have a Pro subscription, you also have the capability to deploy to mobile devices.~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~To deploy to iOS, you need to have a Mac running MacOS Catalina, an iOS device, and an active/paid Developer Account with Apple. From the Console type: ~$wizards.ios.start~ and you will be guided through the deployment process.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~To deploy to iOS, you need to have a Mac running MacOS Catalina, an iOS device, and an active/paid Developer Account with Apple. From the Console type: ~$wizards.ios.start~ and you will be guided through the deployment process.~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~To deploy to Android, you need to have an Android emulator/device, and a environment that is able to run Android SDK. ~dragonruby-publish~ will create an APK for you. From there, you can sign the APK and install it to your device. The signing and installation procedure varies from OS to OS. Here's an example of what the command might look like:~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~To deploy to Android, you need to have an Android emulator/device, and a environment that is able to run Android SDK. ~dragonruby-publish~ will create an APK for you. From there, you can sign the APK and install it to your device. The signing and installation procedure varies from OS to OS. Here's an example of what the command might look like:~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~#+begin_src~ +- PRE start detected. +** Processing line: ~ > adb logcat -e mygame # you'll want to run this in a separate terminal~ +** Processing line: ~ > keytool -genkey -v -keystore mygame.keystore -alias mygame -keyalg RSA -keysize 2048 -validity 10000~ +** Processing line: ~ > apksigner sign --ks mygame.keystore mygame-android.apk~ +** Processing line: ~ > adb install mygame-android.apk~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ ** Processing line: ~* DragonRuby's Philosophy~ - H1 detected. - Determining if line is a header. @@ -109052,559 +111238,6 @@ Follows is a source code listing for all files that have been open sourced. This - Line's tilde count is: 0 - Line contains link marker: false ** Processing line: ~~ -** Processing line: ~* How To Determine What Frame You Are On~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~How To Determine What Frame You Are On~ -- Line's tilde count is: 0 -- Line contains link marker: false -** 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. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting 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~.~ -- Line's tilde count is: 8 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ args.outputs.labels << [10, 670, "#{args.state.tick_count}"]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* How To Get Current Framerate~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~How To Get Current Framerate~ -- Line's tilde count is: 0 -- Line contains link marker: false -** 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. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~Current framerate is a top level property on the Game Toolkit Runtime and is accessible via ~args.gtk.current_framerate~.~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ args.outputs.labels << [10, 710, "framerate: #{args.gtk.current_framerate.round}"]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* How To Render A Sprite Using An Array~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~How To Render A Sprite Using An Array~ -- Line's tilde count is: 0 -- Line contains link marker: false -** 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. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting 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).~ -- Line's tilde count is: 8 -- Line contains link marker: false -- CODE detected. -** Processing line: ~The following code renders a sprite with a ~width~ and ~height~ of ~100~ in the center of the screen.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~The following code renders a sprite with a ~width~ and ~height~ of ~100~ in the center of the screen.~ -- Line's tilde count is: 6 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~args.outputs.sprites~ is used to render a sprite.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~~args.outputs.sprites~ is used to render a sprite.~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ args.outputs.sprites << [~ -** Processing line: ~ 640 - 50, # X~ -** Processing line: ~ 360 - 50, # Y~ -** Processing line: ~ 100, # W~ -** Processing line: ~ 100, # H~ -** Processing line: ~ 'sprites/square-blue.png' # PATH~ -** Processing line: ~ ]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* More Sprite Properties As An Array~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~More Sprite Properties As An Array~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~Here are all the properties you can set on a sprite.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~Here are all the properties you can set on a sprite.~ -- 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.outputs.sprites << [~ -** Processing line: ~ 100, # X~ -** Processing line: ~ 100, # Y~ -** Processing line: ~ 32, # W~ -** Processing line: ~ 64, # H~ -** Processing line: ~ 'sprites/square-blue.png', # PATH~ -** Processing line: ~ 0, # ANGLE~ -** Processing line: ~ 255, # ALPHA~ -** Processing line: ~ 0, # RED_SATURATION~ -** Processing line: ~ 255, # GREEN_SATURATION~ -** Processing line: ~ 0 # BLUE_SATURATION~ -** Processing line: ~ ]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* Different Sprite Representations~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~Different Sprite Representations~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~Using ordinal positioning can get a little unruly given so many properties you have control over.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~Using ordinal positioning can get a little unruly given so many properties you have control over.~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~You can represent a sprite as a ~Hash~:~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~You can represent a sprite as a ~Hash~:~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ args.outputs.sprites << {~ -** Processing line: ~ x: 640 - 50,~ -** Processing line: ~ y: 360 - 50,~ -** Processing line: ~ w: 100,~ -** Processing line: ~ h: 100,~ -** Processing line: ~ path: 'sprites/square-blue.png',~ -** Processing line: ~ angle: 0,~ -** Processing line: ~ a: 255,~ -** Processing line: ~ r: 255,~ -** Processing line: ~ g: 255,~ -** Processing line: ~ b: 255,~ -** Processing line: ~ source_x: 0,~ -** Processing line: ~ source_y: 0,~ -** Processing line: ~ source_w: -1,~ -** Processing line: ~ source_h: -1,~ -** Processing line: ~ flip_vertically: false,~ -** Processing line: ~ flip_horizontally: false,~ -** Processing line: ~ angle_anchor_x: 0.5,~ -** Processing line: ~ angle_anchor_y: 1.0~ -** Processing line: ~ }~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~You can represent a sprite as an ~object~:~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~You can represent a sprite as an ~object~:~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ # Create type with ALL sprite properties AND primitive_marker~ -** Processing line: ~ class Sprite~ -** Processing line: ~ attr_accessor :x, :y, :w, :h, :path, :angle, :a, :r, :g, :b,~ -** Processing line: ~ :source_x, :source_y, :source_w, :source_h,~ -** Processing line: ~ :tile_x, :tile_y, :tile_w, :tile_h,~ -** Processing line: ~ :flip_horizontally, :flip_vertically,~ -** Processing line: ~ :angle_anchor_x, :angle_anchor_y~ -** Processing line: ~~ -** Processing line: ~ def primitive_marker~ -** Processing line: ~ :sprite~ -** Processing line: ~ end~ -** Processing line: ~ end~ -** Processing line: ~~ -** Processing line: ~ class BlueSquare < Sprite~ -** Processing line: ~ def initialize opts~ -** Processing line: ~ @x = opts[:x]~ -** Processing line: ~ @y = opts[:y]~ -** Processing line: ~ @w = opts[:w]~ -** Processing line: ~ @h = opts[:h]~ -** Processing line: ~ @path = 'sprites/square-blue.png'~ -** Processing line: ~ end~ -** Processing line: ~ end~ -** Processing line: ~~ -** Processing line: ~ def tick args~ -** Processing line: ~ args.outputs.sprites << (BlueSquare.new x: 640 - 50,~ -** Processing line: ~ y: 360 - 50,~ -** Processing line: ~ w: 50,~ -** Processing line: ~ h: 50)~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* How To Render A Label~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~How To Render A Label~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~~args.outputs.labels~ is used to render labels.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~~args.outputs.labels~ is used to render labels.~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~Labels are how you display text. This code will go directly inside of the ~def tick args~ method.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~Labels are how you display text. This code will go directly inside of the ~def tick args~ method.~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~Here is the minimum code:~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~Here is the minimum code:~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~#+begin_src~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ # X Y TEXT~ -** Processing line: ~ args.outputs.labels << [640, 360, "I am a black label."]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* A Colored Label~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~A Colored Label~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~#+begin_src~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ # A colored label~ -** Processing line: ~ # X Y TEXT, RED GREEN BLUE ALPHA~ -** Processing line: ~ args.outputs.labels << [640, 360, "I am a redish label.", 255, 128, 128, 255]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* Extended Label Properties~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~Extended Label Properties~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~#+begin_src~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ # A colored label~ -** Processing line: ~ # X Y TEXT SIZE ALIGNMENT RED GREEN BLUE ALPHA FONT FILE~ -** Processing line: ~ args.outputs.labels << [~ -** Processing line: ~ 640, # X~ -** Processing line: ~ 360, # Y~ -** Processing line: ~ "Hello world", # TEXT~ -** Processing line: ~ 0, # SIZE_ENUM~ -** Processing line: ~ 1, # ALIGNMENT_ENUM~ -** Processing line: ~ 0, # RED~ -** Processing line: ~ 0, # GREEN~ -** Processing line: ~ 0, # BLUE~ -** Processing line: ~ 255, # ALPHA~ -** Processing line: ~ "fonts/coolfont.ttf" # FONT~ -** Processing line: ~ ]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~A ~SIZE_ENUM~ of ~0~ represents "default size". A ~negative~ value will decrease the label size. A ~positive~ value will increase the label's size.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~A ~SIZE_ENUM~ of ~0~ represents "default size". A ~negative~ value will decrease the label size. A ~positive~ value will increase the label's size.~ -- Line's tilde count is: 8 -- Line contains link marker: false -- CODE detected. -** Processing line: ~An ~ALIGNMENT_ENUM~ of ~0~ represents "left aligned". ~1~ represents "center aligned". ~2~ represents "right aligned".~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~An ~ALIGNMENT_ENUM~ of ~0~ represents "left aligned". ~1~ represents "center aligned". ~2~ represents "right aligned".~ -- Line's tilde count is: 8 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~* Rendering A Label As A ~Hash~~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~Rendering A Label As A ~Hash~~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE 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. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~You can add additional metadata about your game within a label, which requires you to use a `Hash` instead.~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~#+begin_src~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ args.outputs.labels << {~ -** Processing line: ~ x: 200,~ -** Processing line: ~ y: 550,~ -** Processing line: ~ text: "dragonruby",~ -** Processing line: ~ size_enum: 2,~ -** Processing line: ~ alignment_enum: 1,~ -** Processing line: ~ r: 155,~ -** Processing line: ~ g: 50,~ -** Processing line: ~ b: 50,~ -** Processing line: ~ a: 255,~ -** Processing line: ~ font: "fonts/manaspc.ttf",~ -** Processing line: ~ # You can add any properties you like (this will be ignored/won't cause errors)~ -** Processing line: ~ game_data_one: "Something",~ -** Processing line: ~ game_data_two: {~ -** Processing line: ~ value_1: "value",~ -** Processing line: ~ value_2: "value two",~ -** Processing line: ~ a_number: 15~ -** Processing line: ~ }~ -** Processing line: ~ }~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* Getting The Size Of A Piece Of Text~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~Getting The Size Of A Piece Of Text~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~You can get the render size of any string using ~args.gtk.calcstringbox~.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~You can get the render size of any string using ~args.gtk.calcstringbox~.~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ # TEXT SIZE_ENUM FONT~ -** Processing line: ~ w, h = args.gtk.calcstringbox("some string", 0, "font.ttf")~ -** Processing line: ~~ -** Processing line: ~ # NOTE: The SIZE_ENUM and FONT are optional arguments.~ -** Processing line: ~~ -** Processing line: ~ # Render a label showing the w and h of the text:~ -** Processing line: ~ args.outputs.labels << [~ -** Processing line: ~ 10,~ -** Processing line: ~ 710,~ -** Processing line: ~ # This string uses Ruby's string interpolation literal: #{}~ -** Processing line: ~ "'some string' has width: #{w}, and height: #{h}."~ -** Processing line: ~ ]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* How To Play A Sound~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~How To Play A Sound~ -- Line's tilde count is: 0 -- Line contains link marker: false -** Processing line: ~~ -** Processing line: ~Sounds that end ~.wav~ will play once:~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~Sounds that end ~.wav~ will play once:~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ # Play a sound every second~ -** Processing line: ~ if (args.state.tick_count % 60) == 0~ -** Processing line: ~ args.outputs.sounds << 'something.wav'~ -** Processing line: ~ end~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~Sounds that end ~.ogg~ is considered background music and will loop:~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~Sounds that end ~.ogg~ is considered background music and will loop:~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ # Start a sound loop at the beginning of the game~ -** Processing line: ~ if args.state.tick_count == 0~ -** Processing line: ~ args.outputs.sounds << 'background_music.ogg'~ -** Processing line: ~ end~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~If you want to play a ~.ogg~ once as if it were a sound effect, you can do:~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~If you want to play a ~.ogg~ once as if it were a sound effect, you can do:~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE detected. -** Processing line: ~~ -** Processing line: ~#+begin_src ruby~ -- PRE start detected. -** Processing line: ~ def tick args~ -** Processing line: ~ # Play a sound every second~ -** Processing line: ~ if (args.state.tick_count % 60) == 0~ -** Processing line: ~ args.gtk.queue_sound 'some-ogg.ogg'~ -** Processing line: ~ end~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ -** Processing line: ~* Using ~args.state~ To Store Your Game State~ -- H1 detected. -- Determining if line is a header. -- Line contains ~* ~... gsub-ing empty string -- Formatting line: ~Using ~args.state~ To Store Your Game State~ -- Line's tilde count is: 2 -- Line contains link marker: false -- CODE 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. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting 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~.~ -- Line's tilde count is: 4 -- Line contains link marker: false -- CODE detected. -** Processing line: ~To initialize your game state, use the ~||=~ operator. Any value on the right side of ~||=~ will only be assigned _once_.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~To initialize your game state, use the ~||=~ operator. Any value on the right side of ~||=~ will only be assigned _once_.~ -- Line's tilde count is: 4 -- Line contains link marker: false -- CODE detected. -** Processing line: ~To assign a value every frame, just use the ~=~ operator, but _make sure_ you've initialized a default value.~ -- P detected. -- Determining if line is a header. -- Line does not appear to be a header. -- Formatting line: ~To assign a value every frame, just use the ~=~ operator, but _make sure_ you've initialized a default value.~ -- 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: ~ # initialize your game state ONCE~ -** Processing line: ~ args.state.player.x ||= 0~ -** Processing line: ~ args.state.player.y ||= 0~ -** Processing line: ~ args.state.player.hp ||= 100~ -** Processing line: ~~ -** Processing line: ~ # increment the x position of the character by one every frame~ -** Processing line: ~ args.state.player.x += 1~ -** Processing line: ~~ -** Processing line: ~ # Render a sprite with a label above the sprite~ -** Processing line: ~ args.outputs.sprites << [~ -** Processing line: ~ args.state.player.x,~ -** Processing line: ~ args.state.player.y,~ -** Processing line: ~ 32, 32,~ -** Processing line: ~ "player.png"~ -** Processing line: ~ ]~ -** Processing line: ~~ -** Processing line: ~ args.outputs.labels << [~ -** Processing line: ~ args.state.player.x,~ -** Processing line: ~ args.state.player.y - 50,~ -** Processing line: ~ args.state.player.hp~ -** Processing line: ~ ]~ -** Processing line: ~ end~ -** Processing line: ~#+end_src~ -- PRE end detected. -** Processing line: ~~ -** Processing line: ~~ ** Processing line: ~* Frequently Asked Questions, Comments, and Concerns~ - H1 detected. - Determining if line is a header. @@ -110479,65 +112112,656 @@ Follows is a source code listing for all files that have been open sourced. This - Line's tilde count is: 0 - Line contains link marker: false ** Processing line: ~~ -** Processing line: ~* DOCS: ~GTK::Runtime~~ +** Processing line: ~* CHEATSHEET: How To Determine What Frame You Are On~ - H1 detected. - Determining if line is a header. - Line contains ~* ~... gsub-ing empty string -- Formatting line: ~DOCS: ~GTK::Runtime~~ +- Formatting line: ~CHEATSHEET: How To Determine What Frame You Are On~ +- Line's tilde count is: 0 +- Line contains link marker: false +** 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. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting 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~.~ +- Line's tilde count is: 8 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ args.outputs.labels << [10, 670, "#{args.state.tick_count}"]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: How To Get Current Framerate~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: How To Get Current Framerate~ +- Line's tilde count is: 0 +- Line contains link marker: false +** 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. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~Current framerate is a top level property on the Game Toolkit Runtime and is accessible via ~args.gtk.current_framerate~.~ - Line's tilde count is: 2 - Line contains link marker: false - CODE detected. -** Processing line: ~The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.~ +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ args.outputs.labels << [10, 710, "framerate: #{args.gtk.current_framerate.round}"]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: How To Render A Sprite Using An Array~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: How To Render A Sprite Using An Array~ +- Line's tilde count is: 0 +- Line contains link marker: false +** 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. - Determining if line is a header. - Line does not appear to be a header. -- Formatting line: ~The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.~ +- Formatting 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).~ +- Line's tilde count is: 8 +- Line contains link marker: false +- CODE detected. +** Processing line: ~The following code renders a sprite with a ~width~ and ~height~ of ~100~ in the center of the screen.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~The following code renders a sprite with a ~width~ and ~height~ of ~100~ in the center of the screen.~ +- Line's tilde count is: 6 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~args.outputs.sprites~ is used to render a sprite.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~~args.outputs.sprites~ is used to render a sprite.~ - Line's tilde count is: 2 - Line contains link marker: false - CODE detected. ** Processing line: ~~ -** Processing line: ~* DOCS: ~GTK::Runtime#reset~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ args.outputs.sprites << [~ +** Processing line: ~ 640 - 50, # X~ +** Processing line: ~ 360 - 50, # Y~ +** Processing line: ~ 100, # W~ +** Processing line: ~ 100, # H~ +** Processing line: ~ 'sprites/square-blue.png' # PATH~ +** Processing line: ~ ]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: More Sprite Properties As An Array~ - H1 detected. - Determining if line is a header. - Line contains ~* ~... gsub-ing empty string -- Formatting line: ~DOCS: ~GTK::Runtime#reset~~ +- Formatting line: ~CHEATSHEET: More Sprite Properties As An Array~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~Here are all the properties you can set on a sprite.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~Here are all the properties you can set on a sprite.~ +- 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.outputs.sprites << [~ +** Processing line: ~ 100, # X~ +** Processing line: ~ 100, # Y~ +** Processing line: ~ 32, # W~ +** Processing line: ~ 64, # H~ +** Processing line: ~ 'sprites/square-blue.png', # PATH~ +** Processing line: ~ 0, # ANGLE~ +** Processing line: ~ 255, # ALPHA~ +** Processing line: ~ 0, # RED_SATURATION~ +** Processing line: ~ 255, # GREEN_SATURATION~ +** Processing line: ~ 0 # BLUE_SATURATION~ +** Processing line: ~ ]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: Different Sprite Representations~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: Different Sprite Representations~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~Using ordinal positioning can get a little unruly given so many properties you have control over.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~Using ordinal positioning can get a little unruly given so many properties you have control over.~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~You can represent a sprite as a ~Hash~:~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~You can represent a sprite as a ~Hash~:~ - 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.~ +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ args.outputs.sprites << {~ +** Processing line: ~ x: 640 - 50,~ +** Processing line: ~ y: 360 - 50,~ +** Processing line: ~ w: 100,~ +** Processing line: ~ h: 100,~ +** Processing line: ~ path: 'sprites/square-blue.png',~ +** Processing line: ~ angle: 0,~ +** Processing line: ~ a: 255,~ +** Processing line: ~ r: 255,~ +** Processing line: ~ g: 255,~ +** Processing line: ~ b: 255,~ +** Processing line: ~ source_x: 0,~ +** Processing line: ~ source_y: 0,~ +** Processing line: ~ source_w: -1,~ +** Processing line: ~ source_h: -1,~ +** Processing line: ~ flip_vertically: false,~ +** Processing line: ~ flip_horizontally: false,~ +** Processing line: ~ angle_anchor_x: 0.5,~ +** Processing line: ~ angle_anchor_y: 1.0~ +** Processing line: ~ }~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~You can represent a sprite as an ~object~:~ - P detected. - Determining if line is a header. - Line does not appear to be a header. -- Formatting line: ~This function will reset Kernel.tick_count to 0 and will remove all data from args.state.~ +- Formatting line: ~You can represent a sprite as an ~object~:~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ # Create type with ALL sprite properties AND primitive_marker~ +** Processing line: ~ class Sprite~ +** Processing line: ~ attr_accessor :x, :y, :w, :h, :path, :angle, :a, :r, :g, :b,~ +** Processing line: ~ :source_x, :source_y, :source_w, :source_h,~ +** Processing line: ~ :tile_x, :tile_y, :tile_w, :tile_h,~ +** Processing line: ~ :flip_horizontally, :flip_vertically,~ +** Processing line: ~ :angle_anchor_x, :angle_anchor_y~ +** Processing line: ~~ +** Processing line: ~ def primitive_marker~ +** Processing line: ~ :sprite~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ class BlueSquare < Sprite~ +** Processing line: ~ def initialize opts~ +** Processing line: ~ @x = opts[:x]~ +** Processing line: ~ @y = opts[:y]~ +** Processing line: ~ @w = opts[:w]~ +** Processing line: ~ @h = opts[:h]~ +** Processing line: ~ @path = 'sprites/square-blue.png'~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def tick args~ +** Processing line: ~ args.outputs.sprites << (BlueSquare.new x: 640 - 50,~ +** Processing line: ~ y: 360 - 50,~ +** Processing line: ~ w: 50,~ +** Processing line: ~ h: 50)~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: How To Render A Label~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: How To Render A Label~ - Line's tilde count is: 0 - Line contains link marker: false ** Processing line: ~~ -** Processing line: ~* DOCS: ~GTK::Runtime#calcstringbox~~ +** Processing line: ~~args.outputs.labels~ is used to render labels.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~~args.outputs.labels~ is used to render labels.~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~Labels are how you display text. This code will go directly inside of the ~def tick args~ method.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~Labels are how you display text. This code will go directly inside of the ~def tick args~ method.~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~Here is the minimum code:~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~Here is the minimum code:~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~#+begin_src~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ # X Y TEXT~ +** Processing line: ~ args.outputs.labels << [640, 360, "I am a black label."]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: A Colored Label~ - H1 detected. - Determining if line is a header. - Line contains ~* ~... gsub-ing empty string -- Formatting line: ~DOCS: ~GTK::Runtime#calcstringbox~~ +- Formatting line: ~CHEATSHEET: A Colored Label~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~#+begin_src~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ # A colored label~ +** Processing line: ~ # X Y TEXT, RED GREEN BLUE ALPHA~ +** Processing line: ~ args.outputs.labels << [640, 360, "I am a redish label.", 255, 128, 128, 255]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: Extended Label Properties~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: Extended Label Properties~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~#+begin_src~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ # A colored label~ +** Processing line: ~ # X Y TEXT SIZE ALIGNMENT RED GREEN BLUE ALPHA FONT FILE~ +** Processing line: ~ args.outputs.labels << [~ +** Processing line: ~ 640, # X~ +** Processing line: ~ 360, # Y~ +** Processing line: ~ "Hello world", # TEXT~ +** Processing line: ~ 0, # SIZE_ENUM~ +** Processing line: ~ 1, # ALIGNMENT_ENUM~ +** Processing line: ~ 0, # RED~ +** Processing line: ~ 0, # GREEN~ +** Processing line: ~ 0, # BLUE~ +** Processing line: ~ 255, # ALPHA~ +** Processing line: ~ "fonts/coolfont.ttf" # FONT~ +** Processing line: ~ ]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~A ~SIZE_ENUM~ of ~0~ represents "default size". A ~negative~ value will decrease the label size. A ~positive~ value will increase the label's size.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~A ~SIZE_ENUM~ of ~0~ represents "default size". A ~negative~ value will decrease the label size. A ~positive~ value will increase the label's size.~ +- Line's tilde count is: 8 +- Line contains link marker: false +- CODE detected. +** Processing line: ~An ~ALIGNMENT_ENUM~ of ~0~ represents "left aligned". ~1~ represents "center aligned". ~2~ represents "right aligned".~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~An ~ALIGNMENT_ENUM~ of ~0~ represents "left aligned". ~1~ represents "center aligned". ~2~ represents "right aligned".~ +- Line's tilde count is: 8 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: Rendering A Label As A ~Hash~~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: Rendering A Label As A ~Hash~~ - Line's tilde count is: 2 - Line contains link marker: false - CODE detected. -** Processing line: ~This function returns the width and height of a string.~ +** 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. - Determining if line is a header. - Line does not appear to be a header. -- Formatting line: ~This function returns the width and height of a string.~ +- Formatting line: ~You can add additional metadata about your game within a label, which requires you to use a `Hash` instead.~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~#+begin_src~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ args.outputs.labels << {~ +** Processing line: ~ x: 200,~ +** Processing line: ~ y: 550,~ +** Processing line: ~ text: "dragonruby",~ +** Processing line: ~ size_enum: 2,~ +** Processing line: ~ alignment_enum: 1,~ +** Processing line: ~ r: 155,~ +** Processing line: ~ g: 50,~ +** Processing line: ~ b: 50,~ +** Processing line: ~ a: 255,~ +** Processing line: ~ font: "fonts/manaspc.ttf",~ +** Processing line: ~ # You can add any properties you like (this will be ignored/won't cause errors)~ +** Processing line: ~ game_data_one: "Something",~ +** Processing line: ~ game_data_two: {~ +** Processing line: ~ value_1: "value",~ +** Processing line: ~ value_2: "value two",~ +** Processing line: ~ a_number: 15~ +** Processing line: ~ }~ +** Processing line: ~ }~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: Getting The Size Of A Piece Of Text~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: Getting The Size Of A Piece Of Text~ - Line's tilde count is: 0 - Line contains link marker: false ** Processing line: ~~ +** Processing line: ~You can get the render size of any string using ~args.gtk.calcstringbox~.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~You can get the render size of any string using ~args.gtk.calcstringbox~.~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ ** Processing line: ~#+begin_src ruby~ - PRE start detected. ** Processing line: ~ def tick args~ -** Processing line: ~ args.state.string_size ||= args.gtk.calcstringbox "Hello World"~ -** Processing line: ~ args.state.string_size_font_size ||= args.gtk.calcstringbox "Hello World"~ +** Processing line: ~ # TEXT SIZE_ENUM FONT~ +** Processing line: ~ w, h = args.gtk.calcstringbox("some string", 0, "font.ttf")~ +** Processing line: ~~ +** Processing line: ~ # NOTE: The SIZE_ENUM and FONT are optional arguments.~ +** Processing line: ~~ +** Processing line: ~ # Render a label showing the w and h of the text:~ +** Processing line: ~ args.outputs.labels << [~ +** Processing line: ~ 10,~ +** Processing line: ~ 710,~ +** Processing line: ~ # This string uses Ruby's string interpolation literal: #{}~ +** Processing line: ~ "'some string' has width: #{w}, and height: #{h}."~ +** Processing line: ~ ]~ ** Processing line: ~ end~ ** Processing line: ~#+end_src~ - PRE end detected. ** Processing line: ~~ ** Processing line: ~~ +** Processing line: ~* CHEATSHEET: How To Play A Sound~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: How To Play A Sound~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~Sounds that end ~.wav~ will play once:~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~Sounds that end ~.wav~ will play once:~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ # Play a sound every second~ +** Processing line: ~ if (args.state.tick_count % 60) == 0~ +** Processing line: ~ args.outputs.sounds << 'something.wav'~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~Sounds that end ~.ogg~ is considered background music and will loop:~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~Sounds that end ~.ogg~ is considered background music and will loop:~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ # Start a sound loop at the beginning of the game~ +** Processing line: ~ if args.state.tick_count == 0~ +** Processing line: ~ args.outputs.sounds << 'background_music.ogg'~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~If you want to play a ~.ogg~ once as if it were a sound effect, you can do:~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~If you want to play a ~.ogg~ once as if it were a sound effect, you can do:~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ def tick args~ +** Processing line: ~ # Play a sound every second~ +** Processing line: ~ if (args.state.tick_count % 60) == 0~ +** Processing line: ~ args.gtk.queue_sound 'some-ogg.ogg'~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: Using ~args.state~ To Store Your Game State~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: Using ~args.state~ To Store Your Game State~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE 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. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting 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~.~ +- Line's tilde count is: 4 +- Line contains link marker: false +- CODE detected. +** Processing line: ~To initialize your game state, use the ~||=~ operator. Any value on the right side of ~||=~ will only be assigned _once_.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~To initialize your game state, use the ~||=~ operator. Any value on the right side of ~||=~ will only be assigned _once_.~ +- Line's tilde count is: 4 +- Line contains link marker: false +- CODE detected. +** Processing line: ~To assign a value every frame, just use the ~=~ operator, but _make sure_ you've initialized a default value.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~To assign a value every frame, just use the ~=~ operator, but _make sure_ you've initialized a default value.~ +- 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: ~ # initialize your game state ONCE~ +** Processing line: ~ args.state.player.x ||= 0~ +** Processing line: ~ args.state.player.y ||= 0~ +** Processing line: ~ args.state.player.hp ||= 100~ +** Processing line: ~~ +** Processing line: ~ # increment the x position of the character by one every frame~ +** Processing line: ~ args.state.player.x += 1~ +** Processing line: ~~ +** Processing line: ~ # Render a sprite with a label above the sprite~ +** Processing line: ~ args.outputs.sprites << [~ +** Processing line: ~ args.state.player.x,~ +** Processing line: ~ args.state.player.y,~ +** Processing line: ~ 32, 32,~ +** Processing line: ~ "player.png"~ +** Processing line: ~ ]~ +** Processing line: ~~ +** Processing line: ~ args.outputs.labels << [~ +** Processing line: ~ args.state.player.x,~ +** Processing line: ~ args.state.player.y - 50,~ +** Processing line: ~ args.state.player.hp~ +** Processing line: ~ ]~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* CHEATSHEET: Troubleshoot Performance~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~CHEATSHEET: Troubleshoot Performance~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~1. If you're using ~Array~s for your primitives (~args.outputs.sprites << []~), use ~Hash~ instead (~args.outputs.sprites << { x: ... }~).~ +- OL start detected. +- LI detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~ If you're using ~Array~s for your primitives (~args.outputs.sprites << []~), use ~Hash~ instead (~args.outputs.sprites << { x: ... }~).~ +- Line's tilde count is: 8 +- Line contains link marker: false +- CODE detected. +** Processing line: ~2. If you're using ~Entity~ for your primitives (~args.outputs.sprites << args.state.new_entity~), use ~StrictEntity~ instead (~args.outputs.sprites << args.state.new_entity_strict~).~ +- LI detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~ If you're using ~Entity~ for your primitives (~args.outputs.sprites << args.state.new_entity~), use ~StrictEntity~ instead (~args.outputs.sprites << args.state.new_entity_strict~).~ +- Line's tilde count is: 8 +- Line contains link marker: false +- CODE detected. +** Processing line: ~3. Use ~.each~ instead of ~.map~ if you don't care about the return value.~ +- LI detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~ Use ~.each~ instead of ~.map~ if you don't care about the return value.~ +- Line's tilde count is: 4 +- Line contains link marker: false +- CODE detected. +** Processing line: ~4. When concatenating primitives to outputs, do them in bulk. Instead of:~ +- LI detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~ When concatenating primitives to outputs, do them in bulk. Instead of:~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ args.state.bullets.each do |bullet|~ +** Processing line: ~ args.outputs.sprites << bullet.sprite~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~do~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~do~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~#+begin_src~ +- PRE start detected. +** Processing line: ~ args.outputs.sprites << args.state.bullets.map do |b|~ +** Processing line: ~ b.sprite~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~5. Use ~args.outputs.static_~ variant for things that don't change often (take a look at the Basic Gorillas sample app and Dueling Starships sample app to see how ~static_~ is leveraged.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~5. Use ~args.outputs.static_~ variant for things that don't change often (take a look at the Basic Gorillas sample app and Dueling Starships sample app to see how ~static_~ is leveraged.~ +- Line's tilde count is: 4 +- Line contains link marker: false +- CODE detected. +** Processing line: ~6. Consider using a ~render_target~ if you're doing some form of a camera that moves a lot of primitives (take a look at the Render Target sample apps for more info).~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~6. Consider using a ~render_target~ if you're doing some form of a camera that moves a lot of primitives (take a look at the Render Target sample apps for more info).~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ +** Processing line: ~* DOCS: ~GTK::Runtime~~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~DOCS: ~GTK::Runtime~~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~The GTK::Runtime class is the core of DragonRuby. It is globally accessible via ~$gtk~.~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~~ ** Processing line: ~* DOCS: ~GTK::Runtime#write_file~~ - H1 detected. - Determining if line is a header. @@ -110566,6 +112790,48 @@ Follows is a source code listing for all files that have been open sourced. This - PRE end detected. ** Processing line: ~~ ** Processing line: ~~ +** Processing line: ~* DOCS: ~GTK::Runtime#calcstringbox~~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~DOCS: ~GTK::Runtime#calcstringbox~~ +- Line's tilde count is: 2 +- Line contains link marker: false +- CODE detected. +** Processing line: ~This function returns the width and height of a string.~ +- P detected. +- Determining if line is a header. +- Line does not appear to be a header. +- Formatting line: ~This function returns the width and height of a string.~ +- 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.string_size ||= args.gtk.calcstringbox "Hello World"~ +** Processing line: ~ args.state.string_size_font_size ||= args.gtk.calcstringbox "Hello World"~ +** Processing line: ~ end~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~* DOCS: ~GTK::Runtime#reset~~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~DOCS: ~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. +- Determining if line is a header. +- Line does not appear to be a header. +- 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. - Determining if line is a header. @@ -113742,6 +116008,180 @@ Follows is a source code listing for all files that have been open sourced. This - PRE end detected. ** Processing line: ~~ ** Processing line: ~~ +** Processing line: ~* Rendering Basics - Audio Mixer - main.rb~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~Rendering Basics - Audio Mixer - main.rb~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ # ./samples/01_rendering_basics/06_audio_mixer/app/main.rb~ +** Processing line: ~ $gtk.reset~ +** Processing line: ~~ +** Processing line: ~ $boxsize = 30~ +** Processing line: ~~ +** Processing line: ~ def render_sources args~ +** Processing line: ~ mouse_in_panel = (args.state.selected != 0) && args.inputs.mouse.position.inside_rect?([900, 450, 340, 250])~ +** Processing line: ~ mouse_new_down = (args.state.mouse_held == 1)~ +** Processing line: ~~ +** Processing line: ~ if (mouse_new_down && !mouse_in_panel)~ +** Processing line: ~ args.state.selected = 0 # will reset below if we hit something.~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ args.audio.keys.each { |k|~ +** Processing line: ~ s = args.audio[k]~ +** Processing line: ~~ +** Processing line: ~ if (mouse_new_down) && !mouse_in_panel && args.inputs.mouse.position.inside_rect?([s[:screenx], s[:screeny], $boxsize, $boxsize])~ +** Processing line: ~ args.state.selected = k~ +** Processing line: ~ args.state.dragging_source = true~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ isselected = (k == args.state.selected)~ +** Processing line: ~~ +** Processing line: ~ if isselected && args.state.dragging_source~ +** Processing line: ~ # you can hang anything on the audio hashes you want, so we store the~ +** Processing line: ~ # actual screen position so it doesn't scale weirdly vs your mouse.~ +** Processing line: ~ s[:screenx] = args.inputs.mouse.x - ($boxsize / 2)~ +** Processing line: ~ s[:screeny] = args.inputs.mouse.y - ($boxsize / 2)~ +** Processing line: ~~ +** Processing line: ~ s[:screeny] = 50 if s[:screeny] < 50~ +** Processing line: ~ s[:screeny] = (719 - $boxsize) if s[:screeny] > (719 - $boxsize)~ +** Processing line: ~ s[:screenx] = 0 if s[:screenx] < 0~ +** Processing line: ~ s[:screenx] = (1279 - $boxsize) if s[:screenx] > (1279 - $boxsize)~ +** Processing line: ~~ +** Processing line: ~ s[:x] = ((s[:screenx] / 1279.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range~ +** Processing line: ~ s[:y] = ((s[:screeny] / 719.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ color = isselected ? [ 0, 255, 0, 255 ] : [ 0, 0, 255, 255 ]~ +** Processing line: ~ args.outputs.primitives << [s[:screenx], s[:screeny], $boxsize, $boxsize, *color].solid~ +** Processing line: ~ }~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def render_panel args~ +** Processing line: ~ s = args.audio[args.state.selected]~ +** Processing line: ~ return if s.nil?~ +** Processing line: ~ mouse_down = (args.state.mouse_held > 0)~ +** Processing line: ~~ +** Processing line: ~ args.outputs.primitives << [900, 450, 340, 250, 127, 127, 200, 255].solid~ +** Processing line: ~ args.outputs.primitives << [1075, 690, "Source ##{args.state.selected}", 3, 1, 255, 255, 255].label~ +** Processing line: ~ args.outputs.primitives << [910, 660, 1230, 660, 255, 255, 255].line~ +** Processing line: ~ args.outputs.primitives << [910, 650, "screen: (#{s[:screenx].to_i}, #{s[:screeny].to_i})", 0, 0, 255, 255, 255].label~ +** Processing line: ~ args.outputs.primitives << [910, 625, "position: (#{s[:x].round(5).to_s[0..6]}, #{s[:y].round(5).to_s[0..6]})", 0, 0, 255, 255, 255].label~ +** Processing line: ~~ +** Processing line: ~ slider = [1022, 586, 200, 7]~ +** Processing line: ~ if mouse_down && args.inputs.mouse.position.inside_rect?(slider)~ +** Processing line: ~ s[:pitch] = ((args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0)) * 2.0~ +** Processing line: ~ end~ +** Processing line: ~ slidercolor = (s[:pitch] / 2.0) * 255~ +** Processing line: ~ args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid~ +** Processing line: ~ args.outputs.primitives << [910, 600, "pitch: #{s[:pitch].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label~ +** Processing line: ~~ +** Processing line: ~ slider = [1022, 561, 200, 7]~ +** Processing line: ~ if mouse_down && args.inputs.mouse.position.inside_rect?(slider)~ +** Processing line: ~ s[:gain] = (args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0)~ +** Processing line: ~ end~ +** Processing line: ~ slidercolor = s[:gain] * 255~ +** Processing line: ~ args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid~ +** Processing line: ~ args.outputs.primitives << [910, 575, "gain: #{s[:gain].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label~ +** Processing line: ~~ +** Processing line: ~ checkbox = [1022, 533, 10, 12]~ +** Processing line: ~ if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox)~ +** Processing line: ~ s[:looping] = !s[:looping]~ +** Processing line: ~ end~ +** Processing line: ~ checkboxcolor = s[:looping] ? 255 : 0~ +** Processing line: ~ args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid~ +** Processing line: ~ args.outputs.primitives << [910, 550, "looping:", 0, 0, 255, 255, 255].label~ +** Processing line: ~~ +** Processing line: ~ checkbox = [1022, 508, 10, 12]~ +** Processing line: ~ if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox)~ +** Processing line: ~ s[:paused] = !s[:paused]~ +** Processing line: ~ end~ +** Processing line: ~ checkboxcolor = s[:paused] ? 255 : 0~ +** Processing line: ~ args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid~ +** Processing line: ~ args.outputs.primitives << [910, 525, "paused:", 0, 0, 255, 255, 255].label~ +** Processing line: ~~ +** Processing line: ~ button = [910, 460, 320, 20]~ +** Processing line: ~ if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(button)~ +** Processing line: ~ args.audio.delete(args.state.selected)~ +** Processing line: ~ args.state.selected = 0~ +** Processing line: ~ end~ +** Processing line: ~ args.outputs.primitives << [*button, 255, 0, 0, 255].solid~ +** Processing line: ~ args.outputs.primitives << [button[0] + (button[2] / 2), button[1]+20, "DELETE SOURCE", 0, 1, 255, 255, 0].label~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def spawn_new_sound args, num~ +** Processing line: ~ # Spawn randomly in an area that won't be covered by UI.~ +** Processing line: ~ screenx = (rand * 600.0) + 200.0~ +** Processing line: ~ screeny = (rand * 400.0) + 100.0~ +** Processing line: ~~ +** Processing line: ~ args.state.next_sound_index += 1~ +** Processing line: ~~ +** Processing line: ~ # you can hang anything on the audio hashes you want, so we store the~ +** Processing line: ~ # actual screen position in here for convenience.~ +** Processing line: ~ args.audio[args.state.next_sound_index] = {~ +** Processing line: ~ filename: "sounds/#{num}.#{(num == 6) ? 'ogg' : 'wav'}",~ +** Processing line: ~ screenx: screenx,~ +** Processing line: ~ screeny: screeny,~ +** Processing line: ~ x: ((screenx / 1279.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range~ +** Processing line: ~ y: ((screeny / 719.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range~ +** Processing line: ~ z: 0.0,~ +** Processing line: ~ gain: 1.0,~ +** Processing line: ~ pitch: 1.0,~ +** Processing line: ~ looping: true,~ +** Processing line: ~ paused: false~ +** Processing line: ~ }~ +** Processing line: ~~ +** Processing line: ~ args.state.selected = args.state.next_sound_index~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def render_launcher args~ +** Processing line: ~ total = 6~ +** Processing line: ~ x = (1280 - (total * $boxsize * 3)) / 2~ +** Processing line: ~ y = 10~ +** Processing line: ~ args.outputs.primitives << [0, 0, 1280, ((y*2) + $boxsize), 127, 127, 127, 255].solid~ +** Processing line: ~ for i in 1..total~ +** Processing line: ~ args.outputs.primitives << [x, y, $boxsize, $boxsize, 255, 255, 255, 255].solid~ +** Processing line: ~ args.outputs.primitives << [x+8, y+28, i.to_s, 3, 0, 0, 0, 255, 255].label~ +** Processing line: ~ if args.inputs.mouse.click && args.inputs.mouse.click.point.inside_rect?([x, y, $boxsize, $boxsize])~ +** Processing line: ~ spawn_new_sound args, i~ +** Processing line: ~ end~ +** Processing line: ~ x = x + ($boxsize * 3)~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def render_ui args~ +** Processing line: ~ render_launcher args~ +** Processing line: ~ render_panel args~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def tick args~ +** Processing line: ~ args.state.mouse_held ||= 0~ +** Processing line: ~ args.state.dragging_source ||= false~ +** Processing line: ~ args.state.selected ||= 0~ +** Processing line: ~ args.state.next_sound_index ||= 0~ +** Processing line: ~~ +** Processing line: ~ if args.inputs.mouse.up~ +** Processing line: ~ args.state.mouse_held = 0~ +** Processing line: ~ args.state.dragging_source = false~ +** Processing line: ~ elsif args.inputs.mouse.down || (args.state.mouse_held > 0)~ +** Processing line: ~ args.state.mouse_held += 1~ +** Processing line: ~ else~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ args.outputs.background_color = [ 0, 0, 0, 255 ]~ +** Processing line: ~ render_sources args~ +** Processing line: ~ render_ui args~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ ** Processing line: ~* Input Basics - Keyboard - main.rb~ - H1 detected. - Determining if line is a header. @@ -118149,17 +120589,17 @@ Follows is a source code listing for all files that have been open sourced. This - PRE end detected. ** Processing line: ~~ ** Processing line: ~~ -** Processing line: ~* Advanced Rendering - Render Targets With Alphas - main.rb~ +** Processing line: ~* Advanced Rendering - Render Targets With Tile Manipulation - main.rb~ - H1 detected. - Determining if line is a header. - Line contains ~* ~... gsub-ing empty string -- Formatting line: ~Advanced Rendering - Render Targets With Alphas - main.rb~ +- Formatting line: ~Advanced Rendering - Render Targets With Tile Manipulation - main.rb~ - Line's tilde count is: 0 - Line contains link marker: false ** Processing line: ~~ ** Processing line: ~#+begin_src ruby~ - PRE start detected. -** Processing line: ~ # ./samples/07_advanced_rendering/02_render_targets_with_alphas/app/main.rb~ +** Processing line: ~ # ./samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb~ ** Processing line: ~ # This sample is meant to show you how to do that dripping transition thing~ ** Processing line: ~ # at the start of the original Doom. Most of this file is here to animate~ ** Processing line: ~ # a scene to wipe away; the actual wipe effect is in the last 20 lines or~ @@ -119638,6 +122078,7 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~#+begin_src ruby~ - PRE start detected. ** Processing line: ~ # ./samples/09_performance/01_sprites_as_hash/app/main.rb~ +** Processing line: ~~ ** Processing line: ~ # Sprites represented as Hashes using the queue ~args.outputs.sprites~~ ** Processing line: ~ # code up, but are the "slowest" to render.~ ** Processing line: ~ # The reason for this is the access of the key in the Hash and also~ @@ -120589,12 +123030,12 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ GTK::Entity.__reset_id__!~ ** Processing line: ~ args.state.player_one = "test"~ ** Processing line: ~ result = args.gtk.serialize_state args.state~ -** Processing line: ~ assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}"~ +** Processing line: ~ assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"~ ** Processing line: ~~ ** Processing line: ~ GTK::Entity.__reset_id__!~ ** Processing line: ~ args.gtk.write_file 'state.txt', ''~ ** Processing line: ~ result = args.gtk.serialize_state 'state.txt', args.state~ -** Processing line: ~ assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}"~ +** Processing line: ~ assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"~ ** Processing line: ~ end~ ** Processing line: ~~ ** Processing line: ~ def test_deserialize args, assert~ @@ -120625,8 +123066,9 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ args.state.player_two = args.state.new_entity_strict(:player_strict, name: "Ken")~ ** Processing line: ~~ ** Processing line: ~ serialized_state = args.gtk.serialize_state args.state~ -** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>1, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken"}}'~ +** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>1, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken"}}'~ ** Processing line: ~~ +** Processing line: ~ GTK::Entity.__reset_id__!~ ** Processing line: ~ deserialize_state = args.gtk.deserialize_state serialized_state~ ** Processing line: ~~ ** Processing line: ~ assert.equal! args.state.player_one.name, deserialize_state.player_one.name~ @@ -120642,8 +123084,9 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ args.state.player_two = args.state.new_entity_strict(:player_strict, name: "Ken", blood_type: nil)~ ** Processing line: ~~ ** Processing line: ~ serialized_state = args.gtk.serialize_state args.state~ -** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>3, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken", :blood_type=>nil}}'~ +** Processing line: ~ assert.equal! serialized_state, '{:entity_id=>9, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>2, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken", :blood_type=>nil}}'~ ** Processing line: ~~ +** Processing line: ~ GTK::Entity.__reset_id__!~ ** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ ** Processing line: ~~ ** Processing line: ~ assert.equal! args.state.player_one.name, deserialized_state.player_one.name~ @@ -120664,12 +123107,41 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ args.state.enemy = args.state.new_entity_strict(:enemy, name: "Bison", other_property: 'extra mean')~ ** Processing line: ~~ ** Processing line: ~ serialized_state = args.gtk.serialize_state args.state~ +** Processing line: ~~ +** Processing line: ~ GTK::Entity.__reset_id__!~ ** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ ** Processing line: ~~ ** Processing line: ~ assert.equal! deserialized_state.player.name, "Ryu"~ ** Processing line: ~ assert.equal! deserialized_state.enemy.other_property, "extra mean"~ ** Processing line: ~ end~ ** Processing line: ~~ +** Processing line: ~ def test_by_reference_state args, assert~ +** Processing line: ~ GTK::Entity.__reset_id__!~ +** Processing line: ~ args.state.a = { name: "Jane Doe" }~ +** Processing line: ~ args.state.b = args.state.a~ +** Processing line: ~ assert.equal! args.state.a.object_id, args.state.b.object_id~ +** Processing line: ~ serialized_state = args.gtk.serialize_state args.state~ +** Processing line: ~~ +** Processing line: ~ GTK::Entity.__reset_id__!~ +** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ +** Processing line: ~ assert.equal! deserialized_state.a.object_id, deserialized_state.b.object_id~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def test_by_reference_state_strict_entities args, assert~ +** Processing line: ~ GTK::Entity.__reset_id__!~ +** Processing line: ~ args.state.a = { name: "Jane Doe" }~ +** Processing line: ~ args.state.strict_entity = args.state.new_entity_strict(:couple) do |e|~ +** Processing line: ~ e.one = args.state.new_entity_strict(:person, name: "Jane")~ +** Processing line: ~ e.two = e.one~ +** Processing line: ~ end~ +** Processing line: ~ assert.equal! args.state.strict_entity.one, args.state.strict_entity.two~ +** Processing line: ~ serialized_state = args.gtk.serialize_state args.state~ +** Processing line: ~~ +** Processing line: ~ GTK::Entity.__reset_id__!~ +** Processing line: ~ deserialized_state = args.gtk.deserialize_state serialized_state~ +** Processing line: ~ assert.equal! deserialized_state.strict_entity.one, deserialized_state.strict_entity.two~ +** Processing line: ~ end~ +** Processing line: ~~ ** Processing line: ~ $tests.start~ ** Processing line: ~~ ** Processing line: ~#+end_src~ @@ -120879,11 +123351,14 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~#+begin_src ruby~ - PRE start detected. ** Processing line: ~ # ./samples/12_c_extensions/01_basics/app/main.rb~ -** Processing line: ~ $gtk.ffi_misc.gtk_dlopen("./samples/12_c_extensions/01_basics/build.dir/ext.lib")~ +** Processing line: ~ $gtk.ffi_misc.gtk_dlopen("ext")~ ** Processing line: ~ include FFI::CExt~ ** Processing line: ~~ ** Processing line: ~ def tick args~ -** Processing line: ~ args.outputs.labels << [460, 600, "square(42) = #{square(42)}"]~ +** Processing line: ~ args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1]~ +** Processing line: ~ args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1]~ +** Processing line: ~ args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1]~ +** Processing line: ~ args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1]~ ** Processing line: ~ end~ ** Processing line: ~~ ** Processing line: ~~ @@ -120891,6 +123366,41 @@ Follows is a source code listing for all files that have been open sourced. This - PRE end detected. ** Processing line: ~~ ** Processing line: ~~ +** Processing line: ~* 12 C Extensions - Intermediate - main.rb~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~12 C Extensions - Intermediate - main.rb~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ # ./samples/12_c_extensions/02_intermediate/app/main.rb~ +** Processing line: ~ $gtk.ffi_misc.gtk_dlopen("ext")~ +** Processing line: ~ include FFI::RE~ +** Processing line: ~~ +** Processing line: ~ def split_words(input)~ +** Processing line: ~ words = []~ +** Processing line: ~ last = IntPointer.new~ +** Processing line: ~ re = re_compile("\\w+")~ +** Processing line: ~ first = re_matchp(re, input, last)~ +** Processing line: ~ while first != -1~ +** Processing line: ~ words << input.slice(first, last.value)~ +** Processing line: ~ input = input.slice(last.value + first, input.length)~ +** Processing line: ~ first = re_matchp(re, input, last)~ +** Processing line: ~ end~ +** Processing line: ~ words~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def tick args~ +** Processing line: ~ args.outputs.labels << [640, 500, split_words("hello, dragonriders!").join(' '), 5, 1]~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ ** Processing line: ~* 3d - 3d Cube - main.rb~ - H1 detected. - Determining if line is a header. @@ -133282,6 +135792,11 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ # @return [Outputs]~ ** Processing line: ~ attr_accessor :outputs~ ** Processing line: ~~ +** Processing line: ~ # Contains the means to interact with the audio mixer.~ +** Processing line: ~ #~ +** Processing line: ~ # @return [Hash]~ +** Processing line: ~ attr_accessor :audio~ +** Processing line: ~~ ** Processing line: ~ # Contains display size information to assist in positioning things on the screen.~ ** Processing line: ~ #~ ** Processing line: ~ # @return [Grid]~ @@ -133315,6 +135830,7 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ def initialize runtime, recording~ ** Processing line: ~ @inputs = Inputs.new~ ** Processing line: ~ @outputs = Outputs.new args: self~ +** Processing line: ~ @audio = {}~ ** Processing line: ~ @passes = []~ ** Processing line: ~ @state = OpenEntity.new~ ** Processing line: ~ @state.tick_count = -1~ @@ -133571,7 +136087,7 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ @assertion_performed = true~ ** Processing line: ~ if actual != expected~ ** Processing line: ~ actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip~ -** Processing line: ~ message = "actual:\n#{actual_string} did not equal\nexpected:\n#{expected}.\n#{message}"~ +** Processing line: ~ message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}.\n#{message}"~ ** Processing line: ~ raise message~ ** Processing line: ~ end~ ** Processing line: ~ nil~ @@ -133793,12 +136309,12 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ end~ ** Processing line: ~~ ** Processing line: ~ def save_history~ -** Processing line: ~ $gtk.ffi_file.storefile(@history_fname, @command_history.reverse.join("\n"))~ +** Processing line: ~ $gtk.ffi_file.write_root @history_fname, (@command_history.reverse.join "\n")~ ** Processing line: ~ end~ ** Processing line: ~~ ** Processing line: ~ def load_history~ ** Processing line: ~ @command_history.clear~ -** Processing line: ~ str = $gtk.ffi_file.loadfile(@history_fname)~ +** Processing line: ~ str = $gtk.ffi_file.read @history_fname~ ** Processing line: ~ return if str.nil? # no history to load.~ ** Processing line: ~~ ** Processing line: ~ str.chomp!("\n") # Don't let endlines at the end cause extra blank line.~ @@ -134011,6 +136527,36 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ args.inputs.keyboard.key_down.any? console_toggle_keys~ ** Processing line: ~ end~ ** Processing line: ~~ +** Processing line: ~ def try_search_docs exception~ +** Processing line: ~ string_e = "#{exception}"~ +** Processing line: ~ @last_command_errored = true~ +** Processing line: ~~ +** Processing line: ~ if (string_e.include? "wrong number of arguments")~ +** Processing line: ~ method_name = ((string_e.split ":")[0].gsub "'", "")~ +** Processing line: ~ if !(method_name.include? " ")~ +** Processing line: ~ results = (Kernel.__docs_search_results__ method_name)~ +** Processing line: ~ if !results.include? "* DOCS: No results found."~ +** Processing line: ~ puts (results.join "\n")~ +** Processing line: ~ puts <<-S~ +** Processing line: ~ * INFO: #{results.length} matches(s) found in DOCS for ~#{method_name}~ (see above).~ +** Processing line: ~ You can search the documentation yourself using the following command in the Console:~ +** Processing line: ~ #+begin_src ruby~ +** Processing line: ~ docs_search \"#{method_name}\"~ +** Processing line: ~ #+end_src~ +** Processing line: ~ S~ +** Processing line: ~ log_once_info :exported_search_results, "The search results above has been seen in logs/puts.txt and docs/search_results.txt."~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => se~ +** Processing line: ~ puts <<-S~ +** Processing line: ~ * FATAL: ~GTK::Console#try_search_docs~~ +** Processing line: ~ There was an exception searching for docs (~GTK::Console#try_search_docs~). You might want to let DragonRuby know about this.~ +** Processing line: ~ ** INNER EXCEPTION~ +** Processing line: ~ #{se}~ +** Processing line: ~ S~ +** Processing line: ~ end~ +** Processing line: ~~ ** Processing line: ~ def eval_the_set_command~ ** Processing line: ~ cmd = current_input_str.strip~ ** Processing line: ~ if cmd.length != 0~ @@ -134037,18 +136583,8 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ end~ ** Processing line: ~ @last_command_errored = false~ ** Processing line: ~ rescue Exception => e~ -** Processing line: ~ string_e = "#{e}"~ -** Processing line: ~ puts "* EXCEPTION: #{e}"~ -** Processing line: ~ log "* EXCEPTION: #{e}"~ -** Processing line: ~ @last_command_errored = true~ -** Processing line: ~ if (string_e.include? "wrong number of arguments")~ -** Processing line: ~ method_name = (string_e.split ":")[0].gsub "'", ""~ -** Processing line: ~ results = (Kernel.docs_search method_name).strip~ -** Processing line: ~ if !results.include? "* DOCS: No results found."~ -** Processing line: ~ puts results~ -** Processing line: ~ log results~ -** Processing line: ~ end~ -** Processing line: ~ end~ +** Processing line: ~ try_search_docs e~ +** Processing line: ~ puts "* EXCEPTION: #{e}"~ ** Processing line: ~ end~ ** Processing line: ~ end~ ** Processing line: ~ end~ @@ -134698,6 +137234,7 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ if args.inputs.mouse.click~ ** Processing line: ~ clicked = @buttons.find { |b| args.inputs.mouse.inside_rect? b[:rect] }~ ** Processing line: ~ if clicked~ +** Processing line: ~ args.inputs.mouse.click = nil~ ** Processing line: ~ send clicked[:method]~ ** Processing line: ~ end~ ** Processing line: ~ end~ @@ -136869,6 +139406,10 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ point.inside_rect? rect~ ** Processing line: ~ end~ ** Processing line: ~~ +** Processing line: ~ def inside_circle? center, radius~ +** Processing line: ~ point.point_inside_circle? center, radius~ +** Processing line: ~ end~ +** Processing line: ~~ ** Processing line: ~ alias_method :position, :point~ ** Processing line: ~~ ** Processing line: ~ def clear~ @@ -137476,6 +140017,8 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~~ ** Processing line: ~ if definitions.include?(:ratio)~ ** Processing line: ~ result = rand * result~ +** Processing line: ~ elsif definitions.include?(:int)~ +** Processing line: ~ result = (rand result)~ ** Processing line: ~ end~ ** Processing line: ~~ ** Processing line: ~ result~ @@ -137975,6 +140518,245 @@ Follows is a source code listing for all files that have been open sourced. This - PRE end detected. ** Processing line: ~~ ** Processing line: ~~ +** Processing line: ~* runtime/draw.rb~ +- H1 detected. +- Determining if line is a header. +- Line contains ~* ~... gsub-ing empty string +- Formatting line: ~runtime/draw.rb~ +- Line's tilde count is: 0 +- Line contains link marker: false +** Processing line: ~~ +** Processing line: ~#+begin_src ruby~ +- PRE start detected. +** Processing line: ~ # ./dragon/runtime/draw.rb~ +** Processing line: ~ # Copyright 2019 DragonRuby LLC~ +** Processing line: ~ # MIT License~ +** Processing line: ~ # draw.rb has been released under MIT (*only this file*).~ +** Processing line: ~~ +** Processing line: ~ module GTK~ +** Processing line: ~ class Runtime~ +** Processing line: ~ module Draw~ +** Processing line: ~ def primitives pass~ +** Processing line: ~ if $top_level.respond_to? :primitives_override~ +** Processing line: ~ return $top_level.tick_render @args, pass~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # Don't change this draw order unless you understand~ +** Processing line: ~ # the implications.~ +** Processing line: ~~ +** Processing line: ~ # pass.solids.each { |s| draw_solid s }~ +** Processing line: ~ # while loops are faster than each with block~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.solids.length~ +** Processing line: ~ draw_solid (pass.solids.value idx) # accessing an array using .value instead of [] is faster~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_solids.each { |s| draw_solid s }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_solids.length~ +** Processing line: ~ draw_solid (pass.static_solids.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.sprites.each { |s| draw_sprite s }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.sprites.length~ +** Processing line: ~ draw_sprite (pass.sprites.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_sprites.each { |s| draw_sprite s }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_sprites.length~ +** Processing line: ~ draw_sprite (pass.static_sprites.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.primitives.each { |p| draw_primitive p }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.primitives.length~ +** Processing line: ~ draw_primitive (pass.primitives.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_primitives.each { |p| draw_primitive p }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_primitives.length~ +** Processing line: ~ draw_primitive (pass.static_primitives.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.labels.each { |l| draw_label l }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.labels.length~ +** Processing line: ~ draw_label (pass.labels.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_labels.each { |l| draw_label l }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_labels.length~ +** Processing line: ~ draw_label (pass.static_labels.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.lines.each { |l| draw_line l }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.lines.length~ +** Processing line: ~ draw_line (pass.lines.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_lines.each { |l| draw_line l }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_lines.length~ +** Processing line: ~ draw_line (pass.static_lines.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.borders.each { |b| draw_border b }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.borders.length~ +** Processing line: ~ draw_border (pass.borders.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_borders.each { |b| draw_border b }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_borders.length~ +** Processing line: ~ draw_border (pass.static_borders.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ if !$gtk.production~ +** Processing line: ~ # pass.debug.each { |r| draw_primitive r }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.debug.length~ +** Processing line: ~ draw_primitive (pass.debug.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_debug.each { |r| draw_primitive r }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_debug.length~ +** Processing line: ~ draw_primitive (pass.static_debug.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.reserved.each { |r| draw_primitive r }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.reserved.length~ +** Processing line: ~ draw_primitive (pass.reserved.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ # pass.static_reserved.each { |r| draw_primitive r }~ +** Processing line: ~ idx = 0~ +** Processing line: ~ while idx < pass.static_reserved.length~ +** Processing line: ~ draw_primitive (pass.static_reserved.value idx)~ +** Processing line: ~ idx += 1~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => e~ +** Processing line: ~ pause!~ +** Processing line: ~ pretty_print_exception_and_export! e~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def draw_solid s~ +** Processing line: ~ return unless s~ +** Processing line: ~ if s.respond_to? :draw_override~ +** Processing line: ~ s.draw_override @ffi_draw~ +** Processing line: ~ else~ +** Processing line: ~ @ffi_draw.draw_solid s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => e~ +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :solid~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def draw_sprite s~ +** Processing line: ~ return unless s~ +** Processing line: ~ if s.respond_to? :draw_override~ +** Processing line: ~ s.draw_override @ffi_draw~ +** Processing line: ~ else~ +** Processing line: ~ @ffi_draw.draw_sprite_3 s.x, s.y, s.w, s.h,~ +** Processing line: ~ s.path.s_or_default,~ +** Processing line: ~ s.angle,~ +** Processing line: ~ s.a, s.r, s.g, s.b,~ +** Processing line: ~ s.tile_x, s.tile_y, s.tile_w, s.tile_h,~ +** Processing line: ~ !!s.flip_horizontally, !!s.flip_vertically,~ +** Processing line: ~ s.angle_anchor_x, s.angle_anchor_y,~ +** Processing line: ~ s.source_x, s.source_y, s.source_w, s.source_h~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => e~ +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :sprite~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def draw_screenshot s~ +** Processing line: ~ return unless s~ +** Processing line: ~ if s.respond_to? :draw_override~ +** Processing line: ~ s.draw_override @ffi_draw~ +** Processing line: ~ else~ +** Processing line: ~ @ffi_draw.draw_screenshot s.path.s_or_default,~ +** Processing line: ~ s.x, s.y, s.w, s.h,~ +** Processing line: ~ s.angle,~ +** Processing line: ~ s.a, s.r, s.g, s.b,~ +** Processing line: ~ s.tile_x, s.tile_y, s.tile_w, s.tile_h,~ +** Processing line: ~ !!s.flip_horizontally, !!s.flip_vertically,~ +** Processing line: ~ s.angle_anchor_x, s.angle_anchor_y,~ +** Processing line: ~ s.source_x, s.source_y, s.source_w, s.source_h~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => e~ +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :screenshot~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def draw_label l~ +** Processing line: ~ return unless l~ +** Processing line: ~ if l.respond_to? :draw_override~ +** Processing line: ~ l.draw_override @ffi_draw~ +** Processing line: ~ else~ +** Processing line: ~ @ffi_draw.draw_label l.x, l.y, l.text.s_or_default,~ +** Processing line: ~ l.size_enum, l.alignment_enum,~ +** Processing line: ~ l.r, l.g, l.b, l.a,~ +** Processing line: ~ l.font.s_or_default(nil)~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => e~ +** Processing line: ~ raise_conversion_for_rendering_failed l, e, :label~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def draw_line l~ +** Processing line: ~ return unless l~ +** Processing line: ~ if l.respond_to? :draw_override~ +** Processing line: ~ l.draw_override @ffi_draw~ +** Processing line: ~ else~ +** Processing line: ~ @ffi_draw.draw_line l.x, l.y, l.x2, l.y2, l.r, l.g, l.b, l.a~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => e~ +** Processing line: ~ raise_conversion_for_rendering_failed l, e, :line~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def draw_border s~ +** Processing line: ~ return unless s~ +** Processing line: ~ if s.respond_to? :draw_override~ +** Processing line: ~ s.draw_override @ffi_draw~ +** Processing line: ~ else~ +** Processing line: ~ @ffi_draw.draw_border s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a~ +** Processing line: ~ end~ +** Processing line: ~ rescue Exception => e~ +** Processing line: ~ raise_conversion_for_rendering_failed s, e, :border~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def draw_screenshots~ +** Processing line: ~ @args.outputs.screenshots.each { |s| draw_screenshot s }~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~#+end_src~ +- PRE end detected. +** Processing line: ~~ +** Processing line: ~~ ** Processing line: ~* runtime/framerate_diagnostics.rb~ - H1 detected. - Determining if line is a header. @@ -138000,7 +140782,12 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ You can display these diagnostics using:~ ** Processing line: ~~ ** Processing line: ~ #+begin_src~ -** Processing line: ~ args.outputs.debug << args.gtk.framerate_diagnostics_primitives~ +** Processing line: ~ def tick args~ +** Processing line: ~ # ....~ +** Processing line: ~~ +** Processing line: ~ # IMPORTANT: Put this at the END of the ~tick~ method.~ +** Processing line: ~ args.outputs.debug << args.gtk.framerate_diagnostics_primitives~ +** Processing line: ~ end~ ** Processing line: ~ #+end_src~ ** Processing line: ~~ ** Processing line: ~ ** Draw Calls: ~<<~ Invocation Perf Counter~ @@ -138267,6 +141054,10 @@ Follows is a source code listing for all files that have been open sourced. This ** Processing line: ~ def rtrim!~ ** Processing line: ~ rstrip!~ ** Processing line: ~ end~ +** Processing line: ~~ +** Processing line: ~ def serialize~ +** Processing line: ~ self~ +** Processing line: ~ end~ ** Processing line: ~ end~ ** Processing line: ~~ ** Processing line: ~#+end_src~ diff --git a/dragon/args.rb b/dragon/args.rb index 38a28b7..ecb7040 100644 --- a/dragon/args.rb +++ b/dragon/args.rb @@ -21,6 +21,11 @@ module GTK # @return [Outputs] attr_accessor :outputs + # Contains the means to interact with the audio mixer. + # + # @return [Hash] + attr_accessor :audio + # Contains display size information to assist in positioning things on the screen. # # @return [Grid] @@ -54,6 +59,7 @@ module GTK def initialize runtime, recording @inputs = Inputs.new @outputs = Outputs.new args: self + @audio = {} @passes = [] @state = OpenEntity.new @state.tick_count = -1 diff --git a/dragon/assert.rb b/dragon/assert.rb index 0c26f29..67fc0dd 100644 --- a/dragon/assert.rb +++ b/dragon/assert.rb @@ -98,7 +98,7 @@ end @assertion_performed = true if actual != expected actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip - message = "actual:\n#{actual_string} did not equal\nexpected:\n#{expected}.\n#{message}" + message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}.\n#{message}" raise message end nil diff --git a/dragon/console.rb b/dragon/console.rb index 32deb15..90fd8a9 100644 --- a/dragon/console.rb +++ b/dragon/console.rb @@ -47,12 +47,12 @@ module GTK end def save_history - $gtk.ffi_file.storefile(@history_fname, @command_history.reverse.join("\n")) + $gtk.ffi_file.write_root @history_fname, (@command_history.reverse.join "\n") end def load_history @command_history.clear - str = $gtk.ffi_file.loadfile(@history_fname) + str = $gtk.ffi_file.read @history_fname return if str.nil? # no history to load. str.chomp!("\n") # Don't let endlines at the end cause extra blank line. @@ -265,6 +265,36 @@ S args.inputs.keyboard.key_down.any? console_toggle_keys end + def try_search_docs exception + string_e = "#{exception}" + @last_command_errored = true + + if (string_e.include? "wrong number of arguments") + method_name = ((string_e.split ":")[0].gsub "'", "") + if !(method_name.include? " ") + results = (Kernel.__docs_search_results__ method_name) + if !results.include? "* DOCS: No results found." + puts (results.join "\n") + puts <<-S +* INFO: #{results.length} matches(s) found in DOCS for ~#{method_name}~ (see above). +You can search the documentation yourself using the following command in the Console: +#+begin_src ruby + docs_search \"#{method_name}\" +#+end_src +S + log_once_info :exported_search_results, "The search results above has been seen in logs/puts.txt and docs/search_results.txt." + end + end + end + rescue Exception => se + puts <<-S +* FATAL: ~GTK::Console#try_search_docs~ +There was an exception searching for docs (~GTK::Console#try_search_docs~). You might want to let DragonRuby know about this. +** INNER EXCEPTION +#{se} +S + end + def eval_the_set_command cmd = current_input_str.strip if cmd.length != 0 @@ -291,18 +321,8 @@ S end @last_command_errored = false rescue Exception => e - string_e = "#{e}" - puts "* EXCEPTION: #{e}" - log "* EXCEPTION: #{e}" - @last_command_errored = true - if (string_e.include? "wrong number of arguments") - method_name = (string_e.split ":")[0].gsub "'", "" - results = (Kernel.docs_search method_name).strip - if !results.include? "* DOCS: No results found." - puts results - log results - end - end + try_search_docs e + puts "* EXCEPTION: #{e}" end end end diff --git a/dragon/console_menu.rb b/dragon/console_menu.rb index 7449f4a..d10ceeb 100644 --- a/dragon/console_menu.rb +++ b/dragon/console_menu.rb @@ -107,6 +107,7 @@ module GTK if args.inputs.mouse.click clicked = @buttons.find { |b| args.inputs.mouse.inside_rect? b[:rect] } if clicked + args.inputs.mouse.click = nil send clicked[:method] end end diff --git a/dragon/docs.rb b/dragon/docs.rb index 67a50e0..049ff81 100644 --- a/dragon/docs.rb +++ b/dragon/docs.rb @@ -151,17 +151,8 @@ S end - def docs_search words = nil, &block - words ||= "" - if words.strip.length != 0 - each_word = words.split(' ').find_all { |w| w.strip.length > 0 } - block = lambda do |entry| - each_word.any? { |w| entry.downcase.include? w.downcase } - end - end - - if !block - return <<-S + def __docs_search_help_text__ + <<-S * DOCS: How To Search The Docs To search docs you can use Kernel.docs_search (or just ~docs_search~) by providing it a search term. For example: @@ -178,30 +169,52 @@ You can do more advanced searches by providing a block: end #+end_src S + end + + def __docs_search_results__ words = nil, &block + words ||= "" + + if words.strip.length != 0 + each_word = words.split(' ').find_all { |w| w.strip.length > 3 } + block = lambda do |entry| + each_word.any? { |w| entry.downcase.include? w.downcase } + end end + return [__docs_search_help_text__] if !block + DocsOrganizer.sort_docs_classes! + this_block = block - final_string = "" + + search_results = [] + if self == Kernel $docs_classes.each do |k| DocsOrganizer.find_methods_with_docs(k).each do |m| s = k.send m - final_string += s + "\n" if block.call s + search_results << s if block.call s end end else DocsOrganizer.find_methods_with_docs(self).each do |m| s = send m - final_string += s + "\n" if block.call s + search_results << s if block.call s end end - if final_string.strip.length == 0 - final_string = "* DOCS: No results found." - end + search_results + end + + def docs_search words = nil, &block + results = __docs_search_results__ words, &block + + final_string = results.join "\n" + + final_string = "* DOCS: No results found." if final_string.strip.length == 0 $gtk.write_file_root "docs/search_results.txt", final_string + if !final_string.include? "* DOCS: No results found." log "* INFO: Search results have been written to docs/search_results.txt." end diff --git a/dragon/draw.rb b/dragon/draw.rb new file mode 100644 index 0000000..231ad02 --- /dev/null +++ b/dragon/draw.rb @@ -0,0 +1,223 @@ +# Copyright 2019 DragonRuby LLC +# MIT License +# draw.rb has been released under MIT (*only this file*). + +module GTK + class Runtime + module Draw + def primitives pass + if $top_level.respond_to? :primitives_override + return $top_level.tick_render @args, pass + end + + # Don't change this draw order unless you understand + # the implications. + + # pass.solids.each { |s| draw_solid s } + # while loops are faster than each with block + idx = 0 + while idx < pass.solids.length + draw_solid (pass.solids.value idx) # accessing an array using .value instead of [] is faster + idx += 1 + end + + # pass.static_solids.each { |s| draw_solid s } + idx = 0 + while idx < pass.static_solids.length + draw_solid (pass.static_solids.value idx) + idx += 1 + end + + # pass.sprites.each { |s| draw_sprite s } + idx = 0 + while idx < pass.sprites.length + draw_sprite (pass.sprites.value idx) + idx += 1 + end + + # pass.static_sprites.each { |s| draw_sprite s } + idx = 0 + while idx < pass.static_sprites.length + draw_sprite (pass.static_sprites.value idx) + idx += 1 + end + + # pass.primitives.each { |p| draw_primitive p } + idx = 0 + while idx < pass.primitives.length + draw_primitive (pass.primitives.value idx) + idx += 1 + end + + # pass.static_primitives.each { |p| draw_primitive p } + idx = 0 + while idx < pass.static_primitives.length + draw_primitive (pass.static_primitives.value idx) + idx += 1 + end + + # pass.labels.each { |l| draw_label l } + idx = 0 + while idx < pass.labels.length + draw_label (pass.labels.value idx) + idx += 1 + end + + # pass.static_labels.each { |l| draw_label l } + idx = 0 + while idx < pass.static_labels.length + draw_label (pass.static_labels.value idx) + idx += 1 + end + + # pass.lines.each { |l| draw_line l } + idx = 0 + while idx < pass.lines.length + draw_line (pass.lines.value idx) + idx += 1 + end + + # pass.static_lines.each { |l| draw_line l } + idx = 0 + while idx < pass.static_lines.length + draw_line (pass.static_lines.value idx) + idx += 1 + end + + # pass.borders.each { |b| draw_border b } + idx = 0 + while idx < pass.borders.length + draw_border (pass.borders.value idx) + idx += 1 + end + + # pass.static_borders.each { |b| draw_border b } + idx = 0 + while idx < pass.static_borders.length + draw_border (pass.static_borders.value idx) + idx += 1 + end + + if !$gtk.production + # pass.debug.each { |r| draw_primitive r } + idx = 0 + while idx < pass.debug.length + draw_primitive (pass.debug.value idx) + idx += 1 + end + + # pass.static_debug.each { |r| draw_primitive r } + idx = 0 + while idx < pass.static_debug.length + draw_primitive (pass.static_debug.value idx) + idx += 1 + end + end + + # pass.reserved.each { |r| draw_primitive r } + idx = 0 + while idx < pass.reserved.length + draw_primitive (pass.reserved.value idx) + idx += 1 + end + + # pass.static_reserved.each { |r| draw_primitive r } + idx = 0 + while idx < pass.static_reserved.length + draw_primitive (pass.static_reserved.value idx) + idx += 1 + end + rescue Exception => e + pause! + pretty_print_exception_and_export! e + end + + def draw_solid s + return unless s + if s.respond_to? :draw_override + s.draw_override @ffi_draw + else + @ffi_draw.draw_solid s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a + end + rescue Exception => e + raise_conversion_for_rendering_failed s, e, :solid + end + + def draw_sprite s + return unless s + if s.respond_to? :draw_override + s.draw_override @ffi_draw + else + @ffi_draw.draw_sprite_3 s.x, s.y, s.w, s.h, + s.path.s_or_default, + s.angle, + s.a, s.r, s.g, s.b, + s.tile_x, s.tile_y, s.tile_w, s.tile_h, + !!s.flip_horizontally, !!s.flip_vertically, + s.angle_anchor_x, s.angle_anchor_y, + s.source_x, s.source_y, s.source_w, s.source_h + end + rescue Exception => e + raise_conversion_for_rendering_failed s, e, :sprite + end + + def draw_screenshot s + return unless s + if s.respond_to? :draw_override + s.draw_override @ffi_draw + else + @ffi_draw.draw_screenshot s.path.s_or_default, + s.x, s.y, s.w, s.h, + s.angle, + s.a, s.r, s.g, s.b, + s.tile_x, s.tile_y, s.tile_w, s.tile_h, + !!s.flip_horizontally, !!s.flip_vertically, + s.angle_anchor_x, s.angle_anchor_y, + s.source_x, s.source_y, s.source_w, s.source_h + end + rescue Exception => e + raise_conversion_for_rendering_failed s, e, :screenshot + end + + def draw_label l + return unless l + if l.respond_to? :draw_override + l.draw_override @ffi_draw + else + @ffi_draw.draw_label l.x, l.y, l.text.s_or_default, + l.size_enum, l.alignment_enum, + l.r, l.g, l.b, l.a, + l.font.s_or_default(nil) + end + rescue Exception => e + raise_conversion_for_rendering_failed l, e, :label + end + + def draw_line l + return unless l + if l.respond_to? :draw_override + l.draw_override @ffi_draw + else + @ffi_draw.draw_line l.x, l.y, l.x2, l.y2, l.r, l.g, l.b, l.a + end + rescue Exception => e + raise_conversion_for_rendering_failed l, e, :line + end + + def draw_border s + return unless s + if s.respond_to? :draw_override + s.draw_override @ffi_draw + else + @ffi_draw.draw_border s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a + end + rescue Exception => e + raise_conversion_for_rendering_failed s, e, :border + end + + def draw_screenshots + @args.outputs.screenshots.each { |s| draw_screenshot s } + end + end + end +end diff --git a/dragon/framerate_diagnostics.rb b/dragon/framerate_diagnostics.rb index 6b952bd..7a04efc 100644 --- a/dragon/framerate_diagnostics.rb +++ b/dragon/framerate_diagnostics.rb @@ -12,7 +12,12 @@ module GTK You can display these diagnostics using: #+begin_src - args.outputs.debug << args.gtk.framerate_diagnostics_primitives + def tick args + # .... + + # IMPORTANT: Put this at the END of the ~tick~ method. + args.outputs.debug << args.gtk.framerate_diagnostics_primitives + end #+end_src ** Draw Calls: ~<<~ Invocation Perf Counter diff --git a/dragon/inputs.rb b/dragon/inputs.rb index a4ef40f..befb0e5 100644 --- a/dragon/inputs.rb +++ b/dragon/inputs.rb @@ -502,6 +502,10 @@ module GTK point.inside_rect? rect end + def inside_circle? center, radius + point.point_inside_circle? center, radius + end + alias_method :position, :point def clear diff --git a/dragon/kernel_docs.rb b/dragon/kernel_docs.rb index 95e35be..7ef9470 100644 --- a/dragon/kernel_docs.rb +++ b/dragon/kernel_docs.rb @@ -53,7 +53,7 @@ S final_string += k.docs_all end - final_string += "\n" + (($gtk.read_file_root "docs/source.txt") || "") + final_string += "\n" + (($gtk.read_file "docs/source.txt") || "") html_parse_result = (__docs_to_html__ final_string) diff --git a/dragon/numeric.rb b/dragon/numeric.rb index 6d8b84d..f090360 100644 --- a/dragon/numeric.rb +++ b/dragon/numeric.rb @@ -146,6 +146,8 @@ S if definitions.include?(:ratio) result = rand * result + elsif definitions.include?(:int) + result = (rand result) end result diff --git a/dragon/readme_docs.rb b/dragon/readme_docs.rb index f4d3696..98284d6 100644 --- a/dragon/readme_docs.rb +++ b/dragon/readme_docs.rb @@ -10,13 +10,15 @@ module GTK :docs_usage, :docs_hello_world, :docs_deployment, + :docs_deployment_mobile, :docs_dragonruby_philosophy, + :docs_faq, :docs_ticks_and_frames, :docs_sprites, :docs_labels, :docs_sounds, :docs_game_state, - :docs_faq + :docs_troubleshooting_performance ] end @@ -452,6 +454,35 @@ update the downloads for you. S end + def docs_deployment_mobile +<<-S + +* Deploying To Mobile Devices + +If you have a Pro subscription, you also have the capability to deploy +to mobile devices. + +To deploy to iOS, you need to have a Mac running MacOS Catalina, an +iOS device, and an active/paid Developer Account with Apple. From the +Console type: ~$wizards.ios.start~ and you will be guided through the +deployment process. + +To deploy to Android, you need to have an Android emulator/device, and +a environment that is able to run Android SDK. ~dragonruby-publish~ +will create an APK for you. From there, you can sign the APK and +install it to your device. The signing and installation procedure +varies from OS to OS. Here's an example of what the command might look +like: + +#+begin_src + > adb logcat -e mygame # you'll want to run this in a separate terminal + > keytool -genkey -v -keystore mygame.keystore -alias mygame -keyalg RSA -keysize 2048 -validity 10000 + > apksigner sign --ks mygame.keystore mygame-android.apk + > adb install mygame-android.apk +#+end_src +S + end + def docs_dragonruby_philosophy <<-S * DragonRuby's Philosophy @@ -559,7 +590,7 @@ S def docs_ticks_and_frames <<-S -* How To Determine What Frame You Are On +* CHEATSHEET: How To Determine What Frame You Are On There is a property on ~state~ called ~tick_count~ that is incremented by DragonRuby every time the ~tick~ method is called. The following @@ -571,7 +602,7 @@ code renders a label that displays the current ~tick_count~. end #+end_src -* How To Get Current Framerate +* CHEATSHEET: How To Get Current Framerate Current framerate is a top level property on the Game Toolkit Runtime and is accessible via ~args.gtk.current_framerate~. @@ -586,7 +617,7 @@ S def docs_sprites <<-S -* How To Render A Sprite Using An Array +* CHEATSHEET: How To Render A Sprite Using An Array All file paths should use the forward slash ~/~ *not* backslash ~\~. Game Toolkit includes a number of sprites in the ~sprites~ @@ -609,7 +640,7 @@ The following code renders a sprite with a ~width~ and ~height~ of end #+end_src -* More Sprite Properties As An Array +* CHEATSHEET: More Sprite Properties As An Array Here are all the properties you can set on a sprite. @@ -630,7 +661,7 @@ Here are all the properties you can set on a sprite. end #+end_src -* Different Sprite Representations +* CHEATSHEET: Different Sprite Representations Using ordinal positioning can get a little unruly given so many properties you have control over. @@ -700,7 +731,7 @@ S def docs_labels <<-S -* How To Render A Label +* CHEATSHEET: How To Render A Label ~args.outputs.labels~ is used to render labels. @@ -716,7 +747,7 @@ Here is the minimum code: end #+end_src -* A Colored Label +* CHEATSHEET: A Colored Label #+begin_src def tick args @@ -726,7 +757,7 @@ Here is the minimum code: end #+end_src -* Extended Label Properties +* CHEATSHEET: Extended Label Properties #+begin_src def tick args @@ -754,7 +785,7 @@ label's size. An ~ALIGNMENT_ENUM~ of ~0~ represents "left aligned". ~1~ represents "center aligned". ~2~ represents "right aligned". -* Rendering A Label As A ~Hash~ +* CHEATSHEET: Rendering A Label As A ~Hash~ You can add additional metadata about your game within a label, which requires you to use a `Hash` instead. @@ -782,7 +813,7 @@ You can add additional metadata about your game within a label, which requires y end #+end_src -* Getting The Size Of A Piece Of Text +* CHEATSHEET: Getting The Size Of A Piece Of Text You can get the render size of any string using ~args.gtk.calcstringbox~. @@ -807,7 +838,7 @@ S def docs_sounds <<-S -* How To Play A Sound +* CHEATSHEET: How To Play A Sound Sounds that end ~.wav~ will play once: @@ -846,7 +877,7 @@ S def docs_game_state <<-S -* Using ~args.state~ To Store Your Game State +* CHEATSHEET: Using ~args.state~ To Store Your Game State ~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 @@ -888,7 +919,7 @@ S def animate_a_sprite <<-S -* How To Animate A Sprite Using Separate PNGs +* CHEATSHEET: How To Animate A Sprite Using Separate PNGs DragonRuby has a property on ~Numeric~ called ~frame_index~ that can be used to determine what frame of an animation to show. Here is an @@ -920,6 +951,30 @@ example of how to cycle through 6 sprites every 4 frames. S end + def docs_troubleshooting_performance +<<-S +* CHEATSHEET: Troubleshoot Performance + +1. If you're using ~Array~s for your primitives (~args.outputs.sprites << []~), use ~Hash~ instead (~args.outputs.sprites << { x: ... }~). +2. If you're using ~Entity~ for your primitives (~args.outputs.sprites << args.state.new_entity~), use ~StrictEntity~ instead (~args.outputs.sprites << args.state.new_entity_strict~). +3. Use ~.each~ instead of ~.map~ if you don't care about the return value. +4. When concatenating primitives to outputs, do them in bulk. Instead of: +#+begin_src ruby + args.state.bullets.each do |bullet| + args.outputs.sprites << bullet.sprite + end +#+end_src +do +#+begin_src + args.outputs.sprites << args.state.bullets.map do |b| + b.sprite + end +#+end_src +5. Use ~args.outputs.static_~ variant for things that don't change often (take a look at the Basic Gorillas sample app and Dueling Starships sample app to see how ~static_~ is leveraged. +6. Consider using a ~render_target~ if you're doing some form of a camera that moves a lot of primitives (take a look at the Render Target sample apps for more info). +S + end + def docs_faq <<-S * Frequently Asked Questions, Comments, and Concerns diff --git a/dragon/string.rb b/dragon/string.rb index 5fa273c..121c3f3 100644 --- a/dragon/string.rb +++ b/dragon/string.rb @@ -98,4 +98,8 @@ S def rtrim! rstrip! end + + def serialize + self + end end diff --git a/dragon/trace.rb b/dragon/trace.rb index 3d92ba2..db5abda 100644 --- a/dragon/trace.rb +++ b/dragon/trace.rb @@ -3,6 +3,9 @@ # MIT License # trace.rb has been released under MIT (*only this file*). +# Contributors outside of DragonRuby who also hold Copyright: +# - Dan Healy: https://github.com/danhealy + module GTK module Trace IGNORED_METHODS = [ diff --git a/samples/01_rendering_basics/06_audio_mixer/app/main.rb b/samples/01_rendering_basics/06_audio_mixer/app/main.rb new file mode 100644 index 0000000..a09c7f1 --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/app/main.rb @@ -0,0 +1,158 @@ +$gtk.reset + +$boxsize = 30 + +def render_sources args + mouse_in_panel = (args.state.selected != 0) && args.inputs.mouse.position.inside_rect?([900, 450, 340, 250]) + mouse_new_down = (args.state.mouse_held == 1) + + if (mouse_new_down && !mouse_in_panel) + args.state.selected = 0 # will reset below if we hit something. + end + + args.audio.keys.each { |k| + s = args.audio[k] + + if (mouse_new_down) && !mouse_in_panel && args.inputs.mouse.position.inside_rect?([s[:screenx], s[:screeny], $boxsize, $boxsize]) + args.state.selected = k + args.state.dragging_source = true + end + + isselected = (k == args.state.selected) + + if isselected && args.state.dragging_source + # you can hang anything on the audio hashes you want, so we store the + # actual screen position so it doesn't scale weirdly vs your mouse. + s[:screenx] = args.inputs.mouse.x - ($boxsize / 2) + s[:screeny] = args.inputs.mouse.y - ($boxsize / 2) + + s[:screeny] = 50 if s[:screeny] < 50 + s[:screeny] = (719 - $boxsize) if s[:screeny] > (719 - $boxsize) + s[:screenx] = 0 if s[:screenx] < 0 + s[:screenx] = (1279 - $boxsize) if s[:screenx] > (1279 - $boxsize) + + s[:x] = ((s[:screenx] / 1279.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range + s[:y] = ((s[:screeny] / 719.0) * 2.0) - 1.0 # scale to -1.0 - 1.0 range + end + + color = isselected ? [ 0, 255, 0, 255 ] : [ 0, 0, 255, 255 ] + args.outputs.primitives << [s[:screenx], s[:screeny], $boxsize, $boxsize, *color].solid + } +end + +def render_panel args + s = args.audio[args.state.selected] + return if s.nil? + mouse_down = (args.state.mouse_held > 0) + + args.outputs.primitives << [900, 450, 340, 250, 127, 127, 200, 255].solid + args.outputs.primitives << [1075, 690, "Source ##{args.state.selected}", 3, 1, 255, 255, 255].label + args.outputs.primitives << [910, 660, 1230, 660, 255, 255, 255].line + args.outputs.primitives << [910, 650, "screen: (#{s[:screenx].to_i}, #{s[:screeny].to_i})", 0, 0, 255, 255, 255].label + args.outputs.primitives << [910, 625, "position: (#{s[:x].round(5).to_s[0..6]}, #{s[:y].round(5).to_s[0..6]})", 0, 0, 255, 255, 255].label + + slider = [1022, 586, 200, 7] + if mouse_down && args.inputs.mouse.position.inside_rect?(slider) + s[:pitch] = ((args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0)) * 2.0 + end + slidercolor = (s[:pitch] / 2.0) * 255 + args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid + args.outputs.primitives << [910, 600, "pitch: #{s[:pitch].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label + + slider = [1022, 561, 200, 7] + if mouse_down && args.inputs.mouse.position.inside_rect?(slider) + s[:gain] = (args.inputs.mouse.x - slider[0]).to_f / (slider[2]-1.0) + end + slidercolor = s[:gain] * 255 + args.outputs.primitives << [*slider, slidercolor, slidercolor, slidercolor, 255].solid + args.outputs.primitives << [910, 575, "gain: #{s[:gain].round(3).to_s[0..2]}", 0, 0, 255, 255, 255].label + + checkbox = [1022, 533, 10, 12] + if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox) + s[:looping] = !s[:looping] + end + checkboxcolor = s[:looping] ? 255 : 0 + args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid + args.outputs.primitives << [910, 550, "looping:", 0, 0, 255, 255, 255].label + + checkbox = [1022, 508, 10, 12] + if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(checkbox) + s[:paused] = !s[:paused] + end + checkboxcolor = s[:paused] ? 255 : 0 + args.outputs.primitives << [*checkbox, checkboxcolor, checkboxcolor, checkboxcolor, 255].solid + args.outputs.primitives << [910, 525, "paused:", 0, 0, 255, 255, 255].label + + button = [910, 460, 320, 20] + if (args.state.mouse_held == 1) && args.inputs.mouse.position.inside_rect?(button) + args.audio.delete(args.state.selected) + args.state.selected = 0 + end + args.outputs.primitives << [*button, 255, 0, 0, 255].solid + args.outputs.primitives << [button[0] + (button[2] / 2), button[1]+20, "DELETE SOURCE", 0, 1, 255, 255, 0].label +end + +def spawn_new_sound args, num + # Spawn randomly in an area that won't be covered by UI. + screenx = (rand * 600.0) + 200.0 + screeny = (rand * 400.0) + 100.0 + + args.state.next_sound_index += 1 + + # you can hang anything on the audio hashes you want, so we store the + # actual screen position in here for convenience. + args.audio[args.state.next_sound_index] = { + filename: "sounds/#{num}.#{(num == 6) ? 'ogg' : 'wav'}", + screenx: screenx, + screeny: screeny, + x: ((screenx / 1279.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range + y: ((screeny / 719.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range + z: 0.0, + gain: 1.0, + pitch: 1.0, + looping: true, + paused: false + } + + args.state.selected = args.state.next_sound_index +end + +def render_launcher args + total = 6 + x = (1280 - (total * $boxsize * 3)) / 2 + y = 10 + args.outputs.primitives << [0, 0, 1280, ((y*2) + $boxsize), 127, 127, 127, 255].solid + for i in 1..total + args.outputs.primitives << [x, y, $boxsize, $boxsize, 255, 255, 255, 255].solid + args.outputs.primitives << [x+8, y+28, i.to_s, 3, 0, 0, 0, 255, 255].label + if args.inputs.mouse.click && args.inputs.mouse.click.point.inside_rect?([x, y, $boxsize, $boxsize]) + spawn_new_sound args, i + end + x = x + ($boxsize * 3) + end +end + +def render_ui args + render_launcher args + render_panel args +end + +def tick args + args.state.mouse_held ||= 0 + args.state.dragging_source ||= false + args.state.selected ||= 0 + args.state.next_sound_index ||= 0 + + if args.inputs.mouse.up + args.state.mouse_held = 0 + args.state.dragging_source = false + elsif args.inputs.mouse.down || (args.state.mouse_held > 0) + args.state.mouse_held += 1 + else + end + + args.outputs.background_color = [ 0, 0, 0, 255 ] + render_sources args + render_ui args +end + diff --git a/samples/01_rendering_basics/06_audio_mixer/license-for-audio.txt b/samples/01_rendering_basics/06_audio_mixer/license-for-audio.txt new file mode 100644 index 0000000..f05c25c --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/license-for-audio.txt @@ -0,0 +1,48 @@ +The audio files in this sample are used under various Creative Common licenses: + + +sounds/1.wav: + + Party Pack, Horn Coil 01, Long, 01.wav by InspectorJ (www.jshaw.co.uk) of Freesound.org + (We converted this to mono output.) + Original: https://freesound.org/s/484267/ + License: https://creativecommons.org/licenses/by/3.0/ + + +sounds/2.wav: + + SPLASH.wav by petenice + Original: https://freesound.org/s/9508/ + License: https://creativecommons.org/publicdomain/zero/1.0/ + + +sounds/3.wav: + + buzz roll.wav by bigjoedrummer + (We converted this to mono output.) + Original: https://freesound.org/s/77305/ + License: https://creativecommons.org/publicdomain/zero/1.0/ + + +sounds/4.wav: + + sword04.wav by Erdie + Original: https://freesound.org/s/27858/ + License: https://creativecommons.org/licenses/by/3.0/ + + +sounds/5.wav: + + Game Over Arcade.wav by myfox14 + (We converted this to mono output.) + Original: https://freesound.org/s/382310/ + License: https://creativecommons.org/publicdomain/zero/1.0/ + + +sounds/6.ogg: + + Arcade Music Loop.wav by joshuaempyre ( https://www.empyreanma.com/welcome ) + (We converted this to Ogg Vorbis format, and mono output.) + Original: https://freesound.org/s/251461/ + License: https://creativecommons.org/licenses/by/3.0/ + diff --git a/samples/01_rendering_basics/06_audio_mixer/license-for-sample.txt b/samples/01_rendering_basics/06_audio_mixer/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +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/01_rendering_basics/06_audio_mixer/sounds/1.wav b/samples/01_rendering_basics/06_audio_mixer/sounds/1.wav Binary files differnew file mode 100644 index 0000000..8aa1be3 --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/sounds/1.wav diff --git a/samples/01_rendering_basics/06_audio_mixer/sounds/2.wav b/samples/01_rendering_basics/06_audio_mixer/sounds/2.wav Binary files differnew file mode 100644 index 0000000..52a09c2 --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/sounds/2.wav diff --git a/samples/01_rendering_basics/06_audio_mixer/sounds/3.wav b/samples/01_rendering_basics/06_audio_mixer/sounds/3.wav Binary files differnew file mode 100644 index 0000000..c7bde27 --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/sounds/3.wav diff --git a/samples/01_rendering_basics/06_audio_mixer/sounds/4.wav b/samples/01_rendering_basics/06_audio_mixer/sounds/4.wav Binary files differnew file mode 100644 index 0000000..193cf54 --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/sounds/4.wav diff --git a/samples/01_rendering_basics/06_audio_mixer/sounds/5.wav b/samples/01_rendering_basics/06_audio_mixer/sounds/5.wav Binary files differnew file mode 100644 index 0000000..112f3cc --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/sounds/5.wav diff --git a/samples/01_rendering_basics/06_audio_mixer/sounds/6.ogg b/samples/01_rendering_basics/06_audio_mixer/sounds/6.ogg Binary files differnew file mode 100644 index 0000000..a952191 --- /dev/null +++ b/samples/01_rendering_basics/06_audio_mixer/sounds/6.ogg diff --git a/samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb b/samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb new file mode 100644 index 0000000..2caec43 --- /dev/null +++ b/samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/app/main.rb @@ -0,0 +1,95 @@ +# This sample is meant to show you how to do that dripping transition thing +# at the start of the original Doom. Most of this file is here to animate +# a scene to wipe away; the actual wipe effect is in the last 20 lines or +# so. + +$gtk.reset # reset all game state if reloaded. + +def circle_of_blocks pass, xoffset, yoffset, angleoffset, blocksize, distance + numblocks = 10 + + for i in 1..numblocks do + angle = ((360 / numblocks) * i) + angleoffset + radians = angle * (Math::PI / 180) + x = (xoffset + (distance * Math.cos(radians))).round + y = (yoffset + (distance * Math.sin(radians))).round + pass.solids << [ x, y, blocksize, blocksize, 255, 255, 0 ] + end +end + +def draw_scene args, pass + pass.solids << [0, 360, 1280, 360, 0, 0, 200] + pass.solids << [0, 0, 1280, 360, 0, 127, 0] + + blocksize = 100 + angleoffset = args.state.tick_count * 2.5 + centerx = (1280 - blocksize) / 2 + centery = (720 - blocksize) / 2 + + circle_of_blocks pass, centerx, centery, angleoffset, blocksize * 2, 500 + circle_of_blocks pass, centerx, centery, angleoffset, blocksize, 325 + circle_of_blocks pass, centerx, centery, angleoffset, blocksize / 2, 200 + circle_of_blocks pass, centerx, centery, angleoffset, blocksize / 4, 100 +end + +def tick args + segments = 160 + + # On the first tick, initialize some stuff. + if !args.state.yoffsets + args.state.baseyoff = 0 + args.state.yoffsets = [] + for i in 0..segments do + args.state.yoffsets << rand * 100 + end + end + + # Just draw some random stuff for a few seconds. + args.state.static_debounce ||= 60 * 2.5 + if args.state.static_debounce > 0 + last_frame = args.state.static_debounce == 1 + target = last_frame ? args.render_target(:last_frame) : args.outputs + draw_scene args, target + args.state.static_debounce -= 1 + return unless last_frame + end + + # build up the wipe... + + # this is the thing we're wiping to. + args.outputs.sprites << [ 0, 0, 1280, 720, 'dragonruby.png' ] + + return if (args.state.baseyoff > (1280 + 100)) # stop when done sliding + + segmentw = 1280 / segments + + x = 0 + for i in 0..segments do + yoffset = 0 + if args.state.yoffsets[i] < args.state.baseyoff + yoffset = args.state.baseyoff - args.state.yoffsets[i] + end + + # (720 - yoffset) flips the coordinate system, (- 720) adjusts for the height of the segment. + args.outputs.sprites << [ x, (720 - yoffset) - 720, segmentw, 720, 'last_frame', 0, 255, 255, 255, 255, x, 0, segmentw, 720 ] + x += segmentw + end + + args.state.baseyoff += 4 + + tick_instructions args, "Sample app shows an advanced usage of render_target." +end + +def tick_instructions args, text, y = 715 + return if args.state.key_event_occurred + if args.inputs.mouse.click || + args.inputs.keyboard.directional_vector || + args.inputs.keyboard.key_down.enter || + args.inputs.keyboard.key_down.escape + args.state.key_event_occurred = true + end + + args.outputs.debug << [0, y - 50, 1280, 60].solid + args.outputs.debug << [640, y, text, 1, 1, 255, 255, 255].label + args.outputs.debug << [640, y - 25, "(click to dismiss instructions)" , -2, 1, 255, 255, 255].label +end diff --git a/samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/license-for-sample.txt b/samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/07_advanced_rendering/02_render_targets_with_tile_manipulation/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +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/09_performance/01_sprites_as_hash/app/main.rb b/samples/09_performance/01_sprites_as_hash/app/main.rb index 2bb3f46..6bbb295 100644 --- a/samples/09_performance/01_sprites_as_hash/app/main.rb +++ b/samples/09_performance/01_sprites_as_hash/app/main.rb @@ -1,3 +1,4 @@ + # Sprites represented as Hashes using the queue ~args.outputs.sprites~ # code up, but are the "slowest" to render. # The reason for this is the access of the key in the Hash and also diff --git a/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb b/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb index 22fa862..e67b1ee 100644 --- a/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb +++ b/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb @@ -2,12 +2,12 @@ def test_serialize args, assert GTK::Entity.__reset_id__! args.state.player_one = "test" result = args.gtk.serialize_state args.state - assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}" + assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}" GTK::Entity.__reset_id__! args.gtk.write_file 'state.txt', '' result = args.gtk.serialize_state 'state.txt', args.state - assert.equal! result, "{:entity_id=>3, :tick_count=>-1, :player_one=>\"test\"}" + assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}" end def test_deserialize args, assert @@ -38,8 +38,9 @@ def test_strict_entity_serialization args, assert args.state.player_two = args.state.new_entity_strict(:player_strict, name: "Ken") serialized_state = args.gtk.serialize_state args.state - assert.equal! serialized_state, '{:entity_id=>1, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken"}}' + assert.equal! serialized_state, '{:entity_id=>1, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken"}}' + GTK::Entity.__reset_id__! deserialize_state = args.gtk.deserialize_state serialized_state assert.equal! args.state.player_one.name, deserialize_state.player_one.name @@ -55,8 +56,9 @@ def test_strict_entity_serialization_with_nil args, assert args.state.player_two = args.state.new_entity_strict(:player_strict, name: "Ken", blood_type: nil) serialized_state = args.gtk.serialize_state args.state - assert.equal! serialized_state, '{:entity_id=>3, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>3, :entity_name=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :name=>"Ken", :blood_type=>nil}}' + assert.equal! serialized_state, '{:entity_id=>9, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>2, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken", :blood_type=>nil}}' + GTK::Entity.__reset_id__! deserialized_state = args.gtk.deserialize_state serialized_state assert.equal! args.state.player_one.name, deserialized_state.player_one.name @@ -77,10 +79,39 @@ def test_multiple_strict_entities args, assert args.state.enemy = args.state.new_entity_strict(:enemy, name: "Bison", other_property: 'extra mean') serialized_state = args.gtk.serialize_state args.state + + GTK::Entity.__reset_id__! deserialized_state = args.gtk.deserialize_state serialized_state assert.equal! deserialized_state.player.name, "Ryu" assert.equal! deserialized_state.enemy.other_property, "extra mean" end +def test_by_reference_state args, assert + GTK::Entity.__reset_id__! + args.state.a = { name: "Jane Doe" } + args.state.b = args.state.a + assert.equal! args.state.a.object_id, args.state.b.object_id + serialized_state = args.gtk.serialize_state args.state + + GTK::Entity.__reset_id__! + deserialized_state = args.gtk.deserialize_state serialized_state + assert.equal! deserialized_state.a.object_id, deserialized_state.b.object_id +end + +def test_by_reference_state_strict_entities args, assert + GTK::Entity.__reset_id__! + args.state.a = { name: "Jane Doe" } + args.state.strict_entity = args.state.new_entity_strict(:couple) do |e| + e.one = args.state.new_entity_strict(:person, name: "Jane") + e.two = e.one + end + assert.equal! args.state.strict_entity.one, args.state.strict_entity.two + serialized_state = args.gtk.serialize_state args.state + + GTK::Entity.__reset_id__! + deserialized_state = args.gtk.deserialize_state serialized_state + assert.equal! deserialized_state.strict_entity.one, deserialized_state.strict_entity.two +end + $tests.start diff --git a/samples/12_c_extensions/01_basics/README.md b/samples/12_c_extensions/01_basics/README.md new file mode 100644 index 0000000..6993173 --- /dev/null +++ b/samples/12_c_extensions/01_basics/README.md @@ -0,0 +1,227 @@ +# C Extensions introduction + +This sample app serves as an introduction on how to write C extensions for the +DragonRuby Game Toolkit. You'll need a Pro License +which can be purchased at http://dragonruby.org. The sample app is provided in +the Standard license for those that are curious as to what implementing C Extensions +looks like. + +## Requirements + +In order to use C extensions you need a C compiler. We strongly recommend you +using [Clang](https://clang.llvm.org). + +### Windows + +To get Clang for Windows you can use the [official build](https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe). + +During installation, make sure to choose "Add LLVM to the system PATH for all +users", as on the screenshot  + +On Windows, you also need to install MinGW-w64 toolchain from [here](http://mingw-w64.org/doku.php/download/mingw-builds) +Set the installation directory to "C:\mingw-w64":  and on the next step choose the right version and the +other setting as on the screenshot: . + + +### Linux + +On Debian-based systems you can install Clang via `apt`. It is recommended you +install `clang-10`, but any other 'reasonably' fresh version should work just +fine: + +``` +> apt-get install clang-10 +> clang-10 --version +Apple clang version 10.0.0-4ubuntu1-18.04.2 +Target: x86_64-pc-linux-gnu +Thread model: posix +InstalledDir: /usr/bin +``` + +Raspberry Pi instructions are mostly the same as x86-64 Linux. Out of the box, +Clang can cross-compile for the Pi on any platform, but you'll need to set up +a proper sysroot, which can be complicated, and beyond the scope of this +document. The bold--and patient--can always compile on the Pi device directly, +too. + + +### macOS + +To get Clang on macOS it is recommended to install [Xcode](https://developer.apple.com/xcode/). +Once you've done that you should be able to use Clang from a terminal: + +``` +> clang --version +Apple clang version 11.0.3 (clang-1103.0.32.29) +Target: x86_64-apple-darwin19.5.0 +Thread model: posix +InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin +``` + +### Android + +For Android, you can [install the Android NDK](https://developer.android.com/ndk/) +on Windows, Linux, or macOS. This lets you compile C code for the platform. You +won't be using Java here. + +DragonRuby ships with support for Android on arm32, arm64, x86, and x86-64. This might +be overkill, but it allows for maximum hardware compatibility. You should plan to +compile for all four architectures too, but of course this is optional. + + +## Hello World + +Let's craft the simplest possible C extension: a simple function that +calculates square of an integer. Put the following code into a file named +`ext.c` under `mygame` directory: + +``` +int square(int x) { + return x * x; +} +``` + +You need some glue code to connect this function to the GameToolKit. Good news +are that you don't need to craft this code yourself: it can be generated for +you by `dragonruby-bind` (or `dragonruby-bind.exe` if you are on Windows). + +Run the following command from Linux/macOS terminal: + +``` +> ./dragonruby-bind --output=mygame/ext-bind.c mygame/ext.c +``` + +Or from Windows Powershell: + +``` +> .\dragobruby-bind.exe --output=mygame\ext-bind.c mygame\ext.c +``` + +With the `ext-bind.c` in place you can compile your C extension into a dynamic +or shared library. Here is how you do it: + +On Windows: + +``` +clang -shared \ + --sysroot=C:\mingw-w64\mingw64^ + --target=x86_64-w64-mingw32 -fuse-ld=lld^ + -isystem include -I . -fPIC^ + -o mygame\native\windows-amd64\ext.dll mygame\ext-bind.c +``` + +On Linux: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-amd64/ext.so mygame/ext-bind.c +``` + +On Raspberry Pi, on the device: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-raspberrypi/ext.so mygame/ext-bind.c +``` + +On Raspberry Pi, with a cross-compiler: + +``` +> clang -shared --sysroot=/where/i/installed/a/sysroot \ + --target=arm-linux-gnueabihf -fuse-ld=lld \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-raspberrypi/ext.so mygame/ext-bind.c +``` + +On macOS: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/macos/ext.dylib mygame/ext-bind.c +``` + +On Android: + +(This is what it looks like on Linux, change the appropriate parts for Windows, etc) + +``` +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=armv7-none-linux-androideabi26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a \ + -mthumb -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-arm32/ext.so mygame/ext-bind.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=aarch64-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-arm64/ext.so mygame/ext-bind.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=i686-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-x86/ext.so mygame/ext-bind.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=x86_64-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-amd64/ext.so mygame/ext-bind.c +``` + + + +Now, include the following snippet into the very beginning of `mygame/app/main.rb`: + +``` +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::CExt +puts square(11) +``` + +Now, simply run `dragonruby` (or `dragonruby.exe`) and you should see `121` on +the console. + +Let's do a breakdown of each line! + +1. `$gtk.ffi_misc.gtk_dlopen("ext")` - DragonRuby exposes a special function + called `gtk_dlopen`, you can use it to load a dynamic that holds + the C extension code. It looks for the shared library in + "mygame/native/$PLATFORM/ext.$PLATFORM_DLL_EXTENSION" +2. `include FFI::CExt` - by default, DragonRuby puts all the code available in + the C extension under `FFI::CExt` module. This line serves as a shortcut so + that you don't need to write `FFI::CExt::square` any time you want to call + a function. +3. `puts square(11)` - this line simply prints the value returned from C code. + +Now, you can call the `square` function at any place in the code. Let's see +what's the square value of every pixel on the screen. Here is the full program: + +``` +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::CExt + +def tick args + args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1] + args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1] + args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1] + args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1] +end +``` + +When you run the game now, you will see something like this: + + diff --git a/samples/12_c_extensions/01_basics/app/main.rb b/samples/12_c_extensions/01_basics/app/main.rb index 537b9b1..2d61fa3 100644 --- a/samples/12_c_extensions/01_basics/app/main.rb +++ b/samples/12_c_extensions/01_basics/app/main.rb @@ -1,7 +1,10 @@ -$gtk.ffi_misc.gtk_dlopen("./samples/12_c_extensions/01_basics/build.dir/ext.lib") +$gtk.ffi_misc.gtk_dlopen("ext") include FFI::CExt def tick args - args.outputs.labels << [460, 600, "square(42) = #{square(42)}"] + args.outputs.labels << [640, 500, "mouse.x = #{args.mouse.x.to_i}", 5, 1] + args.outputs.labels << [640, 460, "square(mouse.x) = #{square(args.mouse.x.to_i)}", 5, 1] + args.outputs.labels << [640, 420, "mouse.y = #{args.mouse.y.to_i}", 5, 1] + args.outputs.labels << [640, 380, "square(mouse.y) = #{square(args.mouse.y.to_i)}", 5, 1] end diff --git a/samples/12_c_extensions/01_basics/c-basics-demo.png b/samples/12_c_extensions/01_basics/c-basics-demo.png Binary files differnew file mode 100644 index 0000000..715c0d9 --- /dev/null +++ b/samples/12_c_extensions/01_basics/c-basics-demo.png diff --git a/samples/12_c_extensions/01_basics/llvm-path.png b/samples/12_c_extensions/01_basics/llvm-path.png Binary files differnew file mode 100644 index 0000000..8bab1c9 --- /dev/null +++ b/samples/12_c_extensions/01_basics/llvm-path.png diff --git a/samples/12_c_extensions/01_basics/mingw-path.png b/samples/12_c_extensions/01_basics/mingw-path.png Binary files differnew file mode 100644 index 0000000..6e08419 --- /dev/null +++ b/samples/12_c_extensions/01_basics/mingw-path.png diff --git a/samples/12_c_extensions/01_basics/mingw-version.png b/samples/12_c_extensions/01_basics/mingw-version.png Binary files differnew file mode 100644 index 0000000..0fe6cef --- /dev/null +++ b/samples/12_c_extensions/01_basics/mingw-version.png diff --git a/samples/12_c_extensions/01_basics/native/ext-bindings.c b/samples/12_c_extensions/01_basics/native/ext-bindings.c new file mode 100644 index 0000000..c805961 --- /dev/null +++ b/samples/12_c_extensions/01_basics/native/ext-bindings.c @@ -0,0 +1,68 @@ +#include <mruby.h> +#include <string.h> +#include <assert.h> +#include <mruby/string.h> +#include <mruby/data.h> +#include <dragonruby.h> +#include "app/ext.c" + +// MRuby `typedef`s mrb_int in the mruby/value.h +// Then `#define`s mrb_int in mruby.h +// We need to undo the macro and avoid it's usage +// FIXME: I'm surely doing something wrong +#ifdef mrb_int +#undef mrb_int +#endif + +static void (*drb_free_foreign_object_f)(mrb_state *, void *); +static struct RClass *(*mrb_module_get_f)(mrb_state *, const char *); +static mrb_int (*mrb_get_args_f)(mrb_state *, mrb_args_format, ...); +static struct RClass *(*mrb_module_get_under_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_class_get_under_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_define_module_under_f)(mrb_state *, struct RClass *, const char *); +static void (*mrb_define_module_function_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static struct RClass *(*mrb_define_class_under_f)(mrb_state *, struct RClass *, const char *, struct RClass *); +static void (*mrb_define_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static void (*mrb_define_class_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static struct RData *(*mrb_data_object_alloc_f)(mrb_state *, struct RClass *, void *, const mrb_data_type *); +static mrb_value (*mrb_str_new_cstr_f)(mrb_state *, const char *); +static void (*mrb_raise_f)(mrb_state *, struct RClass *, const char *); +static int drb_ffi__ZTSi_FromRuby(mrb_state *state, mrb_value self) { + return mrb_fixnum(self); +} +static mrb_value drb_ffi__ZTSi_ToRuby(mrb_state *state, int value) { + return mrb_fixnum_value(value); +} +static mrb_value drb_ffi_square_Binding(mrb_state *state, mrb_value value) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(state, "*", &args, &argc); + int x_0 = drb_ffi__ZTSi_FromRuby(state, args[0]); + int ret_val = square(x_0); + return drb_ffi__ZTSi_ToRuby(state, ret_val); +} +static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *)); +DRB_FFI_EXPORT +void drb_register_c_extensions(void *(*lookup)(const char *), mrb_state *state, struct RClass *FFI) { + if (drb_ffi_init_indirect_functions(lookup)) + return; + struct RClass *module = mrb_define_module_under_f(state, FFI, "CExt"); + struct RClass *object_class = state->object_class; + mrb_define_module_function_f(state, module, "square", drb_ffi_square_Binding, MRB_ARGS_REQ(1)); +} +static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *fnname)) { + if (!(mrb_raise_f = (void (*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_raise"))) return -1; + if (!(mrb_module_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_module_get"))) return -1; + if (!(mrb_class_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_class_get_under"))) return -1; + if (!(mrb_get_args_f = (mrb_int (*)(mrb_state *, mrb_args_format, ...)) lookup("mrb_get_args"))) return -1; + if (!(drb_free_foreign_object_f = (void (*)(mrb_state *, void *)) lookup("drb_free_foreign_object"))) return -1; + if (!(mrb_define_module_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_define_module_under"))) return -1; + if (!(mrb_str_new_cstr_f = (mrb_value (*)(mrb_state *, const char *)) lookup("mrb_str_new_cstr"))) return -1; + if (!(mrb_define_class_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *, struct RClass *)) lookup("mrb_define_class_under"))) return -1; + if (!(mrb_define_module_function_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_module_function"))) return -1; + if (!(mrb_data_object_alloc_f = (struct RData *(*)(mrb_state *, struct RClass *, void *, const mrb_data_type *)) lookup("mrb_data_object_alloc"))) return -1; + if (!(mrb_module_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_module_get_under"))) return -1; + if (!(mrb_define_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_method"))) return -1; + if (!(mrb_define_class_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_class_method"))) return -1; + return 0; +} diff --git a/samples/12_c_extensions/01_basics/pre.bat b/samples/12_c_extensions/01_basics/pre.bat index 3b3e27d..16befa3 100644 --- a/samples/12_c_extensions/01_basics/pre.bat +++ b/samples/12_c_extensions/01_basics/pre.bat @@ -1,4 +1,5 @@ set DRB_ROOT=..\..\..\ -md build.dir -%DRB_ROOT%\dragonruby-bind.exe --output=build.dir\ext-bind.c app\ext.c -clang -shared .\build.dir\ext-bind.c --sysroot=C:\mingw-w64\x86_64-8.1.0\mingw64 --target=x86_64-w64-mingw32 -fuse-ld=lld -isystem %DRB_ROOT%\include -I. -o build.dir\ext.lib +md native +md native\windows-amd64 +%DRB_ROOT%\dragonruby-bind.exe --output=native\ext-bind.c app\ext.c +clang -shared .\native\ext-bind.c --sysroot=C:\mingw-w64\mingw64 --target=x86_64-w64-mingw32 -fuse-ld=lld -isystem %DRB_ROOT%\include -I. -o native\windows-amd64\ext.dll diff --git a/samples/12_c_extensions/01_basics/pre.sh b/samples/12_c_extensions/01_basics/pre.sh index 8aa9eb7..07965a5 100755 --- a/samples/12_c_extensions/01_basics/pre.sh +++ b/samples/12_c_extensions/01_basics/pre.sh @@ -1,10 +1,19 @@ #!/bin/sh + +OSTYPE=`uname -s` +if [ "x$OSTYPE" = "xDarwin" ]; then + PLATFORM=macos + DLLEXT=dylib +else + PLATFORM=linux-amd64 + DLLEXT=so +fi + DRB_ROOT=../../.. -mkdir -p build.dir +mkdir -p native/$PLATFORM -$DRB_ROOT/dragonruby-bind --output=build.dir/ext-bindings.c app/ext.c +$DRB_ROOT/dragonruby-bind --output=native/ext-bindings.c app/ext.c clang \ -isystem $DRB_ROOT/include -I. \ - -undefined dynamic_lookup \ - -fPIC -shared build.dir/ext-bindings.c -o build.dir/ext.lib + -fPIC -shared native/ext-bindings.c -o native/$PLATFORM/ext.$DLLEXT diff --git a/samples/12_c_extensions/02_intermediate/README.md b/samples/12_c_extensions/02_intermediate/README.md new file mode 100644 index 0000000..a597944 --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/README.md @@ -0,0 +1,187 @@ +# C Extensions introduction + +This sample app is the next step on mastering C extensions for the +DragonRuby Game Toolkit. You'll need a Pro License which can be purchased at +http://dragonruby.org. The sample app is provided in the Standard license for +those that are curious as to what implementing C Extensions looks like. + +## Requirements + +Make sure you went through the first example '01_basics'. + +## Regular Expressions + +This sample shows how to use a third-party C library. We use a library for +regular expressions: https://github.com/kokke/tiny-regex-c. + +The library comes with two files `re.c` and `re.h`. We need to generate bindings +from the header file. By default, DragonRuby generates bindings under `CExt` +module. This can be overriden by `--ffi-module=` command line flag. + +Run the following command from Linux/macOS terminal: + +``` +> ./dragonruby-bind --output=mygame/re-bindings.c --ffi-module=RE mygame/re.h +``` + +Or from Windows Powershell: + +``` +> .\dragobruby-bind.exe --output=mygame\re-bindings.c --ffi-module=RE mygame\re.h +``` + +Now you can compile the library together with the generated bindings. + +On Windows: + +``` +clang -shared \ + --sysroot=C:\mingw-w64\mingw64^ + --target=x86_64-w64-mingw32 -fuse-ld=lld^ + -isystem include -I . -fPIC^ + -o mygame\native\windows-amd64\ext.dll mygame\re.c mygame\re-bindings.c +``` + +On Linux: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-amd64/ext.so mygame/re-bindings.c mygame/re.c +``` + +On Raspberry Pi, on the device: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-raspberrypi/ext.so mygame/re-bindings.c mygame/re.c +``` + +On Raspberry Pi, with a cross-compiler: + +``` +> clang -shared --sysroot=/where/i/installed/a/sysroot \ + --target=arm-linux-gnueabihf -fuse-ld=lld \ + -isystem include -I . -fPIC \ + -o mygame/native/linux-raspberrypi/ext.so mygame/re-bindings.c mygame/re.c +``` + +On macOS: + +``` +> clang -shared \ + -isystem include -I . -fPIC \ + -o mygame/native/macos/ext.dylib mygame/re-bindings.c mygame/re.c +``` + +On Android: + +(This is what it looks like on Linux, change the appropriate parts for Windows, etc) + +``` +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=armv7-none-linux-androideabi26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a \ + -mthumb -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-arm32/ext.so mygame/re-bindings.c mygame/re.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=aarch64-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-arm64/ext.so mygame/re-bindings.c mygame/re.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=i686-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-x86/ext.so mygame/re-bindings.c mygame/re.c + +> android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \ + --target=x86_64-none-linux-android26 \ + --gcc-toolchain=android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64 \ + --sysroot=/home/icculus/projects/dragonruby/build-tools/host/Linux-amd64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ + -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 \ + -fno-limit-debug-info -fPIC -shared \ + -o mygame/native/android-amd64/ext.so mygame/re-bindings.c mygame/re.c +``` + +Now, the C extension is available under the `FFI::RE` namespace. + +``` +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::RE +``` + +The `tiny-regex-c` exposes three functions, we are interested in the last one: + +```c +int re_match(const char* pattern, const char* text, int* matchlength); +``` + +The first argument is the regex pattern to search for. The second one is the +actual text we want to search through. The last argument is a pointer to an +integer: it stores the location of the last character found in the text. +The function returns the first location of the found pattern, or -1 if the +pattern is not found. The usage in DragonRuby is straightforward: + +```ruby +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::RE + +input = "<<Hello, DragonRiders!>>" +last = IntPointer.new +first = re_match("\\w+", input, last) +if first != -1 + puts "Found a word (#{first}, #{last.value}): '#{input.slice(first, last.value)}'" +end + +def tick args +end +``` + +When you run the game you should see `Found a word (2, 5): 'Hello'` on the console. + +Two things are worth mentioning. The C API requires `const char *` and `int *`. +In case of strings, DragonRuby does the transformation for you transparently. +In the case of `int *` you have to create the right pointer manually (`IntPointer.new`). +The good thing is that you don't need to free or deallocate this memory manually: +DragonRuby also does this for you transparently. + +With this knowledge, we can now create a slightly more complex function that +splits input into an array of words: + +```ruby +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::RE + +def split_words(input) + words = [] + last = IntPointer.new + re = re_compile("\\w+") + first = re_matchp(re, input, last) + while first != -1 + words << input.slice(first, last.value) + input = input.slice(last.value + first, input.length) + first = re_matchp(re, input, last) + end + words +end + +input = "<<Hello, DragonRiders!>>" + +def tick args + args.outputs.labels << [640, 500, split_words(input).join(' '), 5, 1] +end +``` + +Now, you should see a big label with "Hello DragonRiders" on it. + + diff --git a/samples/12_c_extensions/02_intermediate/app/main.rb b/samples/12_c_extensions/02_intermediate/app/main.rb new file mode 100644 index 0000000..2e3fd7c --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/app/main.rb @@ -0,0 +1,19 @@ +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::RE + +def split_words(input) + words = [] + last = IntPointer.new + re = re_compile("\\w+") + first = re_matchp(re, input, last) + while first != -1 + words << input.slice(first, last.value) + input = input.slice(last.value + first, input.length) + first = re_matchp(re, input, last) + end + words +end + +def tick args + args.outputs.labels << [640, 500, split_words("hello, dragonriders!").join(' '), 5, 1] +end diff --git a/samples/12_c_extensions/02_intermediate/app/re.c b/samples/12_c_extensions/02_intermediate/app/re.c new file mode 100644 index 0000000..2d64d9f --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/app/re.c @@ -0,0 +1,527 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + + + +#include "re.h" +#include <stdio.h> + +/* Definitions: */ + +#define MAX_REGEXP_OBJECTS 30 /* Max number of regex symbols in expression. */ +#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */ + + +enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE, /* BRANCH */ }; + +typedef struct regex_t +{ + unsigned char type; /* CHAR, STAR, etc. */ + union + { + unsigned char ch; /* the character itself */ + unsigned char* ccl; /* OR a pointer to characters in class */ + }; +} regex_t; + + + +/* Private function declarations: */ +static int matchpattern(regex_t* pattern, const char* text, int* matchlength); +static int matchcharclass(char c, const char* str); +static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength); +static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength); +static int matchone(regex_t p, char c); +static int matchdigit(char c); +static int matchalpha(char c); +static int matchwhitespace(char c); +static int matchmetachar(char c, const char* str); +static int matchrange(char c, const char* str); +static int matchdot(char c); +static int ismetachar(char c); + + + +/* Public functions: */ +int re_match(const char* pattern, const char* text, int* matchlength) +{ + return re_matchp(re_compile(pattern), text, matchlength); +} + +int re_matchp(re_t pattern, const char* text, int* matchlength) +{ + *matchlength = 0; + if (pattern != 0) + { + if (pattern[0].type == BEGIN) + { + return ((matchpattern(&pattern[1], text, matchlength)) ? 0 : -1); + } + else + { + int idx = -1; + + do + { + idx += 1; + + if (matchpattern(pattern, text, matchlength)) + { + if (text[0] == '\0') + return -1; + + return idx; + } + } + while (*text++ != '\0'); + } + } + return -1; +} + +re_t re_compile(const char* pattern) +{ + /* The sizes of the two static arrays below substantiates the static RAM usage of this module. + MAX_REGEXP_OBJECTS is the max number of symbols in the expression. + MAX_CHAR_CLASS_LEN determines the size of buffer for chars in all char-classes in the expression. */ + static regex_t re_compiled[MAX_REGEXP_OBJECTS]; + static unsigned char ccl_buf[MAX_CHAR_CLASS_LEN]; + int ccl_bufidx = 1; + + char c; /* current char in pattern */ + int i = 0; /* index into pattern */ + int j = 0; /* index into re_compiled */ + + while (pattern[i] != '\0' && (j+1 < MAX_REGEXP_OBJECTS)) + { + c = pattern[i]; + + switch (c) + { + /* Meta-characters: */ + case '^': { re_compiled[j].type = BEGIN; } break; + case '$': { re_compiled[j].type = END; } break; + case '.': { re_compiled[j].type = DOT; } break; + case '*': { re_compiled[j].type = STAR; } break; + case '+': { re_compiled[j].type = PLUS; } break; + case '?': { re_compiled[j].type = QUESTIONMARK; } break; +/* case '|': { re_compiled[j].type = BRANCH; } break; <-- not working properly */ + + /* Escaped character-classes (\s \w ...): */ + case '\\': + { + if (pattern[i+1] != '\0') + { + /* Skip the escape-char '\\' */ + i += 1; + /* ... and check the next */ + switch (pattern[i]) + { + /* Meta-character: */ + case 'd': { re_compiled[j].type = DIGIT; } break; + case 'D': { re_compiled[j].type = NOT_DIGIT; } break; + case 'w': { re_compiled[j].type = ALPHA; } break; + case 'W': { re_compiled[j].type = NOT_ALPHA; } break; + case 's': { re_compiled[j].type = WHITESPACE; } break; + case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break; + + /* Escaped character, e.g. '.' or '$' */ + default: + { + re_compiled[j].type = CHAR; + re_compiled[j].ch = pattern[i]; + } break; + } + } + /* '\\' as last char in pattern -> invalid regular expression. */ +/* + else + { + re_compiled[j].type = CHAR; + re_compiled[j].ch = pattern[i]; + } +*/ + } break; + + /* Character class: */ + case '[': + { + /* Remember where the char-buffer starts. */ + int buf_begin = ccl_bufidx; + + /* Look-ahead to determine if negated */ + if (pattern[i+1] == '^') + { + re_compiled[j].type = INV_CHAR_CLASS; + i += 1; /* Increment i to avoid including '^' in the char-buffer */ + if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '^' */ + { + return 0; + } + } + else + { + re_compiled[j].type = CHAR_CLASS; + } + + /* Copy characters inside [..] to buffer */ + while ( (pattern[++i] != ']') + && (pattern[i] != '\0')) /* Missing ] */ + { + if (pattern[i] == '\\') + { + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1) + { + //fputs("exceeded internal buffer!\n", stderr); + return 0; + } + if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '\\' */ + { + return 0; + } + ccl_buf[ccl_bufidx++] = pattern[i++]; + } + else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + //fputs("exceeded internal buffer!\n", stderr); + return 0; + } + ccl_buf[ccl_bufidx++] = pattern[i]; + } + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + /* Catches cases such as [00000000000000000000000000000000000000][ */ + //fputs("exceeded internal buffer!\n", stderr); + return 0; + } + /* Null-terminate string end */ + ccl_buf[ccl_bufidx++] = 0; + re_compiled[j].ccl = &ccl_buf[buf_begin]; + } break; + + /* Other characters: */ + default: + { + re_compiled[j].type = CHAR; + re_compiled[j].ch = c; + } break; + } + /* no buffer-out-of-bounds access on invalid patterns - see https://github.com/kokke/tiny-regex-c/commit/1a279e04014b70b0695fba559a7c05d55e6ee90b */ + if (pattern[i] == 0) + { + return 0; + } + + i += 1; + j += 1; + } + /* 'UNUSED' is a sentinel used to indicate end-of-pattern */ + re_compiled[j].type = UNUSED; + + return (re_t) re_compiled; +} + +void re_print(regex_t* pattern) +{ + const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" }; + + int i; + int j; + char c; + for (i = 0; i < MAX_REGEXP_OBJECTS; ++i) + { + if (pattern[i].type == UNUSED) + { + break; + } + + printf("type: %s", types[pattern[i].type]); + if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS) + { + printf(" ["); + for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j) + { + c = pattern[i].ccl[j]; + if ((c == '\0') || (c == ']')) + { + break; + } + printf("%c", c); + } + printf("]"); + } + else if (pattern[i].type == CHAR) + { + printf(" '%c'", pattern[i].ch); + } + printf("\n"); + } +} + + + +/* Private functions: */ +static int matchdigit(char c) +{ + return ((c >= '0') && (c <= '9')); +} +static int matchalpha(char c) +{ + return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); +} +static int matchwhitespace(char c) +{ + return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\f') || (c == '\v')); +} +static int matchalphanum(char c) +{ + return ((c == '_') || matchalpha(c) || matchdigit(c)); +} +static int matchrange(char c, const char* str) +{ + return ( (c != '-') + && (str[0] != '\0') + && (str[0] != '-') + && (str[1] == '-') + && (str[2] != '\0') + && ( (c >= str[0]) + && (c <= str[2]))); +} +static int matchdot(char c) +{ +#if defined(RE_DOT_MATCHES_NEWLINE) && (RE_DOT_MATCHES_NEWLINE == 1) + (void)c; + return 1; +#else + return c != '\n' && c != '\r'; +#endif +} +static int ismetachar(char c) +{ + return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D')); +} + +static int matchmetachar(char c, const char* str) +{ + switch (str[0]) + { + case 'd': return matchdigit(c); + case 'D': return !matchdigit(c); + case 'w': return matchalphanum(c); + case 'W': return !matchalphanum(c); + case 's': return matchwhitespace(c); + case 'S': return !matchwhitespace(c); + default: return (c == str[0]); + } +} + +static int matchcharclass(char c, const char* str) +{ + do + { + if (matchrange(c, str)) + { + return 1; + } + else if (str[0] == '\\') + { + /* Escape-char: increment str-ptr and match on next char */ + str += 1; + if (matchmetachar(c, str)) + { + return 1; + } + else if ((c == str[0]) && !ismetachar(c)) + { + return 1; + } + } + else if (c == str[0]) + { + if (c == '-') + { + return ((str[-1] == '\0') || (str[1] == '\0')); + } + else + { + return 1; + } + } + } + while (*str++ != '\0'); + + return 0; +} + +static int matchone(regex_t p, char c) +{ + switch (p.type) + { + case DOT: return matchdot(c); + case CHAR_CLASS: return matchcharclass(c, (const char*)p.ccl); + case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.ccl); + case DIGIT: return matchdigit(c); + case NOT_DIGIT: return !matchdigit(c); + case ALPHA: return matchalphanum(c); + case NOT_ALPHA: return !matchalphanum(c); + case WHITESPACE: return matchwhitespace(c); + case NOT_WHITESPACE: return !matchwhitespace(c); + default: return (p.ch == c); + } +} + +static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + int prelen = *matchlength; + const char* prepoint = text; + while ((text[0] != '\0') && matchone(p, *text)) + { + text++; + (*matchlength)++; + } + while (text >= prepoint) + { + if (matchpattern(pattern, text--, matchlength)) + return 1; + (*matchlength)--; + } + + *matchlength = prelen; + return 0; +} + +static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + const char* prepoint = text; + while ((text[0] != '\0') && matchone(p, *text)) + { + text++; + (*matchlength)++; + } + while (text > prepoint) + { + if (matchpattern(pattern, text--, matchlength)) + return 1; + (*matchlength)--; + } + + return 0; +} + +static int matchquestion(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + if (p.type == UNUSED) + return 1; + if (matchpattern(pattern, text, matchlength)) + return 1; + if (*text && matchone(p, *text++)) + { + if (matchpattern(pattern, text, matchlength)) + { + (*matchlength)++; + return 1; + } + } + return 0; +} + + +#if 0 + +/* Recursive matching */ +static int matchpattern(regex_t* pattern, const char* text, int *matchlength) +{ + int pre = *matchlength; + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[1], &pattern[2], text, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return text[0] == '\0'; + } + else if ((text[0] != '\0') && matchone(pattern[0], text[0])) + { + (*matchlength)++; + return matchpattern(&pattern[1], text+1); + } + else + { + *matchlength = pre; + return 0; + } +} + +#else + +/* Iterative matching */ +static int matchpattern(regex_t* pattern, const char* text, int* matchlength) +{ + int pre = *matchlength; + do + { + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return (text[0] == '\0'); + } +/* Branching is not working properly + else if (pattern[1].type == BRANCH) + { + return (matchpattern(pattern, text) || matchpattern(&pattern[2], text)); + } +*/ + (*matchlength)++; + } + while ((text[0] != '\0') && matchone(*pattern++, *text++)); + + *matchlength = pre; + return 0; +} + +#endif diff --git a/samples/12_c_extensions/02_intermediate/app/re.h b/samples/12_c_extensions/02_intermediate/app/re.h new file mode 100644 index 0000000..fb2d002 --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/app/re.h @@ -0,0 +1,65 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + +#ifndef _TINY_REGEX_C +#define _TINY_REGEX_C + + +#ifndef RE_DOT_MATCHES_NEWLINE +/* Define to 0 if you DON'T want '.' to match '\r' + '\n' */ +#define RE_DOT_MATCHES_NEWLINE 1 +#endif + +#ifdef __cplusplus +extern "C"{ +#endif + + + +/* Typedef'd pointer to get abstract datatype. */ +typedef struct regex_t* re_t; + + +/* Compile regex string pattern to a regex_t-array. */ +re_t re_compile(const char* pattern); + + +/* Find matches of the compiled pattern inside text. */ +int re_matchp(re_t pattern, const char* text, int* matchlength); + + +/* Find matches of the txt pattern inside text (will compile automatically first). */ +int re_match(const char* pattern, const char* text, int* matchlength); + + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef _TINY_REGEX_C */ diff --git a/samples/12_c_extensions/02_intermediate/c-regex-demo.png b/samples/12_c_extensions/02_intermediate/c-regex-demo.png Binary files differnew file mode 100644 index 0000000..19277c8 --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/c-regex-demo.png diff --git a/samples/12_c_extensions/02_intermediate/license-for-regex-library.txt b/samples/12_c_extensions/02_intermediate/license-for-regex-library.txt new file mode 100644 index 0000000..cf1ab25 --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/license-for-regex-library.txt @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to <http://unlicense.org> diff --git a/samples/12_c_extensions/02_intermediate/license-for-sample.txt b/samples/12_c_extensions/02_intermediate/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +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/12_c_extensions/02_intermediate/metadata/game_metadata.txt b/samples/12_c_extensions/02_intermediate/metadata/game_metadata.txt new file mode 100644 index 0000000..80ed5d1 --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/metadata/game_metadata.txt @@ -0,0 +1,5 @@ +devid=dragonruby +devtitle=DragonRuby LLC +gameid=cintermediate +gametitle=C Extensions Intermediate +version=1.0 diff --git a/samples/12_c_extensions/02_intermediate/native/re-bindings.c b/samples/12_c_extensions/02_intermediate/native/re-bindings.c new file mode 100644 index 0000000..6aa5630 --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/native/re-bindings.c @@ -0,0 +1,282 @@ +#include <mruby.h> +#include <string.h> +#include <assert.h> +#include <mruby/string.h> +#include <mruby/data.h> +#include <dragonruby.h> +#include "app/re.h" + +// MRuby `typedef`s mrb_int in the mruby/value.h +// Then `#define`s mrb_int in mruby.h +// We need to undo the macro and avoid it's usage +// FIXME: I'm surely doing something wrong +#ifdef mrb_int +#undef mrb_int +#endif + +static void (*drb_free_foreign_object_f)(mrb_state *, void *); +static struct RClass *(*mrb_module_get_f)(mrb_state *, const char *); +static mrb_int (*mrb_get_args_f)(mrb_state *, mrb_args_format, ...); +static struct RClass *(*mrb_module_get_under_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_class_get_under_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_define_module_under_f)(mrb_state *, struct RClass *, const char *); +static void (*mrb_define_module_function_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static struct RClass *(*mrb_define_class_under_f)(mrb_state *, struct RClass *, const char *, struct RClass *); +static void (*mrb_define_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static void (*mrb_define_class_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static struct RData *(*mrb_data_object_alloc_f)(mrb_state *, struct RClass *, void *, const mrb_data_type *); +static mrb_value (*mrb_str_new_cstr_f)(mrb_state *, const char *); +static void (*mrb_raise_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_exc_get_f)(mrb_state *, const char *); +static void drb_free_foreign_object_indirect(mrb_state *state, void *pointer) { + drb_free_foreign_object_f(state, pointer); +} +struct drb_foreign_object_ZTSP7regex_t { + drb_foreign_object_kind kind; + struct regex_t *value; + int should_free; +}; +static mrb_data_type ForeignObjectType_ZTSP7regex_t = {"regex_t*", drb_free_foreign_object_indirect}; +static struct regex_t *drb_ffi__ZTSP7regex_t_FromRuby(mrb_state *state, mrb_value self) { + if (mrb_nil_p(self)) + return 0; + return ((struct drb_foreign_object_ZTSP7regex_t *)DATA_PTR(self))->value; +} +static mrb_value drb_ffi__ZTSP7regex_t_ToRuby(mrb_state *state, struct regex_t *value) { + struct drb_foreign_object_ZTSP7regex_t *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSP7regex_t)); + ptr->value = value; + ptr->kind = drb_foreign_object_kind_pointer; + struct RClass *FFI = mrb_module_get_f(state, "FFI"); + struct RClass *module = mrb_module_get_under_f(state, FFI, "RE"); + struct RClass *klass = mrb_class_get_under_f(state, module, "Regex_tPointer"); + struct RData *rdata = mrb_data_object_alloc_f(state, klass, ptr, &ForeignObjectType_ZTSP7regex_t); + return mrb_obj_value(rdata); +} +struct drb_foreign_object_ZTSPc { + drb_foreign_object_kind kind; + char *value; + int should_free; +}; +static mrb_data_type ForeignObjectType_ZTSPc = {"char*", drb_free_foreign_object_indirect}; +static char *drb_ffi__ZTSPc_FromRuby(mrb_state *state, mrb_value self) { + if (mrb_nil_p(self)) + return 0; + if (mrb_type(self) == MRB_TT_STRING) + return RSTRING_PTR(self); + return ((struct drb_foreign_object_ZTSPc *)DATA_PTR(self))->value; +} +static mrb_value drb_ffi__ZTSPc_ToRuby(mrb_state *state, char *value) { + struct drb_foreign_object_ZTSPc *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPc)); + ptr->value = value; + ptr->kind = drb_foreign_object_kind_pointer; + struct RClass *FFI = mrb_module_get_f(state, "FFI"); + struct RClass *module = mrb_module_get_under_f(state, FFI, "RE"); + struct RClass *klass = mrb_class_get_under_f(state, module, "CharPointer"); + struct RData *rdata = mrb_data_object_alloc_f(state, klass, ptr, &ForeignObjectType_ZTSPc); + return mrb_obj_value(rdata); +} +static int drb_ffi__ZTSi_FromRuby(mrb_state *state, mrb_value self) { + return mrb_fixnum(self); +} +static mrb_value drb_ffi__ZTSi_ToRuby(mrb_state *state, int value) { + return mrb_fixnum_value(value); +} +struct drb_foreign_object_ZTSPi { + drb_foreign_object_kind kind; + int *value; + int should_free; +}; +static mrb_data_type ForeignObjectType_ZTSPi = {"int*", drb_free_foreign_object_indirect}; +static int *drb_ffi__ZTSPi_FromRuby(mrb_state *state, mrb_value self) { + if (mrb_nil_p(self)) + return 0; + return ((struct drb_foreign_object_ZTSPi *)DATA_PTR(self))->value; +} +static mrb_value drb_ffi__ZTSPi_ToRuby(mrb_state *state, int *value) { + struct drb_foreign_object_ZTSPi *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPi)); + ptr->value = value; + ptr->kind = drb_foreign_object_kind_pointer; + struct RClass *FFI = mrb_module_get_f(state, "FFI"); + struct RClass *module = mrb_module_get_under_f(state, FFI, "RE"); + struct RClass *klass = mrb_class_get_under_f(state, module, "IntPointer"); + struct RData *rdata = mrb_data_object_alloc_f(state, klass, ptr, &ForeignObjectType_ZTSPi); + return mrb_obj_value(rdata); +} +static char drb_ffi__ZTSc_FromRuby(mrb_state *state, mrb_value self) { + return mrb_fixnum(self); +} +static mrb_value drb_ffi__ZTSc_ToRuby(mrb_state *state, char value) { + return mrb_fixnum_value(value); +} +static mrb_value drb_ffi__ZTSP7regex_t_New(mrb_state *mrb, mrb_value self) { + mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot allocate pointer of incomplete type"); + return mrb_nil_value(); +} +static mrb_value drb_ffi__ZTSP7regex_t_GetValue(mrb_state *mrb, mrb_value value) { + mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot access value of incomplete type"); + return mrb_nil_value(); +} +static mrb_value drb_ffi__ZTSP7regex_t_IsNil(mrb_state *state, mrb_value self) { + if (drb_ffi__ZTSP7regex_t_FromRuby(state, self) == 0) + return mrb_true_value(); + else + return mrb_false_value(); +} +static mrb_value drb_ffi__ZTSP7regex_t_GetAt(mrb_state *mrb, mrb_value self) { + mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot access value of incomplete type"); + return mrb_nil_value(); +} +static mrb_value drb_ffi__ZTSP7regex_t_SetAt(mrb_state *mrb, mrb_value self) { + mrb_raise_f(mrb, mrb_exc_get_f(mrb, "RuntimeError"), "Cannot change value of incomplete type"); + return mrb_nil_value(); +} +static mrb_value drb_ffi__ZTSPc_New(mrb_state *mrb, mrb_value self) { + struct drb_foreign_object_ZTSPc *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPc)); + ptr->kind = drb_foreign_object_kind_pointer; + ptr->value = calloc(1, sizeof(char)); + ptr->should_free = 1; + struct RClass *FFI = mrb_module_get_f(mrb, "FFI"); + struct RClass *module = mrb_module_get_under_f(mrb, FFI, "RE"); + struct RClass *klass = mrb_class_get_under_f(mrb, module, "CharPointer"); + struct RData *rdata = mrb_data_object_alloc_f(mrb, klass, ptr, &ForeignObjectType_ZTSPc); + return mrb_obj_value(rdata); +} +static mrb_value drb_ffi__ZTSPc_GetValue(mrb_state *mrb, mrb_value value) { + return drb_ffi__ZTSc_ToRuby(mrb, *drb_ffi__ZTSPc_FromRuby(mrb, value)); +} +static mrb_value drb_ffi__ZTSPc_IsNil(mrb_state *state, mrb_value self) { + if (drb_ffi__ZTSPc_FromRuby(state, self) == 0) + return mrb_true_value(); + else + return mrb_false_value(); +} +static mrb_value drb_ffi__ZTSPc_GetAt(mrb_state *mrb, mrb_value self) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(mrb, "*", &args, &argc); + int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]); + return drb_ffi__ZTSc_ToRuby(mrb, drb_ffi__ZTSPc_FromRuby(mrb, self)[index]); +} +static mrb_value drb_ffi__ZTSPc_SetAt(mrb_state *mrb, mrb_value self) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(mrb, "*", &args, &argc); + int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]); + char new_value = drb_ffi__ZTSc_FromRuby(mrb, args[1]); + drb_ffi__ZTSPc_FromRuby(mrb, self)[index] = new_value; + return mrb_nil_value(); +} +static mrb_value drb_ffi__ZTSPc_GetString(mrb_state *state, mrb_value self) { + return mrb_str_new_cstr_f(state, drb_ffi__ZTSPc_FromRuby(state, self)); +} +static mrb_value drb_ffi__ZTSPi_New(mrb_state *mrb, mrb_value self) { + struct drb_foreign_object_ZTSPi *ptr = calloc(1, sizeof(struct drb_foreign_object_ZTSPi)); + ptr->kind = drb_foreign_object_kind_pointer; + ptr->value = calloc(1, sizeof(int)); + ptr->should_free = 1; + struct RClass *FFI = mrb_module_get_f(mrb, "FFI"); + struct RClass *module = mrb_module_get_under_f(mrb, FFI, "RE"); + struct RClass *klass = mrb_class_get_under_f(mrb, module, "IntPointer"); + struct RData *rdata = mrb_data_object_alloc_f(mrb, klass, ptr, &ForeignObjectType_ZTSPi); + return mrb_obj_value(rdata); +} +static mrb_value drb_ffi__ZTSPi_GetValue(mrb_state *mrb, mrb_value value) { + return drb_ffi__ZTSi_ToRuby(mrb, *drb_ffi__ZTSPi_FromRuby(mrb, value)); +} +static mrb_value drb_ffi__ZTSPi_IsNil(mrb_state *state, mrb_value self) { + if (drb_ffi__ZTSPi_FromRuby(state, self) == 0) + return mrb_true_value(); + else + return mrb_false_value(); +} +static mrb_value drb_ffi__ZTSPi_GetAt(mrb_state *mrb, mrb_value self) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(mrb, "*", &args, &argc); + int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]); + return drb_ffi__ZTSi_ToRuby(mrb, drb_ffi__ZTSPi_FromRuby(mrb, self)[index]); +} +static mrb_value drb_ffi__ZTSPi_SetAt(mrb_state *mrb, mrb_value self) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(mrb, "*", &args, &argc); + int index = drb_ffi__ZTSi_FromRuby(mrb, args[0]); + int new_value = drb_ffi__ZTSi_FromRuby(mrb, args[1]); + drb_ffi__ZTSPi_FromRuby(mrb, self)[index] = new_value; + return mrb_nil_value(); +} +static mrb_value drb_ffi_re_compile_Binding(mrb_state *state, mrb_value value) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(state, "*", &args, &argc); + char *pattern_0 = drb_ffi__ZTSPc_FromRuby(state, args[0]); + struct regex_t *ret_val = re_compile(pattern_0); + return drb_ffi__ZTSP7regex_t_ToRuby(state, ret_val); +} +static mrb_value drb_ffi_re_matchp_Binding(mrb_state *state, mrb_value value) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(state, "*", &args, &argc); + struct regex_t *pattern_0 = drb_ffi__ZTSP7regex_t_FromRuby(state, args[0]); + char *text_1 = drb_ffi__ZTSPc_FromRuby(state, args[1]); + int *matchlength_2 = drb_ffi__ZTSPi_FromRuby(state, args[2]); + int ret_val = re_matchp(pattern_0, text_1, matchlength_2); + return drb_ffi__ZTSi_ToRuby(state, ret_val); +} +static mrb_value drb_ffi_re_match_Binding(mrb_state *state, mrb_value value) { + mrb_value *args = 0; + mrb_int argc = 0; + mrb_get_args_f(state, "*", &args, &argc); + char *pattern_0 = drb_ffi__ZTSPc_FromRuby(state, args[0]); + char *text_1 = drb_ffi__ZTSPc_FromRuby(state, args[1]); + int *matchlength_2 = drb_ffi__ZTSPi_FromRuby(state, args[2]); + int ret_val = re_match(pattern_0, text_1, matchlength_2); + return drb_ffi__ZTSi_ToRuby(state, ret_val); +} +static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *)); +DRB_FFI_EXPORT +void drb_register_c_extensions(void *(*lookup)(const char *), mrb_state *state, struct RClass *FFI) { + if (drb_ffi_init_indirect_functions(lookup)) + return; + struct RClass *module = mrb_define_module_under_f(state, FFI, "RE"); + struct RClass *object_class = state->object_class; + mrb_define_module_function_f(state, module, "re_compile", drb_ffi_re_compile_Binding, MRB_ARGS_REQ(1)); + mrb_define_module_function_f(state, module, "re_matchp", drb_ffi_re_matchp_Binding, MRB_ARGS_REQ(3)); + mrb_define_module_function_f(state, module, "re_match", drb_ffi_re_match_Binding, MRB_ARGS_REQ(3)); + struct RClass *Regex_tPointerClass = mrb_define_class_under_f(state, module, "Regex_tPointer", object_class); + mrb_define_class_method_f(state, Regex_tPointerClass, "new", drb_ffi__ZTSP7regex_t_New, MRB_ARGS_REQ(0)); + mrb_define_method_f(state, Regex_tPointerClass, "value", drb_ffi__ZTSP7regex_t_GetValue, MRB_ARGS_REQ(0)); + mrb_define_method_f(state, Regex_tPointerClass, "[]", drb_ffi__ZTSP7regex_t_GetAt, MRB_ARGS_REQ(1)); + mrb_define_method_f(state, Regex_tPointerClass, "[]=", drb_ffi__ZTSP7regex_t_SetAt, MRB_ARGS_REQ(2)); + mrb_define_method_f(state, Regex_tPointerClass, "nil?", drb_ffi__ZTSP7regex_t_IsNil, MRB_ARGS_REQ(0)); + struct RClass *CharPointerClass = mrb_define_class_under_f(state, module, "CharPointer", object_class); + mrb_define_class_method_f(state, CharPointerClass, "new", drb_ffi__ZTSPc_New, MRB_ARGS_REQ(0)); + mrb_define_method_f(state, CharPointerClass, "value", drb_ffi__ZTSPc_GetValue, MRB_ARGS_REQ(0)); + mrb_define_method_f(state, CharPointerClass, "[]", drb_ffi__ZTSPc_GetAt, MRB_ARGS_REQ(1)); + mrb_define_method_f(state, CharPointerClass, "[]=", drb_ffi__ZTSPc_SetAt, MRB_ARGS_REQ(2)); + mrb_define_method_f(state, CharPointerClass, "nil?", drb_ffi__ZTSPc_IsNil, MRB_ARGS_REQ(0)); + mrb_define_method_f(state, CharPointerClass, "str", drb_ffi__ZTSPc_GetString, MRB_ARGS_REQ(0)); + struct RClass *IntPointerClass = mrb_define_class_under_f(state, module, "IntPointer", object_class); + mrb_define_class_method_f(state, IntPointerClass, "new", drb_ffi__ZTSPi_New, MRB_ARGS_REQ(0)); + mrb_define_method_f(state, IntPointerClass, "value", drb_ffi__ZTSPi_GetValue, MRB_ARGS_REQ(0)); + mrb_define_method_f(state, IntPointerClass, "[]", drb_ffi__ZTSPi_GetAt, MRB_ARGS_REQ(1)); + mrb_define_method_f(state, IntPointerClass, "[]=", drb_ffi__ZTSPi_SetAt, MRB_ARGS_REQ(2)); + mrb_define_method_f(state, IntPointerClass, "nil?", drb_ffi__ZTSPi_IsNil, MRB_ARGS_REQ(0)); +} +static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *fnname)) { + if (!(mrb_raise_f = (void (*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_raise"))) return -1; + if (!(mrb_str_new_cstr_f = (mrb_value (*)(mrb_state *, const char *)) lookup("mrb_str_new_cstr"))) return -1; + if (!(mrb_define_class_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *, struct RClass *)) lookup("mrb_define_class_under"))) return -1; + if (!(mrb_define_class_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_class_method"))) return -1; + if (!(mrb_class_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_class_get_under"))) return -1; + if (!(mrb_module_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_module_get_under"))) return -1; + if (!(drb_free_foreign_object_f = (void (*)(mrb_state *, void *)) lookup("drb_free_foreign_object"))) return -1; + if (!(mrb_data_object_alloc_f = (struct RData *(*)(mrb_state *, struct RClass *, void *, const mrb_data_type *)) lookup("mrb_data_object_alloc"))) return -1; + if (!(mrb_get_args_f = (mrb_int (*)(mrb_state *, mrb_args_format, ...)) lookup("mrb_get_args"))) return -1; + if (!(mrb_module_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_module_get"))) return -1; + if (!(mrb_define_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_method"))) return -1; + if (!(mrb_exc_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_exc_get"))) return -1; + if (!(mrb_define_module_function_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_module_function"))) return -1; + if (!(mrb_define_module_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_define_module_under"))) return -1; + return 0; +} diff --git a/samples/12_c_extensions/02_intermediate/pre.bat b/samples/12_c_extensions/02_intermediate/pre.bat new file mode 100644 index 0000000..2b4ea2e --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/pre.bat @@ -0,0 +1,5 @@ +set DRB_ROOT=..\..\..\ +md native +md native\windows-amd64 +%DRB_ROOT%\dragonruby-bind.exe --ffi-module=RE --output=native\re-bindings.c app\re.h +clang -shared .\native\re-bindings.c .\app\re.c --sysroot=C:\mingw-w64\mingw64 --target=x86_64-w64-mingw32 -fuse-ld=lld -isystem %DRB_ROOT%\include -I. -o native\windows-amd64\ext.dll diff --git a/samples/12_c_extensions/02_intermediate/pre.sh b/samples/12_c_extensions/02_intermediate/pre.sh new file mode 100755 index 0000000..405f205 --- /dev/null +++ b/samples/12_c_extensions/02_intermediate/pre.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` +if [ "x$OSTYPE" = "xDarwin" ]; then + PLATFORM=macos + DLLEXT=dylib +else + PLATFORM=linux-amd64 + DLLEXT=so +fi + +DRB_ROOT=../../.. +mkdir -p native/$PLATFORM + +$DRB_ROOT/dragonruby-bind -ffi-module=RE --output=native/re-bindings.c app/re.h +clang \ + -isystem $DRB_ROOT/include -I. \ + -fPIC -shared app/re.c native/re-bindings.c -o native/$PLATFORM/ext.$DLLEXT + |
