ForeverZer0
Sponsor
Blacksmith System Version: 2.0
By: ForeverZer0
Introduction
Will allow you to create a complete blacksmith system. The player will be able to forge equipment/items by using combinations of weapons, armors, and items in their possession. Also includes a "Enchantment" feature that will allow the player to use special items to add stats, elemental efficiencies, and state altering to weapons and armor. The extraction feature allows for the breaking down of current equipment and items into other ones.
Features
Screenshots
Demo
Demo Link
Script
[rgss]#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
# Blacksmith Shop
# Author: ForeverZer0
# Type: Custom Shop System
# Date: 4.23.2011
# Version: v.2.0
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
#
# Explanation:
# Will allow you to create a complete blacksmithing system. The player will be
# able to forge equipment/items by using combinations of weapons, armors, and
# items in their possession. Also includes a "Enchantment" feature that will
# allow the player to use special items to add stats, elementel efficiencies,
# and state altering to weapons and armor. The extraction feature allows for
# the breaking down of current equipment and items into other ones.
#
# Features:
# - Completely configurable item requirements for every item.
# - Configurable blacksmith 'fees' for every weapon/armor
# - Can use as many different items, with different quantities for each piece
# of equipment.
# - Variable "skill" levels for Blacksmith shops, which lets you decide
# which features the Blacksmith can do.
# - Only have to use a single script call to for the Blacksmith's shop.
# - Can recycle old equipment by extracting items from weapons/armors/items.
#
# Instructions:
# - Place script below debug and above main
# - Configuration and instructions for each are below
# - To call blacksmith shop, this script call:
#
# w = [ WEAPON_IDS ] (Use as many as needed, seperate with commas)
# a = [ ARMOR_IDS ]
# i = [ ITEM_IDS ]
# $scene = Scene_BlackSmith.new(w, a, i)
#
# - All IDs that you included in the script call for items will be be
# available for forging in that shop.
# - You can also include a fourth argument to the call to set the Blacksmith's
# "skill level". Just make an array of true/false elemenets, set up like
# this:
#
# [CAN_FORGE?, CAN_EXTRACT?, CAN_ENCHANT?]
#
# If you are not using the Enchant feature, omit the last option. Just make
# sure that if you do include this argument that the array has a value for
# each skill.
#
# Credits/Thanks:
# - ForeverZer0, for the script.
# - RoseSkye, huge thanks for beta-testing and demo map.
#
# Author's Notes:
# Please report any bugs/issues at http://www.chaos-project.com
#
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
module Blacksmith
#===============================================================================
# BEGIN CONFIGURATION
#===============================================================================
FORGE_SE = ['006-System06', 80, 100]
# SE played when an item is forged. ['FILENAME', VOLUME, PITCH]
EXTRACT_SE = ['020-Teleport03', 80, 100]
# SE played when an item extraction is performed. ['FILENAME', VOLUME, PITCH]
ENCHANT_SE = ['020-Teleport03', 80, 100]
# SE played when an item enchantment is performed. ['FILENAME', VOLUME, PITCH]
USE_ENCHANTMENTS = true
# Set to true to enable the "Enchant" feature of the system.
NO_ENCHANT_WEAPONS = []
NO_ENCHANT_ARMORS = []
# Include IDs of any equipment that cannot be enchanted in the respective
# arrays, seperating by commas. Ignore these if not using enchant feature.
# Define the colors used for the text in the Blacksmith shop.
PLUS_COLOR = Color.new(128, 255, 128)
MINUS_COLOR = Color.new(255, 128, 128)
MAP_BACK = true
# Set to true if you would like slightly opaque windows with the map showing
# through.
#-----------------------------------------------------------------------------
# FORGE DATABASE
#-----------------------------------------------------------------------------
# Define the materials used for each weapon/armor/item that can be forged and
# extracted. They configuration is slightly different than what it was in the
# first version of the script. You can seperately define materials that are
# given during extraction if you like, or ignore it and it will simply return
# the same materials it takes to forge them. It works like this:
#
# STEP 1:
# Create a new "case" in the appropriate method below for the type of item
# you are trying to define. There are three of them, one each for weapons,
# armors, and items. Just use this syntax:
#
# when DATABASE_ID then []
#
# STEP 2:
# Now you can begin to add materials to forge the item. Each material has
# an number which defines what type of item is is. Here is the "key":
#
# 0 = Weapon
# 1 = Armor
# 2 = Item
#
# To define a material for an item, you simply create a three element array
# using this format:
# [ITEM_TYPE, DATABASE_ID, QUANTITY]
#
# ...and add it the appropriate empty array in the case statement you made
# in Step 1. You can add as many different items as you please to forge an
# weapon/armor/item, simply seperate the material arrays with commas. See
# below for a few examples.
#-----------------------------------------------------------------------------
def self.weapon_forges(id)
return case id
when 1 then [[2, 33, 3], [2, 42, 1]] # Bronze Sword
when 2 then [[0, 1, 1], [2, 34, 2], [2, 42, 1]] # Iron Sword
when 3 then [[0, 2, 1], [2, 34, 10]] # Steel Sword
when 4 then [[0, 2, 2], [2, 35, 3], [2, 41, 1]] # Mythril Sword
when 5 then [[2, 33, 5], [2, 43, 1]] # Bronze Spear
when 6 then [[2, 34, 4], [0, 5, 1], [2, 43, 1]] # Iron Spear
when 7 then [[0, 6, 2], [2, 34, 2], [2, 43, 1]] # Steel Spear
when 8 then [[2, 35, 8], [2, 43, 1]] # Mythril Spear
end
end
def self.armor_forges(id)
return case id
when 1 then []
when 2 then []
when 3 then []
when 4 then []
when 5 then []
end
end
def self.item_forges(id)
return case id
when 2 then [[2, 1, 5]]
when 3 then [[2, 2, 5]]
when 5 then [[2, 4, 5]]
when 6 then [[2, 5, 5]]
end
end
#-----------------------------------------------------------------------------
# EXTRACT DATABASE
#-----------------------------------------------------------------------------
# Here you can define the items received when a specific item is extracted.
# It can be setup the same as way as above. Items left undefined will return
# the same items that are required to forge it. You can define an item with an
# empty array to have it return no items, though it can still return gold.
#-----------------------------------------------------------------------------
def self.weapon_extractions(id)
return case id
when 1 then [[2, 33, 1], [2, 42, 1]]
when 2 then [[2, 34, 1], [2, 41, 1]]
when 3 then [[2, 34, 2], [0, 1, 1]]
when 4 then [[2, 33, 5], [2, 34, 5], [2, 41, 1]]
when 5 then [[2, 33, 1], [2, 43, 1]]
when 6 then [[2, 34, 1], [2, 43, 1]]
when 7 then [[2, 34, 2], [0, 5, 1]]
when 8 then [[2, 33, 5], [2, 34, 5], [2, 43, 1]]
else
self.weapon_forges(id)
end
end
def self.armor_extractions(id)
return case id
when 1 then []
when 2 then []
when 3 then []
when 4 then []
when 5 then []
else
self.weapon_forges(id)
end
end
def self.item_extractions(id)
return case id
when 1 then [] # Potion
when 2 then [[2, 1, 2]] # High Potion
when 3 then [[2, 2, 2], [2, 1, 2]] # Full Potion
when 4 then [] # Perfume
when 5 then [[2, 4, 2]] # High Perfume
when 6 then [[2, 4, 2], [2, 5, 2]] # Full Perfume
else
self.item_forges(id)
end
end
#-----------------------------------------------------------------------------
# GOLD DATABASE
#-----------------------------------------------------------------------------
# Here you can define the amount of gold that is required to forge an item,
# and the amount that is given if extracted. There are three methods, one each
# for weapons, armors, and items. Simply follow this pattern for each
# category:
#
# when DATABASE_ID then [FORGE_PRICE, EXTRACT_GOLD,]
#-----------------------------------------------------------------------------
def self.weapon_gold(id)
return case id
when 1 then [200, 50]
when 2 then [450, 225]
when 3 then [1000, 525]
when 4 then [1200, 200]
when 5 then [300, 75]
when 6 then [550, 275]
when 7 then [1200, 600]
when 8 then [1500, 650]
else
[0, 0]
end
end
def self.armor_gold(id)
return case id
when 1 then []
when 2 then []
when 3 then []
when 4 then []
when 5 then []
else
[0, 0]
end
end
def self.item_gold(id)
return case id
when 1 then [100, 0]
when 2 then [50, 25]
when 3 then [250, 25]
when 4 then [100, 0]
when 5 then [50, 25]
when 6 then [250, 25]
else
[0, 0]
end
end
#-----------------------------------------------------------------------------
# ENCHANT DATABASE
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Here you can define what items will alter stats when used to enchant with.
# You need to create a two element array, and add it to the respective array
# below that corresponds with the desired item.
#
# ex.
# when ITEM_ID then [[KEYWORD, VALUE], [KEYWORD, VALUE]]
#
# KEYWORD: See below for a list of possible keywords. Stat changes that
# can affect only weapons will have no effect on armors, and
# vice-versa.
# VALUE : The amount by which to change the stat. Negative values will
# lower the stat.
#-----------------------------------------------------------------------------
# KEYWORDS:
#
# 'ATK' (Weapon Only) 'DEX' 'PDEF'
# 'EVA' (Armor Only) 'AGI' 'MDEF'
# 'STR' 'INT'
#
# ** Keywords have to be written EXACTLY as they appear.
#-----------------------------------------------------------------------------
def self.enchant_stats(item_id)
return case item_id
when 39 then [['AGI', 5]] # Carrot
when 40 then [['STR', 15], ['ATK', 5]] # Behemoth Juice
end
end
#-----------------------------------------------------------------------------
# Define state altering enchantments.
#
# ex.
# when ITEM_ID then [[VALUE, STATE_ID], [VALUE, STATE_ID]]
#
# VALUE: One of three different values to represent states efficiency.
# -1 = Minus state (Does nothing on armors)
# 0 = Neutral
# 1 = Plus state
# STATE_ID: The ID in the database of the state.
#-----------------------------------------------------------------------------
def self.enchant_states(item_id)
return case item_id
when 38 then [[1, 2], [1, 4], [1, 6]] # Chaos Orb
end
end
#-----------------------------------------------------------------------------
# Define element altering enchantments.
#
# ex.
# when ITEM_ID then [[VALUE, ELEMENT_ID], [VALUE, ELEMENT_ID]]
#
# VALUE: One of two different values to represent element efficiency.
# true = Uses element
# false = Doesn't use element (Negates element if present)
# ELEMENT_ID: The ID in the database of the element.
#-----------------------------------------------------------------------------
def self.enchant_elements(item_id)
return case item_id
when 36 then [[true, 3], [false, 5]] # Amethyst
when 37 then [[true, 1]] # Ruby
end
end
#-----------------------------------------------------------------------------
# Define the amount of gold it takes to enchant a weapon or armor with the
# item.
#-----------------------------------------------------------------------------
def self.enchant_gold(item_id)
return case item_id
when 36 then 1500
when 37 then 1100
when 38 then 1337
when 39 then 250
when 40 then 7500
else
0
end
end
#===============================================================================
# END CONFIGURATION
#===============================================================================
def self.materials?(type, id)
# Get the required materials for the item
materials = case type
when 0 then [self.weapon_forges(id), self.weapon_gold(id)]
when 1 then [self.armor_forges(id), self.armor_gold(id)]
when 2 then [self.item_forges(id), self.item_gold(id)]
end
materials[0] = [] if materials[0] == nil
# Check gold, skipping item check if there is not enough.
if $game_party.gold >= materials[1][0]
# Iterate all required materials, making sure enough are in inventory.
materials[0].each {|item|
# Branch by the type of the item.
result = case item[0]
when 0 then ($game_party.weapon_number(item[1]) >= item[2])
when 1 then ($game_party.armor_number(item[1]) >= item[2])
when 2 then ($game_party.item_number(item[1]) >= item[2])
end
# End iteration and return false immidiately if missing required item.
return false unless result
}
return true
end
return false
end
#-----------------------------------------------------------------------------
def self.update_database(item)
# Open the Weapons or Armors .rxdata file and add the created item.
begin
if item.is_a?(RPG::Weapon)
file, data = 'Data/Weapons.rxdata', $data_weapons
elsif item.is_a?(RPG::Armor)
file, data = 'Data/Armors.rxdata', $data_armors
else
return
end
data[item.id] = item
file = File.open(file, 'wb')
Marshal.dump(data, file)
file.close
rescue
print "Could not add #{item.name} to Database."
end
end
#-----------------------------------------------------------------------------
def self.create_item(base_item, enchant_item)
base = base_item.clone
# Do to clone only making shallow copies, it is necessary to also create
# seperate clones of the element and state sets, otherwise the original
# is affected too.
if base_item.is_a?(RPG::Weapon)
elem_set = base_item.element_set.clone
plus_state_set = base_item.plus_state_set.clone
minus_state_set = base_item.minus_state_set.clone
else
guard_elem_set = base_item.guard_element_set.clone
guard_state_set = base_item.guard_state_set.clone
end
# Gather the enchantment data.
stats = self.enchant_stats(enchant_item.id)
states = self.enchant_states(enchant_item.id)
elements = self.enchant_elements(enchant_item.id)
# Iterate through stats
if stats != nil
stats.each {|stat|
case stat[0]
when 'ATK'
if base.is_a?(RPG::Weapon)
base.atk += stat[1]
end
when 'EVA'
if base.is?(RPG::Armor)
base.eva += stat[1]
end
when 'STR' then base.str_plus += stat[1]
when 'DEX' then base.dex_plus += stat[1]
when 'AGI' then base.agi_plus += stat[1]
when 'INT' then base.int_plus += stat[1]
when 'PDEF' then base.pdef_plus += stat[1]
when 'MDEF' then base.mdef_plus += stat[1]
end
}
end
# Iterate through states
if states != nil
states.each {|state|
id = state[1]
if base.is_a?(RPG::Weapon)
case state[0]
when -1
minus_state_set.push(id) unless minus_state_set.include?(id)
plus_state_set -= [id]
when 0
minus_state_set -= [id]
plus_state_set -= [id]
when 1
plus_state_set.push(id) unless plus_state_set.include?(id)
minus_state_set -= [id]
end
elsif base.is_a?(RPG::Armor)
if state[0] == 0
guard_state_set -= [id]
elsif state[0] == 1
guard_state_set.push(id) unless guard_state_set.inlcude?(id)
end
end
}
end
# Iterate through elements
if elements != nil
elements.each {|element|
id = element[1]
if base.is_a?(RPG::Weapon)
if element[0] && !elem_set.include?(id)
elem_set.push(id)
else
elem_set -= [id]
end
elsif base.is_a?(RPG::Armor)
if element[0] && !guard_elem_set.include?(id)
guard_elem_set.push(id)
else
guard_elem_set -= [id]
end
end
}
end
# Give the weapon a new ID, remove the old item, and add the new one.
if base.is_a?(RPG::Weapon)
$game_party.lose_weapon(base_item.id, 1)
base.id = $data_weapons.size
base.element_set = elem_set
base.plus_state_set = plus_state_set
base.minus_state_set = minus_state_set
$data_weapons[base.id] = base
$game_party.gain_weapon(base.id, 1)
elsif base.is_a?(RPG::Armor)
$game_party.lose_armor(base_item.id, 1)
base.id = $data_armors.size
base.guard_element_set = guard_elem_set
base.guard_state_set = guard_state_set
$data_armors[base.id] = base
$game_party.gain_armor(base.id, 1)
end
# Add new item to class equipment
self.update_class_equipment(base_item, base)
# Save the new item to the database.
self.update_database(base)
end
#-----------------------------------------------------------------------------
def self.update_class_equipment(old, new)
# Adds the created item to class equipment that could equip the original
$data_classes.each_index {|i|
next if $data_classes == nil
if old.is_a?(RPG::Weapon) && $data_classes.weapon_set.include?(old.id)
$data_classes.weapon_set.push(new.id)
elsif old.is_a?(RPG::Armor) && $data_classes.armor_set.include?(old.id)
$data_classes.armor_set.push(new.id)
end
}
# Marshal the new data.
begin
file = File.open('Data/Classes.rxdata', 'wb')
Marshal.dump($data_classes, file)
file.close
rescue
print "Could not update RPG::Class database."
end
end
end
$blacksmith = 2.0
#===============================================================================
# ** Window_BlacksmithCommand
#===============================================================================
class Window_BlacksmithCommand < Window_Selectable
def initialize(level)
super(0, 64, 480, 64)
@level = level
if Blacksmith::USE_ENCHANTMENTS
@item_max = @column_max = 4
@commands = ['Forge', 'Extract', 'Enchant', 'Exit']
else
@item_max = @column_max = 3
@commands = ['Forge', 'Extract', 'Exit']
end
self.contents = Bitmap.new(self.width - 32, self.height - 32)
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def refresh
self.contents.clear
(0...@item_max).each {|i| draw_item(i) }
end
#-----------------------------------------------------------------------------
def draw_item(index)
w = self.width / @item_max
self.contents.font.color = @level[index] ? normal_color : disabled_color
self.contents.draw_text(4 + (w * index), 0, w, 32, @commands[index])
end
end
#===============================================================================
# ** Window_BlacksmithForge
#===============================================================================
class Window_BlacksmithForge < Window_Selectable
def initialize(shop_goods)
super(0, 128, 368, 352)
# Initialize window and create instance variable to store available goods.
@shop_goods = shop_goods
self.active = self.visible = false
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def item
return @data[self.index]
end
#-----------------------------------------------------------------------------
def refresh(enchanting = false)
# Dispose bitmap and set to nil if not already.
if self.contents != nil
self.contents = self.contents.dispose
end
# Set flag for enchanting
@enchanting = enchanting
# Create array of equipment, depending on flag
if @enchanting
@data = []
# Add weapons
($data_weapons - [nil]).each {|weapon|
if $game_party.weapon_number(weapon.id) > 0 &&
!Blacksmith::NO_ENCHANT_WEAPONS.include?(weapon.id)
@data.push(weapon)
end
}
# Add Armor
($data_armors - [nil]).each {|armor|
if $game_party.armor_number(armor.id) > 0 &&
!Blacksmith::NO_ENCHANT_ARMORS.include?(armor.id)
@data.push(armor)
end
}
else
@data = @shop_goods
end
# Create a new bitmap, sized for available items
@item_max = @data.size
if @item_max > 0
self.contents = Bitmap.new(width - 32, row_max * 32)
(0...@item_max).each {|i| draw_item(i) }
end
end
#-----------------------------------------------------------------------------
def draw_item(index)
item = @data[index]
# Set a few local variables depending on the type of item.
case item
when RPG::Weapon
quantity = $game_party.weapon_number(item.id)
price, type = Blacksmith.weapon_gold(item.id)[0], 0
when RPG::Armor
quantity = $game_party.armor_number(item.id)
price, type = Blacksmith.armor_gold(item.id)[0], 1
when RPG::Item
quantity = $game_party.item_number(item.id)
price, type = Blacksmith.item_gold(item.id)[0], 2
end
# Don't check material requirments for forging wjen enchanting
result = @enchanting ? true : Blacksmith.materials?(type, item.id)
# Determine the color to use for drawing the item name.
if quantity < 99 && result
self.contents.font.color = normal_color
else
self.contents.font.color = disabled_color
end
# Draw the item name, icon, and price.
x, y = 4, index * 32
rect = Rect.new(x, y, self.width - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
opacity = self.contents.font.color == normal_color ? 255 : 128
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
self.contents.draw_text(x + 28, y, 212, 32, item.name, 0)
if !@enchanting
self.contents.draw_text(x + 240, y, 88, 32, price.to_s, 2)
end
end
#-----------------------------------------------------------------------------
def update_help
@help_window.set_text(self.item == nil ? '' : self.item.description)
end
end
#===============================================================================
# ** Window_BlacksmithExtract
#===============================================================================
class Window_BlacksmithExtract < Window_Selectable
def initialize
super(0, 128, 368, 352)
self.active = self.visible = false
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def item
return @data[self.index]
end
#-----------------------------------------------------------------------------
def refresh
# Dispose current bitmap if defined.
if self.contents != nil
self.contents = self.contents.dispose
end
# Create list of items in inventory
@data = []
(1...$data_weapons.size).each {|i|
result = (Blacksmith.weapon_extractions(i) != nil ||
Blacksmith.weapon_gold(i)[1] != 0)
if $game_party.weapon_number(i) > 0 && result
@data.push($data_weapons)
end
}
(1...$data_armors.size).each {|i|
result = (Blacksmith.armor_extractions(i) != nil ||
Blacksmith.armor_gold(i)[1] != 0)
if $game_party.armor_number(i) > 0 && result
@data.push($data_armors)
end
}
(1...$data_items.size).each {|i|
result = (Blacksmith.item_extractions(i) != nil ||
Blacksmith.item_gold(i)[1] != 0)
if $game_party.item_number(i) > 0 && result
@data.push($data_items)
end
}
@item_max = @data.size
# Create a new bitmap that will contain the listed items
if @item_max > 0
self.contents = Bitmap.new(width - 32, row_max * 32)
(0...@item_max).each {|i| draw_item(i) }
end
end
#-----------------------------------------------------------------------------
def draw_item(index)
item = @data[index]
# Set a few local variables depending on the type of item.
quantity = case item
when RPG::Weapon then $game_party.weapon_number(item.id)
when RPG::Armor then $game_party.armor_number(item.id)
when RPG::Item then $game_party.item_number(item.id)
end
# Draw the name, icon, and quantity of the item.
x, y = 4, index * 32
rect = Rect.new(x, y, self.width / @column_max - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
self.contents.draw_text(x + 28, y, 212, 32, item.name, 0)
end
#-----------------------------------------------------------------------------
def update_help
@help_window.set_text(self.item == nil ? '' : self.item.description)
end
end
#===============================================================================
# ** Window_BlacksmithMaterials
#===============================================================================
class Window_BlacksmithMaterials < Window_Base
attr_accessor :active
def initialize
# Initialize window size and coordinates.
super(0, 128, 368, 352)
self.visible = @active = false
end
#-----------------------------------------------------------------------------
def refresh(item, type = 0)
# Clear the bitmap and set the new materials.
if self.contents != nil
self.contents = self.contents.dispose
end
set_materials(item, type)
# Create a new bitmap, based off the amount of materials
if @materials != nil && @materials.size > 0
self.contents = Bitmap.new(self.width - 32, 64 + (@materials.size * 32))
# Draw each material and quantity required.
self.contents.font.color = system_color
word = type == 0 ? 'Cost' : ($data_system.words.gold + ':')
self.contents.draw_text(4, 0, 212, 32, word, 0)
text = type == 0 ? 'Required Materials:' : 'Extractable Materials:'
self.contents.draw_text(4, 32, 368, 32, text, 0)
self.contents.font.color = normal_color
self.contents.draw_text(244, 0, 88, 32, @price.to_s, 2)
# Enumerate through each material.
@materials.each_index {|i|
# Set local variable to current item, depending on type.
case @materials[0]
when 0
item = $data_weapons[@materials[1]]
enough = $game_party.weapon_number(item.id) >= @materials[2]
when 1
item = $data_armors[@materials[1]]
enough = $game_party.armor_number(item.id) >= @materials[2]
when 2
item = $data_items[@materials[1]]
enough = $game_party.item_number(item.id) >= @materials[2]
end
next if item == nil
# Set local variable to store required amount of this item.
required = @materials[2]
# Set color of text, draw grayed if out if forging and not enough.
self.contents.font.color = normal_color
if type == 0 && !enough
self.contents.font.color = disabled_color
end
# Set coordinates of current line.
x, y = 4, 64 + (i * 32)
# Draw item name, icon, and required amount.
rect = Rect.new(x, y, self.width - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
opacity = self.contents.font.color == normal_color ? 255 : 128
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
self.contents.draw_text(x + 28, y, 212, 32, item.name)
self.contents.draw_text(x + 272, y, 48, 32, 'x', 0)
self.contents.draw_text(x + 272, y, 48, 32, required.to_s, 2)
}
elsif @price > 0
self.contents = Bitmap.new(self.width - 32, 64)
self.contents.font.color = system_color
self.contents.draw_text(4, 0, 212, 32, $data_system.words.gold + ':')
self.contents.font.color = normal_color
self.contents.draw_text(244, 0, 88, 32, @price.to_s, 2)
self.contents.draw_text(4, 32, 368, 32, 'No Materials')
end
end
#-----------------------------------------------------------------------------
def set_materials(item, type)
# Sets the required/extractable items for the passed item.
id = item.id
if item.is_a?(RPG::Weapon)
@materials = type == 0 ? Blacksmith.weapon_forges(id) :
Blacksmith.weapon_extractions(id)
@price = Blacksmith.weapon_gold(id)[type]
elsif item.is_a?(RPG::Armor)
@materials = type == 0 ? Blacksmith.armor_forges(id) :
Blacksmith.armor_extractions(id)
@price = Blacksmith.armor_gold(id)[type]
else
if @materials != 2
@materials = type == 0 ? Blacksmith.item_forges(id) :
Blacksmith.item_extractions(id)
@price = Blacksmith.item_gold(id)[type]
end
end
end
#-----------------------------------------------------------------------------
def update
# Allow scrolling of bitmap if materials don't fit in window.
if @active && self.contents != nil && self.contents.height > 320
if Input.trigger?(Input::UP)
if self.oy > 0
self.oy -= 32
$game_system.se_play($data_system.cursor_se)
end
elsif Input.trigger?(Input::DOWN)
if (self.oy + 320) < self.contents.height
self.oy += 32
$game_system.se_play($data_system.cursor_se)
end
end
end
end
end
#===============================================================================
# ** Window_BlacksmithStatus
#===============================================================================
class Window_BlacksmithStatus < Window_Base
def initialize
super(368, 128, 272, 352)
self.contents = Bitmap.new(width - 32, height - 32)
# Create array of sprites same size as party
@sprites = [Sprite.new, Sprite.new, Sprite.new, Sprite.new]
#@sprites = Array.new($game_party.actors.size, Sprite.new)
# Set coordinates of each sprite
@sprites.each_index {|i|
@sprites.x, @sprites.y = 380, 194 + (i * 64)#(i * 34)
@sprites.z = self.z + 10
}
self.visible = false
# Array of flags for walking
@walk = Array.new($game_party.actors.size, false)
@count, @item = 0, nil
refresh
end
#-----------------------------------------------------------------------------
def refresh
# Clear bitmap and turn off visiblity of each sprite.
self.contents.clear
@sprites.each {|sprite| sprite.visible = false }
# Return if selected item index is undefined.
return if @item == nil
self.contents.font.size = Font.default_size + 2
quantity = case @item
when RPG::Item then $game_party.item_number(@item.id)
when RPG::Weapon then $game_party.weapon_number(@item.id)
when RPG::Armor then $game_party.armor_number(@item.id)
end
self.contents.font.color = system_color
self.contents.draw_text(4, 0, 200, 32, 'Possessed:')
self.contents.font.color = normal_color
self.contents.draw_text(204, 0, 32, 32, quantity.to_s, 2)
# Disable walking animation and end method if selected item is a normal item
if @item.is_a?(RPG::Item)
@walk.collect! {|value| false }
return
end
# Change the font size.
self.contents.font.size = Font.default_size - 1
# Iterate each actor...
$game_party.actors.each_index {|i|
chr = $game_party.actors
# Set local variable to highlighted piece of equipment.
if @item.is_a?(RPG::Weapon)
eqp = $data_weapons[chr.weapon_id]
else
armors = [chr.armor1_id, chr.armor2_id, chr.armor3_id, chr.armor4_id]
eqp = $data_armors[armors[@item.kind]]
end
# Draw the actor sprite.
draw_actor_graphic(i, chr.equippable?(@item))
# Draw message and return if unequippable.
unless chr.equippable?(@item)
self.contents.font.color = normal_color
self.contents.draw_text(32, 54 + (i * 64), 150, 32, 'Cannot Equip')
next
else
# Create array of stat changes.
# [str, dex, agi, int, pdef, mdef, (atk || eva)]
stats = [
(@item == nil ? 0 : @item.str_plus) - (eqp == nil ? 0 : eqp.str_plus),
(@item == nil ? 0 : @item.dex_plus) - (eqp == nil ? 0 : eqp.dex_plus),
(@item == nil ? 0 : @item.agi_plus) - (eqp == nil ? 0 : eqp.agi_plus),
(@item == nil ? 0 : @item.int_plus) - (eqp == nil ? 0 : eqp.int_plus),
(@item == nil ? 0 : @item.pdef) - (eqp == nil ? 0 : eqp.pdef),
(@item == nil ? 0 : @item.mdef) - (eqp == nil ? 0 : eqp.mdef)
]
if @item.is_a?(RPG::Weapon)
stats.push(
(@item == nil ? 0 : @item.atk) - (eqp == nil ? 0 : eqp.atk))
elsif @item.is_a?(RPG::Armor)
stats.push(
(@item == nil ? 0 : @item.eva) - (eqp == nil ? 0 : eqp.eva))
end
# Set local variable to each piece of equipments' name
current_name = eqp == nil ? '' : eqp.name
new_name = @item == nil ? '' : @item.name
# If stats are all equal, show message and end method.
if stats.all? {|stat| stat == 0 }
self.contents.font.color = normal_color
if current_name != new_name
self.contents.draw_text(32, 54 + (i * 64), 150, 32, 'No Change')
else
self.contents.draw_text(32, 54 + (i * 64), 150, 32, 'Equipped')
end
next
end
# Draw any stat changes, using colors to show plus/minus changes
self.contents.font.size = (Font.default_size - 1)
self.contents.font.color = normal_color
self.contents.draw_text(104, 42 + (64*i), 32, 32, 'STR ') if stats[0] != 0
self.contents.draw_text(104, 58 + (64*i), 32, 32, 'DEX ') if stats[1] != 0
self.contents.draw_text(104, 74 + (64*i), 32, 32, 'AGI ') if stats[2] != 0
self.contents.draw_text(176, 42 + (64*i), 32, 32, 'INT ') if stats[3] != 0
self.contents.draw_text(32, 58 + (64*i), 32, 32, 'PDF ') if stats[4] != 0
self.contents.draw_text(32, 74 + (64*i), 32, 32, 'MDF ') if stats[5] != 0
if stats[-1] != 0
# Show stats changes for atk/eva, depending on the equipment type
stat = @item.is_a?(RPG::Weapon) ? 'ATK ' : 'EVA '
self.contents.draw_text(32, 42 + (64 * i), 32, 32, stat) if stat != 0
end
# Show any stat changes
stats.each_index {|j|
next if stats[j] == 0
xy = case j
when 0 then [132, 42 + (64 * i)]
when 1 then [132, 58 + (64 * i)]
when 2 then [132, 74 + (64 * i)]
when 3 then [198, 42 + (64 * i)]
when 4 then [60, 58 + (64 * i)]
when 5 then [60, 74 + (64 * i)]
when 6 then [60, 42 + (64 * i)]
end
# Set color and operator depending on value
if stats[j] < 0
self.contents.font.color, sign = Blacksmith::MINUS_COLOR, '-'
else
self.contents.font.color, sign = Blacksmith::PLUS_COLOR, '+'
end
self.contents.draw_text(xy[0], xy[1], 8, 32, sign, 1)
self.contents.draw_text(xy[0] + 10, xy[1], 24, 32, stats[j].abs.to_s)
}
end
}
end
#-----------------------------------------------------------------------------
def item=(item)
if @item != item
# Change the item variable and refresh.
@item = item
refresh
end
end
#-----------------------------------------------------------------------------
def draw_actor_graphic(id, equipable)
# Draws the actor graphic
actor = $game_party.actors[id]
@sprites[id].bitmap = RPG::Cache.character(actor.character_name,
actor.character_hue)
@sprites[id].src_rect.set(0, 0, @sprites[id].bitmap.width / 4,
@sprites[id].bitmap.height / 4)
# Set walking animation if item is equippable.
@walk[id] = equipable
@sprites[id].tone = Tone.new(0, 0, 0, equipable ? 0 : 255)
@sprites[id].visible = true
end
#-----------------------------------------------------------------------------
def update
super
# Update the walking animation.
@count = (@count + 1) % 40
$game_party.actors.each_index {|i|
next unless @walk
if @sprites.bitmap != nil
w = @sprites.bitmap.width / 4
h = @sprites.bitmap.height / 4
x = (@count / 10) * w
@sprites.src_rect.set(x, 0, w, h)
end
}
end
#-----------------------------------------------------------------------------
def visible=(bool)
super
# Set visible to the actor sprites as well.
@sprites.each {|sprite| sprite.visible = bool }
end
#-----------------------------------------------------------------------------
def dispose
super
# Dispose the actor sprites as well.
@sprites.each {|sprite| sprite.dispose }
end
end
#===============================================================================
# ** Window_BlacksmithExtract
#===============================================================================
class Window_BlacksmithEnchant < Window_Selectable
def initialize
super(0, 128, 368, 352)
self.active = self.visible = false
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def item
return @data[self.index]
end
#-----------------------------------------------------------------------------
def refresh
# Dispose current bitmap if defined.
if self.contents != nil
self.contents = self.contents.dispose
end
# Create list of items in inventory
@data = []
($data_items - [nil]).each {|item|
result = false
result = true if Blacksmith.enchant_stats(item.id) != nil
result = true if Blacksmith.enchant_states(item.id) != nil
result = true if Blacksmith.enchant_elements(item.id) != nil
@data.push(item) if result
}
@item_max = @data.size
# Create a new bitmap that will contain the listed items
if @item_max > 0
self.contents = Bitmap.new(width - 32, row_max * 32)
(0...@item_max).each {|i| draw_item(i) }
end
end
#-----------------------------------------------------------------------------
def draw_item(index)
item = @data[index]
# Set a few local variables depending on the type of item.
quantity = $game_party.item_number(item.id)
# Draw the name, icon, and quantity of the item.
x, y = 4, index * 32
rect = Rect.new(x, y, self.width / @column_max - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24))
self.contents.draw_text(x + 28, y, 212, 32, item.name, 0)
self.contents.draw_text(x + 240, y, 16, 32, ':', 1)
self.contents.draw_text(x + 256, y, 24, 32, quantity.to_s, 2)
end
#-----------------------------------------------------------------------------
def update_help
@help_window.set_text(self.item == nil ? '' : self.item.description)
end
end
#===============================================================================
# ** Scene_Blacksmith
#===============================================================================
class Scene_Blacksmith
def initialize(weapons = [], armors = [], items = [], level = nil)
# Set available goods for this shop based off passed argument.
@goods = []
@goods += weapons.collect {|id| $data_weapons[id] }
@goods += armors.collect {|id| $data_armors[id] }
@goods += items.collect {|id| $data_items[id] }
@goods.uniq!
# Configure the level variable
@level = (level == nil) ? Array.new(4, true) : (level + [true])
@enchants = Blacksmith::USE_ENCHANTMENTS
end
#-----------------------------------------------------------------------------
def main
# Create a Proc to handle confirmation of choices
@confirm_proc = Proc.new {
@help_window.set_text('Are you sure?')
window = Window_Command.new(160, ['Confirm', 'Cancel'])
window.x, window.y, window.z = 224, 192, 9999
loop { Graphics.update; Input.update; window.update
if Input.trigger?(Input::C) || Input.trigger?(Input::B)
result = (Input.trigger?(Input::C) && window.index == 0)
$game_system.se_play($data_system.cancel_se) unless result
window.dispose
break(result)
end
}
}
# Initialize the needed windows.
@command_window = Window_BlacksmithCommand.new(@level)
@forge_window = Window_BlacksmithForge.new(@goods)
@extract_window = Window_BlacksmithExtract.new
@materials_window = Window_BlacksmithMaterials.new
@enchant_window = Window_BlacksmithEnchant.new
@status_window = Window_BlacksmithStatus.new
@dummy_window = Window_Base.new(0, 128, 640, 352)
@gold_window = Window_Gold.new
@gold_window.x, @gold_window.y = 480, 64
@help_window = Window_Help.new
# Bind the help window to the other windows.
@forge_window.help_window = @extract_window.help_window = @help_window
@enchant_window.help_window = @help_window
# Set a windows array for easier handling of all windows.
@windows = [@command_window, @forge_window, @extract_window, @help_window,
@materials_window, @status_window, @dummy_window, @gold_window, @enchant_window]
# Create map sprite if configured to do so, and set window opacity
if Blacksmith::MAP_BACK
@spriteset = Spriteset_Map.new
@windows.each {|window| window.opacity = 160 }
end
# Execute the transition and start the main loop.
Graphics.transition
loop {Graphics.update; Input.update; update; break if $scene != self }
# Freeze the Graphics and dispose the windows
Graphics.freeze
@windows.each {|window| window.dispose }
if Blacksmith::MAP_BACK && @spriteset != nil
@spriteset.dispose
end
end
#-----------------------------------------------------------------------------
def update
# Update the windows
@windows.each {|window| window.update }
# Branch method depending on current action.
if @command_window.active
update_command
elsif @extract_window.active
update_extract
elsif @forge_window.active
update_forge
elsif @materials_window.active
update_materials
elsif @enchant_window.active
update_enchant
end
end
#-----------------------------------------------------------------------------
def back_to_map
# Play SE and return to the map.
$game_system.se_play($data_system.cancel_se)
$scene = Scene_Map.new
end
#-----------------------------------------------------------------------------
def update_command
# Set help text depending on the selected index.
help_text = case @command_window.index
when 0 then 'Use materials to forge new weapons, armors, and items.'
when 1 then 'Extract materials from weapons, armors, and items.'
when 2 then !@enchants ? 'Exit the shop.' :
'Enchant weapons, armors, and items using other items.'
when 3 then 'Exit the shop.'
end
@help_window.set_text(help_text)
# Check for Input.
if Input.trigger?(Input::B)
back_to_map
elsif Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
# Branch depending on command index
case @command_window.index
when 0
# Play SE and return if option is locked.
unless @level[0]
$game_system.se_play($data_system.buzzer_se)
return
end
# Shift scene to forge phase.
@dummy_window.visible = false
@forge_window.refresh(false)
@command_window.active = false
@forge_window.active = @forge_window.visible = true
@status_window.visible = true
when 1
# Play SE and return if option is locked.
unless @level[1]
$game_system.se_play($data_system.buzzer_se)
return
end
# Shift scene to extract phase
@extract_window.refresh
@command_window.active = @dummy_window.visible = false
@extract_window.active = @extract_window.visible = true
@status_window.visible = true
when 2
# Play SE and return if option is locked.
if @enchants
unless @level[2]
$game_system.se_play($data_system.buzzer_se)
return
end
# Shift scene to enchant phase.
@forge_window.refresh(true)
@command_window.active = @dummy_window.visible = false
@forge_window.active = @forge_window.visible = true
@status_window.visible = true
else
back_to_map
end
when 3
back_to_map
end
end
end
#-----------------------------------------------------------------------------
def update_forge
# Update for input when forge window is active.
@item = @status_window.item = @forge_window.item
if Input.trigger?(Input::B)
$game_system.se_play($data_system.cancel_se)
@command_window.active = @dummy_window.visible = true
@forge_window.active = @forge_window.visible = false
@status_window.visible = false
elsif Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
@forge_window.active = @forge_window.visible = false
if @command_window.index == 0
@materials_window.refresh(@item, 0)
@materials_window.visible = @materials_window.active = true
else
@enchant_window.refresh
@enchant_window.visible = @enchant_window.active = true
end
end
end
#-----------------------------------------------------------------------------
def update_extract
# Update for input when extraction window is active.
@item = @status_window.item = @extract_window.item
if Input.trigger?(Input::B)
$game_system.se_play($data_system.cancel_se)
@command_window.active = @dummy_window.visible = true
@extract_window.active = @extract_window.visible = false
@status_window.visible = false
elsif Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
@materials_window.refresh(@item, 1)
@extract_window.active = @extract_window.visible = false
@materials_window.visible = @materials_window.active = true
end
end
#-----------------------------------------------------------------------------
def update_enchant
# Update input on the enchantment items screen.
if Input.trigger?(Input::B)
# Return to previous screen if cancel button is pressed.
$game_system.se_play($data_system.cancel_se)
@enchant_window.visible = @enchant_window.active = false
@forge_window.active = @forge_window.visible = true
elsif Input.trigger?(Input::C) && @confirm_proc.call
enchant_item
end
end
#-----------------------------------------------------------------------------
def enchant_item
# Apply enchantment to weapon/armor using item
$game_party.lose_item(@enchant_window.item.id, 1)
Blacksmith.create_item(@forge_window.item, @enchant_window.item)
# Play SE
$game_system.se_play(RPG::AudioFile.new(*Blacksmith::ENCHANT_SE))
# Refesh windows
[@enchant_window, @status_window].each {|window| window.refresh }
@forge_window.refresh(true)
# Return to previous screen
@enchant_window.visible = @enchant_window.active = false
@forge_window.active = @forge_window.visible = true
end
#-----------------------------------------------------------------------------
def update_materials
# Show help text.
text = 'Press the Action Button to proceed. Press Cancel to go back'
@help_window.set_text(text)
if Input.trigger?(Input::B)
# Return to previous screen if cancel button is pressed.
$game_system.se_play($data_system.cancel_se)
@materials_window.visible = @materials_window.active = false
if @command_window.index == 0
@forge_window.active = @forge_window.visible = true
else
@extract_window.active = @extract_window.visible = true
end
elsif Input.trigger?(Input::C) && @confirm_proc.call
@command_window.index == 0 ? forge_item : extract_item
end
end
#-----------------------------------------------------------------------------
def forge_item
# Set local variables depending on item type.
case @item
when RPG::Weapon
quantity, type = $game_party.weapon_number(@item.id), 0
materials = Blacksmith.weapon_forges(@item.id)
price = Blacksmith.weapon_gold(@item.id)[0]
when RPG::Armor
quantity, type = $game_party.armor_number(@item.id), 1
materials = Blacksmith.armor_forges(@item.id)
price = Blacksmith.armor_gold(@item.id)[0]
when RPG::Item
quantity, type = $game_party.item_number(@item.id), 2
materials = Blacksmith.item_forges(@item.id)
price = Blacksmith.item_gold(@item.id)[0]
end
# If player doesn't have the materials or gold, play SE and end method.
unless Blacksmith.materials?(type, @item.id)
$game_system.se_play($data_system.buzzer_se)
return
end
# End method and play buzzer if inventory is full.
return $game_system.se_play($data_system.buzzer_se) if quantity == 99
# Play the defined SE used when forging.
$game_system.se_play(RPG::AudioFile.new(*Blacksmith::FORGE_SE))
# Remove required materials from inventory and subtract gold cost.
if materials != nil
materials.each {|material|
case material[0]
when 0 then $game_party.lose_weapon(material[1], material[2])
when 1 then $game_party.lose_armor(material[1], material[2])
when 2 then $game_party.lose_item(material[1], material[2])
end
}
end
$game_party.lose_gold(price)
# Add forged item
case @item
when RPG::Weapon then $game_party.gain_weapon(@item.id, 1)
when RPG::Armor then $game_party.gain_armor(@item.id, 1)
when RPG::Item then $game_party.gain_item(@item.id, 1)
end
# Reset windows.
@materials_window.visible = @materials_window.active = false
@forge_window.active = @forge_window.visible = true
# Refresh any windows that may have changed
[@status_window, @gold_window, @extract_window, @forge_window,
@enchant_window].each {|window| window.refresh }
end
#-----------------------------------------------------------------------------
def extract_item
# Set local variables depending on item type.
case @item
when RPG::Weapon
quantity = $game_party.weapon_number(@item.id)
materials = Blacksmith.weapon_extractions(@item.id)
price = Blacksmith.weapon_gold(@item.id)[1]
when RPG::Armor
quantity = $game_party.armor_number(@item.id)
materials = Blacksmith.armor_extractions(@item.id)
price = Blacksmith.armor_gold(@item.id)[1]
when RPG::Item
quantity = $game_party.item_number(@item.id)
materials = Blacksmith.item_extractions(@item.id)
price = Blacksmith.item_gold(@item.id)[1]
end
# If nothing is defined for the extraction, return.
if materials == nil || (materials.empty? && price == 0)
return $game_system.se_play($data_system.buzzer_se)
end
# Play extraction SE
$game_system.se_play(RPG::AudioFile.new(*Blacksmith::EXTRACT_SE))
# Perform extraction, adding materials and increasing gold.
materials.each {|material|
case material[0]
when 0 then $game_party.gain_weapon(material[1], material[2])
when 1 then $game_party.gain_armor(material[1], material[2])
when 2 then $game_party.gain_item(material[1], material[2])
end
}
$game_party.gain_gold(price)
# Remove extracted item from inventory
case @item
when RPG::Weapon then $game_party.lose_weapon(@item.id, 1)
when RPG::Armor then $game_party.lose_armor(@item.id, 1)
when RPG::Item then $game_party.lose_item(@item.id, 1)
end
# Reset windows.
@materials_window.visible = @materials_window.active = false
@extract_window.active = @extract_window.visible = true
# Refresh any windows that may have changed
[@status_window, @gold_window, @extract_window].each {|window| window.refresh }
end
end
[/rgss]
Instructions
Place script below Debug and above Main.
I have written a small application that can be used to make your configurations with a user-friendly GUI instead of typing out confusing arrays in the script. If you choose to download the application, you need not get anything else. All the scripts and the demo can be output from the application. Due to the increased file size and possible instability of embedding Ruby or IronRuby in the application to read your game's Marshaled .rxdata files, I left it out, but have included a one-time script to run in your game that will output a file to use with the program so that you need not copy your database into it. Here are the easy instructions:
The application requires Microsoft's .NET 2.0 Framework or higher to run. If you do not have it and cannot run the application, you can download it here.
Blacksmith Configuration 1.1 (1.05 MB)
Further instructions are within the script.
Compatibility
If you have a script that re-writes Window_Gold, it could cause graphical irregularities on the blacksmith screen.
Scripts that alter items in the database may cause issues, though not tested.
Credits and Thanks
RoseSkye, huge thanks for beta-testing and demo map
Author's Notes
Please report any bugs/issues so that they can be resolved. Enjoy!
Terms and Conditions
Creative Commons - Attribution-NonCommercial-ShareAlike 3.0 Unported
By: ForeverZer0
Introduction
Will allow you to create a complete blacksmith system. The player will be able to forge equipment/items by using combinations of weapons, armors, and items in their possession. Also includes a "Enchantment" feature that will allow the player to use special items to add stats, elemental efficiencies, and state altering to weapons and armor. The extraction feature allows for the breaking down of current equipment and items into other ones.
Features
- Completely configurable item requirements for every item.
- Configurable blacksmith 'fees' for every weapon/armor/item
- Can use as many different items, with different quantities for each piece of equipment.
- Variable "skill" levels for Blacksmith shops, which lets you decide which features the Blacksmith can do.
- Only have to use a single script call to for the Blacksmith's shop.
- Can recycle old equipment by extracting items from weapons/armors
Screenshots



Demo
Demo Link
Script
[rgss]#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
# Blacksmith Shop
# Author: ForeverZer0
# Type: Custom Shop System
# Date: 4.23.2011
# Version: v.2.0
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
#
# Explanation:
# Will allow you to create a complete blacksmithing system. The player will be
# able to forge equipment/items by using combinations of weapons, armors, and
# items in their possession. Also includes a "Enchantment" feature that will
# allow the player to use special items to add stats, elementel efficiencies,
# and state altering to weapons and armor. The extraction feature allows for
# the breaking down of current equipment and items into other ones.
#
# Features:
# - Completely configurable item requirements for every item.
# - Configurable blacksmith 'fees' for every weapon/armor
# - Can use as many different items, with different quantities for each piece
# of equipment.
# - Variable "skill" levels for Blacksmith shops, which lets you decide
# which features the Blacksmith can do.
# - Only have to use a single script call to for the Blacksmith's shop.
# - Can recycle old equipment by extracting items from weapons/armors/items.
#
# Instructions:
# - Place script below debug and above main
# - Configuration and instructions for each are below
# - To call blacksmith shop, this script call:
#
# w = [ WEAPON_IDS ] (Use as many as needed, seperate with commas)
# a = [ ARMOR_IDS ]
# i = [ ITEM_IDS ]
# $scene = Scene_BlackSmith.new(w, a, i)
#
# - All IDs that you included in the script call for items will be be
# available for forging in that shop.
# - You can also include a fourth argument to the call to set the Blacksmith's
# "skill level". Just make an array of true/false elemenets, set up like
# this:
#
# [CAN_FORGE?, CAN_EXTRACT?, CAN_ENCHANT?]
#
# If you are not using the Enchant feature, omit the last option. Just make
# sure that if you do include this argument that the array has a value for
# each skill.
#
# Credits/Thanks:
# - ForeverZer0, for the script.
# - RoseSkye, huge thanks for beta-testing and demo map.
#
# Author's Notes:
# Please report any bugs/issues at http://www.chaos-project.com
#
#:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:
module Blacksmith
#===============================================================================
# BEGIN CONFIGURATION
#===============================================================================
FORGE_SE = ['006-System06', 80, 100]
# SE played when an item is forged. ['FILENAME', VOLUME, PITCH]
EXTRACT_SE = ['020-Teleport03', 80, 100]
# SE played when an item extraction is performed. ['FILENAME', VOLUME, PITCH]
ENCHANT_SE = ['020-Teleport03', 80, 100]
# SE played when an item enchantment is performed. ['FILENAME', VOLUME, PITCH]
USE_ENCHANTMENTS = true
# Set to true to enable the "Enchant" feature of the system.
NO_ENCHANT_WEAPONS = []
NO_ENCHANT_ARMORS = []
# Include IDs of any equipment that cannot be enchanted in the respective
# arrays, seperating by commas. Ignore these if not using enchant feature.
# Define the colors used for the text in the Blacksmith shop.
PLUS_COLOR = Color.new(128, 255, 128)
MINUS_COLOR = Color.new(255, 128, 128)
MAP_BACK = true
# Set to true if you would like slightly opaque windows with the map showing
# through.
#-----------------------------------------------------------------------------
# FORGE DATABASE
#-----------------------------------------------------------------------------
# Define the materials used for each weapon/armor/item that can be forged and
# extracted. They configuration is slightly different than what it was in the
# first version of the script. You can seperately define materials that are
# given during extraction if you like, or ignore it and it will simply return
# the same materials it takes to forge them. It works like this:
#
# STEP 1:
# Create a new "case" in the appropriate method below for the type of item
# you are trying to define. There are three of them, one each for weapons,
# armors, and items. Just use this syntax:
#
# when DATABASE_ID then []
#
# STEP 2:
# Now you can begin to add materials to forge the item. Each material has
# an number which defines what type of item is is. Here is the "key":
#
# 0 = Weapon
# 1 = Armor
# 2 = Item
#
# To define a material for an item, you simply create a three element array
# using this format:
# [ITEM_TYPE, DATABASE_ID, QUANTITY]
#
# ...and add it the appropriate empty array in the case statement you made
# in Step 1. You can add as many different items as you please to forge an
# weapon/armor/item, simply seperate the material arrays with commas. See
# below for a few examples.
#-----------------------------------------------------------------------------
def self.weapon_forges(id)
return case id
when 1 then [[2, 33, 3], [2, 42, 1]] # Bronze Sword
when 2 then [[0, 1, 1], [2, 34, 2], [2, 42, 1]] # Iron Sword
when 3 then [[0, 2, 1], [2, 34, 10]] # Steel Sword
when 4 then [[0, 2, 2], [2, 35, 3], [2, 41, 1]] # Mythril Sword
when 5 then [[2, 33, 5], [2, 43, 1]] # Bronze Spear
when 6 then [[2, 34, 4], [0, 5, 1], [2, 43, 1]] # Iron Spear
when 7 then [[0, 6, 2], [2, 34, 2], [2, 43, 1]] # Steel Spear
when 8 then [[2, 35, 8], [2, 43, 1]] # Mythril Spear
end
end
def self.armor_forges(id)
return case id
when 1 then []
when 2 then []
when 3 then []
when 4 then []
when 5 then []
end
end
def self.item_forges(id)
return case id
when 2 then [[2, 1, 5]]
when 3 then [[2, 2, 5]]
when 5 then [[2, 4, 5]]
when 6 then [[2, 5, 5]]
end
end
#-----------------------------------------------------------------------------
# EXTRACT DATABASE
#-----------------------------------------------------------------------------
# Here you can define the items received when a specific item is extracted.
# It can be setup the same as way as above. Items left undefined will return
# the same items that are required to forge it. You can define an item with an
# empty array to have it return no items, though it can still return gold.
#-----------------------------------------------------------------------------
def self.weapon_extractions(id)
return case id
when 1 then [[2, 33, 1], [2, 42, 1]]
when 2 then [[2, 34, 1], [2, 41, 1]]
when 3 then [[2, 34, 2], [0, 1, 1]]
when 4 then [[2, 33, 5], [2, 34, 5], [2, 41, 1]]
when 5 then [[2, 33, 1], [2, 43, 1]]
when 6 then [[2, 34, 1], [2, 43, 1]]
when 7 then [[2, 34, 2], [0, 5, 1]]
when 8 then [[2, 33, 5], [2, 34, 5], [2, 43, 1]]
else
self.weapon_forges(id)
end
end
def self.armor_extractions(id)
return case id
when 1 then []
when 2 then []
when 3 then []
when 4 then []
when 5 then []
else
self.weapon_forges(id)
end
end
def self.item_extractions(id)
return case id
when 1 then [] # Potion
when 2 then [[2, 1, 2]] # High Potion
when 3 then [[2, 2, 2], [2, 1, 2]] # Full Potion
when 4 then [] # Perfume
when 5 then [[2, 4, 2]] # High Perfume
when 6 then [[2, 4, 2], [2, 5, 2]] # Full Perfume
else
self.item_forges(id)
end
end
#-----------------------------------------------------------------------------
# GOLD DATABASE
#-----------------------------------------------------------------------------
# Here you can define the amount of gold that is required to forge an item,
# and the amount that is given if extracted. There are three methods, one each
# for weapons, armors, and items. Simply follow this pattern for each
# category:
#
# when DATABASE_ID then [FORGE_PRICE, EXTRACT_GOLD,]
#-----------------------------------------------------------------------------
def self.weapon_gold(id)
return case id
when 1 then [200, 50]
when 2 then [450, 225]
when 3 then [1000, 525]
when 4 then [1200, 200]
when 5 then [300, 75]
when 6 then [550, 275]
when 7 then [1200, 600]
when 8 then [1500, 650]
else
[0, 0]
end
end
def self.armor_gold(id)
return case id
when 1 then []
when 2 then []
when 3 then []
when 4 then []
when 5 then []
else
[0, 0]
end
end
def self.item_gold(id)
return case id
when 1 then [100, 0]
when 2 then [50, 25]
when 3 then [250, 25]
when 4 then [100, 0]
when 5 then [50, 25]
when 6 then [250, 25]
else
[0, 0]
end
end
#-----------------------------------------------------------------------------
# ENCHANT DATABASE
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Here you can define what items will alter stats when used to enchant with.
# You need to create a two element array, and add it to the respective array
# below that corresponds with the desired item.
#
# ex.
# when ITEM_ID then [[KEYWORD, VALUE], [KEYWORD, VALUE]]
#
# KEYWORD: See below for a list of possible keywords. Stat changes that
# can affect only weapons will have no effect on armors, and
# vice-versa.
# VALUE : The amount by which to change the stat. Negative values will
# lower the stat.
#-----------------------------------------------------------------------------
# KEYWORDS:
#
# 'ATK' (Weapon Only) 'DEX' 'PDEF'
# 'EVA' (Armor Only) 'AGI' 'MDEF'
# 'STR' 'INT'
#
# ** Keywords have to be written EXACTLY as they appear.
#-----------------------------------------------------------------------------
def self.enchant_stats(item_id)
return case item_id
when 39 then [['AGI', 5]] # Carrot
when 40 then [['STR', 15], ['ATK', 5]] # Behemoth Juice
end
end
#-----------------------------------------------------------------------------
# Define state altering enchantments.
#
# ex.
# when ITEM_ID then [[VALUE, STATE_ID], [VALUE, STATE_ID]]
#
# VALUE: One of three different values to represent states efficiency.
# -1 = Minus state (Does nothing on armors)
# 0 = Neutral
# 1 = Plus state
# STATE_ID: The ID in the database of the state.
#-----------------------------------------------------------------------------
def self.enchant_states(item_id)
return case item_id
when 38 then [[1, 2], [1, 4], [1, 6]] # Chaos Orb
end
end
#-----------------------------------------------------------------------------
# Define element altering enchantments.
#
# ex.
# when ITEM_ID then [[VALUE, ELEMENT_ID], [VALUE, ELEMENT_ID]]
#
# VALUE: One of two different values to represent element efficiency.
# true = Uses element
# false = Doesn't use element (Negates element if present)
# ELEMENT_ID: The ID in the database of the element.
#-----------------------------------------------------------------------------
def self.enchant_elements(item_id)
return case item_id
when 36 then [[true, 3], [false, 5]] # Amethyst
when 37 then [[true, 1]] # Ruby
end
end
#-----------------------------------------------------------------------------
# Define the amount of gold it takes to enchant a weapon or armor with the
# item.
#-----------------------------------------------------------------------------
def self.enchant_gold(item_id)
return case item_id
when 36 then 1500
when 37 then 1100
when 38 then 1337
when 39 then 250
when 40 then 7500
else
0
end
end
#===============================================================================
# END CONFIGURATION
#===============================================================================
def self.materials?(type, id)
# Get the required materials for the item
materials = case type
when 0 then [self.weapon_forges(id), self.weapon_gold(id)]
when 1 then [self.armor_forges(id), self.armor_gold(id)]
when 2 then [self.item_forges(id), self.item_gold(id)]
end
materials[0] = [] if materials[0] == nil
# Check gold, skipping item check if there is not enough.
if $game_party.gold >= materials[1][0]
# Iterate all required materials, making sure enough are in inventory.
materials[0].each {|item|
# Branch by the type of the item.
result = case item[0]
when 0 then ($game_party.weapon_number(item[1]) >= item[2])
when 1 then ($game_party.armor_number(item[1]) >= item[2])
when 2 then ($game_party.item_number(item[1]) >= item[2])
end
# End iteration and return false immidiately if missing required item.
return false unless result
}
return true
end
return false
end
#-----------------------------------------------------------------------------
def self.update_database(item)
# Open the Weapons or Armors .rxdata file and add the created item.
begin
if item.is_a?(RPG::Weapon)
file, data = 'Data/Weapons.rxdata', $data_weapons
elsif item.is_a?(RPG::Armor)
file, data = 'Data/Armors.rxdata', $data_armors
else
return
end
data[item.id] = item
file = File.open(file, 'wb')
Marshal.dump(data, file)
file.close
rescue
print "Could not add #{item.name} to Database."
end
end
#-----------------------------------------------------------------------------
def self.create_item(base_item, enchant_item)
base = base_item.clone
# Do to clone only making shallow copies, it is necessary to also create
# seperate clones of the element and state sets, otherwise the original
# is affected too.
if base_item.is_a?(RPG::Weapon)
elem_set = base_item.element_set.clone
plus_state_set = base_item.plus_state_set.clone
minus_state_set = base_item.minus_state_set.clone
else
guard_elem_set = base_item.guard_element_set.clone
guard_state_set = base_item.guard_state_set.clone
end
# Gather the enchantment data.
stats = self.enchant_stats(enchant_item.id)
states = self.enchant_states(enchant_item.id)
elements = self.enchant_elements(enchant_item.id)
# Iterate through stats
if stats != nil
stats.each {|stat|
case stat[0]
when 'ATK'
if base.is_a?(RPG::Weapon)
base.atk += stat[1]
end
when 'EVA'
if base.is?(RPG::Armor)
base.eva += stat[1]
end
when 'STR' then base.str_plus += stat[1]
when 'DEX' then base.dex_plus += stat[1]
when 'AGI' then base.agi_plus += stat[1]
when 'INT' then base.int_plus += stat[1]
when 'PDEF' then base.pdef_plus += stat[1]
when 'MDEF' then base.mdef_plus += stat[1]
end
}
end
# Iterate through states
if states != nil
states.each {|state|
id = state[1]
if base.is_a?(RPG::Weapon)
case state[0]
when -1
minus_state_set.push(id) unless minus_state_set.include?(id)
plus_state_set -= [id]
when 0
minus_state_set -= [id]
plus_state_set -= [id]
when 1
plus_state_set.push(id) unless plus_state_set.include?(id)
minus_state_set -= [id]
end
elsif base.is_a?(RPG::Armor)
if state[0] == 0
guard_state_set -= [id]
elsif state[0] == 1
guard_state_set.push(id) unless guard_state_set.inlcude?(id)
end
end
}
end
# Iterate through elements
if elements != nil
elements.each {|element|
id = element[1]
if base.is_a?(RPG::Weapon)
if element[0] && !elem_set.include?(id)
elem_set.push(id)
else
elem_set -= [id]
end
elsif base.is_a?(RPG::Armor)
if element[0] && !guard_elem_set.include?(id)
guard_elem_set.push(id)
else
guard_elem_set -= [id]
end
end
}
end
# Give the weapon a new ID, remove the old item, and add the new one.
if base.is_a?(RPG::Weapon)
$game_party.lose_weapon(base_item.id, 1)
base.id = $data_weapons.size
base.element_set = elem_set
base.plus_state_set = plus_state_set
base.minus_state_set = minus_state_set
$data_weapons[base.id] = base
$game_party.gain_weapon(base.id, 1)
elsif base.is_a?(RPG::Armor)
$game_party.lose_armor(base_item.id, 1)
base.id = $data_armors.size
base.guard_element_set = guard_elem_set
base.guard_state_set = guard_state_set
$data_armors[base.id] = base
$game_party.gain_armor(base.id, 1)
end
# Add new item to class equipment
self.update_class_equipment(base_item, base)
# Save the new item to the database.
self.update_database(base)
end
#-----------------------------------------------------------------------------
def self.update_class_equipment(old, new)
# Adds the created item to class equipment that could equip the original
$data_classes.each_index {|i|
next if $data_classes == nil
if old.is_a?(RPG::Weapon) && $data_classes.weapon_set.include?(old.id)
$data_classes.weapon_set.push(new.id)
elsif old.is_a?(RPG::Armor) && $data_classes.armor_set.include?(old.id)
$data_classes.armor_set.push(new.id)
end
}
# Marshal the new data.
begin
file = File.open('Data/Classes.rxdata', 'wb')
Marshal.dump($data_classes, file)
file.close
rescue
print "Could not update RPG::Class database."
end
end
end
$blacksmith = 2.0
#===============================================================================
# ** Window_BlacksmithCommand
#===============================================================================
class Window_BlacksmithCommand < Window_Selectable
def initialize(level)
super(0, 64, 480, 64)
@level = level
if Blacksmith::USE_ENCHANTMENTS
@item_max = @column_max = 4
@commands = ['Forge', 'Extract', 'Enchant', 'Exit']
else
@item_max = @column_max = 3
@commands = ['Forge', 'Extract', 'Exit']
end
self.contents = Bitmap.new(self.width - 32, self.height - 32)
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def refresh
self.contents.clear
(0...@item_max).each {|i| draw_item(i) }
end
#-----------------------------------------------------------------------------
def draw_item(index)
w = self.width / @item_max
self.contents.font.color = @level[index] ? normal_color : disabled_color
self.contents.draw_text(4 + (w * index), 0, w, 32, @commands[index])
end
end
#===============================================================================
# ** Window_BlacksmithForge
#===============================================================================
class Window_BlacksmithForge < Window_Selectable
def initialize(shop_goods)
super(0, 128, 368, 352)
# Initialize window and create instance variable to store available goods.
@shop_goods = shop_goods
self.active = self.visible = false
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def item
return @data[self.index]
end
#-----------------------------------------------------------------------------
def refresh(enchanting = false)
# Dispose bitmap and set to nil if not already.
if self.contents != nil
self.contents = self.contents.dispose
end
# Set flag for enchanting
@enchanting = enchanting
# Create array of equipment, depending on flag
if @enchanting
@data = []
# Add weapons
($data_weapons - [nil]).each {|weapon|
if $game_party.weapon_number(weapon.id) > 0 &&
!Blacksmith::NO_ENCHANT_WEAPONS.include?(weapon.id)
@data.push(weapon)
end
}
# Add Armor
($data_armors - [nil]).each {|armor|
if $game_party.armor_number(armor.id) > 0 &&
!Blacksmith::NO_ENCHANT_ARMORS.include?(armor.id)
@data.push(armor)
end
}
else
@data = @shop_goods
end
# Create a new bitmap, sized for available items
@item_max = @data.size
if @item_max > 0
self.contents = Bitmap.new(width - 32, row_max * 32)
(0...@item_max).each {|i| draw_item(i) }
end
end
#-----------------------------------------------------------------------------
def draw_item(index)
item = @data[index]
# Set a few local variables depending on the type of item.
case item
when RPG::Weapon
quantity = $game_party.weapon_number(item.id)
price, type = Blacksmith.weapon_gold(item.id)[0], 0
when RPG::Armor
quantity = $game_party.armor_number(item.id)
price, type = Blacksmith.armor_gold(item.id)[0], 1
when RPG::Item
quantity = $game_party.item_number(item.id)
price, type = Blacksmith.item_gold(item.id)[0], 2
end
# Don't check material requirments for forging wjen enchanting
result = @enchanting ? true : Blacksmith.materials?(type, item.id)
# Determine the color to use for drawing the item name.
if quantity < 99 && result
self.contents.font.color = normal_color
else
self.contents.font.color = disabled_color
end
# Draw the item name, icon, and price.
x, y = 4, index * 32
rect = Rect.new(x, y, self.width - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
opacity = self.contents.font.color == normal_color ? 255 : 128
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
self.contents.draw_text(x + 28, y, 212, 32, item.name, 0)
if !@enchanting
self.contents.draw_text(x + 240, y, 88, 32, price.to_s, 2)
end
end
#-----------------------------------------------------------------------------
def update_help
@help_window.set_text(self.item == nil ? '' : self.item.description)
end
end
#===============================================================================
# ** Window_BlacksmithExtract
#===============================================================================
class Window_BlacksmithExtract < Window_Selectable
def initialize
super(0, 128, 368, 352)
self.active = self.visible = false
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def item
return @data[self.index]
end
#-----------------------------------------------------------------------------
def refresh
# Dispose current bitmap if defined.
if self.contents != nil
self.contents = self.contents.dispose
end
# Create list of items in inventory
@data = []
(1...$data_weapons.size).each {|i|
result = (Blacksmith.weapon_extractions(i) != nil ||
Blacksmith.weapon_gold(i)[1] != 0)
if $game_party.weapon_number(i) > 0 && result
@data.push($data_weapons)
end
}
(1...$data_armors.size).each {|i|
result = (Blacksmith.armor_extractions(i) != nil ||
Blacksmith.armor_gold(i)[1] != 0)
if $game_party.armor_number(i) > 0 && result
@data.push($data_armors)
end
}
(1...$data_items.size).each {|i|
result = (Blacksmith.item_extractions(i) != nil ||
Blacksmith.item_gold(i)[1] != 0)
if $game_party.item_number(i) > 0 && result
@data.push($data_items)
end
}
@item_max = @data.size
# Create a new bitmap that will contain the listed items
if @item_max > 0
self.contents = Bitmap.new(width - 32, row_max * 32)
(0...@item_max).each {|i| draw_item(i) }
end
end
#-----------------------------------------------------------------------------
def draw_item(index)
item = @data[index]
# Set a few local variables depending on the type of item.
quantity = case item
when RPG::Weapon then $game_party.weapon_number(item.id)
when RPG::Armor then $game_party.armor_number(item.id)
when RPG::Item then $game_party.item_number(item.id)
end
# Draw the name, icon, and quantity of the item.
x, y = 4, index * 32
rect = Rect.new(x, y, self.width / @column_max - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
self.contents.draw_text(x + 28, y, 212, 32, item.name, 0)
end
#-----------------------------------------------------------------------------
def update_help
@help_window.set_text(self.item == nil ? '' : self.item.description)
end
end
#===============================================================================
# ** Window_BlacksmithMaterials
#===============================================================================
class Window_BlacksmithMaterials < Window_Base
attr_accessor :active
def initialize
# Initialize window size and coordinates.
super(0, 128, 368, 352)
self.visible = @active = false
end
#-----------------------------------------------------------------------------
def refresh(item, type = 0)
# Clear the bitmap and set the new materials.
if self.contents != nil
self.contents = self.contents.dispose
end
set_materials(item, type)
# Create a new bitmap, based off the amount of materials
if @materials != nil && @materials.size > 0
self.contents = Bitmap.new(self.width - 32, 64 + (@materials.size * 32))
# Draw each material and quantity required.
self.contents.font.color = system_color
word = type == 0 ? 'Cost' : ($data_system.words.gold + ':')
self.contents.draw_text(4, 0, 212, 32, word, 0)
text = type == 0 ? 'Required Materials:' : 'Extractable Materials:'
self.contents.draw_text(4, 32, 368, 32, text, 0)
self.contents.font.color = normal_color
self.contents.draw_text(244, 0, 88, 32, @price.to_s, 2)
# Enumerate through each material.
@materials.each_index {|i|
# Set local variable to current item, depending on type.
case @materials[0]
when 0
item = $data_weapons[@materials[1]]
enough = $game_party.weapon_number(item.id) >= @materials[2]
when 1
item = $data_armors[@materials[1]]
enough = $game_party.armor_number(item.id) >= @materials[2]
when 2
item = $data_items[@materials[1]]
enough = $game_party.item_number(item.id) >= @materials[2]
end
next if item == nil
# Set local variable to store required amount of this item.
required = @materials[2]
# Set color of text, draw grayed if out if forging and not enough.
self.contents.font.color = normal_color
if type == 0 && !enough
self.contents.font.color = disabled_color
end
# Set coordinates of current line.
x, y = 4, 64 + (i * 32)
# Draw item name, icon, and required amount.
rect = Rect.new(x, y, self.width - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
opacity = self.contents.font.color == normal_color ? 255 : 128
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
self.contents.draw_text(x + 28, y, 212, 32, item.name)
self.contents.draw_text(x + 272, y, 48, 32, 'x', 0)
self.contents.draw_text(x + 272, y, 48, 32, required.to_s, 2)
}
elsif @price > 0
self.contents = Bitmap.new(self.width - 32, 64)
self.contents.font.color = system_color
self.contents.draw_text(4, 0, 212, 32, $data_system.words.gold + ':')
self.contents.font.color = normal_color
self.contents.draw_text(244, 0, 88, 32, @price.to_s, 2)
self.contents.draw_text(4, 32, 368, 32, 'No Materials')
end
end
#-----------------------------------------------------------------------------
def set_materials(item, type)
# Sets the required/extractable items for the passed item.
id = item.id
if item.is_a?(RPG::Weapon)
@materials = type == 0 ? Blacksmith.weapon_forges(id) :
Blacksmith.weapon_extractions(id)
@price = Blacksmith.weapon_gold(id)[type]
elsif item.is_a?(RPG::Armor)
@materials = type == 0 ? Blacksmith.armor_forges(id) :
Blacksmith.armor_extractions(id)
@price = Blacksmith.armor_gold(id)[type]
else
if @materials != 2
@materials = type == 0 ? Blacksmith.item_forges(id) :
Blacksmith.item_extractions(id)
@price = Blacksmith.item_gold(id)[type]
end
end
end
#-----------------------------------------------------------------------------
def update
# Allow scrolling of bitmap if materials don't fit in window.
if @active && self.contents != nil && self.contents.height > 320
if Input.trigger?(Input::UP)
if self.oy > 0
self.oy -= 32
$game_system.se_play($data_system.cursor_se)
end
elsif Input.trigger?(Input::DOWN)
if (self.oy + 320) < self.contents.height
self.oy += 32
$game_system.se_play($data_system.cursor_se)
end
end
end
end
end
#===============================================================================
# ** Window_BlacksmithStatus
#===============================================================================
class Window_BlacksmithStatus < Window_Base
def initialize
super(368, 128, 272, 352)
self.contents = Bitmap.new(width - 32, height - 32)
# Create array of sprites same size as party
@sprites = [Sprite.new, Sprite.new, Sprite.new, Sprite.new]
#@sprites = Array.new($game_party.actors.size, Sprite.new)
# Set coordinates of each sprite
@sprites.each_index {|i|
@sprites.x, @sprites.y = 380, 194 + (i * 64)#(i * 34)
@sprites.z = self.z + 10
}
self.visible = false
# Array of flags for walking
@walk = Array.new($game_party.actors.size, false)
@count, @item = 0, nil
refresh
end
#-----------------------------------------------------------------------------
def refresh
# Clear bitmap and turn off visiblity of each sprite.
self.contents.clear
@sprites.each {|sprite| sprite.visible = false }
# Return if selected item index is undefined.
return if @item == nil
self.contents.font.size = Font.default_size + 2
quantity = case @item
when RPG::Item then $game_party.item_number(@item.id)
when RPG::Weapon then $game_party.weapon_number(@item.id)
when RPG::Armor then $game_party.armor_number(@item.id)
end
self.contents.font.color = system_color
self.contents.draw_text(4, 0, 200, 32, 'Possessed:')
self.contents.font.color = normal_color
self.contents.draw_text(204, 0, 32, 32, quantity.to_s, 2)
# Disable walking animation and end method if selected item is a normal item
if @item.is_a?(RPG::Item)
@walk.collect! {|value| false }
return
end
# Change the font size.
self.contents.font.size = Font.default_size - 1
# Iterate each actor...
$game_party.actors.each_index {|i|
chr = $game_party.actors
# Set local variable to highlighted piece of equipment.
if @item.is_a?(RPG::Weapon)
eqp = $data_weapons[chr.weapon_id]
else
armors = [chr.armor1_id, chr.armor2_id, chr.armor3_id, chr.armor4_id]
eqp = $data_armors[armors[@item.kind]]
end
# Draw the actor sprite.
draw_actor_graphic(i, chr.equippable?(@item))
# Draw message and return if unequippable.
unless chr.equippable?(@item)
self.contents.font.color = normal_color
self.contents.draw_text(32, 54 + (i * 64), 150, 32, 'Cannot Equip')
next
else
# Create array of stat changes.
# [str, dex, agi, int, pdef, mdef, (atk || eva)]
stats = [
(@item == nil ? 0 : @item.str_plus) - (eqp == nil ? 0 : eqp.str_plus),
(@item == nil ? 0 : @item.dex_plus) - (eqp == nil ? 0 : eqp.dex_plus),
(@item == nil ? 0 : @item.agi_plus) - (eqp == nil ? 0 : eqp.agi_plus),
(@item == nil ? 0 : @item.int_plus) - (eqp == nil ? 0 : eqp.int_plus),
(@item == nil ? 0 : @item.pdef) - (eqp == nil ? 0 : eqp.pdef),
(@item == nil ? 0 : @item.mdef) - (eqp == nil ? 0 : eqp.mdef)
]
if @item.is_a?(RPG::Weapon)
stats.push(
(@item == nil ? 0 : @item.atk) - (eqp == nil ? 0 : eqp.atk))
elsif @item.is_a?(RPG::Armor)
stats.push(
(@item == nil ? 0 : @item.eva) - (eqp == nil ? 0 : eqp.eva))
end
# Set local variable to each piece of equipments' name
current_name = eqp == nil ? '' : eqp.name
new_name = @item == nil ? '' : @item.name
# If stats are all equal, show message and end method.
if stats.all? {|stat| stat == 0 }
self.contents.font.color = normal_color
if current_name != new_name
self.contents.draw_text(32, 54 + (i * 64), 150, 32, 'No Change')
else
self.contents.draw_text(32, 54 + (i * 64), 150, 32, 'Equipped')
end
next
end
# Draw any stat changes, using colors to show plus/minus changes
self.contents.font.size = (Font.default_size - 1)
self.contents.font.color = normal_color
self.contents.draw_text(104, 42 + (64*i), 32, 32, 'STR ') if stats[0] != 0
self.contents.draw_text(104, 58 + (64*i), 32, 32, 'DEX ') if stats[1] != 0
self.contents.draw_text(104, 74 + (64*i), 32, 32, 'AGI ') if stats[2] != 0
self.contents.draw_text(176, 42 + (64*i), 32, 32, 'INT ') if stats[3] != 0
self.contents.draw_text(32, 58 + (64*i), 32, 32, 'PDF ') if stats[4] != 0
self.contents.draw_text(32, 74 + (64*i), 32, 32, 'MDF ') if stats[5] != 0
if stats[-1] != 0
# Show stats changes for atk/eva, depending on the equipment type
stat = @item.is_a?(RPG::Weapon) ? 'ATK ' : 'EVA '
self.contents.draw_text(32, 42 + (64 * i), 32, 32, stat) if stat != 0
end
# Show any stat changes
stats.each_index {|j|
next if stats[j] == 0
xy = case j
when 0 then [132, 42 + (64 * i)]
when 1 then [132, 58 + (64 * i)]
when 2 then [132, 74 + (64 * i)]
when 3 then [198, 42 + (64 * i)]
when 4 then [60, 58 + (64 * i)]
when 5 then [60, 74 + (64 * i)]
when 6 then [60, 42 + (64 * i)]
end
# Set color and operator depending on value
if stats[j] < 0
self.contents.font.color, sign = Blacksmith::MINUS_COLOR, '-'
else
self.contents.font.color, sign = Blacksmith::PLUS_COLOR, '+'
end
self.contents.draw_text(xy[0], xy[1], 8, 32, sign, 1)
self.contents.draw_text(xy[0] + 10, xy[1], 24, 32, stats[j].abs.to_s)
}
end
}
end
#-----------------------------------------------------------------------------
def item=(item)
if @item != item
# Change the item variable and refresh.
@item = item
refresh
end
end
#-----------------------------------------------------------------------------
def draw_actor_graphic(id, equipable)
# Draws the actor graphic
actor = $game_party.actors[id]
@sprites[id].bitmap = RPG::Cache.character(actor.character_name,
actor.character_hue)
@sprites[id].src_rect.set(0, 0, @sprites[id].bitmap.width / 4,
@sprites[id].bitmap.height / 4)
# Set walking animation if item is equippable.
@walk[id] = equipable
@sprites[id].tone = Tone.new(0, 0, 0, equipable ? 0 : 255)
@sprites[id].visible = true
end
#-----------------------------------------------------------------------------
def update
super
# Update the walking animation.
@count = (@count + 1) % 40
$game_party.actors.each_index {|i|
next unless @walk
if @sprites.bitmap != nil
w = @sprites.bitmap.width / 4
h = @sprites.bitmap.height / 4
x = (@count / 10) * w
@sprites.src_rect.set(x, 0, w, h)
end
}
end
#-----------------------------------------------------------------------------
def visible=(bool)
super
# Set visible to the actor sprites as well.
@sprites.each {|sprite| sprite.visible = bool }
end
#-----------------------------------------------------------------------------
def dispose
super
# Dispose the actor sprites as well.
@sprites.each {|sprite| sprite.dispose }
end
end
#===============================================================================
# ** Window_BlacksmithExtract
#===============================================================================
class Window_BlacksmithEnchant < Window_Selectable
def initialize
super(0, 128, 368, 352)
self.active = self.visible = false
refresh
self.index = 0
end
#-----------------------------------------------------------------------------
def item
return @data[self.index]
end
#-----------------------------------------------------------------------------
def refresh
# Dispose current bitmap if defined.
if self.contents != nil
self.contents = self.contents.dispose
end
# Create list of items in inventory
@data = []
($data_items - [nil]).each {|item|
result = false
result = true if Blacksmith.enchant_stats(item.id) != nil
result = true if Blacksmith.enchant_states(item.id) != nil
result = true if Blacksmith.enchant_elements(item.id) != nil
@data.push(item) if result
}
@item_max = @data.size
# Create a new bitmap that will contain the listed items
if @item_max > 0
self.contents = Bitmap.new(width - 32, row_max * 32)
(0...@item_max).each {|i| draw_item(i) }
end
end
#-----------------------------------------------------------------------------
def draw_item(index)
item = @data[index]
# Set a few local variables depending on the type of item.
quantity = $game_party.item_number(item.id)
# Draw the name, icon, and quantity of the item.
x, y = 4, index * 32
rect = Rect.new(x, y, self.width / @column_max - 32, 32)
self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
bitmap = RPG::Cache.icon(item.icon_name)
self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24))
self.contents.draw_text(x + 28, y, 212, 32, item.name, 0)
self.contents.draw_text(x + 240, y, 16, 32, ':', 1)
self.contents.draw_text(x + 256, y, 24, 32, quantity.to_s, 2)
end
#-----------------------------------------------------------------------------
def update_help
@help_window.set_text(self.item == nil ? '' : self.item.description)
end
end
#===============================================================================
# ** Scene_Blacksmith
#===============================================================================
class Scene_Blacksmith
def initialize(weapons = [], armors = [], items = [], level = nil)
# Set available goods for this shop based off passed argument.
@goods = []
@goods += weapons.collect {|id| $data_weapons[id] }
@goods += armors.collect {|id| $data_armors[id] }
@goods += items.collect {|id| $data_items[id] }
@goods.uniq!
# Configure the level variable
@level = (level == nil) ? Array.new(4, true) : (level + [true])
@enchants = Blacksmith::USE_ENCHANTMENTS
end
#-----------------------------------------------------------------------------
def main
# Create a Proc to handle confirmation of choices
@confirm_proc = Proc.new {
@help_window.set_text('Are you sure?')
window = Window_Command.new(160, ['Confirm', 'Cancel'])
window.x, window.y, window.z = 224, 192, 9999
loop { Graphics.update; Input.update; window.update
if Input.trigger?(Input::C) || Input.trigger?(Input::B)
result = (Input.trigger?(Input::C) && window.index == 0)
$game_system.se_play($data_system.cancel_se) unless result
window.dispose
break(result)
end
}
}
# Initialize the needed windows.
@command_window = Window_BlacksmithCommand.new(@level)
@forge_window = Window_BlacksmithForge.new(@goods)
@extract_window = Window_BlacksmithExtract.new
@materials_window = Window_BlacksmithMaterials.new
@enchant_window = Window_BlacksmithEnchant.new
@status_window = Window_BlacksmithStatus.new
@dummy_window = Window_Base.new(0, 128, 640, 352)
@gold_window = Window_Gold.new
@gold_window.x, @gold_window.y = 480, 64
@help_window = Window_Help.new
# Bind the help window to the other windows.
@forge_window.help_window = @extract_window.help_window = @help_window
@enchant_window.help_window = @help_window
# Set a windows array for easier handling of all windows.
@windows = [@command_window, @forge_window, @extract_window, @help_window,
@materials_window, @status_window, @dummy_window, @gold_window, @enchant_window]
# Create map sprite if configured to do so, and set window opacity
if Blacksmith::MAP_BACK
@spriteset = Spriteset_Map.new
@windows.each {|window| window.opacity = 160 }
end
# Execute the transition and start the main loop.
Graphics.transition
loop {Graphics.update; Input.update; update; break if $scene != self }
# Freeze the Graphics and dispose the windows
Graphics.freeze
@windows.each {|window| window.dispose }
if Blacksmith::MAP_BACK && @spriteset != nil
@spriteset.dispose
end
end
#-----------------------------------------------------------------------------
def update
# Update the windows
@windows.each {|window| window.update }
# Branch method depending on current action.
if @command_window.active
update_command
elsif @extract_window.active
update_extract
elsif @forge_window.active
update_forge
elsif @materials_window.active
update_materials
elsif @enchant_window.active
update_enchant
end
end
#-----------------------------------------------------------------------------
def back_to_map
# Play SE and return to the map.
$game_system.se_play($data_system.cancel_se)
$scene = Scene_Map.new
end
#-----------------------------------------------------------------------------
def update_command
# Set help text depending on the selected index.
help_text = case @command_window.index
when 0 then 'Use materials to forge new weapons, armors, and items.'
when 1 then 'Extract materials from weapons, armors, and items.'
when 2 then !@enchants ? 'Exit the shop.' :
'Enchant weapons, armors, and items using other items.'
when 3 then 'Exit the shop.'
end
@help_window.set_text(help_text)
# Check for Input.
if Input.trigger?(Input::B)
back_to_map
elsif Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
# Branch depending on command index
case @command_window.index
when 0
# Play SE and return if option is locked.
unless @level[0]
$game_system.se_play($data_system.buzzer_se)
return
end
# Shift scene to forge phase.
@dummy_window.visible = false
@forge_window.refresh(false)
@command_window.active = false
@forge_window.active = @forge_window.visible = true
@status_window.visible = true
when 1
# Play SE and return if option is locked.
unless @level[1]
$game_system.se_play($data_system.buzzer_se)
return
end
# Shift scene to extract phase
@extract_window.refresh
@command_window.active = @dummy_window.visible = false
@extract_window.active = @extract_window.visible = true
@status_window.visible = true
when 2
# Play SE and return if option is locked.
if @enchants
unless @level[2]
$game_system.se_play($data_system.buzzer_se)
return
end
# Shift scene to enchant phase.
@forge_window.refresh(true)
@command_window.active = @dummy_window.visible = false
@forge_window.active = @forge_window.visible = true
@status_window.visible = true
else
back_to_map
end
when 3
back_to_map
end
end
end
#-----------------------------------------------------------------------------
def update_forge
# Update for input when forge window is active.
@item = @status_window.item = @forge_window.item
if Input.trigger?(Input::B)
$game_system.se_play($data_system.cancel_se)
@command_window.active = @dummy_window.visible = true
@forge_window.active = @forge_window.visible = false
@status_window.visible = false
elsif Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
@forge_window.active = @forge_window.visible = false
if @command_window.index == 0
@materials_window.refresh(@item, 0)
@materials_window.visible = @materials_window.active = true
else
@enchant_window.refresh
@enchant_window.visible = @enchant_window.active = true
end
end
end
#-----------------------------------------------------------------------------
def update_extract
# Update for input when extraction window is active.
@item = @status_window.item = @extract_window.item
if Input.trigger?(Input::B)
$game_system.se_play($data_system.cancel_se)
@command_window.active = @dummy_window.visible = true
@extract_window.active = @extract_window.visible = false
@status_window.visible = false
elsif Input.trigger?(Input::C)
$game_system.se_play($data_system.decision_se)
@materials_window.refresh(@item, 1)
@extract_window.active = @extract_window.visible = false
@materials_window.visible = @materials_window.active = true
end
end
#-----------------------------------------------------------------------------
def update_enchant
# Update input on the enchantment items screen.
if Input.trigger?(Input::B)
# Return to previous screen if cancel button is pressed.
$game_system.se_play($data_system.cancel_se)
@enchant_window.visible = @enchant_window.active = false
@forge_window.active = @forge_window.visible = true
elsif Input.trigger?(Input::C) && @confirm_proc.call
enchant_item
end
end
#-----------------------------------------------------------------------------
def enchant_item
# Apply enchantment to weapon/armor using item
$game_party.lose_item(@enchant_window.item.id, 1)
Blacksmith.create_item(@forge_window.item, @enchant_window.item)
# Play SE
$game_system.se_play(RPG::AudioFile.new(*Blacksmith::ENCHANT_SE))
# Refesh windows
[@enchant_window, @status_window].each {|window| window.refresh }
@forge_window.refresh(true)
# Return to previous screen
@enchant_window.visible = @enchant_window.active = false
@forge_window.active = @forge_window.visible = true
end
#-----------------------------------------------------------------------------
def update_materials
# Show help text.
text = 'Press the Action Button to proceed. Press Cancel to go back'
@help_window.set_text(text)
if Input.trigger?(Input::B)
# Return to previous screen if cancel button is pressed.
$game_system.se_play($data_system.cancel_se)
@materials_window.visible = @materials_window.active = false
if @command_window.index == 0
@forge_window.active = @forge_window.visible = true
else
@extract_window.active = @extract_window.visible = true
end
elsif Input.trigger?(Input::C) && @confirm_proc.call
@command_window.index == 0 ? forge_item : extract_item
end
end
#-----------------------------------------------------------------------------
def forge_item
# Set local variables depending on item type.
case @item
when RPG::Weapon
quantity, type = $game_party.weapon_number(@item.id), 0
materials = Blacksmith.weapon_forges(@item.id)
price = Blacksmith.weapon_gold(@item.id)[0]
when RPG::Armor
quantity, type = $game_party.armor_number(@item.id), 1
materials = Blacksmith.armor_forges(@item.id)
price = Blacksmith.armor_gold(@item.id)[0]
when RPG::Item
quantity, type = $game_party.item_number(@item.id), 2
materials = Blacksmith.item_forges(@item.id)
price = Blacksmith.item_gold(@item.id)[0]
end
# If player doesn't have the materials or gold, play SE and end method.
unless Blacksmith.materials?(type, @item.id)
$game_system.se_play($data_system.buzzer_se)
return
end
# End method and play buzzer if inventory is full.
return $game_system.se_play($data_system.buzzer_se) if quantity == 99
# Play the defined SE used when forging.
$game_system.se_play(RPG::AudioFile.new(*Blacksmith::FORGE_SE))
# Remove required materials from inventory and subtract gold cost.
if materials != nil
materials.each {|material|
case material[0]
when 0 then $game_party.lose_weapon(material[1], material[2])
when 1 then $game_party.lose_armor(material[1], material[2])
when 2 then $game_party.lose_item(material[1], material[2])
end
}
end
$game_party.lose_gold(price)
# Add forged item
case @item
when RPG::Weapon then $game_party.gain_weapon(@item.id, 1)
when RPG::Armor then $game_party.gain_armor(@item.id, 1)
when RPG::Item then $game_party.gain_item(@item.id, 1)
end
# Reset windows.
@materials_window.visible = @materials_window.active = false
@forge_window.active = @forge_window.visible = true
# Refresh any windows that may have changed
[@status_window, @gold_window, @extract_window, @forge_window,
@enchant_window].each {|window| window.refresh }
end
#-----------------------------------------------------------------------------
def extract_item
# Set local variables depending on item type.
case @item
when RPG::Weapon
quantity = $game_party.weapon_number(@item.id)
materials = Blacksmith.weapon_extractions(@item.id)
price = Blacksmith.weapon_gold(@item.id)[1]
when RPG::Armor
quantity = $game_party.armor_number(@item.id)
materials = Blacksmith.armor_extractions(@item.id)
price = Blacksmith.armor_gold(@item.id)[1]
when RPG::Item
quantity = $game_party.item_number(@item.id)
materials = Blacksmith.item_extractions(@item.id)
price = Blacksmith.item_gold(@item.id)[1]
end
# If nothing is defined for the extraction, return.
if materials == nil || (materials.empty? && price == 0)
return $game_system.se_play($data_system.buzzer_se)
end
# Play extraction SE
$game_system.se_play(RPG::AudioFile.new(*Blacksmith::EXTRACT_SE))
# Perform extraction, adding materials and increasing gold.
materials.each {|material|
case material[0]
when 0 then $game_party.gain_weapon(material[1], material[2])
when 1 then $game_party.gain_armor(material[1], material[2])
when 2 then $game_party.gain_item(material[1], material[2])
end
}
$game_party.gain_gold(price)
# Remove extracted item from inventory
case @item
when RPG::Weapon then $game_party.lose_weapon(@item.id, 1)
when RPG::Armor then $game_party.lose_armor(@item.id, 1)
when RPG::Item then $game_party.lose_item(@item.id, 1)
end
# Reset windows.
@materials_window.visible = @materials_window.active = false
@extract_window.active = @extract_window.visible = true
# Refresh any windows that may have changed
[@status_window, @gold_window, @extract_window].each {|window| window.refresh }
end
end
[/rgss]
Instructions
Place script below Debug and above Main.
I have written a small application that can be used to make your configurations with a user-friendly GUI instead of typing out confusing arrays in the script. If you choose to download the application, you need not get anything else. All the scripts and the demo can be output from the application. Due to the increased file size and possible instability of embedding Ruby or IronRuby in the application to read your game's Marshaled .rxdata files, I left it out, but have included a one-time script to run in your game that will output a file to use with the program so that you need not copy your database into it. Here are the easy instructions:
- Open application and go to the "Miscellaneous" tab.
- Click the button to for the BlacksmithCache script, and copy the text anywhere in your script editor.
- Run the game once, a file will be output.
- Drag and Drop the file onto the anvil in the bottom-right corner of the application and you are done.
The application requires Microsoft's .NET 2.0 Framework or higher to run. If you do not have it and cannot run the application, you can download it here.
Blacksmith Configuration 1.1 (1.05 MB)
Further instructions are within the script.
Compatibility
If you have a script that re-writes Window_Gold, it could cause graphical irregularities on the blacksmith screen.
Scripts that alter items in the database may cause issues, though not tested.
Credits and Thanks
RoseSkye, huge thanks for beta-testing and demo map
Author's Notes
Please report any bugs/issues so that they can be resolved. Enjoy!
Terms and Conditions
Creative Commons - Attribution-NonCommercial-ShareAlike 3.0 Unported
You are free:
to Share - to copy, distribute and transmit the work
to Remix - to adapt the work
Under the following conditions:
Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
Noncommercial. You may not use this work for commercial purposes.
Share alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one.
- For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page.
- Any of the above conditions can be waived if you get permission from the copyright holder.
- Nothing in this license impairs or restricts the author's moral rights.
to Share - to copy, distribute and transmit the work
to Remix - to adapt the work
Under the following conditions:
Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
Noncommercial. You may not use this work for commercial purposes.
Share alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one.
- For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page.
- Any of the above conditions can be waived if you get permission from the copyright holder.
- Nothing in this license impairs or restricts the author's moral rights.