=begin
#===============================================================================
 Title: Shop Manager
 Author: Tsukihime
 Date: Mar 29, 2013
--------------------------------------------------------------------------------
 ** Change log
 1.5 Mar 29
   - added simple shop refreshing on page change. Will need a better solution
     since this does not track the state of a shop on a different page
 1.4 Mar 13
   - fixed bug where common event shops were not handled appropriately when
     called through effects
 1.3 Mar 1
   - Game_ShopGood now determines whether it should be included or enabled
   - Two shop options implemented: hidden, disabled
 1.2 Feb 25
   - fixed issue where common event shops were not handled correctly
   - exposed Shop Good variables as readers
 1.1
   - ShopManager handles shop scene changing depending on the type of shop
   - added handling for different "types" of shops
   - all goods in a shop can be accessed via shop.shop_goods
   - reference to shop added to shop scene
   - added support for adding and removing goods from a shop
 1.0 Feb 22, 2013
   - Initial Release
--------------------------------------------------------------------------------   
 ** Terms of Use
 * Free to use in commercial/non-commercial projects
 * No real support. The script is provided as-is
 * Will do bug fixes, but no compatibility patches
 * Features may be requested but no guarantees, especially if it is non-trivial
 * Credits to Tsukihime in your project
 * Preserve this header
--------------------------------------------------------------------------------
 ** Description
 
 This script serves as a base for all shop-related scripts.
 
 This script provides functionality for remember a shop's "state".
 Each shop is uniquely identified by a shop ID, and if you visit the same
 shop repeatedly, you should see the same settings.
 
 For example, if a shop holds 10 potions, and you bought 5, then you can
 expect that the next time you visit the shop, it will only have 5 potions
 remaining.
 
 Of course, you will need extra plugins to have that kind of functionality. 
 Several new classes have been defined for working with shops, and in
 particular, shop goods.
 
 In summary:
   -more control over your shops
   -simple API for developers to write shop-related scripts
 
--------------------------------------------------------------------------------
 ** Usage
 
--------------------------------------------------------------------------------
 ** Developers
 
 Here are some specifications that I have written.
 
 Have a look through each class to see what is available for use.
 If you have any suggestions that will improve the base script (ie: this), 
 I will consider adding them.
 
 -- Shop Manager --
 
 This module serves are the interface for all shop-related queries. It handles
 how shops are stored for you so that it is easy to properly obtain a reference
 to a shop.
 
 You should use the Shop Manager whenever possible.
 
 -- Game Shop --
 
 This script treats a shop as a concrete object. A shop is created the first
 time it is accessed, and will be stored with the game for the remainder of
 the game.
 
 A very basic shop is provided, which manages what items are available for sale.
 All shops are stored in a global Game_Shops hash in $game_shops
 
 -- Shop Type --
 
 On top of the basic Game_Shop class, you can define your own shops and
 associate them with custom scenes specific to those shops.
 
 The Shop Manager only requires you to be consistent with your shop name.
 For example, if your shop type is "CraftShop", then you must define a
 
   Game_CraftShop  - the shop object that the shop will be instantiated with
   Scene_CraftShop - the scene that will be called when visiting a CraftShop
   
 Users will set a @shop_type attribute in Game_Interpreter to determine
 what type of shop should be created
 
 -- Managing shops --
 
 This script assumes shops are only called through events or common events.
 Troop events are not supported.
 
 A shop is identified by a map ID and an event ID.
 Shops that are called via common events will have a map ID of 0.
 
 In order to determine whether a normal event or a common event called the
 shop, the "depth" of the interpreter is used.
 
    When depth = 0, then it is a normal event
    When depth > 0, then it is a common event
    
 The shop processing should be handled appropriately depending on the depth
 
 This script assumes that an event is triggered through "normal" interaction;
 that is, you can only interact with events within a map. Any events that should
 be treated as the same event should be done as a common event call.
 
 --- Shop Goods ---
 
 Rather than storing all goods as a simple array of values, this script
 provides a Game_ShopGood class. You can use this to store any additional
 information that you want.
 
 All shop related scenes and windows MUST provide support for handling shop
 goods. While backwards compatibility is provided for the default scripts,
 additional methods have been defined to allow you to retrieve the currently
 selected shop good.
 
 --- Shop Options ---
 
 Since there isn't actually a way to setup individual shop goods, external
 approaches must be used. There is not much specification here yet, so it
 is up to you how you wish to populate your ShopGood objects. I have provided
 a "setup_goods" method in Game_Interpreter that you can alias.
 
