#===============================================================================
# * Box Ranch System - Optimized Version
#===============================================================================
# Configuration is loaded automatically from config.rb
# Constants
module BoxRanchConstants
  RANCH_EVENT_ID_START = 1000  # Starting ID for ranch events
  DEFAULT_SPRITE = "Graphics/Characters/Followers/000"
end
# Helper function to get the correct event name based on configuration
def get_ranch_event_name(species, in_water = false)
  if BoxRanchConfig::USE_REFLECTION_NAMES
    prefix = in_water ? BoxRanchConfig::WATER_EVENT_PREFIX_REFLECTION : BoxRanchConfig::LAND_EVENT_PREFIX_REFLECTION
  else
    prefix = in_water ? BoxRanchConfig::WATER_EVENT_PREFIX : BoxRanchConfig::LAND_EVENT_PREFIX
  end
  return "#{prefix}_#{species}"
end
# Helper function to load the correct Pokémon graphic
def box_ranch_sprite_filename(species, form = 0, gender = 0, shiny = false, in_water = false, levitates = false)
  folder_extra = if in_water
    shiny ? "Swimming Shiny" : "Swimming"
  elsif levitates
    shiny ? "Levitates Shiny" : "Levitates"
  else
    shiny ? "Followers shiny" : "Followers"
  end
 
  # Try to get the graphic file
  fname = nil
  begin
    fname = GameData::Species.check_graphic_file("Graphics/Characters/", species, form, gender, shiny, false, folder_extra)
  rescue
    # Ignore and use fallback
  end
 
  # Fallback chain
  if nil_or_empty?(fname)
    species_name = species.to_s
    fname = "Graphics/Characters/#{folder_extra}/#{species_name}"
   
    if !pbResolveBitmap(fname)
      # Try standard Followers folder
      fname = "Graphics/Characters/Followers/#{species_name}"
      fname = BoxRanchConstants::DEFAULT_SPRITE if !pbResolveBitmap(fname)
    end
  end
 
  return fname
end
# Helper function to check if a Pokémon is a Water type
def is_water_pokemon?(pokemon)
  return false if !pokemon
  return pokemon.types.include?(:WATER)
end
# Helper function to check if a Pokémon can levitate
def is_levitating_pokemon?(pokemon)
  return false if !pokemon
 
  levitating_species = [
    :GASTLY, :HAUNTER, :GENGAR, :KOFFING, :WEEZING, :PORYGON,
    :MISDREAVUS, :UNOWN, :NATU, :XATU, :ESPEON, :MURKROW, :WOBBUFFET,
    :GIRAFARIG, :PINECO, :DUNSPARCE, :GLIGAR, :LUGIA, :CELEBI,
    :DUSTOX, :SHEDINJA, :NINJASK, :WHISMUR, :LOUDRED, :EXPLOUD,
    :VOLBEAT, :ILLUMISE, :FLYGON, :BALTOY, :CLAYDOL, :LUNATONE, :SOLROCK,
    :CASTFORM, :SHUPPET, :BANETTE, :DUSKULL, :CHIMECHO, :GLALIE, :DEOXYS,
    :BRONZOR, :BRONZONG, :DRIFLOON, :DRIFBLIM, :CHINGLING,
    :SPIRITOMB, :CARNIVINE, :ROTOM, :UXIE, :MESPRIT, :AZELF,
    :GIRATINA, :CRESSELIA, :DARKRAI,
    :YAMASK, :SIGILYPH, :SOLOSIS, :DUOSION, :REUNICLUS, :VANILLITE,
    :VANILLISH, :VANILLUXE, :EMOLGA, :TYNAMO, :EELEKTRIK, :EELEKTROSS,
    :CRYOGONAL, :HYDREIGON, :VOLCARONA,
    :VIKAVOLT, :CUTIEFLY, :RIBOMBEE, :COMFEY, :DHELMISE, :LUNALA,
    :NIHILEGO, :CELESTEELA, :KARTANA, :XURKITREE, :PHEROMOSA
  ]
 
  levitating_abilities = [:LEVITATE, :AIRLOCK, :MAGNETRISE, :TELEPATHY]
 
  return levitating_species.include?(pokemon.species) ||
         levitating_abilities.include?(pokemon.ability.id)
end
# Helper function to check if a tile is water
def is_water_tile?(x, y)
  return false if !$game_map
  terrain_tag = $game_map.terrain_tag(x, y)
  return [5, 6, 7].include?(terrain_tag)
end
# Helper function to find water tiles on the map
def find_water_tiles
  return [] if !$game_map
 
  water_tiles = []
  $game_map.width.times do |x|
    $game_map.height.times do |y|
      water_tiles.push([x, y]) if is_water_tile?(x, y)
    end
  end
 
  return water_tiles
end
# Helper function to find land tiles on the map
def find_land_tiles
  return [] if !$game_map
 
  land_tiles = []
  $game_map.width.times do |x|
    $game_map.height.times do |y|
      land_tiles.push([x, y]) if !is_water_tile?(x, y) && $game_map.passable?(x, y, 0)
    end
  end
 
  return land_tiles
end
# Helper function to play the Pokémon's cry
def play_pokemon_cry(pokemon, volume = 90)
  return if !pokemon
 
  if pokemon.is_a?(Pokemon)
    GameData::Species.play_cry_from_pokemon(pokemon, volume) if !pokemon.egg?
  else
    GameData::Species.play_cry_from_species(pokemon, 0, volume)
  end
end
class BoxRanch
  attr_reader :pokemon_events
  attr_accessor :pending_swap_data  # For deferred swap completion
  def initialize
    @pokemon_events = {}
    @pokemon_locations = {}
    @map_id = BoxRanchConfig::MAP_ID
    @water_tiles = []
    @land_tiles = []
    @is_setting_up = false
    @pending_swap_data = nil
  end
  def setup
    return if @is_setting_up || !$game_map
    return if $game_map.map_id != @map_id
    return unless $scene.is_a?(Scene_Map) || $scene.is_a?(Scene_DebugIntro)
   
    @is_setting_up = true
    echoln("BoxRanch: Starting setup on map #{@map_id}")
   
    begin
      setup_ranch_pokemon
    rescue => e
      echoln("BoxRanch Error during setup: #{e.message}")
      echoln(e.backtrace.join("\n"))
    ensure
      @is_setting_up = false
    end
  end
  def update
    # Check if we have a pending swap to complete
    complete_pending_swap if @pending_swap_data
  end
  private
  def setup_ranch_pokemon
    block_autosave(true)
   
    clear_ranch_pokemon
    scan_map_tiles
    load_pokemon_from_boxes
    create_all_events
    refresh_following_pokemon
   
    echoln("BoxRanch: Setup complete! Total events: #{@pokemon_events.size}")
   
    block_autosave(false)
  end
  def block_autosave(state)
    return unless $game_temp && $game_temp.respond_to?(:no_autosave=)
    $game_temp.no_autosave = state
  end
  def scan_map_tiles
    @water_tiles = find_water_tiles
    @land_tiles = find_land_tiles
    echoln("BoxRanch: Found #{@water_tiles.length} water tiles and #{@land_tiles.length} land tiles")
  end
  def load_pokemon_from_boxes
    @pokemon_locations.clear
    pokemon_list = []
   
    $PokemonStorage.maxBoxes.times do |i|
      $PokemonStorage.maxPokemon(i).times do |j|
        pkmn = $PokemonStorage[i, j]
        next unless pkmn
       
        pokemon_list.push(pkmn)
        @pokemon_locations[pkmn] = [i, j]
      end
    end
   
    echoln("BoxRanch: Found #{pokemon_list.length} Pokemon in boxes")
   
    # Create test Pokemon if needed
    pokemon_list = create_test_pokemon if pokemon_list.empty? && BoxRanchConfig::CREATE_TEST_POKEMON
   
    categorize_pokemon(pokemon_list)
  end
  def create_test_pokemon
    test_list = []
    test_list.push(Pokemon.new(BoxRanchConfig::TEST_LAND_SPECIES, BoxRanchConfig::TEST_LEVEL))
   
    if !@water_tiles.empty?
      test_list.push(Pokemon.new(BoxRanchConfig::TEST_WATER_SPECIES, BoxRanchConfig::TEST_LEVEL))
    end
   
    echoln("BoxRanch: Created #{test_list.length} test Pokemon")
    return test_list
  end
  def categorize_pokemon(pokemon_list)
    @water_pokemon = []
    @land_pokemon = []
   
    pokemon_list.each do |pkmn|
      if is_water_pokemon?(pkmn) && !@water_tiles.empty?
        @water_pokemon.push(pkmn)
      else
        @land_pokemon.push(pkmn)
      end
    end
   
    echoln("BoxRanch: #{@land_pokemon.length} land, #{@water_pokemon.length} water Pokemon")
  end
  def create_all_events
    max_land = [@land_pokemon.size, BoxRanchConfig::MAX_LAND_POKEMON].min
    @land_pokemon[0...max_land].each_with_index { |pkmn, i| create_pokemon_event(pkmn, i, false) }
   
    max_water = [@water_pokemon.size, BoxRanchConfig::MAX_WATER_POKEMON].min
    @water_pokemon[0...max_water].each_with_index { |pkmn, i| create_pokemon_event(pkmn, i, true) }
  end
  def create_pokemon_event(pkmn, index, in_water)
    x, y = get_spawn_position(index, in_water)
    event = build_event(pkmn, x, y, in_water)
    game_event = spawn_event(event, x, y)
   
    @pokemon_events[event.id] = pkmn
    echoln("BoxRanch: Created event #{event.id} for #{pkmn.name} at (#{x}, #{y})")
  end
  def get_spawn_position(index, in_water)
    tiles = in_water ? @water_tiles : @land_tiles
   
    if !tiles.empty?
      pos = tiles.sample
      tiles.delete(pos)
      return pos
    end
   
    # Fallback to grid positioning
    return calculate_grid_position(index)
  end
  def calculate_grid_position(index)
    area = { x_start: 30, y_start: 30, width: 15, height: 15, columns: 3, rows: 4 }
   
    column = index % area[:columns]
    row = (index / area[:columns]) % area[:rows]
   
    cell_width = area[:width] / area[:columns]
    cell_height = area[:height] / area[:rows]
   
    x = area[:x_start] + (column * cell_width) + rand(cell_width / 2)
    y = area[:y_start] + (row * cell_height) + rand(cell_height / 2)
   
    return [x, y]
  end
  def build_event(pkmn, x, y, in_water)
    event = RPG::Event.new(x, y)
    event.id = generate_event_id
    event.name = get_ranch_event_name(pkmn.species, in_water)
   
    setup_event_graphic(event, pkmn, in_water)
    setup_event_movement(event, pkmn, in_water)
    setup_event_commands(event, pkmn, in_water)
   
    return event
  end
  def generate_event_id
    existing_ids = $game_map.events.keys
    return existing_ids.empty? ? BoxRanchConstants::RANCH_EVENT_ID_START : [existing_ids.max + 1, BoxRanchConstants::RANCH_EVENT_ID_START].max
  end
  def setup_event_graphic(event, pkmn, in_water)
    levitates = is_levitating_pokemon?(pkmn)
    sprite_path = box_ranch_sprite_filename(pkmn.species, pkmn.form || 0, pkmn.gender, pkmn.shiny?, in_water, levitates)
   
    event.pages[0].graphic.character_name = sprite_path.gsub("Graphics/Characters/", "")
    event.pages[0].graphic.character_hue = 0
    event.pages[0].graphic.direction = 2
  end
  def setup_event_movement(event, pkmn, in_water)
    event.pages[0].through = false
    event.pages[0].always_on_top = false
    event.pages[0].step_anime = true
    event.pages[0].trigger = 0
    event.pages[0].move_type = 1
   
    # Nature-based movement
    nature_value = pkmn.nature.id.to_s.hash.abs
   
    if in_water
      speed_range = BoxRanchConfig::WATER_SPEED_MAX - BoxRanchConfig::WATER_SPEED_MIN + 1
      event.pages[0].move_speed = BoxRanchConfig::WATER_SPEED_MIN + (nature_value % speed_range)
    else
      speed_range = BoxRanchConfig::LAND_SPEED_MAX - BoxRanchConfig::LAND_SPEED_MIN + 1
      event.pages[0].move_speed = BoxRanchConfig::LAND_SPEED_MIN + (nature_value % speed_range)
    end
   
    freq_range = BoxRanchConfig::FREQUENCY_MAX - BoxRanchConfig::FREQUENCY_MIN + 1
    event.pages[0].move_frequency = BoxRanchConfig::FREQUENCY_MIN + (nature_value % freq_range)
   
    event.pages[0].move_route = RPG::MoveRoute.new
    event.pages[0].move_route.repeat = true
    event.pages[0].move_route.skippable = false
    event.pages[0].move_route.list = []
  end
  def setup_event_commands(event, pkmn, in_water)
    event.pages[0].list = []
   
    Compiler::push_script(event.pages[0].list, "play_pokemon_cry(:#{pkmn.species}, 100)")
    Compiler::push_script(event.pages[0].list, "pbMessage(\"#{pkmn.name} looks at you friendly!\")")
   
    if pkmn.shiny?
      Compiler::push_script(event.pages[0].list, "pbMessage(\"#{pkmn.name} shines conspicuously in the sunlight.\")")
    end
   
    nature_text = get_nature_text(pkmn.nature)
    Compiler::push_script(event.pages[0].list, "pbMessage(\"#{nature_text}\")")
   
    if in_water
      Compiler::push_script(event.pages[0].list, "pbMessage(\"It swims happily in the water!\")")
    elsif is_levitating_pokemon?(pkmn)
      Compiler::push_script(event.pages[0].list, "pbMessage(\"It floats elegantly in the air!\")")
    end
   
    Compiler::push_script(event.pages[0].list, "pbMessage(\"Level: #{pkmn.level}\\nAbility: #{pkmn.ability.name}\")")
    Compiler::push_script(event.pages[0].list, "show_pokemon_interaction_menu(:#{pkmn.species}, #{pkmn.level}, #{event.id})")
   
    Compiler::push_end(event.pages[0].list)
  end
  def spawn_event(event, x, y)
    game_event = Game_Event.new($game_map.map_id, event)
    game_event.moveto(x, y)
    game_event.refresh
    $game_map.events[event.id] = game_event
    return game_event
  end
  def get_nature_text(nature)
    nature_texts = {
      :JOLLY => "It dances around cheerfully.",
      :NAIVE => "It is very playful.",
      :HASTY => "It can't stand still and is constantly running around.",
      :CALM => "It rests peacefully.",
      :CAREFUL => "It attentively observes its surroundings.",
      :QUIET => "It enjoys the tranquility of the ranch.",
      :BRAVE => "It bravely shows itself off.",
      :ADAMANT => "It trains its muscles.",
      :NAUGHTY => "It seems to be up to something."
    }
   
    return nature_texts[nature.id] || "It feels very comfortable on the ranch."
  end
  public
  def clear_ranch_pokemon
    echoln("BoxRanch: Clearing #{@pokemon_events.size} ranch events")
   
    # Remove tracked events
    @pokemon_events.keys.each do |event_id|
      $game_map.events.delete(event_id) if $game_map.events[event_id]
    end
   
    @pokemon_events.clear
    @pokemon_locations.clear
    refresh_following_pokemon
  end
  def remove_pokemon_event(event_id)
    block_autosave(true)
   
    echoln("BoxRanch: Removing event #{event_id}")
   
    # Remove the event from the map
    $game_map.events.delete(event_id)
    @pokemon_events.delete(event_id)
   
    # SOLUTION ULTIME: Map transfer pour force un refresh complet
    force_map_reload
   
    block_autosave(false)
  end
  def force_map_reload
    echoln("BoxRanch: Forcing complete map reload to eliminate ghost sprites")
   
    # Sauvegarder la position actuelle
    current_map_id = $game_map.map_id
    current_x = $game_player.x
    current_y = $game_player.y
    current_direction = $game_player.direction
   
    echoln("BoxRanch: Player position: Map #{current_map_id}, (#{current_x}, #{current_y}), Dir #{current_direction}")
   
    # Sauvegarder l'état du Following Pokemon
    follower_toggled = $PokemonGlobal.follower_toggled if $PokemonGlobal
   
    # Préparer le transfert vers la même position
    $game_temp.player_transferring = true
    $game_temp.player_new_map_id = current_map_id
    $game_temp.player_new_x = current_x
    $game_temp.player_new_y = current_y
    $game_temp.player_new_direction = current_direction
   
    # Effectuer le transfert immédiatement
    if $scene.is_a?(Scene_Map)
      $scene.transfer_player
     
      # Petit délai pour que le transfert soit complété
      Graphics.update
      Input.update
      pbUpdateSceneMap
    end
   
    # Restaurer l'état du Following Pokemon
    if $PokemonGlobal && follower_toggled != nil
      $PokemonGlobal.follower_toggled = follower_toggled
    end
   
    # Rafraîchir le Following Pokemon
    if defined?(FollowingPkmn)
      FollowingPkmn.refresh(false)
    end
   
    echoln("BoxRanch: Map reload complete")
    pbWait(0.05)
  end
  def refresh_following_pokemon
    return unless defined?(FollowingPkmn)
    FollowingPkmn.refresh(false)
  end
  # Public method for creating event at specific position (used by swap)
  def create_pokemon_event_at(pkmn, x, y, in_water)
    event = build_event(pkmn, x, y, in_water)
    game_event = spawn_event(event, x, y)
    @pokemon_events[event.id] = pkmn
    echoln("BoxRanch: Created event #{event.id} for #{pkmn.name} at (#{x}, #{y})")
  end
  def get_pokemon_location(pokemon)
    @pokemon_locations[pokemon]
  end
  def set_pokemon_location(pokemon, box_index, slot_index)
    @pokemon_locations[pokemon] = [box_index, slot_index]
  end
  def remove_pokemon_location(pokemon)
    @pokemon_locations.delete(pokemon)
  end
 
  # Store swap data to be completed after event finishes
  def prepare_swap(party_pokemon, x, y, in_water)
    @pending_swap_data = {
      pokemon: party_pokemon,
      x: x,
      y: y,
      in_water: in_water
    }
    echoln("BoxRanch: Swap prepared for #{party_pokemon.name}, will complete after event")
  end
 
  def complete_pending_swap
    return unless @pending_swap_data
    return if $game_system.map_interpreter.running?  # Wait until event is done
   
    echoln("BoxRanch: Completing pending swap")
    data = @pending_swap_data
    @pending_swap_data = nil
   
    # Create the new event
    create_pokemon_event_at(data[:pokemon], data[:x], data[:y], data[:in_water])
   
    # Force a complete refresh
    force_map_reload
  end
