class String
def each_char
if block_given?
scan(/./m) do |x|
yield x
end
else
scan(/./m)
end
end
end
# Default Handle Default String
MOUSE_DEFAULT = "default"
# Mouse Array Cursor Handle Mouse Icon file
MOUSE_ICON = { MOUSE_DEFAULT => "default",
}
# Left-Clicking Control---
# 0 = single click action
# 1 = double-click action
MOUSE_LEFT_ACTION = 0
# Right-Clicking Control--
# 0 = Menu / 'B' pressed
# 1 = Stop Movement
MOUSE_RIGHT_ACTION = 0
# Movement range--
# Range in 'steps' the pathfinding
# for NF Pathfinding version 2 beta
# High values & large maps causes lag
PF_RANGE = 100
module Mouse
#-------------------------------------------------------------------------
# * Win32API calls
#-------------------------------------------------------------------------
@cursor_pos_get = Win32API.new('user32', 'GetCursorPos', 'p', 'i')
@cursor_pos_set = Win32API.new('user32', 'SetCursorPos', 'ii', 'i')
@cursor_show = Win32API.new('user32', 'ShowCursor', 'L', 'i')
@window_find = Win32API.new('user32', 'FindWindowA', %w(p p), 'l')
@window_c_rect = Win32API.new('user32', 'GetClientRect', %w(l p), 'i')
@window_scr2cli = Win32API.new('user32', 'ScreenToClient', %w(l p), 'i')
@readini = Win32API.new('kernel32', 'GetPrivateProfileStringA', %w(p p p p l p), 'l')
@key = Win32API.new("user32", "GetAsyncKeyState", 'i', 'i')
#-------------------------------------------------------------------------
# * Mouse Button Values
#-------------------------------------------------------------------------
Left_Click = 1
Right_Click = 2
Middle_Click = 4
MOUSE_BUTTONS = { Left_Click => 1,
Right_Click => 2,
Middle_Click => 4 }
MOUSE_REPEAT = MOUSE_BUTTONS.clone
MOUSE_REPEAT.keys.each { |k| MOUSE_REPEAT[k] = [false, false, 10]}
#-------------------------------------------------------------------------
# * Game Initialization Values
#-------------------------------------------------------------------------
@game_window = nil # Set game handle to nil
@cursor_show.call(0) # Turn off system mouse
#-------------------------------------------------------------------------
# * Click?
# button : button to check
#-------------------------------------------------------------------------
def Mouse.click?(button)
MOUSE_REPEAT[button][1] >= 1
end
#-------------------------------------------------------------------------
# * Double Clicked?
# button : button to check
#-------------------------------------------------------------------------
def Mouse.dbclick?(button)
MOUSE_REPEAT[button][1] == 2
end
#-------------------------------------------------------------------------
# * Get Mouse Position
#-------------------------------------------------------------------------
def Mouse.pos(catch_anywhere = true)
x, y = screen_to_client(screen_x, screen_y)
width, height = client_size
if catch_anywhere or (x >= 0 and y >= 0 and x < width and y < height)
return x, y
else
return 0, 0
end
end
#-------------------------------------------------------------------------
# * Set Mouse Position
# x : new x-coordinate (0 to 640)
# y : new y-coordinate (0 to 480)
#-------------------------------------------------------------------------
def Mouse.pos_set(x, y)
x = [[x, 0].max, 640].min
y = [[y, 0].max, 480].min
x, y = client_to_screen(x, y)
@cursor_pos_set.call(x, y)
end
#-------------------------------------------------------------------------
# * Get Mouse X-Coordinate Position
#-------------------------------------------------------------------------
def Mouse.pos_x
x, y = pos
return x
end
#-------------------------------------------------------------------------
# * Get Mouse Y-Coordinate Position
#-------------------------------------------------------------------------
def Mouse.pos_y
x, y = pos
return y
end
#-------------------------------------------------------------------------
# * Get Mouse Screen Position
#-------------------------------------------------------------------------
def Mouse.screen
pos = [0, 0].pack('ll')
@cursor_pos_get.call(pos)
return pos.unpack('ll')
end
#-------------------------------------------------------------------------
# * Get Mouse Screen X-Coordinate Position
#-------------------------------------------------------------------------
def Mouse.screen_x
pos = [0, 0].pack('ll')
@cursor_pos_get.call(pos)
return pos.unpack('ll')[0]
end
#-------------------------------------------------------------------------
# * Get Mouse Screen Y-Coordinate Position
#-------------------------------------------------------------------------
def Mouse.screen_y
pos = [0, 0].pack('ll')
@cursor_pos_get.call(pos)
return pos.unpack('ll')[1]
end
#-------------------------------------------------------------------------
# AUTOMATIC FUNCTIONS #
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
# * Get the Game Window's width and height
#-------------------------------------------------------------------------
def Mouse.client_size
rect = [0, 0, 0, 0].pack('l4')
@window_c_rect.call(Mouse.hwnd, rect)
right, bottom = rect.unpack('l4')[2..3]
return right, bottom
end
#-------------------------------------------------------------------------
# * Get the game window handle (specific to game)
#-------------------------------------------------------------------------
def Mouse.hwnd
if @game_window.nil?
game_name = "\0" * 256
@readini.call('Game','Title','',game_name,255,".\\Game.ini")
game_name.delete!("\0")
@game_window = @window_find.call('RGSS Player',game_name)
end
return @game_window
end
#-------------------------------------------------------------------------
# * Convert game window coordinates from screen coordinates
#-------------------------------------------------------------------------
def Mouse.screen_to_client(x, y)
return nil unless x and y
pos = [x, y].pack('ll')
if @window_scr2cli.call(hwnd, pos) != 0
return pos.unpack('ll')
else
return nil
end
end
#--------------------------------------------------------------------------
# * Frame Update (Mouse version)
#--------------------------------------------------------------------------
def Mouse.update
MOUSE_BUTTONS.keys.each do |key|
temp = MOUSE_REPEAT[key][0]
key_pres = @key.call(MOUSE_BUTTONS[key]) != 0
key_trig = temp == key_pres ? 0 : (key_pres ? (MOUSE_REPEAT[key][2].between?(1, 9) ? 2 : 1) : -1)
count = key_trig > 0 ? 0 : [MOUSE_REPEAT[key][2]+1, 20].min
MOUSE_REPEAT[key] = [key_pres, key_trig, count]
end
end
#-------------------------------------------------------------------------
# * Visible?
#-------------------------------------------------------------------------
def Mouse.visible?(visible=true)
if visible
@cursor_show.call(-1)
else
@cursor_show.call(0)
end
end
end
#==============================================================================
# ** Game_System
#------------------------------------------------------------------------------
# This class handles data surrounding the system. Backround music, etc.
# is managed here as well. Refer to "$game_system" for the instance of
# this class.
#==============================================================================
class Game_System
#--------------------------------------------------------------------------
# * Public Instance Variables
#--------------------------------------------------------------------------
attr_accessor :mouse_pf1 # Path Finding v1 detection bool
attr_accessor :mouse_pf2 # Path Finding v2 detection bool
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
alias mouse_init initialize
def initialize
mouse_init
@mouse_pf1 = nil
@mouse_pf2 = nil
end
end
#==============================================================================
# ** Sprite_Mouse
#------------------------------------------------------------------------------
# This sprite is used to display the mouse. It observes the Mouse module and
# automatically changes mouse graphic conditions.
#==============================================================================
class Sprite_Mouse
#--------------------------------------------------------------------------
# * Public Instance Variables
#--------------------------------------------------------------------------
attr_accessor :icon
attr_accessor :x
attr_accessor :y
attr_accessor :clicked
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
@x = 0
@y = 0
@clicked = nil
@sprite = Sprite.new
@sprite.z = 5000
@draw = false
@events = {}
reset
end
#--------------------------------------------------------------------------
# * Reset
#--------------------------------------------------------------------------
def reset
@icon = RPG::Cache.icon(MOUSE_ICON[MOUSE_DEFAULT])
@icon_name = MOUSE_ICON[MOUSE_DEFAULT]
@sprite.bitmap.dispose if @sprite.bitmap != nil and @sprite.bitmap.disposed?
@sprite.bitmap = @icon
@draw = false
# Updates the co-ordinates of the icon
@x, @y = Mouse.pos
@sprite.x = @x
@sprite.y = @y
@sprite.z = 5000
@sprite.visible = true
end
#--------------------------------------------------------------------------
# * Frame Update
#--------------------------------------------------------------------------
def update
Mouse.update
# Updates the co-ordinates of the icon
@x, @y = Mouse.pos
return if @x == nil or @y == nil
#Get Client Size
width,height = Mouse.client_size
@sprite.x = @x
@sprite.y = @y
#Check if needs to restart
(@draw = (@x < 0 or @y < 0 or @x > width or @y > height)) if !@draw
#Reset if need to reset
reset if @draw and @x > 1 and @y > 1 and @x < width and @y < height
#Show mouse if need to
if (@x < 0 or @y < 0 or @x > width or @y > height) and @visible
Mouse.visible?
elsif (@x > 0 or @y > 0 or @x < width or @y < height) and !@visible
Mouse.visible?(false)
end
#Return if Scene is not Scene_Map
return if !$scene.is_a?(Scene_Map)
#Check if the mouse is clicked
mouse_pressed if Mouse.click?(Mouse::Left_Click) and !$game_temp.message_window_showing
if MOUSE_RIGHT_ACTION == 1
if $game_system.mouse_pf1
$game_player.clear_path if Mouse.click?(Mouse::Right_Click)
end
if $game_system.mouse_pf2
$path_finding.setup_depth(PF_RANGE)
$path_finding.setup_player if Mouse.click?(Mouse::Right_Click)
end
end
#Check for mouse over
mouse_over_icon
end
#--------------------------------------------------------------------------
# * Mouse Pressed
#--------------------------------------------------------------------------
def mouse_pressed(button_type = nil)
@clicked = 1
if Mouse.dbclick?(Mouse::Left_Click)
@clicked = 2
end
# Routines called for Path Finding v1
if $game_system.mouse_pf1
# Turn to face regardless
$game_player.find_facing(tile_x, tile_y)
#If there is nothing than move
return if !$game_map.passable?(tile_x, tile_y, 0, $game_player)
$game_player.find_path(tile_x, tile_y)
end
# Routines called for Path Finding v2
if $game_system.mouse_pf2
$path_finding.setup_depth(PF_RANGE)
$path_finding.setup_player
$path_finding.add_paths_player(tile_x, tile_y, true)
$path_finding.start_player
end
end
#--------------------------------------------------------------------------
# * Mouseover Icon
#--------------------------------------------------------------------------
def mouse_over_icon
object = get_object
if object[0]
event = @events[object[1].id]
return if event == nil
list = event.list
return if list == nil
#Check what the list is
for key in MOUSE_ICON.keys
next if !list.include?(key)
next if @icon_name == MOUSE_ICON[key]
@icon = RPG::Cache.icon(MOUSE_ICON[key])
@icon_name = MOUSE_ICON[key]
@sprite.bitmap.dispose if @sprite.bitmap != nil or @sprite.bitmap.disposed?
@sprite.bitmap = @icon
end
elsif @icon_name != MOUSE_ICON[MOUSE_DEFAULT]
@icon = RPG::Cache.icon(MOUSE_ICON[MOUSE_DEFAULT])
@icon_name = MOUSE_ICON[MOUSE_DEFAULT]
@sprite.bitmap.dispose if @sprite.bitmap != nil or @sprite.bitmap.disposed?
@sprite.bitmap = @icon
end
end
#--------------------------------------------------------------------------
# * Get the current x-coordinate of the tile
#--------------------------------------------------------------------------
def tile_x
return ((($game_map.display_x.to_f/4.0).floor + @x.to_f)/32.0).floor
end
#--------------------------------------------------------------------------
# * Get the current y-coordinate of the tile
#--------------------------------------------------------------------------
def tile_y
return ((($game_map.display_y.to_f/4.0).floor + @y.to_f)/32.0).floor
end
#--------------------------------------------------------------------------
# * Get Object
#--------------------------------------------------------------------------
def get_object
for event in $game_map.events.values
return [true,event] if event.x == tile_x and event.y == tile_y
end
return [false,nil]
end
#--------------------------------------------------------------------------
# * MOUSE Refresh(Event, List, Characterset Name
#--------------------------------------------------------------------------
def refresh(event, list)
@events.delete(event.id)
if event.list && event.list[0].code == 108
icon = event.list[0].parameters
end
@events[event.id] = Mouse_Event.new(event.id)
@events[event.id].list = icon
end
end
$mouse = Sprite_Mouse.new
#==============================================================================
# ** Mouse_Event
#------------------------------------------------------------------------------
# This class deals with events. It adds new functionality between events and
# the mouse system. It's used within the Game_Map class.
#==============================================================================
class Mouse_Event
#--------------------------------------------------------------------------
# * Public Instance Variables
#--------------------------------------------------------------------------
attr_accessor :id
attr_accessor :list
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize(id)
@id = id
@list = nil
end
end
#==============================================================================
# ** Game_Event
#------------------------------------------------------------------------------
# This class deals with events. It handles functions including event page
# switching via condition determinants, and running parallel process events.
# It's used within the Game_Map class.
#==============================================================================
class Game_Event < Game_Character
#--------------------------------------------------------------------------
# * Event ID
#--------------------------------------------------------------------------
def id
return @id
end
#--------------------------------------------------------------------------
# * Refresh
#--------------------------------------------------------------------------
alias nf_game_event_refresh refresh
def refresh
nf_game_event_refresh
$mouse.refresh(self, @list)
end
end
#==============================================================================
# ** Input
#------------------------------------------------------------------------------
# Adds new Mouse Input functions into a new class
#==============================================================================
class << Input
#--------------------------------------------------------------------------
# * Update old input calls
#--------------------------------------------------------------------------
alias old_update update unless $@
def Input.update
old_update
$mouse.update
end
#--------------------------------------------------------------------------
# * Update old input triggers
# num : A, B, C
#--------------------------------------------------------------------------
alias old_trigger? trigger? unless $@
def Input.trigger?(num)
return old_trigger?(num) if Mouse.pos(false) == nil
case num
when Input::A
return (old_trigger?(num) or Mouse.click?(Mouse::Middle_Click))
when Input::B
if MOUSE_RIGHT_ACTION == 0
return (old_trigger?(num) or Mouse.click?(Mouse::Right_Click))
else
if !$scene.is_a?(Scene_Map)
return (old_trigger?(num) or Mouse.click?(Mouse::Right_Click))
else
return (old_trigger?(num))
end
end
when Input::C
if MOUSE_LEFT_ACTION == 0
return (old_trigger?(num) or Mouse.click?(Mouse::Left_Click))
else
if !$scene.is_a?(Scene_Map)
return (old_trigger?(num) or Mouse.click?(Mouse::Left_Click))
else
if $mouse.clicked == 2
return (old_trigger?(num) or Mouse.click?(Mouse::Left_Click))
else
return (old_trigger?(num))
end
end
end
else
return old_trigger?(num)
end
end
#--------------------------------------------------------------------------
# * Check to see if an old input call was repeated
# num : B
#--------------------------------------------------------------------------
alias old_repeat? repeat? unless $@
def repeat?(num)
return old_repeat?(num) if Mouse.pos(false) == nil
if num == Input::B
return (old_repeat?(num) or Mouse.repeat?(Mouse::Right_Click))
else
return old_repeat?(num)
end
end
end
#==============================================================================
# ** Game_Character
#------------------------------------------------------------------------------
# This class deals with characters. It's used as a superclass for the
# Game_Player and Game_Event classes.
#==============================================================================
class Game_Character
#--------------------------------------------------------------------------
# * Public Instance Variables
#--------------------------------------------------------------------------
attr_accessor :facingpath # direction faced if 1 tile away
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
alias mouse_init initialize
def initialize
mouse_init
# Path Finding v1 Detection
if defined?(find_path)
$game_system.mouse_pf1 = true
else
if defined?(pf_passable?)
$game_system.mouse_pf2 = true
end
end
end
#--------------------------------------------------------------------------
# * Frame Update
#--------------------------------------------------------------------------
alias mouse_update update
def update
turn_facing if @facingpath != 0
mouse_update
end
#--------------------------------------------------------------------------
# * Find Facing Direction
# Addition to Path Finding v 1
#--------------------------------------------------------------------------
def find_facing(x,y)
sx, sy = @x, @y
@facingpath = find_direction(sx,sy,x,y)
end
#--------------------------------------------------------------------------
# * Find Direction
# Addition to Path Finding v 1
#--------------------------------------------------------------------------
def find_direction(sx,sy,ex,ey)
ffx = sx - ex
ffy = sy - ey
facing = 0
case ffx
when 1 ; facing = 4
when -1; facing = 6
when 0
case ffy
when 1 ; facing = 8
when -1; facing = 2
end
end
return facing
end
#--------------------------------------------------------------------------
# * Turn Towards Object
# Addition to Path Finding v 1
#--------------------------------------------------------------------------
def turn_to(b)
# Get difference in player coordinates
sx = @x - b.x
sy = @y - b.y
# If coordinates are equal
if sx == 0 and sy == 0
return
end
# If horizontal distance is longer
if sx.abs > sy.abs
# Turn to the right or left towards player
sx > 0 ? turn_left : turn_right
# If vertical distance is longer
else
# Turn up or down towards player
sy > 0 ? turn_up : turn_down
end
end
#--------------------------------------------------------------------------
# * Turn Facing Click
# Addition to Path Finding v 1
#--------------------------------------------------------------------------
def turn_facing
case @facingpath
when 2; turn_down
when 4; turn_left
when 6; turn_right
when 8; turn_up
end
# Turn off
@facingpath = 0
end
#--------------------------------------------------------------------------
# * Run Path
# EDIT to Path Finding v 1
#--------------------------------------------------------------------------
def run_path
return if moving?
step = @map[@x,@y]
if step == 1
@map = nil
@runpath = false
turn_to(@object) if @object != nil and in_range?(self, @object, 1)
return
end
dir = rand(2)
case dir
when 0
move_right if @map[@x+1, @y] == step - 1 and step != 0
move_down if @map[@x, @y+1] == step - 1 and step != 0
move_left if @map[@x-1, @y] == step - 1 and step != 0
move_up if @map[@x, @y-1] == step - 1 and step != 0
when 1
move_up if @map[@x, @y-1] == step - 1 and step != 0
move_left if @map[@x-1, @y] == step - 1 and step != 0
move_down if @map[@x, @y+1] == step - 1 and step != 0
move_right if @map[@x+1, @y] == step - 1 and step != 0
end
end
end
class Game_Map
#--------------------------------------------------------------------------
# * Get clicked
#--------------------------------------------------------------------------
def get_clicked
for a in 1..$game_map.events.size
x = @events[a].x == (Mouse.pos_x / 36) + 1
y = @events[a].y == (Mouse.pos_y / 36) + 1
return @events[a] if (x and y)
end
return false
end
#--------------------------------------------------------------------------
# * Make_Menu
#--------------------------------------------------------------------------
def make_menu
return if ! get_clicked
options = get_clicked.make_pages
names = get_clicked.make_names(options)
$scene = Event_Menu.new(get_clicked, options, names)
end
#--------------------------------------------------------------------------
# * Frame Update
#--------------------------------------------------------------------------
alias dropdown_update update
def update
dropdown_update
if Input.trigger?(Input::C)
make_menu
end
end
end
class Game_Event < Game_Character
#--------------------------------------------------------------------------
# * Make pages
#--------------------------------------------------------------------------
def make_pages
options = []
for i in 1..@event.pages.size
break if @event.pages[i - 1] == nil
if @event.pages[i - 1].list[0].parameters[0] != nil and
@event.pages[i - 1].list[0].parameters[0].each_char.include?("%")
options.push(i - 1)
end
end
return options
end
#--------------------------------------------------------------------------
# * Make names
#--------------------------------------------------------------------------
def make_names(options)
names = []
for i in 1..options.size
names.push(@event.pages[options[i - 1]].list[0].parameters[0].gsub("%", " "))
end
return names
end
#--------------------------------------------------------------------------
# * Start Event
#--------------------------------------------------------------------------
def dropdown_start(n)
@page_id = n
start
$game_system.map_interpreter.setup(@event.pages[@page_id].list, @id)
end
#--------------------------------------------------------------------------
# * Frame Update
#--------------------------------------------------------------------------
alias dropdown_update update
def update
dropdown_update
if ! @page_id == nil
@list = nil if @list != @event.pages[@page_id].list
end
end
end
class Event_Menu
#--------------------------------------------------------------------------
# * Object Initialization
# menu_index : command cursor's initial position
#--------------------------------------------------------------------------
def initialize(event, options, names)
@pages = options
@names = names
@event = event
@menu_index = 0
@x = Mouse.pos_x
@y = Mouse.pos_y
event.clear_starting
n = Bitmap.new(640, 32)
@n = n.text_size(@names[0])
n.dispose
n = nil
end
#--------------------------------------------------------------------------
# * Main Processing
#--------------------------------------------------------------------------
def main
@menu = Window_Command.new(@n.width, @names)
@menu.index = @menu_index
@menu.x = @x
@menu.y = @y
@spriteset = Spriteset_Map.new
# Execute transition
Graphics.transition
# Main loop
loop do
# Update game screen
Graphics.update
# Update input information
Input.update
# Frame update
update
# Abort loop if screen is changed
if $scene != self
break
end
end
# Prepare for transition
Graphics.freeze
# Dispose of windows
@menu.dispose
@spriteset.dispose
end
#--------------------------------------------------------------------------
# * Frame Update
#--------------------------------------------------------------------------
def update
# Update windows
@menu.update
@spriteset.update
update_menu
end
#--------------------------------------------------------------------------
# * Frame Update (when command window is active)
#--------------------------------------------------------------------------
def update_menu
# If B button was pressed
if Input.trigger?(Input::B)
# Play cancel SE
$game_system.se_play($data_system.cancel_se)
# Switch to map screen
$scene = Scene_Map.new
return
end
# If C button was pressed
if Input.trigger?(Input::C)
# Branch by command window cursor position
$scene = Scene_Map.new
@event.dropdown_start(@pages[@menu.index])
# set event page to menu index
end # if input trigger
return
end # update_menu
end