local gt = game.translate_string local gui_opened = false local classes_ar = { "sniper", "trooper", "jugger", "monster_hunter", "merc", "assassin" } local pick_title_ar = { [1] = "weapons", [2] = "outfits", [3] = "resists", [4] = "special", } local talent_types_ar = { [1] = "offensive", [2] = "defensive", [3] = "endurance", [4] = "speech", } local pick_stat_t = { weapons = { [1] = { "assault_rifle", {sniper = 2, trooper = 3, jugger = 1, monster_hunter = 2, merc = 3, assassin = 1} }, [2] = { "sniper_rifle", {sniper = 3, trooper = 1, jugger = 1, monster_hunter = 2, merc = 3, assassin = 1} }, [3] = { "shotgun", {sniper = 1, trooper = 2, jugger = 3, monster_hunter = 2, merc = 2, assassin = 1} }, [4] = { "machine_gun", {sniper = 1, trooper = 1, jugger = 3, monster_hunter = 2, merc = 2, assassin = 1} }, [5] = { "pistol", {sniper = 3, trooper = 1, jugger = 1, monster_hunter = 2, merc = 3, assassin = 3} }, [6] = { "smg", {sniper = 1, trooper = 3, jugger = 2, monster_hunter = 2, merc = 3, assassin = 3} }, }, outfits = { [1] = { "heavy", {sniper = 1, trooper = 3, jugger = 3, monster_hunter = 3, merc = 3, assassin = 1} }, [2] = { "medium", {sniper = 3, trooper = 3, jugger = 2, monster_hunter = 3, merc = 3, assassin = 3} }, [3] = { "sci", {sniper = 1, trooper = 3, jugger = 1, monster_hunter = 2, merc = 1, assassin = 3} }, [4] = { "light", {sniper = 3, trooper = 1, jugger = 1, monster_hunter = 1, merc = 1, assassin = 3} }, }, resists = { [1] = { "burn", {sniper = 3, trooper = 2, jugger = 1, monster_hunter = 2, merc = 2, assassin = 2} }, [2] = { "chem", {sniper = 3, trooper = 2, jugger = 1, monster_hunter = 2, merc = 2, assassin = 2} }, [3] = { "shock", {sniper = 3, trooper = 2, jugger = 1, monster_hunter = 2, merc = 2, assassin = 2} }, [4] = { "ballistic", {sniper = 1, trooper = 2, jugger = 3, monster_hunter = 1, merc = 3, assassin = 2} }, [5] = { "explosive", {sniper = 1, trooper = 2, jugger = 3, monster_hunter = 1, merc = 3, assassin = 2} }, [6] = { "rupture", {sniper = 1, trooper = 2, jugger = 3, monster_hunter = 3, merc = 1, assassin = 2} }, [7] = { "strike", {sniper = 1, trooper = 2, jugger = 3, monster_hunter = 3, merc = 1, assassin = 2} }, [8] = { "radiation", {sniper = 3, trooper = 1, jugger = 1, monster_hunter = 1, merc = 1, assassin = 1} }, [9] = { "psi", {sniper = 1, trooper = 1, jugger = 3, monster_hunter = 3, merc = 1, assassin = 1} }, }, special = { [1] = { "speed", {sniper = 3, trooper = 2, jugger = 1, monster_hunter = 2, merc = 1, assassin = 3} }, [2] = { "weight", {sniper = 1, trooper = 2, jugger = 3, monster_hunter = 1, merc = 2, assassin = 1} }, [3] = { "crit", {sniper = 3, trooper = 2, jugger = 1, monster_hunter = 2, merc = 2, assassin = 3} }, [4] = { "melee", {sniper = 1, trooper = 1, jugger = 3, monster_hunter = 3, merc = 2, assassin = 3} }, [5] = { "expl", {sniper = 1, trooper = 2, jugger = 1, monster_hunter = 1, merc = 3, assassin = 1} }, [6] = { "disguise", {sniper = 1, trooper = 1, jugger = 1, monster_hunter = 1, merc = 2, assassin = 3} }, [7] = { "stealth", {sniper = 1, trooper = 1, jugger = 1, monster_hunter = 1, merc = 1, assassin = 3} }, }, } local max_stats_num = 8 -- containers and strings amount local stats_t = { ["offensive"] = { ["titles"] = { "talents_stats_damage", "talents_stats_crit" }, ["sniper"] = { [1] = "sniper_rifle", [2] = "pistol", [3] = "assault_rifle" }, ["trooper"] = { [1] = "assault_rifle", [2] = "smg", [3] = "shotgun" }, ["jugger"] = { [1] = "shotgun", [2] = "machine_gun", [3] = "smg", [4] = "melee" }, ["monster_hunter"] = { [1] = "wpns_all", [2] = "melee" }, ["merc"] = { [1] = "assault_rifle", [2] = "sniper_rifle", [3] = "pistol", [4] = "smg", [5] = "shotgun", [6] = "machine_gun" }, ["assassin"] = { [1] = "pistol", [2] = "smg", [3] = "melee" }, }, ["defensive"] = { ["titles"] = { "talents_stats_def" }, ["sniper"] = { [1] = "burn", [2] = "chem", [3] = "shock", [4] = "radiation", [5] = "ballistic", [6] = "explosive", [7] = "rupture", [8] = "strike" }, ["trooper"] = { [1] = "ballistic", [2] = "explosive", [3] = "rupture", [4] = "strike", [5] = "burn", [6] = "chem", [7] = "shock" }, ["jugger"] = { [1] = "ballistic", [2] = "explosive", [3] = "rupture", [4] = "strike", [5] = "psi" }, ["monster_hunter"] = { [1] = "rupture", [2] = "strike", [3] = "burn", [4] = "chem", [5] = "shock", [6] = "psi", [7] = "ballistic", [8] = "explosive" }, ["merc"] = { [1] = "ballistic", [2] = "explosive", [3] = "burn", [4] = "chem", [5] = "shock", [6] = "rupture", [7] = "strike", [8] = "disguise" }, ["assassin"] = { [1] = "stealth", [2] = "disguise", [3] = "evade" }, }, ["endurance"] = { ["titles"] = { "talents_stats_endur" }, ["sniper"] = { [1] = "speed", [2] = "weight", [3] = "stamina" }, ["trooper"] = { [1] = "speed", [2] = "weight", [3] = "stamina" }, ["jugger"] = { [1] = "speed", [2] = "weight", [3] = "stamina" }, ["monster_hunter"] = { [1] = "speed", [2] = "weight", [3] = "stamina" }, ["merc"] = { [1] = "speed", [2] = "weight", [3] = "stamina" }, ["assassin"] = { [1] = "speed", [2] = "weight", [3] = "stamina" }, }, } ----------------- GUI ------------------ GUI = nil function on_key_press(k) if k ~= talents_functions.talents_key then return end if gui_opened then return end CreateTimeEvent("delay_talent_var_set", "delay_talent_var_set", 0.2, function() gui_opened = true return true end) hide_hud_inventory() if (not GUI) then GUI = TalentsMenu() end if (GUI) and (not GUI:IsShown()) then GUI:ShowDialog(true) Register_UI("TalentsMenu", "talents_menu_xcvb") end end class "TalentsMenu" (CUIScriptWnd) function TalentsMenu:__init() super() self:InitControls() self:InitCallBacks() end function TalentsMenu:InitControls() self:SetWndRect(Frect():set(0, 0, 1024, 768)) self:SetAutoDelete(true) self:AllowMovement(true) self.xml = CScriptXmlInit() local xml = self.xml xml:ParseFile("ui_xcvb_talents.xml") self.picked_class_gui = "null" self.cur_tree = "null" -- background self.bg = xml:InitStatic("bg", self) -- "pick class" self.stats_cont = {} -- main stats containers self.pick_titles = {} -- stat titles self.pick_strs = {} -- stat stats self.pick_bars = {} -- stat bars self:BuildClassMenu(xml) -- "talents" self.talent_conts = {} -- talent containers self.talents = {} -- talents -- "levels" self.tree_conts = {} -- tree containers self.trees = {} -- trees and levels -- stats self.stat_conts = {} -- stat containers self.talent_stats = {} -- stats self:BuildTalentsMenu(xml) -- if class isnt picked then show "pick class" menu if talents_functions.picked_class == "null" then self:ShowClassMenu() self:HideTalentsMenu() -- if class picked then show "talents" menu else self:HideClassMenu() self:ShowTalentsMenu() end end function TalentsMenu:InitCallBacks() -- pick class for idx, class_name in ipairs(classes_ar) do TalentsMenu["OnSelectClass_" .. class_name] = function(self) self:OnSelectClass(class_name) end self:AddCallback(class_name .. "_btn", ui_events.BUTTON_CLICKED, self["OnSelectClass_" .. class_name], self) end self:AddCallback("pick_accept_btn", ui_events.BUTTON_CLICKED, self.OnSelectClassPick, self) -- talent buttons for _, typ in ipairs(talent_types_ar) do for __, class_name in ipairs(classes_ar) do local t = self.talents[typ][class_name] for talent, ___ in pairs(t) do TalentsMenu["OnSelectTalent_" .. talent] = function(self) self:OnSelectTalent(talent) end self:AddCallback(talent, ui_events.BUTTON_CLICKED, self["OnSelectTalent_" .. talent], self) end end end -- tree buttons for _, typ in ipairs(talent_types_ar) do TalentsMenu["OnSelectTree_" .. typ] = function(self) self:OnSelectTree(typ) end self:AddCallback("tree_" .. typ, ui_events.BUTTON_CLICKED, self["OnSelectTree_" .. typ], self) end -- respec button self:AddCallback("talents_respec_btn", ui_events.BUTTON_CLICKED, self.OnClickRespec, self) -- respec ver buttons self:AddCallback("respec_btn_yes", ui_events.BUTTON_CLICKED, self.OnClickRespecYes, self) self:AddCallback("respec_btn_no", ui_events.BUTTON_CLICKED, self.OnClickRespecNo, self) end --------------------------------------------------------- ---------------------- Class Menu ---------------------- --------------------------------------------------------- function TalentsMenu:BuildClassMenu(xml) self.class_main = xml:InitStatic("bg:main_pick", self.bg) self.class_main:Show(false) -- LEFT self.class_left = xml:InitStatic("bg:main_pick:left", self.class_main) -- "pick class" left cont self.pick_class_cont = xml:InitStatic("bg:main_pick:left:pick_class_cont", self.class_left) align_element(self, self.pick_class_cont, self.class_left, "c", "c") self.pick_class_cont:SetWndPos(vector2():set(self.pick_class_cont:GetWndPos().x, self.pick_class_cont:GetWndPos().y - 100)) -- move a bit up -- title self.class_title = xml:InitTextWnd("bg:main_pick:left:pick_class_cont:class_title", self.pick_class_cont) -- classes cont self.classes_cont = xml:InitStatic("bg:main_pick:left:pick_class_cont:classes_cont", self.pick_class_cont) -- sniper button/icon self.pick_class_btns_t = {} for _, class_name in ipairs(classes_ar) do self.pick_class_btns_t[class_name] = {} self.pick_class_btns_t[class_name].cont = xml:InitStatic("bg:main_pick:left:pick_class_cont:classes_cont:" .. class_name .. "_cont", self.classes_cont) self.pick_class_btns_t[class_name].icon = xml:InitStatic("bg:main_pick:left:pick_class_cont:classes_cont:" .. class_name .. "_cont:" .. class_name .. "_icon", self.pick_class_btns_t[class_name].cont) self.pick_class_btns_t[class_name].btn = xml:Init3tButton("bg:main_pick:left:pick_class_cont:classes_cont:" .. class_name .. "_cont:" .. class_name .. "_btn", self.pick_class_btns_t[class_name].cont) self:Register(self.pick_class_btns_t[class_name].btn, class_name .. "_btn") end -- class description self.class_descr_cont = xml:InitStatic("bg:main_pick:left:pick_class_descr_cont", self.class_left) local pick_class_cont_pos = self.pick_class_cont:GetWndPos() local pick_class_cont_height = self.pick_class_cont:GetHeight() self.class_descr_cont:SetWndPos(vector2():set(pick_class_cont_pos.x - 50, pick_class_cont_pos.y + pick_class_cont_height)) self.class_descr_cont:Show(false) -- class description text self.class_descr_text = xml:InitTextWnd("bg:main_pick:left:pick_class_descr_cont:descr_text", self.class_descr_cont) -- RIGHT self.class_right = xml:InitStatic("bg:main_pick:right", self.class_main) self.descr_cont = xml:InitStatic("bg:main_pick:right:descr_cont", self.class_right) -- "pick class" right containers for _, name in ipairs(classes_ar) do self.stats_cont[name] = xml:InitStatic("bg:main_pick:right:descr_cont:pick_stats_cont", self.descr_cont) self.stats_cont[name]:Show(false) -- stats title local cur_w, cur_h = 0, 0 for idx, str in ipairs(pick_title_ar) do -- create title self.pick_titles[idx] = xml:InitTextWnd("bg:main_pick:right:descr_cont:pick_stats_cont:stat_title", self.stats_cont[name]) self.pick_titles[idx]:SetText(gt("stat_" .. str)) -- set title position local title_pos = vector2():set(self.pick_titles[idx]:GetWndPos()) self.pick_titles[idx]:SetWndPos(vector2():set(title_pos.x, cur_h)) -- shift Y pos by title height cur_h = cur_h + self.pick_titles[idx]:GetHeight() -- stats for i, ar in ipairs(pick_stat_t[str]) do -- create stat self.pick_strs[i] = xml:InitTextWnd("bg:main_pick:right:descr_cont:pick_stats_cont:stat_str", self.stats_cont[name]) self.pick_strs[i]:SetText(gt("stat_" .. ar[1])) -- set stat position local elem_pos = vector2():set(self.pick_strs[i]:GetWndPos()) self.pick_strs[i]:SetWndPos(vector2():set(elem_pos.x, cur_h)) -- shift X pos by stat width cur_w = self.pick_strs[i]:GetWidth() -- create bar local bar_val = ar[2][name] self.pick_bars[i] = xml:InitStatic("bg:main_pick:right:descr_cont:pick_stats_cont:stat_bar", self.stats_cont[name]) self.pick_bars[i]:InitTexture("stats_bar_" .. bar_val) -- set bar position self.pick_bars[i]:SetWndPos(vector2():set(cur_w, cur_h)) -- shift Y pos by stat height cur_h = cur_h + self.pick_strs[i]:GetHeight() -- if last elem then add more gap if i == #pick_stat_t[str] then cur_h = cur_h + 25 end end end end -- pick button self.pick_accept_btn = xml:Init3tButton("bg:main_pick:right:descr_cont:pick_accept_btn", self.descr_cont) self:Register(self.pick_accept_btn, "pick_accept_btn") self.pick_accept_btn:Enable(false) end function TalentsMenu:OnSelectClass(class_name) self:ResetClassMenu() self.picked_class_gui = class_name self:ShowClassInfo() end function TalentsMenu:ResetClassMenu() -- hide all classes stats for name, _ in pairs(self.stats_cont) do self.stats_cont[name]:Show(false) end -- remove class name self.class_title:SetText(gt("pick_class_pick_text")) -- remove class descr self.class_descr_text:SetText("") -- hide class descr container self.class_descr_cont:Show(false) -- remove picked class self.picked_class_gui = "null" -- disable pick button self.pick_accept_btn:Enable(false) end function TalentsMenu:ShowClassInfo() local name = self.picked_class_gui -- change class title name self.class_title:SetText(gt("pick_class_" .. name .. "_title")) -- change class descr self.class_descr_text:SetText(gt("pick_class_" .. name .. "_descr")) -- show class descr container self.class_descr_cont:Show(true) -- show stats container self.stats_cont[name]:Show(true) -- enable button self.pick_accept_btn:Enable(true) end function TalentsMenu:HideClassMenu() self:ResetClassMenu() -- hide pick class container self.class_main:Show(false) end function TalentsMenu:ShowClassMenu() self:ResetClassMenu() -- show pick class container self.class_main:Show(true) end function TalentsMenu:OnSelectClassPick() if self.picked_class_gui and self.picked_class_gui ~= "null" then -- set picked class talents_functions.picked_class = self.picked_class_gui -- send message that class was picked local class_str = gt("pick_class_" .. self.picked_class_gui .. "_title") news_manager.send_tip(db.actor, strformat(gt("pick_class_picked"), class_str), 0, nil, 10000) -- hide "pick class" menu self:HideClassMenu() -- show "talents" menu self:ShowTalentsMenu() end end --------------------------------------------------------- --------------------- Talents Menu --------------------- --------------------------------------------------------- function TalentsMenu:BuildTalentsMenu(xml) self.talents_main = xml:InitStatic("bg:main_talent", self.bg) self.talents_main:Show(false) self.talents_left = xml:InitStatic("bg:main_talent:left", self.talents_main) self.talents_right = xml:InitStatic("bg:main_talent:right", self.talents_main) self.talents_descr_cont = xml:InitStatic("bg:main_talent:right:descr_cont", self.talents_right) local gap_x, gap_y = 59, 116 local level_offset_x, level_offset_y = 30, 53 -- create left background, containers and talent table for idx, typ in ipairs(talent_types_ar) do -- LEFT self.talent_conts[typ] = {} self.talents[typ] = {} for __, class_name in ipairs(classes_ar) do self.talent_conts[typ][class_name] = {} -- backgrounds self.talent_conts[typ][class_name]["background"] = xml:InitStatic("bg:main_talent:left:talent_background", self.talents_left) local bg_texture = class_name .. "_" .. typ if typ == "endurance" or typ == "speech" then bg_texture = "bg_" .. typ end self.talent_conts[typ][class_name]["background"]:InitTexture(bg_texture) self.talent_conts[typ][class_name]["background"]:Show(false) -- containers self.talent_conts[typ][class_name]["container"] = xml:InitStatic("bg:main_talent:left:talent_container", self.talents_left) self.talent_conts[typ][class_name]["container"]:Show(false) -- talent table self.talents[typ][class_name] = {} -- create talent icons and buttons local class_t = talents_functions.talents_table[typ][class_name] for talent, talent_t in pairs(class_t) do self.talents[typ][class_name][talent] = {} local pos = { talent_t.pos.x * gap_x, talent_t.pos.y * gap_y } -- icon [1] self.talents[typ][class_name][talent][1] = xml:InitStatic("bg:main_talent:left:talent_container:icon", self.talent_conts[typ][class_name]["container"]) self.talents[typ][class_name][talent][1]:InitTexture("talent_" .. talent .. "_bw") self.talents[typ][class_name][talent][1]:SetWndPos(vector2():set(pos[1], pos[2])) -- button [2] self.talents[typ][class_name][talent][2] = xml:Init3tButton("bg:main_talent:left:talent_container:btn", self.talent_conts[typ][class_name]["container"]) self.talents[typ][class_name][talent][2]:SetWndPos(vector2():set(pos[1], pos[2])) self:Register(self.talents[typ][class_name][talent][2], talent) self.talents[typ][class_name][talent][2]:Enable(false) -- levels [3] self.talents[typ][class_name][talent][3] = xml:InitTextWnd("bg:main_talent:left:talent_container:levels", self.talent_conts[typ][class_name]["container"]) self.talents[typ][class_name][talent][3]:SetWndPos(vector2():set(pos[1] + level_offset_x, pos[2] + level_offset_y)) self.talents[typ][class_name][talent][3]:SetText("0/0") -- links [4] local required_talent for parent_talent, _ in pairs(talent_t.req) do if parent_talent ~= "all" then required_talent = parent_talent end end -- get link based on pos difference local req_pos = required_talent and talents_functions.talents_table[typ][class_name][required_talent].pos if req_pos then self.talents[typ][class_name][talent][4] = {} -- create link(s) self.talents[typ][class_name][talent][4][1] = xml:InitStatic("bg:main_talent:left:talent_container:link", self.talent_conts[typ][class_name]["container"]) self.talents[typ][class_name][talent][4][1]:InitTexture("talents_rect_bw") -- start pos real local start_pos_x = talent_t.pos.x * gap_x local start_pos_y = talent_t.pos.y * gap_y -- req pos real local dest_pos_x = req_pos.x * gap_x local dest_pos_y = req_pos.y * gap_y -- default icon width/height local def_icon_w = self.talents[typ][class_name][talent][1]:GetWidth() local def_icon_h = self.talents[typ][class_name][talent][1]:GetHeight() -- default link width/height local def_link_w = self.talents[typ][class_name][talent][4][1]:GetWidth() local def_link_h = self.talents[typ][class_name][talent][4][1]:GetHeight() local diff_x = req_pos.x - talent_t.pos.x local link1_pos_x = start_pos_x + (def_icon_w - def_link_w) / 2 if diff_x ~= 0 then local link1_pos_y = dest_pos_y + (def_icon_h - def_link_h) / 2 local link1_height = start_pos_y - link1_pos_y self.talents[typ][class_name][talent][4][1]:SetWndSize(vector2():set(def_link_w, link1_height)) self.talents[typ][class_name][talent][4][1]:SetWndPos(vector2():set(link1_pos_x, link1_pos_y)) -- create 2nd link self.talents[typ][class_name][talent][4][2] = xml:InitStatic("bg:main_talent:left:talent_container:link", self.talent_conts[typ][class_name]["container"]) self.talents[typ][class_name][talent][4][2]:InitTexture("talents_rect_bw") if diff_x < 0 then local link2_pos_x = dest_pos_x + def_icon_w local link2_pos_y = link1_pos_y local link2_width = link1_pos_x - link2_pos_x + def_link_w self.talents[typ][class_name][talent][4][2]:SetWndSize(vector2():set(link2_width, def_link_h)) self.talents[typ][class_name][talent][4][2]:SetWndPos(vector2():set(link2_pos_x, link2_pos_y)) else local link2_pos_x = link1_pos_x local link2_pos_y = link1_pos_y local link2_width = dest_pos_x - link1_pos_x self.talents[typ][class_name][talent][4][2]:SetWndSize(vector2():set(link2_width, def_link_h)) self.talents[typ][class_name][talent][4][2]:SetWndPos(vector2():set(link2_pos_x, link2_pos_y)) end -- if diff_x == 0 else local link1_pos_y = dest_pos_y + def_icon_h local link1_height = start_pos_y - link1_pos_y self.talents[typ][class_name][talent][4][1]:SetWndSize(vector2():set(def_link_w, link1_height)) self.talents[typ][class_name][talent][4][1]:SetWndPos(vector2():set(link1_pos_x, link1_pos_y)) end end end end -- RIGHT self.tree_conts[typ] = xml:InitStatic("bg:main_talent:right:descr_cont:tree_container", self.talents_descr_cont) local pos = self.tree_conts[typ]:GetWndPos() local h = self.tree_conts[typ]:GetHeight() local new_y = h * (idx - 1) self.tree_conts[typ]:SetWndPos(vector2():set(pos.x, new_y)) -- create icons and descr self.trees[typ] = {} -- icon container and button [1] self.trees[typ][1] = {} self.trees[typ][1].cont = xml:InitStatic("bg:main_talent:right:descr_cont:tree_container:icon_cont", self.tree_conts[typ]) self.trees[typ][1].icon = xml:InitStatic("bg:main_talent:right:descr_cont:tree_container:icon_cont:icon", self.trees[typ][1].cont) self.trees[typ][1].btn = xml:Init3tButton("bg:main_talent:right:descr_cont:tree_container:icon_cont:btn", self.trees[typ][1].cont) self:Register(self.trees[typ][1].btn, "tree_" .. typ) -- title [2] self.trees[typ][2] = xml:InitTextWnd("bg:main_talent:right:descr_cont:tree_container:title", self.tree_conts[typ]) -- title level [3] self.trees[typ][3] = xml:InitTextWnd("bg:main_talent:right:descr_cont:tree_container:title_level", self.tree_conts[typ]) -- bar container and bar with text [4] self.trees[typ][4] = {} self.trees[typ][4].cont = xml:InitStatic("bg:main_talent:right:descr_cont:tree_container:bar_cont", self.tree_conts[typ]) self.trees[typ][4].bar = xml:InitProgressBar("bg:main_talent:right:descr_cont:tree_container:bar_cont:bar", self.trees[typ][4].cont) self.trees[typ][4].bar:SetProgressPos(0.0) self.trees[typ][4].text = xml:InitTextWnd("bg:main_talent:right:descr_cont:tree_container:bar_cont:bar_text", self.trees[typ][4].cont) -- points [5] self.trees[typ][5] = xml:InitTextWnd("bg:main_talent:right:descr_cont:tree_container:points", self.tree_conts[typ]) end -- respec button self.respec_btn = xml:Init3tButton("bg:main_talent:right:descr_cont:respec_btn", self.talents_descr_cont) self:Register(self.respec_btn, "talents_respec_btn") -- stats self.stat_conts = {} self.stat_conts.main = xml:InitStatic("bg:main_talent:right:descr_cont:stat_container", self.talents_descr_cont) self.stat_conts.title = xml:InitTextWnd("bg:main_talent:right:descr_cont:stat_container:title", self.stat_conts.main) self.stat_conts.sum_1_title = xml:InitTextWnd("bg:main_talent:right:descr_cont:stat_container:sum_1_title", self.stat_conts.main) self.stat_conts.sum_2_title = xml:InitTextWnd("bg:main_talent:right:descr_cont:stat_container:sum_2_title", self.stat_conts.main) self.talent_stats = {} for i = 1, max_stats_num do self.talent_stats[i] = {} -- stat name self.talent_stats[i].name = xml:InitTextWnd("bg:main_talent:right:descr_cont:stat_container:name", self.stat_conts.main) local name_pos = self.talent_stats[i].name:GetWndPos() local name_h = self.talent_stats[i].name:GetHeight() local name_new_y = name_h * (i - 1) + name_pos.y self.talent_stats[i].name:SetWndPos(vector2():set(name_pos.x, name_new_y)) -- stat val 1 self.talent_stats[i].val1 = xml:InitTextWnd("bg:main_talent:right:descr_cont:stat_container:val1", self.stat_conts.main) local val1_pos = self.talent_stats[i].val1:GetWndPos() local val1_h = self.talent_stats[i].val1:GetHeight() local val1_new_y = val1_h * (i - 1) + val1_pos.y self.talent_stats[i].val1:SetWndPos(vector2():set(val1_pos.x, val1_new_y)) -- stat val 2 self.talent_stats[i].val2 = xml:InitTextWnd("bg:main_talent:right:descr_cont:stat_container:val2", self.stat_conts.main) local val2_pos = self.talent_stats[i].val2:GetWndPos() local val2_h = self.talent_stats[i].val2:GetHeight() local val2_new_y = val2_h * (i - 1) + val2_pos.y self.talent_stats[i].val2:SetWndPos(vector2():set(val2_pos.x, val2_new_y)) end -- respec verification container self.respec_cont = xml:InitStatic("bg:main_talent:right:descr_cont:respec_cont", self.talents_descr_cont) align_element(self, self.respec_cont, self.talents_descr_cont, "c", "c") self.respec_cont:Show(false) -- respec text and buttons "yes" and "no" self.respec_money = 100000 self.respec_text = xml:InitTextWnd("bg:main_talent:right:descr_cont:respec_cont:respec_text", self.respec_cont) self.respec_text:SetText(strformat(gt("talents_respec_ver"), self.respec_money)) self.respec_btn_yes = xml:Init3tButton("bg:main_talent:right:descr_cont:respec_cont:respec_btn_yes", self.respec_cont) self:Register(self.respec_btn_yes, "respec_btn_yes") self.respec_btn_no = xml:Init3tButton("bg:main_talent:right:descr_cont:respec_cont:respec_btn_no", self.respec_cont) self:Register(self.respec_btn_no, "respec_btn_no") -- tooltip self.talents_tooltip_cont = xml:InitStatic("bg:main_talent:right:talent_tooltip", self.talents_right) self.talents_tooltip_name = xml:InitTextWnd("bg:main_talent:right:talent_tooltip:name", self.talents_tooltip_cont) self.talents_tooltip_level = xml:InitTextWnd("bg:main_talent:right:talent_tooltip:levels", self.talents_tooltip_cont) self.talents_tooltip_req = xml:InitTextWnd("bg:main_talent:right:talent_tooltip:req", self.talents_tooltip_cont) self.talents_tooltip_descr = xml:InitTextWnd("bg:main_talent:right:talent_tooltip:descr", self.talents_tooltip_cont) self.talents_tooltip_cont:Show(false) end function TalentsMenu:OnSelectTree(typ) pr("select tree typ: %s", typ) if self.cur_tree == typ then return end pr("open tree: %s", typ) -- set current tree self.cur_tree = typ -- reset left talents self:ResetLeftTalents() -- update all talents menu self:UpdateTalentsMenu() end function TalentsMenu:ResetLeftTalents() -- hide all left background and containers for typ, t in pairs(self.talent_conts) do for class_name, t2 in pairs(t) do self.talent_conts[typ][class_name]["background"]:Show(false) self.talent_conts[typ][class_name]["container"]:Show(false) pr("reset left background and container for typ: %s || class: %s", typ, class_name) end end -- show picked one local class_name = talents_functions.picked_class self.talent_conts[self.cur_tree][class_name]["background"]:Show(true) self.talent_conts[self.cur_tree][class_name]["container"]:Show(true) pr("-show left background and container for typ: %s || class: %s", self.cur_tree, class_name) end local picked_font = 0 function TalentsMenu:UpdateTalentsMenu() self:UpdateTrees() self:UpdateTalents() self:UpdateStats() self.respec_cont:Show(false) if self.talents_tooltip_cont:IsShown() then self.talents_tooltip_cont:Show(false) end end function TalentsMenu:OnSelectTalent(talent) local class_name = talents_functions.picked_class pr("pressed talent %s", talent) -- update other script tables for typ, typ_t in pairs(talents_functions.talents_table) do -- talent found and talent tree has points local points = talents_leveling.talent_levels[typ].points if talents_functions.talents_table[typ][class_name][talent] and points > 0 then -- add level talents_functions.talents_table[typ][class_name][talent].level = talents_functions.talents_table[typ][class_name][talent].level + 1 -- remove talent point talents_leveling.talent_levels[typ].points = talents_leveling.talent_levels[typ].points - 1 -- add spent point talents_leveling.talent_levels[typ].spent_points = talents_leveling.talent_levels[typ].spent_points + 1 -- recalc item weight talents_functions.recalc_all_items_weight() end end -- update all talents self:UpdateTalentsMenu() end function TalentsMenu:UpdateTrees() local class_name = talents_functions.picked_class if class_name == "null" then return end -- update trees UI local level_t = talents_leveling.talent_levels local exp_t = talents_leveling.typ_levels for typ, t in pairs(level_t) do -- update icon self.trees[typ][1].icon:InitTexture(class_name .. "_" .. typ .. "_tree") -- update title self.trees[typ][2]:SetText(gt("tree_" .. typ)) -- update title level self.trees[typ][3]:SetText(t.lvl) -- update bar local max_level = t.lvl >= #exp_t local progress_val = max_level and (1.0) or (t.exp / (exp_t[t.lvl + 1])) self.trees[typ][4].bar:SetProgressPos(progress_val) local progress_text = max_level and (gt("tree_max_exp")) or (math.floor(t.exp) .. " / " .. (exp_t[t.lvl + 1])) self.trees[typ][4].text:SetText(progress_text) -- update points local points_text = "" if t.points > 0 then points_text = gt("tree_points") .. " " .. t.points end self.trees[typ][5]:SetText(points_text) end end function TalentsMenu:UpdateTalents() local class_name = talents_functions.picked_class if class_name == "null" then return end -- update UI according to table -- first iterate - update text, check requirements and enable/disable button for typ, typ_t in pairs(talents_functions.talents_table) do local spent_points = talents_leveling.talent_levels[typ].spent_points for talent, t in pairs(typ_t[class_name]) do -- update text self.talents[typ][class_name][talent][3]:SetText(t.level .. "/" .. t.max) -- check if requirements met local reqs_met = size_table(t.req) for req_name, req_val in pairs(t.req) do -- enough points spent if req_name == "all" then if spent_points >= req_val then reqs_met = reqs_met - 1 end -- other talents requirements are picked else local parent_talent = typ_t[class_name][req_name] if parent_talent.level >= req_val then reqs_met = reqs_met - 1 end end end -- 1) if reqs are met - change icon, enable button, update text, add link if reqs_met <= 0 then self.talents[typ][class_name][talent][1]:InitTexture("talent_" .. talent) self.talents[typ][class_name][talent][2]:Enable(true) if self.talents[typ][class_name][talent][4] then for i = 1, #self.talents[typ][class_name][talent][4] do self.talents[typ][class_name][talent][4][i]:InitTexture("talents_rect") end end -- 2) if reqs arent met - change icon to bw, disable button else self.talents[typ][class_name][talent][1]:InitTexture("talent_" .. talent .. "_bw") self.talents[typ][class_name][talent][2]:Enable(false) if self.talents[typ][class_name][talent][4] then for i = 1, #self.talents[typ][class_name][talent][4] do self.talents[typ][class_name][talent][4][i]:InitTexture("talents_rect_bw") end end end end end -- second iterate - check hinders, max out, points left for typ, typ_t in pairs(talents_functions.talents_table) do local points = talents_leveling.talent_levels[typ].points for talent, t in pairs(typ_t[class_name]) do -- 3) disable button of hindered talent and make it bw (+bw link) if t.level > 0 and t.hinder then pr("talent: %s || lvl: %s || hinders: %s", talent, t.level, t.hinder) self.talents[typ][class_name][t.hinder][1]:InitTexture("talent_" .. t.hinder .. "_bw") self.talents[typ][class_name][t.hinder][2]:Enable(false) if self.talents[typ][class_name][t.hinder][4] then for i = 1, #self.talents[typ][class_name][t.hinder][4] do self.talents[typ][class_name][t.hinder][4][i]:InitTexture("talents_rect_bw") end end end -- 4) disable button if talent maxed out or no points left if points <= 0 or t.level >= t.max then self.talents[typ][class_name][talent][2]:Enable(false) end -- 5) if no points left and button level 0 - change icon to bw, disable button (+bw link) if points <= 0 and t.level <= 0 then self.talents[typ][class_name][talent][1]:InitTexture("talent_" .. talent .. "_bw") self.talents[typ][class_name][talent][2]:Enable(false) if self.talents[typ][class_name][talent][4] then for i = 1, #self.talents[typ][class_name][talent][4] do self.talents[typ][class_name][talent][4][i]:InitTexture("talents_rect_bw") end end end end end end function TalentsMenu:UpdateStats() local class_name = talents_functions.picked_class local cur_typ = self.cur_tree -- clear strings first self.stat_conts.sum_1_title:SetText("") self.stat_conts.sum_2_title:SetText("") for i = 1, max_stats_num do self.talent_stats[i].name:SetText("") self.talent_stats[i].val1:SetText("") self.talent_stats[i].val2:SetText("") end -- fill new strings according to typ and class if not stats_t[cur_typ] then return end self.stat_conts.sum_1_title:SetText(gt(stats_t[cur_typ].titles[1]) or "") self.stat_conts.sum_2_title:SetText(gt(stats_t[cur_typ].titles[2]) or "") local class_stats_ar = stats_t[cur_typ][class_name] for i = 1, max_stats_num do if class_stats_ar[i] then -- fill names self.talent_stats[i].name:SetText(gt("stat_" .. class_stats_ar[i])) -- fill values local get_val_ar = talents_functions.calculate_stats(cur_typ, class_name, class_stats_ar[i]) -- get string if get_val_ar then local str1 = get_val_ar[1] .. "% (" .. get_val_ar[2] .. "%)" local str2 = "" if cur_typ == "offensive" and get_val_ar[3] and get_val_ar[4] then str2 = get_val_ar[3] .. "% (" .. get_val_ar[4] .. ")" end -- if stat is weight then change to kg if class_stats_ar[i] == "weight" then str1 = get_val_ar[1] .. " " .. gt("stat_weight_kg") -- if stat is stamina then change to % per sec elseif class_stats_ar[i] == "stamina" then str1 = get_val_ar[1] .. "%/" .. gt("stat_stamina_per_sec") end self.talent_stats[i].val1:SetText(str1) self.talent_stats[i].val2:SetText(str2) end end end end function TalentsMenu:HideTalentsMenu() self.talents_main:Show(false) end function TalentsMenu:ShowTalentsMenu() if talents_functions.picked_class == "null" then return end self.talents_main:Show(true) pr("Show talents menu") if self.cur_tree == "null" then self.cur_tree = "offensive" end -- reset left talents self:ResetLeftTalents() -- update all self:UpdateTalentsMenu() end --------------------------------------------------------- ------------------------ Respec ------------------------ --------------------------------------------------------- function TalentsMenu:OnClickRespec() -- open new verification window self.respec_cont:Show(true) -- enable/disable "yes" button based on money local has_money = db.actor:money() -1 > self.respec_money self.respec_btn_yes:Enable(has_money) end function TalentsMenu:OnClickRespecYes() db.actor:give_money(-self.respec_money) self.respec_cont:Show(false) -- respec talents_functions.respec_talents() talents_leveling.respec_levels() -- send news news_manager.send_tip(db.actor, gt("talents_respeced"), 0, nil, 10000) -- hide "talents" menu self:HideTalentsMenu() -- show "pick class" menu self:ShowClassMenu() -- and close gui self:Close() end function TalentsMenu:OnClickRespecNo() self.respec_cont:Show(false) end -- cbs, utils and close function TalentsMenu:Update() CUIScriptWnd.Update(self) -- hide if self.talents_tooltip_cont:IsShown() then self.talents_tooltip_cont:Show(false) end local typ = self.cur_tree local class_name = talents_functions.picked_class if (typ == "null") or (class_name == "null") then return end -- hover icon tooltip local talent for tal, cont_ar in pairs(self.talents[typ][class_name]) do if cont_ar[2]:IsShown() and cont_ar[2]:IsCursorOverWindow() then talent = tal break end end if not talent then return end -- bg 128, 0 -- main_talent 22, 33 -- right 436, 0 -- xD return tooltip to 0, 0 local start_x = 128+22+436 local start_y = 33 self.talents_tooltip_cont:SetWndPos(vector2():set( -start_x, -start_y )) -- calc new pos based on cursor local elem_height = self.talents_tooltip_cont:GetHeight() local cursor_pos = GetCursorPosition() local tal_t = talents_functions.talents_table[typ][class_name][talent] local pos_y if tal_t.pos.y <= 1 then -- pos down pos_y = cursor_pos.y else -- pos up pos_y = cursor_pos.y - elem_height end if not pos_y then return end -- change pos local elem_width = self.talents_tooltip_cont:GetWidth() local elem_pos = self.talents_tooltip_cont:GetWndPos() self.talents_tooltip_cont:SetWndPos(vector2():set(elem_pos.x + cursor_pos.x - elem_width, elem_pos.y + pos_y)) -- change height local height_mult = talents_functions.get_tooltip_height_mult(typ, class_name, talent) self.talents_tooltip_cont:SetWndSize(vector2():set(200, 200 * height_mult)) -- show self.talents_tooltip_cont:Show(true) -- update name self.talents_tooltip_name:SetText(gt(talent .. "_name")) -- update levels self.talents_tooltip_level:SetText(tal_t.level .. "/" .. tal_t.max) -- update requirements local req_str = talents_functions.get_tooltip_req_str(typ, class_name, talent) self.talents_tooltip_req:SetText(req_str) -- update description local descr_str = talents_functions.get_tooltip_descr_str(typ, class_name, talent) self.talents_tooltip_descr:SetText(descr_str) end function TalentsMenu:OnKeyboard(key, keyboard_action) local res = CUIScriptWnd.OnKeyboard(self, key, keyboard_action) if (res == false) then if keyboard_action == ui_events.WINDOW_KEY_PRESSED then if gui_opened and (key == talents_functions.talents_key or key == DIK_keys.DIK_ESCAPE) then self:Close() end end end return res end function TalentsMenu:Close() if self:IsShown() then self:HideDialog() self:Show(false) if gui_opened then gui_opened = false end Unregister_UI("TalentsMenu") end end function TalentsMenu:__finalize() end ------------------------------------------ function align_element(gui, elem, cont, h, v, text_h, text_v) -- align_element(self, self.elem, self.cont, "c", "b") is bottom center if not (gui and elem) then return end if h and type(h) == "string" and cont then local elem_w = elem:GetWidth() local cont_w = cont:GetWidth() if (elem_w <= cont_w) then local horiz_align if h == "l" then horiz_align = 0 elseif h == "r" then horiz_align = cont_w - elem_w elseif h == "c" then horiz_align = (cont_w - elem_w) / 2 end if horiz_align then local elem_pos = elem:GetWndPos() elem:SetWndPos(vector2():set(horiz_align, elem_pos.y)) end end end if v and type(v) == "string" and cont then local elem_h = elem:GetHeight() local cont_h = cont:GetHeight() if (elem_h <= cont_h) then local vert_align if v == "t" then vert_align = 0 elseif v == "b" then vert_align = cont_h - elem_h elseif v == "c" then vert_align = (cont_h - elem_h) / 2 end if vert_align then local elem_pos = elem:GetWndPos() elem:SetWndPos(vector2():set(elem_pos.x, vert_align)) end end end if text_h and type(text_h) == "number" and text_h < 3 then elem:SetTextAlignment(text_h) end if text_v and type(text_v) == "number" and text_h < 3 then elem:SetVTextAlignment(text_v) end end function pr(...) if talents_functions.talents_dbg then printf(...) end end function update_talents_gui(a) if (a and a == "UIZoom") then return end if GUI ~= nil and talents_functions.picked_class ~= "null" then GUI:UpdateTalentsMenu() end end function on_game_start() RegisterScriptCallback("on_key_press", on_key_press) RegisterScriptCallback("GUI_on_show", update_talents_gui) end