[XP] Simple Event Ai

Started by nathmatt, March 03, 2010, 07:08:00 pm

Previous topic - Next topic

nathmatt

March 03, 2010, 07:08:00 pm Last Edit: April 18, 2012, 05:43:42 pm by nathmatt
Simple Event Ai
Authors: Nathmatt
Version: 1.16
Type: Add-on
Key Term: Misc Add-on



Introduction
Makes events chase you when you get within range.
If the event gets to far from original location return to original location,
allows events to run from you, and when events see you turns on a configurable switch


Features


  • all configurations are done in events name



Screenshots
no screen shot needed


Demo
will add when i can make an actual demo


Script
Spoiler: ShowHide
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
# Simple Event Ai by Nathmatt
# Version: 1.16
# Type: Add On
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#  
#  This work is protected by the following license:
# #----------------------------------------------------------------------------
# #  
# #  Creative Commons - Attribution-NonCommercial-ShareAlike 3.0 Unported
# #  ( http://creativecommons.org/licenses/by-nc-sa/3.0/ )
# #  
# #  You are free:
# #  
# #  to Share - to copy, distribute and transmit the work
# #  to Remix - to adapt the work
# #  
# #  Under the following conditions:
# #  
# #  Attribution. You must attribute the work in the manner specified by the
# #  author or licensor (but not in any way that suggests that they endorse you
# #  or your use of the work).
# #  
# #  Noncommercial. You may not use this work for commercial purposes.
# #  
# #  Share alike. If you alter, transform, or build upon this work, you may
# #  distribute the resulting work only under the same or similar license to
# #  this one.
# #  
# #  - For any reuse or distribution, you must make clear to others the license
# #    terms of this work. The best way to do this is with a link to this web
# #    page.
# #  
# #  - Any of the above conditions can be waived if you get permission from the
# #    copyright holder.
# #  
# #  - Nothing in this license impairs or restricts the author's moral rights.
# #  
# #-----------------------------------------------------------------------------
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
# Config  
#-------------------------------------------------------------------------------
#  This module provides the configurations.
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
module Config
 # the event tag that blocks the view events
 Wall = 1
end
#==============================================================================
# Game_Map
#------------------------------------------------------------------------------
#  This class adds check names to the original Game_Map.
#==============================================================================
class Game_Map
 attr_reader   :map
 alias setup_Event_Ai_later setup
 def setup(map_id)
   setup_Event_Ai_later(map_id)
   check_names
 end
 #--------------------------------------------------------------------------
 # check_names
 #  This calls the check_event_names on all the event on the map.
 #--------------------------------------------------------------------------
 def check_names
   $game_map.events.each_key {|i|
   check_event_names(i) if $game_map.events[i] != nil}
 end
 #--------------------------------------------------------------------------
 # check_event_name
 #  event_id - event ID
 #  Checks events name and adds its id to the proper array.
 #  Then stores a few needed variables
 #--------------------------------------------------------------------------
 def check_event_names(event_id)
   event = @map.events[event_id]
   if event.name.clone.gsub!(/\\[Pp]\[([\d, ]+)\]/) {"#[$1]"}
     get = eval("[#{$1}]")
     view = get[0]
     range = get[1]
     target_id = get[2]
     type = 4
   end
   if event.name.clone.gsub!(/\\[Rr]\[([\d, ]+)\]/) {"#[$1]"}
     get = eval("[#{$1}]")
     view = get[0]
     target_id = get[1]
     type = 2
   end
   if event.name.clone.gsub!(/\\[Cc]\[([\d, ]+)\]/) {"#[$1]"}
     get = eval("[#{$1}]")
     view = get[0]
     target_id = get[1]
     type = 1
   end
   if event.name.clone.gsub!(/\\[Vv]\[([\d, ]+)\]/) {"#[$1]"}
     get = eval("[#{$1}]")
     view = get[0]
     switch_id = get[1]
     target_id = get[2]
     type = 3
   end
   if type.is_a?(Numeric) && type != nil
     view = 0 if !view.is_a?(Numeric) || view == nil
     target_id = 0 if target_id == nil
     $game_map.events[event_id] = Ai_Event.new($game_map.map_id,event,view,type,
     range,event.x,event.y,target_id,switch_id)
   end
 end
