From d5115606c96b0fe0affd72e6e0b72edce7e51042 Mon Sep 17 00:00:00 2001 From: Simon Chiang Date: Fri, 23 Apr 2021 09:46:41 -0600 Subject: Typo fixes --- dragon/controller_config.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'dragon/controller_config.rb') diff --git a/dragon/controller_config.rb b/dragon/controller_config.rb index 091fa1e..c315ab5 100644 --- a/dragon/controller_config.rb +++ b/dragon/controller_config.rb @@ -125,7 +125,7 @@ module GTK def previous_part if @current_part > 0 - # remove the binding that we previous had here so it can be reused. + # remove the binding that we previously had here so it can be reused. bindstr = @bindings[@current_part - 1] @bindings[@current_part - 1] = nil @used_bindings[bindstr] = nil -- cgit v1.2.3 From a1b8552ae442d1fa22df1e569652eb99750dcebf Mon Sep 17 00:00:00 2001 From: Amir Rajan Date: Sat, 7 Aug 2021 00:18:49 -0500 Subject: Sync core oss engine files. --- dragon/args.rb | 51 +--- dragon/assert.rb | 4 +- dragon/attr_gtk.rb | 12 + dragon/attr_sprite.rb | 3 +- dragon/autocomplete.rb | 19 +- dragon/benchmark.rb | 112 ++++++++ dragon/config.rb | 2 +- dragon/console.rb | 49 +++- dragon/console_color.rb | 5 + dragon/console_font_style.rb | 3 +- dragon/console_menu.rb | 8 +- dragon/console_prompt.rb | 15 +- dragon/controller_config.rb | 396 ----------------------------- dragon/directional_input_helper_methods.rb | 6 + dragon/docs.rb | 22 +- dragon/draw.rb | 234 ++++------------- dragon/framerate.rb | 1 + dragon/framerate_diagnostics.rb | 13 +- dragon/geometry.rb | 68 +++-- dragon/grid.rb | 8 + dragon/help.rb | 72 ------ dragon/hotload.rb | 44 +++- dragon/inputs.rb | 60 ++++- dragon/ios_wizard.rb | 328 +++++++++++++++--------- dragon/itch_wizard.rb | 60 +++-- dragon/keys.rb | 18 +- dragon/layout.rb | 182 +++++++++++-- dragon/log.rb | 5 + dragon/metadata.rb | 41 --- dragon/numeric.rb | 117 ++++++--- dragon/outputs_docs.rb | 2 +- dragon/readme_docs.rb | 87 ++++--- dragon/runtime_docs.rb | 32 ++- dragon/string.rb | 24 ++ dragon/tests.rb | 2 +- dragon/wizards.rb | 35 +++ 36 files changed, 1101 insertions(+), 1039 deletions(-) create mode 100644 dragon/benchmark.rb delete mode 100644 dragon/controller_config.rb delete mode 100644 dragon/help.rb delete mode 100644 dragon/metadata.rb (limited to 'dragon/controller_config.rb') diff --git a/dragon/args.rb b/dragon/args.rb index 2b11008..9cf6e33 100644 --- a/dragon/args.rb +++ b/dragon/args.rb @@ -10,58 +10,21 @@ module GTK class Args include ArgsDeprecated include Serialize - - # Contains information related to input devices and input events. - # - # @return [Inputs] attr_accessor :inputs - - # Contains the means to interact with output devices such as the screen. - # - # @return [Outputs] attr_accessor :outputs - - # Contains the means to interact with the audio mixer. - # - # @return [Hash] attr_accessor :audio - - # Contains display size information to assist in positioning things on the screen. - # - # @return [Grid] attr_accessor :grid - - # Provides access to game play recording facilities within Game Toolkit. - # - # @return [Recording] attr_accessor :recording - - # Gives you access to geometry related functions. - # - # @return [Geometry] attr_accessor :geometry - attr_accessor :fn - - # This is where you'll put state associated with your video game. - # - # @return [OpenEntity] attr_accessor :state - - # Gives you access to the top level DragonRuby runtime. - # - # @return [Runtime] + attr_accessor :temp_state attr_accessor :runtime alias_method :gtk, :runtime - attr_accessor :passes - attr_accessor :wizards - attr_accessor :layout - attr_accessor :easing - attr_accessor :string def initialize runtime, recording @@ -70,6 +33,7 @@ module GTK @audio = {} @passes = [] @state = OpenEntity.new + @temp_state = OpenEntity.new @state.tick_count = -1 @runtime = runtime @recording = recording @@ -99,11 +63,12 @@ module GTK def serialize { - state: state.as_hash, - inputs: inputs.serialize, - passes: passes.serialize, - outputs: outputs.serialize, - grid: grid.serialize + state: state.as_hash, + temp_state: temp_state.as_hash, + inputs: inputs.serialize, + passes: passes.serialize, + outputs: outputs.serialize, + grid: grid.serialize } end diff --git a/dragon/assert.rb b/dragon/assert.rb index 5ba344d..974846a 100644 --- a/dragon/assert.rb +++ b/dragon/assert.rb @@ -98,7 +98,7 @@ end @assertion_performed = true if actual != expected actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip - message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}.\n#{message}" + message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}\n#{message}" raise message end nil @@ -108,7 +108,7 @@ end @assertion_performed = true if actual == expected actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip - message = "actual:\n#{actual_string}\n\nequaled\n\nexpected:\n#{expected}.\n#{message}" + message = "actual:\n#{actual_string}\n\nequaled\n\nexpected:\n#{expected}\n#{message}" raise message end nil diff --git a/dragon/attr_gtk.rb b/dragon/attr_gtk.rb index 2f2ccc5..e53e1b9 100644 --- a/dragon/attr_gtk.rb +++ b/dragon/attr_gtk.rb @@ -19,6 +19,10 @@ module AttrGTK args.state end + def temp_state + args.temp_state + end + def inputs args.inputs end @@ -46,4 +50,12 @@ module AttrGTK def layout args.layout end + + def new_entity entity_type, init_hash = nil, &block + args.state.new_entity entity_type, init_hash, &block + end + + def new_entity_strict entity_type, init_hash = nil, &block + args.state.new_entity_strict entity_type, init_hash, &block + end end diff --git a/dragon/attr_sprite.rb b/dragon/attr_sprite.rb index 69ddd8c..754429d 100644 --- a/dragon/attr_sprite.rb +++ b/dragon/attr_sprite.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # attr_sprite.rb has been released under MIT (*only this file*). @@ -37,7 +38,7 @@ module AttrSprite attr_accessor :x, :y, :w, :h, :path, :angle, :a, :r, :g, :b, :tile_x, :tile_y, :tile_w, :tile_h, :flip_horizontally, :flip_vertically, :angle_anchor_x, :angle_anchor_y, :id, - :source_x, :source_y, :source_w, :source_h + :source_x, :source_y, :source_w, :source_h, :blendmode_enum def primitive_marker :sprite diff --git a/dragon/autocomplete.rb b/dragon/autocomplete.rb index 490d6c9..ce29d0a 100644 --- a/dragon/autocomplete.rb +++ b/dragon/autocomplete.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # autocomplete.rb has been released under MIT (*only this file*). @@ -24,7 +25,7 @@ module GTK sub_index = index - previous_line[:sum] word = (cursor_line[:line][0..sub_index - 1]).strip token = (word.split " ")[-1] - dots = (token.split ".") + dots = (token.split ".").flat_map { |s| s.split "[" }.flat_map { |s| s.split "]" }.flat_map { |s| s.split "(" }.flat_map { |s| s.split ")" } dot = dots[-1] end @@ -45,6 +46,10 @@ module GTK ignores ||= [] ignores = [ignores].flatten keys = keys.map { |k| k.to_s } + keys = keys.reject { |k| k.include? '"' } + .reject { |k| k.start_with? "'" } + .reject { |k| k.include? "," } + .reject { |k| k.start_with? "#" } others = ["def", "end"] + [ :entity_keys_by_ref, :entity_name, @@ -102,6 +107,10 @@ module GTK return autocomplete_filter_methods lookup_result.call if lookup_result + if dot[0].upcase == dot[0] && (Object.const_defined? dot.to_sym) + return (Object.const_get dot.to_sym).autocomplete_methods + end + start_collecting = false dots_after_state = dots.find_all do |s| if s == "state" @@ -117,10 +126,16 @@ module GTK target = target.as_hash[k.to_sym] if target.respond_to? :as_hash end - return autocomplete_filter_methods target.as_hash.keys + if target.respond_to? :as_hash + return autocomplete_filter_methods target.as_hash.keys + else + return autocomplete_filter_methods target.autocomplete_methods + end end + text = text.each_line.reject { |l| l.strip.start_with? "#" }.join "\n" + text = text.each_line.map { |l| l.split("#").first }.join "\n" text.gsub!("[", " ") text.gsub!("]", " ") text.gsub!("(", " ") diff --git a/dragon/benchmark.rb b/dragon/benchmark.rb new file mode 100644 index 0000000..d7e8945 --- /dev/null +++ b/dragon/benchmark.rb @@ -0,0 +1,112 @@ +# coding: utf-8 +# Copyright 2019 DragonRuby LLC +# MIT License +# benchmark.rb has been released under MIT (*only this file*). + +module GTK + class Runtime + module Benchmark + def benchmark_single iterations, name, proc + log <<-S +** Invoking :#{name}... +S + time_start = Time.now + idx = 0 + r = nil + while idx < iterations + r = proc.call + idx += 1 + end + + result = (Time.now - time_start).round 3 + + { name: name, + time: result, + time_ms: (result * 1000).to_i } + end + + def benchmark opts = {} + iterations = opts.iterations + + log <<-S +* BENCHMARK: Started +** Caller: #{(caller || []).first} +** Iterations: #{iterations} +S + procs = opts.find_all { |k, v| v.respond_to? :call } + + times = procs.map do |(name, proc)| + benchmark_single iterations, name, proc + end.sort_by { |r| r.time } + + first_place = times.first + second_place = times.second || first_place + + times = times.map do |candidate| + average_time = first_place.time + .add(candidate.time) + .abs + .fdiv(2) + + difference_percentage = 0 + if average_time == 0 + difference_percentage = 0 + else + difference_percentage = first_place.time + .subtract(candidate.time) + .abs + .fdiv(average_time) + .imult(100) + end + + difference_time = ((first_place.time - candidate.time) * 1000).round + candidate.merge(difference_percentage: difference_percentage, + difference_time: difference_time) + end + + too_small_to_measure = false + if (first_place.time + second_place.time) == 0 + too_small_to_measure = true + difference_percentage = 0 + log <<-S +* BENCHMARK: Average time for experiments were too small. Increase the number of iterations. +S + else + difference_percentage = (((first_place.time - second_place.time).abs.fdiv((first_place.time + second_place.time).abs.fdiv(2))) * 100).round + end + + difference_time = first_place.time.-(second_place.time).*(1000).abs.round + + r = { + iterations: iterations, + first_place: first_place, + second_place: second_place, + difference_time: difference_time, + difference_percentage: difference_percentage, + times: times, + too_small_to_measure: too_small_to_measure + } + + log_message = [] + only_one_result = first_place.name == second_place.name + + if only_one_result + log <<-S +* BENCHMARK: #{r.first_place.name} completed in #{r.first_place.time_ms}ms." +S + else + log <<-S +* BENCHMARK: #{r.message} +** Fastest: #{r.first_place.name.inspect} +** Second: #{r.second_place.name.inspect} +** Margin: #{r.difference_percentage}% (#{r.difference_time.abs}ms) #{r.first_place.time_ms}ms vs #{r.second_place.time_ms}ms. +** Times: +#{r.times.map { |t| "*** #{t.name}: #{t.time_ms}ms (#{t.difference_percentage}% #{t.difference_time.abs}ms)." }.join("\n")} +S + end + + r + end + end + end +end diff --git a/dragon/config.rb b/dragon/config.rb index a2887dc..de57085 100644 --- a/dragon/config.rb +++ b/dragon/config.rb @@ -127,7 +127,7 @@ module GTK def previous_part if @current_part > 0 - # remove the binding that we previously had here so it can be reused. + # remove the binding that we previous had here so it can be reused. bindstr = @bindings[@current_part - 1] @bindings[@current_part - 1] = nil @used_bindings[bindstr] = nil diff --git a/dragon/console.rb b/dragon/console.rb index c30c311..b3a2c2a 100644 --- a/dragon/console.rb +++ b/dragon/console.rb @@ -1,10 +1,10 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # console.rb has been released under MIT (*only this file*). # Contributors outside of DragonRuby who also hold Copyright: # - Kevin Fischer: https://github.com/kfischer-okarin -# - Austin Meyer: https://github.com/Niyy module GTK class Console @@ -20,7 +20,7 @@ module GTK :font_style, :menu def initialize - @font_style = FontStyle.new(font: 'font.ttf', size_enum: -1, line_height: 1.1) + @font_style = FontStyle.new(font: 'font.ttf', size_enum: -1.5, line_height: 1.1) @menu = Menu.new self @disabled = false @log_offset = 0 @@ -40,6 +40,8 @@ module GTK @text_color = Color.new [255, 255, 255] @error_color = Color.new [200, 50, 50] @header_color = Color.new [100, 200, 220] + @code_color = Color.new [210, 168, 255] + @comment_color = Color.new [0, 200, 100] @animation_duration = 1.seconds @shown_at = -1 load_history @@ -314,6 +316,8 @@ S if cmd == 'quit' || cmd == ':wq' || cmd == ':q!' || cmd == ':q' || cmd == ':wqa' $gtk.request_quit + elsif cmd.start_with? ':' + send ((cmd.gsub '-', '_').gsub ':', '') else puts "-> #{cmd}" begin @@ -322,13 +326,19 @@ S if $results.nil? puts "=> nil" elsif $results == :console_silent_eval + # do nothing since the console is silent else puts "=> #{$results}" end @last_command_errored = false rescue Exception => e try_search_docs e - puts "* EXCEPTION: #{e}" + # if an exception is thrown and the bactrace includes something helpful, then show it + if (e.backtrace || []).first && (e.backtrace.first.include? "(eval)") + puts "* EXCEPTION: #{e}" + else + puts "* EXCEPTION: #{e}\n#{e.__backtrace_to_org__}" + end end end end @@ -555,6 +565,11 @@ S end render_log_offset args + + args.outputs.reserved << { x: 10.from_right, y: @bottom + 10, + text: "Press CTRL+g or ESCAPE to clear the prompt.", + vertical_alignment_enum: 0, + alignment_enum: 2, r: 80, g: 80, b: 80 }.label! end def render_log_offset args @@ -577,7 +592,7 @@ S end def include_subdued_markers? text - include_any_words? text, subdued_markers + (text.start_with? "* INFO: ") && (include_any_words? text, subdued_markers) end def include_any_words? text, words @@ -723,8 +738,32 @@ S (log_entry.start_with? "**** ") end + def code? log_entry + (just_symbol? log_entry) || (codeblock_marker? log_entry) + end + + def just_symbol? log_entry + scrubbed = log_entry.gsub("*", "").strip + (scrubbed.start_with? ":") && (!scrubbed.include? " ") && (!scrubbed.include? "=>") + end + + def code_comment? log_entry + return true if log_entry.strip.start_with?("# ") + return false + end + + def codeblock_marker? log_entry + return true if log_entry.strip.start_with?("#+begin_src") + return true if log_entry.strip.start_with?("#+end_src") + return false + end + def color_for_log_entry(log_entry) - if include_row_marker? log_entry + if code? log_entry + @code_color + elsif code_comment? log_entry + @comment_color + elsif include_row_marker? log_entry @text_color elsif include_error_marker? log_entry @error_color diff --git a/dragon/console_color.rb b/dragon/console_color.rb index f5b164d..1b19c22 100644 --- a/dragon/console_color.rb +++ b/dragon/console_color.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # console_color.rb has been released under MIT (*only this file*). @@ -22,6 +23,10 @@ module GTK @color end + def to_s + "GTK::Console::Color #{to_h}" + end + def to_h { r: @color[0], g: @color[1], b: @color[2], a: @color[3] } end diff --git a/dragon/console_font_style.rb b/dragon/console_font_style.rb index 8efab4f..0bb7cc4 100644 --- a/dragon/console_font_style.rb +++ b/dragon/console_font_style.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # console_font_style.rb has been released under MIT (*only this file*). @@ -33,7 +34,7 @@ module GTK size_enum: size_enum, alignment_enum: alignment_enum, **color.to_h, - }.label + }.label! end end end diff --git a/dragon/console_menu.rb b/dragon/console_menu.rb index d10ceeb..0f21614 100644 --- a/dragon/console_menu.rb +++ b/dragon/console_menu.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # console_menu.rb has been released under MIT (*only this file*). @@ -51,7 +52,7 @@ module GTK def itch_wizard_clicked @console.scroll_to_bottom - $wizards.itch.start + $wizards.itch.restart end def docs_clicked @@ -76,6 +77,7 @@ module GTK @buttons = [ (button id: :record, row: 0, col: 9, text: "record gameplay", method: :record_clicked), (button id: :replay, row: 0, col: 10, text: "start replay", method: :replay_clicked), + *custom_buttons ] elsif @menu_shown == :hidden @buttons = [ @@ -134,8 +136,8 @@ module GTK method: method }.let do |entity| primitives = [] - primitives << entity[:rect].merge(a: 164).solid - primitives << entity[:rect].merge(r: 255, g: 255, b: 255).border + primitives << entity[:rect].solid!(a: 164) + primitives << entity[:rect].border!(r: 255, g: 255, b: 255) primitives << text.wrapped_lines(5) .map_with_index do |l, i| [ diff --git a/dragon/console_prompt.rb b/dragon/console_prompt.rb index d4c4026..97ea2f9 100644 --- a/dragon/console_prompt.rb +++ b/dragon/console_prompt.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # console_prompt.rb has been released under MIT (*only this file*). @@ -71,14 +72,14 @@ module GTK return if @cursor_position.zero? new_pos = @cursor_position - 1 - (is_word_boundary? @current_input_str[new_pos]) ? + (is_word_boundary? @current_input_str[new_pos]) ? (new_pos -= 1 until !(is_word_boundary? @current_input_str[new_pos - 1]) || new_pos.zero?): (new_pos -= 1 until (is_word_boundary? @current_input_str[new_pos - 1]) || new_pos.zero?) @cursor_position = new_pos update_cursor_position_px end - + def move_cursor_right @cursor_position += 1 if @cursor_position < current_input_str.length update_cursor_position_px @@ -91,7 +92,7 @@ module GTK (is_word_boundary? @current_input_str[new_pos]) ? (new_pos += 1 until !(is_word_boundary? @current_input_str[new_pos]) || (new_pos.equal? str_len)): (new_pos += 1 until (is_word_boundary? @current_input_str[new_pos]) || (new_pos.equal? str_len)) - + @cursor_position = new_pos update_cursor_position_px end @@ -157,11 +158,11 @@ S # partition the original list of items into a string to be printed items.each_slice(columns).each_with_index do |cells, i| - pretty_print_row_separator string_width, cell_width, column_width, columns + pretty_print_row_seperator string_width, cell_width, column_width, columns pretty_print_row cells, string_width, cell_width, column_width, columns end - pretty_print_row_separator string_width, cell_width, column_width, columns + pretty_print_row_seperator string_width, cell_width, column_width, columns end end @@ -193,12 +194,12 @@ S args.outputs.reserved << (@cursor_color.to_h.merge x: x + @cursor_position_px + 0.5, y: y + 5, x2: x + @cursor_position_px + 0.5, - y2: y + @font_style.letter_size.y + 5) + y2: y + @font_style.letter_size.y + 4) args.outputs.reserved << (@cursor_color.to_h.merge x: x + @cursor_position_px + 1, y: y + 5, x2: x + @cursor_position_px + 1, - y2: y + @font_style.letter_size.y + 5) + y2: y + @font_style.letter_size.y + 4) # debugging rectangle for string # args.outputs.reserved << (@cursor_color.to_h.merge x: x, diff --git a/dragon/controller_config.rb b/dragon/controller_config.rb deleted file mode 100644 index c315ab5..0000000 --- a/dragon/controller_config.rb +++ /dev/null @@ -1,396 +0,0 @@ -# Copyright 2019 DragonRuby LLC -# MIT License -# controller_config.rb has been released under MIT (*only this file*). - -# !!! FIXME: add console command to forget custom binding(s) -# !!! FIXME: add console command to forget replace existing binding(s) -# !!! FIXME: add console command go into play_around mode to make sure controller isn't wonky. - -module GTK - class ControllerConfig - def initialize runtime - @runtime = runtime - @raw_joysticks = {} # things that aren't game controllers to try to configure. - @target = nil - @animation_duration = (1.5).seconds - @toggled_at = 0 - @fading = 0 - @current_part = 0 - @part_alpha = 0 - @part_alpha_increment = 10 - @joystick_state = {} - @playing_around = false - @used_bindings = {} - @bindings = [] - @parts = [ - [ 919, 282, 'A button', 'a' ], - [ 960, 323, 'B button', 'b' ], - [ 878, 323, 'X button', 'x' ], - [ 919, 365, 'Y button', 'y' ], - [ 433, 246, 'left stick left', '-leftx' ], - [ 497, 246, 'left stick right', '+leftx' ], - [ 466, 283, 'left stick up', '-lefty' ], - [ 466, 218, 'left stick down', '+lefty' ], - [ 466, 246, 'left stick button', 'leftstick' ], - [ 741, 246, 'right stick left', '-rightx' ], - [ 802, 246, 'right stick right', '+rightx' ], - [ 773, 283, 'right stick up', '-righty' ], - [ 773, 218, 'right stick down', '+righty' ], - [ 772, 246, 'right stick button', 'rightstick' ], - [ 263, 465, 'left shoulder button', 'leftshoulder' ], - [ 263, 503, 'left trigger', 'lefttrigger' ], - [ 977, 465, 'right shoulder button', 'rightshoulder' ], - [ 977, 503, 'right trigger', 'righttrigger' ], - [ 318, 365, 'D-pad up', 'dpup' ], - [ 360, 322, 'D-pad right', 'dpright' ], - [ 318, 280, 'D-pad down', 'dpdown' ], - [ 275, 322, 'D-pad left', 'dpleft' ], - [ 570, 402, 'select/back button', 'back'], - [ 619, 448, 'guide/home button', 'guide' ], - [ 669, 402, 'start button', 'start' ], - ] - end - - def rawjoystick_connected jid, joystickname, guid - return if jid < 0 - @raw_joysticks[jid] = { name: joystickname, guid: guid } - end - - def rawjoystick_disconnected jid - return if jid < 0 - if @raw_joysticks[jid] != nil - @raw_joysticks.delete(jid) - @runtime.ffi_misc.close_raw_joystick(jid) - # Fade out the config screen if we were literally configuring this controller right now. - if !@target.nil? && @target[0] == jid - @target[0] = nil - @toggled_at = Kernel.global_tick_count - @fading = -1 - end - end - end - - def build_binding_string - bindingstr = '' - skip = false - - for i in 0..@parts.length-1 - if skip ; skip = false ; next ; end - - binding = @bindings[i] - next if binding.nil? - - part = @parts[i][3] - - # clean up string: - # if axis uses -a0 for negative and +a0 for positive, just make it "leftx:a0" instead of "-leftx:-a0,+leftx:+a0" - # if axis uses +a0 for negative and -a0 for positive, just make it "leftx:a0~" instead of "-leftx:+a0,+leftx:-a0" - if part == '-leftx' || part == '-lefty' || part == '-rightx' || part == '-righty' - nextbinding = @bindings[i+1] - if binding.start_with?('-a') && nextbinding.start_with?('+a') && binding[2..-1] == nextbinding[2..-1] - skip = true - part = part[1..-1] - binding = binding[1..-1] - elsif binding.start_with?('+a') && nextbinding.start_with?('-a') && binding[2..-1] == nextbinding[2..-1] - skip = true - part = part[1..-1] - binding = "#{binding[1..-1]}~" - end - end - - bindingstr += "#{!bindingstr.empty? ? ',' : ''}#{part}:#{binding}" - end - - details = @target[1] - - # !!! FIXME: no String.delete in mRuby?!?! Maybe so when upgrading. - #name = details[:name].delete(',') - # !!! FIXME: ...no regexp either... :/ - #name = details[:name].gsub(/,/, ' ') # !!! FIXME: will SDL let you escape these instead? - unescaped = details[:name] - name = '' - for i in 0..unescaped.length-1 - ch = unescaped[i] - name += (ch == ',') ? ' ' : ch - end - return "#{details[:guid]},#{name},platform:#{@runtime.platform},#{bindingstr}" - end - - def move_to_different_part part - if !@joystick_state[:axes].nil? - @joystick_state[:axes].each { |i| i[:farthestval] = i[:startingval] if !i.nil? } - end - @current_part = part - end - - def previous_part - if @current_part > 0 - # remove the binding that we previously had here so it can be reused. - bindstr = @bindings[@current_part - 1] - @bindings[@current_part - 1] = nil - @used_bindings[bindstr] = nil - move_to_different_part @current_part - 1 - end - end - - def next_part - if @current_part < (@parts.length - 1) - move_to_different_part @current_part + 1 - else - @playing_around = true - end - end - - def set_binding bindstr - return false if !@used_bindings[bindstr].nil? - @used_bindings[bindstr] = @current_part - @bindings[@current_part] = bindstr - return true - end - - # Called when a lowlevel joystick moves an axis. - def rawjoystick_axis jid, axis, value - return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick. - - @joystick_state[:axes] ||= [] - @joystick_state[:axes][axis] ||= { - moving: false, - startingval: 0, - currentval: 0, - farthestval: 0 - } - - # this is the logic from SDL's controllermap.c, more or less, since this is hard to get right from scratch. - state = @joystick_state[:axes][axis] - state[:currentval] = value - if !state[:moving] - state[:moving] = true - state[:startingval] = value - state[:farthestval] = value - end - - current_distance = (value - state[:startingval]).abs - farthest_distance = (state[:farthestval] - state[:startingval]).abs - if current_distance > farthest_distance - state[:farthestval] = value - farthest_distance = (state[:farthestval] - state[:startingval]).abs - end - - # If we've gone out far enough and started to come back, let's bind this axis - if (farthest_distance >= 16000) && (current_distance <= 10000) - next_part if set_binding("#{(state[:farthestval] < 0) ? '-' : '+'}a#{axis}") - end - end - - # Called when a lowlevel joystick moves a hat. - def rawjoystick_hat jid, hat, value - return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick. - - @joystick_state[:hats] ||= [] - @joystick_state[:hats][hat] = value - - return if value == 0 # 0 == centered, skip it - next_part if set_binding("h#{hat}.#{value}") - end - - # Called when a lowlevel joystick moves a button. - def rawjoystick_button jid, button, pressed - return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick. - - @joystick_state[:buttons] ||= [] - @joystick_state[:buttons][button] = pressed - - return if !pressed - next_part if set_binding("b#{button}") - end - - def calc_fading - if @fading == 0 - return 255 - elsif @fading > 0 # fading in - percent = @toggled_at.global_ease(@animation_duration, :flip, :quint, :flip) - if percent >= 1.0 - percent = 1.0 - @fading = 0 - end - else # fading out - percent = @toggled_at.global_ease(@animation_duration, :flip, :quint) - if percent <= 0.0 - percent = 0.0 - @fading = 0 - end - end - - return (percent * 255.0).to_i - end - - def render_basics args, msg, fade=255 - joystickname = @target[1][:name] - args.outputs.primitives << [0, 0, GAME_WIDTH, GAME_HEIGHT, 255, 255, 255, fade].solid - args.outputs.primitives << [0, 0, GAME_WIDTH, GAME_HEIGHT, 'dragonruby-controller.png', 0, fade, 255, 255, 255].sprite - args.outputs.primitives << [GAME_WIDTH / 2, 700, joystickname, 2, 1, 0, 0, 0, fade].label - args.outputs.primitives << [GAME_WIDTH / 2, 650, msg, 0, 1, 0, 0, 0, 255].label if !msg.empty? - end - - def render_part_highlight args, part, alpha=255 - partsize = 41 - args.outputs.primitives << [part[0], part[1], partsize, partsize, 255, 0, 0, alpha].border - args.outputs.primitives << [part[0]-1, part[1]-1, partsize+2, partsize+2, 255, 0, 0, alpha].border - args.outputs.primitives << [part[0]-2, part[1]-2, partsize+4, partsize+4, 255, 0, 0, alpha].border - end - - def choose_target - if @target.nil? - while !@raw_joysticks.empty? - t = @raw_joysticks.shift # see if there's a joystick waiting on us. - next if t[0] < 0 # just in case. - next if t[1][:guid].nil? # did we already handle this guid? Dump it. - @target = t - break - end - return false if @target.nil? # nothing to configure at the moment. - @toggled_at = Kernel.global_tick_count - @fading = 1 - @current_part = 0 - @part_alpha = 0 - @part_alpha_increment = 10 - @joystick_state = {} - @used_bindings = {} - @playing_around = false - @bindings = [] - end - return true - end - - def render_part_highlight_from_bindstr args, bindstr, alpha=255 - partidx = @used_bindings[bindstr] - return if partidx.nil? - render_part_highlight args, @parts[partidx], alpha - end - - def play_around args - return false if !@playing_around - - if args.inputs.keyboard.key_down.escape - @current_part = 0 - @part_alpha = 0 - @part_alpha_increment = 10 - @used_bindings = {} - @playing_around = false - @bindings = [] - elsif args.inputs.keyboard.key_down.space - jid = @target[0] - bindingstr = build_binding_string - #puts("new controller binding: '#{bindingstr}'") - @runtime.ffi_misc.add_controller_config bindingstr - @runtime.ffi_misc.convert_rawjoystick_to_controller jid - @target[0] = -1 # Conversion closes the raw joystick. - - # Handle any other pending joysticks that have the same GUID (so if you plug in four of the same model, we're already done!) - guid = @target[1][:guid] - @raw_joysticks.each { |jid, details| - if details[:guid] == guid - @runtime.ffi_misc.convert_rawjoystick_to_controller jid - details[:guid] = nil - end - } - - # Done with this guy. - @playing_around = false - @toggled_at = Kernel.global_tick_count - @fading = -1 - return false - end - - render_basics args, 'Now play around with the controller, and make sure it feels right!' - args.outputs.primitives << [GAME_WIDTH / 2, 90, '[ESCAPE]: Reconfigure, [SPACE]: Save this configuration', 0, 1, 0, 0, 0, 255].label - - axes = @joystick_state[:axes] - if !axes.nil? - for i in 0..axes.length-1 - next if axes[i].nil? - value = axes[i][:currentval] - next if value.nil? || (value.abs < 16000) - render_part_highlight_from_bindstr args, "#{value < 0 ? '-' : '+'}a#{i}" - end - end - - hats = @joystick_state[:hats] - if !hats.nil? - for i in 0..hats.length-1 - value = hats[i] - next if value.nil? || (value == 0) - render_part_highlight_from_bindstr args, "h#{i}.#{value}" - end - end - - buttons = @joystick_state[:buttons] - if !buttons.nil? - for i in 0..buttons.length-1 - value = buttons[i] - next if value.nil? || !value - render_part_highlight_from_bindstr args, "b#{i}" - end - end - - return true - end - - def should_tick? - return true if @play_around - return true if @target - return false - end - - def tick args - return true if play_around args - return false if !choose_target - - jid = @target[0] - - if @fading == 0 - # Cancel config? - if args.inputs.keyboard.key_down.escape - # !!! FIXME: prompt to ignore this joystick forever or just this run - @toggled_at = Kernel.global_tick_count - @fading = -1 - end - end - - if @fading == 0 - if args.inputs.keyboard.key_down.backspace - previous_part - elsif args.inputs.keyboard.key_down.space - next_part - end - end - - fade = calc_fading - if (@fading < 0) && (fade == 0) - @runtime.ffi_misc.close_raw_joystick(jid) if jid >= 0 - @target = nil # done with this controller - return false - end - - render_basics args, (@fading >= 0) ? "We don't recognize this controller, so tell us about it!" : '', fade - - return true if fade < 255 # all done for now - - part = @parts[@current_part] - args.outputs.primitives << [GAME_WIDTH / 2, 575, "Please press the #{part[2]}.", 0, 1, 0, 0, 0, 255].label - render_part_highlight args, part, @part_alpha - args.outputs.primitives << [GAME_WIDTH / 2, 90, '[ESCAPE]: Ignore controller, [BACKSPACE]: Go back one button, [SPACE]: Skip this button', 0, 1, 0, 0, 0, 255].label - - @part_alpha += @part_alpha_increment - if (@part_alpha_increment > 0) && (@part_alpha >= 255) - @part_alpha = 255 - @part_alpha_increment = -10 - elsif (@part_alpha_increment < 0) && (@part_alpha <= 0) - @part_alpha = 0 - @part_alpha_increment = 10 - end - - return true - end - end -end diff --git a/dragon/directional_input_helper_methods.rb b/dragon/directional_input_helper_methods.rb index 44913ba..0a15c70 100644 --- a/dragon/directional_input_helper_methods.rb +++ b/dragon/directional_input_helper_methods.rb @@ -70,6 +70,12 @@ S end end + def directional_angle + return nil unless directional_vector + + Math.atan2(up_down, left_right).to_degrees + end + def method_missing m, *args # combine the key with ctrl_ if m.to_s.start_with?("ctrl_") diff --git a/dragon/docs.rb b/dragon/docs.rb index e6f6e2d..cd39483 100644 --- a/dragon/docs.rb +++ b/dragon/docs.rb @@ -46,7 +46,7 @@ module DocsOrganizer unsorted.each do |k| puts <<-S * WARNING: #{klass.name} is not included in DocsOrganizer::class_sort_order. Please place this -module in it's correct topological order. +module in its correct topological order. S end @@ -253,8 +253,9 @@ S parse_log = [] html_start_to_toc_start = <<-S - + + DragonRuby Game Toolkit Documentation @@ -342,6 +343,11 @@ S __docs_append_true_line__ true_lines, current_true_line, parse_log __docs_append_true_line__ true_lines, l, parse_log current_true_line = "" + elsif l.start_with? "***** " + parse_log << "- Header detected." + __docs_append_true_line__ true_lines, current_true_line, parse_log + __docs_append_true_line__ true_lines, l, parse_log + current_true_line = "" else current_true_line += l.rstrip + " " end @@ -373,6 +379,9 @@ S text = text.gsub("]", "-") text = text.gsub(":", "-") text = text.gsub(" ", "-") + text = text.gsub(".", "-") + text = text.gsub(",", "-") + text = text.gsub("?", "-") text end @@ -434,6 +443,15 @@ S link_id = text_to_id.call l # toc_html += "" content_html += "

