=begin
###############################################################################
# #
# Дерево навыков #
# #
###############################################################################
Автор: Денис Кузнецов (http://vk.com/id8137201)
Версия: 0.3 alpha
Релиз от: 27.06.15
Бллагодарность: Kian Ni за помощь с реализацией графической составляющей
Инструкция:
open_skill_tree_system - вызовите скрипт, чтобы выучить навыки
Стандартно у каждого навыка стоимость 1
Чтобы указать другое количество, напишите в заметках навыка:
skill_tree_point_cost = N
N - количество требуемых очков
# в версии 0.3 alpha отсутствует
Если для изучения навыка нужны другие навыки, то укажите в заметках:
skill_tree_need_skill = N
N - индекс требуемого навыка
Если для изучения навыка нужен минимальный уровень, отличный от 1,
то укажите в заметках:
skill_tree_skill_min_lvl = N
N - уровень
Вызовите, чтобы добавить персонажу (ID из Базы данных) очки навыка
add_skill_point(actor_id, skill_point)
Вызовите, чтобы забыть все навыки у персонажа
если add_skill_point = true, то за каждый забытй навык персонаж получит очко навыков
forget_all_skills(actor_id, add_skill_point)
=end
module Skill_Tree_System_Settings
# Количество очков, которые даются за новый уровень
NEW_LEVEL_SKILL_POINT = 1
# Таблица для построения древа. Каждая строка соответствует своему классу в Базе Данных
SKILL_CLASSES = {
1 => [ [3], [4, 5], [6], [7, 8], [9, 10, 11] ],
2 => [ [4], [12], [13], [14, 15], [16, 17, 18], [19], [20] ],
3 => [ [21], [4], [22], [23, 34] ],
4 => [ [22, 23], [24], [25], [26], [27]],
5 => []
}
# ниже не трогать :)
end # module Skill_Tree_System_Settings
class Skill_Tree_System_Viewport < Viewport
attr_accessor :skills
def initialize
super
@skills = []
end
end
class Skill_Tree_System_Scene < Scene_Base
include Skill_Tree_System_Settings
def start
super
@actor = $game_party.menu_actor # текущий персонаж
@player_window_minimize = true # для того, чтобы сначала была анимация, потом уже управление текстом в самом окне через minimize
@skill_info_window_minimize = true
create_all_window
end
def create_all_window
create_player_window
create_description_window
create_skill_tree_window
create_skill_info_window
end
def create_player_window
@create_player_window = Skill_Tree_System_Player_Window.new
end
def create_description_window
@create_description_window = Skill_Tree_System_Skill_Description.new(@actor)
end
def create_skill_tree_window
@viewport = Skill_Tree_System_Viewport.new
@create_skill_tree_window = Skill_Tree_System_Tree_Window.new(@viewport, @actor)
end
def create_skill_info_window
@create_skill_info_window = Skill_Tree_System_Skill_Info_Command_Window.new(@actor)
@create_skill_info_window.set_handler(:cancel, method(:minimize_skill_info_window))
@create_skill_info_window.set_handler(:learn, method(:learn_skill))
@create_skill_info_window.deactivate
end
def maximize_skill_info_window
@skill_info_window_minimize = false
@create_skill_info_window.activate
@create_skill_tree_window.can_update_movement = false
update
end
def minimize_skill_info_window
@skill_info_window_minimize = true
@create_skill_info_window.deactivate
@create_skill_tree_window.can_update_movement = true
update
end
def learn_skill
skill = @create_skill_info_window.skill
@actor.learn_skill(skill.id)
minimize_skill_info_window
layer = @create_skill_tree_window.cursor_layer
index = @create_skill_tree_window.cursor_index
@viewport.skills.each do |index|
index.change_skill_background if index.skill == skill
end
@actor.skill_point -= Skill_Tree_System_Functions.get_skill_point_cost(skill)
@create_skill_tree_window.setup_cursor(layer, index)
end
def next_actor
@actor = $game_party.menu_actor_next
actor_change
end
def prev_actor
@actor = $game_party.menu_actor_prev
actor_change
end
def actor_change
# если один персонаж, то ничего не меняем
return if $game_party.members.size == 1
@create_player_window.actor = @actor
@create_skill_tree_window.actor = @actor
update_skill_id
end
# анимация окна информации о навыке
def skill_info_animation
if @skill_info_window_minimize
if @create_skill_info_window.y == Graphics.height - 48
@create_skill_info_window.minimize = @skill_info_window_minimize if @create_skill_info_window.minimize != @skill_info_window_minimize
return
end
@create_skill_info_window.y += 16
@create_skill_info_window.y = Graphics.height - 48 if @create_skill_info_window.y > Graphics.height - 48
else
@create_skill_info_window.minimize = @skill_info_window_minimize if @create_skill_info_window.minimize != @skill_info_window_minimize
return if @create_skill_info_window.y == Graphics.height - @create_skill_info_window.height
@create_skill_info_window.y -= 16
@create_skill_info_window.y = Graphics.height - @create_skill_info_window.height if @create_skill_info_window.y < Graphics.height - @create_skill_info_window.height
end
end
# анимация окна об игроке
def player_window_animation
if @player_window_minimize
if @create_player_window.y == -@create_player_window.height + 48
### чтобы скрыть информацию только когда окно уже завершило анимацию, а не во время
@create_player_window.minimize = @player_window_minimize if @create_player_window.minimize != @player_window_minimize
return
end
@create_player_window.y -= 12
@create_player_window.y = -@create_player_window.height + 48 if @create_player_window.y < -@create_player_window.height + 48
else
### чтобы сначала отобразить информацию, а раскрывать окно потом
@create_player_window.minimize = @player_window_minimize if @create_player_window.minimize != @player_window_minimize
return if @create_player_window.y == 0
@create_player_window.y += 12
@create_player_window.y = 0 if @create_player_window.y > 0
end
end
def play_all_animation
skill_info_animation
player_window_animation
end
def update_skill_id
skill_id = SKILL_CLASSES[@actor.class_id][@create_skill_tree_window.cursor_layer][@create_skill_tree_window.cursor_index]
@create_skill_info_window.skill_id = skill_id
@create_description_window.skill_id = skill_id
end
def update
super
@player_window_minimize = !@player_window_minimize if Input.trigger?(:Z)
case @create_skill_tree_window.movement
when 5
maximize_skill_info_window
when 6
return_scene
when 7
next_actor
when 8
prev_actor
when 9
else
update_skill_id
@create_skill_tree_window.draw_cost_point_info
Audio.se_play("Audio/SE/Cursor2", 50)
end
play_all_animation
end
end # class Game_Skill_Tree < Scene_Base
class Skill_Tree_System_Skill_Class
attr_accessor :skill
def initialize(x, y, actor, skill, viewport)
@x = x
@y = y
@actor = actor
@skill = skill
@viewport = viewport
@sprite_background = nil
@sprite_icon = nil
create_skill_background
create_skill_icon
end
def create_skill_background
dispose_skill_background
@sprite_background = Sprite.new(@viewport)
skill_lear_state = Skill_Tree_System_Functions.get_skill_learn_state(@actor, @skill)
if skill_lear_state == 1
state_name = "green"
elsif skill_lear_state == 2
state_name = "orange"
else
state_name = "red"
end
@sprite_background.bitmap = Bitmap.new("Graphics/Skill_Tree_System/skill_tree_#{state_name}")
@sprite_background.x = @x
@sprite_background.y = @y
@sprite_background.z = 500
@sprite_background.opacity = 175
@sprite_background.zoom_x = 0.43
@sprite_background.zoom_y = 0.44
end
def change_skill_background
dispose_skill_background
create_skill_background
end
def create_skill_icon
dispose_skill_icon
@sprite_icon = Sprite.new(@viewport)
@sprite_icon.bitmap = Bitmap.new("Graphics/System/Iconset")
rect = Rect.new(@skill.icon_index % 16 * 24, @skill.icon_index / 16 * 24, 24, 24)
@sprite_icon.bitmap.blt(0, 0, @sprite_icon.bitmap, rect)
@sprite_icon.src_rect.set(rect.x, rect.y, 24, 24)
@sprite_icon.x = @x + 2
@sprite_icon.y = @y + 2
@sprite_icon.z = 600
end
def dispose_skill_background
return if @sprite_background.nil?
@sprite_background.bitmap.dispose
@sprite_background.dispose
@sprite_background = nil
end
def dispose_skill_icon
return if @sprite_icon.nil?
@sprite_icon.bitmap.dispose
@sprite_icon.dispose
@sprite_icon = nil
end
def dispose_all
dispose_skill_background
dispose_skill_icon
end
end # class Skill_Tree_System_Skill_Class
class Skill_Tree_System_Tree_Window < Window_Base
include Skill_Tree_System_Settings
attr_accessor :can_update_movement
attr_reader :cursor_layer
attr_reader :cursor_index
def initialize(viewport, actor)
super(0, 96, Graphics.width, Graphics.height - 96)
self.viewport = viewport
setup(actor)
end
def setup(actor)
dispose_all
@can_update_movement = true # можно ли перемещать курсор
@actor = actor
@sprite_cursor = nil
setup_cursor_variable(0, 0)
@skill_class = SKILL_CLASSES[@actor.class_id] # массив навыков для класса
@cursor_x_y = [] # трехмерный массив уровней и индексов навыков с координатами
create_skill_tree
create_skill_cursor
draw_cost_point_info
end
def actor=(actor)
setup(actor)
end
def setup_cursor_variable(layer, index)
@cursor_layer = layer # уровень курсора на древе
@cursor_index = index # индекс курсора на древе
end
def setup_cursor(layer, index)
setup_cursor_variable(layer, index)
@sprite_cursor.x = @cursor_x_y[@cursor_layer][@cursor_index][0]
@sprite_cursor.y = @cursor_x_y[@cursor_layer][@cursor_index][1]
end
def create_skill_tree
x = 24
skill_layer = 0
skill_index = 0
@skill_class.each do |index|
if index.size == 1
y = self.y + (self.height - 24) / 2 - 12
self.viewport.skills.push(Skill_Tree_System_Skill_Class.new(x, y, @actor, $data_skills[index[0]], self.viewport))
@cursor_x_y[skill_layer] = [[x, y]]
else
y = self.y + (self.height - 24) / (index.size + 1) + 12
indexes = []
index.each do |id|
self.viewport.skills.push(Skill_Tree_System_Skill_Class.new(x, y, @actor, $data_skills[id], self.viewport))
indexes[skill_index] = [x, y]
skill_index += 1
y += 48
end
@cursor_x_y[skill_layer] = indexes
end
skill_layer += 1
skill_index = 0
x += 72
end
end
def dispose_skill_tree
self.viewport.skills.each do |index|
index.dispose_all
end
end
def create_skill_cursor
dispose_skill_cursor
@sprite_cursor = Sprite.new(self.viewport)
@sprite_cursor.bitmap = Bitmap.new("Graphics/Skill_Tree_System/cursor")
@sprite_cursor.x = @cursor_x_y[@cursor_layer][@cursor_index][0]
@sprite_cursor.y = @cursor_x_y[@cursor_layer][@cursor_index][1]
@sprite_cursor.z = 500
end
def dispose_skill_cursor
return if @sprite_cursor.nil?
@sprite_cursor.bitmap.dispose
@sprite_cursor.dispose
@sprite_cursor = nil
end
def draw_cost_point_info
contents.clear
skill = $data_skills[SKILL_CLASSES[@actor.class_id][@cursor_layer][@cursor_index]]
draw_text(0, 0, contents_width, line_height, $data_classes[@actor.class_id].name, 2)
draw_text_ex(0, 0, "\\c[21]Требуемое количество очков:\\c[0] " + Skill_Tree_System_Functions.get_skill_point_cost(skill).to_s)
end
def update
if Graphics.frame_count % 20 == 0
@sprite_cursor.opacity = 0
else
@sprite_cursor.opacity = 200
end
end
def movement
return 9 if !@can_update_movement
return up if Input.repeat?(:UP)
return down if Input.repeat?(:DOWN)
return left if Input.repeat?(:LEFT)
return right if Input.repeat?(:RIGHT)
return 5 if Input.trigger?(:C)
return cancel if Input.trigger?(:B)
return 7 if Input.trigger?(:R)
return 8 if Input.trigger?(:L)
return 9
end
def up
return 1 if @skill_class[@cursor_layer].size == 1
@cursor_index -= 1
@cursor_index = @cursor_index % @skill_class[@cursor_layer].size
@sprite_cursor.y = @cursor_x_y[@cursor_layer][@cursor_index][1]
return 1
end
def down
return 2 if @skill_class[@cursor_layer].size == 1
@cursor_index += 1
@cursor_index = @cursor_index % @skill_class[@cursor_layer].size
@sprite_cursor.y = @cursor_x_y[@cursor_layer][@cursor_index][1]
return 2
end
def left
last_cursor = @cursor_layer
@cursor_layer -= 1
@cursor_layer = @cursor_layer % @skill_class.size
@cursor_index = @skill_class[@cursor_layer].size - 1 if @skill_class[@cursor_layer].size < @skill_class[last_cursor].size
@sprite_cursor.x = @cursor_x_y[@cursor_layer][@cursor_index][0]
@sprite_cursor.y = @cursor_x_y[@cursor_layer][@cursor_index][1]
return 3
end
def right
last_cursor = @cursor_layer
@cursor_layer += 1
@cursor_layer = @cursor_layer % @skill_class.size
@cursor_index = @skill_class[@cursor_layer].size - 1 if @skill_class[last_cursor].size > @skill_class[@cursor_layer].size
@sprite_cursor.x = @cursor_x_y[@cursor_layer][@cursor_index][0]
@sprite_cursor.y = @cursor_x_y[@cursor_layer][@cursor_index][1]
return 4
end
def cancel
dispose_all
return 6
end
def dispose_all
dispose_skill_tree
dispose_skill_cursor
self.viewport.skills = []
end
end # class Skill_Tree_System_Tree_Window < Window_Base
class Skill_Tree_System_Skill_Description < Window_Base
include Skill_Tree_System_Settings
def initialize(actor)
@skill = $data_skills[SKILL_CLASSES[actor.class_id][0][0]]
super(0, 0, Graphics.width, 96)
draw_skill_description
end
def skill_id=(skill_id)
@skill = $data_skills[skill_id]
draw_skill_description
end
def draw_skill_description
contents.clear
draw_text_ex(0, line_height, @skill.description)
end
def update
end
end
class Skill_Tree_System_Player_Window < Window_Base
attr_accessor :minimize
def initialize
super(Graphics.width / 12, -Graphics.height / 3 + 48, Graphics.width * 5 / 6, Graphics.height / 3) # y - чтобы сразу было свернуто
self.z = 1500
@actor = $game_party.menu_actor
@minimize = true # свернуто
draw_all_info
end
def actor=(actor)
@actor = actor
draw_all_info
end
def minimize=(minimize)
@minimize = minimize
draw_all_info
end
def draw_all_info
contents.clear
if @minimize
self.opacity = 0
self.contents_opacity = 150
self.back_opacity = 75
else
self.opacity = 255
self.contents_opacity = 255
self.back_opacity = 175
draw_actor_face(@actor, 16, line_height / 4)
draw_actor_simple_status(@actor, 108 + 24, line_height / 2)
end
draw_text(0, contents_height - line_height, contents_width, line_height, "Свободные очки: " + @actor.skill_point.to_s, 1)
end
def draw_actor_simple_status(actor, x, y)
draw_actor_name(actor, x, y)
draw_actor_level(actor, x, y + line_height * 1)
draw_actor_icons(actor, x, y + line_height * 2)
draw_actor_class(actor, x + 120 + 24, y)
draw_actor_hp(actor, x + 120 + 24, y + line_height * 1)
draw_actor_mp(actor, x + 120 + 24, y + line_height * 2)
end
def draw_actor_level(actor, x, y)
change_color(system_color)
draw_text(x, y, 32, line_height, Vocab::level_a)
change_color(normal_color)
draw_text(x + 32, y, 24, line_height, actor.level, 2)
end
def update
end
end # class Skill_Tree_System_Player_Window < Window_Base
class Skill_Tree_System_Skill_Info_Command_Window < Window_Command
include Skill_Tree_System_Settings
attr_accessor :minimize
attr_accessor :skill
def initialize(actor)
@actor = actor
@skill = $data_skills[SKILL_CLASSES[actor.class_id][0][0]]
@minimize = true # свернуто
super(Graphics.width / 12, Graphics.height - 48)
self.z = 1000
draw_all_info
end
def skill_id=(skill_id)
@skill = $data_skills[skill_id]
refresh
draw_all_info
end
def minimize=(minimize)
@minimize = minimize
refresh
select(0)
draw_all_info
end
def alignment
return 1
end
def window_width
Graphics.width * 5 / 6
end
def window_height
Graphics.height - 48
end
def item_rect(index)
rect = Rect.new
rect.width = item_width / 2
rect.height = item_height
rect.x = index * rect.width
rect.y = self.height - item_height - spacing
rect
end
def draw_all_info
if @minimize
self.opacity = 225
self.contents_opacity = 155
self.back_opacity = 75
else
self.opacity = 255
self.contents_opacity = 255
self.back_opacity = 225
end
y = 0
y_plus = line_height * 1.2
change_color(text_color(24))
draw_text(0, y, contents_width, line_height, @skill.name, 1)
change_color(normal_color)
y += line_height
self.contents.fill_rect(0, y, contents_width, 1, normal_color)
y += line_height / 2
if !@minimize
text = $data_system.skill_types[@skill.stype_id]
draw_text_ex(0, y, "\\c[6]Тип:\\c[0] " + (text == "" ? "Нет" : text))
y += y_plus
text = ""
classes = Skill_Tree_System_Functions.get_skill_class_id(@skill)
if classes == [] || classes.size == $data_classes.size
text = "Все"
else
i = 0
classes.each do |class_id|
text += ", " if i > 0
text += $data_classes[class_id].name
i += 1
end
end
draw_text_ex(0, y, "\\c[6]Класс:\\c[0] " + text)
y += y_plus
min_lvl = Skill_Tree_System_Functions.get_skill_min_lvl(@skill)
draw_text_ex(0, y, "\\c[6]Требуемый уровень:\\c[0] " + (@actor.level >= min_lvl ? "\\c[3]" : "\\c[2]") + min_lvl.to_s + "\\c[0]")
y += y_plus
point_cost = Skill_Tree_System_Functions.get_skill_point_cost(@skill)
enough_skill_point = Skill_Tree_System_Functions.point_comparison(point_cost, @actor.skill_point)
if enough_skill_point
text = "\\c[3]"
else
text = "\\c[2]"
end
draw_text_ex(0, y, "\\c[6]Требуемое количество очков:\\c[0] " + text + point_cost.to_s + "\\c[0]")
y += y_plus
text = format_needed_skills_text
if text != ""
draw_text_ex(0, y, "\\c[6]Требуемые навыки:\\c[0] " + text)
y += y_plus
end
if @skill.mp_cost > 0
draw_text_ex(0, y, "\\c[6]Использование MP:\\c[0] " + @skill.mp_cost.to_s + " MP")
y += y_plus
end
if @skill.tp_cost > 0
draw_text_ex(0, y, "\\c[6]Использование TP:\\c[0] " + @skill.tp_cost.to_s + " TP")
y += y_plus
end
end
end
def format_needed_skills_text
need_skill = Skill_Tree_System_Functions.get_needed_skills(@skill)
return "" if need_skill.nil?
skill = $data_skills[need_skill]
text = skill.name + " (" + Skill_Tree_System_Functions.get_skill_point_cost(skill).to_s + ")\\c[0]"
if @actor.skill_learn?(skill)
return "\\c[3]" + text
else
return "\\c[2]" + text
end
end
def make_command_list
return if @minimize
add_command("Закрыть", :cancel)
add_command(@actor.skill_learn?(@skill) ? "Изучено" : "Изучить", :learn, Skill_Tree_System_Functions.get_can_learn_skill(@actor, @skill))
end
def cursor_left(wrap = false)
if index >= col_max || (wrap && col_max == 1)
select((index - col_max + item_max) % item_max)
end
end
def cursor_right(wrap = false)
if index < item_max - col_max || (wrap && col_max == 1)
select((index + col_max) % item_max)
end
end
def cursor_down(wrap = false)
end
def cursor_up(wrap = false)
end
end # class Skill_Tree_System_Skill_Info_Window < Window_Command
class Skill_Tree_System_Functions
include Skill_Tree_System_Settings
# индекс класса в базе данных, к которому относится навык
def self.get_skill_class_id(skill)
classes = []
i = 1
SKILL_CLASSES.each do |class_mas|
class_mas[1].each do |index|
classes.push(i) if index[0] == skill.id
end
i += 1
end
return classes
end
# минимальный уровень для навыка
def self.get_skill_min_lvl(skill)
if skill.note =~ /skill_tree_skill_min_lvl[\s]*=[\s]*([\d]+)/i
min_lvl = $1.to_i
min_lvl = 1 if min_lvl < 1
end
min_lvl = 1 if min_lvl.nil?
return min_lvl
end
def self.min_lvl_comparsion(actor, skill)
return actor.level >= get_skill_min_lvl(skill)
end
# хватает ли очков для изучения навыка
def self.point_comparison(skill_point, player_skill_point)
return skill_point > player_skill_point ? false : true
end
# количество очков, нужных для изучения навыка
def self.get_skill_point_cost(skill)
if skill.note =~ /skill_tree_point_cost[\s]*=[\s]*([\d]+)/i
point_cost = $1.to_i
point_cost = 1 if point_cost.to_i < 1
end
point_cost = 1 if point_cost.nil?
return point_cost
end
# индекс дочернего навыка в базе данных, который нужен, чтобы изучить родительский
def self.get_needed_skills(skill)
if skill.note =~ /skill_tree_need_skill[\s]*=[\s]*([\d]+)/i
need_skill = $1.to_i
end
return need_skill
end
# может ли персонаж выучить навык
def self.get_can_learn_skill(actor, skill)
return false if actor.skill_learn?(skill)
skill_point_cost = get_skill_point_cost(skill)
return false if !point_comparison(skill_point_cost, actor.skill_point)
return false if !min_lvl_comparsion(actor, skill)
return true
end
# состояние навыка (1 - изучено, 2 - можно выучить, 3 - нельзя выучить)
def self.get_skill_learn_state(actor, skill)
return 1 if actor.skill_learn?(skill)
return 2 if get_can_learn_skill(actor, skill)
return 3 # не может выучить
end
end
class Game_Actor < Game_Battler
attr_accessor :skill_point
attr_reader :actor_id
alias denis_kyznetsov_skill_tree_system_game_actor_setup setup
def setup(actor_id)
denis_kyznetsov_skill_tree_system_game_actor_setup(actor_id)
@skill_point = @level - 1
end
def level_up
@level += 1
@skill_point += Skill_Tree_System_Settings::NEW_LEVEL_SKILL_POINT
end
def init_skills
@skills = []
end
end # class Game_Actor < Game_Battler
class Game_Interpreter
def open_skill_tree_system
SceneManager.call(Skill_Tree_System_Scene)
end
def add_skill_point(actor_id, skill_point)
actor = nil
$game_party.members.each do |index|
actor = index if index.actor_id == actor_id
end
return if actor.nil?
actor.skill_point += skill_point
end
def forget_all_skills(actor_id, add_skill_point)
actor = nil
$game_party.members.each do |index|
actor = index if index.actor_id == actor_id
end
return if actor.nil?
$data_skills.each do |skill|
if actor.skill_learn?(skill)
actor.forget_skill(skill.id)
actor.skill_point += Skill_Tree_System_Functions.get_skill_point_cost(skill) if add_skill_point
end
end
end
end # class Game_Interpreter