Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

[XP/VX/ACE] avarisc's Event_Listener Example

Event_Listener API Version: 0.1
By: avarisc

Introduction

Event_Listener API is a tool for script developers.

It provides an extremely simple way to have your script react to triggers in game, without having to alias or any of that hackish stuff. In addition, any script that uses this API as its only point of access to RM will automatically work for any RPG Maker, as the Event_Listener script itself supports any of the current makers (XP, VX, or Ace), and the API itself is the same for all versions.

This is an example of the technique, and is easily expandable.

Features
  • Provides a "maker-independant" way to hook your script to RGSS
  • Extremely easy to use
  • I actually observed the 80 character line limit against my inner grumblings

Screenshots

It does absolutely nothing by itself, and therefore screenshots cannot possibly be relevant.

Demo

There is no use for a demo - this is a plug-n-play script that does nothing by itself.

Script

Code:
#==============================================================================

# ** Event_Listener v0.1                                             by avarisc

#------------------------------------------------------------------------------

#  This script allows easier integration of scripts without endangering the

#  stack limit.  It eliminates the need for aliasing, and provides an API

#  that is independent of RGSS version (so scripts written using this API as

#  an exclusive point of access to RGSS will work across all RPG Maker engines.

#------------------------------------------------------------------------------

# * Usage

#  Using the script was designed to be absolutely simple as it could be.

#  Simple declare a new class, and have it extend Event_Listener.

#

#  E.g. My_Listener < Event_Listener

#

#  That line alone is all you need for your listener to receive events.

#  To react to an event, define a class function with the name of the event

#  you would like to react to.

#

#  E.g. def self.on_new_game        *Note the "self.", as that is required.

#

#  This method in your listener class will be called when the related event

#  has been triggered.  Keep reading for a list of events.

#------------------------------------------------------------------------------

# * Events

#  on_new_game - triggers immediately after a new game has been started

#  on_show_text - triggers before any event uses the "Show Text" command

#==============================================================================

 

class Event_Listener

  

  #--------------------------------------------------------------------------

  # * Class Variables

  #--------------------------------------------------------------------------

  # rgssv - stores the version of RGSS currently in use

  @@rgssv = defined?(Graphics.play_movie)?3:defined?(Graphics.brightness)?2:1

  # decs - populates at first request for descendants of Event_Listener

  @@decs = 0

  

  #--------------------------------------------------------------------------

  # * Accessor Method for RGSS Version

  # Simply returns a number, 1, 2, or 3, that describes the current RGSS #.

  #--------------------------------------------------------------------------

  def self.rgssv

    @@rgssv

  end

  

  #--------------------------------------------------------------------------

  # * Accessor / Auto-populator for Descendant Class List

  # This returns the cached descendant list once its populated.  If it's not

  # populated, this populates it.  It's important that we populate this array

  # on use, rather than on declaration, as descendants have to be defined

  # after this script in the script list (and wouldn't get picked up at the

  # time of this class' definition).

  #--------------------------------------------------------------------------

  def self.descendants

    return @@decs if(@@decs != 0)

    @@decs = []

    ObjectSpace.each_object(::Class) {|klass| @@decs << klass if klass < self }

    @@decs

  end

  

  #--------------------------------------------------------------------------

  # * Send Event

  # Parameter: Descendant function name as string

  # Simply calls class.send(string) to call the event function in descendants

  #--------------------------------------------------------------------------

  def self.send_event(event)

    descendants.each { |c| c.send(event) }

  end

  

  #--------------------------------------------------------------------------

  # * Dummy Event Fallbacks

  # These do nothing at all, and exist so that classes don't have to utilize

  # each and every event - omissions will redirect to the parent class (here)

  #--------------------------------------------------------------------------

  def self.on_new_game() end

  def self.on_show_text() end

  

end

 

#==============================================================================

if(Event_Listener.rgssv == 1) ################## *            RGSS1 EVENT HOOKS

#==============================================================================

 

  class Interpreter

    alias_method(:event_listener_command_101, :command_101)

    def command_101

      Event_Listener.send_event("on_show_text")

      event_listener_command_101

    end

  end

  

#==============================================================================

else ########################################### *     RGSS(2 OR 3) EVENT HOOKS

#==============================================================================

 

  class Game_Interpreter

    alias_method(:event_listener_command_101, :command_101)

    def command_101

      Event_Listener.send_event("on_show_text")

      event_listener_command_101

    end

  end

 

#==============================================================================

end; if(Event_Listener.rgssv == 2) ############# *            RGSS2 EVENT HOOKS

#==============================================================================

 

#==============================================================================

elsif(Event_Listener.rgssv == 3) ############### *            RGSS3 EVENT HOOKS

#==============================================================================

 

#==============================================================================

end ############################################ * ANY RGSS VERSION EVENT HOOKS

