#==============================================================================#
#                             Overworld Shadows                                #
#                                  by Marin                                    #
#==============================================================================#
#                                    Info                                      #
#                                                                              #
#   You'll have likely heard of KleinStudios' Overworld Shadows script; many   #
#    fangames use it, after all. It was not compatible with Essentials v17+    #
#    though, so after getting the suggestion I thought it would be cool if I   #
#   could make something of my own that would work with v16, as well as v17.   #
#==============================================================================#
#                                  Features:                                   #
#                - Blacklisting events from receiving shadows                  #
#                - Whitelisting events to always receive shadows               #
#                - A scaling animation when an event jumps                     #
#==============================================================================#
#                                    Usage                                     #
#                                                                              #
#     Shadow_Path is the path to the shadow graphic. You can change this       #
#   sprite, though you may need to fiddle with positioning of the sprite in    #
#  relation to the event after, though. That's done in "def position_shadow".  #
#                                                                              #
#  As the name literally says, if an event's name includes any of the strings  #
#  in "No_Shadow_If_Event_Name_Has", it won't get a shadow, UNLESS the event's #
#                 name also includes any of the strings in                     #
#   "Always_Give_Shadow_If_Event_Name_Has". This is essentially "overriding".  #
#                                                                              #
#    Case_Sensitive is either true or false. It's used when determing if an    #
# event's name includes a string in the "No_Shadow" and "Always_Give" arrays.  #
#      If true, it must match all strings with capitals exactly as well.       #
#                If false, capitals don't need to match up.                    #
#==============================================================================#
#                    Please give credit when using this.                       #
#==============================================================================#


SHADOW_IMG_FOLDER = "Graphics/Characters/"
SHADOW_IMG_NAME = "shadow"

# Whether or not the event names below need to match in capitals as well.
Case_Sensitive = false

No_Shadow_If_Event_Name_Has = [
  # I like to use "extensions" like these. Up to you though.
  ".shadowless",
  ".noshadow",
  ".sl",
  "Door",
  "Stairs"
]

# Events that have this in their event name will always receive a shadow.
# Does take "Case_Sensitive" into account.
Always_Give_Shadow_If_Event_Name_Has = [
  "Trainer", "npc"
]

# Determines whether or not an event should be given a shadow.
def pbShouldGetShadow?(event)
  return true if event.is_a?(Game_Player) # The player will always have a shadow
  page = pbGetActiveEventPage(event)
  return false unless page
  comments = page.list.select { |e| e.code == 108 || e.code == 408 }.map do |e|
    e.parameters.join
  end
  Always_Give_Shadow_If_Event_Name_Has.each do |e|
    name = event.name.clone
    unless Case_Sensitive
      e.downcase!
      name.downcase!
    end
    return true if name.include?(e) || comments.any? { |c| c.include?(e) }
  end
  No_Shadow_If_Event_Name_Has.each do |e|
    name = event.name.clone
    unless Case_Sensitive
      e.downcase!
      name.downcase!
    end
    return false if name.include?(e) || comments.any? { |c| c.include?(e) }
  end
  return true
end

# Extending so we can access some private instance variables.
class Game_Character
  attr_reader :jump_count
end

unless Spriteset_Map.respond_to?(:viewport)
  class Spriteset_Map
    def viewport
      return @viewport1
    end

    def self.viewport
      return $scene.spriteset.viewport rescue nil
    end
  end
end

# Following Pokémon compatibility
def pbToggleFollowingPokemon
  return if $Trainer.party[0].hp <= 0 || $Trainer.party[0].isEgg?
  if $game_switches[Following_Activated_Switch]
    if $game_switches[Toggle_Following_Switch]
      $game_switches[Toggle_Following_Switch] = false
      $PokemonTemp.dependentEvents.remove_sprite(true)
      $scene.spriteset.usersprites.select do |e|
        e.is_a?(DependentEventSprites)
      end.each do |des|
        des.sprites.each do |e|
          if e && e.shadow
            e.shadow.dispose
            e.shadow = nil
          end
        end
      end
      pbWait(1)
    else
      $game_switches[Toggle_Following_Switch] = true
      $PokemonTemp.dependentEvents.refresh_sprite
      $scene.spriteset.usersprites.select do |e|
        e.is_a?(DependentEventSprites)
      end.each do |des|
        des.sprites.each do |e|
          e.make_shadow if e.respond_to?(:make_shadow)
        end
      end
      pbWait(1)
    end
  end
end

class DependentEventSprites
  def refresh
    for sprite in @sprites
      sprite.dispose
    end
    @sprites.clear
    $PokemonTemp.dependentEvents.eachEvent do |event, data|
      if data[2] == @map.map_id # Check current map
        spr = Sprite_Character.new(@viewport,event,true)
        @sprites.push(spr)
      end
    end
  end
end

unless defined?(pbGetActiveEventPage)
  def pbGetActiveEventPage(event, mapid = nil)
    mapid ||= event.map.map_id if event.respond_to?(:map)
    pages = (event.is_a?(RPG::Event) ? event.pages : event.instance_eval { @event.pages })
    for i in 0...pages.size
      c = pages[pages.size - 1 - i].condition
      ss = !(c.self_switch_valid && !$game_self_switches[[mapid,
                                                          event.id,c.self_switch_ch]])
      sw1 = !(c.switch1_valid && !$game_switches[c.switch1_id])
      sw2 = !(c.switch2_valid && !$game_switches[c.switch2_id])
      var = true
      if c.variable_valid
        if !c.variable_value || !$game_variables[c.variable_id].is_a?(Numeric) ||
          $game_variables[c.variable_id] < c.variable_value
          var = false
        end
      end
      if ss && sw1 && sw2 && var # All conditions are met
        return pages[pages.size - 1 - i]
      end
    end
    return nil
  end
end

class Spriteset_Map
  attr_accessor :usersprites
end

class Sprite_Character
  attr_accessor :shadow

  alias ow_shadow_init initialize
  def initialize(viewport, character = nil, is_follower = false)
    @viewport = viewport
    @is_follower = is_follower
    ow_shadow_init(@viewport, character)




    return unless pbShouldGetShadow?(character)
    return if @is_follower && defined?(Toggle_Following_Switch) &&
      !$game_switches[Toggle_Following_Switch]
    return if @is_follower && defined?(Following_Activated_Switch) &&
      !$game_switches[Following_Activated_Switch]
    @character = character
    if @character.is_a?(Game_Event)
      page = pbGetActiveEventPage(@character)
      return if !page || !page.graphic || page.graphic.character_name == ""
    end
    make_shadow
  end

  def make_shadow
    @shadow.dispose if @shadow
    @shadow = nil
    @shadow = Sprite.new(@viewport)
    @shadow.bitmap = RPG::Cache.load_bitmap(SHADOW_IMG_FOLDER,SHADOW_IMG_NAME)
    # Center the shadow by halving the origin points
    @shadow.ox = @shadow.bitmap.width / 2.0
    @shadow.oy = @shadow.bitmap.height / 2.0
    # Positioning the shadow
    position_shadow
  end

  def position_shadow
    return unless @shadow
    # x = @character.screen_x
    # y = @character.screen_y
    # if @character.jumping?
    #   @totaljump = @character.jump_count if !@totaljump
    #   case @character.jump_count
    #   when 1..(@totaljump / 3)
    #     @shadow.zoom_x += 0.1
    #     @shadow.zoom_y += 0.1
    #   when (@totaljump / 3 + 1)..(@totaljump / 3 + 2)
    #     @shadow.zoom_x += 0.05
    #     @shadow.zoom_y += 0.05
    #   when (@totaljump / 3 * 2 - 1)..(@totaljump / 3 * 2)
    #     @shadow.zoom_x -= 0.05
    #     @shadow.zoom_y -= 0.05
    #   when (@totaljump / 3 * 2 + 1)..(@totaljump)
    #     @shadow.zoom_x -= 0.1
    #     @shadow.zoom_y -= 0.1
    #   end
    #   if @character.jump_count == 1
    #     @shadow.zoom_x = 1.0
    #     @shadow.zoom_y = 1.0
    #     @totaljump = nil
    #   end
    # end
    @shadow.x = x
    @shadow.y = y - 6
    @shadow.z = self.z - 1

    if @shadow
      if @charbitmap.disposed? || @character.instance_eval { @erased }
        @shadow.dispose
        @shadow = nil
      end
    end
  end

  alias ow_shadow_visible visible=
  def visible=(value)
    ow_shadow_visible(value)
    @shadow.visible = value if @shadow
  end

  alias ow_shadow_dispose dispose
  def dispose
    ow_shadow_dispose
    @shadow.dispose if @shadow
    @shadow = nil
  end

  alias ow_shadow_update update
  def update
    ow_shadow_update
    position_shadow

    if @character.is_a?(Game_Event)
      page = pbGetActiveEventPage(@character)
      if @old_page != page
        @shadow.dispose if @shadow
        @shadow = nil
        if page && page.graphic && page.graphic.character_name != "" &&
          pbShouldGetShadow?(@character)
          unless @is_follower && defined?(Toggle_Following_Switch) &&
            !$game_switches[Toggle_Following_Switch]
            unless @is_follower && defined?(Following_Activated_Switch) &&
              !$game_switches[Following_Activated_Switch]
              make_shadow
            end
          end
        end
      end
    end

    @old_page = (@character.is_a?(Game_Event) ? pbGetActiveEventPage(@character) : nil)

    bushdepth = @character.bush_depth
    if @shadow
      @shadow.opacity = self.opacity
      @shadow.visible = (bushdepth == 0)
      if !self.visible || (@is_follower || @character == $game_player) &&
        ($PokemonGlobal.surfing || $PokemonGlobal.diving)
        @shadow.visible = false
      end
    end
  end
end
