#===============================================================================
# 
# Yanfly Engine Melody - Skill Fusion System
# Last Date Updated: 2010.06.02
# Level: Normal, Hard
# 
# This script allows for actors to fuse together skills they've learned into
# a new skill altogether. This system allows actors to constantly recycle their
# skills into something more useful as well as keeping the game fresh. Skills
# can be fused multiple times with different skills, too, to allow for a large
# amount of combinations and possibilities.
# 
#===============================================================================
# Updates
# -----------------------------------------------------------------------------
# o 2010.06.02 - Started Script and Finished.
#===============================================================================
# Instructions
# -----------------------------------------------------------------------------
# To install this script, open up your script editor and copy/paste this script
# to an open slot below ▼ Materials but above ▼ Main. Remember to save.
# 
# -----------------------------------------------------------------------------
# Skill Tags - Insert the following tags into Skill noteboxes.
# -----------------------------------------------------------------------------
# <fusion cost: x>
# This adjusts the fusion cost of creating this particular skill. x is the
# amount of gold required to fuse the skill. This belongs in the notebox of the
# skill being fused.
# 
# <fuse with skill x: y>
# This creates the skill fusion combination. The skill whose notebox contains
# this tag will combine with skill x to form skill y. This tag will also then
# implement the fusion property into skill x, too. This belongs in the notebox
# of either of the skills being fused together.
# 
# -----------------------------------------------------------------------------
# Event Script Call
# -----------------------------------------------------------------------------
# $scene = Scene_Skill_Fusion.new
# This brings up the skill fusion scene. Here, the script takes place.
#===============================================================================

$imported = {} if $imported == nil
$imported["SkillFusionSystem"] = true

module YEM
  module SKILL_FUSION
    
    #===========================================================================
    # Section I. Basic Settings
    # -------------------------------------------------------------------------
    # This section adjusts all of the basic settings regarding the Skill Fusion
    # System scripts such as the vocabulary used in the script, the default
    # cost of skill fusion, and the icon used for the empty skills.
    #===========================================================================
    
    # This is the default fusion cost for creating new skills.
    DEFAULT_COST = 3000
    
    # This hash contains all of the basic vocabulary data used for the script.
    VOCAB ={
      :fusion       => "Fusion",
      :fusion_help  => "Select two skills to fuse together.",
      :select_help  => "Please select an actor.",
      :exit         => "Finish",
      :exit_help    => "End the skill fusion process.",
      :parent_skill => "Parent Skills",
      :child_skill  => "Result Skill",
      :no_skill     => "<No Skill>",
      :fusion_cost  => "Fusion Cost",
    } # Do not remove this.
    
    # This is the icon used for the No Skill result.
    NO_SKILL_ICON = 98
    
    # This will cause the actor to forget the two skills being fused together
    # to create the child skill if this constant is set to true. If you wish
    # for the actor to retain the skills, set this value to false.
    FORGET_PARENT_SKILLS = true
    
    #===========================================================================
    # Section II. Visual Settings
    # -------------------------------------------------------------------------
    # This section adjusts the visual settings for the script, which consist
    # of the actor selection window.
    #===========================================================================
    
    # These two values adjust the sprite offsets for the actor window sprites
    # when they appear. This is in case sprites larger than 32x32 are used for
    # charactersets.
    ACTOR_SPRITE_X_OFFSET = 32
    ACTOR_SPRITE_Y_OFFSET = 32
    
  end # SKILL_FUSION
end # YEM

#===============================================================================
# Editting anything past this point may potentially result in causing computer
# damage, incontinence, explosion of user's head, coma, death, and/or halitosis.
# Therefore, edit at your own risk.
#===============================================================================

module YEM
  module REGEXP
  module SKILL
    
    FUSION_COST = /<(?:FUSION_COST|fusion cost):[ ]*(\d+)>/i
    FUSE_RESULT = /<(?:FUSE_WITH_SKILL|fuse with skill)[ ](\d+):[ ]*(\d+)>/i
    
  end # SKILL
  end # REGEXP
