Chaos Project

RPG Maker => RPG Maker Scripts => RMXP Script Database => Topic started by: Heretic86 on August 09, 2016, 09:03:07 pm

Title: [XP] Heretic's Unlimited Event Page Conditions
Post by: Heretic86 on August 09, 2016, 09:03:07 pm
Heretic's Unlimited Event Page Conditions
Authors: Heretic
Version: 1.0
Type: Event Add-on
Key Term: Misc Add-on



Introduction

Event Page Conditions in RPG Maker XP are quite limited.  They only allow you to pick from 2 Game Swithces, check if a Variable is equal to or greater than a specified value, or a single Self Switch.  This script throws that ALL out the front door and allows you to create Unlimited Page Conditions by using Scripts to be a Condition!  Thus, literally anything that can be evaluated in a Script can be a Page Condition!

Most things can be achieved by using a Parallel Event, then checking a Conditional Branch -> Script, then enabling a Game Switch or some other way to make an Event Page active.  Thats great and all, but this makes things a lot simpler and does not eat up Game Switches or require additional work to make Pages become Active.



Features




Screenshots

No Screenshots

Demo

No demo, yet.


Script

Place above Main and below Scene_Debug / SDK (if used)
Spoiler: ShowHide

#==============================================================================
#
#           HERETIC'S UNLIMITED EVENT PAGE CONDITIONS [XP]
#           Version - 1.0
#           Monday, August 8th, 2016
#
#==============================================================================
#
#  ---  Overview  ---
#
#  This script will allow you to use Scripts as a Page Condition! 
#
#  This completely unlocks the functionality of Page Conditions, which now have
#  absolutely NO LIMITS!  For example, the Default Page Conditions allow
#  you to check a Game Variable, and the Page is only active if that
#  Game Variable is equal to or greater than what ever number you put there.
#  But, you could not check if the Game Variable was equal to or less than!
#  Now you can easily make Game Variable Less Than a Page Condition!
#
#  ---  Installation  ---
#
#  Place above Main and below Scene Debug.  Place below the SDK if used.
#
#  ---  Instructions  ---
#
#  Add a Comment to an Event says "Condition:" followed by a Script Condition.
#
#  Example 1:
#  @>Comment: Condition: $foo == $bar
#
#  Example 2:
#  @>Comment: Condition: $game_player.direction == 2 and $game_switches[12]
#
#  Example 3:
#  @>Comment: Condition: $game_player.moving? and $game_switches[12] and
#           : (not $game_system.map_interpreter.running? or $foo == $bar)
#
#  Example 4: (Not every line needs to be dedicated to evaluation.)
#  @>Comment: Condition: p = $game_player
#           : p.direction != 2
#
#  Example 5: (Comments within Comments work too, note the # Character)
#  @>Comment: Condition: p = $game_player
#           : # If Player is facing Down
#           : p.direction == 2
#
#  ---  Common Events  ----
#
#  This script does NOT affect Common Events, ONLY Map Events.
#
#  ---  Battle Event Pages  ---
#
#  This script does NOT affect Battle Event Page Conditions.  I wrote another
#  script for Battle Event Pages already. 
#
#  That script is called "Unlimited Battle Page Conditions" and is available
#  as one of my many scripts in Heretic's Collection.
#
#  ---  Multi Line Support  ---
#
#  You can extend your scripts to Multiple Lines of Comment Boxes!  The
#  Comment Box should be plenty big enough to hold your text!  Besides
#  that, you're using Script Calls, so if you can't fit your text into
#  one Box, you can write a Method that returns true or false and plug
#  that into your own script!
#
#  You can have Multiple Conditions also, each in their own Comment Box.
#
#
#  ---  Self Switches  ---
#
#  This script doesn't interfere with the use of Self Switches (ABCD).  Feel
#  free to combine both Self Switches and Script Conditions.
#
#
#  ---  Skipping Evaluation  ---
#
#  There may be certain conditions when you do NOT want a certain Page to
#  evaulate its Script Conditions.  For example, running an Event and you
#  don't want the Page to change until it is done doing its thing.  You
#  can turn Script Evaluation ON or OFF by either a Script Call or with
#  a Comment.  It is intended to use BOTH in conjunction with each other.
#  Basically, it gives you more control over when Scripts are evaluated.
#
#  To temporarily prevent a Page from executing its Script Conditions, simply
#  add a Comment to the Event's Page that reads:
#
#  @>Comment: \skip_eval
#
#  This is intended to be turned OFF with a Script Call or Move Route.
#
#  @>Script: $game_map.events[id].skip_eval = true / false
#  @>Set Move Route: This Event
#                  : $>Script: self.skip_eval = false
#                  : $>Script: skip_eval=(false)
#
#  NOTE: The Page MUST BE ACTIVE to change Skip Eval or else you will set
#        the Skip Eval on the WRONG PAGE.
#
#  NOTE: Once Skip Eval has been changed, it will retain the state you set
#        until the Map has been left entirely and transferred back to.  This
#        includes going into Battle Scenes.  The value of Skip Eval will be
#        remembered even after Battles are exited, but RESET if you leave
#        the Map and come back to this Map.
#
#
#  ---  Performance  ---
#
#  Each Script Condition is evaluated once per frame so don't go crazy using
#  too many Script Conditions, or at least make sure they evaluate quickly.
#  Script Conditions all evaluated on each Map.  Thus, if you have many maps
#  but only a few maps that use Script Conditions, don't worry too much.  You
#  may see performance issues if you use lots and lots of Script Conditions
#  on the same map.  This does end up being tons faster than using multiple
#  Parallel Events for very specific purposes however.
#
#
#  ---  Compatability  ---
#
#  This script may have some compatability issues with other scripts that
#  modify the RPG::Event::Page::Conditions class.  It has been tested with
#  More Self-Switches by Gameus / Game Guy and although only briefly tested
#  appears to work just fine.  This script works by replacing attr_accessor
#  methods with my custom definitions.  Instead of returning a property of
#  an object, it now uses self_switch_valid and self_switch_ch as getter
#  methods which consider if Script Conditions are being used.
#
#  It should also work just fine with Optimization Scripts as evaluation of
#  the Script Conditions is handled outside of Event and Game Map update
#  methods entirely.
#
#
#  ---  Legal  ---
#
#  You may distribute this script on websites in the scripts unaltered form.
#
#  You may use this script in Commercial or Non Commercial projects without
#  compensation as long as you give me credit for the use of my scripts.
#
#
#  ---  Config  ---
#
#  This script has no configurable Options.
#
#
#  ---  Version History  ---
#
#  Version 1.0 - Monday, August 8th, 2016
#  - Initial Release
#
#==============================================================================

#==============================================================================
# ** RPG - Main RPG Module
#==============================================================================
module RPG
  #----------------------------------------------------------------------------
  # ** Event - RPG::Event
  #----------------------------------------------------------------------------
  class Event
    #--------------------------------------------------------------------------
    # ** Page - RPG::Event::Page
    #--------------------------------------------------------------------------
    class Page
      #------------------------------------------------------------------------
      # ** Condition - RPG::Event::Page::Condition
      #------------------------------------------------------------------------
      class Condition
        #----------------------------------------------------------------------
        # * Public Instance Variables - RPG::Event::Page::Condition
        #----------------------------------------------------------------------
        attr_accessor :script_condition
        attr_accessor :map_id
        attr_accessor :event_id
        attr_accessor :skip_eval
        #----------------------------------------------------------------------
        # * Initialize - RPG::Event::Page::Condition
        #  - Adds Default Values for new Properties
        #----------------------------------------------------------------------
        alias script_condition_initialize initialize unless $@
        def initialize
          # Call Original or other Aliases
          script_condition_initialize
          # Container for Script Conditions
          @script_condition = nil
          # New Properties used for generating a Self Switches Hash Key
          @map_id = nil
          @event_id = nil
        end
        #----------------------------------------------------------------------
        # * Self Switch Valid - RPG::Event::Page::Condition
        #  - Replaces attr_reader with reader method
        #----------------------------------------------------------------------
        def self_switch_valid
          # Overrides @self_switch_valid value if a Script Condition exists
          return true if not @script_condition.nil?
          # Return Original Value (Self Switch is Checked in Edit Event)
          @self_switch_valid
        end
        #----------------------------------------------------------------------
        # * Self Switch Ch - RPG::Event::Page::Condition
        #  - Replaces attr_reader with reader method
        #  - Self Switch Channel is usually 'A' unless 'BCD' are specified
        #  - Checks both Self Switch and Script Condition to return Channel
        #----------------------------------------------------------------------
        def self_switch_ch
          # Use Self Switch Ch (ABCD) if Valid and Switch is OFF
          if @self_switch_valid
            # Hash Key for Self Switches
            key = [@map_id, @event_id, @self_switch_ch]
            # Use Self Switch Ch (ABCD) if Valid and Switch is OFF
            return @self_switch_ch if not $game_self_switches[key]
          end
          # If Script Condition Array has any Scripts
          if not @script_condition.nil? and @script_condition.size > 0
            # Iterate each Self Switch Script Condition
            for script in @script_condition
              # Create a Hash Key for Self Switches Hash with Script
              ss_key = [@map_id, @event_id, script]
              # Return the Script if Self Switch Script is OFF
              return script if not $game_self_switches[ss_key]
            end
            # Return Last Script in Array as Self Switch Channel
            return @script_condition.last
          end
          # Default Self Switch Channel (ABCD)
          @self_switch_ch
        end
      end
    end
  end
end

#==============================================================================
# ** Game_Event
#==============================================================================
class Game_Event < Game_Character
  #----------------------------------------------------------------------------
  # * Initialize - Game_Event
  #  - Checks each Page of Event for Script Conditions in Comments
  #        map_id : Map ID
  #         event : Game_Event stored in $data
  #----------------------------------------------------------------------------
  alias script_condition_initialize initialize unless $@
  def initialize(map_id, event)
    # Default value and Shorthand
    script, sc = nil, $game_map.script_conditions
    # Iterate each of the Event's Pages
    event.pages.each {|page|
      # Store values in Condition Object for Self Switch Keys
      page.condition.map_id, page.condition.event_id = map_id, event.id
      # Iterate Each Command
      page.list.each {|command|
        # If Event Command is the first line of a Comment
        if command.code == 108
          # If Script is not nil (two Script Conditions in sequence)
          if not script.nil?
            # Modify Condition Object and Add to Hash for Evaluation
            add_script_condition(page.condition, script, map_id, event.id, sc)
            # Clear the String since it is now stored in the Condition Object
            script = nil
          end
          # Scan the Comment and look for \skip_eval Comments for this Page
          command.parameters[0].gsub(/^\\skip_eval/i) {
            page.condition.skip_eval = true
          }
          # Use Regular Expression to look for "Condition:" and Capture String
          command.parameters[0].gsub(/^Condition:(.*)/){ script = $1.strip }
        # Else if Script and Event Command is a Comment, but not the first line
        elsif not script.nil? and command.code == 408
          # Append Script String with any text from this line of the Comment
          script += "\n" + command.parameters[0]
        # If Script String has contents
        elsif not script.nil?
          # Modify Condition Object and Add to Hash for Evaluation
          add_script_condition(page.condition, script, map_id, event.id, sc)
          # Clear the String since it is now stored in the Condition Object
          script = nil
          # Next Command (One Condition per Comment Box)
          next
        end
      }
    }
    # Call Original or other Aliases
    script_condition_initialize(map_id, event)
  end
  #----------------------------------------------------------------------------
  # * Add Script Condition - Game_Event
  #  - Modifies the Condition Object with New Parameters for Script Evaluation
  #               c : RPG::Event::Page::Condition
  #          map_id : Map ID of Event
  #        event_id : Event ID
  #              sc : Shorthand for $game_map.script_conditions
  #----------------------------------------------------------------------------
  def add_script_condition(c, script, map_id, event_id, sc)
    # Loop to check for Syntax Errors
    begin
      # Evaluate the Script String and remember result
      result = eval(script)
    # Script Evaluated has a Syntax Error
    rescue
      # Display Warning to User
      script_condition_error(script, event_id, map_id)
      # Prevent Storing of Script Condition
      return
    end
    # Create a Boolean Value based on result for proper storage
    value = (result) ? true : false
    # Remember value of Script Evaluation in the Self Switch Hash
    $game_self_switches[[map_id, event_id, script]] = value   
    # Create Array if Nil
    c.script_condition = [] if c.script_condition.nil?
    # Push Script on to Script Condition Array
    c.script_condition << script
    # Add Script to Script Conditions Hash in Game Map with Event ID as Key
    sc[event_id] ? sc[event_id] << script : sc[event_id] = [script]
  end
  #----------------------------------------------------------------------------
  # * Eval Script Condition - Game_Event
  #  - Evaluates Script Conditions
  #       scripts : Array of Scripts for this Event's Conditions
  #----------------------------------------------------------------------------
  def eval_script_condition(scripts)
    # Default
    refresh_event = false
    # Iterate each Script in the Array
    for script in scripts
      # Run the Script and remember the result (Syntax already checked)
      result = eval(script)
      # Create a Boolean Value based on result for proper storage
      value = (result) ? true : false
      # If Stored Self Switch Value is not the same as the result
      if $game_self_switches[[$game_map.map_id, id, script]] != value
        # Store the Result in the Self Switches Hash
        $game_self_switches[[$game_map.map_id, id, script]] = value
        # Flag to Refresh this Event after running all Scripts if more than 1
        refresh_event = true
      end
    end
    # Refresh this Event if the Refresh Flag is set
    refresh if refresh_event
  end
  #----------------------------------------------------------------------------
  # * Script Condition Error - Game_Event
  #  - Prints a Warning and shows Script with a Syntax Error
  #        script : String of Script with Syntax Error
  #            id : Event ID with the Problem
  #        map_id : ID of Map with Event with Syntax Error in Condition
  #----------------------------------------------------------------------------
  def script_condition_error(script, id, map_id)
    # If running game from Editor
    if $DEBUG
      # Explain the Problem
      print "Warning: A Script Condition has a Syntax Error\n",
            "Event ID: ", id, "\nMap ID: ", map_id, "\n\n",
            "Condition: ", script, "\n\n",
            "Conditions should evaluate to true or false\n",
            "Example 1: Condition: $foo == $bar\n",
            "Example 2: Condition: $foo == $bar or $baz"
    end
  end
  #----------------------------------------------------------------------------
  # * Erased? - Game_Event
  #  - Returns true if Event is Erased
  #----------------------------------------------------------------------------
  def erased?
    return @erased
  end
  #----------------------------------------------------------------------------
  # * Skip Eval? - Game_Event
  #  - Getter method, returns Flag set in RPG::Event::Page::Condition
  #----------------------------------------------------------------------------
  def skip_eval?
    @page.condition.skip_eval if @page
  end
  #----------------------------------------------------------------------------
  # * Skip Eval= - Game_Event
  #  - Setter method, sets Flag set in RPG::Event::Page::Condition
  #----------------------------------------------------------------------------
  def skip_eval=(arg)
    @page.condition.skip_eval = (arg) ? true : false
  end
