#===============================================================================
#
#===============================================================================
class CustomTilemapAutotiles
  attr_accessor :changed

  def initialize
    @changed = true
    @tiles   = [nil,nil,nil,nil,nil,nil,nil]
  end

  def [](i)
    return @tiles[i]
  end

  def []=(i,value)
    @tiles[i] = value
    @changed  = true
  end
end



#Console::setup_console
class CustomTilemapSprite < Sprite
end



#===============================================================================
#
#===============================================================================
class CustomTilemap
  attr_reader   :tileset
  attr_reader   :autotiles
  attr_reader   :map_data
  attr_reader   :flash_data
  attr_reader   :priorities
  attr_reader   :terrain_tags
  attr_reader   :visible
  attr_reader   :viewport
  attr_reader   :graphicsWidth
  attr_reader   :graphicsHeight
  attr_reader   :ox
  attr_reader   :oy
  attr_accessor :tone
  attr_accessor :color

  Autotiles = [
    [ [27, 28, 33, 34], [ 5, 28, 33, 34], [27,  6, 33, 34], [ 5,  6, 33, 34],
      [27, 28, 33, 12], [ 5, 28, 33, 12], [27,  6, 33, 12], [ 5,  6, 33, 12] ],
    [ [27, 28, 11, 34], [ 5, 28, 11, 34], [27,  6, 11, 34], [ 5,  6, 11, 34],
      [27, 28, 11, 12], [ 5, 28, 11, 12], [27,  6, 11, 12], [ 5,  6, 11, 12] ],
    [ [25, 26, 31, 32], [25,  6, 31, 32], [25, 26, 31, 12], [25,  6, 31, 12],
      [15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
    [ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
      [39, 40, 45, 46], [ 5, 40, 45, 46], [39,  6, 45, 46], [ 5,  6, 45, 46] ],
    [ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
      [17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
    [ [37, 38, 43, 44], [37,  6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
      [37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1,  2,  7,  8] ]
  ]
  Animated_Autotiles_Frames = 5*Graphics.frame_rate/20   # Frequency of updating animated autotiles
  FlashOpacity = [100,90,80,70,80,90]

  def initialize(viewport)
    @tileset             = nil    # Refers to Map Tileset Name
    @autotiles           = CustomTilemapAutotiles.new
    @map_data            = nil    # Refers to 3D Array Of Tile Settings
    @flash_data          = nil    # Refers to 3D Array of Tile Flashdata
    @priorities          = nil    # Refers to Tileset Priorities
    @terrain_tags        = nil    # Refers to Tileset Terrain Tags
    @visible             = true   # Refers to Tileset Visibleness
    @ox                  = 0      # Bitmap Offsets
    @oy                  = 0      # Bitmap Offsets
    @plane               = false
    @haveGraphicsWH      = (Graphics.width!=nil rescue false)
    if @haveGraphicsWH
      @graphicsWidth     = Graphics.width
      @graphicsHeight    = Graphics.height
    else
      @graphicsWidth     = 640
      @graphicsHeight    = 480
    end
    @tileWidth           = Game_Map::TILE_WIDTH rescue 32
    @tileHeight          = Game_Map::TILE_HEIGHT rescue 32
    @tileSrcWidth        = 32
    @tileSrcHeight       = 32
    @diffsizes           = (@tileWidth!=@tileSrcWidth) || (@tileHeight!=@tileSrcHeight)
    @tone                = Tone.new(0,0,0,0)
    @oldtone             = Tone.new(0,0,0,0)
    @color               = Color.new(0,0,0,0)
    @oldcolor            = Color.new(0,0,0,0)
    @selfviewport        = Viewport.new(0,0,graphicsWidth,graphicsHeight)
    @viewport            = (viewport) ? viewport : @selfviewport
    @tiles               = []
    @autotileInfo        = []
    @regularTileInfo     = []
    @oldOx               = 0
    @oldOy               = 0
    @oldViewportOx       = 0
    @oldViewportOy       = 0
    @layer0              = CustomTilemapSprite.new(viewport)
    @layer0.visible      = true
    @nowshown            = false
    @layer0.bitmap       = Bitmap.new([graphicsWidth+320,1].max,[graphicsHeight+320,1].max)
    @layer0.z            = 0
    @layer0.ox           = 0
    @layer0.oy           = 0
    @oxLayer0            = 0
    @oyLayer0            = 0
    @flash               = nil
    @oxFlash             = 0
    @oyFlash             = 0
    @priotiles           = {}
    @priotilesfast       = []
    @prioautotiles       = {}
    @autosprites         = []
    @framecount          = [0,0,0,0,0,0,0,0]   # For autotiles
    @tilesetChanged      = true
    @flashChanged        = false
    @firsttime           = true
    @disposed            = false
    @usedsprites         = false
    @layer0clip          = true
    @firsttimeflash      = true
    @fullyrefreshed      = false
    @fullyrefreshedautos = false
  end

  def dispose
    return if disposed?
    @help.dispose if @help
    @help = nil
    i = 0; len = @autotileInfo.length
    while i<len
      if @autotileInfo[i]
        @autotileInfo[i].dispose
        @autotileInfo[i] = nil
      end
      i += 1
    end
    i = 0; len = @regularTileInfo.length
    while i<len
      if @regularTileInfo[i]
        @regularTileInfo[i].dispose
        @regularTileInfo[i] = nil
      end
      i += 1
    end
    i = 0; len = @tiles.length
    while i<len
      @tiles[i].dispose
      @tiles[i] = nil
      i += 2
    end
    i = 0; len = @autosprites.length
    while i<len
      @autosprites[i].dispose
      @autosprites[i] = nil
      i += 2
    end
    if @layer0
      @layer0.bitmap.dispose if !@layer0.disposed?
      @layer0.bitmap = nil if !@layer0.disposed?
      @layer0.dispose
      @layer0 = nil
    end
    if @flash
      @flash.bitmap.dispose if !@flash.disposed?
      @flash.bitmap = nil if !@flash.disposed?
      @flash.dispose
      @flash = nil
    end
    for i in 0...7
      self.autotiles[i] = nil
    end
    @tiles.clear
    @autosprites.clear
    @autotileInfo.clear
    @regularTileInfo.clear
    @tilemap = nil
    @tileset = nil
    @priorities = nil
    @selfviewport.dispose
    @selfviewport = nil
    @disposed = true
  end

  def disposed?
    return @disposed
  end

  def flash_data=(value)
    @flash_data = value
    @flashChanged = true
  end

  def map_data=(value)
    @map_data = value
    @tilesetChanged = true
  end

  def priorities=(value)
    @priorities = value
    @tilesetChanged = true
  end

  def terrain_tags=(value)
    @terrain_tags = value
    @tilesetChanged = true
  end

  def tileset=(value)
    @tileset = value
    @tilesetChanged = true
  end

  def getResizeFactor
    return $ResizeFactor || 1.0
  end

  def ox=(val)
    rf = getResizeFactor
    if rf!=1.0
      val = (val*rf).to_i
      val = (val/rf).to_i
    end
    wasshown = self.shown?
    @ox = val.floor
    @nowshown = (!wasshown && self.shown?)
  end

  def oy=(val)
    rf = getResizeFactor
    if rf!=1.0
      val = (val*rf).to_i
      val = (val/rf).to_i
    end
    wasshown = self.shown?
    @oy = val.floor
    @nowshown = (!wasshown && self.shown?)
  end

  def visible=(val)
    wasshown = @visible
    @visible = val
    @nowshown = (!wasshown && val)
  end

  def shown?
    return false if !@visible
    xsize  = @map_data.xsize
    xStart = @ox/@tileWidth - 1
    xStart = 0 if xStart<0
    xStart = xsize-1 if xStart>=xsize
    xEnd   = (@ox+@viewport.rect.width)/@tileWidth + 1
    xEnd   = 0 if xEnd<0
    xEnd   = xsize-1 if xEnd>=xsize
    return false if xStart>=xEnd
    ysize  = @map_data.ysize
    yStart = @oy/@tileHeight - 1
    yStart = 0 if yStart<0
    yStart = ysize-1 if yStart>=ysize
    yEnd   = (@oy+@viewport.rect.height)/@tileHeight + 1
    yEnd   = 0 if yEnd<0
    yEnd   = ysize-1 if yEnd>=ysize
    return false if yStart>=yEnd
    return true
  end

  def autotileNumFrames(id)
    autotile = @autotiles[id/48-1]
    return 0 if !autotile || autotile.disposed?
    frames = 1
    if autotile.height==@tileHeight
      frames = autotile.width/@tileWidth
    else
      frames = autotile.width/(3*@tileWidth)
    end
    return frames
  end

  def autotileFrame(id)
    autotile = @autotiles[id/48-1]
    return -1 if !autotile || autotile.disposed?
    frames = 1
    if autotile.height==@tileHeight
      frames = autotile.width/@tileWidth
    else
      frames = autotile.width/(3*@tileWidth)
    end
    return (Graphics.frame_count/Animated_Autotiles_Frames)%frames
  end

  def repaintAutotiles
    for i in 0...@autotileInfo.length
      next if !@autotileInfo[i]
      frame = autotileFrame(i)
      @autotileInfo[i].clear
      bltAutotile(@autotileInfo[i],0,0,i,frame)
    end
  end

  def bltAutotile(bitmap,x,y,id,frame)
    return if frame<0
    autotile = @autotiles[id/48-1]
    return if !autotile || autotile.disposed?
    if autotile.height==@tileSrcHeight
      anim = frame*@tileSrcWidth
      src_rect = Rect.new(anim,0,@tileSrcWidth,@tileSrcHeight)
      if @diffsizes
        bitmap.stretch_blt(Rect.new(x,y,@tileWidth,@tileHeight),autotile,src_rect)
      else
        bitmap.blt(x,y,autotile,src_rect)
      end
    else
      anim = frame*3*@tileSrcWidth
      id %= 48
      tiles = Autotiles[id>>3][id&7]
      src = Rect.new(0,0,0,0)
      halfTileWidth     = @tileWidth>>1
      halfTileHeight    = @tileHeight>>1
      halfTileSrcWidth  = @tileSrcWidth>>1
      halfTileSrcHeight = @tileSrcHeight>>1
      for i in 0...4
        tile_position = tiles[i] - 1
        src.set( (tile_position % 6)*halfTileSrcWidth + anim,
           (tile_position / 6)*halfTileSrcHeight, halfTileSrcWidth, halfTileSrcHeight)
        if @diffsizes
          bitmap.stretch_blt(
             Rect.new(i%2*halfTileWidth+x,i/2*halfTileHeight+y,halfTileWidth,halfTileHeight),
             autotile,src)
        else
          bitmap.blt(i%2*halfTileWidth+x,i/2*halfTileHeight+y, autotile, src)
        end
      end
    end
  end

  def getAutotile(sprite,id)
    frames = @framecount[id/48-1]
    if frames<=1
      anim = 0
    else
      anim = (Graphics.frame_count/Animated_Autotiles_Frames)%frames
    end
    return if anim<0
    bitmap = @autotileInfo[id]
    if !bitmap
       bitmap = Bitmap.new(@tileWidth,@tileHeight)
       bltAutotile(bitmap,0,0,id,anim)
       @autotileInfo[id] = bitmap
    end
    sprite.bitmap = bitmap if sprite.bitmap!=bitmap
  end

  def getRegularTile(sprite,id)
    if @diffsizes
      bitmap = @regularTileInfo[id]
      if !bitmap
        bitmap = Bitmap.new(@tileWidth,@tileHeight)
        rect = Rect.new(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
           @tileSrcWidth,@tileSrcHeight)
        bitmap.stretch_blt(Rect.new(0,0,@tileWidth,@tileHeight),@tileset,rect)
        @regularTileInfo[id] = bitmap
      end
      sprite.bitmap = bitmap if sprite.bitmap!=bitmap
    else
      sprite.bitmap = @tileset if sprite.bitmap!=@tileset
      sprite.src_rect.set(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
         @tileSrcWidth,@tileSrcHeight)
    end
  end

  def addTile(tiles,count,xpos,ypos,id)
    terrain  = @terrain_tags[id]
    priority = @priorities[id]
    if id >= 384   # Tileset tile
      if count>=tiles.length
        sprite = CustomTilemapSprite.new(@viewport)
        tiles.push(sprite,0)
      else
        sprite = tiles[count]
        tiles[count+1] = 0
      end
      sprite.visible = @visible
      sprite.x       = xpos
      sprite.y       = ypos
      sprite.tone    = @tone
      sprite.color   = @color
      getRegularTile(sprite,id)
    else   # Autotile
      if count>=tiles.length
        sprite = CustomTilemapSprite.new(@viewport)
        tiles.push(sprite,1)
      else
        sprite = tiles[count]
        tiles[count+1] = 1
      end
      sprite.visible = @visible
      sprite.x       = xpos
      sprite.y       = ypos
      sprite.tone    = @tone
      sprite.color   = @color
      getAutotile(sprite,id)
    end
    if PBTerrain.hasReflections?(terrain)
      spriteZ = -100
    elsif $PokemonGlobal.bridge>0 && PBTerrain.isBridge?(terrain)
      spriteZ = 1
    else
      spriteZ = (priority==0) ? 0 : ypos+priority*32+32
    end
    sprite.z = spriteZ
    count += 2
    return count
  end

  def refresh_flash
    if @flash_data && !@flash
      @flash = CustomTilemapSprite.new(viewport)
      @flash.visible    = true
      @flash.z          = 1
      @flash.tone       = tone
      @flash.color      = color
      @flash.blend_type = 1
      @flash.bitmap     = Bitmap.new([graphicsWidth*2,1].max,[graphicsHeight*2,1].max)
      @firsttimeflash = true
    elsif !@flash_data && @flash
      @flash.bitmap.dispose if @flash.bitmap
      @flash.dispose
      @flash = nil
      @firsttimeflash = false
    end
  end

  def refreshFlashSprite
    return if !@flash || @flash_data.nil?
    ptX = @ox-@oxFlash
    ptY = @oy-@oyFlash
    if !@firsttimeflash && !@usedsprites &&
       ptX>=0 && ptX+@viewport.rect.width<=@flash.bitmap.width &&
       ptY>=0 && ptY+@viewport.rect.height<=@flash.bitmap.height
      @flash.ox = 0
      @flash.oy = 0
      @flash.src_rect.set(ptX.round,ptY.round,
         @viewport.rect.width,@viewport.rect.height)
      return
    end
    width = @flash.bitmap.width
    height = @flash.bitmap.height
    bitmap = @flash.bitmap
    ysize = @map_data.ysize
    xsize = @map_data.xsize
    @firsttimeflash = false
    @oxFlash = @ox-(width>>2)
    @oyFlash = @oy-(height>>2)
    @flash.ox = 0
    @flash.oy = 0
    @flash.src_rect.set(width>>2,height>>2,
       @viewport.rect.width,@viewport.rect.height)
    @flash.bitmap.clear
    @oxFlash = @oxFlash.floor
    @oyFlash = @oyFlash.floor
    xStart = @oxFlash/@tileWidth
    xStart = 0 if xStart<0
    yStart = @oyFlash/@tileHeight
    yStart = 0 if yStart<0
    xEnd = xStart+(width/@tileWidth)+1
    yEnd = yStart+(height/@tileHeight)+1
    xEnd = xsize if xEnd>=xsize
    yEnd = ysize if yEnd>=ysize
    if xStart<xEnd && yStart<yEnd
      yrange = yStart...yEnd
      xrange = xStart...xEnd
      tmpcolor = Color.new(0,0,0,0)
      for y in yrange
        ypos = (y*@tileHeight)-@oyFlash
        for x in xrange
          xpos = (x*@tileWidth)-@oxFlash
          id = @flash_data[x, y, 0]
          r = (id>>8)&15
          g = (id>>4)&15
          b = (id)&15
          tmpcolor.set(r<<4,g<<4,b<<4)
          bitmap.fill_rect(xpos,ypos,@tileWidth,@tileHeight,tmpcolor)
        end
      end
    end
  end

  def refresh_tileset
    i = 0
    len = @regularTileInfo.length
    while i < len
      if @regularTileInfo[i]
        @regularTileInfo[i].dispose
        @regularTileInfo[i] = nil
      end
      i += 1
    end
    @regularTileInfo.clear
    @priotiles.clear
    ysize = @map_data.ysize
    xsize = @map_data.xsize
    zsize = @map_data.zsize
    if xsize > 100 || ysize > 100
      @fullyrefreshed = false
    else
      for z in 0...zsize
        for y in 0...ysize
          for x in 0...xsize
            id = @map_data[x, y, z]
            next if id == 0
            next if @priorities[id] == 0 && !PBTerrain.hasReflections?(@terrain_tags[id])
            @priotiles[[x, y]] = [] if !@priotiles[[x, y]]
            @priotiles[[x, y]].push([z, id])
          end
        end
      end
      @fullyrefreshed = true
    end
  end

  def refresh_autotiles
    i = 0
    len = @autotileInfo.length
    while i < len
      if @autotileInfo[i]
        @autotileInfo[i].dispose
        @autotileInfo[i] = nil
      end
      i += 1
    end
    i = 0
    len = @autosprites.length
    while i < len
      if @autosprites[i]
        @autosprites[i].dispose
        @autosprites[i] = nil
      end
      i += 2
    end
    @autosprites.clear
    @autotileInfo.clear
    @prioautotiles.clear
    @priorect = nil
    @priorectautos = nil
    hasanimated = false
    for i in 0...7
      numframes = autotileNumFrames(48 * (i + 1))
      hasanimated = true if numframes >= 2
      @framecount[i] = numframes
    end
    if hasanimated
      ysize = @map_data.ysize
      xsize = @map_data.xsize
      zsize = @map_data.zsize
      if xsize > 100 || ysize > 100
        @fullyrefreshedautos = false
      else
        for y in 0...ysize
          for x in 0...xsize
            for z in 0...zsize
              id = @map_data[x, y, z]
              next if id == 0 || id >= 384   # Skip non-autotiles
              next if @priorities[id] != 0 || PBTerrain.hasReflections?(@terrain_tags[id])
              next if @framecount[id / 48 - 1] < 2
              @prioautotiles[[x, y]] = true
              break
            end
          end
        end
        @fullyrefreshedautos = true
      end
    else
      @fullyrefreshedautos = true
    end
  end

  def refreshLayer0(autotiles = false)
    return true if autotiles && !shown?
    ptX = @ox - @oxLayer0
    ptY = @oy - @oyLayer0
    if !autotiles && !@firsttime && !@usedsprites &&
       ptX >= 0 && ptX + @viewport.rect.width <= @layer0.bitmap.width &&
       ptY >= 0 && ptY + @viewport.rect.height <= @layer0.bitmap.height
      if @layer0clip && @viewport.ox == 0 && @viewport.oy == 0
        @layer0.ox = 0
        @layer0.oy = 0
        @layer0.src_rect.set(ptX.round, ptY.round, @viewport.rect.width, @viewport.rect.height)
      else
        @layer0.ox = ptX.round
        @layer0.oy = ptY.round
        @layer0.src_rect.set(0, 0, @layer0.bitmap.width, @layer0.bitmap.height)
      end
      return true
    end
    width  = @layer0.bitmap.width
    height = @layer0.bitmap.height
    bitmap = @layer0.bitmap
    ysize = @map_data.ysize
    xsize = @map_data.xsize
    zsize = @map_data.zsize
    twidth  = @tileWidth
    theight = @tileHeight
    mapdata = @map_data
    if autotiles
      return true if @fullyrefreshedautos && @prioautotiles.length == 0
      xStart = @oxLayer0 / twidth
      xStart = 0 if xStart < 0
      yStart = @oyLayer0 / theight
      yStart = 0 if yStart < 0
      xEnd = xStart + (width / twidth) + 1
      xEnd = xsize if xEnd > xsize
      yEnd = yStart + (height / theight) + 1
      yEnd = ysize if yEnd > ysize
      return true if xStart >= xEnd || yStart >= yEnd
      trans = Color.new(0, 0, 0, 0)
      temprect = Rect.new(0, 0, 0, 0)
      tilerect = Rect.new(0, 0, twidth, theight)
      zrange = 0...zsize
      overallcount = 0
      count = 0
      if !@fullyrefreshedautos
        for y in yStart..yEnd
          for x in xStart..xEnd
            for z in zrange
              id = mapdata[x, y, z]
              next if !id || id < 48 || id >= 384   # Skip non-autotiles
              prioid = @priorities[id]
              next if prioid != 0 || PBTerrain.hasReflections?(@terrain_tags[id])
              fcount = @framecount[id / 48 - 1]
              next if !fcount || fcount < 2
              overallcount += 1
              xpos = (x * twidth) - @oxLayer0
              ypos = (y * theight) - @oyLayer0
              bitmap.fill_rect(xpos, ypos, twidth, theight, trans) if overallcount <= 2000
              break
            end
            for z in zrange
              id = mapdata[x, y, z]
              next if !id || id < 48
              prioid = @priorities[id]
              next if prioid != 0 || PBTerrain.hasReflections?(@terrain_tags[id])
              if overallcount > 2000
                xpos = (x * twidth) - @oxLayer0
                ypos = (y * theight) - @oyLayer0
                count = addTile(@autosprites, count, xpos, ypos, id)
              elsif id >= 384   # Tileset tiles
                temprect.set(((id - 384) & 7) * @tileSrcWidth,
                             ((id - 384) >> 3) * @tileSrcHeight,
                             @tileSrcWidth, @tileSrcHeight)
                xpos = (x * twidth) - @oxLayer0
                ypos = (y * theight) - @oyLayer0
                if @diffsizes
                  bitmap.stretch_blt(Rect.new(xpos, ypos, twidth, theight), @tileset, temprect)
                else
                  bitmap.blt(xpos, ypos, @tileset, temprect)
                end
              else   # Autotiles
                tilebitmap = @autotileInfo[id]
                if !tilebitmap
                  anim = autotileFrame(id)
                  next if anim < 0
                  tilebitmap = Bitmap.new(twidth, theight)
                  bltAutotile(tilebitmap, 0, 0, id, anim)
                  @autotileInfo[id] = tilebitmap
                end
                xpos = (x * twidth) - @oxLayer0
                ypos = (y * theight) - @oyLayer0
                bitmap.blt(xpos, ypos, tilebitmap, tilerect)
              end
            end
          end
        end
        Graphics.frame_reset
      else
        if !@priorect || !@priorectautos ||
           @priorect[0] != xStart || @priorect[1] != yStart ||
           @priorect[2] != xEnd || @priorect[3] != yEnd
          @priorect = [xStart, yStart, xEnd, yEnd]
          @priorectautos = []
          for y in yStart..yEnd
            for x in xStart..xEnd
              @priorectautos.push([x, y]) if @prioautotiles[[x, y]]
            end
          end
        end
        for tile in @priorectautos
          x = tile[0]
          y = tile[1]
          overallcount += 1
          xpos = (x * twidth) - @oxLayer0
          ypos = (y * theight) - @oyLayer0
          bitmap.fill_rect(xpos, ypos, twidth, theight, trans)
          z = 0
          while z < zsize
            id = mapdata[x, y, z]
            z += 1
            next if !id || id < 48
            prioid = @priorities[id]
            next if prioid != 0 || PBTerrain.hasReflections?(@terrain_tags[id])
            if id >= 384   # Tileset tiles
              temprect.set(((id - 384) & 7) * @tileSrcWidth,
                           ((id - 384) >> 3) * @tileSrcHeight,
                           @tileSrcWidth, @tileSrcHeight)
              if @diffsizes
                bitmap.stretch_blt(Rect.new(xpos, ypos, twidth, theight), @tileset, temprect)
              else
                bitmap.blt(xpos, ypos, @tileset, temprect)
              end
            else   # Autotiles
              tilebitmap = @autotileInfo[id]
              if !tilebitmap
                anim = autotileFrame(id)
                next if anim < 0
                tilebitmap = Bitmap.new(twidth, theight)
                bltAutotile(tilebitmap, 0, 0, id, anim)
                @autotileInfo[id] = tilebitmap
              end
              bitmap.blt(xpos, ypos, tilebitmap, tilerect)
            end
          end
        end
        Graphics.frame_reset if overallcount > 500
      end
      @usedsprites = false
      return true
    end
    return false if @usedsprites
    @firsttime = false
    @oxLayer0 = @ox - (width >> 2)
    @oyLayer0 = @oy - (height >> 2)
    if @layer0clip
      @layer0.ox = 0
      @layer0.oy = 0
      @layer0.src_rect.set(width >> 2, height >> 2, @viewport.rect.width, @viewport.rect.height)
    else
      @layer0.ox = (width >> 2)
      @layer0.oy = (height >> 2)
    end
    @layer0.bitmap.clear
    @oxLayer0 = @oxLayer0.round
    @oyLayer0 = @oyLayer0.round
    xStart = @oxLayer0 / twidth
    xStart = 0 if xStart < 0
    yStart = @oyLayer0 / theight
    yStart = 0 if yStart < 0
    xEnd = xStart + (width / twidth) + 1
    xEnd = xsize if xEnd >= xsize
    yEnd = yStart + (height / theight) + 1
    yEnd = ysize if yEnd >= ysize
    if xStart < xEnd && yStart < yEnd
      tmprect = Rect.new(0, 0, 0, 0)
      yrange = yStart...yEnd
      xrange = xStart...xEnd
      for z in 0...zsize
        for y in yrange
          ypos = (y * theight) - @oyLayer0
          for x in xrange
            xpos = (x * twidth) - @oxLayer0
            id = mapdata[x, y, z]
            next if id == 0 || @priorities[id] != 0 || PBTerrain.hasReflections?(@terrain_tags[id])
            if id >= 384   # Tileset tiles
              tmprect.set(((id - 384) & 7) * @tileSrcWidth,
                          ((id - 384) >> 3) * @tileSrcHeight,
                          @tileSrcWidth, @tileSrcHeight)
              if @diffsizes
                bitmap.stretch_blt(Rect.new(xpos, ypos, twidth, theight), @tileset, tmprect)
              else
                bitmap.blt(xpos, ypos, @tileset, tmprect)
              end
            else   # Autotiles
              frames = @framecount[id / 48 - 1]
              if frames <= 1
                frame = 0
              else
                frame = (Graphics.frame_count / Animated_Autotiles_Frames) % frames
              end
              bltAutotile(bitmap, xpos, ypos, id, frame)
            end
          end
        end
      end
      Graphics.frame_reset
    end
    return true
  end

  def refresh(autotiles = false)
    @oldOx = @ox
    @oldOy = @oy
    usesprites = false
    if @layer0
      @layer0.visible = @visible
      usesprites = !refreshLayer0(autotiles)
      return if autotiles && !usesprites
    else
      usesprites = true
    end
    refreshFlashSprite
    xsize = @map_data.xsize
    ysize = @map_data.ysize
    minX = (@ox / @tileWidth) - 1
    minX.clamp(0, xsize - 1)
    maxX = ((@ox + @viewport.rect.width) / @tileWidth) + 1
    maxX.clamp(0, xsize - 1)
    minY = (@oy / @tileHeight) - 1
    minY.clamp(0, ysize - 1)
    maxY = ((@oy + @viewport.rect.height) / @tileHeight) + 1
    maxY.clamp(0, ysize - 1)
    count = 0
    if minX < maxX && minY < maxY
      @usedsprites = usesprites || @usedsprites
      @layer0.visible = false if usesprites && @layer0
      if !@priotilesrect || !@priotilesfast ||
         @priotilesrect[0] != minX || @priotilesrect[1] != minY ||
         @priotilesrect[2] != maxX || @priotilesrect[3] != maxY
        @priotilesrect = [minX, minY, maxX, maxY]
        @priotilesfast = []
        if @fullyrefreshed
          for y in minY..maxY
            for x in minX..maxX
              next if !@priotiles[[x, y]]
              @priotiles[[x, y]].each { |tile| @priotilesfast.push([x, y, tile[0], tile[1]]) }
            end
          end
        else
          for z in 0...@map_data.zsize
            for y in minY..maxY
              for x in minX..maxX
                id = @map_data[x, y, z]
                next if id == 0
                next if @priorities[id] == 0 && !PBTerrain.hasReflections?(@terrain_tags[id])
                @priotilesfast.push([x, y, z, id])
              end
            end
          end
        end
      end
      for prio in @priotilesfast
        xpos = (prio[0] * @tileWidth) - @ox
        ypos = (prio[1] * @tileHeight) - @oy
        count = addTile(@tiles, count, xpos, ypos, prio[3])
      end
    end
    if count < @tiles.length
      bigchange = (count <= (@tiles.length * 2 / 3)) && @tiles.length > 40
      j = count
      len = @tiles.length
      while j < len
        sprite = @tiles[j]
        @tiles[j + 1] = -1
        if bigchange
          sprite.dispose
          @tiles[j] = nil
          @tiles[j + 1] = nil
        elsif !@tiles[j].disposed?
          sprite.visible = false if sprite.visible
        end
        j += 2
      end
      @tiles.compact! if bigchange
    end
  end

  def update
    if @haveGraphicsWH
      @graphicsWidth  = Graphics.width
      @graphicsHeight = Graphics.height
    end
    # Update tone
    if @oldtone != @tone
      @layer0.tone = @tone
      @flash.tone  = @tone if @flash
      for sprite in @autosprites
        sprite.tone = @tone if sprite.is_a?(Sprite)
      end
      for sprite in @tiles
        sprite.tone = @tone if sprite.is_a?(Sprite)
      end
      @oldtone = @tone.clone
    end
    # Update color
    if @oldcolor != @color
      @layer0.color = @color
      @flash.color  = @color if @flash
      for sprite in @autosprites
        sprite.color = @color if sprite.is_a?(Sprite)
      end
      for sprite in @tiles
        sprite.color = @color if sprite.is_a?(Sprite)
      end
      @oldcolor = @color.clone
    end
    # Refresh anything that has changed
    if @autotiles.changed
      refresh_autotiles
      repaintAutotiles
    end
    refresh_flash if @flashChanged
    refresh_tileset if @tilesetChanged
    @flash.opacity = FlashOpacity[(Graphics.frame_count / 2) % 6] if @flash
    mustrefresh = (@oldOx != @ox || @oldOy != @oy || @tilesetChanged || @autotiles.changed)
    if @viewport.ox != @oldViewportOx || @viewport.oy != @oldViewportOy
      mustrefresh = true
      @oldViewportOx = @viewport.ox
      @oldViewportOy = @viewport.oy
    end
    refresh if mustrefresh
    if (Graphics.frame_count % Animated_Autotiles_Frames) == 0 || @nowshown
      repaintAutotiles
      refresh(true)
    end
    @nowshown          = false
    @autotiles.changed = false
    @tilesetChanged    = false
  end
end
