#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#  ▼ Shop unlocks script
#  Author: Trihan
#  Version 1.01
#  Release date: 07/05/2012
#
#  Thanks to Prexus for helping with the data structure.
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=

#-------------------------------------------------------------------------------------------------
#  ▼ UPDATES
#-------------------------------------------------------------------------------------------------
# # 08/05/2012. Compatibility added for Yanfly's Ace Shop Options
# # 07/05/2012. Original release
#-------------------------------------------------------------------------------------------------
#  ▼ TERMS OF USAGE
#-------------------------------------------------------------------------------------------------
# #  You are free to adapt this work to suit your needs.
# #  You can use this work for commercial purposes if you like it.
# #  Credit is appreciated.
# #
# # For support:
# # rmrk.net
# # rpgmaker.net
#-------------------------------------------------------------------------------------------------
#  ▼ INTRODUCTION
#-------------------------------------------------------------------------------------------------
# # Enables shop items to be unlocked by selling a number of required items.
# # The unlocked items will be added to all shops once the requirements are met,
# # as the script doesn't differentiate between vendors. If you want unlocks to
# # be specified to the shop, some changes will be necessary. If you need help
# # doing this feel free to contact me.
# #
# # Because this can potentially result in shops having a lot of things you can
# # buy, I've also retooled the buy window so it uses categories like selling
# # does. At the moment this includes the "Key Items" category, though hopefully
# # I'll eventually disable or remove that for buying. I've also made it
# # compatible with Yanfly's Ace Shop Options script for those who like using
# # that.
#-------------------------------------------------------------------------------------------------
#  ▼ INSTRUCTIONS
#-------------------------------------------------------------------------------------------------
# # Only one notetag is needed on the unlockable item. You can have as many tags
# # as you wish.
# #
# # <require [IWA][item ID], [quantity]>
# #
# # Where the letter denotes [I]tem, [W]eapon or [A]rmour, ID is the number of
# # the item from the database, and quantity is how many need be sold to unlock.
# #
# # Example: <require I1, 5>
# # This means the item requires you to sell 5 of item ID 1 to unlock it for
# # purchase.
#-------------------------------------------------------------------------------------------------
#  ▼ COMPATIBILITY
#-------------------------------------------------------------------------------------------------
# # List of aliases and overwrites:
# #
# # RPG::BaseItem
# # sell_req (new attr method)
# # load_item_notetags (new method)
# # 
# # DataManager
# # load_database (alias)
# # load_item_notetags (new method)
# # init_shopsales_data (new method)
# # make_save_contents (overload)
# # extract_save_contents (alias)
# # 
# # Window_ShopCategory
# # make_command_list (overload)
# # 
# # Window_ShopBuy
# # initialize (overload)
# # category= (new method)
# # make_item_list (alias)
# # 
# # Scene_Shop
# # added_items (new attr method)
# # prepare (alias)
# # start (alias)
# # create_status_window (alias)
# # create_buy_window (alias)
# # create_unlocks_window (new method)
# # add_available_goods (new method)
# # do_sell (alias)
# # command_buy (overload)
# # command_sell (alias)
# # on_buy_cancel (overload)
# # on_category_ok (overload)
# # on_category_cancel (alias)
# # added_items (new method)
# # activate_unlocks_window (new method)
# # on_unlock_continue (new method)
# # on_unlock_cancel (new method)
# # item_type (new method)
# # end_number_input (overload)
# #
# # This script has been specifically designed to be fully compatible with
# # Yanfly's Ace Shop Options script. If there are any issues using this script
# # alongside it, please let me know so I can fix them.
#-------------------------------------------------------------------------------------------------
$imported = {} if $imported.nil?
$imported['cshop'] = true

puts 'Load: Shop script by Trihan'

class RPG::BaseItem
  attr_reader :sell_req
  #--------------------------------------------------------------------------
  # ● Loads the notetags
  #--------------------------------------------------------------------------
  def load_item_notetags
    @sell_req = []
		@note.split(/[\r\n]+/).each do |line|
			case line
			when CSHOP::REGEXP::ITEM_REQ
        case $1
        when "I"
          @sell_req.push([0, $2.to_i, $3.to_i])
        when "W"
          @sell_req.push([1, $2.to_i, $3.to_i])
        when "A"
          @sell_req.push([2, $2.to_i, $3.to_i])
        end
      end
		end
	end
end

module DataManager
    
  # Initialize Shop Sales data
  class <<self; alias shopsales_load_database load_database; end
  #--------------------------------------------------------------------------
  # ● Loads the database
  #--------------------------------------------------------------------------
  def self.load_database
    shopsales_load_database
    $data_shopsales = RPG::ShopSales.new
    load_item_notetags
    init_shopsales_data
  end
  
  #--------------------------------------------------------------------------
  # ● Loads the notetags
  #--------------------------------------------------------------------------
  def self.load_item_notetags
		groups = [$data_items, $data_weapons, $data_armors]
		for group in groups
			for obj in group
				next if obj.nil?
				obj.load_item_notetags
			end
		end
		puts "Read: Item Requirements Notetags"
	end
  
  #--------------------------------------------------------------------------
  # ● Initialises the count of items sold
  #--------------------------------------------------------------------------
  def self.init_shopsales_data
    for index in 0..$data_items.size
      $data_shopsales.item_sales[index] = 0
    end
    for index in 0..$data_weapons.size
      $data_shopsales.weapon_sales[index] = 0
    end
    for index in 0..$data_armors.size
      $data_shopsales.armor_sales[index] = 0
    end
  end
    
  #--------------------------------------------------------------------------
  # ● Dumps shop sales data into save file
  #--------------------------------------------------------------------------
  def self.make_save_contents
    contents = {}
    contents[:system]        = $game_system
    contents[:timer]         = $game_timer
    contents[:message]       = $game_message
    contents[:switches]      = $game_switches
    contents[:variables]     = $game_variables
    contents[:self_switches] = $game_self_switches
    contents[:actors]        = $game_actors
    contents[:party]         = $game_party
    contents[:troop]         = $game_troop
    contents[:map]           = $game_map
    contents[:player]        = $game_player
    contents[:shopsales]     = $data_shopsales
    contents
  end
    
  class <<self; alias shopsales_extract_save_contents extract_save_contents; end
  #--------------------------------------------------------------------------
  # ● Retrieves shop sales data when loading a game
  #--------------------------------------------------------------------------
  def self.extract_save_contents(contents)
    shopsales_extract_save_contents(contents)
    if contents[:shopsales]
      $data_shopsales = contents[:shopsales]
    else
      $data_shopsales = RPG::ShopSales.new
      init_shopsales_data
    end
  end
end

class Window_ShopCategory < Window_Command
  #--------------------------------------------------------------------------
  # ● Removes "key items" from the category list
  #--------------------------------------------------------------------------
  def make_command_list
    add_command(Vocab::item,     :item)
    add_command(Vocab::weapon,   :weapon)
    add_command(Vocab::armor,    :armor)
  end
end

class Window_ShopBuy < Window_Selectable
  #--------------------------------------------------------------------------
  # ● Initialises the buy window with a category of none
  #--------------------------------------------------------------------------
  def initialize(x, y, height, shop_goods)
    super(x, y, window_width, height)
    @shop_goods = shop_goods
    @money = 0
    @category = :none
    refresh
    select(0)
  end
  
  #--------------------------------------------------------------------------
  # ● Adds a category setting method for the buy window
  #--------------------------------------------------------------------------
  def category=(category)
    return if @category == category
    @category = category
    refresh
  end
  
  alias :shopsales_make_item_list :make_item_list
  #--------------------------------------------------------------------------
  # ● Populates the buy window based on sales criteria
  #--------------------------------------------------------------------------
  def make_item_list
    @data = []
    @price = {}
    @shop_goods.each do |goods|
      case goods[0]
        when 0;  item = $data_items[goods[1]]
        when 1;  item = $data_weapons[goods[1]]
        when 2;  item = $data_armors[goods[1]]
      end
      if item
        case @category
        when :item
          if item.is_a?(RPG::Item) && !item.key_item?
            @data.push(item)
            @price[item] = goods[2] == 0 ? item.price : goods[3]
          end
        when :weapon
          if item.is_a?(RPG::Weapon)
            @data.push(item) if item.is_a?(RPG::Weapon)
            @price[item] = goods[2] == 0 ? item.price : goods[3]
          end
        when :armor
          if item.is_a?(RPG::Armor)
            @data.push(item) if item.is_a?(RPG::Armor)
            @price[item] = goods[2] == 0 ? item.price : goods[3]
          end
        end
      end
    end
  end
end

class Scene_Shop < Scene_MenuBase
  
  attr_reader :added_items
    
  alias :shopsales_prepare :prepare
  #--------------------------------------------------------------------------
  # ● Overload to add unlocked items to the buy list
  #--------------------------------------------------------------------------
  def prepare(goods, purchase_only)
    shopsales_prepare(goods, purchase_only)
    add_available_goods
  end
  
  alias :shopsales_start :start
  #--------------------------------------------------------------------------
  # ● Overload to create the window that shows unlocked items
  #--------------------------------------------------------------------------
  def start
    shopsales_start
    create_unlocks_window
  end
  
  alias :shopsales_create_status_window :create_status_window
  #--------------------------------------------------------------------------
  # ● Alias to reposition the status window
  #--------------------------------------------------------------------------
  def create_status_window
    if $imported['YEA-ShopOptions']
      shopsales_create_status_window
    else
      wx = @number_window.width
      wy = @dummy_window.y + 48
      ww = Graphics.width - wx
      wh = Graphics.height - wy
      @status_window = Window_ShopStatus.new(wx, wy, ww, wh)
      @status_window.viewport = @viewport
      @status_window.hide
    end
  end
  
  alias :shopsales_create_buy_window :create_buy_window
  #--------------------------------------------------------------------------
  # ● Alias to reposition the buy window
  #--------------------------------------------------------------------------
  def create_buy_window
    if $imported['YEA-ShopOptions']
      shopsales_create_buy_window
    else
      wy = @dummy_window.y + 48
      wh = Graphics.height - wy
      @buy_window = Window_ShopBuy.new(0, wy, wh, @goods)
      @buy_window.viewport = @viewport
      @buy_window.help_window = @help_window
      @buy_window.status_window = @status_window
      @buy_window.hide
      @buy_window.set_handler(:ok,     method(:on_buy_ok))
      @buy_window.set_handler(:cancel, method(:on_buy_cancel))
    end
  end
  
  #--------------------------------------------------------------------------
  # ● Creates the window that shows unlocked items
  #--------------------------------------------------------------------------
  def create_unlocks_window
    @unlock_window = Window_Unlocks.new
    @unlock_window.viewport = @viewport
    @unlock_window.x = (Graphics.width - @unlock_window.width) / 2
    @unlock_window.y = (Graphics.height - @unlock_window.height) / 2
    @unlock_window.z = 255
    @unlock_window.set_handler(:ok,        method(:on_unlock_continue))
    @unlock_window.set_handler(:cancel,    method(:on_unlock_cancel))
    @unlock_window.hide
  end
    
  #--------------------------------------------------------------------------
  # ● Adds unlocked items to the goods list
  #--------------------------------------------------------------------------
  def add_available_goods
    for i in 1...$data_items.size
      if !$data_items[i].sell_req.empty?
        include = true
        for req in $data_items[i].sell_req
          if req[0] == 0
            if $data_shopsales.item_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 1
            if $data_shopsales.weapon_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 2
            if $data_shopsales.armor_sales[req[1]] < req[2]
              include = false
            end
          end
        end
        @goods.push([0, i, 0, 0, false]) if include == true && !@goods.include?([0, i, 0, 0, false])
      end
    end
    for i in 1...$data_weapons.size
      if !$data_weapons[i].sell_req.empty?
        include = true
        for req in $data_weapons[i].sell_req
          if req[0] == 0
            if $data_shopsales.item_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 1
            if $data_shopsales.weapon_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 2
            if $data_shopsales.armor_sales[req[1]] < req[2]
              include = false
            end
          end
        end
        @goods.push([1, i, 0, 0, false]) if include == true && !@goods.include?([0, i, 0, 0, false])
      end
    end
    for i in 1...$data_armors.size
      if !$data_armors[i].sell_req.empty?
        include = true
        for req in $data_armors[i].sell_req
          if req[0] == 0
            if $data_shopsales.item_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 1
            if $data_shopsales.weapon_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 2
            if $data_shopsales.armor_sales[req[1]] < req[2]
              include = false
            end
          end
        end
        @goods.push([2, i, 0, 0, false]) if include == true && !@goods.include?([0, i, 0, 0, false])
      end
    end
  end
    
  alias :shopsales_do_sell :do_sell
  #--------------------------------------------------------------------------
  # ● Alias to track sold items and immediately unlock items for which the
  # criteria has been met
  #--------------------------------------------------------------------------
  def do_sell(number)
    shopsales_do_sell(number)
    @added_items = []
    case item_type
    when 0
      $data_shopsales.item_sales[@item.id] += number
    when 1
      $data_shopsales.weapon_sales[@item.id] += number
    when 2
      $data_shopsales.armor_sales[@item.id] += number
    end
    for i in 1...$data_items.size
      if !$data_items[i].sell_req.empty?
        include = true
        for req in $data_items[i].sell_req
          if req[0] == 0
            if $data_shopsales.item_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 1
            if $data_shopsales.weapon_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 2
            if $data_shopsales.armor_sales[req[1]] < req[2]
              include = false
            end
          end
        end
        if include == true && !@goods.include?([0, i, 0, 0, false])
          @goods.push([0, i, 0, 0, false])
          @added_items.push($data_items[i])
        end
      end
    end
    for i in 1...$data_weapons.size
      if !$data_weapons[i].sell_req.empty?
        include = true
        for req in $data_weapons[i].sell_req
          if req[0] == 0
            if $data_shopsales.item_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 1
            if $data_shopsales.weapon_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 2
            if $data_shopsales.armor_sales[req[1]] < req[2]
              include = false
            end
          end
        end
        if include == true && !@goods.include?([1, i, 0, 0, false])
          @goods.push([1, i, 0, 0, false])
          @added_items.push($data_weapons[i])
        end
      end
    end
    for i in 1...$data_armors.size
      if !$data_armors[i].sell_req.empty?
        include = true
        for req in $data_armors[i].sell_req
          if req[0] == 0
            if $data_shopsales.item_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 1
            if $data_shopsales.weapon_sales[req[1]] < req[2]
              include = false
            end
          elsif req[0] == 2
            if $data_shopsales.armor_sales[req[1]] < req[2]
              include = false
            end
          end
        end
        if include == true && !@goods.include?([2, i, 0, 0, false])
          @goods.push([2, i, 0, 0, false])
          @added_items.push($data_armors[i])
        end
      end
    end
    if !@added_items.empty?
      activate_unlocks_window
    end
  end
  
  #--------------------------------------------------------------------------
  # ● Overload to add categories to the buy command
  #--------------------------------------------------------------------------
  def command_buy
    @dummy_window.hide
    @buy_window.unselect
    @buy_window.refresh
    @category_window.item_window = @buy_window
    if $imported['YEA-ShopOptions']
      @category_window.activate
      @category_window.x = 0
      @command_window.x = Graphics.width
      @buy_window.x = 0
      @sell_window.x = Graphics.width
      @data_window.item_window = @buy_window
    else
      @category_window.show.activate
      @buy_window.show
      @status_window.show
      @status_window.y = @dummy_window.y + 48
      @status_window.height = Graphics.height - @dummy_window.y - 48
    end
  end
  
  alias :shopsales_command_sell :command_sell
  #--------------------------------------------------------------------------
  # ● Alias to reposition windows when sell command is selected
  #--------------------------------------------------------------------------
  def command_sell
    @category_window.item_window = @sell_window
    if $imported['YEA-ShopOptions']
      shopsales_command_sell
    else
      @dummy_window.hide
      @sell_window.unselect
      @sell_window.refresh
      @category_window.show.activate
      @sell_window.show
      @status_window.y = @dummy_window.y
      @status_window.height = Graphics.height - @dummy_window.y
    end
  end
  
  #--------------------------------------------------------------------------
  # ● Overload to smoothly transition buy cancellation back to category list
  #--------------------------------------------------------------------------
  def on_buy_cancel
    @buy_window.unselect
    @category_window.activate
    @status_window.item = nil
    @help_window.clear
  end
  
  #--------------------------------------------------------------------------
  # ● Overload to transition smoothly to buy/sell after category confirm
  #--------------------------------------------------------------------------
  def on_category_ok
    if @command_window.index == 0
      activate_buy_window
      @buy_window.select(0)
    elsif @command_window.index == 1
      activate_sell_window
      unless $imported['YEA-ShopOptions']
        @status_window.y = @dummy_window.y
        @status_window.height = Graphics.height - @dummy_window.y
      end
      @sell_window.select(0)
    end
  end
  
  alias :shopsales_on_category_cancel :on_category_cancel
  #--------------------------------------------------------------------------
  # ● Alias to transition smoothly to commands after category cancel
  #--------------------------------------------------------------------------
  def on_category_cancel
    if $imported['YEA-ShopOptions']
      shopsales_on_category_cancel
    else
      @command_window.activate
      @dummy_window.show
      if @command_window.index == 0
        @buy_window.hide
        @status_window.hide
      elsif @command_window.index == 1
        @sell_window.hide
      end
      @category_window.hide
    end
  end
  
  #--------------------------------------------------------------------------
  # ● Returns the number of items that have been unlocked
  #--------------------------------------------------------------------------
  def added_items
    @added_items.size
  end
  
  #--------------------------------------------------------------------------
  # ● Activates the unlock window
  #--------------------------------------------------------------------------
  def activate_unlocks_window
    @unlock_window.display_items(@added_items)
    @unlock_window.show.activate
    @category_window.show
    @sell_window.show
    @status_window.hide unless $imported["YEA-ShopOptions"]
  end
  
  #--------------------------------------------------------------------------
  # ● Handler method for pressing the "ok" key when unlock window is active
  #--------------------------------------------------------------------------
  def on_unlock_continue
    if added_items > 10
      for i in 0..9
        @added_items.shift
      end
      activate_unlocks_window
    else
      @unlock_window.hide
      activate_sell_window
    end
  end
  
  #--------------------------------------------------------------------------
  # ● Handler method for pressing the "cancel" key when unlock window is active
  #--------------------------------------------------------------------------
  def on_unlock_cancel
    @unlock_window.hide
    activate_sell_window
  end
  
  #--------------------------------------------------------------------------
  # ● Checks item type of @item
  #--------------------------------------------------------------------------
  def item_type
    if @item.is_a?(RPG::Item)
      return 0
    elsif @item.is_a?(RPG::Weapon)
      return 1
    elsif @item.is_a?(RPG::Armor)
      return 2
    end
  end
  
  #--------------------------------------------------------------------------
  # ● Overload to return to the correct command window after number input ends
  #--------------------------------------------------------------------------
  def end_number_input
    @number_window.hide
    case @command_window.current_symbol
    when :buy
      activate_buy_window
    when :sell
      activate_sell_window unless @unlock_window.active
    end
  end
end

module CSHOP
  
  module REGEXP
    ITEM_REQ = /<require ([IWA])(\d+), (\d+)>/i
  end
  
end

#--------------------------------------------------------------------------
# ● New class that tracks how many of each item has been sold in a shop
#--------------------------------------------------------------------------
class RPG::ShopSales
  #------------------------
  attr_accessor :item_sales
  attr_accessor :weapon_sales
  attr_accessor :armor_sales
  #------------------------
  def initialize
    @item_sales   = []
    @weapon_sales = []
    @armor_sales  = []
  end
end

#--------------------------------------------------------------------------
# ● New class that creates a window showing unlocked items
#--------------------------------------------------------------------------
class Window_Unlocks < Window_Selectable
  def initialize
    super(0, 0, 240, fitting_height(11))
    self.back_opacity = 255
    @show_count = 0
  end
  
  def display_items(items)
    contents.clear
    draw_text(0, 0, contents_width, line_height, "ITEMS UNLOCKED", 1)
    items.each_with_index do |item, i|
      draw_icon(item.icon_index, 0, line_height * (i + 1), true)
      draw_text(0, line_height * (i + 1), contents_width, line_height, item.name, 1)
    end
  end
end