Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- =begin
- BEACONS - NPC SPAWNING
- v0.9 by BadMinotaur
- CHANGELOG
- ==========
- 09/26/15 - Release. Also updated terms of use.
- HOW TO INSTALL THIS SCRIPT
- ===========================
- Paste this script in the RPG Maker Script Editor, beneath "Materials" and above
- "Main Process." There are also two configuration options you'll need to set,
- explained below.
- COMPATIBILITY
- ==============
- This script aliases, but does not overwrite, Game_Map::setup and
- Game_Event::initialize. Any other scripts that modify these two methods have
- a chance at being incompatible with them. If this script breaks something in
- one of those scripts, try placing it before those in the Script Editor.
- If this script has a glaring incompatibility with another popular script,
- please let me know in the forums! I'll see what I can do.
- TERMS OF USE
- =============
- You may not redistribute this script without my written permission.
- You may freely use this script in non-commercial projects.
- You must contact me before use in commercial projects. Don't worry, I'm a
- reasonable fellow.
- You may alter and add on to this script for your own projects. You may not
- redistribute this script as an altered script (in other words, making a change
- does not mean you get to ignore the "don't redistribute" clause above).
- However, you may freely create and redistribute any patches to the script, so
- long as said patches don't unnecessarily duplicate any major functionality of
- the script.
- If you use my script, I'd like credit. "BadMinotaur" will do =)
- HOW TO USE BEACONS
- ===================
- First, you need to set up a container map. This is a map that contains all of
- the events you'll want to spawn. First, make a new, empty map, and make a note
- of its number. Don't worry about naming it anything specific -- you can name it
- and really do anything you want with it -- this script only cares about events
- on this map.
- Open up this script in your script editor, and navigate to the MAP_ID and
- MAP_NAME options, and change them to whatever number your map has.
- NOTE: Please read the instructions on how to find the MAP_ID and the MAP_NAME!
- Most importantly, MAP_NAME is *not* the name you give it inside of RPG Maker!
- It is the file name of the map, without the .rvdata2 extension.
- Now we get to actually make the events we will spawn! Make an event like
- normal. When you're satisfied with your event, add a comment at the very top of
- the event. Here, we're going to "tag" the event. Type this:
- <beacontype: my_beacon_name>
- into the comment. This will let the script know that you now have an NPC named
- "my_beacon_name" that you want to spawn later!
- The great thing about this script is that more than one NPC can have the same
- beacontype. If you gave three different NPCs the beacontype "my_beacon_name",
- the script would choose randomly between each one when spawning them. This is
- great for, say, spawning a town of NPCs that change each time the map loads!
- There is one more tag you can put on these events. By default, this script
- assumes each NPC you're spawning is "generic" -- any number of them can spawn.
- You can instead put another tag next to your beacontype:
- <unique>
- This tag tells the script that if the current NPC is already on the map, STOP
- the spawning! This NPC will only be one per map, no matter what. Great for
- story-specific items or NPCs.
- NOTE: This script checks each *current* page for beacontypes! What this means
- is that if you set a variable/switch that makes your NPC change pages, this
- script will look at the new page to see if it has any beacontypes. So if you
- want the NPC to keep its beacontypes (and uniqueness) you'll need to copy and
- paste them onto each relevant page!
- After you've set up whatever NPCs you want, then you can actually spawn them on
- a map! This is also done using event tags. On whatever map you want an NPC to
- spawn, just add a blank, empty event with the following tag (again, in a
- comment at the top of the event):
- <beacon: my_beacon_name>
- Replacing "my_beacon_name" with whatever name you gave your NPC on the
- container map.
- The cool thing about beacons is that the beacon also looks at what page it is
- on to see what beacontype it will spawn. So, for example, if you put a bunch
- of beacons in a town to spawn citizens, then that town gets taken over by
- goblins, you can set a switch and all of the beacons will now spawn goblins!
- There is one last bit of functionality when using beacons. If you'd like a
- beacon to sometimes spawn a "rare" NPC some percent of the time, tag it like
- this:
- <rare: another_beacon_name>
- Then, the spawner will roll a 100-sided die to see if the rare beacon is
- spawned. The default chance for a rare NPC to spawn from a raretype is 25%, but
- you can change this number with the BEACON_RARE_CHANCE option below.
- NOTE: I say to use an empty event for your beacon, but you don't have to. You
- can give it a graphic, and even other event commands, but none of these will
- matter or show up in-game. When the game sees that it is a beacon, it will
- destroy the original event and replace it with the spawned NPC.
- IF YOU'RE A SCRIPTER (or want to use the "Script Call" event command)
- ======================================================================
- This script has a goodie for you too. I've tried to make spawning NPCs
- dynamically, outside of the beacon system, easy to access. Here's the command:
- NPCSpawner.spawn_beacontype(beacon_name, x, y)
- or
- NPCSpawner.spawn_beacontype_with_rare(beacon_name, rare_name, x, y)
- As long as beacon_name and rare_name correspond to a beacontype you've defined
- in your container map, you're clear to go.
- *******!!BIG CAVEAT!!*******
- ==============================
- As of right now, self-switches on NPCs in the container map DO NOT PERSIST. In
- other words, if you spawn an NPC on a map and change its self-switch, the next
- time it would spawn that NPC, all of its self-switches will be set to OFF! I
- plan to introduce this functionality in a later update to the script, but right
- now don't plan on your NPCs keeping self-switches on.
- A QUIRK IN UNIQUE NPCS
- =======================
- The way the script detects for unique NPCs is very simple, but it can cause
- trouble if you reuse a lot of resources. First, it checks the name of the
- graphic file that the unique NPC is using. Then it checks the index of the
- graphic file that the unique NPC is using. If both of these match any other
- event already on the map, it does NOT spawn this event. The script by default
- prints a warning to the console if this happens.
- =end
- module BadMinotaur_Beacons
- #Change these to change what map your beacons spawn from.
- #The map name is usually "MapXXX", where XXX is the map_id with leading
- #zeroes if necessary. I left it open-ended in case any scripts change the
- #naming convention of maps.
- #If you're not sure what number and name the container map is, select the map
- #in the map list and then look at the bottom right corner of your RPG Maker
- #window. It should say something like "051: Map Name." The number (WITHOUT
- #the leading zeroes) is the MAP_ID, and the MAP_NAME option is that number
- #(WITH leading zeroes) after "Map" -- so our example map would have MAP_ID
- #of 51 and a MAP_NAME of "Map051".
- #Please note that MAP_NAME is not the name you give the map inside of RPG
- #Maker! Instead, MAP_NAME is the *file name* of the map, without the .rvdata2
- #extension on it.
- MAP_ID = 2
- MAP_NAME = "Map002"
- #A number, 1-100. Chance of a beacon spawning a rare beacontype.
- BEACON_RARE_CHANCE = 25
- #Tell the script not to print warnings to console when a beacontype isn't
- #supported. Change this to "true" and the script will shut up.
- SHUT_UP_BEACONTYPE = false
- #Tell the script not to print warnings to console when a unique NPC fails to
- #spawn. Change this to "true" and the script will shut up.
- SHUT_UP_UNIQUE = false
- end
- #We've spun this off into its own class to avoid making sweeping, terrible
- #changes to Game_Event for the sake of NPC spawning working correctly.
- class Game_BM_NPC < Game_Event
- #We're resetting the self-switches. This is to prevent any weird occurences cropping up when
- #using generics.
- def change_self_switches
- keys = [[@map_id, @id, 'A'],
- [@map_id, @id, 'B'],
- [@map_id, @id, 'C'],
- [@map_id, @id, 'D']]
- keys.each do |ks|
- $game_self_switches[ks] = false
- end
- end
- #This is for beacons.
- #Specifically, when we change the ID of the event, we need to change a few
- #other things with it too.
- #Returns the new id.
- def id=(value)
- @old_id = @id
- @id = value
- @event.id = value
- change_self_switches
- refresh
- @id
- end
- end
- class Game_Map
- alias :bmb_old_setup :setup
- #Changing the map setup method.
- #We're adding the NPCSpawner method to build the NPC hash, full of possible
- #beacons. Then, we'll set them up and, if any beacons were found, spawn them.
- def setup(map_id)
- bmb_old_setup(map_id)
- NPCSpawner.build_npc_hash
- setup_beacons
- spawn_npcs if @bmb_beacons.length > 0 #If it found any beacons, spawn them.
- end
- #Set up the beacons for spawning.
- #We look at each event, and check to see if it's a beacon. If so, we push it
- #to the beacons array. It'll be spawned in Game_Map::setup by Game_Map::
- #spawn_npcs.
- def setup_beacons
- @bmb_beacons = Array.new
- @events.each_pair do |key, value|
- if value.bmb_beacon?
- @bmb_beacons.push(value) if value.bmb_beacon?
- @events.delete(key) #Delete the beacon to reduce lag. It's useless now
- #anyway.
- end
- end
- end
- def spawn_npcs
- #For each beacon, look at its beacontype and then spawn something from it.
- #If there's no matching beacontype, we warn the user via console, to help
- #rooting out typos.
- @bmb_beacons.each do |n|
- if NPCSpawner.npc_hash[n.bmb_beacon]
- NPCSpawner.spawn(NPCSpawner.get_random_npc_with_rare(n.bmb_beacon, n.bmb_rare), n.x, n.y)
- else
- unless BadMinotaur_Beacons::SHUT_UP_BEACONTYPE
- puts "WARNING: Beacon \"" + n.bmb_beacon.to_s + "\" tried to spawn NPCs, but found no matching beacontypes!"
- puts "ID: " + n.id.to_s + " X: " + n.x.to_s + " Y: " + n.y.to_s
- end
- end
- end
- end
- #We push each event into a queue, so that everything is spawned all nice and orderly.
- #This ensures there are no missing IDs, that all events are accounted for.
- def push_event_into_queue(event)
- #This unsightly block of code is here to fill in missing gaps. If there is
- #a gap in between, for example, event #10 and event #12, this will place
- #the new event as event id #11.
- highest_key = 0
- newnum = 0
- @events.each_key {|key| highest_key = key if highest_key < key}
- highest_key.times do |n|
- newnum = n if !@events[n]
- end
- #Here is where we actually change the id.
- newnum = @events.length+1 if newnum == 0
- event.id = newnum
- @events[newnum] = event
- #We push the sprite, to force it to display.
- SceneManager.scene.push_new_sprite(@events[newnum]) if SceneManager.scene_is?(Scene_Map)
- end
- end
- module NPCSpawner
- #Variables for maps.
- @map_name = BadMinotaur_Beacons::MAP_NAME
- @map_id = BadMinotaur_Beacons::MAP_ID
- #Actually getting the events.
- def self.get_summons
- map = load_data("Data/" + @map_name + ".rvdata2")
- map.events
- end
- def self.npc_hash; @npc_hash; end;
- #We're going to call this at the beginning of every map loading, to get
- #new pages and the suchlike done.
- def self.build_npc_hash
- #The NPC hash. This is blank at startup but is built during Game_Map.new.
- @npc_hash = {}
- #Convert the raw NPCs to Game_Events and then process the event.
- event_hash = {}
- get_summons.each_value do |event|
- e = Game_BM_NPC.new($game_map.map_id, event.clone)
- e.refresh
- if e.bmb_beacontype
- e.bmb_beacontype.split.each do |s|
- s.strip!
- if event_hash[s]
- event_hash[s].push(e.clone)
- else
- event_hash[s] = Array.new
- event_hash[s].push(e.clone)
- end
- end
- end
- end
- @npc_hash = event_hash
- end
- #This is all the stuff we need to actually spawn an NPC.
- #This method clones the npc we're fed, moves it to where it needs to go, and
- #pushes the new event into the game's map's queue.
- def self.spawn(npc, x, y)
- return nil if !npc
- new_npc = npc.clone
- new_npc.moveto(x, y)
- $game_map.push_event_into_queue(new_npc)
- new_npc
- end
- #This is just a call to help out fellow scripters who want to use the code,
- #and make it easier to use this functionality in the "Script Call..." event
- #command.
- def self.spawn_beacontype(beacontype, x, y)
- spawn(get_random_npc(beacontype), x, y)
- end
- #The same as above, but with rare spawning.
- def self.spawn_beacontype_with_rare(beacontype, raretype, x, y)
- spawn(get_random_npc_with_rare(beacontype, raretype), x, y)
- end
- #Check for uniqueness.
- #Returns false if the event shares the graphic name and graphic index with
- #another event on the map. Returns true if it is truly unique.
- def self.is_event_unique?(event)
- return true if event.bmb_generic?
- $game_map.events.each_value do |evt|
- if event.character_name == evt.character_name && event.character_index == evt.character_index
- puts "WARNING: Tried and failed to spawn a <unique> NPC with graphic name \"" + event.character_name +
- "\" and with graphic index " + character_index.to_s + "!" unless BadMinotaur_Beacons::SHUT_UP_UNIQUE
- return false
- end
- end
- return true
- end
- #Get a random NPC of a beacontype.
- #It includes a warning, to help facilitate typo detections.
- def self.get_random_npc(beacontype="citizen")
- if @npc_hash[beacontype]
- new_npc = @npc_hash[beacontype][Random.rand(@npc_hash[beacontype].length)]
- if is_event_unique?(new_npc)
- return new_npc
- else
- return nil
- end
- else
- puts "WARNING: Tried to spawn unsupported beacontype \"" + beacontype.to_s + "\"!"
- return nil
- end
- end
- #Get an NPC with rare support.
- def self.get_random_npc_with_rare(beacontype="citizen", raretype=nil)
- return get_random_npc(beacontype) unless raretype
- r = Random.rand(100)
- case r
- when (BadMinotaur_Beacons::BEACON_RARE_CHANCE+1)..100
- return get_random_npc(beacontype) if beacontype
- return nil
- when 0..BadMinotaur_Beacons::BEACON_RARE_CHANCE
- return get_random_npc(raretype)
- end
- end
- end
- #Allow other classes to refresh the spriteset.
- class Scene_Map
- def refresh_spriteset_characters
- @spriteset.refresh_characters
- end
- def push_new_sprite(event)
- @spriteset.push_new_sprite(event)
- end
- end
- class Spriteset_Map
- def push_new_sprite(event)
- @character_sprites.push(Sprite_Character.new(@viewport1, event))
- end
- end
- class Game_Event
- alias :bmb_old_initialize :initialize
- attr_accessor :bmb_tagline
- attr_accessor :id
- attr_reader :bmb_beacon
- attr_reader :bmb_beacontype
- attr_reader :bmb_rare
- def initialize(map_id, event)
- bmb_old_initialize(map_id, event)
- @bmb_tagline = bmb_check_for_tags
- bmb_process_tags(@bmb_tagline)
- end
- #Process event tags, putting the important ones in variables.
- def bmb_process_tags(tagline)
- @bmb_beacontype = bmb_check_for_def(/<beacontype:([^>]*)>/)
- @bmb_beacon = bmb_check_for_def(/<beacon:\s?(\w*)\s?>/)
- @bmb_unique = bmb_check_for_def(/<(unique)>/)
- @bmb_rare = bmb_check_for_def(/<rare:\s?(\w*)\s?>/)
- end
- def bmb_check_for_tags
- s = ""
- #We're finding the right page to pull stuff from.
- if find_proper_page
- #Iterate through the list
- find_proper_page.list.each do |i|
- #Here we add on to the string 's' if we find a comment.
- s = s + i.parameters[0].to_s if i.code == 108
- end
- #This else is just in case there IS NO proper page.
- else
- #If the pages are empty, just take the first page and scan it for comments and those are our tags.
- if @event.pages.empty?
- @event.pages[0].list.each do |i|
- s = s + i.parameters[0].to_s if i.code == 108
- end
- end
- end
- return s
- end
- #Check for a specific tag.
- def bmb_check_for_def(matchvalue, entry=1)
- match = @bmb_tagline.match(matchvalue)
- return match[entry] if match
- end
- #Expose the status of the event in regards to being a beacon, unique, or generic.
- #All of these are used in the NPCSpawner module, to check a beacon's status.
- def bmb_beacon?; !!@bmb_beacon; end;
- def bmb_unique?; !!@bmb_unique; end;
- def bmb_generic?; !bmb_unique?; end;
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement