Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

[VX] Mouse module

Mouse Script Version: 1.0
By: Near

Introduction

This script adds a mouse input functionality to your game,
and you can add a cursor image also. Call with: Mouse.mouse_press?

Features

  • Mouse Input
  • Mouse graphic
  • Can make virtually anything clickable.

Screenshots

No screens, sorry.

Demo

Working on a demo.

Script

Code:
 

class Game_Character

  attr_accessor :path_finding  # Finding a path?

  attr_accessor :path_x        # X coordinate to find.

  attr_accessor :path_y        # Y coordinate to find.

  alias npath_init initialize

  alias npath_update update

  def initialize

    @path_finding = false

    @path_x = 0

    @path_y = 0

    npath_init

  end

  def update

    find_path if @path_finding

    npath_update

  end

  # Start finding a path

  def start_path(x,y)

    @path_x = x

    @path_y = y

    @path_finding = true

    return

  end

  # Execute Pathfinding

  def find_path

    if (@x == @path_x && @y == @path_y) || !passable?(@path_x,@path_y)

      clear_path

      return

    end

    if @path_x < @x && @path_y > @y

      path_lower_left

    elsif @path_x > @x && @path_y > @y

      path_lower_right

    elsif @path_x < @x && @path_y < @y

      path_upper_left

    elsif @path_x > @x && @path_y < @y

      path_upper_right

    elsif @path_y > @y

      path_down

    elsif @path_y < @y

      path_up

    elsif @path_x > @x

      path_right

    elsif @path_x < @x

      path_left

    else

      return

    end

  end

  # Clear Path

  def clear_path

    @path_finding = false

    @path_x = 0

    @path_y = 0

    return

  end

  #-------------------  Paths  ------------------#

  def path_lower_left

    if passable?(@x-1,@y+1)

      move_lower_left

    end

    return

  end

  def path_lower_right

     if passable?(@x+1,@y+1)

      move_lower_right

    end

    return

  end

  def path_upper_left

    if passable?(@x-1,@y-1)

      move_upper_left

    end

    return

  end

  def path_upper_right

    if passable?(@x+1,@y-1)

      move_upper_right

    end

    return

  end

  def path_down

    if passable?(@x,@y+1)

      move_down

    end

    return

  end

  def path_up

    if passable?(@x,@y-1)

      move_up

    end

    return

  end

  def path_right

    if passable?(@x+1,@y)

      move_right

    end

    return

  end

  def path_left

    if passable?(@x-1,@y)

      move_left

    end

    return

  end

end

  

 

Code:
 

module Near

  module Library

    module WIN32

      GSM = Win32API.new('user32', 'GetSystemMetrics', 'i', 'i')

      GetCursorPos = Win32API.new('user32', 'GetCursorPos', 'p', 'i')

      ScreenToClient = Win32API.new('user32', 'ScreenToClient', %w(l p), 'i')

      GetClientRect = Win32API.new('user32', 'GetClientRect', %w(l p), 'i')

      GetPrivateString = Win32API.new('kernel32', 'GetPrivateProfileStringA', %w(p p p p l p), 'l')

      FindWindow = Win32API.new('user32', 'FindWindowA', %w(p p), 'l')

      ShowCursor = Win32API.new('user32', 'ShowCursor', 'l', 'l')

    end

  end

  module KeyCode

    Mouse_Left = 1

    Mouse_Right = 2

    Mouse_Middle = 4

  end

end

 

Code:
 

module Mouse

  #--------------------------------------------------------------------------

  # * Config - Customize Here!

  #--------------------------------------------------------------------------

  Icon = "Arrow6" # File must be in .\Graphics\System\<File>

  #--------------------------------------------------------------------------

  # * End of Config - Don't Customize Here!

  #--------------------------------------------------------------------------

  n = (true ? 0 : 1)

  Near::Library::WIN32::ShowCursor.call(n)

  module_function

  def self.tile

    return nil if pos == nil

    return [0,0] unless $scene.is_a?(Scene_Map)

    ox = $game_map.display_x / 4

    oy = $game_map.display_y / 4

    x = (pos[0] + ox) / 32

    y = (pos[1] + oy) / 32

    return [x, y]

  end

  def self.position

    return pos == nil ? [0, 0] : pos

  end

  def self.global_pos

    pos = [0, 0].pack('ll')

    if Near::Library::WIN32::GetCursorPos.call(pos) != 0

      return pos.unpack('ll')

    else

      return nil

    end

  end

  def self.pos

    x, y = self.screen_to_client(*Mouse.global_pos)

    width, height = self.client_size

    begin

      if (x >= 0 and y >= 0 and x < width and y < height)

        return x, y

      else

        return [0,0]

      end

    rescue

      return [0,0]

    end

  end

  def self.screen_to_client(x, y)

    return nil unless x and y

    pos = [x, y].pack('ll')

    if Near::Library::WIN32::ScreenToClient.call(self.hwnd, pos) != 0

      return pos.unpack('ll')

    else

      return nil

    end

  end

  def self.hwnd

    game_name = "\0" * 256

    Near::Library::WIN32::GetPrivateString.call('Game','Title','',game_name,255,".\\Game.ini")

    game_name.delete!("\0")

    return Near::Library::WIN32::FindWindow.call('RGSS Player',game_name)

  end

  def self.client_size

    rect = [0, 0, 0, 0].pack('l4')

    Near::Library::WIN32::GetClientRect.call(self.hwnd, rect)

    right, bottom = rect.unpack('l4')[2..3]

    return right, bottom

  end

  def self.update

    if mouse_press?

      self.check_event(tile[0],tile[1])

    end

  end

  def self.mouse_press?

      if Input.press?(Near::KeyCode::Mouse_Left)

        return true

      end

    return false

  end

  def self.right_press?

    if Input.press?(Near::KeyCode::Mouse_Right)

      return true

    end

    return false

  end

end

 

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 == nil

    case num

    when Input::B

      return (old_trigger?(num) or Mouse.right_press?)

    when Input::C

      return (old_trigger?(num) or Mouse.mouse_press?)

    else

      return old_trigger?(num)

    end

  end

end

 

 

class Sprite_Mouse < Sprite

  #--------------------------------------------------------------------------

  # ● Initialize the mouse

  #--------------------------------------------------------------------------

  def initialize

    super

    self.bitmap = Cache.system(Mouse::Icon)

    @true_x = 0

    @true_y = 0

    self.z = 10001

    self.ox, self.oy = [16, 0]

    self.visible = false

    self.src_rect.set(0, 0, 32, 32)

    update

  end

  #--------------------------------------------------------------------------

  # ● Dispose of the mouse

  #--------------------------------------------------------------------------

  def dispose

    if self.bitmap != nil

      self.bitmap.dispose

    end

    super

  end

  #--------------------------------------------------------------------------

  # ● Update the mouse

  #--------------------------------------------------------------------------

  def update

    super

    #$mouse_icon = "Arrow6" if $mouse_icon == nil

    self.bitmap = Cache.system(Mouse::Icon)    

    self.visible = true if self.visible == false and $scene != nil

    self.x = Mouse.pos[0]

    self.y = Mouse.pos[1]

    @true_x = Mouse.tile[0]

    @true_y = Mouse.tile[1]

    if Mouse.mouse_press?

      self.src_rect.set(0, 0, 32, 32)     

    end

    return

  end

  

end

 

$mouse = Sprite_Mouse.new

 

 

Code:
 

class Window_Selectable < Window_Base

  alias mouse_initialize initialize

  def initialize(x, y, width, height, spacing = 32)

    mouse_initialize(x, y, width, height, spacing)

    @mouse = false

    @scroll_wait = 0

  end

  alias mouse_update update

  def update

    mouse_update

    mouse_operation if self.active || @mouse

  end

  def mouse_operation

    mx = Mouse.pos[0] - (self.x - self.ox + 16)

    my = Mouse.pos[1] - (self.y - self.oy + 16)

    width = self.width / @column_max - 32

    height = 32

 

    for index in 0...@item_max

      x = index % @column_max * (width + 32)

      y = index / @column_max * 32

      if mx > x and

         mx < x + width and

         my > y and

         my < y + height

        mouse_cursor(index)

        break

      end

    end

  end

  def mouse_cursor(index)

    return if @index == index

    @scroll_wait -= 1 if @scroll_wait > 0

    row1 = @index / @column_max

    row2 = index / @column_max

    bottom = self.top_row + (self.page_row_max - 1)

    if row1 == self.top_row and row2 < self.top_row

      return if @scroll_wait > 0

      @index = [@index - @column_max, 0].max

      @scroll_wait = 4

    elsif row1 == bottom and row2 > bottom

      return if @scroll_wait > 0

      @index = [@index + @column_max, @item_max - 1].min

      @scroll_wait = 4

    else

      @index = index

    end

    Sound.play_cursor

  end

