FPLE ACE Addon/Enhancement.
#1
@A.I. Topical Post. Tag.

I upgraded FPLE Ace Using Claude.AI, Integrated a 4-Side & 6-Side box comment, which can create buildings/grass boxes/objects.
6 Side had type2+d1 & type1+d1)) but changed it to a type 0 to save FPS, you can add it back if you want.

For below/Above, type 0 objects you want to walk over add, 1-Side, to them.
I added code the following code so these player facing objects without 1-Side push forward, for NPCs.
if dy == 0 && dx == 0 && surface.character.fple_type == 0 && surface.character.fple_one_side == false.

(Fit,Type1,D1,1Side,V2) objects have a side profile added to them, but should always be, same not below/above.
you can jump over these with a moveset thuf-on/step forward step forward thur-off, they are a type of ledge.

You may have to code in your own.  (Fit,Type2,D1,1Side,V2), I'm not currently tested to check its same function.

I've worked out a fix for rotation letting you see events thur walls briefly, here is the 1st rough draft of the script.
I would have posted a few days sooner, but was having issues with strafing crashing the game for some reason.

The resolution is also no longer Default, it's 608x417, you can place it back with the original script, or pick Res 2.

The Enhanced Script requires Background Addon to function properly, I've not worked out the private call issue.

Code:
#========================Author: MGC=============================
# First Person Labyrinth Explorer (FPLE) Engine - VX Ace version.
# v.1.5 old, now upgraded to v.1.8 using Claude.AI by 5Brainplay.
#================================================================
# 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
  #-----------------------------

# Modify the update method to use a more complete background fill
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
      base_z = (surface[1].abs << 5) + surface[2].abs
      
      # Apply rotation compensation for map surfaces
      if FPLE.angle > 0
        rotation_factor = FPLE.angle / 90.0
        # Increase z-depth for surfaces during rotation to prevent bleeding
        base_z += (rotation_factor * 1000).to_i
      end
      
      surface[5] = base_z
    else # character surfaces
      base_z = (surface[1].abs >> 1) + (surface[2].abs >> 6) - 2
    
      # Check if surface has z-modifier (from multi-sided rendering)
      z_modifier = surface[15] || 0
    
      # Enhanced rotation compensation for character surfaces
      if FPLE.angle > 0
        rotation_factor = FPLE.angle / 90.0
        
        # Push character surfaces further back during rotation
        rotation_penalty = rotation_factor * 500
        
        # Additional penalty based on distance from player
        distance_penalty = Math.sqrt(surface[1] * surface[1] + surface[2] * surface[2]) * 0.1
        
        base_z += (rotation_penalty + distance_penalty).to_i
      end
    
      # Surface type and alignment offsets
      type_offset = surface[7] || 0
      align_offset = surface[8] || 0
    
      z_offset = case type_offset
      when 1 # Horizontal walls
        case surface[2] <=> 0
        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
        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
    
      # Vertical alignment offset
      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
      if @angle == 0 && $game_temp.movement_dir == 9 || $game_temp.movement_dir == 10
        surface[5] = base_z.to_f + z_offset + z_modifier
      else
        surface[5] = base_z.to_f + z_offset + v_align_offset + z_modifier
      end
    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 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 position_occluded_during_rotation?(x, y, player_x, player_y, check_direction)
  # Calculate the path from player to target position
  dx = x - player_x
  dy = y - player_y
  
    if @fple_map.get_data(x, y)[0] > 0  && $game_temp.movement_dir == 9 || $game_temp.movement_dir == 10
      return false unless $game_map.region_id($game_player.x, $game_player.y) == 7
    else
  end
  
  # Don't occlude events that are directly adjacent (doors on walls)
  if dx.abs <= 1 && dy.abs <= 1 && (dx.abs + dy.abs) == 1
    return false
  end
  
  # For single-tile distances, check adjacent walls
  if dx.abs <= 1 && dy.abs <= 1
    case check_direction
    when 2 # Down
      # Check for walls between player and target going down
      return @fple_map.get_data(player_x, player_y + 1)[0] > 0 if dy > 0
    when 4 # Left  
      # Check for walls between player and target going left
      return @fple_map.get_data(player_x - 1, player_y)[0] > 0 if dx < 0
    when 6 # Right
      # Check for walls between player and target going right  
      return @fple_map.get_data(player_x + 1, player_y)[0] > 0 if dx > 0
    when 8 # Up
      # Check for walls between player and target going up
      return @fple_map.get_data(player_x, player_y - 1)[0] > 0 if dy < 0
    end
  end
  
  # For longer distances, use line-of-sight checking
  return line_of_sight_blocked?(player_x, player_y, x, y)
end
  
#-----------------------------
# * Check if line of sight is blocked by walls
# param x1, y1 : Integer - start coordinates
# param x2, y2 : Integer - end coordinates  
# return Boolean - true if blocked
#-----------------------------
def line_of_sight_blocked?(x1, y1, x2, y2)
  # Simple bresenham-like algorithm to check for wall blocking
  dx = (x2 - x1).abs
  dy = (y2 - y1).abs
  
  return false if dx == 0 && dy == 0
  
  x_step = x1 < x2 ? 1 : -1
  y_step = y1 < y2 ? 1 : -1
  
  # Check key points along the path
  if dx > dy
    # More horizontal movement
    (1..dx).each do |i|
      check_x = x1 + (i * x_step)
      check_y = y1 + ((i * dy * y_step) / dx)
      return true if @fple_map.get_data(check_x, check_y)[0] > 0
    end
  else
    # More vertical movement  
    (1..dy).each do |i|
      check_x = x1 + ((i * dx * x_step) / dy)
      check_y = y1 + (i * y_step)
      return true if @fple_map.get_data(check_x, check_y)[0] > 0
    end
  end
  
  false
end

