[XP] Heretic's Mirror Movement (for Puzzles)

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

Previous topic - Next topic

Heretic86

April 29, 2015, 08:05:00 pm Last Edit: April 29, 2015, 10:48:27 pm by Heretic86
Mirror Movement XP
Authors: Heretic
Version: 1.0
Type: Custom Movement System
Key Term: Custom Movement System



Introduction

This script will allow you to easily set up Events to Mirror movements of the Player simply by adding a few comments and without complex eventing.

Mirrors that both try to move to the same location will prevent both from being able to move.  Mirrors are able to both move the same direction without interference from each other.  This is VERY useful for puzzles!


Features


  • Add Comments to create a Mirror Event with no Eventing!

  • Allows for Vertical, Horizontal, Opposite and Clone Movement

  • Highly useful for creating Puzzles

  • Superior to Event Solutions because Mirror Events match Perfectly

  • Mirror Events do not move when the Player can not move

  • Mirror Events will not try to both move to the same location at a distance

  • Mirror Events can be next to each other and both mirror movement

  • Behavior of Mirrors is exactly predictable, necessary for puzzles

  • Mirrors can be Limited where they reflect Movement

  • Use mirror_at?(id, x, y) or mirrors_at?([ids],[xs],[ys]) for Puzzle Solution Detection

  • I fell off a cliff one time and died to death,b ut I got better




Screenshots

No screenshots


Demo

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


Script

Place below Modular Passable (Required)
Place below Collision Optimizer (Optional)
Place below Loop Maps (Optional)

