Advertisement
Zeriab

(WIP) Mouse script conversion to VX Ace

Mar 12th, 2013
306
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 18.87 KB | None | 0 0
  1. ##
  2. # Helper functions
  3. #
  4. class Module
  5.   def attr_sec_accessor(sym, default = 0)
  6.     attr_writer sym
  7.     attr_sec_reader sym, default
  8.   end
  9.  
  10.   def attr_sec_reader(sym, default = 0)
  11.     sym = sym.id2name
  12.     string = "def #{sym} ;" +
  13.              "  @#{sym} = #{default}  if @#{sym}.nil? ;" +
  14.              "  @#{sym} ;" +
  15.              "end ;"
  16.     module_eval(string)
  17.   end
  18. end
  19.  
  20. module Utility
  21.   #############
  22.   # DLL STUFF #
  23.   #############
  24.   READ_INI         = Win32API.new('kernel32',  'GetPrivateProfileStringA',
  25.                                   %w(p p p p l p), 'l')
  26.   WRITE_INI        = Win32API.new('kernel32',  'WritePrivateProfileStringA',
  27.                                   %w(p p p p), 'l')
  28.   ##
  29.   # Read from system ini
  30.   #
  31.   def self.read_ini(key_name, app_name = 'Game', buffer_size = 256,
  32.                     filename = 'Game.ini', default = '')
  33.     buffer = "\0" * buffer_size
  34.     READ_INI.call(app_name, key_name, default, buffer, buffer_size - 1,
  35.                   ".\\" + filename)
  36.     return buffer.delete("\0")
  37.   end
  38.  
  39.   ##
  40.   # Write to system ini
  41.   #
  42.   def self.write_ini(key_name, value, app_name = 'Game', filename = 'Game.ini')
  43.     return WRITE_INI.call(app_name, key_name, value.to_s, ".\\" + filename)
  44.   end
  45. end
  46.  
  47.  
  48. #==============
  49. # Mouse engine
  50. #==============
  51.  
  52. # Encapsulation of a mouse event
  53. MouseEvent = Struct.new(:sender, :trigger, :info)
  54.  
  55. #==============================================================================
  56. # ** Mouse module
  57. #------------------------------------------------------------------------------
  58. # Manages the Win32API calls and the base mouse functionalities
  59. #==============================================================================
  60. module Mouse
  61.   #--------------------------------------------------------------------------
  62.   # * Constants
  63.   #--------------------------------------------------------------------------
  64.   module Configuration
  65.     # Default mouse icon
  66.     DEFAULT_ICON = 'cursor'
  67.     # Frames to wait between each repeat
  68.     REPEAT_WAIT = 4
  69.     # Frames to wait from the first repeat to the following repeats
  70.     REPEAT_START_WAIT = 16
  71.     # Number of frames to wait between each scroll
  72.     SCROLL_WAIT = 5
  73.    
  74.     # Z-value of the cursor sprite.
  75.     CURSOR_Z_VALUE = 50000
  76.     # How many frames to wait between each client window position update.
  77.     # Putting this too low can impact performance. Putting this too high
  78.     # will increase how long time it takes for the cursor to adjust after the
  79.     # client window is moved.
  80.     FRAMES_PER_FIND_WINDOW = 10
  81.   end
  82.   #--------------------------------------------------------------------------
  83.   # * Mouse to Input Triggers
  84.   #--------------------------------------------------------------------------
  85.   LEFT_CLICK = 1
  86.   RIGHT_CLICK = 2
  87.   MIDDLE_CLICK = 4
  88.  
  89.   MOUSE_TRIGGERS = [LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK]
  90.   #--------------------------------------------------------------------------
  91.   # * API Declaration
  92.   #--------------------------------------------------------------------------
  93.   ASYNC_KEY        = Win32API.new('user32',    'GetAsyncKeyState', 'i',     'i')
  94.   SCREEN_TO_CLIENT = Win32API.new('user32',    'ScreenToClient',   %w(l p), 'i')
  95.   CURSOR_POS       = Win32API.new('user32',    'GetCursorPos',     'p',     'i')
  96.   FIND_WINDOW      = Win32API.new('user32',    'FindWindowA',      %w(p p), 'l')
  97.  
  98.   module_function
  99.   ##
  100.   # Start up procedures
  101.   #
  102.   def start_up
  103.     # Create a reference to the configuration module (If you want several)
  104.     @@const = Configuration
  105.     # Initialize button states
  106.     @button_states = Array.new(5, 0)
  107.     # Compute the handle for the window
  108.     window_handle
  109.     # Read mouse status
  110.     mouse_status = Utility.read_ini('mouse')
  111.     @enabled = !(mouse_status == '0')
  112.     # Create mouse sprite if the mouse script is enabled
  113.     if @enabled
  114.       # Create mouse sprite
  115.       create_cursor
  116.       # Initial update
  117.       update
  118.     end
  119.   end
  120.  
  121.   ##
  122.   # Shutdown procedures
  123.   #
  124.   def shutdown
  125.     # Dispose the mouse sprite
  126.     @mouse_sprite.dispose unless (@mouse_sprite.nil? || @mouse_sprite.disposed?)
  127.   end
  128.  
  129.   ##
  130.   # Turn the mouse script on
  131.   # Nothing will happen if it is already turned on
  132.   #
  133.   def turn_on
  134.     return if self.enabled
  135.     @enabled = true
  136.     # Create mouse sprite
  137.     create_cursor
  138.     # Update cursor
  139.     update
  140.   end
  141.  
  142.   ##
  143.   # Turn the mouse script off.
  144.   # Nothing will happen if it is already turned off
  145.   #
  146.   def turn_off
  147.     return unless self.enabled
  148.     @enabled = false
  149.     # Show the cursor if it was hidden
  150.     unless @mouse_sprite.nil? || @mouse_sprite.disposed?
  151.       # Dispose the mouse sprite
  152.       @mouse_sprite.dispose
  153.       # Remove the reference so the garbage cleaner will remove it eventually
  154.       @mouse_sprite = nil
  155.     end
  156.   end
  157.  
  158.   ##
  159.   # Update
  160.   #
  161.   def update
  162.     # Only update if enabled
  163.     return unless self.enabled
  164.     # Get new position
  165.     sx, sy = screen_pos
  166.     # If the position of the mouse have changed
  167.     if @sx != sx || @sy != sy
  168.       # Store new screen x and y
  169.       @sx = sx
  170.       @sy = sy
  171.       # Get relative position
  172.       rx, ry = screen_to_client(sx, sy)
  173.       # Change location of the cursor
  174.       @mouse_sprite.x = rx
  175.       @mouse_sprite.y = ry
  176.     end
  177.     # Check for clicks
  178.     for trigger in MOUSE_TRIGGERS
  179.       # Gets Async State
  180.       state = ASYNC_KEY.call(trigger)
  181.       # Update triggering information
  182.       if state == 0 || state == 1
  183.         if @button_states[trigger] > 0
  184.           @button_states[trigger] *= -1
  185.         else
  186.           @button_states[trigger] = 0
  187.         end
  188.       else
  189.         if @button_states[trigger] > 0
  190.           @button_states[trigger] += 1
  191.         else
  192.           @button_states[trigger] = 1
  193.         end
  194.       end
  195.     end
  196.   end
  197.  
  198.   ##
  199.   # Check for press
  200.   #   Returns false if Mouse is not enabled
  201.   #
  202.   def press?(id = LEFT_CLICK)
  203.     return false unless self.enabled
  204.     return @button_states[id] >= 1
  205.   end
  206.  
  207.   ##
  208.   # Check for trigger
  209.   #   Returns false if Mouse is not enabled
  210.   #
  211.   def trigger?(id = LEFT_CLICK)
  212.     return false unless self.enabled
  213.     return @button_states[id] == 1
  214.   end
  215.  
  216.   ##
  217.   # Check for repeat
  218.   #   Returns false if Mouse is not enabled
  219.   #
  220.   def repeat?(id = LEFT_CLICK)
  221.     return false unless self.enabled
  222.     if @button_states[id] <= 0
  223.       return false
  224.     elsif @button_states[id] == 1
  225.       return true
  226.     elsif @button_states[id] > @@const::REPEAT_START_WAIT
  227.       return @button_states[id] % @@const::REPEAT_WAIT == 1
  228.     end
  229.   end
  230.  
  231.   ##
  232.   # Return true if the mouse script is enabled
  233.   #
  234.   def enabled
  235.     @enabled
  236.   end
  237.  
  238.   ##
  239.   # Get screen coordinates
  240.   #
  241.   def screen_pos
  242.     # Packs the 0,0 position as two longs (To use as buffer)
  243.     pos = [0, 0].pack('ll')
  244.     # Returns Unpacked Cursor Position Call
  245.     return CURSOR_POS.call(pos) == 0 ? nil : pos.unpack('ll')
  246.   end
  247.  
  248.   ##
  249.   # Converts screen X- and Y-coordinates to relative coordinates.
  250.   # Top-left corner = (0, 0)
  251.   #
  252.   def pos
  253.     return @mouse_sprite.x, @mouse_sprite.y
  254.   end
  255.  
  256.   ##
  257.   # Check if the mouse is inside the window
  258.   #
  259.   def inside?
  260.     x, y = pos
  261.     return x >= 0 && x < Graphics.width && y >= 0 && y < Graphics.height
  262.   end
  263.  
  264.   ##
  265.   # Screen coordinates converted to be relative to client window
  266.   # Note that the position of the client window is updated every X frames
  267.   # unless forced by given true as the third argument.
  268.   #
  269.   def screen_to_client(x, y, force = false)
  270.     # Return nil if X & Y empty
  271.     return nil unless x and y
  272.     if @original_pos.nil?
  273.       @screen_to_client_wait = 0
  274.     end
  275.     if @screen_to_client_wait <= 0 || force
  276.       # Locate upper-left corner
  277.       pack = [0, 0].pack('ll')
  278.       SCREEN_TO_CLIENT.call(window_handle, pack)
  279.       @original_pos = pack.unpack('ll')
  280.       # Wait 10 frames before the next look-up
  281.       @screen_to_client_wait = Configuration::FRAMES_PER_FIND_WINDOW
  282.     else
  283.       @screen_to_client_wait -= 1
  284.     end
  285.     # Translocate X- and Y-coordinates
  286.     x += @original_pos[0]
  287.     y += @original_pos[1]
  288.     return x, y
  289.   end
  290.  
  291.   ##
  292.   # Retrieve the window handle
  293.   #
  294.   def window_handle
  295.     if @window_handle.nil?
  296.       # Finds Game Name
  297.       game_name = Utility.read_ini('Title')
  298.       # Finds Window
  299.       @window_handle = FIND_WINDOW.call('RGSS Player', game_name)
  300.     end
  301.     return @window_handle
  302.   end
  303.  
  304.   # Opens up the anonymous class of the module
  305.   class << self
  306.     ###################
  307.     # Private methods #
  308.     ###################
  309.     private
  310.     ##
  311.     # Create the cursor
  312.     #
  313.     def create_cursor
  314.       @mouse_sprite = Sprite.new
  315.       @mouse_sprite.z = Configuration::CURSOR_Z_VALUE
  316.       @mouse_sprite.bitmap = Cache.picture(@@const::DEFAULT_ICON)
  317.     end
  318.   end
  319. end
  320.  
  321.  
  322. ##
  323. # Lets the mouse triggering state be updated together with the
  324. # default Input update method.
  325. #
  326. module Input
  327.   class << self
  328.     alias_method(:zer_mouse_input_update,:update)
  329.     def update
  330.       Mouse.update
  331.       zer_mouse_input_update
  332.     end
  333.   end
  334. end
  335.  
  336. # Initialize the mouse
  337. Mouse.start_up
  338.  
  339.  
  340. #==============================================================================
  341. # ** Mouse_Windows
  342. #------------------------------------------------------------------------------
  343. #  For scenes to include
  344. #==============================================================================
  345.  
  346. module Mouse_Windows
  347.   attr_sec_reader :mouse_windows, '[]'
  348.  
  349.   ##
  350.   # Update the mouse
  351.   #
  352.   def mouse_update
  353.     # Do nothing if the mouse is not enabled
  354.     return unless Mouse.enabled
  355.     mx, my = Mouse.pos
  356.     # Check if the mouse position has changed
  357.     if @mx == mx && @my == my
  358.       moved = false
  359.     else
  360.       moved = true
  361.       @mx = mx
  362.       @my = my
  363.     end
  364.     # Go through the mouse enabled windows
  365.     for window in mouse_windows
  366.       # Remove disposed windows
  367.       if window.disposed?
  368.         mouse_windows.delete(window)
  369.         next
  370.       end
  371.       # Ignore inactive and invisible windows
  372.       next unless window.active && window.visible
  373.       # Check if the mouse is over the window
  374.       if mouse_inside_window?(window)
  375.         # If the mouse have moved
  376.         if moved
  377.           # Calculate relative coordinates
  378.           rx = mx - window.x
  379.           ry = my - window.y
  380.           window.mouse_move(rx, ry)
  381.         end
  382.         # Let the window update with the mouse over it
  383.         window.mouse_update
  384.       end
  385.     end
  386.   end
  387.  
  388.   ##
  389.   # Check if the mouse is inside the given window
  390.   #
  391.   def mouse_inside_window?(window)
  392.     mx, my = @mx, @my
  393.     return mx >= window.x && mx < (window.x + window.width) &&
  394.            my >= window.y && my < (window.y + window.height)
  395.   end
  396.  
  397.   ##
  398.   # Events can be fired on scenes
  399.   #
  400.   def fire_event(event)
  401.     return unless $DEBUG
  402.     raise StandardError.new("fire_event(*args) was not overwritten in #{self.class}")
  403.   end
  404. end
  405.  
  406.  
  407. class Window_Base < Window
  408.   attr_accessor :owner
  409.  
  410.   ##
  411.   # Update mouse position
  412.   # Mouse enabled subclasses should overwrite this method
  413.   #
  414.   def mouse_move(mx, my)
  415.     return unless $TEST
  416.     raise StandardError.new("mouse_move(mx, my) was not overwritten in #{self.class}")
  417.   end
  418.  
  419.   ##
  420.   # Update mouse (Only called if mouse is enabled)
  421.   # Note: The mouse is over the window
  422.   #
  423.   def mouse_update
  424.     return unless $TEST
  425.     raise StandardError.new("mouse_update was not overwritten in #{self.class}")
  426.   end
  427.  
  428.   ##
  429.   # Check if the mouse is over the contents, i.e. not over the window border
  430.   #
  431.   def over_contents?(mx, my)
  432.     return mx >= 16 && mx < width - 16 &&
  433.            my >= 16 && my < height - 16
  434.   end
  435. end
  436.  
  437.  
  438. class Window_Selectable < Window_Base
  439.   attr_sec_accessor :scroll_wait, 0
  440.  
  441.   ##
  442.   # Calculates the index from the x and y coordinate
  443.   # Assumes (0,0) to be upper-left corner of the contents
  444.   #
  445.   def calculate_index(x, y)
  446.     rel_x, rel_y = x - self.padding, y - self.padding
  447.     for index in 0...item_max
  448.       rect = item_rect(index)
  449.       if rel_x.between?(rect.x, rect.x + rect.width) &&
  450.          rel_y.between?(rect.y, rect.y + rect.height)
  451.         return index
  452.       end
  453.     end
  454.     # No item found
  455.     return -1
  456.   end
  457.  
  458.   ##
  459.   #
  460.   #
  461.   def cause_scroll(index)
  462.     return false #TODO
  463.     # Get  row
  464.     row = index / @column_max
  465.     # If row is before top row or more to back than back row
  466.     return row < self.top_row || row > self.top_row + (self.page_row_max - 1)
  467.   end
  468.  
  469.   ##
  470.   # Update mouse position
  471.   # Mouse enabled subclasses should overwrite this method
  472.   #
  473.   def mouse_move(mx, my)
  474.     # Cursor position less than 0 implies that the cursor is hidden
  475.     return if @index < 0 || !self.active
  476.     # Store the coordinates
  477.     @mx, @my = mx, my
  478.     # Ignore the move if it is not over the contents
  479.     #if mx >= self.padding && mx < width self.padding
  480.     index = calculate_index(mx, my)
  481.     # If the index is valid
  482.     if index < item_max && index >= 0
  483.       # Scroll wait
  484.       if cause_scroll(index) && self.scroll_wait > 0
  485.         return
  486.       elsif cause_scroll(index)
  487.         self.scroll_wait = System::Mouse::SCROLL_WAIT
  488.         @scrolling = true
  489.       else
  490.         @scrolling = false
  491.       end
  492.       # Change the index
  493.       select(index)
  494.     end
  495.     #end
  496.   end
  497.  
  498.   ##
  499.   # Update mouse (Only called if mouse is enabled)
  500.   # Note: The mouse is over the window
  501.   #
  502.   def mouse_update
  503.     # Only update if active
  504.     return unless open? && active
  505.     # Scroll wait update
  506.     if self.scroll_wait > 0
  507.       self.scroll_wait -= 1
  508.       return
  509.     elsif @scrolling
  510.       # See if the scrolling should continue
  511.       index = calculate_index(@mx-16, @my-16)
  512.       if cause_scroll(index) && index < @item_max && index >= 0
  513.         self.scroll_wait = Mouse::Configuration::SCROLL_WAIT
  514.         @index = index
  515.       else
  516.         # Stop scrolling
  517.         @scrolling = false
  518.       end
  519.     end
  520.     @mx, @my = -1, -1 if @mx.nil?
  521.     # Check for click
  522.     if Mouse.trigger?(Mouse::LEFT_CLICK) && over_contents?(@mx, @my)
  523.       fire_event(Mouse::LEFT_CLICK)
  524.     end
  525.   end
  526.    
  527.   ##
  528.   # Right mouse button starts the cancel process
  529.   # This is added to the process_handling method so that it is processed
  530.   # regardless of the cursor location.
  531.   #
  532.   alias_method(:zer_mouse_process_handling, :process_handling)
  533.   def process_handling
  534.     return unless open? && active
  535.     zer_mouse_process_handling
  536.     if Mouse.trigger?(Mouse::RIGHT_CLICK)
  537.       process_cancel if cancel_enabled?
  538.     end
  539.   end
  540.    
  541.   ##
  542.   # Accept or cancel according to the trigger.
  543.   # Note, the trigger is consumed if it is a right or left click.
  544.   # TODO
  545.   #
  546.   def fire_event(trigger)
  547.     if trigger == Mouse::LEFT_CLICK
  548.       process_ok if ok_enabled?
  549.     else
  550.       # Fire the event in the owner
  551.       owner.fire_event(MouseEvent.new(self, trigger, index))
  552.     end
  553.   end  
  554. end
  555.  
  556.  
  557. class Scene_Base
  558.   include Mouse_Windows
  559.  
  560.   ##
  561.   # Add the given window to the list mouse enabled windows
  562.   # where the owner of the window is the current scene.
  563.   #
  564.   def add_mouse_window(window)
  565.     # Add as a mouse enabled window
  566.     mouse_windows << window
  567.     # Set owner
  568.     window.owner = self
  569.   end
  570.  
  571.   ##
  572.   # Overwrite the this method in subclasses to assign the mouse enabled windows.
  573.   # add_mouse_window(window) can be used in most cases.
  574.   #
  575.   def assign_mouse_windows
  576.     return unless $TEST
  577.     raise StandardError.new("assign_mouse_windows was not overwritten in #{self.class}")
  578.   end
  579.  
  580.   ##
  581.   # Overwrite this method in scenes where the mouse is enabled.
  582.   #
  583.   def mouse_enabled?
  584.     return false
  585.   end
  586.  
  587.   # Add assignment of mouse windows to the start method
  588.   alias_method(:zer_mouse_post_start, :post_start)
  589.   def post_start
  590.     assign_mouse_windows if mouse_enabled?
  591.     zer_mouse_post_start
  592.   end
  593.  
  594.   ##
  595.   # Update mouse input during update_all_windows
  596.   alias_method(:zer_mouse_update_all_windows, :update_all_windows)
  597.   def update_all_windows
  598.     mouse_update
  599.     zer_mouse_update_all_windows
  600.   end
  601. end
  602.  
  603.  
  604. #=====================
  605. # Scenes integrations
  606. #=====================
  607. class Scene_Title
  608.   ## Mouse is enabled
  609.   def mouse_enabled?; true; end
  610.    
  611.   ##
  612.   # Assign mouse enabled windows
  613.   #
  614.   def assign_mouse_windows
  615.     add_mouse_window(@command_window)
  616.   end
  617. end
  618.  
  619.  
  620. class Scene_Menu < Scene_MenuBase
  621.   ## Mouse is enabled
  622.   def mouse_enabled?; true; end
  623.    
  624.   ##
  625.   # Assign mouse enabled windows
  626.   #
  627.   def assign_mouse_windows
  628.     add_mouse_window(@command_window)
  629.     add_mouse_window(@status_window)
  630.   end
  631. end
  632.  
  633.  
  634. class Scene_Item < Scene_ItemBase
  635.   ## Mouse is enabled
  636.   def mouse_enabled?; true; end
  637.    
  638.   ##
  639.   # Assign mouse enabled windows
  640.   #
  641.   def assign_mouse_windows
  642.     add_mouse_window(@item_window)
  643.     add_mouse_window(@category_window)
  644.     add_mouse_window(@actor_window)
  645.   end
  646. end
  647.  
  648.  
  649. class Scene_Skill < Scene_ItemBase
  650.   ## Mouse is enabled
  651.   def mouse_enabled?; true; end
  652.    
  653.   ##
  654.   # Assign mouse enabled windows
  655.   #
  656.   def assign_mouse_windows
  657.     add_mouse_window(@command_window)
  658.   end
  659. end
  660.  
  661.  
  662. class Scene_Equip < Scene_MenuBase
  663.   ## Mouse is enabled
  664.   def mouse_enabled?; true; end
  665.    
  666.   ##
  667.   # Assign mouse enabled windows
  668.   #
  669.   def assign_mouse_windows
  670.     add_mouse_window(@command_window)
  671.   end
  672. end
  673.  
  674.  
  675. class Scene_Status < Scene_MenuBase
  676.   ## Mouse is enabled
  677.   def mouse_enabled?; true; end
  678.    
  679.   ##
  680.   # Assign mouse enabled windows
  681.   #
  682.   def assign_mouse_windows
  683.     add_mouse_window(@status_window)
  684.   end
  685. end
  686.  
  687.  
  688. class Scene_End < Scene_MenuBase
  689.   ## Mouse is enabled
  690.   def mouse_enabled?; true; end
  691.    
  692.   ##
  693.   # Assign mouse enabled windows
  694.   #
  695.   def assign_mouse_windows
  696.     add_mouse_window(@command_window)
  697.   end
  698. end
  699.  
  700.  
  701. class Scene_Shop < Scene_MenuBase
  702.   ## Mouse is enabled
  703.   def mouse_enabled?; true; end
  704.    
  705.   ##
  706.   # Assign mouse enabled windows
  707.   #
  708.   def assign_mouse_windows
  709.     add_mouse_window(@command_window)
  710.   end
  711. end
  712.  
  713.  
  714. class Scene_Name < Scene_MenuBase
  715.   ## Mouse is enabled
  716.   def mouse_enabled?; true; end
  717.    
  718.   ##
  719.   # Assign mouse enabled windows
  720.   #
  721.   def assign_mouse_windows
  722.     add_mouse_window(@input_window)
  723.   end
  724. end
  725.  
  726.  
  727. class Scene_Debug < Scene_MenuBase
  728.   ## Mouse is enabled
  729.   def mouse_enabled?; true; end
  730.    
  731.   ##
  732.   # Assign mouse enabled windows
  733.   #
  734.   def assign_mouse_windows
  735.     add_mouse_window(@left_window)
  736.     add_mouse_window(@right_window)
  737.   end
  738. end
  739.  
  740.  
  741. class Scene_Battle < Scene_Base
  742.   ## Mouse is enabled
  743.   def mouse_enabled?; true; end
  744.    
  745.   ##
  746.   # Assign mouse enabled windows
  747.   #
  748.   def assign_mouse_windows
  749.     add_mouse_window(@command_window)
  750.   end
  751. end
  752.  
  753.  
  754. class Scene_Gameover < Scene_Base
  755.  
  756.   alias_method(:zer_mouse_update, :update)
  757.   def update
  758.     if Mouse.trigger?(Mouse::LEFT_CLICK) ||
  759.        Mouse.trigger?(Mouse::RIGHT_CLICK)
  760.       super
  761.       goto_title
  762.     else  
  763.       zer_mouse_update
  764.     end
  765.   end
  766. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement