#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
# Continuous Maps by Blizzard
# Version: 1.4
# Type: Custom Map System
# Date: 14.12.2009
# Date v1.2: 11.9.2010
# Date v1.3: 17.11.2012
# Date v1.4: 8.1.2013
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#   
#  This work is protected by the following license:
# #----------------------------------------------------------------------------
# #  
# #  Creative Commons - Attribution-NonCommercial-ShareAlike 3.0 Unported
# #  ( http://creativecommons.org/licenses/by-nc-sa/3.0/ )
# #  
# #  You are free:
# #  
# #  to Share - to copy, distribute and transmit the work
# #  to Remix - to adapt the work
# #  
# #  Under the following conditions:
# #  
# #  Attribution. You must attribute the work in the manner specified by the
# #  author or licensor (but not in any way that suggests that they endorse you
# #  or your use of the work).
# #  
# #  Noncommercial. You may not use this work for commercial purposes.
# #  
# #  Share alike. If you alter, transform, or build upon this work, you may
# #  distribute the resulting work only under the same or similar license to
# #  this one.
# #  
# #  - For any reuse or distribution, you must make clear to others the license
# #    terms of this work. The best way to do this is with a link to this web
# #    page.
# #  
# #  - Any of the above conditions can be waived if you get permission from the
# #    copyright holder.
# #  
# #  - Nothing in this license impairs or restricts the author's moral rights.
# #  
# #----------------------------------------------------------------------------
# 
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
# 
# Compatibility:
# 
#   95% compatible with SDK v1.x. 80% compatible with SDK 2.x. WILL corrupt old
#   savegames. Compatible with Blizz-ABS. Compatible with Tons of Add-ons'
#   catepillar. Not compatible with Intelligent Passability from Blizz-ABS,
#   it's being turned off. Not compatible with Blizz-ABS's caterpillar.
# 
# 
# Features:
# 
#   - virtually creates one continuous map from any number of map fragments
#   - easy to configure
# 
# new in v1.2:
#   
#   - fixed bug where transferring into a non-configured map would crash the
#     game
#   - fixed bug when teleporting into another set of maps would mess up
#     everything
# 
# new in v1.3:
#   
#   - added compatibility with Tons of Add-ons' Catepillar
# 
# new in v1.4:
#   
#   - fixed crash caused by compatibility fix with Tons of Add-ons' Catepillar
#   - added code for proper fog handling
# 
# 
# Instructions:
# 
# - Explanation:
# 
#   This Script will allow you to make the game simulate a continuous map using
#   smaller maps. The system displays the current map and the neighbor maps.
#   Walking over a map border will cause the new map to be loaded as main map
#   and the neighbor maps will be changed.
# 
# 
# Notes:
# 
#   - You may notice lag when walking over a map border.
#   - Do not use too big maps as it increases loading time and the short moment
#     of lag will become more and more visible. Due to the fact that map
#     loading times in Blizz-ABS are increased, those moments will be longer if
#     Blizz-ABS is being used.
#   - Remember to connect ALL neighbor maps or you might experience bugs.
#   - Does not work with Blizz-ABS caterpillar because switching the party
#     leader would technically have to change the map and that causes a lot of
#     problems, including several party members being in several different maps
#     at the same time which might be disabled/removed from the current
#     display.
# 
# 
# If you find any bugs, please report them here:
# http://forum.chaos-project.com
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=

module CTMAPS
  
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# START Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  # define all neighbor maps here
  MAP_DATA = [
    # [MAP_ID1, MAP_ID2, X_OFFSET, Y_OFFSET]
    [1, 2, 25, 0], # connected map 1 and 2 where 2 has an X, Y offset of 25, 0
    [3, 1, 0, -15], # connected map 3 and 1 where 1 has an X, Y offset of 0, -15
    [3, 2, 25, -15],
    [3, 4, 0, 15],
    [3, 5, 25, 15],
    [4, 5, 25, 0],
    [6, 7, 25, 0],
    [8, 6, 0, -15],
    [8, 7, 25, -15],
    [8, 9, 0, 15],
    [8, 10, 25, 15],
    [9, 10, 25, 0],
  ]
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# END Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  MAP_DATA.compact!
  MAPS = {}
  MAP_DATA.each {|data|
      MAPS[data[0]] = [] if MAPS[data[0]] == nil
      MAPS[data[0]] |= [data[1, 3]]
      MAPS[data[1]] = [] if MAPS[data[1]] == nil
      MAPS[data[1]] |= [[data[0], -data[2], -data[3]]]}
  MAP_DATA = nil
  $ctmaps = 1.4
  
end


#==============================================================================
# Game_Temp
#==============================================================================

class Game_Temp
  
  attr_accessor :player_no_straighten
  attr_accessor :player_caterpillar_fix
  
end
  
#==============================================================================
# Game_Character
#==============================================================================

class Game_Character
  
  attr_accessor :x
  attr_accessor :y
  attr_accessor :real_x
  attr_accessor :real_y
  
  alias straighten_ctmaps_later straighten
  def straighten
    if $game_temp.player_no_straighten
      $game_temp.player_no_straighten = nil
      return
    end
    straighten_ctmaps_later
  end
  
  alias moveto_ctmaps_later moveto
  def moveto(x, y)
    ox, oy = $game_map.upper_left
    moveto_ctmaps_later(x - ox, y - oy)
  end
  
end

if $BlizzABS
#==============================================================================
# module BlizzABS
#==============================================================================

module BlizzABS
  
  #============================================================================
  # BlizzABS::Controller
  #============================================================================

  class Controller
    
    alias center_ctmaps_later center
    def center(x, y, flag = false)
      ox, oy = $game_map.upper_left
      center_ctmaps_later(x - ox, y - oy, flag)
    end
    
  end
  
  #============================================================================
  # BlizzABS::Utility
  #============================================================================

  class Utility
    
    alias setup_passability_ctmaps_later setup_passability
    def setup_passability(map, override = false)
      setup_passability_ctmaps_later(map) if override
    end
    
  end
  
end

$BlizzABS = BlizzABS::Processor.new

#==============================================================================
# Map_Battler
#==============================================================================

class Map_Battler
  
  attr_writer :id
  attr_writer :SELF
  
  alias moveto_ctmaps_later moveto
  def moveto(x, y)
    ox, oy = $game_map.upper_left
    moveto_ctmaps_later(x - ox, y - oy)
  end
  
end

else
#==============================================================================
# Game_Player
#==============================================================================

class Game_Player
  
  alias center_ctmaps_later center
  def center(x, y)
    ox, oy = $game_map.upper_left
    center_ctmaps_later(x - ox, y - oy)
  end
  
  alias moveto_ctmaps_later2 moveto
  def moveto(x, y)
    fix = $game_temp.player_caterpillar_fix
    $game_temp.player_caterpillar_fix = false
    if $tons_version == nil || $tons_version < 7.62 ||
        !$game_system.CATERPILLAR || !fix
      moveto_ctmaps_later2(x, y)
      return
    end
    px = self.x
    py = self.y
    moveto_caterpillar(x, y)
    @members.each {|member| member.moveto(x + member.x - px, y + member.y - py)}
  end
  
end

end

#==============================================================================
# Game_Map
#==============================================================================

