FPLE : First Person Labyrinth Explorer
#71
I fixed version without door calls for yami anti-lag, & added public to class game_map for private calls.

I'm currently trying to add a pool array with AI to help save FPS+50%, instead of hovering around 66 vs 99FPS+.
Code:
#===============================================================================
# First Person Labyrinth Explorer (FPLE) Engine - VX Ace version
# v.1.5
# Author: MGC
#===============================================================================
# This script allows the map to be displayed in first-person view,
# while maintaining tile-by-tile movement, similar to the
# Etrian Odyssey series (Nintendo DS/3DS). # THIS IS NOT A SCRIPT FOR MAKING AN FPS, BUT AN FPRPG.
#
# This is a port of version 1.5 of the FPLE for RPG Maker XP, with
# a few small additions.
#
# Features:
# - Can only be enabled on certain maps in the project
# - Forward/backward movement, left/right movement
# and 90° rotation
# - Ability to lower quality while moving to reduce lag
# - Translucency and blend type support
# for events
#
# Limitations:
# - Not really suitable for moving events from one tile to another.
# This should work, but use sparingly. # - Animations are displayed above everything
# - The player cannot jump or move diagonally
# - The resolution must be the default, i.e., 544px x 416px
# - No transparency for walls
# - No different heights: a single level for the floor and ceiling
#
# Instructions:
# This script must be placed below the original scripts but above "Main"
# It requires the MGC_FPLE_Ace_1_1.dll file
#
# - Map Creation
# Maps must be exported into the project from the dedicated editor,
# present in the Marta project starting with version 1.0.0
#
# - Events
# Events with a graphical representation are displayed
# vertically (like walls), centered, and always facing the
# player. The "Comment..." command allows you to control this display:
# "Type 0": Always faces the player (default)
# "Type 1": Horizontally oriented in the RM editor view (top view)
# "Type 2": Vertically oriented in the RM editor view (top view)
# "Stretch": The image is stretched to the dimensions of a wall (square)
# "Fit": The image is stretched to fit within the dimensions of a wall, the
# aspect ratio is maintained. Automatically applied if
# the dimensions exceed those of a wall (new in VX Ace)
# "Zoom X.X": zoom the image (new in VX Ace), for example, to double
# the dimensions, write: "Zoom 2.0"
# "V-Align 0": vertical alignment at the top (at ceiling level)
# "V-Align 1": vertical alignment in the center (default)
# "V-Align 2": vertical alignment at the bottom (at floor level)
# "D-Align 0": depth alignment in front/left, for types 1 and 2
# "D-Align 1": depth alignment in the center, for types 1 and 2 (default)
# "D-Align 2": depth alignment in the back/right, for types 1 and 2
# "1-Side": texture applied to one side only => appears mirrored behind
# "2-Side": texture applied to two side only => appears mirrored behind #Extra Unused Call To Code#
# "4-Side": texture applied to four sides to create the illusion of box
# "6-Side":  texture applied to four sides to create the illusion of grass.
# - Technical Parameters
# In the CONFIGURATION section at the beginning of the script, you will find the
# following constants whose values can be modified:
# - VIEW_DISTANCE: "Surfaces" (textured square polygons of fixed
# dimensions representing tiles and events) are only
# displayed within a certain distance around
# the player. This constant represents this distance,
# expressed in tiles (default value: 6). The higher
# this value, the greater the risk of lag.
# - RESOLUTION: Drawing the entire screen every frame requires
# a lot of resources. During movement, it is
# possible to use a lower screen resolution and the
# Render will then be zoomed to fit 544px x 416px.
# 0: Maximum resolution (544px*416px) Changed to (840px*560px)
# 1: Medium resolution (408px*312px)  Changed to (608px*417px)
# 2: Low resolution (272px*208px) Changed to (544px*417px)
# 3: Minimum resolution (204px*156px) Changed to (408px*278px)
# 4: Test resolution (204px*156px) Changed to (332px*222px)
# - ALWAYS_SAME_RES: If true, the same resolution will be used when stationary
# as when moving (see RESOLUTION)
# If false, the maximum resolution will be used when stationary
# (default value)
# - LIGHT_DISTANCE: To prevent surfaces from suddenly appearing on the screen,
# this constant represents the distance in tiles beyond
# which the opacity of the surfaces will be zero.
# The opacity change is gradual. If there is
# no parallax, the screen background is black and the
# surfaces will become increasingly darker with distance.
# A value of 0 disables this feature.
#
# - Commands that can be used in scripts (possible via the "Script..."
# command of an event):
# - $game_temp.set_view(new_view_distance)
# - $game_temp.set_light(new_light_distance)
# - $game_temp.increase_light
# - $game_temp.decrease_light
#============================================================================
module FPLE
  #--------------------------------------------------------------------------
  # * CONFIGURATION
  #--------------------------------------------------------------------------
  VIEW_DISTANCE = 22 # in tiles, > 0
  LIGHT_DISTANCE = 11 # in tiles, >=0, 0=deactivated
  RESOLUTION = 1 # quality when moving, 0=max, 1=medium, 2=low, 3=ugly
  ALWAYS_SAME_RES = true # if true, quality at stand = quality when moving
                          # if false, quality at stand = quality max
  SKIP_DISTANT_CHARS = true  # Skip character updates beyond view distance
  BATCH_SURFACE_UPDATES = true  # Update surfaces in batches
  AGGRESSIVE_CULLING = true     # More aggressive visibility culling
end
#==============================================================================
# ** FPLE Core
#==============================================================================
module FPLE
  #--------------------------------------------------------------------------
  # * Constantes
  #--------------------------------------------------------------------------
  MAP_SIDES = [4, 3, 5, 2]
  MAP_SIDES_LEFT = [5, 4, 2, 3]
  MAP_SIDES_RIGHT = [3, 2, 4, 5]
  PLAYER_MOVE_FORWARD = [[0, 1], [-1, 0], [1, 0], [0, -1]]
  PLAYER_MOVE_SPEED = [0, 1, 2, 4, 6, 8, 12]
  TURN_LEFT = [6, 2, 8, 4]
  COS_TABLE = [4096, 4095, 4094, 4090, 4086,
  4080, 4074, 4065, 4056, 4046, 4034, 4021, 4006, 3991, 3974,
  3956, 3937, 3917, 3896, 3873, 3849, 3824, 3798, 3770, 3742,
  3712, 3681, 3650, 3617, 3582, 3547, 3511, 3474, 3435, 3396,
  3355, 3314, 3271, 3228, 3183, 3138, 3091, 3044, 2996, 2946,
  2896, 2845, 2793, 2741, 2687, 2633, 2578, 2522, 2465, 2408,
  2349, 2290, 2231, 2171, 2110, 2048, 1986, 1923, 1860, 1796,
  1731, 1666, 1600, 1534, 1468, 1401, 1334, 1266, 1198, 1129,
  1060, 991, 921, 852, 782, 711, 641, 570, 499, 428,
  357, 286, 214, 143, 71, 0]
  RENDER = Win32API.new("MGC_FPLE_Ace_1_2", "RenderFPLE", "lll", "l") # [1.5]
  RENDER_ROT = Win32API.new("MGC_FPLE_Ace_1_2", "RenderFPLErot", "lll", "l") # [1.5]
  #--------------------------------------------------------------------------
  # * Retourne l'angle d'orientation
  # return Integer
  #--------------------------------------------------------------------------
  def self.angle
    return @angle
  end
  #--------------------------------------------------------------------------
  # * Retourne le décalage horizontal
  # return Integer
  #--------------------------------------------------------------------------
  def self.offset_x
    return @offset_x
  end
  #--------------------------------------------------------------------------
  # * Retourne le décalage en profondeur
  # return Integer
  #--------------------------------------------------------------------------
  def self.offset_y
    return @offset_y
  end
  #--------------------------------------------------------------------------
  # * Initialisation
  # param spriteset : FPLE::Spriteset_Map
  # param viewport : Viewport
  #--------------------------------------------------------------------------
  def self.initialize_fple(spriteset, viewport)
    @spriteset = spriteset
    @sprite_screen = Sprite.new(viewport)
    @sprite_move = Sprite.new(viewport)
    self.initialize_bitmaps
    @offset_x = 0
    @offset_y = 0
    @sprite_move.visible = false
    @lux_distance = $game_system.fple_light_distance << 5
    @angle = 0
    @trig = 0
    self.refresh_trig
    @count = 0
    $game_temp.force_render = true
    self.update
  end
  #--------------------------------------------------------------------------
  # * Création des objets Bitmap devant contenir le rendu 3D
  #--------------------------------------------------------------------------
  def self.initialize_bitmaps
    case $game_system.fple_resolution
    when 0
      width = 840
      height = 560
      coefficient_resolution = 0.75
    when 1
      width = 608
      height = 417
      coefficient_resolution = 1
    when 2
      width = 544
      height = 417
      coefficient_resolution = 1.133
    when 3
      width = 408
      height = 278
      coefficient_resolution = 1.5
    when 4
      width = 332
      height = 222
      coefficient_resolution = 2
    end
    if $game_system.fple_always_same_res
      @sprite_screen.bitmap = Bitmap.new(width, height)
      @sprite_screen.zoom_x = coefficient_resolution
      @sprite_screen.zoom_y = coefficient_resolution
    else
      @sprite_screen.bitmap = Bitmap.new(608, 417)
    end
    @sprite_move.bitmap = Bitmap.new(width, height)
    @sprite_move.zoom_x = coefficient_resolution
    @sprite_move.zoom_y = coefficient_resolution
  end
  #--------------------------------------------------------------------------
  # * Cherche les valeurs de sinus et cosinus en focntion de l'angle
  #--------------------------------------------------------------------------
  def self.refresh_trig
    @cos = FPLE::COS_TABLE[@angle]
    @sin = FPLE::COS_TABLE[91 - @angle]
  end
  #--------------------------------------------------------------------------
  # * Dispose
  #--------------------------------------------------------------------------
  def self.dispose
    @sprite_screen.dispose
    @sprite_move.dispose
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
def self.update
  # Run garbage collection periodically during non-movement to reduce memory spikes.
  if !$game_temp.movement && Graphics.frame_count % 120 == 0
    GC.start
  end

  # Check if the player is actively moving or changing the view.
  if $game_temp.movement
    case $game_temp.movement_dir
    when 0 # fading distance + 1
      self.increase_fading_distance
    when 1 # fading distance - 1
      self.decrease_fading_distance
    when 3 # set fading distance
      self.set_fading_distance
    when 2 # Move backward
      self.move_backward
    when 4 # Strafe left
      self.strafe_left
    when 6 # Strafe right
      self.strafe_right
    when 8 # Move forward
      self.move_forward
    when 9 # Turn right
      self.turn_right
    when 10 # turn left
      self.turn_left
    end
  
    # Apply method 2 refactoring for sprite and rendering logic
    sprite_moving = $game_temp.movement
    active_sprite = sprite_moving ? @sprite_move : @sprite_screen
    inactive_sprite = sprite_moving ? @sprite_screen : @sprite_move
    active_sprite.visible = true
    inactive_sprite.visible = false
   if @angle != 0
      # Use lighter background color during rotation to prevent bleeding
      bg_color = Color.new(115, 190, 215, 255)  # Much lighter sky blue background
      active_sprite.bitmap.fill_rect(0, 0, active_sprite.bitmap.width,
                                     active_sprite.bitmap.height, bg_color)
    end
    # Clear the active sprite bitmap
    active_sprite.bitmap.clear unless $game_temp.movement_dir == 9 || $game_temp.movement_dir == 10
    # Determine if we should use rotation (only during movement and when angle != 0)
    use_rotation = sprite_moving && @angle != 0
    if use_rotation
      params = [@offset_y, @offset_x, @lux_distance, $game_player.direction, @trig, @cos, @sin, $game_map.fple_map.texturesets]
      FPLE::RENDER_ROT.call(active_sprite.bitmap.__id__, $game_map.surfaces.__id__, params.__id__)
    else
      params = [@offset_y, @offset_x, @lux_distance, $game_player.direction, $game_map.fple_map.texturesets]
      FPLE::RENDER.call(active_sprite.bitmap.__id__, $game_map.surfaces.__id__, params.__id__)
    end
    # Handle post-movement logic
    unless sprite_moving
      $game_player.update_nonmoving($game_temp.last_moving) if $game_temp.last_moving
    end
  # This block handles rendering when the player is not moving, but other
  # events or conditions (like character surfaces) need updating.
  elsif $game_player.moving? || $game_temp.force_render
    # Process updates for characters and events.
    $game_map.refresh_surfaces
   
    # Extract common character update logic to avoid duplication
    update_character = lambda do |event|
      # Only process if SKIP_DISTANT_CHARS is enabled
      return unless FPLE::SKIP_DISTANT_CHARS
     
      dist_x = ($game_player.real_x / 32) - (event.x)
      dist_y = ($game_player.real_y / 32) - (event.y)
      distance = Math.sqrt(dist_x**2 + dist_y**2)
      return if distance > FPLE::VIEW_DISTANCE
     
      event.update_fple_surface
    end
    # === START OF BATCH AND CULLING FIX ===
    # Updates characters in batches to avoid lag spikes, with optional distance skipping.
    if FPLE::BATCH_SURFACE_UPDATES
      $game_map.events.values.each_with_index do |event, i|
        # Only update a fraction of the events each frame.
        if @count % 5 == i % 5
          update_character.call(event)
        end
      end
    else
      # Fallback for no batching.
      $game_map.events.each_value(&update_character)
    end
    # === END OF BATCH AND CULLING FIX ===
  
    $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
    @sprite_screen.bitmap.clear
   
    # Use consistent parameter structure
    params = [@offset_y, @offset_x, @lux_distance, $game_player.direction, $game_map.fple_map.texturesets]
    FPLE::RENDER.call(@sprite_screen.bitmap.__id__, $game_map.surfaces.__id__, params.__id__)
   
    $game_temp.force_render = false
    @count += 1
  end
