####################################################################
# Module which reads XML formatted note tags and returns them as
# HashMaps
module Tag_reader
###################################################################
# Represents a single XML node
class XML_node
#####################################
# Constructor
def initialize(name)
@name=name
# Set of name=value pairs
@attributes={}
end
###################################
# Adds an attribute
def add_attribute(name, value)
@attributes[name]=value
end
###################################
# Returns this as an XML string
def to_s
result="<"+@name
@attributes.each {
|key, value| result += " "+key+"=\""+value+"\""
}
result += " />\n"
return result
end
attr_reader

name)
attr_reader

attributes)
end
#######################################################
# Reads a set of XML tags
class XML_Reader
def initialize()
@nodes=[]
end
##########################################################
# Reads the XML tags in note_string. Does nothing if the
# note string is empty or nil. Returns the set of nodes read
# from the note_string. This may be empty
def read(note_string)
@nodes.clear
if (note_string == nil)
return @nodes
end
note_s=note_string.to_s.strip
# Ignore empty strings
string_len=note_s.length
if (string_len == 0)
return
end
index=0
worker=""
right_index=0
node_name=""
# Parse the XML string
while (index < string_len)
# Search for < mark
if (note_s[index] != '<')
index += 1
next
end
# Find the > mark after the < mark
right_index=note_s.index("/>",index)
if (right_index == nil)
right_index=note_s.index('>',index)
if (right_index == nil)
break
end
end
# This is a string from < to >
worker=note_s.slice(index, right_index-index+1)
worker=worker.strip
# Find name="value" pairs
# Skip the <name part of the XML
attr_index=worker.index(' ')
node_name=worker.slice(1,attr_index).strip
new_node=XML_node.new(node_name)
while (worker.length > attr_index)
if (worker[attr_index] == ' ')
attr_index += 1
next
end
value_index=worker.index("=\"", attr_index)
if (value_index == nil)
break
end
# Backspace to find the previous space or < mark - start of the
# attribute
attr_start_index=value_index-1
while (worker[attr_start_index] != '<' &&
worker[attr_start_index] != ' ')
attr_start_index -=1
end
# Get to the start of the attribute name
attr_start_index += 1
# Skip the =" marker after attribute name
second_quote=worker.index("\"", value_index+3)
if (second_quote == nil)
break
end
attr=worker.slice(attr_start_index, value_index-attr_index)
value=worker.slice(value_index+2, second_quote-value_index-2)
new_node.add_attribute(attr, value)
attr_index = second_quote+1
end
@nodes << new_node
index=right_index+1
end
return @nodes
end
end
end
#================================================
#
# Script: WeaponMastery
# by: whitesphere (whitesphere@comcast.net)
# September ?? 2014
#================================================
=begin
================================================================================
Description: Allows each Class to grow in Weapon specific skill, increasing their Attack damage as they use the Weapon more. Also allows weapons to be more or less effective for specific Classes (i.e. Knights are better with a Sword than, say a Rogue).
--------------------------------------------------------------------------------
History
v0.5 (2014/09/??)
Initial release
--------------------------------------------------------------------------------
Terms of Use
- You are free to use this script for non-commercial or commercial projects.
- Reported bug will be fixed, but no guarantee on requested features.
- No guarantee either for compatibility fixes.
- Give credit to whitesphere (me), and do not delete this header.
--------------------------------------------------------------------------------
In each Class, add one or more notetags in this format:
<mastery type="1" ATK="5" DEF="10" count="100">
<mastery type="1" ATK="5%" DEF="5%" count="100" skill="33,40">
Valid attributes are:
ATK, DEF, AGI, MAT, MDF, etc.
Values can also be negative to decrease a stat.
This means "For Weapons of Type 1, add 5 to ATK score and 20% to DEF score. Advance to next Mastery level after 100 Attacks"
The second entry also adds Skill 33 and 40 to the Actor only when wielding this appropriate weapon and when the Actor is this particular Class, when this Mastery level is reached.
--------------------------------------------------------------------------------
=end
####################################################################
# Module which reads XML formatted note tags and returns them as
# HashMaps
module Tag_reader
###################################################################
# Represents a single XML node
class XML_node
#####################################
# Constructor
def initialize(name)
@name=name
# Set of name=value pairs
@attributes={}
end
###################################
# Adds an attribute
def add_attribute(name, value)
@attributes[name]=value
end
###################################
# Returns this as an XML string
def to_s
result="<"+@name
@attributes.each {
|key, value| result += " "+key+"=\""+value+"\""
}
result += " />\n"
return result
end
attr_reader

name)
attr_reader

