CCCPaste

ALS AutoAbility

repeat task.wait() until game:IsLoaded()

-- ========================================
-- CONFIGURATION - Edit settings below
-- ========================================

-- Enable/disable the script
getgenv().AutoAbilitiesEnabled = true

-- Game speed multiplier (affects cooldown calculations)
-- Set to 3 if playing on 3x speed, 1 for normal speed, etc.
local GAME_SPEED = 3

-- Tower ability configurations
-- Format: [TowerName] = { ability1, ability2, ... }
-- 
-- Available options for each ability:
-- - name: Ability name (required) - cooldown and requiredLevel auto-detected from TowerInfo
-- - onlyOnBoss: Only use when boss exists (true/false)
-- - onSpecificWaves: Only use on specific waves (e.g., {25, 50} or single wave 50)
-- - minWave: Only use on or after this wave number
-- - maxWave: Only use on or before this wave number
-- - requireBossInRange: Wait for boss to be in tower's range (true/false)
-- - rangeDelay: How long (seconds) boss must be in range before using ability
-- - delayAfterBossSpawn: Wait X seconds after boss spawns before using ability

local TOWER_ABILITIES = {
    ["AiHoshinoEvo"] = {
        {name = "Concert"}
    },
    ["Bulma"] = {
        {name = "Summon Wish Dragon"},
        {name = "Wish: Power"}
    },
    ["AsuraEvo"] = {
        {name = "Lines of Sanzu", onlyOnBoss = true, onSpecificWaves = 50}
    },
    ["NarutoBaryon"] = {
        {name = "Spiralling Shuriken"},
        {name = "Lava Release: Spiralling Shuriken", onlyOnBoss = true, onSpecificWaves = 50}
    },
    ["LelouchEvo"] = {
        {name = "All of you, Die!", onlyOnBoss = true, onSpecificWaves = 50, requireBossInRange = true},
        {name = "Submission", onlyOnBoss = true, onSpecificWaves = 50, requireBossInRange = true}
    },
    ["LightEvo"] = {
        {name = "Write Names", onlyOnBoss = true, onSpecificWaves = 50, delayAfterBossSpawn = 1}
    }
}

-- ========================================
-- SCRIPT CODE - Do not edit below unless you know what you're doing
-- ========================================

local RS = game:GetService("ReplicatedStorage")
local Towers = workspace.Towers

local bossSpawnTime = nil
local lightAbilityUsed = false
local bossInRangeTracker = {}
local abilityCooldowns = {}
local towerInfoCache = {}
local generalBossSpawnTime = nil



local function getTowerInfo(towerName)
    if towerInfoCache[towerName] then
        return towerInfoCache[towerName]
    end
    
    local success, towerData = pcall(function()
        local towerInfoPath = RS:WaitForChild("Modules"):WaitForChild("TowerInfo")
        local towerModule = towerInfoPath:FindFirstChild(towerName)
        if towerModule and towerModule:IsA("ModuleScript") then
            return require(towerModule)
        end
        return nil
    end)
    
    if success and towerData then
        towerInfoCache[towerName] = towerData
        return towerData
    end
    return nil
end

local function getAbilityData(towerName, abilityName)
    local towerInfo = getTowerInfo(towerName)
    if not towerInfo then 
        return nil 
    end
    
    for level = 0, 50 do
        if towerInfo[level] then
            if towerInfo[level].Ability then
                local abilityData = towerInfo[level].Ability
                if abilityData.Name == abilityName and not abilityData.AttributeRequired then
                    return {
                        cooldown = abilityData.Cd,
                        requiredLevel = level,
                        isGlobal = abilityData.IsCdGlobal
                    }
                end
            end
            
            if towerInfo[level].Abilities then
                for _, abilityData in pairs(towerInfo[level].Abilities) do
                    if abilityData.Name == abilityName and not abilityData.AttributeRequired then
                        return {
                            cooldown = abilityData.Cd,
                            requiredLevel = level,
                            isGlobal = abilityData.IsCdGlobal
                        }
                    end
                end
            end
        end
    end
    return nil
end 

local function getTower(name)
    return Towers:FindFirstChild(name)
end

local function getUpgradeLevel(tower)
    if not tower then return 0 end
    local upgradeVal = tower:FindFirstChild("Upgrade")
    if upgradeVal and upgradeVal:IsA("ValueBase") then
        return upgradeVal.Value or 0
    end
    return 0
end

local function getCurrentWave()
    local ok, result = pcall(function()
        local player = game:GetService("Players").LocalPlayer
        local gui = player.PlayerGui:FindFirstChild("Top")
        if not gui then return 0 end
        local frame = gui:FindFirstChild("Frame")
        if not frame then return 0 end
        frame = frame:FindFirstChild("Frame")
        if not frame then return 0 end
        frame = frame:FindFirstChild("Frame")
        if not frame then return 0 end
        frame = frame:FindFirstChild("Frame")
        if not frame then return 0 end
        local button = frame:FindFirstChild("TextButton")
        if not button then return 0 end
        local children = button:GetChildren()
        if #children < 3 then return 0 end
        local text = children[3].Text
        return tonumber(text) or 0
    end)
    return ok and result or 0
end

local function getTowerInfoName(tower)
    if not tower then return nil end
    
    local candidates = {
        tower:GetAttribute("TowerType"),
        tower:GetAttribute("Type"),
        tower:GetAttribute("TowerName"),
        tower:GetAttribute("BaseTower"),
        tower:FindFirstChild("TowerType") and tower.TowerType:IsA("ValueBase") and tower.TowerType.Value,
        tower:FindFirstChild("Type") and tower.Type:IsA("ValueBase") and tower.Type.Value,
        tower:FindFirstChild("TowerName") and tower.TowerName:IsA("ValueBase") and tower.TowerName.Value,
        tower.Name 
    }
    
    for _, candidate in ipairs(candidates) do
        if candidate and type(candidate) == "string" and candidate ~= "" then
            return candidate
        end
    end
    
    return tower.Name 
end

local function getTower(name)
    return Towers:FindFirstChild(name)
end

local function getUpgradeLevel(tower)
    if not tower then return 0 end
    local upgradeVal = tower:FindFirstChild("Upgrade")
    if upgradeVal and upgradeVal:IsA("ValueBase") then
        return upgradeVal.Value or 0
    end
    return 0
end

local function useAbility(tower, abilityName)
    if tower then
        pcall(function()
            local Event = RS.Remotes.Ability
            Event:InvokeServer(tower, abilityName)
        end)
    end
end

local function isOnCooldown(towerName, abilityName)
    local abilityData = getAbilityData(towerName, abilityName)
    if not abilityData or not abilityData.cooldown then return false end
    
    local key = towerName .. "_" .. abilityName
    local lastUsed = abilityCooldowns[key]
    
    if not lastUsed then return false end
    
    local adjustedCooldown = abilityData.cooldown / GAME_SPEED
    local elapsed = tick() - lastUsed
    return elapsed < adjustedCooldown
end

local function setAbilityUsed(towerName, abilityName)
    local key = towerName .. "_" .. abilityName
    abilityCooldowns[key] = tick()
end

local function hasAbilityBeenUnlocked(towerName, abilityName, towerLevel)
    local abilityData = getAbilityData(towerName, abilityName)
    if not abilityData then 
        return false 
    end
    
    local unlocked = towerLevel >= abilityData.requiredLevel
    if not unlocked then
    end
    return unlocked
end

local function isWave50()
    local ok, result = pcall(function()
        local player = game:GetService("Players").LocalPlayer
        local gui = player.PlayerGui:FindFirstChild("Top")
        if not gui then return false end
        local frame = gui:FindFirstChild("Frame")
        if not frame then return false end
        frame = frame:FindFirstChild("Frame")
        if not frame then return false end
        frame = frame:FindFirstChild("Frame")
        if not frame then return false end
        frame = frame:FindFirstChild("Frame")
        if not frame then return false end
        local button = frame:FindFirstChild("TextButton")
        if not button then return false end
        local children = button:GetChildren()
        if #children < 3 then return false end
        local text = children[3].Text
        return text == tostring(BOSS_WAVE) or text == BOSS_WAVE
    end)
    return ok and result
end

local function bossExists()
    local ok, result = pcall(function()
        local enemies = workspace:FindFirstChild("Enemies")
        if not enemies then return false end
        return enemies:FindFirstChild("Boss") ~= nil
    end)
    return ok and result
end

local function bossReadyForAbilities()
    if bossExists() then
        if not generalBossSpawnTime then
            generalBossSpawnTime = tick()
        end
        local elapsed = tick() - generalBossSpawnTime
        return elapsed >= 1
    else
        generalBossSpawnTime = nil
        return false
    end
end

local function checkBossSpawnTime()
    if bossExists() then
        if not bossSpawnTime then
            bossSpawnTime = tick()
            lightAbilityUsed = false
        end
        local elapsed = tick() - bossSpawnTime
        if elapsed >= 16 and not lightAbilityUsed then
            return true
        end
        return false
    else
        bossSpawnTime = nil
        lightAbilityUsed = false
        return false
    end
end

local function getBossPosition()
    local ok, result = pcall(function()
        local enemies = workspace:FindFirstChild("Enemies")
        if not enemies then return nil end
        local boss = enemies:FindFirstChild("Boss")
        if not boss then return nil end
        local hrp = boss:FindFirstChild("HumanoidRootPart")
        if hrp then
            return hrp.Position
        end
        return nil
    end)
    return ok and result or nil
end

local function getTowerPosition(tower)
    if not tower then return nil end
    local ok, result = pcall(function()
        local hrp = tower:FindFirstChild("HumanoidRootPart")
        if hrp then
            return hrp.Position
        end
        return nil
    end)
    return ok and result or nil
end

local function getTowerRange(tower)
    if not tower then return 0 end
    local ok, result = pcall(function()
        local stats = tower:FindFirstChild("Stats")
        if not stats then return 0 end
        local range = stats:FindFirstChild("Range")
        if not range then return 0 end
        return range.Value or 0
    end)
    return ok and result or 0
end

local function isBossInRange(tower)
    local bossPos = getBossPosition()
    local towerPos = getTowerPosition(tower)
    
    if not bossPos or not towerPos then
        return false
    end
    
    local range = getTowerRange(tower)
    if range <= 0 then return false end
    
    local distance = (bossPos - towerPos).Magnitude
    return distance <= range
end

local function checkBossInRangeForDuration(tower, requiredDuration)
    if not tower then return false end
    
    local towerName = tower.Name
    local currentTime = tick()
    
    if isBossInRange(tower) then
        if requiredDuration == 0 then
            return true
        end
        
        if not bossInRangeTracker[towerName] then
            bossInRangeTracker[towerName] = currentTime
            return false
        else
            local timeInRange = currentTime - bossInRangeTracker[towerName]
            if timeInRange >= requiredDuration then
                return true
            end
        end
    else
        bossInRangeTracker[towerName] = nil
    end
    
    return false
end


while getgenv().AutoAbilitiesEnabled do
    wait(0.5)
    
    local currentWave = getCurrentWave()
    local hasBoss = bossExists()
    
    for towerName, abilities in pairs(TOWER_ABILITIES) do
        local tower = getTower(towerName)
        
        if tower then
            local towerInfoName = getTowerInfoName(tower)
            local towerLevel = getUpgradeLevel(tower)
            
            for _, abilityConfig in ipairs(abilities) do
                local shouldUse = true
                local failReason = nil
                
                if not hasAbilityBeenUnlocked(towerInfoName, abilityConfig.name, towerLevel) then
                    shouldUse = false
                    failReason = "Not unlocked (Level " .. towerLevel .. ")"
                end
                
                if isOnCooldown(towerInfoName, abilityConfig.name) then
                    shouldUse = false
                    failReason = "On cooldown"
                end
                
                if abilityConfig.onlyOnBoss and not hasBoss then
                    shouldUse = false
                    failReason = "Boss required but not present"
                end
                
                if abilityConfig.onlyOnBoss and hasBoss and shouldUse then
                    if not bossReadyForAbilities() then
                        shouldUse = false
                        failReason = "Waiting 1s after boss spawn"
                    end
                end
                
                if abilityConfig.onSpecificWaves and shouldUse then
                    local onCorrectWave = false
                    if type(abilityConfig.onSpecificWaves) == "table" then
                        for _, wave in ipairs(abilityConfig.onSpecificWaves) do
                            if currentWave == wave then
                                onCorrectWave = true
                                break
                            end
                        end
                    else
                        onCorrectWave = (currentWave == abilityConfig.onSpecificWaves)
                    end
                    if not onCorrectWave then
                        shouldUse = false
                        failReason = "Wrong wave (Current: " .. currentWave .. ")"
                    end
                end
                
                if abilityConfig.minWave and currentWave < abilityConfig.minWave then
                    shouldUse = false
                end
                
                if abilityConfig.maxWave and currentWave > abilityConfig.maxWave then
                    shouldUse = false
                end
                
                if abilityConfig.requireBossInRange and shouldUse then
                    local rangeDelay = abilityConfig.rangeDelay or 0
                    if not checkBossInRangeForDuration(tower, rangeDelay) then
                        shouldUse = false

                    end
                end
                
                if shouldUse then
                    if abilityConfig.delayAfterBossSpawn then
                        if checkBossSpawnTime() then
                            useAbility(tower, abilityConfig.name)
                            lightAbilityUsed = true
                            setAbilityUsed(towerInfoName, abilityConfig.name)
                        end
                    else
                        useAbility(tower, abilityConfig.name)
                        setAbilityUsed(towerInfoName, abilityConfig.name)
                    end
                end
            end
        end
    end
end