end
Location_Holder = Struct.new(:x, :y)
#==============================================================================
# ** Ai_Event
#------------------------------------------------------------------------------
#  This is a modified version of Game_Event that controls the events movements
#  @ whether or not the event can see its target.
#==============================================================================

class Ai_Event < Game_Character
 #--------------------------------------------------------------------------
 # * Public Instance Variables
 #--------------------------------------------------------------------------
 attr_reader   :trigger                  # trigger
 attr_reader   :list                     # list of event commands
 attr_reader   :starting                 # starting flag
 #--------------------------------------------------------------------------
 # * Object Initialization
 #     map_id : map ID
 #     event  : event (RPG::Event)
 #--------------------------------------------------------------------------
 def initialize(map_id,event,view,type,range,start_x,start_y,target_id,
   switch_id = nil)
   super()
   @map_id        = map_id
   @event         = event
   @id            = event.id
   @view          = view
   @type          = type
   @range         = range
   @switch_id     = switch_id
   @erased        = false
   @starting      = false
   @through       = true
   #call dummy class
   @start = Location_Holder.new(start_x,start_y)
   # get type from type
   @target = check_target(target_id)
   # Move to starting position
   moveto(@event.x, @event.y)
   refresh
 end
 #--------------------------------------------------------------------------
 # * Clear Starting Flag
 #--------------------------------------------------------------------------
 def clear_starting
   @starting = false
 end
 #--------------------------------------------------------------------------
 # * Determine if Over Trigger
 #    (whether or not same position is starting condition)
 #--------------------------------------------------------------------------
 def over_trigger?
   # If not through situation with character as graphic
   # Starting determinant is face
   return false if @character_name != "" and not @through
   # If this position on the map is impassable
   # Starting determinant is face
   return false unless $game_map.passable?(@x, @y, 0)
   # Starting determinant is same position
   return true
 end
 #--------------------------------------------------------------------------
 # * Start Event
 #--------------------------------------------------------------------------
 def start
   # If list of event commands is not empty
   @starting = true if @list.size > 1
 end
 #--------------------------------------------------------------------------
 # * Temporarily Erase
 #--------------------------------------------------------------------------
 def erase
   @erased = true
   refresh
 end
 #--------------------------------------------------------------------------
 # * Refresh
 #--------------------------------------------------------------------------
 def refresh
   # Initialize local variable: new_page
   new_page = nil
   # If not temporarily erased
   unless @erased
     # Check in order of large event pages
     @event.pages.reverse.each { |page|
     # Make possible referrence for event condition with c
     c = page.condition
     # Switch 1 condition confirmation
     if c.switch1_valid
       next if $game_switches[c.switch1_id] == false
     end
     # Switch 2 condition confirmation
     if c.switch2_valid
       next if $game_switches[c.switch2_id] == false
     end
     # Variable condition confirmation
     if c.variable_valid
       next if $game_variables[c.variable_id] < c.variable_value
     end
     # Self switch condition confirmation
     if c.self_switch_valid
       key = [@map_id, @event.id, c.self_switch_ch]
       next if $game_self_switches[key] != true
     end
     # Set local variable: new_page
     new_page = page
     # Remove loop
     break}
   end
   # If event page is the same as last time
   return if new_page == @page
   # Set @page as current event page
   @page = new_page
   # Clear starting flag
   clear_starting
   # If no page fulfills conditions
   if @page == nil
     # Set each instance variable
     @tile_id = 0
     @character_name = ""
     @character_hue = 0
     @move_type = 0
     @through = true
     @trigger = nil
     @list = nil
     @interpreter = nil
     # End method
     return
   end
   # Set each instance variable
   @tile_id = @page.graphic.tile_id
   @character_name = @page.graphic.character_name
   @character_hue = @page.graphic.character_hue
   if @original_direction != @page.graphic.direction
     @direction = @page.graphic.direction
     @original_direction = @direction
     @prelock_direction = 0
   end
   if @original_pattern != @page.graphic.pattern
     @pattern = @page.graphic.pattern
     @original_pattern = @pattern
   end
   @opacity = @page.graphic.opacity
   @blend_type = @page.graphic.blend_type
   @original_type = @page.move_type
   @move_speed = @page.move_speed
   @move_frequency = @page.move_frequency
   @move_route = @page.move_route
   @move_route_index = 0
   @move_route_forcing = false
   @walk_anime = @page.walk_anime
   @step_anime = @page.step_anime
   @direction_fix = @page.direction_fix
   @through = @page.through
   @always_on_top = @page.always_on_top
   @trigger = @page.trigger
   @list = @page.list
   @interpreter = nil
   # If trigger is [parallel process]
   # Create parallel process interpreter
   @interpreter = Interpreter.new if @trigger == 4
   # Auto event start determinant
   check_event_trigger_auto
 end
 #--------------------------------------------------------------------------
 # * Touch Event Starting Determinant
 #--------------------------------------------------------------------------
 def check_event_trigger_touch(x, y)
   # If event is running
   return if $game_system.map_interpreter.running?
   # If trigger is [touch from event] and consistent with player coordinates
   if @trigger == 2 && x == $game_player.x && y == $game_player.y
     # If starting determinant other than jumping is front event
     start if ! jumping? && ! over_trigger?
   end
 end
 #--------------------------------------------------------------------------
 # * Automatic Event Starting Determinant
 #--------------------------------------------------------------------------
 def check_event_trigger_auto
   # If trigger is [touch from event] and consistent with player coordinates
   if @trigger == 2 && @x == $game_player.x && @y == $game_player.y
     # If starting determinant other than jumping is same position event
     start if ! jumping? && over_trigger?
   end
   # If trigger is [auto run]
   start if @trigger == 3
 end
 #------------------------------------------------------------------------
 # check_event_trigger_at
 #  x - x-coordinate
 #  y - y-coordinate
 #  Check event if it was triggered at a specific position.(pixel movement)
 #------------------------------------------------------------------------
 def check_event_trigger_at(x, y)
   # get pixel movement rate
   pix = $BlizzABS.pixel
   # if player touched this event and not jumping and not over_trigger
   if !jumping? && !over_trigger? && $BlizzABS.util.rect_intersection(
     Rect.new(@x * pix, @y * pix, pix, pix), Rect.new(x, y, pix, pix))
     # start
     start
     # started
     return true
   end
   # not started
   return false
 end
 #--------------------------------------------------------------------------
 # distance_from_target(target)
 #  target - is the target
 #  Checks the distance from self and target and returns it.
 #--------------------------------------------------------------------------
 def distance_from_target(target)
   return Math.hypot((@x - target.x),(@y - target.y))
 end
 #--------------------------------------------------------------------------
 # check_target(target_id)
 #  target_id - is the event id unless it equals 0 thean its the player
 #  Checks what kind of target and returns it.
 #--------------------------------------------------------------------------
 def check_target(target_id)
   return $game_player if target_id == 0
   return $game_map.events[target_id] if target_id > 0
 end
 #--------------------------------------------------------------------------
 # move_type_away
 #  moves self away from target if in view
 #--------------------------------------------------------------------------
 def move_type_away
   if distance_from_target(@target) <= @view
     move_away
   else
     @original_type
   end
 end
 #--------------------------------------------------------------------------
 # move_type_toward
 #  moves self toward from target if in view
 #--------------------------------------------------------------------------
 def move_type_toward
   if distance_from_target(@target) <= @view
     move_toward(@target)
   else
     @original_type
   end
 end
 #--------------------------------------------------------------------------
 # view_type
 #  turns on defined switch if event can_see? self
 #--------------------------------------------------------------------------
 def view_type
   if can_see?
     $game_switches[@switch_id] = true
     $game_map.refresh
   end
   return
 end
 #--------------------------------------------------------------------------
 # passave_type
 #  moves self toward target unless it gets out of range from start
 #--------------------------------------------------------------------------
 def passave_type
   if distance_from_target(@target) < @view
     if distance_from_target(@start) > @range
       move_toward(@start)
     else
       move_toward(@target)
     end
   else
     return move_toward(@start)
   end
 end
 #--------------------------------------------------------------------------
 # change_type
 #--------------------------------------------------------------------------
 def change_type(type,view,target_id = nil,range = nil,switch_id = nil)
   @type = type
   @view = view
   @target_id = target_id if target_id != nil
   @range = range if range != nil
   @swith_id = switch_id if switch_id != nil
 end
 #--------------------------------------------------------------------------
 # can_see?
 #  event_id - is the events id
 #  This checks to see if the event can see you.
 #--------------------------------------------------------------------------
 def can_see?
   (0...@view).each { |r|
   case @direction
   when 2
     if $game_map.terrain_tag(@x, (@y + r)) == Config::Wall
       break
     elsif @target.x == @x && @target.y == (@y + r)
       return true
     end
   when 4
     if $game_map.terrain_tag((@x - r), @y) == Config::Wall
       break
     elsif @target.x == (@x - r) && @target.y == @y
       return true
     end
   when 6
     if $game_map.terrain_tag((@x + r), @y) == Config::Wall
       break
     elsif @target.x == (@x + r) && @target.y == @y
       return true
     end
   when 8
     if $game_map.terrain_tag(@x, (@y - r)) == Config::Wall
       break
     elsif @target.x == @x && @target.y == (@y - r)
       return true
     end
   end}
   return false
 end
 def check_other(x,y,d)
   case d
   when 2
     if passable?(x, y,4)
       return move_left
     elsif passable?(x, y,6)
       return move_right
     elsif passable?(x, y,8)  
       return move_up
     end
   when 4
     if passable?(x, y,2)
       return move_down
     elsif passable?(x, y,8)
       return move_up
     elsif passable?(x, y,6)
       return move_right
     end
   when 6
     if passable?(x, y,2)
       return move_down
     elsif passable?(x, y,8)
       return move_up
     elsif passable?(x, y,4)
       return move_left
     end
   when 8
     if passable?(x, y,4)
       return move_left
     elsif passable?(x, y,6)
       return move_right
     elsif passable?(x, y,2)
       return move_down
     end
   end
 end
 #--------------------------------------------------------------------------
 # * Move toward target
 #--------------------------------------------------------------------------
 def move_toward(target)
   # Returns if it is targeting itself
   return if target == @event
   # Get difference in target and selfs coordinates
   sx = @x - target.x
   sy = @y - target.y
   # If coordinates are equal
   return @original_type if sx == 0 && sy == 0
   # Get absolute value of difference
   abs_sx = sx.abs
   abs_sy = sy.abs
   # If horizontal and vertical distances are equal
   # Increase one of them randomly by 1
   rand(2) == 0 ? abs_sx += 1 : abs_sy += 1 if abs_sx == abs_sy
   # If horizontal distance is longer
   if abs_sx > abs_sy
     # Move towards player, prioritize left and right directions
     sx > 0 ? move_left : move_right
     sy > 0 ? move_up : move_down if ! moving? && sy != 0
     # If vertical distance is longer
   else
     # Move towards player, prioritize up and down directions
     sy > 0 ? move_up : move_down
     sx > 0 ? move_left : move_right if ! moving? && sx != 0
   end
   return
 end
 #--------------------------------------------------------------------------
 # * Move away from target
 #--------------------------------------------------------------------------
 def move_away
   # Returns if it is targeting itself
   return if @target == @event
   # Get difference in target and selfs coordinates
   sx = @x - @target.x
   sy = @y - @target.y
   if !passable?(@x, @y,@direction)
     check_other(@x, @y,@direction)
   else  
     # If coordinates are equal
     return if sx == 0 && sy == 0
     # Get absolute value of difference
     abs_sx = sx.abs
     abs_sy = sy.abs
     # If horizontal and vertical distances are equal
     # Increase one of them randomly by 1
     rand(2) == 0 ? abs_sx += 1 : abs_sy += 1 if abs_sx == abs_sy
     # If horizontal distance is longer
     if abs_sx > abs_sy
       # Move away from player, prioritize left and right directions
       sx > 0 ? move_right : move_left
       sy > 0 ? move_down : move_up if ! moving? && sy != 0
       # If vertical distance is longer
     else
       # Move away from player, prioritize up and down directions
       sy > 0 ? move_down : move_up
       sx > 0 ? move_right : move_left if ! moving? && sx != 0
     end
   end
   return
 end
 #--------------------------------------------------------------------------
 # * Frame Update
 #--------------------------------------------------------------------------
 def update
   super
   # Automatic event starting determinant
   check_event_trigger_auto
   # If parallel process is valid
   if @interpreter != nil
     # If not running
     unless @interpreter.running?
       # Set up event
       @interpreter.setup(@list, @event.id)
     end
     # Update interpreter
     @interpreter.update
   end
   # If stop count exceeds a certain value (computed from move frequency)
   if @stop_count > (40 - @move_frequency * 2) * (6 - @move_frequency)
     @move_type = case @type
     when 1
       move_type_toward
     when 2
       move_type_away
     when 3
       view_type
     when 4
       passave_type
     end
   end
 end
