• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

AI units are not casting an spell even if I successfully issued the order

Level 24
Joined
Jun 26, 2020
Messages
1,928
Hello, I made a system to issue some units to cast spells under certain conditions, but for some reason when I issue the order to cast the spell, the unit doesn't do anything, even if the function IssueXOrder returns true, I don't know why is happening, do you have an idea? I would show the code if you need to, is just that is too complicated and is hard to isolate the only important parts, the most important thing is that the IssueXOrder doesn't do anything even if returns true.
 
Level 24
Joined
Jun 26, 2020
Messages
1,928
I believe showing the code would be the best and the fastest way to find solution.
Or even better, not only show the code here in your post, include a demo map as well.
As I said, the system is too complex to isolate the relevant parts, here is part of the system, focus on the places I use the IssueXOrder functions:
Lua:
local current = 0
Timed.echo(0.5, function ()
    current = current + 0.5
    if UnitAlive(data.boss) then
        if dead then
            dead = false
        end

        local whoAlreadyAre = playersOnField:copy()
        local whoWereHere = unitsInTheField:copy()

        unitsInTheField:clear()
        playersOnField:clear()

        local isInBattlefield = false
        -- Check if are units in the battlefield
        for i = 1, numRect do
            ForUnitsInRect(battlefield[data.boss][i], function (u)
                if u ~= data.boss and UnitAlive(u) and IsPlayerInGame(GetOwningPlayer(u)) then
                    unitsInTheField:addSingle(u)
                    playersOnField:addSingle(GetOwningPlayer(u))
                end
            end)
            isInBattlefield = isInBattlefield or RectContainsUnit(battlefield[data.boss][i], data.boss)
        end

        if playersOnField:contains(LocalPlayer) then
            if not playing then
                playing = true
                ChangeMusic(MUSIC)
            end
        end

        if whoAlreadyAre:except(playersOnField):contains(LocalPlayer) then
            if playing then
                playing = false
                RestartMusic()
            end
        end

        if unitsInTheField:isEmpty() then
            -- Reset the boss
            reset()
            IssuePointOrderById(data.boss, Orders.smart, initialPosX, initialPosY)
        else
            if playersOnField:size() >= data.maxPlayers then
                if playersOnField:size() > data.maxPlayers then
                    whoAlreadyAre = playersOnField:except(whoAlreadyAre)
                    for _ = 1, (playersOnField:size() - data.maxPlayers) do
                        local p = whoAlreadyAre:random()
                        if p then
                            playersOnField:removeSingle(p)
                            whoAlreadyAre:removeSingle(p)
                            for u in unitsInTheField:elements() do
                                if GetOwningPlayer(u) == p then
                                    SetUnitPosition(u, GetRectCenterX(data.entrance), GetRectCenterY(data.entrance))
                                elseif whoAlreadyAre:contains(GetOwningPlayer(u)) then
                                    SetUnitPosition(u, GetRectCenterX(data.inner), GetRectCenterY(data.inner))
                                end
                                unitsInTheField:removeSingle(u)
                            end
                        end
                    end
                end
                for _, d in ipairs(data.forceWall) do
                    ModifyGateBJ(bj_GATEOPERATION_CLOSE, d)
                end
            elseif playersOnField:size() < data.maxPlayers then
                if IsDestructableAliveBJ(data.forceWall[1]) then
                    for _, d in ipairs(data.forceWall) do
                        ModifyGateBJ(bj_GATEOPERATION_OPEN, d)
                    end
                end
            end
            -- Reset aggro for units that left the battlefield
            for u in whoWereHere:except(unitsInTheField):elements() do
                ZTS_RemovePlayerUnit(u)
                ZTS_AddPlayerUnit(u)
            end
            -- The chances of casting increases when has low hp
            interval = getInterval(playersOnField:size(), GetUnitHPRatio(data.boss) < 0.5)

            if current >= interval then
                returned = false
                local u = nil
                local maxThreat = -1
                for u2 in unitsInTheField:elements() do
                    if ignored[data.boss][u2] then
                        ZTS_RemovePlayerUnit(u2)
                        ZTS_AddPlayerUnit(u2)
                    else
                        local threat = ZTS_GetThreatUnitAmount(data.boss, u2)
                        if threat > maxThreat then
                            u = u2
                            maxThreat = threat
                        end
                    end
                end
                if u then
                    if not attacking then
                        attacking = true
                        local x, y = GetUnitX(u), GetUnitY(u)
                        Timed.call(2., function ()
                            if UnitCanAttack(data.boss) then
                                IssuePointOrderById(data.boss, Orders.attack, x, y)
                            end
                        end)
                    else
                        if data.spells then
                            if not isCasting[data.boss] then
                                if spellsCasted >= 0
                                    and not BlzIsUnitInvulnerable(u)
                                    and (GetUnitCurrentOrder(data.boss) == Orders.attack or GetUnitCurrentOrder(data.boss) == Orders.smart or GetUnitCurrentOrder(data.boss) == 0)
                                    and (not data.castCondition or data.castCondition()) then

                                    local chances = {}
                                    local options = {}
                                    for spell, stats in pairs(spells) do
                                        if GetUnitAbilityLevel(data.boss, spell) > 0
                                            and BlzGetUnitAbilityCooldownRemaining(data.boss, spell) <= 0
                                            and BlzGetUnitAbilityManaCost(data.boss, spell, GetUnitAbilityLevel(data.boss, spell) - 1) <= GetUnitState(data.boss, UNIT_STATE_MANA) then

                                            table.insert(options, stats)
                                            chances[#options] = (chances[#options-1] or 0) + stats.weight
                                            print(GetObjectName(spell), chances[#options])
                                        end
                                    end

                                    if options[1] then
                                        spellsCasted = 0
                                        local r = chances[#chances] * math.random()
                                        print(r)
                                        for i = 1, #options do
                                            if r < chances[i] then
                                                local stats = options[i]
                                                if stats.ttype == CastType.IMMEDIATE then
                                                    print("immediate", IssueImmediateOrderById(data.boss, stats.order))
                                                elseif stats.ttype == CastType.POINT then
                                                    print("point", IssuePointOrderById(data.boss, stats.order, GetUnitX(u), GetUnitY(u)))
                                                elseif stats.ttype == CastType.TARGET then
                                                    print("target", IssueTargetOrderById(data.boss, stats.order, u))
                                                end
                                                break
                                            end
                                        end
                                    end
                                    PauseSpellAI(data.boss, false)
                                end
                            end
                        end
                    end
                else
                    IssuePointOrderById(data.boss, Orders.move, initialPosX, initialPosY)
                end

                data.actions(u, unitsInTheField)
            end
        end

        if not isInBattlefield and not canLeave[data.boss] then
            --reset()
            IssuePointOrderById(data.boss, Orders.smart, initialPosX, initialPosY)

            Timed.echo(0.5, 10., function ()
                for i = 1, numRect do
                    isInBattlefield = RectContainsUnit(battlefield[data.boss][i], data.boss)
                    if isInBattlefield then
                        return true
                    end
                end
            end, function ()
                reset()
                SetUnitPosition(data.boss, initialPosX, initialPosY)
                DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl", initialPosX, initialPosY))
                DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl", data.boss, "origin"))
            end)
        end

        -- If the boss is not doing anything, set attacking state to false
        if GetUnitCurrentOrder(data.boss) == 0 then
            attacking = false
        end
    else
        if not dead then
            dead = true

            if playersOnField:contains(LocalPlayer) then
                if playing then
                    playing = false
                    RestartMusic()
                end
            end

            isCasting[data.boss] = false

            if data.returnPlace then
                local tm = CreateTimer()
                local window = CreateTimerDialog(tm)
                TimerDialogSetTitle(window, "Digimons returning in:")
                ForUnitsInRect(data.toTeleport, function (u)
                    TimerDialogDisplay(window, GetOwningPlayer(u) == LocalPlayer)
                end)

                TimerStart(tm, 15., false, function ()
                    TimerDialogDisplay(window, false)
                    DestroyTimerDialog(window)
                    PauseTimer(tm)
                    DestroyTimer(tm)

                    ForUnitsInRect(data.toTeleport, function (u)
                        local p = GetOwningPlayer(u)
                        if IsPlayerInGame(p) then
                            local l = GetRandomLocInRect(data.returnPlace)
                            DestroyEffect(AddSpecialEffectLoc("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl", l))
                            DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl", GetUnitX(u), GetUnitY(u)))
                            SetUnitPositionLoc(u, l)
                            RemoveLocation(l)
                            if data.returnEnv then
                                local d = Digimon.getInstance(u)
                                if d then
                                    d.environment = Environment.get(data.returnEnv)
                                    coroutine.wrap(function ()
                                        SyncSelections()
                                        if IsUnitSelected(u, p) then
                                            d.environment:apply(p, true)
                                            if p == LocalPlayer then
                                                PanCameraToTimed(GetUnitX(u), GetUnitY(u), 0.)
                                            end
                                        end
                                    end)()
                                end
                            end
                        end
                    end)
                end)
            end

            if data.forceWall then
                for _, d in ipairs(data.forceWall) do
                    ModifyGateBJ(bj_GATEOPERATION_OPEN, d)
                end
            end

            unitsInTheField:clear()
            playersOnField:clear()

            SetTextTagVisibility(advice, IsVisibleToPlayer(initialPosX, initialPosY, LocalPlayer))

            if not data.manualRevive then
                local remaining = 360.
                Timed.echo(0.02, 360., function ()
                    remaining = remaining - 0.02
                    SetTextTagText(advice, "Revive in: " .. R2I(remaining), 0.023)
                    SetTextTagVisibility(advice, dead and IsVisibleToPlayer(initialPosX, initialPosY, LocalPlayer))
                    -- In case the boss revived for another reason
                    if UnitAlive(data.boss) then
                        onFinish()
                        return true
                    end
                end, onFinish)
            else
                Timed.echo(1., function ()
                    if UnitAlive(data.boss) then
                        onFinish()
                        return true
                    end
                end)
            end

            if data.onDeath then
                data.onDeath()
            end
        end
    end
    if current >= interval then
        current = 0
    end
end)
is it possible they don't have vision of the target? some orders will only go through if the casting player has vision of the target.
I don't think so, because I'm fighting with them with my own units so they can see me.
are you sure you have the correct handle of the unit?(maybe try printing it's name to be sure)
Let's see.
 
Top