# スキルツリーシステムver1.02
# 制作者:TOMO
#
# ツリー型のスキル習得システムです
#
#
# ※使用方法
# 1.スキルツリー画面の呼び出し
# SceneManager.call(Scene_SkillTree)
#
# 2.スキルポイントの追加
# $game_actors[n].add_skill_point(m)
# (nはアクターID、mは追加ポイント)
#
#
# ※補足
# Skillの武器タイプIDは、特徴の「武器タイプ装備」に対応しています
#
# リストに追加したい場合は、
# 対応する武器タイプを装備可能な状態にして下さい
# ※更新履歴
# ver1.02
# 習得済みスキルの強調表示
# 線の自動生成で合流時に重なるミスを修正
#
# ver1.01
# スキルポイントが正常加算されないバグ修正
# 習得確認ウィンドウの追加
# コストウィンドウの表記変更
module TOMO
module SkillTree
Skill = {} # 弄るな!
# スキル名の表示
Name = true
# スキル名表示時の横幅
Width = 144
# 習得済みスキル用アイコンID
LearnedIcon = 125
# 各種用語
SPText = "SP"
CostText = "消費SP:"
PremiseText = "前提:"
DefaultPoint = 1 # デフォルトの消費スキルポイント
Point = "2 + level" # スキルポイントの計算式
# ツリーデータの設定
# Skill[武器タイプID] = {
# :tree => [[1行目],[2行目],…],
# スキルID => {"派生" => [派生先のスキルID],
# "前提" => [習得前提スキルID],
# "SP" => 習得に必要なポイント,
# "反転" => [線を逆にしたい派生先スキルID],}
# }
#
# :tree内で0を指定した場合は、そこを飛ばして表示します
# "派生"を省略した場合は、線が引かれません
# "前提"を省略した場合は、ポイント消費だけで習得可能です
# "SP"を省略した場合は、DefaultPointを参照します
# "反転"を省略した場合は、線は通常通りです("派生"に無いIDは無効)
Skill[1] = {
# ツリー
:tree => [[0,80],
[81,0,82,24],
[0,83],
[0,84]],
80 => {"派生" => [81, 82, 24]},
81 => {"前提" => [80], "派生" => [83]},
82 => {"前提" => [80], "派生" => [83]},
83 => {"前提" => [81, 82], "派生" => [84], "SP" => 3},
84 => {"前提" => [83, 24], "SP" => 5},
24 => {"前提" => [80], "派生" => [84], "反転" => [84]},
}
end
end
class Game_Actor
attr_accessor :used_skill_point # 使用済みスキルポイント
#--------------------------------------------------------------------------
# ● セットアップ
#--------------------------------------------------------------------------
alias tomo_skill_tree_setup setup
def setup(actor_id)
@skill_point = 0
@used_skill_point = 0
tomo_skill_tree_setup(actor_id)
end
#--------------------------------------------------------------------------
# ● スキルポイントの取得
#--------------------------------------------------------------------------
def skill_point
eval(TOMO::SkillTree::Point) + @skill_point - @used_skill_point
end
#--------------------------------------------------------------------------
# ● スキルポイントの追加
#--------------------------------------------------------------------------
def add_skill_point(value)
@skill_point += value
end
end
class Window_SkillTreeTypes < Window_Command
attr_reader :tree_window
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x, y)
super(x, y)
@actor = nil
select(0)
end
#--------------------------------------------------------------------------
# ● ウィンドウ幅の取得
#--------------------------------------------------------------------------
def window_width
return 172
end
#--------------------------------------------------------------------------
# ● 表示行数の取得
#--------------------------------------------------------------------------
def visible_line_number
return 4
end
#--------------------------------------------------------------------------
# ● アクターの設定
#--------------------------------------------------------------------------
def actor=(actor)
return if @actor == actor
@actor = actor
refresh
select(0)
end
#--------------------------------------------------------------------------
# ● コマンドリストの作成
#--------------------------------------------------------------------------
def make_command_list
if @actor
$data_system.weapon_types.size.times do |wtype|
if TOMO::SkillTree::Skill[wtype] && @actor.equip_wtype_ok?(wtype)
name = $data_system.weapon_types[wtype]
add_command(name, :tree_type, true, wtype)
end
end
end
end
#--------------------------------------------------------------------------
# ● フレーム更新
#--------------------------------------------------------------------------
def update
super
@tree_window.tree_type = current_ext if @tree_window
end
#--------------------------------------------------------------------------
# ● スキルツリーウィンドウの設定
#--------------------------------------------------------------------------
def tree_window=(tree_window)
@tree_window = tree_window
end
end
class Window_SkillTree < Window_Selectable
attr_reader :cost_window
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x, y)
super(x, y, Graphics.width - x, Graphics.height - y - fitting_height(3))
@actor = nil
@tree_type = 0
deactivate
end
#--------------------------------------------------------------------------
# ● スキルツリーのセットを取得
#--------------------------------------------------------------------------
def tree
TOMO::SkillTree::Skill[@tree_type]
end
#--------------------------------------------------------------------------
# ● 項目数の取得
#--------------------------------------------------------------------------
def item_max
col_max * row_max
end
#--------------------------------------------------------------------------
# ● 横に項目が並ぶときの空白の幅を取得
#--------------------------------------------------------------------------
def spacing
return 0
end
#--------------------------------------------------------------------------
# ● 桁数の取得
#--------------------------------------------------------------------------
def col_max
tree && tree[:tree] ? tree[:tree].collect{|row| row.size }.max : 1
end
#--------------------------------------------------------------------------
# ● 行数の取得
#--------------------------------------------------------------------------
def row_max
tree && tree[:tree] ? tree[:tree].size : 1
end
#--------------------------------------------------------------------------
# ● 項目の幅を取得
#--------------------------------------------------------------------------
def item_width
TOMO::SkillTree::Name ? TOMO::SkillTree::Width : 24
end
#--------------------------------------------------------------------------
# ● ウィンドウ内容の幅を計算
#--------------------------------------------------------------------------
def contents_width
item_width * col_max
end
#--------------------------------------------------------------------------
# ● ウィンドウ内容の高さを計算
#--------------------------------------------------------------------------
def contents_height
item_height * (row_max * 2 - 1)
end
#--------------------------------------------------------------------------
# ● 項目を描画する矩形の取得
#--------------------------------------------------------------------------
def item_rect(index)
rect = Rect.new
rect.width = item_width
rect.height = item_height
rect.x = index % col_max * (item_width + spacing)
rect.y = index / col_max * 2 * item_height
rect
end
#--------------------------------------------------------------------------
# ● カーソル位置が画面内になるようにスクロール
#--------------------------------------------------------------------------
def ensure_cursor_visible
super
if (index % col_max + 1) * item_width > width - standard_padding * 2
self.ox = (index % col_max - 1) * item_width
else
self.ox = 0
end
end
#--------------------------------------------------------------------------
# ● カーソルを右に移動
#--------------------------------------------------------------------------
def cursor_right(wrap = false)
if col_max >= 2 && index % col_max < col_max - 1
select((index + 1) % item_max)
end
end
#--------------------------------------------------------------------------
# ● カーソルを左に移動
#--------------------------------------------------------------------------
def cursor_left(wrap = false)
if col_max >= 2 && index % col_max > 0
select((index - 1 + item_max) % item_max)
end
end
#--------------------------------------------------------------------------
# ● アクターの設定
#--------------------------------------------------------------------------
def actor=(actor)
return if @actor == actor
@actor = actor
end
#--------------------------------------------------------------------------
# ● スキルツリータイプの設定
#--------------------------------------------------------------------------
def tree_type=(type)
return if @tree_type == type
@tree_type = type
refresh
end
#--------------------------------------------------------------------------
# ● 選択項目のスキルIDを取得
#--------------------------------------------------------------------------
def skill_id
id = tree[:tree][index / col_max][index % col_max] if tree && tree[:tree]
id = 0 unless id
id
end
#--------------------------------------------------------------------------
# ● 選択項目のスキルポイントを取得
#--------------------------------------------------------------------------
def sp
tree[skill_id]["SP"] ? tree[skill_id]["SP"] : TOMO::SkillTree::DefaultPoint
end
#--------------------------------------------------------------------------
# ● スキルが習得済みか?
#--------------------------------------------------------------------------
def learned?(skill)
@actor && @actor.skill_learn?(skill)
end
#--------------------------------------------------------------------------
# ● スキルが習得可能か?
#--------------------------------------------------------------------------
def learn_ok?(skill)
point = TOMO::SkillTree::DefaultPoint
point = tree[skill.id]["SP"] if tree[skill.id]["SP"]
@actor && !@actor.skill_learn?(skill) &&
@actor.skill_point >= point && (tree[skill.id]["前提"] ?
tree[skill.id]["前提"].all?{|id|learned?($data_skills[id])} : true)
end
#--------------------------------------------------------------------------
# ● 選択項目の有効状態を取得
#--------------------------------------------------------------------------
def current_item_enabled?
return false unless @actor
return false unless tree
skill = $data_skills[skill_id]
return false unless skill
return learn_ok?(skill)
end
#--------------------------------------------------------------------------
# ● スキルを許可状態で表示するかどうか
#--------------------------------------------------------------------------
def enable?(skill)
return false unless @actor
return false unless tree
return learned?(skill) || learn_ok?(skill)
end
#--------------------------------------------------------------------------
# ● 項目の描画
#--------------------------------------------------------------------------
def draw_item(index)
rect = item_rect_for_text(index)
if tree && tree[:tree]
skill_id = tree[:tree][index / col_max][index % col_max]
if skill_id && skill_id > 0
skill = $data_skills[skill_id]
if TOMO::SkillTree::Name
draw_item_name(skill, rect.x, rect.y, enable?(skill), rect.width - 24)
else
draw_icon(skill.icon_index, rect.x, rect.y, enable?(skill))
end
if learned?(skill)
bitmap = Cache.system("Iconset")
icon_index = TOMO::SkillTree::LearnedIcon
s_rect = Rect.new(icon_index % 16 * 24, icon_index / 16 * 24, 24, 24)
rect.set(rect.x + 8, rect.y + 8, 16, 16)
contents.stretch_blt(rect, bitmap, s_rect)
end
end
end
end
#--------------------------------------------------------------------------
# ● 全ての線の描画
#--------------------------------------------------------------------------
def draw_lines
if tree && tree[:tree]
row_max.size.times do |start_y|
tree[:tree][start_y].each_with_index do |skill_id, start_x|
next unless skill_id > 0
next unless tree[skill_id]["派生"]
tree[skill_id]["派生"].each do |next_skill|
tree[:tree].each_with_index do |list, end_y|
if list.include?(next_skill)
end_x = list.index(next_skill)
mirror = tree[skill_id]["反転"] &&
tree[skill_id]["反転"].include?(next_skill)
draw_line(start_x, start_y, end_x, end_y, mirror)
end
end
end
end
end
end
end
#--------------------------------------------------------------------------
# ● 線の描画
#--------------------------------------------------------------------------
def draw_line(start_x, start_y, end_x, end_y, mirror = false)
x1, y1 = start_x * item_width, start_y * item_height * 2
x2, y2 = end_x * item_width, end_y * item_height * 2
width, height = item_width / 2, item_height / 2
x, y = x1 + width - 1, y1 + item_height
if x1 == x2
contents.fill_rect(x, y, 2, y2 - y, system_color)
elsif mirror
if x2 > x1
contents.fill_rect(x, y, 2, y2 - y - height - 1, system_color)
contents.fill_rect(x, y2 - height - 1, x2 - x1 + 2,
2, system_color)
contents.fill_rect(x2 + width - 1, y2 - height - 1, 2,
height + 1, system_color)
else
contents.fill_rect(x, y, 2, y2 - y - height - 1, system_color)
contents.fill_rect(x2 + width - 1, y2 - height - 1,
x1 - x2 + 2, 2, system_color)
contents.fill_rect(x2 + width - 1, y2 - height - 1, 2,
height + 1, system_color)
end
elsif x2 > x1
contents.fill_rect(x, y, 2, height + 1, system_color)
contents.fill_rect(x, y + height - 1, x2 - x1 + 2, 2, system_color)
contents.fill_rect(x2 + width - 1, y + height, 2,
y2 - y - height, system_color)
else
contents.fill_rect(x, y, 2, height + 1, system_color)
contents.fill_rect(x2 + width - 1, y + height - 1,
x1 - x2 + 2, 2, system_color)
contents.fill_rect(x2 + width - 1, y + height, 2,
y2 - y - height, system_color)
end
end
#--------------------------------------------------------------------------
# ● リフレッシュ
#--------------------------------------------------------------------------
def refresh
contents.clear
create_contents
draw_all_items
draw_lines
end
#--------------------------------------------------------------------------
# ● コストウィンドウの設定
#--------------------------------------------------------------------------
def cost_window=(cost_window)
@cost_window = cost_window
call_update_help
end
#--------------------------------------------------------------------------
# ● ヘルプテキスト更新
#--------------------------------------------------------------------------
def update_help
@help_window.set_item($data_skills[skill_id]) if skill_id
if @cost_window
@cost_window.skill_data = [skill_id, tree[skill_id]] if tree && skill_id
end
end
end
class Window_SkillTreeStatus < Window_Base
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x, y)
super(x, y, 172, fitting_height(2))
@actor = nil
end
#--------------------------------------------------------------------------
# ● アクターの設定
#--------------------------------------------------------------------------
def actor=(actor)
return if @actor == actor
@actor = actor
refresh
end
#--------------------------------------------------------------------------
# ● リフレッシュ
#--------------------------------------------------------------------------
def refresh
contents.clear
if @actor
draw_actor_name(@actor, 0, 0)
change_color(system_color)
rect = Rect.new(0, line_height, contents_width, line_height)
draw_text(rect, TOMO::SkillTree::SPText)
change_color(normal_color)
draw_text(rect, @actor.skill_point, 2)
end
end
end
class Window_SkillTreeCost < Window_Base
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x, y)
super(x, y, Graphics.width - x, fitting_height(3))
@actor = nil
@skill_data = nil
end
#--------------------------------------------------------------------------
# ● アクターの設定
#--------------------------------------------------------------------------
def actor=(actor)
return if @actor == actor
@actor = actor
end
#--------------------------------------------------------------------------
# ● スキルデータの設定
#--------------------------------------------------------------------------
def skill_data=(skill_data)
return if @skill_data == skill_data
@skill_data = skill_data
refresh
end
#--------------------------------------------------------------------------
# ● リフレッシュ
#--------------------------------------------------------------------------
def refresh
contents.clear
if @skill_data && @skill_data[1]
change_color(system_color)
rect = Rect.new(0, 0, contents_width, line_height)
if @actor && @actor.skill_learn?($data_skills[@skill_data[0]])
draw_text(rect, "取得済み")
else
draw_text(rect, TOMO::SkillTree::CostText)
width = text_size(TOMO::SkillTree::CostText).width
cost = TOMO::SkillTree::DefaultPoint
cost = @skill_data[1]["SP"] if @skill_data[1]["SP"]
change_color(normal_color, @actor.skill_point >= cost)
draw_text(rect.x + width, rect.y, rect.width - width, rect.height, cost)
change_color(system_color)
rect.y += line_height
draw_text(rect, TOMO::SkillTree::PremiseText)
p_width = text_size(TOMO::SkillTree::PremiseText).width
width = TOMO::SkillTree::Width
count = (contents_width - p_width) / width
if @skill_data[1]["前提"]
@skill_data[1]["前提"].each_with_index do |skill_id, i|
x = rect.x + p_width + width * (i % count)
y = rect.y + line_height * (i / count)
enabled = @actor.skill_learn?($data_skills[skill_id])
draw_item_name($data_skills[skill_id], x, y, enabled, width - 24)
end
end
end
end
end
end
class Window_SkillTreeChoice < Window_Selectable
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize
x = (Graphics.width - 360) / 2
y = (Graphics.height - fitting_height(4)) / 2
super(x, y, 360, fitting_height(4))
@actor = nil
@skill_id = 0
@cost = 0
self.openness = 0
deactivate
end
#--------------------------------------------------------------------------
# ● 桁数の取得
#--------------------------------------------------------------------------
def col_max
return 2
end
#--------------------------------------------------------------------------
# ● 項目数の取得
#--------------------------------------------------------------------------
def item_max
col_max
end
#--------------------------------------------------------------------------
# ● 項目を描画する矩形の取得
#--------------------------------------------------------------------------
def item_rect(index)
rect = Rect.new
rect.width = item_width
rect.height = item_height
rect.x = index % col_max * (item_width + spacing)
rect.y = (index / col_max + 3) * item_height
rect
end
#--------------------------------------------------------------------------
# ● 決定ハンドラの呼び出し
#--------------------------------------------------------------------------
def call_ok_handler
handle?(:ok) && index == 1 ? call_handler(:cancel) : super
end
#--------------------------------------------------------------------------
# ● アクターの設定
#--------------------------------------------------------------------------
def actor=(actor)
return if @actor == actor
@actor = actor
end
#--------------------------------------------------------------------------
# ● スキルの設定
#--------------------------------------------------------------------------
def set_skill(skill_id, cost)
return if @skill_id == skill_id && @cost == cost
@skill_id = skill_id
@cost = cost
refresh
end
#--------------------------------------------------------------------------
# ● 水平線の色を取得
#--------------------------------------------------------------------------
def line_color
color = normal_color
color.alpha = 48
color
end
#--------------------------------------------------------------------------
# ● 水平線の描画
#--------------------------------------------------------------------------
def draw_horz_line(y)
line_y = y + line_height / 2 - 1
contents.fill_rect(0, line_y, contents_width, 2, line_color)
end
#--------------------------------------------------------------------------
# ● リフレッシュ
#--------------------------------------------------------------------------
def refresh
contents.clear
if @actor && @skill_id && @cost
draw_item_name($data_skills[@skill_id], 0, 0)
rect = Rect.new(0, 0, contents_width, line_height)
draw_text(rect, "を習得しますか?", 2)
rect.y += line_height
change_color(system_color)
text = TOMO::SkillTree::SPText + ":"
draw_text(rect, text)
rect.width -= text_size(text).width
rect.x += text_size(text).width
draw_text(rect, "→", 1)
change_color(normal_color)
draw_text(rect, @actor.skill_point - @cost, 2)
rect.width = rect.width / 2 - 12
draw_text(rect, @actor.skill_point, 2)
draw_horz_line(line_height * 2)
draw_text(item_rect_for_text(0), "はい", 1)
draw_text(item_rect_for_text(1), "いいえ", 1)
end
end
end
class Scene_SkillTree < Scene_MenuBase
#--------------------------------------------------------------------------
# ● 開始処理
#--------------------------------------------------------------------------
def start
super
create_help_window
create_status_window
create_command_window
create_tree_window
create_choice_window
create_cost_window
end
#--------------------------------------------------------------------------
# ● ステータスウィンドウの作成
#--------------------------------------------------------------------------
def create_status_window
@status_window = Window_SkillTreeStatus.new(0, @help_window.height)
@status_window.actor = @actor
end
#--------------------------------------------------------------------------
# ● コマンドウィンドウの作成
#--------------------------------------------------------------------------
def create_command_window
wy = @status_window.y + @status_window.height
@command_window = Window_SkillTreeTypes.new(0, wy)
@command_window.actor = @actor
@command_window.set_handler(:ok, method(:on_type_ok))
@command_window.set_handler(:cancel, method(:return_scene))
@command_window.set_handler(:pagedown, method(:next_actor))
@command_window.set_handler(:pageup, method(:prev_actor))
end
#--------------------------------------------------------------------------
# ● ツリーウィンドウの作成
#--------------------------------------------------------------------------
def create_tree_window
wx, wy = @command_window.width, @help_window.height
@tree_window = Window_SkillTree.new(wx, wy)
@tree_window.actor = @actor
@tree_window.tree_type = @command_window.current_ext
@tree_window.help_window = @help_window
@tree_window.set_handler(:ok, method(:on_tree_ok))
@tree_window.set_handler(:cancel, method(:on_tree_cancel))
@command_window.tree_window = @tree_window
end
#--------------------------------------------------------------------------
# ● 選択ウィンドウの作成
#--------------------------------------------------------------------------
def create_choice_window
@choice_window = Window_SkillTreeChoice.new
@choice_window.actor = @actor
@choice_window.set_handler(:ok, method(:on_choice_ok))
@choice_window.set_handler(:cancel, method(:on_choice_cancel))
end
#--------------------------------------------------------------------------
# ● コストウィンドウの作成
#--------------------------------------------------------------------------
def create_cost_window
wy = @tree_window.y + @tree_window.height
@cost_window = Window_SkillTreeCost.new(0, wy)
@cost_window.actor = @actor
@tree_window.cost_window = @cost_window
end
#--------------------------------------------------------------------------
# ● スキルツリータイプ[決定]
#--------------------------------------------------------------------------
def on_type_ok
@tree_window.tree_type = @command_window.current_ext
@tree_window.activate.select(0)
end
#--------------------------------------------------------------------------
# ● スキルツリー[決定]
#--------------------------------------------------------------------------
def on_tree_ok
@choice_window.set_skill(@tree_window.skill_id, @tree_window.sp)
@choice_window.open.activate.select(0)
end
#--------------------------------------------------------------------------
# ● スキルツリー[決定]
#--------------------------------------------------------------------------
def on_tree_cancel
@tree_window.unselect
@command_window.activate
end
#--------------------------------------------------------------------------
# ● 選択[決定]
#--------------------------------------------------------------------------
def on_choice_ok
@actor.used_skill_point += @tree_window.sp
@actor.learn_skill(@tree_window.skill_id)
@status_window.refresh
@cost_window.refresh
@choice_window.close.deactivate
@tree_window.activate.refresh
end
#--------------------------------------------------------------------------
# ● 選択[決定]
#--------------------------------------------------------------------------
def on_choice_cancel
@choice_window.close.deactivate
@tree_window.activate.refresh
end
#--------------------------------------------------------------------------
# ● アクターの切り替え
#--------------------------------------------------------------------------
def on_actor_change
@status_window.actor = @actor
@command_window.actor = @actor
@tree_window.actor = @actor
@tree_window.tree_type = @command_window.current_ext
@choice_window.actor = @actor
@cost_window.actor = @actor
@command_window.activate
end
end