end # YEM

#===============================================================================
# RPG::Skill
#===============================================================================

class RPG::Skill < RPG::UsableItem
  
  #--------------------------------------------------------------------------
  # public instance variables
  #--------------------------------------------------------------------------
  attr_accessor :fusion_cost
  attr_accessor :fuse_result

  #--------------------------------------------------------------------------
  # common cache: yem_cache_skill_sfs
  #--------------------------------------------------------------------------
  def yem_cache_skill_sfs
    return if @cached_skill_sfs; @cached_skill_sfs = true
    @fusion_cost = YEM::SKILL_FUSION::DEFAULT_COST
    @fuse_result = {} if @fuse_result == nil
    
    self.note.split(/[\r\n]+/).each { |line|
      case line
      when YEM::REGEXP::SKILL::FUSION_COST
        @fusion_cost = $1.to_i
      when YEM::REGEXP::SKILL::FUSE_RESULT
        partner_skill = $data_skills[$1.to_i]
        next if partner_skill == nil
        @fuse_result[$1.to_i] = $2.to_i
        partner_skill.fuse_result = {} if partner_skill.fuse_result == nil
        partner_skill.fuse_result[self.id] = $2.to_i
      end
    } # end self.note.split
  end # yem_cache_skill_sfs
  
end # RPG::Skill

#===============================================================================
# Scene_Title
#===============================================================================

class Scene_Title < Scene_Base
  
  #--------------------------------------------------------------------------
  # alias method: load_bt_database
  #--------------------------------------------------------------------------
  alias load_bt_database_sfs load_bt_database unless $@
  def load_bt_database
    load_bt_database_sfs
    load_sfs_cache
  end
  
  #--------------------------------------------------------------------------
  # alias method: load_database
  #--------------------------------------------------------------------------
  alias load_database_sfs load_database unless $@
  def load_database
    load_database_sfs
    load_sfs_cache
  end
  
  #--------------------------------------------------------------------------
  # new method: load_sfs_cache
  #--------------------------------------------------------------------------
  def load_sfs_cache
    for obj in $data_skills
      next if obj == nil
      obj.yem_cache_skill_sfs
    end
  end
  
end # Scene_Title

#===============================================================================
# Game_Actors
#===============================================================================

class Game_Actor < Game_Battler
  
  #--------------------------------------------------------------------------
  # new method: learned_skills
  #--------------------------------------------------------------------------
  def learned_skills
    result = []
    for i in @skills
      result.push($data_skills[i])
    end
    return result
  end
  
end # Game_Actor

#===============================================================================
# Window_Skill_Fusion_Actor
#===============================================================================

class Window_Skill_Fusion_Actor < Window_Selectable
  
  #--------------------------------------------------------------------------
  # initialize
  #--------------------------------------------------------------------------
  def initialize(dy)
    super(0, 0, Graphics.width/2, Graphics.height-dy)
    self.active = false
    self.index = 0
    refresh
  end
  
  #--------------------------------------------------------------------------
  # actor
  #--------------------------------------------------------------------------
  def actor; return @data[self.index]; end
  
  #--------------------------------------------------------------------------
  # refresh
  #--------------------------------------------------------------------------
  def refresh
    @data = $game_party.members
    @item_max = @data.size
    create_contents
    for i in 0...@item_max; draw_item(i); end
    dy = @item_max * WLH
    rect = Rect.new(0, dy, contents.width, contents.height-dy)
    self.contents.clear_rect(rect)
  end
    
  #--------------------------------------------------------------------------
  # draw_item
  #--------------------------------------------------------------------------
  def draw_item(index)
    rect = item_rect(index)
    self.contents.clear_rect(rect)
    actor = @data[index]
    return if actor == nil
    ax = YEM::SKILL_FUSION::ACTOR_SPRITE_X_OFFSET
    ay = YEM::SKILL_FUSION::ACTOR_SPRITE_Y_OFFSET
    draw_actor_graphic(actor, rect.x+ax/2, rect.y+ay)
    text = actor.name
    self.contents.draw_text(rect.x+ax, rect.y, contents.width-ay, WLH, text)
  end
  
