Skip to content

Commit

Permalink
seperate leaderboard from tracker so we can have one per class, same …
Browse files Browse the repository at this point in the history
…for scanner.

add class filter to GUI.
  • Loading branch information
rubensayshi committed Jul 31, 2019
1 parent ab7ea46 commit 840d8b4
Show file tree
Hide file tree
Showing 21 changed files with 915 additions and 406 deletions.
2 changes: 2 additions & 0 deletions Makefile
Expand Up @@ -3,6 +3,7 @@
TESTS ?= .*
INCLUDES ?= .*
EXCLUDES ?= libcompressmock.lua
TESTOPTS ?=

# if UPLOADRELEASE is set to anything (y, n, maybe, w/e) then we do -d during the `release` step
# which will attempt to upload the release
Expand Down Expand Up @@ -48,6 +49,7 @@ lint:
tests: libs
busted --coverage \
-m './src/?.lua;./src/?/?.lua;./src/?/init.lua;./libs/?.lua;./libs/?/?.lua;./tests/?.lua;./tests/?/?.lua' \
$(TESTOPTS) \
--pattern='$(INCLUDES)' \
--exclude-pattern='$(EXCLUDES)' \
tests/ tests/util/ --filter='$(TESTS)'
Expand Down
2 changes: 2 additions & 0 deletions TheClassicRace.toc
Expand Up @@ -31,4 +31,6 @@ src\core\scan.lua
src\core\chat-notifier.lua
src\core\sync.lua
src\core\serializer.lua
src\core\leaderboard.lua
src\core\updater.lua
src\gui\status-frame.lua
19 changes: 18 additions & 1 deletion src/config.lua
Expand Up @@ -16,14 +16,16 @@ local TheClassicRaceColors = {
WARLOCK = "|cFF9482C9",
MONK = "|cFF00FF96",
DRUID = "|cFFFF7D0A",
DEMONHUNTER = "POO",
DEMONHUNTER = "|cFFEDA55F",
}
TheClassicRace.Colors = TheClassicRaceColors