end
#===============================================================================
# Interaction Functions
#===============================================================================
def show_pokemon_interaction_menu(species, level, event_id = nil)
  return unless event_id && $box_ranch && $box_ranch.pokemon_events[event_id]
 
  commands = [_INTL("Pet"), _INTL("Feed"), _INTL("Play")]
 
  if $player && $player.party
    party_size = $player.party.length
    if party_size < Settings::MAX_PARTY_SIZE
      commands.push(_INTL("Take to team"))
      commands.push(_INTL("Swap with team")) if party_size > 0
    else
      commands.push(_INTL("Swap with team"))
    end
  end
 
  commands.push(_INTL("Back"))
 
  choice = pbMessage(_INTL("What would you like to do?"), commands, commands.length)
 
  case choice
  when 0  # Pet
    pbMessage(_INTL("You gently pet the Pokémon. It seems to enjoy that!"))
    play_pokemon_cry(species, 70)
  when 1  # Feed
    pbMessage(_INTL("You give the Pokémon some food. It eats happily!"))
  when 2  # Play
    pbMessage(_INTL("You play with the Pokémon for a while. It has fun!"))
    play_pokemon_cry(species, 100)
  else
    action = commands[choice]
    take_to_party(event_id) if action == _INTL("Take to team")
    swap_with_party_pokemon(event_id) if action == _INTL("Swap with team")
  end