end

#==============================================================================
# ** Game_Map
#==============================================================================
class Game_Map
  #----------------------------------------------------------------------------
  # * Public Instance Variables - Game_Map
  #----------------------------------------------------------------------------
  attr_accessor     :script_conditions    # Holds Event Script Page Conditions
  #----------------------------------------------------------------------------
  # * Setup - Game_Event
  #  - Creates a Hash to hold Script Conditions for constant Evaluation
  #      map_id : Map ID from $data
  #----------------------------------------------------------------------------
  alias script_condition_setup setup unless $@
  def setup(map_id)
    # If Map ID is Valid and does not match current Map ID
    if map_id > 0 and map_id != @map_id
      # Create or Clear Hash
      @script_conditions = {}
    end
    # Call Original or other Aliases
    script_condition_setup(map_id)
  end
  #----------------------------------------------------------------------------
  # * Eval Script Conditions - Game_Map
  #  - Evaluates all Script Conditions in Hash and Refreshes Event if needed
  #----------------------------------------------------------------------------
  def eval_script_conditions
    # Check that Script Conditions exists
    return unless @script_conditions
    # Iterate any Script Condition
    @script_conditions.each {|id, scripts|
      # Get the Event
      event = @events[id]
      # If Event does not exist (typically event removed during game editing)
      if not event
        # Remove the Script Conditions from the Hash
        @script_conditions.delete(id)
        # Next Script
        next
      end
      # Skip this Event if Event has a Skip Eval flag
      next if event.skip_eval?
      # Evaluate Script Conditions (Array of Scripts) from within the Event
      event.eval_script_condition(scripts) unless event.erased?
    }
  end