Spoiler: ShowHide
#==============================================================================
#
#           HERETIC'S MIRROR MOVEMENT [XP]
#           Version 1.0
#           Thursday, April 9th, 2015
#
#==============================================================================
#
#  -----  IMPORTANT  -----
#
#  REQUIRES HERETIC' MODULAR PASSABLE SCRIPT
#  REQUIRES HERETIC' MODULAR PASSABLE SCRIPT
#  REQUIRES HERETIC' MODULAR PASSABLE SCRIPT
#
#  Place this script below Modular Passable
#
#
#  -----  OVERVIEW  -----
#
#  This script will allow you to easily set up Events to Mirror movements of
#  the Player simply by adding a few comments and without complex eventing.
#
#  Mirrors that both try to move to the same location will prevent both from
#  being able to move.  Mirrors are able to both move the same direction
#  without interference from each other.  This is VERY useful for puzzles!
#
#
#  -----  FEATURES  -----
#
#  - Add Comments to create a Mirror Event with no eventing!
#  - Allows for Vertical, Horizontal, Opposite and Clone Movement
#  - Useful for creating Puzzles
#  - Superior to Event Solutions because Mirror Events match Perfectly!
#  - Mirror Events do not move when the Player can not move
#  - Mirror Events will not try to both move to same location at a distance
#  - Mirror Events can be next to each other and both mirror movement
#  - Behavior should be exactly predictable, which is needed for Puzzles
#  - I fell off a cliff and died to death one time, but I got better
#
#
#  -----  INSTRUCTIONS  -----
#
#  Default Values for Events are set by putting a \comment[values] Comment on
#  each Page of an Event.  Values can be changed later with Script Calls.
#
#  For installation, place this script above MAIN, below Modular Passable if
#  it is also used, and below any other script that replaces update in either
#  the Game_Character or Sprite_Character classes, or refresh of Game_Event.
#  Place below Modular Passable (required), Collision Optimizer (optional)
#  and below Loop Maps (also optional).
#
#  Place the following Comments within the first 10 lines on each Page of
#  each Event you want to behave as a Mirror.  If you need more Lines to add
#  additional Comment Configurations, add a \comment_limit[new_limit] to
#  that page of your Event.
#
#  This script is not designed to work with 8 Directional Movement or
#  with Looping Maps.
#
#
#  -----  COMMENT CONFIGURATION OPTIONS  -----
#
#  Apply each of the following Comments to an Event for corresponding Effects:
#
#  \mirror[N]             : 0 Vertical, 1 Horizontal, 2 Both, 3 Match
#  \mirror_enable         : Enable this Mirror, Mirros NOT enabled by Default
#  \mirror_limit_x[N,N]   : Minimum and Maximum Map X to constrain movement
#  \mirror_limit_y[N,N]   : Minimum and Maximum Map Y to constrain movement
#  \mirror_player_x[N,N]  : Player Map X Limits, won't mirror if player outside
#  \mirror_player_y[N,N]  : Player Map Y Limits, won't mirror if player outside
#  \mirror_anime          : Mirrors Players Animations
#
#  NOTE: Mirrors are NOT Enabled by default without a Comment!
#
#  Enable a Mirror Event with a Script Call:
#  Set Move Route -> Script: @mirror_enable = true / false
#  $game_map.events[event_id].mirror_enable = true / false
#
#  The Limiters for both the Mirror and Player are not required, but may be
#  very useful if you only want an Event to behave as a Mirror within the
#  range you specify.  Without these Limiters, a Mirror Event will try to
#  move exactly as the Player moves about.  These Limiters are very useful
#  if you are attempting to create a Puzzle based on Mirroring movements.
#
#  Example:
#
#  @>Comment: \mirror[0]
#  @>Comment: \mirror_enable
#  @>Comment: \mirror_limit_x[3, 15]
#  @>Comment: \mirror_limit_y[10, 16]
#  @>Comment: \mirror_player_x[3, 15]
#  @>Comment: \mirror_player_y[17, 23]
#  @>Comment: \mirror_anime
#  @>Comment: \comment_limit[20]
#  @>Comment: \other_script_configs
#
#
#  -----  SCRIPT CALLS  -----
#
#  You can change any of the above properties with Script Calls.  I try to use
#  the exact same property name as comment value whenever possible.  Thus, if
#  an option for a Comment property for \grill_cheese, the Script call will
#  use the property name of @grill_cheese, usually.
#
#  There are only a few script calls you may want access to:
#
#  - $game_system.mirror = true / false
#    : Enable or Disable ALL Mirrors
#  - $game_map.events[id].mirror_enable = true or false
#    : Enable or Disable THIS Mirror
#
#  You may also want to check the location of a Mirror (or any event) by using
#  a simplfied call "mirror_at?(map_id, x, y)" for checking coordinates.
#
#  @>Conditional Branch: Script: mirror_at?(12, 6, 10, mirror[optinal]) == true
#  @>(your stuff here)
#  @>End Branch
#
#  - mirror_at?(map_id, x, y, mirror = false)
#
#  Set the fourth argument to true to check if Event is a Mirror and Enabled.
#
#  mirror_at?(40, 12, 14, true) => false, unless Event 40 is a Mirror
#  mirror_at?(40, 12, 14)       => true, regardless if Event 40 is a Mirror
#
#  ** Some types of Puzzles may require Permutations.  These can be done
#     by calling mirrors_at? which expects ARRAYS, not individual coordinates.
#     (see mirrors_at? in Puzzles section)
#
#
#  -----  PAGE OPTIONS  -----
#
#  These Comments are applied Per Event Page!  This allows you to have
#  different configurations on each Event Page!
#
#  All Page Values will be cleared when an Event changes to another valid Page.
#
#  NOTE:  If you make a change to a Mirror Event during gameplay, then save
#         your game, exit, and Edit anything, those changes will not be
#         retained if they are overridden by Comment Configurations.  This
#         is not a bug, it is inherit to the way RPG Maker works.  If you load
#         a save game after quitting without editing, those properties will
#         still be there, but if you Edit then restart, all stuff in the game
#         that can be "refreshed" will be.  Switches will be retained, but
#         changes to Event properties will be lost, depending on what is
#         changed.
#
#
#  -----  Puzzles  -----
#
#  To use this script for making Puzzles, such as where two Mirror Events need
#  to be moved to specific locations, just use a Parallel Event to check the
#  Real X and Real Y coordinates of your Mirror Events!  Using the Real X and Y
#  is needed because just X and Y will occur with movement, so you'll solve
#  puzzles prematurely.  Real X and Real Y are just X and Y times 128.  Thus
#  an X of 2 is 256 and 3 is 384.
#
#  Puzzle Example:
#
#  @>Conditional Branch: Script:
#  $game_map.events[12].real_x == 256 and $game_map.events[12].real_y == 384
#
#  In the Conditional Branch -> Script, you can fit a LOT of text in one line
#  so the above example should all go in one line!
#
#  The EASY way to do all of that is just run "mirror_at?(id, x, y)" and use
#  normal coordinates.  The command "mirror_at?(" also expects events to
#  not be moving or jumping.  An optional 4th argument of "true" will require
#  the Event is a Mirror and has Mirror Enabled.
#
#  @>Conditional Branch: Script: mirror_at?(25, 5, 5) == true
#  @>(Do stuff here)
#  @>End Branch
#
#  You can also use the mirrors_at?() command to check if a set of Mirrors is
#  at any of the coordinate sets!  The first three arguments must be Arrays!
#
#  - mirrors_at?([id],[x],[y], mirror = false)
#
#  The [X] and [Y] Coordinates MUST be the same size, and MUST correspond
#  to the location of the other!
#
#  Example: You have two mirrors that you want to be either of one of two
#           locations, at x10, y15 or x16, y15, the way you would need to
#           write the script call is as follows:
#
#  @>Conditional Branch: Script: mirrors_at?([4,5],[10,16],[15,15])
#  @>(Do stuff here)
#  @>End Branch
#
#  The result will return true when all the mirrors are at either of the
#  location sets provided.  So Event 4 can be at either x10, y15 or x15, y15.
#
#  NOTE: Although you must provide the same number of x coordinates as y, the
#        number of Event IDs does not need to be the same.  If you use a single
#        Event ID, be sure to encapsulate it into an array with [id].
#  
#
#
#
#  -----  Legal  -----
#
#  You are hereby allowed to copy and distribute this Script without my
#  express permission.  You may modify this script as needed.  
#
#  You may not sell this script.  You may not claim this script to be
#  your property in any way shape or form.  If you use this script in
#  any Commercial products, you are required to give me credit for the
#  use of this script, but do not need to pay me or contact me as
#  permission to use this script in commercial products is hereby
#  granted.
#
#  -----  KNOWN ISSUES  -----
#
#  There are no known issues at this time.
#
#  This script most likely will not work with pixel movement scripts.
#
#  -----  CONFIG  -----
#
#  This script has no configurable options.

