----------------------------------- -- Claim Shield ----------------------------------- require('modules/module_utils') ----------------------------------- local m = Module:new('claim_shield') local claimshieldTime = 7500 -- NOTE: These names are as they are as filenames. -- Example: Behemoth's Dominion => Behemoths_Dominion -- Example: King Behemoth => King_Behemoth -- { zone name, mob name } -- Format: local nmsToShield = { { 'Attohwa_Chasm', 'Citipati' }, { 'Attohwa_Chasm', 'Tiamat' }, { 'Attohwa_Chasm', 'Xolotl' }, { 'Caedarva_Mire', 'Khimaira' }, { 'Castle_Oztroja', 'Mee_Deggi_the_Punisher' }, { 'Castle_Oztroja', 'Quu_Domi_the_Gallant' }, { 'FeiYin', 'Capricious_Cassie' }, { 'FeiYin', 'Eastern_Shadow' }, { 'FeiYin', 'Northern_Shadow' }, { 'FeiYin', 'Southern_Shadow' }, { 'FeiYin', 'Western_Shadow' }, { 'Garlaige_Citadel', 'Serket' }, { 'Giddeus', 'Hoo_Mjuu_the_Torrent' }, { 'Gustav_Tunnel', 'Bune' }, { 'Jugner_Forest', 'King_Arthro' }, { 'King_Ranperres_Tomb', 'Vrtra' }, { 'Labyrinth_of_Onzozo', 'Lord_of_Onzozo' }, { 'Lufaise_Meadows', 'Padfoot' }, { 'Maze_of_Shakhrami', 'Argus' }, { 'Maze_of_Shakhrami', 'Leech_King' }, { 'Mount_Zhayolm', 'Cerberus' }, { 'Rolanberry_Fields', 'Eldritch_Edge' }, { 'Rolanberry_Fields', 'Simurgh' }, { 'Rolanberry_Fields_[S]', 'Lamina' }, { 'Sauromugue_Champaign', 'Blighting_Brand' }, { 'Sauromugue_Champaign', 'Roc' }, { 'Sauromugue_Champaign_[S]', 'Hyakinthos' }, { 'Sea_Serpent_Grotto', 'Charybdis' }, { 'South_Gustaberg', 'Leaping_Lizzy' }, { 'Uleguerand_Range', 'Jormungand' }, { 'Valkurm_Dunes', 'Valkurm_Emperor' }, { 'Wajaom_Woodlands', 'Hydra' }, { 'Western_Altepa_Desert', 'King_Vinegarroon' }, } -- Find the position of a target entity in a table, -- only if they have matching ids local tableFindPosByID = function(t, target) for index, entity in ipairs(t) do if entity:getID() == target:getID() then return index end end return nil end -- Using entity ids: dedupe a table in-place local dedupeByID = function(t) local seen = {} for index, entity in ipairs(t) do if seen[entity:getID()] then table.remove(t, index) else seen[entity:getID()] = true end end end -- Called when the claimshield period ends local timerFunc = function(mob) local enmityList = mob:getEnmityList() -- Filter so that pets will only count as a single entry along -- with their masters local entries = {} for _, v in pairs(enmityList) do local entity = v['entity'] local master = entity:getMaster() if not entity:isPC() and master and master:isPC() then table.insert(entries, master) else table.insert(entries, entity) end end -- Remove duplicates from entries table caused by pets or other shenanigans dedupeByID(entries) local numEntries = #entries mob:setMobMod(xi.mobMod.CLAIM_TYPE, xi.claimType.EXCLUSIVE) mob:setUnkillable(false) mob:setCallForHelpBlocked(false) mob:resetAI() mob:setHP(mob:getMaxHP()) mob:delStatusEffectsByFlag(0xFFFF) -- Delete all effects with all flags -- Select a winner local claimWinner = utils.randomEntry(entries) if claimWinner then mob:updateClaim(claimWinner) -- Message winner and their party/alliance that they've won local alliance = claimWinner:getAlliance() for _, member in pairs(alliance) do local str = string.format('Your group has won the lottery for %s! (out of %i players)', mob:getPacketName(), numEntries) if #alliance == 1 then str = string.format('You have won the lottery for %s! (out of %i players)', mob:getPacketName(), numEntries) end member:printToPlayer(str, xi.msg.channel.SYSTEM_3, '') -- Remove from entries table local pos = tableFindPosByID(entries, member) if pos then table.remove(entries, pos) end end -- Everyone left in the entries table isn't part of the winning group, message them and -- clear them from the enmity list for _, member in pairs(entries) do local str = string.format('Your group was not successful in the lottery for %s. (out of %i players)', mob:getPacketName(), numEntries) if #alliance == 1 then str = string.format('Your were not successful in the lottery for %s. (out of %i players)', mob:getPacketName(), numEntries) end member:printToPlayer(str, xi.msg.channel.SYSTEM_3, '') mob:clearEnmityForEntity(member) end end end -- Called on entity onMobSpawn, sets up timerFunc local listenerFunc = function(mob) print(string.format('Applying Claimshield to %s for %ims', mob:getPacketName(), claimshieldTime)) mob:setMobMod(xi.mobMod.CLAIM_TYPE, xi.claimType.UNCLAIMABLE) mob:setUnkillable(true) mob:setCallForHelpBlocked(true) mob:stun(claimshieldTime) mob:timer(claimshieldTime, timerFunc) end -- Main entrypoint of each override local overrideFunc = function(mob) mob:addListener('SPAWN', string.format('%s_CS_SPAWN', mob:getPacketName()), listenerFunc) -- Call original onMobInitialize super(mob) end -- NOTE: At the time we iterate over these entries, the Lua zone and mob objects won't be ready, -- : so we deal with everything as strings for now. for _, entry in pairs(nmsToShield) do m:addOverride(string.format('xi.zones.%s.mobs.%s.onMobInitialize', entry[1], entry[2]), overrideFunc) end return m