attributes)
end
#######################################################
# Reads a set of XML tags
class XML_Reader
def initialize()
@nodes=[]
end
##########################################################
# Reads the XML tags in note_string. Does nothing if the
# note string is empty or nil. Returns the set of nodes read
# from the note_string. This may be empty
def read(note_string)
@nodes.clear
if (note_string == nil)
return @nodes
end
note_s=note_string.to_s.strip
# Ignore empty strings
string_len=note_s.length
if (string_len == 0)
return
end
index=0
worker=""
right_index=0
node_name=""
# Parse the XML string
while (index < string_len)
# Search for < mark
if (note_s[index] != '<')
index += 1
next
end
# Find the > mark after the < mark
right_index=note_s.index("/>",index)
if (right_index == nil)
right_index=note_s.index('>',index)
if (right_index == nil)
break
end
end
# This is a string from < to >
worker=note_s.slice(index, right_index-index+1)
worker=worker.strip
# Find name="value" pairs
# Skip the <name part of the XML
attr_index=worker.index(' ')
if (attr_index == nil)
return
end
node_name=worker.slice(1,attr_index).strip
new_node=XML_node.new(node_name)
while (worker.length > attr_index)
if (worker[attr_index] == ' ')
attr_index += 1
next
end
value_index=worker.index("=\"", attr_index)
if (value_index == nil)
break
end
# Backspace to find the previous space or < mark - start of the
# attribute
attr_start_index=value_index-1
while (worker[attr_start_index] != '<' &&
worker[attr_start_index] != ' ')
attr_start_index -=1
end
# Get to the start of the attribute name
attr_start_index += 1
# Skip the =" marker after attribute name
second_quote=worker.index("\"", value_index+3)
if (second_quote == nil)
break
end
attr=worker.slice(attr_start_index, value_index-attr_index)
value=worker.slice(value_index+2, second_quote-value_index-2)
new_node.add_attribute(attr, value)
attr_index = second_quote+1
end
@nodes << new_node
index=right_index+1
end
return @nodes
end
end
end
#
module WeaponMastery
##########################################################################
# CONSTANTS
# Defines changes to a single Attribute
class Attribute_entry
attr_reader

attr_name)
attr_reader

attr_change)
attr_reader

attr_index)
attr_reader

attr_type)
def initialize(attr_name, attr_change)
attr_name=attr_name.downcase
@attr_name=nil
@attr_index=0
@attr_type=nil
if (@@name_map[attr_name] != nil)
@attr_name=@@name_map[attr_name][0]
@attr_index=@@name_map[attr_name][1]
@attr_type=@@name_map[attr_name][2]
end
attr_change=attr_change.downcase
@attr_is_percent=false
if (attr_change.end_with?("%"))
@attr_is_percent=true
end
@attr_change=attr_change.to_f
end
def to_s
result=@attr_name.to_s+": "
if (@attr_is_percent)
result += "* "+@attr_change.to_s+"%"
else
result += "+ "+@attr_change.to_s
end
result
end
# Return true if the attribute change is a percent delta, false
# for an absolute value to add or subtract
def percent?
return @attr_is_percent
end
def self.initial()
@@name_strings=["mhp", "mmp", "atk", "def", "mat", "mdf",
"luk", "hit", "eva", "cri", "cev", "mev",
"mrf", "cnt", "hrg", "mrg", "trg", "tgr", "grd",
"rec", "pha", "mcr", "tcr", "pdr", "mdr", "fdr",
"exr"]
@@name_map={}
@@attr_list=[]
@@attr_list << [:mhp, 0,

aram]
@@attr_list << [:mmp, 1,

aram]
@@attr_list << [:atk, 2,

aram]
@@attr_list << [:def, 3,

aram]
@@attr_list << [:mat, 4,

aram]
@@attr_list << [:mdf, 5,

aram]
@@attr_list << [:luk, 6,

aram]
@@attr_list << [:hit, 7,

aram]
@@attr_list << [:eva, 0, :xparam]
@@attr_list << [:cri, 1, :xparam]
@@attr_list << [:cev, 2, :xparam]
@@attr_list << [:mev, 3, :xparam]
@@attr_list << [:mrf, 4, :xparam]
@@attr_list << [:cnt, 5, :xparam]
@@attr_list << [:hrg, 6, :xparam]
@@attr_list << [:mrg, 7, :xparam]
@@attr_list << [:trg, 8, :xparam]
@@attr_list << [:tgr, 0, :sparam]
@@attr_list << [:grd, 1, :sparam]
@@attr_list << [:rec, 2, :sparam]
@@attr_list << [

ha, 3, :sparam]
@@attr_list << [:mcr, 4, :sparam]
@@attr_list << [:tcr, 5, :sparam]
@@attr_list << [

dr, 6, :sparam]
@@attr_list << [:mdr, 7, :sparam]
@@attr_list << [:fdr, 8, :sparam]
@@attr_list << [:exr, 9, :sparam]
index=0
@@name_strings.each {
|entry| @@name_map=@@attr_list[index]
index += 1
}
end
end
# Setup the hashmap
Attribute_entry::initial
# Represents a single level of weapon/item mastery
class Item_level
attr_reader

