mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 13:15:01 +00:00
Initial proof of concept commit
This commit is contained in:
8
Data/Scripts/900_New utilities/001 utilities.rb
Normal file
8
Data/Scripts/900_New utilities/001 utilities.rb
Normal file
@@ -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
|
||||
63
Data/Scripts/901_GameData/Animation.rb
Normal file
63
Data/Scripts/901_GameData/Animation.rb
Normal file
@@ -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
|
||||
2
Data/Scripts/905_New controls/000 UIControls.rb
Normal file
2
Data/Scripts/905_New controls/000 UIControls.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
# Container module for control classes.
|
||||
module UIControls; end
|
||||
180
Data/Scripts/905_New controls/001 basic control.rb
Normal file
180
Data/Scripts/905_New controls/001 basic control.rb
Normal file
@@ -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
|
||||
24
Data/Scripts/905_New controls/002 label.rb
Normal file
24
Data/Scripts/905_New controls/002 label.rb
Normal file
@@ -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
|
||||
68
Data/Scripts/905_New controls/003 checkbox.rb
Normal file
68
Data/Scripts/905_New controls/003 checkbox.rb
Normal file
@@ -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
|
||||
289
Data/Scripts/905_New controls/004 text box.rb
Normal file
289
Data/Scripts/905_New controls/004 text box.rb
Normal file
@@ -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
|
||||
128
Data/Scripts/905_New controls/005 number slider.rb
Normal file
128
Data/Scripts/905_New controls/005 number slider.rb
Normal file
@@ -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
|
||||
139
Data/Scripts/905_New controls/006 value box.rb
Normal file
139
Data/Scripts/905_New controls/006 value box.rb
Normal file
@@ -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
|
||||
50
Data/Scripts/905_New controls/007 button.rb
Normal file
50
Data/Scripts/905_New controls/007 button.rb
Normal file
@@ -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
|
||||
13
Data/Scripts/905_New controls/008_list.rb
Normal file
13
Data/Scripts/905_New controls/008_list.rb
Normal file
@@ -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
|
||||
9
Data/Scripts/905_New controls/009 dropdown list.rb
Normal file
9
Data/Scripts/905_New controls/009 dropdown list.rb
Normal file
@@ -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
|
||||
@@ -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
|
||||
97
Data/Scripts/910_New anim editor/001_anim selection.rb
Normal file
97
Data/Scripts/910_New anim editor/001_anim selection.rb
Normal file
@@ -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
|
||||
}
|
||||
})
|
||||
115
Data/Scripts/910_New anim editor/010 editor scene.rb
Normal file
115
Data/Scripts/910_New anim editor/010 editor scene.rb
Normal file
@@ -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
|
||||
12
Data/Scripts/910_New anim editor/020 button pane.rb
Normal file
12
Data/Scripts/910_New anim editor/020 button pane.rb
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user