#-----------------------------
# * Enhanced character surface filtering with occlusion
# param character_surfaces : Array<FPLE::Surface_Characters>
# param player_dir : Integer - current player direction
# param rotation_dir : Integer - direction of rotation (next direction)
# return Array<FPLE::Surface_Characters> - filtered surfaces
#-----------------------------
def filter_character_surfaces_with_occlusion(character_surfaces, player_dir, rotation_dir = nil)
  return [] if character_surfaces.empty?
  
  player_x = $game_player.x
  player_y = $game_player.y
  filtered_surfaces = []
  
  character_surfaces.each do |surface|
    next unless surface.character && surface.visible && surface.opacity > 0
    
    char_x = surface.character.x
    char_y = surface.character.y
    
    # Skip if too distant
    dist_x = (player_x - char_x).abs  
    dist_y = (player_y - char_y).abs
    max_dist = $game_system.fple_view_distance + 2
    next if dist_x > max_dist || dist_y > max_dist
    
    # Check occlusion for current direction
    current_occluded = position_occluded_during_rotation?(char_x, char_y, player_x, player_y, player_dir)
    
    # Check occlusion for rotation direction if specified
    rotation_occluded = rotation_dir ?
      position_occluded_during_rotation?(char_x, char_y, player_x, player_y, rotation_dir) : false
    
    # Include surface if visible from either direction
    unless current_occluded && rotation_occluded
      filtered_surfaces << surface
    end
  end
  
  filtered_surfaces
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
  
  # Filter character surfaces with occlusion checking
  filtered_surfaces = filter_character_surfaces_with_occlusion(character_surfaces, @current_dir,
    case @current_dir
    when 2 then 6  # Down -> Right
    when 4 then 2  # Left -> Down  
    when 6 then 8  # Right -> Up
    when 8 then 4  # Up -> Left
    end)
  
  clear_character_surfaces(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_right(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_down(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_up(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_left(filtered_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
  
  # Filter character surfaces with occlusion checking
  filtered_surfaces = filter_character_surfaces_with_occlusion(character_surfaces, @current_dir,
    case @current_dir
    when 2 then 4  # Down -> Left
    when 4 then 8  # Left -> Up
    when 6 then 2  # Right -> Down
    when 8 then 6  # Up -> Right
    end)
  
  clear_character_surfaces(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_down(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_left(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_right(filtered_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(filtered_surfaces)
    surfaces_temp2 += add_character_surfaces_up(filtered_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

#-----------------------------
# * Enhanced turn up method (if needed for your system)
# param character_surfaces : Array<FPLE::Surface_Characters>
#-----------------------------
def refresh_surfaces_turn_up(character_surfaces)
  @current_dir = $game_player.direction
  self.surfaces = []
  @x_ref = $game_player.x
  @y_ref = $game_player.y
  surfaces_temp = refresh_current_surface
  
  # Filter character surfaces with occlusion checking
  filtered_surfaces = filter_character_surfaces_with_occlusion(character_surfaces, @current_dir, 8)
  
  clear_character_surfaces(filtered_surfaces)
  
  # Render current direction surfaces
  surfaces_temp1 = case $game_player.direction
  when 2 then refresh_surfaces_down
  when 4 then refresh_surfaces_left  
  when 6 then refresh_surfaces_right
  when 8 then refresh_surfaces_up
  else []
  end
  
  # Render up direction surfaces
  @current_dir = 8
  surfaces_temp2 = refresh_surfaces_up(false)
  @current_dir = $game_player.direction
  
  # Add character surfaces
  surfaces_temp1 += case $game_player.direction
  when 2 then add_character_surfaces_down(filtered_surfaces)
  when 4 then add_character_surfaces_left(filtered_surfaces)
  when 6 then add_character_surfaces_right(filtered_surfaces)
  when 8 then add_character_surfaces_up(filtered_surfaces)
  else []
  end
  
  surfaces_temp2 += add_character_surfaces_up(filtered_surfaces)
  
  surfaces_temp1.each {|surface| adjust_surface_for_rotation(surface)} unless $game_player.direction == 8
  
  # 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

#-----------------------------
# * Enhanced turn down method (if needed for your system)  
# param character_surfaces : Array<FPLE::Surface_Characters>
#-----------------------------
def refresh_surfaces_turn_down(character_surfaces)
  @current_dir = $game_player.direction
  self.surfaces = []
  @x_ref = $game_player.x
  @y_ref = $game_player.y
  surfaces_temp = refresh_current_surface
  
  # Filter character surfaces with occlusion checking
  filtered_surfaces = filter_character_surfaces_with_occlusion(character_surfaces, @current_dir, 2)
  
  clear_character_surfaces(filtered_surfaces)
  
  # Render current direction surfaces
  surfaces_temp1 = case $game_player.direction
  when 2 then refresh_surfaces_down
  when 4 then refresh_surfaces_left
  when 6 then refresh_surfaces_right
  when 8 then refresh_surfaces_up
  else []
  end
  
  # Render down direction surfaces
  @current_dir = 2
  surfaces_temp2 = refresh_surfaces_down(false)
  @current_dir = $game_player.direction
  
  # Add character surfaces
  surfaces_temp1 += case $game_player.direction
  when 2 then add_character_surfaces_down(filtered_surfaces)
  when 4 then add_character_surfaces_left(filtered_surfaces)
  when 6 then add_character_surfaces_right(filtered_surfaces)
  when 8 then add_character_surfaces_up(filtered_surfaces)
  else []
  end
  
  surfaces_temp2 += add_character_surfaces_down(filtered_surfaces)
  
  surfaces_temp1.each {|surface| adjust_surface_for_rotation(surface)} unless $game_player.direction == 2
  
  # 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

Code:
#==============================================================================
# ** FPLE Background Picture Addon - Enhanced
#v.1.5 old, now upgraded to v.1.8 using Claude.AI by 5Brainplay.
#==============================================================================
# This addon creates a background picture that renders behind all FPLE surfaces
# but in front of the engine's default background to prevent color bleeding.
# The background now positions itself at VIEW_DISTANCE + 1 for proper depth.
#==============================================================================

module FPLE
  #--------------------------------------------------------------------------
  # * Background Picture Configuration
  #--------------------------------------------------------------------------
  BACKGROUND_ENABLED = false      # Enable/disable background picture
  BACKGROUND_COLOR = Color.new(115, 190, 215, 255)  # Default color (R,G,B,A)
  BACKGROUND_PICTURE_FILE = nil  # Optional: use "filename" for custom image
  BACKGROUND_Z_DEPTH = -999    # Z-depth (negative = behind everything)
end

#==============================================================================
# ** FPLE Core - Modified
#==============================================================================
module FPLE
  #--------------------------------------------------------------------------
  # * Aliased initialization method
  #--------------------------------------------------------------------------
  class << self
    unless @background_addon_aliased
      alias initialize_fple_background initialize_fple
      @background_addon_aliased = true
    end
  end
 
  #--------------------------------------------------------------------------
  # * Initialize with background
  #--------------------------------------------------------------------------
  def self.initialize_fple(spriteset, viewport)
    initialize_fple_background(spriteset, viewport)
    if BACKGROUND_ENABLED
      create_background_picture(viewport)
    end
  end
 
  #--------------------------------------------------------------------------
  # * Create background picture with view distance integration
  #--------------------------------------------------------------------------
  def self.create_background_picture(viewport)
    @background_sprite = Sprite.new(viewport)
    @background_sprite.z = BACKGROUND_Z_DEPTH
   
    if BACKGROUND_PICTURE_FILE && FileTest.exist?("Graphics/Pictures/#{BACKGROUND_PICTURE_FILE}.png")
      # Use custom image file
      @background_sprite.bitmap = Cache.picture(BACKGROUND_PICTURE_FILE)
    else
      # Create solid color background
      @background_sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height)
      @background_sprite.bitmap.fill_rect(0, 0, Graphics.width, Graphics.height, BACKGROUND_COLOR)
    end
   
    # Position behind everything but visible
    @background_sprite.x = 0
    @background_sprite.y = 0
   
    # Add background surface to the surfaces array for proper depth rendering
    create_background_surface
  end
 
  #--------------------------------------------------------------------------
  # * Create background surface at VIEW_DISTANCE + 1
  #--------------------------------------------------------------------------
  def self.create_background_surface
    # Calculate the distance to render the background at (VIEW_DISTANCE + 1)
    background_distance = ($game_system.fple_view_distance + 1) << 7
   
    # Create a background surface that will be processed by the 3D engine
    @background_surface = [
      0,                          # texture_id (0 = no texture, use solid color)
      background_distance,        # dy (depth distance)
      0,                          # dx (horizontal offset)
      6,                          # type (6 = background plane, custom type)
      0,                          # secondary texture
      0,                          # z-order placeholder
      0,                          # textureset_id
      8,                          # textureset_width
      BACKGROUND_COLOR.red,       # red component
      BACKGROUND_COLOR.green,     # green component
      BACKGROUND_COLOR.blue,      # blue component
      BACKGROUND_COLOR.alpha      # alpha component
    ]
  end
 
  #--------------------------------------------------------------------------
  # * Add background surface to game map surfaces
  #--------------------------------------------------------------------------
  def self.add_background_to_surfaces
    return unless BACKGROUND_ENABLED && @background_surface
   
    # Calculate proper z-depth for background
    distance = ($game_system.fple_view_distance) << 1
    @background_surface[5] = distance << 5  # Set z-order
   
    # Add background surface to the main surfaces array if not already present
    unless $game_map.surfaces.any? { |surface| surface[3] == 6 }
      $game_map.surfaces.push(@background_surface.dup)
    end
  end
 
  #--------------------------------------------------------------------------
  # * Aliased dispose method
  #--------------------------------------------------------------------------
  class << self
    unless @dispose_background_aliased
      alias dispose_background dispose
      @dispose_background_aliased = true
    end
  end
 
  #--------------------------------------------------------------------------
  # * Dispose with background cleanup
  #--------------------------------------------------------------------------
  def self.dispose
    dispose_background
    if @background_sprite
      @background_sprite.dispose
      @background_sprite = nil
    end
    @background_surface = nil
  end
 
  #--------------------------------------------------------------------------
  # * Update background (called during movement/rotation)
  #--------------------------------------------------------------------------
  def self.update_background
    return unless @background_surface && BACKGROUND_ENABLED
   
    # Update the background distance based on current view distance
    background_distance = ($game_system.fple_view_distance) << 7
    @background_surface[1] = background_distance
   
    # Update z-order
    distance = ($game_system.fple_view_distance) << 1
    @background_surface[5] = distance << 5
   
    # Add parallax effects if desired
    if @angle != 0
      # Slight horizontal shift based on rotation
      @background_surface[2] = (@angle * 2)  # Adjust multiplier as needed
    else
      @background_surface[2] = 0
    end
   
    # Adjust for movement offsets if desired (subtle parallax)
    # @background_surface[2] += @offset_x / 8
  end
 
  #--------------------------------------------------------------------------
  # * Aliased update method
  #--------------------------------------------------------------------------
  class << self
    unless @update_background_aliased
      alias update_fple_background update
      @update_background_aliased = true
    end
  end
 
  #--------------------------------------------------------------------------
  # * Update with background
  #--------------------------------------------------------------------------
  def self.update
    update_fple_background
    if BACKGROUND_ENABLED
      update_background
      add_background_to_surfaces
    end
  end
end

#==============================================================================
# ** Game_Map - Background Integration
#==============================================================================
class Game_Map
  #--------------------------------------------------------------------------
  # * Aliased refresh_surfaces method
  #--------------------------------------------------------------------------
  unless @background_surfaces_aliased
    alias refresh_surfaces_background refresh_surfaces
    @background_surfaces_aliased = true
  end
 
  #--------------------------------------------------------------------------
  # * Refresh surfaces with background integration
  #--------------------------------------------------------------------------
  def refresh_surfaces
    refresh_surfaces_background
      surfaces.sort! { |a, b| b[5] - a[5] } if surfaces.any? { |s| s[3] == 6 }
  end
end

#==============================================================================
# ** Game_Interpreter - Script Commands
#==============================================================================
class Game_Interpreter
  #--------------------------------------------------------------------------
  # * Change FPLE Background Color
  #--------------------------------------------------------------------------
  def change_fple_background_color(red, green, blue, alpha = 255)
    return unless $game_system.fple && FPLE::BACKGROUND_ENABLED
   
    # Update the background surface color
    if FPLE.instance_variable_get(:@background_surface)
      surface = FPLE.instance_variable_get(:@background_surface)
      surface[8] = red
      surface[9] = green
      surface[10] = blue
      surface[11] = alpha
    end
   
    # Also update the sprite for immediate visual feedback
    if FPLE.instance_variable_get(:@background_sprite)
      sprite = FPLE.instance_variable_get(:@background_sprite)
      color = Color.new(red, green, blue, alpha)
      sprite.bitmap.fill_rect(0, 0, Graphics.width, Graphics.height, color)
    end
   
    $game_temp.force_render = true
  end
 
  #--------------------------------------------------------------------------
  # * Change FPLE Background Picture
  #--------------------------------------------------------------------------
  def change_fple_background_picture(filename)
    return unless $game_system.fple && FPLE::BACKGROUND_ENABLED
   
    if FPLE.instance_variable_get(:@background_sprite)
      sprite = FPLE.instance_variable_get(:@background_sprite)
      sprite.bitmap.dispose if sprite.bitmap
     
      if filename && FileTest.exist?("Graphics/Pictures/#{filename}.png")
        sprite.bitmap = Cache.picture(filename)
        # When using a picture, set surface type to use texture instead of color
        if FPLE.instance_variable_get(:@background_surface)
          surface = FPLE.instance_variable_get(:@background_surface)
          surface[0] = 1  # Enable texture rendering
        end
      else
        # Fallback to solid color
        sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height)
        sprite.bitmap.fill_rect(0, 0, Graphics.width, Graphics.height, FPLE::BACKGROUND_COLOR)
        # Reset to color mode
        if FPLE.instance_variable_get(:@background_surface)
          surface = FPLE.instance_variable_get(:@background_surface)
          surface[0] = 0  # Disable texture, use color
        end
      end
    end
   
    $game_temp.force_render = true
  end
 
  #--------------------------------------------------------------------------
  # * Toggle FPLE Background
  #--------------------------------------------------------------------------
  def toggle_fple_background(enabled)
    return unless $game_system.fple
   
    if FPLE.instance_variable_get(:@background_sprite)
      sprite = FPLE.instance_variable_get(:@background_sprite)
      sprite.visible = enabled
    end
   
    # Remove or add background surface based on enabled state
    if enabled
      FPLE.add_background_to_surfaces
    else
      $game_map.surfaces.reject! { |surface| surface[3] == 6 }
    end
   
    $game_temp.force_render = true
  end
 
  #--------------------------------------------------------------------------
  # * Update Background Distance (call when VIEW_DISTANCE changes)
  #--------------------------------------------------------------------------
  def update_background_distance
    return unless $game_system.fple && FPLE::BACKGROUND_ENABLED
   
    if FPLE.instance_variable_get(:@background_surface)
      FPLE.update_background
      $game_temp.force_render = true
    end
  end
end

#==============================================================================
# ** Game_Temp - Background Integration
#==============================================================================
class Game_Temp
  #--------------------------------------------------------------------------
  # * Aliased set_view method to update background distance
  #--------------------------------------------------------------------------
  unless @background_view_aliased
    alias set_view_background set_view
    @background_view_aliased = true
  end
 
  #--------------------------------------------------------------------------
  # * Set view distance with background update
  #--------------------------------------------------------------------------
  def set_view(distance)
    set_view_background(distance)
   
    # Update background distance when view distance changes
    if $game_system.fple && FPLE::BACKGROUND_ENABLED
      FPLE.update_background
    end
  end
end

#==============================================================================
# ** Usage Instructions
#==============================================================================
# Place this script below the main FPLE script but above Main.
#
# Configuration:
# - BACKGROUND_ENABLED: Set to true/false to enable/disable
# - BACKGROUND_COLOR: Set the RGB color (0-255 for each value) 
# - BACKGROUND_PICTURE_FILE: Optional - use a picture file instead of solid color
# - BACKGROUND_Z_DEPTH: Z-depth value (negative = behind surfaces)
#
# The background will automatically render at VIEW_DISTANCE + 1, providing
# a backdrop that appears behind all game surfaces but maintains proper depth.
#
# Script Commands (use in events):
# - change_fple_background_color(255, 0, 0)    # Change to red
# - change_fple_background_picture("sunset")   # Use Graphics/Pictures/sunset.png 
# - toggle_fple_background(false)              # Hide background
# - toggle_fple_background(true)               # Show background
# - update_background_distance                 # Refresh after VIEW_DISTANCE changes
#==============================================================================

Code:
#===============================================================================
# FPLE Ground Tile Swap Addon v1.1
# v.1.5 old, now upgraded to v.1.8 using Claude.AI by 5Brainplay.
# Author: Assistant
#===============================================================================
# This addon allows swapping ground tiles in FPLE maps during gameplay.
# Place this script below the main FPLE script but above "Main"
#
# Features:
# - Swap ground tiles at specific coordinates
# - Batch swap multiple tiles
# - Restore original tiles
# - Event-based tile swapping
# - Animated tile transitions (optional)
# - Region-based ground tile swapping
# - Animated region tiles
#
# Usage Examples:
# $game_map.swap_ground_tile(x, y, new_texture_id)
# $game_map.restore_ground_tile(x, y)
# $game_map.batch_swap_ground_tiles([[x1, y1, texture1], [x2, y2, texture2]])
# $game_map.swap_region_ground_tiles(region_id, texture_id)
#
# Event Commands:
# In event "Script..." command:
# swap_ground(x, y, texture_id)
# restore_ground(x, y)
# swap_region_ground(region_id, texture_id)
# animate_region_ground(region_id, base_texture_id)
#===============================================================================
#==============================================================================
# ** Game_Map - Ground Tile Swapping Extensions
#==============================================================================
class Game_Map
  #--------------------------------------------------------------------------
  # * Aliased methods
  #--------------------------------------------------------------------------
  unless @already_aliased_ground_swap
    alias initialize_ground_swap initialize
    alias setup_ground_swap setup
    @already_aliased_ground_swap = true
  end
 
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :original_ground_tiles  # Hash to store original tile data
  attr_accessor :swapped_ground_tiles   # Hash to track swapped tiles
  attr_accessor :animated_ground_tiles  # Hash to store animation data
  attr_accessor :ground_animation_timer # Integer for frame counting
 
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    initialize_ground_swap
    @original_ground_tiles = {}
    @swapped_ground_tiles = {}
    @animated_ground_tiles = {}
    @ground_animation_timer = 0
  end
 
  #--------------------------------------------------------------------------
  # * Setup
  #--------------------------------------------------------------------------
  def setup(map_id)
    setup_ground_swap(map_id)
    @original_ground_tiles = {}
    @swapped_ground_tiles = {}
    @animated_ground_tiles = {}
    @ground_animation_timer = 0
  end
 
  #--------------------------------------------------------------------------
  # * Swap Ground Tile at Specific Coordinates
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate 
  # param new_texture_id : Integer - New ground texture ID
  # param animate : Boolean - Whether to animate the transition (optional)
  #--------------------------------------------------------------------------
  def swap_ground_tile(x, y, new_texture_id, animate = false)
    return unless is_fple? && @fple_map
    return unless valid_coordinates?(x, y)
   
    key = [x, y]
   
    # Store original tile data if not already stored
    unless @original_ground_tiles.has_key?(key)
      original_data = @fple_map.get_data(x, y).dup
      @original_ground_tiles[key] = original_data
    end
   
    # Use FPLE's proper method to change ground texture
    if @fple_map.respond_to?(:change_ground)
      @fple_map.change_ground(x, y, new_texture_id)
    elsif @fple_map.respond_to?(:set_ground_tile)
      @fple_map.set_ground_tile(x, y, new_texture_id)
    else
      # Fallback: try to modify the data structure directly
      current_data = @fple_map.get_data(x, y)
      if current_data && current_data.size > 1
        current_data[1] = new_texture_id
      else
        return false
      end
    end
   
    @swapped_ground_tiles[key] = new_texture_id
   
    # Remove from animated tiles when doing a simple swap
    @animated_ground_tiles.delete(key)
   
    # Trigger visual update
    if animate
      animate_tile_swap(x, y)
    else
      force_surface_refresh
    end
   
    return true
  end
 
  #--------------------------------------------------------------------------
  # * Create Animated Ground Tile (cycles through next 3 tiles)
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate 
  # param base_texture_id : Integer - Base texture ID (will use this +1, +2, +3)
  # param frame_duration : Integer - Frames to wait between texture changes
  # param animate : Boolean - Whether to animate the initial transition
  #--------------------------------------------------------------------------
  def create_animated_ground_tile(x, y, base_texture_id, frame_duration = 30, animate = false)
    return unless is_fple? && @fple_map
    return unless valid_coordinates?(x, y)
   
    key = [x, y]
   
    # Store original tile data if not already stored
    unless @original_ground_tiles.has_key?(key)
      original_data = @fple_map.get_data(x, y).dup
      @original_ground_tiles[key] = original_data
    end
   
    # Setup animation data
    animation_data = {
      :frames => [base_texture_id, base_texture_id + 1, base_texture_id + 2],
      :current_frame => 0,
      :frame_duration => frame_duration,
      :frame_counter => 0
    }
   
    @animated_ground_tiles[key] = animation_data
    @swapped_ground_tiles[key] = base_texture_id
   
    # Set initial texture
    current_data = @fple_map.get_data(x, y)
    if current_data && current_data.size > 1
      current_data[1] = base_texture_id
     
      # Trigger visual update
      if animate
        animate_tile_swap(x, y)
      else
        force_surface_refresh
      end
     
      return true
    end
   
    return false
  end
 
  #--------------------------------------------------------------------------
  # * Restore Original Ground Tile
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  #--------------------------------------------------------------------------
  def restore_ground_tile(x, y)
    return unless is_fple? && @fple_map
    return unless valid_coordinates?(x, y)
   
    key = [x, y]
   
    if @original_ground_tiles.has_key?(key)
      # Restore original data
      original_data = @original_ground_tiles[key]
      current_data = @fple_map.get_data(x, y)
     
      if current_data && original_data
        current_data[1] = original_data[1]  # Restore ground texture
        @swapped_ground_tiles.delete(key)
        @animated_ground_tiles.delete(key)  # Remove animation data
       
        force_surface_refresh
        return true
      end
    end
   
    return false
  end
 
  #--------------------------------------------------------------------------
  # * Batch Swap Multiple Ground Tiles
  # param tile_data : Array<Array> - [[x, y, texture_id], ...]
  #--------------------------------------------------------------------------
  def batch_swap_ground_tiles(tile_data)
    return unless is_fple? && tile_data.is_a?(Array)
   
    success_count = 0
    tile_data.each do |data|
      next unless data.is_a?(Array) && data.size >= 3
      x, y, texture_id = data[0], data[1], data[2]
      success_count += 1 if swap_ground_tile(x, y, texture_id, false)
    end
   
    force_surface_refresh if success_count > 0
    return success_count
  end
 
  #--------------------------------------------------------------------------
  # * Restore All Swapped Ground Tiles
  #--------------------------------------------------------------------------
  def restore_all_ground_tiles
    return unless is_fple? && @fple_map
   
    restored_count = 0
    @swapped_ground_tiles.keys.each do |key|
      x, y = key[0], key[1]
      restored_count += 1 if restore_ground_tile(x, y)
    end
   
    force_surface_refresh if restored_count > 0
    return restored_count
  end
 
  #--------------------------------------------------------------------------
  # * Swap All Ground Tiles in a Specific Region
  # param region_id : Integer - Region ID to target (1-63, or 0 for no region)
  # param new_texture_id : Integer - New ground texture ID for all tiles in region
  # param animate : Boolean - Whether to animate the transitions (optional)
  # param exclude_events : Boolean - Whether to skip tiles with events (optional)
  #--------------------------------------------------------------------------
  def swap_region_ground_tiles(region_id, new_texture_id, animate = false, exclude_events = false)
    return unless is_fple? && @fple_map
    return unless region_id >= 0 && region_id <= 63
   
    success_count = 0
   
    # Loop through all tiles in the map
    (0...width).each do |x|
      (0...height).each do |y|
        # Check if this tile has the target region ID
        if region_id(x, y) == region_id
          # Skip tiles with events if requested
          if exclude_events && events_xy(x, y).any?
            next
          end
         
          # Swap the ground tile
          if swap_ground_tile(x, y, new_texture_id, false)
            success_count += 1
          end
        end
      end
    end
   
    # Handle animation/refresh after all swaps are done
    if success_count > 0
      if animate
        # Create a region-wide flash effect
        $game_map.screen.start_flash(Color.new(255, 255, 255, 80), 15)
      end
      force_surface_refresh
    end
   
    return success_count
  end
 
  #--------------------------------------------------------------------------
  # * Create Animated Ground Tiles for Entire Region
  # param region_id : Integer - Region ID to target (1-63, or 0 for no region)
  # param base_texture_id : Integer - Base texture ID (will use this +1, +2)
  # param frame_duration : Integer - Frames to wait between texture changes
  # param animate : Boolean - Whether to animate the initial transition
  # param exclude_events : Boolean - Whether to skip tiles with events (optional)
  #--------------------------------------------------------------------------
  def animate_region_ground_tiles(region_id, base_texture_id, frame_duration = 30, animate = false, exclude_events = false)
    return unless is_fple? && @fple_map
    return unless region_id >= 0 && region_id <= 63
   
    success_count = 0
   
    # Loop through all tiles in the map
    (0...width).each do |x|
      (0...height).each do |y|
        # Check if this tile has the target region ID
        if region_id(x, y) == region_id
          # Skip tiles with events if requested
          if exclude_events && events_xy(x, y).any?
            next
          end
         
          # Create animated ground tile
          if create_animated_ground_tile(x, y, base_texture_id, frame_duration, false)
            success_count += 1
          end
        end
      end
    end
   
    # Handle animation/refresh after all animations are set up
    if success_count > 0
      if animate
        # Create a region-wide flash effect
        $game_map.screen.start_flash(Color.new(200, 255, 200, 100), 20)
      end
      force_surface_refresh
    end
   
    return success_count
  end
 
  #--------------------------------------------------------------------------
  # * Restore All Ground Tiles in a Specific Region
  # param region_id : Integer - Region ID to target (1-63, or 0 for no region)
  # param exclude_events : Boolean - Whether to skip tiles with events (optional)
  #--------------------------------------------------------------------------
  def restore_region_ground_tiles(region_id, exclude_events = false)
    return unless is_fple? && @fple_map
    return unless region_id >= 0 && region_id <= 63
   
    restored_count = 0
   
    # Loop through all swapped tiles and restore those in the target region
    @swapped_ground_tiles.keys.each do |key|
      x, y = key[0], key[1]
     
      # Check if this tile is in the target region
      if region_id(x, y) == region_id
        # Skip tiles with events if requested
        if exclude_events && events_xy(x, y).any?
          next
        end
       
        # Restore the ground tile
        if restore_ground_tile(x, y)
          restored_count += 1
        end
      end
    end
   
    force_surface_refresh if restored_count > 0
    return restored_count
  end
 
  #--------------------------------------------------------------------------
  # * Check if Ground Tile is Swapped
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # return Boolean
  #--------------------------------------------------------------------------
  def ground_tile_swapped?(x, y)
    return @swapped_ground_tiles.has_key?([x, y])
  end
 
  #--------------------------------------------------------------------------
  # * Check if Ground Tile is Animated
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # return Boolean
  #--------------------------------------------------------------------------
  def ground_tile_animated?(x, y)
    return @animated_ground_tiles.has_key?([x, y])
  end
 
  #--------------------------------------------------------------------------
  # * Update Ground Tile Animations (call this from Scene_Map update)
  #--------------------------------------------------------------------------
  def update_ground_animations
    return unless is_fple? && @fple_map
    return if @animated_ground_tiles.empty?
   
    @ground_animation_timer += 1
    needs_refresh = false
   
    @animated_ground_tiles.each do |key, animation_data|
      animation_data[:frame_counter] += 1
     
      if animation_data[:frame_counter] >= animation_data[:frame_duration]
        # Reset counter and advance frame
        animation_data[:frame_counter] = 0
        animation_data[:current_frame] = (animation_data[:current_frame] + 1) % animation_data[:frames].size
       
        # Update the tile texture using proper FPLE methods
        x, y = key[0], key[1]
        new_texture_id = animation_data[:frames][animation_data[:current_frame]]
       
        # Use FPLE's proper method to change ground texture
        success = false
        if @fple_map.respond_to?(:change_ground)
          @fple_map.change_ground(x, y, new_texture_id)
          success = true
        elsif @fple_map.respond_to?(:set_ground_tile)
          @fple_map.set_ground_tile(x, y, new_texture_id)
          success = true
        else
          # Fallback: try to modify the data structure directly
          current_data = @fple_map.get_data(x, y)
          if current_data && current_data.is_a?(Array) && current_data.size > 1
            current_data[1] = new_texture_id
            success = true
          end
        end
       
        if success
          @swapped_ground_tiles[key] = new_texture_id
          needs_refresh = true
        end
      end
    end
   
    force_surface_refresh if needs_refresh
  end
 
  #--------------------------------------------------------------------------
  # * Get Current Ground Texture ID
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # return Integer
  #--------------------------------------------------------------------------
  def get_ground_texture_id(x, y)
    return nil unless valid_coordinates?(x, y) && @fple_map
   
    tile_data = @fple_map.get_data(x, y)
    return tile_data ? tile_data[1] : nil
  end
 
  #--------------------------------------------------------------------------
  # * Get Original Ground Texture ID
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # return Integer
  #--------------------------------------------------------------------------
  def get_original_ground_texture_id(x, y)
    key = [x, y]
    if @original_ground_tiles.has_key?(key)
      return @original_ground_tiles[key][1]
    else
      return get_ground_texture_id(x, y)
    end
  end
 
  #--------------------------------------------------------------------------
  # * Animate Tile Swap (Simple fade effect)
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  #--------------------------------------------------------------------------
  def animate_tile_swap(x, y)
    # This is a placeholder for animation
    # In a full implementation, you might want to:
    # - Create temporary sprites for transition effects
    # - Use screen flashes or particle effects
    # - Gradually fade between old and new textures
   
    $game_map.screen.start_flash(Color.new(255, 255, 255, 60), 10)
    force_surface_refresh
  end
 
  private
 
  #--------------------------------------------------------------------------
  # * Validate Coordinates
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # return Boolean
  #--------------------------------------------------------------------------
  def valid_coordinates?(x, y)
    return x >= 0 && x < width && y >= 0 && y < height
  end
 
  #--------------------------------------------------------------------------
  # * Force Surface Refresh
  #--------------------------------------------------------------------------
  def force_surface_refresh
    $game_temp.force_render = true if $game_temp
  end
end
#==============================================================================
# ** Game_Interpreter - Event Commands for Ground Swapping
#==============================================================================
class Game_Interpreter
  #--------------------------------------------------------------------------
  # * Swap Ground Tile (Event Command)
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # param texture_id : Integer - New ground texture ID
  #--------------------------------------------------------------------------
  def swap_ground(x, y, texture_id)
    $game_map.swap_ground_tile(x, y, texture_id, true)
  end
 
  #--------------------------------------------------------------------------
  # * Create Animated Ground Tile (Event Command)
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # param base_texture_id : Integer - Base texture ID (uses this +1, +2)
  # param frame_duration : Integer - Frames between animation changes (default: 30)
  #--------------------------------------------------------------------------
  def animate_ground(x, y, base_texture_id, frame_duration = 30)
    $game_map.create_animated_ground_tile(x, y, base_texture_id, frame_duration, true)
  end
 
  #--------------------------------------------------------------------------
  # * Restore Ground Tile (Event Command)
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  #--------------------------------------------------------------------------
  def restore_ground(x, y)
    $game_map.restore_ground_tile(x, y)
  end
 
  #--------------------------------------------------------------------------
  # * Batch Swap Ground Tiles (Event Command)
  # param tile_array : Array<Array> - [[x, y, texture_id], ...]
  #--------------------------------------------------------------------------
  def batch_swap_ground(tile_array)
    $game_map.batch_swap_ground_tiles(tile_array)
  end
 
  #--------------------------------------------------------------------------
  # * Restore All Ground Tiles (Event Command)
  #--------------------------------------------------------------------------
  def restore_all_ground
    $game_map.restore_all_ground_tiles
  end
 
  #--------------------------------------------------------------------------
  # * Swap All Ground Tiles in Region (Event Command)
  # param region_id : Integer - Region ID to target (1-63, or 0 for no region)
  # param texture_id : Integer - New ground texture ID
  # param exclude_events : Boolean - Skip tiles with events (optional)
  #--------------------------------------------------------------------------
  def swap_region_ground(region_id, texture_id, exclude_events = false)
    $game_map.swap_region_ground_tiles(region_id, texture_id, true, exclude_events)
  end
 
  #--------------------------------------------------------------------------
  # * Create Animated Ground Tiles for Region (Event Command)
  # param region_id : Integer - Region ID to target
  # param base_texture_id : Integer - Base texture ID (uses this +1, +2)
  # param frame_duration : Integer - Frames between animation changes (default: 30)
  # param exclude_events : Boolean - Skip tiles with events (optional)
  #--------------------------------------------------------------------------
  def animate_region_ground(region_id, base_texture_id, frame_duration = 30, exclude_events = false)
    $game_map.animate_region_ground_tiles(region_id, base_texture_id, frame_duration, true, exclude_events)
  end
 
  #--------------------------------------------------------------------------
  # * Restore All Ground Tiles in Region (Event Command)
  # param region_id : Integer - Region ID to target
  # param exclude_events : Boolean - Skip tiles with events (optional)
  #--------------------------------------------------------------------------
  def restore_region_ground(region_id, exclude_events = false)
    $game_map.restore_region_ground_tiles(region_id, exclude_events)
  end
 
  #--------------------------------------------------------------------------
  # * Check if Ground Tile is Swapped (Event Command)
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # return Boolean
  #--------------------------------------------------------------------------
  def ground_swapped?(x, y)
    return $game_map.ground_tile_swapped?(x, y)
  end
 
  #--------------------------------------------------------------------------
  # * Check if Ground Tile is Animated (Event Command)
  # param x : Integer - X coordinate
  # param y : Integer - Y coordinate
  # return Boolean
  #--------------------------------------------------------------------------
  def ground_animated?(x, y)
    return $game_map.ground_tile_animated?(x, y)
  end
end
#==============================================================================
# ** Game_Temp - Additional Switches and Variables
#==============================================================================
class Game_Temp
  #--------------------------------------------------------------------------
  # * Aliased methods
  #--------------------------------------------------------------------------
  unless @already_aliased_ground_swap_temp
    alias initialize_ground_swap_temp initialize
    @already_aliased_ground_swap_temp = true
  end
 
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :ground_swap_animation  # Boolean - Enable swap animations
 
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    initialize_ground_swap_temp
    @ground_swap_animation = true
  end
 
  #--------------------------------------------------------------------------
  # * Enable/Disable Ground Swap Animations
  # param enabled : Boolean
  #--------------------------------------------------------------------------
  def set_ground_swap_animation(enabled)
    @ground_swap_animation = enabled
  end
end
#==============================================================================
# ** DataManager - Save/Load Support
#==============================================================================
module DataManager
  #--------------------------------------------------------------------------
  # * Aliased methods
  #--------------------------------------------------------------------------
  class << self
    unless @already_aliased_ground_swap_data
      alias make_save_contents_ground_swap make_save_contents
      alias extract_save_contents_ground_swap extract_save_contents
      @already_aliased_ground_swap_data = true
    end
  end
 
  #--------------------------------------------------------------------------
  # * Create Save Contents
  #--------------------------------------------------------------------------
  def self.make_save_contents
    contents = make_save_contents_ground_swap
    contents[:ground_swaps] = {
      :original_tiles => $game_map.original_ground_tiles,
      :swapped_tiles => $game_map.swapped_ground_tiles,
      :animated_tiles => $game_map.animated_ground_tiles
    }
    contents
  end
 
  #--------------------------------------------------------------------------
  # * Extract Save Contents
  #--------------------------------------------------------------------------
  def self.extract_save_contents(contents)
    extract_save_contents_ground_swap(contents)
    if contents[:ground_swaps]
      $game_map.original_ground_tiles = contents[:ground_swaps][:original_tiles] || {}
      $game_map.swapped_ground_tiles = contents[:ground_swaps][:swapped_tiles] || {}
      $game_map.animated_ground_tiles = contents[:ground_swaps][:animated_tiles] || {}
    end
  end
end
#==============================================================================
# ** Scene_Map - Animation Updates
#==============================================================================
class Scene_Map < Scene_Base
  #--------------------------------------------------------------------------
  # * Aliased methods
  #--------------------------------------------------------------------------
  unless @already_aliased_ground_swap_scene
    alias update_ground_swap_scene update
    @already_aliased_ground_swap_scene = true
  end
 
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    update_ground_swap_scene
    $game_map.update_ground_animations if $game_system.fple
  end
end
#==============================================================================
# ** Usage Examples and Helper Methods
#==============================================================================
# Example usage in events:
#
# Script Command Examples:
# swap_ground(5, 3, 10)                         # Swap tile at (5,3) to texture 10
# animate_ground(5, 3, 15, 60)                  # Animate tile at (5,3) using textures 15,16,17 with 60-frame delays
# animate_ground(2, 4, 20)                      # Animate tile at (2,4) using textures 20,21,22 with default 30-frame delays
# restore_ground(5, 3)                          # Restore original tile at (5,3)
# batch_swap_ground([[1,1,5], [1,2,5]])        # Swap multiple tiles at once
# restore_all_ground                            # Restore all swapped tiles
# swap_region_ground(5, 10)                     # Swap all tiles in region 5 to texture 10
# swap_region_ground(3, 15, true)               # Swap region 3, skip tiles with events
# animate_region_ground(2, 20)                  # Animate all tiles in region 2 using textures 20,21,22
# animate_region_ground(4, 25, 60, true)        # Animate region 4 with 60-frame delays, skip events
# restore_region_ground(5)                      # Restore all tiles in region 5 to original
# restore_region_ground(3, true)                # Restore region 3, skip tiles with events
#
# Conditional Branch Examples:
# if ground_swapped?(5, 3)
#   # Do something if tile is swapped
# end
#
# if ground_animated?(5, 3)
#   # Do something if tile is animated
# end
#
# Advanced Script Examples:
# $game_map.create_animated_ground_tile($game_player.x, $game_player.y, 15, 45)
# $game_map.swap_ground_tile($game_player.x, $game_player.y, 15)
# texture_id = $game_map.get_ground_texture_id(5, 3)
# original_id = $game_map.get_original_ground_texture_id(5, 3)
# count = $game_map.swap_region_ground_tiles(1, 12, false, true)
# $game_map.animate_region_ground_tiles(7, 30, 45)
# Global helper methods for easier access
def swap_ground_tile(x, y, texture_id, animate = true)
  $game_map.swap_ground_tile(x, y, texture_id, animate)
end
def create_animated_ground_tile(x, y, base_texture_id, frame_duration = 30, animate = true)
  $game_map.create_animated_ground_tile(x, y, base_texture_id, frame_duration, animate)
end
def restore_ground_tile(x, y)
  $game_map.restore_ground_tile(x, y)
end
def ground_tile_swapped?(x, y)
  $game_map.ground_tile_swapped?(x, y)
end
def ground_tile_animated?(x, y)
  $game_map.ground_tile_animated?(x, y)
end
def swap_region_ground_tiles(region_id, texture_id, animate = true, exclude_events = false)
  $game_map.swap_region_ground_tiles(region_id, texture_id, animate, exclude_events)
end
def animate_region_ground_tiles(region_id, base_texture_id, frame_duration = 30, animate = true, exclude_events = false)
  $game_map.animate_region_ground_tiles(region_id, base_texture_id, frame_duration, animate, exclude_events)
end
def restore_region_ground_tiles(region_id, exclude_events = false)
  $game_map.restore_region_ground_tiles(region_id, exclude_events)
end

Code:
#==============================================================================
# ** FPLE Third Person Player Sprite
# v.1.5 old, now upgraded to v.1.8 using Claude.AI by 5Brainplay.
#==============================================================================
# This script adds a third-person view sprite of the player character that
# appears on screen during FPLE mode. The sprite can be positioned, scaled,
# and configured to show the player's current state.
#==============================================================================
module FPLE
  #--------------------------------------------------------------------------
  # * Player Sprite Configuration
  #--------------------------------------------------------------------------
  PLAYER_SPRITE_DEFAULT_X = 300              # Default X position on screen
  PLAYER_SPRITE_DEFAULT_Y = 450             # Default Y position on screen
  PLAYER_SPRITE_DEFAULT_SCALE = 3.0         # Default scale multiplier
  PLAYER_SPRITE_DEFAULT_OPACITY = 255       # Default opacity (0-255)
  PLAYER_SPRITE_Z = 1000                    # Z-order (higher = in front)
  PLAYER_SPRITE_DEFAULT_ANIMATE = true      # Default enable walking animation
  PLAYER_SPRITE_UPDATE_DIRECTION = false     # Update sprite direction based on player facing
 
  #--------------------------------------------------------------------------
  # * Player Sprite Settings (Dynamic)
  #--------------------------------------------------------------------------
  @@player_sprite_enabled = true
  @@player_sprite_animate = PLAYER_SPRITE_DEFAULT_ANIMATE
 
  def self.player_sprite_enabled
    @@player_sprite_enabled
  end
 
  def self.player_sprite_enabled=(value)
    @@player_sprite_enabled = value
  end
 
  def self.player_sprite_animate
    @@player_sprite_animate
  end
 
  def self.player_sprite_animate=(value)
    @@player_sprite_animate = value
  end
end
#==============================================================================
# ** FPLE::Player_Sprite
#==============================================================================
module FPLE
  class Player_Sprite < Sprite
    #--------------------------------------------------------------------------
    # * Object Initialization
    #--------------------------------------------------------------------------
    def initialize(viewport = nil)
      super(viewport)
      @character = $game_player
      @tile_id = 0
      @character_name = ""
      @character_index = 0
      @pattern = 0
      @direction = 8
      @animation_count = 0
      @stop_count = 0
      @jump_count = 0
     
      # Set initial position and properties
      self.x = FPLE::PLAYER_SPRITE_DEFAULT_X
      self.y = FPLE::PLAYER_SPRITE_DEFAULT_Y
      self.z = FPLE::PLAYER_SPRITE_Z
      self.zoom_x = FPLE::PLAYER_SPRITE_DEFAULT_SCALE
      self.zoom_y = FPLE::PLAYER_SPRITE_DEFAULT_SCALE
      self.opacity = FPLE::PLAYER_SPRITE_DEFAULT_OPACITY
     
      update
    end
   
    #--------------------------------------------------------------------------
    # * Frame Update
    #--------------------------------------------------------------------------
    def update
      super
      if FPLE.player_sprite_enabled
        update_bitmap
        update_src_rect
        update_visibility
        update_animation if FPLE.player_sprite_animate
      else
        self.visible = false
      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
      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 = tileset_bitmap(@tile_id)
      self.src_rect.set(sx, sy, 32, 32)
      self.ox = 16
      self.oy = 32
    end
   
    #--------------------------------------------------------------------------
    # * Set Character Bitmap
    #--------------------------------------------------------------------------
    def set_character_bitmap
      self.bitmap = Cache.character(@character_name)
      sign = @character_name[/^[\!\$]./]
      if sign && sign.include?('$')
        @cw = bitmap.width / 3
        @ch = bitmap.height / 4
      else
        @cw = bitmap.width / 12
        @ch = bitmap.height / 8
      end
      self.ox = @cw / 2
      self.oy = @ch
    end
   
    #--------------------------------------------------------------------------
    # * Get Tileset Bitmap
    #--------------------------------------------------------------------------
    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
   
    #--------------------------------------------------------------------------
    # * Update Transfer Origin Rectangle
    #--------------------------------------------------------------------------
    def update_src_rect
      if @tile_id == 0
        index = @character.character_index
        pattern = @pattern < 3 ? @pattern : 1
        sx = (index % 4 * 3 + pattern) * @cw
       
        # Update direction based on player facing if enabled
        if FPLE::PLAYER_SPRITE_UPDATE_DIRECTION
          @direction = @character.direction
        end
       
        sy = (index / 4 * 4 + (@direction - 2) / 2) * @ch
        self.src_rect.set(sx, sy, @cw, @ch)
      end
    end
   
    #--------------------------------------------------------------------------
    # * Update Visibility
    #--------------------------------------------------------------------------
    def update_visibility
      self.opacity = @character.opacity
      self.blend_type = @character.blend_type
      self.visible = !@character.transparent && FPLE.player_sprite_enabled
    end
   
    #--------------------------------------------------------------------------
    # * Update Animation
    #--------------------------------------------------------------------------
    def update_animation
      if $game_temp.movement
        # Player is moving, animate the sprite
        @stop_count = 0
        update_animation_pattern
      else
        # Player is stationary
        @stop_count += 1
        if @stop_count > 20  # Stop animation after standing still
          @pattern = 1  # Default standing pattern
          @animation_count = 0
        end
      end
    end
   
    #--------------------------------------------------------------------------
    # * Update Animation Pattern
    #--------------------------------------------------------------------------
    def update_animation_pattern
      @animation_count += 1.5
      if @animation_count > 18
        if @pattern == 0
          @pattern = 1
        elsif @pattern == 1
          @pattern = 2
        elsif @pattern == 2
          @pattern = 1
        end
        @animation_count = 0
      end
    end
   
    #--------------------------------------------------------------------------
    # * Set Position
    #--------------------------------------------------------------------------
    def set_position(x, y)
      self.x = x
      self.y = y
    end
   
    #--------------------------------------------------------------------------
    # * Set Scale
    #--------------------------------------------------------------------------
    def set_scale(scale_x, scale_y = nil)
      scale_y = scale_x if scale_y.nil?
      self.zoom_x = scale_x
      self.zoom_y = scale_y
    end
   
    #--------------------------------------------------------------------------
    # * Set Direction (for manual control)
    #--------------------------------------------------------------------------
    def set_direction(direction)
      @direction = direction
    end
  end
end
#==============================================================================
# ** FPLE::Spriteset_Map - Modified
#==============================================================================
# Add player sprite support to the existing FPLE spriteset
module FPLE
  class Spriteset_Map < ::Spriteset_Map
    #--------------------------------------------------------------------------
    # * Aliased methods for player sprite
    #--------------------------------------------------------------------------
    unless @already_aliased_player_sprite
      alias create_characters_with_player_sprite create_characters
      alias dispose_characters_with_player_sprite dispose_characters
      alias update_characters_with_player_sprite update_characters
      @already_aliased_player_sprite = true
    end
   
    #--------------------------------------------------------------------------
    # * Create Characters (with player sprite)
    #--------------------------------------------------------------------------
    def create_characters
      create_characters_with_player_sprite
      create_player_sprite
    end
   
    #--------------------------------------------------------------------------
    # * Create Player Sprite
    #--------------------------------------------------------------------------
    def create_player_sprite
      if FPLE.player_sprite_enabled
        @player_sprite = FPLE::Player_Sprite.new(@viewport1)
      end
    end
   
    #--------------------------------------------------------------------------
    # * Dispose of Characters (with player sprite)
    #--------------------------------------------------------------------------
    def dispose_characters
      dispose_player_sprite
      dispose_characters_with_player_sprite
    end
   
    #--------------------------------------------------------------------------
    # * Dispose of Player Sprite
    #--------------------------------------------------------------------------
    def dispose_player_sprite
      if @player_sprite
        @player_sprite.dispose
        @player_sprite = nil
      end
    end
   
    #--------------------------------------------------------------------------
    # * Update Characters (with player sprite)
    #--------------------------------------------------------------------------
    def update_characters
      update_characters_with_player_sprite
      update_player_sprite
    end
   
    #--------------------------------------------------------------------------
    # * Update Player Sprite
    #--------------------------------------------------------------------------
    def update_player_sprite
      if @player_sprite
        @player_sprite.update
      end
    end
  end
end
#==============================================================================
# ** Game_Temp - Player Sprite Commands
#==============================================================================
class Game_Temp
  #--------------------------------------------------------------------------
  # * Show Player Sprite
  #--------------------------------------------------------------------------
  def show_player_sprite
    if $game_system.fple
      FPLE.player_sprite_enabled = true
    end
  end
 
  #--------------------------------------------------------------------------
  # * Hide Player Sprite
  #--------------------------------------------------------------------------
  def hide_player_sprite
    if $game_system.fple
      FPLE.player_sprite_enabled = false
    end
  end
 
  #--------------------------------------------------------------------------
  # * Set Player Sprite Position
  #--------------------------------------------------------------------------
  def set_player_sprite_position(x, y)
    if $game_system.fple && SceneManager.scene.is_a?(Scene_Map)
      spriteset = SceneManager.scene.instance_variable_get(:@spriteset)
      if spriteset.is_a?(FPLE::Spriteset_Map) && spriteset.instance_variable_get(:@player_sprite)
        spriteset.instance_variable_get(:@player_sprite).set_position(x, y)
      end
    end
  end
 
  #--------------------------------------------------------------------------
  # * Set Player Sprite Scale
  #--------------------------------------------------------------------------
  def set_player_sprite_scale(scale_x, scale_y = nil)
    if $game_system.fple && SceneManager.scene.is_a?(Scene_Map)
      spriteset = SceneManager.scene.instance_variable_get(:@spriteset)
      if spriteset.is_a?(FPLE::Spriteset_Map) && spriteset.instance_variable_get(:@player_sprite)
        spriteset.instance_variable_get(:@player_sprite).set_scale(scale_x, scale_y)
      end
    end
  end
 
  #--------------------------------------------------------------------------
  # * Get Player Sprite Position
  #--------------------------------------------------------------------------
  def get_player_sprite_position
    if $game_system.fple && SceneManager.scene.is_a?(Scene_Map)
      spriteset = SceneManager.scene.instance_variable_get(:@spriteset)
      if spriteset.is_a?(FPLE::Spriteset_Map) && spriteset.instance_variable_get(:@player_sprite)
        sprite = spriteset.instance_variable_get(:@player_sprite)
        return [sprite.x, sprite.y]
      end
    end
    return [0, 0]
  end
 
  #--------------------------------------------------------------------------
  # * Reset Player Sprite to Default Position
  #--------------------------------------------------------------------------
  def reset_player_sprite_position
    set_player_sprite_position(FPLE::PLAYER_SPRITE_DEFAULT_X, FPLE::PLAYER_SPRITE_DEFAULT_Y)
  end
end
#==============================================================================
# ** Script Call Methods - Global functions for easier access
#==============================================================================
#--------------------------------------------------------------------------
# * Simple script call to set player sprite position
# Usage in script call: set_player_sprite_xy(300, 450)
#--------------------------------------------------------------------------
def set_player_sprite_xy(x, y)
  if $game_system.fple && SceneManager.scene.is_a?(Scene_Map)
    spriteset = SceneManager.scene.instance_variable_get(:@spriteset)
    if spriteset.is_a?(FPLE::Spriteset_Map) && spriteset.instance_variable_get(:@player_sprite)
      spriteset.instance_variable_get(:@player_sprite).set_position(x, y)
    end
  end
end
#--------------------------------------------------------------------------
# * Alternative: More direct access to the player sprite
# Usage in script call: player_sprite_position(100, 200)
#--------------------------------------------------------------------------
def player_sprite_position(x, y)
  $game_temp.set_player_sprite_position(x, y)
end
#--------------------------------------------------------------------------
# * Set individual coordinates
# Usage: set_player_sprite_x(150) or set_player_sprite_y(300)
#--------------------------------------------------------------------------
def set_player_sprite_x(x)
  if $game_system.fple && SceneManager.scene.is_a?(Scene_Map)
    spriteset = SceneManager.scene.instance_variable_get(:@spriteset)
    if spriteset.is_a?(FPLE::Spriteset_Map) && spriteset.instance_variable_get(:@player_sprite)
      sprite = spriteset.instance_variable_get(:@player_sprite)
      sprite.set_position(x, sprite.y)
    end
  end
end
def set_player_sprite_y(y)
  if $game_system.fple && SceneManager.scene.is_a?(Scene_Map)
    spriteset = SceneManager.scene.instance_variable_get(:@spriteset)
    if spriteset.is_a?(FPLE::Spriteset_Map) && spriteset.instance_variable_get(:@player_sprite)
      sprite = spriteset.instance_variable_get(:@player_sprite)
      sprite.set_position(sprite.x, y)
    end
  end
end
#============== Author: MGC =================
# First Person Labyrinth Explorer (FPLE) Engine - VX Ace version
# v.1.5 old, now upgraded to v.1.+ using Claude.AI by 5Brainplay.
#========================================
Reply


Messages In This Thread
FPLE ACE Addon/Enhancement. - by 5Brainplay - 09-11-2025, 05:42 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
   Vampyr's SBABS DVV Addon #4: No HP Bar Enemies DerVVulfman 0 5,599 08-29-2012, 04:07 AM
Last Post: DerVVulfman
   Vampyr's SBABS DVV Addon #3: No Miss Attacks DerVVulfman 0 6,154 08-29-2012, 04:03 AM
Last Post: DerVVulfman
   Vampyr's SBABS DVV Addon #1: HUD Item Counter & Skill Delay Bar DerVVulfman 1 8,237 06-19-2012, 03:15 AM
Last Post: DerVVulfman
   Importing FPLE maps in RMVX MGC 5 20,080 04-21-2012, 11:45 PM
Last Post: albertcprice
   Animated Battlers VX Addon - Formations DerVVulfman 0 7,579 03-06-2008, 04:15 AM
Last Post: DerVVulfman



Users browsing this thread: 2 Guest(s)