end
  #--------------------------------------------------------------------------
  # * Frame Update - Augmentation de la distance d'éclairage
  #--------------------------------------------------------------------------
  def self.increase_fading_distance
    @count += 2
    @lux_distance += 2
    if @count == 32
      @count = 0
      $game_temp.movement = false
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Diminution de la distance d'éclairage
  #--------------------------------------------------------------------------
  def self.decrease_fading_distance
    if @lux_distance == 0
      @count = 0
      $game_temp.movement = false
    else
      @count += 2
      @lux_distance -= 2
      if @count == 32
        @count = 0
        $game_temp.movement = false
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Définition d'une nouvelle distance d'éclairage
  #--------------------------------------------------------------------------
  def self.set_fading_distance
    @lux_distance = $game_system.fple_light_distance << 5
    $game_temp.movement = false
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Déplacement en arrière
  #--------------------------------------------------------------------------
  def self.move_backward
    unless $game_temp.movement_init
      $game_map.refresh_surfaces
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
      $game_temp.movement_init = true
      @offset_y = 128
    end
    @offset_y -= $game_player.move_speed_fple
    if @offset_y <= 0
      @offset_y = 0
      $game_temp.movement = false
      $game_temp.last_moving = true
      $game_map.refresh_surfaces
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Déplacement latéral vers la gauche
  #--------------------------------------------------------------------------
  def self.strafe_left
    unless $game_temp.movement_init
      $game_map.refresh_surfaces
      $game_map.refresh_surfaces_strafe(0)
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
      $game_temp.movement_init = true
      @offset_x = 128
    end
    @offset_x -= $game_player.move_speed_fple
    if @offset_x <= 0
      @offset_x = 0
      $game_temp.movement = false
      $game_temp.last_moving = true
      $game_map.refresh_surfaces
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Déplacement latéral vers la droite
  #--------------------------------------------------------------------------
  def self.strafe_right
    unless $game_temp.movement_init
      $game_map.refresh_surfaces_strafe(-1)
      $game_temp.movement_init = true
    end
    @offset_x += $game_player.move_speed_fple
    if @offset_x >= 128
      @offset_x = 0
      $game_temp.movement = false
      $game_temp.last_moving = true
      $game_map.refresh_surfaces
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Déplacement en avant
  #--------------------------------------------------------------------------
  def self.move_forward
    @offset_y += $game_player.move_speed_fple
    if @offset_y >= 128
      @offset_y = 0
      $game_temp.movement = false
      $game_temp.last_moving = true
      $game_map.refresh_surfaces
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Rotation vers la droite
  #--------------------------------------------------------------------------
  def self.turn_right
    unless $game_temp.movement_init
      $game_map.refresh_surfaces_turn_right(@spriteset.character_surfaces)
      $game_temp.movement_init = true
    end
    @angle += 5
    @trig = 1
    refresh_trig
    if @angle == 90
      @angle = 0
      $game_temp.movement = false
      $game_map.refresh_surfaces
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update - Rotation vers la gauche
  #--------------------------------------------------------------------------
  def self.turn_left
    unless $game_temp.movement_init
      $game_map.refresh_surfaces_turn_left(@spriteset.character_surfaces)
      $game_temp.movement_init = true
      @angle = 90
    end
    @angle -= 5
    @trig = 0
    refresh_trig
    if @angle == 0
      $game_temp.movement = false
      $game_map.refresh_surfaces
      $game_map.refresh_character_surfaces(@spriteset.character_surfaces)
    end
  end
end
#==============================================================================
# ** DataManager
#==============================================================================
module DataManager
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility)
  #--------------------------------------------------------------------------
  class << self
    unless @already_aliased_fple
      alias save_game_without_rescue_fple save_game_without_rescue
      @already_aliased_fple = true
    end
  end
  #--------------------------------------------------------------------------
  # * Execute Save (No Exception Processing)
  #--------------------------------------------------------------------------
  def self.save_game_without_rescue(index)
    if $game_map.fple_map # [1.2]
      textureset = $game_map.fple_map.textureset
      texturesets = $game_map.fple_map.texturesets
      $game_map.fple_map.textureset = nil
      $game_map.fple_map.texturesets = nil
    end
    rc = save_game_without_rescue_fple(index)
    if $game_map.fple_map # [1.2]
      $game_map.fple_map.texturesets = texturesets
      $game_map.fple_map.textureset = textureset
    end
    return rc
  end
end
#============================================================================
# ** Game_System
#============================================================================
class Game_System
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility)
  #--------------------------------------------------------------------------
  unless @already_aliased_fple
    alias initialize_fple initialize
    @already_aliased_fple = true
  end
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :fple # Boolean
  attr_accessor :fple_view_distance # Integer (>0)
  attr_accessor :fple_light_distance # Integer (>=0, 0:deactivated)
  attr_accessor :fple_resolution # Integer (0:max, 1:medium, 2:low, 3:ugly)
  attr_accessor :fple_always_same_res # Boolean
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    initialize_fple
    self.fple = false
    self.fple_view_distance = FPLE::VIEW_DISTANCE
    self.fple_light_distance = FPLE::LIGHT_DISTANCE
    self.fple_resolution = FPLE::RESOLUTION
    self.fple_always_same_res = FPLE::ALWAYS_SAME_RES
  end
end
#==============================================================================
# ** Game_Temp
#==============================================================================
class Game_Temp
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility)
  #--------------------------------------------------------------------------
  unless @already_aliased_fple
    alias initialize_fple initialize
    @already_aliased_fple = true
  end
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :movement_init # Boolean
  attr_accessor :movement # Boolean
  attr_accessor :movement_dir # Integer {2, 4, 6, 8}
  attr_accessor :last_moving # Boolean
  attr_accessor :force_render # Boolean
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    initialize_fple
    self.movement_init = false
    self.movement = false
    self.movement_dir = 8
    self.last_moving = false
    self.force_render = false
  end
  #--------------------------------------------------------------------------
  # * Set view distance
  # param distance : Integer
  #--------------------------------------------------------------------------
  def set_view(distance)
    if distance < 0 then distance = 0 end
    $game_system.fple_view_distance = distance
    self.force_render = true
  end
  #--------------------------------------------------------------------------
  # * Set light distance
  # param distance : Integer
  #--------------------------------------------------------------------------
  def set_light(distance)
    if distance < 0 then distance = 0 end
    $game_system.fple_light_distance = distance
    self.movement = true
    self.movement_dir = 3
  end
  #--------------------------------------------------------------------------
  # * Increase light distance
  #--------------------------------------------------------------------------
  def increase_light
    $game_system.fple_light_distance += 1
    self.movement = true
    self.movement_dir = 0
  end
  #--------------------------------------------------------------------------
  # * Decrease light distance
  #--------------------------------------------------------------------------
  def decrease_light
    if $game_system.fple_light_distance > 0
      $game_system.fple_light_distance -= 1
    end
    self.movement = true
    self.movement_dir = 1
  end
end
#============================================================================
# ** Game_Map
#============================================================================
class Game_Map
public
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility)
  #--------------------------------------------------------------------------
  unless @already_aliased_fple
    alias initialize_fple initialize
    alias setup_fple setup
    alias refresh_fple refresh
    @already_aliased_fple = true
  end
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :surfaces # Array<Array<Integer>>
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    initialize_fple
    self.surfaces = []
    @x_ref = 0
    @y_ref = 0
    @distance_cache = {}
    @last_player_pos = [0, 0]
  end
  #--------------------------------------------------------------------------
  # * Setup
  # param map_id : Integer
  #--------------------------------------------------------------------------
  def setup(map_id)
    @map_id = map_id
    @map = load_data(sprintf("Data/Map%03d.rvdata2", @map_id))
    if is_fple?
      load_fple_map
      $game_system.fple = true
    elsif $game_system.fple
      $game_system.fple = false
    end
    setup_fple(map_id)
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    refresh_fple
    if is_fple?
      unless @fple_map.textureset
        load_fple_map
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Vérifie s'il s'agit d'une carte FPLE
  # return String
  #--------------------------------------------------------------------------
  def is_fple?
    return @map.note[/\[fple:\w+\]/]
  end
  #--------------------------------------------------------------------------
  # * Charge la carte FPLE associée
  #--------------------------------------------------------------------------
  def load_fple_map
    if @fple_map then @fple_map.dispose end
    @map.note[/\[fple:(\w+)\]/]
    File.open('Data_FPLE/' + $1 + '.fple', "rb") {|file|
      map_data = Marshal.load(file)
      @fple_map = FPLE::Map.new(map_data.width, map_data.height,
      map_data.map_name, map_data.tileset_name, map_data.data,
      map_data.subsets_mapping, map_data.textureset_data)
    }
  end
  #--------------------------------------------------------------------------
  # * Retourne la carte FPLE associée
  # return FPLE::Map
  #--------------------------------------------------------------------------
  def fple_map
    @fple_map
  end
  #--------------------------------------------------------------------------
  # * Retourne le nom du tileset
  # return String
  #--------------------------------------------------------------------------
  def tileset_name
    if $game_system.fple
      return @fple_map.tileset_name
    else
      return ""
    end
  end
end
  #--------------------------------------------------------------------------
   def cached_distance(event_id, event_x, event_y)
    player_pos = [$game_player.x, $game_player.y]
   
    # Clear cache if player moved
    if @last_player_pos != player_pos
      @distance_cache.clear
      @last_player_pos = player_pos
    end
   
    cache_key = [event_id, event_x, event_y]
    return @distance_cache[cache_key] if @distance_cache.has_key?(cache_key)
   
    dist_x = (player_pos[0] - event_x).abs
    dist_y = (player_pos[1] - event_y).abs
    distance = Math.sqrt(dist_x * dist_x + dist_y * dist_y)
   
    @distance_cache[cache_key] = distance
    return distance
  end
    #--------------------------------------------------------------------------
  # * Recherche les surfaces visibles de la carte en fonction de
  # l'orientation du joueur
    #--------------------------------------------------------------------------
  def refresh_surfaces
    @current_dir = $game_player.direction
    self.surfaces = []
    @x_ref = $game_player.x
    @y_ref = $game_player.y
    case $game_player.direction
    when 2
      surfaces_temp = refresh_surfaces_down
    when 4
      surfaces_temp = refresh_surfaces_left
    when 6
      surfaces_temp = refresh_surfaces_right
    when 8
      surfaces_temp = refresh_surfaces_up
    end
  
    # set surfaces z attributes with z-fighting prevention
    surfaces_temp.each {|surface| set_surface_z(surface)}
    surfaces.concat(surfaces_temp)
  
    # Enhanced sorting with z-fighting prevention
    if @last_surface_count != surfaces.length || $game_temp.force_render
      surfaces.sort! { |a, b|
        z_diff = b[5] - a[5]
        if z_diff.abs < 0.001 # Very close z-values
          # Use additional criteria for stable sorting
          secondary = (b[1].abs + b[2].abs) - (a[1].abs + a[2].abs)
          secondary.zero? ? b.object_id <=> a.object_id : secondary
        else
          z_diff <=> 0
        end
      }
      @last_surface_count = surfaces.length
    end
  end
     def add_character_surfaces_down_with_rotation_fix(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      dy = surface.dy - ($game_player.y << 7)
      dx = ($game_player.x << 7) - surface.dx
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 2)
    end
    return surfaces_temp
  end
  def add_character_surfaces_up_with_rotation_fix(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      dy = ($game_player.y << 7) - surface.dy
      dx = surface.dx - ($game_player.x << 7)
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 8)
    end
    return surfaces_temp
  end
  def add_character_surfaces_left_with_rotation_fix(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      dy = ($game_player.x << 7) - surface.dx
      dx = ($game_player.y << 7) - surface.dy
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 4)
    end
    return surfaces_temp
  end
  def add_character_surfaces_right_with_rotation_fix(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      dy = surface.dx - ($game_player.x << 7)
      dx = surface.dy - ($game_player.y << 7)
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 6)
    end
    return surfaces_temp
  end
  #--------------------------------------------------------------------------
  # * Add surfaces for single-sided events only during rotation
  #--------------------------------------------------------------------------
  def add_character_surfaces_down_single_only(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      # Only process single-sided events
      #next if surface.character.fple_four_side || surface.character.fple_six_side || surface.character.fple_two_side
      dy = surface.dy - ($game_player.y << 7)
      dx = ($game_player.x << 7) - surface.dx
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 2)
    end
    return surfaces_temp
  end
  def add_character_surfaces_up_single_only(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      # Only process single-sided events
      #next if surface.character.fple_four_side || surface.character.fple_six_side || surface.character.fple_two_side
      dy = ($game_player.y << 7) - surface.dy
      dx = surface.dx - ($game_player.x << 7)
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 8)
    end
    return surfaces_temp
  end