#==============================================================================

 

  class Scene_Title

    alias_method(:event_listener_command_new_game, :command_new_game)

    def command_new_game

      event_listener_command_new_game

      Event_Listener.send_event("on_new_game")

    end

  end

Instructions

Using the script was designed to be absolutely simple as it could be. Simply declare a new class, and have it extend Event_Listener.

E.g. My_Listener < Event_Listener

That line alone is all you need for your listener to receive events. To react to an event, define a class function with the name of the event you would like to react to.

E.g. def self.on_new_game *Note the "self.", as that is required.

This method in your listener class will be called when the related event has been triggered.

Events:

on_new_game - triggers immediately after a new game has been started
on_show_text - triggers before any event uses the "Show Text" command

A demo script that utilizes the Event_Listener API:

Code:
class My_Listener < Event_Listener

  def self.on_new_game

    p "on_new_game"

  end

  

  def self.on_show_text

    p "on_show_text"

  end

end
That's actually everything you need - those functions will automatically be called at the appropriate times. Obviously, you could rename My_Listener to anything you like.

FAQ

Q: That's not enough events.
A: And that's not a question. I'll be adding more as time permits, in the mean time, please suggest some events you'd like to use! I take suggestions seriously.

Q: What about modifying or cancelling an event?
A: These are features I am considering, but it would cause compatibility issues between this and other scripts, as I would have to modify a great number of methods throughout RGSS to implement modifiable and cancellable events.

Compatibility

As it stands, this should be pretty much universally compatible. The only part of RGSS it actually touches directly is done via single aliasing, and is already set up to be cross compatible between all RPG Maker versions.

Credits and Thanks

Thanks to the HBGames community for challenging me to become a better developer. (I miss those days. :blush:)

Author's Notes

I know the first version feels a bit premature. I was going to post it in WIP, but it is technically a release. It's documented, functional, and as far as I can tell, bug free. So I decided to promote it to a full release- at version 0.1.
I look forward to growing it, but I'd like some involvement from the community to help shape its future. Let me know what you would like to see.

Change Log

Code:
 

v0.1----------------------

Released

Added on_new_game

Added on_show_text

 

Future Plans

Future plans have been dismissed in favor of my RGSS replacement. This is being left intact as it is a simple demonstration script of how to achieve the core functionality of an event listener.

Terms and Conditions


This work is licensed under a Creative Commons Attribution 3.0 License.
Attribute to: avarisc (http://www.avarisc.com/)
 
So is this string based event system? Send message "This event has fired" and then the manager will inform the receivers of the event?

Sounds like Ruby's internal object messaging system, it would be perfect if we could pull these sorts of event messaging into the Event System and have NPC events, battle events, etc respond to events.

Imagine an event that fires when you use an item on the field, it means we wouldn't have to flip switches with the item we can move the item used detection onto the events and away from the item itself, would be a very handy system.
 
Ruby's Object has a "send" function, which takes a string and calls a function of that name. The actual Event_Listener class caches all child classes, and calls this on each of them when the trigger is called. Also added some self-contained RGSS version checking.

Battle and map events were the main goal of this, personally I was writing a NPC system that lets me keep AI running for events on other maps, and found that such a callback listener would be quite handy. Threw it together for myself, and dropped it here. It's extremely simple to extend, if you can read my code.

Ways to expand it would be by creating objects, i.e. Battle_Start_Event, that have properties such as:
Battle_Start_Event.getReason == Battle_Reasons.RANDOM_ENCOUNTER.

Making the event cancelable (i.e. event.cancel()) would be quite useful as well. And as this doesn't alias per-script (only once for hooks) should allow any number of scripts to coexist peaceably, so long as their functionality doesn't conflict. A hundred listeners would likely suck performance-wise, but at least it could run, unlike a hundred-level alias stack.

Not sure how viable cross-maker support is - would be neat if there were simple mods that would like to do that. But things like Battle Systems, etc, would require their own Scenes, etc.

If I were to inflate this from a simple drop-in script to an alternative Scripts.rxdata, I could do things like implement/dummy out RGSS3 calls if RGSS1 is being used, and provide a few utilities to create entire Scenes/etc in a maker-independant way.

I'm curious to see what kind of interest there is in using it, and to get some feedback on its direction (unintrusive, or more capable).
 
Wow, that's great. This would have come in so useful when I was starting out. I love things like this as they enable newbies to edit stuff without actually touching anything - it's all isolated and safe.
 
Princess Amy":u1bx82ze said:
Wow, that's great. This would have come in so useful when I was starting out. I love things like this as they enable newbies to edit stuff without actually touching anything - it's all isolated and safe.
You've inspired me! :D
I'll add more events tonight.

Edit:
Doing something a bit more intensive, but makes this actually worth using.
 
I've added dozens of message objects, rewritten the event listener into a message system (which actually sends references to objects involved in a message), and is also cancellable. Was quite amusing to cancel the "on_begin" event (making the game immediately close when it starts).

A preview of the new capabilities:
avtR0S1.png


Edit: Major news/update in the works.
 
So, this has expanded massively since the last update- so much so, that its not quite ready for another release yet :\

Basically, RGSS was not designed to be extendable. Ruby was, and Enterbrain left it at that. I've promoted the project to a full redesign of RGSS to be *completely* extendable. Here are the features I already have working:

1.) Service Locator design model
For those of you unfamiliar with this model, basically things like "Game_Variables" are now handled by Provider_Variables, and accessed by the Services class. For example, Services.get_service("Variables").set_variable(1, true) .
The advantage of this is that a script could easily create a class that extends Provider_Variables, and call Services.register_provider("Variables", My_Variable_Provider), causing the engine to use theirs instead, without a single alias or stack size increase. Of course, this is a bit heavy, which is why its only one of several features, and is intended for total-replacement scripts (for example, an ABS registering a replacement Provider_Battle).
This system also leaves vanilla classes intact, should a more savvy user wish to use your battle system "sometimes".
You can also register entirely new providers, should you want to provide a service for other scripts to use. And load order wouldn't matter in this scenario - because it uses a string system, there are no "That class hasn't been loaded yet!"-type errors.

