Charas-Project

Game Creation => Requests => RPG Maker Programming => Topic started by: GaryCXJk on December 30, 2012, 01:32:29 AM

Title: Free Movement (off-the grid movement) WIP thread
Post by: GaryCXJk on December 30, 2012, 01:32:29 AM
Okay, so, lately I've been working with RPG Maker VX Ace. In fact, I started working with it the moment I bought it, during the holiday sale on Steam. So far it's been great, but recently I've started some project thanks to my brother. I can never say no to any of his requests.

Basically it's a free movement script, which I conveniently call Free Movement. You basically can move off the grid with this script.

It's very WIP, as I wasn't able to test everything.

[noae][noae][noae][noae][noae][noae][noae]
Code: [Select]
module CXJ
  module FREE_MOVEMENT
   
    ENABLE_DIAGONAL = true    # Enables diagonal movement.
   
    DEFAULT_COLLISION = [8, 12, 16, 20]
    DEFAULT_INTERACTION = {
    2 => [4, 0, 24, 24],
    4 => [16, 10, 24, 24],
    6 => [-8, 10, 24, 24],
    8 => [4, 20, 24, 24],
    }

    BOAT_COLLISION      = [4, 4, 24, 24]
    AIRSHIP_COLLISION   = [4, 8, 24, 24]

    PIXELS_PER_STEP = 4
   
    FOLLOWERS_DISTANCE = 16
    FOLLOWERS_DISTANCE_MARGIN = 4

    JUMP_SPEED = 0.5
  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
  #--------------------------------------------------------------------------
  # * New: Determine Valid Coordinates
  #--------------------------------------------------------------------------
  def valid_rect?(x, y, rect)
    x2 = x + (rect.x / 32.0)
    y2 = y + (rect.y / 32.0)
    x3 = x2 + (rect.width / 32.0)
    y3 = y2 + (rect.height / 32.0)
    round_x(x2) >= 0 && round_x(x3) < width && round_y(y2) >= 0 && round_y(y3) < height
  end
  #--------------------------------------------------------------------------
  # * Override: Check Passage
  #     bit:  Inhibit passage check bit
  #--------------------------------------------------------------------------
  def check_passage(x, y, bit)
    x = round_x(x)
    y = round_y(y)
    all_tiles(x, y).each do |tile_id|
      flag = tileset.flags[tile_id]
      next if flag & 0x10 != 0            # [☆]: No effect on passage
      return true  if flag & bit == 0     # [○] : Passable
      return false if flag & bit == bit   # [×] : Impassable
    end
    return false                          # Impassable
  end
  #--------------------------------------------------------------------------
  # * New: Determine Passability of Normal Character
  #     d:  direction (2,4,6,8)
  #    Determines whether the tile at the specified coordinates is passable
  #    in the specified direction.
  #--------------------------------------------------------------------------
  def passable_rect?(x, y, d, rect)
    x2 = x + (rect.x / 32.0)
    y2 = y + (rect.y / 32.0)
    x3 = x2 + (rect.width / 32.0)
    y3 = y2 + (rect.height / 32.0)
    return false unless check_passage(x2, y2, (1 << (d / 2 - 1)) & 0x0f)
    return false unless check_passage(x2, y3, (1 << (d / 2 - 1)) & 0x0f)
    return false unless check_passage(x3, y2, (1 << (d / 2 - 1)) & 0x0f)
    return check_passage(x3, y3, (1 << (d / 2 - 1)) & 0x0f)
  end
  #--------------------------------------------------------------------------
  # * Determine if Passable by Boat
  #--------------------------------------------------------------------------
  def boat_passable_rect?(x, y, rect)
    x2 = x + (rect.x / 32.0)
    y2 = y + (rect.y / 32.0)
    x3 = x2 + (rect.width / 32.0)
    y3 = y2 + (rect.height / 32.0)
    return false unless check_passage(x2, y2, 0x0200)
    return false unless check_passage(x2, y3, 0x0200)
    return false unless check_passage(x3, y2, 0x0200)
    return check_passage(x3, y3, 0x0200)
  end
  #--------------------------------------------------------------------------
  # * Determine if Passable by Ship
  #--------------------------------------------------------------------------
  def ship_passable_rect?(x, y, rect)
    x2 = x + (rect.x / 32.0)
    y2 = y + (rect.y / 32.0)
    x3 = x2 + (rect.width / 32.0)
    y3 = y2 + (rect.height / 32.0)
    return false unless check_passage(x2, y2, 0x0400)
    return false unless check_passage(x2, y3, 0x0400)
    return false unless check_passage(x3, y2, 0x0400)
    return check_passage(x3, y3, 0x0400)
  end
  #--------------------------------------------------------------------------
  # * Determine if Airship can Land
  #--------------------------------------------------------------------------
  def airship_land_ok_rect?(x, y, rect)
    x2 = x + (rect.x / 32.0)
    y2 = y + (rect.y / 32.0)
    x3 = x2 + (rect.width / 32.0)
    y3 = y2 + (rect.height / 32.0)
    return false unless check_passage(x2, y2, 0x0800) && check_passage(x2, y2, 0x0f)
    return false unless check_passage(x2, y3, 0x0800) && check_passage(x2, y3, 0x0f)
    return false unless check_passage(x3, y2, 0x0800) && check_passage(x3, y2, 0x0f)
    return check_passage(x3, y3, 0x0800) && check_passage(x3, y3, 0x0f)
  end
  #--------------------------------------------------------------------------
  # * Get Array of Events at Designated Coordinates
  #--------------------------------------------------------------------------
  def events_xy_rect(x, y, rect)
    @events.values.select {|event| event.pos_rect?(x, y, rect) }
  end
  #--------------------------------------------------------------------------
  # * Get Array of Events at Designated Coordinates (Except Pass-Through)
  #--------------------------------------------------------------------------
  def events_xy_rect_nt(x, y, rect)
    @events.values.select {|event| event.pos_rect_nt?(x, y, rect) }
  end
  #--------------------------------------------------------------------------
  # * Get Array of Tile-Handling Events at Designated Coordinates
  #   (Except Pass-Through)
  #--------------------------------------------------------------------------
  def tile_events_xy_rect(x, y, rect)
    @tile_events.select {|event| event.pos_rect_nt?(x, y, rect) }
  end