end # Window_Skill_Fusion_Actor

#===============================================================================
# Window_Skill_Fusion_List
#===============================================================================

class Window_Skill_Fusion_List < Window_Selectable
  
  #--------------------------------------------------------------------------
  # public instance variables
  #--------------------------------------------------------------------------
  attr_accessor :actor
  
  #--------------------------------------------------------------------------
  # initialize
  #--------------------------------------------------------------------------
  def initialize(dy)
    super(Graphics.width/2, 0, Graphics.width/2, Graphics.height-dy)
    self.active = false
    self.index = 0
    refresh($game_party.members[0])
  end
  
  #--------------------------------------------------------------------------
  # skill
  #--------------------------------------------------------------------------
  def skill; return @data[self.index]; end
  
  #--------------------------------------------------------------------------
  # refresh
  #--------------------------------------------------------------------------
  def refresh(actor)
    if @actor != actor
      self.index = 0
      @actor = actor
    end
    @pair_skill = $scene.pair_skill
    @data = []
    for skill in @actor.learned_skills
      @data.push(skill) if include?(skill)
    end
    self.index = -1 if @data == []
    @item_max = @data.size
    create_contents
    for i in 0...@item_max; draw_item(i); end
  end
  
  #--------------------------------------------------------------------------
  # include?
  #--------------------------------------------------------------------------
  def include?(skill)
    return false if skill.name == ""
    return true
  end
  
  #--------------------------------------------------------------------------
  # draw_item
  #--------------------------------------------------------------------------
  def draw_item(index)
    rect = item_rect(index)
    self.contents.clear_rect(rect)
    skill = @data[index]
    return if skill == nil
    enabled = enable?(skill)
    if skill == $scene.pair_skill
      draw_icon(skill.icon_index, rect.x, rect.y)
      self.contents.font.color = crisis_color
    else
      draw_icon(skill.icon_index, rect.x, rect.y, enabled)
      self.contents.font.color = normal_color
      self.contents.font.color.alpha = enabled ? 255 : 128
    end
    self.contents.draw_text(24, rect.y, contents.width-24, WLH, skill.name)
  end
  
  #--------------------------------------------------------------------------
  # enable?
  #--------------------------------------------------------------------------
  def enable?(skill)
    if @pair_skill != nil
      return false unless @pair_skill.fuse_result.include?(skill.id)
      child_skill = $data_skills[@pair_skill.fuse_result[skill.id]]
      return false if @actor.skill_learn?(child_skill.id)
      return false if child_skill.fusion_cost > $game_party.gold
    end
    return false if skill.fuse_result == {}
    return true
  end
  
  #--------------------------------------------------------------------------
  # set_index
  #--------------------------------------------------------------------------
  def set_index(skill)
    @data = []
    for obj in @actor.learned_skills
      @data.push(obj) if include?(obj)
    end
    self.index = @data.index(skill)
    refresh(@actor)
  end
  
  #--------------------------------------------------------------------------
  # update_help
  #--------------------------------------------------------------------------
  def update_help
    @help_window.set_text(skill == nil ? "" : skill.description)
  end
  
end # Window_Skill_Fusion_List

#===============================================================================
# Window_Skill_Fusion_Data
#===============================================================================

