[XP] Advanced Pathfinding by Event Name

Started by mad.array, October 12, 2011, 11:45:33 am

Previous topic - Next topic

mad.array

October 12, 2011, 11:45:33 am Last Edit: January 16, 2012, 07:41:58 am by mad.array
Advanced Pathfinding & Carrots
Authors: ForeverZer0, modifications by mad.array
Version: 1.01a
Type: Path finding by Event Name, includes array support.
Key Term: Custom Movement System



Introduction

A modification of ForeverZer0s Advanced Pathfinding script (Found here). As well as using tile co-ordinates, the path finding target can now be defined as an event with a specific name. An array of event names can also be input and will be randomly selected.

If multiple events with the given name are found, the script will find the nearest instance and move to that example. Note that unless the target events Through flag is set, you will need to have a range of 1 or more.

The main reason I decided to work on this was to help achieve the feeling of a busy town. I wanted people to have their own little routines and go to specific places such as market stalls and blacksmiths. But how to do it? Well after looking at Pathfinding in general I stumbled accross ForeverZer0s script and started tinkering (I actually came accross it while researching something unrelated, but that just personifies my inability to focus).


Features


  • All the features of ForeverZer0s Advanced Pathfinding.

  • Move to location of another event.

  • Will find the nearest instance of event with the right name.

  • Can choose between events with different names.




Screenshots

No screen shots, as they don't really show movement.


Demo

No demo at the moment. Sorry!


Script

Insert above Main & below default scripts.
Spoiler: ShowHide

#=begin
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
# Advanced Pathfinding
# Author: ForeverZer0, mad.array
# Version: 1.0
# Date: 10.12.2011
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
#
# Introduction:
#   This is an advanced an highly inteligent pathfinding system. It allows for
#   the user to either through script or script call quickly and easily have
#   events or the game player automatically walk a path to a given set of
#   coordinates. The system is smart enough to quickly find paths through
#   relatively complex areas, and asjust on the fly for any obstacle that moves
#   to block its path. I used the A* algorithm, basic search algorithm used
#   often for robotics. More on this algorithm can be read about here:
#
#               http://en.wikipedia.org/wiki/A*_search_algorithm
#
# Features:
#   - Fast and intelligent pathfinding
#   - Easy to use script calls
#   - Optional "range" parameter can have character find alternate locations
#     if the preferred one is blocked and they are within the given range.
#   - Optional callbacks can be given to have something execute if when the
#     character reaches its goal, or when it fails to do so.
#
# Instructions:
#   - Place script below default scripts, and above "Main".
#   - Use the following script call:
#
#     pathfind(X, Y, CHARACTER, RANGE, SUCCESS_PROC, FAIL_PROC)
#    
#     The X and Y are the only required arguments. The others can be omitted.
#    
#     X - The x-coordinate to pathfind to. If X is a string or array then
#         strings must be contained within "quotation marks" and the array
#         within [square brackets].
#     Y - The y-coordinate to pathfind to. If using a string or array, DO NOT
#         leave Y blank. It will cause the script to crash.
#
#     CHARACTER - Either an instance of the character ($game_player,
#                 $game_map.events[ID], etc) or the ID of a character. The ID
#                 will be the event ID. Use -1 for the game player.
#
#     SUCCESS_PROC - A Proc object that will be executed when the player
#                    reaches the defined coordinates.
#     FAILURE_PROC - A Proc object that will be executed when the player
#                    cannot reach the defined coordinates.
#
#   - As default, the pathfinder will make 35 attempts to recalculate a route
#     that gets blocked. This value can be changed in game with the script
#     call:
#           $game_map.collision_retry = NUMBER
#
#     You can change the default value if desired by looking down to the first
#     class below in the main script.
#   - For longer pathfind routes, it is sometimes necessary to reset the
#     search limiter. This may cause increased lag when an object blocks the
#     character from being able to move, but will increase the range that the
#     system can work with. Use the following script call:
#
#         $game_map.search_limiter = NUMBER  (Default 1000)
#
#   - If you are experiencing any compatibility problems, go to the Game_Map
#     class below and set @recalculate_paths to false. This will take away some
#     of the efficiency of recalculating collisions, but will improve may fix
#     your problem.
#
# Compatibility:
#   Highly compatible. May experience issues with Custom Movement scripts,
#   but even then, it is unlikely.
#
# Credits/Thanks:
#   - ForeverZer0, for the script
#   - Special thanks to Jragyn for help making the big maze for the demo and
#     help testing.
#   - Credit goes to the Peter Hart, Nils Nilsson and Bertram Raphael for the
#     original search algorithm that was implemented
#
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

#===============================================================================
# ** Game_Map
#===============================================================================

class Game_Map
 
 attr_accessor :collision_retry
 attr_accessor :recalculate_paths
 attr_accessor :search_limiter
 
 alias zer0_pathfinding_init initialize
 def initialize
   # Initialize instance variables used for pathfinding.
   @collision_retry = 35
   @recalculate_paths = true
   @search_limiter = 1000
   # Original method
   zer0_pathfinding_init
 end
end

#===============================================================================
# ** Interpreter
#===============================================================================

class Interpreter
 
 def pathfind(x, y, *args)
   @x = x
   @y = y
   args[0] = @event_id if args[0] == nil
   args[1] = 0 if args[1] == nil
   if @x.is_a?(Array)
     decidepath(@x, @y, @args)
   end
   findpathevent(@x,@y, args)
   Pathfind.new(Node.new(@x, @y), *args)
 end

 def decidepath(x, y, args)
   r = rand(100) + 1
   for i in 0...x.size
     targ1 = 1 + (100/x.size) * i
     targ2 = (100/x.size) * (i +1)
     if r >= targ1 && r <=targ2
       @x=x[i]
     end
   end
 end

 def findpathevent(x, y, args)
   xs = []
   ys = []
   proxx = []
   proxy = []
   distance = []
   chosen = nil
   if x.is_a?(String)
     for event in $game_map.events.values
       if event.name == x
         xs.push(event.x)
         ys.push(event.y)
       end
     end
     if args[0] >  0
       runnerx = $game_map.events[args[0]].x
       runnery = $game_map.events[args[0]].y
     else
       runnerx = $game_player.x
       runnery = $game_player.y
     end
     if xs.size > 1
       for i in 0...xs.size# - 1
         proxx[i] = runnerx - xs[i]
         proxy[i] = runnery - ys[i]
         proxx[i] *= -1 if proxx[i] <0
         proxy[i] *= -1 if proxy[i] <0
         if proxx[i] > proxy[i]
           distance[i] = proxx[i] - proxy[i]
         elsif proxx[i] < proxy[i]
           distance[i] = proxy[i] - proxx[i]
         elsif proxx[i]==0
           distance[i] = proxy[i]
         elsif proxy[i]==0 || proxy[i] == proxx[i]
           distance[i] = proxx[i]
         end
       end
       j = 0
       while chosen == nil
         chosen = distance.index(j)
         j+=1
       end
         @x = xs[chosen]
         @y = ys[chosen]
     else
       @x = xs[0]
       @y = ys[0]
     end
   end
 end
end


class Game_Event
 
def name
  return @event.name
end
end

#==============================================================================
# ** Pathfind
#==============================================================================

class Pathfind

 attr_reader   :route                  
 attr_accessor :range  
 attr_reader   :goal
 attr_reader   :found
 attr_reader   :character                                
 attr_accessor :success_proc          
 attr_accessor :failure_proc        
 attr_accessor :target    
 attr_accessor :collisions      
 
 def initialize(node, char = -1, range = 0, *callbacks)
   # Set the character. Can either us an ID or an instance of a Game_Character.
   # A value of -1, which is default, is the Game_Player.
   if char.is_a?(Integer)
     @character = (char == -1) ? $game_player : $game_map.events[char]
   elsif char.is_a?(Game_Character)
     @character = char
   end
   # Set forcing flag. Will be disabled for recalculating on the fly.
   @forcing = true
   # Call a public method, since this method may need to be used again,
   # and "initialize" is private.
   setup(node, range, *callbacks)
 end
 
 
 def setup(node, range = 0, *callbacks)
   # Initialize the node we are trying to get to.
   @target = Node.new(node.x, node.y)
   @goal = @target.clone
   # Set beginning nodes and required variables.
   @start_node = Node.new(@character.x, @character.y)
   @nearest = Node.new(0, 0, 0, -1)
   @range, @found, @collisions = range, false, 0
   # Set callbacks for success and failure if included, else nil.
   @success_proc = callbacks[0]
   @failure_proc= callbacks[1]
   # Initialize sets to track open and closed nodes
   @open_set, @close_set = [@start_node], {}  
   # Find the optimal path
   calculate_path  
 end

 def calculate_path
   # Only do calculation if goal is actually passable, unless we only
   # need to get close or within range
   if @character.passable?(@goal.x, @goal.y, 0) || @range > 0
     # Initialize counter
     counter, wait = 0, 0
     until @open_set.empty?

       counter += 1
       # Give up if pathfinding is taking more than 500 iterations
       if counter >= $game_map.search_limiter
         @found = false
         break
       end
       # Get the node with lowest cost and add it to the closed list
       @current_node = get_current
       @close_set[[@current_node.x, @current_node.y]] = @current_node
       if @current_node == @goal ||
          (@range > 0 && @goal.in_range?(@current_node, @range) &&
          !@character.passable?(@goal.x, @goal.y,0))
         # We reached goal, exit the loop!
         @target = @goal
         @goal, @found = @current_node, true
         break
       else # if not goal
         # Keep track of the node with the lowest cost so far
         if @current_node.heuristic < @nearest.heuristic ||
           @nearest.heuristic < 1
           @nearest = @current_node
         end
         # Get adjacent nodes and check if they can be added to the open list
         neighbor_nodes(@current_node).each {|neighbor|
           # Skip Node if it already exists in one of the lists.
           next if can_skip?(neighbor)
           # Add node to open list following the binary heap conventions
           @open_set.push(neighbor)
           arrange(@open_set.size - 1)
         }
       end
     end
   end
   # If no path was found, see if we can get close to goal
   unless @found
     if @range > 0 && @nearest.heuristic > 0  
       # Create an alternate path.
       setup(@nearest, @range, @success_proc, @failure_proc)
     elsif @failure_proc != nil && (($game_map.collision_retry == 0) ||
       (@collisions > $game_map.collision_retry))
       # If out of retries, call the Proc for failure if defined
       @failure_proc.call
     end
   end
   # Create the move route using the generated path
   create_move_route
 end

 def create_move_route
   # There's no path to generate if no path was found
   return if !@found
   # Create a new move route that isn't repeatable
   @route = RPG::MoveRoute.new
   @route.repeat = false
   # Generate path by starting from goal and following parents
   node = @goal
   while node.parent
     # Get direction from parent to node as RPG::MoveCommand
     code = case direction(node.parent.x, node.parent.y, node.x, node.y)
     when 2 then 4 # Up
     when 4 then 3 # Left
     when 6 then 2 # Right
     when 8 then 1 # Down
     else; 0
     end
     # Add movement code to the start of the array
     @route.list.unshift(RPG::MoveCommand.new(code)) if code != 0
     node = node.parent
   end
   # If the path should be assigned to the character
   if (@forcing && !@route.list.empty?)
     @collisions = 0
     @character.paths.push(self)
     @character.force_move_route(@route) if @character.paths.size == 1
   end
   # Reset forcing flag if needed
   @forcing = true
   # Return the constructed RPG::MoveRoute
   return @route
 end
 
 def arrange(index)
   # Rearrange nodes in the open_set
   while index > 0
     # Break loop unless current item's cost is less than parent's
     break if @open_set[index].score > @open_set[index / 2].score
     # Bring lowest value to the top.
     temp = @open_set[index / 2]
     @open_set[index / 2] = @open_set[index]
     @open_set[index] = temp
     index /= 2
   end
 end
 
 def get_current
   return if @open_set.empty?
   return @open_set[0] if @open_set.size == 1
   # Set current node to local variable and replace it with the last
   current = @open_set[0]
   @open_set[0] = @open_set.pop
   # Loop and rearrange array according to the A* algorithm until done.
   y = 0  
   loop {
     x = y
     # If two children exist
     if 2 * x + 1 < @open_set.size
       if @open_set[2 * x].score <= @open_set[x].score
         y = 2 * x
         if @open_set[2 * x + 1].score <= @open_set[y].score
           y = 2 * x + 1
         end
       end
     # If only one child exists
     elsif 2 * x < @open_set.size &&
       @open_set[2 * x].score <= @open_set[x].score
       y = 2 * x
     end
     # Swap a child if it is less than the parent.
     break if x == y
     temp = @open_set[x]
     @open_set[x] = @open_set[y]
     @open_set[y] = temp
   }
   # Return the original first node (which was removed)
   return current
 end

 def direction(x1, y1, x2, y2)
   # Return the numerical direction between coordinates.
   return 6 if x1 > x2 # Right
   return 4 if x1 < x2 # Left
   return 2 if y1 > y2 # Bottom
   return 8 if y1 < y2 # Top
   return 0            
 end
 
 def neighbor_nodes(node)
   # Create array to hold the nodes, then check each direction.
   nodes = []
   nodes.push(get_neighbor(node.x + 1, node.y, node)) # Right
   nodes.push(get_neighbor(node.x - 1, node.y, node)) # Left
   nodes.push(get_neighbor(node.x, node.y + 1, node)) # Down
   nodes.push(get_neighbor(node.x, node.y - 1, node)) # Up
   # Remove any nil elements, then return results.
   return nodes.compact
 end
 
 def get_neighbor(x, y, parent)
   # Calculate direction, return new node if passable.
   direction = direction(x, y, parent.x, parent.y)
   if @character.passable?(parent.x, parent.y, direction)
     # The heuristic is simply the distance
     heuristics = ((x - @goal.x).abs + (y - @goal.y).abs)
     return Node.new(x, y, parent, parent.cost + 1, heuristics)
   end
 end
 
 def can_skip?(node)
   # Branch by if node is in either the open or closed set.
   if @open_set.include?(node)
     index = @open_set.index(node)
     return true if @open_set[index].score <= node.score
     # Swap them and update list order
     @open_set[index] = node
     arrange(index)
     return true
   elsif @close_set[[node.x, node.y]] != nil
     # If the existing passed node has a lower score than this one.
     return true if @close_set[[node.x, node.y]].score <= node.score
     # Update the existing node
     @close_set[[node.x, node.y]] = node
   end
   # Return false if no criteria was met.
   return false
 end
end

#==============================================================================
# ** Game_Character
#==============================================================================

class Game_Character
 
 attr_accessor :paths
 attr_accessor :move_route_forcing
 attr_accessor :move_route

 alias zer0_pathfinding_init initialize
 def initialize
   # Add public instance variable for paths
   @paths = []
   # Original method
   zer0_pathfinding_init
 end
 
 def next_route
   # Stop any custom move route that may be occuring.
   if @move_route != nil
     # Set index and disable forcing of current route
     @move_route_index = @move_route.list.size
     @move_route_forcing = false
     # Reset to what it was originally
     @move_route = @original_move_route
     @move_route_index = @original_move_route_index
     @original_move_route = nil
   end
   # Remove first path from the paths array.
   @paths.shift
   # If there is another path to follow...
   if @paths[0] != nil
     # Setup path again to reflect any changes since original creation
     @forcing = false
     @paths[0].setup(@paths[0].target, @paths[0].range,
       @paths[0].success_proc, @paths[0].failure_proc)
     force_move_route(@paths[0].route) if @paths[0].found
   end
 end
 
 alias zer0_recalculate_paths_move move_type_custom
 def move_type_custom
   if $game_map.recalculate_paths
     # Interrupt if not stopping
     return if jumping? || moving?
     # Loop until finally arriving at move command list
     while @move_route_index < @move_route.list.size
       # Get the move command at index
       command = @move_route.list[@move_route_index]
       # If command code is 0 (end of list)
       if command.code == 0
         # If [repeat action] option is ON
         if @move_route.repeat
           # Reset move route index to the top of the list
           @move_route_index = 0
         end
         # If [repeat action] option is OFF
         unless @move_route.repeat
           # If move route is forced and not repeating
           if @move_route_forcing and not @move_route.repeat
             # The move route is no longer forced (moving ended)
             @move_route_forcing = false
             # Restore original move route
             @move_route = @original_move_route
             @move_route_index = @original_move_route_index
             @original_move_route = nil
             # If there was a path to follow and we reached goal
             if @paths[0] != nil
               if self.x == @paths[0].goal.x &&
                 y == @paths[0].goal.y && @paths[0].success_proc
                 # Call success Proc if goal is reached and it is defined.
                 @paths[0].success_proc.call
               end
               next_route
             end
           end
           # Clear stop count
           @stop_count = 0
         end
         return
       end # if command.code == 0
       # For move commands (from move down to jump)
       if command.code <= 14
         # Branch by command code
         case command.code
         when 1 then move_down                 # Move down
         when 2 then move_left                 # Move left
         when 3 then move_right                # Move right
         when 4 then move_up                   # Move up
         when 5 then move_lower_left           # Move lower left
         when 6 then move_lower_right          # Move lower right
         when 7 then move_upper_left           # Move upper left
         when 8 then move_upper_right          # Move upper right
         when 9 then move_random               # Move random
         when 10 then move_toward_player       # Move toward player
         when 11 then move_away_from_player    # Move away from player
         when 12 then move_forward             # Step forward
         when 13 then move_backward            # Step backward
         when 14 then jump(command.parameters[0], command.parameters[1]) # Jump
         end
         # If movement failure occurs when "Ignore If Can't Move" is unchecked.
         if !@move_route.skippable && !moving? && !jumping?
           # If path is current and collision limit is not reached
           if @paths[0] != nil &&
             @paths[0].collisions < $game_map.collision_retry
             # Setup path again to update starting location.
             # original goal node is used because pathfinding changes
             # the goal node to current node
             goal, range = @paths[0].target, @paths[0].range
             reach = @paths[0].success_proc
             fail = @paths[0].failure_proc
             counter = @paths[0].collisions + 1
             # Find another path to goal
             @paths[0] = Pathfind.new(goal, self, range, reach, fail)
             @paths[0].collisions = counter
             force_move_route(@paths[0].route) if @paths[0].found
             # Wait a bit before starting to follow the new path
             @wait_count = 6
             return
           elsif paths[0] != nil
             # Call failure Proc if defined and set move index.
             @move_route_index = @move_route.list.size
             @paths[0].failure_proc.call if @paths[0].failure_proc != nil
             next_route
           end
           # End method
           return
         end
         # Advance index
         @move_route_index += 1
         return
       end # if command.code <= 14
       # If waiting
       if command.code == 15
         # Set wait count (from provided parameter)
         @wait_count = command.parameters[0] * 2 - 1
         @move_route_index += 1
         return
       end # if command.code == 15
       # If direction change (turning) command
       if command.code >= 16 and command.code <= 26
         # Branch by command code
         case command.code
         when 16 then turn_down                      # Turn down
         when 17 then turn_left                      # Turn left
         when 18 then turn_right                     # Turn right
         when 19 then turn_up                        # Turn up
         when 20 then turn_right_90                  # Turn 90° right
         when 21 then turn_left_90                   # Turn 90° left
         when 22 then turn_180                       # Turn 180°
         when 23 then turn_right_or_left_90          # Turn 90° right or left
         when 24 then turn_random                    # Turn at Random
         when 25 then turn_toward_player             # Turn toward player
         when 26 then turn_away_from_player          # Turn away from player
         end
         @move_route_index += 1
         return
       end
       # If other command (commands that don't 'return')
       if command.code >= 27
         # Branch by command code
         case command.code
         when 27                                              # Switch ON
           $game_switches[command.parameters[0]] = true
           $game_map.need_refresh = true
         when 28                                              # Switch OFF
           $game_switches[command.parameters[0]] = false
           $game_map.need_refresh = true
         when 29 then @move_speed = command.parameters[0]     # Change speed
         when 30 then @move_frequency = command.parameters[0] # Change freq
         when 31 then @walk_anime = true                      # Move ON
         when 32 then @walk_anime = false                     # Move OFF
         when 33 then @step_anime = true                      # Stop ON
         when 34 then @step_anime = false                     # Stop OFF
         when 35 then @direction_fix = true                   # Direction ON
         when 36 then @direction_fix = false                  # Direction OFF
         when 37 then @through = true                         # Through ON
         when 38 then @through = false                        # Through OFF
         when 39 then @always_on_top = true                   # On top ON
         when 40 then @always_on_top = false                  # On top OFF
         when 41                                              # Change Graphic
           # Can't change into a tile
           @tile_id = 0
           @character_name = command.parameters[0]
           @character_hue = command.parameters[1]
           # Update direction
           if @original_direction != command.parameters[2]
             @direction = command.parameters[2]
             @original_direction = @direction
             @prelock_direction = 0
           end
           # Update frame
           if @original_pattern != command.parameters[3]
             @pattern = command.parameters[3]
             @original_pattern = @pattern
           end
         when 42 then @opacity = command.parameters[0]        # Change Opacity
         when 43 then @blend_type = command.parameters[0]     # Change Blending
         when 44 then $game_system.se_play(command.parameters[0]) # Play SE
         when 45 then result = eval(command.parameters[0])    # Script
         end
         # Increment move index.
         @move_route_index += 1
       end
     end
   else
     # Original method
     zer0_recalculate_paths_move
   end
 end