def add_character_surfaces_left_single_only(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      # Only process single-sided events
      #next if surface.character.fple_four_side || surface.character.fple_six_side || surface.character.fple_two_side
      dy = ($game_player.x << 7) - surface.dx
      dx = ($game_player.y << 7) - surface.dy
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 4)
    end
    return surfaces_temp
  end
  def add_character_surfaces_right_single_only(character_surfaces)
    surfaces_temp = []
    i = $game_system.fple_view_distance
    character_surfaces.each do |surface|
      next if surface.displayed # Skip if already processed
      # Only process single-sided events
      #next if surface.character.fple_four_side || surface.character.fple_six_side || surface.character.fple_two_side
      dy = surface.dx - ($game_player.x << 7)
      dx = surface.dy - ($game_player.y << 7)
      add_character_surfaces(surface, dy, dx, surfaces_temp, i, 6)
    end
    return surfaces_temp
  end

      def add_two_sided_surfaces(surface, dy, dx, surfaces_temp, i, dir)
    # Handle special case where player is at same position as box
        j = i << 7
    if dy.between?(128, j) && dx.between?(-j, j)
    event_id = surface.character.id rescue 0
    base_z_offset = (event_id % 1000) * 0.0001
    distance = Math.sqrt(dy * dy + dx * dx)
  
    # Determine visible faces based on position
      faces_to_render = []
  
    # Front face (player looking at object)
    if dy > 32
      faces_to_render << {type: 1, d_align: 0, z_priority: 5, name: :front}
    end
  
    # Back face
    if dy < -32
      faces_to_render << {type: 1, d_align: 2, z_priority: 0, name: :back}
    end
  
    # Left face - FIXED: Use proper alignment values
    if dx < -32
      faces_to_render << {type: 2, d_align: 0, z_priority: 4, name: :left}
    end
  
    # Right face - FIXED: Use proper alignment values
    if dx > 32
      faces_to_render << {type: 2, d_align: 2, z_priority: 1, name: :right}
    end
  
    # For very close viewing, always show some faces
    if distance < 96
      if faces_to_render.empty?
        faces_to_render << {type: 1, d_align: 0, z_priority: 5, name: :front}
        faces_to_render << {type: 2, d_align: 0, z_priority: 4, name: :left}
      end
    end
  
    # Render at least one face
    if faces_to_render.empty?
      faces_to_render << {type: 1, d_align: 0, z_priority: 5, name: :front}
    end
    end
  
    # Render the faces
    faces_to_render.each_with_index do |face_config, face_index|
      side_dy = dy
      side_dx = dx
    
      # FIXED: Apply position adjustments with proper bounds checking
      if face_config[:type] == 1 # Horizontal face
        if face_config[:d_align] == 0 # Front
          side_dy -= 64
        elsif face_config[:d_align] == 2 # Back
          side_dy += 64
        end
      elsif face_config[:type] == 2 # Vertical face
        if face_config[:d_align] == 0 # Left
          side_dx -= 64
        elsif face_config[:d_align] == 2 # Right
          side_dx += 64
        end
      end
    
      # Calculate z-modifier
      z_modifier = base_z_offset + (face_config[:z_priority] * 0.008) + (face_index * 0.001)
    
      surface_data = [0, side_dy, side_dx, 5, 0, 0,
                      surface.bitmap.__id__, face_config[:type],
                      surface.character.fple_v_align, surface.character.fple_stretch,
                      surface.opacity, surface.blend_type,
                      surface.fit, surface.zoom, 0, z_modifier]
    
      surfaces_temp.push(surface_data)
    end
  
    surface.displayed = true
  end
  #--------------------------------------------------------------------------
  # * FIXED: Four-sided surfaces - ensure proper type assignment
  #--------------------------------------------------------------------------
  def add_four_sided_surfaces(surface, dy, dx, surfaces_temp, i, dir)
    # Handle special case where player is at same position as box
        j = i << 7
    if dy.between?(128, j) && dx.between?(-j, j)
    event_id = surface.character.id rescue 0
    base_z_offset = (event_id % 1000) * 0.0001
  
    # Always render all 4 sides for 4-sided boxes
    faces_to_render = [
      {type: 1, d_align: 0, z_priority: 3, name: :front},  # Front
      {type: 1, d_align: 2, z_priority: 0, name: :back},   # Back
      {type: 2, d_align: 0, z_priority: 3, name: :left},     # Left
      {type: 2, d_align: 2, z_priority: 1, name: :right},    # Right
    ]
  
    # Render all faces with strafe-safe positioning
    faces_to_render.each_with_index do |face_config, face_index|
      side_dy = dy
      side_dx = dx
    
      # FIXED: Apply position adjustments with bounds checking and strafe compensation
      if face_config[:type] == 1 # Horizontal face
        if face_config[:d_align] == 0 # Front
          side_dy -= 64
        elsif face_config[:d_align] == 2 # Back
          side_dy += 64
        end
      elsif face_config[:type] == 2 # Vertical face
        if face_config[:d_align] == 0 # Left
          side_dx -= 64
        elsif face_config[:d_align] == 2 # Right
          side_dx += 64
        end
      end
    
      # Calculate z-modifier
      z_modifier = base_z_offset + (face_config[:z_priority] * 0.01) + (face_index * 0.001)
    
      surface_data = [0, side_dy, side_dx, 5, 0, 0,
                      surface.bitmap.__id__, face_config[:type],
                      surface.character.fple_v_align, surface.character.fple_stretch,
                      surface.opacity, surface.blend_type,
                      surface.fit, surface.zoom, 0, z_modifier]
    
      surfaces_temp.push(surface_data)
    end
  
    surface.displayed = true
  end
end
  #--------------------------------------------------------------------------
  # * FIXED: Six-sided surfaces - ensure proper type assignment
  #--------------------------------------------------------------------------
def add_six_sided_surfaces(surface, dy, dx, surfaces_temp, i, dir)
    j = i << 7
    if dy.between?(128, j) && dx.between?(-j, j)
    event_id = surface.character.id rescue 0
    base_z_offset = (event_id % 1000) * 0.0001
  
    # Render all 6 sides for maximum visibility
  
      faces_to_render = [
      {type: 0, d_align: 1, z_priority: 2, name: :center_h}, # Center horizontal
      {type: 1, d_align: 0, z_priority: 4, name: :front},    # Front
      {type: 1, d_align: 2, z_priority: 0, name: :back},     # Back
      {type: 2, d_align: 0, z_priority: 3, name: :left},    # Left
      {type: 2, d_align: 2, z_priority: 1, name: :right},  # Right
    ]
    # Render all faces with strafe protection
    faces_to_render.each_with_index do |face_config, face_index|
      side_dy = dy
      side_dx = dx
    
      # FIXED: Apply position adjustments with strafe-aware bounds checking
      if face_config[:type] == 1 # Horizontal face
        if face_config[:d_align] == 0 # Front
          side_dy -= 64
        elsif face_config[:d_align] == 2 # Back
          side_dy += 64
        end
      elsif face_config[:type] == 2 # Vertical face
        if face_config[:d_align] == 0 # Left
          side_dx -= 64
        elsif face_config[:d_align] == 2 # Right
          side_dx += 64
        end
      end
    
      # Calculate z-modifier
      z_modifier = base_z_offset + (face_config[:z_priority] * 0.008) + (face_index * 0.001)
    
      surface_data = [0, side_dy, side_dx, 5, 0, 0,
                      surface.bitmap.__id__, face_config[:type],
                      surface.character.fple_v_align, surface.character.fple_stretch,
                      surface.opacity, surface.blend_type,
                      surface.fit, surface.zoom, 0, z_modifier]
    
      surfaces_temp.push(surface_data)
    end
  
    surface.displayed = true
  end
  end
    # Render all 6 sides for maximum visibility
  #--------------------------------------------------------------------------
  # * Calcul de la priorité d'affichage d'une surface
  # param surface : Array<Integer>
  #--------------------------------------------------------------------------
def set_surface_z(surface)
  if surface[3] < 5 # map surfaces
    surface[5] = (surface[1].abs << 5) + surface[2].abs
  else # character surfaces
    base_z = (surface[1].abs >> 1) + (surface[2].abs >> 6) - 2
  
    # Check if surface has z-modifier (from 4-side/6-side rendering)
    z_modifier = surface[15] || 0  # Additional z-modifier if present
  
    # Add small offset based on surface type and alignment
    type_offset = surface[7] || 0  # Surface type
    align_offset = surface[8] || 0 # V-align
  
    # Create unique z-values for different surface configurations
    z_offset = case type_offset
    when 1 # Horizontal walls
      case surface[2] <=> 0 # dx comparison
      when -1 then 0.4  # Left side
      when 0 then 0.2   # Center
      when 1 then 0.0   # Right side
      end
    when 2 # Vertical walls
      case surface[1] <=> 0 # dy comparison
      when -1 then 0.3  # Front
      when 0 then 0.1   # Center
      when 1 then -0.1  # Back
      end
    else
      0 # Default for type 0
    end
  
    # Additional offset for vertical alignment
    v_align_offset = case align_offset
    when 0 then 0.03  # Top
    when 1 then 0.01  # Middle
    when 2 then -32.01 # Bottom
    else 0
    end
  
    surface[5] = base_z.to_f + z_offset + v_align_offset + z_modifier
  end
end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces visibles des évènements en fonction de
  # l'orientation du joueur
  # param character_surfaces : Array<FPLE::Surface_Characters>
  #--------------------------------------------------------------------------
# This method updates the list of character surfaces based on visibility and player direction.
public
def refresh_character_surfaces(character_surfaces)
    return if character_surfaces.empty?
  
    max_dist = ($game_system.fple_view_distance || 10) + 3  # Large buffer
    visible_surfaces = character_surfaces.select do |surface|
      next false unless surface.character && surface.visible && surface.opacity > 0
    
      # Very generous distance-based filtering
      char = surface.character
      dist_x = ($game_player.x - char.x).abs
      dist_y = ($game_player.y - char.y).abs
    
      dist_x <= max_dist && dist_y <= max_dist
    end
  
    visible_surfaces = character_surfaces.select { |s|
      s.character && s.visible && s.opacity > 0
    }
  
    clear_character_surfaces(visible_surfaces)
  
    # During rotation, we need to render from multiple directions
    if FPLE.angle > 0
      # For multi-sided events during rotation, only render once from current direction
      # to prevent duplicate/rotating surfaces
      current_surfaces = case $game_player.direction
      when 2 then add_character_surfaces_down_with_rotation_fix(visible_surfaces)
      when 4 then add_character_surfaces_left_with_rotation_fix(visible_surfaces)
      when 6 then add_character_surfaces_right_with_rotation_fix(visible_surfaces)
      when 8 then add_character_surfaces_up_with_rotation_fix(visible_surfaces)
      end
    
      # Only add next direction surfaces for single-sided events
      clear_character_surfaces_for_single_sided(visible_surfaces)
      next_surfaces = case $game_player.direction
      when 2 then add_character_surfaces_right_single_only(visible_surfaces)
      when 4 then add_character_surfaces_down_single_only(visible_surfaces)
      when 6 then add_character_surfaces_up_single_only(visible_surfaces)
      when 8 then add_character_surfaces_left_single_only(visible_surfaces)
      end
    
      surfaces_temp = current_surfaces + next_surfaces
    else
      # Normal rendering - single direction
      surfaces_temp = case $game_player.direction
      when 2 then add_character_surfaces_down(visible_surfaces)
      when 4 then add_character_surfaces_left(visible_surfaces)
      when 6 then add_character_surfaces_right(visible_surfaces)
      when 8 then add_character_surfaces_up(visible_surfaces)
      else []
      end
    end
    surfaces_temp.each { |surface| set_surface_z(surface) }
    @surfaces.concat(surfaces_temp)
    #Old#@surfaces.sort! { |a, b| b[5] - a[5] }

      # Enhanced sorting
    @surfaces.sort! do |a, b|
      z_diff = b[5] - a[5]
      if z_diff.abs < 0.0001
        (b[1].abs + b[2].abs) - (a[1].abs + a[2].abs)
      else
        z_diff <=> 0
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Réinitialise l'indicateur d'affichage des surfaces liées aux évènements
  # param character_surfaces : Array<FPLE::Surface_Characters>
  #--------------------------------------------------------------------------
  def clear_character_surfaces(character_surfaces)
    character_surfaces.each {|surface| surface.displayed = false}
  end

  def clear_character_surfaces_for_single_sided(character_surfaces)
    character_surfaces.each do |surface|
      # Only clear single-sided events - keep multi-sided events as displayed
      unless surface.character.fple_four_side || surface.character.fple_six_side || surface.character.fple_two_side
        surface.displayed = false
      end
    end
  end

  def cleanup_distant_surfaces
    return unless @surfaces
  
    # Only cleanup if we have a lot of surfaces
    max_surfaces = 300 # Very generous limit
    if @surfaces.length > max_surfaces
      # Keep only the closest surfaces
      @surfaces.sort_by! { |s| s[1].abs + s[2].abs }
      @surfaces = @surfaces.first(max_surfaces)
    end
  end
  #--------------------------------------------------------------------------
  # * Vérifie la visibilité d'une surface liée à un évènement et l'ajoute
  # à la liste des surfaces à afficher le cas échéant
  # param surface : FPLE::Surface_Characters
  # param dy : Integer
  # param dx : Integer
  # param surfaces_temp : Array<Array<integer>>
  # param i : Integer
  # param dir : Integer
  #--------------------------------------------------------------------------
