Advertisement
NonSequitur

Untitled

Dec 11th, 2015
359
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.00 KB | None | 0 0
  1. if not playerRanking then
  2.     playerRanking = {
  3.         -- Time in seconds a rank request stays in cache before a new one is made
  4.         cacheDuration = 60,
  5.         -- How many players to show in each page
  6.         pageSize      = 25,
  7.          -- How many promotions there are in your server (e.g knight->elite knight->super op knight = 2 promotions)
  8.         promotions    = 1,
  9.  
  10.         ranks = {
  11.             [SKILL_FIST]     = {},
  12.             [SKILL_SWORD]    = {},
  13.             [SKILL_CLUB]     = {},
  14.             [SKILL_SWORD]    = {},
  15.             [SKILL_AXE]      = {},
  16.             [SKILL_DISTANCE] = {},
  17.             [SKILL_SHIELD]   = {},
  18.             [SKILL_FISHING]  = {},
  19.             [SKILL_MAGLEVEL] = {},
  20.             [SKILL_LEVEL]    = {}
  21.         }
  22.     }
  23. end
  24.  
  25. local function filter(list, predicate)
  26.     local ret = {}
  27.     for k, v in ipairs(list) do
  28.         if predicate(v) then
  29.             table.insert(ret, v)
  30.         end
  31.     end
  32.     return ret
  33. end
  34.  
  35. local function map(list, predicate)
  36.     local ret = {}
  37.     for k, v in ipairs(list) do
  38.         table.insert(ret, predicate(v))
  39.     end
  40.     return ret
  41. end
  42.  
  43. local function bind(func, ...)
  44.     local bindings = {...}
  45.     return function(...)
  46.         local args, passArgs = {...}, { }
  47.         for _, bindedArg in pairs(bindings) do
  48.             local placeholder = (type(bindedArg) == 'string') and bindedArg:match('^placeholder_(%d-)$')
  49.             if placeholder then
  50.                 passArgs[_] = args[tonumber(placeholder)]
  51.             else
  52.                 passArgs[_] = bindedArg
  53.             end
  54.         end
  55.         return func(unpack(passArgs))
  56.     end
  57. end
  58.  
  59. function table.getindex(list, value, isArray)
  60.     local iterator = isArray and ipairs or pairs
  61.     for k, v in iterator(list) do
  62.         if v == value then
  63.             return k
  64.         end
  65.     end
  66.     return false
  67. end
  68. table.contains = table.getindex
  69.  
  70. function string.split(str, sep)
  71.     local ret = {}
  72.     str:gsub('([^' .. sep .. ']+)', function(p) table.insert(ret, p) end)
  73.     return ret
  74. end
  75.  
  76. function string.capitalize(str)
  77.     return str:sub(1, 1):upper() .. str:sub(2)
  78. end
  79.  
  80. local skillKeywords = {
  81.     [SKILL_FIST]     = 'fist',
  82.     [SKILL_CLUB]     = 'club',
  83.     [SKILL_SWORD]    = 'sword',
  84.     [SKILL_AXE]      = 'axe',
  85.     [SKILL_DISTANCE] = {'distance', 'dist'},
  86.     [SKILL_SHIELD]   = 'shield',
  87.     [SKILL_FISHING]  = 'fishing',
  88.     [SKILL_MAGLEVEL] = {'magiclevel', 'ml', 'magic', 'maglevel'},
  89.     [SKILL_LEVEL]    = {'level', 'lvl'}
  90. }
  91. local function getSkillByKeyword(keyword)
  92.     for skill = SKILL_FIST, SKILL_LEVEL do
  93.         local tmp = skillKeywords[skill]
  94.         if type(tmp) == 'string' then
  95.             if tmp == keyword then
  96.                 return skill
  97.             end
  98.         elseif table.contains(tmp, keyword, true) then
  99.             return skill
  100.         end
  101.     end
  102. end
  103.  
  104. local function getSkillName(skill)
  105.     if skill == SKILL_SHIELD then
  106.         return 'Shielding'
  107.     elseif skill == SKILL_MAGLEVEL then
  108.         return 'Magic Level'
  109.     elseif skill == SKILL_FISHING then
  110.         return 'Fishing'
  111.     elseif skill == SKILL_LEVEL then
  112.         return 'Level'
  113.     elseif skill == SKILL_DISTANCE then
  114.         return 'Distance Fighting'
  115.     elseif skill == SKILL_FIST then
  116.         return 'Fist Fighting'
  117.     else
  118.         return skillKeywords[skill][1]:capitalize() .. ' Fighting'
  119.     end
  120. end
  121.  
  122. local function getPlayerSkill(player, skill)
  123.     if skill == SKILL_LEVEL then
  124.         return player:getLevel()
  125.     elseif skill == SKILL_MAGLEVEL then
  126.         return player:getBaseMagicLevel()
  127.     end
  128.     return player:getSkillLevel(skill)
  129. end
  130.  
  131. local queryFormat = 'SELECT `name`, `%s`, `vocation` FROM `players` WHERE `group_id` <= 1 %s ORDER BY `%s` DESC LIMIT %d,%d;'
  132. local vocationFilterFormat = 'AND `vocation` IN (%s)'
  133. local skillColumns = {
  134.     [SKILL_FIST]     = 'skill_fist',
  135.     [SKILL_CLUB]     = 'skill_club',
  136.     [SKILL_SWORD]    = 'skill_sword',
  137.     [SKILL_AXE]      = 'skill_axe',
  138.     [SKILL_DISTANCE] = 'skill_dist',
  139.     [SKILL_SHIELD]   = 'skill_shielding',
  140.     [SKILL_FISHING]  = 'skill_fishing',
  141.     [SKILL_MAGLEVEL] = 'maglevel',
  142.     [SKILL_LEVEL]    = 'level' 
  143. }
  144. local function getRequestQuery(skill, vocationFilter, from, to)
  145.     local skillName = skillColumns[skill]
  146.     return queryFormat:format(
  147.         skillName,
  148.         #vocationFilter == 0 and '' or vocationFilterFormat:format(table.concat(vocationFilter)),
  149.         skillName,
  150.         from, to - from
  151.     )
  152. end
  153.  
  154. -- each list entry is {name = X, skill = Y, vocation = Z}
  155. local function updateRanking(skill, list)
  156.     for _, entry in ipairs(list) do
  157.         local current = filter(playerRanking.ranks[skill], function(e) return e.name == entry.name end)[1]
  158.         if current then
  159.             current.skill = math.max(entry.skill, current.skill)
  160.         else
  161.             table.insert(playerRanking.ranks[skill], entry)
  162.         end
  163.     end
  164. end
  165.  
  166. local function sortRanking(skill)
  167.     table.sort(playerRanking.ranks[skill],
  168.         function(a, b)
  169.             return a.skill > b.skill
  170.         end
  171.     )
  172. end
  173.  
  174. local function checkVocation(entry, vocationFilter)
  175.     return #vocationFilter == 0 or table.contains(vocationFilter, entry.vocation)
  176. end
  177.  
  178. function Vocation.getBase(self)
  179.     local id = self:getId()
  180.     while id > 4 do
  181.         id = id - 4
  182.     end
  183.     return Vocation(id)
  184. end
  185.  
  186. local function transformPlayer(player, skill)
  187.     return {name = player:getName(), vocation = player:getVocation():getBase():getId(), skill = getPlayerSkill(player, skill)}
  188. end
  189.  
  190. local function retrieveRanking(skill, vocationFilter, from, to, callback)
  191.     db.asyncStoreQuery(getRequestQuery(skill, vocationFilter, from, to),
  192.         function(query)
  193.             local list = {}
  194.             if query then
  195.                 repeat
  196.                     local entry ={
  197.                         name = result.getDataString(query, 'name'),
  198.                         vocation = result.getDataInt(query, 'vocation'),
  199.                         skill = result.getDataInt(query, skillColumns[skill])
  200.                     }
  201.                     table.insert(list, entry)
  202.                 until not result.next(query)
  203.             end
  204.  
  205.             local onlinePlayers = filter(
  206.                                         map(
  207.                                             Game.getPlayers(),
  208.                                             bind(transformPlayer, 'placeholder_1', skill)
  209.                                         ),
  210.  
  211.                                         bind(checkVocation, 'placeholder_1', vocationFilter)
  212.                                   )
  213.  
  214.             updateRanking(skill, list)
  215.             updateRanking(skill, onlinePlayers)
  216.             sortRanking(skill)
  217.  
  218.             if callback then
  219.                 return callback()
  220.             end
  221.         end
  222.     )
  223. end
  224.  
  225. local vocations = {'sorcerer', 'druid', 'paladin', 'knight'}
  226.  
  227. local function showRanking(player, skill, vocationFilter, page, skipCheck)
  228.     local rank = playerRanking.ranks[skill]
  229.     rank.expiration = rank.expiration or 0
  230.     local currentTime = os.time()
  231.     local pageSize = playerRanking.pageSize
  232.  
  233.     -- No cached results or cache is too old
  234.     if (#rank == 0 and currentTime < rank.expiration) or (currentTime >= rank.expiration) then
  235.         local rank = { }
  236.         rank.expiration = currentTime + playerRanking.cacheDuration
  237.         playerRanking.ranks[skill] = rank
  238.         return retrieveRanking(skill, vocationFilter, page * pageSize, (page + 1) * pageSize, bind(showRanking, player, skill, vocationFilter, page, true))
  239.     end
  240.  
  241.     -- Filter ranking by specified vocation
  242.     local ranking = filter(rank, bind(checkVocation, 'placeholder_1', vocationFilter))
  243.  
  244.     -- Cached results aren't enough to fill one page, try to retrieve more
  245.     if not skipCheck and (#ranking - page * pageSize) < pageSize then
  246.         return retrieveRanking(skill, vocationFilter, #ranking, #ranking + pageSize, bind(showRanking, player, skill, vocationFilter, page, true))
  247.     end
  248.  
  249.     -- Construct result window
  250.     local message
  251.     if #vocationFilter > 0 then
  252.         local filter = {}
  253.         for _, vocId in ipairs(vocationFilter) do
  254.             local vocName = vocations[vocId]
  255.             if vocName then
  256.                 table.insert(filter, vocName)
  257.             end
  258.         end
  259.  
  260.         message = 'Filter: ' .. table.concat(filter, ', ')
  261.     end
  262.  
  263.     local rankWindow = ModalWindow {
  264.         title = 'Rank: ' .. getSkillName(skill) .. ' Page ' .. (page + 1),
  265.         message = message
  266.     }
  267.  
  268.     local previousButton = rankWindow:addButton((page == 0) and '-' or 'Previous')
  269.     if page > 0 then
  270.         previousButton.callback = bind(showRanking, player, skill, vocationFilter, page - 1, true)
  271.     end
  272.  
  273.     local okButton = rankWindow:addButton('Ok')
  274.     rankWindow:setDefaultEnterButton('Ok')
  275.     rankWindow:setDefaultEscapeButton('Ok')
  276.  
  277.     local hasNext = (#ranking - page * 25) > pageSize
  278.     local nextButton = rankWindow:addButton(hasNext and 'Next' or '-')
  279.     if hasNext then
  280.         nextButton.callback = bind(showRanking, player, skill, vocationFilter, page + 1, false)
  281.     end
  282.  
  283.     if #ranking < page * pageSize + 1 then
  284.         rankWindow:addChoice('This ranking is empty.')
  285.     else
  286.         for n = page * pageSize + 1, (page + 1) * pageSize do
  287.             local entry = ranking[n]           
  288.             if entry then
  289.                 rankWindow:addChoice(string.format('%.2d - %s [%d]', n, entry.name, entry.skill))
  290.             end
  291.         end
  292.     end
  293.  
  294.     rankWindow:sendToPlayer(player)
  295. end
  296.  
  297. function onSay(player, words, param)
  298.     local p = param:split(',%s')
  299.  
  300.     if p[1] == 'help' then
  301.         local helpWindow = ModalWindow {
  302.             title = '!rank help',
  303.             message = 'Usage: !rank skill filter\n\nSkills: fist, club, sword, axe, distance\nshielding, fishing, magiclevel, level\n\n' ..
  304.                       'Filters: sorcerer, druid, paladin, knight\n\n' ..
  305.                       'Select one of the examples and click [Try]'
  306.         }
  307.  
  308.         -- Add usage example
  309.         local example1 = helpWindow:addChoice('!rank level')
  310.         example1.skill = SKILL_LEVEL
  311.         example1.vocationFilter = {}
  312.         local example2 = helpWindow:addChoice('!rank magiclevel paladin')
  313.         example2.skill = SKILL_MAGLEVEL
  314.         example2.vocationFilter = {3, 7}
  315.         local example3 = helpWindow:addChoice('!rank fist knight paladin')
  316.         example3.skill = SKILL_FIST
  317.         example3.vocationFilter = {3, 4, 8, 8}
  318.  
  319.         local tryButton = helpWindow:addButton('Try',
  320.             function(button, choice)
  321.                 return showRanking(player, choice.skill, choice.vocationFilter, 0)
  322.             end
  323.         )
  324.         helpWindow:addButton('Close')
  325.  
  326.         helpWindow:setDefaultEnterButton('Try')
  327.  
  328.         helpWindow:sendToPlayer(player)
  329.         return false
  330.     end
  331.  
  332.     local skill = getSkillByKeyword(p[1])
  333.     if not p[1] then
  334.         skill = SKILL_LEVEL
  335.     end
  336.  
  337.     if not skill then
  338.         player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'Command usage is wrong. Try !rank help for more information.')
  339.         return false
  340.     end
  341.  
  342.     local vocationFilter = { }
  343.     if #p > 1 then
  344.         for i = 2, #p do
  345.             local vocationId = table.getindex(vocations, p[i], true)
  346.             if vocationId then
  347.                 for mul = 0, playerRanking.promotions do
  348.                     table.insert(vocationFilter, vocationId + mul * 4)
  349.                 end
  350.             end
  351.         end
  352.     end
  353.  
  354.     showRanking(player, skill, vocationFilter, 0)
  355.     return false
  356. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement