#==============================================================================
# ** TDS Instant Event Commands
#    Ver: 1.0
#------------------------------------------------------------------------------
#  * Description:
#  This script allows you to process event commands instantly.
#------------------------------------------------------------------------------
#  * Features: 
#  Process Event commands isntantly.
#  Skip to an index command in an event.
#------------------------------------------------------------------------------
#  * Instructions:
#  To instantly skip to an index command of an event use this in a script 
#  call:
#
#    go_to_index(index)
#
#    index = command index
#
#    Example:
#
#    go_to_index(5)
#
#  To adjust the map display position use this in a script call above the
#  "go_to_index" call:
#
#    quick_event_screen_offset(X, Y)
#
#    X = X Offset (In tiles/Can be negative)
#    Y = Y Offset (In tiles/Can be negative)
#
#    Example:
#
#    quick_event_screen_offset(1, -1)
#
#    Note: 
#    This script call is to account for special circustances where instantly
#    scrolling the map does not end up where it's supposed to be due to the
#    player moving as well.
#------------------------------------------------------------------------------
#  * Notes:
#  The indexing sytem can take a bit of getting used to, which is why it's
#  good to have the RGSS console turned on to see which event command index
#  is currently running.
#------------------------------------------------------------------------------
# WARNING:
#
# Do not release, distribute or change my work without my expressed written 
# consent, doing so violates the terms of use of this work.
#
# If you really want to share my work please just post a link to the original
# site.
#
# * Not Knowing English or understanding these terms will not excuse you in any
#   way from the consequenses.
#==============================================================================
# * Import to Global Hash *
#==============================================================================
($imported ||= {})[:TDS_Instant_Event_Commands] = true

#==============================================================================
# ** Game_Interpreter
#------------------------------------------------------------------------------
#  An interpreter for executing event commands. This class is used within the
# Game_Map, Game_Troop, and Game_Event classes.
#==============================================================================

