#===============================================================================
#
# HERETIC'S DIAGONAL STAIRS LITE
# Version 1.0
# Thursday, March 26th, 2015
#
#===============================================================================
#
# --- Requirements and Installation ---
#
# This script should be placed between Scene_Debug and Main.
# * Place it below the SDK if you use it.
#
# Note: There are two versions of this script, Deluxe and Lite.
# - Deluxe has more features but less compatability
# - Lite does not allow for Events as Stairs, but is more compatible
# - Use the Deluxe Version if you use Modular Passable
#
# --- Version History ---
#
# Version 1.0 - September 27th, 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 map 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 will also move Diagonally on Stairs
# - NPCs can interact as expected on Stairs
# - Event Collisions and Triggers work as expected (Customizable)
# - Narrow Stairs work properly
#
#
# --- 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 STAIRS_LITE_COMMENT_LIMIT of 10 (by Default). The Comments do NOT need
# to be on the very first line, however, the Config itself must be on the
# first line of the Comment Box with no additional characters like spaces.
#
# 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.
#
#
# --- 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
#
#
# ----- 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 should be highly compatible with other scripts, except for truly
# exotic scripts, like Pixel Movement. If you have a script that redefines
# either move_left or move_right, then place this script below it. It can
# be placed above any other script up to either Scene_Debug or the SDK.
#
# It will probably work just fine with Pathfinding scripts, but pathfinding
# itself will be probelmatic at best over stairs.
#
#
# ----- 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 = 1 # 0 = Normal, 1 = Diagonal Only, 2 = Diag + Normal
# --- COMMENT SCANNING OPTIONS
STAIRS_LITE_COMMENT_LIMIT = 10 # Number of Lines to check for Comment Config
#==============================================================================
# ** Game_Character
#==============================================================================
class Game_Character
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
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
#--------------------------------------------------------------------------
def use_stairs
# Just changes to True and False for both properties
@stairs = true
@no_stairs = nil
end
#--------------------------------------------------------------------------
# * Change No Stairs Property
#--------------------------------------------------------------------------
def no_stairs
# Just changes to True and False for both properties
@stairs = nil
@no_stairs = true
end
#--------------------------------------------------------------------------
# * Clear Stairs
# - Clears both properties due to setter methods
#--------------------------------------------------------------------------
def clear_stairs
# Clears both properties to nil
@stairs = nil
@no_stairs = nil
end
#--------------------------------------------------------------------------
# * Stairs? - Returns Value of Stairs Property
# - Useful for scripters to tell if a Character has forced stair effects
#--------------------------------------------------------------------------
def use_stairs?
return @stairs
end
#--------------------------------------------------------------------------
# * No Stairs? - Returns Value of No Stairs
# - Useful for scripters to tell if a Character is prohibited stair effects
#--------------------------------------------------------------------------
def no_stairs?
return @no_stairs
end
#--------------------------------------------------------------------------
# * Stair Tag Valid?
# - 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
# - Returns true if Player in Debug Mode for Passing through everything
#--------------------------------------------------------------------------
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
#--------------------------------------------------------------------------
# * Stair Tags Here
# 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)
# Return any Stair Terrain Tags
return tag
end
#--------------------------------------------------------------------------
# * Stair Tags There
# 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
# 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)
# 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 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
# 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 Tiles for passability for Up Left
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, check Events Collision
if stairs_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 Tiles for passability for Down Left
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, check Events Collision
if stairs_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
# 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 Tiles for passability for Up Right
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, check Events Collision
if stairs_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 Tiles for passability if Down Right
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, check Events Collision
if stairs_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
#--------------------------------------------------------------------------
# * Stairs Passable?
# 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
# - New X and Y are the only things skipped in this redef of event passable
#--------------------------------------------------------------------------
def stairs_passable?(x, y, d, new_x, new_y)
# If through is ON
if @through
# passable
return true
end
# Loop all events
for event in $game_map.events.values
# If event coordinates are consistent with move destination
if event.x == new_x and event.y == new_y
# If through is OFF
unless event.through
# If self is event
if self != $game_player
# impassable
return false
end
# With self as the player and partner graphic as character
if event.character_name != ""
# impassable
return false
end
end
end
end
# If player coordinates are consistent with move destination
if $game_player.x == new_x and $game_player.y == new_y
# If through is OFF
unless $game_player.through
# If your own graphic is the character
if @character_name != ""
# impassable
return false
end
end
end
# passable
return true
end
#--------------------------------------------------------------------------
# * Turn Towards Player on Stairs
# - Favors Left and Right while on Stairs on Tiles, not Up and Down
#--------------------------------------------------------------------------
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 = @x - $game_player.x
sy = @y - $game_player.y
# If coordinates are equal
if sx == 0 and sy == 0
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
# If Turn Toward Event is defined (Not Defined in this Script)
if self.method_defined?(:turn_toward_event)
#------------------------------------------------------------------------
# * Turn Towards Event on Stairs
# 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 = @x - target.x
sy = @y - target.y
# 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
end
end
#==============================================================================
# ** Game_Player
#==============================================================================
class Game_Player < Game_Character
#--------------------------------------------------------------------------
# * Check Event Trigger There
# triggers : event triggers - Action Button, Event Touch, Player Touch
# - Triggers Events with appropriate conditions
# - Front Envent Starting Determinant
# - Rewritten to be compatible with Stairs so Events trigger diagonally
#--------------------------------------------------------------------------
def check_event_trigger_there(triggers)
result = false
# If event is running
if $game_system.map_interpreter.running?
return result
end
# Calculate front event coordinates
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
# If Trigger Behavior is to check for Diagonal Events
if STAIR_TRIGGER > 0
# New Y with Diagonal Adjustment
new_yd = new_y
# Look for Stair Tags
t = stair_tags_here
# Abbreviate Constants
l = STAIRS_UPLEFT_TAG
r = STAIRS_UPRIGHT_TAG
# Check for Stair Diagonals Left
if @direction == 4
# Get Stair Tags at New Location
nt = stair_tags_there(4, t)
# Determine Next Coordinate for Trigger
new_yd += ((t == r and nt == r) ? 1 : (t == l and nt == l) ? -1 : 0)
# Check for Stair Diagonals Right
elsif @direction == 6
# Get Stair Tags at New Location
nt = stair_tags_there(6, t)
# Determine Next Coordinate for Trigger
new_yd += ((t == l and nt == l) ? 1 : (t == r and nt == r) ? -1 : 0)
end
end
# All event loops
for event in $game_map.events.values
# Default
next_trigger_check = false
# If Action Button Trigger on Stairs checks Both Forward and Diagonal
if STAIR_TRIGGER == 2 and event.x == new_x and
(event.y == new_y or event.y == new_yd) and
triggers.include?(event.trigger)
# Set Flag to run other checks and start event
next_trigger_check = true
# If Action Button Trigger on Stairs checks only Diagonals
elsif STAIR_TRIGGER == 1 and event.x == new_x and
event.y == new_yd and triggers.include?(event.trigger)
# Set Flag to run other checks and start event
next_trigger_check = true
# Default Action Button Trigger Check (Front Only)
elsif STAIR_TRIGGER == 0 and event.x == new_x and event.y == new_y and
triggers.include?(event.trigger)
# Set Flag to run other checks and start event
next_trigger_check = true
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 event.jumping? and not event.over_trigger? and
not event.starting
# Start the Event
event.start
# Prevents Counter Triggers
result = true
end
end
end
# If fitting event is not found
if result == false
# If front tile is a counter
if $game_map.counter?(new_x, new_y)
# Calculate 1 tile inside coordinates
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
if event.x == new_x and event.y == new_y and
triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
if not event.jumping? and not event.over_trigger?
event.start
result = true
end
end
end
end
end
return result
end
end
#==============================================================================
# ** Game_Event
#==============================================================================
class Game_Event < Game_Character
#--------------------------------------------------------------------------
# * Refresh
# - Checks for \use_stairs and \no_stairs Comments on Event Pages
# Note: Config Comments are ONLY checked on a Page Change or Initialize
#--------------------------------------------------------------------------
alias stairs_event_refresh refresh unless $@
def refresh
# Initial State of current @page before Refresh
page = @page
# Call Original or Other Aliases of Refresh
stairs_event_refresh
# If Event Not Erased and Page has changed
unless @erased or @page == page
# Reset to set again by Comment Conditions (@option = nil)
@stairs = nil
@no_stairs = nil
# For Performance on checking each Command in Page List when Refreshed
count = 0
# For each Event Command as 'command' in Page List of Event Commands
@page.list.each {|command|
# If Command Code is a Comment (Code 108, 408 is Next Line Code)
if command.code == 108
# Get the Comment from the Command Parameters
comment = command.parameters[0]
# Looks for "\use_stairs" in the Comments with Regular Expressens
comment.gsub(/^\\use_stairs\z/i){@stairs = true; @no_stairs = nil}
# Looks for "\no_stairs" in the Comments
comment.gsub(/^\\no_stairs\z/i){@stairs = nil; @no_stairs = true}
end
# Increment Counter
count += 1
# Stop Iterating after Limit reached
break if count > STAIRS_LITE_COMMENT_LIMIT
} # End |command| loop (Event Command List)
end
end
end