end
 
#==============================================================================
# ** Game_CharacterBase
#------------------------------------------------------------------------------
#  This base class handles characters. It retains basic information, such as
# coordinates and graphics, shared by all characters.
#==============================================================================

class Game_CharacterBase
  attr_accessor :move_poll
  #--------------------------------------------------------------------------
  # * Alias: Object Initialization
  #--------------------------------------------------------------------------
  alias game_characterbase_initialize_cxj_fm initialize
  def initialize
    game_characterbase_initialize_cxj_fm
    @move_poll = []
  end
  #--------------------------------------------------------------------------
  # * Alias: Frame Update
  #
  # Added processing of movement being polled.
  #--------------------------------------------------------------------------
  alias game_characterbase_update_cxj_fm update
  def update
    interpret_move unless moving?
    game_characterbase_update_cxj_fm
  end
  #--------------------------------------------------------------------------
  # * New: Movement Interpreting
  #     Interprets the polled movement.
  #--------------------------------------------------------------------------
  def interpret_move(step_left = distance_per_frame)
    if @move_poll.size > 0
      current_move = @move_poll.shift()
      d = current_move[0]
      horz = (d - 1) % 3 - 1
      vert = 1 - ((d - 1) / 3)
      turn_ok = current_move[1]
      set_direction(d)
      processed = false
      if (d % 2 == 0 && passable?(@x, @y, d)) || (d % 2 != 0 && diagonal_passable?(@x, @y, horz, vert))
        process_move(horz, vert)
        processed = true
      elsif d % 2 != 0 && !diagonal_passable?(@x, @y, horz, vert)
        if passable?(@x, @y, horz + 5)
          set_direction(horz + 5)
          process_move(horz, 0)
          processed = true
        end
        if passable?(@x, @y, 5 - vert * 3)
          set_direction(5 - vert * 3)
          process_move(0, vert)
          processed = true
        end
      end
      if(processed)
        pixelstep = CXJ::FREE_MOVEMENT::PIXELS_PER_STEP / 32.0
        if(step_left > pixelstep && !@move_poll.empty?)
          interpret_move(step_left - pixelstep)
        elsif(jumping? && !@move_poll.empty?)
          interpret_move(0)
        end
      else
        @move_poll.clear
      end
      current_move
    end
  end
 
  def process_move(horz, vert)
    pixelstep = CXJ::FREE_MOVEMENT::PIXELS_PER_STEP / 32.0
    @x = @x + horz * pixelstep
    @y = @y + vert * pixelstep
    if(!jumping?)
      @x = $game_map.round_x(@x)
      @y = $game_map.round_y(@y)
      @real_x = @x - horz * pixelstep
      @real_y = @y - vert * pixelstep
      increase_steps
    end
  end
  #--------------------------------------------------------------------------
  # * New: Collision Rectangle
  #     Gets the collision rectangle.
  #--------------------------------------------------------------------------
  def collision_rect
    collision = CXJ::FREE_MOVEMENT::DEFAULT_COLLISION
    return Rect.new(collision[0], collision[1], collision[2] - 1, collision[3] - 1)
  end
  #--------------------------------------------------------------------------
  # * Override: Determine if Passable
  #     d : Direction (2,4,6,8)
  #--------------------------------------------------------------------------
  def passable?(x, y, d)
    horz = (d - 1) % 3 - 1
    vert = 1 - ((d - 1) / 3)
    pixelstep = CXJ::FREE_MOVEMENT::PIXELS_PER_STEP / 32.0
    x2 = $game_map.round_x(x + horz * pixelstep)
    y2 = $game_map.round_y(y + vert * pixelstep)
    return false unless $game_map.valid_rect?(x2, y2, collision_rect)
    return true if @through || debug_through?
    return false unless map_passable_rect?(x, y, d, collision_rect)
    return false unless map_passable_rect?(x2, y2, reverse_dir(d), collision_rect)
    return false if collide_with_characters?(x2, y2)
    return true
  end
  #--------------------------------------------------------------------------
  # * Determine Diagonal Passability
  #     horz : Horizontal (4 or 6)
  #     vert : Vertical (2 or 8)
  #--------------------------------------------------------------------------
  def diagonal_passable?(x, y, horz, vert)
    pixelstep = CXJ::FREE_MOVEMENT::PIXELS_PER_STEP / 32.0
    x2 = $game_map.round_x(x + horz * pixelstep)
    y2 = $game_map.round_y(y + vert * pixelstep)
    d = (horz == 4 ? -1 : 1) + (vert == 2 ? -3 : 3) + 5
    passable?(x, y, vert) && passable?(x, y, horz) && passable?(x, y, d) && passable?(x2, y2, vert) && passable?(x2, y2, horz) && passable?(x2, y2, d)
  end
  #--------------------------------------------------------------------------
  # * Determine if Map is Passable
  #     d : Direction (2,4,6,8)
  #--------------------------------------------------------------------------
  def map_passable_rect?(x, y, d, rect)
    $game_map.passable_rect?(x, y, d, rect)
  end
  #--------------------------------------------------------------------------
  # * Override: Change Direction to Designated Direction
  #     d : Direction (2,4,6,8)
  #
  # Fix for diagonal movement.
  #--------------------------------------------------------------------------
  def set_direction(d)
    if !@direction_fix && d != 0
      @direction = d
      if d % 2 != 0
        @direction+= 1
        @direction-= 2 if d > 5
        @direction = 10 - direction if d > 2 && d < 8
      end
    end
    @stop_count = 0
  end
  #--------------------------------------------------------------------------
  # * Override: Move Straight
  #     d:        Direction (2,4,6,8)
  #     turn_ok : Allows change of direction on the spot
  #
  # Polls the movement instead of processing them immediately.
  #--------------------------------------------------------------------------
  def move_straight(d, turn_ok = true)
    pixelstep = CXJ::FREE_MOVEMENT::PIXELS_PER_STEP / 32.0
    @move_poll+= [[d, turn_ok]] * (distance_per_frame / pixelstep).ceil
  end
  #--------------------------------------------------------------------------
  # * Override: Move Diagonally
  #     horz:  Horizontal (4 or 6)
  #     vert:  Vertical (2 or 8)
  #
  # Polls the movement instead of processing them immediately.
  #--------------------------------------------------------------------------
  def move_diagonal(horz, vert)
    pixelstep = CXJ::FREE_MOVEMENT::PIXELS_PER_STEP / 32.0
    @move_poll+= [[vert + (horz > 5 ? 1 : -1), true]] * (distance_per_frame / pixelstep).ceil
  end
  #--------------------------------------------------------------------------
  # * New: Determine Coordinate Match
  #--------------------------------------------------------------------------
  def pos_rect?(x, y, rect)
    main_left = @x + collision_rect.x / 32.0
    main_top = @y + collision_rect.y / 32.0
    main_right = main_left + collision_rect.width / 32.0
    main_bottom = main_top + collision_rect.height / 32.0
    other_left = x + rect.x / 32.0
    other_top = y + rect.y / 32.0
    other_right = other_left + rect.width / 32.0
    other_bottom = other_top + rect.height / 32.0
    coltest = true
    coltest = false if main_right < other_left
    coltest = false if main_left > other_right
    coltest = false if main_bottom < other_top
    coltest = false if main_top > other_bottom
    if coltest == false && ($game_map.loop_horizontal? || $game_map.loop_vertical?) && x <= $game_map.width && y <= $game_map.height
      return true if $game_map.loop_horizontal? && pos_rect?(x + $game_map.width, y, rect)
      return true if $game_map.loop_vertical? && pos_rect?(x, y + $game_map.height, rect)
    end
    return coltest
  end
  #--------------------------------------------------------------------------
  # * New: Determine if Coordinates Match and Pass-Through Is Off (nt = No Through)
  #--------------------------------------------------------------------------
  def pos_rect_nt?(x, y, rect)
    pos_rect?(x, y, rect) && !@through
  end
  #--------------------------------------------------------------------------
  # * Detect Collision with Event
  #--------------------------------------------------------------------------
  def collide_with_events?(x, y)
    $game_map.events_xy_rect_nt(x, y, collision_rect).any? do |event|
      (event.normal_priority? || self.is_a?(Game_Event)) && event != self
    end
  end
  #--------------------------------------------------------------------------
  # * Override: Detect Collision with Vehicle
  #--------------------------------------------------------------------------
  def collide_with_vehicles?(x, y)
    $game_map.boat.pos_rect_nt?(x, y, collision_rect) || $game_map.ship.pos_rect_nt?(x, y, collision_rect)
  end
  #--------------------------------------------------------------------------
  # * Override: Update While Jumping
  #--------------------------------------------------------------------------
  def update_jump
    @jump_count -= 1
    diff_x = @real_x
    @real_x = (@real_x * @jump_count + @x) / (@jump_count + 1.0)
    @real_y = (@real_y * @jump_count + @y) / (@jump_count + 1.0)
    update_bush_depth
    if @jump_count == 0
      @real_x = @x = $game_map.round_x(@x)
      @real_y = @y = $game_map.round_y(@y)
    end
  end
  #--------------------------------------------------------------------------
  # * Override: Calculate Jump Height
  #--------------------------------------------------------------------------
  def jump_height
    (@jump_peak * @jump_peak - (@jump_count * CXJ::FREE_MOVEMENT::JUMP_SPEED - @jump_peak).abs ** 2) / 2
  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
  #--------------------------------------------------------------------------
  # * Move at Random
  #--------------------------------------------------------------------------
  def move_random
    @move_poll+= [[2 + rand(4) * 2, false]] * (32 / CXJ::FREE_MOVEMENT::PIXELS_PER_STEP)
  end
  #--------------------------------------------------------------------------
  # * Move Toward Character
  #--------------------------------------------------------------------------
  def move_toward_character(character)
    sx = distance_x_from(character.x)
    sy = distance_y_from(character.y)
    if sx.abs > sy.abs
      @move_poll+= [[sx > 0 ? 4 : 6, true]]
      @move_poll+= [[sy > 0 ? 8 : 2, true]] if !@move_succeed && sy != 0
    elsif sy != 0
      @move_poll+= [[sy > 0 ? 8 : 2, true]]
      @move_poll+= [[sx > 0 ? 4 : 6, true]] if !@move_succeed && sx != 0
    end
  end
  #--------------------------------------------------------------------------
  # * Move Away from Character
  #--------------------------------------------------------------------------
  def move_away_from_character(character)
    sx = distance_x_from(character.x)
    sy = distance_y_from(character.y)
    if sx.abs > sy.abs
      move_straight(sx > 0 ? 6 : 4)
      move_straight(sy > 0 ? 2 : 8) if !@move_succeed && sy != 0
    elsif sy != 0
      move_straight(sy > 0 ? 2 : 8)
      move_straight(sx > 0 ? 6 : 4) if !@move_succeed && sx != 0
    end
  end
  #--------------------------------------------------------------------------
  # * Override: Jump
  #     x_plus : x-coordinate plus value
  #     y_plus : y-coordinate plus value
  #--------------------------------------------------------------------------
  def 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
    distance = Math.sqrt(x_plus * x_plus + y_plus * y_plus).round
    pollcount = distance * (32.0 / CXJ::FREE_MOVEMENT::PIXELS_PER_STEP).ceil
    @move_poll+= [[(x_plus < 0 ? -1 : x_plus > 0 ? 1 : 0) + (y_plus < 0 ? 8 : y_plus > 0 ? 2 : 5), false]] * pollcount
    @jump_peak = 10 + distance - @move_speed
    @jump_count = @jump_peak / CXJ::FREE_MOVEMENT::JUMP_SPEED * 2
    @stop_count = 0
    straighten
  end
end
#==============================================================================
# ** Game_Player
#------------------------------------------------------------------------------
#  This class handles the player. It includes event starting determinants and
# map scrolling functions. The instance of this class is referenced by
# $game_player.
#==============================================================================

class Game_Player < Game_Character
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  alias game_player_initialize_cxj_fm initialize
  def initialize
    @last_poll = []
    game_player_initialize_cxj_fm
    @custom_collision = []
    @interaction = CXJ::FREE_MOVEMENT::DEFAULT_INTERACTION
    if @note =~ /<collisionbox:[ ]*(\s*),[ ]*(\s*),[ ]*(\s*),[ ]*(\s*)>/i
      @custom_collision = Rect.new($1, $2, $3 - 1, $4)
    end
    if @note =~ /<interaction (\s*):[ ]*(\s*),[ ]*(\s*),[ ]*(\s*),[ ]*(\s*)>/i && $1 > 0 && $1 < 10 && $1 % 2 == 0
      @interaction[$1] = [$2, $3, $4, $5]
    end
  end
  #--------------------------------------------------------------------------
  # * New: Movement Interpreting
  #     Interprets the polled movement.
  #--------------------------------------------------------------------------
  def interpret_move(step_left = distance_per_frame)
    current_move = super(step_left)
    @last_poll.push(current_move) if !current_move.nil?
  end
  #--------------------------------------------------------------------------
  # * New: Collision Rectangle
  #     Gets the collision rectangle.
  #--------------------------------------------------------------------------
  def collision_rect
    return @custom_collision if @custom_collision.size > 0
    return super
  end
  #--------------------------------------------------------------------------
  # * New: Interaction Rectangle
  #     Gets the interaction rectangle.
  #--------------------------------------------------------------------------
  def interaction_rect
    collision = @interaction[@direction]
    return Rect.new(collision[0], collision[1], collision[2] - 1, collision[3] - 1)
  end
  #--------------------------------------------------------------------------
  # * Override: Processing of Movement via Input from Directional Buttons
  #
  # Added diagonal movement.
  #--------------------------------------------------------------------------
  def move_by_input
    return if !movable? || $game_map.interpreter.running?
    if CXJ::FREE_MOVEMENT::ENABLE_DIAGONAL && Input.dir8 > 0 && Input.dir8 % 2 != 0
      d = Input.dir8
      horz = (d == 1 || d == 7 ? 4 : 6)
      vert = (d == 1 || d == 3 ? 2 : 8)
      move_diagonal(horz, vert)
    elsif Input.dir4 > 0
      move_straight(Input.dir4)
    end
  end
  #--------------------------------------------------------------------------
  # * Detect Collision (Including Followers)
  #--------------------------------------------------------------------------
  def collide_rect?(x, y, rect)
    !@through && (pos_rect?(x, y, rect) || followers.collide_rect?(x, y, rect))
  end
  #--------------------------------------------------------------------------
  # * Trigger Map Event
  #     triggers : Trigger array
  #     normal   : Is priority set to [Same as Characters] ?
  #--------------------------------------------------------------------------
  def start_map_event(x, y, triggers, normal, rect = collision_rect)
    return if $game_map.interpreter.running?
    $game_map.events_xy_rect(x, y, rect).each do |event|
      if event.trigger_in?(triggers) && event.normal_priority? == normal
        event.start
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Determine if Front Event is Triggered
  #--------------------------------------------------------------------------
  def check_event_trigger_there(triggers)
    x2 = $game_map.round_x_with_direction(@x, @direction)
    y2 = $game_map.round_y_with_direction(@y, @direction)
    start_map_event(x2, y2, triggers, true, interaction_rect)
    return if $game_map.any_event_starting?
    return unless $game_map.counter?(x2, y2)
    x3 = $game_map.round_x_with_direction(x2, @direction)
    y3 = $game_map.round_y_with_direction(y2, @direction)
    start_map_event(x3, y3, triggers, true, interaction_rect)
  end
  #--------------------------------------------------------------------------
  # * Board Vehicle
  #    Assumes that the player is not currently in a vehicle.
  #--------------------------------------------------------------------------
  def get_on_vehicle
    front_x = $game_map.round_x_with_direction(@x, @direction)
    front_y = $game_map.round_y_with_direction(@y, @direction)
    @vehicle_type = :boat    if $game_map.boat.pos_rect?(front_x, front_y, interaction_rect)
    @vehicle_type = :ship    if $game_map.ship.pos_rect?(front_x, front_y, interaction_rect)
    @vehicle_type = :airship if $game_map.airship.pos_rect?(@x, @y, collision_rect)
    if vehicle
      @vehicle_getting_on = true
      horz = (@x > vehicle.x ? -1 : @x < vehicle.x ? 1 : 0)
      vert = (@y > vehicle.y ? -3 : @y < vehicle.y ? 3 : 0)
      d = 5 + horz - vert
      set_direction(d)
      @x = vehicle.x
      @y = vehicle.y
      @followers.gather
    end
    @vehicle_getting_on
  end
  #--------------------------------------------------------------------------
  # * Get Off Vehicle
  #    Assumes that the player is currently riding in a vehicle.
  #--------------------------------------------------------------------------
  def get_off_vehicle
    if vehicle.land_ok?(@x, @y, @direction)
      set_direction(2) if in_airship?
      @followers.synchronize(@x, @y, @direction)
      vehicle.get_off
      unless in_airship?
        @x = $game_map.round_x_with_direction(@x, @direction)
        @y = $game_map.round_y_with_direction(@y, @direction)
        @transparent = false
      end
      @vehicle_getting_off = true
      @move_speed = 4
      @through = false
      make_encounter_count
      @followers.gather
    end
    @vehicle_getting_off
  end
  #--------------------------------------------------------------------------
  # * Determine if Map is Passable
  #     d:  Direction (2,4,6,8)
  #--------------------------------------------------------------------------
  def map_passable_rect?(x, y, d, rect)
    case @vehicle_type
    when :boat
      $game_map.boat_passable_rect?(x, y, vehicle.collision_rect)
    when :ship
      $game_map.ship_passable_rect?(x, y, vehicle.collision_rect)
    when :airship
      true
    else
      super
    end
  end
  #--------------------------------------------------------------------------
  # * Override: Move Diagonally
  #--------------------------------------------------------------------------
  def move_diagonal(horz, vert)
    @followers.move if diagonal_passable?(@x, @y, horz, vert) || passable?(@x, @y, horz + 5) || passable?(@x, @y, 5 - vert * 3)
    super
  end
  #--------------------------------------------------------------------------
  # * Alias: Create Encounter Count
  #--------------------------------------------------------------------------
  alias game_player_make_encounter_count_cxj_fm make_encounter_count
  def make_encounter_count
    game_player_make_encounter_count_cxj_fm
    @encounter_count*= (32 / CXJ::FREE_MOVEMENT::PIXELS_PER_STEP) + (32 / 2 < CXJ::FREE_MOVEMENT::PIXELS_PER_STEP ? 1 : 0)
  end
  #--------------------------------------------------------------------------
  # * Detect Collision with Vehicle
  #--------------------------------------------------------------------------
  def collide_with_vehicles?(x, y)
    (@vehicle_type != :boat && $game_map.boat.pos_rect_nt?(x, y, collision_rect)) || (@vehicle_type != :ship && $game_map.ship.pos_rect_nt?(x, y, collision_rect))
  end
  #--------------------------------------------------------------------------
  # * Processing When Not Moving
  #     last_moving : Was it moving previously?
  #--------------------------------------------------------------------------
  alias game_player_update_nonmoving_cxj_fm update_nonmoving
  def update_nonmoving(last_moving)
    game_player_update_nonmoving_cxj_fm(last_moving)
    update_encounter if !last_moving && !@last_poll.empty?
    @last_poll.clear
  end