# This method adds a character surface to the list for rendering if it is visible.
# `surface`: The FPLE surface object for the character.
# `dy`: The change in y-coordinate from the player.
# `dx`: The change in x-coordinate from the player.
# `surfaces_temp`: The array to which the new surface will be added.
# `i`: The view distance index used for calculation.
# `dir`: The player's direction.
def add_character_surfaces(surface, dy, dx, surfaces_temp, i, dir)
  return unless surface.character && surface.character.fple_type

  # Distance culling - more generous for multi-sided events
  distance_sq = dy * dy + dx * dx
  max_distance = $game_system.fple_view_distance || 10

  # Use larger buffer for multi-sided events to prevent edge clipping
  if surface.character.fple_four_side || surface.character.fple_six_side || surface.character.fple_two_side
    rotation_buffer = FPLE.angle > 0 ? 1.5 : 1.3
  else
    rotation_buffer = FPLE.angle > 0 ? 2.0 : 1.0
  end

  max_distance_sq = (max_distance * 96 * rotation_buffer) ** 2
  return if distance_sq > max_distance_sq

  # Early opacity check
  return if surface.opacity <= 0

  # Handle multi-sided boxes
  if surface.character.fple_two_side
    add_two_sided_surfaces(surface, dy, dx, surfaces_temp, i, dir)
    return
  elsif surface.character.fple_four_side
    add_four_sided_surfaces(surface, dy, dx, surfaces_temp, i, dir)
    return
  elsif surface.character.fple_six_side
    add_six_sided_surfaces(surface, dy, dx, surfaces_temp, i, dir)
    return
  end

  # Original single-surface logic for regular events
  j = i << 7
  if dy == 0 && dx == 0 && surface.character.fple_type == 0 && surface.character.fple_one_side == false
    surface.set_relative_attributes(dir)
    safe_dy = 128
    surface_data = [0, safe_dy, 0, 5, 0, 0,
                    surface.bitmap.__id__,
                    0, # Keep type 0 for face-player events at same position
                    surface.character.fple_v_align,
                    surface.character.fple_stretch,
                    surface.opacity,
                    surface.blend_type,
                    surface.fit,
                    surface.zoom,
                    surface.mirror]
    surfaces_temp.push(surface_data)
    surface.displayed = true
    elsif dy.between?(128, j) && dx.between?(-j, j)
    # Position adjustments for wall types
    if surface.v_align == 2
      dy -= 16 unless surface.fit == 1
    end
  
    surface.set_relative_attributes(dir)
  
    if surface.type == 1
      if surface.d_align == 0
        dy -= 64
      elsif surface.d_align == 2
        dy += 64
      elsif surface.d_align == 1 && surface.v_align == 2
        dy -= 16 unless $game_player.moving?
      end
    elsif surface.type == 2
      if surface.d_align == 0
        dx -= 64
      elsif surface.d_align == 2
        dx += 64
      elsif surface.d_align == 1 && surface.v_align == 2
        dx -= 16 unless $game_player.moving?
      end
    end
  
    surface_data = [0, dy, dx, 5, 0, 0,
                    surface.bitmap.__id__, surface.type,
                    surface.v_align, surface.character.fple_stretch,
                    surface.opacity, surface.blend_type,
                    surface.fit, surface.zoom, surface.mirror]
    surfaces_temp.push(surface_data)
    surface.displayed = true
  end
end
  #--------------------------------------------------------------------------
  def add_two_sided_surfaces(surface, dy, dx, surfaces_temp, i, dir)
    # Handle special case where player is at same position as box
    if dx == 0 && dy == 0
      # For multi-sided boxes at same position, render as face-player type
      surfaces_temp.push([0, 0, 0, 5, 0, 0,
                          surface.bitmap.__id__,
                          0, # Face player type for same position
                          surface.character.fple_v_align,
                          surface.character.fple_stretch,
                          surface.opacity,
                          surface.blend_type,
                          surface.fit,
                          surface.zoom,
                          0]) # No mirror
      surface.displayed = true
      return
    end
  
    event_id = surface.character.id rescue 0
    base_z_offset = (event_id % 1000) * 0.0001
    distance = Math.sqrt(dy * dy + dx * dx)
  
    # Determine visible faces based on position
    faces_to_render = []
  
    # Front face (player looking at object)
    if dy > 32
      faces_to_render << {type: 1, d_align: 0, z_priority: 5, name: :front}
    end
  
    # Back face
    if dy < -32
      faces_to_render << {type: 1, d_align: 2, z_priority: 0, name: :back}
    end
  
    # Left face
    if dx < -32
      faces_to_render << {type: 2, d_align: 0, z_priority: 4, name: :left}
    end
  
    # Right face
    if dx > 32
      faces_to_render << {type: 2, d_align: 2, z_priority: 1, name: :right}
    end
  
    # For very close viewing, always show some faces
    if distance < 96
      if faces_to_render.empty?
        faces_to_render << {type: 1, d_align: 0, z_priority: 5, name: :front}
        faces_to_render << {type: 2, d_align: 0, z_priority: 4, name: :left}
      end
    end
  
    # Render at least one face
    if faces_to_render.empty?
      faces_to_render << {type: 1, d_align: 0, z_priority: 5, name: :front}
    end
  
    # Render the faces
    faces_to_render.each_with_index do |face_config, face_index|
      side_dy = dy
      side_dx = dx
    
      # Apply position adjustments
      if face_config[:type] == 1 # Horizontal face
        if face_config[:d_align] == 0 # Front
          side_dy -= 64
        elsif face_config[:d_align] == 2 # Back
          side_dy += 64
        end
      elsif face_config[:type] == 2 # Vertical face
        if face_config[:d_align] == 0 # Left
          side_dx -= 64
        elsif face_config[:d_align] == 2 # Right
          side_dx += 64
        end
      end
    
      # Calculate z-modifier
      z_modifier = base_z_offset + (face_config[:z_priority] * 0.008) + (face_index * 0.001)
    
      surface_data = [0, side_dy, side_dx, 5, 0, 0,
                      surface.bitmap.__id__, face_config[:type], # NEVER type 0
                      surface.character.fple_v_align, surface.character.fple_stretch,
                      surface.opacity, surface.blend_type,
                      surface.fit, surface.zoom, 0, z_modifier] # No mirror for multi-sided
    
      surfaces_temp.push(surface_data)
    end
  
    surface.displayed = true
  end
#--------------------------------------------------------------------------
def should_render_one_side_event(character, player_dir)
  return true unless character.fple_one_side

  # Get the event's original type
  original_type = character.fple_type

  case original_type
  when 0 # Always face player - render from all directions
    return true
  when 1 # Horizontal wall in editor view
    # Only render when looking from north or south
    return [2, 8].include?(player_dir)
  when 2 # Vertical wall in editor view
    # Only render when looking from east or west
    return [4, 6].include?(player_dir)
  end

  return true
end
  #--------------------------------------------------------------------------
def should_render_two_side_event(character, player_dir)
  return true unless character.fple_two_side

  # Get the event's original type
  original_type = character.fple_type

  case original_type
  when 0 # Always face player - render from all directions
    return true
  when 1 # Horizontal wall in editor view
    # Only render when looking from north or south
    return [2, 8].include?(player_dir)
  when 2 # Vertical wall in editor view
    # Only render when looking from east or west
    return [4, 6].include?(player_dir)
  end

  return true
end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces liées à un évènement visibles quand le joueur est
  # orienté vers le bas
  # param character_surfaces : Array<FPLE::Surface_Characters>
  # return Array<Array<Integer>>
  #-------------------------------------------------------------------------z

   #--------------------------------------------------------------------------
  # * Recherche les surfaces liées à un évènement visibles quand le joueur est
  # orienté vers le haut
  # param character_surfaces : Array<FPLE::Surface_Characters>
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
def add_character_surfaces_up(character_surfaces)
  surfaces_temp = []
  i = $game_system.fple_view_distance
  character_surfaces.each {|surface|
    if surface.displayed then next end
    # Check directional visibility for 2-side events
    next unless should_render_two_side_event(surface.character, 8)
  
    dy = ($game_player.y << 7) - surface.dy
    dx = surface.dx - ($game_player.x << 7)
    add_character_surfaces(surface, dy, dx, surfaces_temp, i, 8)
  }
  return surfaces_temp
end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces liées à un évènement visibles quand le joueur est
  # orienté vers le bas
  # param character_surfaces : Array<FPLE::Surface_Characters>
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
def add_character_surfaces_down(character_surfaces)
  surfaces_temp = []
  i = $game_system.fple_view_distance
  character_surfaces.each {|surface|
    if surface.displayed then next end
    # Check directional visibility for 2-side events
    next unless should_render_two_side_event(surface.character, 2)
  
    dy = surface.dy - ($game_player.y << 7)
    dx = ($game_player.x << 7) - surface.dx
    add_character_surfaces(surface, dy, dx, surfaces_temp, i, 2)
  }
  return surfaces_temp
end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces liées à un évènement visibles quand le joueur est
  # orienté vers la gauche
  # param character_surfaces : Array<FPLE::Surface_Characters>
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
def add_character_surfaces_left(character_surfaces)
  surfaces_temp = []
  i = $game_system.fple_view_distance
  character_surfaces.each {|surface|
    if surface.displayed then next end
    # Check directional visibility for 2-side events
    next unless should_render_two_side_event(surface.character, 4)
  
    dy = ($game_player.x << 7) - surface.dx
    dx = ($game_player.y << 7) - surface.dy
    add_character_surfaces(surface, dy, dx, surfaces_temp, i, 4)
  }
  return surfaces_temp
end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces liées à un évènement visibles quand le joueur est
  # orienté vers la droite
  # param character_surfaces : Array<FPLE::Surface_Characters>
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
def add_character_surfaces_right(character_surfaces)
  surfaces_temp = []
  i = $game_system.fple_view_distance
  character_surfaces.each {|surface|
    if surface.displayed then next end
    # Check directional visibility for 2-side events
    next unless should_render_two_side_event(surface.character, 6)
  
    dy = surface.dx - ($game_player.x << 7)
    dx = surface.dy - ($game_player.y << 7)
    add_character_surfaces(surface, dy, dx, surfaces_temp, i, 6)
  }
  return surfaces_temp