item_type)
attr_reader

item_index)
attr_reader

skill_list)
attr_reader

attrs)
# The Constructor
def initialize(item_type, item_index, count, attr_list, skill_list)
# Set to something like "mastery" ro identify what type of item is
# affected
item_type=item_type.downcase
# This is for future use --- perhaps allow Shields to upgrade...
@item_type=:weapon
# Set to 1 to affect Weapon Type #1 in the System tab, 2 for Type #2, # etc.
@item_index=item_index.to_i
@skill_list=[]
if (skill_list != nil)
skill_list.each {
|entry| @skill_list << entry.to_i
}
end
@attrs=attr_list
@key=@item_type.to_s+":"+@item_index.to_s
end
def to_s
result="Entry: "+@item_type.to_s+", index "+@item_index.to_s+", attrs:"
@attrs.each { |entry| result += entry.to_s+"\n"
}
result += "Skills: ["
@skill_list.each { |entry| result += "+entry.to_s
}
result +=]"
result
end
def key
return @key
end
end
# Contains all of the mastery levels for a particular Class
class Item_mastery
attr_reader

level_set)
def initialize(level_set)
@level_set=level_set
end
end
# The current mastery level for a particular Class/Actor/Item Type combination
class Current_mastery
attr_reader

class_id)
attr_reader

actor_id)
attr_reader

item_type)
attr_reader

item_index)
attr_reader

use_count)
def initialize(class_id, actor_id, item_type, item_index)
@class_id=class_id.to_i
@actor_id=@actor_id.to_i
# For future use
@item_type=:weapon
@mastery_index=0
@item_index=item_index.to_i
@use_count=0
@key=Current_mastery::make_key(@item_type, @item_index)
end
def self.make_key(item_type, item_index)
result=item_type.to_s+":"+item_index.to_s
return result
end
def key()
@key
end
def increment()
@use_count += 1
end
# Use when we advance to a new level of mastery
def advance_level()
@use_count=0
@mastery_index = @mastery_index+1
end
def to_s
result="Current: actor="+@actor_id.to_s+", class="+@class_id.to_s
result +=", item type="+@item_type.to_s+", index="+@item_index.to_s
result += ", use count="+@use_count+", level="+@mastery_index.to_s
return result
end
# Given an item type and item index, this examines the Class-specific mastery
# information and returns the appropriate Item_level object. This will return
# nil if none is applicable
def get_mastery_data()
defs=$data_classes[@class_id].mastery_defs()
if (defs == nil)
return nil
end
current_level=@mastery_index
last_match=nil
defs.each { |current|
if (current.item_type != @item_type || current.item_index != @item_index)
next
end
last_match=current
if (current_level == 0)
return last_match
end
current_level -= 1
}
return last_match
end
end
# Class which represents all of the current mastery meta-data for a given
# Actor and Class
class Class_mastery
attr_reader

class_id)
attr_reader