end

#==============================================================================
# ** Node
#==============================================================================

class Node

 attr_accessor :x                      
 attr_accessor :y                      
 attr_accessor :parent                  
 attr_accessor :cost                
 attr_accessor :heuristic                  

 def initialize(x, y, parent = nil, cost = 0, heuristic = 0)
   # Set public instance variables.
   @x, @y, @parent, @cost, @heuristic = x, y, parent, cost, heuristic
 end

 def score
   # Return the current "score" of this node
   return @cost + @heuristic
 end
 
 def in_range?(node, range)
   # Return true/false if Nodes are within RANGE of each other.
   return (@x - node.x).abs + (@y - node.y).abs <= range
 end

 def ==(node)
   # Returns true/false of whether self and other are equal.
   return ((node.is_a?(Node)) && (node.x == @x) && (node.y == @y))
 end
end
#=end



Instructions

Place script below default scripts and above "Main".
Further instructions are within the script and demo.


Compatibility
Should be compatible with most scripts. Cannot be used in conjunction with the original Pathfinding script, but it shouldn't need to, should it?


Credits and Thanks


  • ForeverZer0 for the original script

  • Credit goes to Peter Hart, Nils Nilsson and Bertram Raphael for the original search algorithm that was implemented




Author's Notes

To use the pathfinding script you can follow ForeverZer0s instructions for the default methods or... to move to an event with a specific name use the Script command with these details:

