Submit Your Article


 
RPG Maker

Welcome Guest ( Log In | Register )


  Games Resources RPG Maker VX RPG Maker XP Scripts Tutorials Downloads

> Event Spawner, Help?
Zinx10
post Feb 13 2013, 11:22 AM
Post #1


Master of Darkness
Group Icon

Group: Revolutionary
Posts: 1,194
Type: Developer
RM Skill: Advanced
Rev Points: 5




Hello everyone. I have Kal's script for event spawning. The problem, though, is that the event spawner will only spawn one event per event spawner. That means I will need to make a new event spawner per event. That will stack up in events. While it isn't necessity, it would surely be helpful! Here is the script:
CODE
#                  EventSpawn 1.1.1
#                    by Kal (github.com/kl)
#
# --- Changelog ----
#
# 1.1.1 - Fix absolute positioning bug.
# 1.1   - Can now spawn using an event id as well as an event name.
# 1.0.2 - Improved error messages.
# 1.0.1 - Fix a bug where <spawn-max> would spawn one less than specified.
# 1.0   - Initial release.
#
# --- How To Use ----
#
# First create an event on the map that will act as the spawner event.
# Now you can add various spawn tags to this event (using the Comment function)
# to let the script know what you want it to do.
#
# The spawn tag comments can be added to any event page.
#
# The following commands are available (note do not put in the [ and ])
#
#   <spawn-event [name of event to spawn]>
#   <spawn-timer [number]>
#   <spawn-pos [position string]>
#   <spawn-type [type name]>
#   <spawn-switch [switch ID]>
#   <spawn-max [number]>
#   <spawn-life [number]>
#   <spawn-on-passable [boolean]>
#
# <spawn-event> takes the name of the event to spawn (the name can be found
# in the upper left corner of the event box)
#
# <spawn-timer> takes the number of frames between each spawn (60 frames = 1 sec)
#
# <spawn-pos> takes a string that defines where to spawn the events. It can take
# either a relative or an absolute position. To specify a relative position,
# use up, down, left and right. Then the event will be spawned relative to
# the spawner on tile in that direction.
# To use an absolute position you can give the map coordinate like this
# x,y for example 13,49.
# You can also set the events to spawn a random distance from the spawner
# event. To do that use the tag like this: <spawn-pos random:[number]>
# The number after the colon is the max distance from the spawner that
# events should be able to spawn. If you omit this number the max distance
# is set to 1.
# If you do not set the spawn-pos, the event will be spawned on the same
# tile as the spawner is on.
#
# <spawn-type> takes the type of spawner to use. Currently this script supports
# the normal type (which you get if you don't use this tag) and the
# "loop" type which will keep track of the events that have been spawned,
# and if any of them dies it will be able to spawn again (if it is using
# the spawn-max tag that is)
#
# <spawn-switch> takes the ID of a switch and the spawner will only spawn if
# this switch is on.
#
# <spawn-max> takes the maximum number of events the spawner should spawn.
# Once this number has been reached the spawner will stop spawning.
#
# <spawn-life> takes the number of frames a spawned event should exist.
# After the time is up, the spawned event will be automatically
# despawned (erased). If you don't set this options the spawned
# events will not be despawned at all.
#
# <spawn-on-passable> takes true or false and determines whether the spawner
# should be able to spawn events on unpassable tiles or not (note if you use
# absolute positioning this attribute does nothing). The default is true.
# So if you set spawn-pos to 'up' and the tile above the spawner event is
# unpassable, the event will spawn on an adjecent passable tile.
# If no adjecent tile is passable, it will spawn on the same tile as
# the spawner event.
#
# All the above tags are optional, except for spawn-event and spawn-timer.
#
# --- Example ---
#
# Here is what an example event comment for a spawner could look like:
#
#   <spawn-event EV004>
#   <spawn-timer 120>
#   <spawn-switch 1>
#   <spawn-pos random:4>
#   <spawn-max 3>
#   <spawn-life 400>
#   <spawn-type loop>
#   <spawn-on-passable false>
#

