#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
# Pay to Heal by Blizzard
# Version: 2.0
# Type: Healing System
# Date v2.0: 29.7.2009
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#   
#  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:
# 
#   99% compatible with SDK v1.x. 90% compatible with SDK v2.x.
# 
# 
# Features:
# 
#   - allows healing of HP, SP and states separately
#   - gold-per-HP cost
#   - gold-per-SP cost
#   - different cost for each state
#   - every undefined state is treated as "positive" state and will not be
#     removed upon healing
# 
# new in v2.0:
#   - completely improved
# 
# 
# Instructions:
# 
# - Explanation:
# 
#   This script will allow you to heal your characters as you see fit and pay
#   only for the healing you have made use of.
#   You can call the Scene by using a "Call script" event command. Type into
#   the editor window this text:
# 
#     $scene = Scene_PayToHeal.new(HP_COST, SP_COST)
#   
#   HP_COST and SP_COST are optional parameters. If you specify them, they will
#   override your default configuration.
# 
# - Configuration:
# 
#   ONE_HEAL_SOUND     - defines the sound effect file with volume and pitch to
#                        play when one actor is healed
#   ALL_HEAL_SOUND     - defines the sound effect file with volume and pitch to
#                        play when all actors are healed
#   FULL_HEAL_SOUND    - defines the sound effect file with volume and pitch to
#                        play when all actors are healed completely
#   HEAL_COMMANDS      - list of healing commands in the menu
#   RIGHT_HEAL_WINDOWS - shows command windows on the right
#   HP_COST            - how much healing 1 HP costs
#   SP_COST            - how much healing 1 SP costs
#   
#   
# Note:
# 
#   After the HP healing and SP healing costs have been calculated, they will
#   rounded up if the numbers are not integers.
# 
# 
# If you find any bugs, please report them here:
# http://www.chaosproject.co.nr
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=

module BlizzCFG

#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# START Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

  ONE_HEAL_SOUND = RPG::AudioFile.new('107-Heal03', 80, 100)
  ALL_HEAL_SOUND = RPG::AudioFile.new('108-Heal04', 80, 100)
  FULL_HEAL_SOUND = RPG::AudioFile.new('109-Heal05', 80, 100)
  HEAL_COMMANDS = ['Heal HP', 'Heal SP', 'States', 'Heal all HP',
      'Heal all SP', 'All states', 'Full Heal', 'Exit']
  RIGHT_HEAL_WINDOWS = true
  HP_COST = 0.2
  SP_COST = 2.0
  
  def self.state_cost(id)
    case id
    # START state costs
    #   when STATE_ID then return COST_TO_HEAL
    when 2 then return 100
    when 3 then return 50
    when 4 then return 50
    when 5 then return 50
    when 6 then return 50
    when 7 then return 100
    when 8 then return 100
    when 9 then return 200
    when 10 then return 200
    when 11 then return 200
    when 12 then return 200
    # END state costs
    end
    return 0
  end
  
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# END Configuration
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

end

#==============================================================================
# Array
#==============================================================================

class Array
  
  def sum
    sum = 0
    self.each {|i| sum += i if i.is_a?(Numeric)}
    return sum
  end
  
end

#==============================================================================
# Game_Battler
#==============================================================================

class Game_Battler
  
  def remove_negative_states
    @states.clone.each {|id| remove_state(id) if BlizzCFG.state_cost(id) > 0}
  end
  
  def has_negative_states?
    return @states.any? {|id| BlizzCFG.state_cost(id) > 0}
  end
  
  def recover_all
    @hp = maxhp
    @sp = maxsp
    remove_negative_states
  end
  
end

#==============================================================================
# Window_Dialog
#==============================================================================

class Window_Dialog < Window_Command
  
  def initialize(width, commands = ['Yes', 'No'], caption = 'Are you sure?')
    commands.push('')
    @caption = caption
    @cursor_width = 32
    super(width, commands)
    if $fontface != nil
      self.contents.font.name = $fontface
      self.contents.font.size = $fontsize
    elsif $defaultfonttype != nil
      self.contents.font.name = $defaultfonttype
      self.contents.font.size = $defaultfontsize
    end
    self.x, self.y, self.z = 320 - self.width/2, 200 - self.height/2, 1500
    @item_max, self.opacity = commands.size - 1, 192
    commands.each {|c|
        w = self.contents.text_size(c).width
        @cursor_width = w if @cursor_width < w}
    @cursor_width += 32
    refresh
    update_cursor_rect
  end
  
  def refresh
    super
    self.contents.font.color = system_color
    self.contents.draw_text(4, 0, self.width-40, 32, @caption, 1)
  end
  
  def draw_item(index, color)
    self.contents.font.color = color
    rect = Rect.new(4, 32 * (index+1), self.contents.width - 8, 32)
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    self.contents.draw_text(rect, @commands[index], 1)
  end
  
  def update_cursor_rect
    if @index < 0 || @item_max == 0
      self.cursor_rect.empty
    else
      x = (self.contents.width - @cursor_width) / 2
      self.cursor_rect.set(x, (@index + 1) * 32, @cursor_width, 32)
    end
  end
  
end

#==============================================================================
# Window_GoldCost
#==============================================================================

class Window_GoldCost < Window_Base
  
  attr_reader :cost
  
  def initialize
    super(0, 0, 160, 96)
    self.contents = Bitmap.new(width - 32, height - 32)
    if $fontface != nil
      self.contents.font.name = $fontface
      self.contents.font.size = $fontsize
    elsif $defaultfonttype != nil
      self.contents.font.name = $defaultfonttype
      self.contents.font.size = $defaultfontsize
    end
    self.cost = 0
  end
  
  def cost=(val)
    if @cost != val
      @cost = val
      refresh
    end
  end
  
  def refresh
    self.contents.clear
    cx = contents.text_size($data_system.words.gold).width
    self.contents.font.color = normal_color
    self.contents.draw_text(4, 32, 120 - cx - 2, 32, @cost.to_s, 2)
    self.contents.font.color = system_color
    self.contents.draw_text(4, 0, 120, 32, 'Cost')
    self.contents.draw_text(124 - cx, 32, cx, 32, $data_system.words.gold, 2)
  end
  
end

#==============================================================================
# Scene_PayToHeal
#==============================================================================

class Scene_PayToHeal
  
  def initialize(hp_cost = BlizzCFG::HP_COST, sp_cost = BlizzCFG::SP_COST)
    @hp_cost, @sp_cost = hp_cost, sp_cost
  end
  
  def main
    @command_window = Window_Command.new(160, BlizzCFG::HEAL_COMMANDS)
    @command_window.index = 0
    @command_window.x = 480 if BlizzCFG::RIGHT_HEAL_WINDOWS
    @cost_window = Window_GoldCost.new
    @cost_window.x = 480 if BlizzCFG::RIGHT_HEAL_WINDOWS
    @cost_window.y = 320
    @gold_window = Window_Gold.new
    @gold_window.x = 480 if BlizzCFG::RIGHT_HEAL_WINDOWS
    @gold_window.y = 416
    @status_window = Window_MenuStatus.new
    @status_window.x = 160 if !BlizzCFG::RIGHT_HEAL_WINDOWS
    calculate_costs
    Graphics.transition
    loop do
      Graphics.update
      Input.update
      update
      break if $scene != self
    end
    Graphics.freeze
    @command_window.dispose
    @status_window.dispose
    @gold_window.dispose
    @cost_window.dispose
  end
  
  def update
    if @command_window.active
      update_command
    elsif @status_window.active
      update_status
    elsif @dialog_window != nil && @dialog_window.active
      update_dialog
    end
    if Input.repeat?(Input::C) || Input.repeat?(Input::B) ||
        Input.repeat?(Input::DOWN) || Input.repeat?(Input::LEFT) ||
        Input.repeat?(Input::RIGHT) || Input.repeat?(Input::UP)
      @cost_window.cost = get_cost
    end
  end
    
  def update_command
    @command_window.update
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      $scene = Scene_Map.new
    elsif Input.trigger?(Input::C)
      case @command_window.index
      when 0, 1, 2
        $game_system.se_play($data_system.decision_se)
        @command_window.active = false
        @status_window.active = true
        @status_window.index = 0
      when 3
        if $game_party.gold < @cost_window.cost ||
            !$game_party.actors.any? {|actor| actor.hp != actor.maxhp}
          $game_system.se_play($data_system.buzzer_se)
          return
        end
        $game_system.se_play($data_system.decision_se)
        create_dialog
      when 4
        if $game_party.gold < @cost_window.cost ||
            !$game_party.actors.any? {|actor| actor.sp != actor.maxsp}
          $game_system.se_play($data_system.buzzer_se)
          return
        end
        $game_system.se_play($data_system.decision_se)
        create_dialog
      when 4
        if $game_party.gold < @cost_window.cost ||
            !$game_party.actors.any? {|actor| actor.sp != actor.maxsp}
          $game_system.se_play($data_system.buzzer_se)
          return
        end
        $game_system.se_play($data_system.decision_se)
        create_dialog
      when 6
        if $game_party.gold < @cost_window.cost ||
            !$game_party.actors.any? {|actor| actor.sp != actor.maxsp ||
            actor.hp != actor.maxhp}
          $game_system.se_play($data_system.buzzer_se)
          return
        end
        $game_system.se_play($data_system.decision_se)
        create_dialog
      when 7
        $game_system.se_play($data_system.decision_se)
        $scene = Scene_Map.new
      end
    end
  end
  
  def update_status
    @status_window.update
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      @command_window.active = true
      @status_window.active = false
      @status_window.index = -1
    elsif Input.trigger?(Input::C)
      actor = $game_party.actors[@status_window.index]
      if $game_party.gold < @cost_window.cost ||
          @command_window.index == 0 && actor.maxhp == actor.hp ||
          @command_window.index == 1 && actor.maxsp == actor.sp ||
          @command_window.index == 2 && !actor.has_negative_states?
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      $game_system.se_play($data_system.decision_se)
      @status_window.active = false
      create_dialog
    end
  end
  
  def create_dialog
    @command_window.active = @status_window.active = false
    @dialog_window = Window_Dialog.new(256, ['Yes', 'No'],
        "This will cost #{@cost_window.cost} #{$data_system.words.gold}.")
  end
    
  def update_dialog
    @dialog_window.update
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      if @status_window.index >= 0
        @status_window.active = true
      else
        @command_window.active = true
      end
      @dialog_window.dispose
      @dialog_window = nil
    elsif Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      process_heal if @dialog_window.index == 0
      if @status_window.index >= 0
        @status_window.active = true
      else
        @command_window.active = true
      end
      @dialog_window.dispose
      @dialog_window = nil
    end  
  end
  
  def process_heal
    case @command_window.index
    when 0
      $game_system.se_play(BlizzCFG::ONE_HEAL_SOUND)
      actor = $game_party.actors[@status_window.index]
      actor.hp = actor.maxhp
      calculate_costs(@status_window.index)
    when 1
      $game_system.se_play(BlizzCFG::ONE_HEAL_SOUND)
      actor = $game_party.actors[@status_window.index]
      actor.sp = actor.maxsp
      calculate_costs(@status_window.index)
    when 2
      $game_system.se_play(BlizzCFG::ONE_HEAL_SOUND)
      $game_party.actors[@status_window.index].remove_negative_states
      calculate_costs(@status_window.index)
    when 3
      $game_system.se_play(BlizzCFG::ALL_HEAL_SOUND)
      $game_party.actors.each {|actor| actor.hp = actor.maxhp}
      calculate_costs
    when 4
      $game_system.se_play(BlizzCFG::ALL_HEAL_SOUND)
      $game_party.actors.each {|actor| actor.sp = actor.maxsp}
      calculate_costs
    when 5
      $game_system.se_play(BlizzCFG::ALL_HEAL_SOUND)
      $game_party.actors.each {|actor| actor.remove_negative_states}
      calculate_costs
    when 6
      $game_system.se_play(BlizzCFG::FULL_HEAL_SOUND)
      $game_party.actors.each {|actor| actor.recover_all}
      calculate_costs
    end
    $game_party.lose_gold(@cost_window.cost)
    @cost_window.cost = 0
    @gold_window.refresh
    @status_window.refresh
  end
  
  def calculate_costs(index = nil)
    if index == nil
      @hp_costs = []
      @sp_costs = []
      @state_costs = []
      $game_party.actors.each {|actor|
          @hp_costs.push((actor.maxhp - actor.hp) * @hp_cost)
          @sp_costs.push((actor.maxsp - actor.sp) * @sp_cost)
          cost = 0.0
          actor.states.each {|id| cost += BlizzCFG.state_cost(id)}
          @state_costs.push(cost)}
    else
      actor = $game_party.actors[index]
      @hp_costs[index] = (actor.maxhp - actor.hp) * @hp_cost
      @sp_costs[index] = (actor.maxsp - actor.sp) * @sp_cost
      @state_costs[index] = 0.0
      actor.states.each {|id| @state_costs[index] += BlizzCFG.state_cost(id)}
    end
  end
  
  def get_cost
    cost = 0.0
    if @status_window.index >= 0
      case @command_window.index
      when 0 then cost = @hp_costs[@status_window.index]
      when 1 then cost = @sp_costs[@status_window.index]
      when 2 then cost = @state_costs[@status_window.index]
      end
    else
      case @command_window.index
      when 3 then cost = @hp_costs.map {|c| c.ceil}.sum
      when 4 then cost = @sp_costs.map {|c| c.ceil}.sum
      when 5 then cost = @state_costs.map {|c| c.ceil}.sum
      when 6 then cost = (@hp_costs + @sp_costs + @state_costs).map {|c| c.ceil}.sum
      end
    end
    return cost.ceil
  end
  
end