end
#==============================================================================
# ** Game_Followers
#------------------------------------------------------------------------------
#  This is a wrapper for a follower array. This class is used internally for
# the Game_Player class.
#==============================================================================

class Game_Followers
  #--------------------------------------------------------------------------
  # * Detect Collision
  #--------------------------------------------------------------------------
  def collide_rect?(x, y, rect)
    visible_folloers.any? {|follower| follower.pos_rect?(x, y, rect) }
  end
  #--------------------------------------------------------------------------
  # * Movement
  #--------------------------------------------------------------------------
  def move
    reverse_each {|follower| follower.board if gathering?; follower.chase_preceding_character }
  end
end
#==============================================================================
# ** Game_Vehicle
#------------------------------------------------------------------------------
#  This class handles vehicles. It's used within the Game_Map class. If there
# are no vehicles on the current map, the coordinates are set to (-1,-1).
#==============================================================================

class Game_Vehicle < Game_Character
  #--------------------------------------------------------------------------
  # * New: Collision Rectangle
  #     Gets the collision rectangle.
  #--------------------------------------------------------------------------
  def collision_rect
    collision = CXJ::FREE_MOVEMENT::DEFAULT_COLLISION
    case @type
    when :boat
      collision = CXJ::FREE_MOVEMENT::BOAT_COLLISION
    when :airship
      collision = CXJ::FREE_MOVEMENT::AIRSHIP_COLLISION
    end
    return Rect.new(collision[0], collision[1], collision[2] - 1, collision[3] - 1)
  end
  #--------------------------------------------------------------------------
  # * Determine if Docking/Landing Is Possible
  #     d:  Direction (2,4,6,8)
  #--------------------------------------------------------------------------
  def land_ok?(x, y, d)
    if @type == :airship
      return false unless $game_map.airship_land_ok_rect?(x, y, collision_rect)
      return false unless $game_map.events_xy_rect(x, y, collision_rect).empty?
    else
      x2 = $game_map.round_x_with_direction(x, d)
      y2 = $game_map.round_y_with_direction(y, d)
      return false unless $game_map.valid_rect?(x2, y2, collision_rect)
      return false unless $game_map.passable_rect?(x2, y2, reverse_dir(d), collision_rect)
      return false if collide_with_characters?(x2, y2)
    end
    return true
  end
end

#==============================================================================
# ** Game_Event
#------------------------------------------------------------------------------
#  This class handles events. Functions include event page switching via
# condition determinants and running parallel process events. Used within the
# Game_Map class.
#==============================================================================

class Game_Event < Game_Character
  #--------------------------------------------------------------------------
  # * Initialize Public Member Variables
  #--------------------------------------------------------------------------
  alias game_event_init_public_members_cxj_fm init_public_members
  def init_public_members
    game_event_init_public_members_cxj_fm
    @collisionbox = Rect.new(0, 0, 31, 31)
  end
  #--------------------------------------------------------------------------
  # * Initialize Public Member Variables
  #--------------------------------------------------------------------------
  def set_collision_rect(x, y, width, height)
    @collisionbox = Rect.new(x, y, width - 1, height - 1)
  end
  #--------------------------------------------------------------------------
  # * New: Collision Rectangle
  #     Gets the collision rectangle.
  #--------------------------------------------------------------------------
  def collision_rect
    return @collisionbox
  end
  #--------------------------------------------------------------------------
  # * Override: Move Straight
  #     d:        Direction (2,4,6,8)
  #     turn_ok : Allows change of direction on the spot
  #
  # Polls the movement instead of processing them immediately.
  #--------------------------------------------------------------------------
  def move_straight(d, turn_ok = true)
    @move_poll+= [[d, turn_ok]] * (rand(32 / CXJ::FREE_MOVEMENT::PIXELS_PER_STEP))
  end
  #--------------------------------------------------------------------------
  # * Detect Collision with Player (Including Followers)
  #--------------------------------------------------------------------------
  def collide_with_player_characters?(x, y)
    normal_priority? && $game_player.collide_rect?(x, y, collision_rect)
  end
end
#==============================================================================
# ** Game_Follower
#------------------------------------------------------------------------------
#  This class handles followers. A follower is an allied character, other than
# the front character, displayed in the party. It is referenced within the
# Game_Followers class.
#==============================================================================

class Game_Follower < Game_Character
  #--------------------------------------------------------------------------
  # * Alias: Object Initialization
  #--------------------------------------------------------------------------
  alias game_follower_initialize_cxj_fm initialize
  def initialize(member_index, preceding_character)
    game_follower_initialize_cxj_fm(member_index, preceding_character)
    @force_chase = false
    @board = false
  end
  #--------------------------------------------------------------------------
  # * Pursue Preceding Character
  #--------------------------------------------------------------------------
  def chase_preceding_character
    unless moving? && !@force_chase
      dist = CXJ::FREE_MOVEMENT::FOLLOWERS_DISTANCE / 32.0
      mrgn = CXJ::FREE_MOVEMENT::FOLLOWERS_DISTANCE_MARGIN / 32.0
      sx = distance_x_from(@preceding_character.x)
      sy = distance_y_from(@preceding_character.y)
      sd = Math.hypot(sx, sy)
      if @board
        @x = @preceding_character.x
        @y = @preceding_character.y
        @board = false
      elsif(sd > dist && sx.abs > mrgn && sy.abs > mrgn)
        @move_poll+=[[(sx > 0 ? -1 : 1) + (sy > 0 ? 8 : 2), true]]
      elsif sx.abs > dist && sx.abs > sy.abs
        @move_poll+=[[sx > 0 ? 4 : 6, true]]
      elsif sy.abs > dist && sx.abs < sy.abs
        @move_poll+=[[sy > 0 ? 8 : 2, true]]
      end
    end
  end
 
  def distance_preceding_character
    sx = distance_x_from(@preceding_character.x)
    sy = distance_y_from(@preceding_character.y)
    return Math.hypot(sx, sy)
  end
 
  def process_move(horz, vert)
    super(horz, vert)
    dist = CXJ::FREE_MOVEMENT::FOLLOWERS_DISTANCE / 32.0
    if distance_preceding_character > dist && @move_poll.size == 0
      @force_chase = true
      chase_preceding_character
      @force_chase = false
    end
  end
 
  def board
    @board = true
  end
end

#==============================================================================
# ** 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
  #--------------------------------------------------------------------------
  # * Initialize Public Member Variables
  #--------------------------------------------------------------------------
  def set_collision_rect(x, y, width, height)
    $game_map.events[@event_id].set_collision_rect(x, y, width, height)
  end
end

=begin
class Spriteset_Map
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    create_viewports
    create_tilemap
    create_parallax
    create_characters
    create_shadow
    create_weather
    create_pictures
    create_timer
    @colbox = Sprite.new
    @colbox.bitmap = Bitmap.new(96, 96)
    update
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    update_tileset
    update_tilemap
    update_parallax
    update_characters
    update_shadow
    update_weather
    update_pictures
    update_timer
    update_viewports
    @colbox.x = $game_player.screen_x - 48
    @colbox.y = $game_player.screen_y - 64
    @colbox.bitmap.clear
    @colbox.bitmap.fill_rect(0, 0, 96, 96, Color.new(0, 0, 0, 64))
    cRec = $game_player.collision_rect.dup
    iRec = $game_player.interaction_rect.dup
    d = $game_player.direction
    cRec.set(cRec.x + 32, cRec.y + 32, cRec.width, cRec.height)
    @colbox.bitmap.fill_rect(cRec, Color.new(0, 255, 0, 128))
    #iRec.set(iRec.x + 32, iRec.y + 32, iRec.width, iRec.height)
    iRec.set(iRec.x + 32 + 32 * (d == 4 ? -1 : d == 6 ? 1 : 0), iRec.y + 32 + 32 * (d == 8 ? -1 : d == 2 ? 1 : 0), iRec.width, iRec.height)
    #iRec.set(iRec.x + 48 + 32 * (d == 4 ? -1 : d == 6 ? 1 : 0), iRec.y + 64 + 32 * (d == 8 ? -1 : d == 2 ? 1 : 0), iRec.width, iRec.height)
    @colbox.bitmap.fill_rect(iRec, Color.new(255, 0, 0, 128))
  end
end
=end
[/noae][/noae][/noae][/noae][/noae][/noae][/noae]

Video (http://youtu.be/ZqijOtzLgbA)

There are only a few things really missing, like diagonal sprites and diagonal interaction boxes, and the ability to adjust not only jump length (which is already implemented) but also jump height. I also want to add a method which allows people to move while jumping, and I might want to add some extra stuff, think things like being able to jump into pits (and getting damage from it or getting teleported somewhere else) or swimming, but I'll be seeing about those, as they're not really core elements. After that, I'll be making an Action RPG system to accompany this.

By the way, I also have other scripts available for it on Area91 (http://area91.multiverseworks.com) in case you're wondering.
Title: Re: Free Movement (off-the grid movement) WIP thread
Post by: Moosetroop11 on December 30, 2012, 01:57:56 AM
Ooooooo.

I never got further than rm2k3 so I can't test it out :p
Title: Re: Free Movement (off-the grid movement) WIP thread
Post by: Archem on December 30, 2012, 03:37:40 AM
I know I've already told you this, but you're awesome.
Title: Re: Free Movement (off-the grid movement) WIP thread
Post by: drenrin2120 on December 30, 2012, 04:43:46 AM
Works just fine, Gary! Just wondering, how does it handle cut-scenes? For example, move hero events?
Title: Re: Free Movement (off-the grid movement) WIP thread
Post by: GaryCXJk on December 30, 2012, 05:18:30 PM
Works just fine, Gary! Just wondering, how does it handle cut-scenes? For example, move hero events?
I absolutely have no idea. Those are things that need to be tested.

EDIT: Okay, so I've figured that I needed to modify the methods a bit so that everything moves with steps of 1 tile (32 pixels).
Title: Re: Free Movement (off-the grid movement) WIP thread
Post by: drenrin2120 on December 30, 2012, 07:40:06 PM
Also, I just noticed that touch event aren't triggered by the player when they're running.
Title: Re: Free Movement (off-the grid movement) WIP thread
Post by: GaryCXJk on January 03, 2013, 04:12:03 PM
Test video (http://youtu.be/C1y9oJPwK80)

I've got a new version done, finally.

Download WIP 1 (http://area91.multiverseworks.com/rmvxa/wip/free_movement_wip1.zip)
WIP 1 script (http://area91.multiverseworks.com/rmvxa/wip/free_movement_wip1.rb)
Title: Re: Free Movement (off-the grid movement) WIP thread
Post by: GaryCXJk on January 12, 2013, 04:06:50 AM
A little heads up, I decided to release this script.

http://area91.multiverseworks.com/index.php?action=rmvxa_script&view=free_movement (http://area91.multiverseworks.com/index.php?action=rmvxa_script&view=free_movement)