diff --git a/Inviter.lua b/Inviter.lua
new file mode 100644
index 0000000..b9c6319
--- /dev/null
+++ b/Inviter.lua
@@ -0,0 +1,96 @@
+local Inviter = {
+ started = false,
+ message = "x",
+ kickList = {}
+}
+
+function Inviter:Kick()
+ if not Inviter.started then return end
+ for name, data in pairs(Inviter.kickList) do
+ if data ~= nil and GetTimeStamp() - data.added > LeoDolmenRunner.settings.inviter.kickDelay then
+ LeoDolmenRunner.debug("Kicking " .. name)
+ for i = 1, GetGroupSize() do
+ local tag = GetGroupUnitTagByIndex(i)
+ if GetUnitName(tag) == name then
+ Inviter.kickList[name] = nil
+ GroupKick(tag)
+ break
+ end
+ end
+ end
+ end
+end
+
+function Inviter:CheckOfflines()
+ if not Inviter.started then return end
+ for i = 1, GetGroupSize() do
+ local unitTag = GetGroupUnitTagByIndex(i)
+ local name = GetUnitName(unitTag)
+ if not IsUnitOnline(unitTag) and Inviter.kickList[name] == nil then
+ LeoDolmenRunner.debug("Adding " .. name .. " to kick list")
+ Inviter.kickList[name] = {
+ unitTag = unitTag,
+ added = GetTimeStamp()
+ }
+ elseif IsUnitOnline(unitTag) and Inviter.kickList[name] ~= nil then
+ LeoDolmenRunner.debug("Removing " .. name .. " from kick list, back online")
+ Inviter.kickList[name] = nil
+ end
+ end
+end
+
+function Inviter:Update(tick)
+ if not Inviter.started or not IsUnitSoloOrGroupLeader("player") or tick ~= 5 then return end
+
+ self:CheckOfflines()
+ self:Kick()
+end
+
+function Inviter:ChatMessage(type, from, message)
+ if not Inviter.started or not IsUnitSoloOrGroupLeader("player") then return end
+
+ if GetGroupSize() >= LeoDolmenRunner.settings.inviter.maxSize then return end
+
+ if type ~= CHAT_CHANNEL_SAY and type ~= CHAT_CHANNEL_YELL and type ~= CHAT_CHANNEL_ZONE and type ~= CHAT_CHANNEL_ZONE_LANGUAGE_1 and
+ type ~= CHAT_CHANNEL_ZONE_LANGUAGE_2 and type ~= CHAT_CHANNEL_ZONE_LANGUAGE_3 and type ~= CHAT_CHANNEL_ZONE_LANGUAGE_4 then return end
+
+ if message ~= Inviter.message or from == nil or from == "" then return end
+
+ LeoDolmenRunner.log(zo_strformat("Inviting <<1>>", from))
+ from = from:gsub("%^.+", "")
+ GroupInviteByName(from)
+end
+
+function Inviter:StartStop()
+ if not Inviter.started then
+ Inviter:Start()
+ else
+ Inviter:Stop()
+ end
+end
+
+function Inviter:Stop()
+ LeoDolmenRunner.log("Stopping auto invite")
+ Inviter.started = false
+ EVENT_MANAGER:UnregisterForEvent(LeoDolmenRunner.name, EVENT_CHAT_MESSAGE_CHANNEL)
+ LeoDolmenRunnerWindowInviterPanelStartStopLabel:SetText("Start")
+end
+
+function Inviter:Start(message)
+ if not IsUnitSoloOrGroupLeader("player") then
+ LeoDolmenRunner.log("You need to be group leader to invite.")
+ return
+ end
+
+ if message ~= nil then Inviter.message = message end
+
+ LeoDolmenRunner.log("Starting auto invite. Listening to " .. Inviter.message)
+ Inviter.started = true
+ LeoDolmenRunnerWindowInviterPanelStartStopLabel:SetText("Stop")
+ EVENT_MANAGER:RegisterForEvent(LeoDolmenRunner.name, EVENT_CHAT_MESSAGE_CHANNEL, function(event,...) Inviter:ChatMessage(...) end)
+end
+
+function Inviter:Initialize()
+end
+
+LeoDolmenRunner.inviter = Inviter
diff --git a/LeoDolmenRunner.lua b/LeoDolmenRunner.lua
index 4cb43bd..dd137ed 100644
--- a/LeoDolmenRunner.lua
+++ b/LeoDolmenRunner.lua
@@ -1,371 +1,127 @@
-LeoDolmenRunner = {
- name = "LeoDolmenRunner",
- displayName = "Leo's Dolmen Runner",
- version = "1.0.1",
- chatPrefix = "|c39B027LeoDolmenRunner|r: ",
- hidden = true,
- timeout = 900,
- defaults = {
- autoTravel = false,
- direction = 1,
- started = false,
- run = {
- startedTime = 0,
- dolmensClosed = 0,
- xpPerMinute = 0,
- minutesToLevel = 0,
- hoursToLevel = 0,
- events = {}
- }
- },
- directions = {
- cw = 1,
- ccw = -1
- },
- wayshrines = {
- -- summerset 353 358 349 357 365
- [59] = { region="Alik'r Desert", cw = 60, ccw = 155},
- [60] = { region="Alik'r Desert", cw = 155, ccw = 59},
- [155] = { region="Alik'r Desert", cw = 59, ccw = 60},
- [127] = { region="Auridon", cw = 175, ccw = 176},
- [175] = { region="Auridon", cw = 176, ccw = 127},
- [176] = { region="Auridon", cw = 127, ccw = 175},
- [39] = { region="Bangkorai", cw = 206, ccw = 35},
- [206] = { region="Bangkorai", cw = 35, ccw = 39},
- [35] = { region="Bangkorai", cw = 39, ccw = 206},
- [30] = { region="Deshaan", cw = 27, ccw = 80},
- [27] = { region="Deshaan", cw = 80, ccw = 30},
- [80] = { region="Deshaan", cw = 30, ccw = 27},
- [95] = { region="Eastmarch", cw = 92, ccw = 90},
- [92] = { region="Eastmarch", cw = 90, ccw = 95},
- [90] = { region="Eastmarch", cw = 95, ccw = 92},
- [20] = { region="Glenumbra", cw = 6, ccw = 2},
- [6] = { region="Glenumbra", cw = 2, ccw = 20},
- [2] = { region="Glenumbra", cw = 20, ccw = 6},
- [207] = { region="Grahtwood", cw = 164, ccw = 21},
- [164] = { region="Grahtwood", cw = 21, ccw = 207},
- [21] = { region="Grahtwood", cw = 207, ccw = 164},
- [148] = { region="Greenshade", cw = 152, ccw = 149},
- [152] = { region="Greenshade", cw = 149, ccw = 148},
- [149] = { region="Greenshade", cw = 148, ccw = 152},
- [103] = { region="Malabal Tor", cw = 100, ccw = 105},
- [100] = { region="Malabal Tor", cw = 105, ccw = 103},
- [105] = { region="Malabal Tor", cw = 103, ccw = 100},
- [162] = { region="Reaper's March", cw = 158, ccw = 157},
- [158] = { region="Reaper's March", cw = 157, ccw = 162},
- [157] = { region="Reaper's March", cw = 162, ccw = 158},
- [10] = { region="Ravenspire", cw = 13, ccw = 86},
- [13] = { region="Ravenspire", cw = 86, ccw = 10},
- [86] = { region="Ravenspire", cw = 10, ccw = 13},
- [51] = { region="Shadowfen", cw = 53, ccw = 47},
- [53] = { region="Shadowfen", cw = 47, ccw = 51},
- [47] = { region="Shadowfen", cw = 51, ccw = 53},
- [73] = { region="Stonefalls", cw = 67, ccw = 71},
- [67] = { region="Stonefalls", cw = 71, ccw = 73},
- [71] = { region="Stonefalls", cw = 73, ccw = 67},
- [17] = { region="Stormhaven", cw = 19, ccw = 31},
- [19] = { region="Stormhaven", cw = 31, ccw = 17},
- [31] = { region="Stormhaven", cw = 17, ccw = 19},
- [110] = { region="The Rift", cw = 116, ccw = 114},
- [116] = { region="The Rift", cw = 114, ccw = 110},
- [114] = { region="The Rift", cw = 110, ccw = 116}
- }
-}
-
-local LastUpdateTimestamp = GetTimeStamp()
-
-function LeoDolmenRunner.log(message)
- d(LeoDolmenRunner.chatPrefix .. message)
-end
-
-function LeoDolmenRunner.onFastTravelInteraction(currentWayshrine)
- if LeoDolmenRunner.wayshrines[currentWayshrine] and LeoDolmenRunner.data.autoTravel then
- local nextWayshrine = nil
- if LeoDolmenRunner.data.direction == LeoDolmenRunner.directions.cw then
- nextWayshrine = LeoDolmenRunner.wayshrines[currentWayshrine].cw
- else
- nextWayshrine = LeoDolmenRunner.wayshrines[currentWayshrine].ccw
- end
- local discovered, name = GetFastTravelNodeInfo(nextWayshrine)
- if not discovered then
- LeoDolmenRunner.log("Can't fast travel: " .. name .. " not discovered yet.")
- return
- end
- FastTravelToNode(nextWayshrine)
- end
-end
-
-local function calculateXpPerMinute(xp, timestamp)
- local diff = GetDiffBetweenTimeStamps(GetTimeStamp(), timestamp)
- diff = (diff == 0) and 1 or diff
-
- return xp / (diff / 60)
-end
-
-local function TimeToLevel()
- local xpToLevel = 0
- if (CanUnitGainChampionPoints("player")) then
- xpToLevel = GetNumChampionXPInChampionPoint(GetPlayerChampionPointsEarned("player")) - GetPlayerChampionXP()
- else
- xpToLevel = GetNumExperiencePointsInLevel(GetUnitLevel("player")) - GetUnitXP("player")
- end
-
- local minutesToLevel = math.ceil(xpToLevel / LeoDolmenRunner.data.run.xpPerMinute)
-
- if minutesToLevel < 60 then return 0, minutesToLevel end
-
- local hoursToLevel = math.floor(minutesToLevel / 60)
- minutesToLevel = minutesToLevel - (hoursToLevel * 60)
-
- return hoursToLevel, minutesToLevel
-end
-
-function LeoDolmenRunner:UpdateData()
- local xpGained = 0
- local firstEventTimestamp = 0
- if (#LeoDolmenRunner.data.run.events > 0) then
- for i, event in pairs(LeoDolmenRunner.data.run.events) do
- if GetDiffBetweenTimeStamps(GetTimeStamp(), event.timestamp) > LeoDolmenRunner.timeout then
- LeoDolmenRunner.data.run.events[i] = nil
- else
- xpGained = xpGained + event.xp
-
- if firstEventTimestamp == 0 or event.timestamp < firstEventTimestamp then
- firstEventTimestamp = event.timestamp
- end
-
- end
- end
- end
-
- LeoDolmenRunner.data.run.xpPerMinute = calculateXpPerMinute(xpGained, firstEventTimestamp)
-
- if xpGained > 0 then
- LeoDolmenRunner.data.run.hoursToLevel, LeoDolmenRunner.data.run.minutesToLevel = TimeToLevel()
- end
-end
-
-function LeoDolmenRunner:OnExperienceGain(reason, level, previousExperience, currentExperience, championPoints)
- local event = {
- timestamp = GetTimeStamp(),
- xp = currentExperience - previousExperience
- }
- table.insert(LeoDolmenRunner.data.run.events, event)
-
- if reason == 7 then LeoDolmenRunner.data.run.dolmensClosed = LeoDolmenRunner.data.run.dolmensClosed + 1 end
-
- self:UpdateData()
-end
-
-local function getColor(value, max)
- local rate = value / max
- if rate > 0.8 then return '10FF10' end
- if rate > 0.5 then return 'FFFF00' end
- return 'FF1010'
-end
-
-function LeoDolmenRunner:UpdateTrainingGear()
- local stack = 0
- local stackRepair = 0
- local maxItems = 9
- local numItems = 0
- local bag = SHARED_INVENTORY:GenerateFullSlotData(nil,BAG_WORN)
- for _, data in pairs(bag) do
- local link = GetItemLink(data.bagId, data.slotIndex)
- if GetItemLinkTraitType(link) == ITEM_TRAIT_TYPE_ARMOR_TRAINING or GetItemLinkTraitType(link) == ITEM_TRAIT_TYPE_WEAPON_TRAINING then
- local activeWeaponPair = GetActiveWeaponPairInfo()
- if ( GetItemLinkItemType(link) == ITEMTYPE_ARMOR
- or
- (activeWeaponPair == ACTIVE_WEAPON_PAIR_MAIN and (data.slotIndex == EQUIP_SLOT_MAIN_HAND or data.slotIndex == EQUIP_SLOT_OFF_HAND))
- or
- (activeWeaponPair == ACTIVE_WEAPON_PAIR_BACKUP and (data.slotIndex == EQUIP_SLOT_BACKUP_MAIN or data.slotIndex == EQUIP_SLOT_BACKUP_OFF)))
- then
- local quality = select(8, GetItemInfo(data.bagId, data.slotIndex))
- local condition = GetItemCondition(data.bagId, data.slotIndex)
- local equipType = select(6, GetItemInfo(data.bagId, data.slotIndex))
- numItems = numItems + 1
- local xp = 0
- if GetItemLinkItemType(link) == ITEMTYPE_ARMOR then
- xp = 6 + quality
- elseif equipType == EQUIP_TYPE_TWO_HAND then
- numItems = numItems + 1
- xp = 4 + quality
- else
- xp = 2 + (0.5 * quality)
- end
- stack = stack + xp
- if condition == 0 then stackRepair = stackRepair + xp end
- end
- end
- end
-
- local text = ""
-
- if stackRepair > 0 then
- text = "|c" .. getColor(stack - stackRepair, stack) .. " " .. (stack - stackRepair) .. " / " .. stack .. "%|r (Repair)"
- else
- text = "|c" .. getColor(stack, stack) .. stack .. "%|r"
- end
-
- LeoDolmenRunnerWindowPanelXpFromGear:SetText(text)
- LeoDolmenRunnerWindowPanelTrainingGear:SetText("|c" .. getColor(numItems, maxItems) .. numItems .. " / " .. maxItems .. "|r")
-end
-
-function LeoDolmenRunner:CreateUI()
- if LeoDolmenRunner.data.started then
- LeoDolmenRunnerWindowPanelStartStopLabel:SetText("Stop")
- else
- LeoDolmenRunnerWindowPanelStartStopLabel:SetText("Start")
- end
- self:UpdateTrainingGear()
-end
-
-local function FormatNumber(num)
- return zo_strformat("<<1>>", ZO_LocalizeDecimalNumber(num))
-end
-
-local function FormatTime(seconds, short, colorizeCountdown)
- if short == nil then short = false end
- local formats = {
- dhm = SI_TIME_FORMAT_DDHHMM_DESC_SHORT,
- day = SI_TIME_FORMAT_DAYS,
- hm = SI_TIME_FORMAT_HHMM_DESC_SHORT,
- hms = SI_TIME_FORMAT_HHMMSS_DESC_SHORT,
- hour = SI_TIME_FORMAT_HOURS,
- ms = SI_TIME_FORMAT_MMSS_DESC_SHORT,
- m = SI_TIME_FORMAT_MINUTES
- }
- if seconds and seconds > 0 then
- local ss = seconds % 60
- local mm = math.floor(seconds / 60)
- local hh = math.floor(mm / 60)
- mm = mm % 60
- local dn = math.floor(hh / 24)
- local hhdn = hh - (dn*24)
-
- local ssF = string.format("%02d", ss)
- local mmF = string.format("%02d", mm)
- local hhF = string.format("%02d", hh)
- local hhdnF = string.format("%02d", hhdn)
-
- local result = ''
- if dn > 0 then
- if short then
- result = ZO_CachedStrFormat(GetString(formats.day), dn) .." "..ZO_CachedStrFormat(GetString(formats.hour), hhdnF)
- else
- result = ZO_CachedStrFormat(GetString(formats.dhm), dn, hhdnF, mmF)
- end
- elseif hh > 0 then
- if short then
- result = ZO_CachedStrFormat(GetString(formats.hm), hhF, mmF)
- else
- result = ZO_CachedStrFormat(GetString(formats.hms), hhF, mmF, ssF)
- end
- elseif mm >= 0 then result = ZO_CachedStrFormat(GetString(formats.ms), mmF, ssF)
- end
- return result
- else return ZO_CachedStrFormat(GetString(formats.m), 0) end
-end
-
-function LeoDolmenRunner:UpdateUI()
-
- self:UpdateTrainingGear()
-
- local currentTimestamp = GetTimeStamp()
- if LeoDolmenRunner.data.started then
- if GetDiffBetweenTimeStamps(currentTimestamp, LastUpdateTimestamp) >= 5 then
- self:UpdateData()
- LastUpdateTimestamp = currentTimestamp
- end
-
- local secs = currentTimestamp - LeoDolmenRunner.data.run.startedTime
- if secs > 0 then
- LeoDolmenRunnerWindowPanelTime:SetText(FormatTime(secs))
- end
- end
-
- LeoDolmenRunnerWindowPanelDolmensClosed:SetText(LeoDolmenRunner.data.run.dolmensClosed)
- LeoDolmenRunnerWindowPanelXP:SetText(FormatNumber(math.ceil(LeoDolmenRunner.data.run.xpPerMinute)) .. " / min")
-
- LeoDolmenRunnerWindowPanelXPNext:SetText(ZO_CachedStrFormat(GetString(SI_TIME_FORMAT_HHMM_DESC_SHORT), LeoDolmenRunner.data.run.hoursToLevel, LeoDolmenRunner.data.run.minutesToLevel))
-
- zo_callLater(function() self:UpdateUI() end, 1000)
-end
-
-function LeoDolmenRunner:StartStop()
- if LeoDolmenRunner.data.started then
- LeoDolmenRunner.data.started = false
- LeoDolmenRunner.data.autoTravel = false
- LeoDolmenRunnerWindowPanelStartStopLabel:SetText("Start")
- else
- LeoDolmenRunner.data.run = LeoDolmenRunner.defaults.run
- LeoDolmenRunner.data.run.startedTime = GetTimeStamp()
- LeoDolmenRunner.data.started = true
- LeoDolmenRunner.data.autoTravel = true
- LeoDolmenRunnerWindowPanelStartStopLabel:SetText("Stop")
- end
-end
-
-function LeoDolmenRunner:OnWindowMoveStop()
- LeoDolmenRunner.data.position = {
- left = LeoDolmenRunnerWindow:GetLeft(),
- top = LeoDolmenRunnerWindow:GetTop()
- }
-end
-
-function LeoDolmenRunner:RestorePosition()
- local position = LeoDolmenRunner.data.position or { left = 200; top = 200; }
- local left = position.left
- local top = position.top
-
- LeoDolmenRunnerWindow:ClearAnchors()
- LeoDolmenRunnerWindow:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, left, top)
- LeoDolmenRunnerWindow:SetDrawTier(DT_MEDIUM)
-end
-
-function LeoDolmenRunner:Initialize()
- local showButton, feedbackWindow = LibFeedback:initializeFeedbackWindow(LeoDolmenRunner,
- LeoDolmenRunner.name,LeoDolmenRunnerWindow, "@LeandroSilva",
- {TOPRIGHT, LeoDolmenRunnerWindow, 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.")
- LeoDolmenRunner.feedback = feedbackWindow
- LeoDolmenRunner.feedback:SetDrawLayer(DL_OVERLAY)
- LeoDolmenRunner.feedback:SetDrawTier(DT_MEDIUM)
-
- LeoDolmenRunnerWindowTitle:SetText(LeoDolmenRunner.displayName .. " v" .. LeoDolmenRunner.version)
-
- SLASH_COMMANDS["/ldr"] = function(cmd)
- LeoDolmenRunnerWindow:ToggleHidden()
- end
-
- if GetDisplayName() == "@LeandroSilva" then
- SLASH_COMMANDS["/rr"] = function(cmd)
- ReloadUI()
- end
- end
-
- self:RestorePosition()
-
- self:CreateUI()
- self:UpdateUI()
-end
-
-local function OnAddOnLoaded(event, addonName)
- if addonName ~= LeoDolmenRunner.name then return end
-
- EVENT_MANAGER:UnregisterForEvent(LeoDolmenRunner.name, EVENT_ADD_ON_LOADED)
- LeoDolmenRunner.data = LibSavedVars:NewCharacterSettings( LeoDolmenRunner.name .. "_Data", "Characters", LeoDolmenRunner.defaults )
-
- LeoDolmenRunner:Initialize()
-
- CALLBACK_MANAGER:RegisterCallback("LAM-PanelControlsCreated", OnSettingsControlsCreated)
- EVENT_MANAGER:RegisterForEvent(LeoDolmenRunner.name, EVENT_START_FAST_TRAVEL_INTERACTION, function(eventId, ...) LeoDolmenRunner.onFastTravelInteraction(...) end)
- EVENT_MANAGER:RegisterForEvent(LeoDolmenRunner.name, EVENT_EXPERIENCE_GAIN, function(eventId, ...)
- LeoDolmenRunner:OnExperienceGain(...)
- end)
-
- LeoDolmenRunner.log("started.")
-end
-
-EVENT_MANAGER:RegisterForEvent(LeoDolmenRunner.name, EVENT_ADD_ON_LOADED, OnAddOnLoaded)
+LeoDolmenRunner = {
+ name = "LeoDolmenRunner",
+ displayName = "Leo's Dolmen Runner",
+ version = "1.1.0",
+ chatPrefix = "|c39B027LeoDolmenRunner|r: ",
+ debug = false,
+ defaults = {
+ hidden = true,
+ runner = {
+ autoTravel = true,
+ autoDismiss = true,
+ reapplyBuff = true
+ },
+ inviter = {
+ maxSize = 24,
+ autoKick = false,
+ kickDelay = 10
+ }
+ }
+}
+
+local LDR = LeoDolmenRunner
+
+function LDR.log(message)
+ d(LDR.chatPrefix .. message)
+end
+
+function LDR.debug(message)
+ if not LDR.debug then return end
+ LDR.log("[D] " .. message)
+end
+
+local LastUpdateTimestamp = GetTimeStamp()
+
+function LDR:Update()
+ local currentTimestamp = GetTimeStamp()
+ local tick = 1
+ if GetDiffBetweenTimeStamps(currentTimestamp, LastUpdateTimestamp) >= 5 then
+ tick = 5
+ LastUpdateTimestamp = currentTimestamp
+ end
+
+ self.runner:Update(tick)
+ self.inviter:Update(tick)
+ self.ui:Update(tick)
+end
+
+function LDR:Initialize()
+ local showButton, feedbackWindow = LibFeedback:initializeFeedbackWindow(LeoDolmenRunner,
+ LDR.name,LeoDolmenRunnerWindow, "@LeandroSilva",
+ {TOPRIGHT, LeoDolmenRunnerWindow, TOPRIGHT,-35,-2},
+ {0,1000,10000,"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y9KM4PZU2UZ6A"},
+ "If you find a bug, have a request, or simply wish to donate, send me a mail.")
+ LDR.feedback = feedbackWindow
+ LDR.feedback:SetDrawTier(DT_HIGH)
+
+ LeoDolmenRunnerWindowTitle:SetText(LeoDolmenRunner.displayName .. " v" .. LeoDolmenRunner.version)
+
+ SLASH_COMMANDS["/ldr"] = function(cmd)
+ local options = {}
+ local searchResult = { string.match(cmd,"^(%S*)%s*(.-)$") }
+ for i,v in pairs(searchResult) do
+ if (v ~= nil and v ~= "") then
+ options[i] = string.lower(v)
+ end
+ end
+
+ if #options == 0 or options[1] == "help" then
+ LDR.log("Available options: ")
+ d(" /ldr toggle: Toggles the main window")
+ d(" /ldr show: Shows the main window")
+ d(" /ldr hide: Hides the main window")
+ d(" /ldr start: Starts the runner")
+ d(" /ldr stop: Stops the runner")
+ d(" /ldr cw: Changes direction to clockwise")
+ d(" /ldr ccw: Changes direction to counter clockwise")
+ d(" /ldr <letter>: Starts auto invite listening to the letter <letter>")
+ d(" /ldr ai stop: Stops auto invite")
+ return
+ end
+
+ if options[1] == "toggle" then LeoDolmenRunnerWindow:ToggleHidden()
+ elseif options[1] == "show" then LeoDolmenRunnerWindow:SetHidden(false)
+ elseif options[1] == "hide" then LeoDolmenRunnerWindow:SetHidden(true)
+ elseif options[1] == "start" then LDR.runner:Start()
+ elseif options[1] == "stop" then LDR.runner:Stop()
+ elseif options[1] == "cw" then LDR.runner:CW()
+ elseif options[1] == "ccw" then LDR.runner:CCW()
+ elseif options[1] == "ai" and #options == 2 and options[2] == "stop" then LDR.inviter:Stop()
+ elseif string.len(options[1]) == 1 then LDR.inviter:Start(options[1])
+ else LDR.log("Listening message must be only 1 letter (eg: x).")
+ end
+ end
+
+ if GetDisplayName() == "@LeandroSilva" then
+ SLASH_COMMANDS["/rr"] = function(cmd)
+ ReloadUI()
+ end
+ end
+
+ self.runner:Initialize()
+ self.inviter:Initialize()
+ self.ui:Initialize()
+
+ LDR.settingsPanel = LeoDolmenRunner_Settings:New()
+ LDR.settingsPanel:CreatePanel()
+
+ EVENT_MANAGER:RegisterForUpdate(LeoDolmenRunner.name, 1000, function() LDR:Update() end)
+end
+
+local function OnAddOnLoaded(event, addonName)
+ if addonName ~= LDR.name then return end
+
+ EVENT_MANAGER:UnregisterForEvent(LDR.name, EVENT_ADD_ON_LOADED)
+ LDR.settings = LibSavedVars:NewAccountWide( LDR.name .. "_Data", "Account", LDR.defaults )
+
+ LDR:Initialize()
+
+ CALLBACK_MANAGER:RegisterCallback("LAM-PanelControlsCreated", OnSettingsControlsCreated)
+ EVENT_MANAGER:RegisterForEvent(LDR.name, EVENT_START_FAST_TRAVEL_INTERACTION, function(eventId, ...) LDR.runner:OnFastTravelInteraction(...) end)
+ EVENT_MANAGER:RegisterForEvent(LDR.name, EVENT_EXPERIENCE_GAIN, function(eventId, ...) LDR.runner:OnExperienceGain(...) end)
+ EVENT_MANAGER:RegisterForEvent(LDR.name, EVENT_PLAYER_COMBAT_STATE, function(eventId, ...) LDR.runner:OnCombatState(...) end)
+
+ LDR.log("started.")
+end
+
+EVENT_MANAGER:RegisterForEvent(LDR.name, EVENT_ADD_ON_LOADED, OnAddOnLoaded)
diff --git a/LeoDolmenRunner.txt b/LeoDolmenRunner.txt
index c065702..7d1cde2 100644
--- a/LeoDolmenRunner.txt
+++ b/LeoDolmenRunner.txt
@@ -1,10 +1,14 @@
-## Title: Leo's Dolmen Runner
-## APIVersion: 100029 100030
-## Version: 1.0.1
-## AddOnVersion: 101
-## Author: |c39B027@LeandroSilva|r
-## SavedVariables: LeoDolmenRunner_Data
-## DependsOn: LibFeedback LibAddonMenu-2.0 LibSavedVars
-
-LeoDolmenRunner.lua
-LeoDolmenRunner.xml
+## Title: Leo's Dolmen Runner
+## APIVersion: 100029 100030
+## Version: 1.1.0
+## AddOnVersion: 110
+## Author: |c39B027@LeandroSilva|r
+## SavedVariables: LeoDolmenRunner_Data
+## DependsOn: LibFeedback LibAddonMenu-2.0 LibSavedVars
+
+LeoDolmenRunner.lua
+Runner.lua
+Inviter.lua
+Ui.lua
+Settings.lua
+LeoDolmenRunner.xml
diff --git a/LeoDolmenRunner.xml b/LeoDolmenRunner.xml
index b904175..4a591bd 100644
--- a/LeoDolmenRunner.xml
+++ b/LeoDolmenRunner.xml
@@ -1,29 +1,30 @@
<GuiXml>
- <Font name="LeoDolmenRunnerLargeFont" font="$(MEDIUM_FONT)|18|soft-shadow-thin"/>
<Font name="LeoDolmenRunnerNormalFont" font="$(MEDIUM_FONT)|16|soft-shadow-thin"/>
<Font name="LeoDolmenRunnerSmallFont" font="$(MEDIUM_FONT)|14|soft-shadow-thin"/>
<Controls>
<TopLevelControl name="LeoDolmenRunnerWindow" movable="true" mouseEnabled="true" clampedToScreen="true" hidden="true" tier="MEDIUM">
- <Dimensions x="350" y="250" />
+ <Dimensions x="300" y="250" />
<Anchor point="TOP" relativeTo="GuiRoot" relativePoint="CENTER" offsetY="100" />
- <OnMoveStop> LeoDolmenRunner:OnWindowMoveStop() </OnMoveStop>
+ <OnMoveStop> LeoDolmenRunner.ui:OnWindowMoveStop() </OnMoveStop>
+ <OnHide> LeoDolmenRunner.settings.hidden = true </OnHide>
+ <OnShow> LeoDolmenRunner.settings.hidden = false </OnShow>
<Controls>
<Backdrop name="$(parent)BG" centerColor="000000" edgeColor="222222">
<Anchor point="TOPLEFT" relativePoint="TOPLEFT" relativeTo="$(parent)"/>
- <Dimensions x="350" y="50"/>
+ <Dimensions x="300" y="40"/>
<Edge edgeSize="1"/>
</Backdrop>
- <Label name="$(parent)Title" color="39B027" font="ZoFontWinH3" wrapMode="ELLIPSIS"
+ <Label name="$(parent)Title" color="39B027" font="ZoFontWinH5" wrapMode="ELLIPSIS"
verticalAlignment="CENTER" mouseEnabled="false">
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="16" offsetY="10"/>
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="10" offsetY="10"/>
</Label>
<Button name="$(parent)Close" clickSound="Click">
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT" relativeTo="$(parent)" offsetX="-5" offsetY="4"/>
- <Dimensions x="40" y="40"/>
+ <Dimensions x="30" y="30"/>
<OnClicked>self:GetParent():SetHidden(true)</OnClicked>
<Controls>
<Backdrop name="$(parent)BG" centerColor="101010" edgeColor="202020">
@@ -31,14 +32,14 @@
<Edge edgeSize="1"/>
</Backdrop>
<Texture name="$(parent)Texture" textureFile="esoui/art/buttons/decline_up.dds">
- <Dimensions y="25" x="25"/>
+ <Dimensions y="20" x="20"/>
<Anchor point="128"/>
</Texture>
</Controls>
</Button>
<Button name="$(parent)FeedbackButton" clickSound="Click">
<Anchor point="TOPRIGHT" relativePoint="TOPLEFT" relativeTo="$(parent)Close" offsetX="-5"/>
- <Dimensions x="40" y="40"/>
+ <Dimensions x="30" y="30"/>
<Controls>
<Backdrop name="$(parent)BG" centerColor="101010" edgeColor="202020">
<AnchorFill/>
@@ -48,103 +49,137 @@
</Button>
<Backdrop name="LeoDolmenRunnerWindowPanel" tier="1" centerColor="000000" edgeColor="202020" hidden="false" clampedToScreen="true" movable="false" mouseEnabled="true">
- <Anchor point="TOPLEFT" relativePoint="TOPLEFT" relativeTo="LeoDolmenRunnerWindow" offsetX="0" offsetY="52"/>
- <Dimensions x="350" y="200"/>
+ <Anchor point="TOPLEFT" relativePoint="TOPLEFT" relativeTo="LeoDolmenRunnerWindow" offsetX="0" offsetY="42"/>
+ <Dimensions x="300" y="200"/>
<Edge edgeSize="1"/>
<Controls>
- <Label name="$(parent)XPLabel" text="XP Rate" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
+ <Label name="$(parent)XPLabel" text="XP Rate" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
<Dimensions x="120" y="35" />
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="10" offsetY="10" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="5" offsetY="10" />
</Label>
- <Label name="$(parent)XP" text="0" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Label name="$(parent)XP" text="0" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
<Dimensions x="100" y="35" />
<Anchor point="TOPLEFT" relativeTo="$(parent)XPLabel" relativePoint="TOPRIGHT" offsetX="10" offsetY="0" />
</Label>
- <Label name="$(parent)XPNextLabel" text="ETA to Level" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
+ <Label name="$(parent)XPNextLabel" text="ETA to Level" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
<Dimensions x="120" y="35" />
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="10" offsetY="40" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="5" offsetY="40" />
</Label>
- <Label name="$(parent)XPNext" text="0" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Label name="$(parent)XPNext" text="0" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
<Dimensions x="100" y="35" />
<Anchor point="TOPLEFT" relativeTo="$(parent)XPNextLabel" relativePoint="TOPRIGHT" offsetX="10" offsetY="0" />
</Label>
- <Label name="$(parent)TrainingGearLabel" text="Training Gear" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
+ <Label name="$(parent)TrainingGearLabel" text="Training Gear" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
<Dimensions x="120" y="35" />
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="10" offsetY="70" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="5" offsetY="70" />
</Label>
- <Label name="$(parent)TrainingGear" text="" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Label name="$(parent)TrainingGear" text="" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
<Dimensions x="100" y="35" />
<Anchor point="TOPLEFT" relativeTo="$(parent)TrainingGearLabel" relativePoint="TOPRIGHT" offsetX="10" offsetY="0" />
</Label>
- <Label name="$(parent)XpFromGearLabel" text="XP from Gear" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
+ <Label name="$(parent)XpFromGearLabel" text="XP from Gear" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
<Dimensions x="120" y="35" />
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="10" offsetY="100" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="5" offsetY="100" />
</Label>
- <Label name="$(parent)XpFromGear" text="0 %" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Label name="$(parent)XpFromGear" text="0 %" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
<Dimensions x="160" y="35" />
<Anchor point="TOPLEFT" relativeTo="$(parent)XpFromGearLabel" relativePoint="TOPRIGHT" offsetX="10" offsetY="0" />
</Label>
- <Label name="$(parent)DolmensClosedLabel" text="Dolmens Closed" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
+ <Label name="$(parent)DolmensClosedLabel" text="Dolmens Closed" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="RIGHT">
<Dimensions x="120" y="35" />
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="10" offsetY="130" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="5" offsetY="130" />
</Label>
- <Label name="$(parent)DolmensClosed" text="0" font="LeoDolmenRunnerLargeFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Label name="$(parent)DolmensClosed" text="0" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
<Dimensions x="100" y="35" />
<Anchor point="TOPLEFT" relativeTo="$(parent)DolmensClosedLabel" relativePoint="TOPRIGHT" offsetX="10" offsetY="0" />
</Label>
<Button name="$(parent)StartStop" clickSound="Click">
<Anchor point="BOTTOMLEFT" relativeTo="$(parent)Panel" relativePoint="BOTTOMLEFT" offsetX="5" offsetY="-5" />
- <Dimensions x="75" y="40"/>
- <OnClicked> LeoDolmenRunner:StartStop() </OnClicked>
+ <Dimensions x="75" y="30"/>
+ <OnClicked> LeoDolmenRunner.runner:StartStop() </OnClicked>
<Controls>
<Backdrop name="$(parent)BG" centerColor="101010" edgeColor="202020">
<AnchorFill/>
<Edge edgeSize="1"/>
</Backdrop>
<Label name="$(parent)Label" text="Start" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="CENTER" >
- <Dimensions x="45" y="30" />
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="5"/>
+ <Dimensions x="45" y="25" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="2"/>
</Label>
</Controls>
</Button>
<Label name="$(parent)Time" text="" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER">
- <Dimensions x="60" y="40" />
+ <Dimensions x="60" y="30" />
<Anchor point="TOPLEFT" relativeTo="$(parent)StartStop" relativePoint="TOPRIGHT" offsetX="10" />
</Label>
<Button name="$(parent)Orientation" clickSound="Click">
<Anchor point="BOTTOMRIGHT" relativeTo="$(parent)Panel" relativePoint="BOTTOMRIGHT" offsetX="-5" offsetY="-5" />
- <Dimensions x="60" y="40"/>
- <OnClicked>
- if LeoDolmenRunner.data.direction == LeoDolmenRunner.directions.cw then
- LeoDolmenRunner.data.direction = LeoDolmenRunner.directions.ccw
- self:GetNamedChild("Label"):SetText("CCW")
- else
- LeoDolmenRunner.data.direction = LeoDolmenRunner.directions.cw
- self:GetNamedChild("Label"):SetText("CW")
- end
- </OnClicked>
+ <Dimensions x="60" y="30"/>
+ <OnClicked> LeoDolmenRunner.runner:CWCCW() </OnClicked>
<Controls>
<Backdrop name="$(parent)BG" centerColor="101010" edgeColor="202020">
<AnchorFill/>
<Edge edgeSize="1"/>
</Backdrop>
<Label name="$(parent)Label" text="CW" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="CENTER">
- <Dimensions x="35" y="30" />
- <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="5"/>
+ <Dimensions x="35" y="25" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="2"/>
</Label>
</Controls>
</Button>
</Controls>
</Backdrop>
+
+ <Backdrop name="LeoDolmenRunnerWindowInviterPanel" tier="1" centerColor="000000" edgeColor="202020" hidden="false" clampedToScreen="true" movable="false" mouseEnabled="true">
+ <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeTo="LeoDolmenRunnerWindowPanel" offsetX="0" offsetY="2"/>
+ <Dimensions x="300" y="70"/>
+ <Edge edgeSize="1"/>
+ <Controls>
+
+ <Label name="$(parent)Title" text="Auto Inviter" color="39B027" font="ZoFontWinH5" wrapMode="ELLIPSIS"
+ verticalAlignment="CENTER" mouseEnabled="false">
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="10" offsetY="10"/>
+ </Label>
+
+ <Button name="$(parent)StartStop" clickSound="Click">
+ <Anchor point="BOTTOMLEFT" relativeTo="$(parent)" relativePoint="BOTTOMLEFT" offsetX="5" offsetY="-5" />
+ <Dimensions x="75" y="30"/>
+ <OnClicked> LeoDolmenRunner.inviter:StartStop() </OnClicked>
+ <Controls>
+ <Backdrop name="$(parent)BG" centerColor="101010" edgeColor="202020">
+ <AnchorFill/>
+ <Edge edgeSize="1"/>
+ </Backdrop>
+ <Label name="$(parent)Label" text="Start" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="CENTER" >
+ <Dimensions x="45" y="25" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="2"/>
+ </Label>
+ </Controls>
+ </Button>
+
+ <Label name="$(parent)MessageLabel" text="Listening to" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Dimensions x="80" y="35" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)StartStop" relativePoint="TOPRIGHT" offsetX="6" offsetY="0" />
+ </Label>
+ <Label name="$(parent)Message" text="x" color="39B027" font="LeoDolmenRunnerNormalFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Dimensions x="20" y="35" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)MessageLabel" relativePoint="TOPRIGHT" offsetX="6" offsetY="0" />
+ </Label>
+ <Label name="$(parent)Hint" text="( /ldr <letter> )" font="LeoDolmenRunnerSmallFont" verticalAlignment="CENTER" horizontalAlignment="LEFT">
+ <Dimensions x="80" y="35" />
+ <Anchor point="TOPLEFT" relativeTo="$(parent)Message" relativePoint="TOPRIGHT" offsetX="6" offsetY="0" />
+ </Label>
+ </Controls>
+ </Backdrop>
</Controls>
</TopLevelControl>
</Controls>
diff --git a/Runner.lua b/Runner.lua
new file mode 100644
index 0000000..2890d7f
--- /dev/null
+++ b/Runner.lua
@@ -0,0 +1,285 @@
+local Runner = {
+ started = false,
+ timeout = 900,
+ activeBuff = 0,
+ data = {
+ direction = 1,
+ startedTime = 0,
+ dolmensClosed = 0,
+ xpPerMinute = 0,
+ minutesToLevel = 0,
+ hoursToLevel = 0,
+ events = {}
+ },
+ defaultData = {
+ direction = 1,
+ startedTime = 0,
+ dolmensClosed = 0,
+ xpPerMinute = 0,
+ minutesToLevel = 0,
+ hoursToLevel = 0,
+ events = {}
+ },
+ directions = {
+ cw = 1,
+ ccw = -1
+ },
+ wayshrines = {
+ -- summerset 353 358 349 357 365
+ [59] = { region="Alik'r Desert", cw = 60, ccw = 155},
+ [60] = { region="Alik'r Desert", cw = 155, ccw = 59},
+ [155] = { region="Alik'r Desert", cw = 59, ccw = 60},
+ [127] = { region="Auridon", cw = 175, ccw = 176},
+ [175] = { region="Auridon", cw = 176, ccw = 127},
+ [176] = { region="Auridon", cw = 127, ccw = 175},
+ [39] = { region="Bangkorai", cw = 206, ccw = 35},
+ [206] = { region="Bangkorai", cw = 35, ccw = 39},
+ [35] = { region="Bangkorai", cw = 39, ccw = 206},
+ [30] = { region="Deshaan", cw = 27, ccw = 80},
+ [27] = { region="Deshaan", cw = 80, ccw = 30},
+ [80] = { region="Deshaan", cw = 30, ccw = 27},
+ [95] = { region="Eastmarch", cw = 92, ccw = 90},
+ [92] = { region="Eastmarch", cw = 90, ccw = 95},
+ [90] = { region="Eastmarch", cw = 95, ccw = 92},
+ [20] = { region="Glenumbra", cw = 6, ccw = 2},
+ [6] = { region="Glenumbra", cw = 2, ccw = 20},
+ [2] = { region="Glenumbra", cw = 20, ccw = 6},
+ [207] = { region="Grahtwood", cw = 164, ccw = 21},
+ [164] = { region="Grahtwood", cw = 21, ccw = 207},
+ [21] = { region="Grahtwood", cw = 207, ccw = 164},
+ [148] = { region="Greenshade", cw = 152, ccw = 149},
+ [152] = { region="Greenshade", cw = 149, ccw = 148},
+ [149] = { region="Greenshade", cw = 148, ccw = 152},
+ [103] = { region="Malabal Tor", cw = 100, ccw = 105},
+ [100] = { region="Malabal Tor", cw = 105, ccw = 103},
+ [105] = { region="Malabal Tor", cw = 103, ccw = 100},
+ [162] = { region="Reaper's March", cw = 158, ccw = 157},
+ [158] = { region="Reaper's March", cw = 157, ccw = 162},
+ [157] = { region="Reaper's March", cw = 162, ccw = 158},
+ [10] = { region="Ravenspire", cw = 13, ccw = 86},
+ [13] = { region="Ravenspire", cw = 86, ccw = 10},
+ [86] = { region="Ravenspire", cw = 10, ccw = 13},
+ [51] = { region="Shadowfen", cw = 53, ccw = 47},
+ [53] = { region="Shadowfen", cw = 47, ccw = 51},
+ [47] = { region="Shadowfen", cw = 51, ccw = 53},
+ [73] = { region="Stonefalls", cw = 67, ccw = 71},
+ [67] = { region="Stonefalls", cw = 71, ccw = 73},
+ [71] = { region="Stonefalls", cw = 73, ccw = 67},
+ [17] = { region="Stormhaven", cw = 19, ccw = 31},
+ [19] = { region="Stormhaven", cw = 31, ccw = 17},
+ [31] = { region="Stormhaven", cw = 17, ccw = 19},
+ [110] = { region="The Rift", cw = 116, ccw = 114},
+ [116] = { region="The Rift", cw = 114, ccw = 110},
+ [114] = { region="The Rift", cw = 110, ccw = 116}
+ }
+}
+
+function Runner:Start()
+ LeoDolmenRunner.log("Starting runner")
+ ZO_ShallowTableCopy(Runner.defaultData, Runner.data)
+ Runner.data.startedTime = GetTimeStamp()
+ Runner.started = true
+ LeoDolmenRunnerWindowPanelStartStopLabel:SetText("Stop")
+ self:GetActiveBuff()
+end
+
+function Runner:Stop()
+ LeoDolmenRunner.log("Stopping runner")
+ Runner.started = false
+ LeoDolmenRunnerWindowPanelStartStopLabel:SetText("Start")
+end
+
+function Runner:StartStop()
+ if Runner.started then
+ Runner:Stop()
+ else
+ Runner:Start()
+ end
+end
+
+function Runner:CW()
+ LeoDolmenRunner.log("Changing direction to clock wise")
+ Runner.data.direction = Runner.directions.cw
+ LeoDolmenRunnerWindowPanelOrientationLabel:SetText("CW")
+end
+
+function Runner:CCW()
+ LeoDolmenRunner.log("Changing direction to counter clock wise")
+ Runner.data.direction = Runner.directions.ccw
+ LeoDolmenRunnerWindowPanelOrientationLabel:SetText("CCW")
+end
+
+function Runner:CWCCW()
+ if Runner.data.direction == Runner.directions.cw then
+ Runner:CCW()
+ else
+ Runner:CW()
+ end
+end
+
+function Runner:OnFastTravelInteraction(currentWayshrine)
+ if not Runner.started or Runner.wayshrines[currentWayshrine] == nil then return end
+
+ local nextWayshrine = nil
+ if Runner.data.direction == Runner.directions.cw then
+ nextWayshrine = Runner.wayshrines[currentWayshrine].cw
+ else
+ nextWayshrine = Runner.wayshrines[currentWayshrine].ccw
+ end
+ local discovered, name = GetFastTravelNodeInfo(nextWayshrine)
+ if not discovered then
+ LDR.log("Can't fast travel: " .. name .. " not discovered yet.")
+ return
+ end
+ FastTravelToNode(nextWayshrine)
+end
+
+local function calculateXpPerMinute(xp, timestamp)
+ local diff = GetDiffBetweenTimeStamps(GetTimeStamp(), timestamp)
+ diff = (diff == 0) and 1 or diff
+
+ return xp / (diff / 60)
+end
+
+local function TimeToLevel()
+ local xpToLevel = 0
+ if (CanUnitGainChampionPoints("player")) then
+ xpToLevel = GetNumChampionXPInChampionPoint(GetPlayerChampionPointsEarned("player")) - GetPlayerChampionXP()
+ else
+ xpToLevel = GetNumExperiencePointsInLevel(GetUnitLevel("player")) - GetUnitXP("player")
+ end
+
+ local minutesToLevel = math.ceil(xpToLevel / Runner.data.xpPerMinute)
+
+ if minutesToLevel < 60 then return 0, minutesToLevel end
+
+ local hoursToLevel = math.floor(minutesToLevel / 60)
+ minutesToLevel = minutesToLevel - (hoursToLevel * 60)
+
+ return hoursToLevel, minutesToLevel
+end
+
+function Runner:UpdateData()
+ local xpGained = 0
+ local firstEventTimestamp = 0
+ if (#Runner.data.events > 0) then
+ for i, event in pairs(Runner.data.events) do
+ if GetDiffBetweenTimeStamps(GetTimeStamp(), event.timestamp) > Runner.timeout then
+ Runner.data.events[i] = nil
+ else
+ xpGained = xpGained + event.xp
+
+ if firstEventTimestamp == 0 or event.timestamp < firstEventTimestamp then
+ firstEventTimestamp = event.timestamp
+ end
+
+ end
+ end
+ end
+
+ Runner.data.xpPerMinute = calculateXpPerMinute(xpGained, firstEventTimestamp)
+
+ if xpGained > 0 then
+ Runner.data.hoursToLevel, Runner.data.minutesToLevel = TimeToLevel()
+ end
+end
+
+function Runner:Update(tick)
+ if not Runner.started or tick ~= 5 then return end
+
+ self:UpdateData()
+end
+
+
+local assistants = {
+ 267, -- Nuzhimeh
+ 300, -- Fence
+ 301, -- Tythis
+ 6376, -- Ezabi
+ 6378 -- Fezez
+}
+
+local buffs = {
+ { id = 479, from = 1022, to = 1103 }, -- Witchmother's Whistle
+ { id = 1167, from = 326, to = 403 }, -- The Pie of Misrule
+ { id = 1168, from = 1217, to = 1231 }, -- Breda's Bottomless Mead Mug
+ { id = 1168, from = 101, to = 105 }, -- Breda's Bottomless Mead Mug
+ { id = 7619 }, -- Jubilee Cake 2020
+ { id = 5886 }, -- Jubilee Cake 2019
+ { id = 4786 }, -- Jubilee Cake 2018
+ { id = 1109 }, -- Jubilee Cake 2017
+ { id = 356 }, -- Jubilee Cake 2016
+}
+
+local function isBuffActive(collectibleId)
+ for i = 1, GetNumBuffs("player") do
+ local buffName = GetUnitBuffInfo("player", i)
+ local referenceId = GetCollectibleReferenceId(collectibleId)
+ local abilityName = GetAbilityName(referenceId)
+ if abilityName == buffName then return true end
+ end
+ return false
+end
+
+function Runner:OnCombatState(inCombat)
+ if IsUnitDead("player") then
+ zo_callLater(function() Runner:OnCombatState(inCombat) end, 500)
+ return
+ end
+
+ if not inCombat and LeoDolmenRunner.settings.runner.reapplyBuff then
+ if Runner.activeBuff == 0 then
+ local date = tonumber(os.date("%m%d", GetTimeStamp()))
+ for _, collectible in ipairs(buffs) do
+ local _, _, _, _, unlocked = GetCollectibleInfo(collectible.id)
+ if unlocked and collectible.from and collectible.from <= date and collectible.to >= date then
+ Runner.activeBuff = collectible.id
+ break
+ end
+ end
+ end
+
+ if not isBuffActive(Runner.activeBuff) then
+ LeoDolmenRunner.log("Reapplying " .. GetCollectibleInfo(Runner.activeBuff))
+ UseCollectible(Runner.activeBuff)
+ end
+ end
+
+ if LeoDolmenRunner.settings.runner.autoDismiss then
+ for _, id in pairs(assistants) do
+ if IsCollectibleActive(id) then
+ LeoDolmenRunner.log("Dismissing " .. GetCollectibleInfo(id))
+ UseCollectible(id)
+ end
+ end
+ end
+end
+
+function Runner:OnExperienceGain(reason, level, previousExperience, currentExperience, championPoints)
+ local event = {
+ timestamp = GetTimeStamp(),
+ xp = currentExperience - previousExperience
+ }
+ table.insert(Runner.data.events, event)
+
+ if reason == 7 then Runner.data.dolmensClosed = Runner.data.dolmensClosed + 1 end
+
+ self:UpdateData()
+end
+
+function Runner:GetActiveBuff()
+ for _, collectible in pairs(buffs) do
+ if isBuffActive(collectible.id) then
+ Runner.activeBuff = collectible.id
+ -- LeoDolmenRunner.debug("Found buff " .. Runner.activeBuff)
+ return
+ end
+ end
+end
+
+function Runner:Initialize()
+ Runner.activeBuff = 0
+ self:GetActiveBuff()
+end
+
+LeoDolmenRunner.runner = Runner
diff --git a/Settings.lua b/Settings.lua
new file mode 100644
index 0000000..2c3eeca
--- /dev/null
+++ b/Settings.lua
@@ -0,0 +1,80 @@
+
+LeoDolmenRunner_Settings = ZO_Object:Subclass()
+local LAM = LibAddonMenu2
+
+function LeoDolmenRunner_Settings:New(...)
+ local object = ZO_Object.New(self)
+ object:Initialize(...)
+ return object
+end
+
+function LeoDolmenRunner_Settings:Initialize()
+end
+
+function LeoDolmenRunner_Settings:CreatePanel()
+ local OptionsName = "LeoDolmenRunnerOptions"
+ local panelData = {
+ type = "panel",
+ name = LeoDolmenRunner.name,
+ slashCommand = "/ldropt",
+ displayName = "|c39B027"..LeoDolmenRunner.displayName.."|r",
+ author = "@LeandroSilva",
+ version = LeoDolmenRunner.version,
+ registerForRefresh = true,
+ registerForDefaults = true,
+ website = "https://www.esoui.com/downloads/info2534-LeosDolmenRunner.html"
+ }
+ LAM:RegisterAddonPanel(OptionsName, panelData)
+
+ local optionsData = {
+ {
+ type = "header",
+ name = "|c3f7fffRunner|r"
+ },{
+ type = "checkbox",
+ name = "Auto dismiss assistants",
+ default = true,
+ width = "full",
+ getFunc = function() return LeoDolmenRunner.settings.runner.autoDismiss end,
+ setFunc = function(value) LeoDolmenRunner.settings.runner.autoDismiss = value end,
+ },{
+ type = "checkbox",
+ name = "Auto reapply holiday buff",
+ default = true,
+ width = "full",
+ getFunc = function() return LeoDolmenRunner.settings.runner.reapplyBuff end,
+ setFunc = function(value) LeoDolmenRunner.settings.runner.reapplyBuff = value end,
+ },{
+ type = "header",
+ name = "|c3f7fffAuto Inviter|r"
+ },{
+ type = "checkbox",
+ name = "Auto kick offline",
+ default = true,
+ width = "full",
+ getFunc = function() return LeoDolmenRunner.settings.inviter.autoKick end,
+ setFunc = function(value) LeoDolmenRunner.settings.inviter.autoKick = value end,
+ },{
+ name = "Auto kick delay (secs)",
+ type = "slider",
+ getFunc = function() return LeoDolmenRunner.settings.inviter.kickDelay end,
+ setFunc = function(value) LeoDolmenRunner.settings.inviter.kickDelay = value end,
+ min = 10,
+ max = 600,
+ default = 60,
+ },{
+ name = "Max group size",
+ type = "dropdown",
+ choices = {4, 12, 24},
+ getFunc = function() return LeoDolmenRunner.settings.inviter.maxSize end,
+ setFunc = function(value) LeoDolmenRunner.settings.inviter.maxSize = value end,
+ default = 24,
+ }
+ }
+ LAM:RegisterOptionControls(OptionsName, optionsData)
+end
+
+function LeoDolmenRunner_Settings_OnMouseEnter(control, tooltip)
+ InitializeTooltip(InformationTooltip, control, BOTTOMLEFT, 0, -2, TOPLEFT)
+ SetTooltipText(InformationTooltip, tooltip)
+end
diff --git a/Ui.lua b/Ui.lua
new file mode 100644
index 0000000..92e4e26
--- /dev/null
+++ b/Ui.lua
@@ -0,0 +1,165 @@
+local Ui = {}
+local LDR = LeoDolmenRunner
+
+local function getColor(value, max)
+ local rate = value / max
+ if rate > 0.8 then return '10FF10' end
+ if rate > 0.5 then return 'FFFF00' end
+ return 'FF1010'
+end
+
+function Ui:UpdateTrainingGear()
+ local stack = 0
+ local stackRepair = 0
+ local maxItems = 9
+ local numItems = 0
+ local bag = SHARED_INVENTORY:GenerateFullSlotData(nil,BAG_WORN)
+ for _, data in pairs(bag) do
+ local link = GetItemLink(data.bagId, data.slotIndex)
+ if GetItemLinkTraitType(link) == ITEM_TRAIT_TYPE_ARMOR_TRAINING or GetItemLinkTraitType(link) == ITEM_TRAIT_TYPE_WEAPON_TRAINING then
+ local activeWeaponPair = GetActiveWeaponPairInfo()
+ if ( GetItemLinkItemType(link) == ITEMTYPE_ARMOR
+ or
+ (activeWeaponPair == ACTIVE_WEAPON_PAIR_MAIN and (data.slotIndex == EQUIP_SLOT_MAIN_HAND or data.slotIndex == EQUIP_SLOT_OFF_HAND))
+ or
+ (activeWeaponPair == ACTIVE_WEAPON_PAIR_BACKUP and (data.slotIndex == EQUIP_SLOT_BACKUP_MAIN or data.slotIndex == EQUIP_SLOT_BACKUP_OFF)))
+ then
+ local quality = select(8, GetItemInfo(data.bagId, data.slotIndex))
+ local condition = GetItemCondition(data.bagId, data.slotIndex)
+ local equipType = select(6, GetItemInfo(data.bagId, data.slotIndex))
+ numItems = numItems + 1
+ local xp = 0
+ if GetItemLinkItemType(link) == ITEMTYPE_ARMOR then
+ xp = 6 + quality
+ elseif equipType == EQUIP_TYPE_TWO_HAND then
+ numItems = numItems + 1
+ xp = 4 + quality
+ else
+ xp = 2 + (0.5 * quality)
+ end
+ stack = stack + xp
+ if condition == 0 then stackRepair = stackRepair + xp end
+ end
+ end
+ end
+
+ local text = ""
+
+ if stackRepair > 0 then
+ text = "|c" .. getColor(stack - stackRepair, stack) .. " " .. (stack - stackRepair) .. " / " .. stack .. "%|r (Repair)"
+ else
+ text = "|c" .. getColor(stack, stack) .. stack .. "%|r"
+ end
+
+ LeoDolmenRunnerWindowPanelXpFromGear:SetText(text)
+ LeoDolmenRunnerWindowPanelTrainingGear:SetText("|c" .. getColor(numItems, maxItems) .. numItems .. " / " .. maxItems .. "|r")
+end
+
+function Ui:CreateUI()
+ LeoDolmenRunnerWindowPanelStartStopLabel:SetText(LDR.runner.started and "Stop" or "Start")
+ LeoDolmenRunnerWindowPanelOrientationLabel:SetText(LDR.runner.data.direction == LDR.runner.directions.cw and "CW" or "CCW")
+
+ LeoDolmenRunnerWindowInviterPanel:SetHidden(not IsUnitSoloOrGroupLeader("player"))
+ LeoDolmenRunnerWindowInviterPanelMessage:SetText("x")
+
+ self:UpdateTrainingGear()
+end
+
+local function FormatNumber(num)
+ return zo_strformat("<<1>>", ZO_LocalizeDecimalNumber(num))
+end
+
+local function FormatTime(seconds, short, colorizeCountdown)
+ if short == nil then short = false end
+ local formats = {
+ dhm = SI_TIME_FORMAT_DDHHMM_DESC_SHORT,
+ day = SI_TIME_FORMAT_DAYS,
+ hm = SI_TIME_FORMAT_HHMM_DESC_SHORT,
+ hms = SI_TIME_FORMAT_HHMMSS_DESC_SHORT,
+ hour = SI_TIME_FORMAT_HOURS,
+ ms = SI_TIME_FORMAT_MMSS_DESC_SHORT,
+ m = SI_TIME_FORMAT_MINUTES
+ }
+ if seconds and seconds > 0 then
+ local ss = seconds % 60
+ local mm = math.floor(seconds / 60)
+ local hh = math.floor(mm / 60)
+ mm = mm % 60
+ local dn = math.floor(hh / 24)
+ local hhdn = hh - (dn*24)
+
+ local ssF = string.format("%02d", ss)
+ local mmF = string.format("%02d", mm)
+ local hhF = string.format("%02d", hh)
+ local hhdnF = string.format("%02d", hhdn)
+
+ local result = ''
+ if dn > 0 then
+ if short then
+ result = ZO_CachedStrFormat(GetString(formats.day), dn) .." "..ZO_CachedStrFormat(GetString(formats.hour), hhdnF)
+ else
+ result = ZO_CachedStrFormat(GetString(formats.dhm), dn, hhdnF, mmF)
+ end
+ elseif hh > 0 then
+ if short then
+ result = ZO_CachedStrFormat(GetString(formats.hm), hhF, mmF)
+ else
+ result = ZO_CachedStrFormat(GetString(formats.hms), hhF, mmF, ssF)
+ end
+ elseif mm >= 0 then result = ZO_CachedStrFormat(GetString(formats.ms), mmF, ssF)
+ end
+ return result
+ else return ZO_CachedStrFormat(GetString(formats.m), 0) end
+end
+
+function Ui:Update(tick)
+
+ if not LDR.runner.started then return end
+
+ if tick == 5 then
+ self:UpdateTrainingGear()
+ end
+
+ if LDR.runner.started and LeoDolmenRunner.runner.data.startedTime > 0 then
+ local secs = GetTimeStamp() - LeoDolmenRunner.runner.data.startedTime
+ if secs > 0 then
+ LeoDolmenRunnerWindowPanelTime:SetText(FormatTime(secs))
+ end
+ end
+
+ LeoDolmenRunnerWindowPanelDolmensClosed:SetText(LDR.runner.data.dolmensClosed)
+ LeoDolmenRunnerWindowPanelXP:SetText(FormatNumber(math.ceil(LDR.runner.data.xpPerMinute)) .. " / min")
+
+ LeoDolmenRunnerWindowPanelXPNext:SetText(ZO_CachedStrFormat(GetString(SI_TIME_FORMAT_HHMM_DESC_SHORT), LDR.runner.data.hoursToLevel, LDR.runner.data.minutesToLevel))
+
+ LeoDolmenRunnerWindowInviterPanelMessage:SetText("x")
+end
+
+function Ui:OnWindowMoveStop()
+ LDR.settings.position = {
+ left = self.frame:GetLeft(),
+ top = self.frame:GetTop()
+ }
+end
+
+function Ui:RestorePosition()
+ local position = LDR.settings.position or { left = 200; top = 200; }
+ local left = position.left
+ local top = position.top
+
+ self.frame:ClearAnchors()
+ self.frame:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, left, top)
+ self.frame:SetDrawTier(DT_MEDIUM)
+end
+
+function Ui:Initialize()
+ self.frame = LeoDolmenRunnerWindow
+
+ self:RestorePosition()
+
+ self:CreateUI()
+
+ self.frame:SetHidden(LeoDolmenRunner.settings.hidden)
+end
+
+LeoDolmenRunner.ui = Ui