end

 

class Window_MenuStatus < Window_Selectable

  def mouse_operation   

    return if @index < 0

    mx = Mouse.pos[0] - (self.x - self.ox + 16)

    my = Mouse.pos[1] - (self.y - self.oy + 16)

    x = 0

    width = self.width - 32

    height = 96

    for index in 0...@item_max

      y = index * 116

      if mx > x and

          mx < x + width and

          my > y and

          my < y + height

        mouse_cursor(index)

        break

      end

    end

  end

end

 

class Window_Target < Window_Selectable

  def mouse_operation

    return if @index <= -1

    mx = Mouse.pos[0] - (self.x - self.ox + 16)

    my = Mouse.pos[1] - (self.y - self.oy + 16)

    x = 0

    width = self.width - 32

    height = 96

    for index in 0...@item_max

      y = index * 116

      if mx > x and

          mx < x + width and

          my > y and

          my < y + height

        mouse_cursor(index)

        break

      end

    end

  end

end

 

class Window_NameInput < Window_Base

  alias mouse_update update

  def update

    mouse_update

    mouse_operation if self.active

  end

  def mouse_operation

    last_index = @index

    mx = Mouse.pos[0] - (self.x - self.ox + 16)

    my = Mouse.pos[1] - (self.y - self.oy + 16)

    width = 28

    height = 32

    for index in 0...180

      x = 4 + index / 5 / 9 * 152 + index % 5 * 28

      y = index / 5 % 9 * 32

      if mx > x and

          mx < x + width and

          my > y and

          my < y + height

        @index = index

        break

      end

    end

    x = 544

    y = 9 * 32

    width = 64

    if mx > x and

        mx < x + width and

        my > y and

        my < y + height

      @index = 180

    end

    Sound.play_cursor unless @index == index

  end

end

 

class Window_Message < Window_Selectable

  def mouse_operation

    mx = Mouse.pos[0] - (self.x - self.ox + 16)

    my = Mouse.pos[1] - (self.y - self.oy + 16)

    x = 8

    width = 128 

    height = 32

    for index in 0...@item_max

      y = ($game_temp.choice_start + index) * 32

          

      if mx > x and mx < x + width and my > y and my < y + height

        mouse_cursor(index)

        break

      end

    end

  end

end

 

class Window_PartyCommand < Window_Selectable

  def mouse_operation

    mx = Mouse.pos[0] - (self.x - self.ox + 16)

    my = Mouse.pos[1] - (self.y - self.oy + 16)

    y = 0

    width = 128

    height = 32

    for index in 0...@item_max

      x = 160 + index * 160

      if mx > x and

          mx < x + width and

          my > y and

          my < y + height

        mouse_cursor(index)

        break

      end

    end

  end

end

 

class Window_MenuPosition < Window_Selectable

  def mouse_operation

    mx = Mouse.pos[0] - (self.x - self.ox + 16)

    my = Mouse.pos[1] - (self.y - self.oy + 16)

    y = 0

    width = self.contents.width / @item_max - 10

    height = 32

    for index in 0...@item_max

      x = self.contents.width / (@item_max) * index + 4

      if mx > x and

         mx < x + width and

         my > y and

         my < y + height

        mouse_cursor(index)

        break

      end

    end

  end

end

 

class Scene_File

  alias mouse_update update

  def update

    mouse_update

    save = false

    mx, my = Mouse.pos

    x = 0

    width = (save ? 160 : 640)

    height = 90

    for index in 0...4

      y = 56 + index % 4 * 104

      if mx > x and

          mx < x + width and

          my > y and

          my < y + height

        break if @index == index

        @savefile_windows[@index].selected = false

        @index = index

        @savefile_windows[@index].selected = true

        Sound.play_cursor

        break

      end

    end

  end

end

 

 

Code:
 