end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces visibles quand le joueur tourne vers la droite
  # param character_surfaces : Array<FPLE::Surface_Characters>
  #--------------------------------------------------------------------------
  def refresh_surfaces_turn_right(character_surfaces)
    @current_dir = $game_player.direction
    self.surfaces = []
    @x_ref = $game_player.x
    @y_ref = $game_player.y
    surfaces_temp = refresh_current_surface
    clear_character_surfaces(character_surfaces)
    case $game_player.direction
    when 2
      surfaces_temp1 = refresh_surfaces_down
      @current_dir = 6
      surfaces_temp2 = refresh_surfaces_right(false)
      @current_dir = 2
      surfaces_temp1 += add_character_surfaces_down(character_surfaces)
      surfaces_temp2 += add_character_surfaces_right(character_surfaces)
    when 4
      surfaces_temp1 = refresh_surfaces_left
      @current_dir = 2
      surfaces_temp2 = refresh_surfaces_down(false)
      @current_dir = 4
      surfaces_temp1 += add_character_surfaces_left(character_surfaces)
      surfaces_temp2 += add_character_surfaces_down(character_surfaces)
    when 6
      surfaces_temp1 = refresh_surfaces_right
      @current_dir = 8
      surfaces_temp2 = refresh_surfaces_up(false)
      @current_dir = 6
      surfaces_temp1 += add_character_surfaces_right(character_surfaces)
      surfaces_temp2 += add_character_surfaces_up(character_surfaces)
    when 8
      surfaces_temp1 = refresh_surfaces_up
      @current_dir = 4
      surfaces_temp2 = refresh_surfaces_left(false)
      @current_dir = 8
      surfaces_temp1 += add_character_surfaces_up(character_surfaces)
      surfaces_temp2 += add_character_surfaces_left(character_surfaces)
    end
    surfaces_temp1.each {|surface| adjust_surface_for_rotation(surface)}
    # set surfaces z attributes
    (surfaces_temp + surfaces_temp1 + surfaces_temp2).each {|surface|
      set_surface_z(surface)
    }
    surfaces.concat(surfaces_temp + surfaces_temp1 + surfaces_temp2)
    surfaces.sort! {|a, b| b[5] - a[5]}
  end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces visibles quand le joueur tourne vers la gauche
  # param character_surfaces : Array<FPLE::Surface_Characters>
  #--------------------------------------------------------------------------
  def refresh_surfaces_turn_left(character_surfaces)
    @current_dir = $game_player.direction
    self.surfaces = []
    @x_ref = $game_player.x
    @y_ref = $game_player.y
    surfaces_temp = refresh_current_surface
    clear_character_surfaces(character_surfaces)
    case $game_player.direction
    when 2
      @current_dir = 4
      surfaces_temp1 = refresh_surfaces_left
      @current_dir = 2
      surfaces_temp2 = refresh_surfaces_down(false)
      surfaces_temp1 += add_character_surfaces_left(character_surfaces)
      surfaces_temp2 += add_character_surfaces_down(character_surfaces)
    when 4
      @current_dir = 8
      surfaces_temp1 = refresh_surfaces_up
      @current_dir = 4
      surfaces_temp2 = refresh_surfaces_left(false)
      surfaces_temp1 += add_character_surfaces_up(character_surfaces)
      surfaces_temp2 += add_character_surfaces_left(character_surfaces)
    when 6
      @current_dir = 2
      surfaces_temp1 = refresh_surfaces_down
      @current_dir = 6
      surfaces_temp2 = refresh_surfaces_right(false)
      surfaces_temp1 += add_character_surfaces_down(character_surfaces)
      surfaces_temp2 += add_character_surfaces_right(character_surfaces)
    when 8
      @current_dir = 6
      surfaces_temp1 = refresh_surfaces_right
      @current_dir = 8
      surfaces_temp2 = refresh_surfaces_up(false)
      surfaces_temp1 += add_character_surfaces_right(character_surfaces)
      surfaces_temp2 += add_character_surfaces_up(character_surfaces)
    end
    surfaces_temp1.each {|surface| adjust_surface_for_rotation(surface)}
    # set surfaces z attributes
    (surfaces_temp + surfaces_temp1 + surfaces_temp2).each {|surface|
      set_surface_z(surface)
    }
    surfaces.concat(surfaces_temp + surfaces_temp1 + surfaces_temp2)
    surfaces.sort! {|a, b| b[5] - a[5]}
  end
  #--------------------------------------------------------------------------
  # * Correction des attributs d'une surface visible pendant une rotation
  # param surface : Array<Integer>
  #--------------------------------------------------------------------------
  def adjust_surface_for_rotation(surface)
    if surface[3] < 5
      dy = surface[1]
      surface[1] = -surface[2]
      surface[2] = dy
      if surface[3] < 2
        surface[3] = 1 - surface[3]
      end
    else
      if surface[7] > 0
        surface[7] = 3 - surface[7]
      end  
      dy = surface[1]
      surface[1] = -surface[2]
      surface[2] = dy
    end
  end
  #--------------------------------------------------------------------------
  # * Recherche des surfaces visibles en déplacement latéral
  # param offset : Integer
  #--------------------------------------------------------------------------
  def refresh_surfaces_strafe(offset)
    @current_dir = $game_player.direction
    @x_ref = $game_player.x
    @y_ref = $game_player.y
    case $game_player.direction
    when 2
      @x_ref -= offset
      surfaces_temp = refresh_surfaces_down_strafe
    when 4
      @y_ref -= offset
      surfaces_temp = refresh_surfaces_left_strafe
    when 6
      @y_ref += offset
      surfaces_temp = refresh_surfaces_right_strafe
    when 8
      @x_ref += offset
      surfaces_temp = refresh_surfaces_up_strafe
    end
    # set surfaces z attributes
    surfaces_temp.each {|surface| set_surface_z(surface)}
    surfaces.concat(surfaces_temp)
    surfaces.sort! {|a, b| b[5] - a[5]}
  end
  #--------------------------------------------------------------------------
  # * Rafraîchit la surface à la position du joueur
  # return Array<Array<integer>>
  #--------------------------------------------------------------------------
  def refresh_current_surface
    surfaces_temp = []
    get_surface_ground(surfaces_temp, @x_ref, @y_ref, 0, 0)
    return surfaces_temp
  end
  #--------------------------------------------------------------------------
  # * Recherche les surfaces visibles de la carte quand le joueur est
  # orienté vers le haut
  # param right_ground : Boolean
  # return Array<Array<integer>>
  #--------------------------------------------------------------------------
def refresh_surfaces_up(right_ground = true)
  surfaces_temp = []
  base_distance = $game_system.fple_view_distance
  # Add extra tiles during rotation
  i = FPLE.angle > 0 ? base_distance + 1 : base_distance

  while i > 0
    # peripherical ground/ceiling sprites
    get_surface_ground(surfaces_temp, @x_ref - i, @y_ref - i, i << 1, -i << 1)
    if right_ground
      get_surface_ground(surfaces_temp, @x_ref + i, @y_ref - i, i << 1, i << 1)
    end
    klim = i - 1
    (-klim..klim).each {|k|
      add_surfaces(surfaces_temp, @x_ref + k, @y_ref - i, i << 1, k << 1, 0, 1)
    }
    i -= 1
  end
  return surfaces_temp
end
def refresh_surfaces_down(right_ground = true)
  surfaces_temp = []
  base_distance = $game_system.fple_view_distance
  # Add extra tiles during rotation
  i = FPLE.angle > 0 ? base_distance + 1 : base_distance

  while i > 0
    # peripherical ground sprites
    get_surface_ground(surfaces_temp, @x_ref + i, @y_ref + i, i << 1, -i << 1)
    if right_ground
      get_surface_ground(surfaces_temp, @x_ref - i, @y_ref + i, i << 1, i << 1)
    end
    klim = i - 1
    (-klim..klim).each {|k|
      add_surfaces(surfaces_temp, @x_ref - k, @y_ref + i, i << 1, k << 1, 0, -1)
    }
    i -= 1
  end
  return surfaces_temp
end
def refresh_surfaces_left(right_ground = true)
  surfaces_temp = []
  base_distance = $game_system.fple_view_distance
  # Add extra tiles during rotation
  i = FPLE.angle > 0 ? base_distance + 1 : base_distance

  while i > 0
    # peripherical ground sprites
    get_surface_ground(surfaces_temp, @x_ref - i, @y_ref + i, i << 1, -i << 1)
    if right_ground
      get_surface_ground(surfaces_temp, @x_ref - i, @y_ref - i, i << 1, i << 1)
    end
    klim = i - 1
    (-klim..klim).each {|k|
      add_surfaces(surfaces_temp, @x_ref - i, @y_ref - k, i << 1, k << 1, 1, 0)
    }
    i -= 1
  end
  return surfaces_temp
end
def refresh_surfaces_right(right_ground = true)
  surfaces_temp = []
  base_distance = $game_system.fple_view_distance
  # Add extra tiles during rotation
  i = FPLE.angle > 0 ? base_distance + 1 : base_distance

  while i > 0
    # peripherical ground sprites
    get_surface_ground(surfaces_temp, @x_ref + i, @y_ref - i, i << 1, -i << 1)
    if right_ground
      get_surface_ground(surfaces_temp, @x_ref + i, @y_ref + i, i << 1, i << 1)
    end
    klim = i - 1
    (-klim..klim).each {|k|
      add_surfaces(surfaces_temp, @x_ref + i, @y_ref + k, i << 1, k << 1, -1, 0)
    }
    i -= 1
  end
  return surfaces_temp
end
  #--------------------------------------------------------------------------
  # * Vérifie si un sol ou un plafond doit être affiché
  # param surfaces_temp : Array<Array<Integer>>
  # param x : Integer
  # param y : Integer
  # param dy : Integer
  # param dx : Integer
  #--------------------------------------------------------------------------
  def get_surface_ground(surfaces_temp, x, y, dy, dx)
    if @fple_map.get_data(x, y)[0] == 0 # no wall tile
      add_surface_ground(surfaces_temp, x, y, dy, dx)
    end
  end
  #--------------------------------------------------------------------------
  # * Ajoute une surface de sol ou de plafond qui doit être affichée
  # param surfaces_temp : Array<Array<Integer>>
  # param x : Integer
  # param y : Integer
  # param dy : Integer
  # param dx : Integer
  #--------------------------------------------------------------------------
  def add_surface_ground(surfaces_temp, x, y, dy, dx)
    g_texture_id = @fple_map.get_data(x, y)[1]
    c_texture_id = @fple_map.get_data(x, y)[19]
    if @fple_map.is_texture_id_valid?(g_texture_id)
      if @fple_map.is_texture_id_valid?(c_texture_id)
        surfaces_temp.push([g_texture_id, dy, dx, 4, c_texture_id, 0,
        @fple_map.get_textureset_id(g_texture_id),
        @fple_map.get_textureset_width(g_texture_id),
        @fple_map.get_textureset_id(c_texture_id),
        @fple_map.get_textureset_width(c_texture_id)])
      else
        surfaces_temp.push([g_texture_id, dy, dx, 2, 0, 0,
        @fple_map.get_textureset_id(g_texture_id),
        @fple_map.get_textureset_width(g_texture_id), 0, 8])
      end
    elsif @fple_map.is_texture_id_valid?(c_texture_id)
      surfaces_temp.push([c_texture_id, dy, dx, 3, 0, 0,
      @fple_map.get_textureset_id(c_texture_id),
      @fple_map.get_textureset_width(c_texture_id), 0, 8])
    end
  end
  #--------------------------------------------------------------------------
  # * Recherche des surfaces visibles
  # param surfaces_temp : Array<Array<Integer>>
  # param x : Integer
  # param y : Integer
  # param dy : Integer
  # param dx : Integer
  # param oy : Integer
  # param ox : Integer
  #--------------------------------------------------------------------------
  def add_surfaces(surfaces_temp, x, y, dy, dx, ox, oy)
    if @fple_map.get_data(x, y)[0] > 0 # wall tile
      if @fple_map.get_data(x + ox, y + oy)[0] == 0 # --> visible !
        texture_id = @fple_map.get_data(x, y)[
        FPLE::MAP_SIDES[@current_dir - 1 >> 1]]
        if @fple_map.is_texture_id_valid?(texture_id)
          surfaces_temp.push([texture_id, dy - 1, dx, 1, 0, 0,
          @fple_map.get_textureset_id(texture_id),
          @fple_map.get_textureset_width(texture_id), 0, 8])
        end
      end
    else
      # ground/ceiling sprite
      add_surface_ground(surfaces_temp, x, y, dy, dx)
      # side walls
      if dx <= 0 # left
        if @fple_map.get_data(x - oy, y + ox)[0] > 0 # side wall tile
          texture_id = @fple_map.get_data(x - oy, y + ox)[
          FPLE::MAP_SIDES_LEFT[@current_dir - 1 >> 1]]
          if @fple_map.is_texture_id_valid?(texture_id)
            surfaces_temp.push([texture_id, dy, dx - 1, 0, 0, 0,
            @fple_map.get_textureset_id(texture_id),
            @fple_map.get_textureset_width(texture_id), 0, 8])
          end
        end
      end
      if dx >= 0 # right
        if @fple_map.get_data(x + oy, y - ox)[0] > 0 # side wall tile
          texture_id = @fple_map.get_data(x + oy, y - ox)[
          FPLE::MAP_SIDES_RIGHT[@current_dir - 1 >> 1]]
          if @fple_map.is_texture_id_valid?(texture_id)
            surfaces_temp.push([texture_id, dy, dx + 1, 0, 0, 0,
            @fple_map.get_textureset_id(texture_id),
            @fple_map.get_textureset_width(texture_id), 0, 8])
          end
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Recherche des surfaces visibles en déplacement latéral quand le
  # joueur est orienté vers le haut
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
  def refresh_surfaces_up_strafe
    surfaces_temp = []
    i = $game_system.fple_view_distance
    while i > 0
      get_surface_ground(surfaces_temp, @x_ref + i + 1, @y_ref - i, i << 1,
      i + 1 << 1)
      add_surfaces_strafe(surfaces_temp, @x_ref + i,  @y_ref - i, i << 1, 0, 1)
      # middle side wall
      if @fple_map.get_data(@x_ref + 1, @y_ref - i)[0] == 0 # no wall tile
        if @fple_map.get_data(@x_ref, @y_ref - i)[0] > 0 # side wall tile (Left)
          texture_id = @fple_map.get_data(@x_ref, @y_ref - i)[
          FPLE::MAP_SIDES_LEFT[@current_dir - 1 >> 1]]
          if @fple_map.is_texture_id_valid?(texture_id)
            surfaces_temp.push([texture_id, i << 1, 1, 0, 0, 0,
            @fple_map.get_textureset_id(texture_id),
            @fple_map.get_textureset_width(texture_id), 0, 8])
          end
        end
      end
      i -= 1
    end
    return surfaces_temp
  end
  #--------------------------------------------------------------------------
  # * Recherche des surfaces visibles en déplacement latéral quand le
  # joueur est orienté vers le bas
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
  def refresh_surfaces_down_strafe
    surfaces_temp = []
    i = $game_system.fple_view_distance
    while i > 0
      get_surface_ground(surfaces_temp, @x_ref - i - 1, @y_ref + i, i << 1,
      i + 1 << 1)
      add_surfaces_strafe(surfaces_temp, @x_ref - i,  @y_ref + i, i << 1, 0, -1)
      # middle side wall
      if @fple_map.get_data(@x_ref - 1, @y_ref + i)[0] == 0 # no wall tile
        if @fple_map.get_data(@x_ref, @y_ref + i)[0] > 0 # side wall tile (Left)
          texture_id = @fple_map.get_data(@x_ref, @y_ref + i)[
          FPLE::MAP_SIDES_LEFT[@current_dir - 1 >> 1]]
          if @fple_map.is_texture_id_valid?(texture_id)
            surfaces_temp.push([texture_id, i << 1, 1, 0, 0, 0,
            @fple_map.get_textureset_id(texture_id),
            @fple_map.get_textureset_width(texture_id), 0, 8])
          end
        end
      end
      i -= 1
    end
    return surfaces_temp
  end
  #--------------------------------------------------------------------------
  # * Recherche des surfaces visibles en déplacement latéral quand le
  # joueur est orienté vers la gauche
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
  def refresh_surfaces_left_strafe
    surfaces_temp = []
    i = $game_system.fple_view_distance
    while i > 0
      get_surface_ground(surfaces_temp, @x_ref - i, @y_ref - i - 1, i << 1,
      i + 1 << 1)
      add_surfaces_strafe(surfaces_temp, @x_ref - i,  @y_ref - i, i << 1, 1, 0)
      # middle side wall
      if @fple_map.get_data(@x_ref - i, @y_ref - 1)[0] == 0 # no wall tile
        if @fple_map.get_data(@x_ref - i, @y_ref)[0] > 0 # side wall tile (Left)
          texture_id = @fple_map.get_data(@x_ref - i, @y_ref)[
          FPLE::MAP_SIDES_LEFT[@current_dir - 1 >> 1]]
          if @fple_map.is_texture_id_valid?(texture_id)
            surfaces_temp.push([texture_id, i << 1, 1, 0, 0, 0,
            @fple_map.get_textureset_id(texture_id),
            @fple_map.get_textureset_width(texture_id), 0, 8])
          end
        end
      end
      i -= 1
    end
    return surfaces_temp
  end
  #--------------------------------------------------------------------------
  # * Recherche des surfaces visibles en déplacement latéral quand le
  # joueur est orienté vers la droite
  # return Array<Array<Integer>>
  #--------------------------------------------------------------------------
  def refresh_surfaces_right_strafe
    surfaces_temp = []
    i = $game_system.fple_view_distance
    while i > 0
      get_surface_ground(surfaces_temp, @x_ref + i, @y_ref + i + 1, i << 1,
      i + 1 << 1)
      add_surfaces_strafe(surfaces_temp, @x_ref + i,  @y_ref + i, i << 1, -1, 0)
      # middle side wall
      if @fple_map.get_data(@x_ref + i, @y_ref + 1)[0] == 0 # no wall tile
        if @fple_map.get_data(@x_ref + i, @y_ref)[0] > 0 # side wall tile (Left)
          texture_id = @fple_map.get_data(@x_ref + i, @y_ref)[
          FPLE::MAP_SIDES_LEFT[@current_dir - 1 >> 1]]
          if @fple_map.is_texture_id_valid?(texture_id)
            surfaces_temp.push([texture_id, i << 1, 1, 0, 0, 0,
            @fple_map.get_textureset_id(texture_id),
            @fple_map.get_textureset_width(texture_id), 0, 8])
          end
        end
      end
      i -= 1
    end
    return surfaces_temp
  end
  #--------------------------------------------------------------------------
  # * Recherche des surfaces visibles en déplacement latéral
  # param surfaces_temp : Array<Array<Integer>>
  # param x : Integer
  # param y : Integer
  # param dxy : Integer
  # param oy : Integer
  # param ox : Integer
  #--------------------------------------------------------------------------
  def add_surfaces_strafe(surfaces_temp, x, y, dxy, ox, oy)
    if @fple_map.get_data(x, y)[0] > 0 # wall tile
      if @fple_map.get_data(x + ox, y + oy)[0] == 0 # --> visible !
        texture_id = @fple_map.get_data(x, y)[
        FPLE::MAP_SIDES[@current_dir - 1 >> 1]]
        if @fple_map.is_texture_id_valid?(texture_id)
          surfaces_temp.push([texture_id, dxy - 1, dxy, 1, 0, 0,
          @fple_map.get_textureset_id(texture_id),
          @fple_map.get_textureset_width(texture_id), 0, 8])
        end
      end
    else
      # side wall (Right)
      if @fple_map.get_data(x + oy, y - ox)[0] > 0 # side wall tile
        texture_id = @fple_map.get_data(x + oy, y - ox)[
        FPLE::MAP_SIDES_RIGHT[@current_dir - 1 >> 1]]
        if @fple_map.is_texture_id_valid?(texture_id)
          surfaces_temp.push([texture_id, dxy, dxy + 1, 0, 0, 0,
          @fple_map.get_textureset_id(texture_id),
          @fple_map.get_textureset_width(texture_id), 0, 8])
        end
      end
    end
