-- Kill tracker integration — methods added to TaskBoardController -- This wraps the Tracker.Prey / Tracker.Bounty / Tracker.Weekly systems -- (provided by the Prey tracker widget from another module). -- LuaFormatter off Tracker = Tracker or {} Tracker.Prey = Tracker.Prey or {} Tracker.Bounty = Tracker.Bounty or {} Tracker.Weekly = Tracker.Weekly or {} Tracker.Weekly.slots = Tracker.Weekly.slots or {} local preyTracker = nil local taskboardTrackerButton = nil local restoreEvent = nil local trackerStyleLoaded = false local PREY_BONUS_DAMAGE_BOOST = 0 local PREY_BONUS_DAMAGE_REDUCTION= 1 local PREY_BONUS_XP_BONUS = 2 local PREY_BONUS_IMPROVED_LOOT = 3 local SLOT_STATE_LOCKED = 0 local SLOT_STATE_ACTIVE = 2 -- LuaFormatter on -- Initialization function TaskBoardController:initTracker() if preyTracker then local hasTaskBoardSlots = preyTracker:recursiveGetChildById('bslot1') and preyTracker:recursiveGetChildById('weeklyTasksLabel') if hasTaskBoardSlots then return end preyTracker:destroy() preyTracker = nil end if not trackerStyleLoaded then g_ui.importStyle('/modules/game_taskboard/trackers/styles/kill_tracker.otui') trackerStyleLoaded = true end preyTracker = g_ui.createWidget('TaskBoardKillTracker') preyTracker:setup() -- Hide buttons not needed for this tracker variant for _, id in ipairs({'contextMenuButton', 'newWindowButton', 'toggleFilterButton'}) do local w = preyTracker:recursiveGetChildById(id) if w then w:setVisible(false) end end -- Reposition lock button next to minimize button local lockBtn = preyTracker:recursiveGetChildById('lockButton') local minBtn = preyTracker:recursiveGetChildById('minimizeButton') if lockBtn and minBtn then lockBtn:setVisible(true) lockBtn:breakAnchors() lockBtn:addAnchor(AnchorTop, minBtn:getId(), AnchorTop) lockBtn:addAnchor(AnchorRight, minBtn:getId(), AnchorLeft) lockBtn:setMarginRight(2) lockBtn:setMarginTop(0) end preyTracker:setContentMaximumHeight(500) preyTracker:setContentMinimumHeight(47) preyTracker:close(true) Tracker.Bounty.setInactive() Tracker.Weekly.clearAll() preyTracker.onOpen = function() if taskboardTrackerButton then taskboardTrackerButton:setOn(true) end end preyTracker.onClose = function() if taskboardTrackerButton then taskboardTrackerButton:setOn(false) end end Keybind.new("Windows", "Show/Hide kill tracker", "", "") Keybind.bind("Windows", "Show/Hide kill tracker", {{ type = KEY_DOWN, callback = function() self:toggleTracker() end }}) self:checkTracker() end function TaskBoardController:terminateTracker() if restoreEvent then removeEvent(restoreEvent) restoreEvent = nil end Keybind.delete("Windows", "Show/Hide kill tracker") if taskboardTrackerButton then taskboardTrackerButton:destroy() taskboardTrackerButton = nil end if preyTracker then preyTracker:destroy() preyTracker = nil end end function TaskBoardController:checkTracker() if not g_game.getFeature(GamePrey) then return end if not taskboardTrackerButton then taskboardTrackerButton = modules.game_mainpanel.addToggleButton('taskboardTrackerButton', tr('Kill Tracker'), '/images/options/button_prey', function() self:toggleTracker() end, false, 9) end if restoreEvent then removeEvent(restoreEvent) restoreEvent = nil end restoreEvent = scheduleEvent(function() restoreEvent = nil if preyTracker and preyTracker.restorePosition then preyTracker:restorePosition() end end, 150) end function TaskBoardController:toggleTracker() if not preyTracker then return end if preyTracker:isVisible() then preyTracker:close() else if not preyTracker:getParent() then local panel = modules.game_interface.findContentPanelAvailable(preyTracker, preyTracker:getMinimumHeight()) if not panel then return end preyTracker:setParent(panel) end preyTracker:open() end end -- Prey slot widget helper function Tracker.Prey.getSmallIconPath(bonusType) local path = "/images/game/prey/" if bonusType == PREY_BONUS_DAMAGE_BOOST then return path .. "prey_damage" elseif bonusType == PREY_BONUS_DAMAGE_REDUCTION then return path .. "prey_defense" elseif bonusType == PREY_BONUS_XP_BONUS then return path .. "prey_xp" elseif bonusType == PREY_BONUS_IMPROVED_LOOT then return path .. "prey_loot" end return path .. "prey_no_bonus" end function Tracker.Prey.getExtendIcon(lockType) local path = "/images/game/prey/" local player = g_game.getLocalPlayer() if not player then return path .. "prey-auto-extend-disabled" end local balance = player:getResourceBalance(ResourceTypes.PREY_WILDCARDS) if lockType == 1 then return balance < 1 and (path .. "prey-auto-reroll-enabled-failing") or (path .. "prey-auto-reroll-enabled") elseif lockType == 2 then return balance < 5 and (path .. "prey-lock-prey-enabled-failing") or (path .. "prey-lock-prey-enabled") end return path .. "prey-auto-extend-disabled" end function Tracker.Prey.timeleftTranslation(timeleft) if timeleft == 0 then return "Free" end local hours = string.format('%02.f', math.floor(timeleft / 3600)) local mins = string.format('%02.f', math.floor(timeleft / 60 - (hours * 60))) return hours .. ':' .. mins end function Tracker.Prey.updateWidget(slot, state, currentHolderOutfit, preySlot, showCallback) if not preyTracker then return end local trackerSlot = preyTracker.contentsPanel["slot" .. (slot + 1)] if not trackerSlot then return end if state == SLOT_STATE_LOCKED then trackerSlot:setVisible(false) return end if slot == 2 then trackerSlot:setVisible(true) preyTracker:setContentMaximumHeight(195) end if state == SLOT_STATE_ACTIVE then local creatureAndBonus = preySlot.active.creatureAndBonus local duration = creatureAndBonus.timeLeft:getText() trackerSlot.creature:setOutfit(currentHolderOutfit) trackerSlot.creatureName:setText(short_text(preySlot.title:getText(), 12)) trackerSlot.time:setPercent(creatureAndBonus.timeLeft:getPercent()) trackerSlot.preyType:setImageSource(Tracker.Prey.getSmallIconPath(preySlot.bonusType)) trackerSlot.preyAutoExtend:setImageSource(Tracker.Prey.getExtendIcon(preySlot.lockType)) trackerSlot.creature:show() trackerSlot.noCreature:hide() trackerSlot.onClick = function() showCallback() end trackerSlot:setTooltip(tr("Creature: %s\nDuration: %s\n\nClick in this window to open the prey dialog.", preySlot.title:getText(), duration)) else trackerSlot.creature:hide() trackerSlot.noCreature:show() trackerSlot.creatureName:setText("Inactive") trackerSlot.time:setPercent(0) trackerSlot.preyAutoExtend:setImageSource(Tracker.Prey.getExtendIcon(preySlot.lockType)) trackerSlot.preyType:setImageSource(Tracker.Prey.getSmallIconPath(preySlot.bonusType)) trackerSlot.onClick = function() showCallback() end end end function Tracker.Prey.updateTimeLeft(slot, timeLeft) if not preyTracker then return end local trackerSlot = preyTracker.contentsPanel["slot" .. (slot + 1)] if not trackerSlot then return end local tooltip = trackerSlot:getTooltip() or "Duration: \n" local updated = string.gsub(tooltip, "[^\n]*Duration: [^\n]*\n?", "Duration: " .. Tracker.Prey.timeleftTranslation(timeLeft) .. "\n") trackerSlot:setTooltip(updated) local percent = (timeLeft / (2 * 60 * 60)) * 100 trackerSlot.time:setPercent(percent) end function Tracker.Prey.getWidget() return preyTracker end function Tracker.Prey.getButton() return taskboardTrackerButton end -- Bounty tracker slot local function getBountySlot() return preyTracker and preyTracker.contentsPanel and preyTracker.contentsPanel["bslot1"] end function Tracker.Bounty.setInactive() local slot = getBountySlot() if not slot then return end slot.creature:hide() slot.noCreature:show() slot.creatureName:setText("Inactive") slot.time:setPercent(0) slot.time:setBackgroundColor("#555555") slot:setTooltip("No active Bounty Task.\n\nClick to open the Bounty Task panel.") slot.onClick = function() TaskBoardController:toggle() end end function Tracker.Bounty.setActive(name, outfit, killCount, killTarget) local slot = getBountySlot() if not slot then return end slot.creature:setOutfit(outfit) slot.creature:show() slot.noCreature:hide() slot.creatureName:setText(short_text(name, 12)) local percent = killTarget > 0 and (killCount / killTarget) * 100 or 0 slot.time:setPercent(percent) slot.time:setBackgroundColor("#C28400") slot:setTooltip(tr("Bounty Task: %s\nProgress: %d/%d kills\n\nClick to open the Bounty Task panel.", name, killCount, killTarget)) slot.onClick = function() TaskBoardController:toggle() end end function Tracker.Bounty.setCompleted(name, outfit, killCount, killTarget) local slot = getBountySlot() if not slot then return end slot.creature:setOutfit(outfit) slot.creature:show() slot.noCreature:hide() slot.creatureName:setText(short_text(name, 12)) slot.time:setPercent(100) slot.time:setBackgroundColor("#00AA00") slot:setTooltip(tr("Bounty Task: %s\nProgress: %d/%d kills\nCompleted! Click to claim your reward.", name, killCount, killTarget)) slot.onClick = function() TaskBoardController:toggle() end end function Tracker.Bounty.onKillUpdate(raceId, currentKills, totalKills, isCompleted) local raceData = g_things.getRaceData(raceId) local name = raceData and raceData.name or 'Unknown' name = name:capitalize() local outfit = raceData and raceData.outfit or {} if isCompleted == 1 then Tracker.Bounty.setCompleted(name, outfit, currentKills, totalKills) else Tracker.Bounty.setActive(name, outfit, currentKills, totalKills) end end -- Weekly tracker slots local function getWeeklySlot(index) return preyTracker and preyTracker.contentsPanel and preyTracker.contentsPanel["wslot" .. index] end function Tracker.Weekly.setSectionVisible(visible) if not preyTracker then return end local label = preyTracker.contentsPanel:recursiveGetChildById('weeklyTasksLabel') if label then label:setVisible(visible) end local sep = preyTracker.contentsPanel:recursiveGetChildById('weeklyTasksSeparator') if sep then sep:setVisible(visible) end end function Tracker.Weekly.setSlotInactive(index) local slot = getWeeklySlot(index) if not slot then return end slot.creature:hide() slot.anyCreatureIcon:hide() slot.noCreature:show() slot.creatureName:setText("Inactive") slot.time:setPercent(0) slot.time:setBackgroundColor("#555555") slot:setVisible(false) end function Tracker.Weekly.setSlotActive(index, raceId, name, outfit, currentKills, totalKills) local slot = getWeeklySlot(index) if not slot then return end if raceId == 0 then slot.creature:hide() slot.anyCreatureIcon:show() slot.noCreature:hide() slot.creatureName:setText("Any Creature") else slot.creature:setOutfit(outfit) slot.creature:show() slot.anyCreatureIcon:hide() slot.noCreature:hide() slot.creatureName:setText(short_text(name, 12)) end local percent = totalKills > 0 and (currentKills / totalKills) * 100 or 0 slot.time:setPercent(percent) slot.time:setBackgroundColor("#C28400") slot:setTooltip(tr("Weekly Task: %s\nProgress: %d/%d kills", name, currentKills, totalKills)) slot:setVisible(true) slot.onClick = function() TaskBoardController:show() TaskBoardController:selectTab(TaskBoardController.TAB_WEEKLY) end end function Tracker.Weekly.setSlotCompleted(index, raceId, name, outfit, currentKills, totalKills) local slot = getWeeklySlot(index) if not slot then return end if raceId == 0 then slot.creature:hide() slot.anyCreatureIcon:show() slot.noCreature:hide() slot.creatureName:setText("Any Creature") else slot.creature:setOutfit(outfit) slot.creature:show() slot.anyCreatureIcon:hide() slot.noCreature:hide() slot.creatureName:setText(short_text(name, 12)) end slot.time:setPercent(100) slot.time:setBackgroundColor("#00AA00") slot:setTooltip(tr("Weekly Task: %s\nProgress: %d/%d kills\nCompleted!", name, currentKills, totalKills)) slot:setVisible(true) slot.onClick = function() TaskBoardController:show() TaskBoardController:selectTab(TaskBoardController.TAB_WEEKLY) end end function Tracker.Weekly.clearAll() Tracker.Weekly.slots = {} for i = 1, MAX_WEEKLY_TRACKER_SLOTS do Tracker.Weekly.setSlotInactive(i) end Tracker.Weekly.setSectionVisible(false) end function Tracker.Weekly.loadFromServerData(monsters) Tracker.Weekly.slots = {} if not monsters or #monsters == 0 then Tracker.Weekly.clearAll() return end Tracker.Weekly.setSectionVisible(true) for i, m in ipairs(monsters) do if i > MAX_WEEKLY_TRACKER_SLOTS then break end local raceId = tonumber(m.raceId) or 0 local current = tonumber(m.current) or 0 local total = tonumber(m.total) or 0 local finished = (tonumber(m.state) or 0) == 1 local name = "Any Creature" local outfit = {} if raceId > 0 then local rd = g_things.getRaceData(raceId) name = rd and rd.name or 'Unknown' name = name:capitalize() outfit = rd and rd.outfit or {} end Tracker.Weekly.slots[i] = { raceId = raceId, name = name, outfit = outfit } if finished then Tracker.Weekly.setSlotCompleted(i, raceId, name, outfit, current, total) else Tracker.Weekly.setSlotActive(i, raceId, name, outfit, current, total) end end for i = #monsters + 1, MAX_WEEKLY_TRACKER_SLOTS do local slot = getWeeklySlot(i) if slot then slot:setVisible(false) end end end function Tracker.Weekly.onKillUpdate(raceId, currentKills, totalKills, isCompleted) for i, data in pairs(Tracker.Weekly.slots) do if data.raceId == raceId then if isCompleted == 1 then Tracker.Weekly.setSlotCompleted(i, raceId, data.name, data.outfit, currentKills, totalKills) else Tracker.Weekly.setSlotActive(i, raceId, data.name, data.outfit, currentKills, totalKills) end return end end end