pathfind("carrot",y, e, r)



  • "carrot" is the name of the event to search for. It is case sensitive but only needs to be in quotation marks in the script call, not in the actual event!

  • Y isn't strictly needed for this mode, but cannot be left blank so I advise just putting a 0 in

  • E is the event number, identical to ForeverZer0s script

  • R is range. How many tiles away the moving event will be from its destination before it's satisfied and stops



If you want your event to choose from multiple events then your Script call should look like this:

pathfind(["carrot","orange","apple],y, e, r)


Pretty much the same as before. Just be aware of the limitations of the Script call in RMXP...

I'm afraid I'm a rather haphazard coder so if there are any problems then let me know.


History

Version 1.01a


  • $game_actor changed to $game_player as actor has no x or y.

  • Fixed problem with 'normal' move routes crashing the game when blocked due to not having awareness of the failure_proc



Enjoy
"To walk at speed, manage or oversee..."

"RUN!"

Blizzard

I just wanted to point out that the A* algorithm is public domain so you technically don't have to credit the people who originally invented it.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

ForeverZer0

I didn't think so, but I just chose to give proper credit to them anyways.
Others don't have to for their games, so maybe I should have placed that in Author's Notes instead of Credits. :P
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

mad.array

Duly noted Blizzard. I hope everything else fits the standard.
"To walk at speed, manage or oversee..."

"RUN!"

Poe

November 10, 2011, 02:58:54 pm #4 Last Edit: November 11, 2011, 06:45:04 am by Poe
i got an error running this, and i just started with scripts this week so i figured i'd use this opportunity to learn something and find it myself.:p

at line 151 about it refers to $game_actor to get x and y positions, but that inherits from game_battler which has no x&y variables, so this needs to be $game_player to get them from game_character i think. works now anyway.

also:
QuoteE is the event name or number, identical to ForeverZer0s script

if it's a name to check for, wouldn't that give some trouble with this thing on line 147?
      if args[0] >  0

cos it's a string and not an integer... <- not sure about that and i wouldn't know how to fix it either.


could you add a check on this so it ignores erased events?

mad.array

@Poe: I'm going to have to look through this. I'm not at my home computer at the moment and looking through it in Wordpad isn't very productive. Adding a check for erased events shouldn't be too tough. Sorry I couldn't provide a swifter response, I've not been online for some time. I'll have to get back to you I'm afraid.
"To walk at speed, manage or oversee..."

"RUN!"

mad.array

I've gone ahead and done a minor update to the script. $game_actor has been changed to $game_player thanks to Poe being on the ball.

Also I've altered a line of code from a part of ForeverZer0s original script. I'm not sure if I was the only one having this problem but when using the APF script and I had an events 'normal' move route interrupted, it threw an error relating to the default move route not understanding failure_procs. I didn't want to post in FZs original thread because I wasn't sure if it was a necro (I've never found the rules for this place). But if people are having the same problem and are interested in fixing it then do the following:

Line 466 should just read 'else'. Change it to 'elsif paths[0] != nil'. Without the quotation marks. This will only call failure_procs if the event is using the pathfinding and if there ARE failure_procs.

So, happy pathfinding!
"To walk at speed, manage or oversee..."

"RUN!"

Raziel 0128

Apologies for bumping an old topic.

I had a quick question about using the success proc function. I'm trying to use this script on an NPC enemy moving around on the map. I'm trying to make it head towards the exit of the map unless the player is within 5 tiles of the player in which case it'll chase after you until you get too far away and return to heading to the exit. Once at the exit I'd like to activate either a regular switch or a self switch to turn the event off permanently.

The event itself is a parallel process event with an enemy graphic, and uses the [url =Event Range Conditions]http://forum.chaos-project.com/index.php/topic,9516.0.html[/url] script inside a conditional branch to determine how close the player is. It the activates a "Set move route" and move towards the player with if the player is close and in the "else" branch is uses this script to return to its route.

I've used the following code inside a call script.
pathfind("Exit",0, 48, 1, Proc.new { 
$game_switches[434] = true })


The event will successfully aggro and break aggro with the player however once it reaches the exit event the game crashes and gives the following error:
QuoteScript: 'Game_Map' line 216: SystemStackError occured.
stack level too deep


How do I solve this issue? Thanks for any help.

Mason Wheeler

This looks very nice, but I see one thing that's a bit worrisome: a class in the global namespace called "Node".  That's a generic enough name that it could easily conflict with other scripts that work with trees or graphs.  It might be better to call it PathfindNode or AStarNode or something.

KK20

mad.array hasn't been on for a couple years, so this kind of is grave digging.

While I guess "Node" is a generic class name, I don't really know of any scripts that even use "Node". Anything that does is most likely for path finding, which I don't know why anyone would use two path finding scripts.

It's not even that big of a problem either. The game would tell you that straight away.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Blizzard

You can use my script if it bothers you too much. xD
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Mason Wheeler

Quote from: KK20 on March 28, 2016, 02:12:08 pmIt's not even that big of a problem either. The game would tell you that straight away.

Would it?  It looks like if you declare a class with the same name in two different places, Ruby just treats it like all the same class and merges everything together.

KK20

I meant that the way Node class would be used would be different in both scripts. Clearly one is going to dominate over the other (i.e. no use of alias) so one of the scripts is going to crash as soon as you create a Node instance.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!