class Window_Skill_Fusion_Data < Window_Base
  
  #--------------------------------------------------------------------------
  # initialize
  #--------------------------------------------------------------------------
  def initialize(dy, skill_window)
    super(Graphics.width, 0, Graphics.width/2, Graphics.height-dy)
    @skill_window = skill_window
    @actor = $game_party.members[0]
    clear
  end
  
  #--------------------------------------------------------------------------
  # clear
  #--------------------------------------------------------------------------
  def clear
    @skill1 = nil
    @skill2 = nil
    @child = nil
    refresh
  end
  
  #--------------------------------------------------------------------------
  # apply_skill_selection
  #--------------------------------------------------------------------------
  def apply_skill_selection
    skill = @skill_window.skill
    #---
    if @skill1 != nil
      @skill2 = skill
      clear1 = false
      clear2 = true
    elsif @skill1 == nil
      @skill1 = skill
      clear1 = true
      clear2 = true
    end
    #---
    if @skill1.fuse_result[@skill2.id] == nil
      @child = nil
    else
      child_id = @skill1.fuse_result[@skill2.id]
      @child = $data_skills[child_id]
    end
    refresh
    @skill1 = nil if clear1
    @skill2 = nil if clear2
  end
  
  #--------------------------------------------------------------------------
  # set_skill_selection
  #--------------------------------------------------------------------------
  def set_skill_selection
    @skill1 = @skill_window.skill
    refresh
    @skill_window.refresh(@skill_window.actor)
  end
  
  #--------------------------------------------------------------------------
  # refresh
  #--------------------------------------------------------------------------
  def refresh
    self.contents.clear
    self.contents.font.color = system_color
    no_skill = YEM::SKILL_FUSION::VOCAB[:no_skill]
    text = YEM::SKILL_FUSION::VOCAB[:parent_skill]
    self.contents.draw_text(0, WLH*0, contents.width, WLH, text, 1)
    self.contents.font.color = normal_color
    #---
    if @skill1 != nil
      draw_icon(@skill1.icon_index, 0, WLH*1, true)
      self.contents.draw_text(24, WLH*1, contents.width, WLH, @skill1.name)
    else
      draw_icon(YEM::SKILL_FUSION::NO_SKILL_ICON, 0, WLH*1, false)
      self.contents.font.color.alpha = 128
      self.contents.draw_text(24, WLH*1, contents.width, WLH, no_skill)
    end
    #---
    if @skill2 != nil and @skill2 != @skill1
      draw_icon(@skill2.icon_index, 0, WLH*2, true)
      self.contents.draw_text(24, WLH*2, contents.width, WLH, @skill2.name)
    else
      draw_icon(YEM::SKILL_FUSION::NO_SKILL_ICON, 0, WLH*2, false)
      self.contents.font.color.alpha = 128
      self.contents.draw_text(24, WLH*2, contents.width, WLH, no_skill)
    end
    #---
    self.contents.font.color = system_color
    text = YEM::SKILL_FUSION::VOCAB[:child_skill]
    self.contents.draw_text(0, WLH*3, contents.width, WLH, text, 1)
    self.contents.font.color = normal_color
    #---
    if @child != nil
      draw_icon(@child.icon_index, 0, WLH*4, true)
      self.contents.draw_text(24, WLH*4, contents.width, WLH, @child.name)
    else
      draw_icon(YEM::SKILL_FUSION::NO_SKILL_ICON, 0, WLH*4, false)
      self.contents.font.color.alpha = 128
      self.contents.draw_text(24, WLH*4, contents.width, WLH, no_skill)
    end
    #---
    self.contents.font.color = system_color
    text = YEM::SKILL_FUSION::VOCAB[:fusion_cost]
    self.contents.draw_text(4, WLH*6, contents.width-8, WLH, text, 0)
    cost = @child != nil ? @child.fusion_cost : 0
    text = sprintf("%d%s", cost, Vocab.gold)
    if @child == nil
      self.contents.font.color = normal_color
      self.contents.font.color.alpha = 128
    elsif @child.fusion_cost > $game_party.gold
      self.contents.font.color = knockout_color
    else
      self.contents.font.color = normal_color
    end
    self.contents.draw_text(4, WLH*6, contents.width-8, WLH, text, 2)
  end
  
end # Window_Skill_Fusion_Data

#===============================================================================
# Scene_Skill_Fusion
#===============================================================================

class Scene_Skill_Fusion < Scene_Base
  
  #--------------------------------------------------------------------------
  # public instance variables
  #--------------------------------------------------------------------------
  attr_accessor :pair_skill
  
  #--------------------------------------------------------------------------
  # start
  #--------------------------------------------------------------------------
  def start
    super
    create_menu_background
    @help_window = Window_Help.new
    @gold_window = Window_Gold.new(Graphics.width-160, @help_window.height)
    create_command_window
    create_fusion_viewport
  end
  
  #--------------------------------------------------------------------------
  # terminate
  #--------------------------------------------------------------------------
  def terminate
    super
    dispose_menu_background
    @help_window.dispose
    @gold_window.dispose
    @command_window.dispose
    #---
    @actor_window.dispose
    @skill_window.dispose
    @data_window.dispose
    @fusion_viewport.dispose
  end
  
  #--------------------------------------------------------------------------
  # create_command_window
  #--------------------------------------------------------------------------
  def create_command_window
    array = []
    array.push(YEM::SKILL_FUSION::VOCAB[:fusion])
    array.push(YEM::SKILL_FUSION::VOCAB[:exit])
    @command_window = Window_Command.new(Graphics.width-160, array, array.size)
    @command_window.y = @help_window.height
    update_command_window
  end
  
  #--------------------------------------------------------------------------
  # create_fusion_viewport
  #--------------------------------------------------------------------------
  def create_fusion_viewport
    dy = @command_window.y + @command_window.height
    @fusion_viewport = Viewport.new(0, dy, Graphics.width, Graphics.height-dy)
    @actor_window = Window_Skill_Fusion_Actor.new(dy)
    @actor_window.viewport = @fusion_viewport
    @skill_window = Window_Skill_Fusion_List.new(dy)
    @skill_window.help_window = @help_window
    @skill_window.viewport = @fusion_viewport
    @data_window = Window_Skill_Fusion_Data.new(dy, @skill_window)
    @data_window.viewport = @fusion_viewport
    @fusion_viewport.ox = Graphics.width/2 if $game_party.members.size <= 1
  end
  
  #--------------------------------------------------------------------------
  # return_scene
  #--------------------------------------------------------------------------
  def return_scene
    $scene = Scene_Map.new
  end
  
  #--------------------------------------------------------------------------
  # update_basic
  #--------------------------------------------------------------------------
  def update_basic
    Graphics.update
    Input.update
    @fusion_viewport.update
  end
  
  #--------------------------------------------------------------------------
  # update
  #--------------------------------------------------------------------------
  def update
    super
    update_menu_background
    @fusion_viewport.update
    if @command_window.active
      update_command_window
    elsif @actor_window.active
      update_actor_window
    elsif @skill_window.active
      update_skill_window
    end
  end
  
  #--------------------------------------------------------------------------
  # update_command_window
  #--------------------------------------------------------------------------
  def update_command_window
    @command_window.update
    #---
    if @last_command_index != @command_window.index
      @last_command_index = @command_window.index
      case @last_command_index
      when 0; @help_window.set_text(YEM::SKILL_FUSION::VOCAB[:fusion_help])
      when 1; @help_window.set_text(YEM::SKILL_FUSION::VOCAB[:exit_help])
      end
    end
    #---
    if Input.trigger?(Input::B)
      Sound.play_cancel
      return_scene
    elsif Input.trigger?(Input::C)
      case @command_window.index
      when 0
        @command_window.active = false
        if $game_party.members.size == 0
          Sound.play_buzzer
        elsif $game_party.members.size > 1
          Sound.play_decision
          @actor_window.active = true
          @help_window.set_text(YEM::SKILL_FUSION::VOCAB[:select_help])
        else
          Sound.play_decision
          start_skill_fusion
        end
      when 1
        Sound.play_decision
        return_scene
      end
    end
  end
  
  #--------------------------------------------------------------------------
  # update_actor_window
  #--------------------------------------------------------------------------
  def update_actor_window
    @actor_window.update
    #---
    if @last_actor_index != @actor_window.index
      @last_actor_index = @actor_window.index
      @skill_window.refresh(@actor_window.actor)
      @data_window.clear
    end
    #---
    if Input.trigger?(Input::B)
      Sound.play_cancel
      @actor_window.active = false
      @command_window.active = true
      @help_window.set_text(YEM::SKILL_FUSION::VOCAB[:fusion_help])
    elsif Input.trigger?(Input::C)
      @actor = @actor_window.actor
      if @actor.learned_skills == []
        Sound.play_buzzer
      else
        Sound.play_decision
        start_skill_fusion
      end
    end
  end
  
  #--------------------------------------------------------------------------
  # start_skill_fusion
  #--------------------------------------------------------------------------
  def start_skill_fusion
    @actor_window.active = false
    @data_window.clear
    @data_window.apply_skill_selection
    loop do
      update_basic
      break if @fusion_viewport.ox == Graphics.width/2
      @fusion_viewport.ox = [@fusion_viewport.ox + 24, Graphics.width/2].min
    end
    @skill_window.active = true
    @pair_skill = nil
  end
  
  #--------------------------------------------------------------------------
  # end_skill_fusion
  #--------------------------------------------------------------------------
  def end_skill_fusion
    @skill_window.active = false
    loop do
      break if $game_party.members.size == 1
      update_basic
      break if @fusion_viewport.ox == 0
      @fusion_viewport.ox = [@fusion_viewport.ox - 24, 0].max
    end
    if $game_party.members.size > 1
      @actor_window.active = true
      @help_window.set_text(YEM::SKILL_FUSION::VOCAB[:select_help])
    else
      @command_window.active = true
      @help_window.set_text(YEM::SKILL_FUSION::VOCAB[:fusion_help])
    end
    @data_window.clear
    @pair_skill = nil
    @last_skill_index = nil
  end
  
  #--------------------------------------------------------------------------
  # update_skill_window
  #--------------------------------------------------------------------------
  def update_skill_window
    @skill_window.update
    #---
    if @last_skill_index != @skill_window.index
      @last_skill_index = @skill_window.index
      @data_window.apply_skill_selection
    end
    #---
    if Input.trigger?(Input::B)
      Sound.play_cancel
      if @pair_skill != nil
        @data_window.clear
        @pair_skill = nil
        @skill_window.refresh(@skill_window.actor)
        @data_window.apply_skill_selection
      else
        end_skill_fusion
      end
    elsif Input.trigger?(Input::C)
      skill = @skill_window.skill
      if @skill_window.enable?(skill) and @pair_skill == nil
        Sound.play_decision
        @pair_skill = skill
        @data_window.set_skill_selection
      elsif @skill_window.enable?(skill) and @pair_skill != nil
        Sound.play_shop
        perform_skill_fusion
      else
        Sound.play_buzzer
      end
    end
  end
  
  #--------------------------------------------------------------------------
  # perform_skill_fusion
  #--------------------------------------------------------------------------
  def perform_skill_fusion
    skill1 = @pair_skill
    skill2 = @skill_window.skill
    child = $data_skills[skill1.fuse_result[skill2.id]]
    actor = @actor_window.actor
    actor.learn_skill(child.id)
    $game_party.lose_gold(child.fusion_cost)
    @gold_window.refresh
    if YEM::SKILL_FUSION::FORGET_PARENT_SKILLS
      actor.forget_skill(skill1.id)
      actor.forget_skill(skill2.id)
    end
    @pair_skill = nil
    @skill_window.set_index(child)
    @last_skill_index = @skill_window.index
    @data_window.clear
    @data_window.apply_skill_selection
  end
  
end # Scene_Skill_Fusion

#===============================================================================
# 
# END OF FILE
# 
#===============================================================================