# Check for Modular Passable Script - REQUIRED - DO NOT EDIT
unless $Modular_Passable
 print "Fatal Error: Heretics Mirror Movement 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 :mirror                  # If All Mirror Events are to Update
 #--------------------------------------------------------------------------
 # * Game System Object Initialization
 #--------------------------------------------------------------------------
 alias mirror_movement_initialize initialize unless $@
 def initialize
   # Call Original or other Aliases
   mirror_movement_initialize
   # Enable Updating Mirror Events
   @mirror = true
 end
end

#==============================================================================
# ** Game_Map
#==============================================================================
class Game_Map
 #--------------------------------------------------------------------------
 # * Setup - Game_Map
 #     map_id : map ID
 #--------------------------------------------------------------------------
 alias mirror_movement_map_setup setup unless $@
 def setup(map_id)
   # Create or Empty Mirror Events Array, Events added when Refreshed
   @mirror_events = []
   # Call Original or other Aliases
   mirror_movement_map_setup(map_id)
 end
 #--------------------------------------------------------------------------
 # * Mirror Events = (Reader Method) - Game_Map
 #--------------------------------------------------------------------------
 def mirror_events
   # Return existing Array or Create it
   @mirror_events ||= []
 end
 #--------------------------------------------------------------------------
 # * Mirror Events = (Writer Method) - Game_Map
 #--------------------------------------------------------------------------
 def mirror_events=
   # Return existing Array or Create it
   @mirror_events ||= []
 end
end

#==============================================================================
# ** Game_Character
#==============================================================================
class Game_Character
 #--------------------------------------------------------------------------
 # * Get Pattern - Game_Character
 #--------------------------------------------------------------------------
 def get_pattern
   return @pattern
 end
 #--------------------------------------------------------------------------
 # * Set Pattern - Game_Character
 #--------------------------------------------------------------------------
 def set_pattern=(arg)
   @pattern = arg
 end
 #--------------------------------------------------------------------------
 # * Unlock - Game_Character
 #  - Called at the end of Event Execution
 #--------------------------------------------------------------------------
 alias mirror_movement_unlock unlock unless $@
 def unlock
   # Store Temporary Direction
   d = @direction
   # Call Original or other Aliases
   mirror_movement_unlock
   # Check for Mirror
   if @mirror and @mirror_enable and mirror_in_limit?(@x,@y)
     # Reset Direction to Stored Mirror Direction
     @direction = d unless @direction_fix
   end
 end
end

#==============================================================================
# ** Game_Player
#==============================================================================
class Game_Player < Game_Character
 #--------------------------------------------------------------------------
 # * Update - Game_Player
 #  - Alias of Main Player Update Method with New Methods called
 #--------------------------------------------------------------------------
 alias mirror_movement_player_update update unless $@
 def update
   # Store current Moving
   last_moving = moving?
   # If Move Route Forcing
   if @move_route_forcing and $game_system.mirror
     # Recheck Last Moving by Real Coordinates
     last_moving = (@real_x % 128 != 0 or @real_y % 128 != 0)
   end
   # Call Original or other Aliases
   mirror_movement_player_update
   # Update all of the Mirror Events in Game Map if Moving
   update_mirrors(last_moving)
 end
 #--------------------------------------------------------------------------
 # * Update Mirrors - Game_Player
 #  - Updates all Mirror Events
 #--------------------------------------------------------------------------
 def update_mirrors(last_moving)
   # If System has Mirror Enabled
   if $game_system.mirror
     # Return if Debug Key is Pressed in the Editor
     return if $DEBUG and Input.press?(Input::CTRL)      
     # Check if Player is Moving
     moving = (@x * 128 != @real_x or @y * 128 != @real_y) ? true : false
     # Determine Direction
     d = (@move_route_forcing) ? @direction : Input.dir4
     # Whether to Sort Array (Default)
     sort = false
     # If Mirror Movement Update will occur and needs to be Sorted
     if moving and not last_moving and $game_map.mirror_events.size > 1
       # Create Array of Arrays with Mirrors sorted by Location Values
       mirrors = arrange_mirrors_by_loc(d)
       # Sort is Allowed
       sort = true
     else
       # Just use the Game Map Mirrors Unsorted
       mirrors = [$game_map.mirror_events]
     end
     # For each set of Mirrors in Array sorted by Location in Mirrors Array
     for m_a in mirrors
       # Sort this set of Mirrors with Matching Coordinates by Alt Coordinates
       m_a.sort!{|a,b| mirror_sort(a,b, d)} if sort and m_a.size > 1
       # Player called Updates to each of the Mirror Events in the Array
       for event in m_a
         # Update Mirror Directions based on Player Direction
         event.update_mirror_direction(@direction)
         # Update Mirror Moving Check
         event.update_mirror_moving_check
         # Update Event Mirror Movement ONLY if Player is moving
         event.update_mirror_movement if moving == true and not last_moving
         # Update Pattern in Mirrors
         event.mirror_player_anime
       end
     end
   end
 end
 #--------------------------------------------------------------------------
 # * Arrange Mirrors By Loc - Game_Player
 #  - Returns an Array of Arrays where Mirrors with matching Locational
 #    values are placed into their own Array for later subsorting
 #       dir : Input 4 Directional
 #--------------------------------------------------------------------------
 def arrange_mirrors_by_loc(dir)
   # Sort by X Coordinate if Input Direction is Vertical
   $game_map.mirror_events.sort!{|a,b| a.x <=> b.x} if [2,8].include?(dir)
   # Sort by Y Coordinate if Input Direction is Horizontal
   $game_map.mirror_events.sort!{|a,b| a.y <=> b.y} if [4,6].include?(dir)
   # New Mirror Array to hold Arrays of Mirrors by Location
   mirrors = []
   # All Mirror Events
   for e in $game_map.mirror_events
     # If Mirror is to be Moved and Enabled
     if e.mirror_enable and e.mirror_in_limit?(e.x, e.y) and e.mirror_player?
       # If Input is Vertical
       if dir == 2 or dir == 8
         # Add this Event or Create Array for Events at this X Coordinate
         mirrors[e.x].is_a?(Array) ? mirrors[e.x] << e : mirrors[e.x] = [e]
       # If Input is Horzontal
       elsif dir == 4 or dir == 6
         # Add this Event or Create Array for Events at this Y Coordinate
         mirrors[e.y].is_a?(Array) ? mirrors[e.y] << e : mirrors[e.y] = [e]
       end
     else
       # Update Mirror Moving Check since it will be skipped
       e.update_mirror_moving_check if e.mirror_enable
     end
   end
   # Return Arrays with nil removed or Empty Array when compacted to nil
   return mirrors.compact! || []
 end
 #--------------------------------------------------------------------------
 # * Mirror Sort - Game_Player
 #  - Considers Input, Mirror and Coordinates to sort Mirror Events Array
 #  - When moving, sorts Events backwards of direction of movement to
 #    prevent collisions between Mirrors
 #  - Opposing Mirrors given Sort Preference, then Logical Coordinates
 #  - This may need to be updated if used with 8 Directional Movement
 #      a : first event to compare
 #      b : second event to compare
 #      d : direction
 #  - <=> returns 1 if a > b, 0 if a == b, -1 if a < b for Array sort method
 #--------------------------------------------------------------------------
 def mirror_sort(a, b, d)
   # Shorthand - Mirror Values and Logical Coordinates
   am, bm, ax, bx, ay, by = a.mirror, b.mirror, a.x, b.x, a.y, b.y
   # Compare X and Y values depending on Direction and Mirror Values
   if d == 2    # Down
     return ([0,2].include?(am) or [0,2].include?(bm)) ? ay <=> by : by <=> ay
   elsif d == 8 # Up
     return ([0,2].include?(am) or [0,2].include?(bm)) ? by <=> ay : ay <=> by
   elsif d == 4 # Left
     return ([1,2].include?(am) or [1,2].include?(bm)) ? bx <=> ax : ax <=> bx
   elsif d == 6 # Right
     return ([1,2].include?(am) or [1,2].include?(bm)) ? ax <=> bx : bx <=> ax
   end
   # Default (0 means the two objects compared are equal)
   return 0
 end
 #--------------------------------------------------------------------------
 # * Get Players Mirror Movement Speed - Game_Player
 #  - Just returns Move Speed of Player
 #--------------------------------------------------------------------------
 def get_player_mirror_move_speed
   # Two to the Power of the Player's Movement Speed
   return 2 ** @move_speed
 end
end

#==============================================================================
# ** Game_Event
#==============================================================================
class Game_Event < Game_Character
 #--------------------------------------------------------------------------
 # * Public Instance Variables - Game_Character
 #--------------------------------------------------------------------------
 attr_accessor  :mirror           # 0 Vertical, 1 Horiz, 2 Both, 3 Match
 attr_accessor  :mirror_enable    # Default Enabled
 attr_accessor  :mirror_limit_x   # Map X Values to Allow Movement
 attr_accessor  :mirror_limit_y   # Map X Values to Allow Movement
 attr_accessor  :mirror_player_x  # Player X Range to Copy
 attr_accessor  :mirror_player_y  # Player Y Range to Copy
 attr_accessor  :mirror_anime     # Mirrors Player's Pattern
 attr_reader    :mirror_moving    # True when Movement is Mirror Movement
 #--------------------------------------------------------------------------
 # * Make Mirror Direction
 #  - Inverts Direction based on Mirror Type
 #     d : direction [2,4,6, or 8]
 #--------------------------------------------------------------------------
 def make_mirror_direction(d)
   # If not a Mirror
   return d if @mirror.nil?
   # Four Directional Movement and Mirror not set to Match Players moves
   if @mirror < 3
     # Branch by Mirror Type
     case @mirror
     when 0 # Vertical Mirror
       d = 10 - d if [2,8].include?(d)
     when 1 # Horizontal Mirror
       d = 10 - d if [4,6].include?(d)
     when 2 # Mirror Both (Opposite)
       d = 10 - d
     end
   end
   # Return the Adjusted Mirrored Direction (\mirror[4] is unadjusted)
   return d
 end
 #--------------------------------------------------------------------------
 # * Update Mirror Moving Check - Game_Event
 #--------------------------------------------------------------------------
 def update_mirror_moving_check
   # If a Mirror Moving Flag is set and Movement is not occuring
   if @mirror_moving and @x * 128 == @real_x and @y * 128 == @real_y
     # Clear Mirror Moving Flag      
     @mirror_moving = nil
   end
 end
 #--------------------------------------------------------------------------
 # * Mirror Player? - Game_Event
 #  - Returns True or False if Player is within Range
 #  - Returns True if no Limit on the Player has been set with Comments
 #  - Checked by Comments \mirror_player_x/y[min_x/y, max_x/y]
 #--------------------------------------------------------------------------
 def mirror_player?
   # Shorthand
   x, y = $game_player.real_x, $game_player.real_y
   # Check X Range
   if not @mirror_player_x.nil?
     # Player X out of Mirror Range
     return false if @mirror_player_x[0] and x < @mirror_player_x[0] * 128
     return false if @mirror_player_x[1] and x > @mirror_player_x[1] * 128
   end
   # Check if Player Y within Mirror Range
   if not @mirror_player_y.nil?
     # Player X out of Mirror Range
     return false if @mirror_player_y[0] and y < @mirror_player_y[0] * 128
     return false if @mirror_player_y[1] and y > @mirror_player_y[1] * 128
   end
   # Within Range
   return true
 end
 #--------------------------------------------------------------------------
 # * Mirror Limit? - Game_Event
 #  - Returns True or False if Movement is outside of Limited Range
 #  - Returns True if no Limit on Mirror Movement has been set with Comments
 #  - Checked by Comments \mirror_limit_x/y[min_x/y, max_x/y]
 #--------------------------------------------------------------------------
 def mirror_in_limit?(x, y)
   # If a Mirror Limit X is set
   if not @mirror_limit_x.nil?
     # False if New Y Coordinate is outside Mirror Move Range
     return false if @mirror_limit_x[0] and x < @mirror_limit_x[0]
     return false if @mirror_limit_x[1] and x > @mirror_limit_x[1]
   end
   # If a Mirror Limit Y is set
   if not @mirror_limit_y.nil?
     # False if New Y Coordinate is outside Mirror Move Range
     return false if @mirror_limit_y[0] and y < @mirror_limit_y[0]
     return false if @mirror_limit_y[1] and y > @mirror_limit_y[1]
   end
   # Within Range
   return true
 end
 #--------------------------------------------------------------------------
 # * Mirror Can Move? - Game_Event
 #  - Checks for Collisions with other Mirrors to same location
 #--------------------------------------------------------------------------
 def mirror_can_move?(x, y, d)
   # If only one Mirror Event
   return true if $game_map.mirror_events.size == 1
   # For each Mirror Event
   for e in $game_map.mirror_events
     # Check if Event is checking itself or same Mirror Type
     next if not e.mirror_enable or e.id == @id or e.mirror == @mirror
     # If Mirror Movement is not occuring and Player is in range to Mirror
     if not e.mirror_moving and e.mirror_player?
       # Mirror's Next Direction
       md = e.make_mirror_direction(d)
       # Mirror's Next Logical Coordinates
       mx, my = make_new_xy_8d(e.x, e.y, md)
       # Prevent Two Mirrors from both moving to the same Logical Coordinates
       return false if e.mirror_in_limit?(mx, my) and x == mx and y == my
     end
   end
   # Mirror Movement is allowed
   return true
 end
 #--------------------------------------------------------------------------
 # * Update Mirror Movement - Game_Event
 #  - Handles Mirror Movements, not Direction
 #--------------------------------------------------------------------------
 def update_mirror_movement
   # Check if Mirror Movement is allowed
   return if not @mirror_enable or @mirror_moving or jumping?
   # Check if Player within Mirror Range
   return unless mirror_player?
   # Return if Event is running
   return if @move_route_forcing
   # Determine Mirror Movement by Input
   d = ($game_player.move_route_forcing) ? $game_player.direction : Input.dir4
   # Dont do anything if No Input
   return if d == 0
   # Adjust the Direction to a Mirrored Direction
   new_d = make_mirror_direction(d)
   # Make New Coordinates (8D Method is called but works with 4D Movement)
   new_x, new_y = make_new_xy_8d(@x, @y, new_d)
   # If Mirror Event Movement is within Range
   if mirror_in_limit?(new_x, new_y) and mirror_can_move?(new_x, new_y, d)
     # Branch Movement by Mirrored Direction (true allows turning)
     case new_d
     when 2
       move_down
     when 4
       move_left
     when 6
       move_right
     when 8
       move_up
     end
     # If Movement is occuring after calling Move Commands
     if @x * 128 != @real_x or @y * 128 != @real_y
       # Set Flag that Mirror is Moving to prevent Updating
       @mirror_moving = true
       # Update Movement NOW if Movement is occuring
       update_move
     end
   end
 end
 #--------------------------------------------------------------------------
 # * Update Mirror Direction - Game_Event
 #      dir : Direction
 #--------------------------------------------------------------------------
 def update_mirror_direction(d)
   # Prevent changing Direction while Mirror Movement is occuring
   return if not @mirror_enable or @mirror_moving or jumping?
   # Mirror Player's Direction if not Direction Fix
   if not @direction_fix and (not @move_route_forcing or
      @move_route.repeat) and mirror_in_limit?(@x, @y) and mirror_player?
     # Assign Mirror Direction to Mirror Direction of Player (by arg)
     @direction = make_mirror_direction(d)
   end
 end  
 #--------------------------------------------------------------------------
 # * Mirror Player Anime - Game_Event
 #  - Causes Mirror Event to match the Player's Animations
 #  - Called by Player Update, not this Event's Update method
 #--------------------------------------------------------------------------
 def mirror_player_anime
   # If Option \mirror_anime in a Comment
   if @mirror_anime and @mirror_enable and (not @move_route_forcing or
      @move_route.repeat) and mirror_in_limit?(@x, @y) and mirror_player?
     # Update Pattern to Player's Pattern for Animation
     @pattern = $game_player.get_pattern
     # Clear animation count
     @anime_count = 0  
   end
 end
 #--------------------------------------------------------------------------
 # * Mirror At Valid? - Game_Event
 #  - Used by mirror_at? and mirrors_at?
 #  - Events can not be Erased and must have an Active Page
 #--------------------------------------------------------------------------
 def mirror_at_valid?
   return (not @erased and not @page.nil?)
 end    
 #--------------------------------------------------------------------------
 # * Update (Frame) - Game_Event
 #  - Alias of Main Update Method
 #  - Mirror Updates are handled by the Player Update Method
 #--------------------------------------------------------------------------
 alias mirror_event_main_update update unless $@
 def update
   # Call Original or other Aliases
   mirror_event_main_update
   # If Option \mirror_anime in a Comment
   if @mirror_anime and @mirror_enable and (not @move_route_forcing or
      @move_route.repeat) and mirror_player?
     # Clear animation count
     @anime_count = 0
   end
 end
 #--------------------------------------------------------------------------
 # * Erase - Game_Event
 #--------------------------------------------------------------------------
 alias mirror_movement_erase erase unless $@
 def erase
   # Call Original or other Aliases
   mirror_movement_erase
   # Delete Event from Mirror Events Array
   $game_map.mirror_events.delete(self)
 end
 #--------------------------------------------------------------------------
 # * Reset Page Comment Config - Game_Event
 #  - Resets Mirror Options set by Comment Config during a Page Change
 #--------------------------------------------------------------------------
 alias mirror_reset_page_comment_config reset_page_comment_config unless $@
 def reset_page_comment_config
   # Remove Mirror Event from Map Container
   $game_map.mirror_events.delete(@id) if @mirror
   # Reset to set again by Comment Conditions
   @mirror = nil
   @mirror_enable = nil
   @mirror_limit_x = nil
   @mirror_limit_y = nil
   @mirror_player_x = nil
   @mirror_player_y = nil
   @mirror_anime = nil
   # Runs Original or Other Aliases
   mirror_reset_page_comment_config
 end
 #--------------------------------------------------------------------------
 # * Spin 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
 #
 #   Note: When aliasing, please return the value of aliased methods for
 #         the local variable count to be altered as needed.
 #   return alias_check_page_comment_config(comment, count)
 #--------------------------------------------------------------------------
 alias mirror_check_page_comment_config check_page_comment_config unless $@
 def check_page_comment_config(comment, count)
   # Looks for "\mirror[N]" Comments
   comment.gsub(/^\\mirror\[([0-3])\]\z/i){
     @mirror = $1.to_i if $1.to_i.is_a?(Numeric);
     if not $game_map.mirror_events.include?(self);      
       $game_map.mirror_events << self
     end
     return count;}
   # Looks for "\mirror_enable" Comments to Enable by Default
   comment.gsub(/^\\mirror_enable\z/i){@mirror_enable = true;
     # Since Enabled, update Direction NOW
     if $game_system.mirror and @mirror
       update_mirror_direction($game_player.direction)
     end
     return count;}    
   # Looks for "\mirror_limit_x[N, N]" Comments
   comment.gsub(/^\\mirror_limit_x\[([0-9, ]+)\]\z/i){@mirror_limit_x = $1;
     @mirror_limit_x = @mirror_limit_x.split(",").map { |s| s.to_i };
     return count; }
   # Looks for "\mirror_limit_y[N, N]" Comments
   comment.gsub(/^\\mirror_limit_y\[([0-9, ]+)\]\z/i){@mirror_limit_y = $1;
     @mirror_limit_y = @mirror_limit_y.split(",").map { |s| s.to_i };
     return count; }
   # Looks for "\mirror_player_x[N, N]" Comments for Range
   comment.gsub(/^\\mirror_player_x\[([0-9, ]+)\]\z/i){@mirror_player_x = $1;
     @mirror_player_x = @mirror_player_x.split(",").map { |s| s.to_i };
     return count; }
   # Looks for "\mirror_player_y[N, N]" Comments for Range
   comment.gsub(/^\\mirror_player_y\[([0-9, ]+)\]\z/i){@mirror_player_y = $1;
     @mirror_player_y = @mirror_player_y.split(",").map { |s| s.to_i };
     return count; }
   # Looks for "\mirror_anime" Comments for Cloning Animation
   comment.gsub(/^\\mirror_anime\z/i){ @mirror_anime = true; return count; }
   # Return adjusted or unadjusted counter for other scripts to use this
   return mirror_check_page_comment_config(comment, count)
 end  
