Solistra

SES Event Movement

Dec 30th, 2012
233
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 25.08 KB | None | 0 0
  1. #═╦═════════════════════════════════════════════════════════════════════════════
  2. # ║ § Event Movement (1.0) by Solistra                   [License: CC BY-SA 3.0]
  3. # ║ Original path-finding code by Near_Fantastica              <RMVX Ace>
  4. #═╬═════════════════════════════════════════════════════════════════════════════
  5. # ║ § Change Log
  6. #─╫─────────────────────────────────────────────────────────────────────────────
  7. # ║ v1.0 (December 30th, 2012) - Initial release
  8. # ║
  9. #═╬═════════════════════════════════════════════════════════════════════════════
  10. # ║ § Summary
  11. #─╫─────────────────────────────────────────────────────────────────────────────
  12. # ║ This script adds a number of new movement types for events, including simple
  13. # ║ path finding (originally written by Near_Fantastica for RMXP), easy patrol
  14. # ║ routes, and movement types that can move events closer to other characters
  15. # ║ and positions on the map. There is also a simple script call available for
  16. # ║ determining a character's proximity to any other character (the player or
  17. # ║ another event, for example).
  18. # ║
  19. # ║ NOTE: Make sure to credit Near_Fantastica as well as me (Solistra) if you
  20. # ║ use this script!
  21. # ║
  22. #═╬═════════════════════════════════════════════════════════════════════════════
  23. # ║ § Required Scripts
  24. #─╫─────────────────────────────────────────────────────────────────────────────
  25. # ║ This script requires the SES Core script version 1.0 or higher.
  26. # ║
  27. #═╬═════════════════════════════════════════════════════════════════════════════
  28. # ║ § Known Incompatibilities
  29. #─╫─────────────────────────────────────────────────────────────────────────────
  30. # ║ None.
  31. # ║
  32. #═╬═════════════════════════════════════════════════════════════════════════════
  33. # ║ § Installation
  34. #─╫─────────────────────────────────────────────────────────────────────────────
  35. # ║ Place this script below Materials, but above Main -- preferably relatively
  36. # ║ high in your list of custom scripts. This script must be below the SES Core
  37. # ║ script and the SES Event Memory script (if you are using it).
  38. # ║
  39. #═╬═════════════════════════════════════════════════════════════════════════════
  40. # ║ § Configuration
  41. #─╫─────────────────────────────────────────────────────────────────────────────
  42. # ║ There is only one configuration option: PATH_FINDING_DEPTH. The value of
  43. # ║ this option is important, though: it defines how large the path-finding tree
  44. # ║ may be when trying to determine a path. High values are more computationally
  45. # ║ expensive than low values, but a value that is too low may make the path
  46. # ║ finder relatively useless (as it may not be able to find paths that exist).
  47. # ║ Generally, a value from 50-150 is quite safe, but if you are experiencing
  48. # ║ lag in-game, you should try lowering this value as much as you reasonably
  49. # ║ can.
  50. # ║
  51. # ║ With that in mind, here is some explanation regarding patrol routes: in
  52. # ║ order to use patrol routes, you will have to "tag" the event you want to
  53. # ║ patrol with a comment. This comment is in the format of "<patrol [route]>".
  54. # ║ Patrol routes must be written as a series of comma-separated x and y values
  55. # ║ enclosed in brackets (these: []). The event will then move from its current
  56. # ║ position to the first position in the patrol route, then the next, until it
  57. # ║ reaches the end of the route and goes back to the first position in the
  58. # ║ route, effectively looping it.
  59. # ║
  60. # ║ Example of how to set up the comment:
  61. # ║   <patrol [0,0], [10,5], [5,10]>
  62. # ║
  63. # ║ This will cause the event to move to 0, 0 on the map, then 10, 5, then 5,
  64. # ║ 10, and finally back to 0, 0 -- looping through these patrol points.
  65. # ║
  66. # ║ NOTE: Patrol routes use path finding to get from one destination to another.
  67. # ║ This makes for very efficient patrols, but inefficient processing. Try not
  68. # ║ to use too many events with patrols on the same map -- you may find that
  69. # ║ your map begins to lag otherwise. Consider yourself informed.
  70. # ║
  71. #═╬═════════════════════════════════════════════════════════════════════════════
  72. # ║ § Tags
  73. #─╫─────────────────────────────────────────────────────────────────────────────
  74. # ║ ● Event Comment
  75. # ║   <patrol !route!>
  76. # ║     ► Replacements:
  77. # ║       !route! with a comma-separated list of x and y values written as
  78. # ║       Ruby arrays. Example: <patrol [0,0], [10,5], [5,10]>
  79. # ║
  80. #═╬═════════════════════════════════════════════════════════════════════════════
  81. # ║ § Script Calls
  82. #─╫─────────────────────────────────────────────────────────────────────────────
  83. # ║ This script features a number of script calls relating to event movement
  84. # ║ types (which should be used in an event's Move Route). If you wish to use
  85. # ║ any of these movement types as the default movement type for an event page,
  86. # ║ simply use the Custom movement type and insert the appropriate script call
  87. # ║ in that Move Route.
  88. # ║
  89. # ║ There are a few script calls that you will likely want to make use of if
  90. # ║ you plan to use the path finder: find_path, find_path_to, and reset_path.
  91. # ║ The find_path method finds a path to a given x and y value on the map, while
  92. # ║ the find_path_to method finds the path to a given character (the player or
  93. # ║ another event). This is how to use them:
  94. # ║   find_path([x], [y])        - Finds a path to [x] and [y] on the map.
  95. # ║   find_path_to($game_player) - Finds a path to the player.
  96. # ║   find_path_to(event([id]))  - Finds a path to the event with ID # [id].
  97. # ║   reset_path                 - Cancels path finding by removing the tree.
  98. # ║
  99. # ║ There are also script calls for movement types that can move an event
  100. # ║ towards a specific x and y position on the map or another character (the
  101. # ║ player or another event). These do not use path finding, and are very
  102. # ║ efficient (but also easily confused on complicated maps). To use them, make
  103. # ║ use of these script calls:
  104. # ║   move_type_toward_position([x], [y])
  105. # ║   move_type_toward_character(event([id])
  106. # ║
  107. # ║ In addition to this, there are also path-finding movement types -- but be
  108. # ║ warned: they are NOT efficient at all, and may cause serious lag if abused!
  109. # ║ With that in mind, these movement types will recalculate the path each time
  110. # ║ the character moves, meaning it will actively avoid obstacles. If you wish
  111. # ║ to use them, make use of these script calls:
  112. # ║   move_type_path([x], [y])
  113. # ║   move_type_path_to($game_player)
  114. # ║   move_type_path_to(event([id]))
  115. # ║
  116. # ║ You can also use a script call to determine whether or not a character is
  117. # ║ near the calling event (very useful in Conditional Branches). It will return
  118. # ║ "true" if the character is in proximity, "false" otherwise. Use it like so:
  119. # ║   near_character?($game_player, [range in tiles])
  120. # ║   in_proximity?(event([id]), [range in tiles])
  121. # ║
  122. # ║ If you leave out the range, it will default to 20 tiles to be consistent
  123. # ║ with RGSS3's default near_the_player? method in Game_Event.
  124. # ║
  125. #═╬═════════════════════════════════════════════════════════════════════════════
  126. # ║ § Aliased Methods
  127. #─╫─────────────────────────────────────────────────────────────────────────────
  128. # ║ ● Game_Map
  129. # ║     setup_events
  130. # ║ ● Game_Character
  131. # ║     initialize
  132. # ║     update
  133. # ║ ● Game_Player
  134. # ║     move_by_input
  135. # ║ ● Game_Event
  136. # ║     initialize
  137. # ║     update_self_movement
  138. # ║     refresh
  139. # ║
  140. #═╬═════════════════════════════════════════════════════════════════════════════
  141. # ║ § New Methods
  142. #─╫─────────────────────────────────────────────────────────────────────────────
  143. # ║ ● Game_Character
  144. # ║     move_toward_position
  145. # ║     find_path
  146. # ║     find_path_to
  147. # ║     run_next_path_step
  148. # ║     reset_path
  149. # ║     determine_path_tree
  150. # ║     moved?
  151. # ║     on_movement
  152. # ║     reset_movement_variables
  153. # ║     move_failed?
  154. # ║ ● Game_Event
  155. # ║     move_type_toward_character
  156. # ║     move_type_toward_position
  157. # ║     move_type_path
  158. # ║     move_type_path_to
  159. # ║     move_type_patrol
  160. # ║     near_character?
  161. # ║     determine_patrol_points
  162. # ║
  163. #═╬═════════════════════════════════════════════════════════════════════════════
  164. # ║ ▼ module SES::Event_Movement
  165. #═╩═════════════════════════════════════════════════════════════════════════════
  166. module SES module Event_Movement
  167.   #═╦═══════════════════════════════════════════════════════════════════════════
  168.   # ║ α BEGIN CONFIGURATION
  169.   #═╩═══════════════════════════════════════════════════════════════════════════
  170.   # This determines how large the path tree can be when trying to determine a
  171.   # path. Higher values require more computation while lower values may not
  172.   # find a path -- even if one exists. A modest amount from 50-150 works well.
  173.   PATH_FINDING_DEPTH = 100
  174.   #═╦═══════════════════════════════════════════════════════════════════════════
  175.   # ║ Ω END CONFIGURATION
  176.   #═╩═══════════════════════════════════════════════════════════════════════════
  177. end end
  178. #═╦═════════════════════════════════════════════════════════════════════════════
  179. # ║ ▲ module SES::Event_Movement
  180. #═╩═════════════════════════════════════════════════════════════════════════════
  181.                 ($imported ||= {})["SES - Event Movement"] = 1.0
  182.         if $imported["SES - Core"].nil? || $imported["SES - Core"] < 1.0
  183.         raise("SES Event Movement requires SES Core 1.0 or higher.") end
  184.  
  185.                        if $imported["SES - Event Memory"]
  186.   module SES module Event_Memory; @event_memory[:patrol] = :patrol end end end
  187. #═╦═════════════════════════════════════════════════════════════════════════════
  188. # ║ ▼ class Game_Map
  189. #═╩═════════════════════════════════════════════════════════════════════════════
  190. class Game_Map
  191.   if $imported["SES - Event Memory"]
  192.     # Aliased to remember patrol points if SES Event Memory is in use.
  193.     alias :ses_evmov_gm_setupev :setup_events
  194.     def setup_events(*args, &block)
  195.       ses_evmov_gm_setupev(*args, &block)
  196.       $game_system.events[@map_id].each{|i, v| @events[i].patrol = v[:patrol]
  197.       } if $game_system.events[@map_id]
  198.     end
  199.   end
  200. end
  201. #═╦═════════════════════════════════════════════════════════════════════════════
  202. # ║ ▲ class Game_Map
  203. #─╫─────────────────────────────────────────────────────────────────────────────
  204. # ║ ▼ class Game_Character
  205. #═╩═════════════════════════════════════════════════════════════════════════════
  206. class Game_Character < Game_CharacterBase
  207.   # Initialize the new instance variables @old_x and @old_y.
  208.   alias :ses_evmov_gc_init :initialize
  209.   def initialize(*a, &b) ses_evmov_gc_init(*a, &b); @old_x, @old_y = @x, @y end
  210.  
  211.   # Aliased to provide the ability to run code whenever an event moves via the
  212.   # on_movement method. (Scripters, alias on_movement if you need this.)
  213.   alias :ses_evmov_gc_upd :update
  214.   def update(*a, &b) ses_evmov_gc_upd(*a, &b); on_movement if moved? end
  215.  
  216.   # Moves the character towards a specific x and y position on the map.
  217.   def move_toward_position(x, y)
  218.     sx, sy = @x - x, @y - y
  219.     if sx != 0 || sy != 0
  220.       if sx.abs > sy.abs
  221.         sx > 0 ? move_straight(4) : move_straight(6)
  222.         (sy > 0 ? move_straight(8) : move_straight(2)) if move_failed?
  223.       else
  224.         sy > 0 ? move_straight(8) : move_straight(2)
  225.         (sx > 0 ? move_straight(4) : move_straight(6)) if move_failed?
  226.       end
  227.     end
  228.   end
  229.  
  230.   # Convenience method to quickly build a path tree for an event. Original code
  231.   # by Near_Fantastica, stripped and modified by Solistra.
  232.   def find_path(x, y)
  233.     result = determine_path_tree(@x, @y, x, y)
  234.     @path_tree = result[0].is_a?(Table) ? result[0] : nil
  235.     @path_tree[@x, @y] = result[1] unless @path_tree.nil?
  236.   end
  237.  
  238.   # Convenience method to build a path tree to an ancestor of Game_Character.
  239.   def find_path_to(character) find_path(character.x, character.y) end
  240.  
  241.   # Moves a character along their path tree. Original version was written by
  242.   # Near_Fantastica, stripped and modified by Solistra.
  243.   def run_next_path_step
  244.     return if moving?
  245.     step = @path_tree[@x, @y]
  246.     if step <= 1 then reset_path; return
  247.     else
  248.       move_straight(6) if @path_tree[@x + 1, @y] == step - 1
  249.       move_straight(2) if @path_tree[@x, @y + 1] == step - 1
  250.       move_straight(4) if @path_tree[@x - 1, @y] == step - 1
  251.       move_straight(8) if @path_tree[@x, @y - 1] == step - 1
  252.     end
  253.   end
  254.  
  255.   # Reset the path tree. Original code by Near_Fantastica, modified by Solistra.
  256.   def reset_path() remove_instance_variable(:@path_tree) end
  257.  
  258.   # Creates the path tree used for path finding. Original code written by
  259.   # Near_Fantastica, modified by Solistra.
  260.   def determine_path_tree(sx, sy, ex, ey)
  261.     map = Table.new($game_map.width, $game_map.height)
  262.     map[ex, ey], depth, old_positions, new_positions = 1, 2, [[ex, ey]], []
  263.     depth.upto(SES::Event_Movement::PATH_FINDING_DEPTH) do |step|
  264.       loop do
  265.         break if old_positions[0].nil?
  266.         x, y = old_positions.shift
  267.         return [map, step] if x == sx && y + 1 == sy
  268.         if passable?(x, y, 2) && map[x, y + 1] == 0
  269.           map[x, y + 1] = step
  270.           new_positions.push([x, y + 1])
  271.         end
  272.         return [map, step] if x - 1 == sx && y == sy
  273.         if passable?(x, y, 4) && map[x - 1, y] == 0
  274.           map[x - 1, y] = step
  275.           new_positions.push([x - 1, y])
  276.         end
  277.         return [map, step] if x + 1 == sx && y == sy
  278.         if passable?(x, y, 6) && map[x + 1, y] == 0
  279.           map[x + 1, y] = step
  280.           new_positions.push([x + 1, y])
  281.         end
  282.         return [map, step] if x == sx && y - 1 == sy
  283.         if passable?(x, y, 8) && map[x, y - 1] == 0
  284.           map[x, y - 1] = step
  285.           new_positions.push([x, y - 1])
  286.         end
  287.       end
  288.       old_positions, new_positions = new_positions, []
  289.     end
  290.   end
  291.  
  292.   # Movement-related convenience methods -- certainly easy enough to figure out.
  293.   def moved?() @old_x != @x || @old_y != @y end
  294.   def on_movement() reset_movement_variables end
  295.   def reset_movement_variables() @old_x, @old_y = @x, @y end
  296.   def move_failed?() !@move_succeed end
  297. end
  298. #═╦═════════════════════════════════════════════════════════════════════════════
  299. # ║ ▲ class Game_Character
  300. #─╫─────────────────────────────────────────────────────────────────────────────
  301. # ║ ▼ class Game_Player
  302. #═╩═════════════════════════════════════════════════════════════════════════════
  303. class Game_Player < Game_Character
  304.   # Aliased to move the player along their path tree if one exists before any
  305.   # movement by input is processed.
  306.   alias :ses_evmov_gp_move :move_by_input
  307.   def move_by_input(*args, &block)
  308.     if @path_tree
  309.       run_next_path_step
  310.       move_failed? ? reset_path : return
  311.     end
  312.     ses_evmov_gp_move(*args, &block)
  313.   end
  314. end
  315. #═╦═════════════════════════════════════════════════════════════════════════════
  316. # ║ ▲ class Game_Player
  317. #─╫─────────────────────────────────────────────────────────────────────────────
  318. # ║ ▼ class Game_Event
  319. #═╩═════════════════════════════════════════════════════════════════════════════
  320. class Game_Event < Game_Character
  321.   attr_accessor :patrol
  322.  
  323.   # Aliased to initialize the new instance variable @patrol, which is used to
  324.   # determine the patrol points for an event.
  325.   alias :ses_evmov_ge_init :initialize
  326.   def initialize(*args, &block)
  327.     ses_evmov_ge_init(*args, &block)
  328.     @patrol = determine_patrol_points
  329.   end
  330.  
  331.   # Aliased to move events along their patrol route if a route exists and they
  332.   # are not currently running a path-finding tree.
  333.   alias :ses_evmov_ge_updmov :update_self_movement
  334.   def update_self_movement(*args, &block)
  335.     ses_evmov_ge_updmov(*args, &block)
  336.     if near_the_screen? && @stop_count > stop_count_threshold
  337.       move_type_patrol if @patrol
  338.       run_next_path_step if @path_tree
  339.     end
  340.   end
  341.  
  342.   # Aliased to reset the path when this event is refreshed and to get any new
  343.   # patrol routes (or remove them if necessary).
  344.   alias :ses_evmov_ge_ref :refresh
  345.   def refresh(*args, &block)
  346.     ses_evmov_ge_ref(*args, &block)
  347.     unless @erased
  348.       reset_path if @path_tree
  349.       @patrol = determine_patrol_points
  350.     end
  351.   end
  352.  
  353.   # Self-explanatory enough, but this method provides a new movement type that
  354.   # will move this event towards any ancestor of Game_Character.
  355.   def move_type_toward_character(character)
  356.     if near_character?(character)
  357.       case rand(6)
  358.       when 0..3 then move_toward_character(character)
  359.       when 4 then move_random
  360.       when 5 then move_forward
  361.       end
  362.     else move_random end
  363.   end
  364.  
  365.   # Also self-explanatory, but provides a movement type that will cause this
  366.   # event to move towards a specific x and y position on the map.
  367.   def move_type_toward_position(x, y)
  368.     case rand(6)
  369.     when 0..3 then move_toward_position(x, y)
  370.     when 4 then move_random
  371.     when 5 then move_forward
  372.     end
  373.   end
  374.  
  375.   # Custom movement type that will move to a specified path, moving around any
  376.   # obstacles if they exist. Warning: this recalculates the path each step,
  377.   # which is NOT efficient! Do not abuse this!
  378.   def move_type_path(x, y)
  379.     find_path(x, y) if [@x, @y] != [x, y] || move_failed?
  380.   end
  381.  
  382.   # Convenience method for move_type_path which moves to an ancestor of the
  383.   # Game_Character class.
  384.   def move_type_path_to(character) move_type_path(character.x, character.y) end
  385.  
  386.   # The patrol movement type. Simply uses the path finder to move from one point
  387.   # in its patrol route to the next.
  388.   def move_type_patrol
  389.     return unless @patrol
  390.     [@x,@y] == @patrol[0] ? @patrol.push(@patrol.shift) : find_path(*@patrol[0])
  391.   end
  392.  
  393.   # Method used by move_type_toward_character to keep it consistent with the
  394.   # behavior of the RGSS3 default move_type_toward_player method. Can also be
  395.   # used as a simple proximity-checking method.
  396.   def near_character?(character, range = 20)
  397.     distance_x_from(character.x).abs + distance_y_from(character.y).abs < range
  398.   end
  399.   alias :in_proximity? :near_character?
  400.  
  401.   # Gets patrol points from an event's comments. (Note: patrol points can also
  402.   # be manually added with $game_map.events[event_id].patrol = [route]. The
  403.   # route is just an array of arrays containing x and y values.)
  404.   def determine_patrol_points
  405.     if comments.any?{|c| c =~ /\<patrol (.+)\>/} then eval("[#{$1}]") end
  406.   end
  407. end
  408. #═╦═════════════════════════════════════════════════════════════════════════════
  409. # ║ ▲ class Game_Event
  410. #═╩═════════════════════════════════════════════════════════════════════════════
Advertisement
Add Comment
Please, Sign In to add comment