#{__docs_line_to_html__ l, parse_log}

\n" + elsif l.start_with? "**** " + parse_log << "- H5 detected." + content_html += close_list_if_needed.call inside_ul, inside_ol + inside_ol = false + inside_ul = false + formatted_html = __docs_line_to_html__ l, parse_log + link_id = text_to_id.call l + # toc_html += "" + content_html += "
#{__docs_line_to_html__ l, parse_log}
\n" elsif l.strip.length == 0 && !inside_pre # do nothing elsif l.start_with? "#+begin_src" diff --git a/dragon/draw.rb b/dragon/draw.rb index 128624d..7136994 100644 --- a/dragon/draw.rb +++ b/dragon/draw.rb @@ -1,4 +1,4 @@ -# Contributors outside of DragonRuby who also hold Copyright: Nick Sandberg +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # draw.rb has been released under MIT (*only this file*). @@ -6,197 +6,44 @@ module GTK class Runtime module Draw - - def execute_draw_order pass - # Don't change this draw order unless you understand - # the implications. - render_solids pass - render_static_solids pass - render_sprites pass - render_static_sprites pass - render_primitives pass - render_static_primitives pass - render_labels pass - render_static_labels pass - render_lines pass - render_static_lines pass - render_borders pass - render_static_borders pass - end - def primitives pass if $top_level.respond_to? :primitives_override return $top_level.tick_render @args, pass end - execute_draw_order pass + fn.each_send pass.solids, self, :draw_solid + fn.each_send pass.static_solids, self, :draw_solid + fn.each_send pass.sprites, self, :draw_sprite + fn.each_send pass.static_sprites, self, :draw_sprite + fn.each_send pass.primitives, self, :draw_primitive + fn.each_send pass.static_primitives, self, :draw_primitive + fn.each_send pass.labels, self, :draw_label + fn.each_send pass.static_labels, self, :draw_label + fn.each_send pass.lines, self, :draw_line + fn.each_send pass.static_lines, self, :draw_line + fn.each_send pass.borders, self, :draw_border + fn.each_send pass.static_borders, self, :draw_border if !$gtk.production - # pass.debug.each { |r| draw_primitive r } - idx = 0 - length = pass.debug.length - while idx < length - draw_primitive (pass.debug.at idx) - idx += 1 - end - - # pass.static_debug.each { |r| draw_primitive r } - idx = 0 - length = pass.static_debug.length - while idx < length - draw_primitive (pass.static_debug.at idx) - idx += 1 - end + fn.each_send pass.debug, self, :draw_primitive + fn.each_send pass.static_debug, self, :draw_primitive end - # pass.reserved.each { |r| draw_primitive r } - idx = 0 - length = pass.reserved.length - while idx < length - draw_primitive (pass.reserved.at idx) - idx += 1 - end - - # pass.static_reserved.each { |r| draw_primitive r } - idx = 0 - length = pass.static_reserved.length - while idx < length - draw_primitive (pass.static_reserved.at idx) - idx += 1 - end + fn.each_send pass.reserved, self, :draw_primitive + fn.each_send pass.static_reserved, self, :draw_primitive rescue Exception => e pause! pretty_print_exception_and_export! e end - - def render_solids pass - # pass.solids.each { |s| draw_solid s } - # while loops are faster than each with block - idx = 0 - length = pass.solids.length - while idx < pass.solids.length - draw_solid (pass.solids.at idx) # accessing an array using .value instead of [] is faster - idx += 1 - end - end - - def render_static_solids pass - # pass.static_solids.each { |s| draw_solid s } - idx = 0 - length = pass.static_solids.length - while idx < length - draw_solid (pass.static_solids.at idx) - idx += 1 - end - end - - def render_sprites pass - # pass.sprites.each { |s| draw_sprite s } - idx = 0 - length = pass.sprites.length - while idx < length - draw_sprite (pass.sprites.at idx) - idx += 1 - end - end - - def render_static_sprites pass - # pass.static_sprites.each { |s| draw_sprite s } - idx = 0 - length = pass.static_sprites.length - while idx < length - draw_sprite (pass.static_sprites.at idx) - idx += 1 - end - end - - def render_primitives pass - # pass.primitives.each { |p| draw_primitive p } - idx = 0 - length = pass.primitives.length - while idx < length - draw_primitive (pass.primitives.at idx) - idx += 1 - end - end - - def render_static_primitives pass - # pass.static_primitives.each { |p| draw_primitive p } - idx = 0 - length = pass.static_primitives.length - while idx < length - draw_primitive (pass.static_primitives.at idx) - idx += 1 - end - end - - def render_labels pass - # pass.labels.each { |l| draw_label l } - idx = 0 - length = pass.labels.length - while idx < length - draw_label (pass.labels.at idx) - idx += 1 - end - end - - def render_static_labels pass - # pass.static_labels.each { |l| draw_label l } - idx = 0 - length = pass.static_labels.length - while idx < length - draw_label (pass.static_labels.at idx) - idx += 1 - end - end - - def render_lines pass - # pass.lines.each { |l| draw_line l } - idx = 0 - length = pass.lines.length - while idx < length - draw_line (pass.lines.at idx) - idx += 1 - end - end - - def render_static_lines pass - # pass.static_lines.each { |l| draw_line l } - idx = 0 - length = pass.static_lines.length - while idx < pass.static_lines.length - draw_line (pass.static_lines.at idx) - idx += 1 - end - end - - def render_borders pass - # pass.borders.each { |b| draw_border b } - idx = 0 - length = pass.borders.length - while idx < length - draw_border (pass.borders.at idx) - idx += 1 - end - end - - def render_static_borders pass - # pass.static_borders.each { |b| draw_border b } - idx = 0 - length = pass.static_borders.length - while idx < length - draw_border (pass.static_borders.at idx) - idx += 1 - end - end - def draw_solid s return unless s if s.respond_to? :draw_override s.draw_override @ffi_draw else - @ffi_draw.draw_solid s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a + @ffi_draw.draw_solid_2 s.x, s.y, s.w, s.h, + s.r, s.g, s.b, s.a, + (s.blendmode_enum || 1) end rescue Exception => e raise_conversion_for_rendering_failed s, e, :solid @@ -207,14 +54,15 @@ module GTK if s.respond_to? :draw_override s.draw_override @ffi_draw else - @ffi_draw.draw_sprite_3 s.x, s.y, s.w, s.h, - s.path.s_or_default, + @ffi_draw.draw_sprite_4 s.x, s.y, s.w, s.h, + (s.path || '').to_s, s.angle, s.a, s.r, s.g, s.b, s.tile_x, s.tile_y, s.tile_w, s.tile_h, !!s.flip_horizontally, !!s.flip_vertically, s.angle_anchor_x, s.angle_anchor_y, - s.source_x, s.source_y, s.source_w, s.source_h + s.source_x, s.source_y, s.source_w, s.source_h, + (s.blendmode_enum || 1) end rescue Exception => e raise_conversion_for_rendering_failed s, e, :sprite @@ -225,7 +73,7 @@ module GTK if s.respond_to? :draw_override s.draw_override @ffi_draw else - @ffi_draw.draw_screenshot s.path.s_or_default, + @ffi_draw.draw_screenshot (s.path || '').to_s, s.x, s.y, s.w, s.h, s.angle, s.a, s.r, s.g, s.b, @@ -243,10 +91,13 @@ module GTK if l.respond_to? :draw_override l.draw_override @ffi_draw else - @ffi_draw.draw_label l.x, l.y, l.text.s_or_default, - l.size_enum, l.alignment_enum, - l.r, l.g, l.b, l.a, - l.font.s_or_default(nil) + @ffi_draw.draw_label_3 l.x, l.y, + (l.text || '').to_s, + l.size_enum, l.alignment_enum, + l.r, l.g, l.b, l.a, + l.font, + (l.vertical_alignment_enum || 2), + (l.blendmode_enum || 1) end rescue Exception => e raise_conversion_for_rendering_failed l, e, :label @@ -257,7 +108,21 @@ module GTK if l.respond_to? :draw_override l.draw_override @ffi_draw else - @ffi_draw.draw_line l.x, l.y, l.x2, l.y2, l.r, l.g, l.b, l.a + if l.x2 + @ffi_draw.draw_line_2 l.x, l.y, l.x2, l.y2, + l.r, l.g, l.b, l.a, + (l.blendmode_enum || 1) + else + w = l.w || 0 + w = 1 if w == 0 + h = l.h || 0 + h = 1 if h == 0 + @ffi_draw.draw_line_2 l.x, l.y, + l.x + w - 1, + l.y + h - 1, + l.r, l.g, l.b, l.a, + (l.blendmode_enum || 1) + end end rescue Exception => e raise_conversion_for_rendering_failed l, e, :line @@ -268,7 +133,9 @@ module GTK if s.respond_to? :draw_override s.draw_override @ffi_draw else - @ffi_draw.draw_border s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a + @ffi_draw.draw_border_2 s.x, s.y, s.w, s.h, + s.r, s.g, s.b, s.a, + (s.blendmode_enum || 1) end rescue Exception => e raise_conversion_for_rendering_failed s, e, :border @@ -288,7 +155,6 @@ module GTK pause! pretty_print_exception_and_export! e end - end end end diff --git a/dragon/framerate.rb b/dragon/framerate.rb index 2550443..a9504a6 100644 --- a/dragon/framerate.rb +++ b/dragon/framerate.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # framerate.rb has been released under MIT (*only this file*). diff --git a/dragon/framerate_diagnostics.rb b/dragon/framerate_diagnostics.rb index 4586472..6985011 100644 --- a/dragon/framerate_diagnostics.rb +++ b/dragon/framerate_diagnostics.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # framerate_diagnostics.rb has been released under MIT (*only this file*). @@ -115,7 +116,7 @@ If this warning is getting annoying put the following in your tick method: def framerate_diagnostics_primitives [ - { x: 0, y: 93.from_top, w: 500, h: 93, a: 128 }.solid, + { x: 0, y: 93.from_top, w: 500, h: 93, a: 128 }.solid!, { x: 5, y: 5.from_top, @@ -124,7 +125,7 @@ If this warning is getting annoying put the following in your tick method: g: 255, b: 255, size_enum: -2 - }.label, + }.label!, { x: 5, y: 20.from_top, @@ -133,7 +134,7 @@ If this warning is getting annoying put the following in your tick method: g: 255, b: 255, size_enum: -2 - }.label, + }.label!, { x: 5, y: 35.from_top, @@ -142,7 +143,7 @@ If this warning is getting annoying put the following in your tick method: g: 255, b: 255, size_enum: -2 - }.label, + }.label!, { x: 5, y: 50.from_top, @@ -151,7 +152,7 @@ If this warning is getting annoying put the following in your tick method: g: 255, b: 255, size_enum: -2 - }.label, + }.label!, { x: 5, y: 65.from_top, @@ -160,7 +161,7 @@ If this warning is getting annoying put the following in your tick method: g: 255, b: 255, size_enum: -2 - }.label, + }.label!, ] end diff --git a/dragon/geometry.rb b/dragon/geometry.rb index c2974db..6bceea8 100644 --- a/dragon/geometry.rb +++ b/dragon/geometry.rb @@ -5,6 +5,22 @@ module GTK module Geometry + def self.rotate_point point, angle, around = nil + s = Math.sin a.to_radians + c = Math.cos a.to_radians + px = point.x + py = point.y + cx = 0 + cy = 0 + if around + cx = around.x + cy = around.y + end + + point.merge(x: ((px - cx) * c - (py - cy) * s) + cx, + y: ((px - cx) * s + (py - cy) * c) + cy) + end + # Returns f(t) for a cubic Bezier curve. def self.cubic_bezier t, a, b, c, d s = 1 - t @@ -86,7 +102,7 @@ module GTK rescue Exception => e raise e, <<-S * ERROR: -center_inside_rect for self #{self} and other_rect #{other_rect}. Failed with exception #{e}. +center_inside_rect for self #{self} and other_rect #{other_rect}.\n#{e}. S end @@ -103,7 +119,7 @@ S rescue Exception => e raise e, <<-S * ERROR: -center_inside_rect_x for self #{self} and other_rect #{other_rect}. Failed with exception #{e}. +center_inside_rect_x for self #{self} and other_rect #{other_rect}.\n#{e}. S end @@ -120,7 +136,7 @@ S rescue Exception => e raise e, <<-S * ERROR: -center_inside_rect_y for self #{self} and other_rect #{other_rect}. Failed with exception #{e}. +center_inside_rect_y for self #{self} and other_rect #{other_rect}.\n#{e}. S end @@ -265,10 +281,10 @@ S # @gtk 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 false if ((rect_one.x + rect_one.w) - tolerance) < (rect_two.x + tolerance) + return false if (rect_one.x + tolerance) > ((rect_two.x + rect_two.w) - tolerance) + return false if ((rect_one.y + rect_one.h) - tolerance) < (rect_two.y + tolerance) + return false if (rect_one.y + tolerance) > ((rect_two.y + rect_two.h) - tolerance) return true rescue Exception => e context_help_rect_one = (rect_one.__help_contract_implementation contract_intersect_rect?)[:not_implemented_methods] @@ -296,6 +312,7 @@ S - rect_one: #{rect_one} - rect_two: #{rect_two} #{context_help} +\n#{e} S end @@ -306,14 +323,14 @@ S 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}." + raise e, ":to_square failed for size: #{size} x: #{x} y: #{y} anchor_x: #{anchor_x} anchor_y: #{anchor_y}.\n#{e}" end # @gtk 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}." + raise e, ":distance failed for point_one: #{point_one} point_two #{point_two}.\n#{e}" end # @gtk @@ -322,31 +339,34 @@ S 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}." + raise e, ":angle_from failed for start_point: #{start_point} end_point: #{end_point}.\n#{e}" end # @gtk 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}." + raise e, ":angle_to failed for start_point: #{start_point} end_point: #{end_point}.\n#{e}" end # @gtk 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}" + raise e, ":point_inside_circle? failed for point: #{point} circle_center_point: #{circle_center_point} radius: #{radius}.\n#{e}" end # @gtk def self.inside_rect? inner_rect, outer_rect, tolerance = 0.0 + return nil if !inner_rect + return nil if !outer_rect + inner_rect.x + tolerance >= outer_rect.x - tolerance && - inner_rect.right - tolerance <= outer_rect.right + tolerance && + (inner_rect.x + inner_rect.w) - tolerance <= (outer_rect.x + outer_rect.w) + tolerance && inner_rect.y + tolerance >= outer_rect.y - tolerance && - inner_rect.top - tolerance <= outer_rect.top + tolerance + (inner_rect.y + inner_rect.h) - tolerance <= (outer_rect.y + outer_rect.h) + tolerance rescue Exception => e - raise e, ":inside_rect? failed for inner_rect: #{inner_rect} outer_rect: #{outer_rect}." + raise e, ":inside_rect? failed for inner_rect: #{inner_rect} outer_rect: #{outer_rect}.\n#{e}" end # @gtk @@ -381,7 +401,7 @@ S 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}." + 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}.\n#{e}" end # @gtk @@ -395,7 +415,21 @@ S 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)]." + raise e, ":scale_rect failed for rect: #{rect} percentage: #{percentage} anchors [#{anchor_x} (x), #{anchor_y} (y)].\n#{e}" + end + + def self.rect_to_line rect + l = rect.to_hash.line + l.merge(x2: l.x + l.w - 1, + y2: l.y + l.h) + end + + def self.rect_center_point rect + { x: rect.x + rect.w.half, y: rect.y + rect.h.half } + end + + def rect_center_point + Geometry.rect_center_point self end end # module Geometry end # module GTK diff --git a/dragon/grid.rb b/dragon/grid.rb index c2a6a43..2c808fc 100644 --- a/dragon/grid.rb +++ b/dragon/grid.rb @@ -185,5 +185,13 @@ module GTK def bottom_right [@right, @bottom].point end + + def x + 0 + end + + def y + 0 + end end end diff --git a/dragon/help.rb b/dragon/help.rb deleted file mode 100644 index 9644347..0000000 --- a/dragon/help.rb +++ /dev/null @@ -1,72 +0,0 @@ -# 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 degrees) -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/hotload.rb b/dragon/hotload.rb index 1cb062a..1196e2b 100644 --- a/dragon/hotload.rb +++ b/dragon/hotload.rb @@ -10,7 +10,14 @@ module GTK def hotload_init @hotload_if_needed = 0 @mailbox_if_needed = 0 + + # schema for file_mtimes + # { FILE_PATH: { current: (Time as Fixnum), + # last: (Time as Fixnum) }, + # FILE_PATH: { current: (Time as Fixnum), + # last: (Time as Fixnum) } } @file_mtimes = { } + @suppress_mailbox = true files_to_reload.each { |f| init_mtimes f } init_mtimes 'app/mailbox.rb' @@ -48,6 +55,7 @@ module GTK 'dragon/symbol.rb', 'dragon/numeric_deprecated.rb', 'dragon/numeric.rb', + 'dragon/hash_deprecated.rb', 'dragon/hash.rb', 'dragon/outputs_deprecated.rb', 'dragon/array_docs.rb', @@ -80,6 +88,7 @@ module GTK 'dragon/trace.rb', 'dragon/readme_docs.rb', 'dragon/hotload_client.rb', + 'dragon/wizards.rb', 'dragon/ios_wizard.rb', 'dragon/itch_wizard.rb', ] + core_files_to_reload + @required_files @@ -98,10 +107,8 @@ module GTK end def init_mtimes file - current_key = "current_#{file}".to_sym - last_key = "last_#{file}".to_sym - @file_mtimes[current_key] ||= @ffi_file.mtime(file) - @file_mtimes[last_key] ||= @ffi_file.mtime(file) + @file_mtimes[file] ||= { current: @ffi_file.mtime(file), + last: @ffi_file.mtime(file) } end def hotload_source_files @@ -133,25 +140,36 @@ module GTK end def hotload_if_needed + return if Kernel.tick_count < 0 hotload_source_files check_mailbox end def on_load_succeeded file - @rcb_sender.files_reloaded << file - @rcb_sender.reloaded_files << file + self.files_reloaded << file + self.reloaded_files << file Trace.untrace_classes! end + def reset_all_mtimes + @file_mtimes.each do |file, _| + @file_mtimes[file].current = @ffi_file.mtime(file) + @file_mtimes[file].last = @file_mtimes[file].current + end + + files_to_reload.each do |file, _| + @file_mtimes[file] ||= {} + @file_mtimes[file].current = @ffi_file.mtime(file) + @file_mtimes[file].last = @file_mtimes[file].current + end + end + def reload_if_needed file, force = false - current_key = "current_#{file}".to_sym - last_key = "last_#{file}".to_sym - @file_mtimes[current_key] ||= nil - @file_mtimes[last_key] ||= nil - @file_mtimes[current_key] = @ffi_file.mtime(file) - return if !force && @file_mtimes[last_key] == @file_mtimes[current_key] + @file_mtimes[file] ||= { current: @ffi_file.mtime(file), last: @ffi_file.mtime(file) } + @file_mtimes[file].current = @ffi_file.mtime(file) + return if !force && @file_mtimes[file].current == @file_mtimes[file].last on_load_succeeded file if reload_ruby_file file - @file_mtimes[last_key] = @file_mtimes[current_key] + @file_mtimes[file].last = @file_mtimes[file].current end end end diff --git a/dragon/inputs.rb b/dragon/inputs.rb index 0ae4f86..163fc16 100644 --- a/dragon/inputs.rb +++ b/dragon/inputs.rb @@ -191,6 +191,30 @@ module GTK } end + def self.method_to_key_hash + return @method_to_key_hash if @method_to_key_hash + @method_to_key_hash = {} + string_representation_overrides ||= { + backspace: '\b' + } + char_to_method_hash.each do |k, v| + v.each do |vi| + t = { char_or_raw_key: k } + + if k.is_a? Numeric + t[:raw_key] = k + t[:string_representation] = "raw_key == #{k}" + else + t[:char] = k + t[:string_representation] = "\"#{k.strip}\"" + end + + @method_to_key_hash[vi] = t + end + end + @method_to_key_hash + end + def self.char_to_method char, int = nil methods = char_to_method_hash[char] || char_to_method_hash[int] methods ? methods.dup : [char.to_sym || int] @@ -305,24 +329,32 @@ S end def method_missing m, *args - begin - define_singleton_method(m) do - r = self.instance_variable_get("@#{m.without_ending_bang}".to_sym) - clear_key m - return r + if KeyboardKeys.method_to_key_hash[m.without_ending_bang] + begin + define_singleton_method(m) do + r = self.instance_variable_get("@#{m.without_ending_bang}".to_sym) + clear_key m + return r + end + + return self.send m + rescue Exception => e + log_important "#{e}" end - - return self.send m - rescue Exception => e - log_important "#{e}" end + did_you_mean = KeyboardKeys.method_to_key_hash.find_all do |k, v| + k.to_s[0..1] == m.to_s[0..1] + end.map {|k, v| ":#{k} (#{v[:string_representation]})" } + did_you_mean_string = "" + did_you_mean_string = ". Did you mean #{did_you_mean.join ", "}?" + raise <<-S * ERROR: -There is no member on the keyboard called #{m}. Here is a to_s representation of what's available: - -#{KeyboardKeys.char_to_method_hash.map { |k, v| "[#{k} => #{v.join(",")}]" }.join(" ")} +#{KeyboardKeys.method_to_key_hash.map { |k, v| "** :#{k} #{v.string_representation}" }.join("\n")} +There is no key on the keyboard called :#{m}#{did_you_mean_string}. +Full list of available keys =:points_up:=. S end @@ -706,6 +738,10 @@ module GTK (controller_one && controller_one.directional_vector) end + def directional_angle + keyboard.directional_angle || (controller_one && controller_one.directional_angle) + end + # Returns a signal indicating right (`1`), left (`-1`), or neither ('0'). # # @return [Integer] diff --git a/dragon/ios_wizard.rb b/dragon/ios_wizard.rb index c1c032d..a64cdc9 100644 --- a/dragon/ios_wizard.rb +++ b/dragon/ios_wizard.rb @@ -1,19 +1,11 @@ -# Contributors outside of DragonRuby who also hold Copyright: Michał Dudziński +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # ios_wizard.rb has been released under MIT (*only this file*). -class WizardException < Exception - attr_accessor :console_primitives - - def initialize *console_primitives - @console_primitives = console_primitives - end -end - -class IOSWizard - include Metadata +# Contributors outside of DragonRuby who also hold Copyright: Michał Dudziński +class IOSWizard < Wizard def initialize @doctor_executed_at = 0 end @@ -26,23 +18,45 @@ class IOSWizard @steps ||= [] end - def steps_development_build + def prerequisite_steps [ :check_for_xcode, :check_for_brew, :check_for_certs, - :check_for_device, - :check_for_dev_profile, + ] + end + + def app_metadata_retrieval_steps + [ :determine_team_identifier, :determine_app_name, :determine_app_id, - :blow_away_temp, + ] + end + + def steps_development_build + [ + *prerequisite_steps, + + :check_for_device, + :check_for_dev_profile, + + *app_metadata_retrieval_steps, + + :clear_tmp_directory, :stage_app, + :development_write_info_plist, + :write_entitlements_plist, :compile_icons, - :create_payload_directory, + :clear_payload_directory, + + :create_payload_directory_dev, + + :create_payload, :code_sign_payload, + :create_ipa, :deploy ] @@ -50,20 +64,27 @@ class IOSWizard def steps_production_build [ - :check_for_xcode, - :check_for_brew, - :check_for_certs, + *prerequisite_steps, + :check_for_distribution_profile, - :determine_team_identifier, - :determine_app_name, - :determine_app_id, - :blow_away_temp, + :determine_app_version, + + *app_metadata_retrieval_steps, + + :clear_tmp_directory, :stage_app, + :production_write_info_plist, + :write_entitlements_plist, :compile_icons, - :create_payload_directory, + :clear_payload_directory, + + :create_payload_directory_prod, + + :create_payload, :code_sign_payload, + :create_ipa, :print_publish_help ] @@ -84,31 +105,25 @@ class IOSWizard sprite_path end - def start opts = {} - @opts = opts + def start opts = nil + @opts = opts || {} - unless $gtk.args.fn.eq_any? @opts[:env], :dev, :prod + if !(@opts.is_a? Hash) || !($gtk.args.fn.eq_any? @opts[:env], :dev, :prod) raise WizardException.new( - "* $wizards.ios.start needs to be provided an environment option.", - "** For development builds type: $wizards.ios.start env: :dev", - "** For production builds type: $wizards.ios.start env: :prod" - ) + "* $wizards.ios.start needs to be provided an environment option.", + "** For development builds type: $wizards.ios.start env: :dev", + "** For production builds type: $wizards.ios.start env: :prod" + ) end @production_build = (@opts[:env] == :prod) - - @version = determine_app_version @opts - log_info "I will be using version: '#{@version}'" if @production_build - @steps = steps_development_build @steps = steps_production_build if @production_build @certificate_name = nil + @app_version = opts[:version] + @app_version = "1.0" if @opts[:env] == :dev && !@app_version init_wizard_status - if @production_build - log_info "Starting iOS Wizard so we can create a production build." - else - log_info "Starting iOS Wizard so we can deploy to your device." - end + log_info "Starting iOS Wizard so we can deploy to your device." @start_at = Kernel.global_tick_count steps.each do |m| log_info "Running step ~:#{m}~." @@ -128,8 +143,10 @@ class IOSWizard log "=" * $console.console_text_width else log_error e.to_s + log e.__backtrace_to_org__ end + init_wizard_status $console.set_command "$wizards.ios.start env: :#{@opts[:env]}" end @@ -236,25 +253,69 @@ class IOSWizard return "profiles/development.mobileprovision" end + def ios_metadata_template + <<-S +# ios_metadata.txt is used by the Pro version of DragonRuby Game Toolkit to create iOS apps. +# Information about the Pro version can be found at: http://dragonruby.org/toolkit/game#purchase + +# teamid needs to be set to your assigned Team Id which can be found at https://developer.apple.com/account/#/membership/ +teamid= +# appid needs to be set to your application identifier which can be found at https://developer.apple.com/account/resources/identifiers/list +appid= +# appname is the name you want to show up underneath the app icon on the device. Keep it under 10 characters. +appname= +S + end + + def ios_metadata + contents = $gtk.read_file 'metadata/ios_metadata.txt' + + if !contents + $gtk.write_file 'metadata/ios_metadata.txt', ios_metadata_template + contents = $gtk.read_file 'metadata/ios_metadata.txt' + end + + kvps = contents.each_line + .reject { |l| l.strip.length == 0 || (l.strip.start_with? "#") } + .map do |l| + key, value = l.split("=") + [key.strip.to_sym, value.strip] + end.flatten + Hash[*kvps] + end + + def game_metadata + contents = $gtk.read_file 'metadata/game_metadata.txt' + + kvps = contents.each_line + .reject { |l| l.strip.length == 0 || (l.strip.start_with? "#") } + .map do |l| + key, value = l.split("=") + [key.strip.to_sym, value.strip] + end.flatten + Hash[*kvps] + end + + def raise_ios_metadata_required + raise WizardException.new( + "* mygame/metadata/ios_metadata.txt needs to be filled out.", + "You need to update metadata/ios_metadata.txt with a valid teamid, appname, and appid.", + "Instructions for where the values should come from are within metadata/ios_metadata.txt." + ) + end + def determine_team_identifier - @team_name = (team_identifier_from_provisioning_profile @opts[:env]) - log_info "Team Identifier is: #{@team_name}" + @team_id = (ios_metadata.teamid || "") + raise_ios_metadata_required if @team_id.strip.length == 0 + log_info "Team Identifer is: #{@team_id}" end def determine_app_name - @app_name = (provisioning_profile_xml @opts[:env])[:children].first[:children].first[:children][1][:children].first[:data] + @app_name = (ios_metadata.appname || "") + raise_ios_metadata_required if @app_name.strip.length == 0 log_info "App name is: #{@app_name}." end - def determine_app_version opts - version = @opts[:version] - unless version - version = get_metadata[:version] - version = version.start_with?('#') ? '1.0' : version.split('=').last - end - version.to_s - end - def provisioning_profile_xml environment xml = $gtk.read_file (provisioning_profile_path environment) scrubbed = xml.each_line.map do |l| @@ -275,36 +336,9 @@ class IOSWizard $gtk.parse_xml scrubbed end - def app_id_from_provisioning_profile environment - application_identifier_index = (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children][0][:children][0][:data] - (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children].each.with_index do |node, i| - if node[:children] && node[:children][0] && node[:children][0][:data] == "application-identifier" - application_identifier_index = i - break - end - end - - app_id_with_team_identifier = (provisioning_profile_xml environment)[:children].first[:children].first[:children][13][:children][application_identifier_index + 1][:children].first[:data] - team_identifier = team_identifier_from_provisioning_profile environment - app_id_with_team_identifier.gsub "#{team_identifier}.", "" - end - - def team_identifier_from_provisioning_profile environment - team_identifier_index = (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children][0][:children][0][:data] - - (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children].each.with_index do |node, i| - if node[:children] && node[:children][0] && node[:children][0][:data] == "com.apple.developer.team-identifier" - team_identifier_index = i - break - end - end - - (provisioning_profile_xml environment)[:children].first[:children].first[:children][13][:children][team_identifier_index + 1][:children].first[:data] - end - def determine_app_id - @app_id = app_id_from_provisioning_profile @opts[:env] - + @app_id = ios_metadata.appid + raise_ios_metadata_required if @app_id.strip.length == 0 log_info "App Identifier is set to : #{@app_id}" end @@ -323,7 +357,7 @@ class IOSWizard end end - def blow_away_temp + def clear_tmp_directory sh "rm -rf #{tmp_directory}" end @@ -377,7 +411,7 @@ class IOSWizard "** 1. Open Xcode.", "** 2. Log into your developer account. Xcode -> Preferences -> Accounts.", { w: 700, h: 98, path: get_reserved_sprite("login-xcode.png") }, - "** 3. After logging in, select Manage Certificates...", + "** 3. After loggin in, select Manage Certificates...", { w: 700, h: 115, path: get_reserved_sprite("manage-certificates.png") }, "** 4. Add a certificate for Apple Development.", { w: 700, h: 217, path: get_reserved_sprite("add-cert.png") }, @@ -464,7 +498,8 @@ XML log_info "Creating Entitlements.plist" - $gtk.write_file_root "tmp/ios/Entitlements.plist", entitlement_plist_string.gsub(":app_id", "#{@team_name}.#{@app_id}").strip + $gtk.write_file_root "tmp/ios/Entitlements.plist", entitlement_plist_string.gsub(":app_id", "#{@team_id}.#{@app_id}").strip + $gtk.write_file_root "tmp/ios/Entitlements.txt", entitlement_plist_string.gsub(":app_id", "#{@team_id}.#{@app_id}").strip sh "/usr/bin/plutil -convert binary1 \"#{tmp_directory}/Entitlements.plist\"" sh "/usr/bin/plutil -convert xml1 \"#{tmp_directory}/Entitlements.plist\"" @@ -502,15 +537,15 @@ XML CFBundleExecutable :app_name CFBundleInfoDictionaryVersion - 6.0 + :app_version CFBundlePackageType APPL CFBundleShortVersionString - 5.6 + :app_version CFBundleSignature ???? CFBundleVersion - 5.6 + :app_version CFBundleIcons CFBundlePrimaryIcon @@ -659,13 +694,13 @@ XML CFBundleIdentifier :app_id CFBundleInfoDictionaryVersion - 6.0 + :app_version CFBundleName :app_name CFBundlePackageType APPL CFBundleShortVersionString - 5.2 + :app_version CFBundleSignature ???? CFBundleSupportedPlatforms @@ -673,7 +708,7 @@ XML iPhoneOS CFBundleVersion - 5.2 + :app_version DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild @@ -759,6 +794,7 @@ XML info_plist_string.gsub!(":app_id", @app_id) $gtk.write_file_root "tmp/ios/#{@app_name}.app/Info.plist", info_plist_string.strip + $gtk.write_file_root "tmp/ios/Info.txt", info_plist_string.strip @info_plist_written = true end @@ -812,13 +848,13 @@ XML CFBundleIdentifier :app_id CFBundleInfoDictionaryVersion - 6.0 + :app_version CFBundleName :app_name CFBundlePackageType APPL CFBundleShortVersionString - :version + :app_version CFBundleSignature ???? CFBundleSupportedPlatforms @@ -826,7 +862,7 @@ XML iPhoneOS CFBundleVersion - :version + :app_version DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild @@ -910,9 +946,10 @@ XML info_plist_string.gsub!(":app_name", @app_name) info_plist_string.gsub!(":app_id", @app_id) - info_plist_string.gsub!(":version", @version); + info_plist_string.gsub!(":app_version", @app_version) $gtk.write_file_root "tmp/ios/#{@app_name}.app/Info.plist", info_plist_string.strip + $gtk.write_file_root "tmp/ios/Info.txt", info_plist_string.strip @info_plist_written = true end @@ -934,28 +971,53 @@ XML "#{relative_path}/#{$gtk.cli_arguments[:dragonruby]}" end - def write_ip_address + def embed_mobileprovision + sh %Q[cp #{@provisioning_profile_path} "#{app_path}/embedded.mobileprovision"] + sh %Q[/usr/bin/plutil -convert binary1 "#{app_path}/Info.plist"] + end + + def clear_payload_directory + sh %Q[rm "#{@app_name}".ipa] + sh %Q[rm -rf "#{app_path}/app"] + sh %Q[rm -rf "#{app_path}/sounds"] + sh %Q[rm -rf "#{app_path}/sprites"] + sh %Q[rm -rf "#{app_path}/data"] + sh %Q[rm -rf "#{app_path}/fonts"] + end + + def stage_app + sh %Q[cp -r "#{root_folder}/app/" "#{app_path}/app/"] + sh %Q[cp -r "#{root_folder}/sounds/" "#{app_path}/sounds/"] + sh %Q[cp -r "#{root_folder}/sprites/" "#{app_path}/sprites/"] + sh %Q[cp -r "#{root_folder}/data/" "#{app_path}/data/"] + sh %Q[cp -r "#{root_folder}/fonts/" "#{app_path}/fonts/"] + end + + def create_payload + sh %Q[mkdir -p #{tmp_directory}/ipa_root/Payload] + sh %Q[cp -r "#{app_path}" "#{tmp_directory}/ipa_root/Payload"] + sh %Q[chmod -R 755 "#{tmp_directory}/ipa_root/Payload"] + end + + def create_payload_directory_dev + # write dev machine's ip address for hotloading $gtk.write_file "app/server_ip_address.txt", $gtk.ffi_misc.get_local_ip_address.strip + + embed_mobileprovision + clear_payload_directory + stage_app end - def create_payload_directory - sh "cp #{@provisioning_profile_path} \"#{app_path}/embedded.mobileprovision\"" - sh "/usr/bin/plutil -convert binary1 \"#{app_path}/Info.plist\"" - write_ip_address - sh "rm \"#{@app_name}\".ipa" - sh "rm -rf \"#{app_path}/app\"" - sh "rm -rf \"#{app_path}/sounds\"" - sh "rm -rf \"#{app_path}/sprites\"" - sh "rm -rf \"#{app_path}/data\"" - sh "rm -rf \"#{app_path}/fonts\"" - sh "cp -r \"#{root_folder}/app/\" \"#{app_path}/app/\"" - sh "cp -r \"#{root_folder}/sounds/\" \"#{app_path}/sounds/\"" - sh "cp -r \"#{root_folder}/sprites/\" \"#{app_path}/sprites/\"" - sh "cp -r \"#{root_folder}/data/\" \"#{app_path}/data/\"" - sh "cp -r \"#{root_folder}/fonts/\" \"#{app_path}/fonts/\"" - sh "mkdir -p #{tmp_directory}/ipa_root/Payload" - sh "cp -r \"#{app_path}\" \"#{tmp_directory}/ipa_root/Payload\"" - sh "chmod -R 755 \"#{tmp_directory}/ipa_root/Payload\"" + def create_payload_directory_prod + # production builds does not hotload ip address + sh %Q[rm "#{root_folder}/app/server_ip_address.txt"] + + embed_mobileprovision + stage_app + + # production build marker + sh %Q[mkdir -p "#{app_path}/metadata/"] + sh %Q[touch metadata/DRAGONRUBY_PRODUCTION_BUILD] end def create_ipa @@ -988,10 +1050,22 @@ SCRIPT end def print_publish_help - log_info "Go to https://appstoreconnect.apple.com/apps and create an App if you haven't already done so." - log_info "Go to https://appleid.apple.com and create a 'Application Specific Password'." - log_info "To upload your app, Download Transporter from the App Store https://apps.apple.com/us/app/transporter/id1450874784?mt=12." - log_info "Your app is located at ./tmp/ios/#{@app_name}.ipa" + has_transporter = (sh "ls /Applications/Transporter.app").include? "Contents" + if !has_transporter + $gtk.openurl "https://apps.apple.com/us/app/transporter/id1450874784?mt=12" + $console.set_command "$wizards.ios.start env: :#{@opts[:env]}, version: \"#{@opts[:version]}\"" + raise WizardException.new( + "* To upload your app, Download Transporter from the App Store https://apps.apple.com/us/app/transporter/id1450874784?mt=12." + ) + else + sh "mkdir ./tmp/ios/intermediary_artifacts" + sh "mv \"#{tmp_directory}/#{@app_name}.app\" #{tmp_directory}/intermediary_artifacts/" + sh "mv \"#{tmp_directory}/do_zip.sh\" #{tmp_directory}/intermediary_artifacts" + sh "mv \"#{tmp_directory}/Entitlements.plist\" #{tmp_directory}/intermediary_artifacts" + sh "mv \"#{tmp_directory}/ipa_root\" #{tmp_directory}/intermediary_artifacts/" + sh "open /Applications/Transporter.app" + sh "open ./tmp/ios/" + end end def compile_icons @@ -1012,4 +1086,24 @@ S sh "cp -r \"#{root_folder}/native/\" \"#{app_path}/native/\"" sh "CODESIGN_ALLOCATE=\"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate\" /usr/bin/codesign -f -s \"#{@certificate_name}\" --entitlements #{tmp_directory}/Entitlements.plist \"#{tmp_directory}/#{@app_name}.app/native/ios-device/ext.dylib\"" end + + def set_version version + @app_version = version + start env: @opts[:env], version: version + end + + def app_version + log_info "Attempting to retrieve App Version from metadata/ios_metadata.txt." + ios_version_number = (ios_metadata.version || "").strip + if ios_version_number.length == 0 + log_info "Not found. Attempting to retrieve App Version from metadata/game_metadata.txt." + ios_version_number = (game_metadata.version || "").strip + end + ios_version_number + end + + def determine_app_version + @app_version = app_version + return if @app_version + end end diff --git a/dragon/itch_wizard.rb b/dragon/itch_wizard.rb index c0a69de..e976dc9 100644 --- a/dragon/itch_wizard.rb +++ b/dragon/itch_wizard.rb @@ -1,24 +1,34 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # itch_wizard.rb has been released under MIT (*only this file*). -class ItchWizard - include Metadata - +class ItchWizard < Wizard def steps [ :check_metadata, - :deploy + :deploy, ] end + def write_blank_metadata + $gtk.write_file metadata_file_path, <<-S.strip +#devid=myname +#devtitle=My Name +#gameid=mygame +#gametitle=My Game +#version=0.1 +#icon=metadata/icon.png +S + end + def check_metadata metadata_text = $gtk.read_file metadata_file_path if !metadata_text write_blank_metadata end - if metadata_text.each_line.to_a.length != 6 + if metadata_text.strip.each_line.to_a.length < 6 write_blank_metadata end @@ -30,70 +40,67 @@ class ItchWizard if metadata[:dev_id].start_with?("#") || !@dev_id log "* PROMPT: Please provide your username for Itch." - $console.set_command "$wizards.itch.set_dev_id \"your-itch-username\"" + $console.set_command "$wizards.itch.set_dev_id \"#{metadata[:dev_id]}\"" return :need_dev_id end if metadata[:dev_title].start_with?("#") || !@dev_title log "* PROMPT: Please provide developer's/company's name that you want displayed." - $console.set_command "$wizards.itch.set_dev_title \"Your Name\"" + $console.set_command "$wizards.itch.set_dev_title \"#{metadata[:dev_title]}\"" return :need_dev_title end if metadata[:game_id].start_with?("#") || !@game_id log "* PROMPT: Please provide the id for you game. This is the id you specified when you set up a new game page on Itch." - $console.set_command "$wizards.itch.set_game_id \"your-game-id\"" + $console.set_command "$wizards.itch.set_game_id \"#{metadata[:game_id]}\"" return :need_game_id end if metadata[:game_title].start_with?("#") || !@game_title log "* PROMPT: Please provide the display name for your game. (This can include spaces)" - $console.set_command "$wizards.itch.set_game_title \"Your Game\"" + $console.set_command "$wizards.itch.set_game_title \"#{metadata[:game_title]}\"" return :need_game_title end if metadata[:version].start_with?("#") || !@version log "* PROMPT: Please provide the version for your game." - $console.set_command "$wizards.itch.set_version \"1.0\"" + $console.set_command "$wizards.itch.set_version \"#{metadata[:version]}\"" return :need_version end if metadata[:icon].start_with?("#") || !@icon log "* PROMPT: Please provide icon path for your game." - $console.set_command "$wizards.itch.set_icon \"icon.png\"" + $console.set_command "$wizards.itch.set_icon \"#{metadata[:icon]}\"" return :need_icon end + puts "here!! success!!!" + return :success end def set_dev_id value @dev_id = value - write_metadata start end def set_dev_title value @dev_title = value - write_metadata start end def set_game_id value @game_id = value - write_metadata start end def set_game_title value @game_title = value - write_metadata start end def set_version value @version = value - write_metadata start end @@ -136,7 +143,7 @@ class ItchWizard end if @icon - text += "icon=metadata/#{@icon}\n" + text += "icon=#{@icon}\n" else text += "#icon=metadata/icon.png\n" end @@ -154,10 +161,25 @@ class ItchWizard def deploy log_info "* Running dragonruby-publish: #{package_command}" - results = $gtk.exec package_command + $gtk.openurl "http://itch.io/dashboard" if $gtk.platform == "Mac OS X" + if $gtk.platform? :mac + $gtk.exec "rm -rf ./builds" + end + results = $gtk.exec "#{package_command} --only-package" + puts File.expand_path("./builds") + log "#+begin_src" log results log "#+end_src" + + if $gtk.platform? :mac + $gtk.exec "open ./builds/" + elsif $gtk.platform? :windows + $gtk.exec "powershell \"ii .\"" + end + + $gtk.openurl "https://itch.io/dashboard" + :success end @@ -168,7 +190,7 @@ class ItchWizard steps.each do |m| begin log_info "Running Itch Wizard Step: ~$wizards.itch.#{m}~" - result = (send m) || :success if @wizard_status[m][:result] != :success + result = (send m) || :success @wizard_status[m][:result] = result if result != :success log_info "Exiting wizard. :#{result}" diff --git a/dragon/keys.rb b/dragon/keys.rb index c931aff..c49d98a 100644 --- a/dragon/keys.rb +++ b/dragon/keys.rb @@ -14,7 +14,7 @@ module GTK :l1, :r1, :l2, :r2, :l3, :r3, - :start, :select, + :start, :select, :home, :directional_up, :directional_down, :directional_left, :directional_right ].freeze @@ -22,6 +22,22 @@ module GTK attr label end + def back + @select + end + + def back= value + @select = value + end + + def guide + @home + end + + def guide= value + @home = value + end + # Activate a key. # # @return [void] diff --git a/dragon/layout.rb b/dragon/layout.rb index 1b0f8d0..18ca8c7 100644 --- a/dragon/layout.rb +++ b/dragon/layout.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # layout.rb has been released under MIT (*only this file*). @@ -276,10 +277,18 @@ module GTK device.grid_area.row_count end + def row_max_index + row_count - 1 + end + def col_count device.grid_area.col_count end + def col_max_index + col_count - 1 + end + def gutter_height device.grid_area.gutter end @@ -302,18 +311,124 @@ module GTK def rect_defaults { - row: nil, - col: nil, - h: 1, - w: 1, - dx: 0, - dy: 0, - rect: :control_rect + row: nil, + col: nil, + h: 1, + w: 1, + dx: 0, + dx_ratio: 1, + dy: 0, + dy_ratio: 1, + dh_ratio: 1, + dw_ratio: 1, + merge: nil, + rect: :control_rect } end - def rect opts + def row n + (rect row: n, col: 0, w: 0, h: 0).x + end + + def row_from_bottom n + (rect row: row_count - n, col: 0, w: 0, h: 0).x + end + + def col n + (rect row: 0, col: n, w: 0, h: 0).y + end + + def col_from_right n + (rect row: 0, col: col_max_index - n, w: 0, h: 0).y + end + + def w n + (rect row: 0, col: 0, w: n, h: 1).w + end + + def h n + (rect row: 0, col: 0, w: 1, h: n).h + end + + def rect_group opts + group = opts.group + r = opts.row || 0 + r = row_max_index - opts.row_from_bottom if opts.row_from_bottom + c = opts.col || 0 + c = col_max_index - opts.col_from_right if opts.col_from_right + drow = opts.drow || 0 + dcol = opts.dcol || 0 + w = opts.w || 0 + h = opts.h || 0 + merge = opts[:merge] + + running_row = r + running_col = c + + running_col = calc_col_offset(opts.col_offset) if opts.col_offset + running_row = calc_row_offset(opts.row_offset) if opts.row_offset + + group.map do |i| + group_layout_opts = i.layout || {} + group_layout_opts = group_layout_opts.merge row: running_row, + col: running_col, + merge: merge, + w: w, h: h + result = (rect group_layout_opts).merge i + + if (i.is_a? Hash) && (i.primitive_marker == :label) + if i.alignment_enum == 1 + result.x += result.w.half + elsif i.alignment_enum == 2 + result.x += result.w + end + end + + running_row += drow + running_col += dcol + result + end + end + + def calc_row_offset opts = {} + count = opts[:count] || opts[:length] || 0 + h = opts.h || 1 + (row_count - (count * h)) / 2.0 + end + + def calc_col_offset opts = {} + count = opts[:count] || opts[:length] || 0 + w = opts.w || 1 + (col_count - (count * w)) / 2.0 + end + + def point opts = {} + opts.w = 1 + opts.h = 1 + opts.row ||= 0 + opts.col ||= 0 + r = rect opts + r.x += r.w * opts.col_anchor if opts.col_anchor + r.y += r.h * opts.row_anchor if opts.row_anchor + r + end + + def rect *all_opts + if all_opts.length == 1 + opts = all_opts.first + else + opts = {} + all_opts.each do |o| + opts.merge! o + end + end + + opts.row = row_max_index - opts.row_from_bottom if opts.row_from_bottom + opts.col = col_max_index - opts.col_from_right if opts.col_from_right opts = rect_defaults.merge opts + opts.row ||= 0 + opts.col ||= 0 + result = send opts[:rect] if opts[:row] && opts[:col] && opts[:w] && opts[:h] col = rect_col opts[:col], opts[:w] @@ -321,7 +436,9 @@ module GTK result = control_rect.merge x: col.x, y: row.y, w: col.w, - h: row.h + h: row.h, + center_x: col.center_x, + center_y: row.center_y elsif opts[:row] && !opts[:col] result = rect_row opts[:row], opts[:h] elsif !opts[:row] && opts[:col] @@ -359,13 +476,21 @@ module GTK result[:h] += device.grid_area.gutter * 2 end - result[:x] += opts[:dx] if opts[:dx] - result[:y] += opts[:dy] if opts[:dy] - result[:w] += opts[:dw] if opts[:dw] - result[:h] += opts[:dh] if opts[:dh] + result[:x] += opts[:dx] if opts[:dx] + result[:x] *= opts[:dx_ratio] if opts[:dx_ratio] + result[:y] += opts[:dy] if opts[:dy] + result[:y] *= opts[:dy_ratio] if opts[:dy_ratio] + result[:w] += opts[:dw] if opts[:dw] + result[:w] *= opts[:dw_ratio] if opts[:dw_ratio] + result[:h] += opts[:dh] if opts[:dh] + result[:h] *= opts[:dh_ratio] if opts[:dh_ratio] + result.merge! opts[:merge] if opts[:merge] result[:row] = opts[:row] result[:col] = opts[:col] + result[:h] = result[:h].clamp 0 + result[:w] = result[:w].clamp 0 + if $gtk.args.grid.name == :center result[:x] -= 640 result[:y] -= 360 @@ -400,7 +525,7 @@ module GTK row_y = device.h - row_y - row_h - result = control_rect.merge y: row_y, h: row_h + result = control_rect.merge y: row_y, h: row_h, center_y: (row_y + row_h.half) @rect_cache[:row][index][h] = result @rect_cache[:row][index][h] end @@ -423,7 +548,7 @@ module GTK col_w = col_w.to_i col_w -= 1 if col_w.odd? - result = control_rect.merge x: col_x, w: col_w + result = control_rect.merge x: col_x, w: col_w, center_x: (col_x + col_w.half) @rect_cache[:col][index][w] = result @rect_cache[:col][index][w] end @@ -474,6 +599,26 @@ module GTK @device end + def debug_primitives opts = {} + @primitives ||= col_count.map_with_index do |col| + row_count.map_with_index do |row| + cell = rect row: row, col: col + center = Geometry.rect_center_point cell + [ + cell.merge(opts).border, + cell.merge(opts) + .label!(x: center.x, + y: center.y, + text: "#{row},#{col}", + size_enum: -3, + vertical_alignment_enum: 1, + alignment_enum: 1) + ] + end + end + @primitives + end + def serialize { device: @device.serialize, @@ -487,5 +632,12 @@ module GTK def to_s serialize.to_s end + + def reset + @primitives = nil + @rect_cache ||= {} + @rect_cache.clear + end + end end diff --git a/dragon/log.rb b/dragon/log.rb index 7bf09fd..2e39217 100644 --- a/dragon/log.rb +++ b/dragon/log.rb @@ -129,6 +129,11 @@ module GTK self.puts message end + def self.reset + @once = {} + nil + end + def self.puts_once *ids, message id = "#{ids}" @once ||= {} diff --git a/dragon/metadata.rb b/dragon/metadata.rb deleted file mode 100644 index 88d9b60..0000000 --- a/dragon/metadata.rb +++ /dev/null @@ -1,41 +0,0 @@ -# Contributors outside of DragonRuby who also hold Copyright: Michał Dudziński -# Copyright 2021 DragonRuby LLC -# MIT License -# metadata.rb has been released under MIT (*only this file*). - -module Metadata - def metadata_file_path - "metadata/game_metadata.txt" - end - - def get_metadata - metadata = $gtk.read_file metadata_file_path - - if !metadata - write_blank_metadata - metadata = $gtk.read_file metadata_file_path - end - - dev_id, dev_title, game_id, game_title, version, icon = *metadata.each_line.to_a - - { - dev_id: dev_id.strip, - dev_title: dev_title.strip, - game_id: game_id.strip, - game_title: game_title.strip, - version: version.strip, - icon: icon.strip - } - end - - def write_blank_metadata - $gtk.write_file metadata_file_path, <<-S.strip -#devid=myname -#devtitle=My Name -#gameid=mygame -#gametitle=My Game -#version=0.1 -#icon=metadata/icon.png -S - end -end diff --git a/dragon/numeric.rb b/dragon/numeric.rb index 3cf1314..27cf02e 100644 --- a/dragon/numeric.rb +++ b/dragon/numeric.rb @@ -13,6 +13,35 @@ class Numeric alias_method :lte, :<= alias_method :__original_eq_eq__, :== unless Numeric.instance_methods.include? :__original_eq_eq__ + def to_layout_row opts = {} + $layout.rect(row: self, + col: opts.col || 0, + w: opts.w || 0, + h: opts.h || 0).y + end + + def to_layout_col opts = {} + $layout.rect(row: 0, + col: self, + w: opts.w || 0, + h: opts.h || 0).x + end + + def to_layout_w + $layout.rect(row: 0, col: 0, w: self, h: 1).w + end + + def to_layout_h + $layout.rect(row: 0, col: 0, w: 1, h: self).h + end + + def to_layout_row_from_bottom opts = {} + ($layout.row_max_index - self).to_layout_row opts + end + + def to_layout_col_from_right opts = {} + ($layout.col_max_index - self).to_layout_col opts + end # Converts a numeric value representing seconds into frames. # @@ -28,10 +57,26 @@ class Numeric self / 2.0 end + def third + self / 3.0 + end + + def quarter + self / 4.0 + end + def to_byte clamp(0, 255).to_i end + def clamp *opts + min = (opts.at 0) + max = (opts.at 1) + return min if min && self < min + return max if max && self > max + return self + end + def clamp_wrap min, max max, min = min, max if min > max return self if self >= min && self <= max @@ -314,6 +359,18 @@ S (self % n) == 0 end + def multiply n + self * n + end + + def fmult n + self * n.to_f + end + + def imult n + (self * n).to_i + end + def mult n self * n end @@ -435,12 +492,6 @@ S return gt other end - def == other - return true if __original_eq_eq__ other - return __original_eq_eq__ other.entity_id if other.is_a? OpenEntity - return false - end - # @gtk def map unless block_given? @@ -527,29 +578,29 @@ S end def - other - return nil unless other - super + return self unless other + self - other rescue Exception => e __raise_arithmetic_exception__ other, :-, e end def + other - return nil unless other - super + return self unless other + self + other rescue Exception => e __raise_arithmetic_exception__ other, :+, e end def * other - return nil unless other - super + return self unless other + self * other rescue Exception => e __raise_arithmetic_exception__ other, :*, e end def / other - return nil unless other - super + return self unless other + self / other rescue Exception => e __raise_arithmetic_exception__ other, :/, e end @@ -579,6 +630,10 @@ S def self.clamp n, min, max n.clamp min, max end + + def mid? l, r + (between? l, r) || (between? r, l) + end end class Fixnum @@ -605,39 +660,33 @@ class Fixnum end def + other - return nil unless other - super + return self unless other + self + other rescue Exception => e __raise_arithmetic_exception__ other, :+, e end def * other - return nil unless other - super + return self unless other + self * other rescue Exception => e __raise_arithmetic_exception__ other, :*, e end def / other - return nil unless other - super + return self unless other + self / other rescue Exception => e __raise_arithmetic_exception__ other, :/, e end def - other - return nil unless other - super + return self unless other + self - other rescue Exception => e __raise_arithmetic_exception__ other, :-, e end - def == other - return true if __original_eq_eq__ other - return __original_eq_eq__ other.entity_id if other.is_a? GTK::OpenEntity - return false - end - # Returns `-1` if the number is less than `0`. `+1` if the number # is greater than `0`. Returns `0` if the number is equal to `0`. # @@ -694,28 +743,28 @@ class Float alias_method :__original_divide__, :- unless Float.instance_methods.include? :__original_divide__ def - other - return nil unless other + return self unless other super rescue Exception => e __raise_arithmetic_exception__ other, :-, e end def + other - return nil unless other + return self unless other super rescue Exception => e __raise_arithmetic_exception__ other, :+, e end def * other - return nil unless other + return self unless other super rescue Exception => e __raise_arithmetic_exception__ other, :*, e end def / other - return nil unless other + return self unless other super rescue Exception => e __raise_arithmetic_exception__ other, :/, e @@ -762,4 +811,8 @@ class Integer def nan? false end + + def center other + (self - other).abs.fdiv(2) + end end diff --git a/dragon/outputs_docs.rb b/dragon/outputs_docs.rb index b2dc57d..db15989 100644 --- a/dragon/outputs_docs.rb +++ b/dragon/outputs_docs.rb @@ -150,7 +150,7 @@ Here is an example: S end - + def docs_sprites <<-S * DOCS: ~GTK::Outputs#sprites~ diff --git a/dragon/readme_docs.rb b/dragon/readme_docs.rb index 78e0320..541c3ce 100644 --- a/dragon/readme_docs.rb +++ b/dragon/readme_docs.rb @@ -36,8 +36,6 @@ to get fancy you can provide a ~lambda~ to filter documentation: #+begin_src docs_search { |entry| (entry.include? "Array") && (!entry.include? "Enumerable") } #+end_src - -[[docs_search.gif]] S end @@ -67,20 +65,25 @@ Reply with: I am a Dragon Rider. #+end_quote -* Watch Some Intro Videos +* Intro Videos + +Here are some videos to help you get the lay of the land. -Each video is only 20 minutes and all of them will fit into a lunch -break. So please watch them: +** Quick Api Tour 1. Beginner Introduction to DragonRuby Game Toolkit: [[https://youtu.be/ixw7TJhU08E]] -2. Intermediate Introduction to Ruby Syntax: [[https://youtu.be/HG-XRZ5Ppgc]] -3. Intermediate Introduction to Arrays in Ruby: [[https://youtu.be/N72sEYFRqfo]] -The second and third videos are not required if you are proficient -with Ruby, but *definitely* watch the first one. +** If You Are Completely New to Ruby and Programming + +1. Intermediate Introduction to Ruby Syntax: [[https://youtu.be/HG-XRZ5Ppgc]] +2. Intermediate Introduction to Arrays in Ruby: [[https://youtu.be/N72sEYFRqfo]] +3. You may also want to try this free course provided at [[http://dragonruby.school]]. + +** If You Have Game Dev Experience -You may also want to try this free course provided at -[[http://dragonruby.school]]. +1. Building Tetris - Part 1: [[https://youtu.be/xZMwRSbC4rY]] +2. Building Tetris - Part 2: [[https://youtu.be/C3LLzDUDgz4]] +3. Low Res Game Jam Tutorial: [[https://youtu.be/pCI90ukKCME]] * Getting Started Tutorial @@ -482,10 +485,10 @@ overwhelms beginners who are new to the engine or programming in general. DragonRuby's philosophy is to provide multiple options across the "make it fast" vs "make it right" spectrum, with incremental/intuitive transitions between the options provided. A concrete example of this philosophy -would be render primitives: the spectrum of options allows renderable constructs take -the form of tuples/arrays (easy to pickup, simple, and fast to code/prototype with), +would be render primitives: the spectrum of options allows renderable constructs that +take the form of tuples/arrays (easy to pickup, simple, and fast to code/prototype with), hashes (a little more work, but gives you the ability to add additional properties), -open and string entities (more work than hashes, but yields cleaner apis), +open and strict entities (more work than hashes, but yields cleaner apis), and finally - if you really need full power/flexibility in rendering - classes (which take the most amount of code and programming knowledge to create). @@ -656,11 +659,15 @@ You can represent a sprite as a ~Hash~: flip_vertically: false, flip_horizontally: false, angle_anchor_x: 0.5, - angle_anchor_y: 1.0 + angle_anchor_y: 1.0, + blendmode_enum: 1 } end #+end_src +The ~blendmode_enum~ value can be set to ~0~ (no blending), ~1~ (alpha blending), +~2~ (additive blending), ~3~ (modulo blending), ~4~ (multiply blending). + You can represent a sprite as an ~object~: #+begin_src ruby @@ -670,7 +677,7 @@ You can represent a sprite as an ~object~: :source_x, :source_y, :source_w, :source_h, :tile_x, :tile_y, :tile_w, :tile_h, :flip_horizontally, :flip_vertically, - :angle_anchor_x, :angle_anchor_y + :angle_anchor_x, :angle_anchor_y, :blendmode_enum def primitive_marker :sprite @@ -760,16 +767,17 @@ You can add additional metadata about your game within a label, which requires y #+begin_src def tick args 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", + x: 200, + y: 550, + text: "dragonruby", + size_enum: 2, + alignment_enum: 1, + r: 155, + g: 50, + b: 50, + a: 255, + font: "fonts/manaspc.ttf", + vertical_alignment_enum: 0, # 0 is bottom, 1 is middle, 2 is top # You can add any properties you like (this will be ignored/won't cause errors) game_data_one: "Something", game_data_two: { @@ -801,6 +809,21 @@ You can get the render size of any string using ~args.gtk.calcstringbox~. ] end #+end_src + +** Rendering Labels With New Line Characters And Wrapping + +You can use a strategy like the following to create multiple labels from a String. + +#+begin_src ruby + def tick args + long_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elitteger dolor velit, ultricies vitae libero vel, aliquam imperdiet enim." + max_character_length = 30 + long_strings_split = args.string.wrapped_lines long_string, max_character_length + args.outputs.labels << long_strings_split.map_with_index do |s, i| + { x: 10, y: 600 - (i * 20), text: s } + end + end +#+end_src S end @@ -1031,7 +1054,7 @@ The elevator pitch is: DragonRuby is a Multilevel Cross-platform Runtime. The "multiple levels" within the runtime allows us to target platforms no other Ruby can target: PC, Mac, Linux, Raspberry Pi, WASM, iOS, Android, Nintendo -Switch, PS4, Xbox, and Scadia. +Switch, PS4, Xbox, and Stadia. **** What does Multilevel Cross-platform mean? @@ -1232,13 +1255,11 @@ questions asked. *** But still, you should offer a free version. So I can try it out and see if I like it. -You can try our [web-based sandbox environment](). But it won't do the -runtime justice. Or just come to our [Slack]() or [Discord]() channel -and ask questions. We'd be happy to have a one on one video chat with -you and show off all the cool stuff we're doing. +You can try our web-based sandbox environment at [[http://fiddle.dragonruby.org]]. But it won't do the +runtime justice. Or just come to our Discord Channel at [[http://discord.dragonruby.org]] and ask questions. +We'd be happy to have a one on one video chat with you and show off all the cool stuff we're doing. -Seriously just buy it. Get a refund if you don't like it. We make it -stupid easy to do so. +Seriously just buy it. Get a refund if you don't like it. We make it stupid easy to do so. *** I still think you should do a free version. Think of all people who would give it a shot. diff --git a/dragon/runtime_docs.rb b/dragon/runtime_docs.rb index 90addff..6da72d0 100644 --- a/dragon/runtime_docs.rb +++ b/dragon/runtime_docs.rb @@ -119,18 +119,18 @@ The properties ~args.inputs.mouse.(click|down|previous_click|up)~ each return ~n that has an ~x~, ~y~ properties along with helper functions to determine collision: ~inside_rect?~, ~inside_circle~. *** ~.controller_one~, ~.controller_two~ Represents controllers connected to the usb ports. -**** ~.up +**** ~.up~ Returns ~true~ if ~up~ is pressed or held on the directional or left analog. -**** ~.down +**** ~.down~ Returns ~true~ if ~down~ is pressed or held on the directional or left analog. -**** ~.left +**** ~.left~ Returns ~true~ if ~left~ is pressed or held on the directional or left analog. -**** ~.right +**** ~.right~ Returns ~true~ if ~right~ is pressed or held on the directional or left analog. -**** ~.left_right +**** ~.left_right~ Returns ~-1~ (left), ~0~ (neutral), or ~+1~ (right) depending on results of ~args.inputs.controller_(one|two).left~ and ~args.inputs.controller_(one|two).right~. -**** ~.up_down -Returns ~-1~ (down), ~0~ (neutral), or ~+1~ (up) depending on results of ~args.inputs.controller_(one|two).down~ and ~args.inputs.controller_(one|two).up~. +**** ~.up_down~ +Returns ~-1~ (down), ~0~ (neutral), or ~+1~ (up) depending on results of ~args.inputs.controller_(one|two).up~ and ~args.inputs.controller_(one|two).down~. **** ~.(left_analog_x_raw|right_analog_x_raw)~ Returns the raw integer value for the analog's horizontal movement (~-32,000 to +32,000~). **** ~.left_analog_y_raw|right_analog_y_raw)~ @@ -139,13 +139,13 @@ Returns the raw integer value for the analog's vertical movement (~-32,000 to +3 Returns a number between ~-1~ and ~1~ which represents the percentage the analog is moved horizontally as a ratio of the maximum horizontal movement. **** ~.left_analog_y_perc|right_analog_y_perc)~ Returns a number between ~-1~ and ~1~ which represents the percentage the analog is moved vertically as a ratio of the maximum vertical movement. -**** ~.directional_up)~ +**** ~.directional_up~ Returns ~true~ if ~up~ is pressed or held on the directional. -**** ~.directional_down)~ +**** ~.directional_down~ Returns ~true~ if ~down~ is pressed or held on the directional. -**** ~.directional_left)~ +**** ~.directional_left~ Returns ~true~ if ~left~ is pressed or held on the directional. -**** ~.directional_right)~ +**** ~.directional_right~ Returns ~true~ if ~right~ is pressed or held on the directional. **** ~.(a|b|x|y|l1|r1|l2|r2|l3|r3|start|select)~ Returns ~true~ if the specific button is pressed or held. @@ -170,7 +170,7 @@ Returns ~true~ if ~right~ or ~d~ is pressed or held on the keyboard. **** ~.left_right~ Returns ~-1~ (left), ~0~ (neutral), or ~+1~ (right) depending on results of ~args.inputs.keyboard.left~ and ~args.inputs.keyboard.right~. **** ~.up_down~ -Returns ~-1~ (down), ~0~ (neutral), or ~+1~ (up) depending on results of ~args.inputs.keyboard.down~ and ~args.inputs.keyboard.up~. +Returns ~-1~ (left), ~0~ (neutral), or ~+1~ (right) depending on results of ~args.inputs.keyboard.up~ and ~args.inputs.keyboard.up~. **** keyboard properties The following properties represent keys on the keyboard and are available on ~args.inputs.keyboard.KEY~, ~args.inputs.keyboard.key_down.KEY~, ~args.inputs.keyboard.key_held.KEY~, and ~args.inputs.keyboard.key_up.KEY~: - ~alt~ @@ -301,6 +301,14 @@ Send a Primitive to this collection to render an unfilled rectangle to the scree Send any Primitive to this collection which represents things you render to the screen for debugging purposes. Primitives in this collection will not be rendered in a production release of your game. ** ~args.geometry~ This property contains geometric functions. Functions can be invoked via ~args.geometry.FUNCTION~. + +Here are some general notes with regards to the arguments these geometric functions accept. + +1. ~Rectangles~ can be represented as an ~Array~ with four (or more) values ~[x, y, w, h]~, as a ~Hash~ ~{ x:, y:, w:, h: }~ or an object that responds to ~x~, ~y~, ~w~, and ~h~. +2. ~Points~ can be represent as an ~Array~ with two (or more) values ~[x, y]~, as a ~Hash~ ~{ x:, y:}~ or an object that responds to ~x~, and ~y~. +3. ~Lines~ can be represented as an ~Array~ with four (or more) values ~[x, y, x2, y2]~, as a ~Hash~ ~{ x:, y:, x2:, y2: }~ or an object that responds to ~x~, ~y~, ~x2~, and ~y2~. +4. ~Angles~ are represented as degrees (not radians). + *** ~.inside_rect? rect_1, rect_2~ Returns ~true~ if ~rect_1~ is inside ~rect_2~. *** ~.intersect_rect? rect_2, rect_2~ diff --git a/dragon/string.rb b/dragon/string.rb index 62c151a..153ba27 100644 --- a/dragon/string.rb +++ b/dragon/string.rb @@ -29,6 +29,30 @@ S end end + def char_byte + return nil if self.length == 0 + c = self.each_char.first.bytes + c = c.first if c.is_a? Enumerable + c + end + + def insert_character_at index, char + t = each_char.to_a + t = (t.insert index, char) + t.join + end + + def excluding_character_at index + t = each_char.to_a + t.delete_at index + t.join + end + + def excluding_last_character + return "" if self.length <= 1 + return excluding_character_at(self.length - 1) + end + def end_with_bang? self[-1] == "!" end diff --git a/dragon/tests.rb b/dragon/tests.rb index 7cbba09..ad1a780 100644 --- a/dragon/tests.rb +++ b/dragon/tests.rb @@ -132,7 +132,7 @@ S 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}" + log "#{h[:e].to_s.gsub("* ERROR:", "").strip}\n#{h[:e].__backtrace_to_org__}" end end end diff --git a/dragon/wizards.rb b/dragon/wizards.rb index 3501b80..b0c7ca0 100644 --- a/dragon/wizards.rb +++ b/dragon/wizards.rb @@ -1,7 +1,42 @@ +# coding: utf-8 # Copyright 2019 DragonRuby LLC # MIT License # wizards.rb has been released under MIT (*only this file*). +class Wizard + def metadata_file_path + "metadata/game_metadata.txt" + end + + def get_metadata + metadata = $gtk.read_file metadata_file_path + + if !metadata + write_blank_metadata + metadata = $gtk.read_file metadata_file_path + end + + dev_id, dev_title, game_id, game_title, version, icon = *metadata.each_line.to_a + + { + dev_id: dev_id.strip.gsub("#", "").gsub("devid=", ""), + dev_title: dev_title.strip.gsub("#", "").gsub("devtitle=", ""), + game_id: game_id.strip.gsub("#", "").gsub("gameid=", ""), + game_title: game_title.strip.gsub("#", "").gsub("gametitle=", ""), + version: version.strip.gsub("#", "").gsub("version=", ""), + icon: icon.strip.gsub("#", "").gsub("icon=", "") + } + end +end + +class WizardException < Exception + attr_accessor :console_primitives + + def initialize *console_primitives + @console_primitives = console_primitives + end +end + module GTK class Wizards attr_accessor :ios, :itch -- cgit v1.2.3