end
def take_to_party(event_id)
  return unless $box_ranch && $box_ranch.pokemon_events[event_id] && $player
 
  ranch_pokemon = $box_ranch.pokemon_events[event_id]
 
  if $player.party.length >= Settings::MAX_PARTY_SIZE
    pbMessage(_INTL("Your team is full. You can't take more Pokémon."))
    return
  end
 
  msg = _INTL("Do you want to take {1} (Lv. {2}) to your team?", ranch_pokemon.name, ranch_pokemon.level)
  return unless pbConfirmMessage(msg)
 
  # Add to party
  $player.party.push(ranch_pokemon)
 
  # Remove from box if it came from there
  ranch_box_location = $box_ranch.get_pokemon_location(ranch_pokemon)
  if ranch_box_location
    box_index, slot_index = ranch_box_location
    $PokemonStorage[box_index, slot_index] = nil
    $box_ranch.remove_pokemon_location(ranch_pokemon)
  end
 
  # Remove event (uses force_map_reload internally - this works fine)
  $box_ranch.remove_pokemon_event(event_id)
 
  pbMessage(_INTL("{1} joined your team!", ranch_pokemon.name))
end
def swap_with_party_pokemon(event_id)
  return unless $box_ranch && $box_ranch.pokemon_events[event_id] && $player
  return if !$player.party || $player.party.empty?
 
  ranch_pokemon = $box_ranch.pokemon_events[event_id]
 
  pbMessage(_INTL("Choose a Pokémon from your team to swap with."))
 
  pbChoosePokemon(1, 2)
  chosen_index = $game_variables[1]
 
  if chosen_index < 0 || chosen_index >= $player.party.length
    pbMessage(_INTL("Swap cancelled."))
    return
  end
 
  party_pokemon = $player.party[chosen_index]
  msg = _INTL("Do you want to swap {1} (Lv. {2}) with {3} (Lv. {4})?",
    ranch_pokemon.name, ranch_pokemon.level, party_pokemon.name, party_pokemon.level)
 
  return unless pbConfirmMessage(msg)
 
  # Save event position
  event = $game_map.events[event_id]
  x, y = event.x, event.y
  in_water = event.name.include?("InWater")
 
  # Get box location
  ranch_box_location = $box_ranch.get_pokemon_location(ranch_pokemon)
 
  # Perform swap in data
  $player.party[chosen_index] = ranch_pokemon
 
  if ranch_box_location
    box_index, slot_index = ranch_box_location
    $PokemonStorage[box_index, slot_index] = party_pokemon
    $box_ranch.set_pokemon_location(party_pokemon, box_index, slot_index)
    $box_ranch.remove_pokemon_location(ranch_pokemon)
  end
 
  # Remove the old event (without map reload)
  $game_map.events.delete(event_id)
  $box_ranch.pokemon_events.delete(event_id)
 
  # Show success message FIRST
  pbMessage(_INTL("Swap successful! {1} is now in your team, and {2} is on the Ranch.",
    ranch_pokemon.name, party_pokemon.name))
 
  # AFTER the message, prepare the swap to be completed
  $box_ranch.prepare_swap(party_pokemon, x, y, in_water)