class Game_Interpreter
  #--------------------------------------------------------------------------
  # * Constants (Settings)
  #--------------------------------------------------------------------------
  # Commands that do not require special handling
  QUICK_COMMANDS = (121..123).to_a + (125..129).to_a + (132..138).to_a +
                   (201..206).to_a + (231..235).to_a + (281..285).to_a + 
                   (311..324).to_a +  [118, 211, 216, 217, 221, 222, 223, 355]
  #--------------------------------------------------------------------------
  # * Alias Listing
  #--------------------------------------------------------------------------  
  alias tds_instant_event_commands_game_interpreter_execute_command execute_command  
  #--------------------------------------------------------------------------
  # * Quick Event Call Screen Center Offset (Adjust Screen by offset)
  #     x : x-coordinate offset value
  #     y : y-coordinate offset value  
  #--------------------------------------------------------------------------
  def quick_event_screen_offset(x = 0, y = 0) ; @quick_event_screen_offset = [x, y] end
  #--------------------------------------------------------------------------
  # * Go to Event Index
  #     index : event index
  #--------------------------------------------------------------------------
  def go_to_index(index)
    # Return if Already Past Index or index is bigger than list
    return if index <= @index or index > @list.size
    # Increase Index by 1
    @index += 1
    # While 
    loop do
      # Execute Quick Event Command
      execute_quick_command
      # Break if Index has reached desired index
      break if @index >= index           
      # Increase Index
      @index += 1
    end
    # If Event Screen Offset is not nil
    if !@quick_event_screen_offset.nil?      
      x = $game_map.display_x + @quick_event_screen_offset.at(0)
      y = $game_map.display_y + @quick_event_screen_offset.at(1)      
      # Set Map Display Position
      $game_map.set_display_pos(x, y)
    end
    # Set Quick Evetn Screen Offset to nil
    @quick_event_screen_offset = nil
  end
  #--------------------------------------------------------------------------
  # * Quick Event Command Execution
  #--------------------------------------------------------------------------
  def execute_quick_command
    # Get Command
    command = @list[@index]       
    # Return if Quick Commands List does not include Command Code
    return if !QUICK_COMMANDS.include?(command.code)    
    # Get Command Parameters and Indentation
    @params = command.parameters ; @indent = command.indent          
    # Set Quick Command Parameters
    set_quick_command_parameters(command)
    # Return if Custom Quick Command Process is executed
    return if custom_quick_command_process(command)
    # Get Method Name
    method_name = "command_#{command.code}"
    # Send Method if Class Responds to method
    send(method_name) if respond_to?(method_name)
  end
  #--------------------------------------------------------------------------
  # * Set Quick Command Parameters
  #     command : command
  #--------------------------------------------------------------------------
  def set_quick_command_parameters(command)
    # Command Code Case
    case command.code
    when 223 # Map tone Change
      # Change Fade time to 0 and remove wait
      @params[1] = 0 ; @params[2] = false
    when 234 # Change Picture Tone
      # Change Picture Tone and Remove Wait
      @params[2] = 0 ; @params[3] = false
    when 232 # Move Picture
      # Change Move Picture Duration and Remove Wait
      @params[10] = 0 ; @params[11] = false
    end    
  end
  #--------------------------------------------------------------------------
  # * Custom Quick Command Process
  #     command : command
  #--------------------------------------------------------------------------
  def custom_quick_command_process(command)    
    # Command Code Case
    case command.code
    when 221 # Fadeout Screen
      screen.start_fadeout(1)
      return true
    when 222 # Fadein Screen      
      screen.start_fadein(1)
      return true
    when 204 # Scroll Map
      # Start Map Instant Scroll (Ignoring Speed Parameter)
      $game_map.start_instant_scroll(*@params.first(2))
      return true
    when 205 # Set Move Route
      # Refresh Game Map if needed
      $game_map.refresh if $game_map.need_refresh
      # Get Character
      character = get_character(@params[0])
      # Force instant Move Route If Character is not nil
      character.force_instant_move_route(@params[1]) if character
      return true
    end    
    # Return false by default (No Custom events were runned)
    return false
  end
  #--------------------------------------------------------------------------
  # * Event Command Execution
  #--------------------------------------------------------------------------
  def execute_command(*args, &block)    
    # Print Event List
    p "Event Line: #{@index}"
    # Run Original Method
    tds_instant_event_commands_game_interpreter_execute_command(*args, &block)
  end  
end


#==============================================================================
# ** Game_Map
#------------------------------------------------------------------------------
#  This class handles maps. It includes scrolling and passage determination
# functions. The instance of this class is referenced by $game_map.
#==============================================================================

class Game_Map
  #--------------------------------------------------------------------------
  # * Alias Listing
  #--------------------------------------------------------------------------
  alias tds_quick_event_commands_game_map_update                      update  
  #--------------------------------------------------------------------------
  # * Start Instant Scroll
  #--------------------------------------------------------------------------
  def start_instant_scroll(direction, distance)
    # Reset Scrolling Settings if applicable
    setup_scroll
    # Direction Case (2: Up, 4: Left, 6: Right, 8: Down)
    case direction
    when 2 ; set_display_pos(@display_x, @display_y + distance)
    when 4 ; set_display_pos(@display_x - distance, @display_y)
    when 6 ; set_display_pos(@display_x + distance, @display_y)
    when 8 ; set_display_pos(@display_x, @display_y - distance)
    end
  end
end


#==============================================================================
# ** Game_Character
#------------------------------------------------------------------------------
#  A character class with mainly movement route and other such processing
# added. It is used as a super class of Game_Player, Game_Follower,
# GameVehicle, and Game_Event.
#==============================================================================

