[XP] Heretic's Diagonal Stairs Deluxe

Started by Heretic86, April 29, 2015, 08:05:44 pm

Previous topic - Next topic

Heretic86

April 29, 2015, 08:05:44 pm Last Edit: April 29, 2015, 10:47:15 pm by Heretic86
Diagonal Stairs Deluxe XP
Authors: Heretic
Version: 1.0
Type: Custom Movement System
Key Term: Custom Movement System



Introduction

This script allows you to use Terrain Tags to easily make Characters move diagonally on Stairs.  It is useful with some styles of Tilesets where the stairs require Diagonal movement.  It also allows NPC Events to interact with any and all Stairs as well, which is why this is far superior to any other solutions.


Features


  • Diagonal Movement on Stairs based on Terrain Tags

  • NPCs interact as expected on Stairs

  • Collisions and Triggers work as expect on Stairs (Customizable)

  • NPCs can also interact with Stairs made from Events! (Customizable)




Screenshots

Spoiler: ShowHide



Demo

http://downloads.chaos-project.com/heretic86/MP/DiagonalStairsDeluxeDemo.exe


Script

Place below Modular Passable (Required)
Place below Heretic's Caterpillar (Optional)

Spoiler: ShowHide
#===============================================================================
#
#           HERETIC'S DIAGONAL STAIRS DELUXE [XP]
#           Version 1.0
#           Tuesday, October 21st, 2014
#
#===============================================================================
#
# ---  Requirements and Installation  ---
#
# *** REQUIRES HERETICS MODULAR PASSABLE SCRIPT ***
#
# This script should be placed below Heretics Modular Passable.
# Heretics Modular Passable should be placed just below the real SDK
# It should work fine as long as it is between Modular Passable and Main.
#
# Note: There are two versions of this script.  DELUXE and LITE.  Deluxe
#       is dependant on Modular Passable, may be slightly less compatible
#       but has more features.  The Lite version is NOT Dependant on the
#       Modular Passable script and has no interactive options, but is
#       probably more compatible.
#
# ---  Version History  ---
#
# Version 1.0 - Tuesday, October 21st, 2014 - Initial Release
#
#
# ---  Summary  ---
#
# Definition: NPC - Non Player Character, an Event with a Character Graphic.
#
# This script allows you to use Terrain Tags to easily make Characters move
# diagonally on Stairs.  It is useful with some styles of Tilesets where
# the stairs require Diagonal movement.  It also allows NPC Events to
# interact with any and all Stairs as well, which is why this is
# far superior to any other solutions.
#
# Oh yeah, and I wrote it, thats why!
#
#
# -----  Features  -----
#
# - Diagonal Movement based on Terrain Tags
# - NPCs can interact as expected on Stairs
# - Event Collisions and Triggers work as expected (Customizable)
# - Narrow Stairs work properly
# - NPCs can also interact with Stairs made from Events!(Customizable)
#
#
# ---  Instructions  ---
#
# Just edit your Tileset and flag any Diagonal Stairs with a Terrain Tag.
#
# There are two directions you can go on stairs, up and down.  So there
# are two Terrain Tags for you to use.
#
# Use Terrain Tag 5 (default) for Stairs that go from Lower Right to Upper Left
# Use Terrain Tag 6 (default) for Stairs that go from Lower Left to Upper Right
#
# Note: You MUST use TWO or more Tiles with the same Terrain Tag for the
#       effect to work!
#
# Note: ONLY Left and Right Movement Commands are affected by Stairs!
#
#
# ---  Configurations  ---
#
# You can FORCE any Character to have a specified behavior by using Comments!
#
# The Comment needs to be in the first 10 Lines on EACH PAGE you want to
# force a behavior on.  The 10 Line Limit is controlled in the required
# Modular Passable Script under COMMENT_LIMIT, not in this script.
#
# @>Comment: \some_option
#
# Comment Configurations must use the FIRST LINE of a Comment.  Additional
# lines are not checked.  Each Comment Line and Script Line will count against
# the COMMENT_LIMIT of 10 (by Default).
#
# The PER PAGE feature allows you to specify different behaviors on each
# page of an Event.  It is the same as Page 1 having X graphic and Through, but
# Page 2 has a different graphic and different Through settings.  Trust me, it
# saves you a LOT of work by not requiring everything to be made with scripts.
#
# By default, Events with "Always On Top" or "Through" will not be affected by
# movement on Stair Tiles.  These NPCs can still be "Forced".
#
# \use_stairs - This NPC will ALWAYS use Stairs, regardless of other settings.
# \no_stairs  - This NPC will NEVER use Stairs, regardless of other settings.
# \no_event_tiles - This NPC will NEVER try to move on Events with Tiles.
# \no_event_stairs - This NPC will NEVER try to move on Event Stairs
#
# NOTE: You'll see me use \no_event_tiles in several other scripts.
#
#
# ---  Script Calls  ---
#
# You can FORCE any NPC to have the Behavior you want by using Script Calls
#
# @>Script: $game_map.events[51].use_stairs
# @>Script: $game_player.no_stairs
#
# These can also be called in a Script from Set Move Route
#
# no_event_tiles needs a value to be set: true/false (Events ONLY, no PLAYER)
#
# $game_map.events[5].no_event_tiles = true/false
#
#
# -----  Database Tile Passages  -----
#
# In the Database (F9) under Tilesets, you have a Passage (4 Dir) to allow
# specific movement on a Tile.  In order to allow the Diagonal Movement that
# occurs on Stairs, I check TWO directions, not just one.  For a Stair to
# be considered as Passable for Up Left movement, either Left or Up needs
# to be allowed on your current tile, and either Down or Right needs to
# be allowed on the tile you are trying to move to.  I do not check
# the adjacent tiles because that prohibited any form of Narrow Stairs.
# Adjacent Tile checking means that for normal unmodified Diagonal Movement
# to Up Left, either the whole tile directly to the left or directly up
# from the characters position would have to be passable.  I only check
# for your current and target tiles and ignore adjacents.  Keep this in
# mind when setting up your Passage Settings for your tilesets.
#
#
# -----  Compatability  -----
#
# This script will not be compatible with other scripts that overwrite
# either Game_Map Passable? or Game_Character Passable?  You can try
# to place this script below any that rewrite those methods but compatability
# is not guaranteed for more exotic scripts like Pixel Movement.
#
# Heretic's Caterpillar has been updated to Version 1.99.6 for compatability.
# Heretic's Caterpillar Versions 1.99.5 and below wont be compatible due
# to conflicting definitions.  This is also why the Modular Passable script
# is required.
#
# This script probably wont be compatible with any Pixel Movement Scripts.
#
# Pathfinding over Stair Tiles will most likely be problematic at best.
#
# This script probably wont be needed by any 8 directional movement scripts.
# I have tested 8 dir movement and it does work fine by the way, it is
# just completely superfluous however.
#
#
# -----  Legal  -----
#
# You are hereby granted permission to use and redistribute this script with
# two restrictions:
#
# 1) - You give me credit
# 2) - If distributed in a Demo specific to a Tileset, the Tileset MUST be
#      configured in the Databse.
#
# I feel very strongly about Tilesets that do not have Database Configurations.
#
# You may post this script on any website.
# You may use this script in commercial projects without my explicit permission
# as long as I am credited somewhere in the project.
# You may package this script.
#
# You may NOT sell this script.
# You may NOT claim this script to be your own property.
#
#
# -----  Configuration Options  -----
#
# Terrain Tags  - You'll need TWO different Terrain Tags for your Stairs
#                 Adjust as needed for compatability
#
# Stair Trigger - This Option allows you to define what happens when the
#                 Player presses Enter.  A setting of 0 will only check
#                 for Trigger Events in front of them.  A setting of 1
#                 will only check Diagonals, dependant on which Stair
#                 Tile the Player is standing on.  And lastly, a setting
#                 of 2 will check for both directly in front and diagonally
#                 from the Players position while they stand on Stairs.
#                 If the player is not standing on a Stair Tile, then
#                 only Front Triggering occurs, which is default.
#
#
#===============================================================================

# ---  TERRAIN TAGS OPTIONS  ---

STAIRS_UPLEFT_TAG = 5   # Use for Down Right to Upper Left Stairs
STAIRS_UPRIGHT_TAG = 6  # Use for Down Left to Upper Right Stairs

# ---  TRIGGER BEHAVIOR OPTIONS  ---

STAIR_TRIGGER = 2       # 0 = Normal, 1 = Diagonal Only, 2 = Diag + Normal

# Check for Modular Passable Script
unless $Modular_Passable
 print "Fatal Error: Heretics Diagonal Stairs script\n",
       "requires Heretics Modular Passable Script!\n\n",
       "Modular Passable is Not Available or is below this script.\n",
       "Modular Passable MUST be above this script.\n\n",
       "The Game will now Exit"
 exit
end
#==============================================================================
# ** Game_System
#==============================================================================
class Game_System
 #--------------------------------------------------------------------------
 # * Public Instance Variables - Game_System
 #--------------------------------------------------------------------------
 attr_accessor :event_stairs      # NPCs on Stairs and Events with Stair Tiles
 #--------------------------------------------------------------------------
 # * Object Initialization
 #  - Adds a System Option for using Events with Stair Tiles as Stairs for
 #    both Player and NPC Characters
 #  - With this Option set to TRUE, NPCs can move around on Events with
 #    Terrain Tiles that have Stair Terrain Tags.
 #--------------------------------------------------------------------------
 alias stair_events_initialize initialize unless $@
 def initialize
   # Call Original or other Aliases
   stair_events_initialize
   # New Option for checking Events for Stair Tiles
   @event_stairs = true
 end
end

#==============================================================================
# ** Game_Character
#==============================================================================
class Game_Character
 #--------------------------------------------------------------------------
 # * Object Initialization - Game_Character
 #--------------------------------------------------------------------------
 alias stairs_char_initialize initialize unless $@
 def initialize
   # Call Original or other Aliases
   stairs_char_initialize
   # Add New Properties
   @stairs = nil
   @no_stairs = nil
 end
 #--------------------------------------------------------------------------
 # * Change Stairs Property - Game_Character
 #--------------------------------------------------------------------------
 def use_stairs
   # Just changes to True and False for both properties
   @stairs = true
   @no_stairs = nil
 end
 #--------------------------------------------------------------------------
 # * Change No Stairs Property - Game_Character
 #  NOTE: You may need to call self.no_stairs as running a Script no_stairs=
 #        will create a local variable instance of a method call
 #        if run from Set Move Route -> Script
 #--------------------------------------------------------------------------
 def no_stairs
   # Just changes to True and False for both properties
   @stairs = nil
   @no_stairs = true
 end
 #--------------------------------------------------------------------------
 # * Clear Stairs - Game_Character
 #  - Clears both properties due to setter methods  
 #--------------------------------------------------------------------------
 def clear_stairs
   # Clears both properties to nil
   @stairs = nil
   @no_stairs = nil
 end  
 #--------------------------------------------------------------------------
 # * Stairs? - Game_Character
 #  - Returns Value of Stairs Property
 #  - Useful for scripters to tell if an event is being forced to use stairs
 #--------------------------------------------------------------------------
 def use_stairs?
   return @stairs
 end
 #--------------------------------------------------------------------------
 # * No Stairs? - Game_Character
 #  - Returns Value of No Stairs
 #  - Useful for scripters to tell if an event is prohibited from using stairs
 #--------------------------------------------------------------------------
 def no_stairs?
   return @no_stairs
 end
 #--------------------------------------------------------------------------
 # * Stair Tag Valid? - Game_Character
 #  - Returns true or nil if Terrain Tag matches Config Stair Tags
 #     tag : Terrain Tag
 #--------------------------------------------------------------------------
 def stair_tag_valid?(tag)
   # Valid if one of the two Constants defined in Config
   return true if tag == STAIRS_UPRIGHT_TAG or tag == STAIRS_UPLEFT_TAG
 end
 #--------------------------------------------------------------------------
 # * Stairs Debug - Game_Character
 #  - Returns true if Player in Debug Mode for Passing through everything
 #  - If Player Stairs is Forced, Stair Movement will still occur
 #--------------------------------------------------------------------------
 def stairs_debug?
   # If Player and Debug and CTRL Key and not Stairs Forced
   if self == $game_player and not @stairs and $DEBUG and
      Input.press?(Input::CTRL)
     # Debug Movement for Player is Active
     return true
   end
 end
 #--------------------------------------------------------------------------
 # * Make New XY for Stairs - Game_Character
 #  - Determines new Y Position while on Stairs
 #    d    : direction
 #    tag  : current Stair Tag
 #--------------------------------------------------------------------------
 def make_new_xy_for_stairs(d, tag)
   # Abbreviate Constants
   l = STAIRS_UPLEFT_TAG
   r = STAIRS_UPRIGHT_TAG
   # Determine New Location based on Current Tags
   new_x = @x + (d == 6 ? 1 : d == 4 ? -1 : 0)
   new_y = @y + (((d == 6 and tag == l) or (d == 4 and tag == r)) ? 1 :
                 ((d == 4 and tag == l) or (d == 6 and tag == r)) ? -1 : 0)
   # Return New Coordinates as Array
   return [new_x, new_y]
 end  
 #--------------------------------------------------------------------------
 # * Stair Tags Here - Game_Character
 #     skip_events : true to prevent iterating through events for performance  
 #  - Returns any Terrain Tags from current location
 #  - The skip_events argument is used when Events have been checked before
 #    calling this command which would cause reduncancy in interation
 #  Note: All Event Tiles are checked.  Be sure to use Through on Events
 #        with Tile Graphics if you have any need to Stack Events
 #--------------------------------------------------------------------------
 def stair_tags_here(skip_events = false)
   # If current coordinates given are outside of the map
   unless $game_map.valid?(@x, @y)
    # No Stair Tags on the Outside of a Map
     return 0
   end
   # Get the Terrain Tag from the Map Tiles
   tag = $game_map.terrain_tag(@x, @y)
   # If System Option is Enabled for checking Events with Tile Graphics  
   if $game_system.event_stairs and not skip_events and
      not @no_event_tiles and not @no_event_stairs
     # Get List of Events at Map XY
     event_list = make_events_list(@x, @y)
     # Check Events with Tile Graphics to get Terrain Tags
     for event in event_list
       # If event coordinates are consistent with move destination
       if mp_match_coordinates?(@x, @y, event.x, event.y)        
         # Events with Stair Tiles and Through are ignored
         unless event.tile_id == 0 or event.through
           # Get Terrain Tag from Event Tile
           tag = $game_map.terrain_tags[event.tile_id]
         end
       end
     end
   end
   # Return any Stair Terrain Tags
   return tag
 end
 #--------------------------------------------------------------------------
 # * Stair Tags There - Game_Character
 #     d   : 4 for left and 6 for right ONLY
 #     tag : when Stair Tag Here has already been calculated
 #     - Returns Terrain Tag at New Location if Two Stair Tiles are found
 #     - Returns numeric 0 as a Terrain Tag if not a Stair
 #--------------------------------------------------------------------------
 def stair_tags_there(d, tag = nil)
   # If Direction is not Left or Right
   return 0 unless (d == 4 or d == 6)
   # If current coordinates given are outside of the map
   unless $game_map.valid?(@x, @y)
     # No Stair Tags on the Outside of a Map
     return 0
   end
   # Get any Terrain Tags from current location, including Events with Option
   tag = (tag != nil) ? tag : stair_tags_here
   # Return if no Terrain Tags in either Map or Events
   return 0 unless tag > 0
   # Determine New Location based on Current Tags
   new_x, new_y = make_new_xy_for_stairs(d, tag)
   # If new coordinates given are outside of the map
   unless $game_map.valid?(new_x, new_y)
     # Not a Valid Stair Tag
     return 0
   end
   # Get the Terrain Tag from the Map Tiles
   new_tag = $game_map.terrain_tag(new_x, new_y)    
   # If System Option is Enabled for checking Events with Tile Graphics  
   # and Character is allowed to move on Events with Tiles / Stairs
   if $game_system.event_stairs and
      not @no_event_tiles and not @no_event_stairs
     # Check Events with Tile Graphics to get Terrain Tags
     for event in make_events_list(new_x, new_y)
       # If event coordinates are consistent with move destination
       if mp_match_coordinates?(new_x, new_y, event.x, event.y)
         # Events with Stair Tiles and Through are ignored
         unless event.tile_id == 0 or event.through
           # Get Terrain Tag from Event Tile
           new_tag = $game_map.terrain_tags[event.tile_id]
         end
       end
     end
   end
   # If Matching Stair Terrain Tags
   if tag == new_tag and stair_tag_valid?(tag) and stair_tag_valid?(new_tag)
     # Return the Terrain Tag found for the New Tag at Next Location
     return new_tag
   # When two Matching Terrain Tags not found
   else
     # Return the Default Terrain Tag
     return 0
   end
 end
 #--------------------------------------------------------------------------
 # * Move Left on Stairs - Game_Character
 #     turn_enabled : a flag permits direction change on that spot
 #  - Modifies normal Move Left behavior so that if a character is on
 #    a Stair, it will move up or down those stairs according to the
 #    Terrain Tag of that stair where Default 5 is Up Left, 6 is Down Left
 #--------------------------------------------------------------------------
 alias stairs_move_left move_left unless $@
 def move_left(turn_enabled = true)
   # If Player is pressing CTRL from Editor Game and not Forced Stairs
   debug = stairs_debug?
   # If Stair Movement expected for Character
   if !debug and !@no_stairs and (@stairs or (!@through and !@always_on_top))
     # Get Stair Tag Value
     stair_tag = stair_tags_here()
     # If Stair Tag Valid
     if stair_tag_valid?(stair_tag)
       # Get next Stair Tag Value (4 is Left)
       next_stair_tag = stair_tags_there(4, stair_tag)
       # If Next Stair Tag Valid
       if stair_tag_valid?(next_stair_tag)
         # If Stairs go Up and to the Left
         if stair_tag == STAIRS_UPLEFT_TAG
           # If no direction fix - turn_enabled is ignored
           unless @direction_fix
             # Turn Left
             @direction = 4
           end
           # Check for passability for Up Left (Event Tiles, not All Events)
           return unless ($game_map.passable?(@x, @y, 4) or
                          $game_map.passable?(@x, @y, 8) ) and
                         ($game_map.passable?(@x - 1, @y - 1, 6) or
                         $game_map.passable?(@x - 1, @y - 1, 2) )
           # When stairs tiles are passable up left
           if stair_events_passable?(@x, @y, 6, @x - 1, @y - 1)
             # Update coordinates
             @x -= 1
             @y -= 1
             # Increase steps
             increase_steps
           else
             # Determine if touch event is triggered diagonally
             check_event_trigger_touch(@x - 1, @y - 1)
           end
           # Prevent Moving Twice
           return
         # If Stairs go Down and to the Left
         elsif stair_tag == STAIRS_UPRIGHT_TAG
           # If no direction fix - turn_enabled is ignored
           unless @direction_fix
             # Turn Left
             @direction = 4
           end
           # Check for passability for Down Left (Event Tiles, not All Events)
           return unless ($game_map.passable?(@x, @y, 4) or
                         $game_map.passable?(@x, @y, 2) )and
                         ($game_map.passable?(@x - 1, @y + 1, 6) or
                         $game_map.passable?(@x - 1, @y + 1, 8) )
           # When stairs tiles are passable down left
           if stair_events_passable?(@x, @y, 6, @x - 1, @y + 1)
             # Update coordinates
             @x -= 1
             @y += 1
             # Increase steps
             increase_steps
           else
             # Determine if touch event is triggered diagonally
             check_event_trigger_touch(@x - 1, @y + 1)
           end
           # Prevent Moving Twice
           return
         end
       end
     end
   end
   # Call Original or other Aliases
   stairs_move_left(turn_enabled)
 end
 #--------------------------------------------------------------------------
 # * Move Right on Stairs - Game_Character
 #     turn_enabled : a flag permits direction change on that spot
 #  - Modifies normal Move Right behavior so that if a character is on
 #    a Stair, it will move up or down those stairs according to the
 #    Terrain Tag of that stair where Default 5 is Down Right, 6 is Up Right
 #--------------------------------------------------------------------------
 alias stairs_move_right move_right unless $@
 def move_right(turn_enabled = true)
   # If Player is pressing CTRL from Editor Game and not Forced Stairs
   debug = stairs_debug?
   # If Stair Movement expected for Character
   if !debug and !@no_stairs and (@stairs or (!@through and !@always_on_top))
     # Get Stair Tag Value
     stair_tag = stair_tags_here()
     # If Stair Tag Valid
     if stair_tag_valid?(stair_tag)
       # Get next Stair Tag Value (6 is Right)
       next_stair_tag = stair_tags_there(6, stair_tag)
       # If Next Stair Tag Valid
       if stair_tag_valid?(next_stair_tag)
         # If Stairs go Up and to the Right
         if stair_tag == STAIRS_UPRIGHT_TAG
           # If no direction fix - turn_enabled is ignored
           unless @direction_fix
             # Turn Right
             @direction = 6
           end
           # Check for passability for Up Right (Event Tiles, not All Events)
           return unless ($game_map.passable?(@x, @y, 6) or
                         $game_map.passable?(@x, @y, 8) ) and
                         ($game_map.passable?(@x + 1, @y - 1, 4) or
                         $game_map.passable?(@x + 1, @y - 1, 2) )
           # When stairs tiles are passable up right (right used for passage)
           if stair_events_passable?(@x, @y, 4, @x + 1, @y - 1)
             # Update coordinates
             @x += 1
             @y -= 1
             # Increase steps
             increase_steps
           else
             # Determine if touch event is triggered diagonally
             check_event_trigger_touch(@x + 1, @y - 1)
           end
           # Prevent Moving Twice
           return
         # If Stairs go Down and to the Right
         elsif stair_tag == STAIRS_UPLEFT_TAG
           # If no direction fix - turn_enabled is ignored
           unless @direction_fix
             # Turn Right
             @direction = 6
           end
           # Check for passability if Down Right (Event Tiles, not All Events)
           return unless ($game_map.passable?(@x, @y, 6) or
                         $game_map.passable?(@x, @y, 2) ) and
                         ($game_map.passable?(@x + 1, @y + 1, 4) or
                         $game_map.passable?(@x + 1, @y + 1, 8) )
           # When stairs tiles are passable down right
           if stair_events_passable?(@x, @y, 4, @x + 1, @y + 1)
             # Update coordinates
             @x += 1
             @y += 1
             # Increase steps
             increase_steps
           else
             # Determine if touch event is triggered diagonally
             check_event_trigger_touch(@x + 1, @y + 1)
           end
           # Prevent Moving Twice
           return
         end
       end
     end
   end
   # Call Original or other Aliases
   stairs_move_right(turn_enabled)
 end
 #--------------------------------------------------------------------------
 # * Stair Events Passable? - Game_Character
 #     x     : x-coordinate
 #     y     : y-coordinate
 #     d     : direction (0,2,4,6,8,10)
 #             *  0,10 = determine if all directions are impassable
 #     new_x : target x-coordinate
 #     new_y : target y-coordinate
 #  - Tiles have already been checked so only check for Event Collisions  
 #--------------------------------------------------------------------------
 def stair_events_passable?(x, y, d, new_x, new_y)
   # If through is ON
   if @through
     # passable
     return true
   end
   # Check for any other possible reasons to not allow passage
   unless other_passable?(x, y, d, new_x, new_y)
     # Impassable
     return false
   end
   # Generate a List of Map Events to loop through
   event_list = make_passable_list(x, y, d, new_x, new_y)
   # Loop Events in List (typically ALL Game Map Event Values)
   for event in event_list
     # If event coordinates are consistent with move destination
     if mp_match_coordinates?(new_x, new_y, event.x, event.y)
       # If through is OFF for Event being checked
       unless event.through
         # Impassable - Characters can not move through Events with Characters
         return false if event_character_not_passable?(x,y,d,new_x,new_y,event)
       end
     end
   end
   # If Player is at Location this Event trying to move to
   if mp_match_coordinates?(new_x, new_y, $game_player.x, $game_player.y)
     # If Player is not flagged as Through
     unless $game_player.through
       # Impassable - Event has a Graphic so can NOT move to Players Location
       return false if event_player_not_passable?(x, y, d, new_x, new_y)        
     end
   end
   # passable
   return true
 end
 # If Heretic's Loop Maps is installed
 if Game_Map.method_defined?(:map_loop_passable?)
   #--------------------------------------------------------------------------
   # * Stair Events Passable? - Game_Character
   #  - Alias Patch corrects Looping Map Arguments for this method
   #--------------------------------------------------------------------------
   alias loop_map_stair_events_passable? stair_events_passable? unless $@
   def stair_events_passable?(x, y, d, new_x, new_y)
     # If Map Loops Horizontal
     if $game_map.loop_horizontal?
       # Adjust X Arguments to Map Width
       x %= $game_map.width
       new_x %= $game_map.width
     end
     # if Map Loops Vertical
     if $game_map.loop_vertical?
       # Adjust y Arguments to Map Height
       y %= $game_map.height
       new_y %= $game_map.height
     end
     # Call Original with Adjusted Arguments for Looping Maps
     loop_map_stair_events_passable?(x, y, d, new_x, new_y)
   end
 end # End Loop Map Optional Methods
 #--------------------------------------------------------------------------
 # * Stair Event Not Passable? - Game_Character
 #  - This allows NPCs to move across Events with Stair Terrain Tag Tiles
 #    which is normally not allowed for any Event unless that Event has
 #    a Through flag
 #  - Inverse Logic: -1 * -1 = 1    
 #  - Tile Obstale Bits of the Tile now determines passage
 #     x      : x-coordinate
 #     y      : y-coordinate
 #     d      : direction (0,2,4,6,8)
 #              * 0 = Determines if all directions are impassable (for jumping)
 #     new_x  : Target X Coordinate
 #     new_y  : Target Y Coordinate
 #     event  : Event being checked against in iteration loop
 #     result : results of other Aliases passed as an argument  
 #--------------------------------------------------------------------------
 alias stair_event_not_passable? event_not_passable? unless $@
 def event_not_passable?(x, y, d, new_x, new_y, event, result = nil)
   # If this is an Event and is not the Player
   if self != $game_player
     # Impassable - There is a Flag that prohibits movement on Event Tile
     return true if @no_event_tiles
   end    
   # If target Event has a Tile with a Stair Terain Tag
   if result.nil? and $game_system.event_stairs and event.tile_id > 0 and
      stair_tag_valid?($game_map.terrain_tags[event.tile_id]) and
      @character_name != "" and not @no_event_stairs
     # Change direction (0,2,4,6,8,10) to obstacle bit (0,1,2,4,8,0)
     bit = (1 << (d / 2 - 1)) & 0x0f
     # If obstacle bit is not set
     unless $game_map.passages[tile_id] & bit != 0 or
            $game_map.passages[tile_id] & 0x0f == 0x0f
       # Passable - Conditions Met to allow Passage so false Impassable
       result = false
     end
   end
   # Call Original or other Aliases    
   stair_event_not_passable?(x, y, d, new_x, new_y, event, result)
 end
 # If Heretic's Loop Maps is NOT installed
 unless Game_Map.method_defined?(:map_loop_passable?)
   #--------------------------------------------------------------------------
   # * Character Distance - Game_Character
   #  - Returns the X and Y Distance between self and another Character
   #  - This version does NOT check for Looping Maps because the
   #    script that allows for those features is unavailable here so
   #    this version is only used as a fallback for nonexistent methods
   #      target : Character (Player or Event)
   #--------------------------------------------------------------------------  
   def character_distance(target)
     # Return Difference X and Y values as Array
     return [@x - target.x, @y - target.y]
   end
 end
 #--------------------------------------------------------------------------
 # * Turn Towards Player on Stairs - Game_Character
 #  - Favors Left and Right while on Stairs on Tiles
 #--------------------------------------------------------------------------
 alias stairs_turn_toward_player turn_toward_player unless $@
 def turn_toward_player
   # If Event is on a Stair Tag
   if stair_tag_valid?(stair_tags_here(skip_events = false))
     # Get difference in player coordinates
     sx, sy = character_distance($game_player)
     # If coordinates are equal
     if sx == 0 and sy == 0
       # Dont turn
       return
     end
     # If vertical distance is longer
     if sy.abs > sx.abs
       # Turn up or down towards player
       sy > 0 ? turn_up : turn_down
     # If horizontal distance is longer
     else
       # Turn to the right or left towards player
       sx > 0 ? turn_left : turn_right
     end
     # Prevent turning again
     return
   end
   # Call Original or other Aliases
   stairs_turn_toward_player
 end
 #--------------------------------------------------------------------------
 # * Turn Away From Player on Stairs - Game_Character
 #  - Favors Left and Right while on Stairs on Tiles
 #--------------------------------------------------------------------------
 alias stairs_turn_away_from_player turn_away_from_player unless $@
 def turn_away_from_player
   # If Event is on a Stair Tag
   if stair_tag_valid?(stair_tags_here(skip_events = false))
     # Get difference in player coordinates
     sx, sy = character_distance($game_player)
     # If coordinates are equal
     if sx == 0 and sy == 0
       # Dont turn
       return
     end
     # If vertical distance is longer
     if sy.abs > sx.abs
       # Turn up or down away from player
       sy > 0 ? turn_down : turn_up
     # If horizontal distance is longer
     else
       # Turn to the right or left towards player
       sx > 0 ? turn_right : turn_left
     end
     # Prevent turning again
     return
   end
   # Call Original or other Aliases
   stairs_turn_away_from_player
 end
 #--------------------------------------------------------------------------
 # * Move toward Player - Game_Character
 #  - Favors Left and Right while on Stairs on Tiles  
 #--------------------------------------------------------------------------
 alias stairs_move_toward_player move_toward_player unless $@
 def move_toward_player
   # If Event is on a Stair Tag
   if stair_tag_valid?(stair_tags_here(skip_events = false))    
     # Determine Correct Distance to Target for Looping Maps
     sx, sy = character_distance($game_player)
     # If coordinates are equal
     if sx == 0 and sy == 0
       return
     end
     # Get absolute value of difference
     abs_sx = sx.abs
     abs_sy = sy.abs
     # Favor Horizontal if horizontal and vertical distances are equal
     abs_sx += 1 if abs_sx == abs_sy
     # If horizontal distance is longer
     if abs_sx > abs_sy
       # Move towards player, prioritize left and right directions
       sx > 0 ? move_left : move_right
       if not moving? and sy != 0
         sy > 0 ? move_up : move_down
       end
     # If vertical distance is longer
     else
       # Move towards player, prioritize up and down directions
       sy > 0 ? move_up : move_down
       if not moving? and sx != 0
         sx > 0 ? move_left : move_right
       end
     end
     # Prevent moving again
     return      
   end
   # Call Original or other Aliases
   stairs_move_toward_player
 end
 #--------------------------------------------------------------------------
 # * Move away from Player
 #  - Favors Left and Right while on Stairs on Tiles  
 #--------------------------------------------------------------------------
 alias stairs_move_away_from_player move_away_from_player unless $@
 def move_away_from_player
   # If Event is on a Stair Tag
   if stair_tag_valid?(stair_tags_here(skip_events = false))    
     # Determine Correct Distance to Target for Looping Maps
     sx, sy = character_distance($game_player)
     # If coordinates are equal
     if sx == 0 and sy == 0
       return
     end
     # Get absolute value of difference
     abs_sx = sx.abs
     abs_sy = sy.abs
     # Favor Horizontal if horizontal and vertical distances are equal
     abs_sx += 1 if abs_sx == abs_sy
     # If horizontal distance is longer
     if abs_sx > abs_sy
       # Move away from player, prioritize left and right directions
       sx > 0 ? move_right : move_left
       if not moving? and sy != 0
         sy > 0 ? move_down : move_up
       end
     # If vertical distance is longer
     else
       # Move away from player, prioritize up and down directions
       sy > 0 ? move_down : move_up
       if not moving? and sx != 0
         sx > 0 ? move_right : move_left
       end
     end
     # Prevent moving again
     return        
   end
   # Call Original or other Aliases
   stairs_move_away_from_player      
 end
 # If Turn Toward Event is defined
 if self.method_defined?(:turn_toward_event)
   #------------------------------------------------------------------------
   # * Turn Towards Event on Stairs - Game_Character
   #     id : character ID (0 can be used for Player)
   #  - Favors Left and Right while on Stairs on Tiles
   #  - Method provided by Caterpillar or other Scripts
   #------------------------------------------------------------------------
   alias stairs_turn_toward_event turn_toward_event unless $@
   def turn_toward_event(id)
     # If Character is on a Stair Tag
     if stair_tag_valid?(stair_tags_here(skip_events = false))
       # Get the Target
       target = (id == 0) ? $game_player : $game_map.events[id]
       # Prevent Crash if Target doesnt exist
       if target.nil?
         # This lets the Error Handler in the Aliased Method to work
         stairs_turn_toward_event(id)
         # Prevent other Processing since the game would normally crash here
         return
       end
       # Get difference in target character coordinates
       sx, sy = character_distance(target)
       # If coordinates are equal
       if sx == 0 and sy == 0
         # Dont turn
         return
       end
       # If vertical distance is longer
       if sy.abs > sx.abs
         # Turn up or down towards the target character
         sy > 0 ? turn_up : turn_down
       # If horizontal distance is longer
       else
         # Turn to the right or left towards target character
         sx > 0 ? turn_left : turn_right
       end
       # Prevent turning again
       return        
     end
     # Call Original or other Aliases
     stairs_turn_toward_event(id)
   end
   #------------------------------------------------------------------------
   # * Turn Away From Event on Stairs - Game_Character
   #     id : character ID (0 can be used for Player)
   #  - Favors Left and Right while on Stairs on Tiles
   #  - Method provided by Caterpillar or other Scripts
   #------------------------------------------------------------------------
   alias stairs_turn_away_from_event turn_away_from_event unless $@
   def turn_away_from_event(id)
     # If Character is on a Stair Tag
     if stair_tag_valid?(stair_tags_here(skip_events = false))
       # Get the Target
       target = (id == 0) ? $game_player : $game_map.events[id]
       # Prevent Crash if Target doesnt exist
       if target.nil?
         # This lets the Error Handler in the Aliased Method to work
         stairs_turn_toward_event(id)
         # Prevent other Processing since the game would normally crash here
         return
       end
       # Get difference in target character coordinates
       sx, sy = character_distance(target)
       # If coordinates are equal
       if sx == 0 and sy == 0
         # Dont turn
         return
       end
       # If vertical distance is longer
       if sy.abs > sx.abs
         # Turn up or down away from the target character
         sy > 0 ? turn_down : turn_up
       # If horizontal distance is longer
       else
         # Turn to the right or left away from target character
         sx > 0 ? turn_right : turn_left
       end
       # Prevent turning again
       return        
     end
     # Call Original or other Aliases
     stairs_turn_toward_event(id)
   end    
 end
end

#==============================================================================
# ** Game_Player
#==============================================================================
class Game_Player < Game_Character
 #--------------------------------------------------------------------------
 # * Stair Events Passable? - Game_Player
 #  - Allows for Debug Mode to pass through anything
 #    x     : x-coordinate
 #    y     : y-coordinate
 #    d     : direction (0,2,4,6,8,10)
 #            *  0,10 = determine if all directions are impassable
 #    new_x : target x-coordinate
 #    new_y : target y-coordinate
 #--------------------------------------------------------------------------
 def stair_events_passable?(x, y, d, new_x, new_y)
   # If DEBUG (running game from Editor) and pressing CTRL Key
   return true if $DEBUG and Input.press?(Input::CTRL)
   # Passable / Impassable determined by Parent Method of same name
   return super
 end
 #--------------------------------------------------------------------------
 # * Check Event Trigger There - Game_Player
 #  - Triggers Events while on Stairs so Events can trigger Diagonally
 #  - Front Event Starting Determinant (Default)
 #     triggers : event triggers - Action Button, Event Touch, Player Touch
 #--------------------------------------------------------------------------
 alias stairs_check_event_trigger_there check_event_trigger_there unless $@
 def check_event_trigger_there(triggers)
   result = false
   # If event is running
   if $game_system.map_interpreter.running?
     return result
   end
   # Look for Stair Tags at current location
   tag = stair_tags_here
   # If Left / Right, on Stairs and Options to trigger Diagonal Events
   if tag > 0 and [4, 6].include?(@direction) and STAIR_TRIGGER > 0
     # Shorthand S for Stair Trigger Constant in Config
     s = STAIR_TRIGGER
     # Calculate front event coordinates
     new_x, new_y = make_new_xy(@x, @y, @direction)
     # Get Stair Tags at New Location
     next_tag = stair_tags_there(@direction, tag)
     # Determine Diagonal Coordinates
     new_xd, new_yd = make_new_xy_for_stairs(@direction, tag)
     # Get List of Events at Map XY
     event_list = make_events_list(new_x, new_y)
     # Collision Optimizer causes event_list to already match coordinates
     if s == 2 and $game_map.respond_to?(:get_events_at_xy)
       # Add to very small List of Events if checking Two Locations on Map
       event_list += make_events_list(new_x, new_yd)
     end
     # Loop Events in List
     for e in event_list
       # Default
       next_trigger_check = false
       # If Option Conditions and Coordinates are consistent
       if s == 0 and mp_match_coordinates?(new_x, new_y, e.x, e.y) or
          s == 1 and mp_match_coordinates?(new_x, new_yd, e.x, e.y) or
          s == 2 and (mp_match_coordinates?(new_x, new_y, e.x, e.y) or
                       mp_match_coordinates?(new_x, new_yd, e.x, e.y) )
         # Set Flag to run other checks and start event when Triggerable
         next_trigger_check = true if triggers.include?(e.trigger)
       end
       # If event coordinates and triggers were consistent above
       if next_trigger_check == true
         # If starting determinant is front event (other than jumping)
         if not e.jumping? and not e.over_trigger? and not e.starting
           # Start the Event
           e.start
           # Prevents Counter Triggers
           result = true
         end
       end        
     end
     # If fitting event is not found
     if result == false
       # If front tile is a counter and consistent with Diagonal Options
       if s == 0 and $game_map.counter?(new_x, new_y) or
          s == 1 and $game_map.counter?(new_x, new_yd) or
          s == 2 and ($game_map.counter?(new_x, new_y) or
                      $game_map.counter?(new_x, new_yd) )
         # Calculate 1 tile inside coordinates
         new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
         # Get List of Events at Map XY
         event_list = make_events_list(new_x, new_y)
         # Add to List of Events if checking Two Locations on Map
         event_list += make_events_list(new_x, new_yd) if s == 2
         # Loop Events in List
         for e in event_list
           # If Option Conditions, Coordinates and Triggers are consistent
           if s == 0 and mp_match_coordinates?(new_x, new_y, e.x, e.y) or
              s == 1 and mp_match_coordinates?(new_x, new_yd, e.x, e.y) or
              s == 2 and (mp_match_coordinates?(new_x, new_y, e.x, e.y) or
                           mp_match_coordinates?(new_x, new_yd, e.x, e.y) )
             # If starting determinant is front event (other than jumping)
             if triggers.include?(e.trigger) and
                not e.jumping? and not e.over_trigger? and not e.starting
               # Start Event and set the Result
               e.start
               result = true
             end
           end
         end
       end
     end
     # Prevent Original or other Alias from reprocessing the same work
     return result      
   end
   # Call Original or other Aliases
   stairs_check_event_trigger_there(triggers)
 end
end

#==============================================================================
# ** Game_Event
#==============================================================================
class Game_Event < Game_Character
 #--------------------------------------------------------------------------
 # * Public Instance Variables - Game_Event
 #--------------------------------------------------------------------------
 attr_accessor     :no_event_tiles           # Prevents NPCs on Event Tiles
 attr_accessor     :no_event_stairs          # Prevents NPCs on Event Stairs
 #--------------------------------------------------------------------------
 # * Reset Page Comment Config - Game_Event
 #  - Resets Instance Variables
 #  - @stairs and @no_stairs already have accessor Methods
 #--------------------------------------------------------------------------
 alias stairs_reset_page_comment_config reset_page_comment_config unless $@
 def reset_page_comment_config
   # Reset to set again by Comment Conditions
   @stairs = nil
   @no_stairs = nil
   @no_event_stairs = nil
   @no_event_tiles = nil
   # Runs Original or Other Aliases of this method
   stairs_reset_page_comment_config
 end
 #--------------------------------------------------------------------------
 # * Stairs Check Comment Page Config - Game_Event
 #     comment : the Comment to be checked to adjust Page Properties
 #     count   : integer of which List of Event Commands is checked
 #  - Checks for \use_stairs, \no_stairs, and \no_event_tiles Comments
 #  - Returns count when a Comment is found to prevent checks by other Aliases
 #  Note: When aliasing, please return the value of count, especially
 #        if your script adjusted this value.
 #--------------------------------------------------------------------------
 alias stairs_check_page_comment_config check_page_comment_config unless $@
 def check_page_comment_config(comment, count)
   # Looks for "\use_stairs" in the Comments with Regular Expressions or REGEX
   comment.gsub(/^\\use_stairs\z/i){@stairs = true; @no_stairs = nil;
     return count}
   # Looks for "\no_stairs" in the Comments
   comment.gsub(/^\\no_stairs\z/i){@stairs = nil; @no_stairs = true;
     return count}
   # Looks for "\no_event_tiles" in the Comments (Use on Character Events)
   comment.gsub(/^\\no_event_tiles\z/i){@no_event_tiles = true;
     return count}
   # Looks for "\no_event_tiles" in the Comments (Use on Character Events)
   comment.gsub(/^\\no_event_stairs\z/i){@no_event_stairs = true;
     return count}      
   # Return counter for other Aliases when no Comments are found
   return stairs_check_page_comment_config(comment, count)
 end
end



Instructions

Use a Terrain Tag of 5 for Stairs that go Up and to the Left
Use a Terrain Tag of 6 for Stairs that go Up and to the Right


Compatibility

No known issues at this time.


Credits and Thanks


  • I'd like to thank Hotpockets for being so tasty!




Author's Notes

There are Two Versions of this script: Lite and Deluxe.

This is the Deluxe Version.  It requires Modular Passable to work.  This version will allow you to make Stairs out of Events, and to have NPCs move on Event Stairs with no extra effort.
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)