end
#==============================================================================
# ** Game_Character
#==============================================================================
class Game_Character < Game_CharacterBase
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility)
  #--------------------------------------------------------------------------
  unless @already_aliased_fple
    alias moving_fple_game_character? moving?
    @already_aliased_fple = true
  end
  #--------------------------------------------------------------------------
  # * Determine if Moving
  # return Boolean
  #--------------------------------------------------------------------------
  def moving?
    if $game_system.fple
      return $game_temp.movement || moving_fple_game_character?
    else
      return moving_fple_game_character?
    end
  end
end
#==============================================================================
# ** Game_Player
#==============================================================================
class Game_Player < Game_Character
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility) - mod [1.1]
  #--------------------------------------------------------------------------
  unless @already_aliased_fple
    alias move_straight_fple_game_player move_straight
    alias move_diagonal_fple_game_player move_diagonal
    alias turn_right_90_fple_game_player turn_right_90
    alias turn_left_90_fple_game_player turn_left_90
    alias move_by_input_fple_game_player move_by_input
    alias update_nonmoving_fple_game_player update_nonmoving
    @already_aliased_fple = true
  end
  #--------------------------------------------------------------------------
  # * Initialize Public Member Variables
  #--------------------------------------------------------------------------
  def init_public_members
    super
    @direction = 8
  end
  #--------------------------------------------------------------------------
  # * Move Straight
  # param d : Integer
  # param turn_ok : Boolean
  #--------------------------------------------------------------------------
  def move_straight(d, turn_ok = true)
    if $game_system.fple
      case d
      when 2
        case $game_player.direction
        when 2
          go_forward
        when 4
          strafe_left
        when 6
          strafe_right
        when 8
          go_backward
        end
      when 4
        case $game_player.direction
        when 2
          strafe_right
        when 4
          go_forward
        when 6
          go_backward
        when 8
          strafe_left
        end
      when 6
        case $game_player.direction
        when 2
          strafe_left
        when 4
          go_backward
        when 6
          go_forward
        when 8
          strafe_right
        end
      when 8
        case $game_player.direction
        when 2
          go_backward
        when 4
          strafe_right
        when 6
          strafe_left
        when 8
          go_forward
        end
      end
    else
      move_straight_fple_game_player(d, turn_ok)
    end
  end
  #--------------------------------------------------------------------------
  # * Move Diagonally
  # param horz : Integer
  # param vert : Integer
  #--------------------------------------------------------------------------
  def move_diagonal(horz, vert)
    unless $game_system.fple
      move_diagonal_fple_game_player(horz, vert)
    end
  end
  #--------------------------------------------------------------------------
  # * Jump
  # param x_plus : Integer
  # param y_plus : Integer
  #--------------------------------------------------------------------------
  def jump(x_plus, y_plus)
    unless $game_system.fple
      super(x_plus, y_plus) # [1.1]
    end
  end
  #--------------------------------------------------------------------------
  # * Turn 90° Right
  #--------------------------------------------------------------------------
  def turn_right_90
    turn_right_90_fple_game_player
    if $game_system.fple && !@direction_fix
      $game_temp.movement_init = false
      $game_temp.movement = true
      $game_temp.movement_dir = 9
    end
  end
  #--------------------------------------------------------------------------
  # * Turn 90° Left
  #--------------------------------------------------------------------------
  def turn_left_90
    turn_left_90_fple_game_player
    if $game_system.fple && !@direction_fix
      $game_temp.movement_init = false
      $game_temp.movement = true
      $game_temp.movement_dir = 10
    end
  end
  #--------------------------------------------------------------------------
  # * Go Forward
  #--------------------------------------------------------------------------
  def go_forward
    mvt_data = FPLE::PLAYER_MOVE_FORWARD[(@direction >> 1) - 1]
    if passable?(@x, @y, @direction)
      @x += mvt_data[0]
      @y += mvt_data[1]
      increase_steps
      $game_temp.movement = true
      $game_temp.movement_dir = 8
    else
      check_event_trigger_touch(@x + mvt_data[0], @y + mvt_data[1])
    end
  end
  #--------------------------------------------------------------------------
  # * Go Backward
  #--------------------------------------------------------------------------
  def go_backward
    target_dir = 10 - @direction
    mvt_data = FPLE::PLAYER_MOVE_FORWARD[(target_dir >> 1) - 1]
    if passable?(@x, @y, target_dir)
      @x += mvt_data[0]
      @y += mvt_data[1]
      increase_steps
      $game_temp.movement_init = false
      $game_temp.movement = true
      $game_temp.movement_dir = 2
    else
      check_event_trigger_touch(@x + mvt_data[0], @y + mvt_data[1])
    end
  end
  #--------------------------------------------------------------------------
  # * Strafe Left
  #--------------------------------------------------------------------------
  def strafe_left
    target_dir = FPLE::TURN_LEFT[(@direction >> 1) - 1]
    mvt_data = FPLE::PLAYER_MOVE_FORWARD[(target_dir >> 1) - 1]
    if passable?(@x, @y, target_dir)
      @x += mvt_data[0]
      @y += mvt_data[1]
      increase_steps
      $game_temp.movement_init = false
      $game_temp.movement = true
      $game_temp.movement_dir = 4
    else
      check_event_trigger_touch(@x + mvt_data[0], @y + mvt_data[1])
    end
  end
  #--------------------------------------------------------------------------
  # * Strafe Right
  #--------------------------------------------------------------------------
  def strafe_right
    target_dir = 10 - FPLE::TURN_LEFT[(@direction >> 1) - 1]
    mvt_data = FPLE::PLAYER_MOVE_FORWARD[(target_dir >> 1) - 1]
    if passable?(@x, @y, target_dir)
      @x += mvt_data[0]
      @y += mvt_data[1]
      increase_steps
      $game_temp.movement_init = false
      $game_temp.movement = true
      $game_temp.movement_dir = 6
    else
      check_event_trigger_touch(@x + mvt_data[0], @y + mvt_data[1])
    end
  end
  #--------------------------------------------------------------------------
  # * Processing of Movement via input from the Directional Buttons
  #--------------------------------------------------------------------------
  def move_by_input
    if $game_system.fple
      if !movable? || $game_map.interpreter.running? then return end
      if Input.press?(:L)
        strafe_left
      elsif Input.press?(:R)
        strafe_right
      else
        case Input.dir4
        when 2; go_backward
        when 4; turn_left_90
        when 6; turn_right_90
        when 8; go_forward
        end
      end
    else
      move_by_input_fple_game_player
    end
  end
  #--------------------------------------------------------------------------
  # * Processing when not moving
  # param last_moving : Boolean
  #--------------------------------------------------------------------------
  def update_nonmoving(last_moving)
    if $game_map.interpreter.running? then return end
    update_nonmoving_fple_game_player(last_moving)
    if last_moving then $game_temp.last_moving = false end
  end
  #--------------------------------------------------------------------------
  # * Move speed in FPLE mode
  # return Integer
  #--------------------------------------------------------------------------
  def move_speed_fple
    return FPLE::PLAYER_MOVE_SPEED[real_move_speed]
  end
end
#==============================================================================
# ** Game_Event
#==============================================================================
class Game_Event < Game_Character
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility)
  #--------------------------------------------------------------------------
  unless @already_aliased_fple
    alias refresh_fple_game_character refresh
    @already_aliased_fple = true
  end
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader :fple_type # Integer 0:face the player
                         #         1:horizontal wall in the RMXP editor view
                         #         2:vertical wall in the RMXP editor view
                         #         3:4-sided box (NEW)
                         #         4:6-sided box (NEW)
  attr_reader :fple_v_align # Integer - Vertical align {0:up, 1:middle, 2:down}
  attr_reader :fple_d_align # Integer - Depth align {0:front/left, 1:middle, 2:back/right}
  attr_reader :fple_stretch # Integer (0/1)
  attr_reader :fple_fit # Integer (0/1)
  attr_reader :fple_zoom # Integer (1024 <=> zoom = 1.0)
  attr_reader :fple_one_side # Boolean (0/1)
  attr_reader :fple_two_side # Boolean - NEW: indicates 6-sided box mode
  attr_reader :fple_four_side # Boolean - NEW: indicates 4-sided box mode
  attr_reader :fple_six_side # Boolean - NEW: indicates 6-sided box mode
  #--------------------------------------------------------------------------
  # * Scan the event's commands list
  # param page : RPG::Event::Page
  #--------------------------------------------------------------------------
  def check_commands(page)
    @fple_type = 0
    @fple_v_align = 1
    @fple_d_align = 1
    @fple_stretch = 0
    @fple_fit = 0
    @fple_zoom = 1024
    @fple_one_side = false # [1.5]
    @fple_two_side = false # NEW
    @fple_four_side = false # NEW
    @fple_six_side = false # NEW
    command_list = page.list
    (0..command_list.length - 2).each {|k|
      command = command_list[k]
      if command.code == 108
        comments = command.parameters[0]
        if comments[/Type/]
          @fple_type = comments[/\d+/].to_i
          if @fple_type < 0 || @fple_type > 2 then @fple_type = 0 end
        end
        if comments[/V-Align/]
          @fple_v_align = comments[/\d+/].to_i
          if @fple_v_align < 0 || @fple_v_align > 2 then @fple_v_align = 1 end
        end
        if comments[/D-Align/]
          @fple_d_align = comments[/\d+/].to_i
          if @fple_d_align < 0 || @fple_d_align > 2 then @fple_d_align = 1 end
        end
        if comments[/Stretch/]
          @fple_stretch = 1
        end
        if comments[/Fit/]
          @fple_fit = 1
        end
        if comments[/Zoom/]
          @fple_zoom = (comments[/\d+\.\d+/].to_f * 1024).round
        end
        if comments[/1-Side/] # [1.5]
          @fple_one_side = true
        end
        if comments[/2-Side/] # NEW
          @fple_four_side = true
        end
        if comments[/4-Side/] # NEW
          @fple_four_side = true
        end
        if comments[/6-Side/] # NEW
          @fple_six_side = true
        end
      end
    }
    if @fple_type == 0 then @fple_d_align = 1 end
    if @fple_stretch == 1 then @fple_v_align = 1 end
    if @fple_two_side
    elsif @fple_four_side
      @fple_type = 3 # Special type for 4-sided boxes
      @fple_d_align = 1 # Center alignment makes most sense
    elsif @fple_six_side
      @fple_type = 4 # Special type for 6-sided boxes
      @fple_d_align = 1 # Center alignment makes most sense
    end
  end
 
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    refresh_fple_game_character
    if @page then check_commands(@page) end
  end