---@class TheClassicRaceConfig
local TheClassicRaceConfig = {
Version = "@project-version@",
Debug = false,
Trace = false,
LibWhoDebug = false,
--@debug@
Debug = true,
Trace = true,
Expand Down Expand Up @@ -72,6 +74,21 @@ local TheClassicRaceConfig = {
DEMONHUNTER = 12,
},

PrettyClassNames = {
WARRIOR = "Warrior",
PALADIN = "Paladin",
HUNTER = "Hunter",
ROGUE = "Rogue",
PRIEST = "Priest",
DEATHKNIGHT = "DK",
SHAMAN = "Shaman",
MAGE = "Mage",
WARLOCK = "Warlock",
MONK = "Monk",
DRUID = "Druid",
DEMONHUNTER = "Poo",
},

Network = {
Prefix = "TCRace",
Channel = {
Expand Down
112 changes: 79 additions & 33 deletions src/core/chat-notifier.lua
Expand Up @@ -33,63 +33,109 @@ function TheClassicRaceChatNotifier.new(Config, Core, DB, EventBus)
return self
end

function TheClassicRaceChatNotifier:OnDing(playerInfo, rank)
if not self.DB.profile.options.notifications then
function TheClassicRaceChatNotifier:OnDing(playerInfo, globalRank, classRank)
-- check if we should report on this ding
if not self:ShouldReport(playerInfo, globalRank, classRank) then
return
end

-- for any old dings except the rank 1 we ignore
if rank > 1 and playerInfo.dingedAt < self.Core:Now() - 600 then
return
if playerInfo.name == self.Core:Me() then
self:OnSelfDing(playerInfo, globalRank, classRank)
else
self:OnStrangerDing(playerInfo, globalRank, classRank)
end
end

-- no notifications for ranks below threshold
if rank > self.DB.profile.options.notificationThreshold then
return

function TheClassicRaceChatNotifier:ShouldReport(playerInfo, globalRank, classRank)
-- any old dings except rank 1 we ignore
if playerInfo.dingedAt < self.Core:Now() - 600
and (globalRank == nil or globalRank > 1)
and (classRank == nil or classRank > 1) then
return false
end

if playerInfo.name == self.Core:Me() then
self:OnSelfDing(playerInfo, rank)
if playerInfo.classIndex ~= self.Core:MyClass() then
if not self.DB.profile.options.globalNotifications then
return false
end

-- no notifications for globalRanks below threshold of other classes
if globalRank == nil or globalRank > self.DB.profile.options.globalNotificationThreshold then
return false
end
else
self:OnStrangerDing(playerInfo, rank)
if not self.DB.profile.options.classNotifications then
return false
end

-- no notifications for class ranks below threshold
if classRank == nil or classRank > self.DB.profile.options.classNotificationThreshold then
return false
end
end

return true
end

function TheClassicRaceChatNotifier:OnSelfDing(playerInfo, globalRank, classRank)
self:DingNotification(playerInfo, globalRank, classRank, true)
end

function TheClassicRaceChatNotifier:OnStrangerDing(playerInfo, globalRank, classRank)
self:DingNotification(playerInfo, globalRank, classRank, false)
end

function TheClassicRaceChatNotifier:OnSelfDing(playerInfo, rank)
local chatLink = TheClassicRace:PlayerChatLink(playerInfo.name, "You", self.Core:ClassByIndex(playerInfo.classIndex))
function TheClassicRaceChatNotifier:DingNotification(playerInfo, globalRank, classRank, isSelf)
local className = self.Core:ClassByIndex(playerInfo.classIndex)
local prettyClassName = self.Config.PrettyClassNames[className]
local chatLink
local addressPerson
if isSelf then
chatLink = TheClassicRace:PlayerChatLink(playerInfo.name, "You", className)
addressPerson = chatLink .. " are"
else
chatLink = TheClassicRace:PlayerChatLink(playerInfo.name, nil, className)
addressPerson = chatLink .. " is"
end

if rank == 1 then
if globalRank == 1 then
if playerInfo.level == self.Config.MaxLevel then
TheClassicRace:PPrint("Gratz! The race is over! " .. chatLink .. " are the first to reach max level!!")
TheClassicRace:PPrint("Gratz! The race is over! " .. addressPerson .. " the first to reach max level!!")
else
TheClassicRace:PPrint("Gratz! " .. chatLink .. " are first to reach level " .. playerInfo.level .. "!")
TheClassicRace:PPrint("Gratz! " .. addressPerson .. " first to reach level " .. playerInfo.level .. "!")
end
else
elseif classRank == 1 and globalRank ~= nil then
if playerInfo.level == self.Config.MaxLevel then
TheClassicRace:PPrint("Gratz! " .. chatLink .. " reached max level as #" .. rank .. "!")
TheClassicRace:PPrint("Gratz! The race is over! " .. addressPerson .. " the first to reach max level of all " ..
prettyClassName .. ", and #" .. globalRank .. " for all classes!!")
else
TheClassicRace:PPrint("Gratz! " .. chatLink .. " reached level " .. playerInfo.level .. "! " ..
"Currently rank #" .. rank .. " in the race!")
TheClassicRace:PPrint("Gratz! " .. addressPerson .. " first to reach level " .. playerInfo.level .. " of all " ..
prettyClassName .. ", and #" .. globalRank .. " for all classes!")
end
end
end

function TheClassicRaceChatNotifier:OnStrangerDing(playerInfo, rank)
local chatLink = TheClassicRace:PlayerChatLink(playerInfo.name, nil, self.Core:ClassByIndex(playerInfo.classIndex))

if rank == 1 then
elseif classRank == 1 then
if playerInfo.level == self.Config.MaxLevel then
TheClassicRace:PPrint("The race is over! Gratz to " .. chatLink .. ", first to reach max level!!")
TheClassicRace:PPrint("Gratz! The race is over! " .. addressPerson .. " the first to reach max level of all " ..
prettyClassName .. "!! (not in top " .. self.Config.MaxLeaderboardSize .. " for all classes)")
else
TheClassicRace:PPrint("Gratz to " .. chatLink .. ", " ..
"first to reach level " .. playerInfo.level .. "!")
TheClassicRace:PPrint("Gratz! " .. addressPerson .. " first to reach level " .. playerInfo.level .. " of all " ..
prettyClassName .. "!! (not in top " .. self.Config.MaxLeaderboardSize .. " for all classes)")
end
elseif globalRank ~= nil then
if playerInfo.level == self.Config.MaxLevel then
TheClassicRace:PPrint("Gratz! " .. chatLink .. " reached max level as #" .. classRank .. " of all " .. prettyClassName .. ", " ..
"and #" .. globalRank .. " for all classes!!")
else
TheClassicRace:PPrint("Gratz! " .. chatLink .. " reached level " .. playerInfo.level .. "! " ..
"Currently rank #" .. classRank .. " of all " .. prettyClassName .. " and #" .. globalRank .. " for all classes in the race!")
end
else
if playerInfo.level == self.Config.MaxLevel then
TheClassicRace:PPrint("Gratz to " .. chatLink .. ", reached max level as #" .. rank .. "!")
TheClassicRace:PPrint("Gratz! " .. chatLink .. " reached max level as #" .. classRank .. " of all " .. prettyClassName .. "!" ..
" (not in top " .. self.Config.MaxLeaderboardSize .. " for all classes)")
else
TheClassicRace:PPrint("Gratz to " .. chatLink .. ", reached level " .. playerInfo.level .. "! " ..
"Currently rank #" .. rank .. " in the race!")
TheClassicRace:PPrint("Gratz! " .. chatLink .. " reached level " .. playerInfo.level .. " as #" .. classRank
.. " of all " .. prettyClassName .. "! (not in top " .. self.Config.MaxLeaderboardSize .. " for all classes)")
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/core/core.lua
Expand Up @@ -71,7 +71,7 @@ end

function TheClassicRaceCore:MyClass()
local _, className, _ = UnitClass("player")
return className
return self:ClassIndex(className), className
end

function TheClassicRaceCore:ClassIndex(className)
Expand Down
103 changes: 103 additions & 0 deletions src/core/leaderboard.lua
@@ -0,0 +1,103 @@
-- Addon global
local TheClassicRace = _G.TheClassicRace

--[[
Leaderboard is responsible for maintaining our leaderboard data based on data provided by other parts of the system
to us through the EventBus.
]]--
---@class TheClassicRaceLeaderboard
---@field Config TheClassicRaceConfig
---@field leaderboard table<string, table>
local TheClassicRaceLeaderboard = {}
TheClassicRaceLeaderboard.__index = TheClassicRaceLeaderboard
TheClassicRace.Leaderboard = TheClassicRaceLeaderboard
setmetatable(TheClassicRaceLeaderboard, {
__call = function(cls, ...)
return cls.new(...)
end,
})

function TheClassicRaceLeaderboard.new(Config, leaderboardDB)
local self = setmetatable({}, TheClassicRaceLeaderboard)

self.Config = Config
self.lbdb = leaderboardDB

return self
end

--[[
ProcessPlayerInfo updates the leaderboard and triggers notifications accordingly
]]--
function TheClassicRaceLeaderboard:ProcessPlayerInfo(playerInfo)
TheClassicRace:DebugPrint("[LB] ProcessPlayerInfo: " .. playerInfo.name .. " lvl" .. playerInfo.level)

-- ignore players below our lower bound threshold
if playerInfo.level < self.lbdb.minLevel then
TheClassicRace:DebugPrint("Ignored player info < lvl" .. self.lbdb.minLevel)
return
end

-- determine where to insert the player and his previous rank
-- doing this O(n) isn't very efficient, but considering the small size of the leaderboard this is more than fine
local insertAtRank = nil
local previousRank = nil
for rank, player in ipairs(self.lbdb.players) do
-- find the place where to insert the new player
if insertAtRank == nil and playerInfo.level > player.level then
insertAtRank = rank
end

-- find a possibly previous entry of this player
if previousRank == nil and playerInfo.name == player.name then
previousRank = rank
end
end

local isNew = previousRank == nil
local isDing = not isNew and playerInfo.level > self.lbdb.players[previousRank].level

-- no change
if not isNew and not isDing then
return
end

-- grow the leaderboard up until the max size
if insertAtRank == nil and #self.lbdb.players < self.Config.MaxLeaderboardSize then
insertAtRank = #self.lbdb.players + 1
end

-- not high enough for leaderboard
if insertAtRank == nil then
return
end

-- remove from previous rank
if previousRank ~= nil then
table.remove(self.lbdb.players, previousRank)
end

-- add at new rank
table.insert(self.lbdb.players, insertAtRank, {
name = playerInfo.name,
level = playerInfo.level,
dingedAt = playerInfo.dingedAt,
classIndex = playerInfo.classIndex,
})

-- truncate when leaderboard reached max size
while #self.lbdb.players > self.Config.MaxLeaderboardSize do
table.remove(self.lbdb.players)
end

-- we only care about levels >= our bottom ranked on the leaderboard
local lowestLevel = self.lbdb.players[#self.lbdb.players].level
if #self.lbdb.players >= self.Config.MaxLeaderboardSize then
self.lbdb.minLevel = lowestLevel
end

-- update highest level
self.lbdb.highestLevel = math.max(self.lbdb.highestLevel, playerInfo.level)

return insertAtRank, isNew or isDing, lowestLevel
end

0 comments on commit 840d8b4

Please sign in to comment.