HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Crash After FormGroup()

07-28-2009, 10:07 PM#1
Mystiq
I have this (relatively) simple AI script. It successfully creates an assault group and just after the 5 seconds is up for FormGroup, the map crashes. It doesn't get to the next line. I notice the units don't move like they should.

Collapse JASS:
globals
    integer array attackUnitId
    integer array attackUnitIdCount
    integer attackUnitCount
endglobals

function Msg takes string text returns nothing
    call DisplayTextToPlayer(Player(0), 0, 0, text)
endfunction

function ModInt takes integer dividend, integer divisor returns integer
    local integer modulus = dividend - (dividend / divisor) * divisor

    // If the dividend was negative, the above modulus calculation will
    // be negative, but within (-divisor..0).  We can add (divisor) to
    // shift this result into the desired range of (0..divisor).
    if (modulus < 0) then
        set modulus = modulus + divisor
    endif

    return modulus
endfunction

function IS takes integer i returns string
    local string s = ""
    local integer n
    local integer orig = i
    
    loop
        set n = ModInt(orig, 10)
        if (n == 0) then
            set s = "0" + s
        elseif (n == 1) then
            set s = "1" + s
        elseif (n == 2) then
            set s = "2" + s
        elseif (n == 3) then
            set s = "3" + s
        elseif (n == 4) then
            set s = "4" + s
        elseif (n == 5) then
            set s = "5" + s
        elseif (n == 6) then
            set s = "6" + s
        elseif (n == 7) then
            set s = "7" + s
        elseif (n == 8) then
            set s = "8" + s
        elseif (n == 9) then
            set s = "9" + s
        endif
        set orig = orig / 10
        exitwhen orig == 0
    endloop
    
    return s
endfunction

function GetUnitIdName takes integer uId returns string
    if (uId == GRUNT) then
        return "Grunt"
    elseif (uId == HEAD_HUNTER) then
        return "Head Hunter"
    elseif (uId == CATAPULT) then
        return "Catapult"
    elseif (uId == BERSERKER) then        
        return "Berserker"
    elseif (uId == RAIDER) then
        return "Raider"
    elseif (uId == KODO_BEAST) then
        return "Kodo Beast"
    elseif (uId == TAUREN) then
        return "Tauren"
    elseif (uId == SHAMAN) then
        return "Shaman"
    elseif (uId == WITCH_DOCTOR) then
        return "Witch Doctor"
    elseif (uId == SPIRIT_WALKER_M) or (uId == SPIRIT_WALKER)then
        return "Spirit Walker"
    endif
    return "Unknown Unit Id: " + IS(uId)
endfunction

function AttackMoveKillB takes unit target returns nothing
    call Msg("before debug trace")
    call Sleep( 5 )
    debug call Trace("AttackMoveKillB\n")
    if target == null then
        call SuicideSleep(3)
        return
    endif
    
    call Msg("In AttackMoveKillB")
    call Sleep( 5 )
    debug call Trace("AttackMoveKillA\n")
    
    call Msg("before amk")
    call Sleep( 5 )
    call AttackMoveKill(target)
    
    call Msg("before rutd")
    call Sleep( 5 )
    call ReformUntilTargetDead(target)
    
    call Msg("before sic")
    call Sleep( 5 )
    call SleepInCombat()
endfunction

function SetAttackUnits takes nothing returns nothing
    set attackUnitId[0] = GRUNT
    set attackUnitId[1] = HEAD_HUNTER
    set attackUnitId[2] = CATAPULT
    set attackUnitId[3] = BERSERKER
    set attackUnitId[4] = RAIDER
    set attackUnitId[5] = KODO_BEAST
    set attackUnitId[6] = TAUREN
    set attackUnitId[7] = SHAMAN
    set attackUnitId[8] = WITCH_DOCTOR
    set attackUnitId[9] = SPIRIT_WALKER_M
    set attackUnitCount = 10
endfunction

function ResetAttackReadyUnitCount takes nothing returns nothing
    local integer i
    
    loop
        exitwhen i == attackUnitCount
        set attackUnitIdCount[i] = 0
        set i = i + 1
    endloop
endfunction

function ConfigureAI takes nothing returns nothing
        call SetCampaignAI( )
        call SetDefendPlayer( true )
        call SetRandomPaths( true )
        call SetTargetHeroes( false )
        call SetPeonsRepair( true )
        call SetHeroesFlee( false )
        call SetHeroesBuyItems( true )
        call SetUnitsFlee( true )
        call SetGroupsFlee( true )
        call SetWatchMegaTargets( false )
        call SetIgnoreInjured( false )
        call SetHeroesTakeItems( false )
        call SetSlowChopping( false )
        call SetCaptainChanges( false )
        call SetSmartArtillery( true )
endfunction

function Buildings takes nothing returns nothing
    call SetBuildAll( BUILD_UNIT, 3, PEON, -1)
    call SetBuildAll( BUILD_UNIT, 1, BURROW, -1)
    call SetBuildAll( BUILD_UNIT, 6, PEON, -1)
    call SetBuildAll( BUILD_UNIT, 2, ORC_BARRACKS, -1)
    call SetBuildAll( BUILD_UNIT, 3, BURROW, -1)
    call SetBuildAll( BUILD_UNIT, 1, STRONGHOLD, -1)
    call SetBuildAll( BUILD_UNIT, 2, GRUNT, -1)
    call SetBuildAll( BUILD_UNIT, 3, HEAD_HUNTER, -1)
    call SetBuildAll( BUILD_UNIT, 1, CATAPULT, -1)
    call SetBuildAll( BUILD_UNIT, 3, BURROW, -1)
    call SetBuildAll( BUILD_UNIT, 9, PEON, -1)
    call SetBuildAll( BUILD_UNIT, 1, FORGE, -1)
    call SetBuildAll( BUILD_UNIT, 4, BURROW, -1)
    call SetBuildAll( BUILD_UNIT, 12, PEON, -1)
    call SetBuildAll( BUILD_UNIT, 1, ORC_ALTAR, -1)
    call SetBuildAll( BUILD_UNIT, 5, BURROW, -1)
    call SetBuildAll( BUILD_UNIT, 1, BESTIARY, -1)
    call SetBuildAll( BUILD_UNIT, 1, FORTRESS, -1)
    call SetBuildAll( BUILD_UNIT, 6, BURROW, -1)
    call SetBuildAll( BUILD_UNIT, 1, TOTEM, -1)
    call SetBuildAll( BUILD_UNIT, 1, LODGE, -1)
    call SetBuildAll( BUILD_UNIT, 10, BURROW, -1)
    
    call SetBuildAll( BUILD_UNIT, 3, GRUNT, -1)
    call SetBuildAll( BUILD_UNIT, 3, HEAD_HUNTER, -1)
    call SetBuildAll( BUILD_UNIT, 3, RAIDER, -1)
    call SetBuildAll( BUILD_UNIT, 2, KODO_BEAST, -1)
    call SetBuildAll( BUILD_UNIT, 6, TAUREN, -1)
    call SetBuildAll( BUILD_UNIT, 2, SHAMAN, -1)
    call SetBuildAll( BUILD_UNIT, 2, WITCH_DOCTOR, -1)
    call SetBuildAll( BUILD_UNIT, 3, SPIRIT_WALKER_M, -1)
endfunction

function SetHarvest takes nothing returns nothing
    local integer mine = TownWithMine()
    local integer goldWorkers = 5
    local integer woodWorkers = 4
    call HarvestGold( mine, goldWorkers )
    call HarvestWood( 0, woodWorkers )
endfunction

// get the number of each type of unit we have that can attack
function GetAttackReadyUnitCount takes nothing returns integer
    local integer i = 0
    local integer count = 0

    loop
        exitwhen (i == attackUnitCount)
        set attackUnitIdCount[i] = GetUnitCountDone(attackUnitId[i])
        set count = count + attackUnitIdCount[i]
        //call Msg("Unit id: " + GetUnitIdName(attackUnitId[i]) + ", count: " + IS(attackUnitIdCount[i]))
        set i = i + 1
    endloop
    
    return count
endfunction

// return true if there are any non-zero counts
// ! We should not get here if all the counts are 0, which is the only condition
// that should make this return false.
function RemoveOneRandomNonZeroAttackUnit takes nothing returns boolean
    local integer array nonZero
    local integer nonZeroCount = 0
    local integer i = 0
    
    call Msg("RemoveOneRandomNonZeroAttackUnit")
    
    // loop through all the indices and find the non-zero counts
    loop
        exitwhen i == attackUnitCount
        if ( attackUnitIdCount[i] >= 0 ) then
            set nonZero[nonZeroCount] = i
            set nonZeroCount = nonZeroCount + 1
        endif
        set i = i + 1
    endloop
    
    // if we have at least one non-zero count, reduce a random one by 1
    if ( nonZeroCount > 0 ) then
        call Msg("Removing 1 " + GetUnitIdName(attackUnitId[i]))
        set i = GetRandomInt(0, nonZeroCount - 1)
        set attackUnitIdCount[i] = attackUnitIdCount[i] - 1
        return true
    else
        // return false if they're all 0
        return false
    endif
endfunction


// Return true if there are any units to attack with but ensure that no more
// than count units are sent. Less is ok.
function GetRandomNUnits takes integer maxCount returns boolean
    local integer total = 0
    local integer i = 0
    
    //call ResetAttackReadyUnitCount()
    set total = GetAttackReadyUnitCount()
    call Msg("Total: " + IS(total))

    // loop through all of them
    loop
        exitwhen total <= maxCount
        if ( RemoveOneRandomNonZeroAttackUnit() == true ) then
            set total = total - 1
        else
            call Msg("RemoveOneRandomNonZeroAttackUnit returned false. total: " + IS(total))
            set total = 0 // force a quit
        endif
    endloop
    call Msg("GetRandom(" + IS(maxCount) + ")Units: " + IS(total))
    return total > 4
endfunction


function SetAttackGroup takes nothing returns nothing
    local integer i = 0
    
    loop
        exitwhen i == attackUnitCount
        if ( attackUnitIdCount[i] > 0 ) then
            call Msg("SetAssaultGroup( " + IS(attackUnitIdCount[i]) + ", " + GetUnitIdName(attackUnitId[i]) + ")")
            call SetAssaultGroup(attackUnitIdCount[i], attackUnitIdCount[i], attackUnitId[i])
        endif
        set i = i + 1
    endloop
endfunction

function AttackTarget takes unit target, boolean addAlliance returns nothing
    local real x
    local real y
    if (target == null) then
        return
    endif
    if (addAlliance) then
        call SetAllianceTarget( target )
    endif
    set x = GetUnitX(target)
    set y = GetUnitY(target)
    call PingMinimapEx(x, y, 5, 100, 100, 100, false)
    call Msg("Forming group...")
    call FormGroup( 5, true )
    
    call Msg("Attacking?")    
    call Sleep( 5 )
    call AttackMoveKillB( target )
    call Msg("Wtf alliance stuff.")
    if (not addAlliance) then
        call SetAllianceTarget( null )
    endif
endfunction

function LaunchAttack takes nothing returns nothing
    local unit target = null
    local boolean setAlly = true

    // Don't launch any attack while town is threatened
    if (TownThreatened()) then
        call Sleep( 2 )
        return
    endif

    // Target Priority #1
    call Msg("prio 1")
    if (target == null) then
        set target = GetEnemyExpansion()
    endif

    // Target Priority #2
    call Msg("prio 2")
    if (target == null) then
        set target = GetEnemyExpansion()
        if (target == null) then
            call StartGetEnemyBase(  )
            loop
                exitwhen (not WaitGetEnemyBase())
                call Msg("WaitGetEnemyBase")
                call SuicideSleep( 1 )
            endloop
            set target = GetEnemyBase()
        endif
    endif

    // Target Priority #3
    call Msg("prio 3")
    if (target == null) then
        set target = GetMegaTarget()
    endif

    // Target Priority #4
    call Msg("prio 4")
    if (target == null) then
        set target = GetAllianceTarget()
        if (target != null) then
            set setAlly = false
        endif
    endif

    // Target Priority #5
    call Msg("prio 5")
    if (target == null) then
        set target = GetExpansionFoe()
        if (target != null) then
            set take_exp = false
        endif
    endif

    // Target Priority #6
    call Msg("prio 6")
    if (target == null) then
        set target = GetCreepCamp( 0, 9, false )
    endif

    // Target Priority #7
    call Msg("prio 7")
    if (target == null) then
        set target = GetCreepCamp( 10, 100, true )
    endif
    
    if (target != null) then
        call Msg("target? yar")
        call AttackTarget( target, setAlly )
    else
        // If no target was found, sleep a bit before trying again
        call Msg("target? nar")
        call Sleep( 20 )
    endif
endfunction

function WorkerThread takes nothing returns nothing
    call Msg("WorkThread starting")
    loop
        // Harvesting
        call ClearHarvestAI(  )
        call SetHarvest(  )

        // Building
        call InitBuildArray(  )
        call Buildings(  )

        call Sleep( 1 )
    endloop
endfunction

function AttackThread takes nothing returns nothing
    local integer count = 5
    call Msg("AttackThread starting")    
    loop
        if ( TownThreatened() ) then
            call Msg("TownThreatened()")
            call Sleep( 2 )
        else
            call Msg("Attack tick: " + IS(count))
            if (GetRandomNUnits(count) == true ) then
                call Msg("New attack coming.")
                call InitAssaultGroup()
                call SetAttackGroup()
                call LaunchAttack()
            endif
            call Sleep( 10 )
        endif
    endloop
endfunction

function main takes nothing returns nothing
    call Msg("kos2jass orc.ai")
    call InitAI()
    call Msg("InitAI")
    call ConfigureAI()
    call Msg("ConfigureAI")
    call SetAttackUnits()
    //call CreateCaptains()

    call Sleep ( 1.0 )

    call StartThread( function WorkerThread )
    call StartThread( function AttackThread )

    call PlayGame()
endfunction