end
#==============================================================================
# ** FPLE::Map_Data
#==============================================================================
module FPLE
  class Map_Data
    #--------------------------------------------------------------------------
    # * Public Instance Variables
    #--------------------------------------------------------------------------
    attr_accessor :data, :tileset_name, :map_name
    attr_accessor :used_tiles_map, :textureset_data, :subsets_mapping
    attr_reader :map_id, :width, :height
  end
end
#==============================================================================
# ** FPLE::Map
#==============================================================================
module FPLE
  class Map
    #--------------------------------------------------------------------------
    # * Public Instance Variables
    #--------------------------------------------------------------------------
    attr_accessor :data, :tileset_name, :map_name
    attr_reader :map_id, :width, :height
    attr_accessor :textureset
    attr_accessor :texturesets
    #--------------------------------------------------------------------------
    # * Initialisation
    # param width : integer (largeur de la carte, donc égal à $game_map.width)
    # param height : integer (hauteur de la carte, donc égal à $game_map.height)
    # param name : String (nom du fichier de la carte FPLE)
    # param tileset_name : String (nom du tileset utilisé)
    # param map_data : Array[width * height]<Array<integer>>
    # param subsets_mapping : Hash<Integer,String> {texture_id=>subset_name}
    # param textureset_data : Array<Array> données de création du textureset
    #--------------------------------------------------------------------------
    def initialize(width, height, name, tileset_name, map_data = nil,
      subsets_mapping = nil, textureset_data = nil)
      @width = width
      @height = height
      self.map_name = name
      self.data = Array.new(width * height)
      data.each_index {|index| data[index] = map_data[index]}
      self.tileset_name = tileset_name
      @subsets_mapping = subsets_mapping
      create_textureset_from_data(textureset_data)
    end
    #--------------------------------------------------------------------------
    # * Retourne les données d'un tile par ses coordonées
    # param x : integer ([0, width[)
    # param y : integer ([0, height[)
    # return Array[10]<integer> (map_Data)
    #--------------------------------------------------------------------------
    def get_data(x, y)
      ret = data[get_index(x, y)]
      ret = [-1] unless ret
      return ret
    end
    #--------------------------------------------------------------------------
    # * Retourne l'index d'un tile par ses coordonées
    # param x : integer ([0, width[)
    # param y : integer ([0, height[)
    # return integer (> 0)
    #--------------------------------------------------------------------------
    def get_index(x, y)
      return x + y * width
    end
    #--------------------------------------------------------------------------
    # * Retourne l'abscisse d'un tile par son index
    # return integer ([0, width[)
    #--------------------------------------------------------------------------
    def get_x(index)
      return index - width * (index / width)
    end
    #--------------------------------------------------------------------------
    # * Retourne l'ordonnée d'un tile par son index
    # return integer ([0, height[)
    #--------------------------------------------------------------------------
    def get_y(index)
      return index / width
    end
    #--------------------------------------------------------------------------
    # * Création du textureset
    # param textureset_data : Array<Array> données de création du textureset
    #--------------------------------------------------------------------------
        def get_texture_lazy(texture_id)
      @texture_cache ||= {}
      return @texture_cache[texture_id] if @texture_cache.has_key?(texture_id)
     
      # Only load texture if it will be used
      if is_texture_id_valid?(texture_id)
        @texture_cache[texture_id] = load_texture(texture_id)
      else
        @texture_cache[texture_id] = nil
      end
     
      return @texture_cache[texture_id]
    end
  #--------------------------------------------------------------------------
    def create_textureset_from_data(textureset_data)
      rect = Rect.new(0, 0, 32, 32)
      textureset_height = 1 + (textureset_data.size >> 3) << 5
      self.textureset = Bitmap.new(256, textureset_height)
      src = Cache.tileset(tileset_name)
      textureset_data.each_index {|texture_id|
        t_dat = textureset_data[texture_id]
        if t_dat
          x_trg = texture_id - (texture_id >> 3 << 3) << 5
          y_trg = texture_id >> 3 << 5
          rect.x = t_dat[0]
          rect.y = t_dat[1]
          if t_dat[0] == 0 && t_dat[1] == 0
            @texture_id_nil = texture_id
          end
          textureset.blt(x_trg, y_trg, src, rect)
        end
      }
      @texturesets = [textureset]
      @texturesets_widths = [8]
      @texturesets_mapping_ids = {}
      subsets_names_list = []
      if @subsets_mapping
        @subsets_mapping.each {|texture_id, subset_name|
          if subsets_names_list.include?(subset_name)
            @texturesets_mapping_ids[texture_id] =
            subsets_names_list.index(subset_name) + 1
          else
            subsets_names_list << subset_name
            subset = Cache.tileset(subset_name)
            texturesets << subset
            for k in 5..9
              if subset.width >> k == 1
                @texturesets_widths << k
                break
              end
            end
            @texturesets_mapping_ids[texture_id] = subsets_names_list.size
          end
        }
      end
    end
    #--------------------------------------------------------------------------
    # * Retourne l'index du textureset pour une texture
    # param textureset_data : Integer
    # return Integer
    #--------------------------------------------------------------------------
    def get_textureset_id(texture_id)
      if @texturesets_mapping_ids.has_key?(texture_id)
        return @texturesets_mapping_ids[texture_id]
      else
        return 0
      end
    end
    #--------------------------------------------------------------------------
    # * Retourne la largeur du textureset pour une texture
    # param textureset_data : Integer
    # return Integer
    #--------------------------------------------------------------------------
    def get_textureset_width(texture_id)
      return @texturesets_widths[get_textureset_id(texture_id)]
    end
    #--------------------------------------------------------------------------
    # * Dispose
    #--------------------------------------------------------------------------
    def dispose
      if textureset then textureset.dispose end
    end
    #--------------------------------------------------------------------------
    # * Is texture valid
    # param texture_id : Integer
    # return Boolean
    #--------------------------------------------------------------------------
    def is_texture_id_valid?(texture_id)
      return texture_id && texture_id != @texture_id_nil
    end
  end
end
#==============================================================================
# ** Spriteset_Weather
#==============================================================================
class Spriteset_Weather
  #--------------------------------------------------------------------------
  # * Modification of sprites coordinates when moving forward and backward
  #--------------------------------------------------------------------------
  def update_fple
    @sprites.each {|sprite|
      if $game_temp.movement_dir == 8
        sprite.x += (sprite.x + @ox - 272) / 8
        sprite.y += (sprite.y + @oy - 208) / 8
        sprite.opacity += 8
      elsif $game_temp.movement_dir == 2
        sprite.x -= (sprite.x + @ox - 272) / 8
        sprite.y -= (sprite.y + @oy - 139) / 8
        sprite.opacity -= 8
      end
    }
  end
end
#==============================================================================
# ** FPLE::Spriteset_Map
#==============================================================================
module FPLE
  class Spriteset_Map < ::Spriteset_Map
    #--------------------------------------------------------------------------
    # * Public Instance Variables
    #--------------------------------------------------------------------------
    attr_reader :character_surfaces # Array<FPLE::Surface_Characters>
    attr_reader :viewport1 # Viewport
    #--------------------------------------------------------------------------
    # * Create Tilemap
    #--------------------------------------------------------------------------
    def create_tilemap
      # do nothing
    end
    #--------------------------------------------------------------------------
    # * Create Character Surfaces
    #--------------------------------------------------------------------------
    def create_characters
      @character_surfaces = []
      $game_map.events.values.each {|event|
        character_surfaces << FPLE::Surface_Character.new(self, event)
      }
      @map_id = $game_map.map_id
      initialize_fple_rendering
    end
    #--------------------------------------------------------------------------
    # * Create Airship Shadow Sprite
    #--------------------------------------------------------------------------
    def create_shadow
      # do nothing
    end
    #--------------------------------------------------------------------------
    # * Initialize FPLE rendering
    #--------------------------------------------------------------------------
    def initialize_fple_rendering
      FPLE.initialize_fple(self, @viewport1)
    end
    #--------------------------------------------------------------------------
    # * Dispose
    #--------------------------------------------------------------------------
    def dispose
      dispose_fple_rendering
      super
    end
    #--------------------------------------------------------------------------
    # * Dispose of FPLE rendering
    #--------------------------------------------------------------------------
    def dispose_fple_rendering
      FPLE.dispose
    end
    #--------------------------------------------------------------------------
    # * Free Tilemap
    #--------------------------------------------------------------------------
    def dispose_tilemap
      # do nothing
    end
    #--------------------------------------------------------------------------
    # * Dispose of Character Surfaces
    #--------------------------------------------------------------------------
    def dispose_characters
      @character_surfaces.each {|surface| surface.dispose}
    end
    #--------------------------------------------------------------------------
    # * Free Airship Shadow Sprite
    #--------------------------------------------------------------------------
    def dispose_shadow
      # do nothing
    end
    #--------------------------------------------------------------------------
    # * Update Tileset
    #--------------------------------------------------------------------------
    def update_tileset
      # do nothing
    end
    #--------------------------------------------------------------------------
    # * Update Tilemap
    #--------------------------------------------------------------------------
    def update_tilemap
      # do nothing
    end
    #--------------------------------------------------------------------------
    # * Update Parallax
    #--------------------------------------------------------------------------
    def update_parallax
      super
      if @parallax.bitmap
        @parallax.ox += (@parallax.bitmap.width * FPLE.angle) / 90
      end
    end
    #--------------------------------------------------------------------------
    # * Update Character Sprite
    #--------------------------------------------------------------------------
    def update_characters
      unless @map_id == $game_map.map_id then refresh_characters end
      character_surfaces.each {|surface| surface.update}
      update_fple_rendering
    end
    #--------------------------------------------------------------------------
    # * Update FPLE Rendering
    #--------------------------------------------------------------------------
    def update_fple_rendering
      FPLE.update
    end
    #--------------------------------------------------------------------------
    # * Update Airship Shadow Sprite
    #--------------------------------------------------------------------------
    def update_shadow
      # do nothing
    end
    #--------------------------------------------------------------------------
    # * Update Weather
    #--------------------------------------------------------------------------
    def update_weather
      @weather.type = $game_map.screen.weather_type
      @weather.power = $game_map.screen.weather_power
      @weather.ox = $game_map.display_x * 32 +
      (Graphics.width * FPLE.angle) / 90 +
      (Graphics.width * FPLE.offset_x >> 5)
      @weather.oy = $game_map.display_y * 32
      @weather.update
      if FPLE.offset_y > 0 then @weather.update_fple end
    end
  end
end
#==============================================================================
# ** FPLE::Surface_Character
#==============================================================================
module FPLE
  class Surface_Character
    #--------------------------------------------------------------------------
    # * Public Instance Variables
    #--------------------------------------------------------------------------
    attr_accessor :character # Game_Event
    attr_accessor :bitmap_set # Bitmap
    attr_accessor :bitmap # Bitmap
    attr_accessor :visible # Boolean
    attr_accessor :opacity # Integer
    attr_accessor :blend_type # Integer
    attr_accessor :dx # Integer
    attr_accessor :dy # Integer
    attr_accessor :displayed # Boolean
    attr_reader :spriteset # FPLE::Spriteset_Map
    attr_reader :type # Integer
    attr_reader :v_align # Integer
    attr_reader :d_align # Integer
    attr_reader :fit # Integer (0/1)
    attr_reader :zoom # Integer (1024 <=> zoom = 1.0)
    attr_reader :mirror # Integer (0/1) # [1.5]
    #--------------------------------------------------------------------------
    # * Object Initialization
    # param spriteset : FPLE::Spriteset_Map
    # param character : Game_Event
    #--------------------------------------------------------------------------
    def initialize(spriteset, character = nil)
      @spriteset = spriteset
      self.character = character
      self.displayed = false
      @need_refresh = false
      @dx_old = 0
      @dy_old = 0
      @sx_old = -1
      @sy_old = -1
      @visible_old
      @opacity_old
      @blend_type_old
      @sprite_temp = nil
      @balloon_duration = 0
      if character
        @fit = character.fple_fit
        @zoom = character.fple_zoom
      else
        @fit = 0
        @zoom = 1024
      end
      @mirror = 0 # [1.5]
      update
    end
    #--------------------------------------------------------------------------
    # * Dispose
    #--------------------------------------------------------------------------
    def dispose
      if @sprite_temp
        @sprite_temp.dispose
        @sprite_temp = nil
      end
      if bitmap_set then self.bitmap_set.dispose end
      if bitmap then self.bitmap.dispose end
    end
    #--------------------------------------------------------------------------
    # * Get tile set image that includes the designated tile
    # param tile_id : Integer
    #--------------------------------------------------------------------------
    def tileset_bitmap(tile_id)
      set_number = tile_id / 256
      return Cache.system("TileB") if set_number == 0
      return Cache.system("TileC") if set_number == 1
      return Cache.system("TileD") if set_number == 2
      return Cache.system("TileE") if set_number == 3
      return nil
    end
    #--------------------------------------------------------------------------
    # * Frame Update
    #--------------------------------------------------------------------------
    def update
      update_bitmap
      update_src_rect
      update_position
      update_other
      update_balloon
      setup_new_effect
      if need_refresh? then force_render end
    end
    #--------------------------------------------------------------------------
    # * Update Transfer Origin Bitmap
    #--------------------------------------------------------------------------
    def update_bitmap
      if graphic_changed?
        @tile_id = @character.tile_id
        @character_name = @character.character_name
        @character_index = @character.character_index
        if @tile_id > 0
          set_tile_bitmap
        else
          set_character_bitmap
        end
        @need_refresh = true
      end
    end
    #--------------------------------------------------------------------------
    # * Determine if Graphic Changed
    #--------------------------------------------------------------------------
    def graphic_changed?
      @tile_id != character.tile_id ||
      @character_name != character.character_name ||
      @character_index != character.character_index
    end
    #--------------------------------------------------------------------------
    # * Set Tile Bitmap
    #--------------------------------------------------------------------------
    def set_tile_bitmap
      @sx = (@tile_id / 128 % 2 * 8 + @tile_id % 8) * 32;
      @sy = @tile_id % 256 / 8 % 16 * 32;
      self.bitmap_set = tileset_bitmap(@tile_id)
      @cw = 32
      @ch = 32
      self.bitmap = Bitmap.new(@cw, @ch)
    end
    #--------------------------------------------------------------------------
    # * Set Character Bitmap
    #--------------------------------------------------------------------------
    def set_character_bitmap
      self.bitmap_set = Cache.character(@character_name)
      sign = @character_name[/^[\!\$]./]
      if sign && sign.include?('$')
        @cw = bitmap_set.width / 3
        @ch = bitmap_set.height / 4
      else
        @cw = bitmap_set.width / 12
        @ch = bitmap_set.height / 8
      end
      self.bitmap = Bitmap.new(@cw, @ch)
    end
    #--------------------------------------------------------------------------
    # * Update Transfer Origin Rectangle
    #--------------------------------------------------------------------------
    def update_src_rect
      if @tile_id == 0
        index = character.character_index
        pattern = character.pattern < 3 ? character.pattern : 1
        @sx = (index % 4 * 3 + pattern) * @cw
        unless character.direction_fix
          case $game_player.direction
          when 2
            direction = 10 - character.direction
          when 4
            direction = 10 - FPLE::TURN_LEFT[(character.direction >> 1) - 1]
          when 6
            direction = FPLE::TURN_LEFT[(character.direction >> 1) - 1]
          when 8
            direction = character.direction
          end
        else
          direction = character.direction
        end
        @sy = ((index >> 2 << 2) + (direction - 2 >> 1)) * @ch
      end
      if @sx_old != @sx || @sy_old != @sy
        return if @sx == @sx_old && @sy == @sy_old && !@bitmap_dirty
        self.bitmap.clear
        self.bitmap.blt(0, 0, bitmap_set, Rect.new(@sx, @sy, @cw, @ch))
         @sx_old = @sx
    @sy_old = @sy
    @bitmap_dirty = false
      end
    end
    #--------------------------------------------------------------------------
    # * Update Position
    #--------------------------------------------------------------------------
    def update_position
      self.dx = (128 * character.real_x).to_i
      self.dy = (128 * character.real_y).to_i
    end
    #--------------------------------------------------------------------------
    # * Update Other
    #--------------------------------------------------------------------------
    def update_other
      self.opacity = character.opacity
      self.blend_type = character.blend_type
      self.visible = !character.transparent
    end
    #--------------------------------------------------------------------------
    # * Set New Effect
    #--------------------------------------------------------------------------
    def setup_new_effect
      if character.animation_id > 0
        if displayed
          unless @sprite_temp
            # create a temporary sprite to launch animation
            @sprite_temp = Sprite_Base.new(spriteset.viewport1)
          end
          coordinates = find_coordinates
          if coordinates
            @sprite_temp.x = coordinates[0]
            @sprite_temp.y = coordinates[1]
            animation = $data_animations[character.animation_id]
            @sprite_temp.start_animation(animation)
          else
            @sprite_temp.dispose
            @sprite_temp = nil
          end
        end
        character.animation_id = 0
      end
      if @sprite_temp
        @sprite_temp.update
        unless @sprite_temp.animation?
          @sprite_temp.dispose
          @sprite_temp = nil
        end
      end
      if !@balloon_sprite && character.balloon_id != 0
        @balloon_id = character.balloon_id
        start_balloon
      end
    end
    #--------------------------------------------------------------------------
    # * Determine if Changed
    #--------------------------------------------------------------------------
    def need_refresh?
      @need_refresh || @dx_old != dx || @dy_old != dy ||
      @sx_old != @sx || @sy_old != @sy ||
      @visible_old != visible || @opacity_old != opacity ||
      @blend_type_old != blend_type
    end
    #--------------------------------------------------------------------------
    # * Force Render
    #--------------------------------------------------------------------------
    def force_render
      if displayed then $game_temp.force_render = true end
      @dx_old = dx
      @dy_old = dy
      @sx_old = @sx
      @sy_old = @sy
      @visible_old = visible
      @opacity_old = opacity
      @blend_type_old = blend_type
      @need_refresh = false
    end
    #--------------------------------------------------------------------------
    # * Refresh FPLE attributes sepending on the relative frame of reference
    # param direction : Integer
    #--------------------------------------------------------------------------
def set_relative_attributes(direction)
      # For 4-sided and 6-sided surfaces, we don't need to change attributes
      # since all sides are rendered automatically
      if character.fple_four_side
        @type = 3 # Special identifier for 4-sided
        @d_align = character.fple_d_align
        @v_align = character.fple_v_align
        @mirror = 0 # Could add logic for different mirror states per side if needed
        return
      elsif character.fple_six_side
        @type = 4 # Special identifier for 6-sided
        @d_align = character.fple_d_align
        @v_align = character.fple_v_align
        @mirror = 0 # Could add logic for different mirror states per side if needed
        return
      end
      # Original logic for single surfaces
      case direction
      when 2
        @type = character.fple_type
        @d_align = 2 - character.fple_d_align
        if character.fple_one_side
          @mirror = 1
          if type == 2 && ($game_player.x << 7) - dx <= 0
            @mirror = 0
          end
        end
      when 4
        if character.fple_type > 0
          @type = 3 - character.fple_type
        else
          @type = 0
        end
        if character.fple_type < 2
          @d_align = character.fple_d_align
        elsif character.fple_type == 2
          @d_align = 2 - character.fple_d_align
        end
        if character.fple_one_side
          @mirror = 1
          if type == 2 && ($game_player.y << 7) - dy > 0
            @mirror = 0
          end
        end
      when 6
        if character.fple_type > 0
          @type = 3 - character.fple_type
        else
          @type = 0
        end
        if character.fple_type < 2
          @d_align = 2 - character.fple_d_align
        elsif character.fple_type == 2
          @d_align = character.fple_d_align
        end
        if character.fple_one_side
          @mirror = 0
          if type == 2 && ($game_player.y << 7) - dy < 0
            @mirror = 1
          end
        end
      when 8
        @type = character.fple_type
        @d_align = character.fple_d_align
        if character.fple_one_side
          @mirror = 0
          if type == 2 && ($game_player.x << 7) - dx >= 0
            @mirror = 1
          end
        end
      end
      @v_align = character.fple_v_align
    end
    #--------------------------------------------------------------------------
    # * Calculate screen coordinates to display animations
    # return Array[2]<Integer>
    #--------------------------------------------------------------------------
  def find_coordinates
      direction = $game_player.direction
      case direction
      when 2
        y = dy - ($game_player.y << 7)
        x = ($game_player.x << 7) - dx
      when 4
        y = ($game_player.x << 7) - dx
        x = ($game_player.y << 7) - dy
      when 6
        y = dx - ($game_player.x << 7)
        x = dy - ($game_player.y << 7)
      when 8
        y = ($game_player.y << 7) - dy
        x = dx - ($game_player.x << 7)
      end
      if y == 0 then return nil end
      offset_x = 0
      offset_y = 0
      if character.fple_v_align != 1
        x1_proj = 272 + (272 * x - (136 << 7)) / y
        x2_proj = 272 + (272 * x + (136 << 7)) / y
        offset_x = x2_proj - x1_proj >> 1
        offset_y = offset_x
        if bitmap
          offset_x -= (bitmap.width >> 1)
          offset_y -= (bitmap.height >> 1)
        end
      end
      x_proj = 272 + (272 * x) / y
      y_proj = ((544 * y) - (272 << 7)) / (y << 1)
      if character.fple_v_align == 0
        y_proj -= offset_y
      elsif character.fple_v_align == 2
        y_proj += offset_y
      end
      return [x_proj, y_proj]
    end
    #--------------------------------------------------------------------------
    # * Start Balloon Icon Display
    #--------------------------------------------------------------------------
    def start_balloon
      dispose_balloon
      @balloon_duration = 8 * balloon_speed + balloon_wait
      @balloon_sprite = ::Sprite.new(spriteset.viewport1)
      @balloon_sprite.bitmap = Cache.system("Balloon")
      @balloon_sprite.ox = -16
      @balloon_sprite.oy = +128
      update_balloon
    end
    #--------------------------------------------------------------------------
    # * Dispose of Balloon Icon
    #--------------------------------------------------------------------------
    def dispose_balloon
      if @balloon_sprite
        @balloon_sprite.dispose
        @balloon_sprite = nil
      end
    end
    #--------------------------------------------------------------------------
    # * Update Balloon Icon
    #--------------------------------------------------------------------------
    def update_balloon
      if @balloon_duration > 0
        @balloon_duration -= 1
        if @balloon_duration > 0
          coordinates = find_coordinates
          if coordinates
            @balloon_sprite.x = coordinates[0]
            @balloon_sprite.y = coordinates[1]
            @balloon_sprite.z = 9999
            sx = balloon_frame_index * 32
            sy = (@balloon_id - 1) * 32
            @balloon_sprite.src_rect.set(sx, sy, 32, 32)
          else
            end_balloon
          end
        else
          end_balloon
        end
      end
    end
    #--------------------------------------------------------------------------
    # * End Balloon Icon
    #--------------------------------------------------------------------------
    def end_balloon
      dispose_balloon
      character.balloon_id = 0
    end
    #--------------------------------------------------------------------------
    # * Balloon Icon Display Speed
    #--------------------------------------------------------------------------
    def balloon_speed
      return 8
    end
    #--------------------------------------------------------------------------
    # * Wait Time for Last Frame of Balloon
    #--------------------------------------------------------------------------
    def balloon_wait
      return 12
    end
    #--------------------------------------------------------------------------
    # * Frame Number of Balloon Icon
    #--------------------------------------------------------------------------
    def balloon_frame_index
      return 7 - [(@balloon_duration - balloon_wait) / balloon_speed, 0].max
    end
  end
end

#==============================================================================
# ** Scene_Map
#==============================================================================
class Scene_Map < Scene_Base
  #--------------------------------------------------------------------------
  # * Aliased methods (F12 compatibility)
  #--------------------------------------------------------------------------
  unless @already_aliased_fple
    alias create_spriteset_fple create_spriteset
    alias perform_transfer_fple perform_transfer
    alias post_transfer_fple post_transfer
    @already_aliased_fple = true
  end
  #--------------------------------------------------------------------------
  # * Create Sprite Set
  #--------------------------------------------------------------------------
  def create_spriteset
    if $game_system.fple
      @spriteset = FPLE::Spriteset_Map.new
    else
      create_spriteset_fple
    end
  end
  #--------------------------------------------------------------------------
  # * Player Transfer Processing
  #--------------------------------------------------------------------------
  def perform_transfer
    @exec_transfer = $game_player.transfer?
    @fple_old = $game_system.fple
    perform_transfer_fple
  end
  #--------------------------------------------------------------------------
  # * Post Processing for Transferring Player
  #--------------------------------------------------------------------------
  def post_transfer
    if @exec_transfer && @fple_old != $game_system.fple
      @spriteset.dispose
      create_spriteset
    end
    if $game_system.fple
      $game_temp.force_render = true
    end
    post_transfer_fple
  end
end
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
   FPLE Camera Height DerVVulfman 23 41,519 07-20-2020, 01:41 AM
Last Post: rzrcoon
   [WIP] FPLE 2 MGC 17 50,466 01-08-2018, 04:06 AM
Last Post: Darkantuan
   L's Simple Main Menu #3 - 1-person Landarma 1 9,143 10-14-2010, 04:25 AM
Last Post: Landarma
   1-Person DBS Raziel 0 7,676 03-07-2008, 04:41 AM
Last Post: Raziel



Users browsing this thread: 1 Guest(s)