end

#==============================================================================
# ** Interpreter
#==============================================================================
class Interpreter
 #--------------------------------------------------------------------------
 # * Mirror At? - Interpreter
 #  - Returns true when Event ID (not just Mirrors) are at XY Location
 #          id : Event ID
 #           x : Map X Coordinate
 #           y : Map Y Coordinate
 #      mirror : true or false, use true to require Event to be a Mirror
 #--------------------------------------------------------------------------
 def mirror_at?(id, x, y, mirror = false)
   if $DEBUG
     # Check if Event exists
     if not $game_map.events[id]
       # Explain the problem
       print "Warning: Problem with your \"mirror_at?(",
             id, ",", x, ",", y, ")\" call\n\n",
             "\nThe Event ", id, " does not exist on this Map!"
       # Prevent Crash by not executing the rest of this command
       return false
     end
     # Check X and Y coordinates
     if not x.is_a?(Numeric)
       # Explain the problem
       print "Warning: Problem with your \"mirror_at?(",
             id, ",", x, ",", y, ")\" call\n",
             "\nThe X value \"", x, "\" is not a Number"
       # Prevent Crash by not executing the rest of this command
       return false
     end
     if not y.is_a?(Numeric)
       # Explain the problem
       print "Warning: Problem with your \"mirror_at?(",
             id, ",", x, ",", y, ")\" call\n",
             "\nThe Y value \"", y, "\" is not a Number"
       # Prevent Crash by not executing the rest of this command
       return false
     end
   end
   # Get the Event for Shorthand
   e = $game_map.events[id]
   # Check Real Coordinates against Argument and not Moving or Jumping
   if x * 128 == e.real_x and y * 128 == e.real_y and not e.mirror_moving and
      not e.jumping? and ((e.mirror and e.mirror_enabled) or not mirror) and
      e.mirror_at_valid?
     # Location Matches Arguments
     return true
   end
   # Default
   return false
 end
 #--------------------------------------------------------------------------
 # * Mirrors At? - Interpreter
 #  - Returns true when ANY Event Location matches ANY Coordinate Set
 #          event_ids : Array of Event IDs
 #                 xs : Array of Map X Coordinates
 #                 ys : Array of Map Y Coordinates
 #             mirror : true or false, Require Events to be Mirrors (optional)
 #--------------------------------------------------------------------------  
 def mirrors_at?(event_ids,xs,ys, mirror = false)
   # If Running from Editor
   if $DEBUG
     # Check if Args are Arrays
     if not event_ids.is_a?Array or not xs.is_a?Array or not ys.is_a?Array
       # Explain the problem
       print "Warning: Problem with your \"mirrors_at?(",
             event_ids.inspect, ",", xs.inspect, ",", ys.inspect,
             ")\" call:\n\nThe first three arguments must be Arrays!"
       # Prevent Crash by not executing the rest of this command
       return false
     end
     # Check that X and Y array sizes are the same
     if xs.size != ys.size
       # Explain the problem
       print "Warning: Problem with your \"mirrors_at?(",
             event_ids.inspect, ",", xs.inspect, ",", ys.inspect,
             ")\" call:\n\nThe X and Y Arrays MUST be the same size!"
       # Prevent Crash by not executing the rest of this command
       return false
     end
     # Check All Event IDs
     for event_id in event_ids
       # Check if Event exists on Map
       if not $game_map.events[event_id]
         # Explain the problem
         print "Warning: Problem with your \"mirror_at?(",
               id, ",", x, ",", y, ")\" call\n\n",
               "\nThe Event ", id, " does not exist on this Map!"
         # Prevent Crash by not executing the rest of this command
         return false
       end
     end
   end
   # Result
   result = false
   # Transpose Coordinates into New Array [1,2,3][4,5,6] to [1,4][2,5][3,6]
   locations = [xs,ys].transpose
   # Check each Event ID
   for id in event_ids
     # Get the Event for Shorthand
     e = $game_map.events[id]
     # Check Real Coordinates against Argument and not Moving or Jumping
     if not e.moving? and not e.jumping? and e.mirror_at_valid? and
        ((e.mirror and e.mirror_enable) or not mirror)
       # If Events Coordinates are within the Transposed Array
       if locations.include?([e.x,e.y])
         # Mirror is at one of the Transposed Coordinates so True so far
         result = true
       else
         # Mirror is not contained within the set of Trnasposed Coordinates
         result = false
         # Prevent further Iterations
         break
       end
     else
       # Mirrors are Moving, Jumping or not Mirrors with Optional Arg
       result = false
       # Prevent further Iterations
       break
     end
   end
   # Return Result of Match
   return result
 end
end



Instructions

Create a Mirror by adding a Comment to the first 10 lines of an Event:
\mirror[N] - Use 0 for Vertical, 1 for Horizontal, 2 for Both, 3 for Clone

Enable a Mirror with either a Script or by adding another Comment:
@>Script: $game_map.events[11].mirror_enable = true
@>Comment: \mirror_enable


Compatibility

Not compatible with Seven Years of Bad Luck.  Real Life bad luck, not the script...


Credits and Thanks


  • I'd like to thank whoever invented origami as I use it on Junk Mail to make Origami Boulders very frequently.




Author's Notes

Mirrors are intended for Puzzles!

Most Mirror Puzzles involve moving one or more Mirros to a location.  You can check if Mirrors are at a Location with a couple of included scripts for Conditional Branches: mirror_at?(id, x, y).  If you need to check many mirrors, you can use mirrors_at?( [id, id], [x, x, x], [y, y, y]) which does a form of Permutations and allows ANY Mirror to be at ANY Location in the argument arrays.
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.)