class Game_Character < Game_CharacterBase
  #--------------------------------------------------------------------------
  # * Force Instant Move Route
  #--------------------------------------------------------------------------
  def force_instant_move_route(move_route)
    # Get Instant Move Route
    @instant_move_route = move_route.list.dup    
    # While Instant Move Route is not empty
    while !@instant_move_route.empty?
      # Get Move Command
      command = @instant_move_route.first
      # Process Instant Move Command
      process_instant_move_command(command)
      # Delete Instant Move Route List Command
      @instant_move_route.delete(command)
    end
    # Set Instant Move Route to nil
    @instant_move_route = nil
  end
  #--------------------------------------------------------------------------
  # * Instant Move Straight
  #     d:        Direction (2,4,6,8)
  #     turn_ok : Allows change of direction on the spot
  #--------------------------------------------------------------------------
  def instant_move_straight(d, turn_ok = true)
    @move_succeed = passable?(@x, @y, d)
    if @move_succeed
      set_direction(d)
      @x = $game_map.round_x_with_direction(@x, d)
      @y = $game_map.round_y_with_direction(@y, d)
      moveto(@x, @y) ; increase_steps
    elsif turn_ok
      set_direction(d) ; check_event_trigger_touch_front
    end
  end
  #--------------------------------------------------------------------------
  # * Instant Jump
  #     x_plus : x-coordinate plus value
  #     y_plus : y-coordinate plus value
  #--------------------------------------------------------------------------
  def instant_jump(x_plus, y_plus)
    if x_plus.abs > y_plus.abs
      set_direction(x_plus < 0 ? 4 : 6) if x_plus != 0
    else
      set_direction(y_plus < 0 ? 8 : 2) if y_plus != 0
    end
    # Move to Coordinates
    @x += x_plus ; @y += y_plus ; moveto(@x, @y) ; straighten
  end  
  #--------------------------------------------------------------------------
  # * Instant Move Toward Character
  #--------------------------------------------------------------------------
  def instant_move_toward_character(character)
    sx = distance_x_from(character.x)
    sy = distance_y_from(character.y)
    if sx.abs > sy.abs
      instant_move_straight(sx > 0 ? 4 : 6)
      instant_move_straight(sy > 0 ? 8 : 2) if !@move_succeed && sy != 0
    elsif sy != 0
      instant_move_straight(sy > 0 ? 8 : 2)
      instant_move_straight(sx > 0 ? 4 : 6) if !@move_succeed && sx != 0
    end
  end
  #--------------------------------------------------------------------------
  # * Instant Move Away from Character
  #--------------------------------------------------------------------------
  def instant_move_away_from_character(character)
    sx = distance_x_from(character.x) ; sy = distance_y_from(character.y)
    if sx.abs > sy.abs
      instant_move_straight(sx > 0 ? 6 : 4)
      instant_move_straight(sy > 0 ? 2 : 8) if !@move_succeed && sy != 0
    elsif sy != 0
      instant_move_straight(sy > 0 ? 2 : 8)
      instant_move_straight(sx > 0 ? 6 : 4) if !@move_succeed && sx != 0
    end
  end
  #--------------------------------------------------------------------------
  # * Instant Move Diagonally
  #     horz:  Horizontal (4 or 6)
  #     vert:  Vertical (2 or 8)
  #--------------------------------------------------------------------------
  def instant_move_diagonal(horz, vert)
    @move_succeed = diagonal_passable?(x, y, horz, vert)
    if @move_succeed
      @x = $game_map.round_x_with_direction(@x, horz)
      @y = $game_map.round_y_with_direction(@y, vert)      
      moveto(@x, @y) ; increase_steps
    end
    set_direction(horz) if @direction == reverse_dir(horz)
    set_direction(vert) if @direction == reverse_dir(vert)    
  end
  #--------------------------------------------------------------------------
  # * Process Move Command
  #--------------------------------------------------------------------------
  def process_instant_move_command(command)
    params = command.parameters
    case command.code
    when ROUTE_MOVE_DOWN        ; instant_move_straight(2)
    when ROUTE_MOVE_LEFT        ; instant_move_straight(4)
    when ROUTE_MOVE_RIGHT       ; instant_move_straight(6)
    when ROUTE_MOVE_UP          ; instant_move_straight(8)
    when ROUTE_MOVE_LOWER_L     ; instant_move_diagonal(4, 2)
    when ROUTE_MOVE_LOWER_R     ; instant_move_diagonal(6, 2)
    when ROUTE_MOVE_UPPER_L     ; instant_move_diagonal(4, 8)
    when ROUTE_MOVE_UPPER_R     ; instant_move_diagonal(6, 8)
    when ROUTE_MOVE_RANDOM      ; instant_move_straight(2 + rand(4) * 2, false)
    when ROUTE_MOVE_TOWARD      ; instant_move_toward_character($game_player)
    when ROUTE_MOVE_AWAY        ; instant_move_away_from_character($game_player)
    when ROUTE_MOVE_FORWARD     ; instant_move_straight(@direction)
    when ROUTE_MOVE_BACKWARD
      last_direction_fix = @direction_fix
      @direction_fix = true
      instant_move_straight(reverse_dir(@direction), false)
      @direction_fix = last_direction_fix
    when ROUTE_JUMP ;             instant_jump(params.at(0), params.at(1))
    when ROUTE_TURN_DOWN;         set_direction(2)
    when ROUTE_TURN_LEFT;         set_direction(4)
    when ROUTE_TURN_RIGHT;        set_direction(6)
    when ROUTE_TURN_UP;           set_direction(8)
    when ROUTE_TURN_90D_R;        turn_right_90
    when ROUTE_TURN_90D_L;        turn_left_90
    when ROUTE_TURN_180D;         turn_180
    when ROUTE_TURN_90D_R_L;      turn_right_or_left_90
    when ROUTE_TURN_RANDOM;       turn_random
    when ROUTE_TURN_TOWARD;       turn_toward_player
    when ROUTE_TURN_AWAY;         turn_away_from_player
    when ROUTE_SWITCH_ON;         $game_switches[params[0]] = true
    when ROUTE_SWITCH_OFF;        $game_switches[params[0]] = false
    when ROUTE_CHANGE_SPEED;      @move_speed = params[0]
    when ROUTE_CHANGE_FREQ;       @move_frequency = params[0]
    when ROUTE_WALK_ANIME_ON;     @walk_anime = true
    when ROUTE_WALK_ANIME_OFF;    @walk_anime = false
    when ROUTE_STEP_ANIME_ON;     @step_anime = true
    when ROUTE_STEP_ANIME_OFF;    @step_anime = false
    when ROUTE_DIR_FIX_ON;        @direction_fix = true
    when ROUTE_DIR_FIX_OFF;       @direction_fix = false
    when ROUTE_THROUGH_ON;        @through = true
    when ROUTE_THROUGH_OFF;       @through = false
    when ROUTE_TRANSPARENT_ON;    @transparent = true
    when ROUTE_TRANSPARENT_OFF;   @transparent = false
    when ROUTE_CHANGE_GRAPHIC;    set_graphic(params[0], params[1])
    when ROUTE_CHANGE_OPACITY;    @opacity = params[0]
    when ROUTE_CHANGE_BLENDING;   @blend_type = params[0]
    when ROUTE_SCRIPT
      # Get Script Call
      call = params.at(0).dup            
      # If Script Call Matches Move times Method
      if call =~ /move_times\(.+,.+\)/i
        # Replace Move times by Instant Move Times
        call.sub!(/move_times/, 'instant_move_times')
      end
      # Evaluate Script Call
      eval(call)      
    end
  end
  #--------------------------------------------------------------------------
  # * Move Instantly by Times
  #    code  : move code
  #    times : times to move using code
  #--------------------------------------------------------------------------
  def instant_move_times(code, times)
    # Return if Instant Move Route is nil
    return if @instant_move_route.nil?
    # Convert Code From Symbol to Integer if applicable
    code = SYMBOL_CODE[code] if code.is_a?(Symbol)
    # Add Move Route Code
    times.times {|i| @instant_move_route.insert(0, RPG::MoveCommand.new(code))}
  end  
end