Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ##
- # Helper functions
- #
- class Module
- def attr_sec_accessor(sym, default = 0)
- attr_writer sym
- attr_sec_reader sym, default
- end
- def attr_sec_reader(sym, default = 0)
- sym = sym.id2name
- string = "def #{sym} ;" +
- " @#{sym} = #{default} if @#{sym}.nil? ;" +
- " @#{sym} ;" +
- "end ;"
- module_eval(string)
- end
- end
- module Utility
- #############
- # DLL STUFF #
- #############
- READ_INI = Win32API.new('kernel32', 'GetPrivateProfileStringA',
- %w(p p p p l p), 'l')
- WRITE_INI = Win32API.new('kernel32', 'WritePrivateProfileStringA',
- %w(p p p p), 'l')
- ##
- # Read from system ini
- #
- def self.read_ini(key_name, app_name = 'Game', buffer_size = 256,
- filename = 'Game.ini', default = '')
- buffer = "\0" * buffer_size
- READ_INI.call(app_name, key_name, default, buffer, buffer_size - 1,
- ".\\" + filename)
- return buffer.delete("\0")
- end
- ##
- # Write to system ini
- #
- def self.write_ini(key_name, value, app_name = 'Game', filename = 'Game.ini')
- return WRITE_INI.call(app_name, key_name, value.to_s, ".\\" + filename)
- end
- end
- #==============
- # Mouse engine
- #==============
- # Encapsulation of a mouse event
- MouseEvent = Struct.new(:sender, :trigger, :info)
- #==============================================================================
- # ** Mouse module
- #------------------------------------------------------------------------------
- # Manages the Win32API calls and the base mouse functionalities
- #==============================================================================
- module Mouse
- #--------------------------------------------------------------------------
- # * Constants
- #--------------------------------------------------------------------------
- module Configuration
- # Default mouse icon
- DEFAULT_ICON = 'cursor'
- # Frames to wait between each repeat
- REPEAT_WAIT = 4
- # Frames to wait from the first repeat to the following repeats
- REPEAT_START_WAIT = 16
- # Number of frames to wait between each scroll
- SCROLL_WAIT = 5
- # Z-value of the cursor sprite.
- CURSOR_Z_VALUE = 50000
- # How many frames to wait between each client window position update.
- # Putting this too low can impact performance. Putting this too high
- # will increase how long time it takes for the cursor to adjust after the
- # client window is moved.
- FRAMES_PER_FIND_WINDOW = 10
- end
- #--------------------------------------------------------------------------
- # * Mouse to Input Triggers
- #--------------------------------------------------------------------------
- LEFT_CLICK = 1
- RIGHT_CLICK = 2
- MIDDLE_CLICK = 4
- MOUSE_TRIGGERS = [LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK]
- #--------------------------------------------------------------------------
- # * API Declaration
- #--------------------------------------------------------------------------
- ASYNC_KEY = Win32API.new('user32', 'GetAsyncKeyState', 'i', 'i')
- SCREEN_TO_CLIENT = Win32API.new('user32', 'ScreenToClient', %w(l p), 'i')
- CURSOR_POS = Win32API.new('user32', 'GetCursorPos', 'p', 'i')
- FIND_WINDOW = Win32API.new('user32', 'FindWindowA', %w(p p), 'l')
- module_function
- ##
- # Start up procedures
- #
- def start_up
- # Create a reference to the configuration module (If you want several)
- @@const = Configuration
- # Initialize button states
- @button_states = Array.new(5, 0)
- # Compute the handle for the window
- window_handle
- # Read mouse status
- mouse_status = Utility.read_ini('mouse')
- @enabled = !(mouse_status == '0')
- # Create mouse sprite if the mouse script is enabled
- if @enabled
- # Create mouse sprite
- create_cursor
- # Initial update
- update
- end
- end
- ##
- # Shutdown procedures
- #
- def shutdown
- # Dispose the mouse sprite
- @mouse_sprite.dispose unless (@mouse_sprite.nil? || @mouse_sprite.disposed?)
- end
- ##
- # Turn the mouse script on
- # Nothing will happen if it is already turned on
- #
- def turn_on
- return if self.enabled
- @enabled = true
- # Create mouse sprite
- create_cursor
- # Update cursor
- update
- end
- ##
- # Turn the mouse script off.
- # Nothing will happen if it is already turned off
- #
- def turn_off
- return unless self.enabled
- @enabled = false
- # Show the cursor if it was hidden
- unless @mouse_sprite.nil? || @mouse_sprite.disposed?
- # Dispose the mouse sprite
- @mouse_sprite.dispose
- # Remove the reference so the garbage cleaner will remove it eventually
- @mouse_sprite = nil
- end
- end
- ##
- # Update
- #
- def update
- # Only update if enabled
- return unless self.enabled
- # Get new position
- sx, sy = screen_pos
- # If the position of the mouse have changed
- if @sx != sx || @sy != sy
- # Store new screen x and y
- @sx = sx
- @sy = sy
- # Get relative position
- rx, ry = screen_to_client(sx, sy)
- # Change location of the cursor
- @mouse_sprite.x = rx
- @mouse_sprite.y = ry
- end
- # Check for clicks
- for trigger in MOUSE_TRIGGERS
- # Gets Async State
- state = ASYNC_KEY.call(trigger)
- # Update triggering information
- if state == 0 || state == 1
- if @button_states[trigger] > 0
- @button_states[trigger] *= -1
- else
- @button_states[trigger] = 0
- end
- else
- if @button_states[trigger] > 0
- @button_states[trigger] += 1
- else
- @button_states[trigger] = 1
- end
- end
- end
- end
- ##
- # Check for press
- # Returns false if Mouse is not enabled
- #
- def press?(id = LEFT_CLICK)
- return false unless self.enabled
- return @button_states[id] >= 1
- end
- ##
- # Check for trigger
- # Returns false if Mouse is not enabled
- #
- def trigger?(id = LEFT_CLICK)
- return false unless self.enabled
- return @button_states[id] == 1
- end
- ##
- # Check for repeat
- # Returns false if Mouse is not enabled
- #
- def repeat?(id = LEFT_CLICK)
- return false unless self.enabled
- if @button_states[id] <= 0
- return false
- elsif @button_states[id] == 1
- return true
- elsif @button_states[id] > @@const::REPEAT_START_WAIT
- return @button_states[id] % @@const::REPEAT_WAIT == 1
- end
- end
- ##
- # Return true if the mouse script is enabled
- #
- def enabled
- @enabled
- end
- ##
- # Get screen coordinates
- #
- def screen_pos
- # Packs the 0,0 position as two longs (To use as buffer)
- pos = [0, 0].pack('ll')
- # Returns Unpacked Cursor Position Call
- return CURSOR_POS.call(pos) == 0 ? nil : pos.unpack('ll')
- end
- ##
- # Converts screen X- and Y-coordinates to relative coordinates.
- # Top-left corner = (0, 0)
- #
- def pos
- return @mouse_sprite.x, @mouse_sprite.y
- end
- ##
- # Check if the mouse is inside the window
- #
- def inside?
- x, y = pos
- return x >= 0 && x < Graphics.width && y >= 0 && y < Graphics.height
- end
- ##
- # Screen coordinates converted to be relative to client window
- # Note that the position of the client window is updated every X frames
- # unless forced by given true as the third argument.
- #
- def screen_to_client(x, y, force = false)
- # Return nil if X & Y empty
- return nil unless x and y
- if @original_pos.nil?
- @screen_to_client_wait = 0
- end
- if @screen_to_client_wait <= 0 || force
- # Locate upper-left corner
- pack = [0, 0].pack('ll')
- SCREEN_TO_CLIENT.call(window_handle, pack)
- @original_pos = pack.unpack('ll')
- # Wait 10 frames before the next look-up
- @screen_to_client_wait = Configuration::FRAMES_PER_FIND_WINDOW
- else
- @screen_to_client_wait -= 1
- end
- # Translocate X- and Y-coordinates
- x += @original_pos[0]
- y += @original_pos[1]
- return x, y
- end
- ##
- # Retrieve the window handle
- #
- def window_handle
- if @window_handle.nil?
- # Finds Game Name
- game_name = Utility.read_ini('Title')
- # Finds Window
- @window_handle = FIND_WINDOW.call('RGSS Player', game_name)
- end
- return @window_handle
- end
- # Opens up the anonymous class of the module
- class << self
- ###################
- # Private methods #
- ###################
- private
- ##
- # Create the cursor
- #
- def create_cursor
- @mouse_sprite = Sprite.new
- @mouse_sprite.z = Configuration::CURSOR_Z_VALUE
- @mouse_sprite.bitmap = Cache.picture(@@const::DEFAULT_ICON)
- end
- end
- end
- ##
- # Lets the mouse triggering state be updated together with the
- # default Input update method.
- #
- module Input
- class << self
- alias_method(:zer_mouse_input_update,:update)
- def update
- Mouse.update
- zer_mouse_input_update
- end
- end
- end
- # Initialize the mouse
- Mouse.start_up
- #==============================================================================
- # ** Mouse_Windows
- #------------------------------------------------------------------------------
- # For scenes to include
- #==============================================================================
- module Mouse_Windows
- attr_sec_reader :mouse_windows, '[]'
- ##
- # Update the mouse
- #
- def mouse_update
- # Do nothing if the mouse is not enabled
- return unless Mouse.enabled
- mx, my = Mouse.pos
- # Check if the mouse position has changed
- if @mx == mx && @my == my
- moved = false
- else
- moved = true
- @mx = mx
- @my = my
- end
- # Go through the mouse enabled windows
- for window in mouse_windows
- # Remove disposed windows
- if window.disposed?
- mouse_windows.delete(window)
- next
- end
- # Ignore inactive and invisible windows
- next unless window.active && window.visible
- # Check if the mouse is over the window
- if mouse_inside_window?(window)
- # If the mouse have moved
- if moved
- # Calculate relative coordinates
- rx = mx - window.x
- ry = my - window.y
- window.mouse_move(rx, ry)
- end
- # Let the window update with the mouse over it
- window.mouse_update
- end
- end
- end
- ##
- # Check if the mouse is inside the given window
- #
- def mouse_inside_window?(window)
- mx, my = @mx, @my
- return mx >= window.x && mx < (window.x + window.width) &&
- my >= window.y && my < (window.y + window.height)
- end
- ##
- # Events can be fired on scenes
- #
- def fire_event(event)
- return unless $DEBUG
- raise StandardError.new("fire_event(*args) was not overwritten in #{self.class}")
- end
- end
- class Window_Base < Window
- attr_accessor :owner
- ##
- # Update mouse position
- # Mouse enabled subclasses should overwrite this method
- #
- def mouse_move(mx, my)
- return unless $TEST
- raise StandardError.new("mouse_move(mx, my) was not overwritten in #{self.class}")
- end
- ##
- # Update mouse (Only called if mouse is enabled)
- # Note: The mouse is over the window
- #
- def mouse_update
- return unless $TEST
- raise StandardError.new("mouse_update was not overwritten in #{self.class}")
- end
- ##
- # Check if the mouse is over the contents, i.e. not over the window border
- #
- def over_contents?(mx, my)
- return mx >= 16 && mx < width - 16 &&
- my >= 16 && my < height - 16
- end
- end
- class Window_Selectable < Window_Base
- attr_sec_accessor :scroll_wait, 0
- ##
- # Calculates the index from the x and y coordinate
- # Assumes (0,0) to be upper-left corner of the contents
- #
- def calculate_index(x, y)
- rel_x, rel_y = x - self.padding, y - self.padding
- for index in 0...item_max
- rect = item_rect(index)
- if rel_x.between?(rect.x, rect.x + rect.width) &&
- rel_y.between?(rect.y, rect.y + rect.height)
- return index
- end
- end
- # No item found
- return -1
- end
- ##
- #
- #
- def cause_scroll(index)
- return false #TODO
- # Get row
- row = index / @column_max
- # If row is before top row or more to back than back row
- return row < self.top_row || row > self.top_row + (self.page_row_max - 1)
- end
- ##
- # Update mouse position
- # Mouse enabled subclasses should overwrite this method
- #
- def mouse_move(mx, my)
- # Cursor position less than 0 implies that the cursor is hidden
- return if @index < 0 || !self.active
- # Store the coordinates
- @mx, @my = mx, my
- # Ignore the move if it is not over the contents
- #if mx >= self.padding && mx < width self.padding
- index = calculate_index(mx, my)
- # If the index is valid
- if index < item_max && index >= 0
- # Scroll wait
- if cause_scroll(index) && self.scroll_wait > 0
- return
- elsif cause_scroll(index)
- self.scroll_wait = System::Mouse::SCROLL_WAIT
- @scrolling = true
- else
- @scrolling = false
- end
- # Change the index
- select(index)
- end
- #end
- end
- ##
- # Update mouse (Only called if mouse is enabled)
- # Note: The mouse is over the window
- #
- def mouse_update
- # Only update if active
- return unless open? && active
- # Scroll wait update
- if self.scroll_wait > 0
- self.scroll_wait -= 1
- return
- elsif @scrolling
- # See if the scrolling should continue
- index = calculate_index(@mx-16, @my-16)
- if cause_scroll(index) && index < @item_max && index >= 0
- self.scroll_wait = Mouse::Configuration::SCROLL_WAIT
- @index = index
- else
- # Stop scrolling
- @scrolling = false
- end
- end
- @mx, @my = -1, -1 if @mx.nil?
- # Check for click
- if Mouse.trigger?(Mouse::LEFT_CLICK) && over_contents?(@mx, @my)
- fire_event(Mouse::LEFT_CLICK)
- end
- end
- ##
- # Right mouse button starts the cancel process
- # This is added to the process_handling method so that it is processed
- # regardless of the cursor location.
- #
- alias_method(:zer_mouse_process_handling, :process_handling)
- def process_handling
- return unless open? && active
- zer_mouse_process_handling
- if Mouse.trigger?(Mouse::RIGHT_CLICK)
- process_cancel if cancel_enabled?
- end
- end
- ##
- # Accept or cancel according to the trigger.
- # Note, the trigger is consumed if it is a right or left click.
- # TODO
- #
- def fire_event(trigger)
- if trigger == Mouse::LEFT_CLICK
- process_ok if ok_enabled?
- else
- # Fire the event in the owner
- owner.fire_event(MouseEvent.new(self, trigger, index))
- end
- end
- end
- class Scene_Base
- include Mouse_Windows
- ##
- # Add the given window to the list mouse enabled windows
- # where the owner of the window is the current scene.
- #
- def add_mouse_window(window)
- # Add as a mouse enabled window
- mouse_windows << window
- # Set owner
- window.owner = self
- end
- ##
- # Overwrite the this method in subclasses to assign the mouse enabled windows.
- # add_mouse_window(window) can be used in most cases.
- #
- def assign_mouse_windows
- return unless $TEST
- raise StandardError.new("assign_mouse_windows was not overwritten in #{self.class}")
- end
- ##
- # Overwrite this method in scenes where the mouse is enabled.
- #
- def mouse_enabled?
- return false
- end
- # Add assignment of mouse windows to the start method
- alias_method(:zer_mouse_post_start, :post_start)
- def post_start
- assign_mouse_windows if mouse_enabled?
- zer_mouse_post_start
- end
- ##
- # Update mouse input during update_all_windows
- alias_method(:zer_mouse_update_all_windows, :update_all_windows)
- def update_all_windows
- mouse_update
- zer_mouse_update_all_windows
- end
- end
- #=====================
- # Scenes integrations
- #=====================
- class Scene_Title
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@command_window)
- end
- end
- class Scene_Menu < Scene_MenuBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@command_window)
- add_mouse_window(@status_window)
- end
- end
- class Scene_Item < Scene_ItemBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@item_window)
- add_mouse_window(@category_window)
- add_mouse_window(@actor_window)
- end
- end
- class Scene_Skill < Scene_ItemBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@command_window)
- end
- end
- class Scene_Equip < Scene_MenuBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@command_window)
- end
- end
- class Scene_Status < Scene_MenuBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@status_window)
- end
- end
- class Scene_End < Scene_MenuBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@command_window)
- end
- end
- class Scene_Shop < Scene_MenuBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@command_window)
- end
- end
- class Scene_Name < Scene_MenuBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@input_window)
- end
- end
- class Scene_Debug < Scene_MenuBase
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@left_window)
- add_mouse_window(@right_window)
- end
- end
- class Scene_Battle < Scene_Base
- ## Mouse is enabled
- def mouse_enabled?; true; end
- ##
- # Assign mouse enabled windows
- #
- def assign_mouse_windows
- add_mouse_window(@command_window)
- end
- end
- class Scene_Gameover < Scene_Base
- alias_method(:zer_mouse_update, :update)
- def update
- if Mouse.trigger?(Mouse::LEFT_CLICK) ||
- Mouse.trigger?(Mouse::RIGHT_CLICK)
- super
- goto_title
- else
- zer_mouse_update
- end
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement