#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
# Mouse Controller by Blizzard
# Version: 2.0b
# Type: Custom Input System
# Date: 9.10.2009
# Date v2.0b: 22.7.2010
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#   
#  This work is protected by the following license:
# #----------------------------------------------------------------------------
# #  
# #  Creative Commons - Attribution-NonCommercial-ShareAlike 3.0 Unported
# #  ( http://creativecommons.org/licenses/by-nc-sa/3.0/ )
# #  
# #  You are free:
# #  
# #  to Share - to copy, distribute and transmit the work
# #  to Remix - to adapt the work
# #  
# #  Under the following conditions:
# #  
# #  Attribution. You must attribute the work in the manner specified by the
# #  author or licensor (but not in any way that suggests that they endorse you
# #  or your use of the work).
# #  
# #  Noncommercial. You may not use this work for commercial purposes.
# #  
# #  Share alike. If you alter, transform, or build upon this work, you may
# #  distribute the resulting work only under the same or similar license to
# #  this one.
# #  
# #  - For any reuse or distribution, you must make clear to others the license
# #    terms of this work. The best way to do this is with a link to this web
# #    page.
# #  
# #  - Any of the above conditions can be waived if you get permission from the
# #    copyright holder.
# #  
# #  - Nothing in this license impairs or restricts the author's moral rights.
# #  
# #----------------------------------------------------------------------------
# 
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
# 
# Compatibility:
# 
#   90% compatible with SDK v1.x. 80% compatible with SDK v2.x. May cause
#   incompatibility issues with other custom input Systems. Works with "Custom
#   Game Controls" from Tons of Add-ons and Blizz-ABS's custom controls.
#   This script is not meant to be used as a standalone but rather in
#   combination with special menus that are properly adapted to support a mouse
#   controller system.
#   
#   
# Features:
# 
#   - fully automated mouse control in game
#   - can be enhanced with "Custom Game Controls" from Tons of Add-ons
#   - can be enhanced with "Blizz-ABS Controls"
#   - can be enhanced with "RMX-OS"
# 
# new in 2.0b:
# 
#   - added option to hide Windows' cursor
#   - added possibility to hide and show the ingame cursor during the game
#   - added possibility to change the cursor icon
#   - added several new options
#   - optimized
# 
# 
# Instructions:
# 
# - Explanation:
# 
#   This script can work as a stand-alone for window option selections. To be
#   able to use the mouse buttons, you need a custom Input module. The
#   supported systems are "Custom Game Controls" from Tons of Add-ons,
#   Blizz-ABS Custom Controls and RMX-OS Custom Controls. This script will
#   automatically detect and apply the custom input modules' configuration
#   which is optional.
#   
# - Configuration:
# 
#   MOUSE_ICON          - the default filename of the icon located in the
#                         Graphics/Pictures folder
#   APPLY_BORDERS       - defines whether the ingame cursor can go beyond the
#                         game window borders
#   WINDOW_WIDTH        - defines the window width, required only when using
#                         APPLY_BORDER
#   WINDOW_HEIGHT       - defines the window height, required only when using
#                         APPLY_BORDER
#   HIDE_WINDOWS_CURSOR - hides the Windows Cursor on the window by default
#   AUTO_CONFIGURE      - when using "Custom Game Controls" from Tons of
#                         Add-ons, Blizz-ABS or RMX-OS, this option will
#                         automatically add the left mouse button as
#                         confirmation button
#   
# - Script Calls:
#   
#   You can use a few script calls to manipulate the cursor. Keep in mind that
#   these changes are not being saved with the save file.
#   
#   To hide the ingame Mouse Cursor, use following call.
#   
#     $mouse.hide
#   
#   To show the ingame Mouse Cursor, use following call.
#   
#     $mouse.show
#   
#   To change the cursor image, use following call. Make sure your image is
#   
#     $mouse.set_cursor('IMAGE_NAME')
#   
#   
# Additional Information:
#   
#   Even though there is an API call to determine the size of the window, API
#   calls are CPU expensive so the values for the window size need to be
#   configured manually in this script.
#   
# 
# If you find any bugs, please report them here:
# http://forum.chaos-project.com
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=

$mouse_controller = 2.0

#===============================================================================
# Mouse
#===============================================================================

class Mouse
  
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# START Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  MOUSE_ICON = 'cursor'
  AUTO_CONFIGURE = true
  APPLY_BORDERS = true
  WINDOW_WIDTH = 640
  WINDOW_HEIGHT = 480
  HIDE_WINDOWS_CURSOR = false
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# END Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  
  if HIDE_WINDOWS_CURSOR
    Win32API.new('user32', 'ShowCursor', 'i', 'i').call(0)
  end
  
  SCREEN_TO_CLIENT = Win32API.new('user32', 'ScreenToClient', %w(l p), 'i')
  READ_INI = Win32API.new('kernel32', 'GetPrivateProfileStringA', %w(p p p p l p), 'l')
  FIND_WINDOW = Win32API.new('user32', 'FindWindowA', %w(p p), 'l')
  CURSOR_POSITION = Win32API.new('user32', 'GetCursorPos', 'p', 'i')
  
  def initialize
    @cursor = Sprite.new
    @cursor.z = 1000000
    self.set_cursor(MOUSE_ICON)
    update
  end
  
  def update
    @cursor.x, @cursor.y = self.position
  end
  
  def x
    return @cursor.x
  end
  
  def y
    return @cursor.y
  end
  
  def position
    x, y = self.get_client_position
    if APPLY_BORDERS
      if x < 0
        x = 0
      elsif x >= WINDOW_WIDTH
        x = WINDOW_WIDTH - 1
      end
      if y < 0
        y = 0
      elsif y >= WINDOW_HEIGHT
        y = WINDOW_HEIGHT - 1
      end
    end
    return x, y
  end
  
  def get_client_position
    pos = [0, 0].pack('ll')
    CURSOR_POSITION.call(pos)
    SCREEN_TO_CLIENT.call(WINDOW, pos)
    return pos.unpack('ll')
  end
  
  def set_cursor(image)
    @cursor.bitmap = RPG::Cache.picture(image)
  end
  
  def show
    @cursor.visible = true
  end
  
  def hide
    @cursor.visible = false
  end
  
  def self.find_window
    game_name = "\0" * 256
    READ_INI.call('Game', 'Title', '', game_name, 255, '.\\Game.ini')
    game_name.delete!("\0")
    return FIND_WINDOW.call('RGSS Player', game_name)
  end
  
  WINDOW = self.find_window
  