--------------------------------------------------------------------------------
 ** Compatibility
 
 This script changes the following from the default scripts
 
   DataManager
     aliased  - create_game_objects   
     aliased  - make_save_contents
     aliased  - extract_save_contents
   Game_Interpreter
     replaced - command_302
   Window_ShopBuy
     replaced - prepare
   Scene_Shop
     replaced - make_item_list 
#===============================================================================
=end
$imported = {} if $imported.nil?
$imported["TH_ShopManager"] = 1.5
#===============================================================================
# ** Rest of the Script
#=============================================================================== 
 
#-------------------------------------------------------------------------------
# Main shop manager that acts as the interface between shops and other objects
# The main role of the ShopManager is to essentially manage any shop objects
# that exist in the game. In particular, it provides all of the methods for
# creating, retrieving, and deleting shops.
#-------------------------------------------------------------------------------
module ShopManager
 
  #-----------------------------------------------------------------------------
  # Return a reference to a specific shop
  #-----------------------------------------------------------------------------
  def self.get_shop(map_id, event_id)
    return $game_shops[map_id, event_id]
  end
 
  #-----------------------------------------------------------------------------
  # Indicate that a shop needs to be refreshed
  #-----------------------------------------------------------------------------
  def self.refresh_shop(map_id, event_id)
    shop = get_shop(map_id, event_id)
    shop.need_refresh = true if shop
  end
 
  #-----------------------------------------------------------------------------
  # Setup shop if first time visiting
  #-----------------------------------------------------------------------------
  def self.setup_shop(map_id, event_id, goods, purchase_only, shop_type)
    shop = get_shop(map_id, event_id)
    return shop if shop && !shop.need_refresh
    shop = shop_class(shop_type).new(goods, purchase_only)
    $game_shops[map_id, event_id] = shop
    return shop
  end
 
  #-----------------------------------------------------------------------------
  # Return the appropriate shop class given the shop type
  #-----------------------------------------------------------------------------
  def self.shop_class(shop_type)
    shop_type = "Game_" + shop_type.to_s
    return Object.const_get(shop_type.to_sym)
  end
 
  #-----------------------------------------------------------------------------
  # Return the scene associated with this shop.
  # TO DO
  #-----------------------------------------------------------------------------
  def self.shop_scene(shop)
    shop_scene = "Scene_" + shop.class.name.gsub("Game_", "")
    return Object.const_get(shop_scene.to_sym)
  end
 
  #-----------------------------------------------------------------------------
  # Invokes SceneManager.call on the appropriate scene
  #-----------------------------------------------------------------------------
  def self.call_scene(shop)
 
    SceneManager.call(shop_scene(shop))
    SceneManager.scene.prepare(shop)
  end
 
  #-----------------------------------------------------------------------------
  # Invokes SceneManager.goto on the appropriate scene
  #-----------------------------------------------------------------------------
  def self.goto_scene(shop)
    SceneManager.goto(shop_scene(shop))
    SceneManager.scene.prepare(shop)
  end
 
  #-----------------------------------------------------------------------------
  # Returns a good ID, given a shop and an item. If the item is already in
  # the shop, it will return that good's ID. Otherwise, it will return a new ID
  #-----------------------------------------------------------------------------
  def self.get_good_id(shop, item)
    good = shop.shop_goods.detect {|good| good.item == item}
    return good.nil? ? shop.shop_goods.size + 1 : good.id
  end
 
  #-----------------------------------------------------------------------------
  # Returns a good, given a shop and an item. If the shop already has that good
  # just return it. Otherwise, make a new good. If the price is negative, then
  # the price is the default price. Otherwise, it is the specified price.
  #-----------------------------------------------------------------------------
  def self.get_good(shop, item, price=-1)
    good = shop.shop_goods.detect {|good| good.item == item}
    return good if good
    good_id = shop.shop_goods.size + 1
    type = item_type(item)
    if price < 0
      price_type = price = 0
    else
      price_type = 1
    end
    return Game_ShopGood.new(good_id, type, item.id, price_type, price)
  end
 
  #-----------------------------------------------------------------------------
  # Returns the type of an item.
  #-----------------------------------------------------------------------------
  def self.item_type(item)
    return 0 if item.is_a?(RPG::Item)
    return 1 if item.is_a?(RPG::Weapon)
    return 2 if item.is_a?(RPG::Armor)
    return -1
  end
end
 
#-------------------------------------------------------------------------------
# Shops are stored in a global variable `$game_shops`. This is dumped and
# loaded appropriately.
#-------------------------------------------------------------------------------
module DataManager
 
  class << self
    alias :th_shop_manager_create_game_objects :create_game_objects
    alias :th_shop_manager_make_save_contents :make_save_contents 
    alias :th_shop_manager_extract_save_contents :extract_save_contents
  end
 
  def self.create_game_objects
    th_shop_manager_create_game_objects
    $game_shops = Game_Shops.new
  end
 
  def self.make_save_contents
    contents = th_shop_manager_make_save_contents
    contents[:shops] = $game_shops
    contents
  end
 
  #-----------------------------------------------------------------------------
  # Load shop data
  #-----------------------------------------------------------------------------
  def self.extract_save_contents(contents)
    th_shop_manager_extract_save_contents(contents)
    $game_shops = contents[:shops]
  end
end
 
class Game_Temp
 
  # even if we're not actually calling a shop, it shouldn't affect anything
  # because we are always setting this at each common event call by an event
  attr_accessor :shop_common_event_id
 
  alias :th_shop_manager_reserve_common_event :reserve_common_event
  def reserve_common_event(common_event_id)
    th_shop_manager_reserve_common_event(common_event_id)
    @shop_common_event_id = common_event_id
  end
end
 
class Game_Event < Game_Character
 
  alias :th_shop_manager_setup_page :setup_page
  def setup_page(page)
    th_shop_manager_setup_page(page)
    ShopManager.refresh_shop(@map_id, @id)
  end
end
 
class Game_Interpreter
 
  alias :th_shop_manager_clear :clear
  def clear
    th_shop_manager_clear
    clear_shop_options
    @shop_type = nil
  end
 
  #-----------------------------------------------------------------------------
  # New.
  #-----------------------------------------------------------------------------
  def clear_shop_options
    @shop_options = {}
    @shop_options[:hidden] = {}
    @shop_options[:disabled] = {}
  end
 
  #-----------------------------------------------------------------------------
  # New. We are in a common event only if the shop common event ID is set.
  #-----------------------------------------------------------------------------
  def shop_map_id
    $game_temp.shop_common_event_id ? 0 : @map_id
  end
 
  def shop_event_id
    $game_temp.shop_common_event_id ? $game_temp.shop_common_event_id : @event_id
  end
 
  #--------------------------------------------------------------------------
  # Set the shop's common event ID
  #--------------------------------------------------------------------------
  alias :th_shop_manager_command_117 :command_117
  def command_117
    $game_temp.shop_common_event_id = @params[0]
    th_shop_manager_command_117
  end
 
  #-----------------------------------------------------------------------------
  # Replaced. A shop is setup only once, and is retrieved whenever it is
  # called in the future. This assumes the shop is invoked through "normal"
  # event interactions.
  #-----------------------------------------------------------------------------
  def command_302
    return if $game_party.in_battle
    shop_type = @shop_type || :Shop
    good_id = 1
    goods = []
 
    # setup goods
    good = make_good(@params[0..-1], good_id) # last param is for the shop
    goods.push(good)
    good_id +=1
 
    while next_event_code == 605
     @Index += 1
      good = make_good(@list[@index].parameters, good_id)
      goods.push(good)
      good_id +=1
    end
    # Setup shop if needed
    shop = ShopManager.setup_shop(shop_map_id, shop_event_id, goods, @params[4], shop_type)
 
    # prepare the shop with a reference to the actual shop
    ShopManager.call_scene(shop)
    Fiber.yield
 
    # clear out the shop common event ID.
    $game_temp.shop_common_event_id = nil
  end
 
  #-----------------------------------------------------------------------------
  # New. This is where the goods are setup.
  #-----------------------------------------------------------------------------
  def make_good(goods_array, good_id)
    item_type, item_id, price_type, price = goods_array
    good = Game_ShopGood.new(good_id, item_type, item_id, price_type, price)
 
    # additional setup
    setup_good(good, good_id)
    return good
  end
 
  #-----------------------------------------------------------------------------
  # New. You can do more things with your good here
  #-----------------------------------------------------------------------------
  def setup_good(good, good_id)
    setup_hidden_option(good, good_id)
    setup_disabled_option(good, good_id)
  end
 
  #-----------------------------------------------------------------------------
  # New. Shop options
  #-----------------------------------------------------------------------------
  def hide_good(good_id, condition)
    @shop_options[:hidden][good_id] = condition
  end
 
  def disable_good(good_id, condition)
    @shop_options[:disabled][good_id] = condition
  end
 
  def setup_hidden_option(good, good_id)
    return unless @shop_options[:hidden][good_id]
    good.hidden_condition = @shop_options[:hidden][good_id]
  end
 
  def setup_disabled_option(good, good_id)
    return unless @shop_options[:disabled][good_id]
    good.disable_condition = @shop_options[:disabled][good_id]
  end
end
 
#-------------------------------------------------------------------------------
# A shop good.
# This is a wrapper around a raw item (RPG::Item, RPG::Weapon, etc).
#-------------------------------------------------------------------------------
 
class Game_ShopGood
 
  attr_reader :id         # ID of this good
  attr_reader :item_type
  attr_reader :item_id
  attr_reader :price_type
  attr_accessor :hidden_condition
  attr_accessor :disable_condition
 
  def initialize(id, item_type, item_id, price_type, price)
    @id = id
    @item_type = item_type
    @item_id = item_id
    @price_type = price_type
    @price = price
    @hidden_condition = ""
    @disable_condition = ""
  end
 
  def include?
    return false if eval(@hidden_condition)
    return true
  end
 
  def enable?
    return false if eval(@disable_condition)
    return true
  end
 
  def item
    return $data_items[@item_id] if @item_type == 0
    return $data_weapons[@item_id] if @item_type == 1
    return $data_armors[@item_id] if @item_type == 2
  end
 
  def price
    return item.price if @price_type == 0
    return @price
  end
end
 
#-------------------------------------------------------------------------------
# A shop object. Stores information about the shop such as its inventory
# and other shop-related data
#-------------------------------------------------------------------------------
class Game_Shop
  attr_reader :purchase_only
  attr_reader :shop_goods      # all goods that this shop has. 
  attr_accessor :need_refresh  # shop needs to be refreshed
 
  def initialize(goods, purchase_only=false)
    @shop_goods = goods
    @purchase_only = purchase_only
    @need_refresh = false
  end
  #-----------------------------------------------------------------------------
  # Returns true if the goods should be included
  #-----------------------------------------------------------------------------
  def include?(index)
    true
  end
 
  #-----------------------------------------------------------------------------
  # Return a set of goods for sale
  #-----------------------------------------------------------------------------
  def goods
    @shop_goods
  end
 
  #-----------------------------------------------------------------------------
  # Add a new good to the shop
  #-----------------------------------------------------------------------------
  def add_good(good)
    @shop_goods.push(good) unless @shop_goods.include?(good)
  end
 
  #-----------------------------------------------------------------------------
  # Remove the specified good from the shop
  #-----------------------------------------------------------------------------
  def remove_good(good_id)
    @shop_goods.delete_at(good_id - 1)
  end
end
 
#-------------------------------------------------------------------------------
# A wrapper containing all shops in the game.
#-------------------------------------------------------------------------------
class Game_Shops
 
  #-----------------------------------------------------------------------------
  # Initializes a hash of game shops. Each key is a map ID, pointing to another
  # hash whose keys are event ID's and values are Game_Shop objects.
  #-----------------------------------------------------------------------------
  def initialize
    @data = {}
  end
 
  def [](i, j)
    @data[i] ||= {}
    @data[i][j]
  end
 
  def []=(i, j, shop)
    @data[i] ||= {}
    @data[i][j] = shop
  end
end
 
#-------------------------------------------------------------------------------
# The shop scene now works with the Shop and ShopGood objects
#-------------------------------------------------------------------------------
class Scene_Shop < Scene_MenuBase
  #--------------------------------------------------------------------------
  # Replaced. The scene now takes a Game_Shop object
  #--------------------------------------------------------------------------
  def prepare(shop)
    @shop = shop
    @goods = shop.goods
    @purchase_only = shop.purchase_only
  end
 
  alias :th_shop_manager_on_buy_ok :on_buy_ok
  def on_buy_ok
    @selected_good = @buy_window.current_good
    th_shop_manager_on_buy_ok
  end
end
 
#-------------------------------------------------------------------------------
# @shop_goods is now an array of Game_ShopGoods
#-------------------------------------------------------------------------------
class Window_ShopBuy < Window_Selectable
 
  #--------------------------------------------------------------------------
  # New. Returns true if the good should be included in the shop inventory
  #--------------------------------------------------------------------------
  def include?(shopGood)
    shopGood.include?
  end
 
  alias :th_shop_manager_enable? :enable?
  def enable?(item)
    return false unless @goods[item].enable?
    th_shop_manager_enable?(item)
  end
 
  #--------------------------------------------------------------------------
  # New. Get the currently selected good
  #--------------------------------------------------------------------------
  def current_good
    @goods[item]
  end
 
  #-----------------------------------------------------------------------------
  # Replaced. ShopGood takes care of most information. The data still contains
  # a list of RPG items for now since I don't want to change them to goods yet
  # A separate list of goods for sale is used for 1-1 correspondence
  #-----------------------------------------------------------------------------
  def make_item_list
    @data = []
    @goods = {}
    @price = {}
    @shop_goods.each do |shopGood|
      next unless include?(shopGood)
      item = shopGood.item
      @data.push(item)
      @goods[item] = shopGood
      @price[item] = shopGood.price
    end
  end
end