end

class Control_Sprite_Character
 
 #----------------------------------------------------------------------------
 # character_valid?
 #  Checks if this sprite should be displayed and updated.
 #----------------------------------------------------------------------------
 def character_valid?
   return (@character.is_a?(Game_Event) || @character.is_a?(Ai_Event) ||
       @character.is_a?(Map_Enemy) &&
       @character.precondition || @character.is_a?(Map_Remote) ||
       @character.dropped?) && @character.update? ||
       !@character.is_a?(Map_Enemy) && !@character.is_a?(Map_Remote) &&
       @character.is_a?(Map_Battler)
 end
     
end



Instructions
just name ur event  \p[array] where array is add array of ranges
(example \p[5,7] would be when your distance is 5 from them they chase you and when they get 7 distance from there original location they run back
if you want them to run from you name them \r[range] range is how close you get before they start running from you
/v[range,switch_id] range is the events cant see you switch_id is the id of the switch that turns on when the event
sees you
\c[range,target] event will chase the target when within range
target is either 0 for player or event_id


Compatibility
no compatible issues known


Credits and Thanks


  • Nathmatt
  • Blizzard for his check event name
  • Game_Guy For the events run from you



Author's Notes
no notes
Join Dead Frontier
Sorry, I will no longer be scripting for RMXP. I may or may not give support for my scripts. I don't have the will to script in RGSS anymore.
My script


nathmatt

March 04, 2010, 06:55:54 pm #1 Last Edit: March 05, 2010, 12:42:19 pm by nathmatt
changed update to 1.1 now no need to do anything to the event but name it \p[array] array being the array of ranges you want it to use
update 1.11 small bug fix
update 1.12 added option to turn off the battle call (removed)
update 1.13 added \r[range] to have events run from you
update 1.14 added \v[range,switch] to have events turn on a switch when they see you
Join Dead Frontier
Sorry, I will no longer be scripting for RMXP. I may or may not give support for my scripts. I don't have the will to script in RGSS anymore.
My script


ForeverZer0

Good job. I was doing this through eventing and it was a pain in the ass.
This should make things easier.
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.

nathmatt

May 29, 2010, 09:33:11 pm #3 Last Edit: April 18, 2012, 05:39:37 pm by nathmatt
update 1.15 script has been revamped
update 1.16 fixed how original movement and Ai_movement are processed
Join Dead Frontier
Sorry, I will no longer be scripting for RMXP. I may or may not give support for my scripts. I don't have the will to script in RGSS anymore.
My script