LeoAltholic = {} LeoAltholic.name = "LeoAltholic" LeoAltholic.displayName = "Leo's Altholic" LeoAltholic.version = "1.0.0" LeoAltholic.chatPrefix = "|c39B027" .. LeoAltholic.name .. "|r: " LeoAltholic.timerQueue = {} LeoAltholic.maxTraits = select(3,GetSmithingResearchLineInfo(1,1)) LeoAltholic.jewelryMaxTraits = select(3,GetSmithingResearchLineInfo(7,1)) LeoAltholic.panelList = { "Bio", "Stats", "Skills", "Champion", "Inventory", "Research" } LeoAltholic.craftResearch = {CRAFTING_TYPE_BLACKSMITHING,CRAFTING_TYPE_CLOTHIER,CRAFTING_TYPE_WOODWORKING,CRAFTING_TYPE_JEWELRYCRAFTING} function LeoAltholic.loadPlayerDataPart(skillType, baseElem) if skillType == nil then return end local numSkillLines = GetNumSkillLines(skillType) for i = 1, numSkillLines do local name, rank, discovered, lineId, advised, unlockText = GetSkillLineInfo(skillType,i) if name == nil then name = i; end if discovered then baseElem[i] = {} local baseElemTable = baseElem[i] local qty = GetNumSkillAbilities(skillType, i) baseElemTable.name = name baseElemTable.id = i baseElemTable.qty = qty baseElemTable.rank = rank baseElemTable.lineId = lineId baseElemTable.list = {} for aj = 1, qty do local name2, icon, earnedRank, passive, ultimate, purchased, progressionIndex = GetSkillAbilityInfo(skillType, i, aj) local currentUpgradeLevel, maxUpgradeLevel = GetSkillAbilityUpgradeInfo(skillType, i, aj) if rank >= earnedRank then local _, _, nextUpgradeEarnedRank = GetSkillAbilityNextUpgradeInfo(skillType, i, aj) local plainName = zo_strformat(SI_ABILITY_NAME, name2) name2 = ZO_Skills_GenerateAbilityName(SI_ABILITY_NAME_AND_UPGRADE_LEVELS, name2, currentUpgradeLevel, maxUpgradeLevel, progressionIndex) baseElemTable.list[aj] = {} local selL = baseElemTable.list[aj] selL.plainName = plainName selL.name = name2 selL.earnedRank = earnedRank or 0 selL.level = currentUpgradeLevel selL.maxLevel = maxUpgradeLevel selL.nextUpgradeEarnedRank = nextUpgradeEarnedRank if passive then selL.passive = passive end if ultimate then selL.ultimate = ultimate end end end end end end function LeoAltholic.InitCharsList() if LeoAltholic.savedVariables.CharList == nil then LeoAltholic.savedVariables.CharList = {} end local function getStat(stat) return GetPlayerStat(stat, STAT_BONUS_OPTION_APPLY_BONUS, STAT_SOFT_CAP_OPTION_APPLY_SOFT_CAP) end local numChars = GetNumCharacters() for k, v in pairs(LeoAltholic.savedVariables.CharList) do local deleted = true for i = 1, numChars do local charName = GetCharacterInfo(i) charName = charName:gsub("%^.+", "") if k == charName then deleted = false break end end if deleted then LeoAltholic.savedVariables.CharList[k] = nil end end for _, char in pairs(LeoAltholic.GetCharacters()) do if char.research[craftName] ~= nil then research = {} for _,craft in pairs(LeoAltholic.craftResearch) do local craftName = GetCraftingSkillName(craft) char.research[craft] = GetBonus(craft) for line = 1, GetNumSmithingResearchLines(craft) do local lineName = GetSmithingResearchLineInfo(craft, line) char.research[craft][line] = {} for trait = 1, LeoAltholic.maxTraits do local traitType = GetSmithingResearchLineTraitInfo(craft, line, trait) local traitName = GetString('SI_ITEMTRAITTYPE',traitType) char.research[craft][line][trait] = char.research[craftName][lineName][traitName] end end end end end LeoAltholic.CharName = GetUnitName("player") LeoAltholic.CharNum = 0 local char = LeoAltholic.savedVariables.CharList[LeoAltholic.CharName] or { bio = {} } char.bio.name = LeoAltholic.CharName char.bio.gender = GetUnitGender("player") if char.bio.gender == 1 then char.bio.genderName = "Female" else char.bio.genderName = "Male" end char.bio.level = GetUnitLevel("player") char.bio.effectiveLevel = GetUnitEffectiveLevel("player") char.bio.isChampion = IsUnitChampion("player") char.bio.canChampion = CanUnitGainChampionPoints("player") char.bio.championPoints = GetPlayerChampionPointsEarned("player") char.bio.race = GetUnitRace("player") char.bio.raceId = GetUnitRaceId("player") char.bio.class = GetUnitClass("player") char.bio.classId = GetUnitClassId("player") char.bio.alliance = { id = GetUnitAlliance("player"), name = GetAllianceName(GetUnitAlliance("player")), rank = GetUnitAvARank("player"), points = GetCarriedCurrencyAmount(CURT_ALLIANCE_POINTS) } char.secondsPlayed = GetSecondsPlayed() char.bounty = GetBounty() local riding = {GetRidingStats()} local ridetime = GetTimeUntilCanBeTrained()/1000 or 0 if ridetime > 1 then ridetime = ridetime + GetTimeStamp() end char.attributes = { unspent = GetAttributeUnspentPoints(), health = { points = GetAttributeSpentPoints(ATTRIBUTE_HEALTH), max = getStat(STAT_HEALTH_MAX), recovery = getStat(STAT_HEALTH_REGEN_COMBAT) }, magicka = { points = GetAttributeSpentPoints(ATTRIBUTE_MAGICKA), max = getStat(STAT_MAGICKA_MAX), recovery = getStat(STAT_MAGICKA_REGEN_COMBAT) }, stamina = { points = GetAttributeSpentPoints(ATTRIBUTE_STAMINA), max = getStat(STAT_STAMINA_MAX), recovery = getStat(STAT_STAMINA_REGEN_COMBAT) }, riding = { capacity = riding[1], capacityMax = riding[2], stamina = riding[3], staminaMax = riding[4], speed = riding[5], speedMax = riding[6], time = ridetime }, weapon = { damage = getStat(STAT_WEAPON_POWER), critical = getStat(STAT_CRITICAL_STRIKE), criticalChance = GetCriticalStrikeChance(getStat(STAT_CRITICAL_STRIKE)) }, spell = { damage = getStat(STAT_SPELL_POWER), critical = getStat(STAT_SPELL_CRITICAL), criticalChance = GetCriticalStrikeChance(getStat(STAT_SPELL_CRITICAL)) } } char.attributes.total = char.attributes.unspent + char.attributes.health.points + char.attributes.magicka.points + char.attributes.stamina.points char.resistances = { armor = getStat(STAT_ARMOR_RATING), spell = getStat(STAT_SPELL_RESIST), crit = getStat(STAT_CRITICAL_RESISTANCE), cold = getStat(STAT_DAMAGE_RESIST_COLD), disease = getStat(STAT_DAMAGE_RESIST_DISEASE), drown = getStat(STAT_DAMAGE_RESIST_DROWN), earth = getStat(STAT_DAMAGE_RESIST_EARTH), fire = getStat(STAT_DAMAGE_RESIST_FIRE), generic = getStat(STAT_DAMAGE_RESIST_GENERIC), magic = getStat(STAT_DAMAGE_RESIST_MAGIC), oblivion = getStat(STAT_DAMAGE_RESIST_OBLIVION), physical = getStat(STAT_DAMAGE_RESIST_PHYSICAL), poison = getStat(STAT_DAMAGE_RESIST_POISON), shock = getStat(STAT_DAMAGE_RESIST_SHOCK), start = getStat(STAT_DAMAGE_RESIST_START), mitigation = getStat(STAT_MITIGATION), physical = getStat(STAT_PHYSICAL_RESIST), spell_mitigation = getStat(STAT_SPELL_MITIGATION), miss = getStat(STAT_MISS), parry = getStat(STAT_PARRY), block = getStat(STAT_BLOCK), dodge = getStat(STAT_DODGE) } char.skills = { } char.skills.unspent = GetAvailableSkillPoints() char.skills.skyShards = GetNumSkyShards() local skillType = SKILL_TYPE_ARMOR char.skills.armor = {} local baseElem = char.skills.armor LeoAltholic.loadPlayerDataPart(skillType,baseElem) -- skillType = SKILL_TYPE_WORLD char.skills.world = {} baseElem = char.skills.world LeoAltholic.loadPlayerDataPart(skillType,baseElem) -- skillType = SKILL_TYPE_CLASS char.skills.class = {} baseElem = char.skills.class LeoAltholic.loadPlayerDataPart(skillType,baseElem) -- skillType = SKILL_TYPE_GUILD char.skills.guild = {} baseElem = char.skills.guild LeoAltholic.loadPlayerDataPart(skillType,baseElem) -- skillType = SKILL_TYPE_RACIAL char.skills.racial = {} baseElem = char.skills.racial LeoAltholic.loadPlayerDataPart(skillType,baseElem) -- skillType = SKILL_TYPE_WEAPON char.skills.weapon = {} baseElem = char.skills.weapon LeoAltholic.loadPlayerDataPart(skillType,baseElem) -- skillType = SKILL_TYPE_AVA char.skills.ava = {} baseElem = char.skills.ava LeoAltholic.loadPlayerDataPart(skillType,baseElem) skillType = SKILL_TYPE_TRADESKILL char.skills.craft = {} baseElem = char.skills.craft LeoAltholic.loadPlayerDataPart(skillType,baseElem) local function GetBonus(craft) local skillType0, skillId = GetCraftingSkillLineIndices(craft) local _, rank = GetSkillLineInfo(skillType0,skillId) return {rank = rank, max = GetMaxSimultaneousSmithingResearch(craft) or 1} end char.research = {} for _,craft in pairs(LeoAltholic.craftResearch) do char.research[craft] = GetBonus(craft) for line = 1, GetNumSmithingResearchLines(craft) do char.research[craft][line] = {} for trait = 1, LeoAltholic.maxTraits do if not char.research[craft][line][trait] then char.research[craft][line][trait] = {} end end end end for _,craft in pairs(LeoAltholic.craftResearch) do for line = 1, GetNumSmithingResearchLines(craft) do for trait = 1, LeoAltholic.maxTraits do local traitType, _, known = GetSmithingResearchLineTraitInfo(craft, line, trait) if known == false then local _,remaining = GetSmithingResearchLineTraitTimes(craft,line,trait) if remaining and remaining > 0 then char.research[craft][line][trait] = remaining + GetTimeStamp() else char.research[craft][line][trait] = false end else char.research[craft][line][trait] = true end end end end char.champion = {} for _, attribute in ipairs({ATTRIBUTE_HEALTH, ATTRIBUTE_MAGICKA, ATTRIBUTE_STAMINA}) do char.champion[attribute] = {} char.champion[attribute].spent = 0 char.champion[attribute].unspent = GetNumUnspentChampionPoints(attribute) char.champion[attribute].disciplines = {} end for i = 1, GetNumChampionDisciplines() do local attribute = GetChampionDisciplineAttribute(i) char.champion[attribute].disciplines[i] = { spent = GetNumPointsSpentInChampionDiscipline(i), skills = {} } char.champion[attribute].spent = char.champion[attribute].spent + char.champion[attribute].disciplines[i].spent for j = 1, GetNumChampionDisciplineSkills(i) do if WillChampionSkillBeUnlocked(i, j) then char.champion[attribute].disciplines[i].skills[j] = true else char.champion[attribute].disciplines[i].skills[j] = GetNumPointsSpentOnChampionSkill(i, j) end end end char.inventory = {} char.inventory.size = GetBagSize(BAG_BACKPACK) char.inventory.used = GetNumBagUsedSlots(BAG_BACKPACK) char.inventory.free = GetNumBagFreeSlots(BAG_BACKPACK) local _, _, soulGemEmpty = GetSoulGemInfo(SOUL_GEM_TYPE_EMPTY, char.bio.level, true) local _, _, soulGemFilled = GetSoulGemInfo(SOUL_GEM_TYPE_FILLED, char.bio.level, true) char.inventory.soulGemEmpty = soulGemEmpty char.inventory.soulGemFilled = soulGemFilled char.inventory.ap = GetAlliancePoints() char.inventory.gold = GetCurrentMoney() char.inventory.telvar = GetCarriedCurrencyAmount(CURT_TELVAR_STONES) char.inventory.writVoucher = GetCarriedCurrencyAmount(CURT_WRIT_VOUCHERS) local bag = SHARED_INVENTORY:GenerateFullSlotData(nil,BAG_WORN,BAG_BACKPACK) char.inventory[BAG_WORN] = {} char.inventory[BAG_BACKPACK] = {} for _, data in pairs(bag) do char.inventory[data.bagId][data.slotIndex] = { link = GetItemLink(data.bagId, data.slotIndex), name = data.name, count = data.stackCount } end if char.stats == nil then char.stats = {} end LeoAltholic.savedVariables.CharList[LeoAltholic.CharName] = char end local function copy(obj, seen) if type(obj) ~= 'table' then return obj end if seen and seen[obj] then return seen[obj] end local s = seen or {} local res = setmetatable({}, getmetatable(obj)) s[obj] = res for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end return res end function LeoAltholic.GetCharacters() local playerLines = {} local i = 1 for k, v in pairs(LeoAltholic.savedVariables.CharList) do if k == nil then return end playerLines[i] = copy(v) i = i + 1 end table.sort(playerLines, function(a, b) return a.bio.name < b.bio.name end) return playerLines end function LeoAltholic.GetItems(char, bagId) local itemLines = {} local i = 1 for k, v in pairs(char.inventory[bagId]) do if k == nil then return end itemLines[i] = copy(v) i = i + 1 end table.sort(itemLines, function(a, b) return a.name < b.name end) return itemLines end function LeoAltholic.ProcessQueue() for x,data in pairs(LeoAltholic.timerQueue) do if GetDiffBetweenTimeStamps(data.time, GetTimeStamp()) <= 0 then d(LeoAltholic.chatPrefix .. data.info) table.remove(LeoAltholic.timerQueue, x) end end end function LeoAltholic.GetTimer() LeoAltholic.timerQueue = {} local crafts = {CRAFTING_TYPE_BLACKSMITHING,CRAFTING_TYPE_CLOTHIER,CRAFTING_TYPE_WOODWORKING,CRAFTING_TYPE_JEWELRYCRAFTING} for _, char in pairs(LeoAltholic.GetCharacters()) do if GetDiffBetweenTimeStamps(char.attributes.riding.time - GetTimeStamp()) < 0 then local data = { id = '$M' .. char.bio.name, info = zo_strformat('<<C:1>> has finished a mount training.', char.bio.name), time = char.attributes.riding.time } table.insert(LeoAltholic.timerQueue, data) end for _,craft in pairs(crafts) do local craftName = GetCraftingSkillName(craft) for line = 1, GetNumSmithingResearchLines(craft) do local lineName, lineIcon = GetSmithingResearchLineInfo(craft, line) for trait = 1, LeoAltholic.maxTraits do local traitType = GetSmithingResearchLineTraitInfo(craft, line, trait) local traitName = GetString('SI_ITEMTRAITTYPE',traitType) local traitData = char.research[craft][line][trait] or false if type(traitData) == 'number' then local data = { id = '$R' .. char.bio.name..craft..line..trait, info = zo_strformat( '<<C:1>> has finished a research: |c00FF00<<C:2>> <<C:3>>|r.', char.bio.name, GetString('SI_ITEMTRAITTYPE',traitType), lineName ), time = traitData } if GetDiffBetweenTimeStamps(traitData - GetTimeStamp()) < 0 then d(LeoAltholic.chatPrefix .. data.info) else table.insert(LeoAltholic.timerQueue, data) end end end end end end end function LeoAltholic:OnUpdate() LeoAltholic.ProcessQueue() if LeoAltholic:isHidden() then return end local control for x,char in pairs(LeoAltholic.GetCharacters()) do local riding = '|t20:20:esoui/art/mounts/ridingskill_speed.dds|t ' .. char.attributes.riding.speed .. '%' .. ' |t20:20:esoui/art/mounts/ridingskill_stamina.dds|t ' .. char.attributes.riding.stamina .. ' |t20:20:esoui/art/mounts/ridingskill_capacity.dds|t ' .. char.attributes.riding.capacity .. ' |t22:22:esoui/art/miscellaneous/timer_32.dds|t ' .. LeoAltholic.GetTime(char.attributes.riding.time - GetTimeStamp()) control = WINDOW_MANAGER:GetControlByName('LeoAltholicWindowBioPanel_Character'..x..'Riding') control:SetText(riding) for _,craft in pairs(LeoAltholic.craftResearch) do local i = 1 local craftName = GetCraftingSkillName(craft) for line = 1, GetNumSmithingResearchLines(craft) do local lineName, lineIcon = GetSmithingResearchLineInfo(craft, line) for trait = 1, LeoAltholic.maxTraits do local traitType = GetSmithingResearchLineTraitInfo(craft, line, trait) local traitName = GetString('SI_ITEMTRAITTYPE',traitType) local traitData = char.research[craftName][lineName][traitName] if type(traitData) == 'number' then control = WINDOW_MANAGER:GetControlByName('LeoAltholicWindowResearchPanel_Character'..x..craftName..i.."Timer") control:SetText(LeoAltholic.GetTime(traitData - GetTimeStamp())) i = i + 1 end end end end end end function LeoAltholic.formatNumber(amount) if amount == nil then return nil; end if type(amount) == "string" then amount = tonumber( amount ) end if type(amount) ~= "number" then return amount; end if amount < 1000 then return amount; end return FormatIntegerWithDigitGrouping( amount, GetString( SI_DIGIT_GROUP_SEPARATOR ) ) end function LeoAltholic.Initialize() LeoAltholic.savedVariables = ZO_SavedVars:NewAccountWide("LeoAltholicSavedVariables", 2) local LibFeedback = LibStub:GetLibrary("LibFeedback") local showButton, feedbackWindow = LibFeedback:initializeFeedbackWindow(LeoAltholic, LeoAltholic.name,LeoAltholicWindow, "@LeandroSilva", {TOPRIGHT, LeoAltholicWindow, TOPRIGHT,-50,3}, {0,1000,10000,"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y9KM4PZU2UZ6A"}, "If you found a bug, have a request or a suggestion, or simply wish to donate, send a mail.") LeoAltholic.feedback = feedbackWindow LeoAltholic.RestorePosition() LeoAltholic.InitCharsList() LeoAltholic.InitializeCharacterFrames() LeoAltholic.DisplayCharacterFrames() LeoAltholic.GetTimer() --[[ SLASH_COMMANDS["/rr"] = function(cmd) ReloadUI() end SLASH_COMMANDS["/rc"] = function(cmd) LeoAltholic.savedVariables.CharList = {} ReloadUI() end ]] SLASH_COMMANDS["/leoalt"] = function(cmd) LeoAltholic:ShowUI() end end function LeoAltholic.NewMovementInUIMode(eventCode) if not LeoAltholicWindow:IsHidden() then LeoAltholic:HideUI() end end function LeoAltholic.OnChampionPerksSceneStateChange(oldState,newState) if newState == SCENE_SHOWING then if not LeoAltholicWindow:IsHidden() then LeoAltholic:HideUI() end end end function LeoAltholic.OnPlayerDeactivated(event, addonName) EVENT_MANAGER:UnregisterForEvent(LeoAltholic.Name, EVENT_PLAYER_DEACTIVATED) LeoAltholic.InitCharsList() end function LeoAltholic.OnAddOnLoaded(event, addonName) if addonName == LeoAltholic.name then EVENT_MANAGER:UnregisterForEvent(LeoAltholic.Name, EVENT_ADD_ON_LOADED) LeoAltholic.Initialize() -- d(LeoAltholic.name .. " started.") end end --SCENE_MANAGER:RegisterTopLevel(LeoAltholicWindow, false) EVENT_MANAGER:RegisterForEvent(LeoAltholic.name, EVENT_ADD_ON_LOADED, LeoAltholic.OnAddOnLoaded) EVENT_MANAGER:RegisterForEvent(LeoAltholic.name, EVENT_PLAYER_DEACTIVATED, LeoAltholic.OnPlayerDeactivated) EVENT_MANAGER:RegisterForUpdate(LeoAltholic.name, 5000, function() LeoAltholic.OnUpdate() end) EVENT_MANAGER:RegisterForEvent(LeoAltholic.name, EVENT_NEW_MOVEMENT_IN_UI_MODE, LeoAltholic.NewMovementInUIMode) CHAMPION_PERKS_SCENE:RegisterCallback('StateChange', LeoAltholic.OnChampionPerksSceneStateChange)