hist = { name = "history", version = "38", initialised = false, SV={}, L={}, me = {}, playerName = "", -- might change playerID = "", -- Unique through renames tz_offset =0, debug = false } --GetWorldName() local function log_truncate(max) --Limit log to max for _ = max, (#hist.SV.log-1) do table.remove(hist.SV.log,1) if hist.debug then d("Truncated log. Size now: " .. tostring(#hist.SV.log)) end end end local function log(text) table.insert(hist.SV.log,{["TimeStamp"]=GetTimeStamp(), ["text"] = text, ["Char"] = hist.playerName, ["CharID"] = hist.playerID, ["world"] = GetWorldName(), }) end local function log_me(text) -- per char log table.insert(hist.me.log,{["TimeStamp"]=GetTimeStamp(), ["text"] = text, }) end local Keep_Achievement = function (id) -- is this an achievement we want to save? if hist.IDs[id] then return true else return false end end local function get_start () -- get better start date from Achievements -- Check Achievements local earliest_time = 0 local earliest_Achievement for id, ach in pairs(hist.me.ach) do if earliest_time == 0 then earliest_time = ach.time earliest_Achievement = id elseif earliest_time > ach.time then earliest_time = ach.time end end if earliest_time == 0 then -- No Achievements earliest_time = GetTimeStamp() end if hist.me.Created == nil then hist.me.Created = earliest_time elseif hist.me.Created > earliest_time then -- Quest was earlier, then reset to earlier if hist.debug then d("Start Time Reset for " .. hist.playerName) end log ("Start Time Reset.") hist.me.Created = earliest_time end end local function Achievement(_, name, points, id, link) local description local category_id = GetCategoryInfoFromAchievementId(id) local category_name =GetAchievementCategoryInfo(category_id) _,description,_,_,_,_,_= GetAchievementInfo(id) if Keep_Achievement(id) then hist.me.ach[id] = {} hist.me.ach[id].Category_ID = category_id hist.me.ach[id].Category = category_name hist.me.ach[id].time = GetTimeStamp() hist.me.ach[id].points = points hist.me.ach[id].name = zo_strformat(name) hist.me.ach[id].description = zo_strformat(description) hist.me.ach[id].link = link if hist.debug then d("Achievement Awarded:.. ") d(hist.me.ach[id]) end end end local function log_clear() hist.SV.log = {} log("Cleared") end function load_history() -- load up historic Achievements. -- Note some achievements like "Level 40 Hero" have become obsolete. CategoryID is nil log("Request to load historic Dungeon Achievements") local name,points,description,completed,adate,atime local count = 0 local reject = 0 local flag = 0 for i = 1,2000 do name,description,points,_,completed,adate,atime= GetAchievementInfo(i) if completed then local category_id = GetCategoryInfoFromAchievementId(i) -- nil if bad if category_id ~= nil and Keep_Achievement(i) then if hist.me.ach[i] == nil then hist.me.ach[i] = {} hist.me.ach[i].Category_ID = category_id hist.me.ach[i].Category = GetAchievementCategoryInfo(category_id) hist.me.ach[i].time = (luatz_esodate(adate .. " " .. atime)+ hist.tz_offset) hist.me.ach[i].points = points hist.me.ach[i].name = zo_strformat(name) hist.me.ach[i].description = zo_strformat(description) hist.me.ach[i].link = GetAchievementLink(i) count = count + 1 end else reject = reject +1 end end end log("Added " .. tostring(count) .. " historic achievements.") if hist.debug then d("Added " .. count .. " achievements.") d("Rejected " .. reject .. " achievements.") end end local function fix() -- fix links etc for any old data local description hist.me.ach_updates = nil log("Request to fix Achievement data") local obsolete = 0 for i,_ in pairs(hist.me.ach) do _, description,_,_,_,_,_= GetAchievementInfo(i) local category_id = GetCategoryInfoFromAchievementId(i) local category_name =GetAchievementCategoryInfo(category_id) if category_id ~= nil then hist.me.ach[i]["Category_ID"] = category_id hist.me.ach[i]["Category"] = category_name hist.me.ach[i]["description"] = zo_strformat(description) hist.me.ach[i]["link"] = GetAchievementLink(i) else --Obsolete hist.me.ach[i] = nil obsolete = obsolete +1 end end local msg msg = obsolete .. " obsolete achievments removed." log (msg) if hist.debug then d(msg) end end local function new_map() local map = zo_strformat("<>",GetMapName()) if hist.me.maps[map] == nil then hist.me.maps[map] = {} hist.me.maps[map].visit = 1 hist.me.maps[map].firstvisitdate = GetTimeStamp() if hist.debug then d("New Map: " .. map ) end log("New map: " .. map) end return map end local function Activated() hist.me.timeplayed_last = hist.me.timeplayed hist.me.timeplayed = math.floor(GetSecondsPlayed() /60) if hist.me.levels[hist.me.level] ~= nil then hist.me.levels[hist.me.level].time = hist.me.timeplayed end if hist.world.CP_Level > 0 and hist.world.CP[hist.world.CP_Level] ~= nil then if hist.me.timeplayed_last == nil then -- is nil on new chars hist.me.timeplayed_last = 0 end hist.world.CP[hist.world.CP_Level].time = hist.world.CP[hist.world.CP_Level].time + hist.me.timeplayed - hist.me.timeplayed_last end local map = new_map() if hist.me.maps[map].visit == nil then hist.me.maps[map].visit = 1 else hist.me.maps[map].visit = hist.me.maps[map].visit +1 end end local function levelup(_, unitTag,_) if unitTag ~= "player" then return end hist.me.base_level = GetUnitLevel("player") hist.me.veteran_level = GetUnitVeteranRank("player") -- Duplication of info, but easier to work with local map = new_map() if hist.me.maps[map].timeslevel == nil then hist.me.maps[map].timeslevel = 1 hist.me.maps[map].firstlevel = hist.me.level else hist.me .maps[map].timeslevel = hist.me.maps[map].timeslevel + 1 end -- if hist.me.veteran_level > 0 then return end hist.me.level = hist.me.base_level + hist.me.veteran_level log("Level up to " .. hist.me.level) hist.me.levels[hist.me.level] = { begin = GetTimeStamp(), time = 0, -- Accumulated time in Minutes Ach_Points = GetEarnedAchievementPoints(), deaths = 0 } --Update Level data with map if hist.me.levels[hist.me.level].map == nil then hist.me.levels[hist.me.level].map = map local msg = "First Levelup in map " .. map log (msg) if hist.debug then d(msg) end end end local function CP_Levelup(_,unitTag, _, _) if unitTag ~= "player" then return end hist.world.CP_Level=GetPlayerChampionPointsEarned() hist.world.CP[hist.world.CP_Level] = {} hist.world.CP[hist.world.CP_Level].begin= GetTimeStamp() hist.world.CP[hist.world.CP_Level].playerID = hist.playerID hist.world.CP[hist.world.CP_Level].map = new_map() hist.world.CP[hist.world.CP_Level].deaths =0 hist.world.CP[hist.world.CP_Level].time=0 hist.me.base_level = GetUnitLevel("player") hist.me.veteran_level = GetUnitVeteranRank("player") hist.me.level = hist.me.base_level + hist.me.veteran_level log("CP_Level up to " .. hist.world.CP_Level) end local function CP_Achieved(_,_) log("Achieved Champion Rank.") log_me("Achieved Champion Rank.") end local function gendertext() if (GetUnitGender("player") == GENDER_MALE) then return "M" end if (GetUnitGender("player") == GENDER_FEMALE) then return "F" end return "U" end local function setup_char() log("Set up: " .. hist.playerName ) hist.SV.data[hist.playerID] = {} -- initialise data for current char, using unique GUID hist.me = hist.SV.data[hist.playerID] hist.me.name=hist.playerName hist.me.Class = zo_strformat("<>",GetUnitClass("player")) hist.me.Race = zo_strformat("<>",GetUnitRace("player")) hist.me.Gender = gendertext() hist.me.base_level = GetUnitLevel("player") hist.me.veteran_level = GetUnitVeteranRank("player") hist.me.level = hist.me.base_level + hist.me.veteran_level hist.me.world = GetWorldName() hist.me.Alliance = zo_strformat("<>",GetAllianceName(GetUnitAlliance("player"))) hist.me.ach={} hist.me.maps={} hist.me.log={} hist.me.levels = {} hist.me.levels[hist.me.level] = { begin = GetTimeStamp(), time = 0, -- Accumulated time in Minutes Ach_Points = GetEarnedAchievementPoints(), deaths = 0 } -- end of defaults hist.me["LoadTime"] = GetTimeStamp() load_history() get_start() log_me("Started") end local function Dead() -- Per Level if hist.me.levels[hist.me.level] ~= nil then hist.me.levels[hist.me.level].deaths = hist.me.levels[hist.me.level].deaths +1 -- low level deaths -level 1 to 49, then jump to CP end -- Per Map local map = new_map() --Update map data with Deaths if hist.me.maps[map].deaths == nil then hist.me.maps[map].deaths = 0 hist.me.maps[map].firstdeathlevel = hist.me.level local msg = "First Death in map " .. map log (msg) if hist.debug then d("First Death in map " ..map) end end hist.me.maps[map].deaths = hist.me.maps[map].deaths +1 if hist.me.maps[map].firstdeathdate == nil then hist.me.maps[map].firstdeathdate = GetTimeStamp() end if hist.world.CP_Level == 0 then d("Death: world CP level == 0,aborting processing of CP" ) return end if hist.world.CP[hist.world.CP_Level] ~= nil then hist.world.CP[hist.world.CP_Level].deaths = hist.world.CP[hist.world.CP_Level].deaths +1 else local msg = "Died but CP entry for " .. tostring(hist.world.CP_Level) .. " was nil." log (msg) if hist.debug then d(msg) end end end del_old_Achievements = function() local count = 0 for playerID, player in pairs (hist.SV.data) do log("Deleting old Achievements for " .. player.name) for ID, ach in pairs (player.ach) do if not (Keep_Achievement (ID)) then player.ach[ID] = nil count = count +1 end end end local msg = count .. " obsolete achievement records deleted." log(msg) end del_old_levels = function () for playerID, player in pairs (hist.SV.data) do -- remove levels without maps- these are probably CP and dud anyway. log("Deleting old levels for " .. player.name) local dud = 0 for key,L in pairs (player.levels) do if L.map == nil then dud = dud +1 player.levels[key] = nil else if key > 66 then -- is CP move to accountwide if not already there local cpkey = key-50 -- max level now set to 50, so everything beyon if hist.world.CP[cpkey] == nil then hist.world.CP[cpkey] = {} hist.world.CP[cpkey].begin = L.begin hist.world.CP[cpkey].map = L.map hist.world.CP[cpkey].deaths = L.deaths hist.world.CP[cpkey].playerID = hist.playerID hist.world.CP[cpkey].time = L.time log(cpkey .. " moved to CP") player.levels[key] = nil -- and delete old else log(key .. " not moved to CP") end end end end end end function hist.Initialise(_, addOnName) if (hist.name ~= addOnName) then return end -- find computed time difference. local now = GetTimeStamp() hist.datestr = GetDateStringFromTimestamp(now) hist.timestr = GetTimeString() hist.luatz_ts = luatz_esodate(hist.datestr .." " .. hist.timestr) hist.tz_offset = now - hist.luatz_ts hist.playerName = GetUnitName("player") hist.playerID = GetCurrentCharacterId() -- Load the saved variables hist.SV = ZO_SavedVars:NewAccountWide("History_SV", 1, nil, nil) if hist.SV.log == nil then hist.SV.log = {} log("hist.SV.log created") end if debug then log("now: " .. now) log("hist.datestr: " .. hist.datestr) log("hist.timestr: " .. hist.timestr) log("hist.luatz_ts: " .. hist.luatz_ts) log("hist.tz_offset: " ..hist.tz_offset) end if hist.SV.worlds == nil then hist.SV.worlds = {} end if hist.SV.worlds[GetWorldName()] == nil then hist.SV.worlds[GetWorldName()] = {} end hist.world = hist.SV.worlds[GetWorldName()] if hist.world.CP == nil then hist.world.CP = {} end hist.world.CP_Level = GetPlayerChampionPointsEarned() if (hist.SV.data == nil ) then hist.SV.data = {} log("hist.SV.data created") end if (hist.SV.old ~= nil) then -- Delete legacy data that we never used anyway hist.SV.old = nil end hist.SV.CP_Level = nil -- once only move of CP data to new location. if hist.SV.CP ~= nil then hist.world.CP = hist.SV.CP hist.SV.CP = nil end -- CP leveling screwed, after all of this .. at least make current if hist.world.CP[hist.world.CP_Level] == nil then hist.world.CP[hist.world.CP_Level] = {} hist.world.CP[hist.world.CP_Level].begin= GetTimeStamp() hist.world.CP[hist.world.CP_Level].playerID = hist.playerID hist.world.CP[hist.world.CP_Level].map = "BORKY" hist.world.CP[hist.world.CP_Level].deaths =0 hist.world.CP[hist.world.CP_Level].time=0 hist.world.CP[hist.world.CP_Level].borked = true end --remove old achievement categories if hist.SV.cleaned1 == nil then del_old_levels() hist.SV.cleaned1 = 1 end if hist.SV.cleaned1 == 1 then del_old_Achievements() hist.SV.cleaned1 = 2 log("Deleted Old Achievements, cleaned1=2") end if (hist.SV.start_version == nil ) then hist.SV.start_version = hist.version end hist.SV.this_version = hist.version --- hist.SV.version -- Used by ZOS System, dont touch if hist.SV.data[hist.playerID] == nil then if debug then log("No data found for PlayerID") end -- Ok playerID may be nil, but we might still have data under player name -- In which case we rename it, otherwise it is a new char. if hist.SV.data[hist.playerName] == nil then --No name, no ID, must be new. setup_char() else -- Ok old name data, fix it. log("New data format, saving " .. hist.playerName .. " as " .. hist.playerID) hist.SV.data[hist.playerID] = hist.SV.data[hist.playerName] hist.SV.data[hist.playerID].name = hist.playerName hist.SV.data[hist.playerName] = nil -- and on the eighth day God invented Lua end end hist.me = hist.SV.data[hist.playerID] hist.me.CP = nil hist.me.craft = nil hist.me.base_level = GetUnitLevel("player") hist.me.veteran_level = GetUnitVeteranRank("player") hist.me.level = hist.me.base_level + hist.me.veteran_level hist.me.world = GetWorldName() if hist.me.levels == nil then hist.me.levels = {} end if hist.me.maps == nil then hist.me.maps = {} end if hist.me.log == nil then -- special events we want to keep, like name changes hist.me.log = {} end if hist.debug then log_truncate(500) else log_truncate(20) end hist.SV.lang=GetCVar("language.2") -- For offline use if hist.me.logins == nil then hist.me.logins = 0 end hist.me.logins = hist.me.logins +1 -- count them. hist.me["LoginTime"] = GetTimeStamp() if hist.me["ReloadTime"] == nil then hist.me["ReloadTime"] = GetTimeStamp() end if hist.me["ReloadTime"] + 80080 <= GetTimeStamp() then -- 23 hours or more have past load_history() -- pickup anything that might be missed hist.me["ReloadTime"] = GetTimeStamp() log ("Startup: Timed check on missed achievements.") end if hist.me.name ~= hist.playerName then log("Changed Name, Was " .. hist.me.name) log_me("Changed Name, Was " .. hist.me.name) hist.me.name = hist.playerName end if hist.me.Race ~= zo_strformat("<>",GetUnitRace("player")) then log("Changed Race, Was " .. hist.me.Race) log_me("Changed Race, Was " .. hist.me.Race) hist.me.Race = zo_strformat("<>",GetUnitRace("player")) end if hist.me.Gender ~= gendertext() then log("Changed Gender, Was " .. hist.me.Gender) log_me("Changed Gender, Was " .. hist.me.Gender) hist.me.Gender = gendertext() end EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_ACHIEVEMENT_AWARDED, Achievement) EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_LEVEL_UPDATE, levelup) EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_CHAMPION_POINT_UPDATE, CP_Levelup) EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_CHAMPION_LEVEL_ACHIEVED , CP_Achieved) -- EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_VETERAN_RANK_UPDATE, levelup) EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_PLAYER_ACTIVATED, Activated) -- EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_PLAYER_DEACTIVATED, Activated) EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_PLAYER_DEAD, Dead) EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_ZONE_CHANGED, new_map) hist.initialised = true end SLASH_COMMANDS["/histload"] = load_history SLASH_COMMANDS["/histfix"] = fix SLASH_COMMANDS["/histclear"] = log_clear SLASH_COMMANDS["/histstart"] = get_start EVENT_MANAGER:RegisterForEvent(hist.name, EVENT_ADD_ON_LOADED, hist.Initialise)