actor_id)
def initialize(class_id, actor_id)
@class_id=class_id.to_i
@actor_id=actor_id.to_i
@current_mastery={}
end
# Returns a Current_mastery object for the item type and index
# Creates one if not found
def get(item_type, item_index)
key=Current_mastery::make_key(item_type, item_index)
result=@current_mastery[key]
if (result != nil)
return result
end
result=Current_mastery.new(@class_id, @actor_id, item_type, item_index)
@current_mastery[key]=result
return result
end
# Callers can use this after incrementing use times
def update(current_mastery)
key=current_mastery.key
@current_mastery[key]=current_mastery
end
end
# This class holds ALL of the current mastery levels in a large hashmap
class Complete_class_cache
def initialize()
@mastery={}
end
def make_key(actor_id, class_id, item_type, item_id)
result=actor_id.to_s+"-"+class_id.to_s+"-"+item_type.to_s+"-"+item_id.to_s
return result
end
# Returns a Current_mastery appropriate for the passed in actor ID
# class id, item type (such as :weapon) and item_id (such as 1 for Weapon #1)
# Creates one if needed
def get(actor_id, class_id, item_type, item_id)
key=make_key(actor_id, class_id, item_type, item_id)
current_entry=@mastery[key]
if (current_entry != nil)
result=current_entry.get(item_type, item_id)
return result
end
current_entry=Class_mastery.new(class_id, actor_id)
@mastery[key]=current_entry
result=current_entry.get(item_type, item_id)
return result
end
end
# Create the singleton
def self.init()
@@class_cache=Complete_class_cache.new()
end
# Returns the matching Current_mastery item
def self.get(actor_id, class_id, item_type, item_id)
return @@class_cache.get(actor_id, class_id, item_type, item_id)
end
end
# Create the static member
WeaponMastery::init()
# We update the param methods to take into account any Class specifics
class Game_Actor < Game_Battler
alias actor_setup setup
alias actor_param param
alias actor_xparam xparam
alias actor_sparam sparam
def setup(actor_id)
actor_setup(actor_id)
end
def param(param_id)
value=actor_param(param_id)
# Apply class modifiers for the current weapons (dual wield)
weapons.each { |current| weapon_id=current.wtype_id
current_mastery=WeaponMastery::get(@actor_id, @class_id, :weapon, weapon_id)
mastery_data=current_mastery.get_mastery_data()
if (mastery_data == nil)
next
end
# Look for a matching attribute
mastery_data.attrs.each { |attr|
if (attr.attr_type !=

aram || attr.attr_index != param_id)
next
end
# Adjust the value
if (attr.percent?)
value = (value * attr.attr_change) / 100
else
value += attr.attr_change
end
}
}
[[value, param_max(param_id)].min, param_min(param_id)].max.to_i
end
#--------------------------------------------------------------------------
# * Get Ex-Parameter
#--------------------------------------------------------------------------
def xparam(xparam_id)
value=actor_xparam(xparam_id)
# Apply class modifiers for the current weapons (dual wield)
weapons.each { |current| weapon_id=current.wtype_id
current_mastery=WeaponMastery::get(@actor_id, @class_id, :weapon, weapon_id)
mastery_data=current_mastery.get_mastery_data()
if (mastery_data == nil)
next
end
# Look for a matching attribute
mastery_data.attrs.each { |attr|
if (attr.attr_type != :xparam || attr.attr_index != param_id)
next
end
# Adjust the value
if (attr.percent?)
value = (value * attr.attr_change) / 100
else
value += attr.attr_change
end
}
}
value
end
#--------------------------------------------------------------------------
# * Get Sp-Parameter
#--------------------------------------------------------------------------
def sparam(sparam_id)
value=actor_sparam(sparam_id)
# Apply class modifiers for the current weapons (dual wield)
weapons.each { |current| weapon_id=current.wtype_id
current_mastery=WeaponMastery::get(@actor_id, @class_id, :weapon, weapon_id)
mastery_data=current_mastery.get_mastery_data()
if (mastery_data == nil)
next
end
# Look for a matching attribute
mastery_data.attrs.each { |attr|
if (attr.attr_type != :sparam || attr.attr_index != param_id)
next
end
# Adjust the value
if (attr.percent?)
value = (value * attr.attr_change) / 100
else
value += attr.attr_change
end
}
}
value
end
end
#--------------------------------------------------------------
# Read the mastery definitions
class RPG::Class
def read_note_tag_as_nodes
if (@node_set == nil)
reader=Tag_reader::XML_Reader.new
@node_set=reader.read(@note)
end
return @node_set
end
# Returns the current set of mastery definitions
def mastery_defs
if (@mastery_defs != nil)
if (@mastery_defs.empty?)
return nil
end
return @mastery_defs
end
@mastery_defs=[]
nodes=read_note_tag_as_nodes
if (nodes == nil)
return nil
end
# This is to simplify the search
@attr_types=["mhp", "mmp", "atk", "def", "mat", "mdf",
"luk", "hit", "eva", "cri",
"cev", "mev", "mrf", "cnt", "hrg", "mrg",
"trg", "tgr", "grd", "rec", "pha", "mcr",
"tcr", "pdr", "mdr", "fdr", "exr"]
nodes.each {
|current|
if (current.name != "mastery")
next
end
# A "type" tag is required
weapon_type=current.attributes["type"]
if (weapon_type == nil)
next
end
# Look for "count" or "skills" tags. Both are
# optional
current_skills=current.attributes["skills"]
if (current_skills != nil)
current_skills=current_skills.split(",")
end
current_count=current.attributes["count"]
# Assemble a list of attribute modifications
attr_mods=[]
current.attributes.each {
|key, value| key=key.downcase
if (@attr_types.include?(key))
new_mod=WeaponMastery::Attribute_entry.new(key, value)
attr_mods << new_mod
end
}
# If attr_mods is not empty, add it
if (attr_mods.empty?)
next
end
new_level=WeaponMastery::Item_level.new(":weapon", weapon_type, current_count, attr_mods, current_skills)
@mastery_defs << new_level
}
return @mastery_defs
end
end