class Game_Player

  alias mouse_update update

  def update

    if Mouse.pos != nil and Mouse.tile != nil

    if Mouse.mouse_press?

      x = Mouse.tile[0]

      y = Mouse.tile[1]

      if passable?(x,y)

        start_path(x,y)

      end

    end

    end

    mouse_update

  end

end

 
Instructions

Most of this is self explanatory, but for those of you who
would like to know how to make a window or sprite or what-
ever clickable, here you go:

All you do is check the position of the mouse(Mouse.pos)
and make sure it is within the rectangle of your window.
Ex:
Code:
 

return false if Mouse.pos[0] > @window.x + @window.width || Mouse.pos[1] > @window.y + @window.height

return false if Mouse.pos[0] < @window.x || Mouse.pos[1] < @window.y

return true

 

For sprites, you might use the same rectangle method, or if
you need it to be pixel perfect, you would have to ask som-
one else.

Also, for the cursor Graphic, it has to be in './Graphics/System/' folder.
The configuration is on line 6 of the first part.

The pathfinding script is not necessary, unless you are using the point
and click movement.

FAQ

Waiting for questions.

Compatibility

No known compatibility issues yet.

Credits and Thanks

I have to thank Near_Fantastica, because I looked a bit
as his module to figure how the win32apis were called.

Author's Notes

I like pancakes.

Terms and Conditions

Credit is appreciated, but not necessary.
 
You pathfinding needs some work. Unless I am not reading enough of your code, it seems to only move towards a target point, not really find a path. For that, you could really just cut down your code with a few methods from the MACL.
[rgss]class Game_Character
  #-------------------------------------------------------------------------
  # * Name      : Can Move Towards Target?
  #   Info      : Checks if character can move towards target
  #   Author    : SephirothSpawn
  #   Call Info : Integer Amounts, Destination X & Y
  #-------------------------------------------------------------------------
  def can_move_toward_target?(x, y)
    # Get difference in player coordinates
    sx = @x - x
    sy = @y - y
    # If coordinates are equal, return false
    return false if sx == 0 and sy == 0
    # Get absolute value of difference
    abs_sx = sx.abs
    abs_sy = sy.abs
    # Passable Testings
    pass = {2 => passable?(@x, @y, 2), 4 => passable?(@x, @y, 4),
            6 => passable?(@x, @y, 6), 8 => passable?(@x, @y, 8),
            24 => passable?(@x, @y, 2) && passable?(@x, @y + 1, 4),
            26 => passable?(@x, @y, 2) && passable?(@x, @y + 1, 6),
            42 => passable?(@x, @y, 4) && passable?(@x - 1, @y, 2),
            48 => passable?(@x, @y, 4) && passable?(@x - 1, @y, 8),
            62 => passable?(@x, @y, 6) && passable?(@x + 1, @y, 2),
            68 => passable?(@x, @y, 6) && passable?(@x + 1, @y, 8),
            84 => passable?(@x, @y, 8) && passable?(@x, @y - 1, 4),
            86 => passable?(@x, @y, 8) && passable?(@x, @y - 1, 6)}
    # Movement Testings
    if abs_sx > abs_sy
      if sx != 0
        if sx > 0
          return true, 4 if pass[4]
          sy > 0 ? (return true, 8 if pass[84]) : (return true, 2 if pass[24])
        else
          return true, 6 if pass[6]
          sy > 0 ? (return true, 8 if pass[86]) : (return true, 2 if pass[26])
        end
      end
      if sy != 0
        if sy > 0
          return true, 8 if pass[8]
          sx > 0 ? (return true, 4 if pass[48]) : (return true, 6 if pass[68])
        else
          return true, 2 if pass[2]
          sx > 0 ? (return true, 4 if pass[42]) : (return true, 6 if pass[62])
        end
      end
    else
      if sy != 0
        if sy > 0
          return true, 8 if pass[8]
          sx > 0 ? (return true, 4 if pass[48]) : (return true, 6 if pass[68])
        else
          return true, 2 if pass[2]
          sx > 0 ? (return true, 4 if pass[42]) : (return true, 6 if pass[62])
        end
      end
      if sx != 0
        if sx > 0
          return true, 4 if pass[4]
          sy > 0 ? (return true, 8 if pass[84]) : (return true, 2 if pass[24])
        else
          return true, 6 if pass[6]
          sy > 0 ? (return true, 8 if pass[86]) : (return true, 2 if pass[26])
        end
      end
    end
    # Return False if No Possible Moves
    return false
  end
  #-------------------------------------------------------------------------
  # * Name      : Move Towards Target
  #   Info      : Moves character towards target position
  #   Author    : SephirothSpawn
  #   Call Info : Integer Amounts, Destination X & Y
  #-------------------------------------------------------------------------
  def move_toward_target(x, y)
    # Gets Test Status
    can, dir = can_move_toward_target?(x, y)
    # Returns If Can't Move
    return unless can
    # Moves By Direction
    move_down  if dir == 2
    move_left  if dir == 4
    move_right if dir == 6
    move_up    if dir == 8
  end
 
