Professional Documents
Culture Documents
#===============================================================================
Title: Instance Items
Author: Hime
Date: Jan 29, 2015
URL: http://himeworks.com/2014/01/07/instance-items/
-------------------------------------------------------------------------------** Change log
Jan 29, 2015
- requests for weapons, armors, or items will return a clone of the original
arrays in case you try to operate on it
Dec 1, 2014
- moved instance database reloading into "load_game_without_rescue" instead
of "load_game"
Aug 30, 2014
- fixed issue where removing items, including equips, crashed
May 4, 2014
- rather than reloading template database completely, simply drop the
instance objects
Mar 21, 2014
- fixed issue where conditional branch for equip checking doesn't work
Jan 23, 2014
- added support for refreshing note, description, and icon index
Jan 16, 2014
- added support for refreshing price and features
Jan 14, 2014
- added support for "refreshing" names and params
Jan 12, 2014
- disabling instance mode properly works for equips
- fixed issue with equipping
Jan 9, 2014
- instance counts do not explicitly "gain/lose" a template item
- fixed issue where changing equips forcibly was not working correctly
- fixed issue with battle test when item instances were enabled
Jan 7, 2014
- added several setup methods to the Instance Manager
Dec 10, 2013
- compatibility with Core Equip Slots
- Initial release
-------------------------------------------------------------------------------** Terms of Use
* Free to use in non-commercial projects
* Contact me for commercial use
* 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 Hime Works in your project
* Preserve this header
-------------------------------------------------------------------------------** Description
This script introduces the concept of "instance items". In order to understand
what an instance item is, you need to first understand what a "template item"
is.
All of your database items, weapons, and armors are called "template items".
That is, they serve as templates for in-game items.
An instance item is simply an instance of a template. For example, you design
The goal is to allow developers to write their own scripts that require
"unique" items very easily without having to worry about how to actually
implement it.
This script is designed so that you only need to focus on two things
1. The RPG module, which contains the template weapons, armors, and items.
2. the InstanceManager module, which handles everything related to instances.
A simple script would first load note-tags from the RPG objects and store them
with the templates. For example, suppose we want to give all instance weapons
a random attack bonus. We start by defining the max possible bonus a weapon
could receive.
class RPG::Weapon < RPG::EquipItem
def attack_bonus
50
end
end
Now, we make it so that whenever an instance weapon is created, a random bonus
will be applied to its attack. The InstanceManager provides several "setup"
methods available for you, depending on what kind of object you're working
with:
setup_equip_instance(obj)
setup_weapon_instance(obj)
setup_armor_instance(obj)
setup_item_instance(obj)
use
use
use
use
this
this
this
this
end
#----------------------------------------------------------------------------# Full copy of the template object so we don't have any reference issues
#----------------------------------------------------------------------------def self.make_full_copy(obj)
return Marshal.load(Marshal.dump(obj))
end
def self.instance_enabled?(obj)
return TH::Instance_Items::Enable_Items if obj.is_a?(RPG::Item)
return TH::Instance_Items::Enable_Weapons if obj.is_a?(RPG::Weapon)
return TH::Instance_Items::Enable_Armors if obj.is_a?(RPG::Armor)
return false
end
def self.is_template?(obj)
return obj.id >= @template_counts[:item] if obj.is_a?(RPG::Item)
return obj.id >= @template_counts[:weapon] if obj.is_a?(RPG::Weapon)
return obj.id >= @template_counts[:armor] if obj.is_a?(RPG::Armor)
end
#----------------------------------------------------------------------------# create an instance from the template. Basically just a full copy.
#----------------------------------------------------------------------------def self.make_instance(obj)
new_obj = make_full_copy(obj)
new_obj.template_id = new_obj.id
return new_obj
end
#----------------------------------------------------------------------------# Return the database table that the obj belongs in
#----------------------------------------------------------------------------def self.get_tables(obj)
return @items, $data_items if obj.is_a?(RPG::Item)
return @weapons, $data_weapons if obj.is_a?(RPG::Weapon)
return @armors, $data_armors if obj.is_a?(RPG::Armor)
end
def self.get_template(obj)
return $data_items[obj.template_id] if obj.is_a?(RPG::Item)
return $data_weapons[obj.template_id] if obj.is_a?(RPG::Weapon)
return $data_armors[obj.template_id] if obj.is_a?(RPG::Armor)
end
#----------------------------------------------------------------------------# Returns an instance of the object, assuming it is a valid object, it
# supports instances, and it's not a template
#----------------------------------------------------------------------------def self.get_instance(obj)
return obj if obj.nil? || !instance_enabled?(obj) || !obj.is_template?
new_obj = make_instance(obj)
container, table = get_tables(obj)
id = table.size
new_obj.id = id
# Setup the instance object as required
setup_instance(new_obj)
"def refresh_#{ivar}
var = InstanceManager.get_template(self).#{ivar}
@#{ivar} = make_#{ivar}(InstanceManager.make_full_copy(var))
end
def make_#{ivar}(#{ivar})
#{ivar}
end
"
)
end
_instance_refresh << ";end"
eval(_instance_refresh)
end
class Item < UsableItem
attr_accessor :template_id
def is_template?
return self.template_id == self.id
end
def template_id
@template_id = @id unless @template_id
return @template_id
end
end
class EquipItem < BaseItem
attr_accessor :template_id
def is_template?
self.template_id == self.id
end
def template_id
@template_id = @id unless @template_id
return @template_id
end
end
end
module DataManager
class << self
alias :th_instance_items_load_game_without_rescue :load_game_without_rescue
alias :th_instance_items_create_game_objects :create_game_objects
alias :th_instance_items_make_save_contents :make_save_contents
alias :th_instance_items_extract_save_contents :extract_save_contents
end
def self.create_game_objects
th_instance_items_create_game_objects
InstanceManager.create_game_objects
load_instance_database
end
def self.make_save_contents
contents = th_instance_items_make_save_contents
contents[:instance_weapons] = InstanceManager.weapons
contents[:instance_armors] = InstanceManager.armors
contents[:instance_items] = InstanceManager.items
contents
end
def self.extract_save_contents(contents)
th_instance_items_extract_save_contents(contents)
InstanceManager.weapons = contents[:instance_weapons] || []
InstanceManager.armors = contents[:instance_armors] || []
InstanceManager.items = contents[:instance_items] || []
end
def self.load_game_without_rescue(index)
res = th_instance_items_load_game_without_rescue(index)
reload_instance_database
return res
end
#----------------------------------------------------------------------------# Merges the instance items into the database
#----------------------------------------------------------------------------def self.load_instance_database
InstanceManager.setup
merge_array_data($data_weapons, InstanceManager.weapons)
merge_array_data($data_armors, InstanceManager.armors)
merge_array_data($data_items, InstanceManager.items)
end
def self.reload_instance_database
$data_weapons = $data_weapons[0..InstanceManager.template_counts[:weapon]]
$data_armors = $data_armors[0..InstanceManager.template_counts[:armor]]
$data_items = $data_items[0..InstanceManager.template_counts[:item]]
load_instance_database
end
def self.merge_array_data(arr, hash)
hash.each {|i, val|
arr[i] = val
}
end
end
class Game_Interpreter
alias :th_instance_items_command_111 :command_111
def command_111
result = false
case @params[0]
when 4
actor = $game_actors[@params[1]]
if actor
case @params[2]
when 0 # in party
result = ($game_party.members.include?(actor))
when 1 # name
result = (actor.name == @params[3])
when 2 # Class
result = (actor.class_id == @params[3])
when 3 # Skills
result = (actor.skill_learn?($data_skills[@params[3]]))
when 4 # Weapons
result = (actor.instance_weapons_include?(@params[3]))
when 5 # Armors
result = (actor.instance_armors_include?(@params[3]))
when 6 # States
result = (actor.state?(@params[3]))
end
end
end
@branch[@indent] = result
# none of them passed, so let's check the other conditions
th_instance_items_command_111 if !result
end
end
class Game_Actor < Game_Battler
alias :th_instance_items_init_equips :init_equips
def init_equips(equips)
@equips = Array.new(equip_slots.size) { Game_BaseItem.new }
instance_equips = check_instance_equips(equips)
th_instance_items_init_equips(instance_equips)
end
#----------------------------------------------------------------------------# Replace all initial equips with instances
#----------------------------------------------------------------------------def check_instance_equips(equips)
new_equips = []
equips.each_with_index do |item_id, i|
etype_id = index_to_etype_id(i)
slot_id = empty_slot(etype_id)
if etype_id == 0
equip = $data_weapons[item_id]
else
equip = $data_armors[item_id]
end
new_equips << InstanceManager.get_instance(equip)
end
return new_equips.collect {|obj| obj ? obj.id : 0}
end
alias :th_instance_items_change_equip :change_equip
def change_equip(slot_id, item)
new_item = item
if item && InstanceManager.instance_enabled?(item) && $game_party.has_item?(
item) && item.is_template?
new_item = $game_party.find_instance_item(item)
end
th_instance_items_change_equip(slot_id, new_item)
end
alias :th_instance_items_trade_item_with_party :trade_item_with_party
def trade_item_with_party(new_item, old_item)
if new_item && InstanceManager.instance_enabled?(new_item) && $game_party.ha
s_item?(new_item) && new_item.is_template?
new_item = $game_party.find_instance_item(new_item)
end
th_instance_items_trade_item_with_party(new_item, old_item)
end
#----------------------------------------------------------------------------# New.
#----------------------------------------------------------------------------def instance_weapons_include?(id)
weapons.any? {|obj| obj.template_id == id }
end
#----------------------------------------------------------------------------# New.
#----------------------------------------------------------------------------def instance_armors_include?(id)
armors.any? {|obj| obj.template_id == id }
end
end
class Game_Party < Game_Unit
alias :th_instance_items_init_all_items :init_all_items
def init_all_items
th_instance_items_init_all_items
@item_list = []
@weapon_list = []
@armor_list = []
end
#----------------------------------------------------------------------------# Overwrite. We already keep a list of weapons
#----------------------------------------------------------------------------alias :th_instance_items_weapons :weapons
def weapons
TH::Instance_Items::Enable_Weapons ? @weapon_list.clone : th_instance_items_
weapons
end
#----------------------------------------------------------------------------# Overwrite.
#----------------------------------------------------------------------------alias :th_instance_items_items :items
def items
TH::Instance_Items::Enable_Items ? @item_list.clone : th_instance_items_item
s
end
#----------------------------------------------------------------------------# Overwrite.
#----------------------------------------------------------------------------alias :th_instance_items_armors :armors
def armors
TH::Instance_Items::Enable_Armors ? @armor_list.clone : th_instance_items_ar
mors
end
#----------------------------------------------------------------------------# Returns true if the item type supports instances
#----------------------------------------------------------------------------def instance_enabled?(item)
return InstanceManager.instance_enabled?(item)
end
#----------------------------------------------------------------------------# Returns an instance for the given item. If it is already an instance, then
# we just return that. If it's a template, we create a new instance.
#----------------------------------------------------------------------------def get_instance(item)
return InstanceManager.get_instance(item)
end
#----------------------------------------------------------------------------# Returns the template for the given item
#----------------------------------------------------------------------------def get_template(item)
return InstanceManager.get_template(item)
end
#----------------------------------------------------------------------------# The gain item method performs various checks on the item that you want to
# add to the inventory. Namely, it checks whether it is a template item or
# an instance item, updates the item counts, and so on.
#----------------------------------------------------------------------------alias :th_instance_items_gain_item :gain_item
def gain_item(item, amount, include_equip = false)
# special check for normal items
if !instance_enabled?(item)
th_instance_items_gain_item(item, amount, include_equip)
else
if item
if amount > 0
amount.times do |i|
new_item = get_instance(item)
add_instance_item(new_item)
end
else
amount.abs.times do |i|
item_template = get_template(item)
if item.is_template?
# remove using template rules. If an item was lost, then decrease
# template count by 1.
lose_template_item(item, include_equip)
else
# remove the instance item, and decrease template count by 1
lose_instance_item(item)
end
end
end
else
th_instance_items_gain_item(item, amount, include_equip)
end
end
end
#----------------------------------------------------------------------------# New. Returns the appropriate container list
#----------------------------------------------------------------------------def item_container_list(item)
return @item_list if item.is_a?(RPG::Item)
return @weapon_list if item.is_a?(RPG::Weapon)
return @armor_list if item.is_a?(RPG::Armor)
end
#----------------------------------------------------------------------------# New. Adds the instance item to the appropriate list
#----------------------------------------------------------------------------def add_instance_item(item)
container = item_container(item.class)
container[item.template_id] ||= 0
container[item.template_id] += 1
container[item.id] = 1
container_list = item_container_list(item)
container_list.push(item)
end
#----------------------------------------------------------------------------# New. Returns an instance item that matches the template. If it doesn't
# exist, returns nil
#----------------------------------------------------------------------------def find_instance_item(template_item)
container_list = item_container_list(template_item)
return container_list.find {|obj| obj.template_id == template_item.template_
id }
end
#----------------------------------------------------------------------------# New. Lose an instance item. Simply delete it from the appropriate container
# list
#----------------------------------------------------------------------------def lose_instance_item(item)
container = item_container(item.class)
container[item.template_id] ||= 0
container[item.template_id] -= 1
container[item.id] = 0
container_list = item_container_list(item)
container_list.delete(item)
end
#----------------------------------------------------------------------------# New. Lose a template item. This looks for a
#----------------------------------------------------------------------------def lose_template_item(item, include_equip)
container_list = item_container_list(item)
item_lost = container_list.find {|obj| obj.template_id == item.template_id }
if item_lost
container = item_container(item.class)
container[item.template_id] ||= 0
container[item.template_id] -= 1
container_list.delete(item_lost)
elsif include_equip
discard_members_template_equip(item, 1)
end
return item_lost
end
#----------------------------------------------------------------------------# New. Same as discarding equips, except we follow template discard rules
#----------------------------------------------------------------------------def discard_members_template_equip(item, amount)
n = amount
members.each do |actor|