Item Instances Base
Version: 1.0.0
Author: modern algebra
Date: 26 December 2012
Version History
- <Version 1.0> 2012.12.26 - Original Release
Description
This script changes the item system in RMVX Ace to accomodate for items of the same ID to have different stats. It can thereby serve as a base for scripts that require such functionality, such as durability for weapons and armors, charges for items, weapons that level up, socketing, etc...
This script does none of that by default - it only sets up data structures to permit that, and its only independent use is that you can manually change the stats on an item added to the party through events.
Features
- Allows for items of the same ID to be saved as instances.
- A useful base for other scripts which require that functionality, such as durability scripts, socketing, enchanting items, levelling weapons, etc.
Instructions
Insert this script into its own slot in the Script Editor, above Main but below Materials. This script must also be above every script that requires it.
Script
#==============================================================================
# Instance Items Base [VXA]
# Version: 1.0.0
# Author: modern algebra (rmrk.net)
# Date: 26 December 2012
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Description:
#
# This script changes the item system in RMVX Ace to accomodate for items
# of the same ID to have different stats. It can thereby serve as a base for
# scripts that require such functionality, such as durability for weapons and
# armors, charges for items, weapons that level up, socketing, etc...
#
# This script does none of that by default - it only sets up data structures
# to permit that, and its only independent use is that you can manually
# change the stats on an item added to the party through events.
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Instructions:
#
# Insert this script into its own slot in the Script Editor, above Main but
# below Materials. This script must also be above every script that requires
# it.
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Instructions for Scripters:
#
# If you are a scripter and want to write a script that requires this script
# in order to be used, you have my permission. I ask only that you link back
# to this script at RMRK, as well as provide me with a link to your script so
# that I can ensure compatibility.
#
# I have tried to comment the script where there might be some confusion,
# so you should be able to read through it to gain an understanding as to
# how it works. For the most part, it is designed to mimic as much as
# possible the default arrangement. So, if you access the first 999 indices
# of $data_items, $data_weapons, or $data_armors, you will receive the basic
# classes for those. New instances begin at ID 1000, and each new instance
# will have a unique ID, which is what is returned when you call the #id
# method. Instances will also have a #database_id method which returns the
# ID of its base item. Instance items are now of the Game_IItem, Game_IWeapon,
# or Game_IArmor. Game_IItem inherits its methods from the module
# Game_IUsableItem, and the others inherit from the module Game_IEquipItem.
# Both Game_IEquipItem and Game_IUsableItem inherit from Game_IBaseItem.
#
# If you need to add a new notefield tag, it is recommended you also add it
# to MA_INSTANCE_ITEMS_BASE[:regexp_array], as any regular expressions
# included in that array will make the #instance_based? method in
# RPG::BaseItem return true if the item has that in its note field.
#
# Beyond that, I recommend you read the entire script in order to gain an
# understanding of how it works.
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Support:
#
# If you have any questions, feel free to ask them in the script's topic:
#
# http://rmrk.net/index.php/topic,47427.0.html
#
# I will try to answer as swiftly as possible. If you believe further
# documentation is required, please let me know.
#==============================================================================
$imported = {} unless $imported
$imported[:"MA_InstanceItemsBase 1.0.0"] = true
MA_INSTANCE_ITEMS_BASE = {
# This sets the first index used for instance items. It should be 1000
# unless you are using another script which makes extends the database
# limitations beyond 999.
:index_start => 1000,
# This array tracks the note field codes which identify that an item should
# be created as new instances.
:regexp_array => [/\\INS/i],
# This array holds methods which you would never want to distinguish between
# instances
:ignore_accessors => [],
}
#==============================================================================
# ** MAIIB_RPG_BaseItem
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# This mixes in to RPG::Item, RPG::Weapon, and RPG::Armor, adding the
# following methods:
# overwritten method - ==
# new methods - instance_based?; database_id
#==============================================================================
module MAIIB_RPG_BaseItem
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Instance Based?
# This is true if the item is identified as instance-based.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def instance_based?
@ins_based = MA_INSTANCE_ITEMS_BASE[:regexp_array].any? { |regexp|
!self.note[regexp].nil? } unless @ins_based
@ins_based
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Data ID
# Essentially just aliases #id, but in case any other script overwrites
# or aliases #id, I made it so it just calls it
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def database_id(*args, &block)
id(*args, &block)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Equality?
# Also returns true if other is an instance item with same data.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def ==(other)
super(other) || (other.is_a?(Game_BaseInstanceItem) && super(other.data))
end
end
# MAIIB_RPG_UsableItem and MAIIB_RPG_EquipItem both inherit MAIIB_RPG_BaseItem
module MAIIB_RPG_UsableItem; include MAIIB_RPG_BaseItem; end
module MAIIB_RPG_EquipItem; include MAIIB_RPG_BaseItem; end
module RPG
class Item; include MAIIB_RPG_UsableItem; end
class Weapon; include MAIIB_RPG_EquipItem; end
class Armor; include MAIIB_RPG_EquipItem; end
end
#==============================================================================
# *** DataManager
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# These modifications preserve the
#``````````````````````````````````````````````````````````````````````````````
# Summary of Changes:
# aliased methods - init; extract_save_contents; create_game_objects
# new method - maiib_preserve_old_saves; set_data_to_instance_items
#==============================================================================
class << DataManager
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Initialize
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_inz_2tg6 init
def init(*args, &block)
Game_BaseInstanceItem.init_auto_accessors # Initialize automatic
maiib_inz_2tg6(*args, &block)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Extract Save Contents
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_extractsave_2jw6 extract_save_contents
def extract_save_contents(contents, *args, &block)
maiib_extractsave_2jw6(contents, *args, &block) # Call Original Methods
maiib_preserve_old_saves
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Create Game Objects
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_creatgameobj_4hi8 create_game_objects
def create_game_objects(*args, &block)
maiib_creatgameobj_4hi8(*args, &block) # Call Original Method
set_data_to_instance_items
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Preserve Save Contents
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def maiib_preserve_old_saves
$game_system.init_maiib_data unless $game_system.instance_items
set_data_to_instance_items
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Set Data Instance Items
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def set_data_to_instance_items
$game_system.instance_items[:item].reset_data_array($data_items)
$game_system.instance_items[:weapon].reset_data_array($data_weapons)
$game_system.instance_items[:armor].reset_data_array($data_armors)
$data_items = $game_system.instance_items[:item]
$data_weapons = $game_system.instance_items[:weapon]
$data_armors = $game_system.instance_items[:armor]
end
end
#==============================================================================
# ** Game_BaseInstanceItem
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# This class holds an instance of an Item, Weapon, or Armor
#==============================================================================
module Game_BaseInstanceItem
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Public Instance Variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
attr_writer :id
attr_accessor :data_type
attr_accessor :database_id
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Object Initialization
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def initialize(id, data_type, database_id)
@id = id
@data_type = data_type
@database_id = database_id
self.class.auto_accessors.each { |method|
instance_variable_set(:"@#{method}", Marshal.load(Marshal.dump(data.send(method))))
}
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Get ID
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def id
($game_system && $game_system.retrieve_database_id) ? database_id : @id
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Data
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def data
case data_type
when :item then $data_items[database_id]
when :weapon then $data_weapons[database_id]
when :armor then $data_armors[database_id]
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Method Missing
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def method_missing(method, *args, &block)
if method.to_s[/^(.*?[^=])=$/] && !MA_INSTANCE_ITEMS_BASE[:ignore_accessors].include?(method)
instance_exec($1.to_sym) { |new_meth|
eval("def #{new_meth}=(val); @#{new_meth} = val; end;
def #{new_meth}; @#{new_meth}; end")
}
send(method, *args, &block)
else
data ? data.send(method, *args, &block) : super(method, *args, &block)
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Equality?
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def ==(other); super(other) || (data && data == other); end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Is A?
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def is_a?(*args, &block)
super(*args, &block) || (data && data.is_a?(*args, &block))
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Initialize All Automatic Accessors
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def self.init_auto_accessors
# For all descendants
ObjectSpace.each_object(Class) { |klass|
klass.class_eval { klass.auto_accessors.each { |method|
attr_accessor(method) } } if klass < self
}
end
end
module Game_IUsableItem; include Game_BaseInstanceItem; end
module Game_IEquipItem; include Game_BaseInstanceItem; end
class Game_IItem
include Game_IUsableItem
def initialize(*args); super(*args); end
def self.auto_accessors; [:effects]; end
end
class Game_IWeapon
include Game_IEquipItem
def initialize(*args); super(*args); end
def self.auto_accessors; [:params, :features]; end
end
class Game_IArmor
include Game_IEquipItem
def initialize(*args); super(*args); end
def self.auto_accessors; [:params, :features]; end
end
#==============================================================================
# ** Game_InstanceItems
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# This is a special subclass of an array which holds instance items in a
# separate hashes. It replaces the $data_ classe for items, weapons, and armors
#==============================================================================
class Game_InstanceItems < Array
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Public Instance Variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
attr_accessor :instance_items
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Object Initialization
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def initialize(type, data_array = [])
@instance_items = {}
@type = type
@index_start = MA_INSTANCE_ITEMS_BASE[:index_start]
@last_index = @index_start
@free_indices = []
reset_data_array(data_array)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Type Class
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def type_class
case @type
when :item then Game_IItem
when :weapon then Game_IWeapon
when :armor then Game_IArmor
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Reset Data Array
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def reset_data_array(data_array)
clear
for item in data_array do self << item end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Create Instance
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def create_instance(base_item)
base_item = self[base_item] if base_item.is_a?(Integer)
return base_item if base_item.is_a?(Game_BaseInstanceItem) || !base_item.instance_based?
# Get next free index
if @free_indices.empty?
index = @last_index
@last_index += 1
else
index = @free_indices.shift
end
@instance_items[index] = type_class.new(index, @type, base_item.id)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Destroy Instance
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def destroy_instance(index)
@free_indices.push(index)
@instance_items.delete(index)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Retrieve Value from key
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def [](key)
return key < @index_start ? super(key) : @instance_items[key]
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Set Value to Key
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def []=(key, value)
key < @index_start ? super(key, value) : @instance_items[key] = value
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Items with ID
# This will return an array of all items with the specified ID
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def items_with_id(item_id)
return [self[item_id]] unless self[item_id].instance_based?
return @instance_items.values.select { |ins| ins.item_id == item_id }
end
end
#==============================================================================
# ** Game_System
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Summary of Changes:
# new attr_reader - instance_items
# new attr_accessor - retrieve_database_id
# aliased method - initialize
# new method - init_maiib_data
#==============================================================================
class Game_System
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Public Instance Variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
attr_reader :instance_items
attr_accessor :retrieve_database_id
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Object Initialization
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_intz_2sh7 initialize
def initialize(*args, &block)
maiib_intz_2sh7(*args, &block) # Call Original Method
init_maiib_data
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Initialize Instance Items Base Data
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def init_maiib_data
@instance_items = {
item: Game_InstanceItems.new(:item),
weapon: Game_InstanceItems.new(:weapon),
armor: Game_InstanceItems.new(:armor) }
@retrieve_database_id = false
end
end
#==============================================================================
# ** Game_BaseItem
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Summary of Changes:
# aliased method - set_equip; is_item?; is_weapon?; is_armor?
#==============================================================================
class Game_BaseItem
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Is Item/Weapon/Armor?
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[:Item, :Weapon, :Armor].each { |ic|
alias_method(:"maiib_is_#{ic.downcase}_8jf9", :"is_#{ic.downcase}?")
define_method(:"is_#{ic.downcase}?") do |*args|
send(:"maiib_is_#{ic.downcase}_8jf9", *args) ||
@class == Kernel.const_get(:"Game_I#{ic}")
end
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Set Equipment
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_seteqp_1gn8 set_equip
def set_equip(is_weapon, item_id, *args, &block)
data = is_weapon ? $data_weapons : $data_armors
item_id = data.create_instance(item_id).id if data[item_id] &&
!data[item_id].is_a?(Game_BaseInstanceItem) && data[item_id].instance_based?
maiib_seteqp_1gn8(is_weapon, item_id, *args, &block) # Call Original Method
end
end
#==============================================================================
# ** Game_Party
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Summary of Changes:
# aliased methods - initialize; items; weapons; armors; item_container;
# item_number; members_equip_include?; gain_item; discard_members_equip
# new methods - instance_item_container; instance_item_type;
# instance_item_number; gain_instance_item
#==============================================================================
class Game_Party
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Public Instance Variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
attr_reader :newest_instance_items
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Object Initialization
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_nitialz_4ld5 initialize
def initialize(*args, &block)
@newest_instance_items = []
maiib_nitialz_4ld5(*args, &block) # Call Original Method
end
[:items, :weapons, :armors].each { |method|
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Items/Weapons/Armors
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias_method(:"maiib_#{method}_3js6", method)
define_method(method) do |*args|
result = send(:"maiib_#{method}_3js6", *args) # Call Original Method
# Delete the database items if they are instance items
deletables = []
for i in 0...result.size
item = result[i]
next if item.nil?
break if item.is_a?(Game_BaseInstanceItem)
deletables << i if item.instance_based?
end
deletables.reverse.each { |i| result.delete_at(i) }
# Sort by Database ID, then ID.
result.sort {|a, b| (a.database_id <=> b.database_id).nonzero? || a.id <=> b.id }
end
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Held Instances Of
# item : the Item to check
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def held_instances_of(item)
maiib_items_of_type(item).select { |item2| item2.database_id == item.database_id }
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Item Container
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_itmcontain_7hq9 item_container
def item_container(item_class, *args, &block)
return @items if item_class == Game_IItem
return @weapons if item_class == Game_IWeapon
return @armors if item_class == Game_IArmor
maiib_itmcontain_7hq9(item_class, *args, &block) # Call Original Method
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Items of Type
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def maiib_items_of_type(item)
case instance_item_type(item)
when :item then items
when :weapon then weapons
when :armor then armors
else []
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Instance Type
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def instance_item_type(item)
case item
when RPG::Item, Game_IItem then :item
when RPG::Weapon, Game_IWeapon then :weapon
when RPG::Armor, Game_IArmor then :armor
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Gain Item
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_gainitm_3ky7 gain_item
def gain_item(item, amount, include_equip = false, *args, &block)
return if amount == 0
if item.is_a?(MAIIB_RPG_BaseItem) && item.instance_based?
count = item_number(item)
amount = [[amount, max_item_number(item) - count].min, 0].max if amount > 0
gain_instance_item(item, amount, include_equip)
# Add the new items to the container
@newest_instance_items.each { |ins|
maiib_gainitm_3ky7(ins, amount <=> 0, include_equip) }
# Prepare for database count
item = item.data if item.is_a?(Game_BaseInstanceItem)
end
# Call Original Method and modify the count for database items
maiib_gainitm_3ky7(item, amount, include_equip, *args, &block)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Gain Instance Item
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def gain_instance_item(item, amount, include_equip = false)
@newest_instance_items.clear
if item.is_a?(Game_BaseInstanceItem)
if amount > 0 || (item_number(item) > 0 && amount < 0)
# Add the item
@newest_instance_items << item
amount -= amount <=> 0
end
# If amount was not 1 or -1, apply method to the database item
item = item.data
end
return unless item
if amount > 0 # Create new instances
add_instance_items(item, amount)
elsif amount < 0 # Find and remove instances of the same database item
subtract_instance_items(item, -amount, include_equip)
end
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Lose Instance Item
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def lose_instance_item (item, amount, include_equip = false)
gain_instance_item(item, -amount, include_equip)
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Add Instance Item
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def add_instance_items(item, amount)
data_array = $game_system.instance_items[instance_item_type(item)]
amount.times { @newest_instance_items << data_array.create_instance(item) }
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Subtract Instance Item
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def subtract_instance_items(item, amount, include_equip = false)
data_array = $game_system.instance_items[instance_item_type(item)]
for ins in held_instances_of(item)
break if amount == 0
@newest_instance_items << ins
amount -= 1
end
# Rely on discard_members_equip to dispose of equipped items
end
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# * Discard Members Equip
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
alias maiib_discardequip_3hx7 discard_members_equip
def discard_members_equip(item, amount, *args, &block)
if item.is_a?(MAIIB_RPG_BaseItem) && item.instance_based? &&
!item.is_a?(Game_BaseInstanceItem)
n = amount
members.each { |actor|
while n > 0
ins = actor.equips.find { |equip| equip.database_id == item.database_id }
break unless ins
@newest_instance_items << ins
actor.discard_equip(ins)
n -= 1
end
}
else
# Call Original Method
maiib_discardequip_3hx7(item, amount, *args, &block)
end
end
end
Credit
Support
Please post in this topic at RMRK.net if you have any questions, suggestions, error reports, or comments about this script. Please do not message me privately, as it is more efficient and useful to answer any concerns you have in the topic where others may benefit from our correspondence. Additionally, others might be able to provide you with quicker answers to your questions, as I am occasionally unavailable due to work.
Known Compatibility Issues
This script will likely not work with any other script which purports to do the same thing.
[spoiler=Yanfly's Ace Equip Engine]
There is a small bug in Yanfly's Ace Equip Engine (http://yanflychannel.wordpress.com/rmvxa/gameplay-scripts/ace-equip-engine/). Although it works functionally, when scrolling through instances of the same item, differences in stats will not be shown unless it is the first one you select after looking at some other item. ekomega (http://rmrk.net/index.php/topic,47428.msg539086.html#msg539086) investigated the problem and determined that all you need to do to fix this error is to find line 1098 in Yanfly's script, which should look like this:
#--------------------------------------------------------------------------
# overwrite method: update_help
#--------------------------------------------------------------------------
def update_help
super
return if @actor.nil?
return if @status_window.nil?
return if @last_item == item
@last_item = item
temp_actor = Marshal.load(Marshal.dump(@actor))
temp_actor.force_change_equip(@slot_id, item)
@status_window.set_temp_actor(temp_actor)
endAll you need to do is comment out or delete the following line:
return if @last_item == itemAs far as I know, that should not cause any problems re: lag, since I don't believe that method is called except when the help window is likely to require updating. However, if deleting the line altogether makes you nervous, then you can replace it with:
return if @last_item.equal?(item)Many thanks to ekomega for discovering and fixing this incompatibility.
[/spoiler]
Demo
This script does nothing on its own, so a demo would have no purpose.
Author's Notes
I actually finished this a few months ago, but then did not do anything with it as I planned to write a few sample scripts before releasing it. I never did that, so I let it stagnate for a while. Unfortunately, this means I am not entirely certain that it was actually finished, but I think it is. Please let me know if anything is missing.
Terms of Use
I adopt RMRK's default Terms of Use (http://rmrk.net/index.php/topic,45481.0.html).
So this script is more of a scripters utility rather than something for the regular user?
I don't see myself making any sort of script that would require such a base but nevertheless should the day arrive, I'm sure I'll put it to good use ^-^
Thanks for the base script Modern.
Yeah, basically. It is mostly for my own purposes, since I intend to write a few of those scripts sometime in the unspecified future, but I figured it might be valuable to others as well.
Hi modern algebra!
I really like this script!
I´m currently using it together with your Equipment Stat Variance script and it works great.
What I still need for my project however is equip durability so I am currently trying to create this myself with your base. Well actually I think I already did kind of.
I did not yet implement any hud stuff or events to alter the durability though, because it would be nice to know if I am on the right way with this. Would you be so kind and fly over my code?
As I´m still very new to both the Maker and Ruby it would also be really cool if you (and of course everyone else here) could point out which parts I could do better as I´m always interested in using best practice if possible :)
Here´s the script (unfinished, dev-use only):
# ---------------------------------------------------------------
# Instanced Equip Durability script by p3king
# -> Using InstanceItemBase script by modern algebra (rmrk.net)
# ---------------------------------------------------------------
if $imported && $imported[:"MA_InstanceItemsBase 1.0.0"]
# The default maximum durability an item has when spawned.
# Set this in the armor or weapon notebox to customize:
#
# \dur_max[200]
# Sets max. durability of this item to 200
#
# The value below is taken when you put nothing in the notebox.
P3_IED_DEFAULT_MAX_DURABILITY = 100
# The default current durability an item has when spawned.
# Set this in the armor or weapon notebox to customize:
#
# \dur_cur[150]
# Sets current dur. for this item to 150 (without variance)
#
# The value below is taken when you put nothing in the notebox.
P3_IED_DEFAULT_CUR_DURABILITY = 100
# The default variance in percent that will be applied to the
# current durability. This value will be subtracted.
# Set this in the armor or weapon notebox to customize:
#
# \dur_var[50]
# Sets durability variance to 50%. This means the item's
# current durability will be decreased by up to 50% (random).
#
# The value below is taken when you put nothing in the notebox.
# Note that currently, when nothing is set in the notebox the
# item won´t get instanced and will not use durability.
P3_IED_DEFAULT_VAR_DURABILITY = 30
P3_IED_REGEXP = {
"cur" => /\\DUR_CUR\[(\d+)\]/i,
"max" => /\\DUR_MAX\[(\d+)\]/i,
"var" => /\\DUR_VAR\[(\d+)\]/i
}
P3_IED_REGEXP.each_value do |regexp|
MA_INSTANCE_ITEMS_BASE[:regexp_array].push(regexp)
end
module Game_IEquipItem
alias zsiid_initialize initialize
attr_reader :get_durability
attr_reader :get_max_durability
attr_reader :is_broken
attr_reader :is_durability_enabled
attr_reader :change_durability
# check for notebox vars
def initialize(*args)
zsiid_initialize(*args)
@durability_enabled = false
@durability = self.note[P3_IED_REGEXP["cur"]].nil? ? -1 : $1.to_i
@durability_max = self.note[P3_IED_REGEXP["max"]].nil? ? -1 : $1.to_i
dur_var_perc = self.note[P3_IED_REGEXP["var"]].nil? ? -1 : $1.to_i
if @durability > -1 or @durability_max > -1 or dur_var_perc > -1
@durability_enabled = true
set_default_durability
apply_variable_durability
# print(self.name + " > max: " + @durability_max.to_s + ", cur: " + @durability.to_s + "\n");
end
end
# get item dur.
def get_durability
@durability
end
# get item max dur.
def get_max_durability
@durability_max
end
# return if item is broken
def is_broken?
use_durability? and @durability == 0
end
# return if item uses durability function
def is_durability_enabled?
@durability_enabled
end
# change durability on item
def change_durability(amount)
if @durability_enabled
@durability = @durability + amount
anti_manaburn
end
end
# put default values
def set_default_durability
@durability_max = P3_IED_DEFAULT_MAX_DURABILITY if @durability_max == -1
@durability = @durability_max if @durability == -1
end
# apply variable durability
def apply_variable_durability
var_perc = self.note[P3_IED_REGEXP["var"]].nil? ? P3_IED_DEFAULT_VAR_DURABILITY : $1.to_i
if var_perc != 0
rand = Random.new(Time.now.to_i)
random_val = 1.0 - (rand(var_perc).to_f / 100.0)
@durability = (@durability * random_val).to_i
anti_manaburn
end
end
# prevent values going over min / max
def anti_manaburn
@durability = @durability_max if @durability > @durability_max
@durability = 0 if @durability < 0
end
end
else
# sentence and structure stolen from modern algebra
msgbox("Item Durability could not be installed because you either do not have Instance Items Base or because you have that script placed below the Item Durability script.")
end
Well, I'm not Modern Algebra, but I can help you a little bit with the code that you have. :)
Firstly, you really don't need the get_* methods. Instead of defining a method named "get_durability", all you need is an attr_reader for the @durability instance variable. As it is, you have readers for methods, which isn't what you want to do. Readers and accessors make use of instance variables, not methods -- so defining the "get_durability" method and then creating a reader for "get_durability" is redundant and your hand-written "get_durability" method overrides the method generated for the (non-existent) @get_durability instance variable that your attr_reader created.
I'd also change the logic for your "is_broken?" method a little bit. Right now, you're checking if durability is in use for the item (good), and if its durability is equal to zero (not so good) -- so what if the durability is less than zero? Suddenly, it's not broken, which would be a bug, I think. :) Also, I don't see a "use_durability?" method anywhere, so I'm assuming you meant to reference the "is_durability_enabled?" method, which is really just checking if the @durability_enabled instance variable has a truth-y value. Why not check @durability_enabled directly?
In your "apply_variable_durability", you create a local variable named "rand" with a seeded random number, which is also kind of redundant: the Kernel#rand method already does this.
Here's a modified version so that you can hopefully see what I'm talking about with all of this:
if $imported && $imported[:"MA_InstanceItemsBase 1.0.0"]
P3_IED_DEFAULT_MAX_DURABILITY = 100
P3_IED_DEFAULT_CUR_DURABILITY = 100
P3_IED_DEFAULT_VAR_DURABILITY = 30
# Note: I'd use symbols as keys here, not strings.
P3_IED_REGEXP = {
:cur => /\\DUR_CUR\[(\d+)\]/i,
:max => /\\DUR_MAX\[(\d+)\]/i,
:var => /\\DUR_VAR\[(\d+)\]/i
}
P3_IED_REGEXP.each_value do |regexp|
MA_INSTANCE_ITEMS_BASE[:regexp_array].push(regexp)
end
module Game_IEquipItem
alias :zsiid_initialize :initialize
attr_reader :durability, :max_durability, :durability_enabled
def initialize(*args)
zsiid_initialize(*args)
# Note: technically, you could leave this out here, actually. If the
# variable is not instantiated, it evaluates to nil, which would work
# fine for conditionals.
@durability_enabled = false
# Note: I removed your checks for nil here because both nil and false are
# automatically evaluated to false; no need to explicitly check for it.
@durability = self.note[P3_IED_REGEXP[:cur]] ? $1.to_i : -1
@durability_max = self.note[P3_IED_REGEXP[:max]] ? $1.to_i : -1
dur_var_perc = self.note[P3_IED_REGEXP[:var]] ? $1.to_i : -1
if @durability > -1 or @durability_max > -1 or dur_var_perc > -1
@durability_enabled = true
set_default_durability
apply_variable_durability
end
end
def is_broken?
@durability_enabled and @durability <= 0
end
def change_durability(amount)
return unless @durability_enabled
# Note: this is shorthand for what you were doing before.
@durability += amount
anti_manaburn
end
def set_default_durability
@durability_max = P3_IED_DEFAULT_MAX_DURABILITY if @durability_max == -1
@durability = @durability_max if @durability == -1
end
def apply_variable_durability
var_perc = self.note[P3_IED_REGEXP[:var]] ? $1.to_i : P3_IED_DEFAULT_VAR_DURABILITY
if var_perc != 0
# Note: no need to add .to_f to rand in my opinion.
random_val = 1.0 - (rand(var_perc) / 100.0)
@durability = (@durability * random_val).to_i
anti_manaburn
end
end
# Note: I'd rename this method to something like "clamp_durability",
# personally... describe what it does better.
def anti_manaburn
@durability = @durability_max if @durability > @durability_max
@durability = 0 if @durability < 0
end
end
else
msgbox("Item Durability could not be installed because you either do not have Instance Items Base or because you have that script placed below the Item Durability script.")
end
Also, be aware of the fact that I wrote these changes really quickly and haven't tested them, so if something doesn't work, just assume that I put a problem in there intentionally to help you learn! :D
Wow thanks alot for your effort, that helps alot.
I read about the accessors earlier and I already tried them but I probably did something wrong.
Also, coming from php I´m kind of used to define my getters and setters, I need to stop that :)
I let is_broken? only check for zero because in theory it should never be able to get below.. but you´re right that it´s better checking again. use_durability was the name of durability_enabled before i renamed it.. but not the accessor.
That local rand var you found was a line I forgot to remove. I didn´t know of rand() and google first led me to Random.new.
Regarding the anti_manaburn method, I just couldn´t think of a fitting name in that moment and that was the first thing coming to my mind :D
I´ll update and test my script later tomorrow, now it´s time for me to sleep ;)
Well, it looks like you were trying to use accessors as public / private declarations, which isn't what they are. For now, think of them as syntactic sugar for writing your own getter and setter methods (interesting tidbit, though: they're actually faster when executed than methods are).
And yes, I noticed that in theory, the durability should never get below zero -- but don't always rely on your design that way. It's safer to be sane than to rely on things going as planned. ;)