[/rgss]

It would need some modifications to work with diagonal movement but shouldn't be too difficult. I suggest under each update, you test first if moving?, if not, run move_toward_target, then if still not moving, clear your path.
Code:
 

  def update

    # Original Update

    unless moving?

      move_toward_target(@path_x, @path_y)

      unless moving?

        clear_path

      end

    end

  end

If you would like to learn a true pathfinding algorithm (such as A*), just drop me a line and I can get on msn or the irc or something.


Pretty good script. I hope to see more work from you.
 

e

Sponsor

If I remember correctly, Cowlol has already released a solid A* pathfinding algorithm; you could probably find it easily enough in the first few pages.
 
Thanks for the praise, and thank you especially, seph. I was looking for a way to improve my pathfinding (You know, making you go around walls, not through them.). And I would like to learn the A* pathfinding stuff, and what it means :biggrin: .
etheon: I would like to learn this stuff myself, cuz' one day I will break free of the limited rpg maker programming (and learn more flash).

And I will be uploading a demo.
 
If you want, here's an old pathfinding system I worked on a while back. It's a bit over complex as of now, because this system is intended to support multiple pathfinding algorithms (working on a node-network pathfinding, that should increase things even more up). But it includes the A* method as of now. I don't have time to explain it now, but I'll probably make a tutorial or something over pathfinding sometime this weekend.
[rgss]#==============================================================================
# ** Systems.Pathfinding (V1.0)
#------------------------------------------------------------------------------
# SephirothSpawn
# Version 1.0
# 2008-08-17
#------------------------------------------------------------------------------
# Description:
# ------------
# This system allows the player and events draw a path from one point to
# another in the shortest route possible.
#
# This also can serve as a base for other pathfinding algorithms.
#  
# Method List:
# ------------
#
#   Pathfinding
#   -----------
#   find_path
#   retry_path
#
#   Pathfinding::Node
#   -----------------
#   clear_nodes
#   x, x=
#   y, y=
#   g, g=
#   h, h=
#   parent_id, parent_id=
#   parent
#   cost
#   ==
#  
#   Pathfinding::Seph_A_Star
#   ------------------------
#   find_path
#   retry_path
#   find_next_node (private)
#   adjacent_nodes (private)
#   add_node (private)
#   update_node (private)
#   passable? (private)
#   skip_node? (private)
#
#   Game_Character
#   --------------
#   find_path
#   clear_pathfinding
#   update_pathfinding
#   pathfinding
#   pathfinding_c_proc
#   pathfinding_c_frames
#------------------------------------------------------------------------------
# * Syntax :
#
#   Make Player find path
#    - $game_player.find_path(x, y, special = {}, mode = Pathfinding::Mode)
#==============================================================================
 
MACL::Loaded << 'Systems.Pathfinding'
 
#==============================================================================
# ** Pathfinding
#==============================================================================
 