2.) Messages
Previously called Events (hence "Event_Listener") this was renamed to avoid confusion with the in-game Event terminology. The engine sends messages for almost any action, from major to mundane. The "messages" each have their own instanciated class, which gets passed as an argument to your listener class. These "message" classes contain a set of variables that are useful to have access to about that particular event, and makes these variables modifiable. For example, should you listen for "on_party_gold_changed", you would get a Message_PartyGoldChange object as an argument, and could read or write to the message.amount variable, allowing you to do things as simple as multiplying all gold changes by 2, or you could simply call message.cancel which would negate the event from ever happening. The game would continue as though you simply had not ever received any gold.

3.) Script and Game Credits
I've added a "Scene_Credits", accessible from the title menu. This reads the file "Credits.txt" from the project directory, and displays it in a friendly fashion. Then, and this is the fun part, any scripts that utilize this API (properly, at least) may opt to have credits added automatically (listed after the Credits.txt entries). This can be globally disabled in the configuration should someone feel the need to be ungrateful.

4.) No "Main" script
A sweet and simple change - I've removed the need for a "Main" script at the bottom of the list. This should help avoid confusion with where novice users should place scripts - simply put them at the end of the list. Seeing as the entire system is designed to be conflict-free, this should now be relatively hassle free. Even having a dozen battle systems would run error free, though the last in the list would take priority as default.

5.) Better Scene Management
All scenes now inherit from Scene_Base (assuming you are coming from XP, where they didn't). In addition, the scene controller itself is a Service Provider (described above), meaning if you don't like the way they are managed, you can either listen for and modify events, for lightweight control, or register a complete replacement for Provider_Scene should you need total control.

6.) Easy save/load hooking
If your script needs to save/load data on a per-savegame basis, the API does this in a way that prevents as many conflicts as I could manage. I sort the scripts that request save/load data alphabetically by title, and call them in that order. That should prevent errors from simple things like re-ordering. Adding or removing scripts which require save access may still create some conflict, only way I can see to avoid this would be an entirely new save system (which is not off the table).

7.) Maker Independent
As a total replacement for vanilla RGSS, it is designed to be compatible with any RPG Maker currently available. In fact, I've added setting to select DLL version and Editor version seperately, allowing seamless use of the RGSS3 dll with the XP editor, or vice versa. Editor version controls things such as tilemap and character set implementation, while dll version is used to configure things like resolution and framerate.

8.) In-depth Example Script
I've set up an in-depth example script showcasing a nice, clean layout that demonstrates all the features the API has to offer.

I have a few more ideas I intend to implement, and I still have a ton of patching to do to get this releasable. (Not to mention code cleanup and comments). I'd like to get native Mouse and Network support, but I won't promise this yet. (Would avoid the need for multiple/conflicting network/input scripts, as they would simply be a native part of the API.)

I've learned quite a lot about ruby during this time, and I thought I had about reached its limits. So I'm rather excited to share my progress with you, just have to get it presentable first. There is some very cool stuff in here, though. I'm constantly finding ways to make it more efficient, or easier to use.

Oh, and Xilef, I just realized I never directly answered your question. To put it simply, that kind of thing is *exactly* what inspired this project.
 
Good luck, I know many have tried such a thing as a new RGSS but to be honest you're probably the most likely to succeed in it.
 
You're right.

Seeing as the project has diverted drastically from its original purpose, I'm going to leave this release as-is, for anyone that wants to take the model and run with it. I've started a new thread for the development of my RGSS replacement.

The new thread can be found here:
viewtopic.php?f=155&t=78299
(It's in Script Support for now, until it's ready for release.)
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top