module Kal
  module EventSpawn
    
    module CONFIG
      
      # You can set this constant to a map id and the script will look
      # for events to spawn on that map. That way you can put all your spawn
      # events on one map, and they don't need to present on the map they
      # are spawned on.
      SPAWN_MAP_ID = 2
    end
    
    # <<< END CONFIGURATION >>>
    
    class SpawnError < StandardError; end
    
    #
    # The Factory module is used to parse the spawn tags and create the
    # spawner events.
    #
    module Factory
      module_function
      
      #
      # Creates and returns a spawner given a Game_Event (which is the event
      # that the spawner will spawn) and an RPG::Event (which is the RPG::Event
      # for the spawner event and it holds things such as event comments).
      #
      # This method is called for every event on the map inside Game_Event's
      # constructor. If the event it is called for is not a spawner event
      # then this method returns nil.
      #
      def make_spawner(game_event, rpg_event)
        
        tags = get_spawn_tags(rpg_event)
        
        return nil if tags.empty?
                
        options = make_options_hash(tags.join)

        check_requirements(options, game_event.id)
              
        select_spawner_object(game_event, options)
      end
      
      # ***** HELPER METHODS *****
      
      #
      # Takes an RPG::Event object and returns an array with all the spawn
      # tag comments (e.g. <spawn-timer 120>) for the event.
      #
      def get_spawn_tags(rpg_event)
        # Returns an array with all comments on all pages.
        comments = rpg_event.pages.each_with_object([]) do |page, array|
          array << page.list.select do |cmd|
            cmd.code == 108 || cmd.code == 408
          end
        end
        
        # Selects the comments that are spawn comments and return them.
        spawns = comments.flatten.select { |c| c.parameters.first =~ /^<spawn-/ }
        spawns.map { |c| c.parameters.first }
      end
      
      #
      # Parses a spawn string (all spawn comments as one string) and returns
      # an options hash.
      #
      def make_options_hash(spawn_string)
        
        timer         = spawn_string[/<spawn-timer\s+(\d+)>/i, 1]
        event_name    = spawn_string[/<spawn-event\s+([\d\w]+)>/i, 1]
        event_id      = spawn_string[/spawn-id\s+(\d+[Xx]?)>/i, 1]
        max           = spawn_string[/<spawn-max\s+(\d+)>/i, 1]
        switch        = spawn_string[/<spawn-switch\s+(\d+)>/i, 1]
        life          = spawn_string[/<spawn-life\s+(\d+)>/i, 1]
        not_passable  = spawn_string[/<spawn-on-passable\s+(false)/i, 1]
        type          = spawn_string[/<spawn-type\s+(\w+)>/, 1]
        pos           = spawn_string[/<spawn-pos\s+([\d\w,:]+?)>/, 1]
        
        options = {}
        options[:timer]       = timer.to_i     if timer
        options[:event_name]  = event_name
        options[:event_id]    = event_id
        options[:switch]      = switch.to_i    if switch
        options[:max]         = max.to_i       if max
        options[:life]        = life.to_i      if life
        options[:passable]    = false          if not_passable
        options[:type]        = type
        
        if match = pos && pos.match(/(\d+),(\d+)/)
          options[:position] = :absolute
          options[:x] = match[1].to_i
          options[:y] = match[2].to_i
        else
          options[:position] = :relative
          case pos
          when "up"
            options[:y_adjust] = -1
          when "down"
            options[:y_adjust] = 1
          when "left"
            options[:x_adjust] = -1
          when "right"
            options[:x_adjust] = 1
          when /random/
            distance = pos[/random:(\d+)/, 1] || 1
            options[:random_pos] = distance.to_i
          end
        end
        
        options
      end
      
      #
      # Checks to see if the user set the <spawn-event>/<spawn-id>
      # and <spawn-timer> tags. If not raise exceptions.
      #
      def check_requirements(options, event_id)
        event_msg = "not set for EVENT:" + "%03d" % event_id
        if !options[:event_name] && !options[:event_id]
          raise ArgumentError.new("<spawn-event> or <spawn-id> #{event_msg}")
        elsif !options[:timer]
          raise ArgumentError.new("<spawn-timer> #{event_msg}")
        end
      end
      
      #
      # Selects the Spawner type to use.
      #
      def select_spawner_object(game_event, options)
        case options[:type]
        when "loop"
          LoopSpawner.new(game_event, options)
        else # when no type was selected
          Spawner.new(game_event, options)
        end
      end
    end
    
    #
    # Spawner is the class that keeps track of what event should be spawned,
    # when it should be spawned and where it should be spawned.
    # Each Game_Event that is a spawner will have it's own reference to a
    # Spawner object.
    #
    class Spawner
      include Kal::EventSpawn::CONFIG

      #
      # Sets initial values. game_event is the event that will be spawned.
      #
      def initialize(game_event, options_hash)
        @game_event = game_event
        @options = {x_adjust: 0, y_adjust: 0, passable: true}  # default options
        @options.merge!(options_hash)
        @timer = @options[:timer]
        @spawn_count = 0
        @spawn_map_events = load_spawn_map_events
      end
      
      #
      # Updates a the spawner event's timer (if it should spawn or not).
      #
      def update
        return if @options[:switch] && !$game_switches[@options[:switch]]
        
        @timer -= 1
        if @timer <= 0
          @should_spawn = true
          @timer = @options[:timer]
        end
      end
      
      #
      # Spawns a new event.
      #
      def spawn
        set_spawn_pos  # set the position the spawned event will have.
        
        if name = @options[:event_name]
          event = event_from_name(name)
        elsif id = @options[:event_id]
          event = event_from_id(id)
        end
        
        cloned_event = clone_event(event)
        cloned_event.life_es_kal = @options[:life]
        cloned_event.moveto(@x, @y)  # move the spawned event to it's position.
        
        # Add the spawned event to the Game_Map and the current Spriteset:
        
        $game_map.events[cloned_event.id] = cloned_event
        
        spriteset = SceneManager.scene.instance_eval { @spriteset }
        spriteset.instance_eval do
          @character_sprites << Sprite_Character.new(@viewport1, cloned_event)
        end

        @spawn_count += 1
        @should_spawn = false
      end
      
      #
      # Checks if it is time for the Spawner to spawn or not.
      #
      def should_spawn?
        max = @options[:max]
        max ? @spawn_count < max && @should_spawn : @should_spawn
      end
      
      
      # ***** HELPER METHODS *****
      private
      
      
      #
      # Sets the position that will be used for the next event that
      # should be spawned.
      #
      def set_spawn_pos
        set_absolute_position if @options[:position] == :absolute
        set_relative_position if @options[:position] == :relative
      end
      
      #
      # Sets an absolute position.
      #
      def set_absolute_position
        set_pos(@options[:x], @options[:y])
      end
      
      #
      # Shortcut method to set the @x and @y position variables.
      #
      def set_pos(x, y)
        @x = x
        @y = y
      end
      
      #
      # Sets a relative position. The position can be relative to the
      # position of the spawner (up, down etc) or a random position.
      #
      def set_relative_position
        event_x = @game_event.x
        event_y = @game_event.y
        
        x = event_x + @options[:x_adjust]
        y = event_y + @options[:y_adjust]
        
        if @options[:random_pos]
          set_random_position(event_x, event_y)
        else
          if @options[:passable] && !passable?(x, y)
            set_passable_position(event_x, event_y)
          else
            set_pos(x, y)
          end
        end
      end
      
      #
      # Sets @x and @y to passable tile in a one-tile distance from x,y.
      # If no passable tile exists, sets @x and @y to x,y.
      #
      def set_passable_position(x, y)
        [[y - 1, x], [y + 1, x], [x - 1, y], [x + 1, y]].each do |coord|
          if passable?(coord[0], coord[1])
            @x = coord[0]
            @y = coord[1]
            return
          end
        end
        @x, @y = x, y
      end
      
      #
      # Sets the position to a random coordinate given a start x
      # and a start y.
      #
      def set_random_position(start_x, start_y)
        
        distance = @options[:random_pos]
        coords = get_possible_coords(start_x, start_y, distance).shuffle
        
        if @options[:passable]
          pass = coords.find { |c| passable?(c[0], c[1]) }
          pass ? set_pos(pass[0], pass[1]) : set_pos(start_x, start_y)
        else
          x, y = coords.first
          set_pos(x, y)
        end
      end
      
      #
      # Returns an array of all the coords within distance tiles
      # of x,y excluding x,y itself.
      #
      def get_possible_coords(start_x, start_y, distance)
        coords = []
        
        left_most   = start_x - distance
        right_most  = start_x + distance
        up_most     = start_y - distance
        down_most   = start_y + distance
        
        (left_most..right_most).each do |x|
          (up_most..down_most).each do |y|
            coords << [x, y] unless x == start_x && y == start_y
          end
        end
        coords
      end
                        
      #
      # Check if a tile is passable on the map.
      #
      def passable?(x, y)
        $game_map.passable?(x, y, 2)
      end
      
      #
      # Does a deep clone of the given Game_Event object and sets
      # it's spawn event IDs.
      #
      def clone_event(event)
        id = generate_valid_id
        cloned_event = Marshal.load(Marshal.dump(event))
        cloned_event.set_ids_es_kal(id)
        cloned_event.set_spawn_name_es_kal(@game_event, id)
        cloned_event
      end
            
      #
      # Finds a Game_Event object given an event name.
      # Searches for events on the current map and the spawn map.
      # Raises an exception if the event could not be found.
      #
      def event_from_name(name)
        events = $game_map.events.values + (@spawn_map_events || [])
        event = events.find { |e| e.event.name == name }
        event || raise(SpawnError, event_not_found_msg(name))
      end
      
      #
      # Returns an error message if an event could not be found.
      # identifier is either the Game_Event's id or name.
      #
      def event_name_not_found_msg(name)
        "Event with name '#{name}' could not be found on the current map " +
        "(ID = #{$game_map.map_id})" +
        (SPAWN_MAP_ID ? ", or on the spawn map (ID = #{SPAWN_MAP_ID})." : ".")
      end
      
      #
      # Finds a Game_Event object given an event id.
      # The id is a number (string) with an optional S attached.
      # The S indicates that the id refers to and event id on the spawn map.
      # Raises an exception if the event could not be found.
      #
      def event_from_id(id)
        if id_num = id[/^(\d+)[Xx]/, 1]
          check_spawn_map_set
          event = @spawn_map_events.find { |e| e.id == id_num.to_i }
          event || raise(SpawnError, "Event with id #{id_num} could not " +
            "be found on the spawn map (ID = #{SPAWN_MAP_ID})")
        else
          event = $game_map.events.values.find { |e| e.id == id.to_i }
          event || raise(SpawnError, "Event with id #{id} could not " +
            "be found on the current map (ID = #{$game_map.map_id})")
        end
      end
      
      def check_spawn_map_set
        SPAWN_MAP_ID || raise(SpawnError, "Tried to spawn with a spawn map " +
          "event ID, but SPAWN_MAP_ID is not set. Set it in the script CONFIG.")
      end

      #
      # Generates a valid id based on the number of events already on the map.
      #
      def generate_valid_id
        $game_map.events.keys.max + 1
      end
            
      #
      # Returns an array with all the Game_Events located on the spawn map.
      #
      def load_spawn_map_events
        id = SPAWN_MAP_ID
        if id.is_a?(Numeric) && id > 0
          begin
            map = load_data(sprintf("Data/Map%03d.rvdata2", id))
          rescue Errno::ENOENT => e
            raise LoadError, "ERROR: Could not find a map with ID #{id}\n" +
                             "(SPAWN_EVENT_ID = #{id})"  
          end
          this_map_id = $game_map.map_id
          map.events.values.map { |event| Game_Event.new(this_map_id, event) }
        end
      end
    end
    
    #
    # LoopSpawner is a spawner that will check if any of the events it
    # spawned are erased, and if so it will decrement the @spawn_count
    # variable for that event.
    #
    class LoopSpawner < Spawner
      
      def update
        super
        check_dead_events
      end
      
      def check_dead_events
        alive = $game_map.events.values.select do |game_event|
          game_event.event.name =~ /spawn:#{@game_event.event.id}/ &&
            !game_event.erased
        end
        @spawn_count = alive.size
      end
    end
    
  end # end EventSpawn