end
#===============================================================================
# Event Handlers
#===============================================================================
# Initialize BoxRanch singleton
EventHandlers.add(:on_game_start, :init_box_ranch, proc {
  $box_ranch = BoxRanch.new
})
EventHandlers.add(:on_game_load, :reload_box_ranch, proc {
  $box_ranch = BoxRanch.new
 
  if $game_map && $game_map.map_id == BoxRanchConfig::MAP_ID
    $game_map.need_refresh = true
    pbWait(0.05)
    $box_ranch.setup
  end
})
EventHandlers.add(:on_map_change, :setup_box_ranch, proc { |_old_map_id|
  $box_ranch ||= BoxRanch.new
  $box_ranch.setup
})
EventHandlers.add(:on_box_change, :sync_box_ranch, proc { |_old_box|
  if $box_ranch && $game_map && $game_map.map_id == BoxRanchConfig::MAP_ID
    $box_ranch.setup
  end
})
#===============================================================================
# Game_Map integration
#===============================================================================
class Game_Map
  alias box_ranch_setup setup
  def setup(map_id)
    box_ranch_setup(map_id)
    $box_ranch ||= BoxRanch.new
    $box_ranch.setup if map_id == BoxRanchConfig::MAP_ID
  end
end
#===============================================================================
# Scene_Map integration
#===============================================================================
class Scene_Map
  alias box_ranch_update update
  def update
    box_ranch_update
    $box_ranch.update if $box_ranch
  end
end