diff options
64 files changed, 3270 insertions, 564 deletions
diff --git a/deploy_template/CHANGELOG.txt b/deploy_template/CHANGELOG.txt new file mode 100644 index 0000000..9bde121 --- /dev/null +++ b/deploy_template/CHANGELOG.txt @@ -0,0 +1,690 @@ += 20200225 = + + * [Bugfix] Fixed macOS compatibility back to Mac OS X 10.9 or so. + += 20200224 = + + * [Bugfix] Some of the deprecation notices where throwing an exception. This has been fixed. + * [API] $gtk.system(cmd) has been added. You'll find this helpful for running cli programs from + Heads Up Display. It's probably not a good idea to use this in a production game (since you'll + be responsible for making sure it works on all platforms). + += 20200218 = + + * [API] Enhanced screenshotting api that can be used for image manipulation. + Take a look at the painting sample app. + Example usage: `args.outputs.screenshots << [0, 0, 100, 100, 'bottom-left-corner.png']`. + * [BREAKING] Outputs#static_background_color has been renamed to Outputs#background_color. + * [API] Outputs#debug and Outputs#static_debug layer has been added. You can use this layer + to draw all your guide lines and helper primitives. When the game is published, this layer + will not render. + += 20200217 = + + * [Bugfix] `dragonruby-publish` would only build html5. It now builds + all of the platforms again. Ryan "The Juggernaut" Gordon has given Amir + a warning and has told him to be more careful releasing. Amir cackled + in defiance. + += 20200213 = + + * [API] Added Float#pos? and Float#neg?. + * [Samples] Sample added to show how to use directional_vector and sprite sheets. + * [Samples] Updated 01_api_01_labels sample app to show how to render a label + as an array or hash. + * [Bugfix] Fixed incorrectly named value for labels in 11_hash_primitives sample app. + += 20200211 = + + * [API] Added `args.inputs.directional_vector` and + `args.inputs.(keyboard|controller_one|controller_two).directional_vector. + The sample app to demonstrate the use of this feature is pending. + * [API] Added Numeric#pos? and Numeric#neg?. + * [Bugfix] Fixed looping issues with ogg files and popping issues with + playing sounds in general. + * [Bugfix] `args.gtk.console.toast :message, "some message"` would fail because + of an invalid method name. + * [News] Amir (the CEO of DragonRuby LLP) wrote a 2019 year end review. You can + read it here: http://www.rubymotion.com/news/2020/01/02/year-end-review.html. + * [News] Expect the frequency of updates of GTK to increase. Ryan let Amir have + the deployment passwords, so the rate limiter is now gone :3 + += 20200202 = + + * [API] Hash now supports :scale_rect. + * [Samples] Added sample app that shows how to create a hexagon based grid. + * [Support] Initial work on CHEATSHEET.txt. + * [API] Added args.inputs.keyboard.ctrl_* which will return true if Ctrl is + combined with another key. + * [API] Added args.inputs.click (which only returns the point or nil). + * [API] Added Numeric#even? and Numeric#odd?. + * [API] Added Geometry#point_inside_circle?(point, circle_point, radius). + += 2020130 = + + * [Performance] Rendering api optimizations. Initial tests show that + DragonRuby Game Toolkit can now render more sprites (and calculate + more collisions) than Unity 2D. Checkmate. + * [Support] Fixed documentation for labels (the hash properties are + called size_enum, and alignment_enum). The documentation can be + found under mygame/documentation. + * [Support] Improved help text for exceptions related to ambiguous use + of primitives. + * [Bugfix] Requiring multiple ruby files will no long "blip" an + exception on startup. + += 20191201 = + + * [Samples] Added sample game called The Little Probe that demonstrates + line collision. + * [Samples] Experiments with squares and cubes in 3D. + * [Support] HTML5 games now package a favicon. + * [Samples] [Support] Quite a few of the sample apps have been updated to + include an api listing at the top (expansion of documentation). + * [API] Initial work for http has been created. Take a look at the + http sample app to see how to use it. Take a look at + `samples/99_zz_gtk_unit_tests` for a test suite that asserts async + http. + * [Support] [Samples] Added a sample app that shows how create + collision detection associated with constraint points against + a ramp. This sample app also demostrates the use of `gtk.slowmo!` + which you can use the slow down the tick execution of your game + to see things frame by frame (still experimental). + * [Support] Added sprites to default template folder. The sprites + are `sprites/square-COLOR.png` and `sprites/circle-COLOR.png` where + the colors are of the rainbow ROY G. BIV (eg `sprites/square-red.png`). + * [Sample] Added a sample app that shows how to use the + `static_sprites` rendering api which is the fastest way to render + a sprite (but also hangs on the instances by reference as opposed + to treating the collection as a queue... a trade off between + immutablility and speed). + * [API] Added the `attr_sprite` class macro which can be mixed into + a class so that it quacks like a sprite. The Sprite Limits sample + app has been updated to use this macro. + * [Support] In the event of an exception, the class hierarchy for + the object that threw the exception is provided. + * [Support] In the event of a :method_missing exception, the + instance's API is searched for methods that look like the one you + were trying to call. Those recommendations are printed to the + console. + * [Samples] [Support] Significant enhancements have been made to the + test harness. Take a look at `samples/99_zz_gtk_unit_tests` to see + some of the test suites that are being fleshed out. These test + suites are used as regression during builds. More will come. + * [Samples] Unit tests added for geometry apis. + * [Samples] Unit tests added for parsing json and xml. + * [Bugfix] HTML5 builds no longer fail at startup with an audio + initialization error message. + * [Bugfix] The $gtk.write_file API change was a mistake. The actual + bug it was meant to resolve has been fixed. The responsible + parties have been brutally disciplined. + * [BREAKING] `gtk_args` class macro has been renamed to + `attr_gtk`. This is a little easier to remember because it follows + the same pattern as `attr_accessor`. + * [BREAKING] `PRIMITIVE.w(idth)_half` and `PRIMITIVE.h(eight)_half` + have been deprecated. Use `PRIMITIVE.w.half` (Numeric#half). + * [BREAKING] `PRIMITIVE.angle_given_point` has been deprecated. Use + `PRIMITIVE.angle_from`. + * [API] `PRIMITIVE.angle_to` has been added. Take a look at + `samples/99_zz_gtk_unit_tests` for usages. + * [BREAKING] `intersects_rect?` with an `s` has been deprecated. Use + `intersect_rect?` instead. + NOTE: Ruby's naming convention is to generally *not* include the + \"s\" for interrogative method names (methods that end with a ? + [question mark]). It doesn't sound grammatically correct, but that + has been the rule for a long time (and why intersects_rect? has + been deprecated). + * [Samples] Added a white label version of Clepto Frog, a Ludam Dare + game built by Amir, Karen, and Rye. You can see the final version + (with all assets) here: https://amirrajan.itch.io/clepto-frog. The + code is horrible. Who wrote this crap? + * [Bugfix] Fixed looping issues with `.ogg`. The follow statement + will now correctly work: + ``` + def tick args + if args.state.tick_count == 0 + args.outputs.sounds << 'sounds/sample.ogg' + end + end + ``` + * [API] Added `args.gtk.stop_music` to stop all looping music. + * [API] Added `args.geometry` which contains stateless functions + that exist on primitives. For a full listing of methods open up + the console and type `args.geometry.help`. A lot of these methods + are exercised in the `samples/99_zz_gtk_unit_tests` test suite. + * [Support] The --no-tick switch for ./dragonruby will now hide the + game window (it's a good way to run "headless" unit + tests/regressions suites). Usage: `./dragonruby mygame --eval + some_ruby_file.rb --no-tick`. + * [BREAKING] gtk.on_ticks has been deprecated, use the better named + gtk.scheduled_callbacks instead. + * [BREAKING] gtk.on_tick TICKCOUNT { BLOCK } has been deprecated, use the better named + gtk.schedule_callback TICKCOUNT { BLOCK } instead. + += release-20190930 = + + * [Samples] Added sample app that shows how trace can be used within a class. + * [Support] Added $gtk.log_level. Valid values are :on, + :off. When the value is set to :on, GTK log mesages will show up + in the DragonRuby Console *AND* be written to logs/log.txt. When the + value is set to :off, GTK log messages will *NOT* show up in the + console, but will *STILL* be written to logs/log.txt + * [API] Added args.inputs.mouse.(click|up|down).(x|y) so you don't have to do + args.inputs.mouse.click.point. + * [Support] You can change your window title at runtime with + $gtk.set_window_title('new title'). YOU MUST SET UP YOUR GAME'S METADATA + CORRECTLY EVEN IF YOU USE THIS FUNCTION. + * [Support] The macOS build is now compiled with Link-Time + Optimization, as an experiment, which makes your games a little faster and + your downloads a little smaller. + * [Support] We've split the open source licenses out of README.txt + to their own file, which is packaged with your games, to be a good citizen + of the world. If you add other open source stuff, just copy + open-source-licenses.txt into your game's folder and add your own stuff + to it; that will package with your game instead of the original text file. + * [Support] Added JSON parsing. Try $gtk.parse_json(myJsonString) or + $gtk.parse_json_file(filename) + * [Support] Added XML parsing. Try $gtk.parse_xml(myXmlString) or + $gtk.parse_xml_file(filename) + * [BREAKING] There is a new replay data format that is less crappy. But it breaks + existing replay data. You'll need to re-record your replays (sorry T_T). + To record a replay of gameplay, you can run ./dragonruby mygame --record. + When you close the game, a file called last_replay.txt will be created. + You can re-run a replay as a means to perform visual regression using + ./dragonruby mygame --replay last_replay.txt + * [Support] Added a --production command line option for running an + unpublished game as if it were published. + * [Support] We now offer bleeding edge builds on the "experimental" + channel on itch.io. If you want those, go to where you downloaded this + package, and look for the "experimental" download instead. These are + expected to be less stable and pushed as we need to test things out or + hotfix a bug that isn't ready to roll into an official release. If this is + your jam, though, go for it. + * [OSS] Thank you to StardragonEX, phaedryx, and pusewicz for contributing + to GTK documentation located at github.com/DragonRuby/dragonruby-game-toolkit-contrib. + * [Support] Enabling trace! on an object now outputs + all trace data to a file as opposed to just standard out. This will + make trace debugging significantly more useful to developers that + don't have a high-buffer-history terminal running. + * [Samples] Added platformer that deals with moving objects, jumping, and dealing with inertia. + * [Samples] Added sample showing how to render an isometric grid + (like Into The Breach). + * [Samples] Sample added showing how to use trace! debugging when you + are having trouble with why an exception is being thrown. + * [Samples] Many of the sample apps have had their methods reorganized + for consistency in reading. They also have comments added to them. + * [Experimental] If the game has been running for over a minute, and cannot + keep a stable 60 fps, the game's rendering will be skipped every other + frame to save process cycles (tick method will still execute as fast + as possible, but results from them method will only be rendered at 30 fps). + += release-20190912 = + + * [Bugfix] dragonruby-publish now correctly builds HTML5 packages when + there's a "'" character in the game's name (etc). + * [Bugfix] dragonruby-httpd now serves paths with spaces in them correctly. + * [Bugfix] dragonruby-publish now ignores __MACOSX directories (trash that + macOS tends to generate when you double-click a .zip file to unpack it + from the Finder). + += release-20190909 = + + * [Bugfix] HTML5 builds' "click-to-play" UI now scales the icon. + * [Bugfix] args.gtk.writefile(path, content [, write_to_game_dir = true]) + now writes file to the game folder as opposed to the root directory. + If you need to write to the root directory provide false as the third + parameter to write file. + * [Experimental] There is now a special file in mygame called mailbox.rb. + When `args.gtk.suppress_mailbox = false`, mailbox.rb will be checked for + changes at a high frequency (10 times a second). Because this an + experimental feature, the default value of `args.gtk.suppress_mailbox` is + TRUE. You can use this file as a means to communicate with GTK between process. + There is a sample app that shows how to use the mailbox.rb file. + * [Support] You can now have multiple mouse buttons! + args.inputs.mouse.click and .up will still be set if there was a mouse + button change for the current tick, but you should look at + args.inputs.mouse.button_left (or _middle or _right or _x1 or _x2) as a + bool to see if it's currently pressed down. More than one might be pressed, + and more than one button might have changed in the one .click you'll get. + The same is true for .up + * [Support] You can now see the mouse wheel (or your trackpad + pretending to be a mouse wheel when you stroke it appropriately)! + All wheel input since the last tick is added together. You can check if + args.inputs.mouse.wheel isn't nil to decide if the wheel has moved for + this tick. + ```ruby + m = args.inputs.mouse + if m.wheel + puts("The wheel moved #{m.wheel.x} left/right and #{m.wheel.y} up/down") + end + ``` + += release-20190829 = + + * [Community] Discord link: http://discord.dragonruby.org and Youtube link: http://youtube.dragonruby.org. + * [Support] Plugging in an unrecognized controller will now make DragonRuby pop up a + UI to let the user configure it. During this time your game will receive no + tick calls and so will be effectively paused. It resumes once the user has configured the + controller or abandoned it. This is a work in progress, but we already + think it's pretty cool. + * [Experimental] [Advanced] You can start up ./dragonruby by passing in --eval file_path. The file passed in will + execute on startup. Additionally you can pass in --eval file_path --no-tick which will + exit ./dragonruby immediately after loading all source, and running the file passed to eval. + * [Experimental] [Advanced] `on_tick` callback has been added to $gtk. This can be used to run some form of automation. + For example, you can run the Dueling Starships sample app with the following inside of repl.rb. + ```ruby + # Reset the game and sent the RNG seed to 100 + $gtk.reset 100 + + # Remove any existing callbacks. + $gtk.on_ticks.clear + + # One second into the game, hold down the right directional + # on controller one. + $gtk.on_tick 60 do + $gtk.args.inputs.controller_one.key_held.right = true + end + + # On frame 80, release the right directional, and + # hold down R1. + $gtk.on_tick 80 do + $gtk.args.inputs.controller_one.key_held.right = false + $gtk.args.inputs.controller_one.key_up.right = true + $gtk.args.inputs.controller_one.key_held.r1 = true + end + ``` + There is a full blown automation script under samples/00_beginner_ruby_primer/automation.rb + ./samples/00_beginner_ruby_primer $ ../../dragonruby . --eval app/automation.rb + * [Support] Exceptions `game_state_*.txt files are now written to an exceptions directory + instead of cluttering up the root. + * [Support] Launching dragonruby with --fullscreen on the command line makes the game fullscreen. + * [Support] Launching dragonruby-publish from any directory with no command line arguments + and without a "mygame" folder in the current working directory will + cause it to look for "mygame" in the same directory as dragonruby-publish + itself. Which is to say: it'll work in the default configuration if you + double-click it from the macOS Finder. + * [Samples] New sample app called 02_collisions_02_moving_objects has been added. + * [Support] Previous console messages are subdued if unhandled exception is resolved. + This removes visual confusion if another exception is thrown (easier to determine what the current exception is). + * [Support] Added api documentation about thruthy_keys for keyboards and controllers. + * [API] [Experimental] Hashes now quack like render primitives. For example some_hash[:x] is the same as + some_hash.x and can be interchangeable with some_array.x (this is a work in progress + so there may be some geometric/collision apis that aren't covered). + * [Support] Usability of Console and it's interaction with repl.rb is SIGNIFICANTLY better. + Take a look at the new repl.rb file under mygame/repl.rb to see an interactive + crash course of Ruby that shows how to use variables, functions, conditionals, + loops, and arrays. Special thanks to @raulrita (https://www.twitch.tv/raulrita) + for live streaming and bringing light to these enhancements. + * [Support] `puts` statements that begin with "====" are rendered as teal in the Console. This + provides a visual seperation that will help with print line debugging. + * [OSS] The DragonRuby Game Toolkit Console has been open sourced under an MIT license. You can find it + here: https://github.com/DragonRuby/dragonruby-game-toolkit-contrib + += release-20190823 = + + * [Support] The mygame directory now contains a documentation folder that provides high level + APIs for the engine (it's a work in progress, but a good starting point). + * [Samples] A sample app called "hash primitives" has been added that shows how you can use a Hash + to render a primitive. This will make specifying sprite's advanced properites much easier. + * [Samples] The sprite limits sample app shows how you can ducktype a class and render it as a sprite. + Doing this is a tiny bit more work, but there is a huge perfomance benefit. + * [Bugfix] Internal limits total textures/render targets/fonts is removed. + * [BREAKING] args.dragon has been deprecated, you must now use the new property args.gtk. + * [BREAKING] args.game has been deprecated, you must now use the new property args.state. + * [BREAKING] args.gtk.root has been removed and all attributes have been moved to args.gtk. + * [BREAKING] args.gtk(.root).mouse_focus has been removed, use args.inputs.mouse.has_focus. + * [BREAKING] args.gtk(.root).keyboard_focus has been removed, use args.inputs.keyboard.has_focus. + * [BREAKING] $dragon has been removed, you must now use $gtk. + * [BREAKING] $view has been removed and functionality has been moved to $gtk, use that variable instead. + += release-20190816 = + + * [Bugfix] HTML5: Fixed crash on startup on Chrome if the game's icon isn't tiny. + * [Samples] Added sample app that shows how you can make a paint app (05_mouse_move_paint_app). + * [Samples] Added sample app that shows how you can make a tile editor (05_mouse_move_tile_editor). + * [Samples] Added LOWREZ Jam 2019 game that Amir built: Return of Serenity (99_sample_game_return_of_serenity). + * [Samples] Added Metroidvania sample app which is a great starting point for the "Metroidvania Month" Jam on Itch.io. + * [Support] :ordinal_indicator (º) character added to args.inputs.keyboard. It can be used to bring up the GTK console. + += release-20190813 = + + * [Samples] Added sample app that shows off controller input. + * [API] `args.inputs.keyboard.key_down.delete is now available and works in the GTK console. + * [Bugfix] Requiring a ruby file that doesn't exist will return an error instead of crashing. + * [Support] Console coloring added for exceptions. + * [Support] When running trace!, if an exception occurs. The last method invoked will + be provided in the details. + += release-20190806 = + + * [Bugfix] Having $gtk.reset at the bottom of your main.rb will not cause a recursive require + of all files. + * [Support] In the event of an exception, the source code will not be searched if the + search term is less than three characters. + * [Samples] A Roguelike starting point sample app has been added. + * [Support] If a primitive rendering fails, the primitive type will now be included + with the exception. + * [API] The property `args.outputs.static_background_color = [0, 0, 0]` is now exposed. This allows + you to control the color of the letterbox around your game. Some of the sample apps have been + updated to use this property (for example Dueling Starships). + += release-20190803 = + + * [Bugfix] `gtk_attr` now included the `inputs` attr_accessor. + * [Website] The initial version of http://fiddle.dragonruby.org has + been created. Try it out. + * [Support] Eight new beginner sample apps have been added + which breakdown the tech demo into consumable pieces (with comments). + * [Support] The two introductory Ruby primers now contain links + to video tutorials that you can use to follow along. + * [Support] A game/sample app called Snakemoji has been added. Game + development should be fun and about experimentation. This sample app + was certainly built in the spirit of this. A big thank you to + Anton K. (ai Doge) for this contribution. + * [Support] Added $gtk.trace!(an_object) which will detailed print execution + information. This should make it much easier to track down exceptions. + * [Support] All source files are auto reloaded on save or when + $gtk.reset is executed. + * [Support] § (:section_sign) can now be used to open up the + DragonRuby GTK Console. + += release-20190731 = + + * [Bugfix] Fixed bug in dragon ruby console returning evalued value. + * [Support] Updated collisions sample app with comments. + * [Support] Added comments for sprite animation sample app. + * [Support] dragonruby-publish will look for your game in the + "mygame" directory if not otherwise specified on the command line. + * [Support] dragonruby-publish now puts packaged builds in + "builds" directory instead of ".dragonruby/builds" so you can actually + find them. + += release-20190712 = + + * [Bugfix] dragonruby-publish ignores .git/.hg/.svn directories. + += release-20190711 = + + * [Bugfix] Bug fix for Easing module rename. Sorry about that! + * [Typo] Fixed grammar in sprite limits explanation. + += release-20190709 = + + * [Bugfix] Text scaling now works on Windows. Resize the window and your + fonts should stay crisp now! + * [Bugfix] dragonruby-httpd on Windows now has proper EXE metadata. + * [API] Commonly used properties on `args` are now accessible as top + level methods: + + ``` + def tick args + args.(solids|sprites|lines|labels|borders| + click|click_at|mouse|controller_one|controller_two| + keyboard) + end + ``` + * [BREAKING] `args.game` has been deprecated is favor of the better + named property `args.state`. + * [Support] DragonRuby GTK Console now accepts input + immediately (specifically while it's animating). + * [API] Class macro `gtk_args` has been added. Instead of having to + do: + + ``` + class Game + attr_accessor :args, :grid, :state, :inputs, :outputs, :gtk, :passes, + :current_scene, :other_custom_attrs + + def tick + end + end + + def tick args + $game.args = args + $game.grid = args.grid + $game.state = args.state + $game.outputs = args.outputs + $game.gtk = args.gtk + $game.passes = args.passes + $game.tick + end + ``` + + You can now do: + + ``` + class Game + gtk_args + attr_accessor :current_scene, :other_custom_attrs + + def tick + end + end + + def tick args + $game.args = args + $game.tick + end + ``` + * [API] [BREAKING] The globally accessible GTK Runtime has been renamed `$dragon` to + `$gtk` (you can still use $dragon for now, but it is strongly + recommended that you update your usages to $gtk). + * [API] A new entity type has been introduced and is accessible via `args.state.new_entity_strict` + the usage of StrictEntity over OpenEntity (`args.state.new_entity`) yield significantly faster + property access. The downside is that `args.state.new_entity_strict` requires + you to define all properties upfront within the implicit block. You will recieve + an exception if you attempt to access a property that hasn't be + pre-defined. For usage info and preformance differences, take a look at the Sprite Limit + sample app. + * [Support] Exception messages have been significantly improved. Hashes and Type .to_s + information is well formatted. "stack too deep" exceptions resolved. + * [API] [BREAKING] `args.outputs.PRIMITIVES +=` is no longer supported. Use `args.outputs.PRIMITIVES <<` + for all concatenation of primitives to the outputs collection (trying to pick between += and << was confusing, + we settled on <<). + * [Support] Framerate warnings wait 10 seconds before calculating the moving average. If your + framerates begin to drop, consider using `args.state.new_entity_static` for game structures that have been + fleshed out. + * [Performance] Rendering of primitives is can support over twice as many sprites at 60 fps (see Sprit Limits + sample app for demonstration). + * [Support] Headless testing has been added. Take a look at the Basic Gorillas sample app to see + how headless testing can help you dial into frame-by-frame problems within your game. + * [Samples] The "Controller Analog Usage Advanced Sprites" sample + app now has a helper function with named parameters to create the + sprite. For example, if you needed to flip a sprite vertically, + you'd have to do the following: + + ``` + args.outputs.sprites << [0, 0, 100, 100, + 'fighter.png', + 0, + 255, 255, 255, 255, + 0, 0, -1, -1, false, true] + ``` + + With the helper method, you can write the following: + + ``` + args.outputs.sprites << create_sprite(x: 0, y: 0, w: 100, h: 100, vflip: true) + ``` + + We're still chewing on the API above before it get's integrated + into GTK proper. + * [API] "Superscript Two" can be used to bring up the DragonRuby + Console. People with international keyboards (which don't have a ~ + key) can now use the `superscript_two` key (which is in the same + place as the ~). + * [API] All keyboard attributes now have a `!` variant which will + clear the key if the value was true. Example: + + ``` + args.inputs.keyboard.key_down.up # returns true + args.inputs.keyboard.key_down.up # still returns true + ``` + + VS + + ``` + args.inputs.keyboard.key_down.up! # returns true + args.inputs.keyboard.key_down.up # now returns false for the rest of this tick + ``` + * [Samples] Added a sample app called "Sprite Animation and Keyboard + Input" that shows how to do repeating and one-time sprite + animations driven by keyboard input. + * [Samples] All sample apps have the MIT License added to them (to + remove any ambiguity with regards to if you can use them for + commercial purposes). + * [HTML5] Games published to HTML5 now has a loading progress bar. + * [Samples] Added sample app called "Coordinate Systems" that show + the two coordinate systems supported by the GTK. + * [API] `args.inputs.mouse` now respects `args.dragon.origin_center!`. + * [Samples] Added two sample apps called "Collision" and "Collision + and Entities" that show how to perform collision detection with + draw primitives and `args.state.new_entity`. + * [Samples] Added an interactive sample app called "Beginner Ruby + Primer" that does a better job of introducing you to the language. + * [Samples] [Video] Amir facilitated a workshop at a local user + group. The source code for the workshop is located in a sample app + called "NDDNUG Workshop". The video was recorded and you can use + that to code along: (https://www.youtube.com/watch?v=S3CFce1arC8). + * [Bugfix] Windows and Mac icon generation in dragonruby-publish was + broken in the previous build. Sorry! Fixed now. + * [Bugfix] DragonRuby binaries are now codesigned on macOS. We are still + working on signing games packaged by dragonruby-publish, but stay tuned! + += release-20190604 = + + * [BREAKING] `perc_to_zero` and `perc_to_one` have been deprecated + and now throw and informative exception that will help you fix the + problem. Moving forward, you'll need to use the new `lerp` APIs + (details further down). + * [BREAKING] `intersects_rect?` now takes in an optional parameter + called `tolerance`. This parameter's default is set to `0.1`. The + original tolerance value was set to `0`. So if you still want a + tolerance of `0`, you'll need to update your `intersect_rect?` + calls to: `intersects_rect?(other_rect, 0)`. + * [IMPORTANT] [API] [Video] A Quake-style Heads Up Display (HUD), + has been added. You can access this console by pressing the "~" + key. This console can be used to experiment with Ruby, print debug + logging, and diagnose errors. Here is Ryan showing the console in + action: (https://www.youtube.com/watch?v=hHgV11F2ZPY) + * [IMPORTANT] [API] [Video] Added the ability to create replays so you can + do visual regression of your game. Here is this record/replay capability in + action: (https://www.youtube.com/watch?v=ev0UMVBe-fY) + ``` + args.dragon.start_recording(RNG_SEED) + args.dragon.stop_recording(filename) + args.dragon.start_replay(filename) + args.dragon.stop_replay + ``` + + You can record your game on startup using the following command + (it'll automatically save when you close the game): + + ``` + ./dragonruby(.exe) --record + ``` + * [API] Rendering primitives now support `float` values for position + and size. + * [API] [Sample] [Video] Lerping/Easing APIs have been added. There is a + sample app that shows all the things you can do with them. For + philosophical motivations as to why the APIs look the way they do + (and just simply a fantastic presentation), watch this + presentation given by Squirrel Esierloh *NO REALLY YOU SHOULD WATCH THIS PRESENTATION*: + "Math for Game Programmers: Fast and Funky 1D Nonlinear Transformations" (https://www.youtube.com/watch?v=mr5xkf6zSzk) + * [API] CTAG files now ship with releases. This will let you quickly + see the APIs available in DragonRuby GTK. + * [Sample] Added a "prerequisite" sample app that gives you a crash + course of Ruby the programming language. + * [Sample] The composition of primitives in DragonRuby GTK are + incredibly flexible. A sample app called "Fluid Primitives" has + been beed added to show this flexibility. + * [MacOS] [Linux] [Windows] If your screen resolution is below 720p, + the game will start at a smaller (but still aspect-correct) resolution. + * [Sample] Sample added showing `intersects_rect?` collision + tollerances as a topdown level (similar to what's + in Zelda for the NES). + += release-20190516 = + + * [Community] A big thank you to Nick Culbertson (@mobypixel) for + creating the art and music for these games. + * [HTML5] [Demo] [Experimental] Experimental deployment to HTML5 added. You can + play the HTML5 version here: (https://dragonruby.itch.io/flappydragon). + * [HTML5] `dragonruby-httpd` binary added so that you can test your + HTML5 game locally. + * [RaspberryPI] [Video] [Experimental] Experimental support added to Rasberry Pi. You can + play DragonRuby GTK games, _develop them and publish them_. + Here's a video of Ryan playing Flappy Dragon on his DIY Arcade + Machine: (https://www.youtube.com/watch?v=DCvFMKsvG-Q). + * [API] Mouse movement can now be tracked: `args,inputs.mouse.position`. + * [API] Experimental API added for taking screenshots: `args.dragon.root.take_screenshot = true`. + * [API] Analog support for controllers added: `args.inputs.controller_one.left_analog_x_perc`. + * [API] [Experimental] Mouse and keyboard focus status are available for use: + ``` + args.dragon.root.mouse_focus + args.dragon.root.keyboard_focus + ``` + * [Samples] Added sample app that shows how to use mouse position. + * [Samples] Flappy Dragon reference implementation significantly polished. + * [Samples] [Demo] Added BASIC Gorillas reference implementation. Playable + here: (https://dragonruby.itch.io/basicgorillas). + * [Samples] Added sample app that shows analog controller support in + action. The sample also shows how to manipulate a sprite's + center of rotation, crop, and horizontal/vertical orientation: + ``` + [ + x, y, w, h, # decimal values (scales implicitly) + path, # string + angle, # in degrees + a, # alpha (0 to 255) + r, g, b, # color *saturation* + source_x, source_y, source_w, source_h, (crop, must be given in original pixels) + flip_h, flip_v, (boolean values) + rotation_anchor_x, rotation_anchor_y (decimal between 0 and 1/ratio from bottom left) + ] + ``` + * [API] Added `args.dragon.calcstringbox(str, size_enum, font)` + function that can be used to calculate the `rect` for a string. + += release-20190427 = + + * [Packaging] [Windows] Packaging Windows builds of your game now sets a proper icon and + metadata in the final .exe. + * [API] Keyboard input is improved dramatically, so you can get reasonable + key state in your game. + * [Packaging] You can now specify an application icon in your game_metadata.txt + file instead of possibly having a second copy of the same file in + metadata/icon.png. + * [Sample] [Video] [Demo] Added a full-blown game/reference implementation called + `flappy_dragon` under the `samples` directory. Here's a quick five + second video showing the game in action: + (https://www.youtube.com/watch?v=ZQYRlVA1ru0). You can play it + yourself here: (https://dragonruby.itch.io/flappydragon). + += release-20190422 = + + * [Packaging] Version.txt file added that shows the GIT commit that was + deployed. + * [Packaging] Fixed duplicate "Copying x to y ..." message in `dragon-publish`. + * [Packaging] Automatically package DragonRuby's font and logo so copying the + DragonRuby GTK binary around doesn't cause errors. + * [Linux] [Windows] Line rendering fixes on Linux and Windows. + * [Windows] Bug fix to `golf_with_musical_note` (sound tech demo) sample app where sound + wouldn't play on Windows. + * [Windows] README.txt file formatting updated so that it looks correct in + `notepad.exe`. + * [Packaging] Default icon added to `mygame`. + * [Samples] Reworked `doomwipe` (render targets tech demo) sample app so that + it's less eratic before the effect is revealed. + * [Windows] Fixed bug where rendering would stop on Windows if the screen was + resized. + * [Size] Deleted files that don't need to be packaged with DragonRuby GTK. + += release-20190419 = + + * [IMPORTANT] First release. Hot off the presses. Ready to take over the world! As + soon as we fix that one bug. Oh and that one. Oh and that one too. + * [Community] The community forum is here: (https://dragonruby.itch.io/dragonruby-gtk/community). + * [Community] Or you can join the Slack channel at: (http://slack.rubymotion.com/). + * [Community] Ryan (@icculus) and Amir (@amirrajan) are both on + Twitter and always happy to help anyone that reaches out to them. diff --git a/deploy_template/CHEATSHEET.txt b/deploy_template/CHEATSHEET.txt new file mode 100644 index 0000000..e35f38f --- /dev/null +++ b/deploy_template/CHEATSHEET.txt @@ -0,0 +1,232 @@ +# DragonRuby Game Toolkit Cheatsheet + +A detailed documentation listing is located at +`mygame/documentation`. For real world sample usages of GTK's APIs, +take a look at the `samples` directory. The sample apps are ordered by +beginner to advanced. So look at them in order to get the most out of +them. + +# Minimum Code for Game + +The minimum amount of code required for a game is a file named +`mygame/main.rb` with the following: + +```ruby +def tick args + args.outputs.labels << [640, 360, "Hello World"] +end +``` + +# Args: `args` + +All of GTK's functionality hangs off of `args`. + +## Args' Outputs: `args.outputs` + +All render `primitive`s are accessible under `args.outputs`. + +### Labels: `args.outputs.labels` + +`args.outputs.labels` is used to render labels. + +Labels are how you display text. This code will go directly inside of the `def tick args` method. + +- **Minimum Code** + +```ruby +# X Y TEXT +args.outputs.labels << [640, 360, "I am a black label."] +``` + +- **A Colored Label** + +```ruby +# A colored label +# X Y TEXT, RED GREEN BLUE ALPHA +args.outputs.labels << [640, 360, "I am a redish label.", 255, 128, 128, 255] +``` + +- **Extended Capabilities** + +```ruby +# A colored label +# X Y TEXT SIZE ALIGNMENT RED GREEN BLUE ALPHA FONT FILE +args.outputs.labels << [640, 360, "Hello world", 0, 1, 0, 0, 0, 255, "fonts/coolfont.ttf"] +``` + +A `SIZE` of `0` represents "default size". A negative value will decrease +the label size. A positive value will increase the label's size. + +An `ALIGNMENT` of `0` represents "left aligned". `1` represents "center aligned". `2` represents "right aligned". + +- **Named Parameters/Additional Metadata** + +You can add additional metadata about your game within a label, which requires you to use a `Hash` instead. + +```ruby +args.outputs.labels << { + x: 200, + y: 550, + text: "dragonruby", + size_enum: 2, + alignment_enum: 1, + r: 155, + g: 50, + b: 50, + a: 255, + font: "fonts/manaspc.ttf", + # You can add any properties you like (this will be ignored/won't cause errors) + game_data_one: "Something", + game_data_two: { + value_1: "value", + value_2: "value two", + a_number: 15 + } +} +``` + +- **Getting The Size of a Piece of Text: `args.gtk.calcstringbox`** + +```ruby +def tick args + # local variable + # TEXT SIZE_ENUM FONT + text_size = args.gtk.args.calcstringbox("some string", 0, "font.ttf") + + # print size of a label to screen using Ruby's string formatting capabilities: #{} + args.outputs.labels << [640, 360, "the label's size is: #{text_size.x}, #{text_size.y}, #{text_size.w}, #{text_size.h}"] +end +``` + +### Sprites: `args.outputs.sprites` + +`args.outputs.sprites` is used to render sprites. + +All primitives in GTK have the same considerations as detailed in the Labels section. + +- **Minimum Code** + +```ruby +# X Y WIDTH HEIGHT PATH +args.outputs.sprites << [100, 100, 32, 64, "sprites/player.png"] +``` + +- **Extended Capabilities** + +```ruby +args.outputs.sprites << [ + 100, # X + 100, # Y + 32, # W + 64, # H + "player.png", # PATH + 0, # ANGLE + 255, # ALPHA + 0, # RED_SATURATION + 255, # GREEN_SATURATION + 0, # BLUE_SATURATION + 0, # TILE_X + 0, # TILE_Y + 32, # TILE_W + 32 # TILE_H +] +``` + +- **Named Parameters/Additional Metadata** + +Here are all the properties for `sprites`: + +```ruby +args.outputs.sprites << { + x: 100, + y: 100, + w: 100, + h: 100, + path: "sprites/player.png", + angle: 0, + a, 255 + r: 255, + g: 255, + b: 255, + tile_x: 0, + tile_y: 0, + tile_w: -1, + tile_h: -1, + flip_vertically: false, + flip_horizontally: false, + angle_anchor_x: 0.5, + angle_anchor_y: 1.0 +} +``` + +### Sounds `args.outputs.sounds` + +Sounds that end `.wav` will play once: +`args.outputs.sounds << 'something.wav'`. + +Sounds that end `.ogg` is considered background music and will loop: +`args.outputs.sounds << 'background_music.ogg'`. + +If you want to play a `.ogg` once as if it were a sound effect, you can do: +`args.gtk.queue_sound 'some-ogg.ogg'` + +## Args' State `args.state` + +`args.state` is where you can put all of your game state. + +- **Tick Count `args.state.tick_count`** + +GTK has a fixed time step of 60 frames per second. You can access the current `tick_count` using `args.state.tick_count`: + +```ruby +def tick args + # Render a label showing the current tick_count. + args.outputs.labels << [360, 640, args.state.tick_count] +end +``` + +- **Working With "Open" Game State** + +`args.state` is a "open" data structure that allows you to define +properties of any structure. You don't need to define any kind of +`class`. + +To initialize your game state, use the `||=` operator. Any value on +the right side of `||=` will only be assigned `once`. + +To assign a value every frame, just use the `=` operator, but _make +sure_ you've initalized a default value. + +```ruby +def tick args + # initialize your game state ONCE + args.player.x ||= 0 + args.player.y ||= 0 + args.player.hp ||= 100 + + # increment the x position of the character by one every frame + args.player.x += 1 + + # Render a sprite with a label above the sprite + + # X Y W H PATH + args.outputs.sprites << [args.player.x, args.player.y, 32, 32 "player.png"] + + # X Y TEXT + args.outputs.labels << [args.player.x, args.player.y - 50, args.player.hp] +end +``` + +## Args' Inputs: `args.inputs` + +# Numeric + +## Numeric#elapsed? + +Returns true if a numeric value plus an offset represents a point +in time that has elapsed. This is related to args.state.tick_count. + +``` +args.state.attacked_at ||= args.state.tick_count +puts args.state.attacked_at.elapsed? 60 # will print false after one second. +``` diff --git a/deploy_template/README.txt b/deploy_template/README.txt index 80050db..2d00b4c 100644 --- a/deploy_template/README.txt +++ b/deploy_template/README.txt @@ -1,407 +1,369 @@ -Join the Discord: http://discord.dragonruby.org -Community Forums: https://dragonruby.itch.io/dragonruby-gtk/community -Free Training Course: http://dragonruby.school +* Introduction -Welcome! + Hello world! Do the things in this README file and you'll be well on your way to + building video games! -Here's just a little push to get you started. +* Join the community! -If you want to write a game, it's no different than writing any other -program for any other framework: there are a few simple rules that might be -new to you, but more or less programming is programming no matter what you -are building. + Those who use DragonRuby are called Dragon Riders. This identity is + incredibly important to us. When someone asks you: -Did you not know that? Did you think you couldn't write a game because you're -a "web guy" or you're writing Java at a desk job? Stop letting people tell -you that you can't, because you already have everything you need. + > What game engine do you use? -Here, we're going to be programming in a language called "Ruby." In the -interest of full disclosure, I wrote the C parts of this toolkit and Ruby -looks a little strange to me, but I'm going to walk you through the basics -because we're all learning together, and if you mostly think of yourself as -someone that writes C (or C++, C#, Objective-C), PHP, or Java, then you're -only a step behind me right now. + You can proudly reply with: -Here's the most important thing you should know: Ruby lets you do some -complicated things really easily, and you can learn that stuff later. I'm -going to show you one or two cool tricks, but that's all. - -Do you know what an if statement is? A for-loop? An array? That's all you'll -need to start. + > I am a Dragon Rider. -If you don't know how to program, no worries! Watching these two videos will -help tremendously: +** Subscribe to the News Letter -- https://s3.amazonaws.com/s3.dragonruby.org/dragonruby-gtk-primer.mp4 -- https://s3.amazonaws.com/s3.dragonruby.org/dragonruby-gtk-intermediate.mp4 + The News Letter will keep you in the loop with regards to + http://dragonrubydispatch.com/ -Did you watch the videos? Great! +** Join the Discord -Ok, here are few rules with regards to game development with GTK: - -- Your game is all going to happen under one function... -- ...that runs 60 times a second... -- ...and has to tell the computer what to draw each time. - -That's an entire video game in one run-on sentence. - -Here's that function. You're going to want to put this in mygame/app/main.rb, -because that's where we'll look for it by default. Load it up in your favorite -text editor. - - -def tick args - args.outputs.labels << [ 580, 400, 'Hello World!' ] -end - -Now run "dragonruby" ...did you get a window with "Hello World!" written in -it? Good, you're officially a game developer! - -mygame/app/main.rb, is where the Ruby source code is located. This looks a little strange, so -I'll break it down line by line. In Ruby, a '#' character starts a single-line -comment, so I'll talk about this inline. - -# This "def"ines a function, named "tick," which takes a single argument -# named "args". DragonRuby looks for this function and calls it every -# frame, 60 times a second. "args" is a magic structure with lots of -# information in it. You can set variables in there for your own game state, -# and every frame it will updated if keys are pressed, joysticks moved, -# mice clicked, etc. -def tick args - - # One of the things in "args" is the "outputs" object that your game uses - # to draw things. Afraid of rendering APIs? No problem. In DragonRuby, - # you use arrays to draw things and we figure out the details. - # If you want to draw text on the screen, you give it an array (the thing - # in the [ brackets ]), with an X and Y coordinate and the text to draw. - # The "<<" thing says "append this array onto the list of them at - # args.outputs.labels) - args.outputs.labels << [ 580, 400, 'Hello World!' ] -end - -# Once your "tick" function finishes, we look at all the arrays you made and -# figure out how to draw it. You don't need to know about graphics APIs. -# You're just setting up some arrays! DragonRuby clears out these arrays -# every frame, so you just need to add what you need _right now_ each time. - -Now let's spice this up a little. - -We're going to add some graphics. Each 2D image in DragonRuby is called a -"sprite," and to use them, you just make sure they exist in a reasonable file -format (png, jpg, gif, bmp, etc) and specify them by filename. The first time -you use one, DragonRuby will load it and keep it in video memory for fast -access in the future. If you use a filename that doesn't exist, you get a fun -checkerboard pattern! - -There's a "dragonruby.png" file included, just to get you started. Let's have -it draw every frame with our text: - -def tick args - args.outputs.labels << [ 580, 400, 'Hello World!' ] - args.outputs.sprites << [ 576, 100, 128, 101, 'dragonruby.png' ] -end - -(ProTip: you don't have to restart DragonRuby to test your changes; when you -save main.rb, DragonRuby will notice and reload your program.) - -That ".sprites" line says "add a sprite to the list of sprites we're drawing, -and draw it at position (576, 100) at a size of 128x101 pixels, and you can -find the image to draw at dragonruby.png. - -Quick note about coordinates: (0, 0) is the bottom left corner of the screen, -and positive numbers go up and to the right. This is more "geometrically -correct," even if it's not how you remember doing 2D graphics, but we chose -this for a simpler reason: when you're making Super Mario Brothers and you -want Mario to jump, you should be able to add to Mario's y position as he -goes up and subtract as he falls. It makes things easier to understand. - -Also: your game screen is _always_ 1280x720 pixels. If you resize the window, -we will scale and letterbox everything appropriately, so you never have to -worry about different resolutions. - -Ok, now we have an image on the screen, let's animate it: - -def tick args - args.state.rotation ||= 0 - args.outputs.labels << [ 580, 400, 'Hello World!' ] - args.outputs.sprites << [ 576, 100, 128, 101, 'dragonruby.png', args.state.rotation ] - args.state.rotation -= 1 -end + Amir (one of the creators of DragonRuby) is always available to help + you out. So take the time to join the community Discord. The invite linke is located at: -Now you can see that this function is getting called a lot! - -Here's a fun Ruby thing: "args.state.rotation ||= 0" is shorthand for "if -args.state.rotation isn't initialized, set it to zero." It's a nice way to -embed your initialization code right next to where you need the variable. - -args.state is a place you can hang your own data and have it survive past the -life of the function call. In this case, the current rotation of our sprite, -which is happily spinning at 60 frames per second. If you don't specify -rotation (or alpha, or color modulation, or a source rectangle, etc), -DragonRuby picks a reasonable default, and the array is ordered by the most -likely things you need to tell us: position, size, name. - -One thing we decided to do in DragonRuby is not make you worry about delta -time: your function runs at 60 frames per second (about 16 milliseconds) and -that's that. Having to worry about framerate is something massive triple-AAA -games do, but for fun little 2D games? You'd have to work really hard to not -hit 60fps. All your drawing is happening on a GPU designed to run Fortnite -quickly; it can definitely handle this. - -Since we didn't make you worry about delta time, you can just move the -rotation by 1 every time and it works without you having to keep track of -time and math. Want it to move faster? Subtract 2. - -Now, let's move that image around. - -def tick args - args.state.rotation ||= 0 - args.state.x ||= 576 - args.state.y ||= 100 - - if args.inputs.mouse.click - args.state.x = args.inputs.mouse.click.point.x - 64 - args.state.y = args.inputs.mouse.click.point.y - 50 - end - - args.outputs.labels << [ 580, 400, 'Hello World!' ] - args.outputs.sprites << [ args.state.x, args.state.y, 128, 101, 'dragonruby.png', args.state.rotation ] - - args.state.rotation -= 1 -end - -Everywhere you click your mouse, the image moves there. We set a default -location for it with args.state.x ||= 576, and then we change those variables -when we see the mouse button in action. You can get at the keyboard and game -controllers in similar ways. + DragonRuby Discord: http://discord.dragonruby.org -There is a lot more you can do with DragonRuby, but now you've already got -just about everything you need to make a simple game. After all, even the -most fancy games are just creating objects and moving them around. Experiment -a little. Add a few more things and have them interact in small ways. Want -something to go away? Just don't add it to args.output anymore. +** Introduce Yourself on the Forums -If you want to get a good idea of what's available to you, please check out -the "samples" directory: in there, the "tech_demo" directory is a good dumping -ground of features, and many of the others are little starter games. Just -go to the samples directory, find the sample you want to run, and double click -"dragonruby" within the sample folder. + Take a moment to introducing yourself on the community forum: -There is also a lot more you _can't_ do with DragonRuby, at least not yet. -We are excited about the potential of this, so we wanted to get it in your -hands right away. We intend to add a bunch of features and we would love -feedback on what needs work and what you want that isn't there. + Stickied Community Post: https://itch.io/t/526689/dragonruby-gtk-discord-server-created-join-it-dammit -But now, it's time to show your friends and family that you're a real game -developer! Let's package up what we have and let them play it! + This provides community members a registry of everyone using + DragonRuby. Itch.io holds a lot of game jams, and it'd be awesome if + Dragon Riders had a central place to find each other. -Let's just give it a few bits of information. Point your text editor at -mygame/metadata/game_metadata.txt and make it look like this: - -devid=bob -devtitle=Bob The Game Developer -gameid=mygame -gametitle=My Game -version=0.1 - -(obviously you should change it if your name isn't Bob.) - -See that other program? dragonruby-publish? Let's use that to package up your -game. Run this from the command line: - -./dragonruby-publish --only-package mygame +* Determine how you want to start learning based on your experience level! -(if you're on Windows, don't put the "./" on the front. That's a Mac and -Linux thing.) + Follows are sections pertaining to your experience level as a + programer and experience level with coding in a dynamic language. -This should spit out packaged versions of your game for Windows, Linux and -macOS that you can hand out to friends and family however you like. They -just have to download and double-click it! +** If you have zero experience with programming. -But if you want to get _really_ fancy: Set up a free account on -https://itch.io/, with the same login as you specified for "devid" in -game_metadata.txt, and a product with the gameid. Set a price for it. And -then run... - -./dragonruby-publish mygame + If you have no programing experience at all. You'll want to take the + time to see what DragonRuby is like before jumping in to code. Watch + the following videos in order (each one is only ~20 minutes long). -...and DragonRuby will package _and publish_ your game to itch.io! Tell your -friends to go to your game's very own webpage and buy it! + Don't attempt to code anything shown in the video yet, just watch them to + get familiar with the language and how games are built. -If you make changes to your game, just re-run dragonruby-publish and it'll -update the downloads for you. + 1. Beginner Introduction to Ruby: https://www.youtube.com/watch?v=ixw7TJhU08E + 2. Intermediate Introduction to Ruby Syntax: https://www.youtube.com/watch?v=HG-XRZ5Ppgc + 3. Intermediate Introduction to Arrays in Ruby: https://www.youtube.com/watch?v=N72sEYFRqfo -And that's all! We hope you find DragonRuby useful, and more importantly we -hope you have fun playing around with it. Please check out -https://dragonruby.itch.io/dragonruby-gtk as we add new features and improve -this toolkit based on your feedback! + Once you have watched all the videos. Then (and only then) go back + through the videos and follow along. Here are the locations for the + samples: -BORING LEGAL STUFF AND CREDIT WHERE CREDIT IS DUE: -(if you don't read software licenses, you're done with this README now. IT IS -STRONGLY RECOMMENDED THAT YOU GO THROUGH ALL THE SAMPLE APPS!!) + 1. Beginner Introduction to Ruby: samples/00_beginner_ruby_primer + 2. Intermediate Tutorials: samples/00_intermediate_ruby_primer -DragonRuby uses the following open source libraries! +** If you do not know Ruby, but have experience with C# (Unity) or GML (GameMaker) -- mRuby: https://mruby.org/ + Those engines rot your brain. Forget the concepts that the forced you + to learn. Game development is so much simpler than what they make you + do. Please, try your best to set aside the concepts those engines + teach (we promise our approach to game development is much much easier). -Copyright (c) 2019 mruby developers + Watch these videos to get familiar with the Ruby language and + programming environment (they are ~20 min each so it'll be quick): -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: + 1. Beginner Introduction to Ruby: https://www.youtube.com/watch?v=ixw7TJhU08E + 2. Intermediate Introduction to Ruby Syntax: https://www.youtube.com/watch?v=HG-XRZ5Ppgc + 3. Intermediate Introduction to Arrays in Ruby: https://www.youtube.com/watch?v=N72sEYFRqfo -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + You may also want to try this free course provided at http://dragonruby.school. -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. + After you've watch the videos, you'll be ready to go to the next section. +** You are a dev that is familiar with a dynamically typed language (Ruby, Lua, Python, or JavaScript). -- Simple Directmedia Layer: https://www.libsdl.org/ +*** STEP 1: Work through this Hello World tutorial -Simple DirectMedia Layer -Copyright (C) 1997-2019 Sam Lantinga <[email protected]> + This tutorial is provided by Ryan C Gordon (check out his wikipedia + page). We call him "The Juggernaut": -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. + Welcome! -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: + Here's just a little push to get you started if you're new to programming or + game development. -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. + If you want to write a game, it's no different than writing any other + program for any other framework: there are a few simple rules that might be + new to you, but more or less programming is programming no matter what you + are building. + Did you not know that? Did you think you couldn't write a game because you're + a "web guy" or you're writing Java at a desk job? Stop letting people tell + you that you can't, because you already have everything you need. -- stb_vorbis, stb_image, stb_truetype: https://github.com/nothings/stb/ + Here, we're going to be programming in a language called "Ruby." In the + interest of full disclosure, I (Ryan "The Juggernaut" Gordon) wrote the C + parts of this toolkit and Ruby looks a little strange to me (Amir Rajan wrote the + Ruby parts), but I'm going to walk you through the basics because we're all + learning together, and if you mostly think of yourself as someone that writes + C (or C++, C#, Objective-C), PHP, or Java, then you're only a step behind me right now. -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. + Here's the most important thing you should know: Ruby lets you do some + complicated things really easily, and you can learn that stuff later. I'm + going to show you one or two cool tricks, but that's all. + Do you know what an if statement is? A for-loop? An array? That's all you'll + need to start. -- lodepng: https://lodev.org/lodepng/ + Ok, here are few rules with regards to game development with GTK: -Copyright (c) 2005-2018 Lode Vandevenne + - Your game is all going to happen under one function... + - ...that runs 60 times a second... + - ...and has to tell the computer what to draw each time. -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. + That's an entire video game in one run-on sentence. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: + Here's that function. You're going to want to put this in mygame/app/main.rb, + because that's where we'll look for it by default. Load it up in your favorite + text editor. - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. + #+begin_src ruby + def tick args + args.outputs.labels << [ 580, 400, 'Hello World!' ] + end + #+end_src - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. + Now run `dragonruby` ...did you get a window with "Hello World!" written in + it? Good, you're officially a game developer! - 3. This notice may not be removed or altered from any source - distribution. + `mygame/app/main.rb`, is where the Ruby source code is located. This looks a little strange, so + I'll break it down line by line. In Ruby, a '#' character starts a single-line + comment, so I'll talk about this inline. + #+begin_src ruby -- miniz: https://github.com/richgel999/miniz + # This "def"ines a function, named "tick," which takes a single argument + # named "args". DragonRuby looks for this function and calls it every + # frame, 60 times a second. "args" is a magic structure with lots of + # information in it. You can set variables in there for your own game state, + # and every frame it will updated if keys are pressed, joysticks moved, + # mice clicked, etc. + def tick args -Copyright 2013-2014 RAD Game Tools and Valve Software -Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + # One of the things in "args" is the "outputs" object that your game uses + # to draw things. Afraid of rendering APIs? No problem. In DragonRuby, + # you use arrays to draw things and we figure out the details. + # If you want to draw text on the screen, you give it an array (the thing + # in the [ brackets ]), with an X and Y coordinate and the text to draw. + # The "<<" thing says "append this array onto the list of them at + # args.outputs.labels) + args.outputs.labels << [ 580, 400, 'Hello World!' ] + end -All Rights Reserved. + #+end_src -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: + Once your `tick` function finishes, we look at all the arrays you made and + figure out how to draw it. You don't need to know about graphics APIs. + You're just setting up some arrays! DragonRuby clears out these arrays + every frame, so you just need to add what you need _right now_ each time. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + Now let's spice this up a little. -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. + We're going to add some graphics. Each 2D image in DragonRuby is called a + "sprite," and to use them, you just make sure they exist in a reasonable file + format (png, jpg, gif, bmp, etc) and specify them by filename. The first time + you use one, DragonRuby will load it and keep it in video memory for fast + access in the future. If you use a filename that doesn't exist, you get a fun + checkerboard pattern! + There's a "dragonruby.png" file included, just to get you started. Let's have + it draw every frame with our text: -- MojoAL: https://hg.icculus.org/icculus/mojoAL/ + #+begin_src ruby - Copyright (c) 2018 Ryan C. Gordon and others. + def tick args + args.outputs.labels << [ 580, 400, 'Hello World!' ] + args.outputs.sprites << [ 576, 100, 128, 101, 'dragonruby.png' ] + end - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from - the use of this software. + #+end_src - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: + (ProTip: you don't have to restart DragonRuby to test your changes; when you + save main.rb, DragonRuby will notice and reload your program.) - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software in a - product, an acknowledgment in the product documentation would be - appreciated but is not required. + That `.sprites` line says "add a sprite to the list of sprites we're drawing, + and draw it at position (576, 100) at a size of 128x101 pixels". You can + find the image to draw at dragonruby.png. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. + Quick note about coordinates: (0, 0) is the bottom left corner of the screen, + and positive numbers go up and to the right. This is more "geometrically + correct," even if it's not how you remember doing 2D graphics, but we chose + this for a simpler reason: when you're making Super Mario Brothers and you + want Mario to jump, you should be able to add to Mario's y position as he + goes up and subtract as he falls. It makes things easier to understand. - 3. This notice may not be removed or altered from any source distribution. + Also: your game screen is _always_ 1280x720 pixels. If you resize the window, + we will scale and letterbox everything appropriately, so you never have to + worry about different resolutions. - Ryan C. Gordon <[email protected]> + Ok, now we have an image on the screen, let's animate it: + #+begin_src ruby -- PhysicsFS: https://icculus.org/physfs/ + def tick args + args.state.rotation ||= 0 + args.outputs.labels << [ 580, 400, 'Hello World!' ] + args.outputs.sprites << [ 576, 100, 128, 101, 'dragonruby.png', args.state.rotation ] + args.state.rotation -= 1 + end - Copyright (c) 2001-2019 Ryan C. Gordon and others. + #+end_src - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from - the use of this software. + Now you can see that this function is getting called a lot! - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: + Here's a fun Ruby thing: `args.state.rotation ||= 0` is shorthand for "if + args.state.rotation isn't initialized, set it to zero." It's a nice way to + embed your initialization code right next to where you need the variable. - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software in a - product, an acknowledgment in the product documentation would be - appreciated but is not required. + `args.state` is a place you can hang your own data and have it survive past the + life of the function call. In this case, the current rotation of our sprite, + which is happily spinning at 60 frames per second. If you don't specify + rotation (or alpha, or color modulation, or a source rectangle, etc), + DragonRuby picks a reasonable default, and the array is ordered by the most + likely things you need to tell us: position, size, name. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. + One thing we decided to do in DragonRuby is not make you worry about delta + time: your function runs at 60 frames per second (about 16 milliseconds) and + that's that. Having to worry about framerate is something massive triple-AAA + games do, but for fun little 2D games? You'd have to work really hard to not + hit 60fps. All your drawing is happening on a GPU designed to run Fortnite + quickly; it can definitely handle this. - 3. This notice may not be removed or altered from any source distribution. + Since we didn't make you worry about delta time, you can just move the + rotation by 1 every time and it works without you having to keep track of + time and math. Want it to move faster? Subtract 2. - Ryan C. Gordon <[email protected]> + Now, let's move that image around. + + #+begin_src ruby + + def tick args + args.state.rotation ||= 0 + args.state.x ||= 576 + args.state.y ||= 100 + + if args.inputs.mouse.click + args.state.x = args.inputs.mouse.click.point.x - 64 + args.state.y = args.inputs.mouse.click.point.y - 50 + end + + args.outputs.labels << [ 580, 400, 'Hello World!' ] + args.outputs.sprites << [ args.state.x, args.state.y, 128, 101, 'dragonruby.png', args.state.rotation ] + + args.state.rotation -= 1 + end + + #+end_src + + Everywhere you click your mouse, the image moves there. We set a default + location for it with args.state.x ||= 576, and then we change those variables + when we see the mouse button in action. You can get at the keyboard and game + controllers in similar ways. + + There is a lot more you can do with DragonRuby, but now you've already got + just about everything you need to make a simple game. After all, even the + most fancy games are just creating objects and moving them around. Experiment + a little. Add a few more things and have them interact in small ways. Want + something to go away? Just don't add it to args.output anymore. + +*** STEP 2: Read the CHEATSHEET.txt + + Go to the file CHEATSHEET.txt and skim through it quickly to get a + feel for some of the other APIs you have access to. If you need even + more details you'll find them at `mygame/documentation`. + +*** STEP 3: Run each sample app in order and read the code. + + The sample apps located in the `sample` directory are ordered by + increasing complexity. Run each one of them and read through the + code. Play around by changing values and see how they change the game. + +*** STEP 4: Editor integration. + + There is a file called `vim-ctags` and `emacs-ctags`. The data in + these files are standard output provided by Exuberent CTAGS. Most + editors have a "ctags plugin" so just search for that plugin for your + editor and point it to these files. + +*** STEP 5: Get in the habit of reading the CHANGELOG + + We are constantly adding new features to the engine. Be sure to read + the changelog with every release. + +* How to publish your game. + + Once you've built your game, you're all set to deploy! Good luck in + your game dev journey and if you get stuck, come to the Discord + channel! + +** STEP 1: Create a new Game in Itch.io. + + Log into Itch.io and go to https://itch.io/game/new. + + - Title: Give your game a Title. This value represents your `gametitle`. + - Project URL: Set your project url. This value represents your `gameid`. + - Classification: Keep this as Game. + - Kind of Project: Select HTML from the drop down list. Dont worry, + the HTML project type _aslo supports binary downloads_. + - Uploads: Skip this section for now. + - Embed Options: Set the dropdown value to "Click to launch in fullscreen". + DO NOT use the Embed in page option. iFrames are not reliable with + regards to capturing input. + + You can fill out all the other options later. + +** STEP 2: Go to mygame/metadata/metadata.txt and update it. + + Point your text editor at mygame/metadata/game_metadata.txt and + make it look like this: (Remove the `#` at the beginning of each line). + + #+begin_src text + devid=bob + devtitle=Bob The Game Developer + gameid=mygame + gametitle=My Game + version=0.1 + #+end_src + + The `devid` property is the username you use to log into Itch.io. + The `devtitle` is your name or company name (it can contain spaces). + The `gameid` is the Project URL value (see details in STEP 1). + The `gametitle` is the name of your game (it can contain spaces). + The `version` can be any `major.minor` number format. + +** STEP 3: Build your game for distribution. + + Open up the terminal and run this from the command line: + + #+begin_src sh + ./dragonruby-publish --only-package mygame + #+end_src + + (if you're on Windows, don't put the "./" on the front. That's a Mac and + Linux thing.) + + A directory called `./build` will be created that contains your + binaries. You can upload this to Itch.io manually. For the HTML + version of your game after you upload it. Check the checkbox labeled + "This file will be played in the browser". + + For subsequent updates you can use an automated deployment to Itch.io: + + #+begin_src sh + ./dragonruby-publish mygame + #+end_src + + DragonRuby will package _and publish_ your game to itch.io! Tell your + 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. diff --git a/deploy_template/mygame/app/tests.rb b/deploy_template/mygame/app/tests.rb index 1dc452a..a60c8be 100644 --- a/deploy_template/mygame/app/tests.rb +++ b/deploy_template/mygame/app/tests.rb @@ -2,10 +2,12 @@ # You can put some quick verification tests here, any method # that starts with the `test_` will be run when you save this file. -# here is an example test and game +# Here is an example test and game + +# To run the test: ./dragonruby mygame --eval tests.rb --no-tick class MySuperHappyFunGame - gtk_args + attr_gtk def tick outputs.solids << [100, 100, 300, 300] @@ -21,4 +23,7 @@ def test_universe args, assert puts "test_universe completed successfully" end +puts "running tests" +$gtk.reset 100 +$gtk.log_level = :off $gtk.tests.start diff --git a/deploy_template/mygame/documentation/02-labels.md b/deploy_template/mygame/documentation/02-labels.md index 2adf5e1..ea2642e 100644 --- a/deploy_template/mygame/documentation/02-labels.md +++ b/deploy_template/mygame/documentation/02-labels.md @@ -84,16 +84,16 @@ using the helper method (providing all the parameters). ```ruby args.outputs.labels << { - x: 200, - y: 550, - text: "dragonruby", - size: 2, - alignment: 1, - r: 155, - g: 50, - b: 50, - a: 255, - font: "fonts/manaspc.ttf" + x: 200, + y: 550, + text: "dragonruby", + size_enum: 2, + alignment_enum: 1, + r: 155, + g: 50, + b: 50, + a: 255, + font: "fonts/manaspc.ttf" } ``` diff --git a/deploy_template/mygame/documentation/05-sprites.md b/deploy_template/mygame/documentation/05-sprites.md index 3648fc4..c80b46c 100644 --- a/deploy_template/mygame/documentation/05-sprites.md +++ b/deploy_template/mygame/documentation/05-sprites.md @@ -168,7 +168,7 @@ args.outputs.sprites << [ 100, # X false, # FLIP_HORIZONTALLY false, # FLIP_VERTICALLY 0.5, # ANGLE_ANCHOR_X - 1.0] # ANGLE_ANCHOR_Y + 1.0] # ANCHOR_Y ``` ## Hash (Advanced) @@ -185,7 +185,7 @@ args.outputs.sprites << { h: 100, path: "sprites/player.png", angle: 0, - a: 255, + a, 255 r: 255, g: 255, b: 255, diff --git a/deploy_template/mygame/documentation/99-todo.md b/deploy_template/mygame/documentation/99-todo.md new file mode 100644 index 0000000..39c542a --- /dev/null +++ b/deploy_template/mygame/documentation/99-todo.md @@ -0,0 +1,89 @@ +# Documentation That Needs to be Organized + +## Class macro gtk_args + +Here's how you can use the `gtk_args` class method: + +```ruby +class Game + gtk_args + attr_accessor :current_scene, :other_custom_attrs + + def tick + end +end + +$game = Game.new + +def tick args + $game.args = args + $game.tick +end +``` + +The code above is the similar to: + +```ruby +class Game + attr_accessor :args, :grid, :state, :inputs, :outputs, :gtk, :passes, + :current_scene, :other_custom_attrs + + def tick + end +end + +$game = Game.new + +def tick args + $game.args = args + $game.grid = args.grid + $game.state = args.state + $game.outputs = args.outputs + $game.gtk = args.gtk + $game.passes = args.passes + $game.tick +end +``` + +## Monkey patching the runtime + +You're on your own if you do this :grimacing: + +```ruby +module GTK + class Runtime + alias_method :__original_tick_core__, :tick_core unless Runtime.instance_methods.include?(:__original_tick_core__) + + def tick_core + __original_tick_core__ + $top_level.oh @args + $top_level.god @args + $top_level.why @args + end + end +end + +def tick args +end + +def oh args +end + +def god args +end + +def why args +end +``` + +## MP3's to Wav converstion script: + +```ruby +`ls .`.each_line.to_a.map do |l| + l = l.strip + if l.end_with? "mp3" + `ffmpeg -i #{l} -acodec pcm_s16le -ar 44100 prep-#{l.split(".")[0]}.wav` + `ffmpeg -y -i prep-#{l.split(".")[0]}.wav -f wav -bitexact -acodec pcm_s16le -ar 44100 -ac 1 #{l.split(".")[0]}.wav` + end +end +``` diff --git a/deploy_template/mygame/metadata/game_metadata.txt b/deploy_template/mygame/metadata/game_metadata.txt new file mode 100644 index 0000000..1b03500 --- /dev/null +++ b/deploy_template/mygame/metadata/game_metadata.txt @@ -0,0 +1,6 @@ +#devid=myname +#devtitle=My Name +#gameid=mygame +#gametitle=My Game +#version=0.1 +#icon=metadata/icon.png diff --git a/deploy_template/mygame/metadata/icon.png b/deploy_template/mygame/metadata/icon.png Binary files differnew file mode 100644 index 0000000..e20e8c2 --- /dev/null +++ b/deploy_template/mygame/metadata/icon.png diff --git a/deploy_template/mygame/sprites/circle-black.png b/deploy_template/mygame/sprites/circle-black.png Binary files differnew file mode 100644 index 0000000..c98e23d --- /dev/null +++ b/deploy_template/mygame/sprites/circle-black.png diff --git a/deploy_template/mygame/sprites/circle-blue.png b/deploy_template/mygame/sprites/circle-blue.png Binary files differnew file mode 100644 index 0000000..1726d2a --- /dev/null +++ b/deploy_template/mygame/sprites/circle-blue.png diff --git a/deploy_template/mygame/sprites/circle-gray.png b/deploy_template/mygame/sprites/circle-gray.png Binary files differnew file mode 100644 index 0000000..960f191 --- /dev/null +++ b/deploy_template/mygame/sprites/circle-gray.png diff --git a/deploy_template/mygame/sprites/circle-green.png b/deploy_template/mygame/sprites/circle-green.png Binary files differnew file mode 100644 index 0000000..43cf7ee --- /dev/null +++ b/deploy_template/mygame/sprites/circle-green.png diff --git a/deploy_template/mygame/sprites/circle-indigo.png b/deploy_template/mygame/sprites/circle-indigo.png Binary files differnew file mode 100644 index 0000000..598e240 --- /dev/null +++ b/deploy_template/mygame/sprites/circle-indigo.png diff --git a/deploy_template/mygame/sprites/circle-orange.png b/deploy_template/mygame/sprites/circle-orange.png Binary files differnew file mode 100644 index 0000000..5604a42 --- /dev/null +++ b/deploy_template/mygame/sprites/circle-orange.png diff --git a/deploy_template/mygame/sprites/circle-red.png b/deploy_template/mygame/sprites/circle-red.png Binary files differnew file mode 100644 index 0000000..7f17ca6 --- /dev/null +++ b/deploy_template/mygame/sprites/circle-red.png diff --git a/deploy_template/mygame/sprites/circle-violet.png b/deploy_template/mygame/sprites/circle-violet.png Binary files differnew file mode 100644 index 0000000..681d210 --- /dev/null +++ b/deploy_template/mygame/sprites/circle-violet.png diff --git a/deploy_template/mygame/sprites/circle-white.png b/deploy_template/mygame/sprites/circle-white.png Binary files differnew file mode 100644 index 0000000..bd32155 --- /dev/null +++ b/deploy_template/mygame/sprites/circle-white.png diff --git a/deploy_template/mygame/sprites/circle-yellow.png b/deploy_template/mygame/sprites/circle-yellow.png Binary files differnew file mode 100644 index 0000000..94992eb --- /dev/null +++ b/deploy_template/mygame/sprites/circle-yellow.png diff --git a/deploy_template/mygame/sprites/hexagon-black.png b/deploy_template/mygame/sprites/hexagon-black.png Binary files differnew file mode 100644 index 0000000..f50c872 --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-black.png diff --git a/deploy_template/mygame/sprites/hexagon-blue.png b/deploy_template/mygame/sprites/hexagon-blue.png Binary files differnew file mode 100644 index 0000000..1696bae --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-blue.png diff --git a/deploy_template/mygame/sprites/hexagon-gray.png b/deploy_template/mygame/sprites/hexagon-gray.png Binary files differnew file mode 100644 index 0000000..e8c4c5a --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-gray.png diff --git a/deploy_template/mygame/sprites/hexagon-green.png b/deploy_template/mygame/sprites/hexagon-green.png Binary files differnew file mode 100644 index 0000000..a700602 --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-green.png diff --git a/deploy_template/mygame/sprites/hexagon-indigo.png b/deploy_template/mygame/sprites/hexagon-indigo.png Binary files differnew file mode 100644 index 0000000..15f6f4f --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-indigo.png diff --git a/deploy_template/mygame/sprites/hexagon-orange.png b/deploy_template/mygame/sprites/hexagon-orange.png Binary files differnew file mode 100644 index 0000000..1587173 --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-orange.png diff --git a/deploy_template/mygame/sprites/hexagon-red.png b/deploy_template/mygame/sprites/hexagon-red.png Binary files differnew file mode 100644 index 0000000..d442f39 --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-red.png diff --git a/deploy_template/mygame/sprites/hexagon-violet.png b/deploy_template/mygame/sprites/hexagon-violet.png Binary files differnew file mode 100644 index 0000000..3be5731 --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-violet.png diff --git a/deploy_template/mygame/sprites/hexagon-white.png b/deploy_template/mygame/sprites/hexagon-white.png Binary files differnew file mode 100644 index 0000000..c1ad970 --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-white.png diff --git a/deploy_template/mygame/sprites/hexagon-yellow.png b/deploy_template/mygame/sprites/hexagon-yellow.png Binary files differnew file mode 100644 index 0000000..63f5f34 --- /dev/null +++ b/deploy_template/mygame/sprites/hexagon-yellow.png diff --git a/deploy_template/mygame/sprites/isometric-black.png b/deploy_template/mygame/sprites/isometric-black.png Binary files differnew file mode 100644 index 0000000..fa9e463 --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-black.png diff --git a/deploy_template/mygame/sprites/isometric-blue.png b/deploy_template/mygame/sprites/isometric-blue.png Binary files differnew file mode 100644 index 0000000..a3d8524 --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-blue.png diff --git a/deploy_template/mygame/sprites/isometric-gray.png b/deploy_template/mygame/sprites/isometric-gray.png Binary files differnew file mode 100644 index 0000000..85dcc1d --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-gray.png diff --git a/deploy_template/mygame/sprites/isometric-green.png b/deploy_template/mygame/sprites/isometric-green.png Binary files differnew file mode 100644 index 0000000..ec2773e --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-green.png diff --git a/deploy_template/mygame/sprites/isometric-indigo.png b/deploy_template/mygame/sprites/isometric-indigo.png Binary files differnew file mode 100644 index 0000000..e6be50c --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-indigo.png diff --git a/deploy_template/mygame/sprites/isometric-orange.png b/deploy_template/mygame/sprites/isometric-orange.png Binary files differnew file mode 100644 index 0000000..154d81c --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-orange.png diff --git a/deploy_template/mygame/sprites/isometric-red.png b/deploy_template/mygame/sprites/isometric-red.png Binary files differnew file mode 100644 index 0000000..3448c4d --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-red.png diff --git a/deploy_template/mygame/sprites/isometric-violet.png b/deploy_template/mygame/sprites/isometric-violet.png Binary files differnew file mode 100644 index 0000000..f09bf21 --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-violet.png diff --git a/deploy_template/mygame/sprites/isometric-white.png b/deploy_template/mygame/sprites/isometric-white.png Binary files differnew file mode 100644 index 0000000..a45793d --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-white.png diff --git a/deploy_template/mygame/sprites/isometric-yellow.png b/deploy_template/mygame/sprites/isometric-yellow.png Binary files differnew file mode 100644 index 0000000..9be20c7 --- /dev/null +++ b/deploy_template/mygame/sprites/isometric-yellow.png diff --git a/deploy_template/mygame/sprites/square-black.png b/deploy_template/mygame/sprites/square-black.png Binary files differnew file mode 100644 index 0000000..cea7bd7 --- /dev/null +++ b/deploy_template/mygame/sprites/square-black.png diff --git a/deploy_template/mygame/sprites/square-blue.png b/deploy_template/mygame/sprites/square-blue.png Binary files differnew file mode 100644 index 0000000..b840849 --- /dev/null +++ b/deploy_template/mygame/sprites/square-blue.png diff --git a/deploy_template/mygame/sprites/square-gray.png b/deploy_template/mygame/sprites/square-gray.png Binary files differnew file mode 100644 index 0000000..2142b30 --- /dev/null +++ b/deploy_template/mygame/sprites/square-gray.png diff --git a/deploy_template/mygame/sprites/square-green.png b/deploy_template/mygame/sprites/square-green.png Binary files differnew file mode 100644 index 0000000..5ef7f75 --- /dev/null +++ b/deploy_template/mygame/sprites/square-green.png diff --git a/deploy_template/mygame/sprites/square-indigo.png b/deploy_template/mygame/sprites/square-indigo.png Binary files differnew file mode 100644 index 0000000..2384108 --- /dev/null +++ b/deploy_template/mygame/sprites/square-indigo.png diff --git a/deploy_template/mygame/sprites/square-orange.png b/deploy_template/mygame/sprites/square-orange.png Binary files differnew file mode 100644 index 0000000..bb1eee7 --- /dev/null +++ b/deploy_template/mygame/sprites/square-orange.png diff --git a/deploy_template/mygame/sprites/square-red.png b/deploy_template/mygame/sprites/square-red.png Binary files differnew file mode 100644 index 0000000..3ed5f13 --- /dev/null +++ b/deploy_template/mygame/sprites/square-red.png diff --git a/deploy_template/mygame/sprites/square-violet.png b/deploy_template/mygame/sprites/square-violet.png Binary files differnew file mode 100644 index 0000000..333540c --- /dev/null +++ b/deploy_template/mygame/sprites/square-violet.png diff --git a/deploy_template/mygame/sprites/square-white.png b/deploy_template/mygame/sprites/square-white.png Binary files differnew file mode 100644 index 0000000..378c565 --- /dev/null +++ b/deploy_template/mygame/sprites/square-white.png diff --git a/deploy_template/mygame/sprites/square-yellow.png b/deploy_template/mygame/sprites/square-yellow.png Binary files differnew file mode 100644 index 0000000..0edeeec --- /dev/null +++ b/deploy_template/mygame/sprites/square-yellow.png diff --git a/deploy_template/open-source-licenses.txt b/deploy_template/open-source-licenses.txt new file mode 100644 index 0000000..70abecd --- /dev/null +++ b/deploy_template/open-source-licenses.txt @@ -0,0 +1,215 @@ +BORING LEGAL STUFF AND CREDIT WHERE CREDIT IS DUE: +(if you don't read software licenses, you can skip this text file.) + +DragonRuby uses the following open source libraries! + +- mRuby: https://mruby.org/ + +Copyright (c) 2019 mruby developers + +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. + + +- Simple Directmedia Layer: https://www.libsdl.org/ + +Simple DirectMedia Layer +Copyright (C) 1997-2019 Sam Lantinga <[email protected]> + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + + +- stb_vorbis, stb_image, stb_truetype: https://github.com/nothings/stb/ + +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. + + +- lodepng: https://lodev.org/lodepng/ + +Copyright (c) 2005-2018 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + +- miniz: https://github.com/richgel999/miniz + +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + +All Rights Reserved. + +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. + + +- MojoAL: https://hg.icculus.org/icculus/mojoAL/ + + Copyright (c) 2018 Ryan C. Gordon and others. + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from + the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + + Ryan C. Gordon <[email protected]> + + +- PhysicsFS: https://icculus.org/physfs/ + + Copyright (c) 2001-2019 Ryan C. Gordon and others. + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from + the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + + Ryan C. Gordon <[email protected]> + +- Yxml: https://dev.yorhel.nl/yxml + +Copyright (c) 2013-2014 Yoran Heling + +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. + + +- cJSON: https://github.com/DaveGamble/cJSON + +Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + +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/dragon/args.rb b/dragon/args.rb index d058b1d..487e843 100644 --- a/dragon/args.rb +++ b/dragon/args.rb @@ -4,147 +4,11 @@ # args.rb has been released under MIT (*only this file*). module GTK - class Grid - include Serialize - SCREEN_Y_DIRECTION = -1.0 - - attr_accessor :bottom, :left, :right, :top, - :rect, :origin_x, :origin_y, :center_x, :center_y, - :name - - def initialize - origin_bottom_left! - end - - def __print_origin_help ascii_art - log_once [:grid_ascii_art, @name], <<-S -The origin has been set to :#{@name}. - -#{ascii_art} - -You can change the origin using any of -the following methods: - - grid.origin_bottom_left! - grid.origin_center! - -Example: - - def tick args - args.grid.origin_bottom_left! - end -S - end - - def transform_rect x, y, w, h - new_x = transform_x x - new_y = transform_y y - new_w = w.to_f - new_h = h * SCREEN_Y_DIRECTION - - if new_w < 0 - new_x = new_x + new_w - new_w = new_w.abs - end - - if new_h < 0 - new_y = new_y + new_h - new_h = new_h.abs - end - - [new_x, new_y, new_w, new_h] - end - - def transform_x x - @origin_x + x - end - - def untransform_x x - x - @origin_x - end - - def untransform_y y - @origin_y + y * SCREEN_Y_DIRECTION - end - - def transform_y y - @origin_y + y * SCREEN_Y_DIRECTION - end - - def transform_angle angle - (360 - angle).to_i - end - - def origin_bottom_left! - return if @name == :bottom_left - @name = :bottom_left - @origin_x = 0.0 - @origin_y = 720.0 - @left = 0.0 - @right = 1280.0 - @top = 720.0 - @bottom = 0.0 - @center_x = 640.0 - @center_y = 360.0 - @rect = [@left, @bottom, 1280.0, 720.0] - __print_origin_help <<ASCII -(0, 720) +-------------------+ (1280, 720) - | | - | (640, 360) | - | + | - | | - | | -(0, 0) +-------------------+ (1280, 0) -ASCII - end - - def origin_center! - return if @name == :center - @name = :center - @origin_x = 640.0 - @origin_y = 360.0 - @left = -640.0 - @right = 640.0 - @top = 360.0 - @bottom = -360.0 - @center_x = 0.0 - @center_y = 0.0 - @rect = [@left, @bottom, 1280.0, 720.0] - __print_origin_help <<ASCII -(-640, 360) +-------------------+ ( 640, 360) - | | - | (0, 0) | - | + | - | | - | | -(-640, -360) +-------------------+ (-640, 360) -ASCII - end - - def w - 1280.0 - end - - def w_half - 640.0 - end - - def h - 720.0 - end - - def h_half - 360.0 - end - end -end - -module GTK class Args include ArgsDeprecated attr_accessor :inputs, :outputs, :passes, :runtime, - :grid, :recording + :grid, :recording, :geometry def initialize runtime, recording @inputs = Inputs.new @@ -154,27 +18,28 @@ module GTK @state.tick_count = -1 @runtime = runtime @recording = recording - @grid = Grid.new + @grid = Grid.new runtime.ffi_draw @render_targets = {} @all_tests = [] + @geometry = GTK::Geometry + end + + def tick_count + @state.tick_count + end + + def tick_count= value + @state.tick_count = value end def gtk @runtime end - # @return [OpenEntity] returns `OpenEntity` object that allows for storing state between ticks - # @example Storing a value in a state - # args.state.player_score = 0 - # args.state.current_time = Time.now def state @state end - # @param value [OpenEntity] the new state object to use - # @return [OpenEntity] the new state object - # @example Overwriting the state object - # args.state = OpenEntity.new def state= value @state = value end @@ -206,7 +71,6 @@ module GTK @render_targets[name] end - # @return [GTK::OutputsArray] the array of solids to render during current tick def solids @outputs.solids end @@ -215,7 +79,6 @@ module GTK @outputs.static_solids end - # @return [GTK::OutputsArray] the array of sprites to render during current tick def sprites @outputs.sprites end @@ -224,7 +87,6 @@ module GTK @outputs.static_sprites end - # @return [GTK::OutputsArray] the array of labels to render during current tick def labels @outputs.labels end @@ -233,7 +95,6 @@ module GTK @outputs.static_labels end - # @return [GTK::OutputsArray] the array of lines to render during current tick def lines @outputs.lines end @@ -242,7 +103,6 @@ module GTK @outputs.static_lines end - # @return [GTK::OutputsArray] the array of borders to render during current tick def borders @outputs.borders end @@ -272,7 +132,7 @@ module GTK def click_at return nil unless @inputs.mouse.click - @inputs.mouse.click.created_at + @inpust.mouse.click.created_a end def mouse diff --git a/dragon/assert.rb b/dragon/assert.rb new file mode 100644 index 0000000..47d39ef --- /dev/null +++ b/dragon/assert.rb @@ -0,0 +1,51 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# assert.rb has been released under MIT (*only this file*). + +module GTK + class Assert + attr :assertion_performed + + def ok! + @assertion_performed = true + end + + def true! value, message = nil + @assertion_performed = true + if !value + message = "#{value} was not truthy.\n#{message}" + raise "#{message}" + end + nil + end + + def false! value, message = nil + @assertion_performed = true + if value + message = "#{value} was not falsey.\n#{message}" + raise message + end + nil + end + + def equal! actual, expected, message = nil + @assertion_performed = true + if actual != expected + actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip + message = "actual: #{actual_string} did not equal expected: #{expected}.\n#{message}" + raise message + end + nil + end + + def nil! value, message = nil + @assertion_performed = true + if !value.nil? + message = "#{value} was supposed to be nil, but wasn't.\n#{message}" + raise message + end + nil + end + end +end diff --git a/dragon/attr_gtk.rb b/dragon/attr_gtk.rb new file mode 100644 index 0000000..cf0b9f0 --- /dev/null +++ b/dragon/attr_gtk.rb @@ -0,0 +1,40 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# attr_gtk.rb has been released under MIT (*only this file*). + +module AttrGTK + attr_accessor :args + + def keyboard + args.inputs.keyboard + end + + def grid + args.grid + end + + def state + args.state + end + + def inputs + args.inputs + end + + def outputs + args.outputs + end + + def gtk + args.gtk + end + + def passes + args.passes + end + + def geometry + args.geometry + end +end diff --git a/dragon/console.rb b/dragon/console.rb index 4008eb4..d58f507 100644 --- a/dragon/console.rb +++ b/dragon/console.rb @@ -202,7 +202,7 @@ S addtext "* toast :#{id}" puts "* TOAST: :#{id}" messages.each do |message| - lines = message.to_s.wrapped_lines(self.nconsole_text_width) + lines = message.to_s.wrapped_lines(self.console_text_width) dwim_duration += lines.length.seconds addtext "** #{message}" puts "** #{message}" @@ -262,6 +262,7 @@ S end def inputs_scroll_up_full? args + return false if @disabled args.inputs.keyboard.key_down.pageup || (args.inputs.keyboard.key_up.b && args.inputs.keyboard.key_up.control) end @@ -274,6 +275,7 @@ S end def inputs_scroll_up_half? args + return false if @disabled args.inputs.keyboard.ctrl_u end @@ -285,6 +287,7 @@ S end def inputs_scroll_down_full? args + return false if @disabled args.inputs.keyboard.key_down.pagedown || (args.inputs.keyboard.key_up.f && args.inputs.keyboard.key_up.control) end @@ -297,10 +300,12 @@ S end def inputs_scroll_down_half? args + return false if @disabled args.inputs.keyboard.ctrl_d end def inputs_clear_command? args + return false if @disabled args.inputs.keyboard.escape || args.inputs.keyboard.ctrl_g end diff --git a/dragon/docs.rb b/dragon/docs.rb new file mode 100644 index 0000000..b9d3dbf --- /dev/null +++ b/dragon/docs.rb @@ -0,0 +1,47 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# docs.rb has been released under MIT (*only this file*). + +module GTK + class Docs + def map_with_ys + puts <<-S +* Numeric#map_with_ys +Numeric#map_with_ys is a helper method that is useful for working with coordinates +or rows with columns. Here is an example usage. + +Assume you have a grid with 10 rows (xs) and 5 columns (ys). You can generate +an array of hashes with the following form: + +#+begin_src +[ + { x: 0, y: 0, some_data: "A" }, + { x: 0, y: 1, some_data: "A" }, + { x: 0, y: 2, some_data: "A" }, +... + { x: 9, y: 4, some_data: "A" }, +] +#+end_src + +Using the following code: + +#+begin_src ruby +array_of_hashes = 10.map_with_ys 5 do |x, y| + { x: x, y: y, some_data: "A" } +end + +Take a look at the "hexagon grid" sample app for a real world usage of +this method. +#+end_src +S + end + + def method_missing m, *args + puts <<-S +* DOCUMENTATION MISSING: +It looks like docs are missing for :#{m}. Let the @dragonborne know about it in the Discord channel: http://discord.dragonruby.org. +S + end + end +end diff --git a/dragon/geometry.rb b/dragon/geometry.rb new file mode 100644 index 0000000..77e07a3 --- /dev/null +++ b/dragon/geometry.rb @@ -0,0 +1,253 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# geometry.rb has been released under MIT (*only this file*). + +module GTK + module Geometry + def inside_rect? outer + Geometry.inside_rect? self, outer + end + + def intersect_rect? other, tolerance = 0.1 + Geometry.intersect_rect? self, other, tolerance + end + + def intersects_rect? *args + Geometry.intersects_rect?(*args) + end + + def scale_rect_extended percentage_x: percentage_x, + percentage_y: percentage_y, + anchor_x: anchor_x, + anchor_y: anchor_y + + Geometry.scale_rect_extended self, + percentage_x: percentage_x, + percentage_y: percentage_y, + anchor_x: anchor_x, + anchor_y: anchor_y + end + + def scale_rect percentage, *anchors + Geometry.scale_rect self, percentage, *anchors + end + + def angle_to other_point + Geometry.angle_to self, other_point + end + + def angle_from other_point + Geometry.angle_from self, other_point + end + + def point_inside_circle? circle_center_point, radius + Geometry.point_inside_circle? self, circle_center_point, radius + end + + def anchor_rect anchor_x, anchor_y + current_w = self.w + current_h = self.h + delta_x = -1 * (anchor_x * current_w) + delta_y = -1 * (anchor_y * current_h) + self.rect_shift(delta_x, delta_y) + end + + def angle_given_point other_point + raise ":angle_given_point has been deprecated use :angle_from instead." + end + + def self.shift_line line, x, y + if line.is_a?(Array) || line.is_a?(Hash) + new_line = line.dup + new_line.x += x + new_line.x2 += x + new_line.y += y + new_line.y2 += y + new_line + else + raise "shift_line for #{line} is not supported." + end + end + + def self.intersects_rect? *args + raise <<-S +intersects_rect? (with an \"s\") has been deprecated. +Use intersect_rect? instead (remove the \"s\"). + +* NOTE: +Ruby's naming convention is to *never* include the \"s\" for +interrogative method names (methods that end with a ?). It +doesn't sound grammatically correct, but that has been the +rule for a long time (and why intersects_rect? has been deprecated). + +S + end + + def self.line_y_intercept line + line.y - line_slope(line) * line.x + end + + def self.angle_between_lines line_one, line_two, replace_infinity: nil + m_line_one = line_slope line_one, replace_infinity: replace_infinity + m_line_two = line_slope line_two, replace_infinity: replace_infinity + Math.atan((m_line_one - m_line_two) / (1 + m_line_two * m_line_one)).to_degrees + end + + def self.line_slope line, replace_infinity: nil + (line.y2 - line.y).fdiv(line.x2 - line.x) + .replace_infinity(replace_infinity) + end + + def self.ray_test point, line + slope = (line.y2 - line.y).fdiv(line.x2 - line.x) + + if line.x > line.x2 + point_two, point_one = [point_one, point_two] + end + + r = ((line.x2 - line.x) * (point.y - line.y) - + (point.x - line.x) * (line.y2 - line.y)) + + if r == 0 + return :on + elsif r < 0 + return :right if slope >= 0 + return :left + elsif r > 0 + return :left if slope >= 0 + return :right + end + end + + def self.line_rect line + if line.x > line.x2 + x = line.x2 + y = line.y2 + x2 = line.x + y2 = line.y + else + x = line.x + y = line.y + x2 = line.x2 + y2 = line.y2 + end + + w = x2 - x + h = y2 - y + + { x: x, y: y, w: w, h: h } + end + + def self.line_intersect line_one, line_two + m1 = line_slope(line_one) + m2 = line_slope(line_two) + b1 = line_y_intercept(line_one) + b2 = line_y_intercept(line_two) + x = (b1 - b2) / (m2 - m1) + y = (-b2.fdiv(m2) + b1.fdiv(m1)).fdiv(1.fdiv(m1) - 1.fdiv(m2)) + [x, y] + end + + def self.intersect_rect? rect_one, rect_two, tolerance = 0.1 + return false if rect_one.right - tolerance < rect_two.left + tolerance + return false if rect_one.left + tolerance > rect_two.right - tolerance + return false if rect_one.top - tolerance < rect_two.bottom + tolerance + return false if rect_one.bottom + tolerance > rect_two.top - tolerance + return true + rescue Exception => e + raise e, ":intersect_rect? failed for rect_one: #{rect_one} rect_two: #{rect_two}." + end + + def self.to_square size, x, y, anchor_x = 0.5, anchor_y = nil + anchor_y ||= anchor_x + x = x.shift_left(size * anchor_x) + y = y.shift_down(size * anchor_y) + [x, y, size, size] + rescue Exception => e + raise e, ":to_square failed for size: #{size} x: #{x} y: #{y} anchor_x: #{anchor_x} anchor_y: #{anchor_y}." + end + + def self.distance point_one, point_two + Math.sqrt((point_two.x - point_one.x)**2 + (point_two.y - point_one.y)**2) + rescue Exception => e + raise e, ":distance failed for point_one: #{point_one} point_two #{point_two}." + end + + def self.angle_from start_point, end_point + d_y = end_point.y - start_point.y + d_x = end_point.x - start_point.x + Math::PI.+(Math.atan2(d_y, d_x)).to_degrees + rescue Exception => e + raise e, ":angle_from failed for start_point: #{start_point} end_point: #{end_point}." + end + + def self.angle_to start_point, end_point + angle_from end_point, start_point + rescue Exception => e + raise e, ":angle_to failed for start_point: #{start_point} end_point: #{end_point}." + end + + def self.point_inside_circle? point, circle_center_point, radius + (point.x - circle_center_point.x) ** 2 + (point.y - circle_center_point.y) ** 2 < radius ** 2 + rescue Exception => e + raise e, ":point_inside_circle? failed for point: #{point} circle_center_point: #{circle_center_point} radius: #{radius}" + end + + def self.inside_rect? inner_rect, outer_rect + inner_rect.x >= outer_rect.x && + inner_rect.right <= outer_rect.right && + inner_rect.y >= outer_rect.y && + inner_rect.top <= outer_rect.top + rescue Exception => e + raise e, ":inside_rect? failed for inner_rect: #{inner_rect} outer_rect: #{outer_rect}." + end + + def self.scale_rect_extended rect, + percentage_x: percentage_x, + percentage_y: percentage_y, + anchor_x: anchor_x, + anchor_y: anchor_y + anchor_x ||= 0.0 + anchor_y ||= 0.0 + percentage_x ||= 1.0 + percentage_y ||= 1.0 + new_w = rect.w * percentage_x + new_h = rect.h * percentage_y + new_x = rect.x + (rect.w - new_w) * anchor_x + new_y = rect.y + (rect.h - new_h) * anchor_y + if rect.is_a? Array + return [ + new_x, + new_y, + new_w, + new_h, + *rect[4..-1] + ] + elsif rect.is_a? Hash + return rect.merge(x: new_x, y: new_y, w: new_w, h: new_h) + else + rect.x = new_x + rect.y = new_y + rect.w = new_w + rect.h = new_h + return rect + end + rescue Exception => e + raise e, ":scale_rect_extended failed for rect: #{rect} percentage_x: #{percentage_x} percentage_y: #{percentage_y} anchors_x: #{anchor_x} anchor_y: #{anchor_y}." + end + + def self.scale_rect rect, percentage, *anchors + anchor_x, anchor_y = *anchors.flatten + anchor_x ||= 0 + anchor_y ||= anchor_x + Geometry.scale_rect_extended rect, + percentage_x: percentage, + percentage_y: percentage, + anchor_x: anchor_x, + anchor_y: anchor_y + rescue Exception => e + raise e, ":scale_rect failed for rect: #{rect} percentage: #{percentage} anchors [#{anchor_x} (x), #{anchor_y} (y)]." + end + end # module Geometry +end # module GTK diff --git a/dragon/help.rb b/dragon/help.rb new file mode 100644 index 0000000..aee60d8 --- /dev/null +++ b/dragon/help.rb @@ -0,0 +1,72 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# help.rb has been released under MIT (*only this file*). + +module GTK + class Help + def self.primitive_contract primitive_name + if primitive_name == :label + label_contract + elsif primitive_name == :solid + solid_border_contract + elsif primitive_name == :border + solid_border_contract + elsif primitive_name == :sprite + sprite_contract + else + help_text = "No contract found for primitive #{primitive_name}. The supported primitives are :label, :solid, :border, :sprite." + end + end + + def self.label_contract + <<-S +* :label (if :primitive_marker returns :label) +** :x, :y, :text +** :size_enum +default: 0 +negative value means smaller text +positive value means larger text +** :alignment_enum default: 0 +default: 0 +0: left aligned, 1: center aligned, 2: right aligned +** :r, :g, :b, :a +default: 0's for rgb and 255 for a +** :font +default nil +path to ttf file +S + end + + def self.solid_border_contract + <<-S +* :solid, :border (if :primitive_marker returns :solid or :border) +** :x, :y, :w, :h, :r, :g, :b, :a +S + end + + def self.label_contract + <<-S +* :line (if :primitive_marker returns :line) +** :x, :y, :x2, :y2, :r, :g, :b, :a +S + end + + def self.sprite_contract + <<-S +* :sprite (if :primitive_marker returns :sprite) +** :x, :y, :w, :h +** :angle, :angle_anchor_x, :angle_anchor_y +default for angle: 0 (0 to 360 degress) +default for angle_anchor_(x|y): 0 (decimal value between 0 and 1.0, 0.5 means center) +** :r, :g, :b, :a +** :tile_x, :tile_y +default: 0, x, y offset for sprite to crop at +** :tile_w, :tile_h +default: -1, width and height of crop (-1 means full width and height) +** :flip_horizontally, :flip_vertically +default: falsey value +S + end + end +end diff --git a/dragon/index.rb b/dragon/index.rb index 8a1460c..5d4639d 100644 --- a/dragon/index.rb +++ b/dragon/index.rb @@ -1 +1,23 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# help.rb has been released under MIT (*only this file*). + require 'app/dragon/console.rb' +require 'app/dragon/docs.rb' +require 'app/dragon/help.rb' +require 'app/dragon/log.rb' +require 'app/dragon/geometry.rb' +require 'app/dragon/attr_gtk.rb' +require 'app/dragon/attr_sprite.rb' +require 'app/dragon/string.rb' +require 'app/dragon/numeric.rb' +require 'app/dragon/directional_input_helper_methods.rb' +require 'app/dragon/inputs.rb' +require 'app/dragon/grid.rb' +require 'app/dragon/args.rb' +require 'app/dragon/console.rb' +require 'app/dragon/assert.rb' +require 'app/dragon/tests.rb' +require 'app/dragon/trace.rb' +require 'app/dragon/controller_config.rb' diff --git a/dragon/inputs.rb b/dragon/inputs.rb index 5d16091..07d1dc8 100644 --- a/dragon/inputs.rb +++ b/dragon/inputs.rb @@ -179,14 +179,14 @@ module GTK end def left_right - return -1 if @left - return 1 if @right + return -1 if self.left + return 1 if self.right return 0 end def up_down - return 1 if @up - return -1 if @down + return 1 if self.up + return -1 if self.down return 0 end @@ -273,8 +273,7 @@ S end def method_missing m, *args - # determine if it's a ctrl+some_key combination - if m.to_s.length != 1 && m.end_with_bang? # creation of args.intputs.SOME_KEY! (where the key is queried and then immediately cleared) + if m.to_s.length != 1 && m.end_with_bang? # creation of args.intputs.SOME_KEY! (where the key is queried and then immediately cleared) begin define_singleton_method(m) do r = self.instance_variable_get("@#{m.without_ending_bang}".to_sym) @@ -317,67 +316,20 @@ module GTK @has_focus = false end - def left_right - return -1 if left - return 1 if right - return 0 - end - - def up_down - return 1 if up - return -1 if down - return 0 - end - def left - @key_down.left || @key_held.left + @key_up.left || @key_held.left || a end def right - @key_down.right || @key_held.right + @key_up.right || @key_held.right || d end def up - @key_down.up || @key_held.up + @key_up.up || @key_held.up || w end def down - @key_down.down || @key_held.down - end - - def w - @key_down.w || @key_held.w - end - - def a - @key_down.a || @key_held.a - end - - def s - @key_down.s || @key_held.s - end - - def d - @key_down.d || @key_held.d - end - - def method_missing m, *args - if m.to_s.start_with?("ctrl_") - other_key = m.to_s.split("_").last - define_singleton_method(m) do - return @key_up.send(other_key.to_sym) && key_up.control - end - - return send(m) - elsif @key_down.respond_to? m - define_singleton_method(m) do - @key_down.send(m) || @key_held.send(m) - end - - return send(m) - end - - super + @key_up.down || @key_held.down || s end def clear @@ -402,9 +354,12 @@ module GTK def to_s serialize.to_s end + + include DirectionalInputHelperMethods end end + module GTK class ControllerKeys include Serialize @@ -481,14 +436,14 @@ module GTK end def left_right - return -1 if @key_down.left || @key_held.left - return 1 if @key_down.right || @key_held.right + return -1 if self.left + return 1 if self.right return 0 end def up_down - return 1 if @key_down.up || @key_held.up - return -1 if @key_down.down || @key_held.down + return 1 if self.up + return -1 if self.down return 0 end @@ -505,6 +460,24 @@ module GTK @key_up.clear @key_held.clear end + + def up + @key_up.up || @key_held.up + end + + def down + @key_up.down || @key_held.down + end + + def left + @key_up.left || @key_held.left + end + + def right + @key_up.right || @key_held.right + end + + include DirectionalInputHelperMethods end end @@ -619,6 +592,43 @@ module GTK @text = [] end + def up + keyboard.up || + (controller_one && controller_one.up) + end + + def down + keyboard.down || + (controller_one && controller_one.down) + end + + def left + keyboard.left || + (controller_one && controller_one.left) + end + + def right + keyboard.right || + (controller_one && controller_one.right) + end + + def directional_vector + keyboard.directional_vector || + (controller_one && controller_one.directional_vector) + end + + def left_right + return -1 if self.left + return 1 if self.right + return 0 + end + + def up_down + return 1 if self.up + return -1 if self.down + return 0 + end + def click return nil unless @mouse.click return @mouse.click.point diff --git a/dragon/log.rb b/dragon/log.rb new file mode 100644 index 0000000..60246e6 --- /dev/null +++ b/dragon/log.rb @@ -0,0 +1,236 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# log.rb has been released under MIT (*only this file*). + +XTERM_COLOR = { + black: "\u001b[30m", + red: "\u001b[31m", + green: "\u001b[32m", + yellow: "\u001b[33m", + blue: "\u001b[34m", + magenta: "\u001b[35m", + cyan: "\u001b[36m", + white: "\u001b[37m", + bright_black: "\u001b[30;1m", + bright_red: "\u001b[31;1m", + bright_green: "\u001b[32;1m", + bright_yellow: "\u001b[33;1m", + bright_blue: "\u001b[34;1m", + bright_magenta: "\u001b[35;1m", + bright_cyan: "\u001b[36;1m", + bright_white: "\u001b[37;1m", + reset: "\u001b[0m", +} + +module GTK + class Log + def self.write_to_log_and_puts *args + return if $gtk.production + $gtk.append_file 'logs/log.txt', args.join("\n") + "\n" + args.each { |obj| $gtk.log obj, self } + end + + def self.write_to_log_and_print *args + return if $gtk.production + $gtk.append_file 'logs/log.txt', args.join("\n") + Object.print(*args) + end + + def self.puts_important *args + return if $gtk.production + $gtk.append_file 'logs/log.txt', args.join("\n") + $gtk.notify! "Important notification occurred." + args.each { |obj| $gtk.log obj } + end + + def self.puts *args + message_id, message = args + message ||= message_id + write_to_log_and_puts message + end + + def self.multiline? *args + return true if args.length > 1 + return !args[0].to_s.multiline? + end + + def self.join_lines args + return "" if args.length == 0 + return args if args.is_a? String + return args[0] if args.length == 1 + return args.to_s.join("\n") + end + + def self.headline name + @asterisk_count ||= 1 + @asterisk_count = @asterisk_count.greater(1) + result_from_yield = join_lines yield + result_from_yield = result_from_yield.each_line.map { |l| " #{l}" }.join + r ="#{"*" * @asterisk_count} #{name}\n#{result_from_yield}" + @asterisk_count -= 1 + @asterisk_count = @asterisk_count.greater(1) + r + end + + def self.dynamic_block + "#+BEGIN: +#{join_lines yield} +#+END: + +" + end + + def self.puts_info *args + args ||= [] + title = args[0] + additional = args[1..-1] || [] + additional = "" if additional.length == 0 + if !title.multiline? && join_lines(additional).multiline? + message = headline "INFO: #{title}" do + dynamic_block do + additional + end + end + elsif title.multiline? + message = headline "INFO: " do + dynamic_block do + args + end + end + else + message = "* INFO: #{title} #{additional}".strip + end + + self.puts message + end + + def self.puts_once *ids, message + id = "#{ids}" + @once ||= {} + return if @once[id] + @once[id] = id + write_to_log_and_puts "" + write_to_log_and_puts "#{message.strip}" + write_to_log_and_puts "" + write_to_log_and_puts "[Message ID: #{id}]" + write_to_log_and_puts "" + return if $gtk.cli_arguments[:replay] + return if $gtk.cli_arguments[:record] + $gtk.notify!("One time notification occurred. [Message ID: #{id}] (Open console for more info.)") + end + + def self.puts_once_info *ids, message + id = "#{ids}" + @once ||= {} + return if @once[id] + @once[id] = id + log_info message + end + + def self.print *args + write_to_log_and_print(*args) + end + end +end + +class Object + def log_print *args + GTK::Log.print(*args) + end + + def log_important *args + GTK::Log.puts_important(*args) + end + + def log *args + GTK::Log.puts(*args) + end + + def log_with_color xterm_escape_code, *args + log_print xterm_escape_code + log(*args) + ensure + log_reset_color + end + + def log_reset_color + log_print XTERM_COLOR[:reset] + end + + def log_black *args + log_with_color XTERM_COLOR[:black], *args + end + + def log_red *args + log_with_color XTERM_COLOR[:red], *args + end + + def log_green *args + log_with_color XTERM_COLOR[:green], *args + end + + def log_yellow *args + log_with_color XTERM_COLOR[:yellow], *args + end + + def log_blue *args + log_with_color XTERM_COLOR[:blue], *args + end + + def log_magenta *args + log_with_color XTERM_COLOR[:magenta], *args + end + + def log_cyan *args + log_with_color XTERM_COLOR[:cyan], *args + end + + def log_white *args + log_with_color XTERM_COLOR[:white], *args + end + + def log_bright_black *args + log_with_color XTERM_COLOR[:bright_black], *args + end + + def log_bright_red *args + log_with_color XTERM_COLOR[:bright_red], *args + end + + def log_bright_green *args + log_with_color XTERM_COLOR[:bright_green], *args + end + + def log_bright_yellow *args + log_with_color XTERM_COLOR[:bright_yellow], *args + end + + def log_bright_blue *args + log_with_color XTERM_COLOR[:bright_blue], *args + end + + def log_bright_magenta *args + log_with_color XTERM_COLOR[:bright_magenta], *args + end + + def log_bright_cyan *args + log_with_color XTERM_COLOR[:bright_cyan], *args + end + + def log_bright_white *args + log_with_color XTERM_COLOR[:bright_white], *args + end + + def log_info *args + GTK::Log.puts_info(*args) + end + + def log_once *ids, message + GTK::Log.puts_once(*ids, message) + end + + def log_once_info *ids, message + GTK::Log.puts_once_info(*ids, message) + end +end diff --git a/dragon/numeric.rb b/dragon/numeric.rb new file mode 100644 index 0000000..0ef606e --- /dev/null +++ b/dragon/numeric.rb @@ -0,0 +1,527 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# numeric.rb has been released under MIT (*only this file*). + +class Numeric + include ValueType + include NumericDeprecated + + alias_method :gte, :>= + alias_method :lte, :<= + alias_method :gt, :> + alias_method :lt, :< + alias_method(:original_eq_eq, :==) unless Numeric.instance_methods.include?(:original_eq_eq) + + def seconds + self * 60 + end + + def half + return self / 2.0 + end + + def to_byte + return 0 if self < 0 + return 255 if self > 255 + return self.to_i + end + + def elapsed_time tick_count_override = nil + (tick_count_override || Kernel.tick_count) - self + end + + def elapsed_time_percent duration + elapsed_time.percentage_of duration + end + + def new? + elapsed_time == 0 + end + + def elapsed? offset, tick_count_override = nil + (self + offset) < (tick_count_override || Kernel.tick_count) + end + + def frame_index frame_count, hold_for, repeat, tick_count_override = nil + animation_frame_count = frame_count + animation_frame_hold_time = hold_for + animation_length = animation_frame_hold_time * animation_frame_count + if !repeat && self.+(animation_length) < (tick_count_override || Kernel.tick_count).-(1) + return nil + else + return self.elapsed_time.-(1).idiv(animation_frame_hold_time) % animation_frame_count + end + end + + def zero + 0 + end + + def zero? + self == 0 + end + + def one + 1 + end + + def two + 2 + end + + def five + 5 + end + + def ten + 10 + end + + def above? v + self > v + end + + def below? v + self < v + end + + def left_of? v + self < v + end + + def right_of? v + self > v + end + + def shift_right i + self + i + end + + def shift_left i + shift_right(i * -1) + rescue Exception => e + raise_immediately e, :shift_left, i + end + + def shift_up i + self + i + rescue Exception => e + raise_immediately e, :shift_up, i + end + + def shift_down i + shift_up(i * -1) + rescue Exception => e + raise_immediately e, :shift_down, i + end + + def randomize *definitions + result = self + + if definitions.include?(:sign) + result = rand_sign + end + + if definitions.include?(:ratio) + result = rand * result + end + + result + end + + def rand_sign + return self * -1 if rand > 0.5 + self + end + + def rand_ratio + self * rand + end + + def between? n, n2 + self >= n && self <= n2 || self >= n2 && self <= n + end + + def remainder_of_divide n + mod n + end + + def ease_extended tick_count_override, duration, default_before, default_after, *definitions + GTK::Easing.exec_definitions(self, + tick_count_override, + self + duration, + default_before, + default_after, + *definitions) + end + + def ease_initial_value *definitions + GTK::Easing.initial_value(*definitions) + end + + def ease_final_value *definitions + GTK::Easing.final_value(*definitions) + end + + def global_ease duration, *definitions + ease_extended Kernel.global_tick_count, + duration, + GTK::Easing.initial_value(*definitions), + GTK::Easing.final_value(*definitions), + *definitions + end + + def ease duration, *definitions + ease_extended Kernel.tick_count, + duration, + GTK::Easing.initial_value(*definitions), + GTK::Easing.final_value(*definitions), + *definitions + end + + def to_radians + self * Math::PI.fdiv(180) + end + + def to_degrees + self / Math::PI.fdiv(180) + end + + def to_square x, y, anchor_x = 0.5, anchor_y = nil + GTK::Geometry.to_square(self, x, y, anchor_x, anchor_y) + end + + def vector max_value = 1 + [vector_x(max_value), vector_y(max_value)] + end + + def vector_y max_value = 1 + max_value * Math.sin(self.to_radians) + end + + def vector_x max_value = 1 + max_value * Math.cos(self.to_radians) + end + + def x_vector max_value = 1 + vector_x max_value + end + + def y_vector max_value = 1 + vector_y max_value + end + + def mod n + self % n + end + + def mod_zero? *ns + ns.any? { |n| mod(n) == 0 } + end + + def mult n + self * n + end + + def fdiv n + self / n.to_f + end + + def idiv n + (self / n.to_f).to_i + end + + def towards target, magnitude + return self if self == target + delta = (self - target).abs + return target if delta < magnitude + return self - magnitude if self > target + return self + magnitude + end + + def map_with_ys ys, &block + self.times.flat_map do |x| + ys.map_with_index do |y| + yield x, y + end + end + rescue Exception => e + raise_immediately e, :map_with_ys, [self, ys] + end + + def combinations other_int + self.numbers.product(other_int.numbers) + end + + def percentage_of n + (self / n.to_f).cap_min_max(0, 1) + end + + def cap i + return i if self > i + self + end + + def cap_min_max min, max + return min if self < min + return max if self > max + self + end + + def lesser other + return other if other < self + self + end + + def greater other + return other if other > self + self + end + + def subtract i + self - i + end + + def minus i + self - i + end + + def add i + self + i + end + + def plus i + self + i + end + + def numbers + (0..self).to_a + end + + def >= other + return false if !other + return gte other + end + + def > other + return false if !other + return gt other + end + + def <= other + return false if !other + return lte other + end + + def < other + return false if !other + return gt other + end + + def == other + return true if self.original_eq_eq(other) + if other.is_a?(OpenEntity) + return self.original_eq_eq(other.entity_id) + end + return self.original_eq_eq(other) + end + + def map + unless block_given? + raise <<-S +* ERROR: +A block is required for Numeric#map. + +S + end + + self.to_i.times.map do + yield + end + end + + def map_with_index + unless block_given? + raise <<-S +* ERROR: +A block is required for Numeric#map. + +S + end + + self.to_i.times.map do |i| + yield i + end + end + + def check_numeric! sender, other + return if other.is_a? Numeric + + raise <<-S +* ERROR: +Attempted to invoke :+ on #{self} with the right hand argument of: + +#{other} + +The object above is not a Numeric. + +S + end + + def - other + return nil unless other + check_numeric! :-, other + super + end + + def + other + return nil unless other + check_numeric! :+, other + super + end + + def * other + return nil unless other + check_numeric! :*, other + super + end + + def / other + return nil unless other + check_numeric! :/, other + super + end + + def serialize + self + end +end + +class Fixnum + include ValueType + + alias_method(:original_eq_eq, :==) unless Fixnum.instance_methods.include?(:original_eq_eq) + + def - other + return nil unless other + check_numeric! :-, other + super + end + + def even? + return true if self % 2 == 1 + return false + end + + def odd? + return !even? + end + + def + other + return nil unless other + check_numeric! :+, other + super + end + + def * other + return nil unless other + check_numeric! :*, other + super + end + + def / other + return nil unless other + check_numeric! :/, other + super + end + + def == other + return true if self.original_eq_eq(other) + if other.is_a?(GTK::OpenEntity) + return self.original_eq_eq(other.entity_id) + end + return self.original_eq_eq(other) + end + + def sign + return -1 if self < 0 + return 1 if self > 0 + return 0 + end + + def pos? + sign > 0 + end + + def neg? + sign < 0 + end + + def cos + Math.cos(self.to_radians) + end + + def sin + Math.sin(self.to_radians) + end +end + +class Float + include ValueType + + def - other + return nil unless other + check_numeric! :-, other + super + end + + def + other + return nil unless other + check_numeric! :+, other + super + end + + def * other + return nil unless other + check_numeric! :*, other + super + end + + def / other + return nil unless other + check_numeric! :/, other + super + end + + def serialize + self + end + + def clamp lower, higher + return lower if self < lower + return higher if self > higher + return self + end + + def sign + return -1 if self < 0 + return 1 if self > 0 + return 0 + end + + def replace_infinity scalar + return self if !scalar + return self unless self.infinite? + return -scalar if self < 0 + return scalar if self > 0 + end + + def pos? + sign > 0 + end + + def neg? + sign < 0 + end +end diff --git a/dragon/string.rb b/dragon/string.rb new file mode 100644 index 0000000..8d2d9ba --- /dev/null +++ b/dragon/string.rb @@ -0,0 +1,98 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# string.rb has been released under MIT (*only this file*). + +class String + include ValueType + + def wrapped_lines_recur word, rest, length, aggregate + if word.nil? + return aggregate + elsif rest[0].nil? + aggregate << word + "\n" + return aggregate + elsif (word + " " + rest[0]).length > length + aggregate << word + "\n" + return wrapped_lines_recur rest[0], rest[1..-1], length, aggregate + elsif (word + " " + rest[0]).length <= length + next_word = (word + " " + rest[0]) + return wrapped_lines_recur next_word, rest[1..-1], length, aggregate + else + log <<-S +WARNING: +#{word} is too long to fit in length of #{length}. + +S + next_word = (word + " " + rest[0]) + return wrapped_lines_recur next_word, rest[1..-1], length, aggregate + end + end + + def end_with_bang? + self[-1] == "!" + end + + def without_ending_bang + return self unless end_with_bang? + self[0..-2] + end + + def wrapped_lines length + self.each_line.map do |l| + l = l.rstrip + if l.length < length + l + "\n" + else + words = l.split ' ' + wrapped_lines_recur(words[0], words[1..-1], length, []).flatten + end + end.flatten + end + + def wrap length + wrapped_lines(length).join.rstrip + end + + def multiline? + include? "\n" + end + + def indent_lines amount + self.each_line.each_with_index.map do |l, i| + if i == 0 + l + else + " " * amount + l + end + end.join + end + + def quote + "\"#{self}\"" + end + + def trim + strip + end + + def trim! + strip! + end + + def ltrim + lstrip + end + + def ltrim! + lstrip! + end + + def rtrim + rstrip + end + + def rtrim! + rstrip! + end +end diff --git a/dragon/tests.rb b/dragon/tests.rb new file mode 100644 index 0000000..fc6450e --- /dev/null +++ b/dragon/tests.rb @@ -0,0 +1,136 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# tests.rb has been released under MIT (*only this file*). + +module GTK + class Tests + attr_accessor :failed, :passed, :inconclusive + + def initialize + @failed = [] + @passed = [] + @inconclusive = [] + end + + def run_test m + args = Args.new $gtk, nil + assert = Assert.new + begin + log_test_running m + send(m, args, assert) + if !assert.assertion_performed + log_inconclusive m + else + log_passed m + end + rescue Exception => e + if test_signature_invalid_exception? e, m + log_test_signature_incorrect m + else + mark_test_failed m, e + end + end + end + + def test_methods_focused + Object.methods.find_all { |m| m.start_with?( "focus_test_") } + end + + def test_methods + Object.methods.find_all { |m| m.start_with? "test_" } + end + + def start + log "* TEST: gtk.test.start has been invoked." + if test_methods_focused.length != 0 + @is_running = true + test_methods_focused.each { |m| run_test m } + print_summary + @is_running = false + elsif test_methods.length == 0 + log_no_tests_found + else + @is_running = true + test_methods.each { |m| run_test m } + print_summary + @is_running = false + end + end + + def mark_test_failed m, e + message = "Failed." + self.failed << { m: m, e: e } + log message + end + + def running? + @is_running + end + + def log_inconclusive m + self.inconclusive << {m: m} + log "Inconclusive." + log_once :assertion_ok_note, <<-S +NOTE FOR INCONCLUSIVE TESTS: No assertion was performed in the test. +Add assert.ok! at the end of the test if you are using your own assertions. +S + end + + def log_passed m + self.passed << {m: m} + log "Passed." + end + + def log_no_tests_found + log <<-S +No tests were found. To create a test. Define a method +that begins with test_. For example: +#+begin_src +def test_game_over args, assert + +end +#+end_src +S + end + + def log_test_running m + log "** Running: #{m}" + end + + def test_signature_invalid_exception? e, m + e.to_s.include?(m.to_s) && e.to_s.include?("wrong number of arguments") + end + + def log_test_signature_incorrect m + log "TEST METHOD INVALID:", <<-S +I found a test method called :#{m}. But it needs to have +the following method signature: +#+begin_src +def #{m} args, assert + +end +#+end_src +Please update the method signature to match the code above. If you +did not intend this to be a test method. Rename the method so it does +not start with "test_". +S + end + + def print_summary + log "** Summary" + log "*** Passed" + log "#{self.passed.length} test(s) passed." + self.passed.each { |h| log "**** :#{h[:m]}" } + log "*** Inconclusive" + log "#{self.inconclusive.length} test(s) inconclusive." + self.inconclusive.each { |h| log "**** :#{h[:m]}" } + log "*** Failed" + log "#{self.failed.length} test(s) failed." + self.failed.each do |h| + log "**** Test name: :#{h[:m]}" + log "#{h[:e].to_s.gsub("* ERROR:", "").strip}" + end + end + end +end diff --git a/dragon/trace.rb b/dragon/trace.rb new file mode 100644 index 0000000..c3b4bbd --- /dev/null +++ b/dragon/trace.rb @@ -0,0 +1,150 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# trace.rb has been released under MIT (*only this file*). + +module GTK + module Trace + IGNORED_METHODS = [ + :define_singleton_method, :raise_immediately, :instance_of?, + :raise_with_caller, :initialize_copy, :class_defined?, + :instance_variable_get, :format, :purge_class, :instance_variable_defined?, + :metadata_object_id, :instance_variable_set, :__printstr__, + :instance_variables, :is_a?, :p, :kind_of?, :==, :log_once, + :protected_methods, :log_once_info, :private_methods, :open, + :!=, :initialize, :object_id, :Hash, :methods, :tick, :!, + :respond_to?, :yield_self, :send, :instance_eval, :then, + :__method__, :__send__, :log_print, :dig, :itself, :log_info, + :remove_instance_variable, :raise, :public_methods, :instance_exec, + :gets, :local_variables, :tap, :__id__, :class, :singleton_class, + :block_given?, :_inspect, :puts, :global_variables, :getc, :iterator?, + :hash, :to_enum, :printf, :frozen?, :print, :original_puts, + :srand, :freeze, :rand, :extend, :eql?, :equal?, :sprintf, :clone, + :dup, :to_s, :primitive_determined?, :inspect, :primitive?, :help, + :__object_methods__, :proc, :__custom_object_methods__, :Float, :enum_for, + :__supports_ivars__?, :nil?, :fast_rand, :or, :and, + :__caller_without_noise__, :__gtk_ruby_string_contains_source_file_path__?, + :__pretty_print_exception__, :__gtk_ruby_source_files__, + :String, :log, :Array, :putsc, :Integer, :===, :here, + :raise_error_with_kind_of_okay_message, :better_instance_information, + :lambda, :fail, :method_missing, :__case_eqq, :caller, + :raise_method_missing_better_error, :require, :singleton_methods, + :!~, :loop, :numeric_or_default, :`, :state, :inputs, :outputs, "args=".to_sym, + :grid, :gtk, :dragon, :args, :passes, :tick, :grep_source, :grep_source_file, + :numeric_or_default, :f_or_default, :s_or_default, :i_or_default, + :comment, :primitive_marker, :xrepl, :repl + ] + + def self.traced_classes + @traced_classes ||= [] + @traced_classes + end + + def self.mark_class_as_traced! klass + @traced_classes << klass + end + + def self.untrace_classes! + traced_classes.each do |klass| + klass.class_eval do + all_methods = klass.instance_methods false + if klass.instance_methods.respond_to?(:__trace_call_depth__) + undef_method :__trace_call_depth__ + end + + GTK::Trace.filter_methods_to_trace(all_methods).each do |m| + original_method_name = m + trace_method_name = GTK::Trace.trace_method_name_for m + if klass.instance_methods.include? trace_method_name + alias_method m, trace_method_name + end + end + end + end + $last_method_traced = nil + @traced_classes.clear + $trace_enabled = false + if !$gtk.production + $gtk.write_file 'logs/trace.txt', "Add trace!(SOMEOBJECT) to the top of `tick` and this file will be populated with invocation information.\n" + end + end + + def self.trace_method_name_for m + "__trace_original_#{m}__".to_sym + end + + def self.original_method_name_for m + return m unless m.to_s.start_with?("__trace_original_") && m.to_s.end_with?("__") + m[16..-3] + end + + def self.filter_methods_to_trace methods + methods.reject { |m| m.start_with? "__trace_" }.reject { |m| IGNORED_METHODS.include? m } + end + + def self.flush_trace pad_with_newline = false + $trace_puts ||= [] + if $trace_puts.length > 0 + text = $trace_puts.join("") + if pad_with_newline + $gtk.append_file 'logs/trace.txt', "\n" + text.strip + else + $gtk.append_file 'logs/trace.txt', text.strip + end + end + $trace_puts.clear + end + + def self.trace! instance = nil + $trace_history ||= [] + $trace_enabled = true + $trace_call_depth ||=0 + flush_trace + instance = $top_level unless instance + return if Trace.traced_classes.include? instance.class + all_methods = instance.class.instance_methods false + instance.class.class_eval do + attr_accessor :__trace_call_depth__ unless instance.class.instance_methods.include?(:__trace_call_depth__) + GTK::Trace.filter_methods_to_trace(all_methods).each do |m| + original_method_name = m + trace_method_name = GTK::Trace.trace_method_name_for m + alias_method trace_method_name, m + $trace_puts << "Tracing #{m} on #{instance.class}.\n" + define_method(m) do |*args| + instance.__trace_call_depth__ ||= 0 + tab_width = " " * (instance.__trace_call_depth__ * 8) + instance.__trace_call_depth__ += 1 + $trace_call_depth = instance.__trace_call_depth__ + parameters = "#{args}"[1..-2] + $trace_puts << "\n #{tab_width}#{m}(#{parameters})" + execution_time = Time.new.to_i + $last_method_traced = trace_method_name + $trace_history << [m, parameters] + result = send(trace_method_name, *args) + completion_time = Time.new.to_i + instance.__trace_call_depth__ -= 1 + instance.__trace_call_depth__ = instance.__trace_call_depth__.greater 0 + $trace_puts << "\n #{tab_width} success: #{m}" + if instance.__trace_call_depth__ == 0 + $trace_puts << "\n" + $trace_history.clear + end + result + rescue Exception => e + instance.__trace_call_depth__ -= 1 + instance.__trace_call_depth__ = instance.__trace_call_depth__.greater 0 + $trace_puts << "\n #{tab_width} failed: #{m}" + if instance.__trace_call_depth__ == 0 + $trace_puts << "\n #{tab_width} #{e}" + $trace_puts << "\n" + end + $trace_call_depth = 0 + GTK::Trace.flush_trace true + raise e + end + end + end + mark_class_as_traced! instance.class + end + end +end |