end   # end Kal


class Game_Event
  attr_accessor :spawner_es_kal, :life_es_kal
  attr_reader :event, :erased, :spawned_info_es_kal
  
  #
  # Overrides initialize to create a Spawner instance for this event
  # (or nil if it is not a spawner event).
  #
  alias_method :initialize_es_kal, :initialize
  def initialize(*args)
    initialize_es_kal(*args)
    @spawner_es_kal = Kal::EventSpawn::Factory.make_spawner(self, @event)
  end
  
  #
  # Updates the Spawner and the spawned event's life (if applicable).
  #
  alias_method :update_es_kal, :update
  def update
    update_es_kal
    @spawner_es_kal.update if @spawner_es_kal
    update_life_es_kal
  end
  
  #
  # Checks the spawned event's life and erases it if life becomes zero.
  #
  def update_life_es_kal
    if @life_es_kal
      @life_es_kal -= 1
      erase if @life_es_kal <= 0
    end
  end
    
  #
  # Sets IDs.
  #
  def set_ids_es_kal(id)
    @event.id = id
    @id = id
  end
  
  #
  # Sets the event's name.
  #
  def set_spawn_name_es_kal(game_event, id)
    @event.name = "#{@event.name} spawn:#{game_event.event.id}:#{id}"
  end
end
    
class Game_Map
  attr_reader :map_id
  
  #
  # Gets all Spawner events on the map that should spawn and spawn them.
  #
  alias_method :update_events_es_kal, :update_events
  def update_events
    need_to_spawn = @events.values.select do |event|
      event.spawner_es_kal && event.spawner_es_kal.should_spawn?
    end
    need_to_spawn.each { |event| event.spawner_es_kal.spawn }
    
    update_events_es_kal
  end
end


__________________________
My Games
Phelxyre: Time Unbound (Current Project)
Game Thread
A game where you start in the future, but you go to the past, to make things right, hopefully.

The Hidden World
Game Thread
An Arcade-style game where you must go through various puzzles to see if you go home.

Here are all the things I Support (They Include Links!)




Go to the top of the page
 
+Quote Post
   

Posts in this topic
- Zinx10   Event Spawner   Feb 13 2013, 11:22 AM


Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 

Lo-Fi Version Time is now: 22nd May 2013 - 07:50 AM
RPG RPG Revolution is an Privacy Policy and Legal
eXTReMe Tracker