-- Change autoUpdate to false if you wish to not receive auto updates. -- Change silentUpdate to true if you wish not to receive any message regarding updates local autoUpdate = true local silentUpdate = false local version = 1.076 --[[ _________ .____ ._____. / _____/ ____ __ _________ ____ ____ | | |__\_ |__ \_____ \ / _ \| | \_ __ \_/ ___\/ __ \| | | || __ \ / ( <_> ) | /| | \/\ \__\ ___/| |___| || \_\ \ /_______ /\____/|____/ |__| \___ >___ >_______ \__||___ / \/ \/ \/ \/ \/ SourceLib - a common library by Team TheSource Copyright (C) 2014 Team TheSource This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. Introduction: We were tired of updating every single script we developed so far so we decided to have it a little bit more dynamic with a custom library which we can update instead and every script using it will automatically be updated (of course only the parts which are in this lib). So let's say packet casting get's fucked up or we want to change the way some drawing is done, we just need to update it here and all scripts will have the same tweaks then. Contents: Require -- A basic but powerful library downloader SourceUpdater -- One of the most basic functions for every script we use Spell -- Spells handled the way they should be handled DrawManager -- Easy drawing of all kind of things, comes along with some other classes such as Circle DamageLib -- Calculate the damage done do others and even print it on their healthbar STS -- SimpleTargetSelector is a simple and yet powerful target selector to provide very basic target selecting MenuWrapper -- Easy menu creation with only a few lines Interrupter -- Easy way to handle interruptable spells AntiGetcloser -- Never let them get close to you ]] -- Namespace by pqmailer, added for Prodiction support reasons -- Used for several things in the future, like menues etc. _G.srcLib = {} --[[ '||''|. || || || .... ... . ... ... ... ... .. .... ||''|' .|...|| .' || || || || ||' '' .|...|| || |. || |. || || || || || || .||. '|' '|...' '|..'|| '|..'|. .||. .||. '|...' || '''' Require - A simple library downloader Introduction: If you want to use this class you need to put this at the beginning of you script. Example: ------------------------------------- if player.charName ~= "Brand" then return end require "SourceLib" local libDownloader = Require("Brand script") libDownloader:Add("VPrediction", "https://bitbucket.org/honda7/bol/raw/master/Common/VPrediction.lua") libDownloader:Add("SOW", "https://bitbucket.org/honda7/bol/raw/master/Common/SOW.lua") libDownloader:Check() if libDownloader.downloadNeeded then return end ------------------------------------- Functions: Require(myName) Members: Require.downloadNeeded Methods: Require:Add(name, url) Require:Check() ]] class 'Require' function __require_afterDownload(requireInstance) requireInstance.downloadCount = requireInstance.downloadCount - 1 if requireInstance.downloadCount == 0 then print("" .. requireInstance.myName .. ": Required libraries downloaded! Please reload!") end end function Require:__init(myName) self.myName = myName or GetCurrentEnv().FILE_NAME self.downloadNeeded = false self.requirements = {} end function Require:Add(name, url) assert(name and type(name) == "string" and url and type(url) == "string", "Require:Add(): Some or all arguments are invalid.") self.requirements[name] = url return self end function Require:Check() for scriptName, scriptUrl in pairs(self.requirements) do local scriptFile = LIB_PATH .. scriptName .. ".lua" if FileExist(scriptFile) then require(scriptName) else self.downloadNeeded = true self.downloadCount = self.downloadCount and self.downloadCount + 1 or 1 DownloadFile(scriptUrl, scriptFile, function() __require_afterDownload(self) end) end end return self end --[[ .|'''.| '||' '|' '|| . ||.. ' ... ... ... ... .. .... .... || | ... ... .. || .... .||. .... ... .. ''|||. .| '|. || || ||' '' .| '' .|...|| || | ||' || .' '|| '' .|| || .|...|| ||' '' . '|| || || || || || || || || | || | |. || .|' || || || || |'....|' '|..|' '|..'|. .||. '|...' '|...' '|..' ||...' '|..'||. '|..'|' '|.' '|...' .||. || '''' SourceUpdater - a simple updater class Introduction: Scripts that want to use this class need to have a version field at the beginning of the script, like this: local version = YOUR_VERSION (YOUR_VERSION can either be a string a a numeric value!) It does not need to be exactly at the beginning, like in this script, but it has to be within the first 100 chars of the file, otherwise the webresult won't see the field, as it gathers only about 100 chars Functions: SourceUpdater(scriptName, version, host, updatePath, filePath, versionPath) Members: SourceUpdater.silent | bool | Defines wheather to print notifications or not Methods: SourceUpdater:SetSilent(silent) SourceUpdater:CheckUpdate() ]] class 'SourceUpdater' --[[ Create a new instance of SourceUpdater @param scriptName | string | Name of the script which should be used when printed in chat @param version | float/string | Current version of the script @param host | string | Host, for example "bitbucket.org" or "raw.github.com" @param updatePath | string | Raw path to the script which should be updated @param filePath | string | Path to the file which should be replaced when updating the script @param versionPath | string | (optional) Path to a version file to check against. The version file may only contain the version. ]] function SourceUpdater:__init(scriptName, version, host, updatePath, filePath, versionPath) self.printMessage = function(message) if not self.silent then print("" .. self.UPDATE_SCRIPT_NAME .. ": " .. message .. "") end end self.getVersion = function(version) return tonumber(string.match(version or "", "%d+%.?%d*")) end self.UPDATE_SCRIPT_NAME = scriptName self.UPDATE_HOST = host self.UPDATE_PATH = updatePath .. "?rand="..math.random(1,10000) self.UPDATE_URL = "https://"..self.UPDATE_HOST..self.UPDATE_PATH -- Used for version files self.VERSION_PATH = versionPath and versionPath .. "?rand="..math.random(1,10000) self.VERSION_URL = versionPath and "https://"..self.UPDATE_HOST..self.VERSION_PATH self.UPDATE_FILE_PATH = filePath self.FILE_VERSION = self.getVersion(version) self.SERVER_VERSION = nil self.silent = false end --[[ Allows or disallows the updater to print info about updating @param | bool | Message output or not @return | class | The current instance ]] function SourceUpdater:SetSilent(silent) self.silent = silent return self end --[[ Check for an update and downloads it when available ]] function SourceUpdater:CheckUpdate() local webResult = GetWebResult(self.UPDATE_HOST, self.VERSION_PATH or self.UPDATE_PATH) if webResult then if self.VERSION_PATH then self.SERVER_VERSION = webResult else self.SERVER_VERSION = string.match(webResult, "%s*local%s+version%s+=%s+.*%d+%.%d+") end if self.SERVER_VERSION then self.SERVER_VERSION = self.getVersion(self.SERVER_VERSION) if not self.SERVER_VERSION then print("SourceLib: Please contact the developer of the script \"" .. (GetCurrentEnv().FILE_NAME or "DerpScript") .. "\", since the auto updater returned an invalid version.") return end if self.FILE_VERSION < self.SERVER_VERSION then self.printMessage("New version available: v" .. self.SERVER_VERSION) self.printMessage("Updating, please don't press F9") DelayAction(function () DownloadFile(self.UPDATE_URL, self.UPDATE_FILE_PATH, function () self.printMessage("Successfully updated, please reload!") end) end, 2) else self.printMessage("You've got the latest version: v" .. self.SERVER_VERSION) end else self.printMessage("Something went wrong! Please manually update the script!") end else self.printMessage("Error downloading version info!") end end --[[ .|'''.| '|| '|| ||.. ' ... ... .... || || ''|||. ||' || .|...|| || || . '|| || | || || || |'....|' ||...' '|...' .||. .||. || '''' Spell - Handled with ease! Functions: Spell(spellId, range, packetCast) Members: Spell.range | float | Range of the spell, please do NOT change this value, use Spell:SetRange() instead Spell.rangeSqr | float | Squared range of the spell, please do NOT change this value, use Spell:SetRange() instead Spell.packetCast | bool | Set packet cast state -- This only applies for skillshots Spell.sourcePosition | vector | From where the spell is casted, default: player Spell.sourceRange | vector | From where the range should be calculated, default: player -- This only applies for AOE skillshots Spell.minTargetsAoe | int | Set minimum targets for AOE damage Methods: Spell:SetRange(range) Spell:SetSource(source) Spell:SetSourcePosition(source) Spell:SetSourceRange(source) Spell:SetSkillshot(VP, skillshotType, width, delay, speed, collision) Spell:SetAOE(useAoe, radius, minTargetsAoe) Spell:SetCharged(spellName, chargeDuration, maxRange, timeToMaxRange, abortCondition) Spell:IsCharging() Spell:Charge() Spell:SetHitChance(hitChance) Spell:ValidTarget(target) Spell:GetPrediction(target) Spell:CastIfDashing(target) Spell:CastIfImmobile(target) Spell:Cast(param1, param2) Spell:AddAutomation(automationId, func) Spell:RemoveAutomation(automationId) Spell:ClearAutomations() Spell:TrackCasting(spellName) Spell:WillHitTarget() Spell:RegisterCastCallback(func) Spell:GetLastCastTime() Spell:IsInRange(target, from) Spell:IsReady() Spell:GetManaUsage() Spell:GetCooldown() Spell:GetLevel() Spell:GetName() ]] class 'Spell' -- Class related constants SKILLSHOT_LINEAR = 0 SKILLSHOT_CIRCULAR = 1 SKILLSHOT_CONE = 2 -- Different SpellStates returned when Spell:Cast() is called SPELLSTATE_TRIGGERED = 0 SPELLSTATE_OUT_OF_RANGE = 1 SPELLSTATE_LOWER_HITCHANCE = 2 SPELLSTATE_COLLISION = 3 SPELLSTATE_NOT_ENOUGH_TARGETS = 4 SPELLSTATE_NOT_DASHING = 5 SPELLSTATE_DASHING_CANT_HIT = 6 SPELLSTATE_NOT_IMMOBILE = 7 SPELLSTATE_INVALID_TARGET = 8 SPELLSTATE_NOT_TRIGGERED = 9 -- Spell identifier number used for comparing spells local spellNum = 1 --[[ New instance of Spell @param spellId | int | Spell ID (_Q, _W, _E, _R) @param range | float | Range of the spell @param packetCast | bool | (optional) Enable packet casting @param menu | scriptCofnig | (Sub)Menu to add the spell casting menu to ]] function Spell:__init(spellId, range, packetCast, menu) assert(spellId ~= nil and range ~= nil and type(spellId) == "number" and type(range) == "number", "Spell: Can't initialize Spell without valid arguments.") if _G.srcLib.spellMenu == nil then DelayAction(function(menu) if _G.srcLib.spellMenu == nil and not _G.srcLib.informedOutdated and Prodiction then if tonumber(Prodiction.GetVersion()) < 1.1 then print(" Please update your Prodiction to at least version 1.1 if you want to use it with SourceLib!") _G.srcLib.informedOutdated = true else menu = menu or scriptConfig("[SourceLib] SpellClass", "srcSpellClass") menu:addParam("predictionType", "Prediction Type", SCRIPT_PARAM_LIST, 1, { "VPrediction", "Prodiction" }) _G.srcLib.spellMenu = menu print("SourceLib: Prodiction support enabled, but it's highly recommended to use VPrediction at the moment due to error spamming related to Prodiction!") end end end, 3, { menu }) end self.spellId = spellId self:SetRange(range) self.packetCast = packetCast or false self:SetSource(player) self._automations = {} self._spellNum = spellNum spellNum = spellNum + 1 -- VPredicion default self.predictionType = 1 AddTickCallback(function() -- Prodiction found, apply value if _G.srcLib.spellMenu ~= nil then self:SetPredictionType(_G.srcLib.spellMenu.predictionType) end end) end --[[ Update the spell range with the new given value @param range | float | Range of the spell @return | class | The current instance ]] function Spell:SetRange(range) assert(range and type(range) == "number", "Spell: range is invalid") self.range = range self.rangeSqr = math.pow(range, 2) return self end --[[ Update both the sourcePosition and sourceRange from where everything will be calculated @param source | Cunit | Source position, for example player @return | class | The current instance ]] function Spell:SetSource(source) assert(source, "Spell: source can't be nil!") self.sourcePosition = source self.sourceRange = source return self end --[[ Update the source posotion from where the spell will be shot @param source | Cunit | Source position from where the spell will be shot, player by default @ return | class | The current instance ]] function Spell:SetSourcePosition(source) assert(source, "Spell: source can't be nil!") self.sourcePosition = source return self end --[[ Update the source unit from where the range will be calculated @param source | Cunit | Source object unit from where the range should be calculed @return | class | The current instance ]] function Spell:SetSourceRange(source) assert(source, "Spell: source can't be nil!") self.sourceRange = source return self end --[[ Define this spell as skillshot (can't be reversed) @param VP | class | Instance of VPrediction @param skillshotType | int | Type of this skillshot @param width | float | Width of the skillshot @param delay | float | (optional) Delay in seconds @param speed | float | (optional) Speed in units per second @param collision | bool | (optional) Respect unit collision when casting @rerurn | class | The current instance ]] function Spell:SetSkillshot(VP, skillshotType, width, delay, speed, collision) assert(skillshotType ~= nil, "Spell: Need at least the skillshot type!") self.VP = VP self.skillshotType = skillshotType self.width = width or 0 self.delay = delay or 0 self.speed = speed self.collision = collision if not self.hitChance then self.hitChance = 2 end return self end --[[ Sets the prediction type @param typeId | int | type ID (1 = VPrediction (default), 2 = Prodiction) ]] function Spell:SetPredictionType(typeId) assert(typeId and type(typeId) == 'number', 'Spell:SetPredictionType(): typeId is invalid!') self.predictionType = typeId end --[[ Set the AOE status of this spell, this can be changed later @param useAoe | bool | New AOE state @param radius | float | Radius of the AOE damage @param minTargetsAoe | int | Minimum targets to be hitted by the AOE damage @rerurn | class | The current instance ]] function Spell:SetAOE(useAoe, radius, minTargetsAoe) self.useAoe = useAoe or false self.radius = radius or self.width self.minTargetsAoe = minTargetsAoe or 0 return self end --[[ Define this spell as charged spell @param spellName | string | Name of the spell, example: VarusQ @param chargeDuration | float | Seconds of the spell to charge, after the time the charge expires @param maxRage | float | Max range the spell will have after fully charging @param timeToMaxRange | float | Time in seconds to reach max range after casting the spell @param abortCondition | function | (optional) A function which returns true when the charge process should be stopped. ]] function Spell:SetCharged(spellName, chargeDuration, maxRange, timeToMaxRange, abortCondition) assert(self.skillshotType, "Spell:SetCharged(): Only skillshots can be defined as charged spells!") assert(spellName and type(spellName) == "string" and chargeDuration and type(chargeDuration) == "number", "Spell:SetCharged(): Some or all arguments are invalid!") assert(self.__charged == nil, "Spell:SetCharged(): Already marked as charged spell!") self.__charged = true self.__charged_aborted = true self.__charged_spellName = spellName self.__charged_duration = chargeDuration self.__charged_maxRange = maxRange self.__charged_chargeTime = timeToMaxRange self.__charged_abortCondition = abortCondition or function () return false end self.__charged_active = false self.__charged_castTime = 0 -- Register callbacks if not self.__tickCallback then AddTickCallback(function() self:OnTick() end) self.__tickCallback = true end if not self.__sendPacketCallback then AddSendPacketCallback(function(p) self:OnSendPacket(p) end) self.__sendPacketCallback = true end if not self.__processSpellCallback then AddProcessSpellCallback(function(unit, spell) self:OnProcessSpell(unit, spell) end) self.__processSpellCallback = true end return self end --[[ Returns whether the spell is currently charging or not @return | bool | Spell charging or not ]] function Spell:IsCharging() return self.__charged_abortCondition() == false and self.__charged_active end --[[ Charges the spell ]] function Spell:Charge() assert(self.__charged, "Spell:Charge(): Spell is not defined as chargeable spell!") if not self:IsCharging() then CastSpell(self.spellId, mousePos.x, mousePos.z) end end -- Internal function, do not use! function Spell:_AbortCharge() if self.__charged and self.__charged_active then self.__charged_aborted = true self.__charged_active = false self:SetRange(self.__charged_initialRange) end end --[[ Set the hitChance of the predicted target position when to cast @param hitChance | int | New hitChance for predicted positions @rerurn | class | The current instance ]] function Spell:SetHitChance(hitChance) self.hitChance = hitChance or 2 return self end --[[ Checks if the given target is valid for the spell @param target | userdata | Target to be checked if valid @return | bool | Valid target or not ]] function Spell:ValidTarget(target, range) return ValidTarget(target, range or self.range) end --[[ Returns the prediction results from VPrediction/Prodiction in a VPrediction result layout @return | various data | Prediction result in VPrediction layout ]] function Spell:GetPrediction(target) if self.skillshotType ~= nil then -- VPrediction if self.predictionType == 1 then if self.skillshotType == SKILLSHOT_LINEAR then if self.useAoe then return self.VP:GetLineAOECastPosition(target, self.delay, self.radius, self.range, self.speed, self.sourcePosition) else return self.VP:GetLineCastPosition(target, self.delay, self.width, self.range, self.speed, self.sourcePosition, self.collision) end elseif self.skillshotType == SKILLSHOT_CIRCULAR then if self.useAoe then return self.VP:GetCircularAOECastPosition(target, self.delay, self.radius, self.range, self.speed, self.sourcePosition) else return self.VP:GetCircularCastPosition(target, self.delay, self.width, self.range, self.speed, self.sourcePosition, self.collision) end elseif self.skillshotType == SKILLSHOT_CONE then if self.useAoe then return self.VP:GetConeAOECastPosition(target, self.delay, self.radius, self.range, self.speed, self.sourcePosition) else return self.VP:GetLineCastPosition(target, self.delay, self.width, self.range, self.speed, self.sourcePosition, self.collision) end end -- Prodiction elseif self.predictionType == 2 then if self.useAoe then if self.skillshotType == SKILLSHOT_LINEAR then local pos, info, objects = Prodiction.GetLineAOEPrediction(target, self.range, self.speed, self.delay, self.radius, self.sourcePosition) local hitChance = self.collision and info.collision() and -1 or info.hitchance return pos, hitChance, #objects elseif self.skillshotType == SKILLSHOT_CIRCULAR then local pos, info, objects = Prodiction.GetCircularAOEPrediction(target, self.range, self.speed, self.delay, self.radius, self.sourcePosition) local hitChance = self.collision and info.collision() and -1 or info.hitchance return pos, hitChance, #objects elseif self.skillshotType == SKILLSHOT_CONE then local pos, info, objects = Prodiction.GetConeAOEPrediction(target, self.range, self.speed, self.delay, self.radius, self.sourcePosition) local hitChance = self.collision and info.collision() and -1 or info.hitchance return pos, hitChance, #objects end else local pos, info = Prodiction.GetPrediction(target, self.range, self.speed, self.delay, self.width, self.sourcePosition) local hitChance = self.collision and info.collision() and -1 or info.hitchance return pos, hitChance, info.pos end -- Someday it will look the same as with VPrediction ;D --[[ if self.skillshotType == SKILLSHOT_LINEAR then if self.useAoe then local pos, info, objects = Prodiction.GetLineAOEPrediction(target, self.range, self.speed, self.delay, self.radius, self.sourcePosition) return pos, self.collision and info.collision() and -1 or info.hitchance, type(objects) == "table" and #objects or 10 else local pos, info = Prodiction.GetPrediction(target, self.range, self.speed, self.delay, self.width, self.sourcePosition) return pos, self.collision and info.collision() and -1 or info.hitchance, info.pos end elseif self.skillshotType == SKILLSHOT_CIRCULAR then if self.useAoe then local pos, info, objects = Prodiction.GetCircularAOEPrediction(target, self.range, self.speed, self.delay, self.radius, self.sourcePosition) return pos, self.collision and info.collision() and -1 or info.hitchance, type(objects) == "table" and #objects or 10 else local pos, info = Prodiction.GetPrediction(target, self.range, self.speed, self.delay, self.width, self.sourcePosition) return pos, self.collision and info.collision() and -1 or info.hitchance, info.pos end elseif self.skillshotType == SKILLSHOT_CONE then if self.useAoe then local pos, info, objects = Prodiction.GetConeAOEPrediction(target, self.range, self.speed, self.delay, self.radius, self.sourcePosition) return pos, self.collision and info.collision() and -1 or info.hitchance, type(objects) == "table" and #objects or 10 else local pos, info = Prodiction.GetPrediction(target, self.range, self.speed, self.delay, self.width, self.sourcePosition) return pos, self.collision and info.collision() and -1 or info.hitchance, info.pos end end ]] end end end --[[ Tries to cast the spell when the target is dashing @param target | Cunit | Dashing target to attack @param return | int | SpellState of the current spell ]] function Spell:CastIfDashing(target) -- Don't calculate stuff when target is invalid if not ValidTarget(target) then return SPELLSTATE_INVALID_TARGET end if self.skillshotType ~= nil then local isDashing, canHit, position = self.VP:IsDashing(target, self.delay + 0.07 + GetLatency() / 2000, self.width, self.speed, self.sourcePosition) -- Out of range if self.rangeSqr < _GetDistanceSqr(self.sourceRange, position) then return SPELLSTATE_OUT_OF_RANGE end if isDashing and canHit then -- Collision if not self.collision or self.collision and not self.VP:CheckMinionCollision(target, position, self.delay + 0.07 + GetLatency() / 2000, self.width, self.range, self.speed, self.sourcePosition, false, true) then return self:__Cast(self.spellId, position.x, position.z) else return SPELLSTATE_COLLISION end elseif not isDashing then return SPELLSTATE_NOT_DASHING else return SPELLSTATE_DASHING_CANT_HIT end else local isDashing, canHit, position = self.VP:IsDashing(target, 0.25 + 0.07 + GetLatency() / 2000, 1, math.huge, self.sourcePosition) -- Out of range if self.rangeSqr < _GetDistanceSqr(self.sourceRange, position) then return SPELLSTATE_OUT_OF_RANGE end if isDashing and canHit then return self:__Cast(position.x, position.z) elseif not isDashing then return SPELLSTATE_NOT_DASHING else return SPELLSTATE_DASHING_CANT_HIT end end return SPELLSTATE_NOT_TRIGGERED end --[[ Tries to cast the spell when the target is immobile @param target | Cunit | Immobile target to attack @param return | int | SpellState of the current spell ]] function Spell:CastIfImmobile(target) -- Don't calculate stuff when target is invalid if not ValidTarget(target) then return SPELLSTATE_INVALID_TARGET end if self.skillshotType ~= nil then local isImmobile, position = self.VP:IsImmobile(target, self.delay + 0.07 + GetLatency() / 2000, self.width, self.speed, self.sourcePosition) -- Out of range if self.rangeSqr < _GetDistanceSqr(self.sourceRange, position) then return SPELLSTATE_OUT_OF_RANGE end if isImmobile then -- Collision if not self.collision or (self.collision and not self.VP:CheckMinionCollision(target, position, self.delay + 0.07 + GetLatency() / 2000, self.width, self.range, self.speed, self.sourcePosition, false, true)) then return self:__Cast(position.x, position.z) else return SPELLSTATE_COLLISION end else return SPELLSTATE_NOT_IMMOBILE end else local isImmobile, position = self.VP:IsImmobile(target, 0.25 + 0.07 + GetLatency() / 2000, 1, math.huge, self.sourcePosition) -- Out of range if self.rangeSqr < _GetDistanceSqr(self.sourceRange, target) then return SPELLSTATE_OUT_OF_RANGE end if isImmobile then return self:__Cast(target) else return SPELLSTATE_NOT_IMMOBILE end end return SPELLSTATE_NOT_TRIGGERED end --[[ Cast the spell, respecting previously made decisions about skillshots and AOE stuff @param param1 | userdata/float | When param2 is nil then this can be the target object, otherwise this is the X coordinate of the skillshot position @param param2 | float | Z coordinate of the skillshot position @param return | int | SpellState of the current spell ]] function Spell:Cast(param1, param2) if self.skillshotType ~= nil and param1 ~= nil and param2 == nil then -- Don't calculate stuff when target is invalid if not ValidTarget(param1) then return SPELLSTATE_INVALID_TARGET end local castPosition, hitChance, position, nTargets if self.skillshotType == SKILLSHOT_LINEAR or self.skillshotType == SKILLSHOT_CONE then if self.useAoe then castPosition, hitChance, nTargets = self:GetPrediction(param1) else castPosition, hitChance, position = self:GetPrediction(param1) -- Out of range if self.rangeSqr < _GetDistanceSqr(self.sourceRange, position) then return SPELLSTATE_OUT_OF_RANGE end end elseif self.skillshotType == SKILLSHOT_CIRCULAR then if self.useAoe then castPosition, hitChance, nTargets = self:GetPrediction(param1) else castPosition, hitChance, position = self:GetPrediction(param1) -- Out of range if math.pow(self.range + self.width + self.VP:GetHitBox(param1), 2) < _GetDistanceSqr(self.sourceRange, position) then return SPELLSTATE_OUT_OF_RANGE end end end -- Validation (for Prodiction) if not castPosition then return SPELLSTATE_NOT_TRIGGERED end -- AOE not enough targets if nTargets and nTargets < self.minTargetsAoe then return SPELLSTATE_NOT_ENOUGH_TARGETS end -- Collision detected if hitChance == -1 then return SPELLSTATE_COLLISION end -- Hitchance too low if hitChance and hitChance < self.hitChance then return SPELLSTATE_LOWER_HITCHANCE end -- Out of range if self.rangeSqr < _GetDistanceSqr(self.sourceRange, castPosition) then return SPELLSTATE_OUT_OF_RANGE end param1 = castPosition.x param2 = castPosition.z end -- Cast charged spell if param1 ~= nil and param2 ~= nil and self.__charged and self:IsCharging() then local p = CLoLPacket(230) p:EncodeF(player.networkID) p:Encode1(0x80) p:EncodeF(param1) p:EncodeF(0) p:EncodeF(param2) SendPacket(p) return SPELLSTATE_TRIGGERED end -- Cast the spell return self:__Cast(param1, param2) end --[[ Internal function, do not use this! ]] function Spell:__Cast(param1, param2) if self.packetCast then if param1 ~= nil and param2 ~= nil then if type(param1) ~= "number" and type(param2) ~= "number" and VectorType(param1) and VectorType(param2) then Packet("S_CAST", {spellId = self.spellId, toX = param2.x, toY = param2.z, fromX = param1.x, fromY = param1.z}):send() else Packet("S_CAST", {spellId = self.spellId, toX = param1, toY = param2, fromX = param1, fromY = param2}):send() end elseif param1 ~= nil then Packet("S_CAST", {spellId = self.spellId, toX = param1.x, toY = param1.z, fromX = param1.x, fromY = param1.z, targetNetworkId = param1.networkID}):send() else Packet("S_CAST", {spellId = self.spellId, toX = player.x, toY = player.z, fromX = player.x, fromY = player.z, targetNetworkId = player.networkID}):send() end else if param1 ~= nil and param2 ~= nil then if type(param1) ~= "number" and type(param2) ~= "number" and VectorType(param1) and VectorType(param2) then Packet("S_CAST", {spellId = self.spellId, toX = param2.x, toY = param2.z, fromX = param1.x, fromY = param1.z}):send() else CastSpell(self.spellId, param1, param2) end elseif param1 ~= nil then CastSpell(self.spellId, param1) else CastSpell(self.spellId) end end return SPELLSTATE_TRIGGERED end --[[ Add an automation to the spell to let it cast itself when a certain condition is met @param automationId | string/int | The ID of the automation, example "AntiGapCloser" @param func | function | Function to be called when checking, should return a bool value indicating if it should be casted and optionally the cast params (ex: target or x and z) ]] function Spell:AddAutomation(automationId, func) assert(automationId, "Spell: automationId is invalid!") assert(func and type(func) == "function", "Spell: func is invalid!") for index, automation in ipairs(self._automations) do if automation.id == automationId then return end end table.insert(self._automations, { id == automationId, func = func }) -- Register callbacks if not self.__tickCallback then AddTickCallback(function() self:OnTick() end) self.__tickCallback = true end end --[[ Remove and automation by it's id @param automationId | string/int | The ID of the automation, example "AntiGapCloser" ]] function Spell:RemoveAutomation(automationId) assert(automationId, "Spell: automationId is invalid!") for index, automation in ipairs(self._automations) do if automation.id == automationId then table.remove(self._automations, index) break end end end --[[ Clear all automations assinged to this spell ]] function Spell:ClearAutomations() self._automations = {} end --[[ Track the spell like in OnProcessSpell to add more features to this Spell instance @param spellName | string/table | Case insensitive name(s) of the spell @return | class | The current instance ]] function Spell:TrackCasting(spellName) assert(spellName, "Spell:TrackCasting(): spellName is invalid!") assert(self.__tracked_spellNames == nil, "Spell:TrackCasting(): This spell is already tracked!") assert(type(spellName) == "string" or type(spellName) == "table", "Spell:TrackCasting(): Type of spellName is invalid: " .. type(spellName)) self.__tracked_spellNames = type(spellName) == "table" and spellName or { spellName } -- Register callbacks if not self.__processSpellCallback then AddProcessSpellCallback(function(unit, spell) self:OnProcessSpell(unit, spell) end) self.__processSpellCallback = true end return self end --[[ When the spell is casted and about to hit a target, this will return the following @return | CUnit,float | The target unit, the remaining time in seconds it will take to hit the target, otherwise nil ]] function Spell:WillHitTarget() -- TODO: VPrediction expert's work ;D end --[[ Register a function which will be triggered when the spell is being casted the function will be given the spell object as parameter @param func | function | Function to be called when the spell is being processed (casted) ]] function Spell:RegisterCastCallback(func) assert(func and type(func) == "function" and self.__tracked_castCallback == nil, "Spell:RegisterCastCallback(): func is either invalid or a callback is already registered!") self.__tracked_castCallback = func end --[[ Returns the os.clock() time when the spell was last casted @return | float | Time in seconds when the spell was last casted or nil if the spell was never casted or spell is not tracked ]] function Spell:GetLastCastTime() return self.__tracked_lastCastTime or 0 end --[[ Get if the target is in range @return | bool | In range or not ]] function Spell:IsInRange(target, from) return self.rangeSqr >= _GetDistanceSqr(target, from or self.sourcePosition) end --[[ Get if the spell is ready or not @return | bool | Spell state ready or not ]] function Spell:IsReady() return player:CanUseSpell(self.spellId) == READY end --[[ Get the mana usage of the spell @return | float | Mana usage of the spell ]] function Spell:GetManaUsage() return player:GetSpellData(self.spellId).mana end --[[ Get the CURRENT cooldown of the spell @return | float | Current cooldown of the spell ]] function Spell:GetCooldown(current) return current and player:GetSpellData(self.spellId).currentCd or player:GetSpellData(self.spellId).totalCooldown end --[[ Get the stat points assinged to this spell (level) @return | int | Stat points assinged to this spell (level) ]] function Spell:GetLevel() return player:GetSpellData(self.spellId).level end --[[ Get the name of the spell @return | string | Name of the the spell ]] function Spell:GetName() return player:GetSpellData(self.spellId).name end --[[ Internal callback, don't use this! ]] function Spell:OnTick() -- Automations if self._automations and #self._automations > 0 then for _, automation in ipairs(self._automations) do local doCast, param1, param2 = automation.func() if doCast == true then self:Cast(param1, param2) end end end -- Charged spells if self.__charged then if self:IsCharging() then self:SetRange(math.min(self.__charged_initialRange + (self.__charged_maxRange - self.__charged_initialRange) * ((os.clock() - self.__charged_castTime) / self.__charged_chargeTime), self.__charged_maxRange)) elseif not self.__charged_aborted and os.clock() - self.__charged_castTime > 0.1 then self:_AbortCharge() end end end --[[ Internal callback, don't use this! ]] function Spell:OnProcessSpell(unit, spell) if unit and unit.valid and unit.isMe and spell and spell.name then -- Tracked spells if self.__tracked_spellNames then for _, trackedSpell in ipairs(self.__tracked_spellNames) do if trackedSpell:lower() == spell.name:lower() then self.__tracked_lastCastTime = os.clock() self.__tracked_castCallback(spell) end end end -- Charged spells if self.__charged and self.__charged_spellName:lower() == spell.name:lower() then self.__charged_active = true self.__charged_aborted = false self.__charged_initialRange = self.range self.__charged_castTime = os.clock() self.__charged_count = self.__charged_count and self.__charged_count + 1 or 1 DelayAction(function(chargeCount) if self.__charged_count == chargeCount then self:_AbortCharge() end end, self.__charged_duration, { self.__charged_count }) end end end --[[ Internal callback, don't use this! ]] function Spell:OnSendPacket(p) -- Charged spells if self.__charged then if p.header == 230 then if os.clock() - self.__charged_castTime <= 0.1 then p:Block() end elseif p.header == Packet.headers.S_CAST then local packet = Packet(p) if packet:get("spellId") == self.spellId then if os.clock() - self.__charged_castTime <= self.__charged_duration then self:_AbortCharge() local newPacket = CLoLPacket(230) newPacket:EncodeF(player.networkID) newPacket:Encode1(0x80) newPacket:EncodeF(mousePos.x) newPacket:EncodeF(mousePos.y) newPacket:EncodeF(mousePos.z) SendPacket(newPacket) p:Block() end end end end end function Spell:__eq(other) return other and other._spellNum and other._spellNum == self._spellNum or false end --[[ '||''|. '|| ||' || || ... .. .... ... ... ... ||| ||| .... .. ... .... ... . .... ... .. || || ||' '' '' .|| || || | |'|..'|| '' .|| || || '' .|| || || .|...|| ||' '' || || || .|' || ||| ||| | '|' || .|' || || || .|' || |'' || || .||...|' .||. '|..'|' | | .|. | .||. '|..'|' .||. ||. '|..'|' '||||. '|...' .||. .|....' DrawManager - Tired of having to draw everything over and over again? Then use this! Functions: DrawManager() Methods: DrawManager:AddCircle(circle) DrawManager:RemoveCircle(circle) DrawManager:CreateCircle(position, radius, width, color) DrawManager:OnDraw() ]] class 'DrawManager' --[[ New instance of DrawManager ]] function DrawManager:__init() self.objects = {} AddDrawCallback(function() self:OnDraw() end) end --[[ Add an existing circle to the draw manager @param circle | class | _Circle instance ]] function DrawManager:AddCircle(circle) assert(circle, "DrawManager: circle is invalid!") for _, object in ipairs(self.objects) do assert(object ~= circle, "DrawManager: object was already in DrawManager") end table.insert(self.objects, circle) end --[[ Removes a circle from the draw manager @param circle | class | _Circle instance ]] function DrawManager:RemoveCircle(circle) assert(circle, "DrawManager:RemoveCircle(): circle is invalid!") for index, object in ipairs(self.objects) do if object == circle then table.remove(self.objects, index) end end end --[[ Create a new circle and add it aswell to the DrawManager instance @param position | vector | Center of the circle @param radius | float | Radius of the circle @param width | int | Width of the circle outline @param color | table | Color of the circle in a tale format { a, r, g, b } @return | class | Instance of the newly create Circle class ]] function DrawManager:CreateCircle(position, radius, width, color) local circle = _Circle(position, radius, width, color) self:AddCircle(circle) return circle end --[[ DO NOT CALL THIS MANUALLY! This will be called automatically. ]] function DrawManager:OnDraw() for _, object in ipairs(self.objects) do if object.enabled then object:Draw() end end end --[[ ..|'''.| || '|| .|' ' ... ... .. .... || .... || || ||' '' .| '' || .|...|| '|. . || || || || || ''|....' .||. .||. '|...' .||. '|...' Functions: _Circle(position, radius, width, color) Members: _Circle.enabled | bool | Enable or diable the circle (displayed) _Circle.mode | int | See circle modes below _Circle.position | vector | Center of the circle _Circle.radius | float | Radius of the circle -- These are not changeable when a menu is set _Circle.width | int | Width of the circle outline _Circle.color | table | Color of the circle in a tale format { a, r, g, b } _Circle.quality | float | Quality of the circle, the higher the smoother the circle Methods: _Circle:AddToMenu(menu, paramText, addColor, addWidth, addQuality) _Circle:SetEnabled(enabled) _Circle:Set2D() _Circle:Set3D() _Circle:SetMinimap() _Circle:SetQuality(qualtiy) _Circle:SetDrawCondition(condition) _Circle:LinkWithSpell(spell, drawWhenReady) _Circle:Draw() ]] class '_Circle' -- Circle modes CIRCLE_2D = 0 CIRCLE_3D = 1 CIRCLE_MINIMAP = 2 -- Number of currently created circles local circleCount = 1 --[[ New instance of Circle @param position | vector | Center of the circle @param radius | float | Radius of the circle @param width | int | Width of the circle outline @param color | table | Color of the circle in a tale format { a, r, g, b } ]] function _Circle:__init(position, radius, width, color) assert(position and position.x and (position.y and position.z or position.y), "_Circle: position is invalid!") assert(radius and type(radius) == "number", "_Circle: radius is invalid!") assert(not color or color and type(color) == "table" and #color == 4, "_Circle: color is invalid!") self.enabled = true self.condition = nil self.menu = nil self.menuEnabled = nil self.menuColor = nil self.menuWidth = nil self.menuQuality = nil self.mode = CIRCLE_3D self.position = position self.radius = radius self.width = width or 1 self.color = color or { 255, 255, 255, 255 } self.quality = radius / 5 self._circleId = "circle" .. circleCount self._circleNum = circleCount circleCount = circleCount + 1 end --[[ Adds this circle to a given menu @param menu | scriptConfig | Instance of script config to add this circle to @param paramText | string | Text for the menu entry @param addColor | bool | Add color option @param addWidth | bool | Add width option @param addQuality | bool | Add quality option @return | class | The current instance ]] function _Circle:AddToMenu(menu, paramText, addColor, addWidth, addQuality) assert(menu, "_Circle: menu is invalid!") assert(self.menu == nil, "_Circle: Already bound to a menu!") menu:addSubMenu(paramText or "Circle " .. self._circleNum, self._circleId) self.menu = menu[self._circleId] -- Enabled local paramId = self._circleId .. "enabled" self.menu:addParam(paramId, "Enabled", SCRIPT_PARAM_ONOFF, self.enabled) self.menuEnabled = self.menu._param[#self.menu._param] if addColor or addWidth or addQuality then -- Color if addColor then paramId = self._circleId .. "color" self.menu:addParam(paramId, "Color", SCRIPT_PARAM_COLOR, self.color) self.menuColor = self.menu._param[#self.menu._param] end -- Width if addWidth then paramId = self._circleId .. "width" self.menu:addParam(paramId, "Width", SCRIPT_PARAM_SLICE, self.width, 1, 5) self.menuWidth = self.menu._param[#self.menu._param] end -- Quality if addQuality then paramId = self._circleId .. "quality" self.menu:addParam(paramId, "Quality", SCRIPT_PARAM_SLICE, math.round(self.quality), 10, math.round(self.radius / 5)) self.menuQuality = self.menu._param[#self.menu._param] end end return self end --[[ Set the enable status of the circle @param enabled | bool | Enable state of this circle @return | class | The current instance ]] function _Circle:SetEnabled(enabled) self.enabled = enabled return self end --[[ Set this circle to be displayed 2D @return | class | The current instance ]] function _Circle:Set2D() self.mode = CIRCLE_2D return self end --[[ Set this circle to be displayed 3D @return | class | The current instance ]] function _Circle:Set3D() self.mode = CIRCLE_3D return self end --[[ Set this circle to be displayed on the minimap @return | class | The current instance ]] function _Circle:SetMinimap() self.mode = CIRCLE_MINIMAP return self end --[[ Set the display quality of this circle @return | class | The current instance ]] function _Circle:SetQuality(qualtiy) assert(qualtiy and type(qualtiy) == "number", "_Circle: quality is invalid!") self.quality = quality return self end --[[ Set the draw condition of this circle @return | class | The current instance ]] function _Circle:SetDrawCondition(condition) assert(condition and type(condition) == "function", "_Circle: condition is invalid!") self.condition = condition return self end --[[ Links the spell range with the circle radius @param spell | class | Instance of Spell class @param drawWhenReady | bool | Decides whether to draw the circle when the spell is ready or not @return | class | The current instance ]] function _Circle:LinkWithSpell(spell, drawWhenReady) assert(spell, "_Circle:LinkWithSpell(): spell is invalid") self._linkedSpell = spell self._linkedSpellReady = drawWhenReady or false return self end --[[ Draw this circle, should only be called from OnDraw() ]] function _Circle:Draw() -- Don't draw if condition is not met if self.condition ~= nil and self.condition() == false then return end -- Update values if linked spell is given if self._linkedSpell then -- Temporary error prevention if not self._linkedSpell.IsReady then if not _G.SourceLibLinkedSpellInformed then _G.SourceLibLinkedSpellInformed = true print("SourceLib: The script \"" .. GetCurrentEnv().FILE_NAME .. "\" is causing issues with circle drawing. Please contact he developer of the named script so he fixes the issue, thanks.") end return else if self._linkedSpellReady and not self._linkedSpell:IsReady() then return end -- Update the radius with the spell range self.radius = self._linkedSpell.range end end -- Menu found if self.menu then if self.menuEnabled ~= nil then if not self.menu[self.menuEnabled.var] then return end end if self.menuColor ~= nil then self.color = self.menu[self.menuColor.var] end if self.menuWidth ~= nil then self.width = self.menu[self.menuWidth.var] end if self.menuQuality ~= nil then self.quality = self.menu[self.menuQuality.var] end end local center = WorldToScreen(D3DXVECTOR3(self.position.x, self.position.y, self.position.z)) if not self:PointOnScreen(center.x, center.y) and self.mode ~= CIRCLE_MINIMAP then return end if self.mode == CIRCLE_2D then DrawCircle2D(self.position.x, self.position.y, self.radius, self.width, TARGB(self.color), self.quality) elseif self.mode == CIRCLE_3D then DrawCircle3D(self.position.x, self.position.y, self.position.z, self.radius, self.width, TARGB(self.color), self.quality) elseif self.mode == CIRCLE_MINIMAP then DrawCircleMinimap(self.position.x, self.position.y, self.position.z, self.radius, self.width, TARGB(self.color), self.quality) else print("Circle: Something is wrong with the circle.mode!") end end function _Circle:PointOnScreen(x, y) return x <= WINDOW_W and x >= 0 and y >= 0 and y <= WINDOW_H end function _Circle:__eq(other) return other._circleId and other._circleId == self._circleId or false end --[[ '||''|. '||' || '|| || || .... .. .. .. .... ... . .... || ... || ... || || '' .|| || || || '' .|| || || .|...|| || || ||' || || || .|' || || || || .|' || |'' || || || || | .||...|' '|..'|' .|| || ||. '|..'|' '||||. '|...' .||.....| .||. '|...' .|....' DamageLib - Holy cow, so precise! Functions: DamageLib(source) Members: DamageLib.source | Cunit | Source unit for which the damage should be calculated Methods: DamageLib:RegisterDamageSource(spellId, damagetype, basedamage, perlevel, scalingtype, scalingstat, percentscaling, condition, extra) DamageLib:GetScalingDamage(target, scalingtype, scalingstat, percentscaling) DamageLib:GetTrueDamage(target, spell, damagetype, basedamage, perlevel, scalingtype, scalingstat, percentscaling, condition, extra) DamageLib:CalcSpellDamage(target, spell) DamageLib:CalcComboDamage(target, combo) DamageLib:IsKillable(target, combo) DamageLib:AddToMenu(menu, combo) -Available spells by default (not added yet): _AA: Returns the auto-attack damage. _IGNITE: Returns the ignite damage. _ITEMS: Returns the damage dealt by all the items actives. -Damage types: _MAGIC _PHYSICAL _TRUE -Scaling types: _AP, _AD, _BONUS_AD, _HEALTH, _ARMOR, _MR, _MAXHEALTH, _MAXMANA ]] class 'DamageLib' --Damage types _MAGIC, _PHYSICAL, _TRUE = 0, 1, 2 --Percentage scale type's _AP, _AD, _BONUS_AD, _HEALTH, _ARMOR, _MR, _MAXHEALTH, _MAXMANA = 1, 2, 3, 4, 5, 6, 7, 8 --Percentage scale functions local _ScalingFunctions = { [_AP] = function(x, y) return x * y.source.ap end, [_AD] = function(x, y) return x * y.source.totalDamage end, [_BONUS_AD] = function(x, y) return x * y.source.addDamage end, [_ARMOR] = function(x, y) return x * y.source.armor end, [_MR] = function(x, y) return x * y.source.magicArmor end, [_MAXHEALTH] = function(x, y) return x * y.source.maxHeath end, [_MAXMANA] = function(x, y) return x * y.source.maxMana end, } --[[ New instance of DamageLib @param source | Cunit | Source unit (attacker, player by default) ]] function DamageLib:__init(source) self.sources = {} self.source = source or player --Damage multiplicators: self.Magic_damage_m = 1 self.Physical_damage_m = 1 -- Most common damage sources self:RegisterDamageSource(_IGNITE, _TRUE, 0, 0, _TRUE, _AP, 0, function() return _IGNITE and (self.source:CanUseSpell(_IGNITE) == READY) end, function() return (50 + 20 * self.source.level) end) self:RegisterDamageSource(ItemManager:GetItem("DFG"):GetId(), _MAGIC, 0, 0, _MAGIC, _AP, 0, function() return ItemManager:GetItem("DFG"):GetSlot() and (self.source:CanUseSpell(ItemManager:GetItem("DFG"):GetSlot()) == READY) end, function(target) return 0.15 * target.maxHealth end) self:RegisterDamageSource(ItemManager:GetItem("BOTRK"):GetId(), _MAGIC, 0, 0, _MAGIC, _AP, 0, function() return ItemManager:GetItem("BOTRK"):GetSlot() and (self.source:CanUseSpell(ItemManager:GetItem("BOTRK"):GetSlot()) == READY) end, function(target) return 0.15 * target.maxHealth end) self:RegisterDamageSource(_AA, _PHYSICAL, 0, 0, _PHYSICAL, _AD, 1) end --[[ Register a new spell @param spellId | int | (unique) Spell id to add. @param damagetype | int | The type(s) of the base and perlevel damage (_MAGIC, _PHYSICAL, _TRUE). @param basedamage | int | Base damage(s) of the spell. @param perlevel | int | Damage(s) scaling per level. @param scalingtype | int | Type(s) of the percentage scale (_MAGIC, _PHYSICAL, _TRUE). @param scalingstat | int | Stat(s) that the damage scales with. @param percentscaling | int | Percentage(s) the stat scales with. @param condition | function | (optional) A function that returns true / false depending if the damage will be taken into account or not, the target is passed as param. @param extra | function | (optional) A function returning extra damage, the target is passed as param. -Example Spells: Teemo Q: 80 / 125 / 170 / 215 / 260 (+ 80% AP) (MAGIC) DamageLib:RegisterDamageSource(_Q, _MAGIC, 35, 45, _MAGIC, _AP, 0.8, function() return (player:CanUseSpell(_Q) == READY) end) Akalis E: 30 / 55 / 80 / 105 / 130 (+ 30% AP) (+ 60% AD) (PHYSICAL) DamageLib:RegisterDamageSource(_E, _PHYSICAL, 5, 25, {_PHYSICAL,_PHYSICAL}, {_AP, _AD}, {0.3, 0.6}, function() return (player:GetSpellData(_Q).currentCd < 2) or (player:CanUseSpell(_Q) == READY) end) * damagetype, basedamage, perlevel and scalingtype, scalingstat, percentscaling can be tables if there are 2 or more damage types. ]] function DamageLib:RegisterDamageSource(spellId, damagetype, basedamage, perlevel, scalingtype, scalingstat, percentscaling, condition, extra) condition = condition or function() return true end if spellId then self.sources[spellId] = {damagetype = damagetype, basedamage = basedamage, perlevel = perlevel, condition = condition, extra = extra, scalingtype = scalingtype, percentscaling = percentscaling, scalingstat = scalingstat} end end function DamageLib:GetScalingDamage(target, scalingtype, scalingstat, percentscaling) local amount = (_ScalingFunctions[scalingstat] or function() return 0 end)(percentscaling, self) if scalingtype == _MAGIC then return self.Magic_damage_m * self.source:CalcMagicDamage(target, amount) elseif scalingtype == _PHYSICAL then return self.Physical_damage_m * self.Physical_damage_m * self.source:CalcDamage(target, amount) elseif scalingtype == _TRUE then return amount end return 0 end function DamageLib:GetTrueDamage(target, spell, damagetype, basedamage, perlevel, scalingtype, scalingstat, percentscaling, condition, extra) basedamage = basedamage or 0 perlevel = perlevel or 0 condition = condition(target) scalingtype = scalingtype or 0 scalingstat = scalingstat or _AP percentscaling = percentscaling or 0 extra = extra or function() return 0 end local ScalingDamage = 0 if not condition then return 0 end if type(scalingtype) == "number" then ScalingDamage = ScalingDamage + self:GetScalingDamage(target, scalingtype, scalingstat, percentscaling) elseif type(scalingtype) == "table" then for i, v in ipairs(scalingtype) do ScalingDamage = ScalingDamage + self:GetScalingDamage(target, scalingtype[i], scalingstat[i], percentscaling[i]) end end if damagetype == _MAGIC then return self.Magic_damage_m * self.source:CalcMagicDamage(target, basedamage + perlevel * (spell < 4 and self.source:GetSpellData(spell).level or 0) + extra(target)) + ScalingDamage end if damagetype == _PHYSICAL then return self.Physical_damage_m * self.source:CalcDamage(target, basedamage + perlevel * (spell < 4 and self.source:GetSpellData(spell).level or 0) + extra(target)) + ScalingDamage end if damagetype == _TRUE then return basedamage + perlevel * (spell < 4 and self.source:GetSpellData(spell).level or 0) + extra(target) + ScalingDamage end return 0 end function DamageLib:CalcSpellDamage(target, spell) if not spell then return 0 end local spelldata = self.sources[spell] local result = 0 assert(spelldata, "DamageLib: The spell has to be added first!") local _type = type(spelldata.damagetype) if _type == "number" then result = self:GetTrueDamage(target, spell, spelldata.damagetype, spelldata.basedamage, spelldata.perlevel, spelldata.scalingtype, spelldata.scalingstat, spelldata.percentscaling, spelldata.condition, spelldata.extra) elseif _type == "table" then for i = 1, #spelldata.damagetype, 1 do result = result + self:GetTrueDamage(target, spell, spelldata.damagetype[i], spelldata.basedamage[i], spelldata.perlevel[i], 0, 0, 0, spelldata.condition) end result = result + self:GetTrueDamage(target, spell, 0, 0, 0, spelldata.scalingtype, spelldata.scalingstat, spelldata.percentscaling, spelldata.condition, spelldata.extra) end return result end function DamageLib:CalcComboDamage(target, combo) local totaldamage = 0 for i, spell in ipairs(combo) do if spell == ItemManager:GetItem("DFG"):GetId() and ItemManager:GetItem("DFG"):IsReady() then self.Magic_damage_m = 1.2 end end for i, spell in ipairs(combo) do totaldamage = totaldamage + self:CalcSpellDamage(target, spell) end self.Magic_damage_m = 1 return totaldamage end --[[ Returns if the unit will die after taking the combo damage. @param target | Cunit | Target. @param combo | table | The combo table. ]] function DamageLib:IsKillable(target, combo) return target.health <= self:CalcComboDamage(target, combo) end --[[ Adds the Health bar indicators to the menu. @param menu | scriptConfig | AllClass menu or submenu instance. @param combo | table | The combo table. ]] function DamageLib:AddToMenu(menu, combo) self.menu = menu self.combo = combo self.ticklimit = 5 --5 ticks per seccond self.barwidth = 100 self.cachedDamage = {} menu:addParam("DrawPredictedHealth", "Draw damage after combo.", SCRIPT_PARAM_ONOFF , true) self.enabled = menu.DrawPredictedHealth AddTickCallback(function() self:OnTick() end) AddDrawCallback(function() self:OnDraw() end) end function DamageLib:OnTick() if not self.menu["DrawPredictedHealth"] then return end self.lasttick = self.lasttick or 0 if os.clock() - self.lasttick > 1 / self.ticklimit then self.lasttick = os.clock() for i, enemy in ipairs(GetEnemyHeroes()) do if ValidTarget(enemy) then self.cachedDamage[enemy.hash] = self:CalcComboDamage(enemy, self.combo) end end end end function DamageLib:OnDraw() if not self.menu["DrawPredictedHealth"] then return end for i, enemy in ipairs(GetEnemyHeroes()) do if ValidTarget(enemy) then self:DrawIndicator(enemy) end end end function DamageLib:DrawIndicator(enemy) local damage = self.cachedDamage[enemy.hash] or 0 local SPos, EPos = GetEnemyHPBarPos(enemy) -- Validate data if not SPos then return end local barwidth = EPos.x - SPos.x local Position = SPos.x + math.max(0, (enemy.health - damage) / enemy.maxHealth) * barwidth DrawText("|", 16, math.floor(Position), math.floor(SPos.y + 8), ARGB(255,0,255,0)) DrawText("HP: "..math.floor(enemy.health - damage), 13, math.floor(SPos.x), math.floor(SPos.y), (enemy.health - damage) > 0 and ARGB(255, 0, 255, 0) or ARGB(255, 255, 0, 0)) end --[[ .|'''.| |''||''| .|'''.| ||.. ' || ||.. ' ''|||. || ''|||. . '|| || . '|| |'....|' .||. |'....|' Simple Target Selector (STS) - Why using the regular one when you can have it even more simple. ]] class 'SimpleTS' function STS_GET_PRIORITY(target) if not STS_MENU or not STS_MENU.STS[target.hash] then return 1 else return STS_MENU.STS[target.hash] end end STS_MENU = nil STS_NEARMOUSE = {id = 1, name = "Near mouse", sortfunc = function(a, b) return _GetDistanceSqr(mousePos, a) < _GetDistanceSqr(mousePos, b) end} STS_LESS_CAST_MAGIC = {id = 2, name = "Less cast (magic)", sortfunc = function(a, b) return (player:CalcMagicDamage(a, 100) / a.health) > (player:CalcMagicDamage(b, 100) / b.health) end} STS_LESS_CAST_PHYSICAL = {id = 3, name = "Less cast (physical)", sortfunc = function(a, b) return (player:CalcDamage(a, 100) / a.health) > (player:CalcDamage(b, 100) / b.health) end} STS_PRIORITY_LESS_CAST_MAGIC = {id = 4, name = "Less cast priority (magic)", sortfunc = function(a, b) return STS_GET_PRIORITY(a) * (player:CalcMagicDamage(a, 100) / a.health) > STS_GET_PRIORITY(b) * (player:CalcMagicDamage(b, 100) / b.health) end} STS_PRIORITY_LESS_CAST_PHYSICAL = {id = 5, name = "Less cast priority (physical)", sortfunc = function(a, b) return STS_GET_PRIORITY(a) * (player:CalcDamage(a, 100) / a.health) > STS_GET_PRIORITY(b) * (player:CalcDamage(b, 100) / b.health) end} STS_AVAILABLE_MODES = {STS_NEARMOUSE, STS_LESS_CAST_MAGIC, STS_LESS_CAST_PHYSICAL, STS_PRIORITY_LESS_CAST_MAGIC, STS_PRIORITY_LESS_CAST_PHYSICAL} function SimpleTS:__init(mode) self.mode = mode and mode or STS_LESS_CAST_PHYSICAL AddDrawCallback(function() self:OnDraw() end) AddMsgCallback(function(msg, key) self:OnMsg(msg, key) end) end function SimpleTS:IsValid(target, range, selected) if ValidTarget(target) and (_GetDistanceSqr(target) <= range or (self.hitboxmode and (_GetDistanceSqr(target) <= (math.sqrt(range) + self.VP:GetHitBox(myHero) + self.VP:GetHitBox(target)) ^ 2))) then if selected or (not (HasBuff(target, "UndyingRage") and (target.health == 1)) and not HasBuff(target, "JudicatorIntervention")) then return true end end end function SimpleTS:AddToMenu(menu) self.menu = menu self.menu:addSubMenu("Target Priority", "STS") for i, target in ipairs(GetEnemyHeroes()) do self.menu.STS:addParam(target.hash, target.charName, SCRIPT_PARAM_SLICE, 1, 1, 5, 0) end self.menu.STS:addParam("Info", "Info", SCRIPT_PARAM_INFO, "5 Highest priority") local modelist = {} for i, mode in ipairs(STS_AVAILABLE_MODES) do table.insert(modelist, mode.name) end self.menu:addParam("mode", "Targetting mode: ", SCRIPT_PARAM_LIST, 1, modelist) self.menu["mode"] = self.mode.id self.menu:addParam("Selected", "Focus selected target", SCRIPT_PARAM_ONOFF, true) STS_MENU = self.menu end function SimpleTS:OnMsg(msg, key) if msg == WM_LBUTTONDOWN then local MinimumDistance = math.huge local SelectedTarget for i, enemy in ipairs(GetEnemyHeroes()) do if ValidTarget(enemy) then if _GetDistanceSqr(enemy, mousePos) <= MinimumDistance then MinimumDistance = _GetDistanceSqr(enemy, mousePos) SelectedTarget = enemy end end end if SelectedTarget and MinimumDistance < 150 * 150 then self.STarget = SelectedTarget else self.STarget = nil end end end function SimpleTS:SelectedTarget() return self.STarget end function SimpleTS:GetTarget(range, n, forcemode) assert(range, "SimpleTS: range can't be nil") range = range * range local PosibleTargets = {} local selected = self:SelectedTarget() if self.menu then self.mode = STS_AVAILABLE_MODES[self.menu.mode] if self.menu.Selected and selected and selected.type == player.type and self:IsValid(selected, range, true) then return selected end end for i, enemy in ipairs(GetEnemyHeroes()) do if self:IsValid(enemy, range) then table.insert(PosibleTargets, enemy) end end table.sort(PosibleTargets, forcemode and forcemode.sortfunc or self.mode.sortfunc) return PosibleTargets[n and n or 1] end function SimpleTS:OnDraw() local selected = self:SelectedTarget() if self.menu and self.menu.Selected and ValidTarget(selected) then DrawCircle3D(selected.x, selected.y, selected.z, 100, 2, ARGB(175, 0, 255, 0), 25) end end --[[ '||' . '|| ||' || .||. .... .. .. .. ||| ||| .... .. ... .... ... . .... ... .. || || .|...|| || || || |'|..'|| '' .|| || || '' .|| || || .|...|| ||' '' || || || || || || | '|' || .|' || || || .|' || |'' || || .||. '|.' '|...' .|| || ||. .|. | .||. '|..'|' .||. ||. '|..'|' '||||. '|...' .||. .|....' ItemManager - Better handle them properly Functions: _ItemManager() Methods: _ItemManager:CastOffensiveItems(target) _ItemManager:GetItem(name) ]] class "_ItemManager" function _ItemManager:__init() self.items = { ["DFG"] = {id = 3128, range = 650, cancastonenemy = true}, ["BOTRK"] = {id = 3153, range = 450, cancastonenemy = true} } self.requesteditems = {} end --[[ Casts all known offensive items on the given target @param target | CUnit | Target unit ]] function _ItemManager:CastOffensiveItems(target) for name, itemdata in pairs(self.items) do local item = self:GetItem(name) if item:InRange(target) then item:Cast(target) end end end --[[ Gets the items by name. @param name | string | Name of the item (not the ingame name, the name used when registering, like DFG) @param return | class | Instance of the item that was requested or nil if not found ]] function _ItemManager:GetItem(name) assert(name and self.items[name], "ItemManager: Item not found") if not self.requesteditems[name] then self.requesteditems[name] = Item(self.items[name].id, self.items[name].range) end return self.requesteditems[name] end -- Make a global ItemManager instance. This means you don't need to make an instance for yourself. ItemManager = _ItemManager() --[[ '||' . || .||. .... .. .. .. || || .|...|| || || || || || || || || || .||. '|.' '|...' .|| || ||. Item - Best used in ItemManager Functions: Item(id, range) Methods: Item:GetId() Item:GetRange(sqr) Item:GetSlot() Item:UpdateSlot() Item:IsReady() Item:InRange(target) Item:Cast(param1, param2) ]] class "Item" --[[ Create a new instance of Item @param id | integer | Item id @param range | float | (optional) Range of the item ]] function Item:__init(id, range) assert(id and type(id) == "number", "Item: id is invalid!") assert(not range or range and type(range) == "number", "Item: range is invalid!") self.id = id self.range = range self.rangeSqr = range and range * range self.slot = GetInventorySlotItem(id) end --[[ Returns the id of the item @return | integer | Item id ]] function Item:GetId() return self.id end --[[ Returns the range of the item, only working when the item was defined with a range. @param sqr | boolean | Range squared or not @return | float | Range of the item ]] function Item:GetRange(sqr) return sqr and self.rangeSqr or self.range end --[[ Return the slot the item is in @return | integer | Slot it ]] function Item:GetSlot() self:UpdateSlot() return self.slot end --[[ Updates the item slot to the current one (if changed) ]] function Item:UpdateSlot() self.slot = GetInventorySlotItem(self.id) end --[[ Returns if the item is ready to be casted (only working when it's an active item) @return | boolean | State of the item ]] function Item:IsReady() self:UpdateSlot() return self.slot and (player:CanUseSpell(self.slot) == READY) end --[[ Returns if the item (actually player) is in range of the target @param target | CUnit | Target unit @return | boolean | In range or not ]] function Item:InRange(target) return _GetDistanceSqr(target) <= self.rangeSqr end --[[ Casts the item @param param1 | CUnit/float | Either the target unit itself or as part of the position the X coordinate @param param2 | float | (only use when param1 is given) The Z coordinate @return | integer | The spell state ]] function Item:Cast(param1, param2) self:UpdateSlot() if self.slot then if param1 ~= nil and param2 ~= nil then CastSpell(self.slot, param1, param2) elseif param1 ~= nil then CastSpell(self.slot, param1) else CastSpell(self.slot) end return SPELLSTATE_TRIGGERED end end --[[ '|| ||' '|| '||' '|' ||| ||| .... .. ... ... ... '|. '|. .' ... .. .... ... ... ... ... .... ... .. |'|..'|| .|...|| || || || || || || | ||' '' '' .|| ||' || ||' || .|...|| ||' '' | '|' || || || || || || ||| ||| || .|' || || | || | || || .|. | .||. '|...' .||. ||. '|..'|. | | .||. '|..'|' ||...' ||...' '|...' .||. || || '''' '''' MenuWrapper - Keep it simple! Introduction: If you want to use the MenuWrapper you must create your menu with it! You cannot add this class to an existing menu. Once you have created a new instance, make sure you remember the follwing: - Target selector sub-menu name: ts - Orbwalker sub-menu name: orbwalker - Drawings sub-menu name: drawings If you want to use these sub menus, simply use something like this: MenuWrapper:GetHandle().drawings:addParam("info", "This has been created using MenuWrapper!", SCRIPT_PARAM_INFO, "") This means you can get the scriptConfig instance of the wrapped menu by using this: local myMenu = MenuWrapper:GetHandle() Functions: MenuWrapper(menuName, menuId) Methods: MenuWrapper:SetTargetSelector(targetSelector) MenuWrapper:SetOrbwalker(orbwalker) MenuWrapper:AddCircle(circle, addColor, addWidth, addQuality) MenuWrapper:AddCircles(circleTable, addColor, addWidth, addQuality) MenuWrapper:GetHandle() ]] class 'MenuWrapper' --[[ Create a new MenuWrapper instance @param menuName | string | Display name of the menu @param menuId | string | (optional) ID of the menu (for saving purposes) ]] function MenuWrapper:__init(menuName, menuId) assert(menuName and type(menuName) == "string", "MenuWrapper: menuName is invalid!") self.__menu = scriptConfig(menuName, menuId or menuName) end --[[ Adds a SimpleTS instance to the menu. @param targetSelector | class | SimpleTS instance @return | class | scriptConfig target selector sub-menu ]] function MenuWrapper:SetTargetSelector(targetSelector) assert(targetSelector, "MenuWrapper:SetTargetSelector(): targetSelector is not a valid SimpleTS instance!") assert(self.__targetSelector == nil, "MenuWrapper:SetTargetSelector(): targetSelector was already set!") self.__targetSelector = targetSelector self.__menu:addSubMenu("Target Selector", "ts") self.__targetSelector:AddToMenu(self.__menu.ts) return self.__menu.ts end --[[ Adds a SOW instance to the menu @param orbwalker | class | SOW instance @return | class | scritpConfig orbwalker sub-menu ]] function MenuWrapper:SetOrbwalker(orbwalker) assert(orbwalker, "MenuWrapper:SetOrbwalker(): orbwalker is not a valid SOW instance!") assert(self.__orbwalker == nil, "MenuWrapper:SetOrbwalker(): orwalker was already set!") self.__orbwalker = orbwalker self.__menu:addSubMenu("Orbwalker", "orbwalker") self.__orbwalker:LoadToMenu(self.__menu.orbwalker) return self.__menu.orbwalker end --[[ Adds a circle to the drawings sub-menu @param circle | class | _Circle instance @param addColor | bool | (optional) Add color option @param addWidth | bool | (optional) Add width option @param addQuality | bool | (optional) Add quality option @return | class | scriptConfig drawings sub-menu ]] function MenuWrapper:AddCircle(circle, name, addColor, addWidth, addQuality) return self:AddCircles({ [1] = { circle = circle, name = name } }, addColor, addWidth, addQuality) end --[[ Adds a table of circles to the drawings sub-menu @param circleTable | table | Table containing _Circle instances @param addColor | bool | (optional) Add color option @param addWidth | bool | (optional) Add width option @param addQuality | bool | (optional) Add quality option @return | class | scriptConfig drawings sub-menu ]] function MenuWrapper:AddCircles(circleTable, addColor, addWidth, addQuality) assert(circleTable and type(circleTable) == "table", "MenuWrapper:AddCircles(): circleTable is not a valid table!") for _, entry in ipairs(circleTable) do assert(entry.circle, "MenuWrapper:AddCircles(): circle is not a valid _Circle instance!") assert(entry.name, "MenuWarpper:AddCircles(): circle name was not given!") assert(self.__circles == nil or not table.contains(self.__circles, entry.circle), "MenuWrapper:AddCircles(): The circle was already added!") if not self.__circles then self.__circles = {} end if not self.__drawing then self.__menu:addSubMenu("Drawings", "drawings") self.__drawing = self.__menu.drawings end entry.circle:AddToMenu(self.__drawing, entry.name, addColor, addWidth, addQuality) table.insert(self.__circles, entry.circle) end return self.__drawing end --[[ Returns the wrapped scriptConfig @return | class | scriptConfig instance ]] function MenuWrapper:GetHandle() return self.__menu end --[[ '||' . . || .. ... .||. .... ... .. ... .. ... ... ... ... .||. .... ... .. || || || || .|...|| ||' '' ||' '' || || ||' || || .|...|| ||' '' || || || || || || || || || || | || || || .||. .||. ||. '|.' '|...' .||. .||. '|..'|. ||...' '|.' '|...' .||. || '''' Interrupter - They will never cast! Like alwasy undocumented by honda... ]] class 'Interrupter' local _INTERRUPTIBLE_SPELLS = { ["KatarinaR"] = { charName = "Katarina", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["Meditate"] = { charName = "MasterYi", DangerLevel = 1, MaxDuration = 2.5, CanMove = false }, ["Drain"] = { charName = "FiddleSticks", DangerLevel = 3, MaxDuration = 2.5, CanMove = false }, ["Crowstorm"] = { charName = "FiddleSticks", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["GalioIdolOfDurand"] = { charName = "Galio", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["MissFortuneBulletTime"] = { charName = "MissFortune", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["VelkozR"] = { charName = "Velkoz", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["InfiniteDuress"] = { charName = "Warwick", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["AbsoluteZero"] = { charName = "Nunu", DangerLevel = 4, MaxDuration = 2.5, CanMove = false }, ["ShenStandUnited"] = { charName = "Shen", DangerLevel = 3, MaxDuration = 2.5, CanMove = false }, ["FallenOne"] = { charName = "Karthus", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["AlZaharNetherGrasp"] = { charName = "Malzahar", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, ["Pantheon_GrandSkyfall_Jump"] = { charName = "Pantheon", DangerLevel = 5, MaxDuration = 2.5, CanMove = false }, } function Interrupter:__init(menu, cb) self.callbacks = {} self.activespells = {} AddTickCallback(function() self:OnTick() end) AddProcessSpellCallback(function(unit, spell) self:OnProcessSpell(unit, spell) end) if menu then self:AddToMenu(menu) end if cb then self:AddCallback(cb) end end function Interrupter:AddToMenu(menu) assert(menu, "Interrupter: menu can't be nil!") local SpellAdded = false local EnemyChampioncharNames = {} for i, enemy in ipairs(GetEnemyHeroes()) do table.insert(EnemyChampioncharNames, enemy.charName) end menu:addParam("Enabled", "Enabled", SCRIPT_PARAM_ONOFF, true) for spellName, data in pairs(_INTERRUPTIBLE_SPELLS) do if table.contains(EnemyChampioncharNames, data.charName) then menu:addParam(string.gsub(spellName, "_", ""), data.charName.." - "..spellName, SCRIPT_PARAM_ONOFF, true) SpellAdded = true end end if not SpellAdded then menu:addParam("Info", "Info", SCRIPT_PARAM_INFO, "No spell available to interrupt") end self.Menu = menu end function Interrupter:AddCallback(cb) assert(cb and type(cb) == "function", "Interrupter: callback is invalid!") table.insert(self.callbacks, cb) end function Interrupter:TriggerCallbacks(unit, spell) for i, callback in ipairs(self.callbacks) do callback(unit, spell) end end function Interrupter:OnProcessSpell(unit, spell) if not self.Menu.Enabled then return end if unit.team ~= myHero.team then if _INTERRUPTIBLE_SPELLS[spell.name] then local SpellToInterrupt = _INTERRUPTIBLE_SPELLS[spell.name] if (self.Menu and self.Menu[string.gsub(spell.name, "_", "")]) or not self.Menu then local data = {unit = unit, DangerLevel = SpellToInterrupt.DangerLevel, endT = os.clock() + SpellToInterrupt.MaxDuration, CanMove = SpellToInterrupt.CanMove} table.insert(self.activespells, data) self:TriggerCallbacks(data.unit, data) end end end end function Interrupter:OnTick() for i = #self.activespells, 1, -1 do if self.activespells[i].endT - os.clock() > 0 then self:TriggerCallbacks(self.activespells[i].unit, self.activespells[i]) else table.remove(self.activespells, i) end end end --[[ | . || ..|'''.| '|| ||| .. ... .||. ... .|' ' .... ... ... .... || ... .... .... ... .. | || || || || || || .... '' .|| ||' || .| '' || .| '|. ||. ' .|...|| ||' '' .''''|. || || || || '|. || .|' || || | || || || || . '|.. || || .|. .||. .||. ||. '|.' .||. ''|...'| '|..'|' ||...' '|...' .||. '|..|' |'..|' '|...' .||. || '''' AntiGapcloser - Stay away please, thanks. And again undocumented by honda -.- ]] class 'AntiGapcloser' local _GAPCLOSER_TARGETED, _GAPCLOSER_SKILLSHOT = 1, 2 --Add only very fast skillshots/targeted spells since vPrediction will handle the slow dashes that will trigger OnDash local _GAPCLOSER_SPELLS = { ["AatroxQ"] = "Aatrox", ["AkaliShadowDance"] = "Akali", ["Headbutt"] = "Alistar", ["FioraQ"] = "Fiora", ["DianaTeleport"] = "Diana", ["EliseSpiderQCast"] = "Elise", ["FizzPiercingStrike"] = "Fizz", ["GragasE"] = "Gragas", ["HecarimUlt"] = "Hecarim", ["JarvanIVDragonStrike"] = "JarvanIV", ["IreliaGatotsu"] = "Irelia", ["JaxLeapStrike"] = "Jax", ["KhazixE"] = "Khazix", ["khazixelong"] = "Khazix", ["LeblancSlide"] = "LeBlanc", ["LeblancSlideM"] = "LeBlanc", ["BlindMonkQTwo"] = "LeeSin", ["LeonaZenithBlade"] = "Leona", ["UFSlash"] = "Malphite", ["Pantheon_LeapBash"] = "Pantheon", ["PoppyHeroicCharge"] = "Poppy", ["RenektonSliceAndDice"] = "Renekton", ["RivenTriCleave"] = "Riven", ["SejuaniArcticAssault"] = "Sejuani", ["slashCast"] = "Tryndamere", ["ViQ"] = "Vi", ["MonkeyKingNimbus"] = "MonkeyKing", ["XenZhaoSweep"] = "XinZhao", ["YasuoDashWrapper"] = "Yasuo" } function AntiGapcloser:__init(menu, cb) self.callbacks = {} self.activespells = {} AddTickCallback(function() self:OnTick() end) AddProcessSpellCallback(function(unit, spell) self:OnProcessSpell(unit, spell) end) if menu then self:AddToMenu(menu) end if cb then self:AddCallback(cb) end end function AntiGapcloser:AddToMenu(menu) assert(menu, "AntiGapcloser: menu can't be nil!") local SpellAdded = false local EnemyChampioncharNames = {} for i, enemy in ipairs(GetEnemyHeroes()) do table.insert(EnemyChampioncharNames, enemy.charName) end menu:addParam("Enabled", "Enabled", SCRIPT_PARAM_ONOFF, true) for spellName, charName in pairs(_GAPCLOSER_SPELLS) do if table.contains(EnemyChampioncharNames, charName) then menu:addParam(string.gsub(spellName, "_", ""), charName.." - "..spellName, SCRIPT_PARAM_ONOFF, true) SpellAdded = true end end if not SpellAdded then menu:addParam("Info", "Info", SCRIPT_PARAM_INFO, "No spell available to interrupt") end self.Menu = menu end function AntiGapcloser:AddCallback(cb) assert(cb and type(cb) == "function", "AntiGapcloser: callback is invalid!") table.insert(self.callbacks, cb) end function AntiGapcloser:TriggerCallbacks(unit, spell) for i, callback in ipairs(self.callbacks) do callback(unit, spell) end end function AntiGapcloser:OnProcessSpell(unit, spell) if not self.Menu.Enabled then return end if unit.team ~= myHero.team then if _GAPCLOSER_SPELLS[spell.name] then local Gapcloser = _GAPCLOSER_SPELLS[spell.name] if (self.Menu and self.Menu[string.gsub(spell.name, "_", "")]) or not self.Menu then local add = false if spell.target and spell.target.isMe then add = true startPos = Vector(unit.visionPos) endPos = myHero elseif not spell.target then local endPos1 = Vector(unit.visionPos) + 300 * (Vector(spell.endPos) - Vector(unit.visionPos)):normalized() local endPos2 = Vector(unit.visionPos) + 100 * (Vector(spell.endPos) - Vector(unit.visionPos)):normalized() --TODO check angles etc if (_GetDistanceSqr(myHero.visionPos, unit.visionPos) > _GetDistanceSqr(myHero.visionPos, endPos1) or _GetDistanceSqr(myHero.visionPos, unit.visionPos) > _GetDistanceSqr(myHero.visionPos, endPos2)) then add = true end end if add then local data = {unit = unit, spell = spell.name, startT = os.clock(), endT = os.clock() + 1, startPos = startPos, endPos = endPos} table.insert(self.activespells, data) self:TriggerCallbacks(data.unit, data) end end end end end function AntiGapcloser:OnTick() for i = #self.activespells, 1, -1 do if self.activespells[i].endT - os.clock() > 0 then self:TriggerCallbacks(self.activespells[i].unit, self.activespells[i]) else table.remove(self.activespells, i) end end end --[[ |''||''| || '|| '||' || || . || ... .... || .. || ... .. .. .. ... .||. .... ... .. || || .| '' || .' || || || || || || || .|...|| ||' '' || || || ||'|. || || || || || || || || || .||. .||. '|...' .||. ||. .||.....| .||. .|| || ||. .||. '|.' '|...' .||. TickLimiter - Because potato computers also use SourceLib Functions: TickLimiter(func, frequency) ]] class 'TickLimiter' --[[ Starts TickLimiter instance @param func | function | The function to be called @param frequency | integer | The times the function will called per second. ]] function TickLimiter:__init(func, frequency) assert(frequency and frequency > 0, "TickLimiter: frecuency is invalid!") assert(func and type(func) == "function", "TickLimiter: func is invalid!") self.lasttick = 0 self.interval = 1 / frequency self.func = func AddTickCallback(function() self:OnTick() end) end --[[ Internal callback ]] function TickLimiter:OnTick() if os.clock() - self.lasttick >= self.interval then self.func() self.lasttick = os.clock() end end --[[ '||''|. '|| . '||' '||' '|| '|| || || .... .... || .. .... .||. || || .... .. ... .. || || .... ... .. ||...|' '' .|| .| '' || .' .|...|| || ||''''|| '' .|| || || .' '|| || .|...|| ||' '' || .|' || || ||'|. || || || || .|' || || || |. || || || || .||. '|..'|' '|...' .||. ||. '|...' '|.' .||. .||. '|..'|' .||. ||. '|..'||. .||. '|...' .||. PacketHandler - Even track all dem packets, not just the easy ones! Methods: PacketHandler:HookIncomingPacket(header, callback) PacketHandler:HookOutgoingPacket(header, callback) ]] class '_PacketHandler' --[[ Create a new instance of _PacketHandler. Please don't call this, use the global PacketHandler instead. ]] function _PacketHandler:__init() self.__incomingCallbacks = {} self.__outgoingCallbacks = {} -- Register callbacks AddSendPacketCallback(function(p) self:OnSendPacket(p) end) AddRecvPacketCallback(function(p) self:OnRecvPacket(p) end) -- Only replace the function once if not _G.OldSendPacket then -- Saving the original SendPacket function for later usage _G.OldSendPacket = _G.SendPacket -- Overriden packet sending _G.SendPacket = function(p) for header, callbackTable in pairs(self.__outgoingCallbacks) do if header == p.header then for _, callback in ipairs(callbackTable) do callback(p) end end end _G.OldSendPacket(p) end end end --[[ Hook into an incoming packet. @param header | integer | Header id to hook into @param callback | function | Function to call when the packet in being received ]] function _PacketHandler:HookIncomingPacket(header, callback) -- Precheck if not self.__incomingCallbacks[header] then self.__incomingCallbacks[header] = {} end table.insert(self.__incomingCallbacks[header], callback) end --[[ Hook into an outgoing packet. @param header | integer | Header id to hook into @param callback | function | Function to call when the packet in being sent ]] function _PacketHandler:HookOutgoingPacket(header, callback) -- Precheck if not self.__outgoingCallbacks[header] then self.__outgoingCallbacks[header] = {} end table.insert(self.__outgoingCallbacks[header], callback) end --[[ Internal callback ]] function _PacketHandler:OnRecvPacket(p) for header, callbackTable in pairs(self.__incomingCallbacks) do if header == p.header then for _, callback in ipairs(callbackTable) do callback(p) end end end end --[[ Internal callback ]] function _PacketHandler:OnSendPacket(p) for header, callbackTable in pairs(self.__outgoingCallbacks) do if header == p.header then for _, callback in ipairs(callbackTable) do callback(p) end end end end -- Create a global instsance of PacketHandler PacketHandler = _PacketHandler() --[[ ..|'''.| '||' '||' '|| '|| .|' ' .... .. .. .. .... || || .... .. ... .. || || .... ... .. || .... '' .|| || || || .|...|| ||''''|| '' .|| || || .' '|| || .|...|| ||' '' '|. || .|' || || || || || || || .|' || || || |. || || || || ''|...'| '|..'|' .|| || ||. '|...' .||. .||. '|..'|' .||. ||. '|..'||. .||. '|...' .||. GameHandler - Hooks for everything and some nice extra stuff aswell ]] class '_GameHandler' function _GameHandler:__init() end local gridSize = 50 function _GameHandler:GetGridCoordinates(unit) assert(unit and unit.x and unit.y and unit.z, "GameHandler:GetGridCoordinates(): unit is invalid!") return Vector(math.ceil(unit.x / gridSize), math.ceil(unit.y / gridSize), math.ceil(unit.z / gridSize)) end function _GameHandler:GetGameCoordinates(grid) assert(grid and grid.x and grid.y and grid.z, "GameHandler:GetGameCoordinates(): grid is invalid") return Vector(grid.x * gridSize, grid.y * gridSize, grid.z * gridSize) end function _GameHandler:GetGridGameCoordinates(unit) assert(unit and unit.x and unit.y and unit.z, "GameHandler:GetGridGameCoordinates(): unit is invalid!") return self:GetGameCoordinates(self:GetGridCoordinates(unit)) end -- Create a global instance of GameHandler GameHandler = _GameHandler() --[[ '||' '|' . || '|| || | .||. ... || || | || || || || | || || || '|..' '|.' .||. .||. Util - Just utils. ]] SUMMONERS_RIFT = { 1, 2 } PROVING_GROUNDS = 3 TWISTED_TREELINE = { 4, 10 } CRYSTAL_SCAR = 8 HOWLING_ABYSS = 12 function IsMap(map) assert(map and (type(map) == "number" or type(map) == "table"), "IsMap(): map is invalid!") if type(map) == "number" then return GetGame().map.index == map else for _, id in ipairs(map) do if GetGame().map.index == id then return true end end end end function GetMapName() if IsMap(SUMMONERS_RIFT) then return "Summoners Rift" elseif IsMap(CRYSTAL_SCAR) then return "Crystal Scar" elseif IsMap(HOWLING_ABYSS) then return "Howling Abyss" elseif IsMap(TWISTED_TREELINE) then return "Twisted Treeline" elseif IsMap(PROVING_GROUNDS) then return "Proving Grounds" else return "Unknown map" end end function ProtectTable(t) local proxy = {} local mt = { __index = t, __newindex = function (t,k,v) error('attempt to update a read-only table', 2) end } setmetatable(proxy, mt) return proxy end function _GetDistanceSqr(p1, p2) p2 = p2 or player if p1 and p1.networkID and (p1.networkID ~= 0) and p1.visionPos then p1 = p1.visionPos end if p2 and p2.networkID and (p2.networkID ~= 0) and p2.visionPos then p2 = p2.visionPos end return GetDistanceSqr(p1, p2) end function GetObjectsAround(radius, position, condition) radius = math.pow(radius, 2) position = position or player local objectsAround = {} for i = 1, objManager.maxObjects do local object = objManager:getObject(i) if object and object.valid and (condition and condition(object) == true or not condition) and _GetDistanceSqr(position, object) <= radius then table.insert(objectsAround, object) end end return objectsAround end function HasBuff(unit, buffname) for i = 1, unit.buffCount do local tBuff = unit:getBuff(i) if tBuff.valid and BuffIsValid(tBuff) and tBuff.name == buffname then return true end end return false end function GetSummonerSlot(name, unit) unit = unit or player if unit:GetSpellData(SUMMONER_1).name == name then return SUMMONER_1 end if unit:GetSpellData(SUMMONER_2).name == name then return SUMMONER_2 end end function GetEnemyHPBarPos(enemy) -- Prevent error spamming if not enemy.barData then if not _G.__sourceLib_barDataInformed then print("SourceLib: barData was not found, spudgy please...") _G.__sourceLib_barDataInformed = true end return end local barPos = GetUnitHPBarPos(enemy) local barPosOffset = GetUnitHPBarOffset(enemy) local barOffset = Point(enemy.barData.PercentageOffset.x, enemy.barData.PercentageOffset.y) local barPosPercentageOffset = Point(enemy.barData.PercentageOffset.x, enemy.barData.PercentageOffset.y) local BarPosOffsetX = 169 local BarPosOffsetY = 47 local CorrectionX = 16 local CorrectionY = 4 barPos.x = barPos.x + (barPosOffset.x - 0.5 + barPosPercentageOffset.x) * BarPosOffsetX + CorrectionX barPos.y = barPos.y + (barPosOffset.y - 0.5 + barPosPercentageOffset.y) * BarPosOffsetY + CorrectionY local StartPos = Point(barPos.x, barPos.y) local EndPos = Point(barPos.x + 103, barPos.y) return Point(StartPos.x, StartPos.y), Point(EndPos.x, EndPos.y) end function CountObjectsNearPos(pos, range, radius, objects) local n = 0 for i, object in ipairs(objects) do if _GetDistanceSqr(pos, object) <= radius * radius then n = n + 1 end end return n end function GetBestCircularFarmPosition(range, radius, objects) local BestPos local BestHit = 0 for i, object in ipairs(objects) do local hit = CountObjectsNearPos(object.visionPos or object, range, radius, objects) if hit > BestHit then BestHit = hit BestPos = Vector(object) if BestHit == #objects then break end end end return BestPos, BestHit end function CountObjectsOnLineSegment(StartPos, EndPos, width, objects) local n = 0 for i, object in ipairs(objects) do local pointSegment, pointLine, isOnSegment = VectorPointProjectionOnLineSegment(StartPos, EndPos, object) if isOnSegment and GetDistanceSqr(pointSegment, object) < width * width then n = n + 1 end end return n end function GetBestLineFarmPosition(range, width, objects) local BestPos local BestHit = 0 for i, object in ipairs(objects) do local EndPos = Vector(myHero.visionPos) + range * (Vector(object) - Vector(myHero.visionPos)):normalized() local hit = CountObjectsOnLineSegment(myHero.visionPos, EndPos, width, objects) if hit > BestHit then BestHit = hit BestPos = Vector(object) if BestHit == #objects then break end end end return BestPos, BestHit end function GetPredictedPositionsTable(VP, t, delay, width, range, speed, source, collision) local result = {} for i, target in ipairs(t) do local CastPosition, Hitchance, Position = VP:GetCircularCastPosition(target, delay, width, range, speed, source, collision) table.insert(result, Position) end return result end function MergeTables(t1, t2) for i = 1, #t2 do t1[#t1 + 1] = t2[i] end return t1 end function SelectUnits(units, condition) local result = {} for i, unit in ipairs(units) do if condition(unit) then table.insert(result, unit) end end return result end function SpellToString(id) if id == _Q then return "Q" end if id == _W then return "W" end if id == _E then return "E" end if id == _R then return "R" end end function TARGB(colorTable) assert(colorTable and type(colorTable) == "table" and #colorTable == 4, "TARGB: colorTable is invalid!") return ARGB(colorTable[1], colorTable[2], colorTable[3], colorTable[4]) end function PingClient(x, y, pingType) Packet("R_PING", {x = x, y = y, type = pingType and pingType or PING_FALLBACK}):receive() end local __util_autoAttack = { "frostarrow" } local __util_noAutoAttack = { "shyvanadoubleattackdragon", "shyvanadoubleattack", "monkeykingdoubleattack" } function IsAASpell(spell) if not spell or not spell.name then return end for _, spellName in ipairs(__util_autoAttack) do if spellName == spell.name:lower() then return true end end for _, spellName in ipairs(__util_noAutoAttack) do if spellName == spell.name:lower() then return false end end if spell.name:lower():find("attack") then return true end return false end -- Source: http://lua-users.org/wiki/CopyTable function TableDeepCopy(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[TableDeepCopy(orig_key)] = TableDeepCopy(orig_value) end setmetatable(copy, TableDeepCopy(getmetatable(orig))) elseif orig_type == "Vector" then copy = orig:clone() else -- number, string, boolean, etc copy = orig end return copy end --[[ '||' || . || '|| || . || || .. ... ... .||. ... .... || ... ...... .... .||. ... ... .. ... || || || || || || '' .|| || || ' .|' '' .|| || || .| '|. || || || || || || || || .|' || || || .|' .|' || || || || || || || .||. .||. ||. .||. '|.' .||. '|..'|' .||. .||. ||....| '|..'|' '|.' .||. '|..|' .||. ||. ]] -- We are currently moving to GitHub, gimme a sec to create a repo over there ;) -- Update script if autoUpdate then SourceUpdater("SourceLib", version, "raw.github.com", "/TheRealSource/public/master/common/SourceLib.lua", LIB_PATH .. "SourceLib.lua", "/TheRealSource/public/master/common/SourceLib.version"):SetSilent(silentUpdate):CheckUpdate() end -- Set enemy bar data for i, enemy in ipairs(GetEnemyHeroes()) do enemy.barData = {PercentageOffset = {x = 0, y = 0} }--GetEnemyBarData()--spadge pls end --Summoner spells _IGNITE = GetSummonerSlot("SummonerDot") _FLASH = GetSummonerSlot("SummonerFlash") _EXHAUST = GetSummonerSlot("SummonerExhaust") --Others _AA = 10000 _PASIVE = 10001 -- Temp fix for Prodiction DelayAction(function() LoadProtectedScript('VjUzEzdFTURpN0NFYN50TGhvRUxAbTNLRXlNeE9CZUVMRm1zS155TXlRsm/FSgAtMw3FOU0+hrJlWExBbCRLTPkLeAdy4wQNQK0yy0TvjHhFL+RFTRtsM0tSOUr5ALMkRQtBrzHNBDhNuUfyZNONQW7yCkd5EPjGc20FTcMrsgpFNYy7RLNkRkwd7LNKTTlM/ADzJEUAAa4xFgR5TD/HMGUJzYNvbspFeAG4hXCkREhAbHFPRa9M+0Uv5MVNG2wzS1K5TfkA8yFF6UFtMxYEeUxmRvJlZ8xAbZBLswZSecZydkVMQGk6S0V5GzAWLTAWCRJtN01FeU0JJxsXNkxEbjNLRSYKeUJ+ZUVMJQNFIjcWIxQjHBFFSEptM0sDECEcAwoMNjhAaT9LRXkeOhQ7NRETECxnA0V9RHlGciMsICUiQy4reUl6RnJlLCNAaTZLRXkiCSMcZUFOQG0zOUV9RnlGciMsICU+RzksFyp5QndlRUwyCFIvRX1OeUZyTyRMRGszS0UaIRY1F2VBSkBtMycqDigLRnZgRUxAC1olIXlJf0ZyZWpjLAlSS0FyTXlGBgQ2Y29dHScwGE19UHJlRQ0kCWAuKx0dGCUZADEPIQFfKSQaJnlHcmVFREBtM0NFeU14RnFsRUxAKjMLRf8NOUb15QVNx61zSl35zXlRMmXFAEAsMxYFeUxmRvJlQExAbTdMRXlNESMTASA+QGk0S0V5HRglGQAxTERlM0tFESgYIhcXNkxEajNLRSoSOgchMUVIRm0zSwcVIhotcmVFTEBsM0tFeU15RnJlRUxAbTNLRXlNeUZyZEVMQGwzS0V5TXlGcmVFTEBtM0tFeQ==5B5AAAA59AF52BAFE7CD9B29EDD5C17B') end, 120)