end

$mouse = Mouse.new

#==============================================================================
# module Input
#==============================================================================

module Input
  
  class << Input
    alias update_mousecontroller_later update
  end
  
  def self.update
    $mouse.update
    update_mousecontroller_later
  end
  
  if Mouse::AUTO_CONFIGURE
    if $BlizzABS
      C.push(Input::Key['Mouse Left']) if !C.include?(Input::Key['Mouse Left'])
      if !Attack.include?(Input::Key['Mouse Right'])
        Attack.push(Input::Key['Mouse Right'])
      end
    elsif $tons_version != nil && $tons_version >= 6.4 &&
        TONS_OF_ADDONS::CUSTOM_CONTROLS || defined?(RMXOS)
      C.push(Input::Key['Mouse Left']) if !C.include?(Input::Key['Mouse Left'])
    end
  end
  
end

#===============================================================================
# Rect
#===============================================================================

class Rect
  
  def covers?(x, y)
    return !(x < self.x || x >= self.x + self.width ||
        y < self.y || y >= self.y + self.height)
  end
  
end

#===============================================================================
# Sprite
#===============================================================================

class Sprite
  
  def mouse_in_area?
    return false if self.bitmap == nil
    return ($mouse.x >= self.x && $mouse.x < self.x + self.src_rect.width &&
        $mouse.y >= self.y && $mouse.y < self.y + self.src_rect.height)
  end
  
end

#===============================================================================
# Window_Base
#===============================================================================

class Window_Base
  
  def mouse_in_area?
    return ($mouse.x >= self.x && $mouse.x < self.x + self.width &&
        $mouse.y >= self.y && $mouse.y < self.y + self.height)
  end
  
  def mouse_in_inner_area?
    return ($mouse.x >= self.x + 16 && $mouse.x < self.x + self.width - 16 &&
        $mouse.y >= self.y + 16 && $mouse.y < self.y + self.height - 16)
  end
  
end

#===============================================================================
# Window_Selectable
#===============================================================================

class Window_Selectable
  
  alias contents_is_mousecontroller_later contents=
  def contents=(bitmap)
    contents_is_mousecontroller_later(bitmap)
    begin
      update_selections
      update_mouse if self.active
    rescue
    end
  end
  
  alias index_is_mousecontroller_later index=
  def index=(value)
    index_is_mousecontroller_later(value)
    update_selections
  end
  
  alias active_is_mousecontroller_later active=
  def active=(value)
    active_is_mousecontroller_later(value)
    update_cursor_rect
  end
  
  def update_selections
    @selections = []
    index, ox, oy = self.index, self.ox, self.oy
    (0...@item_max).each {|i|
        @index = i
        update_cursor_rect
        rect = self.cursor_rect.clone
        rect.x += self.ox
        rect.y += self.oy
        @selections.push(rect)}
    @index, self.ox, self.oy = index, ox, oy
    self.cursor_rect.empty
  end
  
  alias update_mousecontroller_later update
  def update
    update_mouse if self.active
    update_mousecontroller_later
  end
  
  def update_mouse
    if self.mouse_in_inner_area?
      update_mouse_selection
      return
    end
    self.index = -1
    if self.contents != nil && @selections.size > 0 && self.mouse_in_area?
      update_mouse_scrolling
    end
  end
  
  def update_mouse_selection
    update_selections if @selections.size != @item_max
    @selections.each_index {|i|
        if @selections[i].covers?($mouse.x - self.x - 16 + self.ox,
            $mouse.y - self.y - 16 + self.oy)
          self.index = i if self.index != i
          return
        end}
    self.index = -1
  end
  
  def update_mouse_scrolling
    if Input.repeat?(Input::C)
      if $mouse.x < self.x + 16
        if self.ox > 0
          $game_system.se_play($data_system.cursor_se)
          self.ox -= @selections[0].width
          self.ox = 0 if self.ox < 0
        end
      elsif $mouse.x >= self.x + self.width - 16
        max_ox = self.contents.width - self.width + 32
        if self.ox <= max_ox
          $game_system.se_play($data_system.cursor_se)
          self.ox += @selections[0].width
          self.ox = max_ox if self.ox >= max_ox
        end
      elsif $mouse.y < self.y + 16
        if self.oy > 0
          $game_system.se_play($data_system.cursor_se)
          self.oy -= @selections[0].height
          self.oy = 0 if self.oy < 0
        end
      elsif $mouse.y >= self.y + self.height - 16
        max_oy = self.contents.height - self.height + 32
        if self.oy <= max_oy
          $game_system.se_play($data_system.cursor_se)
          self.oy += @selections[0].height
          self.oy = max_oy if self.oy >= max_oy
        end
      end
    end
  end
  
end
