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