diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 7141176..948d199 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -23,13 +23,13 @@ when :OPENGL_PLATFORM_MACOSX when :OPENGL_PLATFORM_LINUX gl_library_path = nil - if File.exists?("/usr/lib/x86_64-linux-gnu/libGL.so") # Ubuntu (Debian) + if File.exist?("/usr/lib/x86_64-linux-gnu/libGL.so") # Ubuntu (Debian) gl_library_path = "/usr/lib/x86_64-linux-gnu" - elsif File.exists?("/usr/lib/libGL.so") # Manjaro (ARCH) + elsif File.exist?("/usr/lib/libGL.so") # Manjaro (ARCH) gl_library_path = "/usr/lib" - elsif File.exists?("/usr/lib/arm-linux-gnueabihf/libGL.so") # Raspbian (ARM/Raspberry Pi) + elsif File.exist?("/usr/lib/arm-linux-gnueabihf/libGL.so") # Raspbian (ARM/Raspberry Pi) gl_library_path = "/usr/lib/arm-linux-gnueabihf" end @@ -86,6 +86,7 @@ require_relative "lib/states/game_state" require_relative "lib/ui/menu" require_relative "lib/ui/command" +require_relative "lib/ui/subcommand" Dir.glob("#{IMICFPS::GAME_ROOT_PATH}/lib/ui/commands/*.rb").each do |cmd| require_relative cmd end diff --git a/lib/common_methods.rb b/lib/common_methods.rb index 6852918..e9538be 100644 --- a/lib/common_methods.rb +++ b/lib/common_methods.rb @@ -7,15 +7,13 @@ class IMICFPS def window; $window; end - def delta_time - (Gosu.milliseconds-@delta_time)/1000.0 - end + def delta_time; (Gosu.milliseconds - @delta_time) / 1000.0; end def button_down?(id); window.button_down?(id); end def mouse_x; window.mouse_x; end def mouse_y; window.mouse_y; end - def mouse_x=int; window.mouse_x=int; end - def mouse_y=int; window.mouse_y=int; end + def mouse_x=(int); window.mouse_x = int; end + def mouse_y=(int); window.mouse_y = int; end def gl(&block) window.gl do @@ -31,6 +29,10 @@ class IMICFPS return string end + def control_down?; button_down?(Gosu::KbLeftControl) || button_down?(Gosu::KbRightControl); end + def shift_down?; button_down?(Gosu::KbLeftShift) || button_down?(Gosu::KbRightShift); end + def alt_down?; button_down?(Gosu::KbLeftAlt) || button_down?(Gosu::KbRightAlt); end + def draw_rect(*args) window.draw_rect(*args) end diff --git a/lib/managers/input_mapper.rb b/lib/managers/input_mapper.rb index cb121bd..18febb5 100644 --- a/lib/managers/input_mapper.rb +++ b/lib/managers/input_mapper.rb @@ -10,7 +10,7 @@ class IMICFPS query = @@keymap.dig(id_or_action) if query.is_a?(Integer) - id = query + query elsif query.is_a?(Array) query.each do |key| @@keys[key] = true @@ -28,7 +28,7 @@ class IMICFPS query = @@keymap.dig(id_or_action) if query.is_a?(Integer) - id = query + query elsif query.is_a?(Array) query.each do |key| @@keys[key] = false @@ -40,7 +40,7 @@ class IMICFPS end def self.get(action) - key = @@keymap.dig(action) + @@keymap.dig(action) end def self.set(action, key) diff --git a/lib/objects/entity.rb b/lib/objects/entity.rb index c7cee53..8f3fd2b 100644 --- a/lib/objects/entity.rb +++ b/lib/objects/entity.rb @@ -9,7 +9,7 @@ class IMICFPS attr_accessor :scale, :visible, :renderable, :backface_culling attr_accessor :position, :rotation, :velocity - attr_reader :model, :name, :debug_color, :bounding_box, :collision, :physics, :mass, :drag + attr_reader :name, :debug_color, :bounding_box, :collision, :physics, :mass, :drag def initialize(x: 0, y: 0, z: 0, bound_model: nil, scale: MODEL_METER_SCALE, backface_culling: true, auto_manage: true, manifest_file: nil) @position = Vector.new(x, y, z) @@ -42,7 +42,7 @@ class IMICFPS @bound_model.model.objects.each { |o| o.scale = self.scale } @normalized_bounding_box = normalize_bounding_box_with_offset - box = normalize_bounding_box + normalize_bounding_box end return self diff --git a/lib/ui/command.rb b/lib/ui/command.rb index 58c4c34..7126ad5 100644 --- a/lib/ui/command.rb +++ b/lib/ui/command.rb @@ -86,6 +86,41 @@ class IMICFPS end def autocomplete(console) + split = console.text_input.text.split(" ") + + if @subcommands.size > 0 + if !console.text_input.text.end_with?(" ") && split.size == 2 + list = console.abbrev_search(@subcommands.map { |cmd| cmd.command.to_s}, split.last) + + if list.size == 1 + console.text_input.text = "#{split.first} #{list.first} " + else + return unless list.size > 0 + console.stdin("#{list.map { |cmd| Commands::Style.highlight(cmd)}.join(", ")}") + end + + # List available options on subcommand + elsif (console.text_input.text.end_with?(" ") && split.size == 2) || !console.text_input.text.end_with?(" ") && split.size == 3 + subcommand = @subcommands.detect { |cmd| cmd.command.to_s == (split[1]) } + + if subcommand + if split.size == 2 + console.stdin("Available options: #{subcommand.values.map { |value| Commands::Style.highlight(value) }.join(",")}") + else + list = console.abbrev_search(subcommand.values, split.last) + if list.size == 1 + console.text_input.text = "#{split.first} #{split[1]} #{list.first} " + else + console.stdin("Available options: #{list.map { |value| Commands::Style.highlight(value) }.join(",")}") + end + end + end + + # List available subcommands if command was entered and has only a space after it + elsif console.text_input.text.end_with?(" ") && split.size == 1 + console.stdin("Available subcommands: #{@subcommands.map { |cmd| Commands::Style.highlight(cmd.command)}.join(", ")}") + end + end end def handle_subcommand(arguments, console) @@ -107,91 +142,5 @@ class IMICFPS raise NotImplementedError end end - - class SubCommand - def initialize(parent, command, type) - @parent = parent - @command = command - @type = type - end - - def command - @command - end - - def handle(arguments, console) - if arguments.size > 1 - console.stdin("to many arguments for #{Style.highlight("#{command}")}, got #{Style.error(arguments.size)} expected #{Style.notice(1)}.") - return - end - - case @type - when :boolean - case arguments.last - when "", nil - var = @parent.get(command.to_sym) ? @parent.get(command.to_sym) : false - console.stdin("#{command}: #{Style.highlight(var)}") - when "on" - var = @parent.set(command.to_sym, true) - console.stdin("#{command} => #{Style.highlight(var)}") - when "off" - var = @parent.set(command.to_sym, false) - console.stdin("#{command} => #{Style.highlight(var)}") - else - console.stdin("Invalid argument for #{Style.highlight("#{command}")}, got #{Style.error(arguments.last)} expected #{Style.notice("on")}, or #{Style.notice("off")}.") - end - when :string - case arguments.last - when "", nil - var = @parent.get(command.to_sym) ? @parent.get(command.to_sym) : "\"\"" - console.stdin("#{command}: #{Style.highlight(var)}") - else - var = @parent.set(command.to_sym, arguments.last) - console.stdin("#{command} => #{Style.highlight(var)}") - end - when :integer - case arguments.last - when "", nil - var = @parent.get(command.to_sym) ? @parent.get(command.to_sym) : "nil" - console.stdin("#{command}: #{Style.highlight(var)}") - else - begin - var = @parent.set(command.to_sym, Integer(arguments.last)) - console.stdin("#{command} => #{Style.highlight(var)}") - rescue ArgumentError - console.stdin("Error: #{Style.error("Expected an integer, got '#{arguments.last}'")}") - end - end - when :decimal - case arguments.last - when "", nil - var = @parent.get(command.to_sym) ? @parent.get(command.to_sym) : "nil" - console.stdin("#{command}: #{Style.highlight(var)}") - else - begin - var = @parent.set(command.to_sym, Float(arguments.last)) - console.stdin("#{command} => #{Style.highlight(var)}") - rescue ArgumentError - console.stdin("Error: #{Style.error("Expected a decimal or integer, got '#{arguments.last}'")}") - end - end - else - raise RuntimeError - end - end - - def usage - case @type - when :boolean - "#{Style.highlight(command)} #{Style.notice("[on|off]")}" - when :string - "#{Style.highlight(command)} #{Style.notice("[string]")}" - when :integer - "#{Style.highlight(command)} #{Style.notice("[0]")}" - when :decimal - "#{Style.highlight(command)} #{Style.notice("[0.0]")}" - end - end - end end end \ No newline at end of file diff --git a/lib/ui/commands/debug_command.rb b/lib/ui/commands/debug_command.rb index 4e7affa..54788aa 100644 --- a/lib/ui/commands/debug_command.rb +++ b/lib/ui/commands/debug_command.rb @@ -29,7 +29,7 @@ class IMICFPS end def usage - string = "debug\n #{@subcommands.map { |sub| sub.usage }.join("\n ")}" + "debug\n #{@subcommands.map { |sub| sub.usage }.join("\n ")}" end end end diff --git a/lib/ui/console.rb b/lib/ui/console.rb index 3014b20..e436582 100644 --- a/lib/ui/console.rb +++ b/lib/ui/console.rb @@ -4,6 +4,7 @@ class IMICFPS PADDING = 2 include CommonMethods + attr_reader :text_input def initialize @text_input = Gosu::TextInput.new @@ -12,11 +13,13 @@ class IMICFPS @history_height = window.height / 4 * 3 - (PADDING * 2 + @input.textobject.height) @history = Text.new("=== #{IMICFPS::NAME} v#{IMICFPS::VERSION} (#{IMICFPS::RELEASE_NAME}) ===\n\n", x: 4, y: @history_height, z: Console::Z + 1) - update_history + update_history_y @command_history = [] @command_history_index = 0 + @memory = "" + @background_color = Gosu::Color.rgba(0, 0, 0, 200) @foreground_color = Gosu::Color.rgba(100, 100, 100, 100) @input_color = Gosu::Color.rgba(100, 100, 100, 200) @@ -28,6 +31,7 @@ class IMICFPS @caret_last_change = Gosu.milliseconds @caret_interval = 250 @caret_color = Gosu::Color::WHITE + @selection_color = Gosu::Color.new(0x5522ff22) @width = window.width / 4 * 3 @height = window.height / 4 * 3 @@ -44,12 +48,36 @@ class IMICFPS @history.draw @input.draw # Caret - draw_rect(@input.x + caret_pos, @input.y, Console::PADDING, @input.height, @caret_color, Console::Z + 2) if @show_caret + draw_rect(@input.x + caret_from_left, @input.y, Console::PADDING, @input.height, @caret_color, Console::Z + 2) if @show_caret + # Caret selection + if caret_start != caret_end + if caret_start < @text_input.selection_start + draw_rect(@input.x + caret_from_left, @input.y, caret_selection_width, @input.height, @selection_color, Console::Z) + else + draw_rect((@input.x + caret_from_left) - caret_selection_width, @input.y, caret_selection_width, @input.height, @selection_color, Console::Z) + end + end + end + + def caret_from_left + return 0 if @text_input.caret_pos == 0 + @input.textobject.text_width(@text_input.text[0..@text_input.caret_pos-1]) + end + + def caret_selection_width + @input.textobject.text_width(@text_input.text[caret_start..(caret_end - 1)]) end def caret_pos - return 0 if @text_input.caret_pos == 0 - @input.textobject.text_width(@text_input.text[0..@text_input.caret_pos-1]) + @text_input.caret_pos + end + + def caret_start + @text_input.selection_start < @text_input.caret_pos ? @text_input.selection_start : @text_input.caret_pos + end + + def caret_end + @text_input.selection_start > @text_input.caret_pos ? @text_input.selection_start : @text_input.caret_pos end def update @@ -68,7 +96,7 @@ class IMICFPS @history.text += "\n> #{@text_input.text}" @command_history << @text_input.text @command_history_index = @command_history.size - update_history + update_history_y handle_command @text_input.text = "" @@ -90,13 +118,12 @@ class IMICFPS split = @text_input.text.split(" ") if !@text_input.text.end_with?(" ") && split.size == 1 - list = command_search(@text_input.text) + list = abbrev_search(Commands::Command.list_commands.map { |cmd| cmd.command.to_s}, @text_input.text) if list.size == 1 @text_input.text = "#{list.first} " else - @history.text += "\n#{list.map { |cmd| Commands::Style.highlight(cmd)}.join(", ")}" - update_history + stdin("\n#{list.map { |cmd| Commands::Style.highlight(cmd)}.join(", ")}") end else if split.size > 0 && cmd = Commands::Command.find(split.first) @@ -105,19 +132,64 @@ class IMICFPS end when Gosu::KbBacktick - # Removed backtick character from input + # Remove backtick character from input if @text_input.text.size > 1 @text_input.text = @text_input.text[0..@text_input.text.size - 2] else @text_input.text = "" end + + # Copy + when Gosu::KbC + if control_down? && shift_down? + @memory = @text_input.text[caret_start..caret_end - 1] if caret_start != caret_end + p @memory + elsif control_down? + @text_input.text = "" + end + + # Paste + when Gosu::KbV + if control_down? && shift_down? + string = @text_input.text.chars.insert(caret_pos, @memory).join + _caret_pos = caret_pos + @text_input.text = string + @text_input.caret_pos = _caret_pos + @memory.length + @text_input.selection_start = _caret_pos + @memory.length + end + + # Cut + when Gosu::KbX + if control_down? && shift_down? + @memory = @text_input.text[caret_start..caret_end - 1] if caret_start != caret_end + string = @text_input.text.chars + Array(caret_start..caret_end - 1).each_with_index do |i, j| + string.delete_at(i - j) + end + + @text_input.text = string.join + end + + # Delete word to left of caret + when Gosu::KbW + if control_down? + split = @text_input.text.split(" ") + split.delete(split.last) + @text_input.text = split.join(" ") + end + + # Clear history + when Gosu::KbL + if control_down? + @history.text = "" + end end end def button_up(id) end - def update_history + def update_history_y @history.y = @history_height - (@history.text.lines.count * (@history.textobject.height)) end @@ -130,13 +202,11 @@ class IMICFPS IMICFPS::Commands::Command.use(command, arguments, self) end - def command_search(text) + def abbrev_search(array, text) return [] unless text.length > 0 - @command_search ||= Abbrev.abbrev(Commands::Command.list_commands.map { |cmd| cmd.command.to_s}) - list = [] - @command_search.each do |abbrev, value| + Abbrev.abbrev(array).each do |abbrev, value| next unless abbrev && abbrev.start_with?(text) list << value @@ -147,7 +217,7 @@ class IMICFPS def stdin(string) @history.text += "\n#{string}" - update_history + update_history_y end def focus diff --git a/lib/wavefront/material.rb b/lib/wavefront/material.rb index 77759db..c73c7e4 100644 --- a/lib/wavefront/material.rb +++ b/lib/wavefront/material.rb @@ -3,7 +3,7 @@ class IMICFPS class Material include OpenGL attr_accessor :name, :ambient, :diffuse, :specular - attr_reader :texture + attr_reader :texture_id def initialize(name) @name = name @ambient = Color.new(1, 1, 1, 1) @@ -32,10 +32,6 @@ class IMICFPS @texture = nil end - - def texture_id - @texture_id - end end end end diff --git a/lib/wavefront/model.rb b/lib/wavefront/model.rb index 46b58f2..dd97cbb 100644 --- a/lib/wavefront/model.rb +++ b/lib/wavefront/model.rb @@ -44,8 +44,8 @@ class IMICFPS puts "#{@file_path.split('/').last} took #{((Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)-start_time)/1000.0).round(2)} seconds to parse" if $debug.get(:stats) - # allocate_gl_objects - # populate_buffers + allocate_gl_objects + populate_buffers # populate_arrays @objects.each {|o| @vertex_count+=o.vertices.size} @@ -83,16 +83,18 @@ class IMICFPS def populate_buffers @vertices_buffer_data = [] - verts = [] - colors= [] - norms = [] - uvs = [] + verts = [] + colors = [] + norms = [] + uvs = [] + tex_ids = [] @faces.each do |face| - verts << face.vertices.map { |vert| [vert.x, vert.y, vert.z] } - colors << face.colors.map { |vert| [vert.x, vert.y, vert.z] } - norms << face.normals.map { |vert| [vert.x, vert.y, vert.z, vert.weight] } - uvs << face.uvs.map { |vert| [vert.x, vert.y, vert.z] } if face.material.texture + verts << face.vertices.map { |vert| [vert.x, vert.y, vert.z] } + colors << face.colors.map { |vert| [vert.x, vert.y, vert.z] } + norms << face.normals.map { |vert| [vert.x, vert.y, vert.z, vert.weight] } + uvs << face.uvs.map { |vert| [vert.x, vert.y, vert.z] } if face.material.texture_id + tex_ids << face.material.texture_id if face.material.texture_id end verts.each_with_index do |vert, i| @@ -100,6 +102,7 @@ class IMICFPS @vertices_buffer_data << colors[i] @vertices_buffer_data << norms[i] @vertices_buffer_data << uvs[i] if uvs.size > 0 + @vertices_buffer_data << tex_ids[i] if tex_ids.size > 0 end data = @vertices_buffer_data.flatten.pack("f*")