- Joined
- Apr 12, 2012
- Messages
- 2,642
- Reaction score
- 1,994
- First Language
- English
- Primarily Uses
- RMMV

Shop Unlocks version 1.03
Created by Trihan
Created by Trihan
Introduction
This is a script which allows you to use notetags in items, weapons and armours that will require you to sell a number of other items to a shop in order to unlock that item for purchase. It was originally written as a commission for Clyve back in 2012 but as it's been 5 years and he doesn't appear to be part of the community any more I decided to release it to the public under my Trilobytes brand.
Features
- Make unlockable items in the same vein as Etrian Odyssey.
- Adds categories to the buy window.
- Compatible with Yanfly's Ace Shop Options script.
Screenshots





How to Use
Install the script somewhere above main. If using Yanfly's Ace Shop Options, place this script below it.
To make an item unlockable, use the notetag "<require [IWA][item ID], [quantity]>" where [IWA] is either I (Item), W (Weapon) or A (Armor), item ID is the ID of the item type in the relevant database tab, and quantity is the number of that item required. For example, "<require I1, 5>" means that the item with that notetag will be unlocked after you sell 5 of item 1 to shops. This doesn't have to be all at once; the number you have sold throughout the game is tracked. Because Ace doesn't differentiate between shops, you can sell the items to any event that has a shop processing command (unless it's purchase-only). Note that any item that you want a shop to unlock must be included in its sellable goods list; it won't be there initially but will appear as soon as you meet its requirements. If you don't include an item in the shop goods list, it won't be unlocked in that shop.
Demo
No demo available at present.
Script
Code:
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
# ▼ Shop unlocks script
# Author: Trihan
# Version 1.03
# Release date: 07/05/2012
#
# Thanks to Prexus for helping with the data structure.
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=
#-------------------------------------------------------------------------------------------------
# ▼ UPDATES
#-------------------------------------------------------------------------------------------------
# # 12/06/2012. Fixed a bug where closing the shop and reopening it would result
# # in weapons/armour being removed from the list if the item or weapon with the
# # same ID had already been added.
# # 11/06/2012 - Fixed a compatibility bug with Enelvon/Seiryuki's sell-only
# # script and Ace Shop Options where Sellonly made my category confirm code
# # crash.
# # 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'
Shop = Struct.new(:map, :event, :sales, :unlocked_goods)
class RPG::BaseItem
attr_reader :sell_req
attr_accessor :quantity
#--------------------------------------------------------------------------
# ● Loads the notetags
#--------------------------------------------------------------------------
def load_item_notetags
@sell_req = []
@quantity = -1
@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
when CSHOP::REGEXP::SALE_QUANTITY
@quantity = $1.to_i
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
$data_shops = []
load_item_notetags
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
#--------------------------------------------------------------------------
# ● 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[:shops] = $data_shops
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
end
if contents[:shops]
$data_shops = contents[:shops]
else
$data_shops = []
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, shop_id)
super(x, y, window_width, height)
@shop_goods = shop_goods
@current_id = shop_id
@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
include = true
if item.quantity == 0
include = false
end
case @category
when :item
if !item.is_a?(RPG::Item) || item.key_item?
include = false
end
when :weapon
if !item.is_a?(RPG::Weapon)
include = false
end
when :armor
if !item.is_a?(RPG::Armor)
include = false
end
when :key_item
if !item.key_item?
include = false
end
end
for req in item.sell_req
if req[0] == 0
if $data_shops[@current_id].sales.item_sales[req[1]] < req[2]
include = false
end
elsif req[0] == 1
if $data_shops[@current_id].sales.weapon_sales[req[1]] < req[2]
include = false
end
elsif req[0] == 2
if $data_shops[@current_id].sales.armor_sales[req[1]] < req[2]
include = false
end
end
end
if include == true
@data.push(item)
@price[item] = goods[2] == 0 ? item.price : goods[3]
end
end
end
end
end
class Scene_Shop < Scene_MenuBase
attr_reader :added_items
attr_reader :shop_map
attr_reader :shop_event
attr_reader :current_id
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
create_unlocks_window
@shop_map = $game_map.map_id
@shop_event = $game_map.interpreter.event_id
if $data_shops.size == 0
$data_shops.push Shop.new(@shop_map, @shop_event, RPG::ShopSales.new, [])
else
$data_shops.each do |shop|
if shop.map == @shop_map && shop.event == @shop_event
break
else
$data_shops.push Shop.new(@shop_map, @shop_event, RPG::ShopSales.new, [])
end
end
end
@current_id = $data_shops.index{|shop| shop.map == @shop_map && shop.event == @shop_event}
shopsales_start
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, @current_id)
@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?([1, 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?([2, 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_shops[@current_id].sales.item_sales[@item.id] += number
$data_shopsales.item_sales[@item.id] += number
p $data_shops[@current_id].sales.item_sales[@item.id]
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])
$data_shops[@current_id].unlocked_goods.push($data_items[i])
#$data_items[i].unlocked = true
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])
#$data_weapons[i].unlocked = true
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])
#$data_armors[i].unlocked = true
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
SALE_QUANTITY = /<quantity: (\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 = []
for index in 0..$data_items.size
item_sales[index] = 0
end
for index in 0..$data_weapons.size
weapon_sales[index] = 0
end
for index in 0..$data_armors.size
armor_sales[index] = 0
end
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
FAQ
No questions at present.
Credit and Thanks
- Trihan
- Thanks to Prexus for helping with the shop data structure.
- Thanks to Clyve for making the original commission request.