end

#==============================================================================
# ** Game_Screen
#==============================================================================
class Game_Screen
  #----------------------------------------------------------------------------
  # * Update - Game_Screen
  #  - Calls to eval_script_conditions in the Map, so it will still evaluate
  #    even during Battle Scenes
  #  - Game_Screen is updated after important objects are updated for proper
  #    timing of changing Pages.  Thus, the Map, Events, Interpreter, Player
  #    and System are all updated before evaulating Script Conditions.  If
  #    the evaluations were run, for example, prior to updating either the
  #    Player or Map Interpreter, Page Changes would occur at improper times
  #    and most likely cause you a headache trying to figure out why thinggs
  #    did not work as you expect at the times you expect.
  #----------------------------------------------------------------------------
  alias script_condition_update update unless $@
  def update
    # Call Original or other Aliases
    script_condition_update
    # Update any Event Script Conditions
    $game_map.eval_script_conditions
  end
end



Instructions

To add a Page Condition for an Event, simply put in a Comment followed by your Script Condition:

@>Comment: Condition: $game_player.direction == 2

To override Script Evaluation temporarily, add a \skip_eval Comment to that Event Page, which can be turned off later with a Script Call.  This is useful for Events whose Pages you don't want to change until the Event has finished doing what ever its doing.

**See Documentation for full explanation**



Compatibility

Seems to be pretty compatible with most scripts.  I tested it briefly with More Self-Switches and it seems to work just fine.  I did replace two Properties in RPG::Event::Page::Condition Objects with reader methods, which may cause some compatibility issues with other scripts.  Since this script appears to be almost unique, I doubt it will cause too many issues.



Credits and Thanks




Author's Notes

This is more of an Event Add-on, as it enhances the functionality of Events, but might also qualify as an Environment Add-on.

Anything that can be scripted can be evaluated.  That being said, the evaluations may not fit in the Comment Box.  This can be easily resolved with custom Methods.  If you need help with creating a Custom Condition, feel free to ask any Scripter.

NOTE: All Evaluations should evaluate to true / false.  Don't forget to leave off the word "if" in your evaluations.  Use "$foo == $bar" instead of "if $foo == $bar".
Title: Re: [XP] Heretic's Unlimited Event Page Conditions
Post by: LiTTleDRAgo on August 21, 2016, 01:39:26 am
kinda similar to mine (http://forum.chaos-project.com/index.php/topic,14538.0.html) but I think yours was more compatible friendly since I more focused to cross engine :P
Title: Re: [XP] Heretic's Unlimited Event Page Conditions
Post by: Heretic86 on August 21, 2016, 04:00:30 am
They may both have their places.  I avoided any dependencies, but yours is also multi platform.