module Pathfinding
  #--------------------------------------------------------------------------
  # * Constant
  #
  #   Limit: Number of steps a path will tried to be found before quitting
  #          (-1 for infinity)
  #    - Used in: Seph-A*
  #
  #   Fail Proc: Proc called when path cannot be found
  #    - Used in: Seph-A*
  #
  #   Complete Proc: Proc called when path finished
  #    - Used in: Seph-A*
  #
  #   Collision Frames: Number of frames to wait to retry when failure
  #--------------------------------------------------------------------------
  Defaults = {}
  Defaults['limit']     = -1              # Limit
  Defaults['f_proc']    = nil             # Fail Proc
  Defaults['c_proc']    = nil             # Complete Proc
  Defaults['c_frames']  = 20              # Collision Frames
  Mode                  = 'Seph-A*'
  Prevent_Script_Hang   = 200
  #--------------------------------------------------------------------------
  # * Find Path
  #--------------------------------------------------------------------------
  def self.find_path(character, end_x, end_y, special = {}, mode = Mode)
    # Setup Default Specials
    Defaults.each {|k, v| special[k] = v unless special.has_key?(k)}
    # Save Mode
    @mode = mode
    # Branch By Mode
    if mode == 'Seph-A*'
      # Seph-A*
      Seph_A_Star.find_path(character, end_x, end_y, special)
    end
  end
  #--------------------------------------------------------------------------
  # * Retry Path
  #--------------------------------------------------------------------------
  def self.retry_path
    # Branch By Mode
    if @mode == 'Seph-A*'
      # Seph-A*
      Seph_A_Star.retry_path
    end
  end
end
 
#==============================================================================
# ** Pathfinding::Node
#==============================================================================
 
class Pathfinding::Node
  #--------------------------------------------------------------------------
  # * Saved Nodes
  #--------------------------------------------------------------------------
  @@saved_nodes = {}
  #--------------------------------------------------------------------------
  # * Clear Nodes
  #--------------------------------------------------------------------------
  def self.clear_nodes
    @@saved_nodes = {}
  end
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :x
  attr_accessor :y
  attr_accessor :g
  attr_accessor :h
  attr_accessor :parent_id
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize(x, y, g = 0, h = 0, parent_id = nil)
    # Set Instances
    @x, @y, @g, @h, @parent_id = x, y, g, h, parent_id
    # Save By Object
    @@saved_nodes[object_id] = self
  end
  #--------------------------------------------------------------------------
  # * Parent
  #--------------------------------------------------------------------------
  def parent
    return @@saved_nodes[@parent_id]
  end
  #--------------------------------------------------------------------------
  # * Node Cost
  #--------------------------------------------------------------------------
  def cost
    return @g + @h
  end
  #--------------------------------------------------------------------------
  # * ==
  #--------------------------------------------------------------------------
  def ==(other)
    return other.is_a?(Pathfinding::Node) && other.x == x && other.y == y
  end
end
 
#==============================================================================
# ** Pathfinding::Seph_A_Star
#==============================================================================
 
module Pathfinding::Seph_A_Star
  #--------------------------------------------------------------------------
  # * Retry Path
  #--------------------------------------------------------------------------
  def self.retry_path
    self.find_path(@character, @end_node.x, @end_node.y, @special)
  end
  #--------------------------------------------------------------------------
  # * Find Path
  #--------------------------------------------------------------------------
  def self.find_path(character, end_x, end_y, special = {})
    # Clear Nodes List
    Pathfinding::Node.clear_nodes
    # Setup default specials
    Pathfinding::Defaults.each {|k, v| special[k] = v unless special.has_key?(k)}
    # Saves character
    @character = character
    # Creates nodes
    start_node = Pathfinding::Node.new(character.x, character.y)
    @end_node   = Pathfinding::Node.new(end_x, end_y)
    # Saves specials
    @special  = special
    limit    = special['limit']
    f_proc   = special['f_proc']
    c_proc   = special['c_proc']
    c_frames = special['c_frames']
    # Create lists
    @open_list    = [start_node]
    @closed_list  = {}
    # Clear found flag
    found = false
    # Start count
    count = 1
    # If target can be reached
    if self.passable?(@end_node.x, @end_node.y)
      # As long as list has a node to check
      until @open_list.empty?
        # Add to count
        count += 1
        # Update Graphics (Prevents scripting hanging error)
        Graphics.update if count % Pathfinding::Prevent_Script_Hang == 0
        # Break pathfinding if limit reached
        break if limit != -1 && count > limit
        # Get next node
        next_node = self.find_next_node
        # Add node to closed list
        @closed_list[[next_node.x, next_node.y]] = next_node
        # If next node has same position as end node
        if next_node == @end_node
          # Replace start node
          @end_node = next_node
          # Set found flag
          found = true
          # Break pathfinding
          break
        end
        # Get adjacent nodes
        adj_nodes = self.adjacent_nodes(next_node)
        # Pass through adjacent nodes
        for node in adj_nodes
          # Skip node if node in list
          next if self.skip_node?(node)
          # Add Node to Open List
          @open_list << node
          # Update Node
          self.update_node(@open_list.size - 1)
        end
      end
    end
    # If path cannot be found, call fail proc
    f_proc.call if found == false && f_proc != nil
    # If path found
    if found
      # Create movement list
      mvt_list = []
      # Start from end node
      node = @end_node
      # Keep checking parent node
      while node.parent != nil
        # Get Direction
        if node.parent.x > node.x
          dir = 4
        elsif node.parent.x < node.x
          dir = 6
        elsif node.parent.y > node.y
          dir = 8
        elsif node.parent.y < node.y
          dir = 2
        end
        # Add direction to front of list
        mvt_list.unshift(dir) if dir != nil
        # Switch current node to parent node
        node = node.parent
      end
      # Set pathfinding
      @character.pathfinding = mvt_list
      @character.pathfinding_c_proc = c_proc
      @character.pathfinding_c_frames = c_frames
    # If no path found
    else
      # Clear Pathfinding
      @character.clear_pathfinding
    end
  end
  #--------------------------------------------------------------------------
  # * Find Next Node
  #--------------------------------------------------------------------------
  private
  def self.find_next_node
    # Return nil if no nodes
    return nil if @open_list.empty?
    # Remove Last Element
    last = @open_list.pop
    # Return Last if List Empty
    return last if @open_list.empty?
    # Get original first element
    first = @open_list.first
    # Replace first element with last
    @open_list[0] = last
    # V Counter
    v = 0
    # Loop
    loop do
      u = v
      # If both children exist
      if 2 * u + 1 < @open_list.size
        v = 2 * u     if @open_list[2 * u].cost     <= @open_list.cost
        v = 2 * u + 1 if @open_list[2 * u + 1].cost <= @open_list[v].cost
      # If only one child exists
      elsif 2 * u < @open_list.size
        v = 2 * u     if @open_list[2 * u].cost     <= @open_list.cost
      end
      # Break if same
      break if u == v
      # Swap
      @open_list, @open_list[v] = @open_list[v], @open_list
    end
    # Return first node
    return first
  end
  #--------------------------------------------------------------------------
  # * Get a List of Adjacent Nodes
  #--------------------------------------------------------------------------
  private
  def self.adjacent_nodes(node)
    # Creates list of nodes
    nodes = []
    nodes << self.add_node(node.x, node.y + 1, node)
    nodes << self.add_node(node.x - 1, node.y, node)
    nodes << self.add_node(node.x + 1, node.y, node)
    nodes << self.add_node(node.x, node.y - 1, node)
    # Return Nodes - nil elements
    return nodes.compact
  end
  #--------------------------------------------------------------------------
  # * Add Node
  #--------------------------------------------------------------------------
  private
  def self.add_node(x, y, parent)
    # If passable
    if self.passable?(x, y)
      # Add 1 to Part Cost
      g = parent.g + 1
      # Get heuristic distance (Manhattan distance)
      h = (x - @end_node.x).abs + (y - @end_node.y).abs
      # Create New Node
      return Pathfinding::Node.new(x, y, g, h, parent.object_id)
    end
    # Return nil
    return nil
  end
  #--------------------------------------------------------------------------
  # * Update Node (heap_update)
  #--------------------------------------------------------------------------
  private
  def self.update_node(i)
    # Loop
    while i > 0
      # Break if parents cost is greater than parents
      break if @open_list[i / 2].cost > @open_list.cost
      # Swap node and parent
      @open_list[i / 2], @open_list = @open_list, @open_list[i / 2]
      # Switch index to parent
      i /= 2
    end
  end
  #--------------------------------------------------------------------------
  # * Passable?
  #--------------------------------------------------------------------------
  private
  def self.passable?(x, y)
    return @character.passable?(x, y, 0)
  end
  #--------------------------------------------------------------------------
  # * Skip Node?
  #--------------------------------------------------------------------------
  private
  def self.skip_node?(node)
    # Clear skip flag
    skip_node = false
    # Gets node index
    index = @open_list.index(node)
    # If Node Found
    if index != nil
      # If current node cost less than node being tested
      if @open_list[index].cost <= node.cost
        # Set skip flag
        skip_node = true
      else
        # Replace node
        @open_list[index] = node
        # Update node
        self.update_node(index)
        # Set skip flag
        skip_node = true
      end
    end
    # If closed list has a node
    if @closed_list[[node.x, node.y]] != nil
      # If current node cost less than node being tested
      if @closed_list[[node.x, node.y]].cost <= node.cost
        # Set skip flag
        skip_node = true
      else
        # Replace Node
        @closed_list[[node.x, node.y]] = node
      end
    end
    # Return the result
    return skip_node
  end
end
 
#==============================================================================
# ** Game_Character
#==============================================================================
 
class Game_Character
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :pathfinding
  attr_accessor :pathfinding_c_proc
  attr_accessor :pathfinding_c_frames
  #--------------------------------------------------------------------------
  # * Alias Listings
  #--------------------------------------------------------------------------
  alias_method :seph_pathfinding_gmchr_init,   :initialize
  alias_method :seph_pathfinding_gmchr_update, :update
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    # Clear pathfinding
    clear_pathfinding
    # Original Initialization
    seph_pathfinding_gmchr_init
  end
  #--------------------------------------------------------------------------
  # * Clear Pathfinding
  #--------------------------------------------------------------------------
  def clear_pathfinding
    @pathfinding = nil
    @pathfinding_c_proc = nil
    @pathfinding_c_frames = nil
  end
  #--------------------------------------------------------------------------
  # * Find Pathing
  #--------------------------------------------------------------------------
  def find_path(x, y, special = {}, mode = Pathfinding::Mode)
    # Run Pathfinding
    Pathfinding.find_path(self, x, y, special)
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # Run pathfinding if path defined
    update_pathfinding if @pathfinding != nil
    # Original Update
    seph_pathfinding_gmchr_update
  end
  #--------------------------------------------------------------------------
  # * Frame Update : Pathfinding
  #--------------------------------------------------------------------------
  def update_pathfinding
    # Return If Moving
    return if self.moving?
    # If Empty Pathfinding
    if @pathfinding.empty?
      # Call Complete Proc
      @pathfinding_c_proc.call unless @pathfinding_c_proc == nil
      # Clear Pathfinding
      clear_pathfinding
      return
    end
    # Get Next Direction
    dir = @pathfinding.shift
    # If Passable
    if passable?(x, y, dir)
      # Get Method Name
      m = 'move_'
      m += dir == 2 ? 'down' : dir == 4 ? 'left' : dir == 6 ? 'right' : 'up'
      # Call Next Move
      self.send(m.to_sym)
      return
    end
    # If retry
    if @pathfinding_c_frames != nil && @pathfinding_c_frames > 0
      # Set Wait Count
      @wait_count = @pathfinding_c_frames
      # Retry Path
      Pathfinding.retry_path
    # If don't retry
    else
      # Clear Pathfinding
      clear_pathfinding
    end
  end
end
 
#==============================================================================
# ** Game_Map
#==============================================================================
 
class Game_Map
  #--------------------------------------------------------------------------
  # * Alias Listings
  #--------------------------------------------------------------------------
  alias_method :seph_pathfinding_gmmap_setup, :setup
  #--------------------------------------------------------------------------
  # * Setup
  #--------------------------------------------------------------------------
  def setup(map_id)
    # Orignal Setup
    seph_pathfinding_gmmap_setup(map_id)
    # Clear Player Pathfinding
    $game_player.clear_pathfinding
  end
end
 
#==============================================================================
# ** Game_Player
#==============================================================================
 
class Game_Player
  #--------------------------------------------------------------------------
  # * Alias Listings
  #--------------------------------------------------------------------------
  alias_method :seph_pathfinding_gmplyr_update, :update
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # Clear Path if Direciton Pressed
    clear_pathfinding if Input.dir4 != 0
    # Original Update
    seph_pathfinding_gmplyr_update
  end
end
[/rgss]
 
Looks good, but like I said, I want to learn how to make my own.

I was thinking that maybe I should make a new method 'setup_path'
and have it determine all impassable tiles on the map, and choose a
path from the information.

Can I do that without going:

'passable?(x,x,x)'

for every coordinate on the map?

----

Also, I am working on a demo for XP ( I do not have VX on my new Computer).
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top