diff --git a/Data/Scripts/900_New utilities/001 utilities.rb b/Data/Scripts/900_New utilities/001 utilities.rb new file mode 100644 index 000000000..38aa3c21f --- /dev/null +++ b/Data/Scripts/900_New utilities/001 utilities.rb @@ -0,0 +1,8 @@ +class Bitmap + def outline_rect(x, y, width, height, color, thickness = 1) + fill_rect(x, y, width, thickness, color) + fill_rect(x, y, thickness, height, color) + fill_rect(x, y + height - thickness, width, thickness, color) + fill_rect(x + width - thickness, y, thickness, height, color) + end +end diff --git a/Data/Scripts/901_GameData/Animation.rb b/Data/Scripts/901_GameData/Animation.rb new file mode 100644 index 000000000..4b26e2242 --- /dev/null +++ b/Data/Scripts/901_GameData/Animation.rb @@ -0,0 +1,63 @@ +module GameData + class Animation + attr_reader :name + attr_reader :move, :type # Type is move's type; useful for filtering; move==nil means common animation + attr_reader :version # Hit number + # TODO: Boolean for whether user is on player's side or foe's side. + # TODO: Boolean for not played if target is on user's side. + attr_reader :particles + attr_reader :flags + # TODO: PBS filename. +# attr_reader :pbs_filename + + DATA = {} + # TODO: Make sure the existence of animations.dat is optional. Currently + # it's required. +# DATA_FILENAME = "animations.dat" +# PBS_BASE_FILENAME = "animations" + + extend ClassMethodsIDNumbers + include InstanceMethods + + def register(hash) + DATA[DATA.keys.length] = self.new(hash) + end + + # TODO: Rewrite this to query animations from other criteria. Remember that + # multiple animations could have the same move/version. Odds are this + # method won't be used much at all. + # TODO: Separate exists? methods for move and common animations? +# def exists?(other) +# end + + # TODO: Rewrite this to get animations from other criteria. Remember that + # multiple animations could have the same move/version. Odds are this + # method won't be used much at all. + # TODO: Separate get methods for move and common animations? +# def get(other) +# end + + # TODO: Rewrite this to get animations from other criteria. Remember that + # multiple animations could have the same move/version. Odds are this + # method won't be used much at all. + # TODO: Separate try_get methods for move and common animations? +# def try_get(other) +# end + + def initialize(hash) + @name = hash[:name] + @move = hash[:move] + @type = hash[:type] + @version = hash[:version] || 0 + @particles = [] + # TODO: Copy particles info from hash somehow. + @flags = hash[:flags] || [] + # TODO: Come up with a decent default PBS filename; likely the move's name + # (for move anims) or @name (for common anims). + end + + def move_animation? + return !@move.nil? + end + end +end diff --git a/Data/Scripts/905_New controls/000 UIControls.rb b/Data/Scripts/905_New controls/000 UIControls.rb new file mode 100644 index 000000000..e3bed3025 --- /dev/null +++ b/Data/Scripts/905_New controls/000 UIControls.rb @@ -0,0 +1,2 @@ +# Container module for control classes. +module UIControls; end diff --git a/Data/Scripts/905_New controls/001 basic control.rb b/Data/Scripts/905_New controls/001 basic control.rb new file mode 100644 index 000000000..fea40b138 --- /dev/null +++ b/Data/Scripts/905_New controls/001 basic control.rb @@ -0,0 +1,180 @@ +# TODO: Add "disabled" greying out/non-editable. +# TODO: Add indicator of whether the control's value is "lerping" between frames +# (use yellow somehow?). + +#=============================================================================== +# +#=============================================================================== +class UIControls::BaseControl < BitmapSprite + attr_reader :value +# attr_accessor :disabled # TODO: Make use of this. + + TEXT_COLOR = Color.black + TEXT_SIZE = 18 # Default is 22 if size isn't explicitly set + HOVER_COLOR = Color.cyan # For clickable area when hovering over it + CAPTURE_COLOR = Color.pink # For area you clicked in but aren't hovering over + + def initialize(width, height, viewport) + super(width, height, viewport) + self.bitmap.font.color = TEXT_COLOR + self.bitmap.font.size = TEXT_SIZE + +# @disabled = false # TODO: Make use of this. + @hover_area = nil # Is a symbol from the keys for @interactions if the mouse is hovering over that interaction + @captured_area = nil # Is a symbol from the keys for @interactions (or :none) if this control is clicked in + clear_changed + invalidate + end + + def width + return self.bitmap.width + end + + def height + return self.bitmap.height + end + + def mouse_pos + mouse_coords = Mouse.getMousePos + return nil, nil if !mouse_coords + ret_x = mouse_coords[0] - self.viewport.rect.x - self.x + ret_y = mouse_coords[1] - self.viewport.rect.y - self.y + return ret_x, ret_y + end + + def set_interactive_rects + @interactions = {} + end + + #----------------------------------------------------------------------------- + + def invalid? + return @invalid + end + + # Marks that the control must be redrawn to reflect current logic. + def invalidate + @invalid = true + end + + # Makes the control no longer invalid. + def validate + @invalid = false + end + + def busy? + return !@captured_area.nil? + end + + def changed? + return @changed + end + + def set_changed + @changed = true + end + + def clear_changed + @changed = false + end + + #----------------------------------------------------------------------------- + + def draw_text(this_bitmap, text_x, text_y, this_text) + text_size = this_bitmap.text_size(this_text) + this_bitmap.draw_text(text_x, text_y, text_size.width, text_size.height, this_text, 0) + end + + # Redraws the control only if it is invalid. + def repaint + return if !invalid? + refresh + validate + end + + def refresh + # Paint over control to erase contents (intentionally not using self.bitmap.clear) + self.bitmap.clear + draw_area_highlight + end + + def draw_area_highlight + return if !@interactions || @interactions.empty? + if !@captured_area || @hover_area == @captured_area + # Draw mouse hover over area highlight + rect = @interactions[@hover_area] + self.bitmap.fill_rect(rect.x, rect.y, rect.width, rect.height, HOVER_COLOR) if rect + elsif @captured_area + # Draw captured area highlight + rect = @interactions[@captured_area] + self.bitmap.fill_rect(rect.x, rect.y, rect.width, rect.height, CAPTURE_COLOR) if rect + end + end + + #----------------------------------------------------------------------------- + + # This method is only called if the mouse is in the game window and this + # control has interactive elements. + def on_mouse_press + return if !@interactions || @interactions.empty? + return if @captured_area + @captured_area = nil + mouse_x, mouse_y = mouse_pos + return if !mouse_x || !mouse_y + @interactions.each_pair do |area, rect| + next if !rect.contains?(mouse_x, mouse_y) + @captured_area = area + invalidate + break + end + end + + # Returns whether this control has been properly decaptured. + def on_mouse_release + @captured_area = nil + invalidate + end + + def update_hover_highlight + # Remove the hover highlight if there are no interactions for this control + # or if the mouse is off-screen + mouse_x, mouse_y = mouse_pos + if !@interactions || @interactions.empty? || !mouse_x || !mouse_y + invalidate if @hover_area + @hover_area = nil + return + end + # Check each interactive area for whether the mouse is hovering over it, and + # set @hover_area accordingly + in_area = false + @interactions.each_pair do |area, rect| + next if !rect.contains?(mouse_x, mouse_y) + invalidate if @hover_area != area + @hover_area = area + in_area = true + break + end + if !in_area + invalidate if @hover_area + @hover_area = nil + end + end + + # Updates the logic on the control, invalidating it if necessary. + def update + # TODO: Disabled control stuff. +# return if self.disabled + + update_hover_highlight + + # Detect a mouse press/release + if @interactions && !@interactions.empty? + if Input.trigger?(Input::MOUSELEFT) + on_mouse_press + elsif busy? && Input.release?(Input::MOUSELEFT) + on_mouse_release + end + end + + end +end diff --git a/Data/Scripts/905_New controls/002 label.rb b/Data/Scripts/905_New controls/002 label.rb new file mode 100644 index 000000000..dac2d75bc --- /dev/null +++ b/Data/Scripts/905_New controls/002 label.rb @@ -0,0 +1,24 @@ +#=============================================================================== +# +#=============================================================================== +class UIControls::Label < UIControls::BaseControl + attr_reader :label + + LABEL_END_X = 80 + TEXT_OFFSET_Y = 7 + + def initialize(width, height, viewport, label) + super(width, height, viewport) + @label = label + end + + def label=(value) + @label = value + refresh + end + + def refresh + super + draw_text(self.bitmap, 4, TEXT_OFFSET_Y, @label) + end +end diff --git a/Data/Scripts/905_New controls/003 checkbox.rb b/Data/Scripts/905_New controls/003 checkbox.rb new file mode 100644 index 000000000..faa1d38fc --- /dev/null +++ b/Data/Scripts/905_New controls/003 checkbox.rb @@ -0,0 +1,68 @@ +#=============================================================================== +# NOTE: Strictly speaking, this is a toggle switch and not a checkbox. +#=============================================================================== +class UIControls::Checkbox < UIControls::BaseControl + CHECKBOX_X = 0 + CHECKBOX_WIDTH = 40 + CHECKBOX_HEIGHT = 24 + CHECKBOX_FILL_SIZE = CHECKBOX_HEIGHT - 8 + + UNCHECKED_COLOR = Color.gray + CHECKED_COLOR = Color.new(64, 255, 64) # Green + + def initialize(width, height, viewport, value = false) + super(width, height, viewport) + @value = value + end + + def value=(val) + return if @value == val + @value = val + invalidate + end + + def set_interactive_rects + @checkbox_rect = Rect.new(CHECKBOX_X, (height - CHECKBOX_HEIGHT) / 2, + CHECKBOX_WIDTH, CHECKBOX_HEIGHT) + @interactions = { + :checkbox => @checkbox_rect + } + end + + #----------------------------------------------------------------------------- + + def refresh + super + # Draw checkbox outline + self.bitmap.outline_rect(@checkbox_rect.x + 2, @checkbox_rect.y + 2, + @checkbox_rect.width - 4, @checkbox_rect.height - 4, + self.bitmap.font.color) + # Draw checkbox fill + if @value # If checked + self.bitmap.fill_rect(@checkbox_rect.x + @checkbox_rect.width - CHECKBOX_FILL_SIZE - 4, @checkbox_rect.y + 4, + CHECKBOX_FILL_SIZE, CHECKBOX_FILL_SIZE, CHECKED_COLOR) + self.bitmap.outline_rect(@checkbox_rect.x + @checkbox_rect.width - CHECKBOX_FILL_SIZE - 4, @checkbox_rect.y + 4, + CHECKBOX_FILL_SIZE, CHECKBOX_FILL_SIZE, self.bitmap.font.color) + else + self.bitmap.fill_rect(@checkbox_rect.x + 4, @checkbox_rect.y + 4, + CHECKBOX_FILL_SIZE, CHECKBOX_FILL_SIZE, UNCHECKED_COLOR) + self.bitmap.outline_rect(@checkbox_rect.x + 4, @checkbox_rect.y + 4, + CHECKBOX_FILL_SIZE, CHECKBOX_FILL_SIZE, self.bitmap.font.color) + end + end + + #----------------------------------------------------------------------------- + + def on_mouse_release + return if !@captured_area # Wasn't captured to begin with + # Change this control's value + if @captured_area == :checkbox + mouse_x, mouse_y = mouse_pos + if mouse_x && mouse_y && @interactions[@captured_area].contains?(mouse_x, mouse_y) + @value = !@value # The actual change of this control's value + set_changed + end + end + super # Make this control not busy again + end +end diff --git a/Data/Scripts/905_New controls/004 text box.rb b/Data/Scripts/905_New controls/004 text box.rb new file mode 100644 index 000000000..20fa86850 --- /dev/null +++ b/Data/Scripts/905_New controls/004 text box.rb @@ -0,0 +1,289 @@ +#=============================================================================== +# TODO: Support selecting part of the text by remembering the initial +# cursor position and using it and the current cursor position to +# decide which characters are selected. Maybe? Note that this method +# is only triggered upon the initial mouse press, and isn't repeated +# while it's still held down. +#=============================================================================== +class UIControls::TextBox < UIControls::BaseControl + TEXT_BOX_X = 2 + TEXT_BOX_WIDTH = 172 + TEXT_BOX_HEIGHT = 24 + TEXT_BOX_PADDING = 4 # Gap between sides of text box and text + TEXT_OFFSET_Y = 7 + + def initialize(width, height, viewport, value = "") + super(width, height, viewport) + @value = value + @cursor_pos = -1 + @display_pos = 0 + @cursor_timer = nil + @cursor_shown = false + end + + def value=(new_value) + return if @value == new_value + @value = new_value + invalidate + end + + def insert_char(ch) + @value.insert(@cursor_pos, ch) + @cursor_pos += 1 + @cursor_timer = System.uptime + @cursor_shown = true + invalidate + end + + def delete_at(index) + @value.slice!(index) + @cursor_pos -= 1 if @cursor_pos > index + @cursor_timer = System.uptime + @cursor_shown = true + invalidate + end + + def cursor_pos=(val) + @cursor_pos = val + reset_display_pos + @cursor_timer = System.uptime + @cursor_shown = true + invalidate + end + + def set_interactive_rects + @text_box_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2, + [TEXT_BOX_WIDTH, width].min, TEXT_BOX_HEIGHT) + @interactions = { + :text_box => @text_box_rect + } + end + + #----------------------------------------------------------------------------- + + def busy? + return @cursor_pos >= 0 if @captured_area == :text_box + return super + end + + def reset_interaction + @cursor_pos = -1 + @display_pos = 0 + @cursor_timer = nil + @initial_value = nil + Input.text_input = false + invalidate + end + + #----------------------------------------------------------------------------- + + def get_cursor_index_from_mouse_position + char_widths = [] + @value.to_s.length.times { |i| char_widths[i] = self.bitmap.text_size(@value.to_s[i]).width } + mouse_x, mouse_y = mouse_pos + mouse_x -= @text_box_rect.x + TEXT_BOX_PADDING + return 0 if mouse_x < 0 + (@display_pos...char_widths.length).each do |i| + mouse_x -= char_widths[i] + if mouse_x <= 0 + return (mouse_x.abs >= char_widths[i] / 2) ? i : i + 1 + end + end + return @value.to_s.length + end + + def reset_display_pos + box_width = @text_box_rect.width - (TEXT_BOX_PADDING * 2) + char_widths = [] + @value.to_s.length.times { |i| char_widths[i] = self.bitmap.text_size(@value.to_s[i]).width } + # Text isn't wider than the box + if char_widths.sum <= box_width + return false if @display_pos == 0 + @display_pos = 0 + return true + end + display_pos_changed = false + # Ensure the cursor hasn't gone off the left side of the text box + if @cursor_pos < @display_pos + @display_pos = @cursor_pos + display_pos_changed = true + end + # Ensure the cursor hasn't gone off the right side of the text box + if @cursor_pos > @display_pos + loop do + cursor_x = 0 + (@display_pos...@cursor_pos).each do |i| + cursor_x += char_widths[i] if char_widths[i] + end + break if cursor_x < box_width + @display_pos += 1 + display_pos_changed = true + break if @display_pos == @cursor_pos + end + end + # Ensure there isn't empty space on the right if the text can be moved to + # the right to fill it + if @display_pos > 0 + cursor_x = 0 + (@display_pos...char_widths.length).each do |i| + cursor_x += char_widths[i] if char_widths[i] + end + loop do + cursor_x += char_widths[@display_pos - 1] + break if cursor_x >= box_width + @display_pos -= 1 + display_pos_changed = true + break if @display_pos == 0 + end + end + return display_pos_changed + end + + #----------------------------------------------------------------------------- + + def draw_area_highlight + return if @captured_area == :text_box && (@hover_area == @captured_area || !Input.press?(Input::MOUSELEFT)) + super + end + + def draw_cursor(cursor_x) + return if !@cursor_shown || @cursor_pos < 0 + cursor_y_offset = ((height - TEXT_BOX_HEIGHT) / 2) + 2 + cursor_height = height - (cursor_y_offset * 2) + bitmap.fill_rect(cursor_x, cursor_y_offset, 2, cursor_height, self.bitmap.font.color) + end + + def refresh + super + # Draw text box outline + self.bitmap.outline_rect(@text_box_rect.x, @text_box_rect.y, + @text_box_rect.width, @text_box_rect.height, + self.bitmap.font.color) + # Draw value + char_x = @text_box_rect.x + TEXT_BOX_PADDING + last_char_index = @display_pos + (@value.to_s.length - @display_pos).times do |i| + char = @value.to_s[@display_pos + i] + char_width = self.bitmap.text_size(char).width + cannot_display_next_char = char_x + char_width > @text_box_rect.x + @text_box_rect.width - TEXT_BOX_PADDING + draw_text(self.bitmap, char_x, TEXT_OFFSET_Y, char) if !cannot_display_next_char + # Draw cursor + draw_cursor(char_x - 1) if @display_pos + i == @cursor_pos + break if cannot_display_next_char + last_char_index = @display_pos + i + char_x += char_width + end + # Draw cursor at end + draw_cursor(char_x - 1) if @cursor_pos == @value.to_s.length + # Draw left/right arrows to indicate more text beyond the text box sides + if @display_pos > 0 + bitmap.fill_rect(@text_box_rect.x, (height / 2) - 4, 1, 8, Color.white) + 5.times do |i| + bitmap.fill_rect(@text_box_rect.x - 2 + i, (height / 2) - (i + 1), 1, 2 * (i + 1), self.bitmap.font.color) + end + end + if last_char_index < @value.to_s.length - 1 + bitmap.fill_rect(@text_box_rect.x + @text_box_rect.width - 1, (height / 2) - 4, 1, 8, Color.white) + 5.times do |i| + bitmap.fill_rect(@text_box_rect.x + @text_box_rect.width + 1 - i, (height / 2) - (i + 1), 1, 2 * (i + 1), self.bitmap.font.color) + end + end + end + + #----------------------------------------------------------------------------- + + def on_mouse_press + @captured_area = nil + super + if @captured_area == :text_box + # Clicked into the text box; put the text cursor in there + @cursor_pos = get_cursor_index_from_mouse_position + @cursor_timer = System.uptime + invalidate + else + set_changed if @initial_value && @value != @initial_value + reset_interaction + end + end + + def on_mouse_release + return if !@captured_area # Wasn't captured to begin with + # Start text entry if clicked and released mouse button in the text box + if @captured_area == :text_box + mouse_x, mouse_y = mouse_pos + if mouse_x && mouse_y && @interactions[@captured_area].contains?(mouse_x, mouse_y) + @initial_value = @value.clone + Input.text_input = true + invalidate + return # This control is still captured + end + end + # Released mouse button outside of text box, or initially clicked outside of + # text box; end interaction with this control + set_changed if @initial_value && @value != @initial_value + reset_interaction + super # Make this control not busy again + end + + def update_special_inputs + # Left/right to move cursor + if Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT) + self.cursor_pos = @cursor_pos - 1 if @cursor_pos > 0 + elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT) + self.cursor_pos = @cursor_pos + 1 if @cursor_pos < @value.to_s.length + end + # Home/End to jump to start/end of the text + if Input.triggerex?(:HOME) || Input.repeatex?(:HOME) + self.cursor_pos = 0 + elsif Input.triggerex?(:END) || Input.repeatex?(:END) + self.cursor_pos = @value.to_s.length + end + # Backspace/Delete to remove text + if Input.triggerex?(:BACKSPACE) || Input.repeatex?(:BACKSPACE) + delete_at(@cursor_pos - 1) if @cursor_pos > 0 + elsif Input.triggerex?(:DELETE) || Input.repeatex?(:DELETE) + delete_at(@cursor_pos) if @cursor_pos < @value.to_s.length + end + # Return/Escape to end text input (Escape undoes the change) + if Input.triggerex?(:RETURN) || Input.repeatex?(:RETURN) || + Input.triggerex?(:KP_ENTER) || Input.repeatex?(:KP_ENTER) + set_changed if @initial_value && @value != @initial_value + reset_interaction + @captured_area = nil + elsif Input.triggerex?(:ESCAPE) || Input.repeatex?(:ESCAPE) + @value = @initial_value if @initial_value + reset_interaction + @captured_area = nil + end + end + + def update_text_entry + ret = false + Input.gets.each_char do |ch| + insert_char(ch) + ret = true + end + return ret + end + + def update + super + # TODO: Disabled control stuff. +# return if self.disabled + # Make the cursor flash + if @captured_area == :text_box + cursor_to_show = ((System.uptime - @cursor_timer) / 0.35).to_i.even? + if cursor_to_show != @cursor_shown + @cursor_shown = cursor_to_show + invalidate + end + old_cursor_pos = @cursor_pos + # Update cursor movement, deletions and ending text input + update_special_inputs + return if @cursor_pos != old_cursor_pos || !busy? + # Detect character input and add them to @value + char_inserted = update_text_entry + invalidate if reset_display_pos || char_inserted + end + end +end diff --git a/Data/Scripts/905_New controls/005 number slider.rb b/Data/Scripts/905_New controls/005 number slider.rb new file mode 100644 index 000000000..e94b95b68 --- /dev/null +++ b/Data/Scripts/905_New controls/005 number slider.rb @@ -0,0 +1,128 @@ +#=============================================================================== +# +#=============================================================================== +class UIControls::Slider < UIControls::BaseControl + attr_reader :min_value + attr_reader :max_value + + PLUS_MINUS_SIZE = 16 + SLIDER_PADDING = 6 # Gap between sides of interactive area for slider and drawn slider bar + + MINUS_X = 0 + SLIDER_X = MINUS_X + PLUS_MINUS_SIZE + SLIDER_PADDING + SLIDER_LENGTH = 128 + PLUS_X = SLIDER_X + SLIDER_LENGTH + SLIDER_PADDING + VALUE_X = PLUS_X + PLUS_MINUS_SIZE + 5 + TEXT_OFFSET_Y = 7 + + SLIDER_KNOB_COLOR = Color.red + + def initialize(width, height, viewport, min_value, max_value, value) + super(width, height, viewport) + @min_value = min_value + @max_value = max_value + self.value = value + end + + def value=(new_value) + old_val = @value + @value = new_value.to_i.clamp(self.min_value, self.max_value) + self.invalidate if @value != old_val + end + + def min_value=(new_min) + return if new_min == @min_value + @min_value = new_min + @value = @value.clamp(self.min_value, self.max_value) + self.invalidate + end + + def max_value=(new_max) + return if new_max == @max_value + @max_value = new_max + @value = @value.clamp(self.min_value, self.max_value) + self.invalidate + end + + def set_interactive_rects + @slider_rect = Rect.new(SLIDER_X - SLIDER_PADDING, (self.height - PLUS_MINUS_SIZE) / 2, SLIDER_LENGTH + (SLIDER_PADDING * 2), PLUS_MINUS_SIZE) + @minus_rect = Rect.new(MINUS_X, (self.height - PLUS_MINUS_SIZE) / 2, PLUS_MINUS_SIZE, PLUS_MINUS_SIZE) + @plus_rect = Rect.new(PLUS_X, (self.height - PLUS_MINUS_SIZE) / 2, PLUS_MINUS_SIZE, PLUS_MINUS_SIZE) + @interactions = { + :slider => @slider_rect, + :minus => @minus_rect, + :plus => @plus_rect + } + end + + #----------------------------------------------------------------------------- + + def draw_area_highlight + # Don't want to ever highlight the slider with the capture color, because + # the mouse doesn't need to be on the slider to change this control's value + if @captured_area == :slider + rect = @interactions[@captured_area] + self.bitmap.fill_rect(rect.x, rect.y, rect.width, rect.height, HOVER_COLOR) if rect + else + super + end + end + + def refresh + super + # Draw minus button + self.bitmap.fill_rect(@minus_rect.x + 2, @minus_rect.y + (@minus_rect.height / 2) - 2, @minus_rect.width - 4, 4, self.bitmap.font.color) + # Draw slider bar + self.bitmap.fill_rect(SLIDER_X, (self.height / 2) - 1, SLIDER_LENGTH, 2, self.bitmap.font.color) + # Draw notches on slider bar + 5.times do |i| + self.bitmap.fill_rect(SLIDER_X - 1 + (i * SLIDER_LENGTH / 4), (self.height / 2) - 2, 2, 4, self.bitmap.font.color) + end + # Draw slider knob + fraction = (self.value - self.min_value) / self.max_value.to_f + knob_x = (SLIDER_LENGTH * fraction).to_i + self.bitmap.fill_rect(SLIDER_X + knob_x - 4, (self.height / 2) - 6, 8, 12, SLIDER_KNOB_COLOR) + # Draw plus button + self.bitmap.fill_rect(@plus_rect.x + 2, @plus_rect.y + (@plus_rect.height / 2) - 2, @plus_rect.width - 4, 4, self.bitmap.font.color) + self.bitmap.fill_rect(@plus_rect.x + (@plus_rect.width / 2) - 2, @plus_rect.y + 2, 4, @plus_rect.height - 4, self.bitmap.font.color) + # Draw value text + draw_text(self.bitmap, VALUE_X, TEXT_OFFSET_Y, self.value.to_s) + end + + #----------------------------------------------------------------------------- + + def on_mouse_press + super + @initial_value = @value if @captured_area + end + + def on_mouse_release + return if !@captured_area # Wasn't captured to begin with + set_changed if @initial_value && @value != @initial_value + @initial_value = nil + super + end + + def update + super + # TODO: Disabled control stuff. +# return if self.disabled + case @captured_area + when :minus + # Constant decrement of value while pressing the minus button + if @hover_area == @captured_area && Input.repeat?(Input::MOUSELEFT) + self.value -= 1 + end + when :plus + # Constant incrementing of value while pressing the plus button + if @hover_area == @captured_area && Input.repeat?(Input::MOUSELEFT) + self.value += 1 + end + when :slider + # Constant updating of value depending on mouse's x position + mouse_x, mouse_y = mouse_pos + return if !mouse_x || !mouse_y + self.value = lerp(self.min_value, self.max_value + (self.max_value & 1), SLIDER_LENGTH, mouse_x - SLIDER_X) + end + end +end diff --git a/Data/Scripts/905_New controls/006 value box.rb b/Data/Scripts/905_New controls/006 value box.rb new file mode 100644 index 000000000..14fd5e9a6 --- /dev/null +++ b/Data/Scripts/905_New controls/006 value box.rb @@ -0,0 +1,139 @@ +#=============================================================================== +# +#=============================================================================== +class UIControls::ValueBox < UIControls::TextBox + attr_reader :min_value + attr_reader :max_value + + PLUS_MINUS_SIZE = 16 + CONTROL_PADDING = 2 # Gap between buttons and text box + + MINUS_X = 0 + TEXT_BOX_X = MINUS_X + PLUS_MINUS_SIZE + CONTROL_PADDING + TEXT_BOX_WIDTH = 64 + TEXT_BOX_HEIGHT = 24 + PLUS_X = TEXT_BOX_X + TEXT_BOX_WIDTH + CONTROL_PADDING + + def initialize(width, height, viewport, min_value, max_value, value) + super(width, height, viewport, value) + @min_value = min_value + @max_value = max_value + self.value = value + end + + def value=(new_value) + old_val = @value + @value = new_value.to_i.clamp(self.min_value, self.max_value) + self.invalidate if @value != old_val + end + + def min_value=(new_min) + return if new_min == @min_value + @min_value = new_min + @value = @value.clamp(self.min_value, self.max_value) + self.invalidate + end + + def max_value=(new_max) + return if new_max == @max_value + @max_value = new_max + @value = @value.clamp(self.min_value, self.max_value) + self.invalidate + end + + # TODO: If current value is 0, replace it with ch instead of inserting ch? + def insert_char(ch) + self.value = @value.to_s.insert(@cursor_pos, ch).to_i + @cursor_pos += 1 + @cursor_pos = @cursor_pos.clamp(0, @value.to_s.length) + @cursor_timer = System.uptime + @cursor_shown = true + invalidate + end + + def delete_at(index) + new_val = @value.to_s + new_val.slice!(index) + self.value = new_val.to_i + @cursor_pos -= 1 if @cursor_pos > index + @cursor_pos = @cursor_pos.clamp(0, @value.to_s.length) + @cursor_timer = System.uptime + @cursor_shown = true + invalidate + end + + def set_interactive_rects + @text_box_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2, + TEXT_BOX_WIDTH, TEXT_BOX_HEIGHT) + @minus_rect = Rect.new(MINUS_X, (self.height - PLUS_MINUS_SIZE) / 2, PLUS_MINUS_SIZE, PLUS_MINUS_SIZE) + @plus_rect = Rect.new(PLUS_X, (self.height - PLUS_MINUS_SIZE) / 2, PLUS_MINUS_SIZE, PLUS_MINUS_SIZE) + @interactions = { + :text_box => @text_box_rect, + :minus => @minus_rect, + :plus => @plus_rect + } + end + + #----------------------------------------------------------------------------- + + def refresh + super + # Draw minus button + self.bitmap.fill_rect(@minus_rect.x + 2, @minus_rect.y + (@minus_rect.height / 2) - 2, @minus_rect.width - 4, 4, self.bitmap.font.color) + # Draw plus button + self.bitmap.fill_rect(@plus_rect.x + 2, @plus_rect.y + (@plus_rect.height / 2) - 2, @plus_rect.width - 4, 4, self.bitmap.font.color) + self.bitmap.fill_rect(@plus_rect.x + (@plus_rect.width / 2) - 2, @plus_rect.y + 2, 4, @plus_rect.height - 4, self.bitmap.font.color) + end + + #----------------------------------------------------------------------------- + + def on_mouse_press + @captured_area = nil + super + if @captured_area == :text_box + # Clicked into the text box; put the text cursor in there + @cursor_pos = get_cursor_index_from_mouse_position + @cursor_timer = System.uptime + invalidate + elsif @captured_area + @initial_value = @value + else + set_changed if @initial_value && @value != @initial_value + reset_interaction + end + end + + def update_text_entry + ret = false + Input.gets.each_char do |ch| + next if !["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-"].include?(ch) + if ch == "-" + next if @min_value >= 0 || @cursor_pos > 1 || (@cursor_pos > 0 && @value >= 0) + if @value < 0 + delete_at(0) # Remove the negative sign + ret = true + next + end + end + insert_char(ch) + ret = true + end + return ret + end + + def update + super + case @captured_area + when :minus + # Constant decrement of value while pressing the minus button + if @hover_area == @captured_area && Input.repeat?(Input::MOUSELEFT) + self.value -= 1 + end + when :plus + # Constant incrementing of value while pressing the plus button + if @hover_area == @captured_area && Input.repeat?(Input::MOUSELEFT) + self.value += 1 + end + end + end +end diff --git a/Data/Scripts/905_New controls/007 button.rb b/Data/Scripts/905_New controls/007 button.rb new file mode 100644 index 000000000..058a47746 --- /dev/null +++ b/Data/Scripts/905_New controls/007 button.rb @@ -0,0 +1,50 @@ +#=============================================================================== +# +#=============================================================================== +class UIControls::Button < UIControls::BaseControl + BUTTON_X = 2 + BUTTON_PADDING = 10 + BUTTON_HEIGHT = 28 + TEXT_OFFSET_Y = 7 + + def initialize(width, height, viewport, text = "") + super(width, height, viewport) + @text = text + end + + def set_interactive_rects + text_width = self.bitmap.text_size(@text).width + @button_rect = Rect.new(BUTTON_X, (height - BUTTON_HEIGHT) / 2, + text_width + (BUTTON_PADDING * 2), BUTTON_HEIGHT) + @interactions = { + :button => @button_rect + } + end + + #----------------------------------------------------------------------------- + + # TODO: Make buttons look more different to text boxes? + def refresh + super + # Draw button outline + self.bitmap.outline_rect(@button_rect.x, @button_rect.y, + @button_rect.width, @button_rect.height, + self.bitmap.font.color) + # Draw button text + draw_text(self.bitmap, BUTTON_X + BUTTON_PADDING, TEXT_OFFSET_Y, @text) + end + + #----------------------------------------------------------------------------- + + def on_mouse_release + return if !@captured_area # Wasn't captured to begin with + # Change this control's value + if @captured_area == :button + mouse_x, mouse_y = mouse_pos + if mouse_x && mouse_y && @interactions[@captured_area].contains?(mouse_x, mouse_y) + set_changed + end + end + super # Make this control not busy again + end +end diff --git a/Data/Scripts/905_New controls/008_list.rb b/Data/Scripts/905_New controls/008_list.rb new file mode 100644 index 000000000..60c7881e9 --- /dev/null +++ b/Data/Scripts/905_New controls/008_list.rb @@ -0,0 +1,13 @@ +#=============================================================================== +# TODO +# TODO: Click an option to select it. It remains selected indefinitely. Once an +# option is selected, there's probably no way to unselect everything; the +# selection can only be moved to a different option. +# TODO: Scrollable. +# TODO: Find some way to not redraw the entire thing if the hovered option +# changes. Maybe have another bitmap to write the text on (refreshed only +# when the list is scrolled), and self's bitmap draws the hover colour +# only. +#=============================================================================== +class UIControls::List < UIControls::BaseControl +end diff --git a/Data/Scripts/905_New controls/009 dropdown list.rb b/Data/Scripts/905_New controls/009 dropdown list.rb new file mode 100644 index 000000000..f3c12db67 --- /dev/null +++ b/Data/Scripts/905_New controls/009 dropdown list.rb @@ -0,0 +1,9 @@ +#=============================================================================== +# TODO +#=============================================================================== +class UIControls::DropdownList < UIControls::BaseControl + def initialize(width, height, viewport, options, value) + # NOTE: options is a hash: keys are symbols, values are display names. + super(width, height, viewport) + end +end diff --git a/Data/Scripts/906_New controls container/001 controls container.rb b/Data/Scripts/906_New controls container/001 controls container.rb new file mode 100644 index 000000000..fe558b466 --- /dev/null +++ b/Data/Scripts/906_New controls container/001 controls container.rb @@ -0,0 +1,161 @@ +#=============================================================================== +# Controls are arranged in a list in self's bitmap. Each control is given a +# "self's bitmap's width" x LINE_SPACING area of self's bitmap to draw itself +# in. +# TODO: The act of "capturing" a control makes other controls in this container +# not update themselves, i.e. they won't colour themselves with a hover +# highlight if the mouse happens to move over it while another control is +# captured. Is there a better way of dealing with this? I'm leaning +# towards the control itself deciding if it's captured, and it being +# treated as uncaptured once it says its value has changed, but I think +# this would require manually telling all other controls in this container +# that something else is captured and they shouldn't show a hover +# highlight when updated (perhaps as a parameter in def update), which I +# don't think is ideal. Mark self as "busy" while a control is captured. +#=============================================================================== +class UIControls::ControlsContainer + attr_reader :x, :y + attr_reader :controls + + LINE_SPACING = 32 + OFFSET_FROM_LABEL_X = 80 + OFFSET_FROM_LABEL_Y = 0 + + def initialize(x, y, width, height) + @viewport = Viewport.new(x, y, width, height) + @viewport.z = 99999 + @x = x + @y = y + @width = width + @height = height + @controls = [] + @control_rects = [] + @row_count = 0 + @captured = nil + end + + def dispose + @controls.each { |c| c[1]&.dispose } + @controls.clear + @viewport.dispose + end + + #----------------------------------------------------------------------------- + + def add_label(id, label, has_label = false) + id = (id.to_s + "_label").to_sym + add_control(id, UIControls::Label.new(*control_size(has_label), @viewport, label), has_label) + end + + def add_checkbox(id, value, has_label = false) + add_control(id, UIControls::Checkbox.new(*control_size(has_label), @viewport, value), has_label) + end + + def add_labelled_checkbox(id, label, value) + add_label(id, label) + add_checkbox(id, value, true) + end + + def add_text_box(id, value, has_label = false) + add_control(id, UIControls::TextBox.new(*control_size(has_label), @viewport, value), has_label) + end + + def add_labelled_text_box(id, label, value) + add_label(id, label) + add_text_box(id, value, true) + end + + def add_slider(id, min_value, max_value, value, has_label = false) + add_control(id, UIControls::Slider.new(*control_size(has_label), @viewport, min_value, max_value, value), has_label) + end + + def add_labelled_slider(id, label, min_value, max_value, value) + add_label(id, label) + add_slider(id, min_value, max_value, value, true) + end + + def add_value_box(id, min_value, max_value, value, has_label = false) + add_control(id, UIControls::ValueBox.new(*control_size(has_label), @viewport, min_value, max_value, value), has_label) + end + + def add_labelled_value_box(id, label, min_value, max_value, value) + add_label(id, label) + add_value_box(id, min_value, max_value, value, true) + end + + def add_button(id, button_text, has_label = false) + add_control(id, UIControls::Button.new(*control_size(has_label), @viewport, button_text), has_label) + end + + def add_labelled_button(id, label, button_text) + add_label(id, label) + add_button(id, button_text, true) + end + + def add_dropdown_list(id, options, value, has_label = false) + add_control(id, UIControls::DropdownList.new(*control_size(has_label), @viewport, options, value), has_label) + end + + def add_labelled_dropdown_list(id, label, options, value) + add_label(id, label) + add_dropdown_list(id, options, value, true) + end + + #----------------------------------------------------------------------------- + + def repaint + @controls.each { |ctrl| ctrl[1].repaint } + end + + #----------------------------------------------------------------------------- + + def update + # Update controls + if @captured + # TODO: Ideally all controls will be updated here, if only to redraw + # themselves if they happen to be invalidated somehow. But that + # involves telling each control whether any other control is busy, + # to ensure that they don't show their hover colours or anything, + # which is fiddly and I'm not sure if it's the best approach. + @captured.update + @captured = nil if !@captured.busy? + else + @controls.each do |ctrl| + ctrl[1].update + @captured = ctrl[1] if ctrl[1].busy? + end + end + # Check for updated controls + @controls.each do |ctrl| + next if !ctrl[1].changed? + # TODO: Get the new value from ctrl and put it in a hash for the main + # editor class to notice and use. + ctrl[1].clear_changed + end + # Redraw controls if needed + repaint + end + + #----------------------------------------------------------------------------- + + private + + def control_size(has_label = false) + if has_label + return @width - OFFSET_FROM_LABEL_X, LINE_SPACING - OFFSET_FROM_LABEL_Y + end + return @width, LINE_SPACING + end + + def add_control(id, control, add_offset = false) + i = @controls.length + control_y = (add_offset ? @row_count - 1 : @row_count) * LINE_SPACING + @control_rects[i] = Rect.new(0, control_y, control.width, control.height) + control.x = @control_rects[i].x + (add_offset ? OFFSET_FROM_LABEL_X : 0) + control.y = @control_rects[i].y + (add_offset ? OFFSET_FROM_LABEL_Y : 0) + control.set_interactive_rects + @controls[i] = [id, control] + @row_count += 1 if !add_offset + repaint + end +end diff --git a/Data/Scripts/910_New anim editor/001_anim selection.rb b/Data/Scripts/910_New anim editor/001_anim selection.rb new file mode 100644 index 000000000..443235291 --- /dev/null +++ b/Data/Scripts/910_New anim editor/001_anim selection.rb @@ -0,0 +1,97 @@ +# TODO: Come up with a better name for this class. I'm not sure I want to merge +# this class with the editor class. +class AnimationEditorLoadScreen + WINDOW_WIDTH = Settings::SCREEN_WIDTH + (32 * 10) + WINDOW_HEIGHT = Settings::SCREEN_HEIGHT + (32 * 10) + + ANIMATIONS_LIST_X = 4 + ANIMATIONS_LIST_Y = 4 + ANIMATIONS_LIST_WIDTH = 300 + ANIMATIONS_LIST_HEIGHT = WINDOW_HEIGHT - (ANIMATIONS_LIST_Y * 2) + + def initialize + @viewport = Viewport.new(0, 0, AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT) + @viewport.z = 99999 + @screen_bitmap = BitmapSprite.new(AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT, @viewport) + draw_editor_background + end + + def dispose + @screen_bitmap.dispose + @viewport.dispose + end + + def draw_editor_background + # Fill the whole screen with black + @screen_bitmap.bitmap.fill_rect( + 0, 0, AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT, Color.black + ) + # Outline around animations list + @screen_bitmap.bitmap.outline_rect( + ANIMATIONS_LIST_X - 3, ANIMATIONS_LIST_Y - 3, + ANIMATIONS_LIST_WIDTH + 6, ANIMATIONS_LIST_HEIGHT + 6, Color.white + ) + @screen_bitmap.bitmap.outline_rect( + ANIMATIONS_LIST_X - 2, ANIMATIONS_LIST_Y - 2, + ANIMATIONS_LIST_WIDTH + 4, ANIMATIONS_LIST_HEIGHT + 4, Color.black + ) + @screen_bitmap.bitmap.outline_rect( + ANIMATIONS_LIST_X - 1, ANIMATIONS_LIST_Y - 1, + ANIMATIONS_LIST_WIDTH + 2, ANIMATIONS_LIST_HEIGHT + 2, Color.white + ) + # Fill the animations list with white + @screen_bitmap.bitmap.fill_rect( + ANIMATIONS_LIST_X, ANIMATIONS_LIST_Y, ANIMATIONS_LIST_WIDTH, ANIMATIONS_LIST_HEIGHT, Color.white + ) + end + + def update + # TODO: Update the controls (animations list, Load button, etc.). + end + + def run + Input.text_input = false + loop do + inputting_text = Input.text_input + Graphics.update + Input.update + update + if !inputting_text + break if Input.trigger?(Input::BACK) + end + # Open editor with animation + # TODO: If the Load button is pressed while an animation is selected. + if Input.trigger?(Input::USE) + # TODO: Add animation to be edited as an argument. + screen = AnimationEditor.new + screen.run + end + end + dispose + end +end + +#=============================================================================== +# Start +#=============================================================================== +def test_anim_editor + Graphics.resize_screen(AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT) + pbSetResizeFactor(1) + screen = AnimationEditorLoadScreen.new + screen.run + Graphics.resize_screen(Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) + pbSetResizeFactor($PokemonSystem.screensize) + $game_map&.autoplay +end + +#=============================================================================== +# Add to Debug menu +#=============================================================================== +MenuHandlers.add(:debug_menu, :use_pc, { + "name" => "Test new animation editor", + "parent" => :main, + "description" => "Test new animation editor", + "effect" => proc { + test_anim_editor + } +}) diff --git a/Data/Scripts/910_New anim editor/010 editor scene.rb b/Data/Scripts/910_New anim editor/010 editor scene.rb new file mode 100644 index 000000000..df3ad2ee3 --- /dev/null +++ b/Data/Scripts/910_New anim editor/010 editor scene.rb @@ -0,0 +1,115 @@ +# TODO: Should I split this code into visual and mechanical classes, a la the +# other UI screens? +#=============================================================================== +# TODO: Need a way to recognise when text is being input into something +# (Input.text_input) and disable all keyboard shortcuts if so. If only +# this class has keyboard shortcuts in it, then it should be okay already. +#=============================================================================== +class AnimationEditor + WINDOW_WIDTH = AnimationEditorLoadScreen::WINDOW_WIDTH + WINDOW_HEIGHT = AnimationEditorLoadScreen::WINDOW_HEIGHT + + CANVAS_X = 4 + CANVAS_Y = 32 + 4 + CANVAS_WIDTH = Settings::SCREEN_WIDTH + CANVAS_HEIGHT = Settings::SCREEN_HEIGHT + SIDE_PANEL_X = CANVAS_X + CANVAS_WIDTH + 4 + 4 + SIDE_PANEL_Y = CANVAS_Y + SIDE_PANEL_WIDTH = WINDOW_WIDTH - SIDE_PANEL_X - 4 + SIDE_PANEL_HEIGHT = CANVAS_HEIGHT + (32 * 2) + + # TODO: Add a parameter which is the animation to be edited, and also a + # parameter for that animation's ID in GameData (just for the sake of + # saving changes over the same GameData slot). + def initialize + @viewport = Viewport.new(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT) + @viewport.z = 99999 + @screen_bitmap = BitmapSprite.new(WINDOW_WIDTH, WINDOW_HEIGHT, @viewport) + draw_editor_background + # Canvas + @canvas = Sprite.new(@viewport) + @canvas.x = CANVAS_X + @canvas.y = CANVAS_Y + @canvas.bitmap = RPG::Cache.load_bitmap("Graphics/Battlebacks/", "field_bg") + # Side pane + @side_pane = ControlPane.new(SIDE_PANEL_X, SIDE_PANEL_Y, SIDE_PANEL_WIDTH, SIDE_PANEL_HEIGHT) + set_side_panel_contents + end + + def dispose + @screen_bitmap.dispose + @canvas.dispose + @side_pane.dispose + @viewport.dispose + end + + def draw_editor_background + # Fill the whole screen with black + @screen_bitmap.bitmap.fill_rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, Color.black) + # Outline around canvas + @screen_bitmap.bitmap.outline_rect(CANVAS_X - 3, CANVAS_Y - 3, CANVAS_WIDTH + 6, CANVAS_HEIGHT + 6, Color.white) + @screen_bitmap.bitmap.outline_rect(CANVAS_X - 2, CANVAS_Y - 2, CANVAS_WIDTH + 4, CANVAS_HEIGHT + 4, Color.black) + @screen_bitmap.bitmap.outline_rect(CANVAS_X - 1, CANVAS_Y - 1, CANVAS_WIDTH + 2, CANVAS_HEIGHT + 2, Color.white) + # Outline around side panel + @screen_bitmap.bitmap.outline_rect(SIDE_PANEL_X - 3, SIDE_PANEL_Y - 3, SIDE_PANEL_WIDTH + 6, SIDE_PANEL_HEIGHT + 6, Color.white) + @screen_bitmap.bitmap.outline_rect(SIDE_PANEL_X - 2, SIDE_PANEL_Y - 2, SIDE_PANEL_WIDTH + 4, SIDE_PANEL_HEIGHT + 4, Color.black) + @screen_bitmap.bitmap.outline_rect(SIDE_PANEL_X - 1, SIDE_PANEL_Y - 1, SIDE_PANEL_WIDTH + 2, SIDE_PANEL_HEIGHT + 2, Color.white) + # Fill the side panel with white + @screen_bitmap.bitmap.fill_rect(SIDE_PANEL_X, SIDE_PANEL_Y, SIDE_PANEL_WIDTH, SIDE_PANEL_HEIGHT, Color.white) + end + + def set_side_panel_contents + @side_pane.add_labelled_text_box(:name, "Name", "Untitled") + @side_pane.add_labelled_value_box(:x, "X", -128, CANVAS_WIDTH + 128, 64) + @side_pane.add_labelled_value_box(:y, "Y", -128, CANVAS_HEIGHT + 128, 96) + @side_pane.add_labelled_value_box(:zoom_x, "Zoom X", 0, 1000, 100) + @side_pane.add_labelled_value_box(:zoom_y, "Zoom Y", 0, 1000, 100) + @side_pane.add_labelled_value_box(:angle, "Angle", -1080, 1080, 0) + @side_pane.add_labelled_checkbox(:visible, "Visible", true) + @side_pane.add_labelled_slider(:opacity, "Opacity", 0, 255, 255) + @side_pane.add_labelled_checkbox(:flip, "Flip", false) + @side_pane.add_labelled_dropdown_list(:priority, "Priority", { # TODO: Include sub-priority. + :behind_all => "Behind all", + :behind_user => "Behind user", + :above_user => "In front of user", + :above_all => "In front of everything" + }, :above_user) +# @side_pane.add_labelled_dropdown_list(:focus, "Focus", { +# :user => "User", +# :target => "Target", +# :user_and_target => "User and target", +# :screen => "Screen" +# }, :user) + @side_pane.add_labelled_button(:color, "Color/tone", "Edit") + @side_pane.add_labelled_button(:graphic, "Graphic", "Change") + end + + def update + @canvas.update + @side_pane.update + # TODO: Check @side_pane for whether it's changed. Note that it includes + # buttons which won't themselves have a value but will flag themselves + # as changed when clicked; code here should determine what happens if + # a button is pressed (unless I put said code in a proc passed to the + # button control; said code will be lengthy). + end + + def run + Input.text_input = false + loop do + inputting_text = Input.text_input + Graphics.update + Input.update + update + if !inputting_text + if Input.trigger?(Input::BACK) + # TODO: Ask to save/discard changes. + # TODO: When saving, add animation to GameData and rewrite animation's + # parent PBS file (which could include multiple animations). + break + end + end + end + dispose + end +end diff --git a/Data/Scripts/910_New anim editor/020 button pane.rb b/Data/Scripts/910_New anim editor/020 button pane.rb new file mode 100644 index 000000000..e2bf531e1 --- /dev/null +++ b/Data/Scripts/910_New anim editor/020 button pane.rb @@ -0,0 +1,12 @@ +#=============================================================================== +# +#=============================================================================== +class AnimationEditor::ControlPane < UIControls::ControlsContainer + def on_control_release + # TODO: Update data for @captured control, because it may have changed. + # Gather data from all controls in this container and put them in a + # hash; it's up to the main editor screen to notice/read it, edit + # animation data accordingly, and then tell this container to nil that + # hash again. + end +end