class Game_Map
  
  attr_reader :upper_left
  attr_reader :lower_right
  attr_reader :maps
    
  alias init_ctmaps_later initialize
  def initialize
    @maps = {}
    @events = {}
    init_ctmaps_later
  end
  
  alias setup_ctmaps_later setup
  def setup(map_id)
    last_maps = @maps
    last_map_id = @map_id
    @upper_left = [0, 0]
    @lower_right = [0, 0]
    events = @events
    fog_ox = @fog_ox
    fog_oy = @fog_oy
    fog_tone = @fog_tone
    fog_tone_target = @fog_tone_target
    fog_tone_duration = @fog_tone_duration
    fog_opacity_duration = @fog_opacity_duration
    fog_opacity_target = @fog_opacity_target
    setup_ctmaps_later(map_id)
    @maps = get_neighbor_maps
    if !@maps.has_key?(last_map_id)
      @events = {}
      last_maps = {}
      last_map_id = 0
    else
      @events = events
      @fog_ox = fog_ox
      @fog_oy = fog_oy
      @fog_tone = fog_tone
      @fog_tone_target = fog_tone_target
      @fog_tone_duration = fog_tone_duration
      @fog_opacity_duration = fog_opacity_duration
      @fog_opacity_target = fog_opacity_target
    end
    setup_continuous_map
    setup_continuous_events(last_maps, last_map_id)
    if $BlizzABS
      @virtual_passability = $BlizzABS.util.setup_passability(@map, true)
    end
  end
  
  def get_neighbor_maps
    maps = {}
    maps[@map_id] = [@map, 0, 0]
    @upper_left = [0, 0]
    @lower_right = [@map.width, @map.height]
    if CTMAPS::MAPS.has_key?(@map_id)
      CTMAPS::MAPS[@map_id].each {|data|
          id, x, y = data
          if @maps[id] != nil
            map = @maps[id][0]
          else
            map = load_data(sprintf('Data/Map%03d.rxdata', id))
          end
          maps[id] = [map, x, y]
          @upper_left[0] = x if @upper_left[0] > x
          @upper_left[1] = y if @upper_left[1] > y
          width, height = x + map.width, y + map.height
          @lower_right[0] = width if @lower_right[0] < width
          @lower_right[1] = height if @lower_right[1] < height}
      maps.each_value {|data|
          data[1] -= @upper_left[0]
          data[2] -= @upper_left[1]}
    end
    return maps
  end
  
  def setup_continuous_map
    width = @lower_right[0] - @upper_left[0]
    height = @lower_right[1] - @upper_left[1]
    data = Table.new(width, height, 3)
    @map = @map.clone
    @map.events = {}
    @maps.each_key {|id|
        map, ox, oy = @maps[id]
        if id == @map_id
          map.events.each_key {|i| @map.events[i] = map.events[i]}
        else
          map.events.each_key {|i| @map.events[id * 1000 + i] = map.events[i]}
        end
        (0...map.width).each {|x| (0...map.height).each {|y| (0...3).each {|z|
            data[x + ox, y + oy, z] = map.data[x, y, z]}}}}
    @map.width = width
    @map.height = height
    @map.data = data
  end
  
  def setup_continuous_events(last_maps, last_map_id)
    if last_maps[@map_id] != nil
      correct_event_positions(last_maps)
      correct_fog_position(last_maps)
    end
    delete_removed_events(last_maps, last_map_id)
    shift_events_from_last_map(last_maps, last_map_id) if last_map_id != 0
    shift_events_for_new_map
    add_new_events(last_maps)
  end
  
  def delete_removed_events(last_maps, last_map_id)
    keys = []
    @maps.each_key {|id|
        keys += @events.keys.find_all {|key|
            @events[key].real_x < 0 || @events[key].real_y < 0 ||
            @events[key].real_x >= (@lower_right[0] - @upper_left[0]) * 128 ||
            @events[key].real_y >= (@lower_right[1] - @upper_left[1]) * 128}}
    (keys | keys).each {|key| @events.delete(key)}
  end
  
  def shift_events_from_last_map(last_maps, last_map_id)
    @events.each_key {|key|
        if key < 1000
          @events[last_map_id * 1000 + key] = @events[key]
          if $BlizzABS && @events[key].is_a?(Map_Battler)
            @events[key].SELF = @events[key].id = last_map_id * 1000 + key
          end
          @events.delete(key)
        end}
  end
  
  def shift_events_for_new_map
    @events.each_key {|key|
        if key >= @map_id * 1000 && key < (@map_id + 1) * 1000
          @events[key % 1000] = @events[key]
          if $BlizzABS && @events[key].is_a?(Map_Battler)
            @events[key].SELF = @events[key].id = key % 1000
          end
          @events.delete(key)
        end}
  end
  
  def correct_event_positions(last_maps)
    pix = $BlizzABS.pixel if $BlizzABS
    sx = @maps[@map_id][1] - last_maps[@map_id][1]
    sy = @maps[@map_id][2] - last_maps[@map_id][2]
    @events.each_value {|event|
        if $BlizzABS && event.is_a?(Map_Battler)
          x, y = sx * pix, sy * pix
        else
          x, y = sx, sy
        end
        event.x += x
        event.y += y
        event.real_x += sx * 128
        event.real_y += sy * 128}
  end
  
  def correct_fog_position(last_maps)
    @fog_ox -= (@maps[@map_id][1] - last_maps[@map_id][1]) * 32
    @fog_oy -= (@maps[@map_id][2] - last_maps[@map_id][2]) * 32
  end
  
  def add_new_events(last_maps = {})
    if last_maps[@map_id] == nil
      @maps[@map_id][0].events.each_key {|i|
          if @events[i] == nil
            @events[i] = Game_Event.new(@map_id, @maps[@map_id][0].events[i])
            $BlizzABS.check_event_name(i) if $BlizzABS
          end}
    end
    pix = $BlizzABS.pixel if $BlizzABS
    (@maps.keys - last_maps.keys - [@map_id]).each {|id|
        map, sx, sy = @maps[id]
        sx -= @maps[@map_id][1]
        sy -= @maps[@map_id][2]
        map.events.each_key {|i|
            if @events[id * 1000 + i] == nil
              @events[id * 1000 + i] = event = Game_Event.new(id, map.events[i])
              if $BlizzABS
                $BlizzABS.check_event_name(id * 1000 + i)
                if event.is_a?(Map_Battler)
                  x, y = sx * pix, sy * pix
                else
                  x, y = sx, sy
                end
              else
                x, y = sx, sy
              end
              event.x += x
              event.y += y
              event.real_x += sx * 128
              event.real_y += sy * 128
            end}}
  end
  
  alias update_ctmaps_later update
  def update
    update_ctmaps_later
    return if $game_player.moving? || @maps.size <= 1
    map, x, y = @maps[@map_id]
    if $game_player.real_x < x * 128 || $game_player.real_y < y * 128 ||
        $game_player.real_x >= x * 128 + map.width * 128 ||
        $game_player.real_y >= y * 128 + map.height * 128
      (@maps.keys - [@map_id]).each {|id|
          map, x, y = @maps[id]
          if $game_player.real_x >= x * 128 && $game_player.real_y >= y * 128 &&
              $game_player.real_x < x * 128 + map.width * 128 &&
              $game_player.real_y < y * 128 + map.height * 128
            $game_temp.player_transferring = true
            $game_temp.player_no_straighten = true
            $game_temp.player_caterpillar_fix = true
            $game_temp.player_new_x = $game_player.real_x / 128 - x
            $game_temp.player_new_y = $game_player.real_y / 128 - y
            $game_temp.player_new_map_id = id
            $game_temp.player_new_direction = 0
            break
          end}
    end
  end
  
end
